What happens
When evaluate cannot resolve a variable's declared type, it does not say so. It
falls back to reading the variable as an object pointer and reports the class
that happens to live at that address, which looks exactly like a correct answer.
Observed once during a full parallel suite run (8 workers), in
NestedTypeNames_ResolveOnBothBitnesses, on the x86 target:
EmptyCols -> $134720 (EVariantInvalidNullOpError) [EVariantInvalidNullOpError]
EmptyCols is an empty set. The correct answer is [] with type TColors.
What came back is an address plus an unrelated RTL exception class, presented
with the same confidence as any other value. Re-run alone, the same expression
answers correctly, so the code that decodes the set is fine: the type was simply
not resolvable at that instant, while a background symbol index was still
building under load.
Why this is worth fixing on its own
A missing answer costs the user a second look. A confident wrong answer costs
them the debugging session: EVariantInvalidNullOpError at $134720 is exactly
the kind of thing somebody chases for twenty minutes before doubting the
debugger. The debugger's job here is to be trustworthy about what it does not
know.
There is already precedent for the guard: GetStackFrames refuses to cache a
walk taken while AnyBackgroundIndexingPending. The evaluate path has no
equivalent.
What it should do instead
- An unresolved declared type must FAIL the evaluation (or render as clearly
unknown), never silently degrade into "read it as a pointer and name the class
at that address".
- The pointer render should only be reachable when the declared type actually
IS a class or an untyped pointer, not as a fallback for "I could not tell".
- If background indexing is pending for the module the expression's symbol lives
in, the evaluate path should either wait within the interactive budget (as
other paths do) or report that symbols are still loading, which the frontends
already know how to display.
A test in the shape of the observation belongs with the fix: evaluate a
non-class-typed variable with the type resolution deliberately unavailable, and
assert the result is a refusal rather than a plausible-looking address.
Context
Seen while turning the background symbol prefetcher on by default (35de2bb). It
is NOT attributed to the prefetcher: the prefetcher does not touch the main
executable's symbols, which is what this test reads, and the same class of
failure is recorded in docs/KNOWN_UNKNOWNS.md from before it. The suite's
sequential re-check classifies the occurrence as load-sensitive and the run stays
green, which is correct for a scheduling artefact but does not make the wrong
answer acceptable.
What happens
When
evaluatecannot resolve a variable's declared type, it does not say so. Itfalls back to reading the variable as an object pointer and reports the class
that happens to live at that address, which looks exactly like a correct answer.
Observed once during a full parallel suite run (8 workers), in
NestedTypeNames_ResolveOnBothBitnesses, on the x86 target:EmptyColsis an empty set. The correct answer is[]with typeTColors.What came back is an address plus an unrelated RTL exception class, presented
with the same confidence as any other value. Re-run alone, the same expression
answers correctly, so the code that decodes the set is fine: the type was simply
not resolvable at that instant, while a background symbol index was still
building under load.
Why this is worth fixing on its own
A missing answer costs the user a second look. A confident wrong answer costs
them the debugging session:
EVariantInvalidNullOpErrorat$134720is exactlythe kind of thing somebody chases for twenty minutes before doubting the
debugger. The debugger's job here is to be trustworthy about what it does not
know.
There is already precedent for the guard:
GetStackFramesrefuses to cache awalk taken while
AnyBackgroundIndexingPending. The evaluate path has noequivalent.
What it should do instead
unknown), never silently degrade into "read it as a pointer and name the class
at that address".
IS a class or an untyped pointer, not as a fallback for "I could not tell".
in, the evaluate path should either wait within the interactive budget (as
other paths do) or report that symbols are still loading, which the frontends
already know how to display.
A test in the shape of the observation belongs with the fix: evaluate a
non-class-typed variable with the type resolution deliberately unavailable, and
assert the result is a refusal rather than a plausible-looking address.
Context
Seen while turning the background symbol prefetcher on by default (35de2bb). It
is NOT attributed to the prefetcher: the prefetcher does not touch the main
executable's symbols, which is what this test reads, and the same class of
failure is recorded in
docs/KNOWN_UNKNOWNS.mdfrom before it. The suite'ssequential re-check classifies the occurrence as load-sensitive and the run stays
green, which is correct for a scheduling artefact but does not make the wrong
answer acceptable.