From 986b7e813f7c528c353399965e61887febe2f4ee Mon Sep 17 00:00:00 2001 From: Josh L Date: Fri, 11 Feb 2022 13:51:43 -0800 Subject: [PATCH 01/51] Filling out template with PR 1084 --- proposals/p1084.md | 62 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 proposals/p1084.md diff --git a/proposals/p1084.md b/proposals/p1084.md new file mode 100644 index 0000000000000..7899cea478990 --- /dev/null +++ b/proposals/p1084.md @@ -0,0 +1,62 @@ +# Generics details 9: forward declarations + + + +[Pull request](https://github.com/carbon-language/carbon-lang/pull/1084) + + + +## Table of contents + +- [Problem](#problem) +- [Background](#background) +- [Proposal](#proposal) +- [Details](#details) +- [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) +- [Alternatives considered](#alternatives-considered) + + + +## Problem + +TODO: What problem are you trying to solve? How important is that problem? Who +is impacted by it? + +## Background + +TODO: Is there any background that readers should consider to fully understand +this problem and your approach to solving it? + +## Proposal + +TODO: Briefly and at a high level, how do you propose to solve the problem? Why +will that in fact solve it? + +## Details + +TODO: Fully explain the details of the proposed solution. + +## Rationale based on Carbon's goals + +TODO: How does this proposal effectively advance Carbon's goals? Rather than +re-stating the full motivation, this should connect that motivation back to +Carbon's stated goals for the project or language. This may evolve during +review. Use links to appropriate goals, for example: + +- [Community and culture](/docs/project/goals.md#community-and-culture) +- [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) +- [Performance-critical software](/docs/project/goals.md#performance-critical-software) +- [Software and language evolution](/docs/project/goals.md#software-and-language-evolution) +- [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) +- [Practical safety and testing mechanisms](/docs/project/goals.md#practical-safety-and-testing-mechanisms) +- [Fast and scalable development](/docs/project/goals.md#fast-and-scalable-development) +- [Modern OS platforms, hardware architectures, and environments](/docs/project/goals.md#modern-os-platforms-hardware-architectures-and-environments) +- [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) + +## Alternatives considered + +TODO: What alternative solutions have you considered? From 821e99ae82a75c116b66f2a514bbd069847ed556 Mon Sep 17 00:00:00 2001 From: Josh L Date: Fri, 11 Feb 2022 15:03:56 -0800 Subject: [PATCH 02/51] Rough draft with the main points to make --- docs/design/generics/details.md | 133 ++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index 356da99dbf463..fe216ffd276c3 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -92,6 +92,10 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [`final` impls](#final-impls) - [Libraries that can contain `final` impls](#libraries-that-can-contain-final-impls) - [Comparison to Rust](#comparison-to-rust) +- [Forward declarations and cyclic references](#forward-declarations-and-cyclic-references) + - [Declaring interfaces](#declaring-interfaces) + - [Declaring implementations](#declaring-implementations) + - [Declaration examples](#declaration-examples) - [Interface members with definitions](#interface-members-with-definitions) - [Interface defaults](#interface-defaults) - [`final` members](#final-members) @@ -4277,6 +4281,135 @@ differences between the Carbon and Rust plans: ordering on type structures, picking one as higher priority even without one being more specific in the sense of only applying to a subset of types. +## Forward declarations and cyclic references + +Interfaces and their implementations may be forward declared and then later +defined. This is needed to allow cyclic references, for example when declaring +the edges and nodes of a graph. + +- declaration is the first part of a forward declaration and a definition +- forward declarations = declaration + `;` +- definition = declaration + `{` body `}` +- between the first declaration and the end of the definition the interface or + implementation is called "incomplete" + +### Declaring interfaces + +An interface may be declared earlier in a file before it is defined. + +- The definition must be in the same file as the declaration. +- Declaration includes the parameter list for the interface. +- declaration part of a forward declaration and the corresponding definition + must match +- The name of the interface may not be used until after the parameter list of + the declaration. In particular, may not use the name of the interface in the + parameter list. FIXME: Workaround. +- An incomplete interface may be used in constraints in declarations of types + or functions. +- Any name lookup into an incomplete interface is an error. For example, an + attempt to access a member of an interface using `MyInterface.MemberName` or + an attempt to define the body of a generic function using that interface as + a constraint is illegal. + +### Declaring implementations + +- The definition must be in the same library as the declaration. They must + either be in the same file, or the declaration can be in the API file and + the definition in an impl file. +- If there is both a forward declaration and a definition, only the first + declaration must specify the assignment of associated constants with a + `where` clause. If later declarations repeat the `where` clause, it must + match. +- The keyword `external`, when it precedes `impl`, is part of the declaration + of the implementation and must match between a forward declaration and + definition. +- You may forward declare an implementation of a defined interface but not an + incomplete interface. It may be for any declared type, whether it is + incomplete or defined. +- Every internal implementation must be declared (or defined) inside the scope + of the class definition. It may also be declared before or defined + afterwards. + +### Declaration examples + +``` +// Forward declaration of interfaces +interface Interface1; +interface Interface2; +interface Interface3; +interface Interface4; +interface Interface5; +interface Interface6; + +// Forward declaration of class type +class MyClass; + +// ❌ Illegal: Can't declare implementation of incomplete interface. +// external impl MyClass as Interface1; + +// Definition of interfaces that were previously declared +interface Interface1 { + let T1:! Type; +} +interface Interface2 { + let T2:! Type; +} +interface Interface3 { + let T3:! Type; +} +interface Interface4 { + let T4:! Type; +} + +// Forward declaration of external implementations +external impl MyClass as Interface1 where .T1 = i32; +external impl MyClass as Interface2 where .T2 = bool; + +// Forward declaration of an internal implementation +impl MyClass as Interface3 where .T3 = f32; +impl MyClass as Interface4 where .T4 = String; + +interface Interface5 { + let T5:! Type; +} +interface Interface6 { + let T6:! Type; +} + +// Definition of the previously declared class type +class MyClass { + // Definition of previously declared external impl. + // Note: no need to repeat assignments to associated constants. + external impl as Interface1 { } + + // Definition of previously declared internal impl. + // Note: allowed but not required to repeat `where` clause. + impl as Interface3 where .T3 = f32 { } + + // Redeclaration of previously declared internal impl. + // Every internal implementation must be declared in + // the class definition. + impl as Interface4; + + // Forward declaration of external implementation. + external impl MyClass as Interface5 where .T5 = u64; + + // Forward declaration of internal implementation. + impl MyClass as Interface6 where .T6 = u8; +} + +// It would be legal to move the following definitions from +// the API file to the implementation file for this library. + +// Definition of previously declared external impls. +external impl MyClass as Interface2 { } +external impl MyClass as Interface5 { } + +// Definition of previously declared internal impls. +impl MyClass as Interface4 { } +impl MyClass as Interface6 { } +``` + ## Interface members with definitions Interfaces may provide definitions for members, such as a function body for an From 881a3291dc6a4f9743c91a49e85a74adf324a987 Mon Sep 17 00:00:00 2001 From: Josh L Date: Fri, 11 Feb 2022 16:31:12 -0800 Subject: [PATCH 03/51] Update examples --- docs/design/generics/details.md | 55 ++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index fe216ffd276c3..4c449524bfd72 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -4344,7 +4344,8 @@ interface Interface6; // Forward declaration of class type class MyClass; -// ❌ Illegal: Can't declare implementation of incomplete interface. +// ❌ Illegal: Can't declare implementation of incomplete +// interface. // external impl MyClass as Interface1; // Definition of interfaces that were previously declared @@ -4379,11 +4380,13 @@ interface Interface6 { // Definition of the previously declared class type class MyClass { // Definition of previously declared external impl. - // Note: no need to repeat assignments to associated constants. + // Note: no need to repeat assignments to associated + // constants. external impl as Interface1 { } // Definition of previously declared internal impl. - // Note: allowed but not required to repeat `where` clause. + // Note: allowed but not required to repeat `where` + // clause. impl as Interface3 where .T3 = f32 { } // Redeclaration of previously declared internal impl. @@ -4398,8 +4401,9 @@ class MyClass { impl MyClass as Interface6 where .T6 = u8; } -// It would be legal to move the following definitions from -// the API file to the implementation file for this library. +// It would be legal to move the following definitions +// from the API file to the implementation file for +// this library. // Definition of previously declared external impls. external impl MyClass as Interface2 { } @@ -4410,6 +4414,47 @@ impl MyClass as Interface4 { } impl MyClass as Interface6 { } ``` +Cyclic reference: + +``` +// Forward declaration of interface +interface EdgeInterface; + +// Definition that only uses the declaration of `Edge`, +// not its definition. +interface NodeBootstrap { + let EdgeType:! EdgeInterface; + fn Edges[me: Self]() -> Vector(EdgeType); +} + +// Now can define `EdgeInterface` in terms of +// `NodeBootstrap`. +interface EdgeInterface { + let NodeType:! NodeBootstrap where .EdgeType == Self; + fn Head[me: Self]() -> NodeType; +} + +// Make `NodeInterface` a named constraint defined in +// terms of `NodeBootstrap`, adding in constraints that +// couldn't be written until `EdgeInterface` was defined. +constraint NodeInterface { + extends NodeBootsrap where .EdgeType.NodeType == Self; +} +``` + +Work around for the restriction about not being able to name an interface in its +parameter list. + +``` +// Want to require that `T` satisfies `CommonType(Self)`, +// but that can't be done in the parameter list. +interface CommonType(T:! Type) { + let Result:! Type; + // Instead add the requirement inside the definition. + impl T as CommonType(Self); +} +``` + ## Interface members with definitions Interfaces may provide definitions for members, such as a function body for an From 3107b8c8d73b8903392614099c01f271ced1574e Mon Sep 17 00:00:00 2001 From: Josh L Date: Mon, 14 Feb 2022 15:46:48 -0800 Subject: [PATCH 04/51] Mark defaults with `default` so they may be declared separately --- docs/design/generics/details.md | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index 4c449524bfd72..2041a78265933 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -4459,8 +4459,9 @@ interface CommonType(T:! Type) { Interfaces may provide definitions for members, such as a function body for an associated function or method or a value for an associated constant. If these -definitions may be overridden in implementations, they are called "defaults." -Otherwise they are called "final members." +definitions may be overridden in implementations, they are called "defaults" and +prefixed with the `default` keyword. Otherwise they are called "final members" +and prefixed with the `final` keyword. ### Interface defaults @@ -4472,12 +4473,25 @@ interface Vector { fn Add[me: Self](b: Self) -> Self; fn Scale[me: Self](v: f64) -> Self; // Default definition of `Invert` calls `Scale`. - fn Invert[me: Self]() -> Self { + default fn Invert[me: Self]() -> Self { return me.Scale(-1.0); } } ``` +A default function or method may also be defined out of line: + +``` +interface Vector { + fn Add[me: Self](b: Self) -> Self; + fn Scale[me: Self](v: f64) -> Self; + default fn Invert[me: Self]() -> Self; +} +fn Vector.Invert[me: Self]() -> Self { + return me.Scale(-1.0); +} +``` + An impl of that interface for a type may omit a definition of `Invert` to use the default, or provide a definition to override the default. @@ -4493,7 +4507,7 @@ types, and interface parameters, using the `= ` syntax. ``` interface Add(Right:! Type = Self) { - let Result:! Type = Self; + default let Result:! Type = Self; fn DoAdd[me: Self](right: Right) -> Result; } @@ -4523,7 +4537,7 @@ More generally, default expressions may reference other associated types or ``` interface Iterator { let Element:! Type; - let Pointer:! Type = Element*; + default let Pointer:! Type = Element*; } ``` From 26ccbc7a2ad95db858e4cc72062d6a6bfb4908a5 Mon Sep 17 00:00:00 2001 From: Josh L Date: Tue, 15 Feb 2022 09:18:37 -0800 Subject: [PATCH 05/51] Out-of-line definition of final members --- docs/design/generics/details.md | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index 2041a78265933..ec4e1cc3beef9 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -4546,11 +4546,11 @@ interface. ``` interface TotalOrder { - fn TotalLess[me: Self](right: Self) -> Bool; + fn TotalLess[me: Self](right: Self) -> bool; // ❌ Illegal: May not provide definition // for required interface. impl PartialOrder { - fn PartialLess[me: Self](right: Self) -> Bool { + fn PartialLess[me: Self](right: Self) -> bool { return me.TotalLess(right); } } @@ -4562,12 +4562,12 @@ instead: ``` interface TotalOrder { - fn TotalLess[me: Self](right: Self) -> Bool; + fn TotalLess[me: Self](right: Self) -> bool; impl PartialOrder; } external impl [T:! TotalOrder] T as PartialOrder { - fn PartialLess[me: Self](right: Self) -> Bool { + fn PartialLess[me: Self](right: Self) -> bool { return me.TotalLess(right); } } @@ -4591,18 +4591,18 @@ overridden in impls. ``` interface TotalOrder { - fn TotalLess[me: Self](right: Self) -> Bool; - final fn TotalGreater[me: Self](right: Self) -> Bool { + fn TotalLess[me: Self](right: Self) -> bool; + final fn TotalGreater[me: Self](right: Self) -> bool { return right.TotalLess(me); } } class String { impl as TotalOrder { - fn TotalLess[me: Self](right: Self) -> Bool { ... } + fn TotalLess[me: Self](right: Self) -> bool { ... } // ❌ Illegal: May not provide definition of final // method `TotalGreater`. - fn TotalGreater[me: Self](right: Self) -> Bool { ... } + fn TotalGreater[me: Self](right: Self) -> bool { ... } } } @@ -4615,6 +4615,18 @@ interface Add(T:! Type = Self) { } ``` +Final members may also be defined out-of-line: + +``` +interface TotalOrder { + fn TotalLess[me: Self](right: Self) -> bool; + final fn TotalGreater[me: Self](right: Self) -> bool; +} +fn TotalOrder.TotalGreater[me: Self](right: Self) -> bool { + return right.TotalLess(me); +} +``` + There are a few reasons for this feature: - When overriding would be inappropriate. From b6641e17da6baee84c6dbdccb870417b010f33c6 Mon Sep 17 00:00:00 2001 From: Josh L Date: Tue, 15 Feb 2022 11:32:55 -0800 Subject: [PATCH 06/51] Impls definitions for incomplete types --- docs/design/generics/details.md | 9 ++++++++- proposals/p1084.md | 8 ++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index ec4e1cc3beef9..b518e9fe8b27d 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -4328,7 +4328,13 @@ An interface may be declared earlier in a file before it is defined. incomplete or defined. - Every internal implementation must be declared (or defined) inside the scope of the class definition. It may also be declared before or defined - afterwards. + afterwards. Note that the class itself is incomplete in the scope of the + class definition, but member function bodies defined inline are processed as + if they appeared immediately after the end of the outermost enclosing class, + see + [question-for-leads issue #472](https://github.com/carbon-language/carbon-lang/issues/472) + and + [proposal #875: Principle: information accumulation](https://github.com/carbon-language/carbon-lang/pull/875). ### Declaration examples @@ -4385,6 +4391,7 @@ class MyClass { external impl as Interface1 { } // Definition of previously declared internal impl. + // Note: allowed even though `MyClass` is incomplete. // Note: allowed but not required to repeat `where` // clause. impl as Interface3 where .T3 = f32 { } diff --git a/proposals/p1084.md b/proposals/p1084.md index 7899cea478990..4861d8b4cdbf7 100644 --- a/proposals/p1084.md +++ b/proposals/p1084.md @@ -60,3 +60,11 @@ review. Use links to appropriate goals, for example: ## Alternatives considered TODO: What alternative solutions have you considered? + +FIXME: Forbid forward declarations of impls for incomplete types + +FIXME: Allow external impl declarations to be redeclared as internal + +FIXME: No `default` keyword, so default definitions would always have to be +inline, see issue +[#1082: Use `default` keyword in interface defaults?](https://github.com/carbon-language/carbon-lang/issues/1082) From ff75bfaf555879af57bdf04999583117073053d0 Mon Sep 17 00:00:00 2001 From: Josh L Date: Tue, 15 Feb 2022 13:57:21 -0800 Subject: [PATCH 07/51] Work on proposal text --- proposals/p1084.md | 67 ++++++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/proposals/p1084.md b/proposals/p1084.md index 4861d8b4cdbf7..7d2fc55deace9 100644 --- a/proposals/p1084.md +++ b/proposals/p1084.md @@ -15,7 +15,6 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) -- [Details](#details) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) @@ -23,39 +22,48 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ## Problem -TODO: What problem are you trying to solve? How important is that problem? Who -is impacted by it? +Developers want to organize their code for readability and convenience. For +example, they may want to present the public API of their type in a concise way. +That includes the ability to say a type implements an interface without +repeating the full contents of that interface. + +The Carbon compiler can give better diagnostics if it can assume every +identifier it encounters refers to something earlier declaration in the file. +However, sometimes multiple entities will reference each other in a cycle so no +one entity can be defined first. ## Background -TODO: Is there any background that readers should consider to fully understand -this problem and your approach to solving it? +We have decided to tackle these problems in a manner similar to C++ by +supporting forward declarations: -## Proposal +- [issue #472: Open question: Calling functions defined later in the same file](https://github.com/carbon-language/carbon-lang/issues/472) +- [proposal #875: Principle: information accumulation](https://github.com/carbon-language/carbon-lang/pull/875). -TODO: Briefly and at a high level, how do you propose to solve the problem? Why -will that in fact solve it? +Use of the `default` keyword in `interface` definitions to allow defaulted +members to be defined out-of-line was originally proposed in +[withdrawn proposal #1034](https://github.com/carbon-language/carbon-lang/pull/1034). -## Details +## Proposal -TODO: Fully explain the details of the proposed solution. +This proposal makes changes to these sections of the +[generics details design document](/docs/design/generics/details.md): + +- [Forward declarations and cyclic references](/docs/design/generics/details.md#forward-declarations-and-cyclic-references) + section added +- [Interface members with definitions](/docs/design/generics/details.md#interface-members-with-definitions) + section added to ## Rationale based on Carbon's goals -TODO: How does this proposal effectively advance Carbon's goals? Rather than -re-stating the full motivation, this should connect that motivation back to -Carbon's stated goals for the project or language. This may evolve during -review. Use links to appropriate goals, for example: - -- [Community and culture](/docs/project/goals.md#community-and-culture) -- [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) -- [Performance-critical software](/docs/project/goals.md#performance-critical-software) -- [Software and language evolution](/docs/project/goals.md#software-and-language-evolution) -- [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) -- [Practical safety and testing mechanisms](/docs/project/goals.md#practical-safety-and-testing-mechanisms) -- [Fast and scalable development](/docs/project/goals.md#fast-and-scalable-development) -- [Modern OS platforms, hardware architectures, and environments](/docs/project/goals.md#modern-os-platforms-hardware-architectures-and-environments) -- [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) +Forward declarations are intended to advance these goals: + +- [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem), + by making Carbon easier to interpret by code in a single top-down pass. +- [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write), + by allowing developers to separate declaration from definition when + organizing the presentation of their code, and imposing constraints that + allow readers to interpret the code with less skipping around. ## Alternatives considered @@ -66,5 +74,12 @@ FIXME: Forbid forward declarations of impls for incomplete types FIXME: Allow external impl declarations to be redeclared as internal FIXME: No `default` keyword, so default definitions would always have to be -inline, see issue -[#1082: Use `default` keyword in interface defaults?](https://github.com/carbon-language/carbon-lang/issues/1082) +inline, see discussion in +[the #syntax channel on Discord](https://discord.com/channels/655572317891461132/709488742942900284/941408009689641010) +and +[question-for-leads issue #1082: Use `default` keyword in interface defaults?](https://github.com/carbon-language/carbon-lang/issues/1082) + +FIXME: For simplicity, generally Carbon entities should either be "incomplete" +or "defined" and never "partially defined". We make an exception for interface +implementations, due to their role as the +[one static open extension mechanism](https://github.com/carbon-language/carbon-lang/pull/998). From ff823d55b8903be6b9712e64ae3819d419f55488 Mon Sep 17 00:00:00 2001 From: Josh L Date: Tue, 15 Feb 2022 14:20:17 -0800 Subject: [PATCH 08/51] Finish first pass of design --- docs/design/generics/details.md | 62 +++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index b518e9fe8b27d..2e0f6aa9b002c 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -96,6 +96,8 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [Declaring interfaces](#declaring-interfaces) - [Declaring implementations](#declaring-implementations) - [Declaration examples](#declaration-examples) + - [Example of declaring interfaces with cyclic references](#example-of-declaring-interfaces-with-cyclic-references) + - [Interfaces with parameters constrained by the same interface](#interfaces-with-parameters-constrained-by-the-same-interface) - [Interface members with definitions](#interface-members-with-definitions) - [Interface defaults](#interface-defaults) - [`final` members](#final-members) @@ -4285,25 +4287,33 @@ differences between the Carbon and Rust plans: Interfaces and their implementations may be forward declared and then later defined. This is needed to allow cyclic references, for example when declaring -the edges and nodes of a graph. - -- declaration is the first part of a forward declaration and a definition -- forward declarations = declaration + `;` -- definition = declaration + `{` body `}` -- between the first declaration and the end of the definition the interface or - implementation is called "incomplete" +the edges and nodes of a graph. It is also a tool that may be used to make code +more readable. + +The syntax described in the [interface](#interfaces) and +[implementation](#implementing-interfaces) sections described the syntax for +their _definition_, which consists of a declaration followed by a body contained +in curly braces `{` ... `}`. A _forward declaration_ is a declaration followed +by a semicolon `;`. A forward declaration is a promise that the entity being +declared will be defined later. Between the first declaration of an entity, +which may be in a forward declaration or the first part of a definition, and the +end of the definition the interface or implementation is called _incomplete_. +There are additional restrictions on how the name of an incomplete entity may be +used. ### Declaring interfaces -An interface may be declared earlier in a file before it is defined. +An interface may be forward declared subject to these rules: - The definition must be in the same file as the declaration. -- Declaration includes the parameter list for the interface. -- declaration part of a forward declaration and the corresponding definition - must match -- The name of the interface may not be used until after the parameter list of - the declaration. In particular, may not use the name of the interface in the - parameter list. FIXME: Workaround. +- The declaration includes the parameter list for the interface. +- The declaration part of a forward declaration and the corresponding + definition must match. +- The name of the interface must not be used until after the parameter list of + the declaration. In particular, it is illegal to use the name of the + interface in the parameter list. There is a + [workaround](#interfaces-with-parameters-constrained-by-the-same-interface) + for the use cases when this would come up. - An incomplete interface may be used in constraints in declarations of types or functions. - Any name lookup into an incomplete interface is an error. For example, an @@ -4313,12 +4323,15 @@ An interface may be declared earlier in a file before it is defined. ### Declaring implementations +An implementation of an interface for a type may be forward declared subject to +these rules: + - The definition must be in the same library as the declaration. They must either be in the same file, or the declaration can be in the API file and the definition in an impl file. - If there is both a forward declaration and a definition, only the first declaration must specify the assignment of associated constants with a - `where` clause. If later declarations repeat the `where` clause, it must + `where` clause. If a later declaration repeats the `where` clause, it must match. - The keyword `external`, when it precedes `impl`, is part of the declaration of the implementation and must match between a forward declaration and @@ -4327,11 +4340,11 @@ An interface may be declared earlier in a file before it is defined. incomplete interface. It may be for any declared type, whether it is incomplete or defined. - Every internal implementation must be declared (or defined) inside the scope - of the class definition. It may also be declared before or defined - afterwards. Note that the class itself is incomplete in the scope of the - class definition, but member function bodies defined inline are processed as - if they appeared immediately after the end of the outermost enclosing class, - see + of the class definition. It may also be declared before the class definition + or defined afterwards. Note that the class itself is incomplete in the scope + of the class definition, but member function bodies defined inline are + processed as if they appeared immediately after the end of the outermost + enclosing class, see [question-for-leads issue #472](https://github.com/carbon-language/carbon-lang/issues/472) and [proposal #875: Principle: information accumulation](https://github.com/carbon-language/carbon-lang/pull/875). @@ -4421,7 +4434,7 @@ impl MyClass as Interface4 { } impl MyClass as Interface6 { } ``` -Cyclic reference: +### Example of declaring interfaces with cyclic references ``` // Forward declaration of interface @@ -4449,8 +4462,11 @@ constraint NodeInterface { } ``` -Work around for the restriction about not being able to name an interface in its -parameter list. +### Interfaces with parameters constrained by the same interface + +To work around +[the restriction about not being able to name an interface in its parameter list](#declaring-interfaces), +instead include that requirement in the body of the interface. ``` // Want to require that `T` satisfies `CommonType(Self)`, From e6e7c32095a012a42f7c4d33bee2d5fa188f31f2 Mon Sep 17 00:00:00 2001 From: Josh L Date: Tue, 15 Feb 2022 16:13:31 -0800 Subject: [PATCH 09/51] Polish examples --- docs/design/generics/details.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index 2e0f6aa9b002c..b2a20e6aa5bd0 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -4436,12 +4436,17 @@ impl MyClass as Interface6 { } ### Example of declaring interfaces with cyclic references +In this example, `NodeInterface` has an `EdgeType` associated type that is +constrained to implement `EdgeInterface`, and `EdgeInterface` has a `NodeType` +associated type that is constrained to implement `NodeInterface`. Furthermore, +The `NodeType` of an `EdgeType` is the original type, and the other way around. + ``` // Forward declaration of interface interface EdgeInterface; -// Definition that only uses the declaration of `Edge`, -// not its definition. +// Definition that only uses the declaration of +// `EdgeInterface`, not its definition. interface NodeBootstrap { let EdgeType:! EdgeInterface; fn Edges[me: Self]() -> Vector(EdgeType); @@ -4510,6 +4515,8 @@ interface Vector { fn Scale[me: Self](v: f64) -> Self; default fn Invert[me: Self]() -> Self; } +// `Vector` is considered complete at this point, +// even though `Vector.Invert` is still incomplete. fn Vector.Invert[me: Self]() -> Self { return me.Scale(-1.0); } @@ -4645,6 +4652,8 @@ interface TotalOrder { fn TotalLess[me: Self](right: Self) -> bool; final fn TotalGreater[me: Self](right: Self) -> bool; } +// `TotalOrder` is considered complete at this point, even +// though `TotalOrder.TotalGreater` is still incomplete. fn TotalOrder.TotalGreater[me: Self](right: Self) -> bool { return right.TotalLess(me); } From a1981670569e202f2570c0175a7a051615cf62bb Mon Sep 17 00:00:00 2001 From: Josh L Date: Tue, 15 Feb 2022 16:32:37 -0800 Subject: [PATCH 10/51] Fix up alternatives --- proposals/p1084.md | 51 +++++++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/proposals/p1084.md b/proposals/p1084.md index 7d2fc55deace9..8c7705dbf5b3e 100644 --- a/proposals/p1084.md +++ b/proposals/p1084.md @@ -17,6 +17,9 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [Proposal](#proposal) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) + - [No `default` keyword on interface members](#no-default-keyword-on-interface-members) + - [Declaring an implementation of an incomplete interface](#declaring-an-implementation-of-an-incomplete-interface) + - [No implementations for incomplete types](#no-implementations-for-incomplete-types) @@ -67,19 +70,39 @@ Forward declarations are intended to advance these goals: ## Alternatives considered -TODO: What alternative solutions have you considered? +### No `default` keyword on interface members -FIXME: Forbid forward declarations of impls for incomplete types - -FIXME: Allow external impl declarations to be redeclared as internal - -FIXME: No `default` keyword, so default definitions would always have to be -inline, see discussion in +Without the `default` keyword, default definitions would always have to be +inline. We discussed this in [the #syntax channel on Discord](https://discord.com/channels/655572317891461132/709488742942900284/941408009689641010) -and -[question-for-leads issue #1082: Use `default` keyword in interface defaults?](https://github.com/carbon-language/carbon-lang/issues/1082) - -FIXME: For simplicity, generally Carbon entities should either be "incomplete" -or "defined" and never "partially defined". We make an exception for interface -implementations, due to their role as the -[one static open extension mechanism](https://github.com/carbon-language/carbon-lang/pull/998). +which eventually led to the +[question-for-leads issue #1082: Use `default` keyword in interface defaults?](https://github.com/carbon-language/carbon-lang/issues/1082). + +The conclusion was that we did want to support forward declarations of default +interface members. To make it so that users would have a single place to look to +see whether the member had a definition even when it might be out of line, we +decided to use a `default` keyword as a prefix of the declaration. We considered +putting the keyword at the end of the declaration, but we decided it was more +readable if it wasn't next to the return type. It was also more consistent with +`final`, and alternative to `default`, which also now supports forward +declaration. + +### Declaring an implementation of an incomplete interface + +We did not have any use cases for forward declaring an impl of an incomplete +interface, and so we took the conservative position of forbidding that. We could +add this feature in the future if use cases were found, but clearly we can't +have impl definitions until the interface is defined. + +### No implementations for incomplete types + +For simplicity, generally Carbon entities should either be "incomplete" or +"defined" and never "partially defined". However, the set of interfaces +implemented for a type is by necessity only ever partially known by the nature +of being the +[one static open extension mechanism](https://github.com/carbon-language/carbon-lang/pull/998) +in Carbon. As a result, we felt there was more leeway for implementing +interfaces for incomplete types. This happens incidentally when implementing the +interface inline in the scope of a class definition. We also wanted to allow it +in the case where there was only a forward declaration of the type in an API +file. From 7f4d8031dc4637db3f23221c54b09988c0a42201 Mon Sep 17 00:00:00 2001 From: Josh L Date: Fri, 11 Feb 2022 13:51:43 -0800 Subject: [PATCH 11/51] Filling out template with PR 1084 --- proposals/p1084.md | 62 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 proposals/p1084.md diff --git a/proposals/p1084.md b/proposals/p1084.md new file mode 100644 index 0000000000000..7899cea478990 --- /dev/null +++ b/proposals/p1084.md @@ -0,0 +1,62 @@ +# Generics details 9: forward declarations + + + +[Pull request](https://github.com/carbon-language/carbon-lang/pull/1084) + + + +## Table of contents + +- [Problem](#problem) +- [Background](#background) +- [Proposal](#proposal) +- [Details](#details) +- [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) +- [Alternatives considered](#alternatives-considered) + + + +## Problem + +TODO: What problem are you trying to solve? How important is that problem? Who +is impacted by it? + +## Background + +TODO: Is there any background that readers should consider to fully understand +this problem and your approach to solving it? + +## Proposal + +TODO: Briefly and at a high level, how do you propose to solve the problem? Why +will that in fact solve it? + +## Details + +TODO: Fully explain the details of the proposed solution. + +## Rationale based on Carbon's goals + +TODO: How does this proposal effectively advance Carbon's goals? Rather than +re-stating the full motivation, this should connect that motivation back to +Carbon's stated goals for the project or language. This may evolve during +review. Use links to appropriate goals, for example: + +- [Community and culture](/docs/project/goals.md#community-and-culture) +- [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) +- [Performance-critical software](/docs/project/goals.md#performance-critical-software) +- [Software and language evolution](/docs/project/goals.md#software-and-language-evolution) +- [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) +- [Practical safety and testing mechanisms](/docs/project/goals.md#practical-safety-and-testing-mechanisms) +- [Fast and scalable development](/docs/project/goals.md#fast-and-scalable-development) +- [Modern OS platforms, hardware architectures, and environments](/docs/project/goals.md#modern-os-platforms-hardware-architectures-and-environments) +- [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) + +## Alternatives considered + +TODO: What alternative solutions have you considered? From 90aa39034b3fc5f8c9a211d738c28434d94b14dd Mon Sep 17 00:00:00 2001 From: Josh L Date: Fri, 11 Feb 2022 15:03:56 -0800 Subject: [PATCH 12/51] Rough draft with the main points to make --- docs/design/generics/details.md | 133 ++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index 356da99dbf463..fe216ffd276c3 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -92,6 +92,10 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [`final` impls](#final-impls) - [Libraries that can contain `final` impls](#libraries-that-can-contain-final-impls) - [Comparison to Rust](#comparison-to-rust) +- [Forward declarations and cyclic references](#forward-declarations-and-cyclic-references) + - [Declaring interfaces](#declaring-interfaces) + - [Declaring implementations](#declaring-implementations) + - [Declaration examples](#declaration-examples) - [Interface members with definitions](#interface-members-with-definitions) - [Interface defaults](#interface-defaults) - [`final` members](#final-members) @@ -4277,6 +4281,135 @@ differences between the Carbon and Rust plans: ordering on type structures, picking one as higher priority even without one being more specific in the sense of only applying to a subset of types. +## Forward declarations and cyclic references + +Interfaces and their implementations may be forward declared and then later +defined. This is needed to allow cyclic references, for example when declaring +the edges and nodes of a graph. + +- declaration is the first part of a forward declaration and a definition +- forward declarations = declaration + `;` +- definition = declaration + `{` body `}` +- between the first declaration and the end of the definition the interface or + implementation is called "incomplete" + +### Declaring interfaces + +An interface may be declared earlier in a file before it is defined. + +- The definition must be in the same file as the declaration. +- Declaration includes the parameter list for the interface. +- declaration part of a forward declaration and the corresponding definition + must match +- The name of the interface may not be used until after the parameter list of + the declaration. In particular, may not use the name of the interface in the + parameter list. FIXME: Workaround. +- An incomplete interface may be used in constraints in declarations of types + or functions. +- Any name lookup into an incomplete interface is an error. For example, an + attempt to access a member of an interface using `MyInterface.MemberName` or + an attempt to define the body of a generic function using that interface as + a constraint is illegal. + +### Declaring implementations + +- The definition must be in the same library as the declaration. They must + either be in the same file, or the declaration can be in the API file and + the definition in an impl file. +- If there is both a forward declaration and a definition, only the first + declaration must specify the assignment of associated constants with a + `where` clause. If later declarations repeat the `where` clause, it must + match. +- The keyword `external`, when it precedes `impl`, is part of the declaration + of the implementation and must match between a forward declaration and + definition. +- You may forward declare an implementation of a defined interface but not an + incomplete interface. It may be for any declared type, whether it is + incomplete or defined. +- Every internal implementation must be declared (or defined) inside the scope + of the class definition. It may also be declared before or defined + afterwards. + +### Declaration examples + +``` +// Forward declaration of interfaces +interface Interface1; +interface Interface2; +interface Interface3; +interface Interface4; +interface Interface5; +interface Interface6; + +// Forward declaration of class type +class MyClass; + +// ❌ Illegal: Can't declare implementation of incomplete interface. +// external impl MyClass as Interface1; + +// Definition of interfaces that were previously declared +interface Interface1 { + let T1:! Type; +} +interface Interface2 { + let T2:! Type; +} +interface Interface3 { + let T3:! Type; +} +interface Interface4 { + let T4:! Type; +} + +// Forward declaration of external implementations +external impl MyClass as Interface1 where .T1 = i32; +external impl MyClass as Interface2 where .T2 = bool; + +// Forward declaration of an internal implementation +impl MyClass as Interface3 where .T3 = f32; +impl MyClass as Interface4 where .T4 = String; + +interface Interface5 { + let T5:! Type; +} +interface Interface6 { + let T6:! Type; +} + +// Definition of the previously declared class type +class MyClass { + // Definition of previously declared external impl. + // Note: no need to repeat assignments to associated constants. + external impl as Interface1 { } + + // Definition of previously declared internal impl. + // Note: allowed but not required to repeat `where` clause. + impl as Interface3 where .T3 = f32 { } + + // Redeclaration of previously declared internal impl. + // Every internal implementation must be declared in + // the class definition. + impl as Interface4; + + // Forward declaration of external implementation. + external impl MyClass as Interface5 where .T5 = u64; + + // Forward declaration of internal implementation. + impl MyClass as Interface6 where .T6 = u8; +} + +// It would be legal to move the following definitions from +// the API file to the implementation file for this library. + +// Definition of previously declared external impls. +external impl MyClass as Interface2 { } +external impl MyClass as Interface5 { } + +// Definition of previously declared internal impls. +impl MyClass as Interface4 { } +impl MyClass as Interface6 { } +``` + ## Interface members with definitions Interfaces may provide definitions for members, such as a function body for an From 36f1de529e3f4cad6fbc3e0e2d69f6f1d0f997cb Mon Sep 17 00:00:00 2001 From: Josh L Date: Fri, 11 Feb 2022 16:31:12 -0800 Subject: [PATCH 13/51] Update examples --- docs/design/generics/details.md | 55 ++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index fe216ffd276c3..4c449524bfd72 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -4344,7 +4344,8 @@ interface Interface6; // Forward declaration of class type class MyClass; -// ❌ Illegal: Can't declare implementation of incomplete interface. +// ❌ Illegal: Can't declare implementation of incomplete +// interface. // external impl MyClass as Interface1; // Definition of interfaces that were previously declared @@ -4379,11 +4380,13 @@ interface Interface6 { // Definition of the previously declared class type class MyClass { // Definition of previously declared external impl. - // Note: no need to repeat assignments to associated constants. + // Note: no need to repeat assignments to associated + // constants. external impl as Interface1 { } // Definition of previously declared internal impl. - // Note: allowed but not required to repeat `where` clause. + // Note: allowed but not required to repeat `where` + // clause. impl as Interface3 where .T3 = f32 { } // Redeclaration of previously declared internal impl. @@ -4398,8 +4401,9 @@ class MyClass { impl MyClass as Interface6 where .T6 = u8; } -// It would be legal to move the following definitions from -// the API file to the implementation file for this library. +// It would be legal to move the following definitions +// from the API file to the implementation file for +// this library. // Definition of previously declared external impls. external impl MyClass as Interface2 { } @@ -4410,6 +4414,47 @@ impl MyClass as Interface4 { } impl MyClass as Interface6 { } ``` +Cyclic reference: + +``` +// Forward declaration of interface +interface EdgeInterface; + +// Definition that only uses the declaration of `Edge`, +// not its definition. +interface NodeBootstrap { + let EdgeType:! EdgeInterface; + fn Edges[me: Self]() -> Vector(EdgeType); +} + +// Now can define `EdgeInterface` in terms of +// `NodeBootstrap`. +interface EdgeInterface { + let NodeType:! NodeBootstrap where .EdgeType == Self; + fn Head[me: Self]() -> NodeType; +} + +// Make `NodeInterface` a named constraint defined in +// terms of `NodeBootstrap`, adding in constraints that +// couldn't be written until `EdgeInterface` was defined. +constraint NodeInterface { + extends NodeBootsrap where .EdgeType.NodeType == Self; +} +``` + +Work around for the restriction about not being able to name an interface in its +parameter list. + +``` +// Want to require that `T` satisfies `CommonType(Self)`, +// but that can't be done in the parameter list. +interface CommonType(T:! Type) { + let Result:! Type; + // Instead add the requirement inside the definition. + impl T as CommonType(Self); +} +``` + ## Interface members with definitions Interfaces may provide definitions for members, such as a function body for an From 3adddb4e90bf91828e2a1bd0437625aa3ffc4576 Mon Sep 17 00:00:00 2001 From: Josh L Date: Mon, 14 Feb 2022 15:46:48 -0800 Subject: [PATCH 14/51] Mark defaults with `default` so they may be declared separately --- docs/design/generics/details.md | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index 4c449524bfd72..2041a78265933 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -4459,8 +4459,9 @@ interface CommonType(T:! Type) { Interfaces may provide definitions for members, such as a function body for an associated function or method or a value for an associated constant. If these -definitions may be overridden in implementations, they are called "defaults." -Otherwise they are called "final members." +definitions may be overridden in implementations, they are called "defaults" and +prefixed with the `default` keyword. Otherwise they are called "final members" +and prefixed with the `final` keyword. ### Interface defaults @@ -4472,12 +4473,25 @@ interface Vector { fn Add[me: Self](b: Self) -> Self; fn Scale[me: Self](v: f64) -> Self; // Default definition of `Invert` calls `Scale`. - fn Invert[me: Self]() -> Self { + default fn Invert[me: Self]() -> Self { return me.Scale(-1.0); } } ``` +A default function or method may also be defined out of line: + +``` +interface Vector { + fn Add[me: Self](b: Self) -> Self; + fn Scale[me: Self](v: f64) -> Self; + default fn Invert[me: Self]() -> Self; +} +fn Vector.Invert[me: Self]() -> Self { + return me.Scale(-1.0); +} +``` + An impl of that interface for a type may omit a definition of `Invert` to use the default, or provide a definition to override the default. @@ -4493,7 +4507,7 @@ types, and interface parameters, using the `= ` syntax. ``` interface Add(Right:! Type = Self) { - let Result:! Type = Self; + default let Result:! Type = Self; fn DoAdd[me: Self](right: Right) -> Result; } @@ -4523,7 +4537,7 @@ More generally, default expressions may reference other associated types or ``` interface Iterator { let Element:! Type; - let Pointer:! Type = Element*; + default let Pointer:! Type = Element*; } ``` From a58824196d4f8d2ee8be703bb70b3decdf4d8aa0 Mon Sep 17 00:00:00 2001 From: Josh L Date: Tue, 15 Feb 2022 09:18:37 -0800 Subject: [PATCH 15/51] Out-of-line definition of final members --- docs/design/generics/details.md | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index 2041a78265933..ec4e1cc3beef9 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -4546,11 +4546,11 @@ interface. ``` interface TotalOrder { - fn TotalLess[me: Self](right: Self) -> Bool; + fn TotalLess[me: Self](right: Self) -> bool; // ❌ Illegal: May not provide definition // for required interface. impl PartialOrder { - fn PartialLess[me: Self](right: Self) -> Bool { + fn PartialLess[me: Self](right: Self) -> bool { return me.TotalLess(right); } } @@ -4562,12 +4562,12 @@ instead: ``` interface TotalOrder { - fn TotalLess[me: Self](right: Self) -> Bool; + fn TotalLess[me: Self](right: Self) -> bool; impl PartialOrder; } external impl [T:! TotalOrder] T as PartialOrder { - fn PartialLess[me: Self](right: Self) -> Bool { + fn PartialLess[me: Self](right: Self) -> bool { return me.TotalLess(right); } } @@ -4591,18 +4591,18 @@ overridden in impls. ``` interface TotalOrder { - fn TotalLess[me: Self](right: Self) -> Bool; - final fn TotalGreater[me: Self](right: Self) -> Bool { + fn TotalLess[me: Self](right: Self) -> bool; + final fn TotalGreater[me: Self](right: Self) -> bool { return right.TotalLess(me); } } class String { impl as TotalOrder { - fn TotalLess[me: Self](right: Self) -> Bool { ... } + fn TotalLess[me: Self](right: Self) -> bool { ... } // ❌ Illegal: May not provide definition of final // method `TotalGreater`. - fn TotalGreater[me: Self](right: Self) -> Bool { ... } + fn TotalGreater[me: Self](right: Self) -> bool { ... } } } @@ -4615,6 +4615,18 @@ interface Add(T:! Type = Self) { } ``` +Final members may also be defined out-of-line: + +``` +interface TotalOrder { + fn TotalLess[me: Self](right: Self) -> bool; + final fn TotalGreater[me: Self](right: Self) -> bool; +} +fn TotalOrder.TotalGreater[me: Self](right: Self) -> bool { + return right.TotalLess(me); +} +``` + There are a few reasons for this feature: - When overriding would be inappropriate. From 029faa71ea2a3eba652f0f9f3ecd887eaf4b5d1c Mon Sep 17 00:00:00 2001 From: Josh L Date: Tue, 15 Feb 2022 11:32:55 -0800 Subject: [PATCH 16/51] Impls definitions for incomplete types --- docs/design/generics/details.md | 9 ++++++++- proposals/p1084.md | 8 ++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index ec4e1cc3beef9..b518e9fe8b27d 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -4328,7 +4328,13 @@ An interface may be declared earlier in a file before it is defined. incomplete or defined. - Every internal implementation must be declared (or defined) inside the scope of the class definition. It may also be declared before or defined - afterwards. + afterwards. Note that the class itself is incomplete in the scope of the + class definition, but member function bodies defined inline are processed as + if they appeared immediately after the end of the outermost enclosing class, + see + [question-for-leads issue #472](https://github.com/carbon-language/carbon-lang/issues/472) + and + [proposal #875: Principle: information accumulation](https://github.com/carbon-language/carbon-lang/pull/875). ### Declaration examples @@ -4385,6 +4391,7 @@ class MyClass { external impl as Interface1 { } // Definition of previously declared internal impl. + // Note: allowed even though `MyClass` is incomplete. // Note: allowed but not required to repeat `where` // clause. impl as Interface3 where .T3 = f32 { } diff --git a/proposals/p1084.md b/proposals/p1084.md index 7899cea478990..4861d8b4cdbf7 100644 --- a/proposals/p1084.md +++ b/proposals/p1084.md @@ -60,3 +60,11 @@ review. Use links to appropriate goals, for example: ## Alternatives considered TODO: What alternative solutions have you considered? + +FIXME: Forbid forward declarations of impls for incomplete types + +FIXME: Allow external impl declarations to be redeclared as internal + +FIXME: No `default` keyword, so default definitions would always have to be +inline, see issue +[#1082: Use `default` keyword in interface defaults?](https://github.com/carbon-language/carbon-lang/issues/1082) From a57e37e486de8e336deca3a8808c5b9607e02c7f Mon Sep 17 00:00:00 2001 From: Josh L Date: Tue, 15 Feb 2022 13:57:21 -0800 Subject: [PATCH 17/51] Work on proposal text --- proposals/p1084.md | 67 ++++++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/proposals/p1084.md b/proposals/p1084.md index 4861d8b4cdbf7..7d2fc55deace9 100644 --- a/proposals/p1084.md +++ b/proposals/p1084.md @@ -15,7 +15,6 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) -- [Details](#details) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) @@ -23,39 +22,48 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ## Problem -TODO: What problem are you trying to solve? How important is that problem? Who -is impacted by it? +Developers want to organize their code for readability and convenience. For +example, they may want to present the public API of their type in a concise way. +That includes the ability to say a type implements an interface without +repeating the full contents of that interface. + +The Carbon compiler can give better diagnostics if it can assume every +identifier it encounters refers to something earlier declaration in the file. +However, sometimes multiple entities will reference each other in a cycle so no +one entity can be defined first. ## Background -TODO: Is there any background that readers should consider to fully understand -this problem and your approach to solving it? +We have decided to tackle these problems in a manner similar to C++ by +supporting forward declarations: -## Proposal +- [issue #472: Open question: Calling functions defined later in the same file](https://github.com/carbon-language/carbon-lang/issues/472) +- [proposal #875: Principle: information accumulation](https://github.com/carbon-language/carbon-lang/pull/875). -TODO: Briefly and at a high level, how do you propose to solve the problem? Why -will that in fact solve it? +Use of the `default` keyword in `interface` definitions to allow defaulted +members to be defined out-of-line was originally proposed in +[withdrawn proposal #1034](https://github.com/carbon-language/carbon-lang/pull/1034). -## Details +## Proposal -TODO: Fully explain the details of the proposed solution. +This proposal makes changes to these sections of the +[generics details design document](/docs/design/generics/details.md): + +- [Forward declarations and cyclic references](/docs/design/generics/details.md#forward-declarations-and-cyclic-references) + section added +- [Interface members with definitions](/docs/design/generics/details.md#interface-members-with-definitions) + section added to ## Rationale based on Carbon's goals -TODO: How does this proposal effectively advance Carbon's goals? Rather than -re-stating the full motivation, this should connect that motivation back to -Carbon's stated goals for the project or language. This may evolve during -review. Use links to appropriate goals, for example: - -- [Community and culture](/docs/project/goals.md#community-and-culture) -- [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem) -- [Performance-critical software](/docs/project/goals.md#performance-critical-software) -- [Software and language evolution](/docs/project/goals.md#software-and-language-evolution) -- [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write) -- [Practical safety and testing mechanisms](/docs/project/goals.md#practical-safety-and-testing-mechanisms) -- [Fast and scalable development](/docs/project/goals.md#fast-and-scalable-development) -- [Modern OS platforms, hardware architectures, and environments](/docs/project/goals.md#modern-os-platforms-hardware-architectures-and-environments) -- [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code) +Forward declarations are intended to advance these goals: + +- [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem), + by making Carbon easier to interpret by code in a single top-down pass. +- [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write), + by allowing developers to separate declaration from definition when + organizing the presentation of their code, and imposing constraints that + allow readers to interpret the code with less skipping around. ## Alternatives considered @@ -66,5 +74,12 @@ FIXME: Forbid forward declarations of impls for incomplete types FIXME: Allow external impl declarations to be redeclared as internal FIXME: No `default` keyword, so default definitions would always have to be -inline, see issue -[#1082: Use `default` keyword in interface defaults?](https://github.com/carbon-language/carbon-lang/issues/1082) +inline, see discussion in +[the #syntax channel on Discord](https://discord.com/channels/655572317891461132/709488742942900284/941408009689641010) +and +[question-for-leads issue #1082: Use `default` keyword in interface defaults?](https://github.com/carbon-language/carbon-lang/issues/1082) + +FIXME: For simplicity, generally Carbon entities should either be "incomplete" +or "defined" and never "partially defined". We make an exception for interface +implementations, due to their role as the +[one static open extension mechanism](https://github.com/carbon-language/carbon-lang/pull/998). From 451bfb5d9a278f6bfc530b8c7abbe001acf375c7 Mon Sep 17 00:00:00 2001 From: Josh L Date: Tue, 15 Feb 2022 14:20:17 -0800 Subject: [PATCH 18/51] Finish first pass of design --- docs/design/generics/details.md | 62 +++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index b518e9fe8b27d..2e0f6aa9b002c 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -96,6 +96,8 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [Declaring interfaces](#declaring-interfaces) - [Declaring implementations](#declaring-implementations) - [Declaration examples](#declaration-examples) + - [Example of declaring interfaces with cyclic references](#example-of-declaring-interfaces-with-cyclic-references) + - [Interfaces with parameters constrained by the same interface](#interfaces-with-parameters-constrained-by-the-same-interface) - [Interface members with definitions](#interface-members-with-definitions) - [Interface defaults](#interface-defaults) - [`final` members](#final-members) @@ -4285,25 +4287,33 @@ differences between the Carbon and Rust plans: Interfaces and their implementations may be forward declared and then later defined. This is needed to allow cyclic references, for example when declaring -the edges and nodes of a graph. - -- declaration is the first part of a forward declaration and a definition -- forward declarations = declaration + `;` -- definition = declaration + `{` body `}` -- between the first declaration and the end of the definition the interface or - implementation is called "incomplete" +the edges and nodes of a graph. It is also a tool that may be used to make code +more readable. + +The syntax described in the [interface](#interfaces) and +[implementation](#implementing-interfaces) sections described the syntax for +their _definition_, which consists of a declaration followed by a body contained +in curly braces `{` ... `}`. A _forward declaration_ is a declaration followed +by a semicolon `;`. A forward declaration is a promise that the entity being +declared will be defined later. Between the first declaration of an entity, +which may be in a forward declaration or the first part of a definition, and the +end of the definition the interface or implementation is called _incomplete_. +There are additional restrictions on how the name of an incomplete entity may be +used. ### Declaring interfaces -An interface may be declared earlier in a file before it is defined. +An interface may be forward declared subject to these rules: - The definition must be in the same file as the declaration. -- Declaration includes the parameter list for the interface. -- declaration part of a forward declaration and the corresponding definition - must match -- The name of the interface may not be used until after the parameter list of - the declaration. In particular, may not use the name of the interface in the - parameter list. FIXME: Workaround. +- The declaration includes the parameter list for the interface. +- The declaration part of a forward declaration and the corresponding + definition must match. +- The name of the interface must not be used until after the parameter list of + the declaration. In particular, it is illegal to use the name of the + interface in the parameter list. There is a + [workaround](#interfaces-with-parameters-constrained-by-the-same-interface) + for the use cases when this would come up. - An incomplete interface may be used in constraints in declarations of types or functions. - Any name lookup into an incomplete interface is an error. For example, an @@ -4313,12 +4323,15 @@ An interface may be declared earlier in a file before it is defined. ### Declaring implementations +An implementation of an interface for a type may be forward declared subject to +these rules: + - The definition must be in the same library as the declaration. They must either be in the same file, or the declaration can be in the API file and the definition in an impl file. - If there is both a forward declaration and a definition, only the first declaration must specify the assignment of associated constants with a - `where` clause. If later declarations repeat the `where` clause, it must + `where` clause. If a later declaration repeats the `where` clause, it must match. - The keyword `external`, when it precedes `impl`, is part of the declaration of the implementation and must match between a forward declaration and @@ -4327,11 +4340,11 @@ An interface may be declared earlier in a file before it is defined. incomplete interface. It may be for any declared type, whether it is incomplete or defined. - Every internal implementation must be declared (or defined) inside the scope - of the class definition. It may also be declared before or defined - afterwards. Note that the class itself is incomplete in the scope of the - class definition, but member function bodies defined inline are processed as - if they appeared immediately after the end of the outermost enclosing class, - see + of the class definition. It may also be declared before the class definition + or defined afterwards. Note that the class itself is incomplete in the scope + of the class definition, but member function bodies defined inline are + processed as if they appeared immediately after the end of the outermost + enclosing class, see [question-for-leads issue #472](https://github.com/carbon-language/carbon-lang/issues/472) and [proposal #875: Principle: information accumulation](https://github.com/carbon-language/carbon-lang/pull/875). @@ -4421,7 +4434,7 @@ impl MyClass as Interface4 { } impl MyClass as Interface6 { } ``` -Cyclic reference: +### Example of declaring interfaces with cyclic references ``` // Forward declaration of interface @@ -4449,8 +4462,11 @@ constraint NodeInterface { } ``` -Work around for the restriction about not being able to name an interface in its -parameter list. +### Interfaces with parameters constrained by the same interface + +To work around +[the restriction about not being able to name an interface in its parameter list](#declaring-interfaces), +instead include that requirement in the body of the interface. ``` // Want to require that `T` satisfies `CommonType(Self)`, From 91ed3e5caec0de6356a59a27d3ddd157ca94fd52 Mon Sep 17 00:00:00 2001 From: Josh L Date: Tue, 15 Feb 2022 16:13:31 -0800 Subject: [PATCH 19/51] Polish examples --- docs/design/generics/details.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index 2e0f6aa9b002c..b2a20e6aa5bd0 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -4436,12 +4436,17 @@ impl MyClass as Interface6 { } ### Example of declaring interfaces with cyclic references +In this example, `NodeInterface` has an `EdgeType` associated type that is +constrained to implement `EdgeInterface`, and `EdgeInterface` has a `NodeType` +associated type that is constrained to implement `NodeInterface`. Furthermore, +The `NodeType` of an `EdgeType` is the original type, and the other way around. + ``` // Forward declaration of interface interface EdgeInterface; -// Definition that only uses the declaration of `Edge`, -// not its definition. +// Definition that only uses the declaration of +// `EdgeInterface`, not its definition. interface NodeBootstrap { let EdgeType:! EdgeInterface; fn Edges[me: Self]() -> Vector(EdgeType); @@ -4510,6 +4515,8 @@ interface Vector { fn Scale[me: Self](v: f64) -> Self; default fn Invert[me: Self]() -> Self; } +// `Vector` is considered complete at this point, +// even though `Vector.Invert` is still incomplete. fn Vector.Invert[me: Self]() -> Self { return me.Scale(-1.0); } @@ -4645,6 +4652,8 @@ interface TotalOrder { fn TotalLess[me: Self](right: Self) -> bool; final fn TotalGreater[me: Self](right: Self) -> bool; } +// `TotalOrder` is considered complete at this point, even +// though `TotalOrder.TotalGreater` is still incomplete. fn TotalOrder.TotalGreater[me: Self](right: Self) -> bool { return right.TotalLess(me); } From 289017303219ab005f7993db1be31ef2fafe02d5 Mon Sep 17 00:00:00 2001 From: Josh L Date: Tue, 15 Feb 2022 16:32:37 -0800 Subject: [PATCH 20/51] Fix up alternatives --- proposals/p1084.md | 51 +++++++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/proposals/p1084.md b/proposals/p1084.md index 7d2fc55deace9..8c7705dbf5b3e 100644 --- a/proposals/p1084.md +++ b/proposals/p1084.md @@ -17,6 +17,9 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [Proposal](#proposal) - [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals) - [Alternatives considered](#alternatives-considered) + - [No `default` keyword on interface members](#no-default-keyword-on-interface-members) + - [Declaring an implementation of an incomplete interface](#declaring-an-implementation-of-an-incomplete-interface) + - [No implementations for incomplete types](#no-implementations-for-incomplete-types) @@ -67,19 +70,39 @@ Forward declarations are intended to advance these goals: ## Alternatives considered -TODO: What alternative solutions have you considered? +### No `default` keyword on interface members -FIXME: Forbid forward declarations of impls for incomplete types - -FIXME: Allow external impl declarations to be redeclared as internal - -FIXME: No `default` keyword, so default definitions would always have to be -inline, see discussion in +Without the `default` keyword, default definitions would always have to be +inline. We discussed this in [the #syntax channel on Discord](https://discord.com/channels/655572317891461132/709488742942900284/941408009689641010) -and -[question-for-leads issue #1082: Use `default` keyword in interface defaults?](https://github.com/carbon-language/carbon-lang/issues/1082) - -FIXME: For simplicity, generally Carbon entities should either be "incomplete" -or "defined" and never "partially defined". We make an exception for interface -implementations, due to their role as the -[one static open extension mechanism](https://github.com/carbon-language/carbon-lang/pull/998). +which eventually led to the +[question-for-leads issue #1082: Use `default` keyword in interface defaults?](https://github.com/carbon-language/carbon-lang/issues/1082). + +The conclusion was that we did want to support forward declarations of default +interface members. To make it so that users would have a single place to look to +see whether the member had a definition even when it might be out of line, we +decided to use a `default` keyword as a prefix of the declaration. We considered +putting the keyword at the end of the declaration, but we decided it was more +readable if it wasn't next to the return type. It was also more consistent with +`final`, and alternative to `default`, which also now supports forward +declaration. + +### Declaring an implementation of an incomplete interface + +We did not have any use cases for forward declaring an impl of an incomplete +interface, and so we took the conservative position of forbidding that. We could +add this feature in the future if use cases were found, but clearly we can't +have impl definitions until the interface is defined. + +### No implementations for incomplete types + +For simplicity, generally Carbon entities should either be "incomplete" or +"defined" and never "partially defined". However, the set of interfaces +implemented for a type is by necessity only ever partially known by the nature +of being the +[one static open extension mechanism](https://github.com/carbon-language/carbon-lang/pull/998) +in Carbon. As a result, we felt there was more leeway for implementing +interfaces for incomplete types. This happens incidentally when implementing the +interface inline in the scope of a class definition. We also wanted to allow it +in the case where there was only a forward declaration of the type in an API +file. From 369010f09194dfa8c6e8e0580b4961e34aeca167 Mon Sep 17 00:00:00 2001 From: Josh L Date: Wed, 16 Feb 2022 15:15:45 -0800 Subject: [PATCH 21/51] Remove future work, add reference to this proposal --- docs/design/generics/details.md | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index b2a20e6aa5bd0..9216322a49ce1 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -118,7 +118,6 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [Bridge for C++ customization points](#bridge-for-c-customization-points) - [Variadic arguments](#variadic-arguments) - [Range constraints on generic integers](#range-constraints-on-generic-integers) - - [Separate declaration and definition of impl](#separate-declaration-and-definition-of-impl) - [References](#references) @@ -4780,14 +4779,6 @@ between multiple generic integer parameters. For example, if `J < K` and secondary syntactic concern about how to write this kind of constraint on a parameter, as opposed to an associated type, as in `N:! u32 where ___ >= 2`. -### Separate declaration and definition of impl - -There is a desire to support a short declaration that a type implements an -interface without giving a full definition of that implementation for API files. -Everything needed for type checking is provided in the interface definition, -except for the assignments to associated constants and types, and so those must -be included in the declaration as well. - ## References - [#553: Generics details part 1](https://github.com/carbon-language/carbon-lang/pull/553) @@ -4799,3 +4790,4 @@ be included in the declaration as well. - [#983: Generic details 7: final impls](https://github.com/carbon-language/carbon-lang/pull/983) - [#990: Generics details 8: interface default and final members](https://github.com/carbon-language/carbon-lang/pull/990) - [#1013: Generics: Set associated constants using where constraints](https://github.com/carbon-language/carbon-lang/pull/1013) +- [#1084: Generics details 9: forward declarations](https://github.com/carbon-language/carbon-lang/pull/1084) From 0837099fbf407e0a5f279dc910104d718a5aebee Mon Sep 17 00:00:00 2001 From: Josh L Date: Wed, 16 Feb 2022 15:19:16 -0800 Subject: [PATCH 22/51] impl -> impl as --- docs/design/generics/details.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index 9216322a49ce1..1aea88acf4c17 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -4578,7 +4578,7 @@ interface TotalOrder { fn TotalLess[me: Self](right: Self) -> bool; // ❌ Illegal: May not provide definition // for required interface. - impl PartialOrder { + impl as PartialOrder { fn PartialLess[me: Self](right: Self) -> bool { return me.TotalLess(right); } @@ -4592,7 +4592,7 @@ instead: ``` interface TotalOrder { fn TotalLess[me: Self](right: Self) -> bool; - impl PartialOrder; + impl as PartialOrder; } external impl [T:! TotalOrder] T as PartialOrder { From 221824a8ee10c039e4fb0cbcc566847e277194ca Mon Sep 17 00:00:00 2001 From: Josh L Date: Tue, 22 Feb 2022 12:05:38 -0800 Subject: [PATCH 23/51] Impls must be declared before being queried --- docs/design/generics/details.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index 1aea88acf4c17..d63a81212df42 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -4347,6 +4347,10 @@ these rules: [question-for-leads issue #472](https://github.com/carbon-language/carbon-lang/issues/472) and [proposal #875: Principle: information accumulation](https://github.com/carbon-language/carbon-lang/pull/875). +- For [coherence](goals.md#coherence), we require that any impl that matches + an [impl lookup](#impl-lookup) query in the same file, must be declared + before the query. This can be done with a definition or a forward + declaration. ### Declaration examples From 081c07edfb2166ede9f5d6d4429d73cec47826ba Mon Sep 17 00:00:00 2001 From: josh11b Date: Mon, 28 Feb 2022 10:01:38 -0800 Subject: [PATCH 24/51] Apply suggestions from code review Co-authored-by: Richard Smith --- docs/design/generics/details.md | 6 +++--- proposals/p1084.md | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index d63a81212df42..d145616ab29bf 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -4289,8 +4289,8 @@ defined. This is needed to allow cyclic references, for example when declaring the edges and nodes of a graph. It is also a tool that may be used to make code more readable. -The syntax described in the [interface](#interfaces) and -[implementation](#implementing-interfaces) sections described the syntax for +The [interface](#interfaces) and +[implementation](#implementing-interfaces) sections describe the syntax for their _definition_, which consists of a declaration followed by a body contained in curly braces `{` ... `}`. A _forward declaration_ is a declaration followed by a semicolon `;`. A forward declaration is a promise that the entity being @@ -4442,7 +4442,7 @@ impl MyClass as Interface6 { } In this example, `NodeInterface` has an `EdgeType` associated type that is constrained to implement `EdgeInterface`, and `EdgeInterface` has a `NodeType` associated type that is constrained to implement `NodeInterface`. Furthermore, -The `NodeType` of an `EdgeType` is the original type, and the other way around. +the `NodeType` of an `EdgeType` is the original type, and the other way around. ``` // Forward declaration of interface diff --git a/proposals/p1084.md b/proposals/p1084.md index 8c7705dbf5b3e..8d78828beada1 100644 --- a/proposals/p1084.md +++ b/proposals/p1084.md @@ -31,7 +31,7 @@ That includes the ability to say a type implements an interface without repeating the full contents of that interface. The Carbon compiler can give better diagnostics if it can assume every -identifier it encounters refers to something earlier declaration in the file. +identifier it encounters refers to some earlier declaration in the file. However, sometimes multiple entities will reference each other in a cycle so no one entity can be defined first. @@ -84,7 +84,7 @@ see whether the member had a definition even when it might be out of line, we decided to use a `default` keyword as a prefix of the declaration. We considered putting the keyword at the end of the declaration, but we decided it was more readable if it wasn't next to the return type. It was also more consistent with -`final`, and alternative to `default`, which also now supports forward +`final`, an alternative to `default`, which also now supports forward declaration. ### Declaring an implementation of an incomplete interface From 6ff944a21dcfa0d9b29763b19f43648c1e8fe649 Mon Sep 17 00:00:00 2001 From: Josh L Date: Mon, 28 Feb 2022 21:49:51 +0000 Subject: [PATCH 25/51] Fix formatting --- docs/design/generics/details.md | 1 + proposals/p1084.md | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index d145616ab29bf..048a9c31bee7c 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -4795,3 +4795,4 @@ parameter, as opposed to an associated type, as in `N:! u32 where ___ >= 2`. - [#990: Generics details 8: interface default and final members](https://github.com/carbon-language/carbon-lang/pull/990) - [#1013: Generics: Set associated constants using where constraints](https://github.com/carbon-language/carbon-lang/pull/1013) - [#1084: Generics details 9: forward declarations](https://github.com/carbon-language/carbon-lang/pull/1084) + diff --git a/proposals/p1084.md b/proposals/p1084.md index 8d78828beada1..c63ce3c0da067 100644 --- a/proposals/p1084.md +++ b/proposals/p1084.md @@ -106,3 +106,4 @@ interfaces for incomplete types. This happens incidentally when implementing the interface inline in the scope of a class definition. We also wanted to allow it in the case where there was only a forward declaration of the type in an API file. + From 65680adfebe3c2f063d94be1f8fb22ec5e29d80b Mon Sep 17 00:00:00 2001 From: Josh L Date: Mon, 28 Feb 2022 23:03:26 +0000 Subject: [PATCH 26/51] Fix formatting --- docs/design/generics/details.md | 20 +++++++++----------- proposals/p1084.md | 1 - 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index 048a9c31bee7c..a7d1fc967a11c 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -4289,16 +4289,15 @@ defined. This is needed to allow cyclic references, for example when declaring the edges and nodes of a graph. It is also a tool that may be used to make code more readable. -The [interface](#interfaces) and -[implementation](#implementing-interfaces) sections describe the syntax for -their _definition_, which consists of a declaration followed by a body contained -in curly braces `{` ... `}`. A _forward declaration_ is a declaration followed -by a semicolon `;`. A forward declaration is a promise that the entity being -declared will be defined later. Between the first declaration of an entity, -which may be in a forward declaration or the first part of a definition, and the -end of the definition the interface or implementation is called _incomplete_. -There are additional restrictions on how the name of an incomplete entity may be -used. +The [interface](#interfaces) and [implementation](#implementing-interfaces) +sections describe the syntax for their _definition_, which consists of a +declaration followed by a body contained in curly braces `{` ... `}`. A _forward +declaration_ is a declaration followed by a semicolon `;`. A forward declaration +is a promise that the entity being declared will be defined later. Between the +first declaration of an entity, which may be in a forward declaration or the +first part of a definition, and the end of the definition the interface or +implementation is called _incomplete_. There are additional restrictions on how +the name of an incomplete entity may be used. ### Declaring interfaces @@ -4795,4 +4794,3 @@ parameter, as opposed to an associated type, as in `N:! u32 where ___ >= 2`. - [#990: Generics details 8: interface default and final members](https://github.com/carbon-language/carbon-lang/pull/990) - [#1013: Generics: Set associated constants using where constraints](https://github.com/carbon-language/carbon-lang/pull/1013) - [#1084: Generics details 9: forward declarations](https://github.com/carbon-language/carbon-lang/pull/1084) - diff --git a/proposals/p1084.md b/proposals/p1084.md index c63ce3c0da067..8d78828beada1 100644 --- a/proposals/p1084.md +++ b/proposals/p1084.md @@ -106,4 +106,3 @@ interfaces for incomplete types. This happens incidentally when implementing the interface inline in the scope of a class definition. We also wanted to allow it in the case where there was only a forward declaration of the type in an API file. - From 50f60122dd03a8aad0e549b5be2fa82f64e5800f Mon Sep 17 00:00:00 2001 From: Josh L Date: Tue, 1 Mar 2022 01:39:52 +0000 Subject: [PATCH 27/51] Checkpoint progress. --- proposals/p1084.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/p1084.md b/proposals/p1084.md index 8d78828beada1..67eb688e2a25c 100644 --- a/proposals/p1084.md +++ b/proposals/p1084.md @@ -62,7 +62,7 @@ This proposal makes changes to these sections of the Forward declarations are intended to advance these goals: - [Language tools and ecosystem](/docs/project/goals.md#language-tools-and-ecosystem), - by making Carbon easier to interpret by code in a single top-down pass. + by making Carbon easier to interpret by tooling in a single top-down pass. - [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write), by allowing developers to separate declaration from definition when organizing the presentation of their code, and imposing constraints that From adf55c17d8d1f01bee457295cf5329c69d166b99 Mon Sep 17 00:00:00 2001 From: Josh L Date: Thu, 3 Mar 2022 00:25:23 +0000 Subject: [PATCH 28/51] Expand rationale --- proposals/p1084.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/proposals/p1084.md b/proposals/p1084.md index 67eb688e2a25c..24ad75ab30f3e 100644 --- a/proposals/p1084.md +++ b/proposals/p1084.md @@ -67,6 +67,14 @@ Forward declarations are intended to advance these goals: by allowing developers to separate declaration from definition when organizing the presentation of their code, and imposing constraints that allow readers to interpret the code with less skipping around. +- [Fast and scalable development](/docs/project/goals.md#fast-and-scalable-development) + from potential build performance improvements that come from allowing an + `impl` to be defined in the `impl` file instead of the `api` file. + +The rationale behind using forward declarations are covered in more detail in: + +- [issue #472: Open question: Calling functions defined later in the same file](https://github.com/carbon-language/carbon-lang/issues/472) +- [proposal #875: Principle: information accumulation](https://github.com/carbon-language/carbon-lang/pull/875). ## Alternatives considered From f0bd96adcf48017fce6f8ca92db2273eb534b18e Mon Sep 17 00:00:00 2001 From: Josh L Date: Fri, 4 Mar 2022 00:21:20 +0000 Subject: [PATCH 29/51] Can't access members of interface in its own definition --- docs/design/generics/details.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index a7d1fc967a11c..e60d3c875be61 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -4485,6 +4485,17 @@ interface CommonType(T:! Type) { } ``` +Note however that `CommonType` is still incomplete inside its definition, so no +constraints on members of `CommonType` are allowed. + +``` +interface CommonType(T:! Type) { + let Result:! Type; + // ❌ Illegal: `CommonType` is incomplete + impl T as CommonType(Self) where .Result == Result; +} +``` + ## Interface members with definitions Interfaces may provide definitions for members, such as a function body for an From 07f3f1f0fd3b7b9373bcdf6689332d86be07e587 Mon Sep 17 00:00:00 2001 From: Josh L Date: Fri, 4 Mar 2022 01:01:56 +0000 Subject: [PATCH 30/51] Impls need only be declared in prioritization blocks --- docs/design/generics/details.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index e60d3c875be61..bd7626d612e6f 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -3933,9 +3933,10 @@ Since at most one library can define impls with a given type structure, all impls with a given type structure must be in the same library. Furthermore by the [impl declaration access rules](#access), they will be defined in the API file for the library if they could match any query from outside the library. If -there is more than one impl with that type structure, they must be written -together in a prioritization block. Once a type structure is selected for a -query, the first impl in the prioritization block that matches is selected. +there is more than one impl with that type structure, they must be +[declared](#declaring-implementations) together in a prioritization block. Once +a type structure is selected for a query, the first impl in the prioritization +block that matches is selected. **Open question:** How are prioritization blocks written? A block starts with a keyword like `match_first` or `impl_priority` and then a sequence of impl @@ -4335,8 +4336,9 @@ these rules: of the implementation and must match between a forward declaration and definition. - You may forward declare an implementation of a defined interface but not an - incomplete interface. It may be for any declared type, whether it is - incomplete or defined. + incomplete interface. This allows the assignment of associated constants in + the impl declaration to be verified. A forward impl declaration may be for + any declared type, whether it is incomplete or defined. - Every internal implementation must be declared (or defined) inside the scope of the class definition. It may also be declared before the class definition or defined afterwards. Note that the class itself is incomplete in the scope From 648cc8579dfae74cac662dfd7ec33c80c6f89dd5 Mon Sep 17 00:00:00 2001 From: Josh L Date: Fri, 4 Mar 2022 20:30:05 +0000 Subject: [PATCH 31/51] Clarify match_first can use defined impls explicitly. --- docs/design/generics/details.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index bd7626d612e6f..a7a66bd0b0068 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -3934,9 +3934,9 @@ impls with a given type structure must be in the same library. Furthermore by the [impl declaration access rules](#access), they will be defined in the API file for the library if they could match any query from outside the library. If there is more than one impl with that type structure, they must be -[declared](#declaring-implementations) together in a prioritization block. Once -a type structure is selected for a query, the first impl in the prioritization -block that matches is selected. +[defined](#implementing-interfaces) or [declared](#declaring-implementations) +together in a prioritization block. Once a type structure is selected for a +query, the first impl in the prioritization block that matches is selected. **Open question:** How are prioritization blocks written? A block starts with a keyword like `match_first` or `impl_priority` and then a sequence of impl From cebd6e825023e3152a41397700f4e93f66d96c1c Mon Sep 17 00:00:00 2001 From: Josh L Date: Thu, 10 Mar 2022 02:22:33 +0000 Subject: [PATCH 32/51] Changes based on feedback --- docs/design/generics/details.md | 48 +++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index a7a66bd0b0068..f5bf47c3f0687 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -4302,26 +4302,46 @@ the name of an incomplete entity may be used. ### Declaring interfaces +The declaration for an interface consists of: + +- the keyword introducer `interface`, +- the name of the interface, and +- the parameter list, if any. + +The name of an interface can not be used until its first declaration is +complete. In particular, it is illegal to use the name of the interface in the +parameter list. There is a +[workaround](#interfaces-with-parameters-constrained-by-the-same-interface) for +the use cases when this would come up. + An interface may be forward declared subject to these rules: - The definition must be in the same file as the declaration. -- The declaration includes the parameter list for the interface. - The declaration part of a forward declaration and the corresponding definition must match. -- The name of the interface must not be used until after the parameter list of - the declaration. In particular, it is illegal to use the name of the - interface in the parameter list. There is a - [workaround](#interfaces-with-parameters-constrained-by-the-same-interface) - for the use cases when this would come up. - An incomplete interface may be used in constraints in declarations of types or functions. -- Any name lookup into an incomplete interface is an error. For example, an - attempt to access a member of an interface using `MyInterface.MemberName` or - an attempt to define the body of a generic function using that interface as +- An attempt to define the body of a generic function using that interface as a constraint is illegal. +- Any name lookup into an incomplete interface is an error. For example, it is + illegal to attempt to access a member of an interface using + `MyInterface.MemberName`. ### Declaring implementations +The declaration of an interface implementation consists of: + +- an optional `external` keyword, +- the keyword introducer `impl`, +- an optional deduced parameter list in square brackets `[`...`]`, +- a type, including an optional argument list, +- the keyword `as`, and +- a [type-of-type](#type-of-types), including an optional + [argument list](#parameterized-interfaces) and + [`where` clause](#where-constraints) assigning + [associated constants](#associated-constants) and + [associated types](#associated-types). + An implementation of an interface for a type may be forward declared subject to these rules: @@ -4331,13 +4351,13 @@ these rules: - If there is both a forward declaration and a definition, only the first declaration must specify the assignment of associated constants with a `where` clause. If a later declaration repeats the `where` clause, it must - match. -- The keyword `external`, when it precedes `impl`, is part of the declaration - of the implementation and must match between a forward declaration and - definition. + match in that it produces the associated constants with the same values if + the original `where` clause is ignored. +- The presence of the keyword `external` before `impl` must match between a + forward declaration and definition. - You may forward declare an implementation of a defined interface but not an incomplete interface. This allows the assignment of associated constants in - the impl declaration to be verified. A forward impl declaration may be for + the `impl` declaration to be verified. A forward impl declaration may be for any declared type, whether it is incomplete or defined. - Every internal implementation must be declared (or defined) inside the scope of the class definition. It may also be declared before the class definition From 24f05a39eab1cba9ba443cc64bb47d2754e382bf Mon Sep 17 00:00:00 2001 From: Josh L Date: Thu, 10 Mar 2022 17:38:11 +0000 Subject: [PATCH 33/51] Forward declaring named constraints --- docs/design/generics/details.md | 113 ++++++++++++++++++-------------- proposals/p1084.md | 41 ++++++++++++ 2 files changed, 103 insertions(+), 51 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index f5bf47c3f0687..9c78409ab5c37 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -93,7 +93,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [Libraries that can contain `final` impls](#libraries-that-can-contain-final-impls) - [Comparison to Rust](#comparison-to-rust) - [Forward declarations and cyclic references](#forward-declarations-and-cyclic-references) - - [Declaring interfaces](#declaring-interfaces) + - [Declaring interfaces and named constraints](#declaring-interfaces-and-named-constraints) - [Declaring implementations](#declaring-implementations) - [Declaration examples](#declaration-examples) - [Example of declaring interfaces with cyclic references](#example-of-declaring-interfaces-with-cyclic-references) @@ -4285,47 +4285,50 @@ differences between the Carbon and Rust plans: ## Forward declarations and cyclic references -Interfaces and their implementations may be forward declared and then later -defined. This is needed to allow cyclic references, for example when declaring -the edges and nodes of a graph. It is also a tool that may be used to make code -more readable. - -The [interface](#interfaces) and [implementation](#implementing-interfaces) -sections describe the syntax for their _definition_, which consists of a -declaration followed by a body contained in curly braces `{` ... `}`. A _forward -declaration_ is a declaration followed by a semicolon `;`. A forward declaration -is a promise that the entity being declared will be defined later. Between the -first declaration of an entity, which may be in a forward declaration or the -first part of a definition, and the end of the definition the interface or -implementation is called _incomplete_. There are additional restrictions on how -the name of an incomplete entity may be used. - -### Declaring interfaces - -The declaration for an interface consists of: - -- the keyword introducer `interface`, -- the name of the interface, and +Interfaces, named constraints, and their implementations may be forward declared +and then later defined. This is needed to allow cyclic references, for example +when declaring the edges and nodes of a graph. It is also a tool that may be +used to make code more readable. + +The [interface](#interfaces), [named constraint](#named-constraints), and +[implementation](#implementing-interfaces) sections describe the syntax for +their _definition_, which consists of a declaration followed by a body contained +in curly braces `{` ... `}`. A _forward declaration_ is a declaration followed +by a semicolon `;`. A forward declaration is a promise that the entity being +declared will be defined later. Between the first declaration of an entity, +which may be in a forward declaration or the first part of a definition, and the +end of the definition the interface or implementation is called _incomplete_. +There are additional restrictions on how the name of an incomplete entity may be +used. + +### Declaring interfaces and named constraints + +The declaration for an interface or named constraint consists of: + +- an optional access-control keyword, +- the keyword introducer `interface`, `constraint`, or `template constraint`, +- the name of the interface or constraint, and - the parameter list, if any. -The name of an interface can not be used until its first declaration is -complete. In particular, it is illegal to use the name of the interface in the -parameter list. There is a +The name of an interface or constraint can not be used until its first +declaration is complete. In particular, it is illegal to use the name of the +interface in its parameter list. There is a [workaround](#interfaces-with-parameters-constrained-by-the-same-interface) for the use cases when this would come up. -An interface may be forward declared subject to these rules: +An interface or named constraint may be forward declared subject to these rules: - The definition must be in the same file as the declaration. - The declaration part of a forward declaration and the corresponding definition must match. -- An incomplete interface may be used in constraints in declarations of types - or functions. -- An attempt to define the body of a generic function using that interface as - a constraint is illegal. -- Any name lookup into an incomplete interface is an error. For example, it is - illegal to attempt to access a member of an interface using - `MyInterface.MemberName`. +- An incomplete interface or named constraint may be used as constraints in + declarations of types or functions. +- An attempt to define the body of a generic function using an incomplete + interface or named constraint is illegal. +- Any name lookup into an incomplete interface or named constraint is an + error. For example, it is illegal to attempt to access a member of an + interface using `MyInterface.MemberName` or constrain a member using a + `where` clause. ### Declaring implementations @@ -4464,37 +4467,45 @@ In this example, `NodeInterface` has an `EdgeType` associated type that is constrained to implement `EdgeInterface`, and `EdgeInterface` has a `NodeType` associated type that is constrained to implement `NodeInterface`. Furthermore, the `NodeType` of an `EdgeType` is the original type, and the other way around. +This is accomplished by naming and then forward declaring the constraints that +can't be stated directly: ``` -// Forward declaration of interface -interface EdgeInterface; +// Forward declare interfaces used in +// parameter lists of constraints. +interface Edge; +interface Node; -// Definition that only uses the declaration of -// `EdgeInterface`, not its definition. -interface NodeBootstrap { - let EdgeType:! EdgeInterface; - fn Edges[me: Self]() -> Vector(EdgeType); -} +// Forward declare named constraints used in +// interface definitions. +private constraint EdgeFor(N:! Node); +private constraint NodeFor(E:! Edge); -// Now can define `EdgeInterface` in terms of -// `NodeBootstrap`. -interface EdgeInterface { - let NodeType:! NodeBootstrap where .EdgeType == Self; +// Define interfaces using named constraints. +interface Edge { + let NodeType:! NodeFor(Self); fn Head[me: Self]() -> NodeType; } +interface Node { + let EdgeType:! EdgeFor(Self); + fn Edges[me: Self]() -> Vector(EdgeType); +} -// Make `NodeInterface` a named constraint defined in -// terms of `NodeBootstrap`, adding in constraints that -// couldn't be written until `EdgeInterface` was defined. -constraint NodeInterface { - extends NodeBootsrap where .EdgeType.NodeType == Self; +// Now that the interfaces are defined, can +// refer to members of the interface, so it is +// now legal to define the named constraints. +private constraint EdgeFor(N:! Node) { + extends Edge where .NodeType == N; +} +private constraint NodeFor(E:! Edge) { + extends Node where .EdgeType == E; } ``` ### Interfaces with parameters constrained by the same interface To work around -[the restriction about not being able to name an interface in its parameter list](#declaring-interfaces), +[the restriction about not being able to name an interface in its parameter list](#declaring-interfaces-and-named-constraints), instead include that requirement in the body of the interface. ``` diff --git a/proposals/p1084.md b/proposals/p1084.md index 24ad75ab30f3e..32df9e38b8ce7 100644 --- a/proposals/p1084.md +++ b/proposals/p1084.md @@ -20,6 +20,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [No `default` keyword on interface members](#no-default-keyword-on-interface-members) - [Declaring an implementation of an incomplete interface](#declaring-an-implementation-of-an-incomplete-interface) - [No implementations for incomplete types](#no-implementations-for-incomplete-types) + - [No forward declaration of named constraints](#no-forward-declaration-of-named-constraints) @@ -114,3 +115,43 @@ interfaces for incomplete types. This happens incidentally when implementing the interface inline in the scope of a class definition. We also wanted to allow it in the case where there was only a forward declaration of the type in an API file. + +### No forward declaration of named constraints + +We considered omitting the ability to forward declare named constraints, but we +discovered that ability made declaring interfaces with cyclic dependencies +easier and cleaner. Without this feature, +[the graph example of cyclic references](/docs/design/generics/details.md#example-of-declaring-interfaces-with-cyclic-references) +looked like this instead: + +``` +// Forward declaration of interface +interface EdgeInterface; + +// Definition that only uses the declaration of +// `EdgeInterface`, not its definition. +interface NodeBootstrap { + let EdgeType:! EdgeInterface; + fn Edges[me: Self]() -> Vector(EdgeType); +} + +// Now can define `EdgeInterface` in terms of +// `NodeBootstrap`. +interface EdgeInterface { + let NodeType:! NodeBootstrap where .EdgeType == Self; + fn Head[me: Self]() -> NodeType; +} + +// Make `NodeInterface` a named constraint defined in +// terms of `NodeBootstrap`, adding in constraints that +// couldn't be written until `EdgeInterface` was defined. +constraint NodeInterface { + extends NodeBootsrap where .EdgeType.NodeType == Self; +} +``` + +We did not like how the definition of `NodeInterface` was split into two pieces, +making it harder to understand what it contained. + +This question was discussed in +[the #generics channel on Discord](https://discord.com/channels/655572317891461132/941071822756143115/951288264315265114). From c9cdca6c125f979c1376e50099cb74599df09be0 Mon Sep 17 00:00:00 2001 From: Josh L Date: Thu, 10 Mar 2022 21:24:15 +0000 Subject: [PATCH 34/51] Don't repeat `private` --- docs/design/generics/details.md | 9 +++++---- proposals/p1084.md | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index 9c78409ab5c37..ae86700ecfefb 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -4305,7 +4305,7 @@ used. The declaration for an interface or named constraint consists of: -- an optional access-control keyword, +- an optional access-control keyword like `private`, - the keyword introducer `interface`, `constraint`, or `template constraint`, - the name of the interface or constraint, and - the parameter list, if any. @@ -4319,6 +4319,7 @@ the use cases when this would come up. An interface or named constraint may be forward declared subject to these rules: - The definition must be in the same file as the declaration. +- Only the first declaration will have an access-control keyword. - The declaration part of a forward declaration and the corresponding definition must match. - An incomplete interface or named constraint may be used as constraints in @@ -4334,13 +4335,13 @@ An interface or named constraint may be forward declared subject to these rules: The declaration of an interface implementation consists of: -- an optional `external` keyword, +- optional modifier keywords `final`, `external`, - the keyword introducer `impl`, - an optional deduced parameter list in square brackets `[`...`]`, -- a type, including an optional argument list, +- a type, including an optional parameter pattern, - the keyword `as`, and - a [type-of-type](#type-of-types), including an optional - [argument list](#parameterized-interfaces) and + [parameter pattern](#parameterized-interfaces) and [`where` clause](#where-constraints) assigning [associated constants](#associated-constants) and [associated types](#associated-types). diff --git a/proposals/p1084.md b/proposals/p1084.md index 32df9e38b8ce7..c606f0738afb7 100644 --- a/proposals/p1084.md +++ b/proposals/p1084.md @@ -21,6 +21,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [Declaring an implementation of an incomplete interface](#declaring-an-implementation-of-an-incomplete-interface) - [No implementations for incomplete types](#no-implementations-for-incomplete-types) - [No forward declaration of named constraints](#no-forward-declaration-of-named-constraints) + - [Repeating `private` in both declaration and definition](#repeating-private-in-both-declaration-and-definition) @@ -155,3 +156,17 @@ making it harder to understand what it contained. This question was discussed in [the #generics channel on Discord](https://discord.com/channels/655572317891461132/941071822756143115/951288264315265114). + +### Repeating `private` in both declaration and definition + +We considered repeating the access-control keyword `private` as a prefix of all +`impl` declarations and definitions. The +[current rule](/docs/design/generics/details.md#declaring-interfaces-and-named-constraints) +only marks the first declaration or definition, which is consistent with +[the policy of not repeating access-control keywords stated in an API file in an impl file](/docs/design/code_and_name_organization#exporting-entities-from-an-api-file). + +This was discussed in +[the #syntax channel on Discord](https://discord.com/channels/655572317891461132/709488742942900284/951520959544823868), +but this decision should be considered provisional since it was not considered +deeply. We would be open to revisiting this decision in the future, once we had +some experience with it. From 54258bf8405265de9290d1b4b4aa71dfcf86fd68 Mon Sep 17 00:00:00 2001 From: Josh L Date: Thu, 10 Mar 2022 23:23:38 +0000 Subject: [PATCH 35/51] Forward-declared named interfaces help more --- docs/design/generics/details.md | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index ae86700ecfefb..88598920c2fab 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -4495,10 +4495,10 @@ interface Node { // Now that the interfaces are defined, can // refer to members of the interface, so it is // now legal to define the named constraints. -private constraint EdgeFor(N:! Node) { +constraint EdgeFor(N:! Node) { extends Edge where .NodeType == N; } -private constraint NodeFor(E:! Edge) { +constraint NodeFor(E:! Edge) { extends Node where .EdgeType == E; } ``` @@ -4530,6 +4530,25 @@ interface CommonType(T:! Type) { } ``` +Instead, a forward-declared named constraint can be used in place of the +constraint that can only be defined later. This is +[the same strategy used to work around cyclic references](#example-of-declaring-interfaces-with-cyclic-references). + +``` +private constraint CommonTypeResult(T:! Type, R:! Type); + +interface CommonType(T:! Type) { + let Result:! Type; + // ✅ Allowed: `CommonTypeResult` is incomplete, but + // no members are accessed. + impl T as CommonTypeResult(Self, Result); +} + +constraint CommonTypeResult(T:! Type, R:! Type) { + extends CommonType(T) where .Result == R; +} +``` + ## Interface members with definitions Interfaces may provide definitions for members, such as a function body for an From 8020e83c4a170be4f4ce5930e59936cf98ca6e7c Mon Sep 17 00:00:00 2001 From: josh11b Date: Thu, 10 Mar 2022 15:59:07 -0800 Subject: [PATCH 36/51] Apply suggestions from code review Co-authored-by: Richard Smith --- docs/design/generics/details.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index 88598920c2fab..35a8436f65d13 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -4464,9 +4464,9 @@ impl MyClass as Interface6 { } ### Example of declaring interfaces with cyclic references -In this example, `NodeInterface` has an `EdgeType` associated type that is -constrained to implement `EdgeInterface`, and `EdgeInterface` has a `NodeType` -associated type that is constrained to implement `NodeInterface`. Furthermore, +In this example, `Node` has an `EdgeType` associated type that is +constrained to implement `Edge`, and `Edge` has a `NodeType` +associated type that is constrained to implement `Node`. Furthermore, the `NodeType` of an `EdgeType` is the original type, and the other way around. This is accomplished by naming and then forward declaring the constraints that can't be stated directly: @@ -4719,7 +4719,7 @@ interface TotalOrder { final fn TotalGreater[me: Self](right: Self) -> bool; } // `TotalOrder` is considered complete at this point, even -// though `TotalOrder.TotalGreater` is still incomplete. +// though `TotalOrder.TotalGreater` is not yet defined. fn TotalOrder.TotalGreater[me: Self](right: Self) -> bool { return right.TotalLess(me); } From 85749845326904db5082486a3dbb0db8353cf7b6 Mon Sep 17 00:00:00 2001 From: Josh L Date: Fri, 11 Mar 2022 00:05:35 +0000 Subject: [PATCH 37/51] Fix formatting --- docs/design/generics/details.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index 35a8436f65d13..c1c0dc5439b9f 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -4464,12 +4464,11 @@ impl MyClass as Interface6 { } ### Example of declaring interfaces with cyclic references -In this example, `Node` has an `EdgeType` associated type that is -constrained to implement `Edge`, and `Edge` has a `NodeType` -associated type that is constrained to implement `Node`. Furthermore, -the `NodeType` of an `EdgeType` is the original type, and the other way around. -This is accomplished by naming and then forward declaring the constraints that -can't be stated directly: +In this example, `Node` has an `EdgeType` associated type that is constrained to +implement `Edge`, and `Edge` has a `NodeType` associated type that is +constrained to implement `Node`. Furthermore, the `NodeType` of an `EdgeType` is +the original type, and the other way around. This is accomplished by naming and +then forward declaring the constraints that can't be stated directly: ``` // Forward declare interfaces used in From 96476920277adfce3bfefe64b7db0b7eef08ecfe Mon Sep 17 00:00:00 2001 From: josh11b Date: Thu, 10 Mar 2022 16:55:54 -0800 Subject: [PATCH 38/51] Update docs/design/generics/details.md Co-authored-by: Richard Smith --- docs/design/generics/details.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index c1c0dc5439b9f..9a4cf68bca02e 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -4361,7 +4361,7 @@ these rules: forward declaration and definition. - You may forward declare an implementation of a defined interface but not an incomplete interface. This allows the assignment of associated constants in - the `impl` declaration to be verified. A forward impl declaration may be for + the `impl` declaration to be verified. An impl forward declaration may be for any declared type, whether it is incomplete or defined. - Every internal implementation must be declared (or defined) inside the scope of the class definition. It may also be declared before the class definition From 3db88c9d858cd7923bb95063244dacb3c67cf076 Mon Sep 17 00:00:00 2001 From: Josh L Date: Fri, 11 Mar 2022 01:07:58 +0000 Subject: [PATCH 39/51] Fix formatting --- docs/design/generics/details.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index 9a4cf68bca02e..22bba602651d6 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -4361,8 +4361,8 @@ these rules: forward declaration and definition. - You may forward declare an implementation of a defined interface but not an incomplete interface. This allows the assignment of associated constants in - the `impl` declaration to be verified. An impl forward declaration may be for - any declared type, whether it is incomplete or defined. + the `impl` declaration to be verified. An impl forward declaration may be + for any declared type, whether it is incomplete or defined. - Every internal implementation must be declared (or defined) inside the scope of the class definition. It may also be declared before the class definition or defined afterwards. Note that the class itself is incomplete in the scope From 57ca05551380b83b506b73637f5fe60174d1e571 Mon Sep 17 00:00:00 2001 From: Josh L Date: Wed, 16 Mar 2022 23:56:52 +0000 Subject: [PATCH 40/51] Incorporate feedback, discussion, and #1132 --- docs/design/generics/details.md | 63 +++++++++++++++++++++++++++------ proposals/p1084.md | 32 +++++++++++++++++ 2 files changed, 85 insertions(+), 10 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index 22bba602651d6..9efa23138e7bf 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -95,6 +95,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [Forward declarations and cyclic references](#forward-declarations-and-cyclic-references) - [Declaring interfaces and named constraints](#declaring-interfaces-and-named-constraints) - [Declaring implementations](#declaring-implementations) + - [Matching and agreeing](#matching-and-agreeing) - [Declaration examples](#declaration-examples) - [Example of declaring interfaces with cyclic references](#example-of-declaring-interfaces-with-cyclic-references) - [Interfaces with parameters constrained by the same interface](#interfaces-with-parameters-constrained-by-the-same-interface) @@ -4319,13 +4320,15 @@ the use cases when this would come up. An interface or named constraint may be forward declared subject to these rules: - The definition must be in the same file as the declaration. -- Only the first declaration will have an access-control keyword. -- The declaration part of a forward declaration and the corresponding - definition must match. +- Only the first declaration may have an access-control keyword. - An incomplete interface or named constraint may be used as constraints in - declarations of types or functions. + declarations of types, functions, interfaces, or named constraints. This + includes an `impl as` or `extends` declaration inside an interface or named + constraint. - An attempt to define the body of a generic function using an incomplete interface or named constraint is illegal. +- An attempt to call a generic function using an incomplete interface or named + constraint in its signature is illegal. - Any name lookup into an incomplete interface or named constraint is an error. For example, it is illegal to attempt to access a member of an interface using `MyInterface.MemberName` or constrain a member using a @@ -4354,15 +4357,15 @@ these rules: the definition in an impl file. - If there is both a forward declaration and a definition, only the first declaration must specify the assignment of associated constants with a - `where` clause. If a later declaration repeats the `where` clause, it must - match in that it produces the associated constants with the same values if - the original `where` clause is ignored. -- The presence of the keyword `external` before `impl` must match between a - forward declaration and definition. + `where` clause. Later declarations may omit the `where` clause by writing + `where _` instead. - You may forward declare an implementation of a defined interface but not an incomplete interface. This allows the assignment of associated constants in the `impl` declaration to be verified. An impl forward declaration may be - for any declared type, whether it is incomplete or defined. + for any declared type, whether it is incomplete or defined. Note that this + does not apply to `impl as` declarations in an interface or named constraint + definition, as those are considered interface requirements not forward + declarations. - Every internal implementation must be declared (or defined) inside the scope of the class definition. It may also be declared before the class definition or defined afterwards. Note that the class itself is incomplete in the scope @@ -4377,6 +4380,46 @@ these rules: before the query. This can be done with a definition or a forward declaration. +### Matching and agreeing + +Carbon needs to determine if two declarations match in order to say which +definition a forward declaration corresponds to and to verify that nothing is +defined twice. Declarations that match must also agree, meaning they are +consistent with each other. + +Interface and named constraint declarations match if their names are the same +after name and alias resolution. To agree: + +- The introducer keyword or keywords much be the same. +- The types and order of parameters in the parameter list, if any, must match. + The parameter names may be omitted, but if they are included in both + declarations, they must match. +- Types agree if they correspond to the same expression tree, after name and + alias resolution and canonicalization of parentheses. Note that no other + evaluation of type expressions is performed. + +Interface implementation declarations match if the type and interface +expressions match: + +- `Self` is considered to match its meaning in the scope it is used. So in + `class MyClass { ... }`, `Self` is considered an alias for `MyClass`. +- If the type part is omitted, it is considered to match `Self` in the current + context. +- Types match if they have the same name after name and alias resolution and + the same parameters, or are the same type parameter. +- Interfaces match if they have the same name after name and alias resolution + and the same parameters. Note that a named constraint that is equivalent to + an interface, as in `constraint Equivalent { extends MyInterface; }`, is not + considered to match. + +For implementations to agree: + +- The presence of modifier keywords such as `external` before `impl` must + match between a forward declaration and definition. +- If a both declarations include `where` clauses, that is neither use + `where _`, they must match in that they produce the associated constants + with the same values considered separately. + ### Declaration examples ``` diff --git a/proposals/p1084.md b/proposals/p1084.md index c606f0738afb7..6783faad97bb8 100644 --- a/proposals/p1084.md +++ b/proposals/p1084.md @@ -22,6 +22,8 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [No implementations for incomplete types](#no-implementations-for-incomplete-types) - [No forward declaration of named constraints](#no-forward-declaration-of-named-constraints) - [Repeating `private` in both declaration and definition](#repeating-private-in-both-declaration-and-definition) + - [Allow function bodies using incomplete interfaces](#allow-function-bodies-using-incomplete-interfaces) + - [Require parameter names to match when specified](#require-parameter-names-to-match-when-specified) @@ -170,3 +172,33 @@ This was discussed in but this decision should be considered provisional since it was not considered deeply. We would be open to revisiting this decision in the future, once we had some experience with it. + +### Allow function bodies using incomplete interfaces + +We +[considered](https://docs.google.com/document/d/1cRrhRrmaUf2hVi2lFcHsYo2j0jI6t9RGZoYjWhRxp14/edit?resourcekey=0-xWHBEZ8zIqnJiB4yfBSLfA#heading=h.oqmpxtubjmkm) +allowing a function definition to use an incomplete interface. One concern was +whether the criteria for when the function body depended on something in the +interface's definition would be too subtle for developers to reason about. We +eventually concluded that, unless using a monomorphization compilation strategy, +efficient code generation for a generic function would need to use the +interface's definition. For example, an interface that represented a single +function call might use a function pointer instead of a witness table. This same +argument led to the requirement that the interface's definition be visible at +call sites as well. + +### Require parameter names to match when specified + +We decided to diverge from C++ in requiring parameter names to match between +declarations for a few reasons: + +- wanting to avoid the confusion that we've experienced when they don't match, + noting that common C++ lint tools ask to make them match; +- wanting reflection to return a single parameter name for a parameter; and +- wanting the parameter names to be consistent with the single docstring we + expect to associate with a function. + +This was discussed in +[open discussion on 2022-03-14](https://docs.google.com/document/d/1cRrhRrmaUf2hVi2lFcHsYo2j0jI6t9RGZoYjWhRxp14/edit?resourcekey=0-xWHBEZ8zIqnJiB4yfBSLfA#heading=h.oqmpxtubjmkm) +and +[question-for-leads issue #1132](https://github.com/carbon-language/carbon-lang/issues/1132). From 4384c09bdf77e9569298b6dca659f8b7cad42ef5 Mon Sep 17 00:00:00 2001 From: Josh L Date: Thu, 17 Mar 2022 15:34:39 +0000 Subject: [PATCH 41/51] `Self` should not be equivalent to `Self` in another context --- docs/design/generics/details.md | 8 ++++---- proposals/p1084.md | 12 ++++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index 9efa23138e7bf..eca87f7e00f48 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -4401,10 +4401,10 @@ after name and alias resolution. To agree: Interface implementation declarations match if the type and interface expressions match: -- `Self` is considered to match its meaning in the scope it is used. So in - `class MyClass { ... }`, `Self` is considered an alias for `MyClass`. -- If the type part is omitted, it is considered to match `Self` in the current - context. +- If the type part is omitted, it is rewritten to `Self` in the context of the + declaration. +- `Self` is rewritted to its meaning in the scope it is used. So in + `class MyClass { ... }`, `Self` is rewritten to `MyClass`. - Types match if they have the same name after name and alias resolution and the same parameters, or are the same type parameter. - Interfaces match if they have the same name after name and alias resolution diff --git a/proposals/p1084.md b/proposals/p1084.md index 6783faad97bb8..ecdd80990a009 100644 --- a/proposals/p1084.md +++ b/proposals/p1084.md @@ -19,6 +19,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [Alternatives considered](#alternatives-considered) - [No `default` keyword on interface members](#no-default-keyword-on-interface-members) - [Declaring an implementation of an incomplete interface](#declaring-an-implementation-of-an-incomplete-interface) + - [Allow definition of private interfaces in separate impl file](#allow-definition-of-private-interfaces-in-separate-impl-file) - [No implementations for incomplete types](#no-implementations-for-incomplete-types) - [No forward declaration of named constraints](#no-forward-declaration-of-named-constraints) - [Repeating `private` in both declaration and definition](#repeating-private-in-both-declaration-and-definition) @@ -106,6 +107,17 @@ interface, and so we took the conservative position of forbidding that. We could add this feature in the future if use cases were found, but clearly we can't have impl definitions until the interface is defined. +### Allow definition of private interfaces in separate impl file + +This proposal requires the definition of an interface to be in the same file as +any declaration of it. We +[anticipate](https://github.com/carbon-language/carbon-lang/pull/1084#discussion_r824214281) +the possibility that we will find a use case for declaring a private interface +in an API file that is defined in the corresponding impl file. An example where +this may arise is if the constraint is only used when defining private members +of an exported class. We would be willing to change if we see demand for this in +the future. + ### No implementations for incomplete types For simplicity, generally Carbon entities should either be "incomplete" or From 015d68f4ec5a159bcb77acab1036f090e50fff40 Mon Sep 17 00:00:00 2001 From: Josh L Date: Thu, 17 Mar 2022 16:27:01 +0000 Subject: [PATCH 42/51] Reflect merging of #875 --- docs/design/generics/details.md | 31 ++++++------------------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index eca87f7e00f48..90feeeadacd95 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -61,7 +61,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [Another type implements parameterized interface](#another-type-implements-parameterized-interface) - [Implied constraints](#implied-constraints) - [Must be legal type argument constraints](#must-be-legal-type-argument-constraints) - - [Open question: referencing names in the interface being defined](#open-question-referencing-names-in-the-interface-being-defined) + - [Referencing names in the interface being defined](#referencing-names-in-the-interface-being-defined) - [Manual type equality](#manual-type-equality) - [`observe` declarations](#observe-declarations) - [Other constraints as type-of-types](#other-constraints-as-type-of-types) @@ -2943,10 +2943,10 @@ fn NumDistinct[T:! Type where HashSet(.Self) is Type] This has the same advantages over repeating the constraints on `HashSet` arguments in the type of `T` as the general implied constraints above. -### Open question: referencing names in the interface being defined +### Referencing names in the interface being defined -Should the constraint in a `where` clause be required to only reference earlier -names from this scope, as in this example? +The constraint in a `where` clause is required to only reference earlier names +from this scope, as in this example: ``` interface Graph { @@ -2955,22 +2955,6 @@ interface Graph { } ``` -The downside is that if you could reference later names, there is a more -pleasingly symmetric formulation of those same constraints: - -``` -interface Graph { - let E: Edge where .V == V; - let V: Vert where .E == E; -} -``` - -**TODO:** Revisit this question once issue -[#472: Open question: Calling functions defined later in the same file](https://github.com/carbon-language/carbon-lang/issues/472) -and proposal -[#875: Principle: information accumulation](https://github.com/carbon-language/carbon-lang/pull/875) -are resolved. - ### Manual type equality Imagine we have some function with generic parameters: @@ -4370,11 +4354,8 @@ these rules: of the class definition. It may also be declared before the class definition or defined afterwards. Note that the class itself is incomplete in the scope of the class definition, but member function bodies defined inline are - processed as if they appeared immediately after the end of the outermost - enclosing class, see - [question-for-leads issue #472](https://github.com/carbon-language/carbon-lang/issues/472) - and - [proposal #875: Principle: information accumulation](https://github.com/carbon-language/carbon-lang/pull/875). + processed + - For [coherence](goals.md#coherence), we require that any impl that matches an [impl lookup](#impl-lookup) query in the same file, must be declared before the query. This can be done with a definition or a forward From 3a2514a05a85c4cda50da91b9b3e8c4b73ca35e9 Mon Sep 17 00:00:00 2001 From: Josh L Date: Thu, 17 Mar 2022 16:28:13 +0000 Subject: [PATCH 43/51] Enable link --- docs/design/generics/details.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index f086fefc8f648..211f8c9fc173d 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -4362,7 +4362,7 @@ these rules: or defined afterwards. Note that the class itself is incomplete in the scope of the class definition, but member function bodies defined inline are processed - + [as if they appeared immediately after the end of the outermost enclosing class](/docs/project/principles/information_accumulation.md#exceptions). - For [coherence](goals.md#coherence), we require that any impl that matches an [impl lookup](#impl-lookup) query in the same file, must be declared before the query. This can be done with a definition or a forward From bf2dc7e0e114ffe50fc4345d2a879057a8f53635 Mon Sep 17 00:00:00 2001 From: Josh L Date: Fri, 18 Mar 2022 23:17:50 +0000 Subject: [PATCH 44/51] Update overview --- docs/design/generics/details.md | 2 +- docs/design/generics/overview.md | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index 211f8c9fc173d..466f45ba59e78 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -4886,5 +4886,5 @@ parameter, as opposed to an associated type, as in `N:! u32 where ___ >= 2`. - [#950: Generic details 6: remove facets](https://github.com/carbon-language/carbon-lang/pull/950) - [#983: Generic details 7: final impls](https://github.com/carbon-language/carbon-lang/pull/983) - [#990: Generics details 8: interface default and final members](https://github.com/carbon-language/carbon-lang/pull/990) -- [#1013: Generics: Set associated constants using where constraints](https://github.com/carbon-language/carbon-lang/pull/1013) +- [#1013: Generics: Set associated constants using `where` constraints](https://github.com/carbon-language/carbon-lang/pull/1013) - [#1084: Generics details 9: forward declarations](https://github.com/carbon-language/carbon-lang/pull/1084) diff --git a/docs/design/generics/overview.md b/docs/design/generics/overview.md index 4a4b35c2e49b8..c19df6247584e 100644 --- a/docs/design/generics/overview.md +++ b/docs/design/generics/overview.md @@ -162,6 +162,12 @@ interface Comparable { } ``` +Functions and methods may be given a default implementation by prefixing the +declaration with `default` and putting the function body in curly braces +`{`...`}` in place of the terminating `;` of the function declaration. To +prevent that implementation from being overridden, use `final` instead of +`default`. + Interfaces describe functionality, but not data; no variables may be declared in an interface. @@ -634,8 +640,6 @@ priority order in a prioritization block. to support evolution. - Types should be able to define overloads for operators by implementing standard interfaces. -- There should be a way to provide default implementations of methods in - interfaces and other ways to reuse code across implementations. - There should be a way to define generic associated and higher-ranked/kinded types. @@ -647,3 +651,4 @@ priority order in a prioritization block. - [#920: Generic parameterized impls (details 5)](https://github.com/carbon-language/carbon-lang/pull/920) - [#950: Generic details 6: remove facets](https://github.com/carbon-language/carbon-lang/pull/950) - [#1013: Generics: Set associated constants using `where` constraints](https://github.com/carbon-language/carbon-lang/pull/1013) +- [#1084: Generics details 9: forward declarations](https://github.com/carbon-language/carbon-lang/pull/1084) From 60caa636fdb85f0477f7aee63e08928b40960cf6 Mon Sep 17 00:00:00 2001 From: Josh L Date: Wed, 23 Mar 2022 19:02:50 +0000 Subject: [PATCH 45/51] This implements the resolution of #1132 --- proposals/p1084.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/proposals/p1084.md b/proposals/p1084.md index ecdd80990a009..9bde92697854a 100644 --- a/proposals/p1084.md +++ b/proposals/p1084.md @@ -52,6 +52,10 @@ Use of the `default` keyword in `interface` definitions to allow defaulted members to be defined out-of-line was originally proposed in [withdrawn proposal #1034](https://github.com/carbon-language/carbon-lang/pull/1034). +This proposal implements the decisions in +[issue #1132: How do we match forward declarations with their definitions?](https://github.com/carbon-language/carbon-lang/issues/1132) +as they apply to generic interfaces, implementations, and so on. + ## Proposal This proposal makes changes to these sections of the From d982c7f619c655a9677cc41734624769f3d850bf Mon Sep 17 00:00:00 2001 From: Josh L Date: Sun, 27 Mar 2022 21:22:29 +0000 Subject: [PATCH 46/51] Specify what `Self` matches --- docs/design/generics/details.md | 7 +++++-- proposals/p1084.md | 17 +++++++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index 466f45ba59e78..7ae7cdd295e36 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -4391,8 +4391,11 @@ expressions match: - If the type part is omitted, it is rewritten to `Self` in the context of the declaration. -- `Self` is rewritted to its meaning in the scope it is used. So in - `class MyClass { ... }`, `Self` is rewritten to `MyClass`. +- `Self` is rewritted to its meaning in the scope it is used. In a class + scope, this should match the type name and optional parameter expression + after `class`. So in `class MyClass extends MyBase { ... }`, `Self` is + rewritten to `MyClass`. In `class Vector(T:! Movable) { ... }`, `Self` is + rewritten to `Vector(T:! Movable)`. - Types match if they have the same name after name and alias resolution and the same parameters, or are the same type parameter. - Interfaces match if they have the same name after name and alias resolution diff --git a/proposals/p1084.md b/proposals/p1084.md index 9bde92697854a..da9132f7b97b9 100644 --- a/proposals/p1084.md +++ b/proposals/p1084.md @@ -24,7 +24,8 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [No forward declaration of named constraints](#no-forward-declaration-of-named-constraints) - [Repeating `private` in both declaration and definition](#repeating-private-in-both-declaration-and-definition) - [Allow function bodies using incomplete interfaces](#allow-function-bodies-using-incomplete-interfaces) - - [Require parameter names to match when specified](#require-parameter-names-to-match-when-specified) + - [Don't require parameter names to match](#dont-require-parameter-names-to-match) + - [Allow deduced parameters to vary](#allow-deduced-parameters-to-vary) @@ -203,7 +204,7 @@ function call might use a function pointer instead of a witness table. This same argument led to the requirement that the interface's definition be visible at call sites as well. -### Require parameter names to match when specified +### Don't require parameter names to match We decided to diverge from C++ in requiring parameter names to match between declarations for a few reasons: @@ -218,3 +219,15 @@ This was discussed in [open discussion on 2022-03-14](https://docs.google.com/document/d/1cRrhRrmaUf2hVi2lFcHsYo2j0jI6t9RGZoYjWhRxp14/edit?resourcekey=0-xWHBEZ8zIqnJiB4yfBSLfA#heading=h.oqmpxtubjmkm) and [question-for-leads issue #1132](https://github.com/carbon-language/carbon-lang/issues/1132). + +### Allow deduced parameters to vary + +We decided to apply +[the same matching requirements for other parameter names](#dont-require-parameter-names-to-match) +to deduced parameters for consistency. We may in the future allow some rewrites +between equivalent expressions, such as between `Vector(T:! Type)` and +`[T:! Type] Vector(T)`, but for now we are starting with the more restrictive +rule. This wad discussed in +[open discussion on 2022-03-24](https://docs.google.com/document/d/1cRrhRrmaUf2hVi2lFcHsYo2j0jI6t9RGZoYjWhRxp14/edit?resourcekey=0-xWHBEZ8zIqnJiB4yfBSLfA#heading=h.w4zgqvarhnbn) +and in +[#syntax channel on Discord](https://discord.com/channels/655572317891461132/709488742942900284/953798170750615622). From ac3d570ebdb72f03472eec78af0d8588b0c7eeb0 Mon Sep 17 00:00:00 2001 From: josh11b Date: Fri, 8 Apr 2022 14:57:08 -0700 Subject: [PATCH 47/51] Apply suggestions from code review Co-authored-by: Richard Smith --- docs/design/generics/details.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index 7ae7cdd295e36..ef3b010a42fea 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -4315,7 +4315,8 @@ An interface or named constraint may be forward declared subject to these rules: - An incomplete interface or named constraint may be used as constraints in declarations of types, functions, interfaces, or named constraints. This includes an `impl as` or `extends` declaration inside an interface or named - constraint. + constraint, but excludes specifying the values for associated constants + because that would involve name lookup into the incomplete constraint. - An attempt to define the body of a generic function using an incomplete interface or named constraint is illegal. - An attempt to call a generic function using an incomplete interface or named @@ -4407,7 +4408,8 @@ For implementations to agree: - The presence of modifier keywords such as `external` before `impl` must match between a forward declaration and definition. -- If a both declarations include `where` clauses, that is neither use +- If either declaration includes a `where` clause, they must both include one. + If neither uses `where _`, they must match in that they produce the associated constants with the same values considered separately. @@ -4463,18 +4465,18 @@ class MyClass { // Definition of previously declared external impl. // Note: no need to repeat assignments to associated // constants. - external impl as Interface1 { } + external impl as Interface1 where _ { } // Definition of previously declared internal impl. // Note: allowed even though `MyClass` is incomplete. // Note: allowed but not required to repeat `where` // clause. - impl as Interface3 where .T3 = f32 { } + impl as Interface3 where .T3 = f32 { } // Redeclaration of previously declared internal impl. // Every internal implementation must be declared in // the class definition. - impl as Interface4; + impl as Interface4 where _; // Forward declaration of external implementation. external impl MyClass as Interface5 where .T5 = u64; @@ -4488,12 +4490,12 @@ class MyClass { // this library. // Definition of previously declared external impls. -external impl MyClass as Interface2 { } -external impl MyClass as Interface5 { } +external impl MyClass as Interface2 where _ { } +external impl MyClass as Interface5 where _ { } // Definition of previously declared internal impls. -impl MyClass as Interface4 { } -impl MyClass as Interface6 { } +impl MyClass as Interface4 where _ { } +impl MyClass as Interface6 where _ { } ``` ### Example of declaring interfaces with cyclic references From 11643866e9bf9a6d4ce7175acd125286f5b08c33 Mon Sep 17 00:00:00 2001 From: Josh L Date: Fri, 8 Apr 2022 22:14:57 +0000 Subject: [PATCH 48/51] Implement review suggestions --- docs/design/generics/details.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index ef3b010a42fea..24344cbe373d1 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -4308,6 +4308,12 @@ interface in its parameter list. There is a [workaround](#interfaces-with-parameters-constrained-by-the-same-interface) for the use cases when this would come up. +An expression forming a constraint, such as `C & D`, is incomplete if any of the +interfaces or constraints used in the expression are incomplete. A constraint +expression using a [`where` clause](#where-constraints), like `C where ...`, is +invalid if `C` is incomplete, since there is no way to look up member names of +`C` that appear after `where`. + An interface or named constraint may be forward declared subject to these rules: - The definition must be in the same file as the declaration. @@ -4346,7 +4352,9 @@ these rules: - The definition must be in the same library as the declaration. They must either be in the same file, or the declaration can be in the API file and - the definition in an impl file. + the definition in an impl file. **Future work:** Carbon may require the + definition of [parameterized impls](#parameterized-impls) to be in the API + file, to support separate compilation. - If there is both a forward declaration and a definition, only the first declaration must specify the assignment of associated constants with a `where` clause. Later declarations may omit the `where` clause by writing @@ -4409,9 +4417,8 @@ For implementations to agree: - The presence of modifier keywords such as `external` before `impl` must match between a forward declaration and definition. - If either declaration includes a `where` clause, they must both include one. - If neither uses - `where _`, they must match in that they produce the associated constants - with the same values considered separately. + If neither uses `where _`, they must match in that they produce the + associated constants with the same values considered separately. ### Declaration examples @@ -4608,7 +4615,8 @@ interface Vector { } ``` -A default function or method may also be defined out of line: +A default function or method may also be defined out of line, later in the same +file as the interface definition: ``` interface Vector { From 5cec063b389776cafb5e9e2eed22dccac6b66d60 Mon Sep 17 00:00:00 2001 From: Josh L Date: Fri, 8 Apr 2022 22:22:49 +0000 Subject: [PATCH 49/51] Checkpoint progress. --- docs/design/generics/overview.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/design/generics/overview.md b/docs/design/generics/overview.md index c9c6dc910a9da..2f6810f8bee90 100644 --- a/docs/design/generics/overview.md +++ b/docs/design/generics/overview.md @@ -654,8 +654,7 @@ external impl Distance as MultipliableWith(like f64) ... ## Future work -- Support functions should have a way to accept types that types that vary at - runtime. +- Functions should have a way to accept types that vary at runtime. - You should have the ability to mark entities as `upcoming` or `deprecated` to support evolution. - There should be a way to define generic associated and higher-ranked/kinded From 6e78b58a14f13b7c95da2102c243777b67161881 Mon Sep 17 00:00:00 2001 From: Josh L Date: Sat, 9 Apr 2022 00:05:50 +0000 Subject: [PATCH 50/51] Fix bad merge --- executable_semantics/syntax/parser.ypp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/executable_semantics/syntax/parser.ypp b/executable_semantics/syntax/parser.ypp index 68af5085cf51d..c0a6c8d51d84e 100644 --- a/executable_semantics/syntax/parser.ypp +++ b/executable_semantics/syntax/parser.ypp @@ -903,9 +903,9 @@ declaration: } | INTERFACE identifier LEFT_CURLY_BRACE declaration_list RIGHT_CURLY_BRACE { - auto ty_ty = arena->New(context.source_loc()); + auto ty_ty = arena -> New(context.source_loc()); auto self = - arena->New(context.source_loc(), "Self", ty_ty); + arena -> New(context.source_loc(), "Self", ty_ty); $$ = arena->New(context.source_loc(), $2, self, $4); } | impl_kind IMPL expression AS expression LEFT_CURLY_BRACE declaration_list RIGHT_CURLY_BRACE From 0348ec337193bddf1349e797d26fee6bf80178a7 Mon Sep 17 00:00:00 2001 From: josh11b Date: Sat, 9 Apr 2022 22:36:17 -0700 Subject: [PATCH 51/51] Update proposals/p1084.md Co-authored-by: Chandler Carruth --- proposals/p1084.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/p1084.md b/proposals/p1084.md index da9132f7b97b9..986aeb76790c5 100644 --- a/proposals/p1084.md +++ b/proposals/p1084.md @@ -227,7 +227,7 @@ We decided to apply to deduced parameters for consistency. We may in the future allow some rewrites between equivalent expressions, such as between `Vector(T:! Type)` and `[T:! Type] Vector(T)`, but for now we are starting with the more restrictive -rule. This wad discussed in +rule. This was discussed in [open discussion on 2022-03-24](https://docs.google.com/document/d/1cRrhRrmaUf2hVi2lFcHsYo2j0jI6t9RGZoYjWhRxp14/edit?resourcekey=0-xWHBEZ8zIqnJiB4yfBSLfA#heading=h.w4zgqvarhnbn) and in [#syntax channel on Discord](https://discord.com/channels/655572317891461132/709488742942900284/953798170750615622).