Skip to content

Commit 9ed33c1

Browse files
committed
Address comments.
1 parent 5f58373 commit 9ed33c1

File tree

2 files changed

+165
-78
lines changed

2 files changed

+165
-78
lines changed

working/augmentation-libraries/feature-specification.md

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,7 @@ This proposal defines that format. The idea is that a Dart compiler executes
6767
macros and then produces one or more new part files that contain all
6868
of the changes that the macros made to the library where they are applied, as
6969
new declarations to be added or augmentations that modify existing
70-
declarations. The
71-
compiler then adds those part files to the existing libraries.
70+
declarations. The compiler then adds those part files to the existing libraries.
7271

7372
But improved part files and augmenting declarations are not *only* a
7473
serialization format for macros. They are first-class language features that
@@ -106,9 +105,9 @@ We define a partial ordering on syntactic declarations of a library,
106105
*is above*, such that a syntactic declaration *A* is *above* a syntactic
107106
declaration *B* if and only if:
108107

109-
* *A* and *B* occur in the same file, and the start of the *A* declaration is
108+
* *A* and *B* occur in the same file, and the start of the *A* declaration is
110109
syntactically before the start of the *B* declaration, in source order, or
111-
* A file included by the file containing *A* contains *B*.
110+
* A file included by the file containing *A* contains *B*.
112111

113112
We define a *total ordering relation* (transitive, anti-symmetric, irreflexive)
114113
on declarations of a library, *is before* (and its reverse, *is after*) such
@@ -205,21 +204,21 @@ and a setter name.)_
205204
For the following, we’ll say that one declaration of a library is *above*
206205
another declaration of the same library if and only if:
207206

208-
* The former declaration is in the same file as the latter declaration, and it
209-
is textually earlier in the file (“above” in the source code as normally
210-
presented), or
211-
* The former declaration is in a file that is a direct or transitive parent
212-
file of the file of the latter declaration (“above” in the file tree
213-
hierarchy).
207+
* The former declaration is in the same file as the latter declaration, and it
208+
is textually earlier in the file (“above” in the source code as normally
209+
presented), or
210+
* The former declaration is in a file that is a direct or transitive parent
211+
file of the file of the latter declaration (“above” in the file tree
212+
hierarchy).
214213

215214
We can similarly define *below* as the inverse of that relation. Both *before*
216215
and *after* define *strict partial orders* on declarations in a library.
217216

218217
It’s a **compile-time error** if a library contains an augmentation declaration
219-
and a corresponding non-augmentation base declaration, and the the base
218+
and a corresponding non-augmentation base declaration, and the base
220219
declaration is not *above* the augmentation declaration.
221220

222-
These requirements ensure that declarations that contribution to the same
221+
These requirements ensure that declarations that contribute to the same
223222
effective declaration, one base declaration and zero or more augmentation
224223
declarations, are *totally ordered* by the *above* relation, with the base
225224
declaration at the top, and the declarations all being in files on a single

working/augmentation-libraries/parts_with_imports.md

Lines changed: 154 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -87,41 +87,46 @@ part file a problem.
8787
Pre-feature part files inherit the entire import scope from the library file.
8888
Each declaration of the library file and each part file is included in the
8989
library’s declaration scope. It’s viable to think of part files as being
90-
textually included in the library file. There is a is even a rule against
90+
textually included in the library file. There is even a rule against
9191
declaring a `part` inclusion of the same file more than once, which matches
9292
perfectly with that way of thinking.
9393

9494
## Feature
9595

96-
This feature allows a part file to have `import`, `export` and `part`
97-
directives of its own, where `import` directives only affect the part file
98-
itself, and its transitive part files. A library is defined by the source code
99-
of its library file and *all* transitively included part files, which can be an
100-
arbitrarily deep *tree*. A part file inherits the imports and import prefixes
101-
of its parent file (the library or part file that included it) into its
102-
top-level scope, but can choose to ignore or shadow those using its own
103-
imports.
96+
This feature allows a part file to have `import`, `export` and `part` directives
97+
of its own, where `import` directives only affect the part file itself, and its
98+
transitive part files. A library is defined by the source code of its library
99+
file and *all* transitively included part files, which can be an arbitrarily
100+
deep *tree*. A part file inherits the imported declarations and import prefixes
101+
of all its transitive parent files (the library or part files that included it),
102+
but can choose to ignore or shadow those using its own imports.
104103

