mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 20:40:11 +01:00
Implement the toolchain side of proposal #7254, removing the `:!` binding syntax for generic and template parameters in favor of the keywords `generic`, `template`, and `runtime` plus contextual defaults for phase. For valid programs this is semantics-preserving: each binding resolves to the same phase, and produces the same SemIR, as it did under `:!`/`:`. The parser derives a binding's phase from its syntactic context plus any explicit phase keyword; new diagnostics and error recovery for misused keywords are described below. Implementation details for each component: - Lexer: remove the `:!` (`ColonExclaim`) token, move its virtual parse-node budget onto `:`, and add the `generic` and `runtime` keywords. - Parser: thread a `BindingContext` (`ExplicitParam`, `DeducedParam`, or `CompileTimeEntityParam`) from declaration introducers down through parameter lists to each binding pattern, using a one-token lookahead to distinguish a name-qualifier parameter list from a declaration's own final list. Parameters of a compile-time entity (`class`, `interface`, `constraint`, `choice`, `alias`, `export`, `namespace`) and deduced `[]` parameters default to checked generic; explicit function parameters and local bindings default to runtime. `HandleBindingPattern` resolves the phase from that context plus the keyword: a `generic` keyword needs no node of its own (the phase is carried by the binding's node kind), while a `runtime` keyword is preserved as a `RuntimeBindingName` node so `check` can name it in a diagnostic. A phase keyword that is merely redundant with the contextual default is diagnosed here, without invalidating the parse tree. - Check: a phase keyword that is invalid for its context (for example `runtime` on a checked-generic parameter) is diagnosed here, and recovers by building an error binding that still introduces the name so that later uses of it do not produce cascading errors. The removed `:!` syntax is now rejected as an ordinary parse error. The `form`/`:?`/`->?` ("extended types") portion of proposal #7254 is left for a separate change. Assisted-by: Claude Code
513 lines
13 KiB
Plaintext
513 lines
13 KiB
Plaintext
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
|
// Exceptions. See /LICENSE for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon
|
|
//
|
|
// AUTOUPDATE
|
|
// TIP: To test this file alone, run:
|
|
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/lookup/specialization.carbon
|
|
// TIP: To dump output, run:
|
|
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/lookup/specialization.carbon
|
|
|
|
// --- specialized_self_first.carbon
|
|
library "[[@TEST_NAME]]";
|
|
|
|
interface Z(T: type) {
|
|
let X: type;
|
|
}
|
|
|
|
class C {}
|
|
impl C as Z(C) where .X = C {}
|
|
|
|
impl forall [T: type] T as Z(T) where .X = () {}
|
|
|
|
fn F() {
|
|
// The specialization of `Z(C)` should match in preference to the blanket impl
|
|
// of `Z(T)`. If the blanket impl is chosen, then `a` will have type `()`
|
|
// which will fail to typecheck here.
|
|
let unused a: C.(Z(C).X) = {} as C;
|
|
}
|
|
|
|
// --- specialized_self_second.carbon
|
|
library "[[@TEST_NAME]]";
|
|
|
|
interface Z(T: type) {
|
|
let X: type;
|
|
}
|
|
|
|
impl forall [T: type] T as Z(T) where .X = () {}
|
|
|
|
class C {}
|
|
impl C as Z(C) where .X = C {}
|
|
|
|
fn F() {
|
|
// The specialization of `Z(C)` should match in preference to the blanket impl
|
|
// of `Z(T)`. If the blanket impl is chosen, then `a` will have type `()`
|
|
// which will fail to typecheck here.
|
|
let unused a: C.(Z(C).X) = {} as C;
|
|
}
|
|
|
|
// --- specialized_constraint_first.carbon
|
|
library "[[@TEST_NAME]]";
|
|
|
|
interface Z(T: type) {
|
|
let X: type;
|
|
}
|
|
|
|
class C {}
|
|
|
|
impl C as Z(C) where .X = C {}
|
|
impl forall [T: type] C as Z(T) where .X = () {}
|
|
|
|
fn F() {
|
|
// The specialization of `Z(C)` should match in preference to the blanket impl
|
|
// of `Z(T)`. If the blanket impl is chosen, then `a` will have type `()`
|
|
// which will fail to typecheck here.
|
|
let unused a: C.(Z(C).X) = {} as C;
|
|
}
|
|
|
|
// --- specialized_constraint_second.carbon
|
|
library "[[@TEST_NAME]]";
|
|
|
|
interface Z(T: type) {
|
|
let X: type;
|
|
}
|
|
|
|
class C {}
|
|
|
|
impl forall [T: type] C as Z(T) where .X = () {}
|
|
impl C as Z(C) where .X = C {}
|
|
|
|
fn F() {
|
|
// The specialization of `Z(C)` should match in preference to the blanket impl
|
|
// of `Z(T)`. If the blanket impl is chosen, then `a` will have type `()`
|
|
// which will fail to typecheck here.
|
|
let unused a: C.(Z(C).X) = {} as C;
|
|
}
|
|
|
|
// --- specialized_self_vs_constraint_self_first.carbon
|
|
library "[[@TEST_NAME]]";
|
|
|
|
interface Z(T: type) {
|
|
let X: type;
|
|
}
|
|
|
|
class C(T: type) {}
|
|
|
|
impl forall [T: type] C(()) as Z(T) where .X = C(()) {}
|
|
impl forall [T: type] C(T) as Z(C(())) where .X = () {}
|
|
|
|
fn F() {
|
|
// The specialization of `C(())` should match in preference to the blanket impl
|
|
// of `C(T)`. If the blanket impl is chosen, then `a` will have type `()`
|
|
// which will fail to typecheck here.
|
|
let unused a: C(()).(Z(C(())).X) = {} as C(());
|
|
}
|
|
|
|
// --- specialized_self_vs_constraint_self_second.carbon
|
|
library "[[@TEST_NAME]]";
|
|
|
|
interface Z(T: type) {
|
|
let X: type;
|
|
}
|
|
|
|
class C(T: type) {}
|
|
|
|
impl forall [T: type] C(T) as Z(C(())) where .X = () {}
|
|
impl forall [T: type] C(()) as Z(T) where .X = C(()) {}
|
|
|
|
fn F() {
|
|
// The specialization of `Z(C)` should match in preference to the blanket impl
|
|
// of `Z(T)`. If the blanket impl is chosen, then `a` will have type `()`
|
|
// which will fail to typecheck here.
|
|
let unused a: C(()).(Z(C(())).X) = {} as C(());
|
|
}
|
|
|
|
// --- generic_class_with_fully_specified_impl.carbon
|
|
library "[[@TEST_NAME]]";
|
|
|
|
interface Z(T: type) {
|
|
let X: type;
|
|
}
|
|
|
|
impl forall [T: type] T as Z(T) where .X = {.a: ()} {}
|
|
|
|
class C(T: type) {}
|
|
impl C(()) as Z(()) where .X = C(()) {}
|
|
|
|
impl forall [T: type] C(T) as Z(T) where .X = {.b: ()} {}
|
|
|
|
fn F() {
|
|
// The specialization of `Z(C)` should match in preference to the blanket
|
|
// impls of `Z(T)`. If a blanket impl is chosen, then `a` will have a struct
|
|
// type which will fail to typecheck here when constructed from a `C` value.
|
|
let unused a: C(()).(Z(()).X) = {} as C(());
|
|
}
|
|
|
|
// --- generic_class_with_blanket_impl_first.carbon
|
|
library "[[@TEST_NAME]]";
|
|
|
|
interface Z(T: type) {
|
|
let X: type;
|
|
}
|
|
|
|
class C(T: type) {}
|
|
|
|
impl forall [T: type] C(T) as Z(T) where .X = C(()) {}
|
|
|
|
impl forall [T: type] T as Z(T) where .X = () {}
|
|
|
|
fn F() {
|
|
// The specialization of `Z(C)` should match in preference to the blanket
|
|
// impls of `Z(T)`. If a blanket impl is chosen, then `a` will have type `()`
|
|
// which will fail to typecheck here when constructed from a `C` value.
|
|
let unused a: C(()).(Z(()).X) = {} as C(());
|
|
}
|
|
|
|
// --- generic_class_with_blanket_impl_second.carbon
|
|
library "[[@TEST_NAME]]";
|
|
|
|
interface Z(T: type) {
|
|
let X: type;
|
|
}
|
|
|
|
impl forall [T: type] T as Z(T) where .X = () {}
|
|
|
|
class C(T: type) {}
|
|
|
|
impl forall [T: type] C(T) as Z(T) where .X = C(()) {}
|
|
|
|
fn F() {
|
|
// The specialization of `Z(C)` should match in preference to the blanket
|
|
// impls of `Z(T)`. If a blanket impl is chosen, then `a` will have type `()`
|
|
// which will fail to typecheck here when constructed from a `C` value.
|
|
let unused a: C(()).(Z(()).X) = {} as C(());
|
|
}
|
|
|
|
// --- specialized_class_with_facet_value_param.carbon
|
|
library "[[@TEST_NAME]]";
|
|
|
|
interface Z {
|
|
let X: type;
|
|
}
|
|
|
|
class D(T: type) {}
|
|
impl forall [T: type] D(T) as Z where .X = D(()) {}
|
|
|
|
class E {}
|
|
impl E as Z where .X = () {}
|
|
|
|
class C(T: Z, U: Z) {}
|
|
|
|
// This places a FacetValue of type FacetType(Z) at the position of `D` in the
|
|
// self type, because the class C requires a facet value satisfying Z. It tests
|
|
// that we correctly determine that this FacetType is not symbolic, and look at
|
|
// the parametes of D for symbolic references.
|
|
impl forall [T: Z] C(T, D(E)) as Z where .X = () {}
|
|
impl forall [T: Z] C(D(T), T) as Z where .X = () {}
|
|
// This is the best match, `T` is in the last position compared to the others.
|
|
impl forall [T: Z] C(D(E), T) as Z where .X = C(E, E) {}
|
|
impl forall [T: Z] C(T, T) as Z where .X = () {}
|
|
|
|
fn F() {
|
|
let unused a: C(D(E), D(E)).(Z.X) = {} as C(E, E);
|
|
}
|
|
|
|
// --- fail_specialized_class_with_symbolic_facet_value_param.carbon
|
|
|
|
interface Z {
|
|
let X: type;
|
|
}
|
|
|
|
impl forall [T: type] T as Z where .X = T {}
|
|
|
|
interface Y {}
|
|
class C(T: Y) {}
|
|
|
|
class D {}
|
|
impl D as Y {}
|
|
|
|
// D can be either a concrete or symbolic FacetValue, depending on what the
|
|
// caller has.
|
|
impl forall [D: Y] C(D) as Z where .X = () {}
|
|
|
|
fn F[D: Y](unused d: D) {
|
|
// The FacetValue deduced for the param of `C` will be a symbolic FacetValue
|
|
// because we are in a generic where `D` is an unknown type, which will cause
|
|
// the query and impl self type to be C(FacetValue) for a symbolic FacetValue.
|
|
//
|
|
// CHECK:STDERR: fail_specialized_class_with_symbolic_facet_value_param.carbon:[[@LINE+7]]:30: error: cannot implicitly convert expression of type `()` to `C(D).(Z.X)` [ConversionFailure]
|
|
// CHECK:STDERR: let unused a: C(D).(Z.X) = ();
|
|
// CHECK:STDERR: ^~
|
|
// CHECK:STDERR: fail_specialized_class_with_symbolic_facet_value_param.carbon:[[@LINE+4]]:30: note: type `()` does not implement interface `Core.ImplicitAs(C(D).(Z.X))` [MissingImplInMemberAccessInContext]
|
|
// CHECK:STDERR: let unused a: C(D).(Z.X) = ();
|
|
// CHECK:STDERR: ^~
|
|
// CHECK:STDERR:
|
|
let unused a: C(D).(Z.X) = ();
|
|
}
|
|
|
|
// --- pointer_specialization_first.carbon
|
|
library "[[@TEST_NAME]]";
|
|
|
|
interface Z {
|
|
let X: type;
|
|
}
|
|
|
|
class C {}
|
|
impl C* as Z where .X = C {}
|
|
|
|
impl forall [T: type] T* as Z where .X = () {}
|
|
|
|
fn F() {
|
|
// The specialization of `Z(C)` should match in preference to the blanket impl
|
|
// of `Z(T)`. If the blanket impl is chosen, then `a` will have type `()`
|
|
// which will fail to typecheck here.
|
|
let unused a: (C*).(Z.X) = {} as C;
|
|
}
|
|
|
|
// --- pointer_specialization_second.carbon
|
|
library "[[@TEST_NAME]]";
|
|
|
|
interface Z {
|
|
let X: type;
|
|
}
|
|
|
|
impl forall [T: type] T* as Z where .X = () {}
|
|
|
|
class C {}
|
|
impl C* as Z where .X = C {}
|
|
|
|
fn F() {
|
|
// The specialization of `Z(C)` should match in preference to the blanket impl
|
|
// of `Z(T)`. If the blanket impl is chosen, then `a` will have type `()`
|
|
// which will fail to typecheck here.
|
|
let unused a: (C*).(Z.X) = {} as C;
|
|
}
|
|
|
|
// --- cycle_in_deduce_avoided_by_specialization.carbon
|
|
library "[[@TEST_NAME]]";
|
|
|
|
interface Z {
|
|
let X: type;
|
|
}
|
|
|
|
class C(T: type) {}
|
|
|
|
// This impl makes a cycle, but it's not considered at all for `C(())` since
|
|
// there is another impl with a better type structure, so no diagnostic is
|
|
// emitted.
|
|
impl forall [T: Z] T as Z where .X = () {}
|
|
// Also a cycle, and also a worse match for `C(())`.
|
|
impl forall [T: Z] C(T) as Z where .X = () {}
|
|
|
|
impl C(()) as Z where .X = C(()) {}
|
|
|
|
fn F() {
|
|
let unused a: C(()).(Z.X) = {} as C(());
|
|
}
|
|
|
|
// --- final_specialization_before_generic_use_of_type_constant.carbon
|
|
library "[[@TEST_NAME]]";
|
|
|
|
interface Z(T: type) {
|
|
let X: type;
|
|
}
|
|
|
|
class C {}
|
|
|
|
impl forall [T: type, U: type] T as Z(U) where .X = () {}
|
|
|
|
final impl forall [T: type] T as Z(C) where .X = C {}
|
|
|
|
fn F[unused U: type](generic T: Z(C)) {
|
|
// The value of `.X` can be known to be `C` here when the impl `T as Z(C)` is
|
|
// final.
|
|
let unused a: T.X = {} as C;
|
|
}
|
|
|
|
// --- fail_specialization_written_after_generic_use_of_type_constant.carbon
|
|
library "[[@TEST_NAME]]";
|
|
|
|
interface Z(T: type) {
|
|
let X: type;
|
|
}
|
|
|
|
class C {}
|
|
|
|
impl forall [T: type, U: type] T as Z(U) where .X = () {}
|
|
|
|
fn F[unused U: type](generic T: Z(C)) {
|
|
// The value of `.X` is symbolic, it can't be assigned a value of type `C`.
|
|
// CHECK:STDERR: fail_specialization_written_after_generic_use_of_type_constant.carbon:[[@LINE+7]]:23: error: cannot implicitly convert expression of type `C` to `T.(Z(C).X)` [ConversionFailure]
|
|
// CHECK:STDERR: let unused a: T.X = {} as C;
|
|
// CHECK:STDERR: ^~~~~~~
|
|
// CHECK:STDERR: fail_specialization_written_after_generic_use_of_type_constant.carbon:[[@LINE+4]]:23: note: type `C` does not implement interface `Core.ImplicitAs(T.(Z(C).X))` [MissingImplInMemberAccessInContext]
|
|
// CHECK:STDERR: let unused a: T.X = {} as C;
|
|
// CHECK:STDERR: ^~~~~~~
|
|
// CHECK:STDERR:
|
|
let unused a: T.X = {} as C;
|
|
}
|
|
|
|
final impl forall [T: type] T as Z(C) where .X = C {}
|
|
|
|
// --- specialization_written_after_generic_use.carbon
|
|
library "[[@TEST_NAME]]";
|
|
|
|
interface Z {
|
|
let V: type;
|
|
fn ZZ() -> V*;
|
|
}
|
|
|
|
var t: ();
|
|
|
|
interface Y {}
|
|
impl forall [T: Y] T as Z where .V = () {
|
|
fn ZZ() -> ()* { return &t; }
|
|
}
|
|
|
|
fn H(generic W: Z, X: W.(Z.V)*) -> W.(Z.V)* {
|
|
return X;
|
|
}
|
|
|
|
fn G(generic U: Y) -> U.(Z.V)* {
|
|
return H(U, U.(Z.ZZ)());
|
|
}
|
|
|
|
class C {
|
|
impl as Y {}
|
|
}
|
|
|
|
var s: {};
|
|
|
|
impl C as Z where .V = {} {
|
|
fn ZZ() -> {}* { return &s; }
|
|
}
|
|
|
|
fn F() {
|
|
let unused x: {}* = G(C);
|
|
}
|
|
|
|
// --- specialization_written_after_generic_use_with_generic_interface.carbon
|
|
library "[[@TEST_NAME]]";
|
|
|
|
interface Z(T: type) {
|
|
let V: type;
|
|
fn ZZ() -> V*;
|
|
}
|
|
|
|
var t: ();
|
|
|
|
interface Y {}
|
|
impl forall [T: Y] T as Z(T) where .V = () {
|
|
fn ZZ() -> ()* { return &t; }
|
|
}
|
|
|
|
fn H(generic U: Y, generic W: Y & Z(U), X: W.(Z(U).V)*) -> W.(Z(U).V)* {
|
|
return X;
|
|
}
|
|
|
|
fn G(generic U: Y) -> U.(Z(U).V)* {
|
|
return H(U, U, U.(Z(U).ZZ)());
|
|
}
|
|
|
|
class C {
|
|
impl as Y {}
|
|
}
|
|
|
|
var s: {};
|
|
|
|
impl C as Z(C) where .V = {} {
|
|
fn ZZ() -> {}* { return &s; }
|
|
}
|
|
|
|
fn F() {
|
|
let unused x: {}* = G(C);
|
|
}
|
|
|
|
// --- type_structure_first_difference.carbon
|
|
library "[[@TEST_NAME]]";
|
|
|
|
interface Z(T: type) {
|
|
let X: type;
|
|
fn MakeX() -> X;
|
|
}
|
|
|
|
class C {}
|
|
|
|
// Type structure: "?(?)"
|
|
impl forall [T: type] T as Z(T) where .X = {.less_good: ()} {
|
|
fn MakeX() -> {.less_good: ()} { return {.less_good = ()}; }
|
|
}
|
|
|
|
// Type structure: "?(c)". Should outrank the previous impl.
|
|
impl forall [T: type] T as Z(C) where .X = () {
|
|
fn MakeX() -> () { return (); }
|
|
}
|
|
|
|
fn F(generic T: Z(C)) -> T.(Z(C).X) {
|
|
return T.MakeX();
|
|
}
|
|
|
|
fn G() {
|
|
// This won't typecheck if the first impl is selected.
|
|
let unused a: () = F(C);
|
|
}
|
|
|
|
// --- extend_impl_as_specialization.carbon
|
|
library "[[@TEST_NAME]]";
|
|
|
|
interface Z(T: type) {
|
|
let X: type;
|
|
}
|
|
|
|
impl forall [T: type, S: type] T as Z(S) where .X = {} {}
|
|
|
|
class C(S: type) {
|
|
extend impl as Z(S) where .X = () {}
|
|
fn CC(a: Self.(Z(S).X)*) -> Self.(Z(S).X)* { return a; }
|
|
}
|
|
|
|
fn F() {
|
|
var t: ();
|
|
let a: ()* = C(()).CC(&t);
|
|
let unused b: C(()).X = *a;
|
|
}
|
|
|
|
// --- final_impl_as_specialization.carbon
|
|
library "[[@TEST_NAME]]";
|
|
|
|
interface Z(T: type) {
|
|
let X: type;
|
|
}
|
|
|
|
impl forall [T: type, S: type] T as Z(S) where .X = {} {}
|
|
|
|
class C(S: type) {
|
|
final impl as Z(S) where .X = () {}
|
|
fn CC() -> Self.(Z(S).X) { return (); }
|
|
}
|
|
|
|
fn F() {
|
|
let unused a: () = C(()).CC();
|
|
}
|
|
|
|
// --- final_extend_impl_as_specialization.carbon
|
|
library "[[@TEST_NAME]]";
|
|
|
|
interface Z(T: type) {
|
|
let X: type;
|
|
}
|
|
|
|
impl forall [T: type, S: type] T as Z(S) where .X = {} {}
|
|
|
|
class C(S: type) {
|
|
extend final impl as Z(S) where .X = () {}
|
|
fn CC() -> Self.(Z(S).X) { return (); }
|
|
}
|
|
|
|
fn F() {
|
|
let a: () = C(()).CC();
|
|
let unused b: C(()).X = a;
|
|
}
|