From 6af5930cdd21abcbb31045eb0819f044509eea1f Mon Sep 17 00:00:00 2001 From: jonmeow Date: Thu, 13 Oct 2022 20:24:56 +0000 Subject: [PATCH 01/11] Update name lookup with regards to #875. --- docs/design/classes.md | 73 ++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 39 deletions(-) diff --git a/docs/design/classes.md b/docs/design/classes.md index 5c1dc1eefb3e9..5c8d07c22c406 100644 --- a/docs/design/classes.md +++ b/docs/design/classes.md @@ -915,69 +915,63 @@ after any names needed to describe its type. #### Name lookup in member function definitions -When defining a member function lexically inline, we delay type checking of the -function body until the definition of the current type is complete. This means -that name lookup _for members of objects_ is also delayed. That means that you -can reference `me.F()` in a lexically inline method definition even before the -declaration of `F` in that class definition. However, other names still need to -be declared before they are used. This includes unqualified names, names within -namespaces, and names _for members of types_. +When defining a member function lexically inline, the body is deferred and +processed as if it appeared immediately after the end of the outermost enclosing +class, like in C++. -``` +For example, given a class with all inline function definitions: + +```carbon class Point { fn Distance[me: Self]() -> f32 { - // ✅ Allowed: `x` and `y` are names for members of an object, - // and so lookup is delayed until `type_of(me) == Self` is complete. return Math.Sqrt(me.x * me.x + me.y * me.y); } - fn CreatePolarInvalid(r: f32, theta: f32) -> Point { - // ❌ Forbidden: unqualified name used before declaration. - return Create(r * Math.Cos(theta), r * Math.Sin(theta)); - } - fn CreatePolarValid1(r: f32, theta: f32) -> Point { - // ❌ Forbidden: `Create` is not yet declared. + fn CreatePolar(r: f32, theta: f32) -> Point { return Point.Create(r * Math.Cos(theta), r * Math.Sin(theta)); } - fn CreatePolarValid2(r: f32, theta: f32) -> Point { - // ❌ Forbidden: `Create` is not yet declared. - return Self.Create(r * Math.Cos(theta), r * Math.Sin(theta)); - } fn Create(x: f32, y: f32) -> Point { - // ✅ Allowed: checking that conversion of `{.x: f32, .y: f32}` - // to `Point` is delayed until `Point` is complete. return {.x = x, .y = y}; } - fn CreateXEqualsY(xy: f32) -> Point { - // ✅ Allowed: `Create` is declared earlier. - return Create(xy, xy); - } + var x: f32; + var y: f32; +} +``` - fn CreateXAxis(x: f32) -> Point; +These are all parsed as if they were defined outside the class scope: - fn Angle[me: Self]() -> f32; +```carbon +class Point { + fn Distance[me: Self]() -> f32; + fn CreatePolar(r: f32, theta: f32) -> Point; + fn Create(x: f32, y: f32) -> Point; var x: f32; var y: f32; } -fn Point.CreateXAxis(x: f32) -> Point { - // ✅ Allowed: `Point` type is complete. - // Members of `Point` like `Create` are in scope. - return Create(x, 0); +fn Point.Distance[me: Self]() -> f32 { + return Math.Sqrt(me.x * me.x + me.y * me.y); +} + +fn Point.CreatePolar(r: f32, theta: f32) -> Point { + return Point.Create(r * Math.Cos(theta), r * Math.Sin(theta)); } -fn Point.Angle[me: Self]() -> f32 { - // ✅ Allowed: `Point` type is complete. - // Function is checked immediately. - return Math.ATan2(me.y, me.x); +fn Point.Create(x: f32, y: f32) -> Point { + return {.x = x, .y = y}; } ``` -**Note:** The details of name lookup are still being decided in issue -[#472: Open question: Calling functions defined later in the same file](https://github.com/carbon-language/carbon-lang/issues/472). +Additional details for name lookup are being asked as part of +[#2286: Clarifications for name lookup in classes](https://github.com/carbon-language/carbon-lang/issues/2286): + +- Should unqualified name lookup be supported? For example, `Create()` instead + of `Point.Create()`. +- Should instances be usable to access type members? For example, + `Point.Create(0, 0).Create(0,0)`. ### Nominal data classes @@ -2103,9 +2097,10 @@ the type of `U.x`." ## References - [#257: Initialization of memory and variables](https://github.com/carbon-language/carbon-lang/pull/257) -- [#561: Basic classes: use cases, struct literals, struct types, and future wor](https://github.com/carbon-language/carbon-lang/pull/561) +- [#561: Basic classes: use cases, struct literals, struct types, and future work](https://github.com/carbon-language/carbon-lang/pull/561) - [#722: Nominal classes and methods](https://github.com/carbon-language/carbon-lang/pull/722) - [#777: Inheritance](https://github.com/carbon-language/carbon-lang/pull/777) +- [#875: Principle: Information accumulation](https://github.com/carbon-language/carbon-lang/pull/875) - [#981: Implicit conversions for aggregates](https://github.com/carbon-language/carbon-lang/pull/981) - [#1154: Destructors](https://github.com/carbon-language/carbon-lang/pull/1154) - [#2107: Clarify rules around `Self` and `.Self`](https://github.com/carbon-language/carbon-lang/pull/2107) From 3e429c79955fe9829d02d746c6327a37d29910df Mon Sep 17 00:00:00 2001 From: jonmeow Date: Thu, 13 Oct 2022 21:01:06 +0000 Subject: [PATCH 02/11] Update classes.md --- docs/design/classes.md | 76 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/docs/design/classes.md b/docs/design/classes.md index 5c8d07c22c406..4af1817ef003d 100644 --- a/docs/design/classes.md +++ b/docs/design/classes.md @@ -75,6 +75,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [No `static` variables](#no-static-variables) - [Computed properties](#computed-properties) - [Interfaces implemented for data classes](#interfaces-implemented-for-data-classes) +- [Alternatives considered](#alternatives-considered) - [References](#references) @@ -2094,6 +2095,81 @@ that "`T` is comparable to `U` if they have the same field names in the same order, and for every field `x`, the type of `T.x` implements `ComparableTo` for the type of `U.x`." +## Alternatives considered + +- [#257: Initialization of memory and variables](https://github.com/carbon-language/carbon-lang/pull/257) + + - [Require compile-time-proven initialization](/proposals/p0257.md#require-compile-time-proven-initialization) + - [C/C++ uninitialized](/proposals/p0257.md#cc-uninitialized) + - [Allow passing unformed objects to parameters or returning them?](/proposals/p0257.md#allow-passing-unformed-objects-to-parameters-or-returning-them) + - [Allow assigning an unformed object to another unformed object?](/proposals/p0257.md#allow-assigning-an-unformed-object-to-another-unformed-object) + - [Fully destructive move (Rust)](/proposals/p0257.md#fully-destructive-move-rust) + - [Completely non-destructive move (C++)](/proposals/p0257.md#completely-non-destructive-move-c) + - [Named return variable in place of a return type](/proposals/p0257.md#named-return-variable-in-place-of-a-return-type) + - [Allow unformed members](/proposals/p0257.md#allow-unformed-members) + +- [#561: Basic classes: use cases, struct literals, struct types, and future work](https://github.com/carbon-language/carbon-lang/pull/561) + + - [Early proposal #98](https://github.com/carbon-language/carbon-lang/pull/98) + - [Interfaces implemented for anonymous data classes](/proposals/p0561.md#interfaces-implemented-for-anonymous-data-classes) + - [Access control](/proposals/p0561.md#access-control) + - [Introducer for structural data class types](/proposals/p0561.md#introducer-for-structural-data-class-types) + - [Terminology](/proposals/p0561.md#terminology) + +- [#722: Nominal classes and methods](https://github.com/carbon-language/carbon-lang/pull/722) + + - [Method syntax](/proposals/p0722.md#method-syntax) + - [Marking mutating methods at the call site](/proposals/p0722.md#marking-mutating-methods-at-the-call-site) + - [Differences between functions and methods](/proposals/p0722.md#differences-between-functions-and-methods) + - [Specifying linkage as part of the access modifier](/proposals/p0722.md#specifying-linkage-as-part-of-the-access-modifier) + - [Nominal data class](/proposals/p0722.md#nominal-data-class) + - [Let constants](/proposals/p0722.md#let-constants) + +- [#777: Inheritance](https://github.com/carbon-language/carbon-lang/pull/777) + + - [Classes are final by default](/proposals/p0777.md#classes-are-final-by-default) + - [Allow keywords to be written when they would have no effect](/proposals/p0777.md#allow-keywords-to-be-written-when-they-would-have-no-effect) + - [Different virtual override keywords](/proposals/p0777.md#different-virtual-override-keywords) + - [Different virtual override keyword placement](/proposals/p0777.md#different-virtual-override-keyword-placement) + - [Final methods](/proposals/p0777.md#final-methods) + - [Constructors](/proposals/p0777.md#constructors) + - [Implicit abstract classes](/proposals/p0777.md#implicit-abstract-classes) + - [No extensible objects with non-virtual destructors](/proposals/p0777.md#no-extensible-objects-with-non-virtual-destructors) + - [Separate "exact" and "or derived" variations on types](/proposals/p0777.md#separate-exact-and-or-derived-variations-on-types) + - [Separate "exact" and "or derived" variations on pointers](/proposals/p0777.md#separate-exact-and-or-derived-variations-on-pointers) + +- [#875: Principle: Information accumulation](https://github.com/carbon-language/carbon-lang/pull/875) + + - Allow information to be used before it is provided + [globally](/proposals/p0875.md#strict-global-consistency), + [within a file](/proposals/p0875.md#context-sensitive-local-consistency), + or + [within a top-level declaration](/proposals/p0875.md#top-down-with-minimally-deferred-type-checking). + - [Do not allow inline method bodies to use members before they are declared](/proposals/p0875.md#strict-top-down) + - [Do not allow separate declaration and definition](/proposals/p0875.md#disallow-separate-declaration-and-definition) + +- [#981: Implicit conversions for aggregates](https://github.com/carbon-language/carbon-lang/pull/981) + + - [Field order is not significant](/proposals/p0981.md#field-order-is-not-significant) + - [Different field orders are incompatible](/proposals/p0981.md#different-field-orders-are-incompatible) + - [Explicit instead of implicit conversions](/proposals/p0981.md#explicit-instead-of-implicit-conversions) + +- [#1154: Destructors](https://github.com/carbon-language/carbon-lang/pull/1154) + + - [Types implement destructor interface](/proposals/p1154.md#types-implement-destructor-interface) + - [Prevent virtual function calls in destructors](/proposals/p1154.md#prevent-virtual-function-calls-in-destructors) + - [Allow functions to act as destructors](/proposals/p1154.md#allow-functions-to-act-as-destructors) + - [Allow private destructors](/proposals/p1154.md#allow-private-destructors) + - [Allow multiple conditional destructors](/proposals/p1154.md#allow-multiple-conditional-destructors) + - [Type-of-type naming](/proposals/p1154.md#type-of-type-naming) + - [Other approaches to extensible classes without vtables](/proposals/p1154.md#other-approaches-to-extensible-classes-without-vtables) + +- [#2107: Clarify rules around `Self` and `.Self`](https://github.com/carbon-language/carbon-lang/pull/2107) + + - [`Self` not a keyword](/proposals/p2107.md#self-not-a-keyword) + - [Make `Self` a member of all types](/proposals/p2107.md#make-self-a-member-of-all-types) + - [`where` operator could be associative](/proposals/p2107.md#where-operator-could-be-associative) + ## References - [#257: Initialization of memory and variables](https://github.com/carbon-language/carbon-lang/pull/257) From 8b0d50cf4f829c4535c37cb9d0080ed56549bc89 Mon Sep 17 00:00:00 2001 From: jonmeow Date: Fri, 21 Oct 2022 17:41:35 +0000 Subject: [PATCH 03/11] Switch to proposal --- docs/design/classes.md | 96 +++++++++++++++++++++++++++------- proposals/p2287.md | 116 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 194 insertions(+), 18 deletions(-) create mode 100644 proposals/p2287.md diff --git a/docs/design/classes.md b/docs/design/classes.md index 4af1817ef003d..4dfb1c304f105 100644 --- a/docs/design/classes.md +++ b/docs/design/classes.md @@ -37,7 +37,8 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [Member functions](#member-functions) - [Class functions](#class-functions) - [Methods](#methods) - - [Name lookup in member function definitions](#name-lookup-in-member-function-definitions) + - [Deferred member function definitions](#deferred-member-function-definitions) + - [Name lookup in classes](#name-lookup-in-classes) - [Nominal data classes](#nominal-data-classes) - [Member type](#member-type) - [Let](#let) @@ -914,13 +915,13 @@ the `me` parameter must be in the same list in square brackets `[`...`]`. The `me` parameter may appear in any position in that list, as long as it appears after any names needed to describe its type. -#### Name lookup in member function definitions +#### Deferred member function definitions When defining a member function lexically inline, the body is deferred and processed as if it appeared immediately after the end of the outermost enclosing class, like in C++. -For example, given a class with all inline function definitions: +For example, given a class with inline function definitions: ```carbon class Point { @@ -928,10 +929,6 @@ class Point { return Math.Sqrt(me.x * me.x + me.y * me.y); } - fn CreatePolar(r: f32, theta: f32) -> Point { - return Point.Create(r * Math.Cos(theta), r * Math.Sin(theta)); - } - fn Create(x: f32, y: f32) -> Point { return {.x = x, .y = y}; } @@ -946,7 +943,6 @@ These are all parsed as if they were defined outside the class scope: ```carbon class Point { fn Distance[me: Self]() -> f32; - fn CreatePolar(r: f32, theta: f32) -> Point; fn Create(x: f32, y: f32) -> Point; var x: f32; @@ -957,22 +953,86 @@ fn Point.Distance[me: Self]() -> f32 { return Math.Sqrt(me.x * me.x + me.y * me.y); } -fn Point.CreatePolar(r: f32, theta: f32) -> Point { - return Point.Create(r * Math.Cos(theta), r * Math.Sin(theta)); -} - fn Point.Create(x: f32, y: f32) -> Point { return {.x = x, .y = y}; } ``` -Additional details for name lookup are being asked as part of -[#2286: Clarifications for name lookup in classes](https://github.com/carbon-language/carbon-lang/issues/2286): +#### Name lookup in classes + +[Member access](expressions/member_access.md) is an expression; details are +covered there. Because function definitions are +[deferred](#deferred-member-function-definitions), name lookup in classes works +the same regardless of whether a function is inline. The class body forms a +scope for name lookup, and function definitions can perform unqualified name +lookup within that scope. + +For example: + +```carbon +class Square { + fn GetArea[me: Self]() -> f32 { + // ✅ OK: performs name lookup on `me`. + return me.size * me.size; + // ❌ Error: an instance is required. + return size * size; + // ❌ Error: an instance is required. + return Square.size * Square.size; + } + + fn GetDoubled[me: Self]() -> Square { + // ✅ OK: performs name lookup on `Square` for `Create`. + return Square.Create(me.size); + // ✅ OK: performs unqualified name lookup within class scope for `Create`. + return Create(me.size); + // ✅ OK: performs name lookup on `me` for `Create`. + return me.Create(me.size); + } + + fn Create(size: f32) -> Square; + + var size: f32; +} +``` + +The example's name lookups refer to `Create` and `size` which are defined after +the example member access; this is valid because of +[deferred member function definitions](#deferred-member-function-definitions). -- Should unqualified name lookup be supported? For example, `Create()` instead - of `Point.Create()`. -- Should instances be usable to access type members? For example, - `Point.Create(0, 0).Create(0,0)`. +However, function signatures must still complete lookup without deferring. For +example: + +```carbon +class List { + // ❌ Error: `Iterator` has not yet been defined. + fn Iterate() -> Iterator*; + fn Iterate() -> Iterator; + + class Iterator; + + // ✅ OK: A forward declaration is sufficient to produce a pointer. + fn Iterate() -> Iterator*; + // ❌ Error: A forward declaration cannot be used to return a value. + fn Iterate() -> Iterator; + + class Iterator { + ... + } + + // ✅ OK: The definition of Iterator is now available. + fn Iterate() -> Iterator; +} +``` + +An out-of-line function definition's full signature is evaluated as if in-scope. +For example: + +```carbon +// ✅ OK: Performs unqualified name lookup into `List` for `Iterator`. +fn List.Iterate() -> Iterator { + ... +} +``` ### Nominal data classes diff --git a/proposals/p2287.md b/proposals/p2287.md new file mode 100644 index 0000000000000..a380a4afb58a6 --- /dev/null +++ b/proposals/p2287.md @@ -0,0 +1,116 @@ +# Allow unqualified name lookup for class members + + + +[Pull request](https://github.com/carbon-language/carbon-lang/pull/####) + + + +## Table of contents + +- [Abstract](#abstract) +- [Problem](#problem) +- [Background](#background) +- [Proposal](#proposal) + - [Namespaces](#namespaces) +- [Rationale](#rationale) +- [Alternatives considered](#alternatives-considered) + - [No unqualified lookup when defining outside a scope](#no-unqualified-lookup-when-defining-outside-a-scope) + + + +## Abstract + +Allow unqualified name lookup for class members, whether inside the class scope +or within an out-of-line function definition. + +## Problem + +[Member access](/docs/design/expressions/member_access.md) defines certain +member access behaviors. However, it doesn't cover what happens if an +unqualified name lookup occurs within a class, particularly for an out-of-line +member function definition. + +## Background + +The [member access design](/docs/design/expressions/member_access.md) and +[information accumulation principle](/docs/project/principles/information_accumulation.md) +affect this. + +This will also work similarly to +[unqualified lookup within C++](https://en.cppreference.com/w/cpp/language/unqualified_lookup). + +## Proposal + +Allow unqualified name lookup for class members. + +This proposal updates [the class design](/docs/design/classes.md) to address +this. + +### Namespaces + +More generally, this should also be true of other scopes used in declarations. +In particular, namespaces should also follow the same rule. However, since +[name lookup](/docs/design/name_lookup.md) has not been fleshed out, this won't +make much of an update to it. + +An example for namespaces would be: + +```carbon +namespace Foo; +var Foo.a: i32 = 0; + +class Foo.B {} + +// `b` and `a` are valid here because unqualified name lookup occurs within `Foo`. +fn Foo.C(B b) -> i32 { + return a; +} +``` + +## Rationale + +- [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) + - Performing unqualified name lookup for class members should be fairly + unsurprising to readers, and should allow for more concise code when + working within a namespace. +- [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) + - This behavior will be similar to how C++ works. + +## Alternatives considered + +### No unqualified lookup when defining outside a scope + +We could not support unqualified lookup when defining something that is +presented within the top-level scope of the file. + +Note this has subtle implications. If `Foo.C` in the namespace example is +considered to be outside the `Foo` scope for this purpose, it means the function +would need to look like: + +``` +fn Foo.C(Foo.B b) -> i32 { + return Foo.a; +} +``` + +It could also mean that, on a class, an inline declaration +`fn Foo() -> ClassMember` is valid, while an out-of-line definition +`fn Class.Foo() -> ClassMember` is not, requiring `Class.ClassMember`. + +Advantages: + +- Explicit in access. + - For example, name lookup results could be mildly confusing if both + `package.a` and `package.Foo.a` are defined but `package.Foo.a` is + hidden in code while `package.a` is easy to find. It's likely that + `package.Foo.a` would be considered unambiguous for unqualified name + lookup. + +Disadvantages: + +- Very verbose, and could prove un-ergonomic for developers. From d9ca40cf2351e7d2c86ccee58a4b388ac6de9ca8 Mon Sep 17 00:00:00 2001 From: jonmeow Date: Fri, 21 Oct 2022 17:45:42 +0000 Subject: [PATCH 04/11] links --- docs/design/classes.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/design/classes.md b/docs/design/classes.md index 4dfb1c304f105..97aa75c8a8418 100644 --- a/docs/design/classes.md +++ b/docs/design/classes.md @@ -2230,6 +2230,10 @@ the type of `U.x`." - [Make `Self` a member of all types](/proposals/p2107.md#make-self-a-member-of-all-types) - [`where` operator could be associative](/proposals/p2107.md#where-operator-could-be-associative) +- [#2287: Allow unqualified name lookup for class members](https://github.com/carbon-language/carbon-lang/pull/2287) + + - [No unqualified lookup when defining outside a scope](/proposals/p2287.md#no-unqualified-lookup-when-defining-outside-a-scope) + ## References - [#257: Initialization of memory and variables](https://github.com/carbon-language/carbon-lang/pull/257) @@ -2240,3 +2244,4 @@ the type of `U.x`." - [#981: Implicit conversions for aggregates](https://github.com/carbon-language/carbon-lang/pull/981) - [#1154: Destructors](https://github.com/carbon-language/carbon-lang/pull/1154) - [#2107: Clarify rules around `Self` and `.Self`](https://github.com/carbon-language/carbon-lang/pull/2107) +- [#2287: Allow unqualified name lookup for class members](https://github.com/carbon-language/carbon-lang/pull/2287) From 2204d53827f75603eb4229603c245f9e97a57036 Mon Sep 17 00:00:00 2001 From: jonmeow Date: Fri, 21 Oct 2022 22:24:59 +0000 Subject: [PATCH 05/11] More unqualified lookup --- docs/design/classes.md | 7 ++- proposals/p2287.md | 116 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 113 insertions(+), 10 deletions(-) diff --git a/docs/design/classes.md b/docs/design/classes.md index 97aa75c8a8418..234c27fa727ab 100644 --- a/docs/design/classes.md +++ b/docs/design/classes.md @@ -974,10 +974,15 @@ class Square { fn GetArea[me: Self]() -> f32 { // ✅ OK: performs name lookup on `me`. return me.size * me.size; - // ❌ Error: an instance is required. + // ❌ Error: finds `Square.size`, but an instance is required. return size * size; // ❌ Error: an instance is required. return Square.size * Square.size; + // ✅ OK: performs instance binding with `me`. + return me.(Square.size) * me.(Square.size); + // ✅ OK: uses unqualified name lookup to find `Square.size`, then performs + // instance binding with `me`. + return me.(size) * me.(size); } fn GetDoubled[me: Self]() -> Square { diff --git a/proposals/p2287.md b/proposals/p2287.md index a380a4afb58a6..8fca04b48f220 100644 --- a/proposals/p2287.md +++ b/proposals/p2287.md @@ -1,4 +1,4 @@ -# Allow unqualified name lookup for class members +# Allow unqualified name lookup