mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
Clarify name bindings in namespaces. (#3407)
- Require namespace members be declared in the same name scope as the
namespace is declared.
- Allow binding patterns to directly declare names in namespaces.
- Disallow using different namespaces in the same binding pattern.
---------
Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
Co-authored-by: Richard Smith <richard@metafoo.co.uk>
This commit is contained in:
co-authored by
Chandler Carruth
Richard Smith
parent
af9b323aad
commit
6c812db05c
@@ -33,6 +33,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
- [Imports from the current package](#imports-from-the-current-package)
|
||||
- [Namespaces](#namespaces)
|
||||
- [Re-declaring imported namespaces](#re-declaring-imported-namespaces)
|
||||
- [Declaring namespace members](#declaring-namespace-members)
|
||||
- [Aliasing](#aliasing)
|
||||
- [Caveats](#caveats)
|
||||
- [Package and library name conflicts](#package-and-library-name-conflicts)
|
||||
@@ -618,10 +619,11 @@ fn GetArea(c: Circle) { ... }
|
||||
|
||||
### Namespaces
|
||||
|
||||
Namespaces offer named paths for entities. Namespaces may be nested. Multiple
|
||||
libraries may contribute to the same namespace. In practice, packages may have
|
||||
namespaces such as `Testing` containing entities that benefit from an isolated
|
||||
space but are present in many libraries.
|
||||
Namespaces offer named paths for entities. Namespaces must be declared at file
|
||||
scope, and may be nested. Multiple libraries may contribute to the same
|
||||
namespace. In practice, packages may have namespaces such as `Testing`
|
||||
containing entities that benefit from an isolated space but are present in many
|
||||
libraries.
|
||||
|
||||
The `namespace` keyword's syntax may loosely be expressed as a regular
|
||||
expression:
|
||||
@@ -671,6 +673,54 @@ namespace Shapes;
|
||||
struct Shapes.Square { ... };
|
||||
```
|
||||
|
||||
#### Declaring namespace members
|
||||
|
||||
Namespace members may only be declared in the same name scope which was used to
|
||||
declare the namespace. For example:
|
||||
|
||||
```carbon
|
||||
namespace NS;
|
||||
|
||||
// ✅ Allowed: declaration is in file scope, which also declared `NS`.
|
||||
class NS.ClassT {
|
||||
// ❌ Error: A class body has its own name scope.
|
||||
var NS.a: i32 = 0;
|
||||
}
|
||||
|
||||
fn Function() {
|
||||
// ❌ Error: A function body has its own name scope.
|
||||
var NS.b: i32 = 1;
|
||||
}
|
||||
|
||||
// ✅ Allowed: declaration is in file scope, which also declared `NS`.
|
||||
namespace NS.MemberNS;
|
||||
|
||||
// ✅ Allowed: declaration is in file scope, which also declared `NS.MemberNS`.
|
||||
class NS.MemberNS.MemberClassT {}
|
||||
```
|
||||
|
||||
When multiple names are declared by binding patterns in the same pattern, all
|
||||
names must be in the same namespace. Because namespace members can only be
|
||||
declared in the same scope as the namespace, a namespace-qualified pattern
|
||||
binding can only be used in the pattern of a `var` or `let` declaration. For
|
||||
example:
|
||||
|
||||
```carbon
|
||||
namespace NS;
|
||||
|
||||
// ✅ Allowed: `a` and `b` use the default namespace.
|
||||
var (a: i32, b: i32) = (1, 2);
|
||||
|
||||
// ✅ Allowed: `c` and `d` are in the same namespace.
|
||||
var (NS.c: i32, NS.d: i32) = (3, 4);
|
||||
|
||||
// ❌ Error: `e` and `f` are not in the same namespace.
|
||||
var (e: i32, NS.f: i32) = (5, 6);
|
||||
```
|
||||
|
||||
This restriction only applies when declaring names in binding patterns, not
|
||||
other name uses in patterns.
|
||||
|
||||
#### Aliasing
|
||||
|
||||
Carbon's [alias keyword](/docs/design/aliases.md) will support aliasing
|
||||
@@ -969,6 +1019,10 @@ should be part of a larger testing plan.
|
||||
- Namespaces
|
||||
- [File-level namespaces](/proposals/p0107.md#file-level-namespaces)
|
||||
- [Scoped namespaces](/proposals/p0107.md#scoped-namespaces)
|
||||
- [Allow prefixing a tuple binding pattern with a namespace](/proposals/p3407.md#allow-prefixing-a-tuple-binding-pattern-with-a-namespace)
|
||||
- [Allow binding patterns to declare names in multiple namespaces](/proposals/p3407.md#allow-binding-patterns-to-declare-names-in-multiple-namespaces)
|
||||
- [Allow declaring names in namespaces not owned by the current scope](/proposals/p3407.md#allow-declaring-names-in-namespaces-not-owned-by-the-current-scope)
|
||||
- [Allow declaring namespaces in scopes other than the file scope](/proposals/p3407.md#allow-declaring-namespaces-in-scopes-other-than-the-file-scope)
|
||||
|
||||
## References
|
||||
|
||||
@@ -978,3 +1032,5 @@ should be part of a larger testing plan.
|
||||
[#2550: Simplified package declaration for the main package](https://github.com/carbon-language/carbon-lang/pull/2550)
|
||||
- Proposal
|
||||
[#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)
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
# Clarify name bindings in namespaces.
|
||||
|
||||
<!--
|
||||
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/3407)
|
||||
|
||||
<!-- toc -->
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [Abstract](#abstract)
|
||||
- [Problem](#problem)
|
||||
- [Background](#background)
|
||||
- [Proposal](#proposal)
|
||||
- [Rationale](#rationale)
|
||||
- [Alternatives considered](#alternatives-considered)
|
||||
- [Allow prefixing a tuple binding pattern with a namespace](#allow-prefixing-a-tuple-binding-pattern-with-a-namespace)
|
||||
- [Allow binding patterns to declare names in multiple namespaces](#allow-binding-patterns-to-declare-names-in-multiple-namespaces)
|
||||
- [Allow declaring names in namespaces not owned by the current scope](#allow-declaring-names-in-namespaces-not-owned-by-the-current-scope)
|
||||
- [Allow declaring namespaces in scopes other than the file scope](#allow-declaring-namespaces-in-scopes-other-than-the-file-scope)
|
||||
|
||||
<!-- tocstop -->
|
||||
|
||||
## Abstract
|
||||
|
||||
- Require namespace members be declared in the same name scope as the
|
||||
namespace is declared.
|
||||
- Declaring namespaces outside file scope is disallowed, so this means
|
||||
only at file scope.
|
||||
- Allow binding patterns to directly declare names in namespaces.
|
||||
- Disallow introducing bindings in different namespaces in the same pattern.
|
||||
|
||||
## Problem
|
||||
|
||||
While the trivial case of `class NS.C` seems to be supported as a consequence of
|
||||
proposal
|
||||
[#107: Code and name organization](https://github.com/carbon-language/carbon-lang/pull/107),
|
||||
it lacks detail. For example, there are a couple syntactic options when binding
|
||||
multiple names.
|
||||
|
||||
Also, there's no clear decision around code such as:
|
||||
|
||||
```carbon
|
||||
namespace NS;
|
||||
class ClassT {
|
||||
// Is this a class member accessed through `NS`, or a file scope member inside
|
||||
// `NS`? What is its lifetime?
|
||||
var NS.a: i32 = 0;
|
||||
}
|
||||
```
|
||||
|
||||
This proposal mainly aims to remove ambiguities.
|
||||
|
||||
## Background
|
||||
|
||||
Namespaces are covered in
|
||||
[code and name organization](/docs/design/code_and_name_organization/#namespaces).
|
||||
Binding patterns are covered in
|
||||
[pattern matching](/docs/design/pattern_matching.md#binding-patterns).
|
||||
|
||||
There's some discussion of `var` in
|
||||
[values, variables, and pointers](/docs/design/values.md), but it's specific to
|
||||
locals. That doesn't address other use cases, such as globals or member
|
||||
variables.
|
||||
|
||||
## Proposal
|
||||
|
||||
When used to declare names in binding patterns, as in `var` or `let`, all names
|
||||
must be in the same namespace. `namespace` members must be declared from within
|
||||
the same scope that declared the `namespace`.
|
||||
|
||||
There was uncertainty about whether namespaces could be declared outside of file
|
||||
scopes; for now, disallow it.
|
||||
|
||||
See the changes to
|
||||
[code and name organization](/docs/design/code_and_name_organization/#namespaces)
|
||||
for reference.
|
||||
|
||||
## Rationale
|
||||
|
||||
- [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write)
|
||||
|
||||
- Requiring that declarations of multiple names use `NS.a` syntax is
|
||||
consistent with the single variable case.
|
||||
- Requiring namespace members be declared while in the same name scope as
|
||||
the namespace itself makes lifetimes clearer.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
### Allow prefixing a tuple binding pattern with a namespace
|
||||
|
||||
We could use the namespace to prefix the binding tuple. For example:
|
||||
|
||||
```carbon
|
||||
var NS.(a: i32, b: i32) = (3, 4);
|
||||
```
|
||||
|
||||
It's rare that we would have a single statement declare multiple names. As a
|
||||
consequence, the separation of the namespace qualifier from the declared
|
||||
identifier might end up unique to this syntax. In that context, we prefer `NS.a`
|
||||
for consistency with other cases, such as `class NS.class`.
|
||||
|
||||
### Allow binding patterns to declare names in multiple namespaces
|
||||
|
||||
We could allow binding patterns to declare names in multiple namespaces. For
|
||||
example:
|
||||
|
||||
```carbon
|
||||
namespace NS;
|
||||
var (NS.a: i32, b: i32) = InitData();
|
||||
```
|
||||
|
||||
Mixing namespaces could be confusing: for example, `b` could be misunderstood to
|
||||
be declared in `NS`. We lack data that would demonstrate benefits to offset
|
||||
that.
|
||||
|
||||
We disallow mixing namespaces in a single declaration for simplicity.
|
||||
|
||||
### Allow declaring names in namespaces not owned by the current scope
|
||||
|
||||
We could allow declaring names in namespaces not owned by the current scope. For
|
||||
example:
|
||||
|
||||
```carbon
|
||||
namespace NS;
|
||||
class ClassT {
|
||||
var NS.val: i32;
|
||||
|
||||
class NS.ChildT {}
|
||||
}
|
||||
```
|
||||
|
||||
Here, `package.NS.val` would be a global, but `ClassT.NS.val` looks more like an
|
||||
instance member. It's also unclear whether `ClassT.NS.val` (or
|
||||
`instance.NS.val`) could be used to reference the produced variable, since `NS`
|
||||
is not inside `ClassT`'s name scope. The naming problems extend to non-binding
|
||||
declarations such as `NS.ChildT`, too.
|
||||
|
||||
Disallowing using namespaces to cross name scopes is consistent with rules that
|
||||
generally prevent declaring names in other name scopes, such as:
|
||||
|
||||
```carbon
|
||||
class A {
|
||||
class B {
|
||||
// `C` must be declared directly inside `A`.
|
||||
class A.C;
|
||||
}
|
||||
}
|
||||
|
||||
// `D` must be declared within `A`, even if separately defined.
|
||||
class A.D {}
|
||||
```
|
||||
|
||||
Both the `namespace` declaration and names declared within the `namespace` must
|
||||
be written in the same name scope. This avoids name lookup ambiguities, and
|
||||
builds consistency in name scope boundaries across declarations.
|
||||
|
||||
### Allow declaring namespaces in scopes other than the file scope
|
||||
|
||||
We could allow declaring namespaces in scopes other than the file scope. It's
|
||||
ambiguous what path should have resulted from proposal
|
||||
[#107: Code and name organization](https://github.com/carbon-language/carbon-lang/pull/107),
|
||||
although examples all focus on file scope, so other scopes weren't carefully
|
||||
considered.
|
||||
|
||||
It might prove useful in some situations. For example, perhaps a complex class
|
||||
would find a member namespace useful:
|
||||
|
||||
```carbon
|
||||
class Complex {
|
||||
namespace OptionSet1;
|
||||
class OptionSet1.MemberClassA;
|
||||
class OptionSet1.MemberClassB;
|
||||
|
||||
namespace OptionSet2;
|
||||
class OptionSet2.MemberClassC;
|
||||
class OptionSet2.MemberClassD;
|
||||
|
||||
namespace Vars;
|
||||
var Vars.a;
|
||||
}
|
||||
```
|
||||
|
||||
This proposal takes a stance against declaring namespaces other than the file
|
||||
scope because:
|
||||
|
||||
- Proposal
|
||||
[#107: Code and name organization](https://github.com/carbon-language/carbon-lang/pull/107)
|
||||
only mentions file scope namespaces, implicitly disallowing it in other
|
||||
scopes.
|
||||
- Disallowing namespaces in other scopes is consistent with C++.
|
||||
|
||||
If allowed, it would be necessary to decide whether `Complex.Vars.a` would have
|
||||
instance or global lifetime.
|
||||
|
||||
For now, namespaces may only be declared at file scope, which gives consistency
|
||||
with C++. This decision may be reevaluated in a future proposal.
|
||||
Reference in New Issue
Block a user