Skip to content

evaluate: an unresolved type degrades into a pointer render instead of failing #11

Description

@csm101

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

  1. 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".
  2. 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".
  3. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions