Ver Fonte

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>
Jon Ross-Perkins há 2 anos atrás
pai
commit
6c812db05c
2 ficheiros alterados com 261 adições e 4 exclusões
  1. 60 4
      docs/design/code_and_name_organization/README.md
  2. 201 0
      proposals/p3407.md

+ 60 - 4
docs/design/code_and_name_organization/README.md

@@ -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)

+ 201 - 0
proposals/p3407.md

@@ -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.