mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 13:41:06 +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
129 lines
5.0 KiB
Plaintext
129 lines
5.0 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/named_constraint/incomplete.carbon
|
|
// TIP: To dump output, run:
|
|
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/named_constraint/incomplete.carbon
|
|
|
|
// --- fail_incomplete_constraint_in_impl_lookup.carbon
|
|
library "[[@TEST_NAME]]";
|
|
|
|
interface Z {}
|
|
|
|
// An incomplete constraint is a valid type for a symbolic facet, and is just
|
|
// not able to provide any witness for an impl lookup on that facet.
|
|
constraint N;
|
|
fn F(generic T: N) {
|
|
// CHECK:STDERR: fail_incomplete_constraint_in_impl_lookup.carbon:[[@LINE+4]]:3: error: cannot convert type `T` that implements `N` into type implementing `Z` [ConversionFailureFacetToFacet]
|
|
// CHECK:STDERR: T as Z;
|
|
// CHECK:STDERR: ^~~~~~
|
|
// CHECK:STDERR:
|
|
T as Z;
|
|
}
|
|
|
|
// --- fail_does_not_diagnose_identify_failure_in_deduce.carbon
|
|
library "[[@TEST_NAME]]";
|
|
|
|
constraint N;
|
|
|
|
interface Z {}
|
|
// Since N is an incomplete constraint, it can not be identified as a target for
|
|
// impl lookup to determine if C implements N. No diagnostic is attached to the
|
|
// impl when the lookup fails, we simply fail to convert.
|
|
impl forall [T: N] T as Z {}
|
|
|
|
class C;
|
|
|
|
fn F() {
|
|
// CHECK:STDERR: fail_does_not_diagnose_identify_failure_in_deduce.carbon:[[@LINE+4]]:3: error: cannot convert type `C` into type implementing `Z` [ConversionFailureTypeToFacet]
|
|
// CHECK:STDERR: C as Z;
|
|
// CHECK:STDERR: ^~~~~~
|
|
// CHECK:STDERR:
|
|
C as Z;
|
|
}
|
|
|
|
// --- constraint_completed_after_deducing_impl_refers_to_it.carbon
|
|
library "[[@TEST_NAME]]";
|
|
|
|
constraint N;
|
|
|
|
interface Z {}
|
|
// N is incomplete here, but that's okay. We don't need it to be identified
|
|
// until a lookup tries to use this impl.
|
|
impl forall [T: N] T as Z {}
|
|
|
|
class C;
|
|
|
|
// N is now identified, so it can be the target of a convertion, such as for
|
|
// deducing `T` in the impl above.
|
|
constraint N {}
|
|
|
|
fn F() {
|
|
C as Z;
|
|
}
|
|
|
|
// --- fail_impl_lookup_target_not_identified.carbon
|
|
library "[[@TEST_NAME]]";
|
|
|
|
constraint Z;
|
|
|
|
fn AsZ(unused generic T: Z) {}
|
|
|
|
fn F() {
|
|
// Converting to facet type `Z` requires `Z` to be identified.
|
|
// CHECK:STDERR: fail_impl_lookup_target_not_identified.carbon:[[@LINE+10]]:3: error: facet type `Z` can not be identified [ImplLookupInUnidentifiedFacetType]
|
|
// CHECK:STDERR: AsZ(());
|
|
// CHECK:STDERR: ^~~~~~~
|
|
// CHECK:STDERR: fail_impl_lookup_target_not_identified.carbon:[[@LINE-9]]:1: note: constraint was forward declared here [NamedConstraintForwardDeclaredHere]
|
|
// CHECK:STDERR: constraint Z;
|
|
// CHECK:STDERR: ^~~~~~~~~~~~~
|
|
// CHECK:STDERR: fail_impl_lookup_target_not_identified.carbon:[[@LINE-10]]:23: note: initializing generic parameter `T` declared here [InitializingGenericParam]
|
|
// CHECK:STDERR: fn AsZ(unused generic T: Z) {}
|
|
// CHECK:STDERR: ^~~~
|
|
// CHECK:STDERR:
|
|
AsZ(());
|
|
}
|
|
|
|
// --- fail_incomplete_self.carbon
|
|
library "[[@TEST_NAME]]";
|
|
|
|
// Returning a facet of type `W` requires `W` to be complete, but it's currently
|
|
// being defined where this is called. This causes an error diagnostic at the
|
|
// call.
|
|
//
|
|
// TODO: Define the function to `return U` once we can write `T: Core.Copy`.
|
|
// Right now, due to #5750, facets are not seen to implement `Core.Copy`.
|
|
eval fn ReturnFacet[T: type](generic U: T) -> T;
|
|
|
|
interface Y { let YT: type; }
|
|
|
|
interface Z {}
|
|
|
|
interface X { let XZ: Z; }
|
|
constraint W {
|
|
require impls Z;
|
|
// CHECK:STDERR: fail_incomplete_self.carbon:[[@LINE+17]]:31: error: function returns incomplete type `W` [IncompleteTypeInFunctionReturnType]
|
|
// CHECK:STDERR: require impls X where .XZ = ReturnFacet(Self);
|
|
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~
|
|
// CHECK:STDERR: fail_incomplete_self.carbon:[[@LINE-5]]:1: note: constraint is currently being defined [NamedConstraintIncompleteWithinDefinition]
|
|
// CHECK:STDERR: constraint W {
|
|
// CHECK:STDERR: ^~~~~~~~~~~~~~
|
|
// CHECK:STDERR: fail_incomplete_self.carbon:[[@LINE-15]]:44: note: return type declared here [IncompleteReturnTypeHere]
|
|
// CHECK:STDERR: eval fn ReturnFacet[T: type](generic U: T) -> T;
|
|
// CHECK:STDERR: ^~~~
|
|
// CHECK:STDERR:
|
|
// CHECK:STDERR: fail_incomplete_self.carbon:[[@LINE+7]]:31: error: use of undefined generic function [MissingGenericFunctionDefinition]
|
|
// CHECK:STDERR: require impls X where .XZ = ReturnFacet(Self);
|
|
// CHECK:STDERR: ^~~~~~~~~~~
|
|
// CHECK:STDERR: fail_incomplete_self.carbon:[[@LINE-22]]:1: note: generic function declared here [MissingGenericFunctionDefinitionHere]
|
|
// CHECK:STDERR: eval fn ReturnFacet[T: type](generic U: T) -> T;
|
|
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// CHECK:STDERR:
|
|
require impls X where .XZ = ReturnFacet(Self);
|
|
}
|