Add experimental when clauses on auto decorators - #11806
Add experimental when clauses on auto decorators#11806Timothee Guerin (timotheeguerin) wants to merge 2 commits into
when clauses on auto decorators#11806Conversation
Emitters targeting different languages routinely need different metadata for
the same declaration, and today that is expressed by inventing a decorator per
emitter. This adds a `when` suffix on `auto` decorator applications so a single
spec can carry per-scope metadata:
@clientName("Widget") when language("csharp") | language("java")
@clientName("widget") when language("python")
@clientName("Thing")
model Widget {}
Emitters read the value for their own scope. `EmitContext.scope` is prefilled
with the emitter's package name and `createScope()` narrows it for emitters
that serve several languages from one package.
Two properties keep this out of conditional-compilation territory:
- Validation is unconditional; only storage is conditioned. A typo inside a
`when`-scoped decorator is an error even when no emitter selects that scope.
- Conditioned values live in a parallel state map keyed `dec-scoped:<fqn>`, so
the existing `getAutoDecoratorValue(program, fqn, target)` contract - and the
`auto` <-> `extern` migration story - are untouched.
`when` is a contextual keyword, so it remains usable as an identifier.
Also fixes a pre-existing gap this depends on: realm clones had no back-pointer
to the type they were cloned from, so any state-map read against a
version-projected clone silently returned `undefined`. `Realm.sourceForType` /
`Realm.sourceOf()` make scoped metadata compose with versioning.
Gated behind the `scoped-decorators` feature flag.
Refs: microsoft#10551
commit: |
|
❌ There is undocummented changes. Run The following packages have changes but are not documented.
The following packages have already been documented:
Show changes
|
|
You can try these changes here
|
|
Does this |
This is still an experiment but yes there should be some priority, most likely specified by the emitter/library where you specify under which scope you want to see the type graph. Also, while enum AzureScopes {
csharp,
python,
javascript,
} |
Emitters targeting different languages routinely need different metadata for the same declaration. Today that is expressed by inventing a decorator per emitter, and the spec ends up carrying one
@clientName-shaped decorator per target.This adds a
whensuffix onautodecorator applications, so one declaration can carry per-scope metadata:Emitters read the value for their own scope.
EmitContext.scopeis prefilled with the emitter's package name;createScope({ language, target })narrows it for emitters serving several languages from one package:Gated behind the
scoped-decoratorsfeature flag.Why this isn't conditional compilation
That was the main risk, and two properties are load-bearing:
when-scoped decorator is an error even when no emitter will ever select that scope.dec-scoped:<fqn>). The unscopedgetAutoDecoratorValue(program, fqn, target)returns exactly what it returned before, so theauto↔externmigration contract is untouched and scope-unaware emitters are unaffected.whenis a contextual keyword, so it remains usable as an identifier — no migration needed.Drive-by fix: realm clone provenance
Realm clones had no back-pointer to the type they were cloned from (
Realm.realmForTypemaps type→realm;getTypeAtVersiononly goes forward). BecauseStateMapRealmView.#select()routes onrealm.hasType(), any state-map read against a version-projected clone silently returnedundefined— versioning only works today because it reads fromoriginalby hand.Realm.sourceForType+Realm.sourceOf()fix this generally, and are what make scoped metadata compose with versioning.Scope of this PR
This is Phase 1 only —
whenonauto decapplications. It deliberately never touches the symbol table or the type graph, which is why it's safe.I spiked the structural phases separately before proposing this; findings are written up in #10551. The short version: Phase 2 as specified in the design doc is conditional compilation and should not be built as written, but a narrower present-or-absent form is viable. That discussion belongs on the issue, not here.
Known gap
Dimension overlap is unresolved:
emitter("@typespec/http-client-csharp")andlanguage("csharp")both match the C# emitter. The design doc says "error on overlapping conditions", but this overlap is inherent, not accidental — an error is the wrong answer. Today the topmost application wins. This needs a specificity rule before the flag comes off.Breaking change
EmitContextgained requiredscope/createScopemembers, which breaks hand-rolled contexts (one in-repo call site inopenapi3is fixed here). Same class as the earlierperfaddition, but calling it out.Compiler suite
4223 passed(baseline4172) with zero regressions;versioning137 andopenapi32583 also green.Refs #10551