Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Address extension accessibility and specificity #4058

Closed
wants to merge 2 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 122 additions & 2 deletions working/augmentation-libraries/parts_with_imports.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Part files with imports

Authors: [email protected], [email protected], [email protected] <br>
Version: 1.0 (See [Changelog](#Changelog) at end)
Version: 1.1 (See [Changelog](#Changelog) at end)

This is a stand-along definition of _improved part files_, where the title of
this document/feature is highlighting only the most prominent part of the
Expand Down Expand Up @@ -296,6 +296,118 @@ library, so the library author should fix the conflict by renaming the prefix.
That such a name conflict is a compile-time error, makes it much easier to
detect if it happens._

### Extension accessibility and specificity

An `extension` declaration is specified to be *accessible* to the code of a
library if the extension declaration is imported by an `import` declaration
of that library that doesn't hide the declaration.
It's "in the import scope" somewhere, even if the extension cannot be
directly referred to by name, for example due to naming conflicts or shadowing.

With the enhanced parts with imports feature, an extension imported by a `part`
file is not in the import scope of its parent or sibling files.
The definition of an extension being accessible is changed from the phrasing
of the [extension feature specification on accessibility][] to:

> An extension is accessible (with priority *n*) in a Dart file if either:
> * The extension is declared by the library of the Dart file, in which
> case it's accessible with priority zero.
> * The extension is accessible by import with priority *n* in the Dart file.
>
> An extension is accessible by import with priority *n* in a Dart file iff:
> * The Dart file *imports* the extension, meaning that the Dart file
> contains a non-deferred import directive whose imported
> library has the extension in its export scope, and where the name of
> the extension is not private, and is not hidden by either a `hide`
> combinator mentioning the extension name or by a `show` combinator
> not mentioning the name.
> In that case the extension is accessible with priority 1.
> _This includes non-deferred imports with a prefix, and even an unnamed
> prefix using the the "wildcard variable" unnamed variable notation
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: "the the" -> "the".

> `as _`._
> * Or otherwise, if the Dart file is a part file and the extension is
> accessible by import in the part file's parent file with priority *n*.
> In that case the extension is accessible by import to the current file
> with priority *n + 1*. _(Lower is better.)_

The "priority *n*" of an extension for a particular Dart file is a measure
of how far up in the parent chain the import for that extension occurred.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe: "parent" -> "part of"?

A value of "1" means the current Dart file, and each step up the parent
chain increases the value by one. A value of zero means the declaration
is in the current library, and that is the priority of the extension
in all files of the library, even if the same extension is *also* imported.

[extension feature specification on accessibility]: https://github.com/dart-lang/language/blob/main/accepted/2.7/static-extension-methods/feature-specification.md#accessibility "Accessibility"

The rules for *applicability* of extensions do not change.

The conflict resolution, used when multiple accessible extensions apply
to an invocation, are updated to give priority to an extension imported
by a part file over extensions made accssible by its parent file.
This maintains the guarantee that a part file can ignore
parent file imports, as long as it imports all its own dependencies:
every name it refers to itself, *and* every extension that it refers
to *implicitly*.

The [extension feature specification on specificity] is updated to
the following:

> Let *i* be a member invocation with target expression `e` and corresponding
> member name *m*, and let *E1* and *E2* denote different accessible and
> applicable extensions for *i*.
>
> Whether *E1* is more specific than *E2* wrt. *i* of *e* is determined by
> the following steps, stopping when an answer is given:
> * If *E1* is accessible with priority *n1* &gt; 0 and *E2* is accessible
> with priority *n2* &gt; 0 _(so both accessible by import)_ then
> * if *n1* < *n2* then *E1* is more specific than *E2*, and
> * if *n2* < *n1* then *E1* is *not* more specific than *E2*.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason this is phrased as "E1 is not more specific than E2" instead of saying "E2 is more specific than E1"?

It reads to me like you're trying to define a partial order, but I don't quite see why when n2 < n1 in this clause. I might be missing some context.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a partial order. And even more, this text is part of a partial order specification.
There can be distinct extensions where neither is more specific than the other. If they are in the set of applicable extensions, then lookup fails.

Saying that E2 is more specific than E1 would not answer whether E1 is more specific than E2 or not.
That would be assuming that the relation is anti-symmetric. (It probably is, but that's a consequence of the definition deliberately avoiding any mutual more-specific relations, and this rule is part of making that so, so it can't also assume it.)

The relation being defined is "E1 more specific than E2" which should give an answer of true or false.
The items are in priority order, and this item gives an answer of "yes" in one case and "no" in another, and otherwise it falls through to the next item (hence "part of a partial order").
Answering any other question doesn't help. (But the rules do imply that if E1 is not more specific than E2 for this reason, then E2 is more specific than E1, since it's the same n values.)

> * If the *E2* extension is declared in a platform library and the *E1*
> extension is not, then *E1* is more specific than *E2*.
> * If the *E1* extension is declared in a platform library and the *E2*
> extension is not, then *E1* is *not* more specific than *E2*.
> * Let *T1* be the instantiated "on" type of *E1* wrt. `e`
> and *T2* be the instantiated "on" type of *E2* wrt. `e`.
> * If *T1* is a subtype of *T2*:
> * If *T2* is not also a subtype of *T1*, then *E1* is more specific
> than *E2*.
> * If *T2* is also a subtype of *T1*:
> * let *B1* be the instantiate-to-bounds `on` type of *E1*
> and *B2* be the instantiate-to-bounds `on` type of *E2*.
> * If *B1* is a subtype of *B2* and *B2* is not a subtype of *B1*,
> then *E1* is more specific than *E2*.
> * Otherwise *E1* is _not_ more specific than *E2*.

The only change to behavior (not just refactoring) is the first step,
which makes one imported extension more specific than another imported
extension if the former is imported "closer to" the place of use than
the latter.

The change does give priority to a platform extension imported by a
part file, over a non-platform extension imported by a parent file.
This should be non-breaking on introduction since there are no part
files with imports. It may make it breaking to add an extension to
a platform file that is imported in a part file, since it can now
shadow an extension for the same invocation imported by a parent
file.
It does, however, ensure that part files which import all their
dependencies, including platform library dependencies with extensions,
can safely ignore their parent file imports.

This change does not affect the priority of extensions declared
in the library itself. They are still on even footing with imported
extensions, allowing a library to declare *more or less specific*
extensions without automatically taking precedence over imported
extensions.
_(To give declarations of the current library the highest priority, remove
the "&gt; 0" from the priority rule. This would include locally declared
extensions in the comparison with highest priority. Doing so would be a
potentially breaking change, so it's deliberately omitted, even though
it could make sense. The library declaration scope is below the combined
import scope in the part file scope chain.)_

[extension feature specification on specificity]: https://github.com/dart-lang/language/blob/main/accepted/2.7/static-extension-methods/feature-specification.md#specificity "Specificity"

### Export directives

Any Dart file can contain an `export` directive. It makes no difference which
Expand Down Expand Up @@ -611,11 +723,19 @@ will already be compatible.

## Changelog

### 1.1

* Addresses extension accessibility and specificitiy.
* Makes extension accessibility follow the import scope that
extensions are imported into, so inherited by parts, but not
visible outside of the file doing the import.
* Makes closer imports in the import scope chain be more specific
than ones further up.

### 1.0

* Initial version. The corresponding version of [Augmentations], which refers
to part files with imports, is version 1.21.

* Combines augmentation libraries, libraries and part files into just
libraries and part files, where the part files can have import, export and
further part directives. Those part directives can use configurable imports.
Expand Down
Loading