I "solved" it with a UDA called GcScan in my code. It can be attached to any field or type and is a tri-state. In the undecided case (GcScan.auto) it recursively scans for potential GC pointers, excluding those marked GcScan.no. In the decided case (GcScan.yes/no) it simply assumes the user knows better and short-circuits the recursion.
If I use a third party struct that contains pointers and does not use my GcScan UDA, but is known not to point to GC memory, I'd tag the outer level with @GcScan.no. Memory allocation via malloc is also wrapped in a function (a typed allocator) that takes GcScan as a hint, so it may or may not add the freshly allocated memory as a GC range. Again, GcScan.auto would trigger auto-detection from the type. -- Marco