mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 18:40:16 +01:00
More consistent package syntax (#3927)
Change the syntax for `package` declarations from: ```carbon [package Foo] [library "bar"] api; [package Foo] [library "bar"] impl; ``` to ```carbon [package Foo] [library "bar"]; impl [package Foo] [library "bar"]; ```
This commit is contained in:
+46
-42
@@ -194,7 +194,7 @@ The `import` declaration imports a
|
||||
[library from a package](#files-libraries-packages). It must appear at the top
|
||||
of a Carbon source file, the first thing after the
|
||||
[optional `package` declaration](#package-declaration). Libraries can optionally
|
||||
be split into [api and implementation files](#files-libraries-packages), like
|
||||
be split into [API and implementation files](#files-libraries-packages), like
|
||||
C++'s header and source files but without requiring a source file in any cases.
|
||||
This declaration from the example:
|
||||
|
||||
@@ -963,13 +963,14 @@ forward declaration that introduces the name before it is defined, plus any
|
||||
number of declarations in a
|
||||
[`match_first` block](generics/details.md#prioritization-rule). Forward
|
||||
declarations can be used to separate interface from implementation, such as to
|
||||
declare a name in an [api file](#files-libraries-packages) that is defined in an
|
||||
[impl file](#files-libraries-packages). Forward declarations also allow entities
|
||||
to be used before they are defined, such as to allow cyclic references. A name
|
||||
that has been declared but not defined is called _incomplete_, and in some cases
|
||||
there are limitations on what can be done with an incomplete name. Within a
|
||||
definition, the defined name is incomplete until the end of the definition is
|
||||
reached, but is complete in the bodies of member functions because they are
|
||||
declare a name in an [API file](#files-libraries-packages) that is defined in an
|
||||
[implementation file](#files-libraries-packages). Forward declarations also
|
||||
allow entities to be used before they are defined, such as to allow cyclic
|
||||
references. A name that has been declared but not defined is called
|
||||
_incomplete_, and in some cases there are limitations on what can be done with
|
||||
an incomplete name. Within a definition, the defined name is incomplete until
|
||||
the end of the definition is reached, but is complete in the bodies of member
|
||||
functions because they are
|
||||
[parsed as if they appeared after the definition](#class-functions-and-factory-functions).
|
||||
|
||||
A name is valid until the end of the innermost enclosing
|
||||
@@ -2190,13 +2191,15 @@ selected yet.
|
||||
### Files, libraries, packages
|
||||
|
||||
- **Files** are grouped into libraries, which are in turn grouped into
|
||||
packages.
|
||||
packages. There are two kinds of files:
|
||||
- API files describe the interface of a library.
|
||||
- Implementation files implement part of the interface of a library.
|
||||
- **Libraries** are the granularity of code reuse through imports.
|
||||
- **Packages** are the unit of distribution.
|
||||
|
||||
Each library must have exactly one `api` file. This file includes declarations
|
||||
for all public names of the library. Definitions for those declarations must be
|
||||
in some file in the library, either the `api` file or an `impl` file.
|
||||
Each library must have exactly one API file. This file includes declarations for
|
||||
all public names of the library. Definitions for those declarations must be in
|
||||
some file in the library, either the API file or an implementation file.
|
||||
|
||||
Every package has its own namespace. This means libraries within a package need
|
||||
to coordinate to avoid name conflicts, but not across packages.
|
||||
@@ -2211,11 +2214,12 @@ to coordinate to avoid name conflicts, but not across packages.
|
||||
|
||||
Files start with an optional package declaration, consisting of:
|
||||
|
||||
- optionally, the `impl` modifier keyword, indicating the file is an
|
||||
implementation file rather than an API file,
|
||||
- optionally, the `package` keyword followed by an identifier specifying the
|
||||
package name,
|
||||
- optionally, the `library` keyword followed by a string with the library
|
||||
name,
|
||||
- either `api` or `impl`, and
|
||||
name, and
|
||||
- a terminating semicolon (`;`).
|
||||
|
||||
For example:
|
||||
@@ -2223,25 +2227,25 @@ For example:
|
||||
```carbon
|
||||
// Package name is `Geometry`.
|
||||
// Library name is "Shapes".
|
||||
// This file is an `api` file, not an `impl` file.
|
||||
package Geometry library "Shapes" api;
|
||||
// This file is an API file, not an implementation file.
|
||||
package Geometry library "Shapes";
|
||||
```
|
||||
|
||||
Parts of this declaration may be omitted:
|
||||
|
||||
- If the package keyword is not specified, as in `library "Widgets" api;`, the
|
||||
- If the package keyword is not specified, as in `library "Widgets";`, the
|
||||
file contributes to the `Main` package. No other package may import from the
|
||||
`Main` package, and it cannot be named explicitly.
|
||||
|
||||
- If the library keyword is not specified, as in `package Geometry api;`, this
|
||||
- If the library keyword is not specified, as in `package Geometry;`, this
|
||||
file contributes to the default library.
|
||||
|
||||
- If both keywords are omitted, the package declaration must be omitted
|
||||
entirely. In this case, the file is an `impl` file belonging to the default
|
||||
library of the `Main` package, which implicitly has an empty `api` file.
|
||||
This library is used to define the entry point for the program, and tests
|
||||
and smaller examples may choose to reside entirely within this library. No
|
||||
other library can import this library even from within the default package.
|
||||
entirely. In this case, the file is the API file for the default library of
|
||||
the `Main` package, which cannot have any implementation files. This library
|
||||
is used to define the entry point for the program, and tests and smaller
|
||||
examples may choose to reside entirely within this library. No other library
|
||||
can import this library even from within the default package.
|
||||
|
||||
If the default library of the `Main` package contains a function named `Run`,
|
||||
that function is the program entry point. Otherwise, the program's entry point
|
||||
@@ -2282,11 +2286,11 @@ given library to the top-level scope of the current file as
|
||||
[`private`](#name-visibility) names, and similarly for names in
|
||||
[namespaces](#namespaces).
|
||||
|
||||
Every `impl` file automatically imports the `api` file for its library.
|
||||
Every implementation file automatically imports the API file for its library.
|
||||
Attempting to perform an import of the current library is invalid.
|
||||
|
||||
```
|
||||
package MyPackage library "Widgets" impl;
|
||||
impl package MyPackage library "Widgets";
|
||||
|
||||
// ❌ Error, this import is performed implicitly.
|
||||
import MyPackage library "Widgets";
|
||||
@@ -2309,7 +2313,7 @@ When the package name is specified, the `import` declaration imports a library
|
||||
from another package.
|
||||
|
||||
```carbon
|
||||
package MyPackage impl;
|
||||
impl package MyPackage;
|
||||
|
||||
// Import the "Vector" library from the `LinearAlgebra` package.
|
||||
import LinearAlgebra library "Vector";
|
||||
@@ -2344,17 +2348,17 @@ package can only be imported from within that package.
|
||||
|
||||
The names visible from an imported library are determined by these rules:
|
||||
|
||||
- Declarations in an `api` file are by default _public_, which means visible
|
||||
to any file that imports that library. This matches class members, which are
|
||||
- Declarations in an API file are by default _public_, which means visible to
|
||||
any file that imports that library. This matches class members, which are
|
||||
also [default public](#access-control).
|
||||
- A `private` prefix on a declaration in an `api` file makes the name _library
|
||||
private_. This means the name is visible in the file and all `impl` files
|
||||
for the same library.
|
||||
- A `private` prefix on a declaration in an API file makes the name _library
|
||||
private_. This means the name is visible in the file and all implementation
|
||||
files for the same library.
|
||||
- The visibility of a name is determined by its first declaration, considering
|
||||
`api` files before `impl` files. The `private` prefix is only allowed on the
|
||||
first declaration.
|
||||
- A name declared in an `impl` file and not the corresponding `api` file is
|
||||
_file private_, meaning visible in just that file. Its first declaration
|
||||
API files before implementation files. The `private` prefix is only allowed
|
||||
on the first declaration.
|
||||
- A name declared in an implementation file and not the corresponding API file
|
||||
is _file private_, meaning visible in just that file. Its first declaration
|
||||
must be marked with a `private` prefix. **TODO:** This needs to be finalized
|
||||
in a proposal to resolve inconsistency between
|
||||
[#665](https://github.com/carbon-language/carbon-lang/issues/665#issuecomment-914661914)
|
||||
@@ -2363,7 +2367,7 @@ The names visible from an imported library are determined by these rules:
|
||||
to: two different libraries can have different private names `foo` without
|
||||
conflict, but a private name conflicts with a public name in the same scope.
|
||||
|
||||
At most one `api` file in a package transitively used in a program may declare a
|
||||
At most one API file in a package transitively used in a program may declare a
|
||||
given name public.
|
||||
|
||||
> References:
|
||||
@@ -2383,9 +2387,9 @@ given name public.
|
||||
The top-level scope in a file is the scope of the package. This means:
|
||||
|
||||
- Within this scope (and its sub-namespaces), all visible names from the same
|
||||
package appear. This includes names from the same file, names from the `api`
|
||||
file of a library when inside an `impl` file, and names from imported
|
||||
libraries of the same package.
|
||||
package appear. This includes names from the same file, names from the API
|
||||
file of a library when inside an implementation file, and names from
|
||||
imported libraries of the same package.
|
||||
- In scopes where package members might have a name conflict with something
|
||||
else, the syntax `package.Foo` can be used to name the `Foo` member of the
|
||||
current package.
|
||||
@@ -2441,7 +2445,7 @@ namespace are considered in scope and may be found by
|
||||
package `P` defines some of its members inside a namespace `N`:
|
||||
|
||||
```carbon
|
||||
package P api;
|
||||
package P;
|
||||
|
||||
// Defines namespace `N` within the current package.
|
||||
namespace N;
|
||||
@@ -2642,8 +2646,8 @@ fn F();
|
||||
|
||||
Common types that we expect to be used universally will be provided for every
|
||||
file are made available as if there was a special "prelude" package that was
|
||||
imported automatically into every `api` file. Dedicated type literal syntaxes
|
||||
like `i32` and `bool` refer to types defined within this package, based on the
|
||||
imported automatically into every API file. Dedicated type literal syntaxes like
|
||||
`i32` and `bool` refer to types defined within this package, based on the
|
||||
["all APIs are library APIs" principle](/docs/project/principles/library_apis_only.md).
|
||||
|
||||
> **TODO:** Prelude provisionally imports the `Carbon` package which includes
|
||||
|
||||
@@ -2137,7 +2137,7 @@ implications:
|
||||
However, there are likely to be differences between computed properties and
|
||||
other data members, such as the ability to take the address of them. We might
|
||||
want to support "read only" data members, that can be read through the public
|
||||
api but only modified with private access, for data members which may need to
|
||||
API but only modified with private access, for data members which may need to
|
||||
evolve into a computed property. There are also questions regarding how to
|
||||
support assigning or modifying computed properties, such as using `+=`.
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
- [Package and library name conflicts](#package-and-library-name-conflicts)
|
||||
- [Potential refactorings](#potential-refactorings)
|
||||
- [Update imports](#update-imports)
|
||||
- [Between `api` and `impl` files](#between-api-and-impl-files)
|
||||
- [Between API and implementation files](#between-api-and-implementation-files)
|
||||
- [Other refactorings](#other-refactorings)
|
||||
- [Preference for few child namespaces](#preference-for-few-child-namespaces)
|
||||
- [Redundant markers](#redundant-markers)
|
||||
@@ -107,7 +107,7 @@ _libraries_<sup><small>[[define](/docs/guides/glossary.md#library)]</small></sup
|
||||
can be written containing pieces of the program:
|
||||
|
||||
```
|
||||
library "Colors" api;
|
||||
library "Colors";
|
||||
|
||||
choice Color { Red, Green, Blue }
|
||||
|
||||
@@ -115,7 +115,7 @@ fn ColorName(c: Color) -> String;
|
||||
```
|
||||
|
||||
```
|
||||
library "Colors" impl;
|
||||
impl library "Colors";
|
||||
|
||||
fn ColorName(c: Color) -> String {
|
||||
match (c) {
|
||||
@@ -139,11 +139,11 @@ A library is the basic unit of _dependency_. Separating code into multiple
|
||||
libraries can speed up the overall build while also making it clear which code
|
||||
is being reused.
|
||||
|
||||
A library has a single `api` file which defines its interface, plus zero or more
|
||||
`impl` files that can provide any implementation details that were omitted from
|
||||
the `api` file. These files are distinguished by the `library` declaration
|
||||
ending with `api;` or `impl;`. By convention, implementation files also use a
|
||||
file extension of `.impl.carbon`.
|
||||
A library has a single API file which defines its interface, plus zero or more
|
||||
implementation files that can provide any implementation details that were
|
||||
omitted from the API file. These files are distinguished by whether the
|
||||
`library` declaration starts with the `impl` modifier. By convention,
|
||||
implementation files also use a file extension of `.impl.carbon`.
|
||||
|
||||
Separating a library into interface and implementation may help organize code as
|
||||
a library grows, or to let the build system distinguish between the dependencies
|
||||
@@ -168,15 +168,15 @@ single identifier, such as `Geometry`. An example API file in the `Geometry`
|
||||
package would start with:
|
||||
|
||||
```
|
||||
package Geometry api;
|
||||
package Geometry;
|
||||
```
|
||||
|
||||
A tiny package may consist of a single library with a single `api` file. As with
|
||||
A tiny package may consist of a single library with a single API file. As with
|
||||
libraries, additional implementation files can be added to the package by using
|
||||
the `impl` keyword in the package declaration:
|
||||
|
||||
```
|
||||
package Geometry impl;
|
||||
impl package Geometry;
|
||||
```
|
||||
|
||||
However, as a package adds more files, it will probably want to separate out
|
||||
@@ -187,7 +187,7 @@ the `Main` package. For example, an API file adding the library `Shapes` to the
|
||||
[shorthand](#shorthand-notation-for-libraries-in-packages), would start with:
|
||||
|
||||
```
|
||||
package Geometry library "Shapes" api;
|
||||
package Geometry library "Shapes";
|
||||
```
|
||||
|
||||
This library can be imported within the same package by using:
|
||||
@@ -213,7 +213,7 @@ package. Names declared within this package can be found as members of the name
|
||||
this syntax, and if omitted, the default (unnamed) library is imported.
|
||||
|
||||
```
|
||||
// Imports the source file beginning `package Geometry api;`
|
||||
// Imports the source file beginning `package Geometry;`
|
||||
import Geometry;
|
||||
```
|
||||
|
||||
@@ -239,7 +239,7 @@ defines `Circle` then the name path will be `Geometry.Circle`. However, it can
|
||||
be named `Geometry.TwoDimensional.Circle` with a `namespace`; for example:
|
||||
|
||||
```
|
||||
package Geometry library "Shapes" api;
|
||||
package Geometry library "Shapes";
|
||||
namespace TwoDimensional;
|
||||
struct TwoDimensional.Circle { ... };
|
||||
```
|
||||
@@ -310,49 +310,50 @@ The `package` directive's syntax may be loosely expressed as a regular
|
||||
expression:
|
||||
|
||||
```regex
|
||||
package IDENTIFIER (library STRING)? (api|impl);
|
||||
(impl)? package IDENTIFIER (library STRING)?;
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```carbon
|
||||
package Geometry library "Objects/FourSides" api;
|
||||
impl package Geometry library "Objects/FourSides";
|
||||
```
|
||||
|
||||
Breaking this apart:
|
||||
|
||||
- The identifier passed to the `package` keyword, `Geometry`, is the package
|
||||
name and will prefix both library and namespace paths.
|
||||
- The use of the `impl` keyword indicates this is an implementation files as
|
||||
described under [libraries](#libraries). If it were omitted, this would
|
||||
instead be an API file.
|
||||
- The identifier after the `package` keyword, `Geometry`, is the package name
|
||||
and will prefix both library and namespace paths.
|
||||
- The `package` keyword also declares a package entity matching the
|
||||
package name. A package entity is almost identical to a namespace
|
||||
entity, except with some package/import-specific handling. In other
|
||||
words, if the file declares `struct Line`, that may be used from within
|
||||
the file as both `Line` directly and `Geometry.TwoDimensional.Line`
|
||||
entity, except with some package/import-specific handling. For example,
|
||||
if the file declares `namespace TwoDimensional;` and
|
||||
`struct TwoDimensional.Line`, the struct may be used from files in other
|
||||
packages that import the library as `Geometry.TwoDimensional.Line`,
|
||||
using the `Geometry` package entity created by the `package` keyword.
|
||||
- `Main` is invalid for use as the package name. `Main` libraries must be
|
||||
defined by either the [`library` directive](#library-directives) or the
|
||||
[`Main//default`](#maindefault) rule.
|
||||
- When the optional `library` keyword is specified, sets the name of the
|
||||
library within the package. In this example, the
|
||||
`Geometry//Objects/FourSides` library will be used.
|
||||
- If the `library` portion is omitted, the file is implicitly part of the
|
||||
default library, which does not have a string name.
|
||||
- The use of the `api` keyword indicates this is an API files as described
|
||||
under [libraries](#libraries). If it instead had `impl`, this would be an
|
||||
implementation file.
|
||||
- The string after the `library` keyword sets the name of the library within
|
||||
the package. In this example, the `Geometry//Objects/FourSides` library will
|
||||
be used.
|
||||
- If the `library` portion were omitted, the file would implicitly be part
|
||||
of the default library, which does not have a string name.
|
||||
|
||||
#### `library` directives
|
||||
|
||||
The syntax for `library` directives is the same, without the `package` portion:
|
||||
|
||||
```regex
|
||||
library STRING (api|impl);
|
||||
(impl)? library STRING;
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```carbon
|
||||
library "PrimeGenerator" impl;
|
||||
impl library "PrimeGenerator";
|
||||
```
|
||||
|
||||
If the `library` directive is used, the file is implicitly part of the `Main`
|
||||
@@ -361,7 +362,7 @@ package, whose name cannot be written explicitly.
|
||||
#### `Main//default`
|
||||
|
||||
If neither a `package` directive nor a `library` directive is provided, the file
|
||||
is an `api` file for `Main//default`. An `impl` cannot be provided for
|
||||
is an API file for `Main//default`. An `impl` cannot be provided for
|
||||
`Main//default`.
|
||||
|
||||
#### Files and libraries
|
||||
@@ -377,8 +378,7 @@ path consists of only the package name. There is no "global" namespace.
|
||||
- Entities within a file may be defined in [child namespaces](#namespaces).
|
||||
|
||||
Files contributing to the `Geometry//Objects/FourSides` library must all start
|
||||
with `package Geometry library "Objects/FourSides"`, but will differ on
|
||||
`api`/`impl` types.
|
||||
with [`impl`] `package` `Geometry` `library` `"Objects/FourSides"` `;`.
|
||||
|
||||
#### Shorthand notation for libraries in packages
|
||||
|
||||
@@ -419,8 +419,8 @@ Every Carbon library consists of one or more files. Each Carbon library has a
|
||||
primary file that defines its API, and may optionally contain additional files
|
||||
that are implementation.
|
||||
|
||||
- An API file's `package` directive will have `api`. For example,
|
||||
`package Geometry library "Shapes" api;`
|
||||
- An API file's `package` directive does not include the `impl` modifier. For
|
||||
example, `package Geometry library "Shapes";`
|
||||
- API filenames must have the `.carbon` extension. They must not have a
|
||||
`.impl.carbon` extension.
|
||||
- API file paths will correspond to the library name.
|
||||
@@ -428,13 +428,13 @@ that are implementation.
|
||||
be expected to be similar to a "Math/Algebra" library being in a
|
||||
"Math/Algebra.carbon" file path.
|
||||
- The package will not be used when considering the file path.
|
||||
- An implementation file's `package` directive will have `impl`. For example,
|
||||
`package Geometry library "Shapes" impl;`.
|
||||
- An implementation file's `package` directive includes an `impl` modifier.
|
||||
For example, `impl package Geometry library "Shapes";`.
|
||||
- Implementation filenames must have the `.impl.carbon` extension.
|
||||
- Implementation file paths need not correspond to the library name.
|
||||
- Implementation files implicitly import the library's API. Implementation
|
||||
files cannot import each other. There is no facility for file or
|
||||
non-`api` imports.
|
||||
files cannot import each other. There is no facility for file or non-API
|
||||
imports.
|
||||
|
||||
The difference between API and implementation will act as a form of access
|
||||
control. API files must compile independently of implementation, only importing
|
||||
@@ -459,21 +459,19 @@ may be marked as `private` to indicate they should only be visible to other
|
||||
parts of the library.
|
||||
|
||||
```carbon
|
||||
package Geometry library "Shapes" api;
|
||||
package Geometry library "Shapes";
|
||||
|
||||
// Circle is an API, and will be available to other libraries as
|
||||
Geometry.Circle.
|
||||
// Circle is part of the public API of the library, and will be available to
|
||||
// other libraries as Geometry.Circle.
|
||||
struct Circle { ... }
|
||||
|
||||
// CircleHelper is private, and so will not be available to other libraries.
|
||||
private fn CircleHelper(circle: Circle) { ... }
|
||||
|
||||
// Only entities in namespaces should be marked as an API, not the namespace
|
||||
// itself.
|
||||
namespace Operations;
|
||||
|
||||
// Operations.GetCircumference is an API, and will be available to
|
||||
// other libraries as Geometry.Operations.GetCircumference.
|
||||
// Operations.GetCircumference is part of the public API of the library, and
|
||||
// will be available to other libraries as Geometry.Operations.GetCircumference.
|
||||
fn Operations.GetCircumference(circle: Circle) { ... }
|
||||
```
|
||||
|
||||
@@ -488,15 +486,16 @@ However, separate implementation files are still desirable for a few reasons:
|
||||
- From a code maintenance perspective, having smaller files can make a library
|
||||
more maintainable.
|
||||
|
||||
Entities in the `impl` file should never have visibility keywords. If they are
|
||||
forward declared in the `api` file, they use the declaration's visibility; if
|
||||
they are only present in the `impl` file, they are implicitly `private`.
|
||||
Entities in an implementation file should never have visibility keywords. If
|
||||
they are forward declared in the API file, they use the declaration's
|
||||
visibility; if they are only present in an implementation file, they are
|
||||
implicitly `private`.
|
||||
|
||||
#### Granularity of libraries
|
||||
|
||||
The compilation graph of Carbon will generally consist of `api` files depending
|
||||
on each other, and `impl` files depending only on `api` files. Compiling a given
|
||||
file requires compiling the transitive closure of `api` files first.
|
||||
The compilation graph of Carbon will generally consist of API files depending on
|
||||
each other, and implementation files depending only on API files. Compiling a
|
||||
given file requires compiling the transitive closure of API files first.
|
||||
Parallelization of compilation is then limited by how large that transitive
|
||||
closure is, in terms of total volume of code rather than quantity. This also
|
||||
affects build cache invalidation.
|
||||
@@ -504,16 +503,15 @@ affects build cache invalidation.
|
||||
In order to maximize opportunities to improve compilation performance, we will
|
||||
encourage granular libraries. Conceptually, we want libraries to be very small,
|
||||
possibly containing only a single class. The choice of only allowing a single
|
||||
`api` file per library should help encourage developers to write small
|
||||
libraries.
|
||||
API file per library should help encourage developers to write small libraries.
|
||||
|
||||
#### Exporting namespaces
|
||||
|
||||
A namespace declared in an `api` file is only exported if it contains at least
|
||||
one `public` non-namespace name. For example, given this code:
|
||||
A namespace declared in an API file is only exported if it contains at least one
|
||||
`public` non-namespace name. For example, given this code:
|
||||
|
||||
```carbon
|
||||
package Checksums library "Sha" api;
|
||||
package Checksums library "Sha";
|
||||
|
||||
namespace Sha256;
|
||||
namespace ImplementationDetails;
|
||||
@@ -525,7 +523,7 @@ fn Sha256.HexDigest(data: Bytes) -> String { ... }
|
||||
Calling code may look like:
|
||||
|
||||
```carbon
|
||||
package Caller api;
|
||||
package Caller;
|
||||
|
||||
import Checksums library "Sha";
|
||||
|
||||
@@ -564,7 +562,7 @@ entities may be [aliased](/docs/design/aliases.md) if desired.
|
||||
For example, given a library:
|
||||
|
||||
```carbon
|
||||
package Math api;
|
||||
package Math;
|
||||
namespace Trigonometry;
|
||||
fn Trigonometry.Sin(...);
|
||||
```
|
||||
@@ -572,7 +570,7 @@ fn Trigonometry.Sin(...);
|
||||
Calling code would import it and use it like:
|
||||
|
||||
```carbon
|
||||
package Geometry api;
|
||||
package Geometry;
|
||||
|
||||
import Math;
|
||||
|
||||
@@ -591,8 +589,8 @@ import Math;
|
||||
import Math library "Trigonometry";
|
||||
```
|
||||
|
||||
NOTE: A library must never import itself. Any `impl` files in a library
|
||||
automatically import the `api`, so a self-import should never be required.
|
||||
NOTE: A library must never import itself. Any implementation files in a library
|
||||
automatically import the API, so a self-import should never be required.
|
||||
|
||||
#### Imports from the current package
|
||||
|
||||
@@ -607,7 +605,7 @@ namespace.
|
||||
For example:
|
||||
|
||||
```carbon
|
||||
package Geometry api;
|
||||
package Geometry;
|
||||
|
||||
// This is required even though it's still in the Geometry package.
|
||||
import library "Shapes";
|
||||
@@ -660,7 +658,7 @@ For example, if the `Geometry//Shapes/ThreeSides` library provides the
|
||||
`Geometry.Shapes` namespace, this code is still valid:
|
||||
|
||||
```carbon
|
||||
package Geometry library "Shapes/FourSides" api;
|
||||
package Geometry library "Shapes/FourSides";
|
||||
|
||||
import library "Shapes/ThreeSides";
|
||||
|
||||
@@ -781,56 +779,58 @@ check build dependencies for where imports should be added from, such as a
|
||||
database of possible entities and their libraries. However, adding references
|
||||
may require manually adding imports.
|
||||
|
||||
#### Between `api` and `impl` files
|
||||
#### Between API and implementation files
|
||||
|
||||
- Move an implementation of an API from an `api` file to an `impl` file, while
|
||||
leaving a declaration behind.
|
||||
- Move the definition of an entity from an API file to an implementation file,
|
||||
while leaving a declaration behind.
|
||||
|
||||
- This should be a local change that will not affect any calling code.
|
||||
- Inlining will be affected because the implementation won't be visible to
|
||||
callers.
|
||||
- [Update imports](#update-imports).
|
||||
|
||||
- Split an `api` and `impl` file.
|
||||
- Split an API and implementation file.
|
||||
|
||||
- This is a repeated operation of individual API moves, as noted above.
|
||||
|
||||
- Move an implementation of an API from an `impl` file to an `api` file.
|
||||
- Move the definition of an entity from an implementation file to the API
|
||||
file.
|
||||
|
||||
- This should be a local change that will not affect any calling code.
|
||||
- Inlining will be affected because the implementation becomes visible to
|
||||
callers.
|
||||
- [Update imports](#update-imports).
|
||||
|
||||
- Combine an `api` and `impl` file.
|
||||
- Combine an API and implementation file.
|
||||
|
||||
- This is a repeated operation of individual API moves, as noted above.
|
||||
|
||||
- Remove the `api` label from a declaration.
|
||||
- Add the `private` modifier to a declaration.
|
||||
|
||||
- Search for library-external callers, and fix them first.
|
||||
|
||||
- Add the `api` label to a declaration.
|
||||
- Remove the `private` modifier from a declaration.
|
||||
|
||||
- This should be a local change that will not affect any calling code.
|
||||
|
||||
- Move a non-`api`-labeled declaration from an `api` file to an `impl` file.
|
||||
- Move a `private` declaration from the API file to an implementation file.
|
||||
|
||||
- The declaration must be moved to the same file as the implementation of
|
||||
the declaration.
|
||||
- The declaration can only be used by the `impl` file that now contains
|
||||
it. Search for other callers within the library, and fix them first.
|
||||
- The declaration must be moved to the same file as the definition of the
|
||||
entity.
|
||||
- The declaration can only be used by the implementation file that now
|
||||
contains it. Search for other callers within the library, and fix them
|
||||
first.
|
||||
- [Update imports](#update-imports).
|
||||
|
||||
- Move a non-`api`-labeled declaration from an `impl` file to an `api` file.
|
||||
- Move a `private` declaration from an implementation file to the API file.
|
||||
|
||||
- This should be a local change that will not affect any calling code.
|
||||
- [Update imports](#update-imports).
|
||||
|
||||
- Move a declaration and implementation from one `impl` file to another.
|
||||
- Move a declaration and definition from one implementation file to another.
|
||||
|
||||
- Search for any callers within the source `impl` file, and either move
|
||||
them too, or fix them first.
|
||||
- Search for any callers within the source implementation file, and either
|
||||
move them too, or fix them first.
|
||||
- [Update imports](#update-imports).
|
||||
|
||||
#### Other refactorings
|
||||
@@ -841,15 +841,14 @@ may require manually adding imports.
|
||||
- All call sites must be changed, as the package name changes.
|
||||
- [Update imports](#update-imports).
|
||||
|
||||
- Move an `api`-labeled declaration and implementation between different
|
||||
packages.
|
||||
- Move a public declaration and definition between different packages.
|
||||
|
||||
- The imports of all calling files must be updated accordingly.
|
||||
- All call sites must be changed, as the package name changes.
|
||||
- [Update imports](#update-imports).
|
||||
|
||||
- Move an `api`-labeled declaration and implementation between libraries in
|
||||
the same package.
|
||||
- Move a public declaration and definition between libraries in the same
|
||||
package.
|
||||
|
||||
- The imports of all calling files must be updated accordingly.
|
||||
- As long as the namespaces remain the same, no call sites will need to be
|
||||
@@ -858,20 +857,19 @@ may require manually adding imports.
|
||||
|
||||
- Rename a library.
|
||||
|
||||
- This is equivalent to a repeated operation of moving an `api`-labeled
|
||||
declaration and implementation between libraries in the same package.
|
||||
- This is equivalent to a repeated operation of moving a public
|
||||
declaration and definition between libraries in the same package.
|
||||
|
||||
- Move a declaration and implementation from one namespace to another.
|
||||
- Move a declaration and definition from one namespace to another.
|
||||
|
||||
- Ensure the new namespace is declared for the declaration and
|
||||
implementation.
|
||||
- Ensure the new namespace is declared for the declaration and definition.
|
||||
- Update the namespace used by call sites.
|
||||
- The imports of all calling files may remain the same.
|
||||
|
||||
- Rename a namespace.
|
||||
|
||||
- This is equivalent to a repeated operation of moving a declaration and
|
||||
implementation from one namespace to another.
|
||||
definition from one namespace to another.
|
||||
|
||||
- Rename a file, or move a file between directories.
|
||||
|
||||
@@ -901,21 +899,21 @@ fewer namespaces.
|
||||
|
||||
We use a few possibly redundant markers for packages and libraries:
|
||||
|
||||
- The `package` keyword requires one of `api` and `impl`, rather than
|
||||
excluding either or both.
|
||||
- The filename repeats the `api` versus `impl` choice.
|
||||
- The filename and the presence or absence of the `impl` keyword duplicate the
|
||||
API versus implementation choice.
|
||||
- The filename and the library name portion of the package declaration
|
||||
duplicate the name of the library.
|
||||
- The `import` keyword requires the full library.
|
||||
|
||||
These choices are made to assist human readability and tooling:
|
||||
|
||||
- Being explicit about imports creates the opportunity to generate build
|
||||
dependencies from files, rather than having them maintained separately.
|
||||
- Being explicit about `api` versus `impl` makes it easier for both humans and
|
||||
tooling to determine what to expect.
|
||||
- Repeating the type in the filename makes it possible to check the type
|
||||
without reading file content.
|
||||
- Repeating the type in the file content makes non-file-system-based builds
|
||||
possible.
|
||||
- Being explicit about API versus implementation in the filename makes it
|
||||
easier for both humans and tooling to determine what to expect, and makes it
|
||||
possible to check the type without reading file content.
|
||||
- Repeating the type and library name in the file content makes
|
||||
non-file-system-based builds possible.
|
||||
|
||||
## Open questions
|
||||
|
||||
@@ -972,8 +970,8 @@ import Carbon library "Utilities"
|
||||
|
||||
### Test file type
|
||||
|
||||
Similar to `api` and `impl`, we may eventually want a type like `test`. This
|
||||
should be part of a larger testing plan.
|
||||
Similar to API and implementation files, we may eventually want test-specific
|
||||
files. This should be part of a larger testing plan.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
@@ -989,7 +987,7 @@ should be part of a larger testing plan.
|
||||
- [Use a different name for the main package](/proposals/p2550.md#use-a-different-name-for-the-main-package)
|
||||
- [Use a different name for the entry point](/proposals/p2550.md#use-a-different-name-for-the-entry-point)
|
||||
- [Distinguish file scope from package scope](/proposals/p2550.md#distinguish-file-scope-from-package-scope)
|
||||
- [Default to `Main//default impl` instead of `Main//default api`](/proposals/p3403.md#default-to-maindefault-impl-instead-of-maindefault-api)
|
||||
- [Default to an implementation file for `Main//default` instead of an API file](/proposals/p3403.md#default-to-maindefault-impl-instead-of-maindefault-api)
|
||||
- Libraries
|
||||
- [Allow exporting namespaces](/proposals/p0107.md#allow-exporting-namespaces)
|
||||
- [Allow importing implementation files from within the same library](/proposals/p0107.md#allow-importing-implementation-files-from-within-the-same-library)
|
||||
@@ -1000,9 +998,10 @@ should be part of a larger testing plan.
|
||||
- [Collapse file and library concepts](/proposals/p0107.md#collapse-file-and-library-concepts)
|
||||
- [Collapse the library concept into packages](/proposals/p0107.md#collapse-the-library-concept-into-packages)
|
||||
- [Collapse the package concept into libraries](/proposals/p0107.md#collapse-the-package-concept-into-libraries)
|
||||
- [Default `api` to private](/proposals/p0752.md#default-api-to-private)
|
||||
- [Default API to private](/proposals/p0752.md#default-api-to-private)
|
||||
- [Default `impl` to public](/proposals/p0752.md#default-impl-to-public)
|
||||
- [Different file type labels](/proposals/p0107.md#different-file-type-labels)
|
||||
- [Don't default to introducing an API file](/proposals/p3927.md#mandatory-api-or-impl-as-suffix)
|
||||
- [Function-like syntax](/proposals/p0107.md#function-like-syntax)
|
||||
- [Inlining from implementation files](/proposals/p0107.md#inlining-from-implementation-files)
|
||||
- [Library-private access controls](/proposals/p0107.md#library-private-access-controls)
|
||||
@@ -1010,6 +1009,8 @@ should be part of a larger testing plan.
|
||||
- [Managing API versus implementation in libraries](/proposals/p0107.md#managing-api-versus-implementation-in-libraries)
|
||||
- [Multiple API files](/proposals/p0107.md#multiple-api-files)
|
||||
- [Name paths as library names](/proposals/p0107.md#name-paths-as-library-names)
|
||||
- [Put the `impl` modifier at the end](/proposals/p3927.md#mandatory-api-or-impl-as-suffix)
|
||||
- [Put the `impl` modifier before `library`](/proposals/p3927.md#put-the-impl-modifier-before-library)
|
||||
- Imports
|
||||
- [Block imports](/proposals/p0107.md#block-imports)
|
||||
- [Block imports of libraries of a single package](/proposals/p0107.md#block-imports-of-libraries-of-a-single-package)
|
||||
@@ -1034,3 +1035,5 @@ should be part of a larger testing plan.
|
||||
[#3403: Change Main//default to an api file](https://github.com/carbon-language/carbon-lang/pull/3403)
|
||||
- Proposal
|
||||
[#3453: Clarify name bindings in namespaces.](https://github.com/carbon-language/carbon-lang/pull/3407)
|
||||
- Proposal
|
||||
[#3927: More consistent package syntax](https://github.com/carbon-language/carbon-lang/pull/3927)
|
||||
|
||||
@@ -252,7 +252,7 @@ expression preceding the period. In a struct literal, the entity is the struct
|
||||
type. For example:
|
||||
|
||||
```
|
||||
package Foo api;
|
||||
package Foo;
|
||||
namespace N;
|
||||
fn N.F() {}
|
||||
|
||||
|
||||
@@ -220,7 +220,7 @@ package that is automatically imported by the prelude:
|
||||
### Usage
|
||||
|
||||
```carbon
|
||||
package sample api;
|
||||
package sample;
|
||||
|
||||
fn Sum(x: i32, y: i32) -> i32 {
|
||||
return x + y;
|
||||
|
||||
@@ -121,11 +121,11 @@ fn Add(a: i64, b: i64) -> i64 {
|
||||
|
||||
The corresponding definition may be provided later in the same file or, when the
|
||||
declaration is in an
|
||||
[`api` file of a library](code_and_name_organization/#libraries), in the `impl`
|
||||
file of the same library. The signature of a function declaration must match the
|
||||
corresponding definition. This includes the [return clause](#return-clause);
|
||||
even though an omitted return type has equivalent behavior to `-> ()`, the
|
||||
presence or omission must match.
|
||||
[API file of a library](code_and_name_organization/#libraries), in an
|
||||
implementation file of the same library. The signature of a function declaration
|
||||
must match the corresponding definition. This includes the
|
||||
[return clause](#return-clause); even though an omitted return type has
|
||||
equivalent behavior to `-> ()`, the presence or omission must match.
|
||||
|
||||
## Function calls
|
||||
|
||||
|
||||
@@ -261,7 +261,7 @@ constraint resolution, and it is possible for a facet type to be formed for
|
||||
which constraint resolution would always fail. For example:
|
||||
|
||||
```carbon
|
||||
package Broken api;
|
||||
package Broken;
|
||||
|
||||
interface X {
|
||||
let T:! type;
|
||||
|
||||
@@ -47,7 +47,6 @@ The following words are interpreted as keywords:
|
||||
- `addr`
|
||||
- `alias`
|
||||
- `and`
|
||||
- `api`
|
||||
- `as`
|
||||
- `auto`
|
||||
- `base`
|
||||
|
||||
@@ -16,7 +16,7 @@ Images are managed in
|
||||
A sample of quicksort in Carbon.
|
||||
|
||||
```cpp
|
||||
package Sorting api;
|
||||
package Sorting;
|
||||
|
||||
fn Partition[T:! Comparable & Movable](s: Slice(T))
|
||||
-> i64 {
|
||||
@@ -78,7 +78,7 @@ auto main() -> int {
|
||||
|
||||
```cpp
|
||||
// Carbon:
|
||||
package Geometry api;
|
||||
package Geometry;
|
||||
import Math;
|
||||
|
||||
class Circle {
|
||||
@@ -112,7 +112,7 @@ struct Circle {
|
||||
};
|
||||
|
||||
// Carbon exposing a function for C++:
|
||||
package Geometry api;
|
||||
package Geometry;
|
||||
import Cpp library "circle.h";
|
||||
import Math;
|
||||
|
||||
|
||||
@@ -40,9 +40,9 @@ outside the common case.
|
||||
|
||||
## Principle
|
||||
|
||||
In Carbon, every public function is declared in some Carbon `api` file, and
|
||||
every public `interface`, `impl`, and first-class type is defined in some Carbon
|
||||
`api` file. In some cases, the bodies of public functions will not be defined as
|
||||
In Carbon, every public function is declared in some Carbon API file, and every
|
||||
public `interface`, `impl`, and first-class type is defined in some Carbon API
|
||||
file. In some cases, the bodies of public functions will not be defined as
|
||||
Carbon code, or will be defined as hybrid Carbon code using intrinsics that
|
||||
aren't available to ordinary Carbon code. However, we will try to minimize those
|
||||
situations.
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
# More consistent package syntax
|
||||
|
||||
<!--
|
||||
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/3927)
|
||||
|
||||
<!-- toc -->
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [Abstract](#abstract)
|
||||
- [Problem](#problem)
|
||||
- [Background](#background)
|
||||
- [Proposal](#proposal)
|
||||
- [Details](#details)
|
||||
- [Rationale](#rationale)
|
||||
- [Alternatives considered](#alternatives-considered)
|
||||
- [Mandatory `api` or `impl` as suffix](#mandatory-api-or-impl-as-suffix)
|
||||
- [Put the `impl` modifier before `library`](#put-the-impl-modifier-before-library)
|
||||
|
||||
<!-- tocstop -->
|
||||
|
||||
## Abstract
|
||||
|
||||
Change the syntax for `package` declarations from:
|
||||
|
||||
```carbon
|
||||
[package Foo] [library "bar"] api;
|
||||
[package Foo] [library "bar"] impl;
|
||||
```
|
||||
|
||||
to
|
||||
|
||||
```carbon
|
||||
[package Foo] [library "bar"];
|
||||
impl [package Foo] [library "bar"];
|
||||
```
|
||||
|
||||
## Problem
|
||||
|
||||
The `package` declaration is currently inconsistent with other Carbon
|
||||
declarations:
|
||||
|
||||
- Modifier keywords for other declarations precede the introducer keyword.
|
||||
However, for package declarations, the `api` / `impl` modifier appears at
|
||||
the end of the declaration.
|
||||
- For most declarations describing the public interface of a library, we
|
||||
default to `public` because we prioritize the readability of the public
|
||||
interface over other concerns. However, the `api` tag in a `package` API
|
||||
declaration is mandatory, making the library interface more verbose than
|
||||
necessary.
|
||||
|
||||
## Background
|
||||
|
||||
[Proposal #107: Code and name organization](/proposals/p0107.md) introduced the
|
||||
current syntax. It did
|
||||
[consider the possibility of omitting the `api` keyword](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p0107.md#different-file-type-labels):
|
||||
|
||||
> We've considered dropping `api` from naming, but that creates a definition
|
||||
> from absence of a keyword. It also would be more unusual if both `impl` and
|
||||
> `test` must be required, that api would be excluded. We prefer the more
|
||||
> explicit name.
|
||||
|
||||
However, this argument did not and could not consider the inconsistencies
|
||||
between the choice made for package declaration and the choices made for other
|
||||
declarations, because those inconsistencies were created by later changes:
|
||||
|
||||
- #107 used the `api` keyword as a marker for exporting names from an API
|
||||
file. Later, [proposal #752: api file default public](/proposals/p0752.md)
|
||||
removed this use of the `api` keyword, with the new rule being that
|
||||
declarations are in the public API by default, with an explicit keyword used
|
||||
to mark non-public declarations. This removed all uses of the `api` keyword
|
||||
other than in package declarations.
|
||||
- Rules for modifier keywords were added incrementally by various proposals,
|
||||
with the common syntactic approach that modifier keywords precede an
|
||||
introducer keyword in a declaration.
|
||||
|
||||
In addition, the idea of having `test` files in addition to `api` and `impl`
|
||||
files has not yet been realised, and we have no current plans to add such a
|
||||
feature. While that may be an interesting avenue to pursue in future, using
|
||||
`test` as a modifier keyword may also be an interesting avenue to explore in
|
||||
that case too -- for example, to allow functions and types within an API file to
|
||||
be declared as test-only with a `test` modifier -- and so the possibility of
|
||||
`test` files isn't a robust rationale for choosing the syntax for `api` and
|
||||
`impl`.
|
||||
|
||||
## Proposal
|
||||
|
||||
Remove the `api` keyword from `package` and `library` declarations. Consistent
|
||||
with #752, the way we define a public interface is by saying nothing at all.
|
||||
|
||||
Turn the `impl` keyword on such declarations into a modifier keyword, consistent
|
||||
with its use in `impl fn` declarations. This reorders the `impl` keyword to the
|
||||
start of the declaration.
|
||||
|
||||
In documentation, we refer to API files as "API files", not as "`api` files".
|
||||
For consistency, we also refer to implementation files as "implementation
|
||||
files", not as "`impl` files". We were previously inconsistent and used both
|
||||
terms.
|
||||
|
||||
## Details
|
||||
|
||||
See design changes.
|
||||
|
||||
## Rationale
|
||||
|
||||
- [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write)
|
||||
- Small readability and writability gain for API files.
|
||||
- More consistency between different kinds of declaration.
|
||||
- Minor risk that an `impl` file will be interpreted as an API file due to
|
||||
missing the `impl` modifier. However, this is likely to be caught
|
||||
quickly, whether by file naming conventions, the lack of an implicit
|
||||
import of the "real" API file, or by duplicate API file detection in the
|
||||
toolchain.
|
||||
- [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code)
|
||||
- Marginally more consistent with C++ modules, which use `module Foo` vs
|
||||
`export module Foo` for the two cases -- with a leading keyword.
|
||||
However, C++ defaults to not exporting, so the case with a keyword is
|
||||
reversed, both here and in all other declarations.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
### Mandatory `api` or `impl` as suffix
|
||||
|
||||
The rationale for changing from the status quo ante of having a mandatory `impl`
|
||||
or `api` keyword as a suffix is documented above.
|
||||
|
||||
This proposal also harmonizes the `impl package` syntax with the
|
||||
`import package` syntax:
|
||||
|
||||
```carbon
|
||||
impl package Foo library "bar";
|
||||
|
||||
import package Baz library "quux";
|
||||
```
|
||||
|
||||
We consistently use a prefix for the package declaration for both of these
|
||||
cases. We also anticipate doing so for the `import reexport package ...` or
|
||||
`export import package ...` syntax that is currently under discussion.
|
||||
|
||||
As a trivial side benefit, the degenerate case of the package declaration for
|
||||
the default library in the Main package would now be expressed as simply `;`
|
||||
rather than `api;`. We retain the rule that the package declaration is omitted
|
||||
entirely in this case, which is slightly easier to justify given that the
|
||||
omitted declaration would comprise only a semicolon.
|
||||
|
||||
### Put the `impl` modifier before `library`
|
||||
|
||||
Because an implementation file is implementing part of a library, we can
|
||||
consider placing the `impl` keyword before the `library` keyword -- as a
|
||||
modifier on the `library` portion of the declaration -- instead of at the start
|
||||
of the overall package declaration.
|
||||
|
||||
This leads to constructs such as:
|
||||
|
||||
```carbon
|
||||
package Foo impl library "bar";
|
||||
package Foo impl;
|
||||
impl library "bar";
|
||||
```
|
||||
|
||||
While there is a logical rationale and consistent explanation for this approach,
|
||||
it results in the `impl` keyword's positioning appearing inconsistent: sometimes
|
||||
at the start, middle, or end of the declaration. This also doesn't make the
|
||||
`impl` declaration consistent with `import`, where the same argument can be
|
||||
made: an `import` imports the library, not the package.
|
||||
|
||||
As a result, we do not pursue this direction.
|
||||
Reference in New Issue
Block a user