Partial hardening for discovery-pass tolerance during nested object-literal method resolution#260
Merged
Merged
Conversation
…iteral method resolution
Investigates the "chained sibling-method return" gap found while extending
test coverage (a value-returning method can't `return this.sibling(...)` or
`let x = this.sibling(...)` within the same object literal, even though a
bare-statement call to the same sibling works fine).
Root-caused via --debug-only=mlir tracing: an enclosing function with no
explicit return type runs a speculative discovery pass (dummyRun=true,
allowPartialResolve=true) that recurses into any nested object literal's
methods. A method's prototype is only registered into the literal's
(mutable) storage type AFTER its own discovery completes, so an
earlier-declared sibling can still be missing from `this`'s type when a
later method's body is walked for captured-variable/return-type discovery,
regardless of source order.
Fixes two of the (at least three) layers this surfaces, both using the same
dummyRun/allowPartialResolve tolerance idiom already established elsewhere
in both files (mlirGenCallExpression's existing
`!result.value && genContext.allowPartialResolve` case):
- mlirGenPropertyAccessExpressionBaseLogic (MLIRGenAccessCall.cpp): the
"Can't resolve property" error now returns mlir::success() (no value)
instead of hard-failing during a dummy/partial-resolve run.
- discoverFunctionReturnTypeAndCapturedVars (MLIRGenFunctions.cpp): the
"'return' is not found" error is skipped (still returns mlir::failure()
to signal non-convergence, but without emitting a diagnostic) when the
OUTER context is itself a dummy/partial-resolve run.
NOT a complete fix - the original repro still fails, now with a bare
"failed statement" and no specific diagnostic. A third, more fundamental
layer remains: mlir::failure() without an emitted error still propagates as
a hard abort through ordinary sequential statement processing, so the
nested discovery's now-silent non-convergence still aborts the whole
enclosing function's discovery via a statement (`let calc = {...}`)
entirely unrelated to that function's own return type. Full mechanism and
the remaining gap are documented in
docs/interface-vtable-simplification-design.md for whoever picks this up
next - it's not yet known how many more call sites need the same
tolerance, and each layer found so far needed a separately-targeted fix.
Both changes verified individually and together to cause zero change in
behavior for the existing suite: 730/730 passed, unchanged. No regression
test added (the repro this was investigating is still broken).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Investigates (does not fully fix) the "chained sibling-method return" gap found in #259: a value-returning method can't
return this.sibling(...)orlet x = this.sibling(...)within the same object literal — even though calling the same sibling as a bare statement (discarding the result) works fine.Root cause (traced via
--debug-only=mlir)An enclosing function with no explicit return type runs a speculative discovery pass (
dummyRun=true/allowPartialResolve=true) to infer its return type and captured variables. That pass recurses into any nested object literal's methods, including ones unrelated to the enclosing function's own return type. A method's prototype is only registered into the literal's (mutable) storage type after its own discovery completes — so an earlier-declared sibling can still be missing fromthis's type when a later method's body is walked, regardless of source order.What's fixed here (2 of ≥3 layers)
Both changes use the same
dummyRun/allowPartialResolvetolerance idiom already established elsewhere in both files (mlirGenCallExpression's existing!result.value && genContext.allowPartialResolvecase):mlirGenPropertyAccessExpressionBaseLogic(MLIRGenAccessCall.cpp) — "Can't resolve property" now returnsmlir::success()(no value) instead of hard-failing during a dummy/partial-resolve run.discoverFunctionReturnTypeAndCapturedVars(MLIRGenFunctions.cpp) — "'return' is not found" is skipped (still signals non-convergence viamlir::failure(), but without emitting a diagnostic) when the outer context is itself a dummy/partial-resolve run.What's NOT fixed
The original repro still fails — now with a bare
error: failed statementand no specific diagnostic. A third, more fundamental layer remains:mlir::failure(), even without an emitted diagnostic, still propagates as a hard abort through ordinary sequential statement processing. So the nested discovery's now-silent non-convergence still aborts the entire enclosing function's discovery via a statement (let calc = {...}) that has nothing to do with that function's own return type. Fixing this needs the statement-processing layer to tolerate it too, and it's not yet known how many more call sites need the same treatment — each of the two layers fixed here needed its own separately-targeted gate.Full mechanism and the remaining gap are documented in
docs/interface-vtable-simplification-design.mdfor whoever picks this up next.Test plan
Both changes verified individually and together to cause zero behavior change for the existing suite: 730/730 passed, unchanged. No regression test added — the repro this was investigating is still broken, so there's nothing passing yet to lock in.
🤖 Generated with Claude Code