105104
The design goals and principles are:
106105

107106
* *Backwards compatible*: If a part file has no `import`, `export` or `part`
108107
directive, it works just like it always has.
109108
* Because of that, it’s always safe to move one or more declarations into
110-
a new part file. _(Although augmentation declarations modifies that
111-
slightly.)_
109+
a new part file.
112110
* Similarly it’s always possible and safe to combine a part file with no
113111
`import`s back into its parent file.
114-
* *Library member declarations are library-global*: All top-level
115-
declarations in the library file and all transitive part files are equal,
116-
and are all in scope in every file. They introduce declarations into the
117-
library’s declaration scope, which is the most significant scope in all
118-
files of the library. If there is any conflict, top-level declarations win!
112+
113+
_(Augmentations modify both of these properties slightly, because order of
114+
declarations also matter.)_
115+
116+
* *Library member declarations are library-global*: All top-level declarations
117+
in the library file and all transitive part files are equal, and are all in
118+
scope in every file. They introduce declarations into the library’s
119+
declaration scope, which is the most significant scope in all files of the
120+
library. If there is any conflict with imported names, top-level
121+
declarations win!
122+
119123
* *The unit of ownership is the library*. It’s quite possible for one part
120124
file to introduce a conflict with another part file. It always was, but
121125
there are new ways too. If that happens, the library owner, who most likely
122126
introduced the problem, is expected to fix it. There is no attempt to hide
123127
name conflicts between declarations in separate tree-branches of the
124128
library structure.
129+
125130
* *Import inheritance is a only suggestion*: Aka. other files’ imports cannot
126131
break your code (at least if you’re not depending on them). A part file is
127132
never restricted by the imports it inherits from its parent file. It can
@@ -132,6 +137,17 @@ The design goals and principles are:
132137
why a macro should document any non-fresh names it introduces, so a library
133138
using the macro can rename any declarations that would conflict._
134139

140+
* Because of that, it’s possible to convert an existing library into a
141+
part file of another library. Since a library is self-contained and
142+
imports all external names that it refers to, making it a part file will
143+
not cause any conflict due to inherited imports. _(Obviously still need
144+
to avoid conflicts with top-level declarations.)_
145+
* And similarly, if a part file *is* self-contained, it can be converted
146+
into a separate library and imported back into the original library, or
147+
it can be moved to another position in the part tree hierarchy. _(Again
148+
augmentations introduce complications, which is why it’s usually a good
149+
idea to keep all augmentations inside the same part sub-tree).
150+
135151
### Grammar
136152

