Skip to content

fix(dsl): resolve modifier applications through the receiver (#100) - #106

Merged
JArmandoAnaya merged 1 commit into
mainfrom
fix/dsl-modifier-application
Aug 2, 2026
Merged

fix(dsl): resolve modifier applications through the receiver (#100)#106
JArmandoAnaya merged 1 commit into
mainfrom
fix/dsl-modifier-application

Conversation

@JArmandoAnaya

Copy link
Copy Markdown
Contributor

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_lane therefore interns
under the actor's qualified name, and ordinary lookup for keep_lane finds
nothing — 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:

  • member position — the enclosing declaration, which is §7.3.12.4.2's third
    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 the with: block is
where 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 holds
a TypeInfo& across the typing call, which can grow Program::types.

Two diagnostics got better on the way:

  • a name that exists but is not a modifier now says what it is, instead of
    "unknown modifier";
  • a modifier belonging to an unrelated actor says so — modifier 'keep_lane' belongs to 'vehicle' and cannot be applied to 'std::person' — instead of
    claiming the name does not exist.

Tests

  • dsl_types_test.cpp +8: both positions, an inherited receiver, an actor
    applying 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 from
    both 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.md and this issue's trail.

Closes #100

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
@JArmandoAnaya
JArmandoAnaya merged commit e10bd27 into main Aug 2, 2026
12 checks passed
@JArmandoAnaya
JArmandoAnaya deleted the fix/dsl-modifier-application branch August 2, 2026 11:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[dsl] Actor-associated modifiers cannot be applied — the actor prefix stays in the declared name

1 participant