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

How do we handle deferred imports in enhanced parts? #4081

Open
eernstg opened this issue Sep 5, 2024 · 1 comment
Open

How do we handle deferred imports in enhanced parts? #4081

eernstg opened this issue Sep 5, 2024 · 1 comment
Labels
enhanced-parts question Further information is requested

Comments

@eernstg
Copy link
Member

eernstg commented Sep 5, 2024

The feature specification on parts with imports contains the following scope description:

Each Dart file (library file or part file) defines a combined import scope
which combines the combined import scope of its parent file with its own
imports and import prefixes. The combined import scope of a dart files is
defined as:

  • Let C be the combined import scope of the parent file, or an empty scope
    if the current file is a library file.
  • Let I be a scope containing all the imported declarations of all
    non-prefixed import directives of the current file. The parent scope of
    I is C.
    • The import scope are computed the same way as for a pre-feature
      library. The implicit import of dart:core only applies to the
      library file. As usual, it’s a compile-time error if any import‘s
      target URI does not resolve to a valid Dart library file.
    • Let’s introduce importsOf(S), where S is a set of import
      directives from a single Dart file, to refer to that computation, which
      introduces a scope containing the declarations introduced by all the
      import s (the declarations of the export scope of each imported
      library, minus those hidden by a show or hide operator, combined
      such that a name conflicts of different declarations is not an error,
      but the name is marked as conflicted in the scope, and then referencing
      it is an error.)
  • Let P be a prefix scope containing all the import prefixes declared by
    the current file. The parent scope of P is I.
    • The P scope contains an entry for each name where the current file
      has an import directive with that name as prefix, as name. (If an
      import is deferred, it’s a compile-time error if more than one
      import directive in the same file has that prefix name, as usual.
      It’s not an error if two import deferred prefixes have the same name
      if they occur in different files, other file’s imports are only
      suggestions.
      )
    • The P scope binds each such name to a prefix import scope,
      Pname, computed as importsOf(Sname)
      where Sname is the set of import directives with that
      prefix name.
    • If an import is deferred, its Pname is a deferred
      scope
      which has an extra loadLibrary member added, as usual, and the
      import has an implicit hide loadLibrary modifier.
    • If Pname is not deferred, and the parent scope in C
      has a non-deferred prefix import scope with the same name,
      Cname, then the parent scope of Pname is
      Cname. A part file can use the same prefix as a prefix
      that it inherits, because inherited imports are only suggestions. If it
      adds to that import scope, by importing into it, that can shadow
      existing declarations, just like in the top-level declaration scope. A
      deferred prefix import scope cannot be extended, and cannot extend
      another prefix scope, deferred prefix scopes are always linked to a
      single import directive.
    • It’s possible to look further up in the import chain C for a prefix
      scope to extend. Here it’s chosen that that importing parent file gets
      to decide which names the part file has access to. If it wants to make
      a transitive parent import prefix available, it should just not shadow
      it.

The commentary "It’s not an error if two import deferred prefixes have the same name if they occur in different files, other file’s imports are only suggestions" seems to imply that we can have the following:

graph BT;
  Lib["Library 'main.dart'"] -->|import deferred as p| Import
  Part1["Part 'part1.dart'"] -->|part of| Lib
  Part2["Part 'part2.dart'"] -->|part of| Part1
  Import["Library 'other_lib.dart'"]
  Import1["Library 'other_lib1.dart'"]
  Import2["Library 'other_lib2.dart"]
  Part1 -->|import deferred as p| Import1
  Part2 -->|import deferred as p| Import2
Loading

Given that this is not an error, we would presumably have a situation where p in part2 shadows p in part1, which in turn shadows p in main. No errors, those deferred imports in parents are simply shadowed away.

However, if the import isn't deferred then we have a different situation:

graph BT;
  Lib["Library 'main.dart'"] -->|import as p| Import
  Part1["Part 'part1.dart'"] -->|part of| Lib
  Part2["Part 'part2.dart'"] -->|part of| Part1
  Import["Library 'other_lib.dart'"]
  Import1["Library 'other_lib1.dart'"]
  Import2["Library 'other_lib2.dart"]
  Part1 -->|import as p| Import1
  Part2 -->|import as p| Import2
Loading

With this setup, the bindings available for the prefix p in part2 are all the names from other_lib2 plus all the names that aren't already taken from other_lib1 plus all the names that aren't already taken from other_lib.

These approaches are surprisingly different. Is this what we want?

Another possibility would be to simply say that it is an error for the file tree that constitutes a library if, globally, any deferred library prefix has the same name as any other library prefix (deferred or not).

@dart-lang/language-team, WDYT?

@eernstg eernstg added question Further information is requested enhanced-parts labels Sep 5, 2024
@lrhn
Copy link
Member

lrhn commented Sep 5, 2024

What is specified is what I want, unsurprisingly 😁.

Deferred imports are really singletons, living by themselves.
That's what the current rule of "no two imports with the same name if any of them are deferred" means. The unit of deferred loading is a single import statement. A single library to be loaded deferredly.
They do not combine with other import prefixes.

We don't know, and haven't needed to say, whether the error would be that you can't deferred-load two libraries, or that two deferred imports with the same name would count as a name clash, beause each introduce a name independently.

This is generalized here to having imports in multiple scopes, using the latter interpretation. Each deferred import is a stand-alone declaration, not combining with anything else, but there is only a name clash if two are declared in the same file. Otherwise it's just shadowing as normal, they shadow any inherited import with the same name, including any inherited import prefix with the same name.

Where a non-deferred prefixed import would use an inherited (non-deferred) prefix with the same name as parent scope, the deferred import just doesn't do that. That's the only difference. One-prefix, one library.

Another possibility would be to simply say that it is an error for the file tree that constitutes a library if, globally, any deferred library prefix has the same name as any other library prefix (deferred or not).

Since you can always rename prefixes, that wouldn't restrict what you can do, only what you can name it.
It's not impossible. I don't see a strong need for it.

I think it would feel fairly annoying if a deferred import prefix in one part file conflicts with a prefix import in an unrelated sibling/cousin-third-removed part file, when neither file can see the names of the other.

If we make a restriction (again, don't think it's needed), I'd only make it an error to have an import prefix and a deferred prefix with the same name if one actually shadows the other, that is if one of them is in the combined import scope of the parent file of the declaration of the other.
If you can't see the other name, it's not a confict.

Or, at a minimum, the two should be on the same part of path to the library file, so one could potentially see the other, if it hasn't been shadowed on the way.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhanced-parts question Further information is requested
Projects
Development

No branches or pull requests

2 participants