137153
We extend the grammar of part files to allow `import`, `export` and `part` file
@@ -147,7 +163,7 @@ other two. We restrict the `part of` directive to only allow the string version.
147163
148164
-- Added "<importOrExport>* <partDirective>*"
149165
<partDeclaration> ::=
150-
<partHeader> <importOrExport>* <partDiretive>* (<metadata>
166+
<partHeader> <importOrExport>* <partDirective>* (<metadata>
151167
<topLevelDeclaration>)* <EOF>
152168
```
153169

@@ -398,54 +414,115 @@ In short:
398414

399415
## Language versioning and tooling
400416

417+
Dart language versioning is an extra-linguistic feature which allows the SDK
418+
tooling to compile programs containing libraries written for different versions
419+
of the language. As such, the language semantics (usually) do not refer to
420+
language versions at all.
421+
422+
Similarly the language has no notion of a “package”, but tooling does consider
423+
Dart files to belong to (at most) one package, and some of the files as
424+
“having a `package:` URI”. This information is written into a metadata file,
425+
`package_config.json` that is also used to resolve `package:` URIs, and to
426+
assign default language versions to files that belong to a package.
427+
428+
Because of that, the restrictions in this section are not *language* rules,
429+
instead they are restrictions enforced by the *tooling* in order to allow
430+
multi-language-version programs to be compiled.
431+
432+
### Pre-feature code interaction and migration
433+
401434
This feature is language versioned, so no existing code is affected at launch.
402435

436+
The only non-backwards compatible change is to disallow `part of dotted.name;`.
437+
That use has been discouraged by the
438+
[`use_string_in_part_of_directives`][string_part_of_lint] lint, which was
439+
introduced with Dart 2.19 in January 2023, and has been part of the official
440+
“core” Dart lints since June 2023. The “core” lints are enforced more strongly
441+
than the “recommended” lints, including counting against Pub score, so any
442+
published code has had incentive to satisfy the lint.
443+
444+
The lint has a quick-fix, so migration can be achieved by enabling the lint
445+
(directly, or by including the “recommended” or “core” lint sets, which is
446+
already itself recommended practice) and running `dart fix`, which will change
447+
any `part of dotted.name;` to the future-safer `part of 'parent_file.dart';`.
448+
449+
All in all, there is very little expected migration since all actively
450+
developed code, which is expected to use and follow recommended or core lints,
451+
will already be compatible.
452+
453+
[string_part_of_lint]: https://dart.dev/tools/linter-rules/use_string_in_part_of_directives "use_string_in_part_of_directives lint"
454+
455+
We will enforce a set of rules that weren’t as clearly defined before
456+
(see next section), and therefore maybe not strictly enforced, so there is a
457+
risk that some pathologically designed library may break one of those rules.
458+
Other than that, a pre-feature library can be used as post-feature library as
459+
long as it satisfies these very reasonable rules.
460+
403461
The feature has no effect at the library boundary level, meaning the export
404462
scope of a library, so pre-feature and post-feature libraries can safely
405-
coexist.
406-
407-
As with pre-feature libraries, all files in a library must have the same
408-
associated *language version*. If any file has a language-version override
409-
marker (a line like `// @dart=3.12` before any Dart code), then *every file* in
410-
the library *must* have a language override marker. _(And they must still have
411-
the same language version, so it must be the same marker.)_
412-
413-
Also, every file in a library must belong to the same *package*. The Dart
414-
language itself has no notion of packages, but the tooling uses a file’s
415-
package to derive its default language version. The Dart SDK will require that
416-
all files in a package belong to the same library, ensuring that they’ll always
417-
have the same language version. Also, if the library file is inside the `lib/`
418-
directory, so it has a `package:` URI as canonical URI, then so must all its
419-
sub-part files. We haven’t specified these requirements before,
420-
it has always been assumed, but technically it is possible to write programs
421-
where a part belongs to different package than their library. The Dart SDK’s
422-
multi-language-version support, which based on files belonging to packages,
423-
will not support libraries that are not entirely in a single package.
424-
425-
That is, the extra restrictions enforced by Dart tools are:
426-
427-
* If any file of a library has a `// @dart=` language version marker, then
428-
*all* files in that library must have a language version marker with
429-
*the same* language version.
430-
Or, phrased differently starting at the root:
431-
* If a library file has a language version marker,
432-
then it’s a compile-time error for any sub-part file
433-
to not have the same language version marker.
434-
* If a library file has no language version marker,
435-
then it’s a compile-time error for any sub-part file
436-
to have a language version marker.
437-
438-
* If a library file belongs to a package,
439-
then all its sub-part files must belong to the same package.
440-
* If a library file has a `package:` URI (is located inside the package-URI
441-
directory specified by the package configuration, usually
442-
`"packageUri": "lib/"` of a `package_config.json` file),
443-
then all its sub-part files must also be inside that package-URI directory.
444-
* _A library file outside of the `lib/` directory can technically have
445-
part files inside the `lib/` directory, specified using `package:`
446-
URIs, but those part files can only be included in a program by
447-
referencing the library file, which means the part files might as well
448-
be placed outside of the `lib/` directory._
463+
coexist. A library can start using the feature without any effect on client
464+
libraries. There is no need to worry about migration order.
465+
466+
### Explicit sanity rules
467+
468+
The following rules are rules enforced by tooling, not the language, since they
469+
rely on features that are not part of the language (files having a language
470+
version, a language version marker, or belonging to a package).
471+
472+
All pre-feature libraries should already be following these rules, which exist
473+
mainly ensure that different files of a library will *always* have the same
474+
language versions. _Some of these rules have not all been expressed explicitly
475+
before, because they are considered blindingly obvious. We’re making them
476+
explicit here, and will enforce the rules strictly for post-feature code, if we
477+
didn’t already._
478+
479+
* It’s a **compile-time error** if two Dart files of a library do not have the
480+
same language version._All Dart files in a library must have the same
481+
language version._ Can be expressed locally as:
482+
* It’s a compile-time error if the associated language version of a part
483+
file is not the same as the language version of its parent file.
484+
485+
* It’s a **compile-time error** if any file of a library has a
486+
language-version override marker (a line like `// @dart=3.12` before any
487+
Dart code), and any *other* file of the same library does not have a
488+
language-version override marker. _While it’s still possible for that
489+
library to currently have the same language version across all files, that
490+
won’t stay true if the default language version for the package changes._
491+
Can be expressed locally as:
492+
493+
* If a part file has a language version marker, then it’s a compile-time
494+
error if its parent files does not have a language version marker. _The
495+
version marker it has must be for the same version due to the previous
496+
rule._
497+
498+
* If a part file has no language version marker, then it’s a compile-time
499+
error if its parent file has a language version marker.
500+
501+
* It’s a **compile-time error** if two Dart files of a library do not belong
502+
to the same package. _Every file in a library must belong to the same
503+
package to ensure that they always have the same default language version.
504+
It’s also likely to break a lot of assumptions if they don’t._ Can be
505+
expressed locally as:
506+
507+
* It’s a compile-time error if a part file does not belong to the same
508+
package as its parent file.
509+
510+
The Dart SDK’s multi-language-version support, which based on files
511+
belonging to packages, will not support libraries that are not entirely in a
512+
single package.
513+
514+
* We *may* want to also make it a **compile-time** error if two Dart files of
515+
a library are not both inside the `lib/` directory or both outside of it.
516+
_Having a parent file inside `lib/` with a part file outside will not
517+
compile if the parent file is accessed using a `package:` URI. Having a
518+
parent file outside of `lib/` with a part file inside works, but the part
519+
file might as well be outside since the only way to use it is to go through
520+
the parent file._ The only reason to maybe not enforce this rule would be a
521+
file inside `lib/` that is *never* accessed using a`package:` URI, and which
522+
depends on files outside of `lib/` for something. If some frameworks do
523+
that, maybe a Flutter `main` file, then we should just keep giving warnings
524+
about the pattern. The `lib/` directory should be self-contained because all
525+
libraries in it can be accessed by other packages, and no other files can.
449526

450527
### User guidance tooling
451528

@@ -466,24 +543,35 @@ can be applied.
466543

467544
It may very well be that annotations affecting declarations (which is typically
468545
what annotations on library declarations do) have no benefit from being limited
469-
based on something as (so far) semantically arbitrary as source ordering.
546+
based on something as (so far) semantically arbitrary as source ordering. But on
547+
the other hand, users may choose to order source depending on properties that
548+
annotations apply to. _The analyzer may want to review annotations that apply to
549+
a library for whether they can reasonably apply to any sub-tree of parts. For
550+
example `@Deprecated(…)` could apply to every member in a sub-tree, allowing a
551+
library to keep its deprecated API, and its necessary imports, separate from the
552+
rest, so that it can all be removed as a single operation, and then marking all
553+
that API as deprecated with one annotation._
470554

471555
##### An `// ignore` applying to a sub-tree
472556

473557
The analyzer recognizes `// ignore: …` comments as applying to the same or next
474-
line. For ignoring multiple warnings, there is a `// ignore_for_file: …`
475-
comment which covers the entire file.
558+
line. For ignoring multiple warnings, there is a `// ignore_for_file: …` comment
559+
which covers the entire file. There is no `ignore_for_library` that would apply
560+
to the entire library, including parts.
476561

477562
It can be considered whether to have an `// ignore_for_all_files: …` (or a
478-
better name) which applies to an entire subtree, not just the current file.
563+
better name) which applies to an entire sub-tree, not just the current file, and
564+
not the entire library. It would apply to the entire library if applied to the
565+
library file.
479566

480567
It may very well be better to *not* that, and have each sub-part write its own
481568
`// ignore_for_file: ...`. That makes it very easy to see which ignores are in
482569
effect for a file.
483570

484571
##### Invalid part file structure correction
485572

486-
When analyzing an incomplete or invalid Dart program, any and all of the compile-time errors above may apply.
573+
When analyzing an incomplete or invalid Dart program, any and all of the
574+
compile-time errors above may apply.
487575

488576
It’s possible to have part files with parent-file cycles, part files with a
489577
parent URI which doesn’t denote any existing file, or files with a `part`

0 commit comments

Comments
 (0)