Skip to content

Commit 2f8b466

Browse files
authored
[macros] Specify that identifiers in strings must refer to locals. (#2076)
* [macros] Specify that identifiers in strings must refer to locals. * Clarify how bare identifiers in composed Code objects are resolved. * Identifiers in strings resolve where the generated code ends up.
1 parent 5b1115c commit 2f8b466

File tree

1 file changed

+40
-26
lines changed

1 file changed

+40
-26
lines changed

working/macros/feature-specification.md

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ Authors: Jacob MacDonald, Bob Nystrom
44

55
Status: **Work In Progress**
66

7+
### Changelog
8+
9+
- *2022/01/25:* Specify that identifiers in strings can only refer to local
10+
declarations.
11+
712
## Introduction
813

914
The [motivation][] document explains why we are working on static
@@ -532,7 +537,7 @@ challenges still remain.
532537

533538
#### Referring to generated declarations
534539

535-
A key use of macros is generating new declarations. Since this declarations
540+
A key use of macros is generating new declarations. Since these declarations
536541
aren't useful if not called, it implies that handwritten code may contain
537542
references to declarations produced by macros. This means that before macros are
538543
applied, code may contain identifiers that cannot be resolved.
@@ -617,31 +622,40 @@ macros to library augmentations.
617622

618623
#### Resolving identifiers in generated code
619624

620-
Macros will likely want to introduce references to identifiers that are not in
621-
the scope of the library in which they are running, but are in the scope of the
622-
macro itself, or possibly even references which are not in scope of either the
623-
macro itself or the library where it is applied.
624-
625-
Even if an identifier is in scope of the library in which the macro is applied
626-
(let's say its exported by the macro library), that identifier could be shadowed
627-
by another identifier in the library.
628-
629-
To enable a macro to safely emit a reference to a known identifier, there is
630-
a `Identifier` subtype of `Code`. This class takes both a simple name for the
631-
identifier (no prefix allowed), as well as a library URI, where that identifier
632-
should be looked up.
633-
634-
The generated code should be equivalent to adding a new import to the library,
635-
with the specified URI and a unique prefix. In the code the identifier will be
636-
emitted with that unique prefix followed by its simple name.
637-
638-
Note that technically this allows macros to add references to libraries that
639-
the macro itself does not depend on, and the users application also may not
640-
depend on. This is discouraged, but not prevented, and should result in an error
641-
if it happens.
642-
643-
**TODO**: Investigate other approaches, see [this
644-
comment](https://github.com/dart-lang/language/pull/1779#discussion_r683843130).
625+
When a macro generates code containing an identifier, the identifier must be
626+
resolved in the context of some namespace to determine what declaration it
627+
refers to. It's not enough to simply resolve generated code in the same
628+
namespace where the macro is applied. The macro author may want to, for example,
629+
generate a call to a utility function that the macro author knows about but that
630+
the library applying the macro is unaware of. Or a macro may want to generate
631+
code that creates an instance of some class that is an implementation detail of
632+
the macro and not in scope where the macro is applied.
633+
634+
To support this, there is an `Identifier` subtype of `Code`. An instance of this
635+
class represents an identifier resolved in the context of a known library's
636+
namespace. When the `Identifier` object is inserted into other generated code,
637+
it retains its original resolution.
638+
639+
*A compiler could implement this by generating an import of the library
640+
containing the declaration that the identifier refers to. The compiler adds a
641+
unique prefix to the import and then the identifier emitted as a prefixed
642+
identifier followed by the identifier's simple name.*
643+
644+
Macros also build generated code using strings. Identifiers that appear in bare
645+
strings are resolved where they appear in the generated code, and where that
646+
generated code appears in the library, after all macros in the library have
647+
finished executing. For example, if a macro is generating the body of an
648+
instance method, an identifier in a string used to build that body might resolve
649+
to a local variable declared inside that method, to an instance member on the
650+
surrounding class (which may or may not have been produced by some macro), to a
651+
static method, or to a top level identifier.
652+
653+
If a macro wants to generate code containing an identifier that unambigiously
654+
refers to a top level declaration and can't inadvertently capture a local
655+
variable in surrounding generated code, the macro can create an `Identifier` for
656+
that top level declaration and insert that into the generated code.
657+
658+
**TODO: Define this API. See [here](https://github.com/dart-lang/language/pull/1779#discussion_r683843130).**
645659

646660
### Generating macro applications
647661

0 commit comments

Comments
 (0)