|
@@ -14,13 +14,13 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
- [Background](#background)
|
|
- [Background](#background)
|
|
|
- [Generic parameters](#generic-parameters)
|
|
- [Generic parameters](#generic-parameters)
|
|
|
- [Interfaces](#interfaces)
|
|
- [Interfaces](#interfaces)
|
|
|
- - [Relationship to templates](#relationship-to-templates)
|
|
|
|
|
-- [Goals](#goals)
|
|
|
|
|
|
|
+- [Templates](#templates)
|
|
|
|
|
+- [Checked-generic goals](#checked-generic-goals)
|
|
|
- [Use cases](#use-cases)
|
|
- [Use cases](#use-cases)
|
|
|
- [Generic programming](#generic-programming)
|
|
- [Generic programming](#generic-programming)
|
|
|
- [Upgrade path from C++ abstract interfaces](#upgrade-path-from-c-abstract-interfaces)
|
|
- [Upgrade path from C++ abstract interfaces](#upgrade-path-from-c-abstract-interfaces)
|
|
|
- [Dependency injection](#dependency-injection)
|
|
- [Dependency injection](#dependency-injection)
|
|
|
- - [Generics instead of open overloading and ADL](#generics-instead-of-open-overloading-and-adl)
|
|
|
|
|
|
|
+ - [Checked generics instead of open overloading and ADL](#checked-generics-instead-of-open-overloading-and-adl)
|
|
|
- [Performance](#performance)
|
|
- [Performance](#performance)
|
|
|
- [Better compiler experience](#better-compiler-experience)
|
|
- [Better compiler experience](#better-compiler-experience)
|
|
|
- [Encapsulation](#encapsulation)
|
|
- [Encapsulation](#encapsulation)
|
|
@@ -34,11 +34,10 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
- [Interfaces are nominal](#interfaces-are-nominal)
|
|
- [Interfaces are nominal](#interfaces-are-nominal)
|
|
|
- [Interop and evolution](#interop-and-evolution)
|
|
- [Interop and evolution](#interop-and-evolution)
|
|
|
- [Bridge for C++ customization points](#bridge-for-c-customization-points)
|
|
- [Bridge for C++ customization points](#bridge-for-c-customization-points)
|
|
|
-- [What we are not doing](#what-we-are-not-doing)
|
|
|
|
|
|
|
+- [What we are not doing with checked generics](#what-we-are-not-doing-with-checked-generics)
|
|
|
- [Not the full flexibility of templates](#not-the-full-flexibility-of-templates)
|
|
- [Not the full flexibility of templates](#not-the-full-flexibility-of-templates)
|
|
|
- - [Template use cases that are out of scope](#template-use-cases-that-are-out-of-scope)
|
|
|
|
|
- - [Generics will be checked when defined](#generics-will-be-checked-when-defined)
|
|
|
|
|
- - [Specialization strategy](#specialization-strategy)
|
|
|
|
|
|
|
+ - [Checked generics will be checked when defined](#checked-generics-will-be-checked-when-defined)
|
|
|
|
|
+ - [Implementation strategy](#implementation-strategy)
|
|
|
- [References](#references)
|
|
- [References](#references)
|
|
|
|
|
|
|
|
<!-- tocstop -->
|
|
<!-- tocstop -->
|
|
@@ -62,7 +61,8 @@ Carbon's checked generics will feature
|
|
|
[early type checking](terminology.md#early-versus-late-type-checking) and
|
|
[early type checking](terminology.md#early-versus-late-type-checking) and
|
|
|
[complete definition checking](terminology.md#complete-definition-checking).
|
|
[complete definition checking](terminology.md#complete-definition-checking).
|
|
|
|
|
|
|
|
-Carbon's template generics, in contrast, will more closely follow the
|
|
|
|
|
|
|
+Carbon's [template generics](#templates), in contrast, will more closely follow
|
|
|
|
|
+the
|
|
|
[compile-time duck typing](https://en.wikipedia.org/wiki/Duck_typing#Templates_or_generic_types)
|
|
[compile-time duck typing](https://en.wikipedia.org/wiki/Duck_typing#Templates_or_generic_types)
|
|
|
approach of C++ templates.
|
|
approach of C++ templates.
|
|
|
|
|
|
|
@@ -105,7 +105,7 @@ languages:
|
|
|
(compile-time only)
|
|
(compile-time only)
|
|
|
- [Abstract base classes](<https://en.wikipedia.org/wiki/Class_(computer_programming)#Abstract_and_concrete>)
|
|
- [Abstract base classes](<https://en.wikipedia.org/wiki/Class_(computer_programming)#Abstract_and_concrete>)
|
|
|
in C++, etc. (run-time only)
|
|
in C++, etc. (run-time only)
|
|
|
-- [Go interfaces](https://gobyexample.com/interfaces) (run-time only)
|
|
|
|
|
|
|
+- [Go interfaces](https://gobyexample.com/interfaces)
|
|
|
|
|
|
|
|
In addition to specifying the methods available on a type, we may in the future
|
|
In addition to specifying the methods available on a type, we may in the future
|
|
|
expand the role of interfaces to allow other type constraints, such as on size,
|
|
expand the role of interfaces to allow other type constraints, such as on size,
|
|
@@ -116,7 +116,7 @@ instead of, rather than in addition to, standard inheritance-and-classes
|
|
|
object-oriented language support. For the moment, everything beyond specifying
|
|
object-oriented language support. For the moment, everything beyond specifying
|
|
|
the _methods_ available is out of scope.
|
|
the _methods_ available is out of scope.
|
|
|
|
|
|
|
|
-### Relationship to templates
|
|
|
|
|
|
|
+## Templates
|
|
|
|
|
|
|
|
The entire idea of statically typed languages is that coding against specific
|
|
The entire idea of statically typed languages is that coding against specific
|
|
|
types and interfaces is a better model and experience. Unfortunately, templates
|
|
types and interfaces is a better model and experience. Unfortunately, templates
|
|
@@ -124,37 +124,37 @@ don't provide many of those benefits to programmers until it's too late, when
|
|
|
users are consuming the API. Templates also come with high overhead, such as
|
|
users are consuming the API. Templates also come with high overhead, such as
|
|
|
[template error messages](#better-compiler-experience).
|
|
[template error messages](#better-compiler-experience).
|
|
|
|
|
|
|
|
-We want Carbon code to move towards more rigorously type checked constructs.
|
|
|
|
|
|
|
+We want Carbon code to move towards more rigorously type-checked constructs.
|
|
|
However, existing C++ code is full of unrestricted usage of compile-time
|
|
However, existing C++ code is full of unrestricted usage of compile-time
|
|
|
duck-typed templates. They are incredibly convenient to write and so likely will
|
|
duck-typed templates. They are incredibly convenient to write and so likely will
|
|
|
continue to exist for a long time.
|
|
continue to exist for a long time.
|
|
|
|
|
|
|
|
-The question of whether Carbon has direct support for templates is out of scope
|
|
|
|
|
-for this document. The generics design is not completely separate from
|
|
|
|
|
-templates, so it is written as if Carbon will have its own templating system. It
|
|
|
|
|
-is assumed to be similar to C++ templates with some specific changes:
|
|
|
|
|
|
|
+Carbon will have direct support for templates in addition to checked generics.
|
|
|
|
|
+Carbon's template system will be similar to C++ templates with some specific
|
|
|
|
|
+changes:
|
|
|
|
|
|
|
|
-- It may have some limitations to be more compatible with generics, much like
|
|
|
|
|
- how we
|
|
|
|
|
- [restrict overloading](#generics-instead-of-open-overloading-and-adl).
|
|
|
|
|
|
|
+- It may have some limitations to be more compatible with checked generics,
|
|
|
|
|
+ much like how we
|
|
|
|
|
+ [restrict overloading](#checked-generics-instead-of-open-overloading-and-adl).
|
|
|
- We likely will have a different method of selecting between different
|
|
- We likely will have a different method of selecting between different
|
|
|
template instantiations, since
|
|
template instantiations, since
|
|
|
[SFINAE](https://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error)
|
|
[SFINAE](https://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error)
|
|
|
makes it difficult to deliver high quality compiler diagnostics.
|
|
makes it difficult to deliver high quality compiler diagnostics.
|
|
|
|
|
|
|
|
-We assume Carbon will have templates for a few different reasons:
|
|
|
|
|
|
|
+Other aspects of Carbon will reduce the number of situations where errors will
|
|
|
|
|
+only be detected after the template is
|
|
|
|
|
+[instantiated](terminology.md#instantiation):
|
|
|
|
|
|
|
|
-- Carbon generics will definitely have to interact with _C++_ templates, and
|
|
|
|
|
- many of the issues will be similar.
|
|
|
|
|
-- We want to leave room in the design for templates, since it seems like it
|
|
|
|
|
- would be easier to remove templates if they are not pulling their weight
|
|
|
|
|
- than figure out how to add them in if they turn out to be needed.
|
|
|
|
|
-- We may want to have templates in Carbon as a temporary measure, to make it
|
|
|
|
|
- easier for users to transition off of C++ templates.
|
|
|
|
|
|
|
+- Carbon's grammar won't require type information to disambiguate parsing, and
|
|
|
|
|
+ so definitions may be parsed without knowing the values of template
|
|
|
|
|
+ arguments.
|
|
|
|
|
+- Carbon's name resolution is more restricted. Every package has its own
|
|
|
|
|
+ namespace, and there will be no
|
|
|
|
|
+ [argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl).
|
|
|
|
|
|
|
|
-## Goals
|
|
|
|
|
|
|
+## Checked-generic goals
|
|
|
|
|
|
|
|
-Our goal for generics support in Carbon is to get most of the expressive
|
|
|
|
|
|
|
+Our goal for checked generics support in Carbon is to get most of the expressive
|
|
|
benefits of C++ templates and open overloading with fewer downsides.
|
|
benefits of C++ templates and open overloading with fewer downsides.
|
|
|
Additionally, we want to support some dynamic dispatch use cases; for example,
|
|
Additionally, we want to support some dynamic dispatch use cases; for example,
|
|
|
in cases that inheritance struggles with.
|
|
in cases that inheritance struggles with.
|
|
@@ -162,7 +162,7 @@ in cases that inheritance struggles with.
|
|
|
### Use cases
|
|
### Use cases
|
|
|
|
|
|
|
|
To clarify the expressive range we are aiming for, here are some specific use
|
|
To clarify the expressive range we are aiming for, here are some specific use
|
|
|
-cases we expect Carbon generics to cover.
|
|
|
|
|
|
|
+cases we expect Carbon checked generics to cover.
|
|
|
|
|
|
|
|
#### Generic programming
|
|
#### Generic programming
|
|
|
|
|
|
|
@@ -183,18 +183,19 @@ generally be used with [static dispatch](#dispatch-control).
|
|
|
|
|
|
|
|
#### Upgrade path from C++ abstract interfaces
|
|
#### Upgrade path from C++ abstract interfaces
|
|
|
|
|
|
|
|
-Interfaces in C++ are often represented by abstract base classes. Generics
|
|
|
|
|
-should offer an alternative that does not rely on inheritance. This means looser
|
|
|
|
|
-coupling and none of the problems of multiple inheritance. Some people, such as
|
|
|
|
|
|
|
+Interfaces in C++ are often represented by abstract base classes. Checked
|
|
|
|
|
+generics should offer an alternative that does not rely on inheritance. This
|
|
|
|
|
+means looser coupling and none of the problems of multiple inheritance. Some
|
|
|
|
|
+people, such as
|
|
|
[Sean Parent](https://sean-parent.stlab.cc/papers-and-presentations/#better-code-runtime-polymorphism),
|
|
[Sean Parent](https://sean-parent.stlab.cc/papers-and-presentations/#better-code-runtime-polymorphism),
|
|
|
advocate for runtime polymorphism patterns in C++ that avoid inheritance because
|
|
advocate for runtime polymorphism patterns in C++ that avoid inheritance because
|
|
|
it can cause runtime performance, correctness, and code maintenance problems in
|
|
it can cause runtime performance, correctness, and code maintenance problems in
|
|
|
some situations. Those patterns require a lot of boilerplate and complexity in
|
|
some situations. Those patterns require a lot of boilerplate and complexity in
|
|
|
C++. It would be nice if those patterns were simpler to express with Carbon
|
|
C++. It would be nice if those patterns were simpler to express with Carbon
|
|
|
-generics. More generally, Carbon generics will provide an alternative for those
|
|
|
|
|
-situations inheritance doesn't handle as well. As a specific example, we would
|
|
|
|
|
-like Carbon generics to supplant the need to support multiple inheritance in
|
|
|
|
|
-Carbon.
|
|
|
|
|
|
|
+checked generics. More generally, Carbon checked generics will provide an
|
|
|
|
|
+alternative for those situations inheritance doesn't handle as well. As a
|
|
|
|
|
+specific example, we would like Carbon checked generics to supplant the need to
|
|
|
|
|
+support multiple inheritance in Carbon.
|
|
|
|
|
|
|
|
This is a case that would use [dynamic dispatch](#dispatch-control).
|
|
This is a case that would use [dynamic dispatch](#dispatch-control).
|
|
|
|
|
|
|
@@ -202,15 +203,16 @@ This is a case that would use [dynamic dispatch](#dispatch-control).
|
|
|
|
|
|
|
|
Types which only support subclassing for test stubs and mocks, as in
|
|
Types which only support subclassing for test stubs and mocks, as in
|
|
|
["dependency injection"](https://en.wikipedia.org/wiki/Dependency_injection),
|
|
["dependency injection"](https://en.wikipedia.org/wiki/Dependency_injection),
|
|
|
-should be able to easily migrate to generics. This extends outside the realm of
|
|
|
|
|
-testing, allowing general configuration of how dependencies can be satisfied.
|
|
|
|
|
-For example, generics might be used to configure how a library writes logs.
|
|
|
|
|
|
|
+should be able to easily migrate to checked generics. This extends outside the
|
|
|
|
|
+realm of testing, allowing general configuration of how dependencies can be
|
|
|
|
|
+satisfied. For example, checked generics might be used to configure how a
|
|
|
|
|
+library writes logs.
|
|
|
|
|
|
|
|
This would allow you to avoid the runtime overhead of virtual functions, using
|
|
This would allow you to avoid the runtime overhead of virtual functions, using
|
|
|
[static dispatch](#dispatch-control) without the
|
|
[static dispatch](#dispatch-control) without the
|
|
|
[poor build experience of templates](#better-compiler-experience).
|
|
[poor build experience of templates](#better-compiler-experience).
|
|
|
|
|
|
|
|
-#### Generics instead of open overloading and ADL
|
|
|
|
|
|
|
+#### Checked generics instead of open overloading and ADL
|
|
|
|
|
|
|
|
One name lookup problem we would like to avoid is caused by open overloading.
|
|
One name lookup problem we would like to avoid is caused by open overloading.
|
|
|
Overloading is where you provide multiple implementations of a function with the
|
|
Overloading is where you provide multiple implementations of a function with the
|
|
@@ -226,20 +228,22 @@ function was originally defined. Together these enable
|
|
|
This is commonly used to provide a type-specific implementation of some
|
|
This is commonly used to provide a type-specific implementation of some
|
|
|
operation, but doesn't provide any enforcement of consistency across the
|
|
operation, but doesn't provide any enforcement of consistency across the
|
|
|
different overloads. It makes the meaning of code dependent on which overloads
|
|
different overloads. It makes the meaning of code dependent on which overloads
|
|
|
-are imported, and is at odds with being able to type check a function
|
|
|
|
|
-generically.
|
|
|
|
|
|
|
+are imported, and is at odds with being able to type check a function's
|
|
|
|
|
+definition before instantiation.
|
|
|
|
|
|
|
|
Our goal is to address this use case, known more generally as
|
|
Our goal is to address this use case, known more generally as
|
|
|
[the expression problem](https://eli.thegreenplace.net/2016/the-expression-problem-and-its-solutions),
|
|
[the expression problem](https://eli.thegreenplace.net/2016/the-expression-problem-and-its-solutions),
|
|
|
-with a generics mechanism that does enforce consistency so that type checking is
|
|
|
|
|
-possible without seeing all implementations. This will be Carbon's replacement
|
|
|
|
|
-for open overloading. As a consequence, Carbon generics will need to be able to
|
|
|
|
|
-support operator overloading.
|
|
|
|
|
|
|
+with a checked-generics mechanism that does enforce consistency so that type
|
|
|
|
|
+checking is possible without seeing all implementations. This will be Carbon's
|
|
|
|
|
+replacement for open overloading. This is encapsulated in Carbon's
|
|
|
|
|
+["one static open extension mechanism" principle](/docs/project/principles/static_open_extension.md).
|
|
|
|
|
+As a consequence, Carbon checked generics will need to be able to support
|
|
|
|
|
+operator overloading.
|
|
|
|
|
|
|
|
A specific example is the absolute value function `Abs`. We would like to write
|
|
A specific example is the absolute value function `Abs`. We would like to write
|
|
|
`Abs(x)` for a variety of types. For some types `T`, such as `Int32` or
|
|
`Abs(x)` for a variety of types. For some types `T`, such as `Int32` or
|
|
|
`Float64`, the return type will be the same `T`. For other types, such as
|
|
`Float64`, the return type will be the same `T`. For other types, such as
|
|
|
-`Complex64` or `Quaternion`, the return type will be different. The generic
|
|
|
|
|
|
|
+`Complex64` or `Quaternion`, the return type will be different. Checked generic
|
|
|
functions that call `Abs` will need a way to specify whether they only operate
|
|
functions that call `Abs` will need a way to specify whether they only operate
|
|
|
on `T` such that `Abs` has signature `T -> T`.
|
|
on `T` such that `Abs` has signature `T -> T`.
|
|
|
|
|
|
|
@@ -250,14 +254,14 @@ overloading, which will
|
|
|
### Performance
|
|
### Performance
|
|
|
|
|
|
|
|
For any real-world C++ template, there shall be an idiomatic reformulation in
|
|
For any real-world C++ template, there shall be an idiomatic reformulation in
|
|
|
-Carbon generics that has equal or better performance.
|
|
|
|
|
|
|
+Carbon checked generics that has equal or better performance.
|
|
|
[Performance is the top priority for Carbon](/docs/project/goals.md#performance-critical-software),
|
|
[Performance is the top priority for Carbon](/docs/project/goals.md#performance-critical-software),
|
|
|
-and we expect to use generics pervasively, and so they can't compromise that
|
|
|
|
|
-goal in release builds.
|
|
|
|
|
|
|
+and we expect to use checked generics pervasively, and so they can't compromise
|
|
|
|
|
+that goal in release builds.
|
|
|
|
|
|
|
|
**Nice to have:** There are cases where we should aim to do better than C++
|
|
**Nice to have:** There are cases where we should aim to do better than C++
|
|
|
-templates. For example, the additional structure of generics should make it
|
|
|
|
|
-easier to reduce generated code duplication, reducing code size and cache
|
|
|
|
|
|
|
+templates. For example, the additional structure of checked generics should make
|
|
|
|
|
+it easier to reduce generated code duplication, reducing code size and cache
|
|
|
misses.
|
|
misses.
|
|
|
|
|
|
|
|
### Better compiler experience
|
|
### Better compiler experience
|
|
@@ -266,12 +270,13 @@ Compared to C++ templates, we expect to reduce build times, particularly in
|
|
|
development builds. We also expect the compiler to be able to report clearer
|
|
development builds. We also expect the compiler to be able to report clearer
|
|
|
errors, and report them earlier in the build process.
|
|
errors, and report them earlier in the build process.
|
|
|
|
|
|
|
|
-One source of improvement is that the bodies of generic functions and types can
|
|
|
|
|
-be type checked once when they are defined, instead of every time they are used.
|
|
|
|
|
-This is both a reduction in the total work done, and how errors can be reported
|
|
|
|
|
-earlier. On use, the errors can be a lot clearer since they will be of the form
|
|
|
|
|
-"argument did not satisfy function's contract as stated in its signature"
|
|
|
|
|
-instead of "substitution failed at this line of the function's implementation."
|
|
|
|
|
|
|
+One source of improvement is that the bodies of checked generic functions and
|
|
|
|
|
+types can be type checked once when they are defined, instead of every time they
|
|
|
|
|
+are used. This is both a reduction in the total work done, and how errors can be
|
|
|
|
|
+reported earlier. On use, the errors can be a lot clearer since they will be of
|
|
|
|
|
+the form "argument did not satisfy function's contract as stated in its
|
|
|
|
|
+signature" instead of "substitution failed at this line of the function's
|
|
|
|
|
+implementation."
|
|
|
|
|
|
|
|
**Nice to have:** In development builds, we will have the option of using
|
|
**Nice to have:** In development builds, we will have the option of using
|
|
|
[dynamic dispatch](#dispatch-control) to reduce build times. We may also be able
|
|
[dynamic dispatch](#dispatch-control) to reduce build times. We may also be able
|
|
@@ -284,31 +289,30 @@ arguments or identical implementations and only generating code for them once.
|
|
|
With a template, the implementation is part of the interface and types are only
|
|
With a template, the implementation is part of the interface and types are only
|
|
|
checked when the function is called and the template is instantiated.
|
|
checked when the function is called and the template is instantiated.
|
|
|
|
|
|
|
|
-A generic function is type checked when it is defined, and type checking can't
|
|
|
|
|
-use any information that is only known when the function is instantiated such as
|
|
|
|
|
-the exact argument types. Furthermore, calls to a generic function may be type
|
|
|
|
|
-checked using only its declaration, not its body. You should be able to call a
|
|
|
|
|
-generic function using only a forward declaration.
|
|
|
|
|
|
|
+A checked-generic function is type checked when it is defined, and type checking
|
|
|
|
|
+can't use any information that is only known when the function is instantiated
|
|
|
|
|
+such as the exact argument types. Furthermore, calls to a checked-generic
|
|
|
|
|
+function may be type checked using only its declaration, not its body.
|
|
|
|
|
|
|
|
### Predictability
|
|
### Predictability
|
|
|
|
|
|
|
|
-A general property of generics is they are more predictable than templates. They
|
|
|
|
|
-make clear when a type satisfies the requirements of a function; they have a
|
|
|
|
|
-documented contract. Further, that contract is enforced by the compiler, not
|
|
|
|
|
-sensitive to implementation details in the function body. This eases evolution
|
|
|
|
|
-by reducing (but not eliminating) the impact of
|
|
|
|
|
|
|
+A general property of checked generics is they are more predictable than
|
|
|
|
|
+templates. They make clear when a type satisfies the requirements of a function;
|
|
|
|
|
+they have a documented contract. Further, that contract is enforced by the
|
|
|
|
|
+compiler, not sensitive to implementation details in the function body. This
|
|
|
|
|
+eases evolution by reducing (but not eliminating) the impact of
|
|
|
[Hyrum's law](https://www.hyrumslaw.com/).
|
|
[Hyrum's law](https://www.hyrumslaw.com/).
|
|
|
|
|
|
|
|
**Nice to have:** We also want well-defined boundaries between what is legal and
|
|
**Nice to have:** We also want well-defined boundaries between what is legal and
|
|
|
not. This is "will my code be accepted by the compiler" predictability. We would
|
|
not. This is "will my code be accepted by the compiler" predictability. We would
|
|
|
prefer to avoid algorithms in the compiler with the form "run for up to N steps
|
|
prefer to avoid algorithms in the compiler with the form "run for up to N steps
|
|
|
and report an error if it isn't resolved by then." For example, C++ compilers
|
|
and report an error if it isn't resolved by then." For example, C++ compilers
|
|
|
-will typically have a template recursion limit. With generics, these problems
|
|
|
|
|
-arise due to trying to reason whether something is legal in all possible
|
|
|
|
|
-instantiations, rather than with specific, concrete types.
|
|
|
|
|
|
|
+will typically have a template recursion limit. With checked generics, these
|
|
|
|
|
+problems arise due to trying to reason whether something is legal in all
|
|
|
|
|
+possible instantiations, rather than with specific, concrete types.
|
|
|
|
|
|
|
|
Some of this is likely unavoidable or too costly to avoid, as most existing
|
|
Some of this is likely unavoidable or too costly to avoid, as most existing
|
|
|
-generics systems
|
|
|
|
|
|
|
+checked generics systems
|
|
|
[have undecidable aspects to their type system](https://3fx.ch/typing-is-hard.html),
|
|
[have undecidable aspects to their type system](https://3fx.ch/typing-is-hard.html),
|
|
|
including [Rust](https://sdleffler.github.io/RustTypeSystemTuringComplete/) and
|
|
including [Rust](https://sdleffler.github.io/RustTypeSystemTuringComplete/) and
|
|
|
[Swift](https://forums.swift.org/t/swift-type-checking-is-undecidable/39024). We
|
|
[Swift](https://forums.swift.org/t/swift-type-checking-is-undecidable/39024). We
|
|
@@ -343,13 +347,13 @@ we should invest in putting the proof search into IDEs or other tooling.
|
|
|
Enable simple user control of whether to use dynamic or static dispatch.
|
|
Enable simple user control of whether to use dynamic or static dispatch.
|
|
|
|
|
|
|
|
**Implementation strategy:** There are two strategies for generating code for
|
|
**Implementation strategy:** There are two strategies for generating code for
|
|
|
-generic functions:
|
|
|
|
|
|
|
+checked-generic functions:
|
|
|
|
|
|
|
|
-- Static specialization strategy: Like template parameters, the values for
|
|
|
|
|
- generic parameters must be statically known at the callsite, or known to be
|
|
|
|
|
- a generic parameter to the calling function. This can generate separate,
|
|
|
|
|
- specialized versions of each combination of generic and template arguments,
|
|
|
|
|
- in order to optimize for those types or values.
|
|
|
|
|
|
|
+- Static strategy: Like template parameters, the values for checked parameters
|
|
|
|
|
+ must be statically known at the callsite, or known to be a generic parameter
|
|
|
|
|
+ to the calling function. This can generate separate, specialized versions of
|
|
|
|
|
+ each combination of checked and template arguments, in order to optimize for
|
|
|
|
|
+ those types or values.
|
|
|
- Dynamic strategy: This is when the compiler generates a single version of
|
|
- Dynamic strategy: This is when the compiler generates a single version of
|
|
|
the function that uses runtime dispatch to get something semantically
|
|
the function that uses runtime dispatch to get something semantically
|
|
|
equivalent to separate instantiation, but likely with different size, build
|
|
equivalent to separate instantiation, but likely with different size, build
|
|
@@ -363,17 +367,17 @@ code analysis, specific features used in the code, or profiling -- maybe some
|
|
|
specific specializations are needed for performance, but others would just be
|
|
specific specializations are needed for performance, but others would just be
|
|
|
code bloat.
|
|
code bloat.
|
|
|
|
|
|
|
|
-We require that all generic functions can be compiled using the static
|
|
|
|
|
-specialization strategy. For example, the values for generic parameters must be
|
|
|
|
|
|
|
+We require that all checked generic functions can be compiled using the static
|
|
|
|
|
+specialization strategy. For example, the values for checked parameters must be
|
|
|
statically known at the callsite. Other limitations are
|
|
statically known at the callsite. Other limitations are
|
|
|
-[listed below](#specialization-strategy).
|
|
|
|
|
|
|
+[listed below](#implementation-strategy).
|
|
|
|
|
|
|
|
-**Nice to have:** It is desirable that the majority of functions with generic
|
|
|
|
|
|
|
+**Nice to have:** It is desirable that the majority of functions with checked
|
|
|
parameters also support the dynamic strategy. Specific features may prevent the
|
|
parameters also support the dynamic strategy. Specific features may prevent the
|
|
|
compiler from using the dynamic strategy, but they should ideally be relatively
|
|
compiler from using the dynamic strategy, but they should ideally be relatively
|
|
|
rare, and easy to identify. Language features should avoid making it observable
|
|
rare, and easy to identify. Language features should avoid making it observable
|
|
|
whether function code generated once or many times. For example, you should not
|
|
whether function code generated once or many times. For example, you should not
|
|
|
-be able to take the address of a function with generic parameters, or determine
|
|
|
|
|
|
|
+be able to take the address of a function with checked parameters, or determine
|
|
|
if a function was instantiated more than once using function-local static
|
|
if a function was instantiated more than once using function-local static
|
|
|
variables.
|
|
variables.
|
|
|
|
|
|
|
@@ -382,12 +386,14 @@ limit the extent it is used automatically by implementations. For example, the
|
|
|
following features would benefit substantially from guaranteed monomorphization:
|
|
following features would benefit substantially from guaranteed monomorphization:
|
|
|
|
|
|
|
|
- Field packing in class layout. For example, packing a `bool` into the lower
|
|
- Field packing in class layout. For example, packing a `bool` into the lower
|
|
|
- bits of a pointer, or packing bit-fields with generic widths.
|
|
|
|
|
|
|
+ bits of a pointer, or packing bit-fields with checked-parameter widths.
|
|
|
- Allocating local variables in stack storage. Without monomorphization, we
|
|
- Allocating local variables in stack storage. Without monomorphization, we
|
|
|
would need to perform dynamic memory allocation -- whether on the stack or
|
|
would need to perform dynamic memory allocation -- whether on the stack or
|
|
|
- the heap -- for local variables whose sizes depend on generic parameters.
|
|
|
|
|
|
|
+ the heap -- for local variables whose sizes depend on checked parameters.
|
|
|
- Passing parameters to functions. We cannot pass values of generic types in
|
|
- Passing parameters to functions. We cannot pass values of generic types in
|
|
|
registers.
|
|
registers.
|
|
|
|
|
+- Finding [specialized](terminology.md#specialization) implementations of
|
|
|
|
|
+ interfaces beyond those clearly needed from the function signature.
|
|
|
|
|
|
|
|
While it is possible to address these with dynamic dispatch, handling some of
|
|
While it is possible to address these with dynamic dispatch, handling some of
|
|
|
them might have far-reaching and surprising performance implications. We don't
|
|
them might have far-reaching and surprising performance implications. We don't
|
|
@@ -407,53 +413,50 @@ acceptable even when running a development or debug build.
|
|
|
### Upgrade path from templates
|
|
### Upgrade path from templates
|
|
|
|
|
|
|
|
We want there to be a natural, incremental upgrade path from templated code to
|
|
We want there to be a natural, incremental upgrade path from templated code to
|
|
|
-generic code.
|
|
|
|
|
-[Assuming Carbon will support templates directly](#relationship-to-templates),
|
|
|
|
|
-the first step of migrating C++ template code would be to first convert it to a
|
|
|
|
|
-Carbon template. The problem is then how to convert templates to generics within
|
|
|
|
|
-Carbon. This gives us these sub-goals:
|
|
|
|
|
-
|
|
|
|
|
-- Users should be able to convert a single template parameter to be generic at
|
|
|
|
|
- a time. A hybrid function with both template and generic parameters has all
|
|
|
|
|
|
|
+checked-generic code. The first step of migrating C++ template code would be to
|
|
|
|
|
+first convert it to a [Carbon template](#templates). The problem is then how to
|
|
|
|
|
+convert templates to checked generics within Carbon. This gives us these
|
|
|
|
|
+sub-goals:
|
|
|
|
|
+
|
|
|
|
|
+- Users should be able to convert a single template parameter to be checked at
|
|
|
|
|
+ a time. A hybrid function with both template and checked parameters has all
|
|
|
the limitations of a template function: it can't be completely definition
|
|
the limitations of a template function: it can't be completely definition
|
|
|
checked, it can't use the dynamic strategy, etc. Even so, there are still
|
|
checked, it can't use the dynamic strategy, etc. Even so, there are still
|
|
|
benefits from enforcing the function's declared contract for those
|
|
benefits from enforcing the function's declared contract for those
|
|
|
parameters that have been converted.
|
|
parameters that have been converted.
|
|
|
-- Converting from a template parameter to a generic parameter should be safe.
|
|
|
|
|
|
|
+- Converting from a template parameter to a checked parameter should be safe.
|
|
|
It should either work or fail to compile, never silently change semantics.
|
|
It should either work or fail to compile, never silently change semantics.
|
|
|
- We should minimize the effort to convert functions and types from templated
|
|
- We should minimize the effort to convert functions and types from templated
|
|
|
- to generic. Ideally it should just require specifying the type constraints,
|
|
|
|
|
|
|
+ to checked. Ideally it should just require specifying the type constraints,
|
|
|
affecting just the signature of the function, not its body.
|
|
affecting just the signature of the function, not its body.
|
|
|
-- **Nice to have:** It should be legal to call templated code from generic
|
|
|
|
|
- code when it would have the same semantics as if called from non-generic
|
|
|
|
|
- code, and an error otherwise. This is to allow more templated functions to
|
|
|
|
|
- be converted to generics, instead of requiring them to be converted
|
|
|
|
|
- specifically in bottom-up order.
|
|
|
|
|
-- **Nice to have:** Provide a way to migrate from a template to a generic
|
|
|
|
|
- without immediately updating all of the types used with the template. For
|
|
|
|
|
- example, if the generic code requires types to implement a new interface,
|
|
|
|
|
- one possible solution would use the original template code to provide an
|
|
|
|
|
- implementation for that interface for any type that structurally has the
|
|
|
|
|
- methods used by the original template.
|
|
|
|
|
-
|
|
|
|
|
-If Carbon does not end up having direct support for templates, the transition
|
|
|
|
|
-will necessarily be less incremental.
|
|
|
|
|
|
|
+- **Nice to have:** It should be legal to call templated code from
|
|
|
|
|
+ checked-generic code when it would have the same semantics as if called from
|
|
|
|
|
+ non-generic code, and an error otherwise. This is to allow more templated
|
|
|
|
|
+ functions to be converted to checked generics, instead of requiring them to
|
|
|
|
|
+ be converted specifically in bottom-up order.
|
|
|
|
|
+- **Nice to have:** Provide a way to migrate from a template to a checked
|
|
|
|
|
+ generic without immediately updating all of the types used with the
|
|
|
|
|
+ template. For example, if the checked-generic code requires types to
|
|
|
|
|
+ implement a new interface, one possible solution would use the original
|
|
|
|
|
+ template code to provide an implementation for that interface for any type
|
|
|
|
|
+ that structurally has the methods used by the original template.
|
|
|
|
|
|
|
|
### Path from regular functions
|
|
### Path from regular functions
|
|
|
|
|
|
|
|
-Replacing a regular, non-parameterized function with a generic function should
|
|
|
|
|
-not affect existing callers of the function. There may be some differences, such
|
|
|
|
|
-as when taking the address of the function, but ordinary calls should not see
|
|
|
|
|
-any difference. In particular, the return type of a generic function should
|
|
|
|
|
-match, without any type erasure or additional named members.
|
|
|
|
|
|
|
+Replacing a regular, non-parameterized function with a checked-generic function
|
|
|
|
|
+should not affect existing callers of the function. There may be some
|
|
|
|
|
+differences, such as when taking the address of the function, but ordinary calls
|
|
|
|
|
+should not see any difference. In particular, the return type of a
|
|
|
|
|
+checked-generic function should match, without any type erasure or additional
|
|
|
|
|
+named members.
|
|
|
|
|
|
|
|
### Coherence
|
|
### Coherence
|
|
|
|
|
|
|
|
We want the generics system to have the
|
|
We want the generics system to have the
|
|
|
[_coherence_ property](terminology.md#coherence), so that the implementation of
|
|
[_coherence_ property](terminology.md#coherence), so that the implementation of
|
|
|
-an interface for a type is well defined. Since a generic function only depends
|
|
|
|
|
-on interface implementations, they will always behave consistently on a given
|
|
|
|
|
-type, independent of context. For more on this, see
|
|
|
|
|
|
|
+an interface for a type is well defined. Since a checked-generic function only
|
|
|
|
|
+depends on interface implementations, they will always behave consistently on a
|
|
|
|
|
+given type, independent of context. For more on this, see
|
|
|
[this description of what coherence is and why Rust enforces it](https://github.com/Ixrec/rust-orphan-rules#what-is-coherence).
|
|
[this description of what coherence is and why Rust enforces it](https://github.com/Ixrec/rust-orphan-rules#what-is-coherence).
|
|
|
|
|
|
|
|
Coherence greatly simplifies the language design, since it reduces the need for
|
|
Coherence greatly simplifies the language design, since it reduces the need for
|
|
@@ -479,7 +482,7 @@ cases remain:
|
|
|
- They should be some way of selecting between multiple implementations of an
|
|
- They should be some way of selecting between multiple implementations of an
|
|
|
interface for a given type. For example, a _Song_ might support multiple
|
|
interface for a given type. For example, a _Song_ might support multiple
|
|
|
orderings, such as by title or by artist. These would be represented by
|
|
orderings, such as by title or by artist. These would be represented by
|
|
|
- having multiple implementations of a _Comparable_ interface.
|
|
|
|
|
|
|
+ having multiple implementations of a _Ordered_ interface.
|
|
|
- In order to allow libraries to be composed, there must be some way of saying
|
|
- In order to allow libraries to be composed, there must be some way of saying
|
|
|
a type implements an interface that is in another package that the authors
|
|
a type implements an interface that is in another package that the authors
|
|
|
of the type were unaware of. This is especially important since the library
|
|
of the type were unaware of. This is especially important since the library
|
|
@@ -490,7 +493,7 @@ We should have some mechanism for addressing these use cases. There are multiple
|
|
|
approaches that could work:
|
|
approaches that could work:
|
|
|
|
|
|
|
|
- Interface implementations could be external to types and are passed in to
|
|
- Interface implementations could be external to types and are passed in to
|
|
|
- generic functions separately.
|
|
|
|
|
|
|
+ checked-generic functions separately.
|
|
|
- There could be some way to create multiple types that are compatible with a
|
|
- There could be some way to create multiple types that are compatible with a
|
|
|
given value that you can switch between using casts to select different
|
|
given value that you can switch between using casts to select different
|
|
|
interface implementations. This is the approach used by Rust
|
|
interface implementations. This is the approach used by Rust
|
|
@@ -518,14 +521,15 @@ the definition of `T`.
|
|
|
|
|
|
|
|
### Learn from others
|
|
### Learn from others
|
|
|
|
|
|
|
|
-Many languages have implemented generics systems, and we should learn from those
|
|
|
|
|
-experiences. We should copy what works and makes sense in the context of Carbon,
|
|
|
|
|
-and change decisions that led to undesirable compromises. We are taking the
|
|
|
|
|
-strongest guidance from Rust and Swift, which have similar goals and significant
|
|
|
|
|
-experience with the implementation and usability of generics. They both use
|
|
|
|
|
-nominal interfaces, were designed with generics from the start, and produce
|
|
|
|
|
-native code. Contrast with Go which uses structural interfaces, or Java which
|
|
|
|
|
-targets a virtual machine that predated its generics feature.
|
|
|
|
|
|
|
+Many languages have implemented checked-generics systems, and we should learn
|
|
|
|
|
+from those experiences. We should copy what works and makes sense in the context
|
|
|
|
|
+of Carbon, and change decisions that led to undesirable compromises. We are
|
|
|
|
|
+taking the strongest guidance from Rust and Swift, which have similar goals and
|
|
|
|
|
+significant experience with the implementation and usability of checked
|
|
|
|
|
+generics. They both use nominal interfaces, were designed with checked generics
|
|
|
|
|
+from the start, and produce native code. Contrast with Go which uses structural
|
|
|
|
|
+interfaces, or Java which targets a virtual machine that predated its generics
|
|
|
|
|
+feature.
|
|
|
|
|
|
|
|
For example, Rust has found that supporting defaults for interface methods is a
|
|
For example, Rust has found that supporting defaults for interface methods is a
|
|
|
valuable feature. It is useful for [evolution](#interop-and-evolution),
|
|
valuable feature. It is useful for [evolution](#interop-and-evolution),
|
|
@@ -559,8 +563,8 @@ interface, even if those methods happen to have the same signature.
|
|
|
### Interop and evolution
|
|
### Interop and evolution
|
|
|
|
|
|
|
|
[Evolution is a high priority for Carbon](/docs/project/goals.md#software-and-language-evolution),
|
|
[Evolution is a high priority for Carbon](/docs/project/goals.md#software-and-language-evolution),
|
|
|
-and so will need mechanisms to support evolution when using generics. New
|
|
|
|
|
-additions to an interface might:
|
|
|
|
|
|
|
+and so will need mechanisms to support evolution when using checked generics.
|
|
|
|
|
+New additions to an interface might:
|
|
|
|
|
|
|
|
- need default implementations
|
|
- need default implementations
|
|
|
- be marked "upcoming" to allow for a period of transition
|
|
- be marked "upcoming" to allow for a period of transition
|
|
@@ -607,47 +611,39 @@ to work from C++, and Carbon functions could use that interface to invoke `swap`
|
|
|
on C++ types.
|
|
on C++ types.
|
|
|
|
|
|
|
|
Similarly, we will want some way to implement Carbon interfaces for C++ types.
|
|
Similarly, we will want some way to implement Carbon interfaces for C++ types.
|
|
|
-For example, we might have a template implementation of an `Addable` interface
|
|
|
|
|
|
|
+For example, we might have a template implementation of an `AddWith` interface
|
|
|
for any C++ type that implements `operator+`.
|
|
for any C++ type that implements `operator+`.
|
|
|
|
|
|
|
|
-## What we are not doing
|
|
|
|
|
|
|
+## What we are not doing with checked generics
|
|
|
|
|
|
|
|
-What are we **not** doing with generics, particularly things that some other
|
|
|
|
|
-languages do?
|
|
|
|
|
|
|
+What are we **not** doing with checked generics, particularly things that some
|
|
|
|
|
+other languages do?
|
|
|
|
|
|
|
|
### Not the full flexibility of templates
|
|
### Not the full flexibility of templates
|
|
|
|
|
|
|
|
-Generics don't need to provide full flexibility of C++ templates:
|
|
|
|
|
|
|
+Checked generics don't need to provide full flexibility of C++ templates:
|
|
|
|
|
|
|
|
-- The current assumption is that
|
|
|
|
|
- [Carbon templates](#relationship-to-templates) will cover those cases that
|
|
|
|
|
- don't fit inside generics, such as code that relies on compile-time duck
|
|
|
|
|
- typing.
|
|
|
|
|
|
|
+- [Carbon templates](#templates) will cover those cases that don't fit inside
|
|
|
|
|
+ checked generics, such as code that relies on compile-time duck typing.
|
|
|
- We won't allow a specialization of some generic interface for some
|
|
- We won't allow a specialization of some generic interface for some
|
|
|
particular type to actually expose a _different_ interface, with different
|
|
particular type to actually expose a _different_ interface, with different
|
|
|
methods or different types in method signatures. This would break modular
|
|
methods or different types in method signatures. This would break modular
|
|
|
type checking.
|
|
type checking.
|
|
|
- [Template metaprogramming](https://en.wikipedia.org/wiki/Template_metaprogramming)
|
|
- [Template metaprogramming](https://en.wikipedia.org/wiki/Template_metaprogramming)
|
|
|
- will not be supported by Carbon generics. We expect to address those use
|
|
|
|
|
- cases with metaprogramming or templates in Carbon.
|
|
|
|
|
-
|
|
|
|
|
-### Template use cases that are out of scope
|
|
|
|
|
|
|
+ will not be supported by Carbon checked generics. We expect to address those
|
|
|
|
|
+ use cases with metaprogramming or [templates](#templates) in Carbon.
|
|
|
|
|
+- [Expression templates](https://en.wikipedia.org/wiki/Expression_templates)
|
|
|
|
|
+ are out of scope. It may be possible to express this approach in Carbon's
|
|
|
|
|
+ checked-generics system, but they won't drive any accommodation in the
|
|
|
|
|
+ checked-generics design.
|
|
|
|
|
|
|
|
-We will also not require Carbon generics to support
|
|
|
|
|
-[expression templates](https://en.wikipedia.org/wiki/Expression_templates),
|
|
|
|
|
-[variadics](https://en.wikipedia.org/wiki/Variadic_function), or
|
|
|
|
|
-[variadic templates](https://en.wikipedia.org/wiki/Variadic_template). Those are
|
|
|
|
|
-all out of scope. It would be fine for our generics system to support these
|
|
|
|
|
-features, but they won't drive any accommodation in the generics design, at
|
|
|
|
|
-least until we have some resolution about templates in Carbon.
|
|
|
|
|
-
|
|
|
|
|
-### Generics will be checked when defined
|
|
|
|
|
|
|
+### Checked generics will be checked when defined
|
|
|
|
|
|
|
|
C++ compilers must defer full type checking of templates until they are
|
|
C++ compilers must defer full type checking of templates until they are
|
|
|
-instantiated by the user. Carbon will not defer type checking of generic
|
|
|
|
|
|
|
+instantiated by the user. Carbon will not defer type checking of checked-generic
|
|
|
definitions.
|
|
definitions.
|
|
|
|
|
|
|
|
-### Specialization strategy
|
|
|
|
|
|
|
+### Implementation strategy
|
|
|
|
|
|
|
|
We want all generic Carbon code to support [static dispatch](#dispatch-control).
|
|
We want all generic Carbon code to support [static dispatch](#dispatch-control).
|
|
|
This means we won't support unbounded type families. Unbounded type families are
|
|
This means we won't support unbounded type families. Unbounded type families are
|
|
@@ -656,7 +652,7 @@ when recursion creates an infinite collection of types, such as in
|
|
|
or:
|
|
or:
|
|
|
|
|
|
|
|
```carbon
|
|
```carbon
|
|
|
-fn Sort[T:! Comparable](list: List(T)) -> List(T) {
|
|
|
|
|
|
|
+fn Sort[T:! Ordered](list: List(T)) -> List(T) {
|
|
|
if (list.size() == 1) return list;
|
|
if (list.size() == 1) return list;
|
|
|
var chunks: List(List(T)) = FormChunks(list, sqrt(list.size()));
|
|
var chunks: List(List(T)) = FormChunks(list, sqrt(list.size()));
|
|
|
chunks = chunks.ApplyToEach(Sort);
|
|
chunks = chunks.ApplyToEach(Sort);
|
|
@@ -665,15 +661,14 @@ fn Sort[T:! Comparable](list: List(T)) -> List(T) {
|
|
|
}
|
|
}
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-This, given an implementation of `Comparable` for any list with elements that
|
|
|
|
|
-are themselves `Comparable`, would recursively call itself to produce a set of
|
|
|
|
|
-types without bound. That is, calling `Sort` on a `List(Int)` would internally
|
|
|
|
|
-call `Sort` on a `List(List(Int))` and so on recursively without any static
|
|
|
|
|
-limit.
|
|
|
|
|
|
|
+This, given an implementation of `Ordered` for any list with elements that are
|
|
|
|
|
+themselves `Ordered`, would recursively call itself to produce a set of types
|
|
|
|
|
+without bound. That is, calling `Sort` on a `List(Int)` would internally call
|
|
|
|
|
+`Sort` on a `List(List(Int))` and so on recursively without any static limit.
|
|
|
|
|
|
|
|
-We won't require all generic Carbon code to support dynamic dispatch, but we
|
|
|
|
|
-would like it to be an implementation option for the compiler in the majority of
|
|
|
|
|
-cases.
|
|
|
|
|
|
|
+We won't require all checked-generic Carbon code to support dynamic dispatch,
|
|
|
|
|
+but we would like it to be an implementation option for the compiler in the
|
|
|
|
|
+majority of cases.
|
|
|
|
|
|
|
|
Lastly, runtime specialization is out of scope as an implementation strategy.
|
|
Lastly, runtime specialization is out of scope as an implementation strategy.
|
|
|
That is, some language runtimes JIT a specialization when it is first needed,
|
|
That is, some language runtimes JIT a specialization when it is first needed,
|
|
@@ -681,5 +676,8 @@ but it is not a goal for Carbon to support such an implementation strategy.
|
|
|
|
|
|
|
|
## References
|
|
## References
|
|
|
|
|
|
|
|
|
|
+Proposals:
|
|
|
|
|
+
|
|
|
- [#24: Generics goals](https://github.com/carbon-language/carbon-lang/pull/24)
|
|
- [#24: Generics goals](https://github.com/carbon-language/carbon-lang/pull/24)
|
|
|
- [#950: Generic details 6: remove facets](https://github.com/carbon-language/carbon-lang/pull/950)
|
|
- [#950: Generic details 6: remove facets](https://github.com/carbon-language/carbon-lang/pull/950)
|
|
|
|
|
+- [#2138: Checked and template generic terminology](https://github.com/carbon-language/carbon-lang/pull/2138)
|