fix(dsl): resolve modifier applications through the receiver (#100) - #106
Merged
Conversation
An actor-associated modifier could not be applied at all, in either of the positions §7.3.12.4.1 defines. §7.3.12.2 puts such a modifier's name "in the actor scope", so `modifier vehicle.keep_lane` interns under the actor's qualified name and ordinary lookup never finds `keep_lane`. Application now resolves the actor expression's type and walks its inheritance chain, probing the name table once per step — an exact lookup, not a scan, so it stays deterministic. With the actor omitted the receiver is what the site already implies: the enclosing declaration in a member position (§7.3.12.4.2's third example), the invoked behavior's actor inside a `with:` block. The larger half: a `with:` block was not validated at all — a nonsense modifier name was accepted silently — and that is where the domain model expects nearly every movement modifier to be applied. It is checked now. The check moved from pass 3 to pass 4, because resolving the receiver means typing an expression, which needs an ExpressionContext. Two diagnostics improved on the way: a name that exists but is not a modifier says what it is, and a modifier belonging to an unrelated actor says so instead of "unknown". Tests: 8 in dsl_types_test.cpp over the language rule, 3 in dsl_stdlib_test.cpp applying §8.7, §8.9 and §8.12.2 modifiers from both positions — the surface #100 asked the fix to be tested against. Closes #100
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.
An actor-associated modifier could not be applied at all — in either of the two
positions §7.3.12.4.1 defines. This unblocks p8-s3 (#46), whose whole surface is
§8.9's seventeen movement modifiers.
The cause
§7.3.12.2 says an actor-associated modifier's name is "a unique name … in the
actor scope". The declaration
modifier vehicle.keep_lanetherefore internsunder the actor's qualified name, and ordinary lookup for
keep_lanefindsnothing — which is exactly what the application site was doing.
The fix
Application resolves the actor expression's type and walks its inheritance
chain, probing the name table once per step. That is an exact map lookup per
supertype, not a scan, so it stays deterministic and costs nothing at library
scale.
With the actor omitted, the receiver is what the site already implies:
example (a modifier applied inside an actor declaration, applying to every
instance), or the actor a behavior is declared on;
with:block — the invoked behavior's actor, which is what§7.3.12.4.1 means by "can be omitted when it is the same as the scenario actor
the modifier is applied in" and what Code 57 shows.
The larger half of the bug: a
with:block was not validated at all.no_such_modifier(1)inside one was accepted silently, and thewith:block iswhere the domain model expects nearly every movement modifier to be applied.
The check moved from resolution pass 3 to pass 4, because resolving a receiver
means typing an expression and that needs an
ExpressionContext. Nothing holdsa
TypeInfo&across the typing call, which can growProgram::types.Two diagnostics got better on the way:
"unknown modifier";
modifier 'keep_lane' belongs to 'vehicle' and cannot be applied to 'std::person'— instead ofclaiming the name does not exist.
Tests
dsl_types_test.cpp+8: both positions, an inherited receiver, an actorapplying its own modifier without naming itself, an unrelated actor rejected,
argument names checked inside a
with:block, and the not-a-modifier message.dsl_stdlib_test.cpp+3: modifiers from §8.7, §8.9 and §8.12.2 applied fromboth positions;
person.keep_lane()rejected; and the three unassociated§8.9 modifiers applied plainly, since the §8.8/§8.9 name collision gives them
a different application path from their fourteen associated siblings.
1321 gtest (+11), 194 pytest. No existing test relied on the silent acceptance.
No ADR
The scoping model is the standard's, stated outright in §7.3.12.2 — this makes
the implementation match it rather than deciding anything. The reasoning lives
in code comments,
frontends/dsl/README.mdand this issue's trail.Closes #100