Skip to content

Commit f65daa6

Browse files
authored
change shadowing rules to remove need for multiple resolutions (#3452)
1 parent 41fc594 commit f65daa6

File tree

1 file changed

+31
-41
lines changed

1 file changed

+31
-41
lines changed

working/macros/feature-specification.md

Lines changed: 31 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -908,68 +908,58 @@ has two colliding declarations.
908908

909909
#### Shadowing declarations
910910

911-
All the rules below apply only to the library in which a macro is
912-
applied—macro applications in imported libraries are considered to be
913-
fully expanded already and are treated exactly the same as handwritten code.
914-
915911
Macros may add member declarations that shadow top-level declarations in the
916-
library. When that happens, we want to ensure that the intent of the
917-
user-written code is clear. Consider the following example:
912+
library. When that happens, we have to choose how references to that shadowed
913+
member resolve. Consider the following example:
918914

919915
```dart
920916
int get x => 1;
921917
922918
@GenerateX()
923919
class Bar {
924-
// Generated: int get x => 2;
920+
// Generated
921+
int get x => 2;
925922
926923
// Should this return the top level `x`, or the generated instance getter?
927924
int get y => x;
928925
}
929926
```
930927

931-
There are several potential choices we could make here:
932-
933-
1. Any identifier that can be resolved before macro application keeps its
934-
original resolution. Here, `x` would still resolve to the original,
935-
top-level variable.
936-
937-
2. Re-resolve all identifiers after macros are applied, which may change what
938-
they resolve to. In the example here, `x` would re-resolve to the generated
939-
instance getter `x`.
928+
In this situation, resolution is done based on the final macro generated code,
929+
as if it was written by hand. Effectively, this means that resolution of bodies
930+
must be delayed until after macro execution is complete.
940931

941-
3. Make it a compile-time error for a macro to introduce an identifier that
942-
shadows another.
932+
This (mostly) avoids the need for resolving method bodies multiple times, and
933+
also means that all macro code could be replaced with a hand-authored library.
943934

944-
4. Make it a compile-time error to *use* an identifier shadowed by one produced
945-
by a macro.
935+
##### Constant evaluation, Identifiers, and shadowed declarations
946936

947-
The first two choices could be very confusing to users, some will expect one
948-
behavior while others expect the other. The third choice would work but might be
949-
overly restrictive. The final option still avoids the ambiguity, and is a bit
950-
more permissive than the third, so we take that approach.
937+
Given that constant evaluation can be attempted in any phase, it is possible for
938+
it to return a _different result_ for the same piece of code between phases. In
939+
particular the types phase and declaration phase may introduce declarations
940+
which shadow previously resolved identifiers from other libraries.
951941

952-
It's also possible that a top-level declaration and an instance declaration that
953-
shadows it are *both* produced by macros. If we resolved a hand-written
954-
identifier with the same name at different points during macro expansion, it
955-
might refer to different macro-generated declarations. That would also be
956-
confusing, and we don't want to allow that.
942+
If this happens, it would always cause a constant evaluation failure in the
943+
later phase, since identifiers from the current [strongly connected component][]
944+
are not allowed during const evaluation. This is not enough however to catch all
945+
situations, because a macro may not attempt const evaluation at that point, and
946+
could have previously gotten an incorrect result.
957947

958-
These constraints produce this rule: It is a compile-time error if any
959-
hand-authored identifier in a library containing a macro application would bind
960-
to a different declaration when resolved before and after macro expansion in
961-
that library.
948+
Similarly, a Code object provided as a part of a metadata annotation argument
949+
may have Identifiers which were originally resolved to one declaration, and then
950+
later resolved to a different declaration.
962951

963-
This follows from the general principle that macros should not alter the
964-
meaning of existing code. Adding the getter `x` in the example above shadows the
965-
top-level `x`, changing the meaning of the original code.
952+
In order to resolve these discrepancies we add this rule:
966953

967-
Note, that if the getter were written as `int get y => this.x;`, then a macro
968-
*would* be allowed to introduce the new getter `x`, because `this.x` could not
969-
previously be resolved.
954+
- **It is a compile time error for a macro to add a declaration which shadows**
955+
**any previously resolved identifier.**. These errors occur after a macro
956+
runs, when the compiler is merging in the macro results, and so it is not
957+
catchable or detectable by macros.
970958

971-
**TODO**: Revisit this to see if it aligns with the scoping rules of compiling
972-
macros to library augmentations.
959+
This situation can typically only happen because of one of the above scenarios
960+
surrounding metadata annotations or const evaluation, and can typically be
961+
resolved by adding an import prefix. To force resolution to the generated symbol
962+
in the current library, a library can import itself with a prefix.
973963

974964
#### Resolving identifiers in generated code
975965

0 commit comments

Comments
 (0)