From e32937648fe28eff79b0e81421c637e237353a4d Mon Sep 17 00:00:00 2001 From: Josh L Date: Tue, 20 Sep 2022 23:38:57 +0000 Subject: [PATCH 01/33] Filling out template with PR 2200 --- proposals/p2200.md | 87 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 proposals/p2200.md diff --git a/proposals/p2200.md b/proposals/p2200.md new file mode 100644 index 0000000000000..9b6eee9281cbf --- /dev/null +++ b/proposals/p2200.md @@ -0,0 +1,87 @@ +# Basic templates + + + +[Pull request](https://github.com/carbon-language/carbon-lang/pull/2200) + + + +## Table of contents + +- [TODO: Initial proposal setup](#todo-initial-proposal-setup) +- [Abstract](#abstract) +- [Problem](#problem) +- [Background](#background) +- [Proposal](#proposal) +- [Details](#details) +- [Rationale](#rationale) +- [Alternatives considered](#alternatives-considered) + + + +## TODO: Initial proposal setup + +> TIP: Run `./new_proposal.py "TITLE"` to do new proposal setup. + +1. Copy this template to `new.md`, and create a commit. +2. Create a GitHub pull request, to get a pull request number. + - Add the `proposal draft` label to the pull request. +3. Rename `new.md` to `/proposals/p####.md`, where `####` should be the pull + request number. +4. Update the title of the proposal (the `TODO` on line 1). +5. Update the link to the pull request (the `####` on line 11). +6. Delete this section. + +TODOs indicate where content should be updated for a proposal. See +[Carbon Governance and Evolution](/docs/project/evolution.md) for more details. + +## Abstract + +TODO: Describe, in a succinct paragraph, the gist of this document. This +paragraph should be reproduced verbatim in the PR summary. + +## 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 + +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 and principles. This may evolve during review. Use links +to appropriate sections of [`/docs/project/goals.md`](/docs/project/goals.md), +and/or to documents in [`/docs/project/principles`](/docs/project/principles). +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 73a53fa718da66e50c5a0c223f1cc8924967c52e Mon Sep 17 00:00:00 2001 From: Josh L Date: Thu, 22 Sep 2022 15:51:32 +0000 Subject: [PATCH 02/33] Checkpoint progress. --- proposals/p2200.md | 166 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 144 insertions(+), 22 deletions(-) diff --git a/proposals/p2200.md b/proposals/p2200.md index 9b6eee9281cbf..5bbe8c86ec4d2 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -12,33 +12,19 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ## Table of contents -- [TODO: Initial proposal setup](#todo-initial-proposal-setup) - [Abstract](#abstract) - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) - [Details](#details) + - [Value phases](#value-phases) + - [Name lookup](#name-lookup) + - [Transition from C++ templates to Carbon checked generics](#transition-from-c-templates-to-carbon-checked-generics) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) -## TODO: Initial proposal setup - -> TIP: Run `./new_proposal.py "TITLE"` to do new proposal setup. - -1. Copy this template to `new.md`, and create a commit. -2. Create a GitHub pull request, to get a pull request number. - - Add the `proposal draft` label to the pull request. -3. Rename `new.md` to `/proposals/p####.md`, where `####` should be the pull - request number. -4. Update the title of the proposal (the `TODO` on line 1). -5. Update the link to the pull request (the `####` on line 11). -6. Delete this section. - -TODOs indicate where content should be updated for a proposal. See -[Carbon Governance and Evolution](/docs/project/evolution.md) for more details. - ## Abstract TODO: Describe, in a succinct paragraph, the gist of this document. This @@ -46,22 +32,158 @@ paragraph should be reproduced verbatim in the PR summary. ## Problem -TODO: What problem are you trying to solve? How important is that problem? Who -is impacted by it? +FIXME: Starting with +[#24: Generics goals](https://github.com/carbon-language/carbon-lang/pull/24), +templates have been \_\_, not an accepted part of the design. We now understand +enough about how they should fit into the language to decide that we are +including the feature in the language, and how. + +FIXME: Use cases addressed: + +- FIXME: Transition for C++ templates +- FIXME: Familiarity for C++ developers +- FIXME: Gate for features we don't want to expose by default in checked + generics for software engineering reasons since they pierce abstraction + boundaries + + - FIXME: Compile-time duck typing features that use the structural + properties of types, like having a method with a particular name, rather + than semantic properties like implementing an interface. + - FIXME: Branching in code based on type identity, for example in a + `match` statement, which would effectively be an implementation of + `typecase` + + > Thatte, Satish. (1994). Semantics of Type Classes Revisited.. 208-219. + > 10.1145/182409.182459. + +Out of scope for this proposal are any questions about passing a checked generic +argument value to a template parameter. See question-for-leads issue +[#2153: Generics calling templates](https://github.com/carbon-language/carbon-lang/issues/2153). ## Background TODO: Is there any background that readers should consider to fully understand this problem and your approach to solving it? +There have been a number of prior proposals and questions-for-leads issues on +templates on which this proposal builds: + +- Proposal + [#24: Generics goals](https://github.com/carbon-language/carbon-lang/pull/24) + talked about the reasons for templates, without committing Carbon to + including them. These reasons include making it easier to transition C++ + template code to Carbon and providing functionality outside of what we want + to support with checked generics. +- Proposal + [#447: Generics terminology](https://github.com/carbon-language/carbon-lang/pull/447) + defined terminology. This included some of the differences between checked + and template generics, and definitions for terms like _instantiation_. +- Proposal + [#553: Generic details part 1](https://github.com/carbon-language/carbon-lang/pull/553) + defined `auto` as a template construct, and described how templates do not + require constraints to find member names. +- Question-for-leads issue + [#565: Generic syntax to replace provisional `$`s](https://github.com/carbon-language/carbon-lang/issues/565) + implemented in proposal + [#676: `:!` generic syntax](https://github.com/carbon-language/carbon-lang/pull/676) + defined the syntax for template bindings. +- Proposal + [#731: Generics details 2: adapters, associated types, parameterized interfaces](https://github.com/carbon-language/carbon-lang/pull/731) + included that template values may be passed to generic parameters. +- Proposal + [#818: Constraints for generics](https://github.com/carbon-language/carbon-lang/pull/818) + included `template constraint` to defined named constraints with fewer + restrictions for use with template parameters. +- Proposal + [#875: Principle: information accumulation](https://github.com/carbon-language/carbon-lang/pull/875) + considered how the principle benefited and was impacted by templates. +- Question-for-leads issue + [#949: Constrained template name lookup](https://github.com/carbon-language/carbon-lang/issues/949) + implemented in proposal + [#989: Member access expressions](https://github.com/carbon-language/carbon-lang/pull/989) + defined how name lookup works for template parameters. It provided a path to + incrementally adopt constraints on template parameters, a stepping stone to + transitioning to checked generics. +- Proposal + [#950: Generics details 6: remove facets](https://github.com/carbon-language/carbon-lang/pull/950) + included the impact on the semantics of templates in its rationale. +- Proposal + [#1146: Generic details 12: parameterized types](https://github.com/carbon-language/carbon-lang/pull/1146) + allowed template type parameters. +- Proposal + [#1270: Update and expand README content and motivation for Carbon](https://github.com/carbon-language/carbon-lang/pull/1270) + advertised that Carbon would support templates for "seamless C++ interop." +- Terminology was updated in proposal + [#2138: Checked and template generic terminology](https://github.com/carbon-language/carbon-lang/pull/2138). + +TODO: Update if proposal +[#2188: Pattern matching syntax and semantics](https://github.com/carbon-language/carbon-lang/pull/2188) +is accepted first. + +Terminology: FIXME + +- [Dependent names](/docs/design/generics/terminology.md#dependent-names) +- [Instantiation](/docs/design/generics/terminology.md#instantiation) + ## Proposal -TODO: Briefly and at a high level, how do you propose to solve the problem? Why -will that in fact solve it? +FIXME Templates are officially a feature by Carbon. + +FIXME Describe instantiation and monomorphization errors. + +In many ways, template generic parameters work like checked generic parameters. +The following are true for any kind of generic parameter: + +- The value passed to a generic parameter must be able to be evaluated at + compile time. +- Generic parameters may have constraints that will be enforced by the + compiler on the value supplied by the caller. +- FIXME + +The main differences between checked and templated generics are: + +- Type checking of any expression dependent on a templated parameter may not + be completed until its value is known. +- FIXME: Name lookup + +FIXME: In contrast with C++ templates, no SFINAE. Specialization of a template +affects implementation, not API, unlike C++'s `vector` situation. No +current support for template-template parameters. ## Details -TODO: Fully explain the details of the proposed solution. +### Value phases + +FIXME + +### Name lookup + +FIXME + +Note that in some cases of looking up a qualified name, lookup will not depend +on the value of the template parameter and can be checked before instantiation, +as in: + +```carbon +interface A { + fn F[me: Self](); +} + +fn G[template T:! A](x: T) { + // No question what this resolves to. + x.(A.F)(); + // Monomorphization error if `T.F` means something + // different than `T.(A.F)`. + x.F(); +} +``` + +### Transition from C++ templates to Carbon checked generics + +FIXME: Explain story, even though already accepted in uestion-for-leads issue +[#949: Constrained template name lookup](https://github.com/carbon-language/carbon-lang/issues/949) +and implemented in proposal +[#989: Member access expressions](https://github.com/carbon-language/carbon-lang/pull/989). ## Rationale From fee5fadf7ed937ab4acbcf870f93d1b7dd216648 Mon Sep 17 00:00:00 2001 From: Josh L Date: Sat, 24 Sep 2022 00:38:47 +0000 Subject: [PATCH 03/33] Checkpoint progress. --- proposals/p2200.md | 177 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 135 insertions(+), 42 deletions(-) diff --git a/proposals/p2200.md b/proposals/p2200.md index 5bbe8c86ec4d2..ee506a5640349 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -1,4 +1,4 @@ -# Basic templates +# Template generics + +**Note:** the naming of value phases is the subject of open question-for-leads +issue +[#1391: New name for "constant" value phase](https://github.com/carbon-language/carbon-lang/issues/1391). +This terminology comes from +[a discussion in #typesystem on Discord](https://discord.com/channels/655572317891461132/708431657849585705/992817321074774098), +in particular +[this message](https://discord.com/channels/655572317891461132/708431657849585705/994396535561408623). + +### Template constraints + FIXME ### Name lookup -FIXME +Name lookup for templates has already been decided in question-for-leads issue +[#949: Constrained template name lookup](https://github.com/carbon-language/carbon-lang/issues/949) +and proposal +[#989: Member access expressions](https://github.com/carbon-language/carbon-lang/pull/989). +Briefly, name lookup is done both in the actual type value supplied at the call +site and the constraints on the parameter. If the name is found in both, it is +an error if they resolve to different entities. + +Look up into the calling type gives _compile-time duck typing_ behavior. +Example: FIXME. Note that in some cases of looking up a qualified name, lookup will not depend on the value of the template parameter and can be checked before instantiation, @@ -170,21 +248,36 @@ interface A { } fn G[template T:! A](x: T) { - // No question what this resolves to. + // No question what this resolves to: x.(A.F)(); - // Monomorphization error if `T.F` means something - // different than `T.(A.F)`. + + // Will generate a monomorphization error if + // `T.F` means something different than `T.(A.F)`: x.F(); } ``` ### Transition from C++ templates to Carbon checked generics -FIXME: Explain story, even though already accepted in uestion-for-leads issue +FIXME: Explain story, even though already accepted in question-for-leads issue [#949: Constrained template name lookup](https://github.com/carbon-language/carbon-lang/issues/949) and implemented in proposal [#989: Member access expressions](https://github.com/carbon-language/carbon-lang/pull/989). +FIXME: Stages: + +- An unconstrained C++ template +- An unconstrained Carbon template generic +- A constrained Carbon template generic +- A constrained Carbon checked generic + +FIXME: How we get failures instead of silent changes in meaning/semantics. + +### Branching on type identity + +FIXME: Branching in code based on type identity, for example in a `match` +statement, which would effectively be an implementation of "typecase." + ## Rationale TODO: How does this proposal effectively advance Carbon's goals? Rather than From a9f0c43238890bfdceff9af1d631e454c7c47fa2 Mon Sep 17 00:00:00 2001 From: Josh L Date: Tue, 27 Sep 2022 00:18:34 +0000 Subject: [PATCH 04/33] Checkpoint progress. --- proposals/p2200.md | 129 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 115 insertions(+), 14 deletions(-) diff --git a/proposals/p2200.md b/proposals/p2200.md index ee506a5640349..1b7dbbf861745 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -20,18 +20,26 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [Details](#details) - [Syntax](#syntax) - [Value phases](#value-phases) + - [`auto`](#auto) - [Template constraints](#template-constraints) - [Name lookup](#name-lookup) - [Transition from C++ templates to Carbon checked generics](#transition-from-c-templates-to-carbon-checked-generics) - [Branching on type identity](#branching-on-type-identity) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) + - [Only checked generics](#only-checked-generics) + - [SFINAE](#sfinae) + - [Template API specialization](#template-api-specialization) +- [Future work](#future-work) + - [Expanded template constraints](#expanded-template-constraints) + - [Predicates: constraints on values](#predicates-constraints-on-values) + - [Generics calling templates](#generics-calling-templates) ## Abstract -TODO: Describe, in a succinct paragraph, the gist of this document. This +FIXME: Describe, in a succinct paragraph, the gist of this document. This paragraph should be reproduced verbatim in the PR summary. ## Problem @@ -163,25 +171,59 @@ The main differences between checked and templated generics are: As a consequence of these differences, type checking of any expression dependent on a templated parameter may not be completed until its value is known. In -addition, templated generics support branching on the value of a templated type -value. - -FIXME: In contrast with C++ templates, no SFINAE. Specialization of a template -affects implementation, not API, unlike C++'s `vector` situation. No -current support for template-template parameters. +addition, templated generics support branching on the value of a templated type. + +In contrast with C++ templates, with Carbon template generics: + +- Substitution failure is an error. In C++, the SFINAE rule + ([cppreference](https://en.cppreference.com/w/cpp/language/sfinae), + [wikipedia](https://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error)) + will skip functions in overload resolution that fail to instantiate. + Instead, Carbon template parameters use constraints to control when the + function is available. +- Carbon template specialization does not allow ad hoc changes to the API of + the function or type being specialized, only its implementation. This is in + contrast to C++, where + [C++'s `std::vector`](https://en.cppreference.com/w/cpp/container/vector_bool) + has different return types for certain methods. Anything that can vary in an + API must be explicitly marked using associated types of an interface, as is + described in the + ["parameterized type specialization" design](/docs/design/generics/details.md#specialization). ## Details ### Syntax -FIXME: Template generic bindings are declared using the `template` keyword in -addition to the `:!` of all generic bindings. +Template generic bindings are declared using the `template` keyword in addition +to the `:!` of all generic bindings. All generic bindings can only be used in +`let` context to produce r-values, not in a `var` context to produce l-values. +This includes `let` declarations, as in: + +```carbon +// `N` is a constant that may be used in types. +let template N:! i64 = 4; +var my_array: [u8; N] = (255, 128, 64, 255); +``` + +Function parameters also default to a `let` context and may use `template`: -FIXME: Example. +```carbon +// `U` is a templated type parameter that must be specified +// explicitly by the caller. +fn Cast[template T:! Type](x: T, template U:! Type) -> U { + return x as U; +} + +let x: i32 = 7; +// Calls `Cast` with `T` set to `i32` and `U` set to `i64`. +let y: auto = Cast(x, i64); +// Type of `y` is `i64`. +``` -Note: (checked and template) generic bindings may only be used in `let` context -(like parameters or `let` declarations) to produce r-values, not in a `var` -context to produce l-values. +Branching on the value of a templated type will be done using a `match` +statement, but is outside the scope of this proposal. See instead pending +proposal +[#2188: Pattern matching syntax and semantics](https://github.com/carbon-language/carbon-lang/pull/2188). ### Value phases @@ -221,6 +263,29 @@ This terminology comes from in particular [this message](https://discord.com/channels/655572317891461132/708431657849585705/994396535561408623). +### `auto` + +The `auto` keyword is a shortcut for an unnamed templated type, as in: + +```carbon +// Type of `x` is the same as the return type of function `F`. +let x: auto = F(); +``` + +This was first added to Carbon in proposal +[#553: Generic details part 1](https://github.com/carbon-language/carbon-lang/pull/553) +and further specified by open proposal +[#2188: Pattern matching syntax and semantics](https://github.com/carbon-language/carbon-lang/pull/2188). + +The `auto` keyword may also be used to omit the return type, as specified in +[#826: Function return type inference](https://github.com/carbon-language/carbon-lang/pull/826). +This feature is being reconsidered in the open question-for-leads issue +[#1008: How much complexity should we invest in deduced/inferred return types?](https://github.com/carbon-language/carbon-lang/issues/1008). + +The semantics of `let x:! auto = ...` is the subject of open question-for-leads +issue +[#996: Generic `let` with `auto`?](https://github.com/carbon-language/carbon-lang/issues/996). + ### Template constraints FIXME @@ -299,4 +364,40 @@ For example: ## Alternatives considered -TODO: What alternative solutions have you considered? +### Only checked generics + +FIXME: Rust's approach + +FIXME: Reasons are above + +### SFINAE + +FIXME: confusion about source of error + +### Template API specialization + +FIXME: See approach: +[specialization of parameterized types](/docs/design/generics/details.md#specialization) + +FIXME: Incompatible with checked generics + +## Future work + +### Expanded template constraints + +FIXME: follow C++ concepts to see the range of kinds of constraints found to be +useful in practice: + +- https://en.cppreference.com/w/cpp/language/constraints +- https://en.cppreference.com/w/cpp/language/requires +- [Examples of named requirements for the C++ standard library](https://en.cppreference.com/w/cpp/named_req) + +### Predicates: constraints on values + +FIXME: See question-for-leads issue +[#2153: Generics calling templates](https://github.com/carbon-language/carbon-lang/issues/2153). + +### Generics calling templates + +FIXME: See question-for-leads issue +[#2153: Generics calling templates](https://github.com/carbon-language/carbon-lang/issues/2153). From 7eb2a26a70faee7e4c3033dc8bdbbd484ae78335 Mon Sep 17 00:00:00 2001 From: Josh L Date: Wed, 28 Sep 2022 22:50:44 +0000 Subject: [PATCH 05/33] Checkpoint progress. --- proposals/p2200.md | 143 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 118 insertions(+), 25 deletions(-) diff --git a/proposals/p2200.md b/proposals/p2200.md index 1b7dbbf861745..a956015f67703 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -189,6 +189,9 @@ In contrast with C++ templates, with Carbon template generics: API must be explicitly marked using associated types of an interface, as is described in the ["parameterized type specialization" design](/docs/design/generics/details.md#specialization). +- Constraints on a Carbon template type affect how lookup is done into that + type, as described in + [proposal #989](https://github.com/carbon-language/carbon-lang/pull/989). ## Details @@ -229,33 +232,27 @@ proposal R-values are divided into three different _value phases_: -FIXME: update these copy pastes - - +So: -**Note:** the naming of value phases is the subject of open question-for-leads +- A `let template T:! ...` or `fn F(template T:! ...)` declaration binds `T` + with constant value phase, +- A `let T:! ...` or `fn F(T:! ...)` declaration binds `T` with symbolic value + phase, +- A `let x: ...` or `fn F(x: ...)` declaration binds `x` with runtime value + phase. + +**Note:** The naming of value phases is the subject of open question-for-leads issue [#1391: New name for "constant" value phase](https://github.com/carbon-language/carbon-lang/issues/1391). This terminology comes from @@ -263,6 +260,11 @@ This terminology comes from in particular [this message](https://discord.com/channels/655572317891461132/708431657849585705/994396535561408623). +**Note:** The situations in which a value with one phase can be used to +initialize a binding with a different value phase is partially considered in +question-for-leads issue +[#2153: Generics calling templates](https://github.com/carbon-language/carbon-lang/issues/2153). + ### `auto` The `auto` keyword is a shortcut for an unnamed templated type, as in: @@ -288,7 +290,57 @@ issue ### Template constraints -FIXME +Template constraints have already been introduced in proposal +[#818: Constraints for generics](https://github.com/carbon-language/carbon-lang/pull/818). +In brief, a `template constraint` declaration is like a `constraint` +declaration, except that it may also contain function and field declarations. +Only types with matching declarations will satisfying the template constraint. +Note that the matching declarations must be in the type declaration itself, not +in an external impl. + +```carbon +interface A { fn F[me: Self](); } +interface B { fn F[me: Self](); } +class C { } +external impl C as A; +external impl C as B; +template constraint HasF { + fn F[me: Self](); +} +fn G[template T:! HasF](x: T); +var y: C = {}; +// Can't call `G` with with `y` since it doesn't have any internal +// implementation of a method `F` satisfying `HasF`, even though `C` +// externally implements both `A` and `B` with such an `F`. May +// define an adapter for `C` to get a type that implements `HasF`, +// with `A.F`, `B.F`, or some other definition. +``` + +This was discussed in +[#generics-and-templates on 2022-09-20](https://discord.com/channels/655572317891461132/941071822756143115/1021903925613449316). + +Whether template constraints may be used as constraints on checked-generic +parameters is being considered in question-for-leads issue +[#2153: Generics calling templates](https://github.com/carbon-language/carbon-lang/issues/2153). +Even if we allow a checked-generic parameter to use a template constraint, we +want to focus checked generics on semantic properties encapsulated in +interfaces, not structural properties tested by template constraints. So we +would not allow lookup into checked-generic type to find type members outside of +an interface: + +```carbon +template constraint HasF { + fn F[me: Self](); +} +// If we allow a checked generic to use a template +// constraint, as in: +fn H[T:! HasF](x: T) { + // We still will not support calling `F` on `x`: + // ❌ x.F(); +} +``` + +These members would only be found using a template type parameter. ### Name lookup @@ -300,8 +352,47 @@ Briefly, name lookup is done both in the actual type value supplied at the call site and the constraints on the parameter. If the name is found in both, it is an error if they resolve to different entities. -Look up into the calling type gives _compile-time duck typing_ behavior. -Example: FIXME. +Look up into the calling type gives _compile-time duck typing_ behavior, much +like C++ templates, as in: + +```carbon +fn F[template T:! Type](x: T) { + // Calls whatever `M` is declared in `T`, and will + // fail if `T` does not have a matching member `M`. + x.M(); +} + +class C1 { fn M[me: Self](); } +var x1: C1 = {}; +// Calls `F` with `T` equal to `C1`, which succeeds. +F(x1); + +class C2 { fn M[addr me: Self*](); } +var x2: C2 = {}; +// Calls `F` with `T` equal to `C2`, which fails, +// since `x` is an r-value in `F` and `C2.M` requires +// an l-value. +F(x2); + +class C3 { fn M[me: Self](p: i32); } +var x3: C3 = {}; +// Calls `F` with `T` equal to `C3`, which fails, +// since `C3.M` must be passed an argument value. +F(x3); + +class C4 { fn M[me: Self](p: i32 = 4); } +var x4: C4 = {}; +// Calls `F` with `T` equal to `C4`, which succeeds, +// using the default value of `4` for `p` when +// calling `C4.M`. +F(x4); + +class C5 { var v: i32; } +var x5: C5 = {.v = 5}; +// Calls `F` with `T` equal to `C5`, which fails, +// since `T` has no member `M`. +F(x5); +``` Note that in some cases of looking up a qualified name, lookup will not depend on the value of the template parameter and can be checked before instantiation, @@ -313,11 +404,13 @@ interface A { } fn G[template T:! A](x: T) { - // No question what this resolves to: + // No question what this resolves to, can be checked + // when `G` is defined: x.(A.F)(); // Will generate a monomorphization error if - // `T.F` means something different than `T.(A.F)`: + // `T.F` means something different than `T.(A.F)`, + // can only be checked when `G` is called: x.F(); } ``` From f4197b5c528ee3cc17c4216b05b6120bc65ca289 Mon Sep 17 00:00:00 2001 From: Josh L Date: Wed, 5 Oct 2022 04:58:54 +0000 Subject: [PATCH 06/33] Checkpoint progress. --- proposals/p2200.md | 162 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 145 insertions(+), 17 deletions(-) diff --git a/proposals/p2200.md b/proposals/p2200.md index a956015f67703..6c638d4dfe99a 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -24,12 +24,17 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [Template constraints](#template-constraints) - [Name lookup](#name-lookup) - [Transition from C++ templates to Carbon checked generics](#transition-from-c-templates-to-carbon-checked-generics) + - [To template Carbon with structural constraints](#to-template-carbon-with-structural-constraints) + - [To interface constraints](#to-interface-constraints) + - [To checked generic](#to-checked-generic) + - [Validity can depend on value](#validity-can-depend-on-value) - [Branching on type identity](#branching-on-type-identity) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Only checked generics](#only-checked-generics) - [SFINAE](#sfinae) - [Template API specialization](#template-api-specialization) + - [Value phase of bindings determined by initializer](#value-phase-of-bindings-determined-by-initializer) - [Future work](#future-work) - [Expanded template constraints](#expanded-template-constraints) - [Predicates: constraints on values](#predicates-constraints-on-values) @@ -138,7 +143,8 @@ is accepted first. [C++](https://en.cppreference.com/w/cpp/language/dependent_name), but is different from ["dependent types"](https://en.wikipedia.org/wiki/Dependent_type). -- [_Instantiation_](/docs/design/generics/terminology.md#instantiation) or +- [_Instantiation_](/docs/design/generics/terminology.md#instantiation), + _substitution_, or [_monomorphizaton_](https://en.wikipedia.org/wiki/Monomorphization) is the process of duplicating the implementation of a function and then substituting in the values of any (checked or template) generic arguments. @@ -146,6 +152,13 @@ is accepted first. known as _monomorphization errors_. These mostly occur in expressions dependent on some template parameter, but can also occur for other reasons like hitting an implementation limit. +- _SFINAE_ stands for "Substitution failure is not an error", which is the + policy in C++, see + [cppreference](https://en.cppreference.com/w/cpp/language/sfinae), + [wikipedia](https://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error). + It means that functions from an overload set with monorphization errors, or + "substitution failure", are simply ignored instead of causing compilation to + fail. ## Proposal @@ -175,9 +188,7 @@ addition, templated generics support branching on the value of a templated type. In contrast with C++ templates, with Carbon template generics: -- Substitution failure is an error. In C++, the SFINAE rule - ([cppreference](https://en.cppreference.com/w/cpp/language/sfinae), - [wikipedia](https://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error)) +- Substitution failure is an error. In C++, [the SFINAE rule](#terminology) will skip functions in overload resolution that fail to instantiate. Instead, Carbon template parameters use constraints to control when the function is available. @@ -265,6 +276,10 @@ initialize a binding with a different value phase is partially considered in question-for-leads issue [#2153: Generics calling templates](https://github.com/carbon-language/carbon-lang/issues/2153). +FIXME: what is definitely allowed + +FIXME: value phase of expressions + ### `auto` The `auto` keyword is a shortcut for an unnamed templated type, as in: @@ -293,10 +308,11 @@ issue Template constraints have already been introduced in proposal [#818: Constraints for generics](https://github.com/carbon-language/carbon-lang/pull/818). In brief, a `template constraint` declaration is like a `constraint` -declaration, except that it may also contain function and field declarations. -Only types with matching declarations will satisfying the template constraint. -Note that the matching declarations must be in the type declaration itself, not -in an external impl. +declaration, except that it may also contain function and field declarations, +called _structural constraints_. Only types with matching declarations will +satisfying the template constraint. Note that the declarations matching the +structural constraints must be in the type declaration itself, not in an +external impl. ```carbon interface A { fn F[me: Self](); } @@ -319,6 +335,26 @@ var y: C = {}; This was discussed in [#generics-and-templates on 2022-09-20](https://discord.com/channels/655572317891461132/941071822756143115/1021903925613449316). +Structural constraints do not affect [name lookup](#name-lookup) into template +type parameters. They guarantee that a name will be available in the type, but +don't change the outcome. + +```carbon +template constraint HasF { + fn F[me: Self](); +} +class C { + fn F[me: Self](); +} +fn G[template T:! HasF](x: T) { + x.F(); +} +var y: C = {}; +// Call to `F` inside `G` is not ambiguous since +// `C.F` and `HasF.F` refer to the same function. +G(y); +``` + Whether template constraints may be used as constraints on checked-generic parameters is being considered in question-for-leads issue [#2153: Generics calling templates](https://github.com/carbon-language/carbon-lang/issues/2153). @@ -332,7 +368,7 @@ an interface: template constraint HasF { fn F[me: Self](); } -// If we allow a checked generic to use a template +// ❓ If we allow a checked generic to use a template // constraint, as in: fn H[T:! HasF](x: T) { // We still will not support calling `F` on `x`: @@ -342,6 +378,11 @@ fn H[T:! HasF](x: T) { These members would only be found using a template type parameter. +[Expanding the kinds of template constraints](#expanded-template-constraints) +and +[defining a way to put constraints on values](#predicates-constraints-on-values) +are both [future work](#future-work). + ### Name lookup Name lookup for templates has already been decided in question-for-leads issue @@ -349,8 +390,8 @@ Name lookup for templates has already been decided in question-for-leads issue and proposal [#989: Member access expressions](https://github.com/carbon-language/carbon-lang/pull/989). Briefly, name lookup is done both in the actual type value supplied at the call -site and the constraints on the parameter. If the name is found in both, it is -an error if they resolve to different entities. +site and the interface constraints on the parameter. If the name is found in +both, it is an error if they resolve to different entities. Look up into the calling type gives _compile-time duck typing_ behavior, much like C++ templates, as in: @@ -417,19 +458,100 @@ fn G[template T:! A](x: T) { ### Transition from C++ templates to Carbon checked generics +We have a specific +[goal for generics](/docs/design/generics/goals.md#upgrade-path-from-templates) +that we have a smooth story for transitioning from C++ templates to Carbon +checked generics. Adding template generics to Carbon allows this to be done in +steps. These steps serve two purposes. One is to allow any updates needed for +callers and types used as parameters to be done incrementally. The second is to +avoid any silent changes in semantics that would occur from jumping directly to +Carbon checked generics. Each step will either preserve the meaning of the code +or result in compile failures. + +#### To template Carbon with structural constraints + +The first step is to convert the C++ function with one or more template +parameters to a Carbon function with template generic parameters. Any +[non-type template parameters](https://en.cppreference.com/w/cpp/language/template_parameters#Non-type_template_parameter) +can be converted to template generic parameters with the equivalent type, as in: + +``` +// This C++ function: +void F_CPlusPlus(); + +// gets converted to Carbon: +fn F_Carbon(template N:! i32); +``` + +Other template parameters can either be declared without constraints, using +`template T:! Type`, or using [structural constraints](#template-constraints). + +To see if this transition can cause silent changes in meaning, consider how this +new Carbon function will be different from the old C++ one: + +- The conversion of the body of the code in the function could introduce + differences, but only template concerns are in scope for this proposal. +- The C++ code could use ad hoc API specialization. The only way to translate + that to Carbon is through + [explicit parameterization of the API](/docs/design/generics/details.md#specialization), + which is not expected to introduce silent changes in meaning. +- The C++ code could rely on [SFINAE](#terminology). C++ uses of + `std::enable_if` should be translated to equivalent + [template constraints](#template-constraints). Generally making substitution + failure an error is expected to make less code compile, not introduce silent + changes in meaning. +- As long as the constraints on template type parameters are + [structural](#template-constraints) and not interface constraints, the name + lookup rules into those type parameters will consistently look in the type + for both C++ and Carbon. + +#### To interface constraints + FIXME: Explain story, even though already accepted in question-for-leads issue [#949: Constrained template name lookup](https://github.com/carbon-language/carbon-lang/issues/949) and implemented in proposal [#989: Member access expressions](https://github.com/carbon-language/carbon-lang/pull/989). -FIXME: Stages: +FIXME: Step 1 is to define or identify the interfaces that will be required. Do +the existing types being used with this template already provide this +functionality by implementing an existing interface? + +FIXME: Step 2: Two choices, for each interface to be required: + +FIXME: A. implement interface for every type before changing from template +constraint to interface. + +FIXME: B. Create templated impl of interface, providing an implementation of +interface for every type satisfying template constraint. Can then switch +function to use interface instead of template constraint. Individual +implementations of interface will take priority over blanket impl. Can remove +blanket impl once interface implemented for all types. -- An unconstrained C++ template -- An unconstrained Carbon template generic -- A constrained Carbon template generic -- A constrained Carbon checked generic +FIXME: Step 3: Callers will fail if type doesn't implement interface or there is +a name conflict between the type and interface. -FIXME: How we get failures instead of silent changes in meaning/semantics. +FIXME: Can add qualifications to the body of the templated function to specify +which names should come from type and which from interfaces required by +constraints. + +#### To checked generic + +FIXME: Name lookup will stop looking at the type. Definition will compile as +long as all names can be found in constraints, which is the condition for this +change being safe. + +FIXME: Qualifications may be removed. If they are needed, compiler will complain +about ambiguity in the definition of the function. + +### Validity can depend on value + +FIXME: A templated parameter may be used in ways where the validity of the +result depends on the value of the parameter, not just its type. Example: +whether two array types are compatible depends on whether they have the same +size. With a symbolic constant sizes, they will only be considered equal if the +compiler can show that the two sizes are equal always symbolicly. If the size is +a template parameter, the checking will be delayed until the value of the +template parameter is known. ### Branching on type identity @@ -474,6 +596,12 @@ FIXME: See approach: FIXME: Incompatible with checked generics +### Value phase of bindings determined by initializer + +FIXME: Types versus non-type values. Name lookup rules different for types. + +FIXME: Link to open discussion notes + ## Future work ### Expanded template constraints From d1f5f91b9ec64612ee6e84350a157722e3eef883 Mon Sep 17 00:00:00 2001 From: Josh L Date: Thu, 6 Oct 2022 04:44:47 +0000 Subject: [PATCH 07/33] Checkpoint progress. --- proposals/p2200.md | 161 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 135 insertions(+), 26 deletions(-) diff --git a/proposals/p2200.md b/proposals/p2200.md index 6c638d4dfe99a..bb22922aace48 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -507,41 +507,150 @@ new Carbon function will be different from the old C++ one: #### To interface constraints -FIXME: Explain story, even though already accepted in question-for-leads issue -[#949: Constrained template name lookup](https://github.com/carbon-language/carbon-lang/issues/949) -and implemented in proposal -[#989: Member access expressions](https://github.com/carbon-language/carbon-lang/pull/989). +The next step is to switch from structural constraints to interface constraints. +The interfaces that are providing the functionality that the function relies on +must be identified or created. In some cases this could be done automatically +when names are resolved consistently to interface methods in the types currently +being used to instantiate the function. Once that is done, there are two +approaches: + +- Implement the interface for every instantiating type. Once that is done, the + function's constraint can be updated. +- Alternatively, a blanket implementation of the interface could be defined + for any type implementing the structural constraints so that the function's + constraint can be updated first. After that, the interface can be + implemented for types individually, overriding the blanket implementation + until the blanket implementation is no longer needed. This second choice + requires changes to the library defining the interface, and is most + appropriate when it is a new interface specifically created for this + function. + +In either case, the compiler will give an error if the interface is not +implemented for some types before the step is finished. + +An example of the second approach, starting with a templated function with a +structural constraint: -FIXME: Step 1 is to define or identify the interfaces that will be required. Do -the existing types being used with this template already provide this -functionality by implementing an existing interface? +```carbon +template constraint HasF { + fn F[me: Self](); +} -FIXME: Step 2: Two choices, for each interface to be required: +fn G[template T:! HasF](x: T) { + x.F(); +} -FIXME: A. implement interface for every type before changing from template -constraint to interface. +class C { + fn F[me: Self](); +} +var y: C = {}; +G(y); +``` -FIXME: B. Create templated impl of interface, providing an implementation of -interface for every type satisfying template constraint. Can then switch -function to use interface instead of template constraint. Individual -implementations of interface will take priority over blanket impl. Can remove -blanket impl once interface implemented for all types. +First, a new interface is created with a blanket implementation and the +function's constraints are updated to use it instead. Calls in the function body +should be qualified to avoid ambiguity errors. -FIXME: Step 3: Callers will fail if type doesn't implement interface or there is -a name conflict between the type and interface. +```carbon +template constraint HasF { + fn F[me: Self](); +} -FIXME: Can add qualifications to the body of the templated function to specify -which names should come from type and which from interfaces required by -constraints. +// New interface +interface NewF { + fn DoF[me: Self](); +} -#### To checked generic +// Blanket implementation +external impl forall [template T:! HasF] T as NewF { + // If the functions are identical, can instead do: + // alias DoF = T.F; + fn DoF[me: Self]() { + me.F(); + // Or: me.(T.F)(); + } +} + +// Changed constraint +fn G[template T:! NewF](x: T) { + // Call function from interface + x.(NewF.DoF)(); + // Could use `x.DoF();` instead, but that will + // give a compile error if `T` has a definition + // for `DoF` in addition to the one in `NewF`. +} -FIXME: Name lookup will stop looking at the type. Definition will compile as -long as all names can be found in constraints, which is the condition for this -change being safe. +class C { + fn F[me: Self](); +} +var y: C = {}; +// Still works since `C` implements `NewF` +// from blanket implementation. +G(y); +``` + +Then the interface is implemented for types used as parameters: + +```carbon +// ... +class C { + impl as NewF { + // `NewF.DoF` will be called by `G`, not `C.F`. + fn DoF[me: Self](); + } + // No longer needed: fn F[me: Self](); +} +// ... +``` + +Once all types have implemented the new interface, the blanket implementation +can be removed: + +```carbon +// Template constraint `HasF` no longer needed. + +// New interface +interface NewF { + fn DoF[me: Self](); +} + +// Blanket implementation no longer needed. + +fn G[template T:! NewF](x: T) { + x.DoF(); +} + +class C { + impl as NewF { + fn DoF[me: Self](); + } +} +var y: C = {}; +// `C` implements `NewF` directly. +G(y); +``` + +The [name lookup rules](#name-lookup) ensure that unqualified names will only +have one possible meaning, or the compiler will report an error. This error may +be resolved by adding qualifications, which is done with the context of an +ambiguity for a specific type. This avoids silent changes in the meaning of the +code. Once the disambiguating qualifications have been added, the transition to +checked generic becomes safe. + +#### To checked generic -FIXME: Qualifications may be removed. If they are needed, compiler will complain -about ambiguity in the definition of the function. +Once all needed qualifications are in place, the `template` keyword can be +removed. After this, names will only be looked up in the interface constraints, +not the type. If this does not cover the names used by the function, the +compiler will report an error and this step can be rolled back and the previous +step can be repeated to cover what was missed. + +Qualifications in the body of the function can be removed at this point, if +desired, since the compiler will complain if this introduces an ambiguity from +two different interfaces using that name. It would be reasonable to leave the +qualifications in if the type parameter has multiple interface constraints, both +as documentation for readers and to protect against future name collisions if +the interfaces are changed. ### Validity can depend on value From 70d873acaff3add848398a7b327b25796ebe5eb0 Mon Sep 17 00:00:00 2001 From: Josh L Date: Thu, 6 Oct 2022 20:46:47 +0000 Subject: [PATCH 08/33] Checkpoint progress. --- proposals/p2200.md | 183 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 139 insertions(+), 44 deletions(-) diff --git a/proposals/p2200.md b/proposals/p2200.md index bb22922aace48..549cd43e75112 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -19,6 +19,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [Proposal](#proposal) - [Details](#details) - [Syntax](#syntax) + - [Branching on type identity](#branching-on-type-identity) - [Value phases](#value-phases) - [`auto`](#auto) - [Template constraints](#template-constraints) @@ -28,17 +29,17 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [To interface constraints](#to-interface-constraints) - [To checked generic](#to-checked-generic) - [Validity can depend on value](#validity-can-depend-on-value) - - [Branching on type identity](#branching-on-type-identity) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Only checked generics](#only-checked-generics) - [SFINAE](#sfinae) - - [Template API specialization](#template-api-specialization) + - [Ad hoc API specialization](#ad-hoc-api-specialization) - [Value phase of bindings determined by initializer](#value-phase-of-bindings-determined-by-initializer) - [Future work](#future-work) - [Expanded template constraints](#expanded-template-constraints) - [Predicates: constraints on values](#predicates-constraints-on-values) - [Generics calling templates](#generics-calling-templates) + - [Which expressions will be evaluated at compile time](#which-expressions-will-be-evaluated-at-compile-time) @@ -54,7 +55,7 @@ Starting with we have assumed templates (also known as "template generics" in Carbon) will be a feature of Carbon, but it has not been an accepted part of the design. We now understand enough about how they should fit into the language to decide that we -are including the feature in the language, and how. +are including the feature in the language, and what form they should take. Template generics will address these use cases: @@ -157,7 +158,7 @@ is accepted first. [cppreference](https://en.cppreference.com/w/cpp/language/sfinae), [wikipedia](https://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error). It means that functions from an overload set with monorphization errors, or - "substitution failure", are simply ignored instead of causing compilation to + "substitution failure," are simply ignored instead of causing compilation to fail. ## Proposal @@ -234,6 +235,8 @@ let y: auto = Cast(x, i64); // Type of `y` is `i64`. ``` +#### Branching on type identity + Branching on the value of a templated type will be done using a `match` statement, but is outside the scope of this proposal. See instead pending proposal @@ -276,9 +279,10 @@ initialize a binding with a different value phase is partially considered in question-for-leads issue [#2153: Generics calling templates](https://github.com/carbon-language/carbon-lang/issues/2153). -FIXME: what is definitely allowed - -FIXME: value phase of expressions +**Note:** Exactly which expressions in terms of constants result in constants is +an open question that is not resolved by this proposal. In particular, which +function calls will be evaluated at compile time is not yet specified. See +[future work](#which-expressions-will-be-evaluated-at-compile-time). ### `auto` @@ -654,62 +658,131 @@ the interfaces are changed. ### Validity can depend on value -FIXME: A templated parameter may be used in ways where the validity of the -result depends on the value of the parameter, not just its type. Example: -whether two array types are compatible depends on whether they have the same -size. With a symbolic constant sizes, they will only be considered equal if the -compiler can show that the two sizes are equal always symbolicly. If the size is -a template parameter, the checking will be delayed until the value of the -template parameter is known. - -### Branching on type identity - -FIXME: Branching in code based on type identity, for example in a `match` -statement, which would effectively be an implementation of "typecase." +A templated parameter may be used in ways where the validity of the result +depends on the value of the parameter, not just its type. As an example, whether +two array types are compatible depends on whether they have the same size. With +a symbolic constant sizes, they will only be considered equal if the compiler +can show that the two sizes are always equal symbolically. If the size is a +template parameter, the checking will be delayed until the value of the template +parameter is known. ## Rationale -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 and principles. This may evolve during review. Use links -to appropriate sections of [`/docs/project/goals.md`](/docs/project/goals.md), -and/or to documents in [`/docs/project/principles`](/docs/project/principles). -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) +This proposal advances these Carbon goals: + +- [Performance-critical software](/docs/project/goals.md#performance-critical-software), + by providing an alternative to checked generics that has greater access to + the specific value of the parameter. - [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) + by specifically marking code that is using features that should receive + greater scruitiny. +- [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code), + specifically migrating C++ code using templates, as detailed in the + ["transition from C++ templates to Carbon checked generics" section](#transition-from-c-templates-to-carbon-checked-generics). ## Alternatives considered ### Only checked generics -FIXME: Rust's approach - -FIXME: Reasons are above +Like Rust, Carbon could use only checked generics and not support templates. The +reasons for this approach are detailed in the ["problem" section](#problem). ### SFINAE -FIXME: confusion about source of error +We could use the [SFINAE rule](#terminology), to match C++. While familiar, it +prevents the compiler from being able to distinguish between "this code is not +meant for this case" from "this code has an error." The goal of eliminating +SFINAE is to get away from the verbose and unclear errors that templates are +infamous for. C++ itself, with +[concepts](https://en.cppreference.com/w/cpp/language/constraints), is moving +toward stating requirements up front to improve the quality of error +diagnostics. + +### Ad hoc API specialization + +Consider a type with a template type parameter: + +```carbon +class Vector(template T:! Type) { + fn GetPointer[addr me: Self*](index: i32) -> T*; + // ... +} +``` -### Template API specialization +To be able to type check a checked generic function using that type: -FIXME: See approach: -[specialization of parameterized types](/docs/design/generics/details.md#specialization) +```carbon +fn SetFirst[T:! Type](vec: Vector(T)*, val: T) { + let p: T* = vec->GetPointer(0); + *p = val; +} +``` -FIXME: Incompatible with checked generics +we need a guarantee that the function signature used to type check the function +is correct. This won't in general be true if ad hoc specialization is allowed: + +```carbon +// ❌ Not legal Carbon, no ad hoc specialization +class Vector(bool) { + // `let p: T* = vec->GetPointer(0)` won't type check + // in `SetFirst` with `T == bool`. + fn GetPointer[addr me: Self*](index: i32) -> BitProxy; + // ... +} +``` + +Specialization is still important for performance, but Carbon's +[existing approach to specialization of parameterized types](/docs/design/generics/details.md#specialization) +makes it clear what parts of the signature can vary, and what properties all +specializations will have. In this example, `Vector` would have to be declared +alongside an interface, and implementations of that interface, as in: + +```carbon +class Vector(template T:! Type); + +interface VectorSpecialization { + let PointerType: Deref(Self); + fn GetPointer(p: Vector(Self)*, index: i32) -> PointerType; +} + +// Blanket implementation provides default when there is +// no specialization. +impl forall [T:! Type] T as VectorSpecialization + where .PointerType = T* { ... } + +// Specialization for `bool`. +impl bool as VectorSpecialization + where .PointerType = BitProxy { ... } + +class Vector(template T:! Type) { + // Return type of `GetPointer` varies with `T`, but must + // implement `Deref(T)`. + fn GetPointer[addr me: Self*](index: i32) + -> T.(VectorSpecialization.PointerType) { + return T.(VectorSpecialization.GetPointer)(me, index); + } + // ... +} +``` ### Value phase of bindings determined by initializer -FIXME: Types versus non-type values. Name lookup rules different for types. +We considered allowing a `let` binding to result in a name with +[constant value phase](#value-phases) if the initializer was a constant, even if +it was not declared using the `template` keyword. That would mean that +`let x: i32 = 5;` would declare `x` as constant, rather than a runtime value. + +For non-type values, this would be a strict improvement in usability. With the +proposal, there is a choice between writing the concise form `let x: i32 = 5;` +and `let template x:! i32 = 5;` that lets the compiler use the value of `x` +directly. The `let template` form is both longer and uses `template` which in +other contexts might merit closer scrutiny, but is generally either desirable or +harmless in this context. The problem is that for type values, changing from a +symbolic value to a constant results in a change to the +[name lookup](#name-lookup) rules, which is not going to always be desired. -FIXME: Link to open discussion notes +This decision was the result of a discussion in open discussions on +[2022-09-09](https://docs.google.com/document/d/1tEt4iM6vfcY0O0DG0uOEMIbaXcZXlNREc2ChNiEtn_w/edit#heading=h.g1xcokypbstm). ## Future work @@ -731,3 +804,25 @@ FIXME: See question-for-leads issue FIXME: See question-for-leads issue [#2153: Generics calling templates](https://github.com/carbon-language/carbon-lang/issues/2153). + +### Which expressions will be evaluated at compile time + +The section on [value phases](#value-phases) and the +["value phase of bindings determined by initializer" alternative](#value-phase-of-bindings-determined-by-initializer) +still leave open some questions about how value phases interact, and what gets +evaluated at compile time. For example, what happens when there is a function +call in the initializer of a `let template`, as in: + +```carbon +let x: i32 = 5; +let template Y:! i32 = F(x); +``` + +Is the function call evaluated at compile time in order to determine a value for +`Y`, or is this an error? Does it depend on something about `F`, such as its +definition being visible to the caller and being free of side effects? Is this +only allowed since the value of `x` can be determined at compile time, even +though it is a runtime value, or would the declaration of `x` have to change? If +we allow this construction, observe that the parameter to `F` has different +value phases when called at compile time compared to run time, which might +affect the interpretation of the body of `F`. From bc891f2743b6a94cf142ff5870b7bac81ccd31fa Mon Sep 17 00:00:00 2001 From: Josh L Date: Thu, 6 Oct 2022 21:46:04 +0000 Subject: [PATCH 09/33] Checkpoint progress. --- proposals/p2200.md | 65 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 11 deletions(-) diff --git a/proposals/p2200.md b/proposals/p2200.md index 549cd43e75112..02f8b322183c5 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -38,15 +38,17 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [Future work](#future-work) - [Expanded template constraints](#expanded-template-constraints) - [Predicates: constraints on values](#predicates-constraints-on-values) - - [Generics calling templates](#generics-calling-templates) + - [Checked generics calling templates](#checked-generics-calling-templates) - [Which expressions will be evaluated at compile time](#which-expressions-will-be-evaluated-at-compile-time) ## Abstract -FIXME: Describe, in a succinct paragraph, the gist of this document. This -paragraph should be reproduced verbatim in the PR summary. +Add template generics, with optional constraints but no SFINAE, to Carbon. +Template generics allows the compiler postpone type checking of expressions +dependent on a template parameter until the function is called and the value of +that parameter is known. ## Problem @@ -788,21 +790,62 @@ This decision was the result of a discussion in open discussions on ### Expanded template constraints -FIXME: follow C++ concepts to see the range of kinds of constraints found to be -useful in practice: +[Template constraints](#template-constraints) will need to support other kinds +of structural constraints. In particular, the kinds of constraints that can be +expressed in C++20 Concepts: -- https://en.cppreference.com/w/cpp/language/constraints -- https://en.cppreference.com/w/cpp/language/requires -- [Examples of named requirements for the C++ standard library](https://en.cppreference.com/w/cpp/named_req) +- [Constraints and concepts (since C++20)](https://en.cppreference.com/w/cpp/language/constraints) +- [Requires expression (since C++20)](https://en.cppreference.com/w/cpp/language/requires) + +This is both to allow the constraints of existing C++ code to be migrated, and +because we expect constraints that were found to be useful in C++ will also be +useful for Carbon. ### Predicates: constraints on values -FIXME: See question-for-leads issue +We will need some mechanism to express that the value of a non-type template +parameter meets some criteria. For example, the size parameter of an array must +not be less than 0. We are considering a construct called _predicates_ to +represent these kinds of constraints, see the question-for-leads issue [#2153: Generics calling templates](https://github.com/carbon-language/carbon-lang/issues/2153). -### Generics calling templates +### Checked generics calling templates + +For checked generics interoperation with existing templates, and to allow +templates to be migrated to checked generics in any order, we want Carbon to +support supplying a symbolic constant argument value, such as from a checked +generic function, to a function taking a template parameter. One approach that +already works is using a template implementation of an interface, as in this +example: + +```carbon +fn TemplateFunction[template T:! Type](x: T) -> T; + +// `Wrapper` is an interface wrapper around +// `TemplateFunction`. + +interface Wrapper { + fn F[me: Self]() -> Self; +} + +external impl forall [template T:! Type] T as Wrapper { + fn F[me: Self]() -> Self { + TemplateFunction(me); + } +} + +// ✅ Allowed: +fn CheckedGeneric[T:! Wrapper](z: T) -> T { + return z.(Wrapper.F)(); +} + +// ⚠️ Future work, see #2153: +fn CheckedGenericDirect[T:! Type](z: T) -> T { + return TemplateFunction(z); +} +``` -FIXME: See question-for-leads issue +More direct interoperation is being considered in question-for-leads issue [#2153: Generics calling templates](https://github.com/carbon-language/carbon-lang/issues/2153). ### Which expressions will be evaluated at compile time From cbc85b18e2b962d1781be36df4c4602bc505a128 Mon Sep 17 00:00:00 2001 From: Josh L Date: Thu, 6 Oct 2022 21:48:27 +0000 Subject: [PATCH 10/33] Add example to abstract --- proposals/p2200.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/proposals/p2200.md b/proposals/p2200.md index 02f8b322183c5..072bffb018693 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -50,6 +50,14 @@ Template generics allows the compiler postpone type checking of expressions dependent on a template parameter until the function is called and the value of that parameter is known. +Example usage: + +```carbon +fn Identity[template T:! Type](x: T) -> T { + return x; +} +``` + ## Problem Starting with From aa5cb72b3441808eaca2186d062caa3d51e5dc55 Mon Sep 17 00:00:00 2001 From: Josh L Date: Tue, 11 Oct 2022 22:38:53 +0000 Subject: [PATCH 11/33] Template dependent --- proposals/p2200.md | 160 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 156 insertions(+), 4 deletions(-) diff --git a/proposals/p2200.md b/proposals/p2200.md index 072bffb018693..ecfea7df3e840 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -29,6 +29,11 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [To interface constraints](#to-interface-constraints) - [To checked generic](#to-checked-generic) - [Validity can depend on value](#validity-can-depend-on-value) + - [Template dependent](#template-dependent) + - [Unqualified member name lookup](#unqualified-member-name-lookup) + - [Qualified member name lookup](#qualified-member-name-lookup) + - [Impl lookup](#impl-lookup) + - [Use of dependent value is dependent](#use-of-dependent-value-is-dependent) - [Rationale](#rationale) - [Alternatives considered](#alternatives-considered) - [Only checked generics](#only-checked-generics) @@ -148,12 +153,15 @@ is accepted first. ## Terminology -- A _dependent_ name or expression is one whose meaning depends on, that is - varies with, some (checked or template) generic parameter. This is - consistent with the meaning of this term in +- A _template dependent_ name or expression is one whose meaning depends on, + that is varies with, some template generic parameter. Specifically this + refers to an expression that can not be fully checked until the value of the + parameter is known. This is consistent with the meaning of this term in [C++](https://en.cppreference.com/w/cpp/language/dependent_name), but is different from - ["dependent types"](https://en.wikipedia.org/wiki/Dependent_type). + ["dependent types"](https://en.wikipedia.org/wiki/Dependent_type). The + specifics of how this works in Carbon are being proposed in + [a later section](#template-dependent). - [_Instantiation_](/docs/design/generics/terminology.md#instantiation), _substitution_, or [_monomorphizaton_](https://en.wikipedia.org/wiki/Monomorphization) is the @@ -192,6 +200,8 @@ The main differences between checked and templated generics are: [proposal #989](https://github.com/carbon-language/carbon-lang/pull/989). - A templated parameter may be used in ways where the validity of the result depends on the value of the parameter, not just its type. +- Impl lookup is delayed until all templated types, interfaces, and parameters + are known. As a consequence of these differences, type checking of any expression dependent on a templated parameter may not be completed until its value is known. In @@ -676,6 +686,148 @@ can show that the two sizes are always equal symbolically. If the size is a template parameter, the checking will be delayed until the value of the template parameter is known. +### Template dependent + +Expressions fall under three categories: + +- Expressions that are valid and have meaning determined without knowing the + value of any template parameter are _not template dependent_. +- Expressions whose meaning and validity requires knowing the value of a + template parameter are _template dependent_. +- Expressions that have a meaning without knowing the value of any template + parameter, assuming it is valid, but whose validity requires knowing the + value of a template parameter are _template validity dependent_. These + expressions can trigger a monomorphization error, but are not considered + template dependent for purposes of a containing expression. + +#### Unqualified member name lookup + +There are two cases when performing unqualified member-name lookup into a +templated type: + +```carbon +fn F[template T:! I](x: T) { + x.G(); +} +``` + +- If generic name lookup would succeed, in this example it would be because + `G` is a member of `I`, then the result of name lookup is template validity + dependent. This means that template instantiation may fail if `T` has a + member `G` different than `T.(I.G)`. Assuming it succceeds, though, it will + definitely have meaning determined by `I.G`. +- If the member name is not found in the constraint, lookup may still succeed + once the type is known, so the result is template dependent. +- If the lookup is ambiguous prior to knowing the value of the type, for + example if `G` has two distinct meanings in `I`, then the code is invalid. + +The value of the expression will be dependent. For example, if `U` is an +associated type of `I`, then `T.U` as an expression is template validity +dependent, but the value of that expression is dependent. The value is not +always needed to perform checking, for example `x.G()` can be checked without +ever determining the value of `x.G` as long as its signature can be determined, +if it is valid. + +#### Qualified member name lookup + +Adding qualifier to the member name, as in `T.(U.X)`, can make the lookup less +dependent on the template parameter: + +- If `T` is dependent, and `U` is an interface, the value of the expression is + dependent, but the type of the expression is not. + - If `T` is known to implement `U`, then the lookup is not dependent. For + example, there could be a requirement on `T` or a sufficiently general + implementation of `U` that includes all possible values of `T`. + - Otherwise, the lookup is template validity dependent. + +```carbon +interface Hashable { + let HashType:! Type; + fn Hash[me: Self]() -> HashType; +} + +external impl forall [T:! Serializable] T as Hashable; + +fn F[template T:! Serializable](x: T) { + // `T` is required to implement `Serializable` so this + // is not dependent. + x.(Serializable.Serialize)(); + + // Any `T` implementing `Serializable` also implements + // `Hashable`, since there is a blanket implementation, + // so this is not dependent. Note: there may be a + // specialization of this impl, so we can't rely on + // knowing how `T` implements `Hashable`. + x.(Hashable.Hash)(); + + // Unclear whether `T` implements `Printable`, but if + // does, clear what this means, so this is template + // validity dependent + x.(Printable.Print)(); + + match (x.(Hashable.Hash)()) { + // Uses the value of the associated type + // `Hashable.HashType` that is template dependent. + case _: u64 => { ... } + default => { ... } + } +} +``` + +- If `U.X` is dependent, then the entire expression is dependent. + +#### Impl lookup + +If the validity of an expression requires that an impl exist for a type, and +that can't be determined until the value of a template parameter is known, then +the expression is template validity dependent. For example, in the example from +the previous section, `x.(Printable.Print)()` is valid if `T` implements +`Printable`. This can also occur without a qualified lookup, for example: + +```carbon +fn F[T:! Printable](x: T); +fn G[template T:! Type](x: T) { + // Valid if `T` implements `Printable`, so this + // expression is template validity dependent. + F(x); +} +``` + +The values of members of an impl for a template-dependent type are template +dependent, unless they can be resolved to a not template-dependent expression +using checked-generic impl resolution. + +``` +final external impl [T:! Type] T* as D + where .Result = T and .Index = i32; +fn F[T:! Type](p: T*) { + // `(T*).(D.Index)` uses the final impl of `D` for `T*`, + // and so equals `i32`, which is not dependent. + // `(T*).(D.Result)` is recognized as equal to `T`, which + // is template dependent. +} +``` + +#### Use of dependent value is dependent + +To match the expectations of C++ templates, uses of dependent values are also +template dependent. For example: + +``` +fn TypeName[template T:! Type](x: T) -> String { + match (x) { + // Each entire case body is dependent + case _: i32 => { return "int"; } + case _: bool => { return "bool"; } + case _: auto* => { return "pointer"; } + // Allowed even though body of case is invalid for + // `T != Vector(String)` + case _: Vector(String) => { return x.front(); } + default => { return "unknown"; } + } +} +``` + ## Rationale This proposal advances these Carbon goals: From 06c5d7a6e39c0afe873ca701802be7e4cd0cd326 Mon Sep 17 00:00:00 2001 From: Josh L Date: Thu, 13 Oct 2022 22:38:24 +0000 Subject: [PATCH 12/33] Simpler dependent rules alternative --- proposals/p2200.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/proposals/p2200.md b/proposals/p2200.md index ecfea7df3e840..ca036531d99af 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -40,6 +40,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [SFINAE](#sfinae) - [Ad hoc API specialization](#ad-hoc-api-specialization) - [Value phase of bindings determined by initializer](#value-phase-of-bindings-determined-by-initializer) + - [Simpler template dependent rules that delayed more checking](#simpler-template-dependent-rules-that-delayed-more-checking) - [Future work](#future-work) - [Expanded template constraints](#expanded-template-constraints) - [Predicates: constraints on values](#predicates-constraints-on-values) @@ -946,6 +947,21 @@ symbolic value to a constant results in a change to the This decision was the result of a discussion in open discussions on [2022-09-09](https://docs.google.com/document/d/1tEt4iM6vfcY0O0DG0uOEMIbaXcZXlNREc2ChNiEtn_w/edit#heading=h.g1xcokypbstm). +### Simpler template dependent rules that delayed more checking + +We considered simpler rules for which expressions were considered +[template dependent](#template-dependent), like that any expression involving a +template parameter was template dependent. This had the downside that it would +have delayed checking of more expressions, and would have resulted in greater +differences between template and checked generic semantics. Ultimately we +thought that the developer experience would be better if errors were delivered +earlier. + +This was discussed in +[open discussion on 2022-10-10](https://discord.com/channels/655572317891461132/941071822756143115/1029411854277165137) +and on +[Discord #generics-and-templates starting 2022-10-11](https://discord.com/channels/655572317891461132/941071822756143115/1029411854277165137). + ## Future work ### Expanded template constraints From 3449878bd5ff1d9f3971d4c0c7b204e91059dbc2 Mon Sep 17 00:00:00 2001 From: josh11b Date: Mon, 24 Oct 2022 13:04:10 -0700 Subject: [PATCH 13/33] Apply suggestions from code review Co-authored-by: Richard Smith --- proposals/p2200.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/proposals/p2200.md b/proposals/p2200.md index ca036531d99af..a46e3e23c0580 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -52,7 +52,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ## Abstract Add template generics, with optional constraints but no SFINAE, to Carbon. -Template generics allows the compiler postpone type checking of expressions +Template generics allows the compiler to postpone type checking of expressions dependent on a template parameter until the function is called and the value of that parameter is known. @@ -168,8 +168,8 @@ is accepted first. [_monomorphizaton_](https://en.wikipedia.org/wiki/Monomorphization) is the process of duplicating the implementation of a function and then substituting in the values of any (checked or template) generic arguments. -- Errors that are only detected once the argument value from the call site are - known as _monomorphization errors_. These mostly occur in expressions +- Errors that are only detected once the argument value from the call site is + known are called _monomorphization errors_. These mostly occur in expressions dependent on some template parameter, but can also occur for other reasons like hitting an implementation limit. - _SFINAE_ stands for "Substitution failure is not an error", which is the @@ -177,7 +177,7 @@ is accepted first. [cppreference](https://en.cppreference.com/w/cpp/language/sfinae), [wikipedia](https://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error). It means that functions from an overload set with monorphization errors, or - "substitution failure," are simply ignored instead of causing compilation to + "substitution failure," within their signatures are simply ignored instead of causing compilation to fail. ## Proposal @@ -196,8 +196,8 @@ The following are true for any kind of generic parameter: The main differences between checked and templated generics are: -- Member lookup into a templated type looks in both the actual type value - provided by the caller in addition to in any constraints on that type see +- Member lookup into a templated type looks in the actual type value + provided by the caller in addition to in any constraints on that type; see [proposal #989](https://github.com/carbon-language/carbon-lang/pull/989). - A templated parameter may be used in ways where the validity of the result depends on the value of the parameter, not just its type. @@ -247,6 +247,7 @@ Function parameters also default to a `let` context and may use `template`: // `U` is a templated type parameter that must be specified // explicitly by the caller. fn Cast[template T:! Type](x: T, template U:! Type) -> U { + // OK, check for `T is As(U)` delayed until values of `T` and `U` are known. return x as U; } @@ -386,7 +387,7 @@ parameters is being considered in question-for-leads issue Even if we allow a checked-generic parameter to use a template constraint, we want to focus checked generics on semantic properties encapsulated in interfaces, not structural properties tested by template constraints. So we -would not allow lookup into checked-generic type to find type members outside of +would not allow lookup into a checked-generic type to find type members outside of an interface: ```carbon @@ -703,7 +704,7 @@ Expressions fall under three categories: #### Unqualified member name lookup -There are two cases when performing unqualified member-name lookup into a +There are three cases when performing unqualified member-name lookup into a templated type: ```carbon From ee54f5a2c67bec5d2ff915f1eb6f8191ca3d9773 Mon Sep 17 00:00:00 2001 From: Josh L Date: Mon, 24 Oct 2022 20:08:26 +0000 Subject: [PATCH 14/33] Fix formatting --- proposals/p2200.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/proposals/p2200.md b/proposals/p2200.md index a46e3e23c0580..0c8072a4bae83 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -169,16 +169,16 @@ is accepted first. process of duplicating the implementation of a function and then substituting in the values of any (checked or template) generic arguments. - Errors that are only detected once the argument value from the call site is - known are called _monomorphization errors_. These mostly occur in expressions - dependent on some template parameter, but can also occur for other reasons - like hitting an implementation limit. + known are called _monomorphization errors_. These mostly occur in + expressions dependent on some template parameter, but can also occur for + other reasons like hitting an implementation limit. - _SFINAE_ stands for "Substitution failure is not an error", which is the policy in C++, see [cppreference](https://en.cppreference.com/w/cpp/language/sfinae), [wikipedia](https://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error). It means that functions from an overload set with monorphization errors, or - "substitution failure," within their signatures are simply ignored instead of causing compilation to - fail. + "substitution failure," within their signatures are simply ignored instead + of causing compilation to fail. ## Proposal @@ -196,8 +196,8 @@ The following are true for any kind of generic parameter: The main differences between checked and templated generics are: -- Member lookup into a templated type looks in the actual type value - provided by the caller in addition to in any constraints on that type; see +- Member lookup into a templated type looks in the actual type value provided + by the caller in addition to in any constraints on that type; see [proposal #989](https://github.com/carbon-language/carbon-lang/pull/989). - A templated parameter may be used in ways where the validity of the result depends on the value of the parameter, not just its type. @@ -387,8 +387,8 @@ parameters is being considered in question-for-leads issue Even if we allow a checked-generic parameter to use a template constraint, we want to focus checked generics on semantic properties encapsulated in interfaces, not structural properties tested by template constraints. So we -would not allow lookup into a checked-generic type to find type members outside of -an interface: +would not allow lookup into a checked-generic type to find type members outside +of an interface: ```carbon template constraint HasF { From 4184bc7ce9cd246f0e85c093e492aa0ba755c0aa Mon Sep 17 00:00:00 2001 From: Josh L Date: Mon, 24 Oct 2022 21:45:27 +0000 Subject: [PATCH 15/33] Address review suggestion --- proposals/p2200.md | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/proposals/p2200.md b/proposals/p2200.md index 0c8072a4bae83..59cf9a54746e9 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -231,9 +231,7 @@ In contrast with C++ templates, with Carbon template generics: ### Syntax Template generic bindings are declared using the `template` keyword in addition -to the `:!` of all generic bindings. All generic bindings can only be used in -`let` context to produce r-values, not in a `var` context to produce l-values. -This includes `let` declarations, as in: +to the `:!` of all generic bindings. This includes `let` declarations, as in: ```carbon // `N` is a constant that may be used in types. @@ -257,6 +255,18 @@ let y: auto = Cast(x, i64); // Type of `y` is `i64`. ``` +Note that generic bindings, checked or template, can only be used in `let` +context to produce r-values, not in a `var` context to produce l-values. + +```carbon +// ❌ Error: Can't use `:!` with `var`. Can't be both a +// compile-time constant and a variable. +var N:! i64 = 4; +// ❌ Error: Can't use `template :!` with `var` for the +// same reason. +var template M:! i64 = 5; +``` + #### Branching on type identity Branching on the value of a templated type will be done using a `match` From e1952daead1d33d2834a9198222573e68cbc85d8 Mon Sep 17 00:00:00 2001 From: Josh L Date: Mon, 24 Oct 2022 23:12:38 +0000 Subject: [PATCH 16/33] Address review suggestion --- proposals/p2200.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/proposals/p2200.md b/proposals/p2200.md index 59cf9a54746e9..4803e1b19be00 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -749,7 +749,9 @@ dependent on the template parameter: dependent, but the type of the expression is not. - If `T` is known to implement `U`, then the lookup is not dependent. For example, there could be a requirement on `T` or a sufficiently general - implementation of `U` that includes all possible values of `T`. + implementation of `U` that includes all possible values of `T`. The + resulting value is dependent unless the implementation of `U` is + `final`, see [the impl lookup section](#impl-lookup). - Otherwise, the lookup is template validity dependent. ```carbon From 0fdf7611edc33d598d9a5082e8ca623920444a49 Mon Sep 17 00:00:00 2001 From: josh11b Date: Mon, 24 Oct 2022 16:13:35 -0700 Subject: [PATCH 17/33] Apply suggestions from code review Co-authored-by: Richard Smith --- proposals/p2200.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/proposals/p2200.md b/proposals/p2200.md index 4803e1b19be00..79954b8d87164 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -347,8 +347,8 @@ In brief, a `template constraint` declaration is like a `constraint` declaration, except that it may also contain function and field declarations, called _structural constraints_. Only types with matching declarations will satisfying the template constraint. Note that the declarations matching the -structural constraints must be in the type declaration itself, not in an -external impl. +structural constraints must be found by member lookups in the type. It is not +sufficient for them to be declared only in an external impl. ```carbon interface A { fn F[me: Self](); } @@ -389,6 +389,11 @@ var y: C = {}; // Call to `F` inside `G` is not ambiguous since // `C.F` and `HasF.F` refer to the same function. G(y); +class D extends C { + alias F = C.(A.F); +} +// OK, `z.(HasF.F)` will resolve to `z.(C.(A.F))`. +fn Run(z: D) { G(z); } ``` Whether template constraints may be used as constraints on checked-generic From 0949a3ad93ea00c80ee806707ddba9f4393ff769 Mon Sep 17 00:00:00 2001 From: Josh L Date: Tue, 25 Oct 2022 03:33:27 +0000 Subject: [PATCH 18/33] Reference #1371 --- proposals/p2200.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/proposals/p2200.md b/proposals/p2200.md index 79954b8d87164..a55488bab9fb9 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -306,10 +306,17 @@ This terminology comes from in particular [this message](https://discord.com/channels/655572317891461132/708431657849585705/994396535561408623). +**Note:** This reflects the resolution of question-for-leads issue +[#1371: Is `let` referentially transparent?](https://github.com/carbon-language/carbon-lang/issues/1371) +that the value phase of a binding is determined by the kind of binding, and not +anything about the initializer. + **Note:** The situations in which a value with one phase can be used to -initialize a binding with a different value phase is partially considered in -question-for-leads issue -[#2153: Generics calling templates](https://github.com/carbon-language/carbon-lang/issues/2153). +initialize a binding with a different value phase is future work, partially +considered in question-for-leads issue +[#2153: Generics calling templates](https://github.com/carbon-language/carbon-lang/issues/2153) +in addition to +[#1371: Is `let` referentially transparent?](https://github.com/carbon-language/carbon-lang/issues/1371). **Note:** Exactly which expressions in terms of constants result in constants is an open question that is not resolved by this proposal. In particular, which From 7c6c2f03190afd5795c95a8c9fef0ee12cdde2de Mon Sep 17 00:00:00 2001 From: Josh L Date: Tue, 25 Oct 2022 15:39:10 +0000 Subject: [PATCH 19/33] Ambiguity --- proposals/p2200.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/proposals/p2200.md b/proposals/p2200.md index a55488bab9fb9..2fb733a4b059d 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -739,7 +739,9 @@ fn F[template T:! I](x: T) { `G` is a member of `I`, then the result of name lookup is template validity dependent. This means that template instantiation may fail if `T` has a member `G` different than `T.(I.G)`. Assuming it succceeds, though, it will - definitely have meaning determined by `I.G`. + definitely have meaning determined by `I.G`. There may still be ambiguity + making the result dependent if it is not known whether `x` is a type and + `I.G` is a method and so has an implicit `me` parameter. - If the member name is not found in the constraint, lookup may still succeed once the type is known, so the result is template dependent. - If the lookup is ambiguous prior to knowing the value of the type, for @@ -758,7 +760,10 @@ Adding qualifier to the member name, as in `T.(U.X)`, can make the lookup less dependent on the template parameter: - If `T` is dependent, and `U` is an interface, the value of the expression is - dependent, but the type of the expression is not. + dependent, but the type of the expression is not. One exception is that + there may still be ambiguity making the result dependent if it is not known + whether `T` is a type and `U.X` is a method and so has an implicit `me` + parameter. - If `T` is known to implement `U`, then the lookup is not dependent. For example, there could be a requirement on `T` or a sufficiently general implementation of `U` that includes all possible values of `T`. The From 249fefc0396647ad0ca1655d147670c07580864c Mon Sep 17 00:00:00 2001 From: Josh L Date: Thu, 27 Oct 2022 00:13:33 +0000 Subject: [PATCH 20/33] Qualified access less ambiguous under new rules --- proposals/p2200.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/proposals/p2200.md b/proposals/p2200.md index 2fb733a4b059d..b07a2e9255f65 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -760,10 +760,7 @@ Adding qualifier to the member name, as in `T.(U.X)`, can make the lookup less dependent on the template parameter: - If `T` is dependent, and `U` is an interface, the value of the expression is - dependent, but the type of the expression is not. One exception is that - there may still be ambiguity making the result dependent if it is not known - whether `T` is a type and `U.X` is a method and so has an implicit `me` - parameter. + dependent, but the type of the expression is not. - If `T` is known to implement `U`, then the lookup is not dependent. For example, there could be a requirement on `T` or a sufficiently general implementation of `U` that includes all possible values of `T`. The From 93fd60d1bf3445763cc4d81d9faee9b8c2ba8950 Mon Sep 17 00:00:00 2001 From: Josh L Date: Sat, 29 Oct 2022 03:13:06 +0000 Subject: [PATCH 21/33] Link to #2360 --- proposals/p2200.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/p2200.md b/proposals/p2200.md index b07a2e9255f65..e5443bfefb32d 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -761,7 +761,8 @@ dependent on the template parameter: - If `T` is dependent, and `U` is an interface, the value of the expression is dependent, but the type of the expression is not. - - If `T` is known to implement `U`, then the lookup is not dependent. For + - If `T` is known to implement `U`, then the lookup is not dependent, see + [#2360](https://github.com/carbon-language/carbon-lang/pull/2360). For example, there could be a requirement on `T` or a sufficiently general implementation of `U` that includes all possible values of `T`. The resulting value is dependent unless the implementation of `U` is From 991712f252ef30da86f28ad22637f12678386f97 Mon Sep 17 00:00:00 2001 From: josh11b Date: Mon, 31 Oct 2022 16:42:25 -0700 Subject: [PATCH 22/33] Apply suggestions from code review Co-authored-by: Richard Smith --- proposals/p2200.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/p2200.md b/proposals/p2200.md index e5443bfefb32d..48295ca3fdff6 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -760,7 +760,8 @@ Adding qualifier to the member name, as in `T.(U.X)`, can make the lookup less dependent on the template parameter: - If `T` is dependent, and `U` is an interface, the value of the expression is - dependent, but the type of the expression is not. + dependent, but the type of the expression is not dependent unless the type + of `U.X` involves `Self`. - If `T` is known to implement `U`, then the lookup is not dependent, see [#2360](https://github.com/carbon-language/carbon-lang/pull/2360). For example, there could be a requirement on `T` or a sufficiently general From 39736da3608068a93a4e639c4504b2e7d5d29c7a Mon Sep 17 00:00:00 2001 From: Josh L Date: Thu, 3 Nov 2022 19:44:17 +0000 Subject: [PATCH 23/33] Updates based on review suggestions --- proposals/p2200.md | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/proposals/p2200.md b/proposals/p2200.md index 48295ca3fdff6..f37491a98aa64 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -756,16 +756,17 @@ if it is valid. #### Qualified member name lookup -Adding qualifier to the member name, as in `T.(U.X)`, can make the lookup less +Adding qualifier to the member name, as in `x.(U.V)`, can make the lookup less dependent on the template parameter: -- If `T` is dependent, and `U` is an interface, the value of the expression is +- If `x` is dependent, and `U` is an interface, the value of the expression is dependent, but the type of the expression is not dependent unless the type - of `U.X` involves `Self`. - - If `T` is known to implement `U`, then the lookup is not dependent, see + of `U.V` involves `Self`. + - If the type of `x` is known to implement `U`, then the lookup is not + dependent, see [#2360](https://github.com/carbon-language/carbon-lang/pull/2360). For - example, there could be a requirement on `T` or a sufficiently general - implementation of `U` that includes all possible values of `T`. The + example, there could be a requirement on `x` or a sufficiently general + implementation of `U` that includes all possible types of `x`. The resulting value is dependent unless the implementation of `U` is `final`, see [the impl lookup section](#impl-lookup). - Otherwise, the lookup is template validity dependent. @@ -804,7 +805,7 @@ fn F[template T:! Serializable](x: T) { } ``` -- If `U.X` is dependent, then the entire expression is dependent. +- If `U.V` is dependent, then the entire expression is dependent. #### Impl lookup @@ -841,7 +842,20 @@ fn F[T:! Type](p: T*) { #### Use of dependent value is dependent To match the expectations of C++ templates, uses of dependent values are also -template dependent. For example: +template dependent, propagating dependence from subexpressions to enclosing +expressions. For example, if `expr` is a dependent expression, each of these is +dependent: + +- `(expr)` +- `F(expr)` +- `expr + 1` +- `if expr then a else b` +- `if a then expr else b` +- `expr as T` +- `a as expr` + +For `match`, which `case` body is executed may be dependent on the type of the +match expression. For example: ``` fn TypeName[template T:! Type](x: T) -> String { From 1a670dd5e5bf64688956a681023910479cc23c39 Mon Sep 17 00:00:00 2001 From: Josh L Date: Fri, 4 Nov 2022 20:04:54 +0000 Subject: [PATCH 24/33] Clarification --- proposals/p2200.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/proposals/p2200.md b/proposals/p2200.md index f37491a98aa64..34636e236887a 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -724,6 +724,11 @@ Expressions fall under three categories: expressions can trigger a monomorphization error, but are not considered template dependent for purposes of a containing expression. +Note that an expression may not be template dependent even though it has a +template dependent sub-expression. For example, a function may have a value that +is function dependent, but calling that function only needs the type of the +function (meaning the function's signature) to not be template dependent. + #### Unqualified member name lookup There are three cases when performing unqualified member-name lookup into a From a1575c9d3e9adbf649643cc2fa0637d867a9d8c5 Mon Sep 17 00:00:00 2001 From: Josh L Date: Fri, 4 Nov 2022 23:04:56 +0000 Subject: [PATCH 25/33] Checkpoint progress. --- proposals/p2200.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/p2200.md b/proposals/p2200.md index 34636e236887a..769ca5654b430 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -725,7 +725,7 @@ Expressions fall under three categories: template dependent for purposes of a containing expression. Note that an expression may not be template dependent even though it has a -template dependent sub-expression. For example, a function may have a value that +template-dependent sub-expression. For example, a function may have a value that is function dependent, but calling that function only needs the type of the function (meaning the function's signature) to not be template dependent. From cfb657ed1910de317727c37a8bba42abc46c3a2e Mon Sep 17 00:00:00 2001 From: Josh L Date: Wed, 16 Nov 2022 22:04:54 +0000 Subject: [PATCH 26/33] Reflect updates to #2360 --- proposals/p2200.md | 92 +++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 45 deletions(-) diff --git a/proposals/p2200.md b/proposals/p2200.md index 769ca5654b430..95208be539f6c 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -30,8 +30,8 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [To checked generic](#to-checked-generic) - [Validity can depend on value](#validity-can-depend-on-value) - [Template dependent](#template-dependent) - - [Unqualified member name lookup](#unqualified-member-name-lookup) - - [Qualified member name lookup](#qualified-member-name-lookup) + - [Simple member access](#simple-member-access) + - [Compound member access](#compound-member-access) - [Impl lookup](#impl-lookup) - [Use of dependent value is dependent](#use-of-dependent-value-is-dependent) - [Rationale](#rationale) @@ -729,7 +729,7 @@ template-dependent sub-expression. For example, a function may have a value that is function dependent, but calling that function only needs the type of the function (meaning the function's signature) to not be template dependent. -#### Unqualified member name lookup +#### Simple member access There are three cases when performing unqualified member-name lookup into a templated type: @@ -759,56 +759,58 @@ always needed to perform checking, for example `x.G()` can be checked without ever determining the value of `x.G` as long as its signature can be determined, if it is valid. -#### Qualified member name lookup +#### Compound member access Adding qualifier to the member name, as in `x.(U.V)`, can make the lookup less dependent on the template parameter: - If `x` is dependent, and `U` is an interface, the value of the expression is dependent, but the type of the expression is not dependent unless the type - of `U.V` involves `Self`. - - If the type of `x` is known to implement `U`, then the lookup is not - dependent, see - [#2360](https://github.com/carbon-language/carbon-lang/pull/2360). For - example, there could be a requirement on `x` or a sufficiently general - implementation of `U` that includes all possible types of `x`. The - resulting value is dependent unless the implementation of `U` is - `final`, see [the impl lookup section](#impl-lookup). + of `U.V` involves `Self`. The lookup itself follows proposal + [#2360](https://github.com/carbon-language/carbon-lang/pull/2360): + + - If `U.V` is an _instance_ member, and the type of `x` is known to + implement `U`, then the lookup is not dependent. For example, there + could be a requirement on `x` or a sufficiently general implementation + of `U` that includes all possible types of `x`. The resulting value is + dependent unless the implementation of `U` is `final`, see + [the impl lookup section](#impl-lookup). - Otherwise, the lookup is template validity dependent. -```carbon -interface Hashable { - let HashType:! Type; - fn Hash[me: Self]() -> HashType; -} - -external impl forall [T:! Serializable] T as Hashable; - -fn F[template T:! Serializable](x: T) { - // `T` is required to implement `Serializable` so this - // is not dependent. - x.(Serializable.Serialize)(); - - // Any `T` implementing `Serializable` also implements - // `Hashable`, since there is a blanket implementation, - // so this is not dependent. Note: there may be a - // specialization of this impl, so we can't rely on - // knowing how `T` implements `Hashable`. - x.(Hashable.Hash)(); - - // Unclear whether `T` implements `Printable`, but if - // does, clear what this means, so this is template - // validity dependent - x.(Printable.Print)(); - - match (x.(Hashable.Hash)()) { - // Uses the value of the associated type - // `Hashable.HashType` that is template dependent. - case _: u64 => { ... } - default => { ... } - } -} -``` + ```carbon + interface Hashable { + let HashType:! Type; + fn Hash[me: Self]() -> HashType; + } + + external impl forall [T:! Serializable] T as Hashable; + + fn F[template T:! Serializable](x: T) { + // `T` is required to implement `Serializable` and + // `Serialize` is an instance member, so this is not + // dependent. + x.(Serializable.Serialize)(); + + // Any `T` implementing `Serializable` also implements + // `Hashable`, since there is a blanket implementation, + // so this is not dependent. Note: there may be a + // specialization of this impl, so we can't rely on + // knowing how `T` implements `Hashable`. + x.(Hashable.Hash)(); + + // Unclear whether `T` implements `Printable`, but if + // does, clear what this means, so this is template + // validity dependent + x.(Printable.Print)(); + + match (x.(Hashable.Hash)()) { + // Uses the value of the associated type + // `Hashable.HashType` that is template dependent. + case _: u64 => { ... } + default => { ... } + } + } + ``` - If `U.V` is dependent, then the entire expression is dependent. From 3e12325079380b48d2db5a662deba9e90e103882 Mon Sep 17 00:00:00 2001 From: josh11b Date: Wed, 16 Nov 2022 16:10:13 -0800 Subject: [PATCH 27/33] Apply suggestions from code review Co-authored-by: Richard Smith --- proposals/p2200.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/proposals/p2200.md b/proposals/p2200.md index 95208be539f6c..22e5dfdb9b67d 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -353,7 +353,7 @@ Template constraints have already been introduced in proposal In brief, a `template constraint` declaration is like a `constraint` declaration, except that it may also contain function and field declarations, called _structural constraints_. Only types with matching declarations will -satisfying the template constraint. Note that the declarations matching the +satisfy the template constraint. Note that the declarations matching the structural constraints must be found by member lookups in the type. It is not sufficient for them to be declared only in an external impl. @@ -665,7 +665,7 @@ interface NewF { // Blanket implementation no longer needed. fn G[template T:! NewF](x: T) { - x.DoF(); + x.(NewF.DoF)(); } class C { @@ -778,6 +778,14 @@ dependent on the template parameter: - Otherwise, the lookup is template validity dependent. ```carbon + interface Serializable { + fn Serialize[me: Self](); + } + + interface Printable { + fn Print[me: Self](); + } + interface Hashable { let HashType:! Type; fn Hash[me: Self]() -> HashType; From a86e4bd58450ad6e5ca2fa17b7f76eafa4dc3c08 Mon Sep 17 00:00:00 2001 From: Josh L Date: Thu, 17 Nov 2022 00:10:56 +0000 Subject: [PATCH 28/33] Fix formatting --- proposals/p2200.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/p2200.md b/proposals/p2200.md index 22e5dfdb9b67d..dfe692d5af399 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -781,7 +781,7 @@ dependent on the template parameter: interface Serializable { fn Serialize[me: Self](); } - + interface Printable { fn Print[me: Self](); } From 4af340ef3f42d7cbd11acef7f2432e0389195693 Mon Sep 17 00:00:00 2001 From: Josh L Date: Thu, 17 Nov 2022 00:15:10 +0000 Subject: [PATCH 29/33] Implement suggestions from review --- proposals/p2200.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/proposals/p2200.md b/proposals/p2200.md index dfe692d5af399..1cd8df676b83b 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -339,8 +339,6 @@ and further specified by open proposal The `auto` keyword may also be used to omit the return type, as specified in [#826: Function return type inference](https://github.com/carbon-language/carbon-lang/pull/826). -This feature is being reconsidered in the open question-for-leads issue -[#1008: How much complexity should we invest in deduced/inferred return types?](https://github.com/carbon-language/carbon-lang/issues/1008). The semantics of `let x:! auto = ...` is the subject of open question-for-leads issue From 1d3b74a2d44e947625068461e9440fe42f26f4e8 Mon Sep 17 00:00:00 2001 From: Josh L Date: Thu, 17 Nov 2022 00:35:35 +0000 Subject: [PATCH 30/33] Add note based on review comment --- proposals/p2200.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/proposals/p2200.md b/proposals/p2200.md index 1cd8df676b83b..0d62b750b3216 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -867,6 +867,10 @@ dependent: - `expr as T` - `a as expr` +Note that we may revisit some of these decisions in the future. For example, we +could say that `expr` being dependent doesn't mean `if expr then a else b` and +`expr as T` are dependent, but we are using a conservative rule for now. + For `match`, which `case` body is executed may be dependent on the type of the match expression. For example: From ba53094be9f4b536722a5a098a33639b1dee8aee Mon Sep 17 00:00:00 2001 From: Josh L Date: Thu, 17 Nov 2022 01:03:43 +0000 Subject: [PATCH 31/33] Clarify connection between template dependent and monomorphization errors. --- proposals/p2200.md | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/proposals/p2200.md b/proposals/p2200.md index 0d62b750b3216..3bdecf364efbd 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -715,13 +715,21 @@ Expressions fall under three categories: - Expressions that are valid and have meaning determined without knowing the value of any template parameter are _not template dependent_. - Expressions whose meaning and validity requires knowing the value of a - template parameter are _template dependent_. + template parameter are _template dependent_. Template dependent expressions + are not fully type checked until the template is instantiated, which can + result in monomorphization errors. Further, template dependent + subexpressions commonly cause a containing expression to also be dependent, + as described in the + ["use of dependent value is dependent" section](#use-of-dependent-value-is-dependent). - Expressions that have a meaning without knowing the value of any template parameter, assuming it is valid, but whose validity requires knowing the value of a template parameter are _template validity dependent_. These expressions can trigger a monomorphization error, but are not considered template dependent for purposes of a containing expression. +The compiler will type check expressions that are not template dependent when +the function is defined, and they won't trigger monomorphization errors. + Note that an expression may not be template dependent even though it has a template-dependent sub-expression. For example, a function may have a value that is function dependent, but calling that function only needs the type of the @@ -862,14 +870,16 @@ dependent: - `(expr)` - `F(expr)` - `expr + 1` -- `if expr then a else b` - `if a then expr else b` -- `expr as T` - `a as expr` -Note that we may revisit some of these decisions in the future. For example, we -could say that `expr` being dependent doesn't mean `if expr then a else b` and -`expr as T` are dependent, but we are using a conservative rule for now. +In some cases, an expression's type and value category can be determined even +when a subexpression is dependent. This makes the expression template validity +dependent, rather than dependent. For example, the types of these expressions +are not dependent, even when `expr` is a dependent subexpression: + +- `if expr then a else b` +- `expr as T` For `match`, which `case` body is executed may be dependent on the type of the match expression. For example: From 2b49dc4cd4cf9387b2744ede825ad231a8b288f3 Mon Sep 17 00:00:00 2001 From: josh11b Date: Thu, 17 Nov 2022 10:14:57 -0800 Subject: [PATCH 32/33] Apply suggestions from code review Co-authored-by: Chandler Carruth --- proposals/p2200.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/p2200.md b/proposals/p2200.md index 3bdecf364efbd..907e4053f0f08 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -51,7 +51,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ## Abstract -Add template generics, with optional constraints but no SFINAE, to Carbon. +Add template generics, with optional constraints but no [SFINAE](https://en.cppreference.com/w/cpp/language/sfinae), to Carbon. Template generics allows the compiler to postpone type checking of expressions dependent on a template parameter until the function is called and the value of that parameter is known. From c328b0abb9c114da8152d0465e1efec7e953a04d Mon Sep 17 00:00:00 2001 From: Josh L Date: Thu, 17 Nov 2022 18:15:19 +0000 Subject: [PATCH 33/33] Fix formatting --- proposals/p2200.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/proposals/p2200.md b/proposals/p2200.md index 907e4053f0f08..d6980cdb8558b 100644 --- a/proposals/p2200.md +++ b/proposals/p2200.md @@ -51,10 +51,11 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ## Abstract -Add template generics, with optional constraints but no [SFINAE](https://en.cppreference.com/w/cpp/language/sfinae), to Carbon. -Template generics allows the compiler to postpone type checking of expressions -dependent on a template parameter until the function is called and the value of -that parameter is known. +Add template generics, with optional constraints but no +[SFINAE](https://en.cppreference.com/w/cpp/language/sfinae), to Carbon. Template +generics allows the compiler to postpone type checking of expressions dependent +on a template parameter until the function is called and the value of that +parameter is known. Example usage: