Quellcode durchsuchen

Exporting imported names (#3938)

In order to support exporting imported names, add `export import library
<library>` and `export <name reference>` syntax.

---------

Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
Jon Ross-Perkins vor 1 Jahr
Ursprung
Commit
122a361317
1 geänderte Dateien mit 337 neuen und 0 gelöschten Zeilen
  1. 337 0
      proposals/p3938.md

+ 337 - 0
proposals/p3938.md

@@ -0,0 +1,337 @@
+# Exporting imported names
+
+<!--
+Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+Exceptions. See /LICENSE for license information.
+SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+-->
+
+[Pull request](https://github.com/carbon-language/carbon-lang/pull/3938)
+
+<!-- toc -->
+
+## Table of contents
+
+-   [Abstract](#abstract)
+-   [Problem](#problem)
+-   [Background](#background)
+    -   [Carbon exports](#carbon-exports)
+    -   [Other languages](#other-languages)
+-   [Proposal](#proposal)
+    -   [Source file introduction](#source-file-introduction)
+-   [Future work](#future-work)
+    -   [Namespaces](#namespaces)
+-   [Rationale](#rationale)
+-   [Alternatives considered](#alternatives-considered)
+    -   [Other `export` syntax structures](#other-export-syntax-structures)
+    -   [Other `export name` placements](#other-export-name-placements)
+    -   [Re-exporting cross-package](#re-exporting-cross-package)
+
+<!-- tocstop -->
+
+## Abstract
+
+In order to support exporting imported names, add
+`export import library <library>` and `export <name reference>` syntax.
+
+## Problem
+
+As we develop libraries such as the prelude, we want the ability to indicate
+that an imported name should be re-exported for indirect use. At present, we can
+use the prototype `alias` to expose names on a case-by-case basis
+(`alias Foo = Bar;`), but it doesn't work to export the _same_ name
+(`alias Foo = Foo;` is a name conflict), and we want to be able to more broadly
+forward a library's exported names.
+
+For example:
+
+```
+package Foo library "internal";
+
+// Declare C.
+class C;
+```
+
+```
+package Foo;
+
+// We want the ability to expose everything imported here.
+import library "internal";
+```
+
+```
+import library Foo;
+
+// Uses C by way of the default library.
+var c: Foo.C;
+```
+
+## Background
+
+Some of the syntax options were discussed
+[on Discord](https://discord.com/channels/655572317891461132/1217182321933815820/1234534411350048810).
+
+### Carbon exports
+
+Names declared in a Carbon file are currently exported by default. A `private`
+keyword may be used to prevent that export. Note C++ and TypeScript use
+private-by-default behavior, so the syntax choices that make sense elsewhere may
+not make as much sense for Carbon.
+
+As described in the problem statement, `alias` offers incomplete re-export
+support. However, `alias` is not fully designed; it's modeled on informal
+discussions.
+
+### Other languages
+
+In [C++ modules](https://en.cppreference.com/w/cpp/language/modules), this is
+`export import ...`.
+
+In
+[TypeScript modules](https://www.typescriptlang.org/docs/handbook/2/modules.html),
+similar syntax might look like:
+
+```typescript
+import * from '<module>'
+export * from '<module>'
+```
+
+In Python, names in a module are generally public, and imported names are
+accessible too. For example, given `import datetime`, the module makes the name
+`datetime` available to clients. There is interest in
+[more explicit `export` syntax](https://discuss.python.org/t/add-the-export-keyword-to-python/28444).
+
+In other languages, such as Java, Kotlin, or Go, direct re-exports aren't
+supported. Instead, the expectation seems to be that either a copy of the entity
+would be exported, or it should just be moved.
+
+## Proposal
+
+Support the `export` keyword as a modifier to `import library <library>`
+(excluding cross-package imports). This is `export import` for short. For
+example:
+
+```carbon
+export import library "lib";
+```
+
+Additionally, support the `export` keyword on individual, file-scoped or
+namespace-scoped entities (excluding entities in other packages, and namespaces
+themselves). This is `export name` for short. For example:
+
+```carbon
+// Export an entity:
+export Foo;
+// Export an entity inside a namespace:
+export NS.Bar;
+
+// Invalid: exporting namespaces is disallowed.
+export NS;
+```
+
+Although exporting cross-package names is disallowed, note that `alias` can be
+used to add a package-local name that originates from another package, which
+then is valid for export. For example:
+
+```carbon
+import package Other;
+
+// Invalid: cross-package exports are disallowed.
+export Other.Obj;
+
+// This introduces a package-local name. The alias is exported, and other
+// libraries importing this library may export `Obj`.
+alias Obj = Other.Obj;
+```
+
+The `export` keyword is only valid in files which are valid to import. It is
+invalid in files which cannot be imported: implementation files and
+`Main//default`.
+
+### Source file introduction
+
+In the
+[source file introduction](/docs/design/code_and_name_organization/README.md#source-file-introduction),
+`export import` directives are intermingled with other `import` directives.
+`export name` directives are normal code and cannot be intermingled with any
+`import` directives, including `export import` directives.
+
+This allows:
+
+```
+import library "foo";
+export import library "wiz";
+import library "bar";
+
+export FooType;
+
+class C { ... };
+
+export BarType;
+```
+
+This disallows:
+
+```
+import library "foo";
+// Invalid: All `import` directives must come before other code, including
+// `export name`.
+export FooType;
+
+import library "bar";
+
+class C { ... };
+
+// Invalid: `export import` must be grouped with `import` directives.
+export import library "wiz";
+```
+
+## Future work
+
+### Namespaces
+
+Namespaces are not valid arguments to `export`; entities in namespaces must be
+individually exported.
+
+This keeps open a future design option of having `export` on a namespace export
+all imported names inside the namespace, such as `export NS;`. This could also
+be achieved with `*` syntax, such as `export NS.*;`. There hasn't been
+discussion of this option, and this proposal takes no stance on the option.
+
+## Rationale
+
+-   [Software and language evolution](/docs/project/goals.md#software-and-language-evolution)
+    -   `export <name>` allows moving entities between libraries without needing
+        to make modifications to clients, enabling more incremental
+        refactorings.
+-   [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write)
+    -   Export logic in general is intended to support factoring large or
+        complex APIs into multiple, smaller files. For example, with the
+        prelude, we'll have many types, interfaces, and implementations: concise
+        re-exporting logic will make it easy to provide a singular
+        `prelude.carbon` that exports all related functionality.
+
+## Alternatives considered
+
+### Other `export` syntax structures
+
+We discussed several different syntax choices.
+
+A couple placement alternatives discussed were:
+
+-   Put `export` before `library`. For example, `import export library "lib"`.
+    -   An advantage of this is that if we support cross-package re-exports,
+        `import Foo export library "lib"` could make it clearer the library is
+        being re-exported, rather than the package.
+    -   A disadvantage is that we would probably not put other keywords between
+        the package and library.
+-   Put `export` as a suffix. For example, `import library "lib" export`.
+    -   An advantage of this is that it makes `import` statements line up better
+        when some may not have the `export` modifier.
+
+The current design uses `export` as a prefix. This is for consistency with how
+we put other modifier keywords, such as `private` or `extern`, prior to the
+introducer keyword.
+
+A couple keyword alternatives discussed (alongside placement options) were:
+
+-   `reexport`
+    -   An advantage noted is that it may read more intuitively for some
+        developers.
+    -   This proposal suggests `export` because it mirrors `import`, and it's
+        consistent with multiple other languages. It's also shorter, and Carbon
+        often chooses keywords for shortness.
+-   `exported` and `reexported`
+    -   These didn't seem to read as clearly as `export` or `reexport`.
+
+### Other `export name` placements
+
+We see several options for `export name` placement. This compares them, focusing
+on advantages and disadvantages for each option.
+
+1. `export name` with `import`s
+
+    `export name` can (only) appear in the preamble, with the imports, and
+    cannot appear with the other declarations in the library. Note this option
+    could either have `export name` refer to earlier `import`s (creating an
+    ordering consistency issue), or expend additional effort in order to track
+    whether a name was already imported at the site of the `export`.
+
+    Advantages:
+
+    - No need to teach developers they cannot (don't need to) `export` locally
+      introduced names.
+
+    Disadvantages:
+
+    - Although the restricted placement might imply placement is tied to
+      specific libraries, that's not the case. This could mislead developers.
+        - In theory, we could enforce this, but then we could end up breaking
+          code if the path a name is imported through changes.
+
+2. `export name` with other declarations
+
+    `export name` can only appear after imports. This means that all names valid
+    for `export` will already be made available.
+
+    Advantages:
+
+    - `import` remains very special.
+    - Makes it unambiguous that names valid for `export` are already imported.
+
+    Disadvantages:
+
+    - Prevents placing `export name` next to the import that is expected to add
+      the name.
+    - Means `export import` and `export name` will be in different sections: no
+      single place to look for re-exports.
+
+3. No ordering for `export name`
+
+    Let developers choose what the prefer.
+
+    Advantages:
+
+    - Maximum flexibility, HOA rule.
+
+    Disadvantages:
+
+    - Most inconsistent with the desire to treat `import` as special.
+
+We're choosing option (2). The name lookup issues avoided by requiring `export`
+be below `import` directives seem worthwhile.
+
+A possible option to (2) might be to create an additional section dedicated to
+`export name` below the `import` section. This proposal suggests avoiding that
+in order to avoid increasing the amount of enforced ordering in Carbon files.
+
+### Re-exporting cross-package
+
+As proposed, re-exporting names from other packages will not be supported. This
+is done to continue maintaining package boundaries, and so that names aren't
+unexpectedly introduced. For example:
+
+```carbon
+package Foo;
+
+class C;
+```
+
+```carbon
+package Bar;
+
+export import Foo;
+```
+
+```carbon
+package Wiz;
+
+import Bar;
+```
+
+In the last package `Wiz`, it might be confusing if the name `Foo.C` should be
+introduced: typically importing `Bar` would put everything under the `Bar`
+namespace.
+
+We may choose to re-examine this choice, but this proposal does not include
+support.