diff --git a/working/augmentation-libraries/feature-specification.md b/working/augmentation-libraries/feature-specification.md
index 28ee461e1..508bc7391 100644
--- a/working/augmentation-libraries/feature-specification.md
+++ b/working/augmentation-libraries/feature-specification.md
@@ -3,10 +3,9 @@
Author: rnystrom@google.com, jakemac@google.com, lrn@google.com
Version: 1.35 (see [Changelog](#Changelog) at end)
-Augmentations allow spreading your implementation across multiple locations,
+Augmentations allow splitting a declaration across multiple locations,
both within a single file and across multiple files. They can add new top-level
-declarations, inject new members into classes, and wrap functions and variables
-in additional code.
+declarations, inject new members into classes, and provide bodies for functions.
## Motivation
@@ -15,7 +14,7 @@ a single file, you can usually split it into multiple libraries and then have
one main library export the others. That works well when the functionality in
each file is made of separate top-level declarations.
-However, sometimes a single *class* declaration is too large to fit comfortably
+However, sometimes a single declaration is too large to fit comfortably
in a file. Dart libraries and even part files are no help there. Because of
this, users have asked for something like partial classes in C# ([#252][] 71 👍,
[#678][] 18 👍). C# also supports splitting [the declaration and implementation
@@ -41,44 +40,16 @@ machine-generated code in separate files so that the code generator doesn't
inadvertently erase a user's code. AngularDart generates a separate library for
the component. The freezed and built_value packages generate part files.
-### Macros
-
-The language team is investigating adding [macros][] to Dart. In theory, this
-would *reduce* the need for text-based code generation language features.
-Instead of code generation, users could write macros instead. They'd let the
-compiler expand them, and not worry about anything ending up on disk.
-
-[macros]: https://github.com/dart-lang/language/blob/master/working/macros/feature-specification.md
-
-If users never needed to fix bugs in macros, look at stack traces, step into
-code in a debugger, or navigate to the location of a compile error, that would
-be true. But understanding and digging into code is fundamental to programming.
-An error could be thrown from code generated by a macro. A macro might generate
-code that produces a compile error (either deliberately because the macro was
-misused or because the macro itself has a bug). A user might be profiling their
-application and a macro might generate particularly slow code.
-
-In all of those cases, users need some way to read and understand code produced
-by macros. So, even if that code is generated automatically by the compiler as
-part of macro expansion, it's still useful to have it be in a canonical
-well-specified form that users can understand.
-
-This proposal defines that format. The idea is that a Dart compiler executes
-macros and then produces one or more new part files that contain all
-of the changes that the macros made to the library where they are applied, as
-new declarations to be added or augmentations that modify existing
-declarations. The compiler then adds those part files to the existing libraries.
-
-But improved part files and augmenting declarations are not *only* a
-serialization format for macros. They are first-class language features that
-can be produced by non-macro code generators or written by hand by users who
-simply want to break a giant library or class into smaller files.
+This approach works well when the generated code consists of completely separate
+declarations from the hand-authored code. But if a code generator wants to, say,
+add a method to a hand-authored class, then the language is of little help. This
+proposal addresses that limitation by adding *augmentations*.
### Augmentation declarations
This feature introduces the modifier `augment` as the first token of many
-kinds of declarations. These declarations are known as _augmentation
-declarations_.
+kinds of declarations. These declarations are known as *augmentation
+declarations*.
*In Dart without this feature there are no augmentation declarations. Now
that we are adding augmentation declarations we need to have a term that
@@ -86,73 +57,59 @@ denotes a declaration which is not an augmentation. That is, it is one of
the "normal" declarations that we've had all the time.*
We say that a declaration which is not an augmentation declaration is an
-_introductory_ declaration.
+*introductory declaration*.
Augmentation declarations include:
-* Type augmentations, which can add new members to types, including adding new
- values to enums, or even alter the type hierarchy by adding mixin
- applications to a class.
-
-* Function augmentations, which can replace the body of a function, or provide
- a body if none was present.
-
-* Variable augmentations, which can wrap the initializer of a variable in the
- augmented library, or provide an initializer if none was present.
-
-A non-local variable induces a getter and possibly a setter. It is possible
-to augment getters and setters (this is a kind of function augmentation),
-including the ones that are induced by variables.
+* Type augmentations, which can add new members to types, add new values to
+ enums, or add to the `with` or `implements` clauses.
-*This means that augmentation of a variable declaration can be a getter
-declaration that augments the induced getter, a setter declaration that
-augments the induced setter, or a variable declaration that augments the
-initializer. The augmenting declarations can themselves be an implicitly
-induced getter or setter, or an explicitly declared one.*
-
-*Note that an abstract variable declaration and an external variable
-declaration correspond to a getter declaration and possibly a setter
-declaration. There is no notion of an initializing expression, and hence
-also no support for augmenting it.*
+* Function augmentations, which can provide a body.
These operations cannot be expressed today using only imports, exports, or
part files. Any Dart file (library file or part file) can contain
augmentation declarations. *In particular, an augmentation can augment a
declaration in the same file in which it occurs.*
-A type augmentation can add new members to an existing type, or augment a
-member declaration in the same context *(that is, in the same type
-augmentation, or in a type declaration that it augments)*.
-
-Because of augmentations, non-abstract class, mixin, mixin class, enum,
-extension type, and extension declarations are now allowed to contain
-abstract member declarations, as long as those members are equipped with a
-body by an augmentation declaration.
-
-### Augmented expressions
-
-An augmentation that replaces the body of a function may also want to
-preserve and run the code of the augmented declaration (hence the name
-"augmentation"). It may want to run its own code before the augmented
-code, after it, or both. To support that, we allow a new expression syntax
-inside the "bodies" of augmenting declarations (some function bodies and
-variable initializers). Inside an expression in an augmenting member
-declaration, the identifier `augmented` can be used to refer to the augmented
-function, getter, or setter body, or variable initializer. This is a contextual
-reserved word within `augment` declarations, and has no special meaning outside
-of that context. See the [augmented expression](#augmented-expression) section
-for a full specification of what `augmented` means, and how it must be used, in
-the various contexts.
-
-*Note that within an augmenting member declaration, a reference to a member
-by the same name refers to the final version of the member (and not the one
-being augmented). The only way to reference the augmented member is by
-using the keyword `augmented`.*
+An augmentation can fill in a body for a declared member that has no body, but
+can't *replace* an existing body or add to it. In order to allow augmentations
+to provide bodies for static methods and top-level functions, we allow
+declarations of those to be "abstract" and lack a body as long as a body is
+eventually provided by an augmenting declaration.
+
+#### Design principle
+
+When designing this feature, a fundamental question is how much power to give
+augmentations. Giving them more ability to change the introductory declaration
+makes them more powerful and expressive. But the more an augmentation can
+change, the less a reader can correctly assume from reading only the
+introductory declaration. That can make code using augmentations harder to
+understand and work with.
+
+To balance those, the general principle of this feature is that augmentations
+can *add new capabilities* to the declaration and *fill in implementation*, but
+generally can't change any property a reader knows to be true from the
+introductory declaration or any prior augmentation. In other words, if a program
+would work without the augmentation being applied, it should generally still
+work after the augmentation is applied. *Note that this a design principle and
+not a strict guarantee.*
+
+For example, if the introductory declaration of a function takes an `int`
+parameter and returns a `String`, then any augmentation must also take an `int`
+and return a `String`. That way a reader knows how to call the function and what
+they'll get back without having to read the augmentations.
+
+Likewise, if an introductory class declaration has a generative constructor,
+then the reader assumes they can inherit from that class and call that as a
+superclass constructor. Therefore, an augmentation of the class is prohibited
+from changing the constructor to a factory.
## Syntax
The grammar changes are fairly simple. The grammar is modified to allow an
-`augment` modifier before various declarations:
+`augment` modifier before various declarations. Also, functions, getters, and
+setters are allowed to have `;` bodies even when not instance members (except
+for local functions, which must still have a real body).
```
topLevelDeclaration ::= classDeclaration
@@ -172,14 +129,17 @@ topLevelDeclaration ::= classDeclaration
| 'augment'? 'late' 'final' type? initializedIdentifierList ';'
| 'augment'? 'late'? varOrType initializedIdentifierList ';'
-classDeclaration ::= 'augment'? (classModifiers | mixinClassModifiers)
+classDeclaration ::=
+ 'augment'? (classModifiers | mixinClassModifiers)
'class' typeWithParameters superclass? interfaces?
memberedDeclarationBody
| 'augment'? classModifiers 'mixin'? 'class' mixinApplicationClass
-mixinDeclaration ::= 'augment'? 'base'? 'mixin' typeIdentifier
- typeParameters? ('on' typeNotVoidNotFunctionList)? interfaces?
- memberedDeclarationBody
+mixinDeclaration ::=
+ 'base'? 'mixin' typeIdentifier typeParameters?
+ ('on' typeNotVoidNotFunctionList)? interfaces? memberedDeclarationBody
+ | 'augment' 'base'? 'mixin' typeIdentifier typeParameters?
+ interfaces? memberedDeclarationBody
extensionDeclaration ::=
'extension' typeIdentifierNotType? typeParameters? 'on' type
@@ -191,7 +151,8 @@ extensionTypeDeclaration ::=
'extension' 'type' 'const'? typeIdentifier
typeParameters? representationDeclaration interfaces?
memberedDeclarationBody
- | 'augment' 'extension' 'type' typeIdentifier typeParameters? interfaces?
+ | 'augment' 'extension' 'type' 'const'? typeIdentifier
+ typeParameters? interfaces?
memberedDeclarationBody
enumType ::= 'augment'? 'enum' typeIdentifier
@@ -206,234 +167,238 @@ memberedDeclarationBody ::= '{' memberDeclarations '}'
memberDeclarations ::= (metadata memberDeclaration)*
-memberDeclaration ::= 'augment'? declaration ';'
+memberDeclaration ::= declaration
| 'augment'? methodSignature functionBody
enumEntry ::= metadata 'augment'? identifier argumentPart?
| metadata 'augment'? identifier typeArguments?
'.' identifierOrNew arguments
-declaration ::= 'external'? factoryConstructorSignature
- | 'external' constantConstructorSignature
- | 'external' constructorSignature
- | 'external'? 'static'? getterSignature
- | 'external'? 'static'? setterSignature
- | 'external'? 'static'? functionSignature
- | 'external' ('static'? finalVarOrType | 'covariant' varOrType) identifierList
- | 'external'? operatorSignature
- | 'abstract' (finalVarOrType | 'covariant' varOrType) identifierList
- | 'static' 'const' type? initializedIdentifierList
- | 'static' 'final' type? initializedIdentifierList
- | 'static' 'late' 'final' type? initializedIdentifierList
- | 'static' 'late'? varOrType initializedIdentifierList
- | 'covariant' 'late' 'final' type? identifierList
- | 'covariant' 'late'? varOrType initializedIdentifierList
- | 'late'? 'final' type? initializedIdentifierList
- | 'late'? varOrType initializedIdentifierList
- | redirectingFactoryConstructorSignature
- | constantConstructorSignature (redirection | initializers)?
- | constructorSignature (redirection | initializers)?
+declaration ::=
+ 'augment'? 'external'? factoryConstructorSignature ';'
+ | 'augment'? 'external' constantConstructorSignature ';'
+ | 'augment'? 'external' constructorSignature ';'
+ | 'augment'? 'external'? 'static'? getterSignature ';'
+ | 'augment'? 'external'? 'static'? setterSignature ';'
+ | 'augment'? 'external'? 'static'? functionSignature ';'
+ | 'external' ('static'? finalVarOrType | 'covariant' varOrType) identifierList ';'
+ | 'augment'? 'external'? operatorSignature ';'
+ | 'augment'? 'abstract' (finalVarOrType | 'covariant' varOrType) identifierList ';'
+ | 'static' 'const' type? initializedIdentifierList ';'
+ | 'static' 'final' type? initializedIdentifierList ';'
+ | 'static' 'late' 'final' type? initializedIdentifierList ';'
+ | 'static' 'late'? varOrType initializedIdentifierList ';'
+ | 'covariant' 'late' 'final' type? identifierList ';'
+ | 'covariant' 'late'? varOrType initializedIdentifierList ';'
+ | 'late'? 'final' type? initializedIdentifierList ';'
+ | 'late'? varOrType initializedIdentifierList ';'
+ | 'augment'? redirectingFactoryConstructorSignature ';'
+ | 'augment'? constantConstructorSignature (redirection | initializers)? ';'
+ | 'augment'? constructorSignature (redirection | initializers)? ';'
```
-It is a compile-time error if:
+*Note that the grammar for putting `augment` before an extension type
+declaration doesn't allow also specifying a representation field. This is by
+design. An extension type augmentation always inherits the representation field
+of the introductory declaration and can't specify it.*
-* A declaration marked `augment` is also marked `external`. **(TODO: Probably
- remove for functions, so change to "A variable declaration". A macro should
- be able to implement a method as an external with a `@JS()` annotation.)**
+*Likewise, the grammar for an augmenting `mixin` declaration does not allow
+specifying an `on` clause. Only the introductory declaration permits that. We
+could relax this restriction if compelling use cases arise.*
## Static semantics
### Declaration ordering relations
As part of the meta-programming and augmentation features, we expand the
-capabilities of part files. See ["Parts with Imports"][parts_with_imports.md].
-
-With that feature, a part file can now have its own `import` and `export`
-directives, and further nested `part` files, with part files inheriting the
-imports and prefixes of their parent (part or library) file.
+[capabilities of part files][parts with imports]. With that feature, a part file
+can now have its own `import` and `export` directives, and further nested `part`
+files, with part files inheriting the imports and prefixes of their parent (part
+or library) file.
-Augmentation declarations interact with part files mainly in restrictions on
-where an augmenting declaration may occur relative to the declaration it
-augments, as described below.
+[parts with imports]: parts_with_imports.md
-For this, we define the following relations on *declarations* based on the
-relations between *files* of a library.
+Augmentation declarations interact with part files in restrictions on where an
+augmenting declaration may occur relative to the declaration it augments. We
+define the following relations on *declarations* based on the relations between
+*files* of a library.
We say that a syntactic declaration *occurs in* a Dart file if the
declaration's source code occurs in that Dart file.
-We then say that a Dart file *contains* a declaration if the declaration occurs
-in the file itself, or if any of the files included by the Dart file contain
-the declaration. _That is, if the declaration occurs in a file in the subtree
-of that Dart file._
+We say that a Dart file *contains* a declaration if the declaration occurs in
+the file itself, or if any of the part files transitively included by the Dart
+file contain the declaration. *That is, if the declaration occurs in a file in
+the part subtree of that Dart file.*
-We then define two *orderings* of declarations in a library, one partial and one
-complete, as follows:
+We then define two orderings of declarations in a library, one partial and one
+complete.
-We define a partial ordering on syntactic declarations of a library,
-*is above*, such that a syntactic declaration *A* is *above* a syntactic
-declaration *B* if and only if:
+#### "Is above"
-* *A* and *B* occur in the same file, and the start of *A* is syntactically
- before the start of *B*, in source order, or
-* The file where *A* occurs includes the file where *B* occurs.
+A syntactic declaration *A* *is above* a syntactic declaration *B* if and only
+if:
-We define a *total ordering relation* (transitive, anti-symmetric, irreflexive)
-on declarations of a library, *is before* (and its reverse, *is after*) such
-that for any two syntactic declarations *A*, and *B*:
+* *A* and *B* occur in the same file, and *A*'s declared name is syntactically
+ before *B*'s declared name, in source order, or
+
+* The file where *A* occurs includes the file where *B* occurs. *In other
+ words, if there is a `part` chain from the file where A is declared to the
+ file where B is declared, then A is above B.*
+
+This is a partial order. If A and B occur in sibling part files where neither
+declaration's file contains the other, then there is no is above relation
+between the declarations.
+
+#### "Is before" and "is after"
+
+For any two syntactic declarations *A*, and *B*:
+
+* If *A* is above *B* then *A* is before *B*.
+
+* If *B* is above *A* then *B* is before *A*.
+
+* Otherwise, *A* and *B* are in sibling branches of the part tree:
+
+ * Let *F* be the least containing file for those two files. *Find the
+ nearest root file in the part subtree that contains both A and B.
+ Neither A nor B will occur directly in F because if it did, then A or B
+ would be above the other and the previous clauses would have handled
+ it.*
-* If *A* and *B* occur in the same file, then:
- * If the start of *A* is syntactically before the start of *B* in source
- order, then *A* is before *B*.
- * Otherwise *B* is before *A*.
-* Otherwise *A* and *B* occur in different files:
- * Let *F* be the least containing file for those two files.
- * If *A* occurs in *F* then *A* is before *B*.
- * If *B* occurs in *F* then *B* is before *A*.
- * Otherwise *A* and *B* are contained in distinct included files of *F*.
* If the `part` directive in *F* including the file that contains *A* is
syntactically before the `part` directive in *F* including the file that
contains *B* in source order, then *A* is before *B*.
+
* Otherwise *B* is before *A*.
Then *B* *is after* *A* if and only if *A* *is before* *B*.
-*In short, if *A* is above *B*, then *A* is before *B*. Otherwise, they are
-in sibling part subtrees and the directive in the subtree whose `part`
-directive occurs first is before the other.*
+*In short, we complete the partial "is above" order by taking the order of
+`part` directives themselves into account when declarations are in sibling
+branches of the part tree.*
This order is total. It effectively orders declarations by a pre-order
-depth-first traversal of the file tree, visiting declarations of a file
-in source order, and then recursing on `part`-directives in source order.
+depth-first traversal of the file tree, visiting declarations of a file in
+source order, and then recursing on `part` directives in source order.
-[parts_with_imports.md]: parts_with_imports.md "Parts with Imports Feature Specification"
+### Augmentation context
-### Declaration context
+Prior to this proposal, an entity like a class or function is introduced by a
+single syntactic declaration. With augmentations, an entity may be composed out
+of multiple declarations, the introductory one and any number of augmentations.
+We define a notion of a *augmentation context* to help us talk about the
+location where we need to look to collect all of the declarations that define
+some entity.
-The _context_ of a top-level declaration in a Dart file is the library of the
-associated tree of Dart files. The context of a member declaration in a type
-declaration named `N` is the set of type declarations (introductory or
-augmenting) named `N` in the enclosing set of Dart files.
+* The augmentation context of a top-level declaration is the library and its
+ associated tree of part files.
-*In Dart without this feature, a declaration generally introduces an entity
-(a class, a method, a variable, etc). With the augmentation feature, such
-entities are introduced by a sequence of declarations rather than a single
-declaration. A single declaration can still do it, that's just a special
-case, but we need to talk about these sequences of declarations as being a
-single thing. The notion of a context helps us doing this by indicating the
-location where we need to look in order to find all those declarations.
-Note that this location does not have to be contiguous, it can consist of a
-set of ranges (e.g., the context of a member declaration can be a set of
-class declarations).*
+* The augmentation context of a member declaration in a type or extension
+ declaration named *N* is the set of type declarations (introductory and
+ augmenting) named *N* in the enclosing set of Dart files.
-*Some declarations do not match any of these cases (e.g., a local variable
-declaration in a method body), but this does not matter: We never need to
-talk about the context of a local variable.*
+*Note that augmentation context is only defined for the kinds of declarations
+that can be augmented. We don't define an augmentation context for, say, local
+variable declarations, because those aren't subject to augmentation.*
### Scoping
-The static and instance member namespaces for a type or extension declaration,
-augmenting or not, are lexical only. Only the declarations (augmenting or not)
-declared inside the actual declaration are part of the lexical scope that
-member declarations are resolved in.
-
-_This means that a static or instance member declared in the augmented
-declaration of a class is not *lexically* in scope in a corresponding
-augmenting declaration of that class, just as an inherited instance member
-is not in the lexical scope of a class declaration._
-
-If a member declaration needs to reference a static or instance member
-declared in another introductory or augmenting declaration of the same
-type, it can use `this.name` for instance members an `TypeName.name` for
-static members to be absolutely sure. Or it can rely on the default if
-`name` is not in the lexical scope at all, in which case it’s interpreted
-as `this.name` if it occurs inside a scope where a `this` is
-available. _This approach is always potentially dangerous, since any
-third-party import adding a declaration with the same name would break the
-code. In practice that’s almost never a problem, because instance members
-and top-level declarations usually use different naming strategies._
-
-Example:
+The static and instance member namespaces for an augmented type or extension
+declaration include the declarations of all members in the introductory and
+augmenting declarations. Identifiers in the bodies of members are resolved
+against that complete merged namespace. *In other words, augmentations are
+applied before identifiers inside members are resolved.*
-```dart
-// Main library "some_lib.dart":
-import 'other_lib.dart';
+It is already a **compile-time error** for multiple declarations to have the
+same name in the same scope. This error is checked *after* part files and
+augmentations have been applied. *In other words, it's an error to declare the
+same top-level name in a library and a part, the same top-level name in two
+parts, the same static or instance name inside an introductory declaration and
+an augmentation on that declaration, or the same static or instance name inside
+two augmentations of the same declaration.*
-part 'some_augment.dart';
+*For example:*
-const b = 37;
+```dart
+// Library "main.dart":
+part 'other.dart';
+
+const name = 'top level';
class C {
- static const int b = 42;
- bool isEven(int n) {
- if (n == 0) return true;
- return !_isOdd(n - 1);
+ test() {
+ print(name);
}
}
-// Augmentation "some_augment.dart":
-part of 'some_lib.dart';
+main() {
+ C().test();
+}
-import 'also_lib.dart';
+// Part file "other.dart":
+part of 'main.dart';
augment class C {
- bool _isOdd(int n) => !this.isEven(n - 1);
- void printB() { print(b); } // Prints 37
+ String get name => 'member';
}
```
-This code is fine. Code in `C.isEven` can refer to members added
-in the augmentation like `_isOdd()` because there is no other `_isOdd` in
-scope, and code in `C._isOdd` works too by explicitly using `this.isEvent` to
-ensure it calls the correct method.
+This program prints "member", not "top level". When `name` is resolved inside
+`test()` it walks up to the instance member scope for `C`. Since that scope
+contains the merged members of all applied augmentations, it finds the `name`
+getter added by the augmentation and uses that instead of continuing and
+finding the top level name.
You can visualize the namespace nesting sort of like this:
```
-some_lib.dart :
- : 1;
+ set x(String value) {}
+
+ @metadataToAdd
+ augment abstract var x; // What type is inherited here?
+}
+```
+
+It's a **compile-time error** if an abstract variable augments a getter and
+setter that don't have a combined signature.
## Applying augmentations
-An augmentation declaration _D_ is a declaration marked with the new
-built-in identifier `augment`, which makes _D_ augment a declaration _D1_
-with the same name and in the same context as _D_. _D1_ is determined as
-being before _D_ and after every other declaration with the same name and
-in the same context which is before _D_ *(that is, _D1_ is the greatest
-declaration which is smaller than _D_, according to the 'after'
-ordering)*. A compile-time error occurs if no declaration satisfies the
-requirements on _D1_.
+An augmentation declaration *D* is a declaration marked with the built-in
+identifier `augment`. We add `augment` as a built-in identifier as a language
+versioned change, to avoid breaking pre-feature code.
+
+*D* augments a declaration *I* with the same name and in the same augmentation
+context as *D*. There may be multiple augmentations in the augmentation context
+of *D*. More precisely, *I* is the declaration before *D* and after every other
+declaration before *D*.
+
+It's a **compile-time error** if there is no matching declaration *I*. *In other
+words, it's an error to have a declaration marked `augment` with no declaration
+to apply it to.*
-We say that _D1_ is the declaration which is _augmented_ by _D_.
+We say that *I* is the declaration which is *augmented by* *D*.
-*Note that _D1_ can be an augmentation declaration or an introductory
-declaration.*
+*In other words, take all of the declarations with the same name in some
+augmentation context, order them according to the "after" relation, and each
+augments the result of all the prior augmentations applied to the original
+declaration . The first one must not be marked `augment` and all the subsequent
+ones must be.*
An augmentation declaration does not introduce a new name into the surrounding
scope. *We could say that it attaches itself to the existing name.*
-Making `augment` a built-in identifier is language versioned, to make it
-non-breaking for pre-feature code.
+### Complete and incomplete declarations
+
+Augmentations aren't allowed to *replace* code, so they mostly add entirely new
+declarations to the surrounding type. However, function and constructor
+augmentations can fill in a body for an augmented declaration that is lacks one.
+
+More precisely, a function or constructor declaration (introductory or
+augmenting) is *incomplete* if all of:
+
+* The body syntax is `;`.
+
+* The function is not marked `external`. *An `external` function is considered
+ to have a body, just not one that is visible as Dart code.*
+
+* There is no redirection, initializer list, initializing formals, or super
+ parameters. *Obviously, this only applies to constructor declarations.*
+
+If a declaration is not *incomplete* then it is *complete*.
+
+It's a **compile-time error** if an augmentation is complete and any declaration
+before it in the augmentation chain is also complete. *In other words, once a
+declaration has acquired a body, no augmentation can replace it with another.*
+
+*It is allowed to augment a complete declaration long as the augmentation itself
+is incomplete. This can be useful for an augmentation to add metadata.*
+
+*Examples:*
+
+```dart
+a() {}
+augment a() {} // Error.
+
+b();
+augment b() {} // OK.
+
+c() {}
+@meta
+augment c(); // OK.
+
+d() {}
+augment d(); // OK.
+augment d() {} // Error.
+```
+
+*Note that the initializer list and body are not treated separately. If a
+constructor declaration has an initializer list and `;` body, it is still
+considered complete. Likewise, a constructor with no initializer list but a
+non-`;` body is complete. Thus a constructor can't acquire an initializer
+list in one declaration and a constructor body in another. For example:*
+
+```dart
+class C {
+ C() : assert(true);
+}
+
+augment class C {
+ augment C() { body; } // Error. C() is already complete.
+}
+```
### Application order
The same declaration can be augmented multiple times by separate augmentation
declarations. This occurs in the situation where an augmentation
declaration has an augmented declaration which is itself an augmentation
-declaration, and so on, until an introductory declaration is reached. This
-will happen in a finite number of steps because of the nature of the
-before/after ordering.
+declaration, and so on, until an introductory declaration is reached.
+
+In some cases (enum values, `with` clauses, etc.), the order that augmentations
+are applied is user-visible, so must be specified. Augmentations are ordered
+using the *after* relation and are applied from least to greatest in that order.
-Declarations that contribute to the same effective declaration, one
-introductory declaration and zero or more augmentation declarations with
-the same name and in the same context, are *totally ordered* by the *after*
-relation, with the introductory declaration being least, and the
-augmentation declarations greater than that.
+*For example:*
-*In particular, if all the augmentation declarations occur on the same path
-in the tree of Dart files that constitute the current library then they are
-ordered by their depth in the tree.*
+```dart
+enum E { a }
+augment enum E { b }
+augment enum E { c }
+```
-This applies both to top-level declarations and to member declarations of,
-for example, class declarations.
+*The resulting enum has values `a`, `b`, and `c`, in that order.*
### Augmenting class-like declarations
@@ -504,59 +557,50 @@ augment class SomeClass {
}
```
-This means that instead of creating a new declaration, the augmentation
-modifies a corresponding declaration in the library (which is 'before' this
-one).
-
A class, enum, extension type, mixin, or mixin class augmentation may
specify `extends`, `implements` and `with` clauses (when generally
supported). The types in these clauses are appended to the introductory
-declarations’ clauses of the same kind, and if that clause did not exist
-previously, then it is added with the new types. All regular rules apply
-after this appending process, so you cannot have multiple `extends` on a
-class, or an `on` clause on an enum, etc.
+declarations' clauses of the same kind, and if that clause did not exist
+previously, then it is added with the new types.
+
+*Example:*
+
+```dart
+class C with M1 implements I1 {}
+augment class C with M2 implements I2 {}
+
+// Is equivalent to:
+class C with M1, M2 implements I1, I2 {}
+```
Instance or static members defined in the body of the augmenting type,
including enum values, are added to the instance or static namespace of the
-corresponding type in the augmented library. In other words, the augmentation
-can add new members to an existing type.
+corresponding type in the introductory declaration. *In other words, the
+augmentation can add new members to an existing type.*
Instance and static members inside a class-like declaration may themselves
be augmentations. In that case, they augment the corresponding members in
-the same context (one introductory declaration and zero or more augmenting
-declarations, all occurring before the current augmenting type
-declaration), according to the rules in the following subsections.
+the same augmentation context, according to the rules in the following
+subsections.
-It's a **compile-time** error if a library contains two top-level declarations
-with the same name, and:
+It's a **compile-time** error if:
-* Neither is an augmenting declaration, or
-* one of the declarations is a class-like declarations and the other is
- not of the same kind, meaning that at either one is a class, mixin,
- enum, extension or extension type declaration, and the other is not the
- same kind of declaration.
+* A library contains two top-level declarations with the same name, and one of
+ the declarations is a class-like declaration and the other is not of the
+ same kind, meaning that either one is a class, mixin, enum, extension or
+ extension type declaration, and the other is not the same kind of
+ declaration.
-It is a **compile-time error** if:
+* An augmenting class declaration has an `extends` clause and any prior
+ declaration for the same class also has an `extends` clause.
* The augmenting declaration and augmented declaration do not have all the
same modifiers: `abstract`, `base`, `final`, `interface`, `sealed` and
`mixin` for `class` declarations, and `base` for `mixin` declarations.
- *This is not a technical requirement, but it ensures that looking at either
- declaration shows the complete capabilities of the declaration. It also
- deliberately prevents an augmentation from introducing a restriction that
- isn't visible to a reader of the main declaration.*
-
-* The augmenting declaration declares an `extends` clause for a `class`
- declaration, but one was already present _(or the `class` was a `mixin
- class` declaration, which does not allow `extends` clauses)_.
-
-* An augmenting extension declares an `on` clause *(this is a syntax
- error)*. We also do not allow adding further restrictions to a `mixin`
- declaration, so no further types can be added to its `on` clause, if it
- even has one. These restrictions could both be lifted later if we have a
- compelling use case, as there is no fundamental reason it cannot be
- allowed.
+ *This is not a technical requirement, but follows our design principle that
+ what is known from reading the introductory declaration will still be true
+ after augmentation.*
* The type parameters of the augmenting declaration do not match the
augmented declarations's type parameters. This means there must be
@@ -574,45 +618,37 @@ It is a **compile-time error** if:
### Augmenting functions
-A top-level function, static method, instance method, or operator may be
-augmented to replace or wrap the augmented body in additional code:
+A top-level function, static method, instance method, operator, getter, or
+setter may be augmented to provide a body or add metadata:
```dart
-// Wrap the augmented function in profiling:
-augment int slowCalculation(int a, int b) {
- var watch = Stopwatch()..start();
- var result = augmented(a, b);
- print(watch.elapsedMilliseconds);
- return result;
+class Person {
+ final String name;
+ final int age;
+
+ Map toJson();
}
-```
-The augmentation replaces the augmented function’s body with the augmenting
-function’s body.
+// Provide a body for `toJson()`:
+augment class Person {
+ augment toJson() => {'name': name, 'age': age};
+}
+```
-Inside the augmenting function’s body, a special `augmented(…)` expression
-may be used to execute the augmented function body. That expression takes
-an argument list matching the augmented function's parameter list, and it
-has the same return type as the enclosing function.
+It's a **compile-time** error if:
-The augmenting function does not have to pass the same arguments to
-`augmented(…)` as were passed to it. It may invoke `augmented` once, more
-than once, or not at all.
+* The function signature of the augmenting function does not exactly match the
+ function signature of the augmented function. This means that:
-An augmenting function declaration may have an empty body (`;`) in order to
-only augment the metadata or doc comments of the function. In this case the
-body of the augmented member is not altered.
+ * Any provided return types must be the same type.
-It is a compile-time error if:
+ * There must be same number or required and optional positional
+ parameters, all with the same types (when provided), the same number of
+ named parameters, each pairwise with the same name, same type (when
+ provided) and same `required` and `covariant` modifiers.
-* The function signature of the augmenting function does not exactly match the
- function signature of the augmented function. This means that any provided
- return types must be the same type; there must be same number or required
- and optional positional parameters, all with the same types (when provided),
- the same number of named parameters, each pairwise with the same name, same
- type (when provided) and same `required` and `covariant` modifiers, and any
- type parameters and their bounds (when provided) must be the same (like for
- type declarations).
+ * Any type parameters and their bounds (when provided) must be the same
+ (like for type declarations).
*Since repeating the signature is, by definition, redundant, this doesn't
accomplish anything semantically. But it ensures that anyone reading the
@@ -622,395 +658,127 @@ It is a compile-time error if:
* The augmenting function specifies any default values. *Default values are
defined solely by the introductory function.*
-* An augmenting declaration uses `augmented` when the augmented declaration
- has no concrete implementation. Note that all external declarations are
- assumed to have an implementation provided by another external source, and
- they will throw a runtime exception when called if not.
-
-### Augmenting variables, getters, and setters
-
-While the language treats variables, getters, and setters as
-[mostly interchangeable][uniform], within augmentations we do not allow
-augmenting getters and setters with variables. Since augmentations are tightly
-coupled to the libraries they augment, this restriction has minimal impact, and
-it does not greatly affect the ability of a library to change a field to a
-getter/setter pair or vice-versa.
-
-[uniform]: https://en.wikipedia.org/wiki/Uniform_access_principle
-
-You can think of variable, getter, and setter declarations all as ways to define
-a higher-level "property" construct. A property has a name and a type. It may
-have one or more other capabilities:
-
-* **A backing storage location.** You get this when you declare a variable
- which is not `abstract` and not `external`.
- Having a storage location enables (and often requires) having the
- variable initialized by generative constructors. A variable may also
- have an **initializing expression** that gets run either lazily for
- top-level and static variables, or at object construction/initialization
- time for instance variables.
-
-* **A getter function.** This function’s body is provided explicitly when
- you declare a getter. A variable declaration provides an implicit
- getter body that returns the value in the backing storage
- location. (Late variables do some additional checking in that implicit
- body.)
-
-* **A setter function.** A setter declaration provides a body explicitly. A
- non-final variable declaration provides an implicit setter body that stores
- the given value in the storage location. (Again, late variables do some
- additional updates and/or checks.)
-
-Variable declarations may be marked `abstract` or `external` and, if so,
-those are mapped over to the corresponding getter and setter functions.
-
-An `abstract` variable declaration is equivalent to an abstract getter
-declaration, and if not `final`, also an abstract setter declaration. An
-`external` variable defines an `external` getter and, if not `final`, an
-`external` setter. Unlike abstract declarations, they are considered to
-have a concrete implementation.
-
-Variables which require an initializer expression (such as those which have a
-non-nullable type and are not marked `late`) need not initially be defined
-with one, as long as there exists some augmentation which supplies it.
-
-Augmentations on variables, getters, and setters works mostly at the level of
-these separate capabilities. For example, augmenting a variable with a getter
-replaces the augmented variable's implicit getter body with the augmenting
-getter's.
-
-More specifically:
-
-* **Augmenting with a getter:** An augmenting getter can augment a getter
- declaration, or the implicit getter of a variable declaration, with all
- prior augmentations applied, by replacing the body of the augmented getter
- with the body of the augmenting getter. Inside the augmenting getter’s
- body, an `augmented` expression executes the augmented getter’s body.
-
- An augmenting getter declaration may have an empty body (`;`) in order to
- only augment the metadata or doc comments of the getter. In this case the
- body of the augmented getter is not altered.
-
- Synthetic getters cannot be augmented with metadata or doc comments.
-
-* **Augmenting with a setter:** An augmenting setter can augment a setter
- declaration, or the implicit setter of a variable declaration, with all
- prior augmentations applied, by replacing the augmented setter’s body with
- the augmenting setter’s body. Inside the augmenting setter’s body, an
- `augmented = ` assignment invokes the augmented setter with the
- value of the expression.
-
- An augmenting setter declaration may have an empty body (`;`) in order to
- only augment the metadata or doc comments of the setter. In this case the
- body of the augmented setter is not altered.
-
- Synthetic setters cannot be augmented with metadata or doc comments.
-
-* **Augmenting a getter and/or setter with a variable:** This is a
- compile-time error in all cases. *Augmenting an abstract or external
- variable with a variable is also a compile-time error, as those are
- actually just syntax sugar for a getter and possibly a setter. An
- augmenting variable replaces its augmented variable’s initializer
- expression, and that can only be done on a declaration that can have an
- initializer expression.*
-
- We may decide in the future to allow augmenting abstract getters, setters,
- or variables with variables, but for now you can instead use the following
- workaround:
-
- - Add a new field.
- - Augment the getter and/or setter to delegate to that field.
-
- If a non-abstract, non-external variable is augmented by an augmenting
- getter or setter, you **can** still augment the variable, as you are only
- augmenting the initializer, metadata, or doc comments of the augmented
- variable. This is not considered to be augmenting the augmenting getter or
- setter, since those are not actually altered.
-
- The reason for this compile time error is that whether a member declaration
- is a field versus a getter/setter is a visible property of the declaration
- inside the same class or even library:
-
- * It determines whether the member can be initialized in a constructor
- initializer list.
- * It is also a visible distinction when introspecting on a program with
- the analyzer, macros, or mirrors.
-
- When a declaration is augmented, we don't want the augmentation to be able
- to change any of the known properties of the existing member being
- augmented. For example, we don't allow you to augment a method with a getter
- that returns a function. Augmenting a getter/setter pair with a field would
- change the "can be used in a constructor initializer" property, so we forbid
- it. Augmenting a field with a getter/setter doesn't change that property so
- it is allowed.
-
-* **Augmenting a variable with a variable:** Augmenting a variable with a
- variable only alters its initializer, metadata, or doc comments. *As
- usual, external and abstract variables cannot augment their
- initializing expression, since it does not exist.*
-
- Augmenting initializer expressions replace the augmented initializer (or
- provide one where none existed previously). The augmenting initializer
- may use an `augmented` expression which executes the augmented initializer
- expression (if present) when evaluated. If no initializer is provided then
- the augmented initializer is not altered.
-
- The `late` property of a variable must always be consistent between the
- augmented variable and its augmenting variables.
-
- If the introductory variable declaration does not have a type
- annotation, then the variable's declared type is found using only that
- declaration, without looking at any further augmenting declarations.
- The type can either be inferred from an initializer expression of the
- introductory variable declaration, be inherited from a superinterface
- for an instance variable, or default to a type of `dynamic` if neither
- applies. *This ensures that augmenting a variable doesn't change its
- type. That is necessary to ensure that macros cannot change the
- signature of a declaration, a signature which may have been depended on
- by other code, or other macros.*
-
-It is a **compile-time error** if:
-
-* The introductory and augmenting declarations do not have the same
- declared types (return type for getters, parameter type for setters,
- declared type for variables). This only applies where types are not
- omitted in the augmenting declaration.
-
-* An augmenting declaration uses `augmented` when the augmented declaration
- has no concrete implementation. Note that all external declarations are
- assumed to have an implementation provided by another external source, and
- otherwise they will throw a runtime error when called.
-
-* An augmenting variable’s initializing expression uses `augmented`, and
- the stack of augmented declarations do not include a variable with an
- explicit initializing expression. For nullable fields, the implicit null
- initialization only happens if there is no explicit initializer after the
- entire stack of augmentations has been applied.
-
-* A non-writable variable declaration is augmented with a setter. (Instead,
- the author can declare a *non-augmenting* setter that goes alongside the
- implicit getter defined by the final variable.) _A non-writable variable
- declaration is any that does not introduce a setter, including non-`late`
- `final` variables, `late final` variables with an initializer, and `const`
- variables._
-
-* A non-final variable is augmented with a final variable. We don't want to
- leave the augmented setter in a weird state.
- * A final variable can be augmented with a non-`final` augmenting
- variable, and that will not add any setter. An augmenting variable
- declaration only affects the initializer expression, not setters.
-
-* A variable is augmented with another variable, and one is `late` and
- the other is not. *(Augmentation cannot change `late`-ness, and since being
- `late` does affect the initializer expression, the augmenting variable is
- required to repeat the `late`.)*
-
-* A getter or setter declaration is augmented by an augmenting variable.
-
-* A late final variable with no initializer expression is augmented by an
- augmenting variable with an initializer expression.
- _A late final variable with no initializer has a setter, while one with an
- initializer does not. An augmentation must not change whether there is a
- setter._
-
-* A `const` variable is augmented by an augmenting getter. **(TODO: Can a
- const variable be augmented by another const variable, changing its value,
- or is that too weird?)**
-
-* An `abstract` variable is augmented with a non-abstract variable.
-
-* An `external` variable is augmented with an `abstract` variable.
-
-### Augmenting enum members
-
-Some enum members can not be augmented: It is a compile-time error if an
-augmenting declaration in an enum declaration (introductory or augmenting)
-has the name `values`, `index`, `hashCode`, or `==`.
-
-*It has always been an error for an enum declaration to declare a member
-named `index`, `hashCode`, `==`, or `values`, and this rule just clarifies
-that this error is applicable for augmenting declarations as well.*
-
-Enum values can _only_ be augmented by enum values, and the implicit getter
-introduced by them is not augmentable. The only thing you are allowed to do
-when augmenting an enum value is add metadata annotations or doc comments.
+* A function is not complete after all augmentations are applied, unless it
+ is in a context where it can be abstract. *Every function declaration
+ eventually needs to have a body filled in unless it's an instance method
+ that can be abstract. In that case, if no declaration provides a body, it
+ is considered abstract.*
-When augmenting an enum value, no constructor invocation should be provided.
-The original value is always used, and the explicit constructor invocation (if
-present) should not be copied.
+### Augmenting variables
-New enum values may be defined in an augmenting enum, and they will be appended
-to the current values of the declaration in augmentation application order.
+A class-like augmentation can add *new* variables (that is instance or static
+fields) to the type being augmented:
-Augmenting an existing enum value never changes the order in which it appears in
-`values`.
+```dart
+class C {}
-For example:
+augment class C {
+ int x = 3;
+ static int y = 4;
+}
```
-// main.dart
-part 'a.dart';
-part 'c.dart';
-enum A {
- first,
- second.custom(1);
+Variable declarations themselves can't be augmented (by variables, getters, or
+setters, even if the getter or setter is incomplete), with the exception of
+abstract instance variable declarations.
- final int b;
+*A variable declaration implicitly has code for the synthesized getter and
+setter that access and modify the underlying backing storage. Since we don't
+allow augmentations to replace code, that implies that augmentations can't
+change variables. So we don't allow them to be augmented.*
- const A() : b = 0;
+#### Abstract instance variables
- const A.custom(this.b);
-}
-}
+Dart supports `abstract` field declarations. They are syntactic sugar for
+declaring an abstract getter and, if not `final`, an abstract setter. They don't
+actually declare a variable with any backing storage.
-// a.dart
-part of 'main.dart';
-part 'b.dart';
-
-augment enum A {
- third;
+Because abstract variables are effectively abstract getter and setter
+declarations, they can be augmented and used in augmentations just like function
+declarations:
- /// Some doc comment
- augment first; // This is still `first` in values.
-
- @someAnnotation
- augment second; // Don't repeat the argument list, original is used.
-}
+*Examples:*
-// b.dart
-part of 'a.dart';
-
-augment enum A {
- fourth;
-}
+```dart
+class C {
+ // Augment an abstract variable with a getter:
+ abstract final int a;
+ augment int get a => 1; // OK.
-// c.dart
-part of 'main.dart';
+ // Augment an abstract variable with a getter and setter:
+ abstract int b;
+ augment int get b => 1; // OK.
+ augment set b(int value) {} // OK.
-augment enum A {
- fifth;
+ // Augment a getter with an abstract variable:
+ int get c;
- // Error, enum value augmentations cannot have an explicit constructor
- // invocation.
- augment third.custom(3);
+ @someMetadata
+ augment abstract final int c; // (Not very useful, but valid.)
}
```
-Then `A.values` is `[A.first, A.second, A.third, A.fourth, A.fifth]`.
+### Augmenting enum members
-It is a compile-time error if:
+An augmentation of an enum type can add new members to the enum, including new
+enum values. Enum values are appended in augmentation application order.
-* An augmenting getter is defined for an enum value. _An enum value
- counts as a constant variable._
-* An enum value augmentation provides an explicit constructor invocation.
+Enum values themselves can't be augmented since they are essentially constant
+variables and variables can't be augmented.
-### Augmenting constructors
+It's a **compile-time error** if:
-Constructors are (as always) more complex. We have many kinds of constructors,
-and what it means to augment each is different. For the purposes of this section
-we will call out three specific kinds of constructors:
+* A declaration inside an augmenting enum declaration has the name `values`,
+ `index`, `hashCode`, or `==`. *It has always been an error for an enum
+ declaration to declare a member named `index`, `hashCode`, `==`, or
+ `values`, and this rule just clarifies that this error is applicable for
+ augmenting declarations as well.*
-**Non-redirecting generative constructors**: These always produce a new
-instance, and have an optional initializer list and optional body.
-**Non-redirecting factory constructors**: These are much like static methods,
-and might return a subtype of the current type. They also may not create a new
-instance, but return an existing one. They must have a body.
-**Redirecting constructors**: Both generative and factory constructors can be
-redirecting, although the syntax looks slightly different for each.
+### Augmenting constructors
-It may not always be apparent whether a constructor is redirecting or not based
-on a given declaration (if there is no body, initializer list, or redirecting
-constructor invocation). These constructors are considered to be "potentially
-redirecting" or "potentially non-redirecting". An augmentation may alter this
-property by augmenting a constructor in a way that makes it concretely
-redirecting or not.
+Augmenting constructors works similar to augmenting a function, with some extra
+rules to handle features unique to constructors like redirections and
+initializer lists.
-It is a compile-time error if:
+It's a **compile-time error** if:
* The signature of the constructor augmentation does not match the original
constructor. It must have the same number of positional parameters, the same
- named parameters, and matching parameters must have the same type,
- optionality, and any `required` modifiers must match. Any initializing
- formals and super parameters must also be the same in both constructors.
+ named parameters, and matching parameters must have the same type (if
+ provided), optionality, and any `required` modifiers must match. Any
+ initializing formals and super parameters must also be the same in both
+ constructors.
* The augmenting constructor parameters specify any default values.
*Default values are defined solely by the introductory constructor.*
* The introductory constructor is `const` and the augmenting constructor
- is not or vice versa.
+ is not or vice versa. *An augmentation can't change whether or not a
+ constructor is const because that affects whether users are allowed to use
+ the constructor in a const context.*
* The introductory constructor is marked `factory` and the augmenting
- constructor is not, or vice versa.
+ constructor is not, or vice versa. *An augmentation can't change whether or
+ not a constructor is generative because that affects whether users are
+ allowed to call the constructor in a subclass's initializer list.*
-* The introductory constructor has a super initializer _(super
- constructor invocation at the end of the initializer list)_ and the
- augmenting constructor does too. _An augmentation can replace the
- implicit default `super()` with a concrete super-invocation, but cannot
- replace a declared super constructor._ **(TODO: Why not? We allow
- "replacing implementation", and this is *something* like that.)**
+An incomplete constructor can be completed by adding an initializer list and/or
+a body, or by adding a redirection:
-* The resulting constructor is not valid *(it has a redirection as well as
- some initializer list elements, or it has multiple `super` initializers,
- etc)*.
-
-* A non-redirecting constructor augments a constructor which is not
- potentially non-redirecting.
-
-* A redirecting constructor augments a constructor which is not potentially
- redirecting.
-
-#### Non-redirecting generative constructors
-
-These are probably the most complex constructors, but also the most common.
-
-At a high level, a non-redirecting generative constructor marked `augment` may:
-
-* Augment the constructor with an _additional_ constructor body (bodies are
- invoked in augmentation order, starting at the introductory declaration).
-
-* Add initializers (and/or asserts) to the initializer list, as well as a
- `super` call at the end of the initializer list.
-
-#### Non-redirecting factory constructors
-
-A non-redirecting factory constructor marked `augment` works in the same way as
-a normal function augmentation.
-
-If it has a body, it replaces the body of the augmented constructor (if
-present), and it may invoke the augmented body by calling
-`augmented(arguments)`.
-
-#### Redirecting generative constructors
-
-A redirecting generative constructor marked `augment` adds its redirection
-to the augmented constructor.
-
-This converts it into a redirecting generative constructor, removing the
-potentially non-redirecting property of the constructor.
-
-It is a compile-time error if:
-
-* The augmented constructor has any initializers.
-* The augmented constructor has a body.
-* The augmented constructor has a redirection.
-
-This redirecting generative constructor now behaves exactly like any other
-redirecting generative constructor when it is invoked.
-
-#### Redirecting factory constructors
-
-A redirecting factory constructor marked `augment` adds its factory redirection
-*(e.g., `= C.name`)* to the augmented constructor.
-
-The result of applying the augmenting constructor is a redirecting factory
-constructor with the same target constructor designation as the augmenting
-constructor. This removes the potentially non-redirecting property of the
-constructor.
+```dart
+class C {
+ C.generative();
+ factory C.fact();
-It is a compile-time error if:
+ C.other();
+}
-* The augmented factory constructor has a body, or it is redirecting.
+augment class C {
+ augment C.generative() : this.other();
+ factory augment C.fact() = C.other;
+}
+```
### Augmenting extension types
@@ -1040,136 +808,40 @@ extension type A(int b) {
other types in which case the extension type syntax will then be understood by
users to be a primary constructor for the extension type.*
-The extension type's representation object is _not_ a variable, even though it
-looks and behaves much like one, and it cannot be augmented as such. It is a
-compile time error to have any augmenting declaration with the same name as the
-representation object.
+[primary constructors]:
+https://github.com/dart-lang/language/blob/main/working/2364%20-%20primary%20constructors/feature-specification.md
+
+The extension type's representation object is *not* a variable, even though it
+looks and behaves much like one, and it cannot be augmented as such.
-It is a compile time error if:
+It's a **compile-time error** if:
+
+* An augmenting declaration has the same name as the representation variable.
* An extension type augmentation contains a representation field clause.
-[primary constructors]:
-https://github.com/dart-lang/language/blob/main/working/2364%20-%20primary%20constructors/feature-specification.md
+### Augmenting with metadata annotations
+
+An augmenting declaration can have metadata attached to it. The language doesn't
+specify how metadata is used. Tools may choose to append metadata from
+augmentations to the resulting combined declaration or allow inspecting the
+metadata on the individual augmentations.
-### Augmenting external members
-
-When augmenting an `external` member, it is assumed that a real implementation
-of that member has already been filled by some tool prior to any augmentations
-being applied. Thus, it is allowed to use `augmented` from augmenting members
-on external declarations, but it may throw a `NoSuchMethodError` error at
-runtime if no implementation was in fact provided.
-
-**NOTE**: Macros should _not_ be able to statically tell if an external body has
-been filled in by a compiler, because it could lead to a different result on
-different platforms or tools.
-
-**TODO: Should we add a syntax to let the augmentation dynamically detect
-whether there is an external implementation to call?**
-
-### Augmenting with metadata annotations and doc comments
-
-All declarations can be augmented with metadata annotations and/or doc comments
-directly preceding an augmenting declaration.
-
-In both cases, these should be appended to existing metadata or doc comments. For
-metadata annotations, these may trigger additional macro applications.
-
-## Augmented expression
-
-The exact result of an `augmented` expression depends on what is being
-augmented, but it generally follows the same rules as any normal identifier:
-
-* **Augmenting getters**: Within an augmenting getter `augmented` invokes
- the augmented getter and evaluates to its return value. If augmenting a
- variable with a getter, this will invoke the implicitly induced getter
- from the augmented variable declaration.
-
-* **Augmenting setters**: Within an augmenting setter `augmented` must be
- followed by an `=` and will directly invoke the augmented setter. If
- augmenting a variable with a setter, this will invoke the implicitly
- induced setter from the augmented variable declaration.
-
-* **Augmenting fields**: Within an augmenting variable declaration,
- `augmented` can only be used in an initializer expression, and refers
- to the augmented variable's initializing expression, which is
- immediately evaluated.
-
- It is a compile-time error to use `augmented` in an augmenting
- variable's initializer if the member being augmented is not a variable
- declaration with an initializing expression.
-
-* **Augmenting functions**: Inside an augmenting function body (including
- factory constructors but not generative constructors) `augmented` refers to
- the augmented function. Tear-offs are not allowed, and this function must
- immediately be invoked.
-
-* **Augmenting non-redirecting generative constructors**: Unlike other
- functions, `augmented` has no special meaning in non-redirecting generative
- constructors. It is still a reserved word inside the body of these
- constructors, since they are within the scope of an augmenting declaration.
-
- There is instead an implicit order in which these augmented constructors are
- invoked, and they all receive the same arguments. See
- [this section](#non-redirecting-generative-constructors) for more
- information.
-
-* **Augmenting operators**: When augmenting an operator, `augmented`
- refers to the augmented operator method, which must be immediately
- invoked using function call syntax. For example, when augmenting
- `operator +` you could use `augmented(1)` to call the augmented
- operator, and when augmenting `operator []=` you would use the
- `augmented(key, value)` syntax.
-
- * Note that `augmented` in such an augmenting operator method body is
- not an expression by itself, and cannot be used to tear off the
- augmented operator method. Similar to `super`, it is a syntactic
- form which can only be used in limited ways.
-
-* **Augmenting enum values**: When augmenting an enum value, `augmented`
- has no meaning and is not allowed.
-
-In all relevant cases, if the augmented member is an instance member, it is
-invoked with the same value for `this`.
-
-Assume that the identifier `augmented` occurs in a source location where no
-enclosing declaration is augmenting. In this case, the identifier is taken
-to be a reference to a declaration which is in scope.
-
-*In other words, `augmented` is just a normal identifier when it occurs
-anywhere other than inside an augmenting declaration.*
-
-*Note that, for example, `augmented()` is an invocation of the augmented
-function or method when it occurs in an augmenting function or method
-declaration. (In the latter case, the augmenting method declaration must
-occur inside an augmenting type-introducing declaration, e.g., an
-augmenting class or mixin declaration). This is also true if `augmented()`
-occurs inside a local function declaration inside the body of that function
-or method declaration. We could say that `augmented` is a contextual
-reserved word because it is unable to refer to a declaration in scope when
-it occurs inside an augmenting declaration, it always has the special
-meaning which is associated with augmentations.*
-
-A compile-time error occurs if a declaration with the basename `augmented`
-occurs in a location where any enclosing declaration is augmenting. *This
-error is applicable to all such declarations, e.g., local functions, local
-variables, parameters, and type parameters.*
-
-Consider a non-augmenting member declaration _Dm_ that occurs inside an
-augmenting type declaration _Dt_. A compile-time error occurs if the
-identifier `augmented` occurs in _Dm_.
-
-*For example, inside `augment class C` we could have a declaration like
-`void f() {...augmented()...}`. This is an error because the outer
-`augment` forces the meaning of `augmented` to be about augmentation in the
-entire scope, but the method declaration is introductory and hence there is
-no earlier declaration to augment.*
+*In practice, most code generators use the [analyzer package][] to introspect
+over code. Code generators introspecting on the _syntax_ of some code likely
+want to see the metadata for each syntactic declaration separately. Code
+generators introspecting over the resolved semantic model of the code (which is
+more common) probably want to see the metadata of the introductory declaration
+and all augmentations appended into a single list of metadata accessible from
+the combined declaration.*
+
+[analyzer package]: https://pub.dev/packages/analyzer
## Dynamic semantics
The application of augmentation declarations to an augmented declaration
produces something that looks and behaves like a single declaration: It has
-a single name, a single type or function signature, and it’s what all
+a single name, a single type or function signature, and it's what all
references to the *name* refers to inside and outside of the library.
Unlike before, that single *semantic declaration* now consists of multiple
@@ -1219,8 +891,8 @@ augmenting declarations and an introductory declaration as follows:
#### Members
-A class declaration stack, *C*, of a one non-augmenting and zero or more
-augmenting class declarations, defines an *augmented interface* (member
+A class declaration stack, *C*, of an introductory declaration and zero or more
+augmenting declarations, defines an *augmented interface* (member
signatures) and *augmented implementation* (instance members declarations)
based on the individual syntactic declarations.
@@ -1251,7 +923,7 @@ abstract method* as:
* Let *Ctop* be the latest element of the stack and
*Crest* the rest of the stack.
* If *Ctop* is a non-variable declaration, and is not declared
- `abstract`, the *C* doe
+ abstract, the *C* doe
* If *Ctop* declares a function body, then *C* does not define an
abstract method.
* Otherwise *C* defines an abstract method if *Crest* defines an
@@ -1286,13 +958,13 @@ For example, we define the *augmented parameter list* of a non-empty stack,
_This will usually be exactly the parameter list of the introductory
declaration, but the ordering of named parameters may differ. This is mostly
intended as an example, in practice the augmented parameter list can just be
-the parameter list of the introductory declaration, but it’s more
+the parameter list of the introductory declaration, but it's more
direct and clearly correct to use the actual parameter list of the declaration
when creating the parameter scope that its body will run in._
Similarly we define the _augmented function type_ of the declaration stack.
Because of the restrictions we place on augmentations, they will all have the
-same function type as the introductory declaration, but again it’s
+same function type as the introductory declaration, but again it's
simpler to assign a function type to every declaration.
#### Invocation
@@ -1326,16 +998,6 @@ follows:
except that type parameters of the class are bound to the runtime type
arguments of those parameters for the instance *o*).
* Execute the body *B* in this parameter scope, with `this` bound to *o*.
- * If *B* contains an expression of the form `augmented(args)`
- (type arguments omitted if empty), then:
- * The static type of `augmented` is the augmented function type of
- *Crest*. The expression is type-inferred as a function
- value invocation of a function with that static type.
- * To evaluate the expression, evaluate `args` to an argument list
- *A2*, invoke *Crest* with argument list *A2* and type
- arguments that are the types of `TypeArgs`. The result of
- `augmented(args)` is the same as the result of that
- invocation (returned value or thrown error).
* _There would have been a compile-time error if there is no earlier
declaration with a body._
* The result of invoking *C* is the returned or thrown result of
@@ -1372,10 +1034,6 @@ A possible name could be `augmentation_ordering`.
Its effect would be to **report a warning** *if* for any two (top-level)
augmenting declarations with name *n*, one is not *above* the other.
-The lint would only apply to user-written augmenting declarations, it should
-not include macro generated augmentations. Those are placed where the macro
-processor chooses to place them, usually after all other augmentations.
-
If the lint is satisfied, then all augmenting declarations are ordered by
the *before* relation, which means that no two of them can be in different
sibling parts of the same file, and therefore all the augmenting
@@ -1383,18 +1041,29 @@ declarations occur along a single path down the part-file tree. _This
ensures that *part file directive ordering* has no effect on augmentation
application order._
-The language specification doesn’t specify lints or warnings, so this lint
+The language specification doesn't specify lints or warnings, so this lint
suggestion is not normative. We wish to have the lint, and preferably
include it in the "recommended" lint set, because it can help users avoid
accidental problems. We want it as a lint instead of a language restriction
-so that it doesn’t interfere with macro-generated code, and so that users
-can `// ignore:` it if they know what they’re doing.
+so that it doesn't interfere with macro-generated code, and so that users
+can `// ignore:` it if they know what they're doing.
## Changelog
### 1.35
* Reorganize sections.
+* Remove references to macros.
+* Don't allow augmentations to wrap or replace code. Remove support for
+ `augmented` expressions. Disallow an augmentation from providing a body to
+ a declaration that already has one.
+* Remove support for augmenting variables.
+* Simplify constructor augmentations: no concatenating initializers or merging
+ initializers from one augmentation and a body from another.
+* Remove support for augmenting typedefs.
+* Remove support for augmenting redirecting constructors.
+* Allow a function augmentation to have an `external` body.
+* Rewrite "Scoping" section to be clearer.
### 1.34
@@ -1467,7 +1136,7 @@ can `// ignore:` it if they know what they’re doing.
### 1.22
* Unify augmentation libraries and parts.
- [Parts with imports specification][parts_with_imports.md] moved into
+ [Parts with imports][parts with imports] moved into
separate document, as a stand-alone feature that is not linked to
augmentations.
* Augmentation declarations can occur in any file, whether a library or
@@ -1484,7 +1153,7 @@ can `// ignore:` it if they know what they’re doing.
augmenting or non-augmenting declarations with the same name, and making
them all available in each declaration.
* Avoid defining a syntactic merging, since it requires very careful scope
- management, which isn’t necessary if we can just extend properties that are
+ management, which isn't necessary if we can just extend properties that are
currently defined for single declarations to the combination of a
declaration plus zero or more augmentations.