From 98a72b9d13870882b5a2fc06c98e935f97989d25 Mon Sep 17 00:00:00 2001 From: danakj Date: Fri, 14 Nov 2025 18:11:37 -0500 Subject: [PATCH 01/31] complete-not-identify --- toolchain/check/handle_require.cpp | 16 +- toolchain/check/interface.cpp | 2 +- .../generic/extend_type_completion.carbon | 105 +++++++++ .../impl/fail_undefined_interface.carbon | 203 ++++++++++++++---- .../testdata/interface/incomplete.carbon | 97 +++++++++ .../named_constraint/empty_generic.carbon | 11 +- toolchain/check/type_completion.cpp | 149 ++++++++++--- toolchain/diagnostics/diagnostic_kind.def | 1 + toolchain/sem_ir/stringify.cpp | 4 + 9 files changed, 513 insertions(+), 75 deletions(-) create mode 100644 toolchain/check/testdata/generic/extend_type_completion.carbon diff --git a/toolchain/check/handle_require.cpp b/toolchain/check/handle_require.cpp index 20ab48063ba99..889b0d51c35e8 100644 --- a/toolchain/check/handle_require.cpp +++ b/toolchain/check/handle_require.cpp @@ -154,6 +154,7 @@ static auto TypeStructureReferencesSelf( struct ValidateRequireResult { SemIR::FacetType facet_type; + SemIR::TypeId facet_type_type_id; const SemIR::IdentifiedFacetType* identified; }; @@ -164,12 +165,14 @@ static auto ValidateRequire(Context& context, SemIR::LocId loc_id, SemIR::InstId constraint_inst_id, SemIR::InstId scope_inst_id) -> std::optional { - auto constraint_constant_value_inst_id = - context.constant_values().GetConstantInstId(constraint_inst_id); - auto constraint_facet_type = context.insts().TryGetAs( - constraint_constant_value_inst_id); + auto constraint_constant_value_id = + context.constant_values().Get(constraint_inst_id); + auto constraint_type_id = + SemIR::TypeId::ForTypeConstant(constraint_constant_value_id); + auto constraint_facet_type = + context.types().TryGetAs(constraint_type_id); if (!constraint_facet_type) { - if (constraint_constant_value_inst_id != SemIR::ErrorInst::InstId) { + if (constraint_constant_value_id != SemIR::ErrorInst::ConstantId) { CARBON_DIAGNOSTIC( RequireImplsMissingFacetType, Error, "`require` declaration constrained by a non-facet type; " @@ -218,6 +221,7 @@ static auto ValidateRequire(Context& context, SemIR::LocId loc_id, } return ValidateRequireResult{.facet_type = *constraint_facet_type, + .facet_type_type_id = constraint_type_id, .identified = &identified}; } @@ -244,7 +248,7 @@ auto HandleParseNode(Context& context, Parse::RequireDeclId node_id) -> bool { return true; } - auto [constraint_facet_type, identified] = *validated; + auto [constraint_facet_type, constraint_type_id, identified] = *validated; if (identified->required_interfaces().empty()) { // A `require T impls type` adds no actual constraints, so nothing to do. DiscardGenericDecl(context); diff --git a/toolchain/check/interface.cpp b/toolchain/check/interface.cpp index 779e6a15e23fb..760db856b473f 100644 --- a/toolchain/check/interface.cpp +++ b/toolchain/check/interface.cpp @@ -122,7 +122,7 @@ static auto GetGenericArgsWithSelfType(Context& context, arg_ids.reserve(std::max(reserve_args_size, interface_args.size() + 1)); // Start with the enclosing arguments from the interface. - arg_ids.assign(interface_args.begin(), interface_args.end()); + llvm::append_range(arg_ids, interface_args); // Add the `Self` argument. arg_ids.push_back(GetSelfFacet(context, interface_specific_id, generic_id, diff --git a/toolchain/check/testdata/generic/extend_type_completion.carbon b/toolchain/check/testdata/generic/extend_type_completion.carbon new file mode 100644 index 0000000000000..d2710e465ed18 --- /dev/null +++ b/toolchain/check/testdata/generic/extend_type_completion.carbon @@ -0,0 +1,105 @@ +// 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/int.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/generic/extend_type_completion.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/generic/extend_type_completion.carbon + +// --- class_impl_doesnt_need_complete_interface.carbon +library "[[@TEST_NAME]]"; + +interface K(T:! type) {} + +class C(N:! i32) { + impl as K(array(i32, N)) {} +} + +// C does not extend K so the type of K is not completed. No error. +var v: C(-1); + +// --- fail_class_extend_impl_does_need_complete_interface.carbon +library "[[@TEST_NAME]]"; + +interface K(T:! type) {} + +class C(N:! i32) { + // CHECK:STDERR: fail_class_extend_impl_does_need_complete_interface.carbon:[[@LINE+3]]:18: error: array bound of -1 is negative [ArrayBoundNegative] + // CHECK:STDERR: extend impl as K(array(i32, N)) {} + // CHECK:STDERR: ^~~~~~~~~~~~~~~~ + extend impl as K(array(i32, N)) {} +} + +// C extends K so the type of K is completed, but is invalid. +// CHECK:STDERR: fail_class_extend_impl_does_need_complete_interface.carbon:[[@LINE+4]]:8: note: in `C(-1)` used here [ResolvingSpecificHere] +// CHECK:STDERR: var v: C(-1); +// CHECK:STDERR: ^~~~~ +// CHECK:STDERR: +var v: C(-1); + +// --- interface_require_impls_doesnt_need_complete_interface.carbon +library "[[@TEST_NAME]]"; + +interface K(T:! type) {} +interface J(N:! i32) { + require impls K(array(i32, N)); +} + +// J does not extend K so the type of K is not completed. No error. +var v: J(-1); + +// --- fail_interface_extend_require_impls_does_need_complete_interface.carbon +library "[[@TEST_NAME]]"; + +interface K(T:! type) {} +interface J(N:! i32) { + // CHECK:STDERR: fail_interface_extend_require_impls_does_need_complete_interface.carbon:[[@LINE+3]]:37: error: array bound of -1 is negative [ArrayBoundNegative] + // CHECK:STDERR: extend require impls K(array(i32, N)); + // CHECK:STDERR: ^ + extend require impls K(array(i32, N)); +} + +// J extends K so the type of K is completed, but is invalid. +// +// TODO: The error location should be the type, like in the class case above. We +// need a location for the type in context.bind_name_map() to use as the +// location to Convert(). +// +// CHECK:STDERR: fail_interface_extend_require_impls_does_need_complete_interface.carbon:[[@LINE+4]]:1: note: in `require` used here [ResolvingSpecificHere] +// CHECK:STDERR: var v: J(-1); +// CHECK:STDERR: ^~~~~~~~~~~~ +// CHECK:STDERR: +var v: J(-1); + +// --- constraint_require_impls_doesnt_need_complete_interface.carbon +library "[[@TEST_NAME]]"; + +interface K(T:! type) {} +constraint J(N:! i32) { + require impls K(array(i32, N)); +} + +// J does not extend K so the type of K is not completed. No error. +var v: J(-1); + +// --- fail_constraint_extend_require_impls_does_need_complete_interface.carbon +library "[[@TEST_NAME]]"; + +interface K(T:! type) {} +constraint J(N:! i32) { + // CHECK:STDERR: fail_constraint_extend_require_impls_does_need_complete_interface.carbon:[[@LINE+3]]:37: error: array bound of -1 is negative [ArrayBoundNegative] + // CHECK:STDERR: extend require impls K(array(i32, N)); + // CHECK:STDERR: ^ + extend require impls K(array(i32, N)); +} + +// J extends K so the type of K is completed, but is invalid. +// CHECK:STDERR: fail_constraint_extend_require_impls_does_need_complete_interface.carbon:[[@LINE+4]]:1: note: in `require` used here [ResolvingSpecificHere] +// CHECK:STDERR: var v: J(-1); +// CHECK:STDERR: ^~~~~~~~~~~~ +// CHECK:STDERR: +var v: J(-1); diff --git a/toolchain/check/testdata/impl/fail_undefined_interface.carbon b/toolchain/check/testdata/impl/fail_undefined_interface.carbon index b93ce2af5d23b..3e0c212a4b12d 100644 --- a/toolchain/check/testdata/impl/fail_undefined_interface.carbon +++ b/toolchain/check/testdata/impl/fail_undefined_interface.carbon @@ -41,7 +41,7 @@ class C {} // CHECK:STDERR: impl C as J {} -// --- fail_incomplete_where.carbon +// --- incomplete_where.carbon library "[[@TEST_NAME]]"; class C {} @@ -49,16 +49,9 @@ class C {} interface I {} interface Incomplete; -// CHECK:STDERR: fail_incomplete_where.carbon:[[@LINE+7]]:1: error: definition of impl as incomplete facet type `I where .Self impls Incomplete` [ImplAsIncompleteFacetTypeDefinition] -// CHECK:STDERR: impl C as I where .Self impls Incomplete {} -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// CHECK:STDERR: fail_incomplete_where.carbon:[[@LINE-5]]:1: note: interface was forward declared here [InterfaceForwardDeclaredHere] -// CHECK:STDERR: interface Incomplete; -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~ -// CHECK:STDERR: impl C as I where .Self impls Incomplete {} -// --- fail_declaration_incomplete_where_rewrite.carbon +// --- declaration_incomplete_where_rewrite.carbon library "[[@TEST_NAME]]"; class C {} @@ -66,15 +59,29 @@ class C {} interface J { let T:! type; } interface Incomplete; -// CHECK:STDERR: fail_declaration_incomplete_where_rewrite.carbon:[[@LINE+7]]:1: error: declaration of impl as incomplete facet type `J where .Self impls Incomplete and .(J.T) = ()` with rewrites [ImplAsIncompleteFacetTypeRewrites] -// CHECK:STDERR: impl C as J where .Self impls Incomplete and .T = (); -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// CHECK:STDERR: fail_declaration_incomplete_where_rewrite.carbon:[[@LINE-5]]:1: note: interface was forward declared here [InterfaceForwardDeclaredHere] -// CHECK:STDERR: interface Incomplete; -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~ -// CHECK:STDERR: impl C as J where .Self impls Incomplete and .T = (); +impl C as J where .Self impls Incomplete and .T = () {} + +// --- fail_declaration_lookup_into_incomplete.carbon +library "[[@TEST_NAME]]"; + +class C {} + +interface I {} +interface Incomplete; + +impl C as I where .Self impls Incomplete { + // CHECK:STDERR: fail_declaration_lookup_into_incomplete.carbon:[[@LINE+7]]:19: error: member access into incomplete facet type `Incomplete` [QualifiedExprInIncompleteFacetTypeScope] + // CHECK:STDERR: fn F() -> Self.(Incomplete.T); + // CHECK:STDERR: ^~~~~~~~~~~~ + // CHECK:STDERR: fail_declaration_lookup_into_incomplete.carbon:[[@LINE-6]]:1: note: interface was forward declared here [InterfaceForwardDeclaredHere] + // CHECK:STDERR: interface Incomplete; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + fn F() -> Self.(Incomplete.T); +}; + // CHECK:STDOUT: --- fail_empty_struct.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { @@ -139,7 +146,7 @@ impl C as J where .Self impls Incomplete and .T = (); // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_incomplete_where.carbon +// CHECK:STDOUT: --- incomplete_where.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] @@ -151,6 +158,7 @@ impl C as J where .Self impls Incomplete and .T = (); // CHECK:STDOUT: %.Self: %I.type = symbolic_binding .Self [symbolic_self] // CHECK:STDOUT: %.Self.binding.as_type: type = symbolic_binding_type .Self, %.Self [symbolic_self] // CHECK:STDOUT: %I_where.type: type = facet_type <@I where .Self impls @Incomplete> [concrete] +// CHECK:STDOUT: %I.impl_witness: = impl_witness file.%I.impl_witness_table [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -169,12 +177,14 @@ impl C as J where .Self impls Incomplete and .T = (); // CHECK:STDOUT: %.Self.ref: %I.type = name_ref .Self, %.Self [symbolic_self = constants.%.Self] // CHECK:STDOUT: %Incomplete.ref: type = name_ref Incomplete, file.%Incomplete.decl [concrete = constants.%Incomplete.type] // CHECK:STDOUT: %.Self.as_type: type = facet_access_type %.Self.ref [symbolic_self = constants.%.Self.binding.as_type] -// CHECK:STDOUT: %.loc15_19: type = converted %.Self.ref, %.Self.as_type [symbolic_self = constants.%.Self.binding.as_type] -// CHECK:STDOUT: %.loc15_13: type = where_expr %.Self [concrete = constants.%I_where.type] { +// CHECK:STDOUT: %.loc8_19: type = converted %.Self.ref, %.Self.as_type [symbolic_self = constants.%.Self.binding.as_type] +// CHECK:STDOUT: %.loc8_13: type = where_expr %.Self [concrete = constants.%I_where.type] { // CHECK:STDOUT: requirement_base_facet_type constants.%I.type -// CHECK:STDOUT: requirement_impls %.loc15_19, %Incomplete.ref +// CHECK:STDOUT: requirement_impls %.loc8_19, %Incomplete.ref // CHECK:STDOUT: } // CHECK:STDOUT: } +// CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @C.as.I.impl [concrete] +// CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { @@ -189,9 +199,9 @@ impl C as J where .Self impls Incomplete and .T = (); // CHECK:STDOUT: // CHECK:STDOUT: interface @Incomplete; // CHECK:STDOUT: -// CHECK:STDOUT: impl @C.as.I.impl: %C.ref as %.loc15_13 { +// CHECK:STDOUT: impl @C.as.I.impl: %C.ref as %.loc8_13 { // CHECK:STDOUT: !members: -// CHECK:STDOUT: witness = +// CHECK:STDOUT: witness = file.%I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { @@ -202,7 +212,7 @@ impl C as J where .Self impls Incomplete and .T = (); // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_declaration_incomplete_where_rewrite.carbon +// CHECK:STDOUT: --- declaration_incomplete_where_rewrite.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] @@ -220,6 +230,7 @@ impl C as J where .Self impls Incomplete and .T = (); // CHECK:STDOUT: %impl.elem0: type = impl_witness_access %J.lookup_impl_witness, element0 [symbolic_self] // CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: %J_where.type: type = facet_type <@J where .Self impls @Incomplete and %impl.elem0 = %empty_tuple.type> [concrete] +// CHECK:STDOUT: %J.impl_witness: = impl_witness file.%J.impl_witness_table [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -232,24 +243,48 @@ impl C as J where .Self impls Incomplete and .T = (); // CHECK:STDOUT: %J.decl: type = interface_decl @J [concrete = constants.%J.type] {} {} // CHECK:STDOUT: %Incomplete.decl: type = interface_decl @Incomplete [concrete = constants.%Incomplete.type] {} {} // CHECK:STDOUT: impl_decl @C.as.J.impl [concrete] {} { -// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] -// CHECK:STDOUT: %J.ref: type = name_ref J, file.%J.decl [concrete = constants.%J.type] -// CHECK:STDOUT: %.Self: %J.type = symbolic_binding .Self [symbolic_self = constants.%.Self] -// CHECK:STDOUT: %.Self.ref.loc15_19: %J.type = name_ref .Self, %.Self [symbolic_self = constants.%.Self] -// CHECK:STDOUT: %Incomplete.ref: type = name_ref Incomplete, file.%Incomplete.decl [concrete = constants.%Incomplete.type] -// CHECK:STDOUT: %.Self.as_type.loc15_19: type = facet_access_type %.Self.ref.loc15_19 [symbolic_self = constants.%.Self.binding.as_type] -// CHECK:STDOUT: %.loc15_19: type = converted %.Self.ref.loc15_19, %.Self.as_type.loc15_19 [symbolic_self = constants.%.Self.binding.as_type] -// CHECK:STDOUT: %.Self.ref.loc15_46: %J.type = name_ref .Self, %.Self [symbolic_self = constants.%.Self] -// CHECK:STDOUT: %T.ref: %J.assoc_type = name_ref T, @T.%assoc0 [concrete = constants.%assoc0] -// CHECK:STDOUT: %.Self.as_type.loc15_46: type = facet_access_type %.Self.ref.loc15_46 [symbolic_self = constants.%.Self.binding.as_type] -// CHECK:STDOUT: %.loc15_46: type = converted %.Self.ref.loc15_46, %.Self.as_type.loc15_46 [symbolic_self = constants.%.Self.binding.as_type] -// CHECK:STDOUT: %impl.elem0: type = impl_witness_access constants.%J.lookup_impl_witness, element0 [symbolic_self = constants.%impl.elem0] -// CHECK:STDOUT: %.loc15_52.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] -// CHECK:STDOUT: %.loc15_52.2: type = converted %.loc15_52.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] -// CHECK:STDOUT: %.loc15_13: type = where_expr %.Self [concrete = constants.%J_where.type] { +// CHECK:STDOUT: %C.ref.loc8: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %J.ref.loc8: type = name_ref J, file.%J.decl [concrete = constants.%J.type] +// CHECK:STDOUT: %.Self.2: %J.type = symbolic_binding .Self [symbolic_self = constants.%.Self] +// CHECK:STDOUT: %.Self.ref.loc8_19: %J.type = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self] +// CHECK:STDOUT: %Incomplete.ref.loc8: type = name_ref Incomplete, file.%Incomplete.decl [concrete = constants.%Incomplete.type] +// CHECK:STDOUT: %.Self.as_type.loc8_19: type = facet_access_type %.Self.ref.loc8_19 [symbolic_self = constants.%.Self.binding.as_type] +// CHECK:STDOUT: %.loc8_19: type = converted %.Self.ref.loc8_19, %.Self.as_type.loc8_19 [symbolic_self = constants.%.Self.binding.as_type] +// CHECK:STDOUT: %.Self.ref.loc8_46: %J.type = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self] +// CHECK:STDOUT: %T.ref.loc8: %J.assoc_type = name_ref T, @T.%assoc0 [concrete = constants.%assoc0] +// CHECK:STDOUT: %.Self.as_type.loc8_46: type = facet_access_type %.Self.ref.loc8_46 [symbolic_self = constants.%.Self.binding.as_type] +// CHECK:STDOUT: %.loc8_46: type = converted %.Self.ref.loc8_46, %.Self.as_type.loc8_46 [symbolic_self = constants.%.Self.binding.as_type] +// CHECK:STDOUT: %impl.elem0.loc8: type = impl_witness_access constants.%J.lookup_impl_witness, element0 [symbolic_self = constants.%impl.elem0] +// CHECK:STDOUT: %.loc8_52.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] +// CHECK:STDOUT: %.loc8_52.2: type = converted %.loc8_52.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] +// CHECK:STDOUT: %.loc8_13: type = where_expr %.Self.2 [concrete = constants.%J_where.type] { +// CHECK:STDOUT: requirement_base_facet_type constants.%J.type +// CHECK:STDOUT: requirement_impls %.loc8_19, %Incomplete.ref.loc8 +// CHECK:STDOUT: requirement_rewrite %impl.elem0.loc8, %.loc8_52.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: %J.impl_witness_table = impl_witness_table (%impl_witness_assoc_constant), @C.as.J.impl [concrete] +// CHECK:STDOUT: %J.impl_witness: = impl_witness %J.impl_witness_table [concrete = constants.%J.impl_witness] +// CHECK:STDOUT: %impl_witness_assoc_constant: type = impl_witness_assoc_constant constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] +// CHECK:STDOUT: impl_decl @C.as.J.impl [concrete] {} { +// CHECK:STDOUT: %C.ref.loc10: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %J.ref.loc10: type = name_ref J, file.%J.decl [concrete = constants.%J.type] +// CHECK:STDOUT: %.Self.1: %J.type = symbolic_binding .Self [symbolic_self = constants.%.Self] +// CHECK:STDOUT: %.Self.ref.loc10_19: %J.type = name_ref .Self, %.Self.1 [symbolic_self = constants.%.Self] +// CHECK:STDOUT: %Incomplete.ref.loc10: type = name_ref Incomplete, file.%Incomplete.decl [concrete = constants.%Incomplete.type] +// CHECK:STDOUT: %.Self.as_type.loc10_19: type = facet_access_type %.Self.ref.loc10_19 [symbolic_self = constants.%.Self.binding.as_type] +// CHECK:STDOUT: %.loc10_19: type = converted %.Self.ref.loc10_19, %.Self.as_type.loc10_19 [symbolic_self = constants.%.Self.binding.as_type] +// CHECK:STDOUT: %.Self.ref.loc10_46: %J.type = name_ref .Self, %.Self.1 [symbolic_self = constants.%.Self] +// CHECK:STDOUT: %T.ref.loc10: %J.assoc_type = name_ref T, @T.%assoc0 [concrete = constants.%assoc0] +// CHECK:STDOUT: %.Self.as_type.loc10_46: type = facet_access_type %.Self.ref.loc10_46 [symbolic_self = constants.%.Self.binding.as_type] +// CHECK:STDOUT: %.loc10_46: type = converted %.Self.ref.loc10_46, %.Self.as_type.loc10_46 [symbolic_self = constants.%.Self.binding.as_type] +// CHECK:STDOUT: %impl.elem0.loc10: type = impl_witness_access constants.%J.lookup_impl_witness, element0 [symbolic_self = constants.%impl.elem0] +// CHECK:STDOUT: %.loc10_52.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] +// CHECK:STDOUT: %.loc10_52.2: type = converted %.loc10_52.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] +// CHECK:STDOUT: %.loc10_13: type = where_expr %.Self.1 [concrete = constants.%J_where.type] { // CHECK:STDOUT: requirement_base_facet_type constants.%J.type -// CHECK:STDOUT: requirement_impls %.loc15_19, %Incomplete.ref -// CHECK:STDOUT: requirement_rewrite %impl.elem0, %.loc15_52.2 +// CHECK:STDOUT: requirement_impls %.loc10_19, %Incomplete.ref.loc10 +// CHECK:STDOUT: requirement_rewrite %impl.elem0.loc10, %.loc10_52.2 // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: } @@ -274,7 +309,10 @@ impl C as J where .Self impls Incomplete and .T = (); // CHECK:STDOUT: assoc_const T:! type; // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: impl @C.as.J.impl: %C.ref as %.loc15_13; +// CHECK:STDOUT: impl @C.as.J.impl: %C.ref.loc8 as %.loc8_13 { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: witness = file.%J.impl_witness +// CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] @@ -288,3 +326,86 @@ impl C as J where .Self impls Incomplete and .T = (); // CHECK:STDOUT: // CHECK:STDOUT: specific @T(constants.%.Self) {} // CHECK:STDOUT: +// CHECK:STDOUT: --- fail_declaration_lookup_into_incomplete.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] +// CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] +// CHECK:STDOUT: %Incomplete.type: type = facet_type <@Incomplete> [concrete] +// CHECK:STDOUT: %.Self: %I.type = symbolic_binding .Self [symbolic_self] +// CHECK:STDOUT: %.Self.binding.as_type: type = symbolic_binding_type .Self, %.Self [symbolic_self] +// CHECK:STDOUT: %I_where.type: type = facet_type <@I where .Self impls @Incomplete> [concrete] +// CHECK:STDOUT: %I.impl_witness: = impl_witness file.%I.impl_witness_table [concrete] +// CHECK:STDOUT: %C.as.I.impl.F.type: type = fn_type @C.as.I.impl.F [concrete] +// CHECK:STDOUT: %C.as.I.impl.F: %C.as.I.impl.F.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: .I = %I.decl +// CHECK:STDOUT: .Incomplete = %Incomplete.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} +// CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} +// CHECK:STDOUT: %Incomplete.decl: type = interface_decl @Incomplete [concrete = constants.%Incomplete.type] {} {} +// CHECK:STDOUT: impl_decl @C.as.I.impl [concrete] {} { +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] +// CHECK:STDOUT: %.Self: %I.type = symbolic_binding .Self [symbolic_self = constants.%.Self] +// CHECK:STDOUT: %.Self.ref: %I.type = name_ref .Self, %.Self [symbolic_self = constants.%.Self] +// CHECK:STDOUT: %Incomplete.ref: type = name_ref Incomplete, file.%Incomplete.decl [concrete = constants.%Incomplete.type] +// CHECK:STDOUT: %.Self.as_type: type = facet_access_type %.Self.ref [symbolic_self = constants.%.Self.binding.as_type] +// CHECK:STDOUT: %.loc8_19: type = converted %.Self.ref, %.Self.as_type [symbolic_self = constants.%.Self.binding.as_type] +// CHECK:STDOUT: %.loc8_13: type = where_expr %.Self [concrete = constants.%I_where.type] { +// CHECK:STDOUT: requirement_base_facet_type constants.%I.type +// CHECK:STDOUT: requirement_impls %.loc8_19, %Incomplete.ref +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @C.as.I.impl [concrete] +// CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @I { +// CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: witness = () +// CHECK:STDOUT: +// CHECK:STDOUT: !requires: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @Incomplete; +// CHECK:STDOUT: +// CHECK:STDOUT: impl @C.as.I.impl: %C.ref as %.loc8_13 { +// CHECK:STDOUT: %C.as.I.impl.F.decl: %C.as.I.impl.F.type = fn_decl @C.as.I.impl.F [concrete = constants.%C.as.I.impl.F] { +// CHECK:STDOUT: %return.patt: = return_slot_pattern [concrete] +// CHECK:STDOUT: %return.param_patt: = out_param_pattern %return.patt, call_param0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %Self.ref: type = name_ref Self, @C.as.I.impl.%C.ref [concrete = constants.%C] +// CHECK:STDOUT: %Incomplete.ref: type = name_ref Incomplete, file.%Incomplete.decl [concrete = constants.%Incomplete.type] +// CHECK:STDOUT: %T.ref: = name_ref T, [concrete = ] +// CHECK:STDOUT: %return.param: ref = out_param call_param0 +// CHECK:STDOUT: %return: ref = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Incomplete = +// CHECK:STDOUT: .F = %C.as.I.impl.F.decl +// CHECK:STDOUT: witness = file.%I.impl_witness +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @C.as.I.impl.F() -> ; +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/interface/incomplete.carbon b/toolchain/check/testdata/interface/incomplete.carbon index 2436d928bf701..aa05700dd02e3 100644 --- a/toolchain/check/testdata/interface/incomplete.carbon +++ b/toolchain/check/testdata/interface/incomplete.carbon @@ -53,6 +53,26 @@ interface B { require impls A; } +impl () as B {} + +// --- fail_incomplete_extend_interface.carbon +library "[[@TEST_NAME]]"; + +interface A; + +interface B { + extend require impls A; +} + +// CHECK:STDERR: fail_incomplete_extend_interface.carbon:[[@LINE+7]]:1: error: definition of impl as incomplete facet type `B` [ImplAsIncompleteFacetTypeDefinition] +// CHECK:STDERR: impl () as B {} +// CHECK:STDERR: ^~~~~~~~~~~~~~ +// CHECK:STDERR: fail_incomplete_extend_interface.carbon:[[@LINE-9]]:1: note: interface was forward declared here [InterfaceForwardDeclaredHere] +// CHECK:STDERR: interface A; +// CHECK:STDERR: ^~~~~~~~~~~~ +// CHECK:STDERR: +impl () as B {} + // --- fail_incomplete_interface_in_where.carbon library "[[@TEST_NAME]]"; @@ -148,6 +168,73 @@ interface B { // CHECK:STDOUT: %B.type: type = facet_type <@B> [concrete] // CHECK:STDOUT: %Self: %B.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 0, %Self [symbolic] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] +// CHECK:STDOUT: %B.impl_witness: = impl_witness file.%B.impl_witness_table [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .A = %A.decl +// CHECK:STDOUT: .B = %B.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %A.decl: type = interface_decl @A [concrete = constants.%A.type] {} {} +// CHECK:STDOUT: %B.decl: type = interface_decl @B [concrete = constants.%B.type] {} {} +// CHECK:STDOUT: impl_decl @empty_tuple.type.as.B.impl [concrete] {} { +// CHECK:STDOUT: %.loc9_7.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] +// CHECK:STDOUT: %.loc9_7.2: type = converted %.loc9_7.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] +// CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %B.impl_witness_table = impl_witness_table (), @empty_tuple.type.as.B.impl [concrete] +// CHECK:STDOUT: %B.impl_witness: = impl_witness %B.impl_witness_table [concrete = constants.%B.impl_witness] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @A; +// CHECK:STDOUT: +// CHECK:STDOUT: interface @B { +// CHECK:STDOUT: %Self: %B.type = symbolic_binding Self, 0 [symbolic = constants.%Self] +// CHECK:STDOUT: %B.require0.decl = require_decl @B.require0 [concrete] { +// CHECK:STDOUT: require %Self.as_type impls <@A> +// CHECK:STDOUT: } { +// CHECK:STDOUT: %Self.as_type: type = facet_access_type @B.%Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] +// CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .A = +// CHECK:STDOUT: witness = () +// CHECK:STDOUT: +// CHECK:STDOUT: !requires: +// CHECK:STDOUT: @B.require0 { +// CHECK:STDOUT: require @B.require0.%Self.as_type impls <@A> +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic require @B.require0(@B.%Self: %B.type) { +// CHECK:STDOUT: %Self: %B.type = symbolic_binding Self, 0 [symbolic = %Self (constants.%Self)] +// CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 0, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: impl @empty_tuple.type.as.B.impl: %.loc9_7.2 as %B.ref { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: witness = file.%B.impl_witness +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @B.require0(constants.%Self) { +// CHECK:STDOUT: %Self => constants.%Self +// CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_incomplete_extend_interface.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %A.type: type = facet_type <@A> [concrete] +// CHECK:STDOUT: %B.type: type = facet_type <@B> [concrete] +// CHECK:STDOUT: %Self: %B.type = symbolic_binding Self, 0 [symbolic] +// CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 0, %Self [symbolic] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -157,6 +244,11 @@ interface B { // CHECK:STDOUT: } // CHECK:STDOUT: %A.decl: type = interface_decl @A [concrete = constants.%A.type] {} {} // CHECK:STDOUT: %B.decl: type = interface_decl @B [concrete = constants.%B.type] {} {} +// CHECK:STDOUT: impl_decl @empty_tuple.type.as.B.impl [concrete] {} { +// CHECK:STDOUT: %.loc16_7.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] +// CHECK:STDOUT: %.loc16_7.2: type = converted %.loc16_7.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] +// CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B.type] +// CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @A; @@ -186,6 +278,11 @@ interface B { // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 0, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: impl @empty_tuple.type.as.B.impl: %.loc16_7.2 as %B.ref { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: witness = +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: specific @B.require0(constants.%Self) { // CHECK:STDOUT: %Self => constants.%Self // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type diff --git a/toolchain/check/testdata/named_constraint/empty_generic.carbon b/toolchain/check/testdata/named_constraint/empty_generic.carbon index 82ec701a0a4ff..5d9a00b91be5a 100644 --- a/toolchain/check/testdata/named_constraint/empty_generic.carbon +++ b/toolchain/check/testdata/named_constraint/empty_generic.carbon @@ -36,7 +36,7 @@ fn G(T:! Z, U:! Empty(T), V:! type) { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %empty_struct.4bc: %Empty.type.a8a = struct_value () [concrete] // CHECK:STDOUT: %Empty.type.b8d23b.1: type = facet_type <@Empty, @Empty(%T.d9f)> [symbolic] -// CHECK:STDOUT: %Self.aa1: %Empty.type.b8d23b.1 = symbolic_binding Self, 1 [symbolic] +// CHECK:STDOUT: %Self.aa1546.1: %Empty.type.b8d23b.1 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %U.aa1546.1: %Empty.type.b8d23b.1 = symbolic_binding U, 1 [symbolic] // CHECK:STDOUT: %pattern_type.e25e9f.1: type = pattern_type %Empty.type.b8d23b.1 [symbolic] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] @@ -58,6 +58,7 @@ fn G(T:! Z, U:! Empty(T), V:! type) { // CHECK:STDOUT: %F.specific_fn.55c: = specific_function %F, @F(%T.binding.as_type, %Empty.facet.832) [symbolic] // CHECK:STDOUT: %Empty.facet.146: %Empty.type.b8d23b.2 = facet_value %T.binding.as_type, () [symbolic] // CHECK:STDOUT: %F.specific_fn.d3f: = specific_function %F, @F(%T.binding.as_type, %Empty.facet.146) [symbolic] +// CHECK:STDOUT: %Self.aa1546.2: %Empty.type.b8d23b.2 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %F.specific_fn.4af: = specific_function %F, @F(%T.binding.as_type, %U.aa1546.2) [symbolic] // CHECK:STDOUT: %Empty.facet.920: %Empty.type.b8d23b.2 = facet_value %V, () [symbolic] // CHECK:STDOUT: %F.specific_fn.905: = specific_function %F, @F(%T.binding.as_type, %Empty.facet.920) [symbolic] @@ -113,10 +114,10 @@ fn G(T:! Z, U:! Empty(T), V:! type) { // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %Empty.type: type = facet_type <@Empty, @Empty(%T.loc16_18.1)> [symbolic = %Empty.type (constants.%Empty.type.b8d23b.1)] -// CHECK:STDOUT: %Self.loc16_28.2: @Empty.%Empty.type (%Empty.type.b8d23b.1) = symbolic_binding Self, 1 [symbolic = %Self.loc16_28.2 (constants.%Self.aa1)] +// CHECK:STDOUT: %Self.loc16_28.2: @Empty.%Empty.type (%Empty.type.b8d23b.1) = symbolic_binding Self, 1 [symbolic = %Self.loc16_28.2 (constants.%Self.aa1546.1)] // CHECK:STDOUT: // CHECK:STDOUT: constraint { -// CHECK:STDOUT: %Self.loc16_28.1: @Empty.%Empty.type (%Empty.type.b8d23b.1) = symbolic_binding Self, 1 [symbolic = %Self.loc16_28.2 (constants.%Self.aa1)] +// CHECK:STDOUT: %Self.loc16_28.1: @Empty.%Empty.type (%Empty.type.b8d23b.1) = symbolic_binding Self, 1 [symbolic = %Self.loc16_28.2 (constants.%Self.aa1546.1)] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self.loc16_28.1 @@ -221,6 +222,10 @@ fn G(T:! Z, U:! Empty(T), V:! type) { // CHECK:STDOUT: // CHECK:STDOUT: specific @Empty(constants.%T.binding.as_type) { // CHECK:STDOUT: %T.loc16_18.1 => constants.%T.binding.as_type +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %Empty.type => constants.%Empty.type.b8d23b.2 +// CHECK:STDOUT: %Self.loc16_28.2 => constants.%Self.aa1546.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @G(constants.%T.3b3, constants.%U.aa1546.2, constants.%V) { diff --git a/toolchain/check/type_completion.cpp b/toolchain/check/type_completion.cpp index 4cf75ce9d63e1..1432a64f2898d 100644 --- a/toolchain/check/type_completion.cpp +++ b/toolchain/check/type_completion.cpp @@ -14,7 +14,9 @@ #include "toolchain/check/type.h" #include "toolchain/diagnostics/format_providers.h" #include "toolchain/sem_ir/constant.h" +#include "toolchain/sem_ir/generic.h" #include "toolchain/sem_ir/ids.h" +#include "toolchain/sem_ir/specific_interface.h" #include "toolchain/sem_ir/specific_named_constraint.h" #include "toolchain/sem_ir/type_info.h" #include "toolchain/sem_ir/typed_insts.h" @@ -82,6 +84,128 @@ static auto ForEachRequireImpls( } } +static auto MakeSpecificForRequireDecl(Context& context, SemIR::LocId loc_id, + SemIR::SpecificId entity_specific_id, + SemIR::GenericId require_generic_id) + -> SemIR::SpecificId { + auto entity_specific_args_id = + context.specifics().GetArgsOrEmpty(entity_specific_id); + auto entity_specific_args = + context.inst_blocks().Get(entity_specific_args_id); + + const auto& require_generic = context.generics().Get(require_generic_id); + const auto& require_self_specific = + context.specifics().Get(require_generic.self_specific_id); + auto require_self_specific_args = + context.inst_blocks().Get(require_self_specific.args_id); + + llvm::SmallVector arg_ids; + arg_ids.reserve(entity_specific_args.size() + 1); + // Start with the enclosing arguments from the entity. + llvm::append_range(arg_ids, entity_specific_args); + // Add the `Self` argument from the require decl. + arg_ids.push_back(require_self_specific_args.back()); + + return MakeSpecific(context, loc_id, require_generic_id, arg_ids); +} + +static auto RequireCompleteFacetType(Context& context, SemIR::LocId loc_id, + const SemIR::FacetType& facet_type, + MakeDiagnosticBuilderFn diagnoser) + -> bool { + llvm::SmallVector work = {facet_type.facet_type_id}; + while (!work.empty()) { + auto next_facet_type_id = work.pop_back_val(); + const auto& facet_type_info = context.facet_types().Get(next_facet_type_id); + + auto require_complete_interface = + [&](SemIR::SpecificInterface req_interface) -> bool { + auto interface_id = req_interface.interface_id; + const auto& interface = context.interfaces().Get(interface_id); + if (!interface.is_complete()) { + if (diagnoser) { + auto builder = diagnoser(); + NoteIncompleteInterface(context, interface_id, builder); + builder.Emit(); + } + return false; + } + + ForEachRequireImpls( + context, interface, [&](const SemIR::RequireImpls& require) { + if (require.extend_self) { + auto require_specific_id = MakeSpecificForRequireDecl( + context, loc_id, req_interface.specific_id, + require.generic_id); + auto const_facet_type = SemIR::GetConstantValueInSpecific( + context.sem_ir(), require_specific_id, + require.facet_type_inst_id); + if (const_facet_type != SemIR::ErrorInst::ConstantId) { + auto facet_type = context.insts().GetAs( + context.constant_values().GetInstId(const_facet_type)); + work.push_back(facet_type.facet_type_id); + } + } + }); + + if (req_interface.specific_id.has_value()) { + ResolveSpecificDefinition(context, loc_id, req_interface.specific_id); + } + return true; + }; + + auto require_complete_named_constraint = + [&](SemIR::SpecificNamedConstraint req_constraint) -> bool { + auto named_constraint_id = req_constraint.named_constraint_id; + const auto& constraint = + context.named_constraints().Get(named_constraint_id); + if (!constraint.is_complete()) { + if (diagnoser) { + auto builder = diagnoser(); + NoteIncompleteNamedConstraint(context, named_constraint_id, builder); + builder.Emit(); + } + return false; + } + + ForEachRequireImpls( + context, constraint, [&](const SemIR::RequireImpls& require) { + if (require.extend_self) { + auto require_specific_id = MakeSpecificForRequireDecl( + context, loc_id, req_constraint.specific_id, + require.generic_id); + auto const_facet_type = SemIR::GetConstantValueInSpecific( + context.sem_ir(), require_specific_id, + require.facet_type_inst_id); + if (const_facet_type != SemIR::ErrorInst::ConstantId) { + auto facet_type = context.insts().GetAs( + context.constant_values().GetInstId(const_facet_type)); + work.push_back(facet_type.facet_type_id); + } + } + }); + + if (req_constraint.specific_id.has_value()) { + ResolveSpecificDefinition(context, loc_id, req_constraint.specific_id); + } + return true; + }; + + for (auto extends : facet_type_info.extend_constraints) { + if (!require_complete_interface(extends)) { + return false; + } + } + for (auto extends : facet_type_info.extend_named_constraints) { + if (!require_complete_named_constraint(extends)) { + return false; + } + } + } + + return true; +} + namespace { // Worklist-based type completion mechanism. // @@ -396,32 +520,9 @@ auto TypeCompleter::AddNestedIncompleteTypes(SemIR::Inst type_inst) -> bool { break; } case CARBON_KIND(SemIR::FacetType inst): { - auto identified_id = - RequireIdentifiedFacetType(*context_, inst, diagnoser_); - if (!identified_id.has_value()) { + if (!RequireCompleteFacetType(*context_, loc_id_, inst, diagnoser_)) { return false; } - - const auto& identified = - context_->identified_facet_types().Get(identified_id); - - for (auto req_interface : identified.required_interfaces()) { - auto interface_id = req_interface.interface_id; - const auto& interface = context_->interfaces().Get(interface_id); - if (!interface.is_complete()) { - if (diagnoser_) { - auto builder = diagnoser_(); - NoteIncompleteInterface(*context_, interface_id, builder); - builder.Emit(); - } - return false; - } - - if (req_interface.specific_id.has_value()) { - ResolveSpecificDefinition(*context_, loc_id_, - req_interface.specific_id); - } - } break; } diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 39cace550c306..2ee339f3a9df3 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -364,6 +364,7 @@ CARBON_DIAGNOSTIC_KIND(ImplLookupInUnidentifiedFacetType) CARBON_DIAGNOSTIC_KIND(RequireImplsExtendWithExplicitSelf) CARBON_DIAGNOSTIC_KIND(RequireImplsMissingFacetType) CARBON_DIAGNOSTIC_KIND(RequireImplsMissingSelf) +CARBON_DIAGNOSTIC_KIND(RequireImplsIncompleteFacetType) CARBON_DIAGNOSTIC_KIND(RequireImplsUnidentifiedFacetType) CARBON_DIAGNOSTIC_KIND(RequireInWrongScope) diff --git a/toolchain/sem_ir/stringify.cpp b/toolchain/sem_ir/stringify.cpp index 119ed5d39bc0f..99490ebdb91e2 100644 --- a/toolchain/sem_ir/stringify.cpp +++ b/toolchain/sem_ir/stringify.cpp @@ -814,6 +814,10 @@ auto StringifySpecific(const File& sem_ir, SpecificId specific_id) sem_ir.interfaces().Get(interface_decl.interface_id), specific_id); break; } + case CARBON_KIND(RequireImplsDecl _): { + step_stack.Push("require"); + break; + } default: { // TODO: Include the specific arguments here. step_stack.PushInstId(generic.decl_id); From 1a64cdcc37130546916dc63b9b65914e99f7b916 Mon Sep 17 00:00:00 2001 From: danakj Date: Mon, 17 Nov 2025 13:59:29 -0500 Subject: [PATCH 02/31] touchup --- toolchain/check/type_completion.cpp | 75 ++++++++++++----------------- 1 file changed, 32 insertions(+), 43 deletions(-) diff --git a/toolchain/check/type_completion.cpp b/toolchain/check/type_completion.cpp index 1432a64f2898d..49c46b58730a9 100644 --- a/toolchain/check/type_completion.cpp +++ b/toolchain/check/type_completion.cpp @@ -109,6 +109,19 @@ static auto MakeSpecificForRequireDecl(Context& context, SemIR::LocId loc_id, return MakeSpecific(context, loc_id, require_generic_id, arg_ids); } +static auto GetFacetTypeInSpecific(Context& context, SemIR::InstId facet_type, + SemIR::SpecificId specific_id) + -> SemIR::FacetTypeId { + auto const_facet_type = SemIR::GetConstantValueInSpecific( + context.sem_ir(), specific_id, facet_type); + auto facet_type_in_specific = context.insts().TryGetAs( + context.constant_values().GetInstId(const_facet_type)); + if (!facet_type_in_specific.has_value()) { + return SemIR::FacetTypeId::None; + } + return facet_type_in_specific->facet_type_id; +} + static auto RequireCompleteFacetType(Context& context, SemIR::LocId loc_id, const SemIR::FacetType& facet_type, MakeDiagnosticBuilderFn diagnoser) @@ -118,9 +131,8 @@ static auto RequireCompleteFacetType(Context& context, SemIR::LocId loc_id, auto next_facet_type_id = work.pop_back_val(); const auto& facet_type_info = context.facet_types().Get(next_facet_type_id); - auto require_complete_interface = - [&](SemIR::SpecificInterface req_interface) -> bool { - auto interface_id = req_interface.interface_id; + for (auto extends : facet_type_info.extend_constraints) { + auto interface_id = extends.interface_id; const auto& interface = context.interfaces().Get(interface_id); if (!interface.is_complete()) { if (diagnoser) { @@ -135,28 +147,21 @@ static auto RequireCompleteFacetType(Context& context, SemIR::LocId loc_id, context, interface, [&](const SemIR::RequireImpls& require) { if (require.extend_self) { auto require_specific_id = MakeSpecificForRequireDecl( - context, loc_id, req_interface.specific_id, - require.generic_id); - auto const_facet_type = SemIR::GetConstantValueInSpecific( - context.sem_ir(), require_specific_id, - require.facet_type_inst_id); - if (const_facet_type != SemIR::ErrorInst::ConstantId) { - auto facet_type = context.insts().GetAs( - context.constant_values().GetInstId(const_facet_type)); - work.push_back(facet_type.facet_type_id); + context, loc_id, extends.specific_id, require.generic_id); + auto facet_type_id = GetFacetTypeInSpecific( + context, require.facet_type_inst_id, require_specific_id); + if (facet_type_id.has_value()) { + work.push_back(facet_type_id); } } }); - if (req_interface.specific_id.has_value()) { - ResolveSpecificDefinition(context, loc_id, req_interface.specific_id); + if (extends.specific_id.has_value()) { + ResolveSpecificDefinition(context, loc_id, extends.specific_id); } - return true; - }; - - auto require_complete_named_constraint = - [&](SemIR::SpecificNamedConstraint req_constraint) -> bool { - auto named_constraint_id = req_constraint.named_constraint_id; + } + for (auto extends : facet_type_info.extend_named_constraints) { + auto named_constraint_id = extends.named_constraint_id; const auto& constraint = context.named_constraints().Get(named_constraint_id); if (!constraint.is_complete()) { @@ -172,33 +177,17 @@ static auto RequireCompleteFacetType(Context& context, SemIR::LocId loc_id, context, constraint, [&](const SemIR::RequireImpls& require) { if (require.extend_self) { auto require_specific_id = MakeSpecificForRequireDecl( - context, loc_id, req_constraint.specific_id, - require.generic_id); - auto const_facet_type = SemIR::GetConstantValueInSpecific( - context.sem_ir(), require_specific_id, - require.facet_type_inst_id); - if (const_facet_type != SemIR::ErrorInst::ConstantId) { - auto facet_type = context.insts().GetAs( - context.constant_values().GetInstId(const_facet_type)); - work.push_back(facet_type.facet_type_id); + context, loc_id, extends.specific_id, require.generic_id); + auto facet_type_id = GetFacetTypeInSpecific( + context, require.facet_type_inst_id, require_specific_id); + if (facet_type_id.has_value()) { + work.push_back(facet_type_id); } } }); - if (req_constraint.specific_id.has_value()) { - ResolveSpecificDefinition(context, loc_id, req_constraint.specific_id); - } - return true; - }; - - for (auto extends : facet_type_info.extend_constraints) { - if (!require_complete_interface(extends)) { - return false; - } - } - for (auto extends : facet_type_info.extend_named_constraints) { - if (!require_complete_named_constraint(extends)) { - return false; + if (extends.specific_id.has_value()) { + ResolveSpecificDefinition(context, loc_id, extends.specific_id); } } } From e271440e4149951bcf22481fc8aa9034d6e70803 Mon Sep 17 00:00:00 2001 From: danakj Date: Mon, 17 Nov 2025 14:07:51 -0500 Subject: [PATCH 03/31] MakeCopyOfSpecificAndAppendSelf --- toolchain/check/type_completion.cpp | 56 +++++++++++++++++------------ 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/toolchain/check/type_completion.cpp b/toolchain/check/type_completion.cpp index 49c46b58730a9..d5df36d6f388a 100644 --- a/toolchain/check/type_completion.cpp +++ b/toolchain/check/type_completion.cpp @@ -84,29 +84,37 @@ static auto ForEachRequireImpls( } } -static auto MakeSpecificForRequireDecl(Context& context, SemIR::LocId loc_id, - SemIR::SpecificId entity_specific_id, - SemIR::GenericId require_generic_id) - -> SemIR::SpecificId { - auto entity_specific_args_id = - context.specifics().GetArgsOrEmpty(entity_specific_id); - auto entity_specific_args = - context.inst_blocks().Get(entity_specific_args_id); - - const auto& require_generic = context.generics().Get(require_generic_id); +// Makes a copy of a Specific from one generic to apply to another given +// `generic_id`, with a given `Self` argument appended to the end. +static auto MakeCopyOfSpecificAndAppendSelf( + Context& context, SemIR::LocId loc_id, SemIR::SpecificId specific_id, + SemIR::GenericId generic_id, SemIR::InstId self_id) -> SemIR::SpecificId { + auto source_specific_args_id = + context.specifics().GetArgsOrEmpty(specific_id); + auto source_specific_args = + context.inst_blocks().Get(source_specific_args_id); + + llvm::SmallVector arg_ids; + arg_ids.reserve(source_specific_args.size() + 1); + // Start with the enclosing arguments from the source Specific. + llvm::append_range(arg_ids, source_specific_args); + // Add the new `Self` argument. + arg_ids.push_back(self_id); + + return MakeSpecific(context, loc_id, generic_id, arg_ids); +} + +static auto GetRequireImplsSpecificSelf(Context& context, + const SemIR::RequireImpls& require) + -> SemIR::InstId { + const auto& require_generic = context.generics().Get(require.generic_id); const auto& require_self_specific = context.specifics().Get(require_generic.self_specific_id); auto require_self_specific_args = context.inst_blocks().Get(require_self_specific.args_id); - - llvm::SmallVector arg_ids; - arg_ids.reserve(entity_specific_args.size() + 1); - // Start with the enclosing arguments from the entity. - llvm::append_range(arg_ids, entity_specific_args); - // Add the `Self` argument from the require decl. - arg_ids.push_back(require_self_specific_args.back()); - - return MakeSpecific(context, loc_id, require_generic_id, arg_ids); + // The last argument of a `require` generic is always `Self`, as require can + // not have any parameters of its own, only enclosing parameters. + return require_self_specific_args.back(); } static auto GetFacetTypeInSpecific(Context& context, SemIR::InstId facet_type, @@ -146,8 +154,9 @@ static auto RequireCompleteFacetType(Context& context, SemIR::LocId loc_id, ForEachRequireImpls( context, interface, [&](const SemIR::RequireImpls& require) { if (require.extend_self) { - auto require_specific_id = MakeSpecificForRequireDecl( - context, loc_id, extends.specific_id, require.generic_id); + auto require_specific_id = MakeCopyOfSpecificAndAppendSelf( + context, loc_id, extends.specific_id, require.generic_id, + GetRequireImplsSpecificSelf(context, require)); auto facet_type_id = GetFacetTypeInSpecific( context, require.facet_type_inst_id, require_specific_id); if (facet_type_id.has_value()) { @@ -176,8 +185,9 @@ static auto RequireCompleteFacetType(Context& context, SemIR::LocId loc_id, ForEachRequireImpls( context, constraint, [&](const SemIR::RequireImpls& require) { if (require.extend_self) { - auto require_specific_id = MakeSpecificForRequireDecl( - context, loc_id, extends.specific_id, require.generic_id); + auto require_specific_id = MakeCopyOfSpecificAndAppendSelf( + context, loc_id, extends.specific_id, require.generic_id, + GetRequireImplsSpecificSelf(context, require)); auto facet_type_id = GetFacetTypeInSpecific( context, require.facet_type_inst_id, require_specific_id); if (facet_type_id.has_value()) { From ac0469b5564497461b4137375de553f168599b62 Mon Sep 17 00:00:00 2001 From: danakj Date: Mon, 17 Nov 2025 14:11:28 -0500 Subject: [PATCH 04/31] bit-simpler --- toolchain/check/type_completion.cpp | 92 +++++++++++++---------------- 1 file changed, 42 insertions(+), 50 deletions(-) diff --git a/toolchain/check/type_completion.cpp b/toolchain/check/type_completion.cpp index d5df36d6f388a..f385fd99fe1c4 100644 --- a/toolchain/check/type_completion.cpp +++ b/toolchain/check/type_completion.cpp @@ -72,18 +72,6 @@ static auto NoteIncompleteNamedConstraint( } } -template - requires SameAsOneOf -static auto ForEachRequireImpls( - Context& context, const T& entity, - llvm::function_refvoid> f) -> void { - for (auto require_impls_id : - context.require_impls_blocks().Get(entity.require_impls_block_id)) { - const auto& require = context.require_impls().Get(require_impls_id); - f(require); - } -} - // Makes a copy of a Specific from one generic to apply to another given // `generic_id`, with a given `Self` argument appended to the end. static auto MakeCopyOfSpecificAndAppendSelf( @@ -151,19 +139,20 @@ static auto RequireCompleteFacetType(Context& context, SemIR::LocId loc_id, return false; } - ForEachRequireImpls( - context, interface, [&](const SemIR::RequireImpls& require) { - if (require.extend_self) { - auto require_specific_id = MakeCopyOfSpecificAndAppendSelf( - context, loc_id, extends.specific_id, require.generic_id, - GetRequireImplsSpecificSelf(context, require)); - auto facet_type_id = GetFacetTypeInSpecific( - context, require.facet_type_inst_id, require_specific_id); - if (facet_type_id.has_value()) { - work.push_back(facet_type_id); - } - } - }); + for (auto require_impls_id : context.require_impls_blocks().Get( + interface.require_impls_block_id)) { + const auto& require = context.require_impls().Get(require_impls_id); + if (require.extend_self) { + auto require_specific_id = MakeCopyOfSpecificAndAppendSelf( + context, loc_id, extends.specific_id, require.generic_id, + GetRequireImplsSpecificSelf(context, require)); + auto facet_type_id = GetFacetTypeInSpecific( + context, require.facet_type_inst_id, require_specific_id); + if (facet_type_id.has_value()) { + work.push_back(facet_type_id); + } + } + } if (extends.specific_id.has_value()) { ResolveSpecificDefinition(context, loc_id, extends.specific_id); @@ -182,19 +171,20 @@ static auto RequireCompleteFacetType(Context& context, SemIR::LocId loc_id, return false; } - ForEachRequireImpls( - context, constraint, [&](const SemIR::RequireImpls& require) { - if (require.extend_self) { - auto require_specific_id = MakeCopyOfSpecificAndAppendSelf( - context, loc_id, extends.specific_id, require.generic_id, - GetRequireImplsSpecificSelf(context, require)); - auto facet_type_id = GetFacetTypeInSpecific( - context, require.facet_type_inst_id, require_specific_id); - if (facet_type_id.has_value()) { - work.push_back(facet_type_id); - } - } - }); + for (auto require_impls_id : context.require_impls_blocks().Get( + constraint.require_impls_block_id)) { + const auto& require = context.require_impls().Get(require_impls_id); + if (require.extend_self) { + auto require_specific_id = MakeCopyOfSpecificAndAppendSelf( + context, loc_id, extends.specific_id, require.generic_id, + GetRequireImplsSpecificSelf(context, require)); + auto facet_type_id = GetFacetTypeInSpecific( + context, require.facet_type_inst_id, require_specific_id); + if (facet_type_id.has_value()) { + work.push_back(facet_type_id); + } + } + } if (extends.specific_id.has_value()) { ResolveSpecificDefinition(context, loc_id, extends.specific_id); @@ -944,23 +934,25 @@ auto RequireIdentifiedFacetType(Context& context, for (auto extend : facet_type_info.extend_named_constraints) { const auto& constraint = context.named_constraints().Get(extend.named_constraint_id); - ForEachRequireImpls( - context, constraint, [&](const SemIR::RequireImpls& require) { - if (facet_type_extends && require.extend_self) { - extend_facet_types.push_back(require.facet_type_id); - } else { - impls_facet_types.push_back(require.facet_type_id); - } - }); + for (auto require_impls_id : context.require_impls_blocks().Get( + constraint.require_impls_block_id)) { + const auto& require = context.require_impls().Get(require_impls_id); + if (facet_type_extends && require.extend_self) { + extend_facet_types.push_back(require.facet_type_id); + } else { + impls_facet_types.push_back(require.facet_type_id); + } + } } for (auto impls : facet_type_info.self_impls_named_constraints) { const auto& constraint = context.named_constraints().Get(impls.named_constraint_id); - ForEachRequireImpls(context, constraint, - [&](const SemIR::RequireImpls& require) { - impls_facet_types.push_back(require.facet_type_id); - }); + for (auto require_impls_id : context.require_impls_blocks().Get( + constraint.require_impls_block_id)) { + const auto& require = context.require_impls().Get(require_impls_id); + impls_facet_types.push_back(require.facet_type_id); + } } } From acd93330bfd9328ec48294c4fd2e569ccf5d94a0 Mon Sep 17 00:00:00 2001 From: danakj Date: Mon, 17 Nov 2025 14:14:34 -0500 Subject: [PATCH 05/31] whitespace --- toolchain/check/type_completion.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/toolchain/check/type_completion.cpp b/toolchain/check/type_completion.cpp index f385fd99fe1c4..8ad5abdf6b927 100644 --- a/toolchain/check/type_completion.cpp +++ b/toolchain/check/type_completion.cpp @@ -158,6 +158,7 @@ static auto RequireCompleteFacetType(Context& context, SemIR::LocId loc_id, ResolveSpecificDefinition(context, loc_id, extends.specific_id); } } + for (auto extends : facet_type_info.extend_named_constraints) { auto named_constraint_id = extends.named_constraint_id; const auto& constraint = From b8e5c897e9a872fd7e5c21ea9d41fa10d0a974c1 Mon Sep 17 00:00:00 2001 From: danakj Date: Mon, 17 Nov 2025 15:27:04 -0500 Subject: [PATCH 06/31] complete-at-require-decl --- toolchain/check/handle_require.cpp | 24 ++++- .../testdata/facet/fail_incomplete.carbon | 23 ----- .../impl/impl_as_named_constraint.carbon | 12 +-- .../testdata/interface/incomplete.carbon | 94 +++++++++++-------- 4 files changed, 76 insertions(+), 77 deletions(-) diff --git a/toolchain/check/handle_require.cpp b/toolchain/check/handle_require.cpp index 889b0d51c35e8..e5fbfbe4ba4a3 100644 --- a/toolchain/check/handle_require.cpp +++ b/toolchain/check/handle_require.cpp @@ -163,7 +163,7 @@ struct ValidateRequireResult { static auto ValidateRequire(Context& context, SemIR::LocId loc_id, SemIR::TypeInstId self_inst_id, SemIR::InstId constraint_inst_id, - SemIR::InstId scope_inst_id) + SemIR::InstId scope_inst_id, bool extend) -> std::optional { auto constraint_constant_value_id = context.constant_values().Get(constraint_inst_id); @@ -210,6 +210,22 @@ static auto ValidateRequire(Context& context, SemIR::LocId loc_id, return std::nullopt; } + if (extend) { + if (!RequireCompleteType( + context, constraint_type_id, SemIR::LocId(constraint_inst_id), [&] { + CARBON_DIAGNOSTIC(RequireImplsIncompleteFacetType, Error, + "`extend require` of incomplete facet type {0}", + InstIdAsType); + return context.emitter().Build(constraint_inst_id, + RequireImplsIncompleteFacetType, + constraint_inst_id); + })) { + // The constraint is invalid, and a diagnostic was emitted by + // RequireCompleteType(). + return std::nullopt; + } + } + if (scope_inst_id == SemIR::ErrorInst::InstId) { // `require` is in the wrong scope. return std::nullopt; @@ -241,8 +257,10 @@ auto HandleParseNode(Context& context, Parse::RequireDeclId node_id) -> bool { auto scope_inst_id = context.node_stack().Pop(); + bool extend = introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend); + auto validated = ValidateRequire(context, node_id, self_inst_id, - constraint_inst_id, scope_inst_id); + constraint_inst_id, scope_inst_id, extend); if (!validated) { DiscardGenericDecl(context); return true; @@ -255,8 +273,6 @@ auto HandleParseNode(Context& context, Parse::RequireDeclId node_id) -> bool { return true; } - bool extend = introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend); - auto require_impls_decl = SemIR::RequireImplsDecl{// To be filled in after. .require_impls_id = SemIR::RequireImplsId::None, diff --git a/toolchain/check/testdata/facet/fail_incomplete.carbon b/toolchain/check/testdata/facet/fail_incomplete.carbon index 53495105b0608..8187875c0dd2b 100644 --- a/toolchain/check/testdata/facet/fail_incomplete.carbon +++ b/toolchain/check/testdata/facet/fail_incomplete.carbon @@ -80,29 +80,6 @@ impl C as X; // Requires X complete. impl C as X {} -// --- fail_incomplete_through_constraint.carbon -library "[[@TEST_NAME]]"; - -interface Z; -constraint Y { - extend require impls Z; -} - -class C {} - -// Requires Y identified. -impl C as Y; - -// Requires Y complete. -// CHECK:STDERR: fail_incomplete_through_constraint.carbon:[[@LINE+7]]:1: error: definition of impl as incomplete facet type `Y` [ImplAsIncompleteFacetTypeDefinition] -// CHECK:STDERR: impl C as Y {} -// CHECK:STDERR: ^~~~~~~~~~~~~ -// CHECK:STDERR: fail_incomplete_through_constraint.carbon:[[@LINE-14]]:1: note: interface was forward declared here [InterfaceForwardDeclaredHere] -// CHECK:STDERR: interface Z; -// CHECK:STDERR: ^~~~~~~~~~~~ -// CHECK:STDERR: -impl C as Y {} - // --- fail_impl_lookup_incomplete.carbon library "[[@TEST_NAME]]"; diff --git a/toolchain/check/testdata/impl/impl_as_named_constraint.carbon b/toolchain/check/testdata/impl/impl_as_named_constraint.carbon index ff78010220baa..fd725dd9b42f6 100644 --- a/toolchain/check/testdata/impl/impl_as_named_constraint.carbon +++ b/toolchain/check/testdata/impl/impl_as_named_constraint.carbon @@ -24,8 +24,8 @@ impl () as A {} // --- fail_too_many_interfaces_in_constraint.carbon library "[[@TEST_NAME]]"; -interface A1; -interface A2; +interface A1 {} +interface A2 {} constraint B { extend require impls A1; extend require impls A2; @@ -40,17 +40,11 @@ impl () as B {} // --- one_extend_impls_interface_in_constraint.carbon library "[[@TEST_NAME]]"; -interface A; +interface A {} constraint B { extend require impls A; } -// Requries B identified. -impl () as B; - -interface A {} - -// Requries B complete. impl () as B {} // --- fail_one_impls_interface_in_constraint.carbon diff --git a/toolchain/check/testdata/interface/incomplete.carbon b/toolchain/check/testdata/interface/incomplete.carbon index aa05700dd02e3..b10d68e88f987 100644 --- a/toolchain/check/testdata/interface/incomplete.carbon +++ b/toolchain/check/testdata/interface/incomplete.carbon @@ -44,6 +44,22 @@ interface B { require impls A; } +// --- fail_incomplete_extend_constraint.carbon +library "[[@TEST_NAME]]"; + +constraint A; + +interface B { + // CHECK:STDERR: fail_incomplete_extend_constraint.carbon:[[@LINE+7]]:24: error: facet type `A` cannot be identified in `require` declaration [RequireImplsUnidentifiedFacetType] + // CHECK:STDERR: extend require impls A; + // CHECK:STDERR: ^ + // CHECK:STDERR: fail_incomplete_extend_constraint.carbon:[[@LINE-6]]:1: note: constraint was forward declared here [NamedConstraintForwardDeclaredHere] + // CHECK:STDERR: constraint A; + // CHECK:STDERR: ^~~~~~~~~~~~~ + // CHECK:STDERR: + extend require impls A; +} + // --- incomplete_interface.carbon library "[[@TEST_NAME]]"; @@ -61,18 +77,16 @@ library "[[@TEST_NAME]]"; interface A; interface B { + // CHECK:STDERR: fail_incomplete_extend_interface.carbon:[[@LINE+7]]:24: error: `extend require` of incomplete facet type `A` [RequireImplsIncompleteFacetType] + // CHECK:STDERR: extend require impls A; + // CHECK:STDERR: ^ + // CHECK:STDERR: fail_incomplete_extend_interface.carbon:[[@LINE-6]]:1: note: interface was forward declared here [InterfaceForwardDeclaredHere] + // CHECK:STDERR: interface A; + // CHECK:STDERR: ^~~~~~~~~~~~ + // CHECK:STDERR: extend require impls A; } -// CHECK:STDERR: fail_incomplete_extend_interface.carbon:[[@LINE+7]]:1: error: definition of impl as incomplete facet type `B` [ImplAsIncompleteFacetTypeDefinition] -// CHECK:STDERR: impl () as B {} -// CHECK:STDERR: ^~~~~~~~~~~~~~ -// CHECK:STDERR: fail_incomplete_extend_interface.carbon:[[@LINE-9]]:1: note: interface was forward declared here [InterfaceForwardDeclaredHere] -// CHECK:STDERR: interface A; -// CHECK:STDERR: ^~~~~~~~~~~~ -// CHECK:STDERR: -impl () as B {} - // --- fail_incomplete_interface_in_where.carbon library "[[@TEST_NAME]]"; @@ -161,6 +175,36 @@ interface B { // CHECK:STDOUT: // CHECK:STDOUT: constraint @A; // CHECK:STDOUT: +// CHECK:STDOUT: --- fail_incomplete_extend_constraint.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %A.type: type = facet_type <@A> [concrete] +// CHECK:STDOUT: %B.type: type = facet_type <@B> [concrete] +// CHECK:STDOUT: %Self: %B.type = symbolic_binding Self, 0 [symbolic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .A = %A.decl +// CHECK:STDOUT: .B = %B.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %A.decl: type = constraint_decl @A [concrete = constants.%A.type] {} {} +// CHECK:STDOUT: %B.decl: type = interface_decl @B [concrete = constants.%B.type] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @B { +// CHECK:STDOUT: %Self: %B.type = symbolic_binding Self, 0 [symbolic = constants.%Self] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .A = +// CHECK:STDOUT: witness = () +// CHECK:STDOUT: +// CHECK:STDOUT: !requires: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: constraint @A; +// CHECK:STDOUT: // CHECK:STDOUT: --- incomplete_interface.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { @@ -232,9 +276,6 @@ interface B { // CHECK:STDOUT: %A.type: type = facet_type <@A> [concrete] // CHECK:STDOUT: %B.type: type = facet_type <@B> [concrete] // CHECK:STDOUT: %Self: %B.type = symbolic_binding Self, 0 [symbolic] -// CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 0, %Self [symbolic] -// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] -// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -244,23 +285,12 @@ interface B { // CHECK:STDOUT: } // CHECK:STDOUT: %A.decl: type = interface_decl @A [concrete = constants.%A.type] {} {} // CHECK:STDOUT: %B.decl: type = interface_decl @B [concrete = constants.%B.type] {} {} -// CHECK:STDOUT: impl_decl @empty_tuple.type.as.B.impl [concrete] {} { -// CHECK:STDOUT: %.loc16_7.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] -// CHECK:STDOUT: %.loc16_7.2: type = converted %.loc16_7.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] -// CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B.type] -// CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @A; // CHECK:STDOUT: // CHECK:STDOUT: interface @B { // CHECK:STDOUT: %Self: %B.type = symbolic_binding Self, 0 [symbolic = constants.%Self] -// CHECK:STDOUT: %B.require0.decl = require_decl @B.require0 [concrete] { -// CHECK:STDOUT: require %Self.as_type impls <@A> -// CHECK:STDOUT: } { -// CHECK:STDOUT: %Self.as_type: type = facet_access_type @B.%Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] -// CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A.type] -// CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self @@ -268,24 +298,6 @@ interface B { // CHECK:STDOUT: witness = () // CHECK:STDOUT: // CHECK:STDOUT: !requires: -// CHECK:STDOUT: @B.require0 { -// CHECK:STDOUT: require @B.require0.%Self.as_type impls <@A> -// CHECK:STDOUT: } -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: generic require @B.require0(@B.%Self: %B.type) { -// CHECK:STDOUT: %Self: %B.type = symbolic_binding Self, 0 [symbolic = %Self (constants.%Self)] -// CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 0, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: impl @empty_tuple.type.as.B.impl: %.loc16_7.2 as %B.ref { -// CHECK:STDOUT: !members: -// CHECK:STDOUT: witness = -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: specific @B.require0(constants.%Self) { -// CHECK:STDOUT: %Self => constants.%Self -// CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_incomplete_interface_in_where.carbon From b92f9b1232b64fed20769349f64c9697fced54ae Mon Sep 17 00:00:00 2001 From: danakj Date: Mon, 17 Nov 2025 15:32:52 -0500 Subject: [PATCH 07/31] no-recurse-needed --- toolchain/check/type_completion.cpp | 135 ++++++---------------------- 1 file changed, 28 insertions(+), 107 deletions(-) diff --git a/toolchain/check/type_completion.cpp b/toolchain/check/type_completion.cpp index 8ad5abdf6b927..4bb744d4cf3c8 100644 --- a/toolchain/check/type_completion.cpp +++ b/toolchain/check/type_completion.cpp @@ -72,124 +72,45 @@ static auto NoteIncompleteNamedConstraint( } } -// Makes a copy of a Specific from one generic to apply to another given -// `generic_id`, with a given `Self` argument appended to the end. -static auto MakeCopyOfSpecificAndAppendSelf( - Context& context, SemIR::LocId loc_id, SemIR::SpecificId specific_id, - SemIR::GenericId generic_id, SemIR::InstId self_id) -> SemIR::SpecificId { - auto source_specific_args_id = - context.specifics().GetArgsOrEmpty(specific_id); - auto source_specific_args = - context.inst_blocks().Get(source_specific_args_id); - - llvm::SmallVector arg_ids; - arg_ids.reserve(source_specific_args.size() + 1); - // Start with the enclosing arguments from the source Specific. - llvm::append_range(arg_ids, source_specific_args); - // Add the new `Self` argument. - arg_ids.push_back(self_id); - - return MakeSpecific(context, loc_id, generic_id, arg_ids); -} - -static auto GetRequireImplsSpecificSelf(Context& context, - const SemIR::RequireImpls& require) - -> SemIR::InstId { - const auto& require_generic = context.generics().Get(require.generic_id); - const auto& require_self_specific = - context.specifics().Get(require_generic.self_specific_id); - auto require_self_specific_args = - context.inst_blocks().Get(require_self_specific.args_id); - // The last argument of a `require` generic is always `Self`, as require can - // not have any parameters of its own, only enclosing parameters. - return require_self_specific_args.back(); -} - -static auto GetFacetTypeInSpecific(Context& context, SemIR::InstId facet_type, - SemIR::SpecificId specific_id) - -> SemIR::FacetTypeId { - auto const_facet_type = SemIR::GetConstantValueInSpecific( - context.sem_ir(), specific_id, facet_type); - auto facet_type_in_specific = context.insts().TryGetAs( - context.constant_values().GetInstId(const_facet_type)); - if (!facet_type_in_specific.has_value()) { - return SemIR::FacetTypeId::None; - } - return facet_type_in_specific->facet_type_id; -} - static auto RequireCompleteFacetType(Context& context, SemIR::LocId loc_id, const SemIR::FacetType& facet_type, MakeDiagnosticBuilderFn diagnoser) -> bool { - llvm::SmallVector work = {facet_type.facet_type_id}; - while (!work.empty()) { - auto next_facet_type_id = work.pop_back_val(); - const auto& facet_type_info = context.facet_types().Get(next_facet_type_id); + const auto& facet_type_info = + context.facet_types().Get(facet_type.facet_type_id); - for (auto extends : facet_type_info.extend_constraints) { - auto interface_id = extends.interface_id; - const auto& interface = context.interfaces().Get(interface_id); - if (!interface.is_complete()) { - if (diagnoser) { - auto builder = diagnoser(); - NoteIncompleteInterface(context, interface_id, builder); - builder.Emit(); - } - return false; - } - - for (auto require_impls_id : context.require_impls_blocks().Get( - interface.require_impls_block_id)) { - const auto& require = context.require_impls().Get(require_impls_id); - if (require.extend_self) { - auto require_specific_id = MakeCopyOfSpecificAndAppendSelf( - context, loc_id, extends.specific_id, require.generic_id, - GetRequireImplsSpecificSelf(context, require)); - auto facet_type_id = GetFacetTypeInSpecific( - context, require.facet_type_inst_id, require_specific_id); - if (facet_type_id.has_value()) { - work.push_back(facet_type_id); - } - } - } - - if (extends.specific_id.has_value()) { - ResolveSpecificDefinition(context, loc_id, extends.specific_id); + for (auto extends : facet_type_info.extend_constraints) { + auto interface_id = extends.interface_id; + const auto& interface = context.interfaces().Get(interface_id); + if (!interface.is_complete()) { + if (diagnoser) { + auto builder = diagnoser(); + NoteIncompleteInterface(context, interface_id, builder); + builder.Emit(); } + return false; } - for (auto extends : facet_type_info.extend_named_constraints) { - auto named_constraint_id = extends.named_constraint_id; - const auto& constraint = - context.named_constraints().Get(named_constraint_id); - if (!constraint.is_complete()) { - if (diagnoser) { - auto builder = diagnoser(); - NoteIncompleteNamedConstraint(context, named_constraint_id, builder); - builder.Emit(); - } - return false; - } + if (extends.specific_id.has_value()) { + ResolveSpecificDefinition(context, loc_id, extends.specific_id); + } + } - for (auto require_impls_id : context.require_impls_blocks().Get( - constraint.require_impls_block_id)) { - const auto& require = context.require_impls().Get(require_impls_id); - if (require.extend_self) { - auto require_specific_id = MakeCopyOfSpecificAndAppendSelf( - context, loc_id, extends.specific_id, require.generic_id, - GetRequireImplsSpecificSelf(context, require)); - auto facet_type_id = GetFacetTypeInSpecific( - context, require.facet_type_inst_id, require_specific_id); - if (facet_type_id.has_value()) { - work.push_back(facet_type_id); - } - } + for (auto extends : facet_type_info.extend_named_constraints) { + auto named_constraint_id = extends.named_constraint_id; + const auto& constraint = + context.named_constraints().Get(named_constraint_id); + if (!constraint.is_complete()) { + if (diagnoser) { + auto builder = diagnoser(); + NoteIncompleteNamedConstraint(context, named_constraint_id, builder); + builder.Emit(); } + return false; + } - if (extends.specific_id.has_value()) { - ResolveSpecificDefinition(context, loc_id, extends.specific_id); - } + if (extends.specific_id.has_value()) { + ResolveSpecificDefinition(context, loc_id, extends.specific_id); } } From 82195862a7354685990f124543147eb8916fb2d0 Mon Sep 17 00:00:00 2001 From: danakj Date: Tue, 18 Nov 2025 18:06:55 -0500 Subject: [PATCH 08/31] recurse-on-facets --- .../generic/extend_type_completion.carbon | 22 +++ toolchain/check/type_completion.cpp | 141 ++++++++++++++---- toolchain/sem_ir/require_impls.h | 2 + 3 files changed, 137 insertions(+), 28 deletions(-) diff --git a/toolchain/check/testdata/generic/extend_type_completion.carbon b/toolchain/check/testdata/generic/extend_type_completion.carbon index d2710e465ed18..63ea4ab2df7e2 100644 --- a/toolchain/check/testdata/generic/extend_type_completion.carbon +++ b/toolchain/check/testdata/generic/extend_type_completion.carbon @@ -103,3 +103,25 @@ constraint J(N:! i32) { // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: var v: J(-1); + +// --- interface_require_impls_doesnt_need_complete_self.carbon +library "[[@TEST_NAME]]"; + +interface K {} +interface J(N:! i32) { + require array(Self, N) impls K; +} + +// J does not extend K so the type of Self impling K is not completed. No error. +var v: J(-1); + +// --- constraint_require_impls_doesnt_need_complete_self.carbon +library "[[@TEST_NAME]]"; + +interface K {} +constraint J(N:! i32) { + require array(Self, N) impls K; +} + +// J does not extend K so the type of Self impling K is not completed. No error. +var v: J(-1); diff --git a/toolchain/check/type_completion.cpp b/toolchain/check/type_completion.cpp index 4bb744d4cf3c8..fdd0049581fd8 100644 --- a/toolchain/check/type_completion.cpp +++ b/toolchain/check/type_completion.cpp @@ -72,45 +72,130 @@ static auto NoteIncompleteNamedConstraint( } } +// Makes a copy of a Specific from one generic to apply to another given +// `generic_id`, with a given `Self` argument appended to the end. +static auto MakeCopyOfSpecificAndAppendSelf( + Context& context, SemIR::LocId loc_id, SemIR::SpecificId specific_id, + SemIR::GenericId generic_id, SemIR::InstId self_id) -> SemIR::SpecificId { + auto source_specific_args_id = + context.specifics().GetArgsOrEmpty(specific_id); + auto source_specific_args = + context.inst_blocks().Get(source_specific_args_id); + + llvm::SmallVector arg_ids; + arg_ids.reserve(source_specific_args.size() + 1); + // Start with the enclosing arguments from the source Specific. + llvm::append_range(arg_ids, source_specific_args); + // Add the new `Self` argument. + arg_ids.push_back(self_id); + + return MakeSpecific(context, loc_id, generic_id, arg_ids); +} + +static auto GetRequireImplsSpecificSelf(Context& context, + const SemIR::RequireImpls& require) + -> SemIR::InstId { + const auto& require_generic = context.generics().Get(require.generic_id); + const auto& require_self_specific = + context.specifics().Get(require_generic.self_specific_id); + auto require_self_specific_args = + context.inst_blocks().Get(require_self_specific.args_id); + // The last argument of a `require` generic is always `Self`, as require can + // not have any parameters of its own, only enclosing parameters. + return require_self_specific_args.back(); +} + +static auto GetFacetTypeInSpecific(Context& context, + SemIR::SpecificId specific_id, + SemIR::InstId facet_type) + -> SemIR::FacetTypeId { + auto const_facet_type = SemIR::GetConstantValueInSpecific( + context.sem_ir(), specific_id, facet_type); + if (const_facet_type == SemIR::ErrorInst::ConstantId) { + return SemIR::FacetTypeId::None; + } + auto facet_type_in_specific = context.insts().GetAs( + context.constant_values().GetInstId(const_facet_type)); + return facet_type_in_specific.facet_type_id; +} + static auto RequireCompleteFacetType(Context& context, SemIR::LocId loc_id, const SemIR::FacetType& facet_type, MakeDiagnosticBuilderFn diagnoser) -> bool { - const auto& facet_type_info = - context.facet_types().Get(facet_type.facet_type_id); + llvm::SmallVector work = {facet_type.facet_type_id}; + while (!work.empty()) { + auto next_facet_type_id = work.pop_back_val(); + const auto& facet_type_info = context.facet_types().Get(next_facet_type_id); - for (auto extends : facet_type_info.extend_constraints) { - auto interface_id = extends.interface_id; - const auto& interface = context.interfaces().Get(interface_id); - if (!interface.is_complete()) { - if (diagnoser) { - auto builder = diagnoser(); - NoteIncompleteInterface(context, interface_id, builder); - builder.Emit(); + struct SpecificForRequires { + SemIR::SpecificId specific_id; + SemIR::RequireImplsBlockId requires_block_id; + }; + llvm::SmallVector specifics; + + for (auto extends : facet_type_info.extend_constraints) { + auto interface_id = extends.interface_id; + const auto& interface = context.interfaces().Get(interface_id); + if (!interface.is_complete()) { + if (diagnoser) { + auto builder = diagnoser(); + NoteIncompleteInterface(context, interface_id, builder); + builder.Emit(); + } + return false; + } + if (interface.generic_id.has_value()) { + specifics.push_back( + {extends.specific_id, interface.require_impls_block_id}); + } + if (extends.specific_id.has_value()) { + ResolveSpecificDefinition(context, loc_id, extends.specific_id); } - return false; - } - - if (extends.specific_id.has_value()) { - ResolveSpecificDefinition(context, loc_id, extends.specific_id); } - } - for (auto extends : facet_type_info.extend_named_constraints) { - auto named_constraint_id = extends.named_constraint_id; - const auto& constraint = - context.named_constraints().Get(named_constraint_id); - if (!constraint.is_complete()) { - if (diagnoser) { - auto builder = diagnoser(); - NoteIncompleteNamedConstraint(context, named_constraint_id, builder); - builder.Emit(); + for (auto extends : facet_type_info.extend_named_constraints) { + auto named_constraint_id = extends.named_constraint_id; + const auto& constraint = + context.named_constraints().Get(named_constraint_id); + if (!constraint.is_complete()) { + if (diagnoser) { + auto builder = diagnoser(); + NoteIncompleteNamedConstraint(context, named_constraint_id, builder); + builder.Emit(); + } + return false; + } + if (constraint.generic_id.has_value()) { + specifics.push_back( + {extends.specific_id, constraint.require_impls_block_id}); + } + if (extends.specific_id.has_value()) { + ResolveSpecificDefinition(context, loc_id, extends.specific_id); } - return false; } - if (extends.specific_id.has_value()) { - ResolveSpecificDefinition(context, loc_id, extends.specific_id); + // Formulate a specific for each `require` declaration, as the specific may + // introduce errors. Then recurse on those specific facet types to find + // other interfaces/constraints that may contain other require declarations. + for (auto [specific_id, requires_block_id] : specifics) { + for (auto require_impls_id : + context.require_impls_blocks().Get(requires_block_id)) { + const auto& require = context.require_impls().Get(require_impls_id); + if (require.extend_self) { + auto require_specific_id = MakeCopyOfSpecificAndAppendSelf( + context, loc_id, specific_id, require.generic_id, + GetRequireImplsSpecificSelf(context, require)); + // Construct the specific facet type. If None is returned, it means an + // error was diagnosed. + auto facet_type_id = GetFacetTypeInSpecific( + context, require_specific_id, require.facet_type_inst_id); + if (!facet_type_id.has_value()) { + return false; + } + work.push_back(facet_type_id); + } + } } } diff --git a/toolchain/sem_ir/require_impls.h b/toolchain/sem_ir/require_impls.h index cb95f491b0883..6b526ce3bbcd2 100644 --- a/toolchain/sem_ir/require_impls.h +++ b/toolchain/sem_ir/require_impls.h @@ -23,6 +23,8 @@ struct RequireImpls : Printable { // Evaluates to the `FacetType` that the self-type must implement. TypeInstId facet_type_inst_id; // The `FacetTypeInfo` derived from the `facet_type_inst_id` instruction. + // TODO: Remove this, we need to use the inst to get a constant value in the + // appropriate specific. FacetTypeId facet_type_id; // If the facet type extends `Self`. When true, the `self_id` will be `Self`. bool extend_self; From c183562581484dd422b7a3f695174c0f7739388c Mon Sep 17 00:00:00 2001 From: danakj Date: Tue, 18 Nov 2025 18:10:45 -0500 Subject: [PATCH 09/31] autoupdate --- .../check/testdata/impl/import_generic.carbon | 81 ++++++++++++++++--- 1 file changed, 69 insertions(+), 12 deletions(-) diff --git a/toolchain/check/testdata/impl/import_generic.carbon b/toolchain/check/testdata/impl/import_generic.carbon index 41faf2bc9654b..77f7530eba245 100644 --- a/toolchain/check/testdata/impl/import_generic.carbon +++ b/toolchain/check/testdata/impl/import_generic.carbon @@ -295,6 +295,7 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %Self: @N.require0.%N.type (%N.type.b8d) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.aa1)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %I.type.loc15_27.2: type = facet_type <@I, @I(%T)> [symbolic = %I.type.loc15_27.2 (constants.%I.type.070)] +// CHECK:STDOUT: %require_complete: = require_complete_type %I.type.loc15_27.2 [symbolic = %require_complete (constants.%require_complete.c94)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @C.as.I.impl.f3e(%T.loc8_14.1: type) { @@ -374,6 +375,7 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %Self => constants.%Self.aa1 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %I.type.loc15_27.2 => constants.%I.type.070 +// CHECK:STDOUT: %require_complete => constants.%require_complete.c94 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_import_generic.impl.carbon @@ -549,6 +551,7 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %Self: @N.require0.%N.type (%N.type.b8d23b.1) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.aa1)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T)> [symbolic = %I.type (constants.%I.type.070)] +// CHECK:STDOUT: %require_complete: = require_complete_type %I.type [symbolic = %require_complete (constants.%require_complete.c94)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @C.as.I.impl.f3ed6b.1(imports.%Main.import_ref.efcd44.2: type) [from "import_generic.carbon"] { @@ -706,6 +709,7 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %Self => constants.%Self.aa1 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %I.type => constants.%I.type.070 +// CHECK:STDOUT: %require_complete => constants.%require_complete.c94 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.I.impl.3f8440.1(constants.%T) { @@ -835,6 +839,7 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %Self: @N.require0.%N.type (%N.type.b8d) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.aa1)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %I.type.loc10_27.2: type = facet_type <@I, @I(%T)> [symbolic = %I.type.loc10_27.2 (constants.%I.type.070)] +// CHECK:STDOUT: %require_complete: = require_complete_type %I.type.loc10_27.2 [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @C.as.I.impl(%T.loc7_14.1: type) { @@ -884,6 +889,7 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %Self => constants.%Self.aa1 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %I.type.loc10_27.2 => constants.%I.type.070 +// CHECK:STDOUT: %require_complete => constants.%require_complete // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- import_generic_with_different_specific.impl.carbon @@ -910,10 +916,13 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %N.type.673: type = generic_named_constaint_type @N [concrete] // CHECK:STDOUT: %empty_struct: %N.type.673 = struct_value () [concrete] // CHECK:STDOUT: %N.type.b8d23b.1: type = facet_type <@N, @N(%T)> [symbolic] -// CHECK:STDOUT: %Self.aa1: %N.type.b8d23b.1 = symbolic_binding Self, 1 [symbolic] -// CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.aa1 [symbolic] +// CHECK:STDOUT: %Self.aa1546.1: %N.type.b8d23b.1 = symbolic_binding Self, 1 [symbolic] +// CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.aa1546.1 [symbolic] // CHECK:STDOUT: %ptr.3f2: type = ptr_type %ptr.4f0 [symbolic] // CHECK:STDOUT: %N.type.b8d23b.2: type = facet_type <@N, @N(%ptr.3f2)> [symbolic] +// CHECK:STDOUT: %Self.aa1546.2: %N.type.b8d23b.2 = symbolic_binding Self, 1 [symbolic] +// CHECK:STDOUT: %I.type.71a: type = facet_type <@I, @I(%ptr.3f2)> [symbolic] +// CHECK:STDOUT: %require_complete.08c: = require_complete_type %I.type.71a [symbolic] // CHECK:STDOUT: %require_complete.a37: = require_complete_type %N.type.b8d23b.2 [symbolic] // CHECK:STDOUT: %I.impl_witness.524: = impl_witness file.%I.impl_witness_table.loc6, @C.as.I.impl.3f8(%T) [symbolic] // CHECK:STDOUT: } @@ -996,7 +1005,7 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %N.type: type = facet_type <@N, @N(%T)> [symbolic = %N.type (constants.%N.type.b8d23b.1)] -// CHECK:STDOUT: %Self: @N.%N.type (%N.type.b8d23b.1) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.aa1)] +// CHECK:STDOUT: %Self: @N.%N.type (%N.type.b8d23b.1) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.aa1546.1)] // CHECK:STDOUT: // CHECK:STDOUT: constraint { // CHECK:STDOUT: !members: @@ -1012,9 +1021,10 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: generic require @N.require0(imports.%Main.import_ref.efcd44.3: type, imports.%Main.import_ref.d4d: @N.%N.type (%N.type.b8d23b.1)) [from "import_generic_with_different_specific.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %N.type: type = facet_type <@N, @N(%T)> [symbolic = %N.type (constants.%N.type.b8d23b.1)] -// CHECK:STDOUT: %Self: @N.require0.%N.type (%N.type.b8d23b.1) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.aa1)] +// CHECK:STDOUT: %Self: @N.require0.%N.type (%N.type.b8d23b.1) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.aa1546.1)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T)> [symbolic = %I.type (constants.%I.type.070)] +// CHECK:STDOUT: %require_complete: = require_complete_type %I.type [symbolic = %require_complete (constants.%require_complete.c94)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @C.as.I.impl.f3e(imports.%Main.import_ref.efcd44.2: type) [from "import_generic_with_different_specific.carbon"] { @@ -1104,16 +1114,21 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: specific @N.require0(constants.%T, constants.%Self.aa1) { +// CHECK:STDOUT: specific @N.require0(constants.%T, constants.%Self.aa1546.1) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %N.type => constants.%N.type.b8d23b.1 -// CHECK:STDOUT: %Self => constants.%Self.aa1 +// CHECK:STDOUT: %Self => constants.%Self.aa1546.1 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %I.type => constants.%I.type.070 +// CHECK:STDOUT: %require_complete => constants.%require_complete.c94 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N(constants.%ptr.3f2) { // CHECK:STDOUT: %T => constants.%ptr.3f2 +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %N.type => constants.%N.type.b8d23b.2 +// CHECK:STDOUT: %Self => constants.%Self.aa1546.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.I.impl.3f8(constants.%T) { @@ -1125,6 +1140,19 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %I.impl_witness => constants.%I.impl_witness.524 // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: specific @N.require0(constants.%ptr.3f2, constants.%Self.aa1546.1) { +// CHECK:STDOUT: %T => constants.%ptr.3f2 +// CHECK:STDOUT: %N.type => constants.%N.type.b8d23b.2 +// CHECK:STDOUT: %Self => constants.%Self.aa1546.1 +// CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type +// CHECK:STDOUT: %I.type => constants.%I.type.71a +// CHECK:STDOUT: %require_complete => constants.%require_complete.08c +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @I(constants.%ptr.3f2) { +// CHECK:STDOUT: %T => constants.%ptr.3f2 +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: --- fail_import_generic_decl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { @@ -1144,6 +1172,7 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %N.type.b8d: type = facet_type <@N, @N(%T)> [symbolic] // CHECK:STDOUT: %Self.aa1: %N.type.b8d = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.aa1 [symbolic] +// CHECK:STDOUT: %require_complete: = require_complete_type %J.type.8ec [symbolic] // CHECK:STDOUT: %J.impl_witness.224: = impl_witness file.%J.impl_witness_table.loc15, @D.as.J.impl.b47(%T) [symbolic] // CHECK:STDOUT: %ptr: type = ptr_type %T [symbolic] // CHECK:STDOUT: %J.type.4fa: type = facet_type <@J, @J(%ptr)> [symbolic] @@ -1250,6 +1279,7 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %Self: @N.require0.%N.type (%N.type.b8d) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.aa1)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %J.type.loc8_27.2: type = facet_type <@J, @J(%T)> [symbolic = %J.type.loc8_27.2 (constants.%J.type.8ec)] +// CHECK:STDOUT: %require_complete: = require_complete_type %J.type.loc8_27.2 [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @D.as.J.impl.b47(%T.loc15_14.1: type) { @@ -1279,6 +1309,10 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @J(constants.%T) { // CHECK:STDOUT: %T.loc5_13.1 => constants.%T +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %J.type => constants.%J.type.8ec +// CHECK:STDOUT: %Self.loc5_23.2 => constants.%Self.f68 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N(constants.%T) { @@ -1291,6 +1325,7 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %Self => constants.%Self.aa1 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %J.type.loc8_27.2 => constants.%J.type.8ec +// CHECK:STDOUT: %require_complete => constants.%require_complete // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @D.as.J.impl.b47(constants.%T) { @@ -1331,11 +1366,14 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %N.type.673: type = generic_named_constaint_type @N [concrete] // CHECK:STDOUT: %empty_struct: %N.type.673 = struct_value () [concrete] // CHECK:STDOUT: %N.type.b8d23b.1: type = facet_type <@N, @N(%T)> [symbolic] -// CHECK:STDOUT: %Self.aa1: %N.type.b8d23b.1 = symbolic_binding Self, 1 [symbolic] -// CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.aa1 [symbolic] +// CHECK:STDOUT: %Self.aa1546.1: %N.type.b8d23b.1 = symbolic_binding Self, 1 [symbolic] +// CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.aa1546.1 [symbolic] +// CHECK:STDOUT: %require_complete.387: = require_complete_type %J.type.8ec [symbolic] // CHECK:STDOUT: %require_complete.a37d6c.1: = require_complete_type %N.type.b8d23b.1 [symbolic] // CHECK:STDOUT: %J.impl_witness.b5245e.1: = impl_witness file.%J.impl_witness_table.loc29, @D.as.J.impl.3b0484.1(%T) [symbolic] // CHECK:STDOUT: %N.type.b8d23b.2: type = facet_type <@N, @N(%ptr)> [symbolic] +// CHECK:STDOUT: %Self.aa1546.2: %N.type.b8d23b.2 = symbolic_binding Self, 1 [symbolic] +// CHECK:STDOUT: %require_complete.d4d: = require_complete_type %J.type.4fa [symbolic] // CHECK:STDOUT: %require_complete.a37d6c.2: = require_complete_type %N.type.b8d23b.2 [symbolic] // CHECK:STDOUT: %J.impl_witness.b5245e.2: = impl_witness file.%J.impl_witness_table.loc39, @D.as.J.impl.3b0484.2(%T) [symbolic] // CHECK:STDOUT: } @@ -1461,7 +1499,7 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %N.type: type = facet_type <@N, @N(%T)> [symbolic = %N.type (constants.%N.type.b8d23b.1)] -// CHECK:STDOUT: %Self: @N.%N.type (%N.type.b8d23b.1) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.aa1)] +// CHECK:STDOUT: %Self: @N.%N.type (%N.type.b8d23b.1) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.aa1546.1)] // CHECK:STDOUT: // CHECK:STDOUT: constraint { // CHECK:STDOUT: !members: @@ -1477,9 +1515,10 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: generic require @N.require0(imports.%Main.import_ref.efcd44.4: type, imports.%Main.import_ref.d4d: @N.%N.type (%N.type.b8d23b.1)) [from "fail_import_generic_decl.carbon"] { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %N.type: type = facet_type <@N, @N(%T)> [symbolic = %N.type (constants.%N.type.b8d23b.1)] -// CHECK:STDOUT: %Self: @N.require0.%N.type (%N.type.b8d23b.1) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.aa1)] +// CHECK:STDOUT: %Self: @N.require0.%N.type (%N.type.b8d23b.1) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.aa1546.1)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %J.type: type = facet_type <@J, @J(%T)> [symbolic = %J.type (constants.%J.type.8ec)] +// CHECK:STDOUT: %require_complete: = require_complete_type %J.type [symbolic = %require_complete (constants.%require_complete.387)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @D.as.J.impl.b470bf.1(imports.%Main.import_ref.efcd44.2: type) [from "fail_import_generic_decl.carbon"] { @@ -1624,14 +1663,19 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @N(constants.%T) { // CHECK:STDOUT: %T => constants.%T +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %N.type => constants.%N.type.b8d23b.1 +// CHECK:STDOUT: %Self => constants.%Self.aa1546.1 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: specific @N.require0(constants.%T, constants.%Self.aa1) { +// CHECK:STDOUT: specific @N.require0(constants.%T, constants.%Self.aa1546.1) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %N.type => constants.%N.type.b8d23b.1 -// CHECK:STDOUT: %Self => constants.%Self.aa1 +// CHECK:STDOUT: %Self => constants.%Self.aa1546.1 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %J.type => constants.%J.type.8ec +// CHECK:STDOUT: %require_complete => constants.%require_complete.387 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @D.as.J.impl.3b0484.1(constants.%T) { @@ -1643,6 +1687,10 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @N(constants.%ptr) { // CHECK:STDOUT: %T => constants.%ptr +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %N.type => constants.%N.type.b8d23b.2 +// CHECK:STDOUT: %Self => constants.%Self.aa1546.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @D.as.J.impl.3b0484.2(constants.%T) { @@ -1653,3 +1701,12 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %J.impl_witness => constants.%J.impl_witness.b5245e.2 // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: specific @N.require0(constants.%ptr, constants.%Self.aa1546.1) { +// CHECK:STDOUT: %T => constants.%ptr +// CHECK:STDOUT: %N.type => constants.%N.type.b8d23b.2 +// CHECK:STDOUT: %Self => constants.%Self.aa1546.1 +// CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type +// CHECK:STDOUT: %J.type => constants.%J.type.4fa +// CHECK:STDOUT: %require_complete => constants.%require_complete.d4d +// CHECK:STDOUT: } +// CHECK:STDOUT: From 97c2a3ccd844d0e32350cc547a382fd7b8e39faa Mon Sep 17 00:00:00 2001 From: danakj Date: Wed, 19 Nov 2025 09:24:33 -0500 Subject: [PATCH 10/31] comment --- toolchain/check/type_completion.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/toolchain/check/type_completion.cpp b/toolchain/check/type_completion.cpp index fdd0049581fd8..3851d917a6938 100644 --- a/toolchain/check/type_completion.cpp +++ b/toolchain/check/type_completion.cpp @@ -178,6 +178,10 @@ static auto RequireCompleteFacetType(Context& context, SemIR::LocId loc_id, // Formulate a specific for each `require` declaration, as the specific may // introduce errors. Then recurse on those specific facet types to find // other interfaces/constraints that may contain other require declarations. + // We only formulate a specific for the facet type in the `require` + // declaration since `extend require` can only be applied to `Self`, so the + // self-type in these `require` declarations never uses any generic + // parameters. for (auto [specific_id, requires_block_id] : specifics) { for (auto require_impls_id : context.require_impls_blocks().Get(requires_block_id)) { From bb93fd50eb4611f9692a33ac9d9b10c15e672042 Mon Sep 17 00:00:00 2001 From: danakj Date: Wed, 19 Nov 2025 10:16:32 -0500 Subject: [PATCH 11/31] rewrites-complete --- toolchain/check/facet_type.cpp | 13 +- .../impl/fail_undefined_interface.carbon | 178 ++++++++++++++++++ toolchain/diagnostics/diagnostic_kind.def | 1 - 3 files changed, 185 insertions(+), 7 deletions(-) diff --git a/toolchain/check/facet_type.cpp b/toolchain/check/facet_type.cpp index 73dd7ebc39265..02455cfe65aab 100644 --- a/toolchain/check/facet_type.cpp +++ b/toolchain/check/facet_type.cpp @@ -62,12 +62,10 @@ static auto IncompleteFacetTypeDiagnosticBuilder( return context.emitter().Build(loc_id, ImplAsIncompleteFacetTypeDefinition, facet_type_inst_id); } else { - CARBON_DIAGNOSTIC( - ImplAsIncompleteFacetTypeRewrites, Error, - "declaration of impl as incomplete facet type {0} with rewrites", - InstIdAsType); - return context.emitter().Build(loc_id, ImplAsIncompleteFacetTypeRewrites, - facet_type_inst_id); + context.TODO(loc_id, + "declaration of impl as incomplete interface with a rewrite " + "constraint"); + return context.emitter().BuildSuppressed(); } } @@ -105,6 +103,9 @@ auto InitialFacetTypeImplWitness( .specific_id = self_specific_id}); } + // The presence of any rewrite constraints requires that we know how many + // entries to allocate in the witness table, which requires the entire facet + // type to be complete, even if this was a declaration. if (!RequireCompleteType( context, facet_type_id, SemIR::LocId(facet_type_inst_id), [&] { return IncompleteFacetTypeDiagnosticBuilder( diff --git a/toolchain/check/testdata/impl/fail_undefined_interface.carbon b/toolchain/check/testdata/impl/fail_undefined_interface.carbon index 3e0c212a4b12d..2688fd398d1d5 100644 --- a/toolchain/check/testdata/impl/fail_undefined_interface.carbon +++ b/toolchain/check/testdata/impl/fail_undefined_interface.carbon @@ -41,6 +41,58 @@ class C {} // CHECK:STDERR: impl C as J {} +// --- fail_class_with_rewrite.carbon +library "[[@TEST_NAME]]"; + +interface J; +interface K { let X:! type; } +class C {} + +// CHECK:STDERR: fail_class_with_rewrite.carbon:[[@LINE+7]]:19: error: member access into object of incomplete type `J` [IncompleteTypeInMemberAccess] +// CHECK:STDERR: impl C as J where .X = (); +// CHECK:STDERR: ^~ +// CHECK:STDERR: fail_class_with_rewrite.carbon:[[@LINE-7]]:1: note: interface was forward declared here [InterfaceForwardDeclaredHere] +// CHECK:STDERR: interface J; +// CHECK:STDERR: ^~~~~~~~~~~~ +// CHECK:STDERR: +impl C as J where .X = (); + +// CHECK:STDERR: fail_class_with_rewrite.carbon:[[@LINE+7]]:19: error: member access into object of incomplete type `J` [IncompleteTypeInMemberAccess] +// CHECK:STDERR: impl C as J where .X = () {} +// CHECK:STDERR: ^~ +// CHECK:STDERR: fail_class_with_rewrite.carbon:[[@LINE-16]]:1: note: interface was forward declared here [InterfaceForwardDeclaredHere] +// CHECK:STDERR: interface J; +// CHECK:STDERR: ^~~~~~~~~~~~ +// CHECK:STDERR: +impl C as J where .X = () {} + +// --- fail_class_with_qualified_rewrite.carbon +library "[[@TEST_NAME]]"; + +interface J; +interface K { let X:! type; } +class C {} + +// TODO: The failure here should be that J is incomplete, once the rewrite of +// `.(K.X)` works. Since any rewrite in the decl requires us to know the size of +// the witness table which requires all interfaces to be complete. +// +// CHECK:STDERR: fail_class_with_qualified_rewrite.carbon:[[@LINE+8]]:38: error: expected identifier or `Self` after `.` [ExpectedIdentifierOrSelfAfterPeriod] +// CHECK:STDERR: impl C as J where .Self impls K and .(K.X) = (); +// CHECK:STDERR: ^ +// CHECK:STDERR: +// CHECK:STDERR: fail_class_with_qualified_rewrite.carbon:[[@LINE+4]]:38: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] +// CHECK:STDERR: impl C as J where .Self impls K and .(K.X) = (); +// CHECK:STDERR: ^ +// CHECK:STDERR: +impl C as J where .Self impls K and .(K.X) = (); + +// CHECK:STDERR: fail_class_with_qualified_rewrite.carbon:[[@LINE+4]]:38: error: expected identifier or `Self` after `.` [ExpectedIdentifierOrSelfAfterPeriod] +// CHECK:STDERR: impl C as J where .Self impls K and .(K.X) = () {} +// CHECK:STDERR: ^ +// CHECK:STDERR: +impl C as J where .Self impls K and .(K.X) = () {} + // --- incomplete_where.carbon library "[[@TEST_NAME]]"; @@ -146,6 +198,132 @@ impl C as I where .Self impls Incomplete { // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: --- fail_class_with_rewrite.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %J.type: type = facet_type <@J> [concrete] +// CHECK:STDOUT: %K.type: type = facet_type <@K> [concrete] +// CHECK:STDOUT: %Self: %K.type = symbolic_binding Self, 0 [symbolic] +// CHECK:STDOUT: %K.assoc_type: type = assoc_entity_type @K [concrete] +// CHECK:STDOUT: %assoc0: %K.assoc_type = assoc_entity element0, @K.%X [concrete] +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: %.Self: %J.type = symbolic_binding .Self [symbolic_self] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .J = %J.decl +// CHECK:STDOUT: .K = %K.decl +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %J.decl: type = interface_decl @J [concrete = constants.%J.type] {} {} +// CHECK:STDOUT: %K.decl: type = interface_decl @K [concrete = constants.%K.type] {} {} +// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} +// CHECK:STDOUT: impl_decl @C.as..impl [concrete] {} { +// CHECK:STDOUT: %C.ref.loc14: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %J.ref.loc14: type = name_ref J, file.%J.decl [concrete = constants.%J.type] +// CHECK:STDOUT: %.Self.2: %J.type = symbolic_binding .Self [symbolic_self = constants.%.Self] +// CHECK:STDOUT: %.Self.ref.loc14: %J.type = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self] +// CHECK:STDOUT: %.loc14_25: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] +// CHECK:STDOUT: %.loc14_13: type = where_expr %.Self.2 [concrete = ] { +// CHECK:STDOUT: requirement_base_facet_type constants.%J.type +// CHECK:STDOUT: requirement_rewrite , +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: impl_decl @C.as..impl [concrete] {} { +// CHECK:STDOUT: %C.ref.loc16: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %J.ref.loc16: type = name_ref J, file.%J.decl [concrete = constants.%J.type] +// CHECK:STDOUT: %.Self.1: %J.type = symbolic_binding .Self [symbolic_self = constants.%.Self] +// CHECK:STDOUT: %.Self.ref.loc16: %J.type = name_ref .Self, %.Self.1 [symbolic_self = constants.%.Self] +// CHECK:STDOUT: %.loc16_25: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] +// CHECK:STDOUT: %.loc16_13: type = where_expr %.Self.1 [concrete = ] { +// CHECK:STDOUT: requirement_base_facet_type constants.%J.type +// CHECK:STDOUT: requirement_rewrite , +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @J; +// CHECK:STDOUT: +// CHECK:STDOUT: interface @K { +// CHECK:STDOUT: %Self: %K.type = symbolic_binding Self, 0 [symbolic = constants.%Self] +// CHECK:STDOUT: %X: type = assoc_const_decl @X [concrete] { +// CHECK:STDOUT: %assoc0: %K.assoc_type = assoc_entity element0, @K.%X [concrete = constants.%assoc0] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .X = @X.%assoc0 +// CHECK:STDOUT: witness = (%X) +// CHECK:STDOUT: +// CHECK:STDOUT: !requires: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic assoc_const @X(@K.%Self: %K.type) { +// CHECK:STDOUT: assoc_const X:! type; +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: impl @C.as..impl: %C.ref.loc14 as %.loc14_13 { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: witness = +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @X(constants.%Self) {} +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_class_with_qualified_rewrite.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %K.type: type = facet_type <@K> [concrete] +// CHECK:STDOUT: %Self: %K.type = symbolic_binding Self, 0 [symbolic] +// CHECK:STDOUT: %K.assoc_type: type = assoc_entity_type @K [concrete] +// CHECK:STDOUT: %assoc0: %K.assoc_type = assoc_entity element0, @K.%X [concrete] +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @J; +// CHECK:STDOUT: +// CHECK:STDOUT: interface @K { +// CHECK:STDOUT: %Self: %K.type = symbolic_binding Self, 0 [symbolic = constants.%Self] +// CHECK:STDOUT: %X: type = assoc_const_decl @X [concrete] { +// CHECK:STDOUT: %assoc0: %K.assoc_type = assoc_entity element0, @K.%X [concrete = constants.%assoc0] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .X = @X.%assoc0 +// CHECK:STDOUT: witness = (%X) +// CHECK:STDOUT: +// CHECK:STDOUT: !requires: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic assoc_const @X(@K.%Self: %K.type) { +// CHECK:STDOUT: assoc_const X:! type; +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @X(constants.%Self) {} +// CHECK:STDOUT: // CHECK:STDOUT: --- incomplete_where.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 2ee339f3a9df3..377d24857c305 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -328,7 +328,6 @@ CARBON_DIAGNOSTIC_KIND(ExtendImplSelfAs) CARBON_DIAGNOSTIC_KIND(ExtendImplSelfAsDefault) CARBON_DIAGNOSTIC_KIND(ImplAccessMemberBeforeSet) CARBON_DIAGNOSTIC_KIND(ImplAsIncompleteFacetTypeDefinition) -CARBON_DIAGNOSTIC_KIND(ImplAsIncompleteFacetTypeRewrites) CARBON_DIAGNOSTIC_KIND(ImplAsNonFacetType) CARBON_DIAGNOSTIC_KIND(ImplAsOutsideClass) CARBON_DIAGNOSTIC_KIND(ImplAssociatedConstantNeedsValue) From 92e0bc1ac51e0f86544d4474c054f9d7720cb12c Mon Sep 17 00:00:00 2001 From: danakj Date: Wed, 19 Nov 2025 10:37:37 -0500 Subject: [PATCH 12/31] autoupdate --- .../testdata/impl/fail_undefined_interface.carbon | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/toolchain/check/testdata/impl/fail_undefined_interface.carbon b/toolchain/check/testdata/impl/fail_undefined_interface.carbon index 2688fd398d1d5..63304ada6e52f 100644 --- a/toolchain/check/testdata/impl/fail_undefined_interface.carbon +++ b/toolchain/check/testdata/impl/fail_undefined_interface.carbon @@ -235,12 +235,12 @@ impl C as I where .Self impls Incomplete { // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @C.as..impl [concrete] {} { -// CHECK:STDOUT: %C.ref.loc16: type = name_ref C, file.%C.decl [concrete = constants.%C] -// CHECK:STDOUT: %J.ref.loc16: type = name_ref J, file.%J.decl [concrete = constants.%J.type] +// CHECK:STDOUT: %C.ref.loc23: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %J.ref.loc23: type = name_ref J, file.%J.decl [concrete = constants.%J.type] // CHECK:STDOUT: %.Self.1: %J.type = symbolic_binding .Self [symbolic_self = constants.%.Self] -// CHECK:STDOUT: %.Self.ref.loc16: %J.type = name_ref .Self, %.Self.1 [symbolic_self = constants.%.Self] -// CHECK:STDOUT: %.loc16_25: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] -// CHECK:STDOUT: %.loc16_13: type = where_expr %.Self.1 [concrete = ] { +// CHECK:STDOUT: %.Self.ref.loc23: %J.type = name_ref .Self, %.Self.1 [symbolic_self = constants.%.Self] +// CHECK:STDOUT: %.loc23_25: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] +// CHECK:STDOUT: %.loc23_13: type = where_expr %.Self.1 [concrete = ] { // CHECK:STDOUT: requirement_base_facet_type constants.%J.type // CHECK:STDOUT: requirement_rewrite , // CHECK:STDOUT: } From 47b1728d3ce6ccf14bb27e3ed32550ed8132636e Mon Sep 17 00:00:00 2001 From: danakj Date: Wed, 19 Nov 2025 11:23:32 -0500 Subject: [PATCH 13/31] avoid-uaf --- toolchain/check/impl_lookup.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/toolchain/check/impl_lookup.cpp b/toolchain/check/impl_lookup.cpp index d1398828271d6..9ed8863a9febb 100644 --- a/toolchain/check/impl_lookup.cpp +++ b/toolchain/check/impl_lookup.cpp @@ -184,7 +184,7 @@ static auto FindAndDiagnoseImplLookupCycle( } struct InterfacesFromConstantId { - llvm::ArrayRef interfaces; + llvm::SmallVector interfaces; SemIR::BuiltinConstraintMask builtin_constraint_mask; bool other_requirements; }; @@ -211,9 +211,14 @@ static auto GetInterfacesFromConstantId( if (!identified_id.has_value()) { return std::nullopt; } - return {{.interfaces = context.identified_facet_types() - .Get(identified_id) - .required_interfaces(), + // TODO: IdentifiedFacetTypes are held in a RelationalValueStore which does + // not protect against UAF through reallocation, so we must copy data out of + // it. + llvm::SmallVector interfaces( + context.identified_facet_types() + .Get(identified_id) + .required_interfaces()); + return {{.interfaces = std::move(interfaces), .builtin_constraint_mask = facet_type_info.builtin_constraint_mask, .other_requirements = facet_type_info.other_requirements}}; } From c312a16f69bcc9841e6f6b5d0595fe2dc400c313 Mon Sep 17 00:00:00 2001 From: danakj Date: Wed, 19 Nov 2025 14:57:19 -0500 Subject: [PATCH 14/31] test-and-fix-multi-level-specific-resolution --- .../generic/extend_type_completion.carbon | 24 ++++++++++++++----- .../check/testdata/impl/import_generic.carbon | 4 ++-- toolchain/check/type_completion.cpp | 21 ++++++++-------- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/toolchain/check/testdata/generic/extend_type_completion.carbon b/toolchain/check/testdata/generic/extend_type_completion.carbon index 63ea4ab2df7e2..6745c476032d0 100644 --- a/toolchain/check/testdata/generic/extend_type_completion.carbon +++ b/toolchain/check/testdata/generic/extend_type_completion.carbon @@ -62,18 +62,24 @@ interface J(N:! i32) { // CHECK:STDERR: ^ extend require impls K(array(i32, N)); } +interface I(N:! i32) { + // CHECK:STDERR: fail_interface_extend_require_impls_does_need_complete_interface.carbon:[[@LINE+3]]:24: note: in `require` used here [ResolvingSpecificHere] + // CHECK:STDERR: extend require impls J(N); + // CHECK:STDERR: ^~~~ + extend require impls J(N); +} -// J extends K so the type of K is completed, but is invalid. +// I extend J extends K so the type of K is completed, but is invalid. // // TODO: The error location should be the type, like in the class case above. We // need a location for the type in context.bind_name_map() to use as the // location to Convert(). // // CHECK:STDERR: fail_interface_extend_require_impls_does_need_complete_interface.carbon:[[@LINE+4]]:1: note: in `require` used here [ResolvingSpecificHere] -// CHECK:STDERR: var v: J(-1); +// CHECK:STDERR: var v: I(-1); // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: -var v: J(-1); +var v: I(-1); // --- constraint_require_impls_doesnt_need_complete_interface.carbon library "[[@TEST_NAME]]"; @@ -96,13 +102,19 @@ constraint J(N:! i32) { // CHECK:STDERR: ^ extend require impls K(array(i32, N)); } +constraint I(N:! i32) { + // CHECK:STDERR: fail_constraint_extend_require_impls_does_need_complete_interface.carbon:[[@LINE+3]]:24: note: in `require` used here [ResolvingSpecificHere] + // CHECK:STDERR: extend require impls J(N); + // CHECK:STDERR: ^~~~ + extend require impls J(N); +} -// J extends K so the type of K is completed, but is invalid. +// I extends J extends K so the type of K is completed, but is invalid. // CHECK:STDERR: fail_constraint_extend_require_impls_does_need_complete_interface.carbon:[[@LINE+4]]:1: note: in `require` used here [ResolvingSpecificHere] -// CHECK:STDERR: var v: J(-1); +// CHECK:STDERR: var v: I(-1); // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: -var v: J(-1); +var v: I(-1); // --- interface_require_impls_doesnt_need_complete_self.carbon library "[[@TEST_NAME]]"; diff --git a/toolchain/check/testdata/impl/import_generic.carbon b/toolchain/check/testdata/impl/import_generic.carbon index 77f7530eba245..b7bdbf5188daa 100644 --- a/toolchain/check/testdata/impl/import_generic.carbon +++ b/toolchain/check/testdata/impl/import_generic.carbon @@ -920,9 +920,9 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.aa1546.1 [symbolic] // CHECK:STDOUT: %ptr.3f2: type = ptr_type %ptr.4f0 [symbolic] // CHECK:STDOUT: %N.type.b8d23b.2: type = facet_type <@N, @N(%ptr.3f2)> [symbolic] -// CHECK:STDOUT: %Self.aa1546.2: %N.type.b8d23b.2 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %I.type.71a: type = facet_type <@I, @I(%ptr.3f2)> [symbolic] // CHECK:STDOUT: %require_complete.08c: = require_complete_type %I.type.71a [symbolic] +// CHECK:STDOUT: %Self.aa1546.2: %N.type.b8d23b.2 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %require_complete.a37: = require_complete_type %N.type.b8d23b.2 [symbolic] // CHECK:STDOUT: %I.impl_witness.524: = impl_witness file.%I.impl_witness_table.loc6, @C.as.I.impl.3f8(%T) [symbolic] // CHECK:STDOUT: } @@ -1372,8 +1372,8 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %require_complete.a37d6c.1: = require_complete_type %N.type.b8d23b.1 [symbolic] // CHECK:STDOUT: %J.impl_witness.b5245e.1: = impl_witness file.%J.impl_witness_table.loc29, @D.as.J.impl.3b0484.1(%T) [symbolic] // CHECK:STDOUT: %N.type.b8d23b.2: type = facet_type <@N, @N(%ptr)> [symbolic] -// CHECK:STDOUT: %Self.aa1546.2: %N.type.b8d23b.2 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %require_complete.d4d: = require_complete_type %J.type.4fa [symbolic] +// CHECK:STDOUT: %Self.aa1546.2: %N.type.b8d23b.2 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %require_complete.a37d6c.2: = require_complete_type %N.type.b8d23b.2 [symbolic] // CHECK:STDOUT: %J.impl_witness.b5245e.2: = impl_witness file.%J.impl_witness_table.loc39, @D.as.J.impl.3b0484.2(%T) [symbolic] // CHECK:STDOUT: } diff --git a/toolchain/check/type_completion.cpp b/toolchain/check/type_completion.cpp index 3851d917a6938..94956b06f1d69 100644 --- a/toolchain/check/type_completion.cpp +++ b/toolchain/check/type_completion.cpp @@ -124,6 +124,7 @@ static auto RequireCompleteFacetType(Context& context, SemIR::LocId loc_id, MakeDiagnosticBuilderFn diagnoser) -> bool { llvm::SmallVector work = {facet_type.facet_type_id}; + llvm::SmallVector resolve; while (!work.empty()) { auto next_facet_type_id = work.pop_back_val(); const auto& facet_type_info = context.facet_types().Get(next_facet_type_id); @@ -132,7 +133,7 @@ static auto RequireCompleteFacetType(Context& context, SemIR::LocId loc_id, SemIR::SpecificId specific_id; SemIR::RequireImplsBlockId requires_block_id; }; - llvm::SmallVector specifics; + llvm::SmallVector specific_requires; for (auto extends : facet_type_info.extend_constraints) { auto interface_id = extends.interface_id; @@ -146,11 +147,9 @@ static auto RequireCompleteFacetType(Context& context, SemIR::LocId loc_id, return false; } if (interface.generic_id.has_value()) { - specifics.push_back( + specific_requires.push_back( {extends.specific_id, interface.require_impls_block_id}); - } - if (extends.specific_id.has_value()) { - ResolveSpecificDefinition(context, loc_id, extends.specific_id); + resolve.push_back(extends.specific_id); } } @@ -167,11 +166,9 @@ static auto RequireCompleteFacetType(Context& context, SemIR::LocId loc_id, return false; } if (constraint.generic_id.has_value()) { - specifics.push_back( + specific_requires.push_back( {extends.specific_id, constraint.require_impls_block_id}); - } - if (extends.specific_id.has_value()) { - ResolveSpecificDefinition(context, loc_id, extends.specific_id); + resolve.push_back(extends.specific_id); } } @@ -182,7 +179,7 @@ static auto RequireCompleteFacetType(Context& context, SemIR::LocId loc_id, // declaration since `extend require` can only be applied to `Self`, so the // self-type in these `require` declarations never uses any generic // parameters. - for (auto [specific_id, requires_block_id] : specifics) { + for (auto [specific_id, requires_block_id] : specific_requires) { for (auto require_impls_id : context.require_impls_blocks().Get(requires_block_id)) { const auto& require = context.require_impls().Get(require_impls_id); @@ -203,6 +200,10 @@ static auto RequireCompleteFacetType(Context& context, SemIR::LocId loc_id, } } + for (auto specific_id : resolve) { + ResolveSpecificDefinition(context, loc_id, specific_id); + } + return true; } From fe9a842c0207bcc68a10c0525f44d57817c16c0d Mon Sep 17 00:00:00 2001 From: danakj Date: Wed, 19 Nov 2025 15:00:37 -0500 Subject: [PATCH 15/31] one-less-malloc --- toolchain/check/type_completion.cpp | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/toolchain/check/type_completion.cpp b/toolchain/check/type_completion.cpp index 94956b06f1d69..64fd7f5d0f16b 100644 --- a/toolchain/check/type_completion.cpp +++ b/toolchain/check/type_completion.cpp @@ -123,18 +123,18 @@ static auto RequireCompleteFacetType(Context& context, SemIR::LocId loc_id, const SemIR::FacetType& facet_type, MakeDiagnosticBuilderFn diagnoser) -> bool { + struct SpecificForRequires { + SemIR::SpecificId specific_id; + SemIR::RequireImplsBlockId requires_block_id; + }; + llvm::SmallVector specifics; + int specifics_done = 0; + llvm::SmallVector work = {facet_type.facet_type_id}; - llvm::SmallVector resolve; while (!work.empty()) { auto next_facet_type_id = work.pop_back_val(); const auto& facet_type_info = context.facet_types().Get(next_facet_type_id); - struct SpecificForRequires { - SemIR::SpecificId specific_id; - SemIR::RequireImplsBlockId requires_block_id; - }; - llvm::SmallVector specific_requires; - for (auto extends : facet_type_info.extend_constraints) { auto interface_id = extends.interface_id; const auto& interface = context.interfaces().Get(interface_id); @@ -147,9 +147,8 @@ static auto RequireCompleteFacetType(Context& context, SemIR::LocId loc_id, return false; } if (interface.generic_id.has_value()) { - specific_requires.push_back( + specifics.push_back( {extends.specific_id, interface.require_impls_block_id}); - resolve.push_back(extends.specific_id); } } @@ -166,9 +165,8 @@ static auto RequireCompleteFacetType(Context& context, SemIR::LocId loc_id, return false; } if (constraint.generic_id.has_value()) { - specific_requires.push_back( + specifics.push_back( {extends.specific_id, constraint.require_impls_block_id}); - resolve.push_back(extends.specific_id); } } @@ -179,7 +177,9 @@ static auto RequireCompleteFacetType(Context& context, SemIR::LocId loc_id, // declaration since `extend require` can only be applied to `Self`, so the // self-type in these `require` declarations never uses any generic // parameters. - for (auto [specific_id, requires_block_id] : specific_requires) { + auto requires_specifics = + llvm::ArrayRef(specifics).drop_front(specifics_done); + for (auto [specific_id, requires_block_id] : requires_specifics) { for (auto require_impls_id : context.require_impls_blocks().Get(requires_block_id)) { const auto& require = context.require_impls().Get(require_impls_id); @@ -198,9 +198,10 @@ static auto RequireCompleteFacetType(Context& context, SemIR::LocId loc_id, } } } + specifics_done += requires_specifics.size(); } - for (auto specific_id : resolve) { + for (auto [specific_id, _] : specifics) { ResolveSpecificDefinition(context, loc_id, specific_id); } From aa2fcc0e8d5ec86419ef48a9bd3ac22659ceab4c Mon Sep 17 00:00:00 2001 From: danakj Date: Wed, 19 Nov 2025 15:12:06 -0500 Subject: [PATCH 16/31] add-comment --- toolchain/check/type_completion.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/toolchain/check/type_completion.cpp b/toolchain/check/type_completion.cpp index 64fd7f5d0f16b..5eb5556417af2 100644 --- a/toolchain/check/type_completion.cpp +++ b/toolchain/check/type_completion.cpp @@ -201,6 +201,9 @@ static auto RequireCompleteFacetType(Context& context, SemIR::LocId loc_id, specifics_done += requires_specifics.size(); } + // Specific definitions are resolved from the interfaces in the facet type + // down, each require target depends on the specific referring to it being + // resolved already. for (auto [specific_id, _] : specifics) { ResolveSpecificDefinition(context, loc_id, specific_id); } From 4fe03656f60bba9fb57957a1579b880eda7bf1a6 Mon Sep 17 00:00:00 2001 From: danakj Date: Wed, 19 Nov 2025 16:18:05 -0500 Subject: [PATCH 17/31] extends --- toolchain/check/testdata/generic/extend_type_completion.carbon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolchain/check/testdata/generic/extend_type_completion.carbon b/toolchain/check/testdata/generic/extend_type_completion.carbon index 6745c476032d0..144e7307c70e3 100644 --- a/toolchain/check/testdata/generic/extend_type_completion.carbon +++ b/toolchain/check/testdata/generic/extend_type_completion.carbon @@ -69,7 +69,7 @@ interface I(N:! i32) { extend require impls J(N); } -// I extend J extends K so the type of K is completed, but is invalid. +// I extends J extends K so the type of K is completed, but is invalid. // // TODO: The error location should be the type, like in the class case above. We // need a location for the type in context.bind_name_map() to use as the From a35442d3345ae2fc0fe1433f29aae135074c0db2 Mon Sep 17 00:00:00 2001 From: danakj Date: Wed, 19 Nov 2025 16:18:57 -0500 Subject: [PATCH 18/31] no-k --- .../impl/fail_undefined_interface.carbon | 53 +++++-------------- 1 file changed, 13 insertions(+), 40 deletions(-) diff --git a/toolchain/check/testdata/impl/fail_undefined_interface.carbon b/toolchain/check/testdata/impl/fail_undefined_interface.carbon index 63304ada6e52f..65a44170d925f 100644 --- a/toolchain/check/testdata/impl/fail_undefined_interface.carbon +++ b/toolchain/check/testdata/impl/fail_undefined_interface.carbon @@ -45,13 +45,12 @@ impl C as J {} library "[[@TEST_NAME]]"; interface J; -interface K { let X:! type; } class C {} // CHECK:STDERR: fail_class_with_rewrite.carbon:[[@LINE+7]]:19: error: member access into object of incomplete type `J` [IncompleteTypeInMemberAccess] // CHECK:STDERR: impl C as J where .X = (); // CHECK:STDERR: ^~ -// CHECK:STDERR: fail_class_with_rewrite.carbon:[[@LINE-7]]:1: note: interface was forward declared here [InterfaceForwardDeclaredHere] +// CHECK:STDERR: fail_class_with_rewrite.carbon:[[@LINE-6]]:1: note: interface was forward declared here [InterfaceForwardDeclaredHere] // CHECK:STDERR: interface J; // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: @@ -60,7 +59,7 @@ impl C as J where .X = (); // CHECK:STDERR: fail_class_with_rewrite.carbon:[[@LINE+7]]:19: error: member access into object of incomplete type `J` [IncompleteTypeInMemberAccess] // CHECK:STDERR: impl C as J where .X = () {} // CHECK:STDERR: ^~ -// CHECK:STDERR: fail_class_with_rewrite.carbon:[[@LINE-16]]:1: note: interface was forward declared here [InterfaceForwardDeclaredHere] +// CHECK:STDERR: fail_class_with_rewrite.carbon:[[@LINE-15]]:1: note: interface was forward declared here [InterfaceForwardDeclaredHere] // CHECK:STDERR: interface J; // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: @@ -202,10 +201,6 @@ impl C as I where .Self impls Incomplete { // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %J.type: type = facet_type <@J> [concrete] -// CHECK:STDOUT: %K.type: type = facet_type <@K> [concrete] -// CHECK:STDOUT: %Self: %K.type = symbolic_binding Self, 0 [symbolic] -// CHECK:STDOUT: %K.assoc_type: type = assoc_entity_type @K [concrete] -// CHECK:STDOUT: %assoc0: %K.assoc_type = assoc_entity element0, @K.%X [concrete] // CHECK:STDOUT: %C: type = class_type @C [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] @@ -217,30 +212,28 @@ impl C as I where .Self impls Incomplete { // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .J = %J.decl -// CHECK:STDOUT: .K = %K.decl // CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %J.decl: type = interface_decl @J [concrete = constants.%J.type] {} {} -// CHECK:STDOUT: %K.decl: type = interface_decl @K [concrete = constants.%K.type] {} {} // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: impl_decl @C.as..impl [concrete] {} { -// CHECK:STDOUT: %C.ref.loc14: type = name_ref C, file.%C.decl [concrete = constants.%C] -// CHECK:STDOUT: %J.ref.loc14: type = name_ref J, file.%J.decl [concrete = constants.%J.type] +// CHECK:STDOUT: %C.ref.loc13: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %J.ref.loc13: type = name_ref J, file.%J.decl [concrete = constants.%J.type] // CHECK:STDOUT: %.Self.2: %J.type = symbolic_binding .Self [symbolic_self = constants.%.Self] -// CHECK:STDOUT: %.Self.ref.loc14: %J.type = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self] -// CHECK:STDOUT: %.loc14_25: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] -// CHECK:STDOUT: %.loc14_13: type = where_expr %.Self.2 [concrete = ] { +// CHECK:STDOUT: %.Self.ref.loc13: %J.type = name_ref .Self, %.Self.2 [symbolic_self = constants.%.Self] +// CHECK:STDOUT: %.loc13_25: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] +// CHECK:STDOUT: %.loc13_13: type = where_expr %.Self.2 [concrete = ] { // CHECK:STDOUT: requirement_base_facet_type constants.%J.type // CHECK:STDOUT: requirement_rewrite , // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: impl_decl @C.as..impl [concrete] {} { -// CHECK:STDOUT: %C.ref.loc23: type = name_ref C, file.%C.decl [concrete = constants.%C] -// CHECK:STDOUT: %J.ref.loc23: type = name_ref J, file.%J.decl [concrete = constants.%J.type] +// CHECK:STDOUT: %C.ref.loc22: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %J.ref.loc22: type = name_ref J, file.%J.decl [concrete = constants.%J.type] // CHECK:STDOUT: %.Self.1: %J.type = symbolic_binding .Self [symbolic_self = constants.%.Self] -// CHECK:STDOUT: %.Self.ref.loc23: %J.type = name_ref .Self, %.Self.1 [symbolic_self = constants.%.Self] -// CHECK:STDOUT: %.loc23_25: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] -// CHECK:STDOUT: %.loc23_13: type = where_expr %.Self.1 [concrete = ] { +// CHECK:STDOUT: %.Self.ref.loc22: %J.type = name_ref .Self, %.Self.1 [symbolic_self = constants.%.Self] +// CHECK:STDOUT: %.loc22_25: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] +// CHECK:STDOUT: %.loc22_13: type = where_expr %.Self.1 [concrete = ] { // CHECK:STDOUT: requirement_base_facet_type constants.%J.type // CHECK:STDOUT: requirement_rewrite , // CHECK:STDOUT: } @@ -249,25 +242,7 @@ impl C as I where .Self impls Incomplete { // CHECK:STDOUT: // CHECK:STDOUT: interface @J; // CHECK:STDOUT: -// CHECK:STDOUT: interface @K { -// CHECK:STDOUT: %Self: %K.type = symbolic_binding Self, 0 [symbolic = constants.%Self] -// CHECK:STDOUT: %X: type = assoc_const_decl @X [concrete] { -// CHECK:STDOUT: %assoc0: %K.assoc_type = assoc_entity element0, @K.%X [concrete = constants.%assoc0] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = %Self -// CHECK:STDOUT: .X = @X.%assoc0 -// CHECK:STDOUT: witness = (%X) -// CHECK:STDOUT: -// CHECK:STDOUT: !requires: -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: generic assoc_const @X(@K.%Self: %K.type) { -// CHECK:STDOUT: assoc_const X:! type; -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: impl @C.as..impl: %C.ref.loc14 as %.loc14_13 { +// CHECK:STDOUT: impl @C.as..impl: %C.ref.loc13 as %.loc13_13 { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } @@ -280,8 +255,6 @@ impl C as I where .Self impls Incomplete { // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: specific @X(constants.%Self) {} -// CHECK:STDOUT: // CHECK:STDOUT: --- fail_class_with_qualified_rewrite.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { From 56886b7e0368318785df77cb82dc437bd3412b20 Mon Sep 17 00:00:00 2001 From: danakj Date: Wed, 19 Nov 2025 16:20:18 -0500 Subject: [PATCH 19/31] fix-test --- .../impl/fail_undefined_interface.carbon | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/toolchain/check/testdata/impl/fail_undefined_interface.carbon b/toolchain/check/testdata/impl/fail_undefined_interface.carbon index 65a44170d925f..68b0dbc4e36fa 100644 --- a/toolchain/check/testdata/impl/fail_undefined_interface.carbon +++ b/toolchain/check/testdata/impl/fail_undefined_interface.carbon @@ -65,28 +65,30 @@ impl C as J where .X = (); // CHECK:STDERR: impl C as J where .X = () {} -// --- fail_class_with_qualified_rewrite.carbon +// --- fail_todo_class_with_qualified_rewrite.carbon library "[[@TEST_NAME]]"; interface J; interface K { let X:! type; } class C {} -// TODO: The failure here should be that J is incomplete, once the rewrite of -// `.(K.X)` works. Since any rewrite in the decl requires us to know the size of -// the witness table which requires all interfaces to be complete. +// TODO: There should be no error here. // -// CHECK:STDERR: fail_class_with_qualified_rewrite.carbon:[[@LINE+8]]:38: error: expected identifier or `Self` after `.` [ExpectedIdentifierOrSelfAfterPeriod] +// CHECK:STDERR: fail_todo_class_with_qualified_rewrite.carbon:[[@LINE+8]]:38: error: expected identifier or `Self` after `.` [ExpectedIdentifierOrSelfAfterPeriod] // CHECK:STDERR: impl C as J where .Self impls K and .(K.X) = (); // CHECK:STDERR: ^ // CHECK:STDERR: -// CHECK:STDERR: fail_class_with_qualified_rewrite.carbon:[[@LINE+4]]:38: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] +// CHECK:STDERR: fail_todo_class_with_qualified_rewrite.carbon:[[@LINE+4]]:38: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] // CHECK:STDERR: impl C as J where .Self impls K and .(K.X) = (); // CHECK:STDERR: ^ // CHECK:STDERR: impl C as J where .Self impls K and .(K.X) = (); -// CHECK:STDERR: fail_class_with_qualified_rewrite.carbon:[[@LINE+4]]:38: error: expected identifier or `Self` after `.` [ExpectedIdentifierOrSelfAfterPeriod] +// TODO: The failure here should be that J is incomplete, once the rewrite of +// `.(K.X)` works. Since any rewrite in the decl requires us to know the size of +// the witness table which requires all interfaces to be complete. +// +// CHECK:STDERR: fail_todo_class_with_qualified_rewrite.carbon:[[@LINE+4]]:38: error: expected identifier or `Self` after `.` [ExpectedIdentifierOrSelfAfterPeriod] // CHECK:STDERR: impl C as J where .Self impls K and .(K.X) = () {} // CHECK:STDERR: ^ // CHECK:STDERR: @@ -255,7 +257,7 @@ impl C as I where .Self impls Incomplete { // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_class_with_qualified_rewrite.carbon +// CHECK:STDOUT: --- fail_todo_class_with_qualified_rewrite.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %K.type: type = facet_type <@K> [concrete] From cc27be408b5b8fb1e3beb63976103d2f4cccd175 Mon Sep 17 00:00:00 2001 From: danakj Date: Wed, 19 Nov 2025 16:26:18 -0500 Subject: [PATCH 20/31] Revert "avoid-uaf" This reverts commit 8c5e57337015aa9cbd40666e07188dafbe0e9546. --- toolchain/check/impl_lookup.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/toolchain/check/impl_lookup.cpp b/toolchain/check/impl_lookup.cpp index 9ed8863a9febb..d1398828271d6 100644 --- a/toolchain/check/impl_lookup.cpp +++ b/toolchain/check/impl_lookup.cpp @@ -184,7 +184,7 @@ static auto FindAndDiagnoseImplLookupCycle( } struct InterfacesFromConstantId { - llvm::SmallVector interfaces; + llvm::ArrayRef interfaces; SemIR::BuiltinConstraintMask builtin_constraint_mask; bool other_requirements; }; @@ -211,14 +211,9 @@ static auto GetInterfacesFromConstantId( if (!identified_id.has_value()) { return std::nullopt; } - // TODO: IdentifiedFacetTypes are held in a RelationalValueStore which does - // not protect against UAF through reallocation, so we must copy data out of - // it. - llvm::SmallVector interfaces( - context.identified_facet_types() - .Get(identified_id) - .required_interfaces()); - return {{.interfaces = std::move(interfaces), + return {{.interfaces = context.identified_facet_types() + .Get(identified_id) + .required_interfaces(), .builtin_constraint_mask = facet_type_info.builtin_constraint_mask, .other_requirements = facet_type_info.other_requirements}}; } From 9305cca5359112d192a58e91663f8e41a2b2e326 Mon Sep 17 00:00:00 2001 From: danakj Date: Wed, 19 Nov 2025 18:19:14 -0500 Subject: [PATCH 21/31] move-tests --- .../testdata/facet/fail_incomplete.carbon | 70 ---- ...ned_interface.carbon => incomplete.carbon} | 364 +++++++++++++++++- .../testdata/interface/incomplete.carbon | 258 ------------- 3 files changed, 354 insertions(+), 338 deletions(-) rename toolchain/check/testdata/impl/{fail_undefined_interface.carbon => incomplete.carbon} (65%) diff --git a/toolchain/check/testdata/facet/fail_incomplete.carbon b/toolchain/check/testdata/facet/fail_incomplete.carbon index 8187875c0dd2b..c2c5e88130f16 100644 --- a/toolchain/check/testdata/facet/fail_incomplete.carbon +++ b/toolchain/check/testdata/facet/fail_incomplete.carbon @@ -10,76 +10,6 @@ // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/fail_incomplete.carbon -// --- fail_incomplete_interface.carbon -library "[[@TEST_NAME]]"; - -interface X; -class C {} - -// Requires X identified. -impl C as X; - -// Requires X complete. -// CHECK:STDERR: fail_incomplete_interface.carbon:[[@LINE+7]]:1: error: definition of impl as incomplete facet type `X` [ImplAsIncompleteFacetTypeDefinition] -// CHECK:STDERR: impl C as X {} -// CHECK:STDERR: ^~~~~~~~~~~~~ -// CHECK:STDERR: fail_incomplete_interface.carbon:[[@LINE-10]]:1: note: interface was forward declared here [InterfaceForwardDeclaredHere] -// CHECK:STDERR: interface X; -// CHECK:STDERR: ^~~~~~~~~~~~ -// CHECK:STDERR: -impl C as X {} - -// --- fail_incomplete_interface_without_forward_decl.carbon -library "[[@TEST_NAME]]"; - -interface X; -class C {} - -// Requires X complete. -// CHECK:STDERR: fail_incomplete_interface_without_forward_decl.carbon:[[@LINE+7]]:1: error: definition of impl as incomplete facet type `X` [ImplAsIncompleteFacetTypeDefinition] -// CHECK:STDERR: impl C as X {} -// CHECK:STDERR: ^~~~~~~~~~~~~ -// CHECK:STDERR: fail_incomplete_interface_without_forward_decl.carbon:[[@LINE-7]]:1: note: interface was forward declared here [InterfaceForwardDeclaredHere] -// CHECK:STDERR: interface X; -// CHECK:STDERR: ^~~~~~~~~~~~ -// CHECK:STDERR: -impl C as X {} - -// --- fail_unidentified_constraint.carbon -library "[[@TEST_NAME]]"; - -constraint X; -class C {} - -// Requires X identified. -// CHECK:STDERR: fail_unidentified_constraint.carbon:[[@LINE+7]]:1: error: facet type `X` cannot be identified in `impl as` [ImplOfUnidentifiedFacetType] -// CHECK:STDERR: impl C as X; -// CHECK:STDERR: ^~~~~~~~~~~~ -// CHECK:STDERR: fail_unidentified_constraint.carbon:[[@LINE-7]]:1: note: constraint was forward declared here [NamedConstraintForwardDeclaredHere] -// CHECK:STDERR: constraint X; -// CHECK:STDERR: ^~~~~~~~~~~~~ -// CHECK:STDERR: -impl C as X; - -// --- nested_require_incomplete_interface.carbon -library "[[@TEST_NAME]]"; - -interface Z; -constraint Y { - require impls Z; -} -interface X { - require impls Y; -} - -class C {} - -// Requires X identified. -impl C as X; - -// Requires X complete. -impl C as X {} - // --- fail_impl_lookup_incomplete.carbon library "[[@TEST_NAME]]"; diff --git a/toolchain/check/testdata/impl/fail_undefined_interface.carbon b/toolchain/check/testdata/impl/incomplete.carbon similarity index 65% rename from toolchain/check/testdata/impl/fail_undefined_interface.carbon rename to toolchain/check/testdata/impl/incomplete.carbon index 68b0dbc4e36fa..0273d467a45b6 100644 --- a/toolchain/check/testdata/impl/fail_undefined_interface.carbon +++ b/toolchain/check/testdata/impl/incomplete.carbon @@ -8,34 +8,59 @@ // // AUTOUPDATE // TIP: To test this file alone, run: -// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/fail_undefined_interface.carbon +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/incomplete.carbon // TIP: To dump output, run: -// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/fail_undefined_interface.carbon +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/incomplete.carbon // --- fail_empty_struct.carbon - library "[[@TEST_NAME]]"; interface I; + +// Requires I identified. +impl {} as I; + // CHECK:STDERR: fail_empty_struct.carbon:[[@LINE+7]]:1: error: definition of impl as incomplete facet type `I` [ImplAsIncompleteFacetTypeDefinition] // CHECK:STDERR: impl {} as I {} // CHECK:STDERR: ^~~~~~~~~~~~~~ -// CHECK:STDERR: fail_empty_struct.carbon:[[@LINE-4]]:1: note: interface was forward declared here [InterfaceForwardDeclaredHere] +// CHECK:STDERR: fail_empty_struct.carbon:[[@LINE-8]]:1: note: interface was forward declared here [InterfaceForwardDeclaredHere] // CHECK:STDERR: interface I; // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: impl {} as I {} // --- fail_class.carbon - library "[[@TEST_NAME]]"; interface J; class C {} + +// Requires I identified. +// CHECK:STDERR: fail_class.carbon:[[@LINE+4]]:11: error: name `I` not found [NameNotFound] +// CHECK:STDERR: impl C as I; +// CHECK:STDERR: ^ +// CHECK:STDERR: +impl C as I; + // CHECK:STDERR: fail_class.carbon:[[@LINE+7]]:1: error: definition of impl as incomplete facet type `J` [ImplAsIncompleteFacetTypeDefinition] // CHECK:STDERR: impl C as J {} // CHECK:STDERR: ^~~~~~~~~~~~~ -// CHECK:STDERR: fail_class.carbon:[[@LINE-5]]:1: note: interface was forward declared here [InterfaceForwardDeclaredHere] +// CHECK:STDERR: fail_class.carbon:[[@LINE-13]]:1: note: interface was forward declared here [InterfaceForwardDeclaredHere] +// CHECK:STDERR: interface J; +// CHECK:STDERR: ^~~~~~~~~~~~ +// CHECK:STDERR: +impl C as J {} + +// --- fail_class_no_forward_decl.carbon +library "[[@TEST_NAME]]"; + +interface J; +class C {} + +// CHECK:STDERR: fail_class_no_forward_decl.carbon:[[@LINE+7]]:1: error: definition of impl as incomplete facet type `J` [ImplAsIncompleteFacetTypeDefinition] +// CHECK:STDERR: impl C as J {} +// CHECK:STDERR: ^~~~~~~~~~~~~ +// CHECK:STDERR: fail_class_no_forward_decl.carbon:[[@LINE-6]]:1: note: interface was forward declared here [InterfaceForwardDeclaredHere] // CHECK:STDERR: interface J; // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: @@ -135,12 +160,77 @@ impl C as I where .Self impls Incomplete { fn F() -> Self.(Incomplete.T); }; +// --- fail_unidentified_constraint.carbon +library "[[@TEST_NAME]]"; + +constraint X; +class C {} + +// Requires X identified. +// CHECK:STDERR: fail_unidentified_constraint.carbon:[[@LINE+7]]:1: error: facet type `X` cannot be identified in `impl as` [ImplOfUnidentifiedFacetType] +// CHECK:STDERR: impl C as X; +// CHECK:STDERR: ^~~~~~~~~~~~ +// CHECK:STDERR: fail_unidentified_constraint.carbon:[[@LINE-7]]:1: note: constraint was forward declared here [NamedConstraintForwardDeclaredHere] +// CHECK:STDERR: constraint X; +// CHECK:STDERR: ^~~~~~~~~~~~~ +// CHECK:STDERR: +impl C as X; + +// --- nested_require_incomplete_interface.carbon +library "[[@TEST_NAME]]"; + +interface Z; +constraint Y { + // Not extend, so only required to be identified. + require impls Z; +} +interface X { + // Not extend, so only required to be identified. + require impls Y; +} + +class C {} +impl C as X {} + +// --- fail_incomplete_constraint.carbon +library "[[@TEST_NAME]]"; + +constraint A; + +interface B { + // CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE+7]]:17: error: facet type `A` cannot be identified in `require` declaration [RequireImplsUnidentifiedFacetType] + // CHECK:STDERR: require impls A; + // CHECK:STDERR: ^ + // CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE-6]]:1: note: constraint was forward declared here [NamedConstraintForwardDeclaredHere] + // CHECK:STDERR: constraint A; + // CHECK:STDERR: ^~~~~~~~~~~~~ + // CHECK:STDERR: + require impls A; +} + +// --- fail_incomplete_extend_constraint.carbon +library "[[@TEST_NAME]]"; + +constraint A; + +interface B { + // CHECK:STDERR: fail_incomplete_extend_constraint.carbon:[[@LINE+7]]:24: error: facet type `A` cannot be identified in `require` declaration [RequireImplsUnidentifiedFacetType] + // CHECK:STDERR: extend require impls A; + // CHECK:STDERR: ^ + // CHECK:STDERR: fail_incomplete_extend_constraint.carbon:[[@LINE-6]]:1: note: constraint was forward declared here [NamedConstraintForwardDeclaredHere] + // CHECK:STDERR: constraint A; + // CHECK:STDERR: ^~~~~~~~~~~~~ + // CHECK:STDERR: + extend require impls A; +} + // CHECK:STDOUT: --- fail_empty_struct.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] +// CHECK:STDOUT: %I.impl_witness: = impl_witness file.%I.impl_witness_table [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -149,15 +239,22 @@ impl C as I where .Self impls Incomplete { // CHECK:STDOUT: } // CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: impl_decl @empty_struct_type.as.I.impl [concrete] {} { -// CHECK:STDOUT: %.loc12_7.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] -// CHECK:STDOUT: %.loc12_7.2: type = converted %.loc12_7.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] -// CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] +// CHECK:STDOUT: %.loc6_7.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] +// CHECK:STDOUT: %.loc6_7.2: type = converted %.loc6_7.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] +// CHECK:STDOUT: %I.ref.loc6: type = name_ref I, file.%I.decl [concrete = constants.%I.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @empty_struct_type.as.I.impl [concrete] +// CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness] +// CHECK:STDOUT: impl_decl @empty_struct_type.as.I.impl [concrete] {} { +// CHECK:STDOUT: %.loc15_7.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] +// CHECK:STDOUT: %.loc15_7.2: type = converted %.loc15_7.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] +// CHECK:STDOUT: %I.ref.loc15: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I; // CHECK:STDOUT: -// CHECK:STDOUT: impl @empty_struct_type.as.I.impl: %.loc12_7.2 as %I.ref { +// CHECK:STDOUT: impl @empty_struct_type.as.I.impl: %.loc6_7.2 as %I.ref.loc6 { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } @@ -175,6 +272,50 @@ impl C as I where .Self impls Incomplete { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .J = %J.decl // CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: .I = +// CHECK:STDOUT: } +// CHECK:STDOUT: %J.decl: type = interface_decl @J [concrete = constants.%J.type] {} {} +// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} +// CHECK:STDOUT: impl_decl @C.as..impl [concrete] {} { +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %I.ref: = name_ref I, [concrete = ] +// CHECK:STDOUT: } +// CHECK:STDOUT: impl_decl @C.as.J.impl [concrete] {} { +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %J.ref: type = name_ref J, file.%J.decl [concrete = constants.%J.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @J; +// CHECK:STDOUT: +// CHECK:STDOUT: impl @C.as..impl: %C.ref as ; +// CHECK:STDOUT: +// CHECK:STDOUT: impl @C.as.J.impl: %C.ref as %J.ref { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: witness = +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_class_no_forward_decl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %J.type: type = facet_type <@J> [concrete] +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .J = %J.decl +// CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: } // CHECK:STDOUT: %J.decl: type = interface_decl @J [concrete = constants.%J.type] {} {} // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} @@ -562,3 +703,206 @@ impl C as I where .Self impls Incomplete { // CHECK:STDOUT: // CHECK:STDOUT: fn @C.as.I.impl.F() -> ; // CHECK:STDOUT: +// CHECK:STDOUT: --- fail_unidentified_constraint.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %X.type: type = facet_type <@X> [concrete] +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .X = %X.decl +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %X.decl: type = constraint_decl @X [concrete = constants.%X.type] {} {} +// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} +// CHECK:STDOUT: impl_decl @C.as..impl [concrete] {} { +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %X.ref: type = name_ref X, file.%X.decl [concrete = constants.%X.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: constraint @X; +// CHECK:STDOUT: +// CHECK:STDOUT: impl @C.as..impl: %C.ref as %X.ref; +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- nested_require_incomplete_interface.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %Z.type: type = facet_type <@Z> [concrete] +// CHECK:STDOUT: %Y.type: type = facet_type <@Y> [concrete] +// CHECK:STDOUT: %Self.861: %Y.type = symbolic_binding Self, 0 [symbolic] +// CHECK:STDOUT: %Self.binding.as_type.1a1: type = symbolic_binding_type Self, 0, %Self.861 [symbolic] +// CHECK:STDOUT: %X.type: type = facet_type <@X> [concrete] +// CHECK:STDOUT: %Self.40f: %X.type = symbolic_binding Self, 0 [symbolic] +// CHECK:STDOUT: %Self.binding.as_type.dcc: type = symbolic_binding_type Self, 0, %Self.40f [symbolic] +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: %X.impl_witness: = impl_witness file.%X.impl_witness_table [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .Z = %Z.decl +// CHECK:STDOUT: .Y = %Y.decl +// CHECK:STDOUT: .X = %X.decl +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Z.decl: type = interface_decl @Z [concrete = constants.%Z.type] {} {} +// CHECK:STDOUT: %Y.decl: type = constraint_decl @Y [concrete = constants.%Y.type] {} {} +// CHECK:STDOUT: %X.decl: type = interface_decl @X [concrete = constants.%X.type] {} {} +// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} +// CHECK:STDOUT: impl_decl @C.as.X.impl [concrete] {} { +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %X.ref: type = name_ref X, file.%X.decl [concrete = constants.%X.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %X.impl_witness_table = impl_witness_table (), @C.as.X.impl [concrete] +// CHECK:STDOUT: %X.impl_witness: = impl_witness %X.impl_witness_table [concrete = constants.%X.impl_witness] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @Z; +// CHECK:STDOUT: +// CHECK:STDOUT: interface @X { +// CHECK:STDOUT: %Self: %X.type = symbolic_binding Self, 0 [symbolic = constants.%Self.40f] +// CHECK:STDOUT: %X.require1.decl = require_decl @X.require1 [concrete] { +// CHECK:STDOUT: require %Self.as_type impls <@Y> +// CHECK:STDOUT: } { +// CHECK:STDOUT: %Self.as_type: type = facet_access_type @X.%Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.dcc)] +// CHECK:STDOUT: %Y.ref: type = name_ref Y, file.%Y.decl [concrete = constants.%Y.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .Y = +// CHECK:STDOUT: witness = () +// CHECK:STDOUT: +// CHECK:STDOUT: !requires: +// CHECK:STDOUT: @X.require1 { +// CHECK:STDOUT: require @X.require1.%Self.as_type impls <@Y> +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: constraint @Y { +// CHECK:STDOUT: %Self: %Y.type = symbolic_binding Self, 0 [symbolic = constants.%Self.861] +// CHECK:STDOUT: %Y.require0.decl = require_decl @Y.require0 [concrete] { +// CHECK:STDOUT: require %Self.as_type impls <@Z> +// CHECK:STDOUT: } { +// CHECK:STDOUT: %Self.as_type: type = facet_access_type @Y.%Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.1a1)] +// CHECK:STDOUT: %Z.ref: type = name_ref Z, file.%Z.decl [concrete = constants.%Z.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .Z = +// CHECK:STDOUT: +// CHECK:STDOUT: !requires: +// CHECK:STDOUT: @Y.require0 { +// CHECK:STDOUT: require @Y.require0.%Self.as_type impls <@Z> +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic require @Y.require0(@Y.%Self: %Y.type) { +// CHECK:STDOUT: %Self: %Y.type = symbolic_binding Self, 0 [symbolic = %Self (constants.%Self.861)] +// CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 0, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.1a1)] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic require @X.require1(@X.%Self: %X.type) { +// CHECK:STDOUT: %Self: %X.type = symbolic_binding Self, 0 [symbolic = %Self (constants.%Self.40f)] +// CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 0, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.dcc)] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: impl @C.as.X.impl: %C.ref as %X.ref { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: witness = file.%X.impl_witness +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @Y.require0(constants.%Self.861) { +// CHECK:STDOUT: %Self => constants.%Self.861 +// CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type.1a1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @X.require1(constants.%Self.40f) { +// CHECK:STDOUT: %Self => constants.%Self.40f +// CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type.dcc +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_incomplete_constraint.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %A.type: type = facet_type <@A> [concrete] +// CHECK:STDOUT: %B.type: type = facet_type <@B> [concrete] +// CHECK:STDOUT: %Self: %B.type = symbolic_binding Self, 0 [symbolic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .A = %A.decl +// CHECK:STDOUT: .B = %B.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %A.decl: type = constraint_decl @A [concrete = constants.%A.type] {} {} +// CHECK:STDOUT: %B.decl: type = interface_decl @B [concrete = constants.%B.type] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @B { +// CHECK:STDOUT: %Self: %B.type = symbolic_binding Self, 0 [symbolic = constants.%Self] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .A = +// CHECK:STDOUT: witness = () +// CHECK:STDOUT: +// CHECK:STDOUT: !requires: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: constraint @A; +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_incomplete_extend_constraint.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %A.type: type = facet_type <@A> [concrete] +// CHECK:STDOUT: %B.type: type = facet_type <@B> [concrete] +// CHECK:STDOUT: %Self: %B.type = symbolic_binding Self, 0 [symbolic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .A = %A.decl +// CHECK:STDOUT: .B = %B.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %A.decl: type = constraint_decl @A [concrete = constants.%A.type] {} {} +// CHECK:STDOUT: %B.decl: type = interface_decl @B [concrete = constants.%B.type] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @B { +// CHECK:STDOUT: %Self: %B.type = symbolic_binding Self, 0 [symbolic = constants.%Self] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .A = +// CHECK:STDOUT: witness = () +// CHECK:STDOUT: +// CHECK:STDOUT: !requires: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: constraint @A; +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/interface/incomplete.carbon b/toolchain/check/testdata/interface/incomplete.carbon index b10d68e88f987..484458dd06cf9 100644 --- a/toolchain/check/testdata/interface/incomplete.carbon +++ b/toolchain/check/testdata/interface/incomplete.carbon @@ -28,80 +28,7 @@ interface I { let T:! C; } -// --- fail_incomplete_constraint.carbon -library "[[@TEST_NAME]]"; - -constraint A; - -interface B { - // CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE+7]]:17: error: facet type `A` cannot be identified in `require` declaration [RequireImplsUnidentifiedFacetType] - // CHECK:STDERR: require impls A; - // CHECK:STDERR: ^ - // CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE-6]]:1: note: constraint was forward declared here [NamedConstraintForwardDeclaredHere] - // CHECK:STDERR: constraint A; - // CHECK:STDERR: ^~~~~~~~~~~~~ - // CHECK:STDERR: - require impls A; -} - -// --- fail_incomplete_extend_constraint.carbon -library "[[@TEST_NAME]]"; - -constraint A; - -interface B { - // CHECK:STDERR: fail_incomplete_extend_constraint.carbon:[[@LINE+7]]:24: error: facet type `A` cannot be identified in `require` declaration [RequireImplsUnidentifiedFacetType] - // CHECK:STDERR: extend require impls A; - // CHECK:STDERR: ^ - // CHECK:STDERR: fail_incomplete_extend_constraint.carbon:[[@LINE-6]]:1: note: constraint was forward declared here [NamedConstraintForwardDeclaredHere] - // CHECK:STDERR: constraint A; - // CHECK:STDERR: ^~~~~~~~~~~~~ - // CHECK:STDERR: - extend require impls A; -} - -// --- incomplete_interface.carbon -library "[[@TEST_NAME]]"; - -interface A; - -interface B { - require impls A; -} -impl () as B {} - -// --- fail_incomplete_extend_interface.carbon -library "[[@TEST_NAME]]"; - -interface A; - -interface B { - // CHECK:STDERR: fail_incomplete_extend_interface.carbon:[[@LINE+7]]:24: error: `extend require` of incomplete facet type `A` [RequireImplsIncompleteFacetType] - // CHECK:STDERR: extend require impls A; - // CHECK:STDERR: ^ - // CHECK:STDERR: fail_incomplete_extend_interface.carbon:[[@LINE-6]]:1: note: interface was forward declared here [InterfaceForwardDeclaredHere] - // CHECK:STDERR: interface A; - // CHECK:STDERR: ^~~~~~~~~~~~ - // CHECK:STDERR: - extend require impls A; -} - -// --- fail_incomplete_interface_in_where.carbon -library "[[@TEST_NAME]]"; - -interface A; - -interface B { - // CHECK:STDERR: fail_incomplete_interface_in_where.carbon:[[@LINE+7]]:25: error: member access into object of incomplete type `A` [IncompleteTypeInMemberAccess] - // CHECK:STDERR: require impls A where .X = {}; - // CHECK:STDERR: ^~ - // CHECK:STDERR: fail_incomplete_interface_in_where.carbon:[[@LINE-6]]:1: note: interface was forward declared here [InterfaceForwardDeclaredHere] - // CHECK:STDERR: interface A; - // CHECK:STDERR: ^~~~~~~~~~~~ - // CHECK:STDERR: - require impls A where .X = {}; -} // CHECK:STDOUT: --- fail_incomplete_type.carbon // CHECK:STDOUT: @@ -145,188 +72,3 @@ interface B { // CHECK:STDOUT: // CHECK:STDOUT: specific @T(constants.%Self) {} // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_incomplete_constraint.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %A.type: type = facet_type <@A> [concrete] -// CHECK:STDOUT: %B.type: type = facet_type <@B> [concrete] -// CHECK:STDOUT: %Self: %B.type = symbolic_binding Self, 0 [symbolic] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .A = %A.decl -// CHECK:STDOUT: .B = %B.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %A.decl: type = constraint_decl @A [concrete = constants.%A.type] {} {} -// CHECK:STDOUT: %B.decl: type = interface_decl @B [concrete = constants.%B.type] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: interface @B { -// CHECK:STDOUT: %Self: %B.type = symbolic_binding Self, 0 [symbolic = constants.%Self] -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = %Self -// CHECK:STDOUT: .A = -// CHECK:STDOUT: witness = () -// CHECK:STDOUT: -// CHECK:STDOUT: !requires: -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: constraint @A; -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_incomplete_extend_constraint.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %A.type: type = facet_type <@A> [concrete] -// CHECK:STDOUT: %B.type: type = facet_type <@B> [concrete] -// CHECK:STDOUT: %Self: %B.type = symbolic_binding Self, 0 [symbolic] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .A = %A.decl -// CHECK:STDOUT: .B = %B.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %A.decl: type = constraint_decl @A [concrete = constants.%A.type] {} {} -// CHECK:STDOUT: %B.decl: type = interface_decl @B [concrete = constants.%B.type] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: interface @B { -// CHECK:STDOUT: %Self: %B.type = symbolic_binding Self, 0 [symbolic = constants.%Self] -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = %Self -// CHECK:STDOUT: .A = -// CHECK:STDOUT: witness = () -// CHECK:STDOUT: -// CHECK:STDOUT: !requires: -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: constraint @A; -// CHECK:STDOUT: -// CHECK:STDOUT: --- incomplete_interface.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %A.type: type = facet_type <@A> [concrete] -// CHECK:STDOUT: %B.type: type = facet_type <@B> [concrete] -// CHECK:STDOUT: %Self: %B.type = symbolic_binding Self, 0 [symbolic] -// CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 0, %Self [symbolic] -// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] -// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] -// CHECK:STDOUT: %B.impl_witness: = impl_witness file.%B.impl_witness_table [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .A = %A.decl -// CHECK:STDOUT: .B = %B.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %A.decl: type = interface_decl @A [concrete = constants.%A.type] {} {} -// CHECK:STDOUT: %B.decl: type = interface_decl @B [concrete = constants.%B.type] {} {} -// CHECK:STDOUT: impl_decl @empty_tuple.type.as.B.impl [concrete] {} { -// CHECK:STDOUT: %.loc9_7.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] -// CHECK:STDOUT: %.loc9_7.2: type = converted %.loc9_7.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] -// CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B.type] -// CHECK:STDOUT: } -// CHECK:STDOUT: %B.impl_witness_table = impl_witness_table (), @empty_tuple.type.as.B.impl [concrete] -// CHECK:STDOUT: %B.impl_witness: = impl_witness %B.impl_witness_table [concrete = constants.%B.impl_witness] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: interface @A; -// CHECK:STDOUT: -// CHECK:STDOUT: interface @B { -// CHECK:STDOUT: %Self: %B.type = symbolic_binding Self, 0 [symbolic = constants.%Self] -// CHECK:STDOUT: %B.require0.decl = require_decl @B.require0 [concrete] { -// CHECK:STDOUT: require %Self.as_type impls <@A> -// CHECK:STDOUT: } { -// CHECK:STDOUT: %Self.as_type: type = facet_access_type @B.%Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] -// CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A.type] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = %Self -// CHECK:STDOUT: .A = -// CHECK:STDOUT: witness = () -// CHECK:STDOUT: -// CHECK:STDOUT: !requires: -// CHECK:STDOUT: @B.require0 { -// CHECK:STDOUT: require @B.require0.%Self.as_type impls <@A> -// CHECK:STDOUT: } -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: generic require @B.require0(@B.%Self: %B.type) { -// CHECK:STDOUT: %Self: %B.type = symbolic_binding Self, 0 [symbolic = %Self (constants.%Self)] -// CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 0, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: impl @empty_tuple.type.as.B.impl: %.loc9_7.2 as %B.ref { -// CHECK:STDOUT: !members: -// CHECK:STDOUT: witness = file.%B.impl_witness -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: specific @B.require0(constants.%Self) { -// CHECK:STDOUT: %Self => constants.%Self -// CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_incomplete_extend_interface.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %A.type: type = facet_type <@A> [concrete] -// CHECK:STDOUT: %B.type: type = facet_type <@B> [concrete] -// CHECK:STDOUT: %Self: %B.type = symbolic_binding Self, 0 [symbolic] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .A = %A.decl -// CHECK:STDOUT: .B = %B.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %A.decl: type = interface_decl @A [concrete = constants.%A.type] {} {} -// CHECK:STDOUT: %B.decl: type = interface_decl @B [concrete = constants.%B.type] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: interface @A; -// CHECK:STDOUT: -// CHECK:STDOUT: interface @B { -// CHECK:STDOUT: %Self: %B.type = symbolic_binding Self, 0 [symbolic = constants.%Self] -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = %Self -// CHECK:STDOUT: .A = -// CHECK:STDOUT: witness = () -// CHECK:STDOUT: -// CHECK:STDOUT: !requires: -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_incomplete_interface_in_where.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %A.type: type = facet_type <@A> [concrete] -// CHECK:STDOUT: %B.type: type = facet_type <@B> [concrete] -// CHECK:STDOUT: %Self: %B.type = symbolic_binding Self, 0 [symbolic] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .A = %A.decl -// CHECK:STDOUT: .B = %B.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %A.decl: type = interface_decl @A [concrete = constants.%A.type] {} {} -// CHECK:STDOUT: %B.decl: type = interface_decl @B [concrete = constants.%B.type] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: interface @A; -// CHECK:STDOUT: -// CHECK:STDOUT: interface @B { -// CHECK:STDOUT: %Self: %B.type = symbolic_binding Self, 0 [symbolic = constants.%Self] -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = %Self -// CHECK:STDOUT: .A = -// CHECK:STDOUT: witness = () -// CHECK:STDOUT: -// CHECK:STDOUT: !requires: -// CHECK:STDOUT: } -// CHECK:STDOUT: From 05af854cd6b87140c2994db8eaec6e303b862660 Mon Sep 17 00:00:00 2001 From: danakj Date: Thu, 20 Nov 2025 10:03:57 -0500 Subject: [PATCH 22/31] try-move-requirecompletetype --- toolchain/check/handle_require.cpp | 44 ++++++----- .../check/testdata/impl/import_generic.carbon | 52 +++++-------- toolchain/check/type_completion.cpp | 78 ------------------- 3 files changed, 44 insertions(+), 130 deletions(-) diff --git a/toolchain/check/handle_require.cpp b/toolchain/check/handle_require.cpp index e5fbfbe4ba4a3..21881eb5af928 100644 --- a/toolchain/check/handle_require.cpp +++ b/toolchain/check/handle_require.cpp @@ -163,7 +163,7 @@ struct ValidateRequireResult { static auto ValidateRequire(Context& context, SemIR::LocId loc_id, SemIR::TypeInstId self_inst_id, SemIR::InstId constraint_inst_id, - SemIR::InstId scope_inst_id, bool extend) + SemIR::InstId scope_inst_id) -> std::optional { auto constraint_constant_value_id = context.constant_values().Get(constraint_inst_id); @@ -210,22 +210,6 @@ static auto ValidateRequire(Context& context, SemIR::LocId loc_id, return std::nullopt; } - if (extend) { - if (!RequireCompleteType( - context, constraint_type_id, SemIR::LocId(constraint_inst_id), [&] { - CARBON_DIAGNOSTIC(RequireImplsIncompleteFacetType, Error, - "`extend require` of incomplete facet type {0}", - InstIdAsType); - return context.emitter().Build(constraint_inst_id, - RequireImplsIncompleteFacetType, - constraint_inst_id); - })) { - // The constraint is invalid, and a diagnostic was emitted by - // RequireCompleteType(). - return std::nullopt; - } - } - if (scope_inst_id == SemIR::ErrorInst::InstId) { // `require` is in the wrong scope. return std::nullopt; @@ -257,10 +241,8 @@ auto HandleParseNode(Context& context, Parse::RequireDeclId node_id) -> bool { auto scope_inst_id = context.node_stack().Pop(); - bool extend = introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend); - auto validated = ValidateRequire(context, node_id, self_inst_id, - constraint_inst_id, scope_inst_id, extend); + constraint_inst_id, scope_inst_id); if (!validated) { DiscardGenericDecl(context); return true; @@ -273,6 +255,8 @@ auto HandleParseNode(Context& context, Parse::RequireDeclId node_id) -> bool { return true; } + bool extend = introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend); + auto require_impls_decl = SemIR::RequireImplsDecl{// To be filled in after. .require_impls_id = SemIR::RequireImplsId::None, @@ -291,6 +275,26 @@ auto HandleParseNode(Context& context, Parse::RequireDeclId node_id) -> bool { require_impls_decl.require_impls_id = require_impls_id; ReplaceInstBeforeConstantUse(context, decl_id, require_impls_decl); + // We look for a complete type after BuildGenericDecl, so that the resulting + // RequireCompleteType instruction is part of the enclosing interface or named + // constraint generic definition. Then requiring enclosing entity to be + // complete will resolve that definition and also construct a specific for the + // `constraint_inst_id`, finding any monomorphization errors that result. + if (extend) { + if (!RequireCompleteType( + context, constraint_type_id, SemIR::LocId(constraint_inst_id), [&] { + CARBON_DIAGNOSTIC(RequireImplsIncompleteFacetType, Error, + "`extend require` of incomplete facet type {0}", + InstIdAsType); + return context.emitter().Build(constraint_inst_id, + RequireImplsIncompleteFacetType, + constraint_inst_id); + })) { + DiscardGenericDecl(context); + return true; + } + } + context.require_impls_stack().AppendToTop(require_impls_id); return true; diff --git a/toolchain/check/testdata/impl/import_generic.carbon b/toolchain/check/testdata/impl/import_generic.carbon index b7bdbf5188daa..587517317a79a 100644 --- a/toolchain/check/testdata/impl/import_generic.carbon +++ b/toolchain/check/testdata/impl/import_generic.carbon @@ -265,6 +265,8 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: !definition: // CHECK:STDOUT: %N.type: type = facet_type <@N, @N(%T.loc14_14.1)> [symbolic = %N.type (constants.%N.type.b8d)] // CHECK:STDOUT: %Self.loc14_24.2: @N.%N.type (%N.type.b8d) = symbolic_binding Self, 1 [symbolic = %Self.loc14_24.2 (constants.%Self.aa1)] +// CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T.loc14_14.1)> [symbolic = %I.type (constants.%I.type.070)] +// CHECK:STDOUT: %require_complete: = require_complete_type %I.type [symbolic = %require_complete (constants.%require_complete.c94)] // CHECK:STDOUT: // CHECK:STDOUT: constraint { // CHECK:STDOUT: %Self.loc14_24.1: @N.%N.type (%N.type.b8d) = symbolic_binding Self, 1 [symbolic = %Self.loc14_24.2 (constants.%Self.aa1)] @@ -295,7 +297,6 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %Self: @N.require0.%N.type (%N.type.b8d) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.aa1)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %I.type.loc15_27.2: type = facet_type <@I, @I(%T)> [symbolic = %I.type.loc15_27.2 (constants.%I.type.070)] -// CHECK:STDOUT: %require_complete: = require_complete_type %I.type.loc15_27.2 [symbolic = %require_complete (constants.%require_complete.c94)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @C.as.I.impl.f3e(%T.loc8_14.1: type) { @@ -375,7 +376,6 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %Self => constants.%Self.aa1 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %I.type.loc15_27.2 => constants.%I.type.070 -// CHECK:STDOUT: %require_complete => constants.%require_complete.c94 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- fail_import_generic.impl.carbon @@ -533,6 +533,8 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: !definition: // CHECK:STDOUT: %N.type: type = facet_type <@N, @N(%T)> [symbolic = %N.type (constants.%N.type.b8d23b.1)] // CHECK:STDOUT: %Self: @N.%N.type (%N.type.b8d23b.1) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.aa1)] +// CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T)> [symbolic = %I.type (constants.%I.type.070)] +// CHECK:STDOUT: %require_complete: = require_complete_type %I.type [symbolic = %require_complete (constants.%require_complete.c94)] // CHECK:STDOUT: // CHECK:STDOUT: constraint { // CHECK:STDOUT: !members: @@ -551,7 +553,6 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %Self: @N.require0.%N.type (%N.type.b8d23b.1) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.aa1)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T)> [symbolic = %I.type (constants.%I.type.070)] -// CHECK:STDOUT: %require_complete: = require_complete_type %I.type [symbolic = %require_complete (constants.%require_complete.c94)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @C.as.I.impl.f3ed6b.1(imports.%Main.import_ref.efcd44.2: type) [from "import_generic.carbon"] { @@ -709,7 +710,6 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %Self => constants.%Self.aa1 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %I.type => constants.%I.type.070 -// CHECK:STDOUT: %require_complete => constants.%require_complete.c94 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.I.impl.3f8440.1(constants.%T) { @@ -809,6 +809,8 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: !definition: // CHECK:STDOUT: %N.type: type = facet_type <@N, @N(%T.loc9_14.1)> [symbolic = %N.type (constants.%N.type.b8d)] // CHECK:STDOUT: %Self.loc9_24.2: @N.%N.type (%N.type.b8d) = symbolic_binding Self, 1 [symbolic = %Self.loc9_24.2 (constants.%Self.aa1)] +// CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T.loc9_14.1)> [symbolic = %I.type (constants.%I.type.070)] +// CHECK:STDOUT: %require_complete: = require_complete_type %I.type [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: // CHECK:STDOUT: constraint { // CHECK:STDOUT: %Self.loc9_24.1: @N.%N.type (%N.type.b8d) = symbolic_binding Self, 1 [symbolic = %Self.loc9_24.2 (constants.%Self.aa1)] @@ -839,7 +841,6 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %Self: @N.require0.%N.type (%N.type.b8d) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.aa1)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %I.type.loc10_27.2: type = facet_type <@I, @I(%T)> [symbolic = %I.type.loc10_27.2 (constants.%I.type.070)] -// CHECK:STDOUT: %require_complete: = require_complete_type %I.type.loc10_27.2 [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @C.as.I.impl(%T.loc7_14.1: type) { @@ -889,7 +890,6 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %Self => constants.%Self.aa1 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %I.type.loc10_27.2 => constants.%I.type.070 -// CHECK:STDOUT: %require_complete => constants.%require_complete // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- import_generic_with_different_specific.impl.carbon @@ -920,9 +920,9 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.aa1546.1 [symbolic] // CHECK:STDOUT: %ptr.3f2: type = ptr_type %ptr.4f0 [symbolic] // CHECK:STDOUT: %N.type.b8d23b.2: type = facet_type <@N, @N(%ptr.3f2)> [symbolic] +// CHECK:STDOUT: %Self.aa1546.2: %N.type.b8d23b.2 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %I.type.71a: type = facet_type <@I, @I(%ptr.3f2)> [symbolic] // CHECK:STDOUT: %require_complete.08c: = require_complete_type %I.type.71a [symbolic] -// CHECK:STDOUT: %Self.aa1546.2: %N.type.b8d23b.2 = symbolic_binding Self, 1 [symbolic] // CHECK:STDOUT: %require_complete.a37: = require_complete_type %N.type.b8d23b.2 [symbolic] // CHECK:STDOUT: %I.impl_witness.524: = impl_witness file.%I.impl_witness_table.loc6, @C.as.I.impl.3f8(%T) [symbolic] // CHECK:STDOUT: } @@ -1006,6 +1006,8 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: !definition: // CHECK:STDOUT: %N.type: type = facet_type <@N, @N(%T)> [symbolic = %N.type (constants.%N.type.b8d23b.1)] // CHECK:STDOUT: %Self: @N.%N.type (%N.type.b8d23b.1) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.aa1546.1)] +// CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T)> [symbolic = %I.type (constants.%I.type.070)] +// CHECK:STDOUT: %require_complete: = require_complete_type %I.type [symbolic = %require_complete (constants.%require_complete.c94)] // CHECK:STDOUT: // CHECK:STDOUT: constraint { // CHECK:STDOUT: !members: @@ -1024,7 +1026,6 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %Self: @N.require0.%N.type (%N.type.b8d23b.1) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.aa1546.1)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T)> [symbolic = %I.type (constants.%I.type.070)] -// CHECK:STDOUT: %require_complete: = require_complete_type %I.type [symbolic = %require_complete (constants.%require_complete.c94)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @C.as.I.impl.f3e(imports.%Main.import_ref.efcd44.2: type) [from "import_generic_with_different_specific.carbon"] { @@ -1120,7 +1121,6 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %Self => constants.%Self.aa1546.1 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %I.type => constants.%I.type.070 -// CHECK:STDOUT: %require_complete => constants.%require_complete.c94 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N(constants.%ptr.3f2) { @@ -1129,6 +1129,8 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: !definition: // CHECK:STDOUT: %N.type => constants.%N.type.b8d23b.2 // CHECK:STDOUT: %Self => constants.%Self.aa1546.2 +// CHECK:STDOUT: %I.type => constants.%I.type.71a +// CHECK:STDOUT: %require_complete => constants.%require_complete.08c // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @C.as.I.impl.3f8(constants.%T) { @@ -1140,15 +1142,6 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %I.impl_witness => constants.%I.impl_witness.524 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: specific @N.require0(constants.%ptr.3f2, constants.%Self.aa1546.1) { -// CHECK:STDOUT: %T => constants.%ptr.3f2 -// CHECK:STDOUT: %N.type => constants.%N.type.b8d23b.2 -// CHECK:STDOUT: %Self => constants.%Self.aa1546.1 -// CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type -// CHECK:STDOUT: %I.type => constants.%I.type.71a -// CHECK:STDOUT: %require_complete => constants.%require_complete.08c -// CHECK:STDOUT: } -// CHECK:STDOUT: // CHECK:STDOUT: specific @I(constants.%ptr.3f2) { // CHECK:STDOUT: %T => constants.%ptr.3f2 // CHECK:STDOUT: } @@ -1249,6 +1242,8 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: !definition: // CHECK:STDOUT: %N.type: type = facet_type <@N, @N(%T.loc7_14.1)> [symbolic = %N.type (constants.%N.type.b8d)] // CHECK:STDOUT: %Self.loc7_24.2: @N.%N.type (%N.type.b8d) = symbolic_binding Self, 1 [symbolic = %Self.loc7_24.2 (constants.%Self.aa1)] +// CHECK:STDOUT: %J.type: type = facet_type <@J, @J(%T.loc7_14.1)> [symbolic = %J.type (constants.%J.type.8ec)] +// CHECK:STDOUT: %require_complete: = require_complete_type %J.type [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: // CHECK:STDOUT: constraint { // CHECK:STDOUT: %Self.loc7_24.1: @N.%N.type (%N.type.b8d) = symbolic_binding Self, 1 [symbolic = %Self.loc7_24.2 (constants.%Self.aa1)] @@ -1279,7 +1274,6 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %Self: @N.require0.%N.type (%N.type.b8d) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.aa1)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %J.type.loc8_27.2: type = facet_type <@J, @J(%T)> [symbolic = %J.type.loc8_27.2 (constants.%J.type.8ec)] -// CHECK:STDOUT: %require_complete: = require_complete_type %J.type.loc8_27.2 [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @D.as.J.impl.b47(%T.loc15_14.1: type) { @@ -1325,7 +1319,6 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %Self => constants.%Self.aa1 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %J.type.loc8_27.2 => constants.%J.type.8ec -// CHECK:STDOUT: %require_complete => constants.%require_complete // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @D.as.J.impl.b47(constants.%T) { @@ -1372,8 +1365,8 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %require_complete.a37d6c.1: = require_complete_type %N.type.b8d23b.1 [symbolic] // CHECK:STDOUT: %J.impl_witness.b5245e.1: = impl_witness file.%J.impl_witness_table.loc29, @D.as.J.impl.3b0484.1(%T) [symbolic] // CHECK:STDOUT: %N.type.b8d23b.2: type = facet_type <@N, @N(%ptr)> [symbolic] -// CHECK:STDOUT: %require_complete.d4d: = require_complete_type %J.type.4fa [symbolic] // CHECK:STDOUT: %Self.aa1546.2: %N.type.b8d23b.2 = symbolic_binding Self, 1 [symbolic] +// CHECK:STDOUT: %require_complete.d4d: = require_complete_type %J.type.4fa [symbolic] // CHECK:STDOUT: %require_complete.a37d6c.2: = require_complete_type %N.type.b8d23b.2 [symbolic] // CHECK:STDOUT: %J.impl_witness.b5245e.2: = impl_witness file.%J.impl_witness_table.loc39, @D.as.J.impl.3b0484.2(%T) [symbolic] // CHECK:STDOUT: } @@ -1500,6 +1493,8 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: !definition: // CHECK:STDOUT: %N.type: type = facet_type <@N, @N(%T)> [symbolic = %N.type (constants.%N.type.b8d23b.1)] // CHECK:STDOUT: %Self: @N.%N.type (%N.type.b8d23b.1) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.aa1546.1)] +// CHECK:STDOUT: %J.type: type = facet_type <@J, @J(%T)> [symbolic = %J.type (constants.%J.type.8ec)] +// CHECK:STDOUT: %require_complete: = require_complete_type %J.type [symbolic = %require_complete (constants.%require_complete.387)] // CHECK:STDOUT: // CHECK:STDOUT: constraint { // CHECK:STDOUT: !members: @@ -1518,7 +1513,6 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %Self: @N.require0.%N.type (%N.type.b8d23b.1) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.aa1546.1)] // CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] // CHECK:STDOUT: %J.type: type = facet_type <@J, @J(%T)> [symbolic = %J.type (constants.%J.type.8ec)] -// CHECK:STDOUT: %require_complete: = require_complete_type %J.type [symbolic = %require_complete (constants.%require_complete.387)] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic impl @D.as.J.impl.b470bf.1(imports.%Main.import_ref.efcd44.2: type) [from "fail_import_generic_decl.carbon"] { @@ -1667,6 +1661,8 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: !definition: // CHECK:STDOUT: %N.type => constants.%N.type.b8d23b.1 // CHECK:STDOUT: %Self => constants.%Self.aa1546.1 +// CHECK:STDOUT: %J.type => constants.%J.type.8ec +// CHECK:STDOUT: %require_complete => constants.%require_complete.387 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @N.require0(constants.%T, constants.%Self.aa1546.1) { @@ -1675,7 +1671,6 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %Self => constants.%Self.aa1546.1 // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: %J.type => constants.%J.type.8ec -// CHECK:STDOUT: %require_complete => constants.%require_complete.387 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @D.as.J.impl.3b0484.1(constants.%T) { @@ -1691,6 +1686,8 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: !definition: // CHECK:STDOUT: %N.type => constants.%N.type.b8d23b.2 // CHECK:STDOUT: %Self => constants.%Self.aa1546.2 +// CHECK:STDOUT: %J.type => constants.%J.type.4fa +// CHECK:STDOUT: %require_complete => constants.%require_complete.d4d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @D.as.J.impl.3b0484.2(constants.%T) { @@ -1701,12 +1698,3 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %J.impl_witness => constants.%J.impl_witness.b5245e.2 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: specific @N.require0(constants.%ptr, constants.%Self.aa1546.1) { -// CHECK:STDOUT: %T => constants.%ptr -// CHECK:STDOUT: %N.type => constants.%N.type.b8d23b.2 -// CHECK:STDOUT: %Self => constants.%Self.aa1546.1 -// CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type -// CHECK:STDOUT: %J.type => constants.%J.type.4fa -// CHECK:STDOUT: %require_complete => constants.%require_complete.d4d -// CHECK:STDOUT: } -// CHECK:STDOUT: diff --git a/toolchain/check/type_completion.cpp b/toolchain/check/type_completion.cpp index 5eb5556417af2..b2749be67b729 100644 --- a/toolchain/check/type_completion.cpp +++ b/toolchain/check/type_completion.cpp @@ -72,53 +72,6 @@ static auto NoteIncompleteNamedConstraint( } } -// Makes a copy of a Specific from one generic to apply to another given -// `generic_id`, with a given `Self` argument appended to the end. -static auto MakeCopyOfSpecificAndAppendSelf( - Context& context, SemIR::LocId loc_id, SemIR::SpecificId specific_id, - SemIR::GenericId generic_id, SemIR::InstId self_id) -> SemIR::SpecificId { - auto source_specific_args_id = - context.specifics().GetArgsOrEmpty(specific_id); - auto source_specific_args = - context.inst_blocks().Get(source_specific_args_id); - - llvm::SmallVector arg_ids; - arg_ids.reserve(source_specific_args.size() + 1); - // Start with the enclosing arguments from the source Specific. - llvm::append_range(arg_ids, source_specific_args); - // Add the new `Self` argument. - arg_ids.push_back(self_id); - - return MakeSpecific(context, loc_id, generic_id, arg_ids); -} - -static auto GetRequireImplsSpecificSelf(Context& context, - const SemIR::RequireImpls& require) - -> SemIR::InstId { - const auto& require_generic = context.generics().Get(require.generic_id); - const auto& require_self_specific = - context.specifics().Get(require_generic.self_specific_id); - auto require_self_specific_args = - context.inst_blocks().Get(require_self_specific.args_id); - // The last argument of a `require` generic is always `Self`, as require can - // not have any parameters of its own, only enclosing parameters. - return require_self_specific_args.back(); -} - -static auto GetFacetTypeInSpecific(Context& context, - SemIR::SpecificId specific_id, - SemIR::InstId facet_type) - -> SemIR::FacetTypeId { - auto const_facet_type = SemIR::GetConstantValueInSpecific( - context.sem_ir(), specific_id, facet_type); - if (const_facet_type == SemIR::ErrorInst::ConstantId) { - return SemIR::FacetTypeId::None; - } - auto facet_type_in_specific = context.insts().GetAs( - context.constant_values().GetInstId(const_facet_type)); - return facet_type_in_specific.facet_type_id; -} - static auto RequireCompleteFacetType(Context& context, SemIR::LocId loc_id, const SemIR::FacetType& facet_type, MakeDiagnosticBuilderFn diagnoser) @@ -128,7 +81,6 @@ static auto RequireCompleteFacetType(Context& context, SemIR::LocId loc_id, SemIR::RequireImplsBlockId requires_block_id; }; llvm::SmallVector specifics; - int specifics_done = 0; llvm::SmallVector work = {facet_type.facet_type_id}; while (!work.empty()) { @@ -169,36 +121,6 @@ static auto RequireCompleteFacetType(Context& context, SemIR::LocId loc_id, {extends.specific_id, constraint.require_impls_block_id}); } } - - // Formulate a specific for each `require` declaration, as the specific may - // introduce errors. Then recurse on those specific facet types to find - // other interfaces/constraints that may contain other require declarations. - // We only formulate a specific for the facet type in the `require` - // declaration since `extend require` can only be applied to `Self`, so the - // self-type in these `require` declarations never uses any generic - // parameters. - auto requires_specifics = - llvm::ArrayRef(specifics).drop_front(specifics_done); - for (auto [specific_id, requires_block_id] : requires_specifics) { - for (auto require_impls_id : - context.require_impls_blocks().Get(requires_block_id)) { - const auto& require = context.require_impls().Get(require_impls_id); - if (require.extend_self) { - auto require_specific_id = MakeCopyOfSpecificAndAppendSelf( - context, loc_id, specific_id, require.generic_id, - GetRequireImplsSpecificSelf(context, require)); - // Construct the specific facet type. If None is returned, it means an - // error was diagnosed. - auto facet_type_id = GetFacetTypeInSpecific( - context, require_specific_id, require.facet_type_inst_id); - if (!facet_type_id.has_value()) { - return false; - } - work.push_back(facet_type_id); - } - } - } - specifics_done += requires_specifics.size(); } // Specific definitions are resolved from the interfaces in the facet type From a18bbae9bf121c17d1e89f96517349030db85892 Mon Sep 17 00:00:00 2001 From: danakj Date: Thu, 20 Nov 2025 14:31:27 -0500 Subject: [PATCH 23/31] works-but-import-crash --- toolchain/check/eval.cpp | 8 + toolchain/check/handle_require.cpp | 8 +- .../generic/extend_type_completion.carbon | 16 +- .../check/testdata/impl/incomplete.carbon | 46 ----- .../testdata/interface/incomplete.carbon | 193 ++++++++++++++++++ 5 files changed, 213 insertions(+), 58 deletions(-) diff --git a/toolchain/check/eval.cpp b/toolchain/check/eval.cpp index 1a8bfe02a326b..5e08925158f3c 100644 --- a/toolchain/check/eval.cpp +++ b/toolchain/check/eval.cpp @@ -867,6 +867,14 @@ static auto ResolveSpecificDeclForInst(EvalContext& eval_context, for (const auto& interface : info.self_impls_constraints) { ResolveSpecificDeclForSpecificId(eval_context, interface.specific_id); } + for (const auto& constraint : info.extend_named_constraints) { + ResolveSpecificDeclForSpecificId(eval_context, + constraint.specific_id); + } + for (const auto& constraint : info.self_impls_named_constraints) { + ResolveSpecificDeclForSpecificId(eval_context, + constraint.specific_id); + } break; } case CARBON_KIND(SemIR::SpecificId specific_id): { diff --git a/toolchain/check/handle_require.cpp b/toolchain/check/handle_require.cpp index 21881eb5af928..50a6c9fd31620 100644 --- a/toolchain/check/handle_require.cpp +++ b/toolchain/check/handle_require.cpp @@ -278,8 +278,9 @@ auto HandleParseNode(Context& context, Parse::RequireDeclId node_id) -> bool { // We look for a complete type after BuildGenericDecl, so that the resulting // RequireCompleteType instruction is part of the enclosing interface or named // constraint generic definition. Then requiring enclosing entity to be - // complete will resolve that definition and also construct a specific for the - // `constraint_inst_id`, finding any monomorphization errors that result. + // complete will resolve that definition (via ResolveSpecificDefinition()) and + // also construct a specific for the `constraint_inst_id`, finding any + // monomorphization errors that result. if (extend) { if (!RequireCompleteType( context, constraint_type_id, SemIR::LocId(constraint_inst_id), [&] { @@ -290,13 +291,12 @@ auto HandleParseNode(Context& context, Parse::RequireDeclId node_id) -> bool { RequireImplsIncompleteFacetType, constraint_inst_id); })) { - DiscardGenericDecl(context); + // DiscardGenericDecl(context); return true; } } context.require_impls_stack().AppendToTop(require_impls_id); - return true; } diff --git a/toolchain/check/testdata/generic/extend_type_completion.carbon b/toolchain/check/testdata/generic/extend_type_completion.carbon index 144e7307c70e3..360657d3bdf5f 100644 --- a/toolchain/check/testdata/generic/extend_type_completion.carbon +++ b/toolchain/check/testdata/generic/extend_type_completion.carbon @@ -57,13 +57,13 @@ library "[[@TEST_NAME]]"; interface K(T:! type) {} interface J(N:! i32) { - // CHECK:STDERR: fail_interface_extend_require_impls_does_need_complete_interface.carbon:[[@LINE+3]]:37: error: array bound of -1 is negative [ArrayBoundNegative] + // CHECK:STDERR: fail_interface_extend_require_impls_does_need_complete_interface.carbon:[[@LINE+3]]:24: error: array bound of -1 is negative [ArrayBoundNegative] // CHECK:STDERR: extend require impls K(array(i32, N)); - // CHECK:STDERR: ^ + // CHECK:STDERR: ^~~~~~~~~~~~~~~~ extend require impls K(array(i32, N)); } interface I(N:! i32) { - // CHECK:STDERR: fail_interface_extend_require_impls_does_need_complete_interface.carbon:[[@LINE+3]]:24: note: in `require` used here [ResolvingSpecificHere] + // CHECK:STDERR: fail_interface_extend_require_impls_does_need_complete_interface.carbon:[[@LINE+3]]:24: note: in `J(-1)` used here [ResolvingSpecificHere] // CHECK:STDERR: extend require impls J(N); // CHECK:STDERR: ^~~~ extend require impls J(N); @@ -75,7 +75,7 @@ interface I(N:! i32) { // need a location for the type in context.bind_name_map() to use as the // location to Convert(). // -// CHECK:STDERR: fail_interface_extend_require_impls_does_need_complete_interface.carbon:[[@LINE+4]]:1: note: in `require` used here [ResolvingSpecificHere] +// CHECK:STDERR: fail_interface_extend_require_impls_does_need_complete_interface.carbon:[[@LINE+4]]:1: note: in `I(-1)` used here [ResolvingSpecificHere] // CHECK:STDERR: var v: I(-1); // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: @@ -97,20 +97,20 @@ library "[[@TEST_NAME]]"; interface K(T:! type) {} constraint J(N:! i32) { - // CHECK:STDERR: fail_constraint_extend_require_impls_does_need_complete_interface.carbon:[[@LINE+3]]:37: error: array bound of -1 is negative [ArrayBoundNegative] + // CHECK:STDERR: fail_constraint_extend_require_impls_does_need_complete_interface.carbon:[[@LINE+3]]:24: error: array bound of -1 is negative [ArrayBoundNegative] // CHECK:STDERR: extend require impls K(array(i32, N)); - // CHECK:STDERR: ^ + // CHECK:STDERR: ^~~~~~~~~~~~~~~~ extend require impls K(array(i32, N)); } constraint I(N:! i32) { - // CHECK:STDERR: fail_constraint_extend_require_impls_does_need_complete_interface.carbon:[[@LINE+3]]:24: note: in `require` used here [ResolvingSpecificHere] + // CHECK:STDERR: fail_constraint_extend_require_impls_does_need_complete_interface.carbon:[[@LINE+3]]:24: note: in `{}` used here [ResolvingSpecificHere] // CHECK:STDERR: extend require impls J(N); // CHECK:STDERR: ^~~~ extend require impls J(N); } // I extends J extends K so the type of K is completed, but is invalid. -// CHECK:STDERR: fail_constraint_extend_require_impls_does_need_complete_interface.carbon:[[@LINE+4]]:1: note: in `require` used here [ResolvingSpecificHere] +// CHECK:STDERR: fail_constraint_extend_require_impls_does_need_complete_interface.carbon:[[@LINE+4]]:1: note: in `{}` used here [ResolvingSpecificHere] // CHECK:STDERR: var v: I(-1); // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: diff --git a/toolchain/check/testdata/impl/incomplete.carbon b/toolchain/check/testdata/impl/incomplete.carbon index 0273d467a45b6..f26016adc203b 100644 --- a/toolchain/check/testdata/impl/incomplete.carbon +++ b/toolchain/check/testdata/impl/incomplete.carbon @@ -208,22 +208,6 @@ interface B { require impls A; } -// --- fail_incomplete_extend_constraint.carbon -library "[[@TEST_NAME]]"; - -constraint A; - -interface B { - // CHECK:STDERR: fail_incomplete_extend_constraint.carbon:[[@LINE+7]]:24: error: facet type `A` cannot be identified in `require` declaration [RequireImplsUnidentifiedFacetType] - // CHECK:STDERR: extend require impls A; - // CHECK:STDERR: ^ - // CHECK:STDERR: fail_incomplete_extend_constraint.carbon:[[@LINE-6]]:1: note: constraint was forward declared here [NamedConstraintForwardDeclaredHere] - // CHECK:STDERR: constraint A; - // CHECK:STDERR: ^~~~~~~~~~~~~ - // CHECK:STDERR: - extend require impls A; -} - // CHECK:STDOUT: --- fail_empty_struct.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { @@ -876,33 +860,3 @@ interface B { // CHECK:STDOUT: // CHECK:STDOUT: constraint @A; // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_incomplete_extend_constraint.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %A.type: type = facet_type <@A> [concrete] -// CHECK:STDOUT: %B.type: type = facet_type <@B> [concrete] -// CHECK:STDOUT: %Self: %B.type = symbolic_binding Self, 0 [symbolic] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .A = %A.decl -// CHECK:STDOUT: .B = %B.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %A.decl: type = constraint_decl @A [concrete = constants.%A.type] {} {} -// CHECK:STDOUT: %B.decl: type = interface_decl @B [concrete = constants.%B.type] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: interface @B { -// CHECK:STDOUT: %Self: %B.type = symbolic_binding Self, 0 [symbolic = constants.%Self] -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = %Self -// CHECK:STDOUT: .A = -// CHECK:STDOUT: witness = () -// CHECK:STDOUT: -// CHECK:STDOUT: !requires: -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: constraint @A; -// CHECK:STDOUT: diff --git a/toolchain/check/testdata/interface/incomplete.carbon b/toolchain/check/testdata/interface/incomplete.carbon index 484458dd06cf9..138cd83d97c5f 100644 --- a/toolchain/check/testdata/interface/incomplete.carbon +++ b/toolchain/check/testdata/interface/incomplete.carbon @@ -28,7 +28,49 @@ interface I { let T:! C; } +// --- fail_incomplete_extend_constraint.carbon +library "[[@TEST_NAME]]"; + +constraint A; + +interface B { + // CHECK:STDERR: fail_incomplete_extend_constraint.carbon:[[@LINE+7]]:24: error: facet type `A` cannot be identified in `require` declaration [RequireImplsUnidentifiedFacetType] + // CHECK:STDERR: extend require impls A; + // CHECK:STDERR: ^ + // CHECK:STDERR: fail_incomplete_extend_constraint.carbon:[[@LINE-6]]:1: note: constraint was forward declared here [NamedConstraintForwardDeclaredHere] + // CHECK:STDERR: constraint A; + // CHECK:STDERR: ^~~~~~~~~~~~~ + // CHECK:STDERR: + extend require impls A; +} + +// --- fail_extend_require_enclosing.carbon +library "[[@TEST_NAME]]"; + +interface A { + // CHECK:STDERR: fail_extend_require_enclosing.carbon:[[@LINE+7]]:24: error: `extend require` of incomplete facet type `A` [RequireImplsIncompleteFacetType] + // CHECK:STDERR: extend require impls A; + // CHECK:STDERR: ^ + // CHECK:STDERR: fail_extend_require_enclosing.carbon:[[@LINE-4]]:1: note: interface is currently being defined [InterfaceIncompleteWithinDefinition] + // CHECK:STDERR: interface A { + // CHECK:STDERR: ^~~~~~~~~~~~~ + // CHECK:STDERR: + extend require impls A; +} +// --- fail_extend_require_enclosing_generic.carbon +library "[[@TEST_NAME]]"; + +interface A(T:! type) { + // CHECK:STDERR: fail_extend_require_enclosing_generic.carbon:[[@LINE+7]]:24: error: `extend require` of incomplete facet type `A({})` [RequireImplsIncompleteFacetType] + // CHECK:STDERR: extend require impls A({}); + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: fail_extend_require_enclosing_generic.carbon:[[@LINE-4]]:1: note: interface is currently being defined [InterfaceIncompleteWithinDefinition] + // CHECK:STDERR: interface A(T:! type) { + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + extend require impls A({}); +} // CHECK:STDOUT: --- fail_incomplete_type.carbon // CHECK:STDOUT: @@ -72,3 +114,154 @@ interface I { // CHECK:STDOUT: // CHECK:STDOUT: specific @T(constants.%Self) {} // CHECK:STDOUT: +// CHECK:STDOUT: --- fail_incomplete_extend_constraint.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %A.type: type = facet_type <@A> [concrete] +// CHECK:STDOUT: %B.type: type = facet_type <@B> [concrete] +// CHECK:STDOUT: %Self: %B.type = symbolic_binding Self, 0 [symbolic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .A = %A.decl +// CHECK:STDOUT: .B = %B.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %A.decl: type = constraint_decl @A [concrete = constants.%A.type] {} {} +// CHECK:STDOUT: %B.decl: type = interface_decl @B [concrete = constants.%B.type] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @B { +// CHECK:STDOUT: %Self: %B.type = symbolic_binding Self, 0 [symbolic = constants.%Self] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .A = +// CHECK:STDOUT: witness = () +// CHECK:STDOUT: +// CHECK:STDOUT: !requires: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: constraint @A; +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_extend_require_enclosing.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %A.type: type = facet_type <@A> [concrete] +// CHECK:STDOUT: %Self: %A.type = symbolic_binding Self, 0 [symbolic] +// CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 0, %Self [symbolic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .A = %A.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %A.decl: type = interface_decl @A [concrete = constants.%A.type] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @A { +// CHECK:STDOUT: %Self: %A.type = symbolic_binding Self, 0 [symbolic = constants.%Self] +// CHECK:STDOUT: %A.require0.decl = require_decl @A.require0 [concrete] { +// CHECK:STDOUT: require %Self.as_type impls <@A> +// CHECK:STDOUT: } { +// CHECK:STDOUT: %Self.as_type: type = facet_access_type @A.%Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] +// CHECK:STDOUT: %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .A = +// CHECK:STDOUT: witness = () +// CHECK:STDOUT: +// CHECK:STDOUT: !requires: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic require @A.require0(@A.%Self: %A.type) { +// CHECK:STDOUT: %Self: %A.type = symbolic_binding Self, 0 [symbolic = %Self (constants.%Self)] +// CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 0, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @A.require0(constants.%Self) { +// CHECK:STDOUT: %Self => constants.%Self +// CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_extend_require_enclosing_generic.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %type: type = facet_type [concrete] +// CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] +// CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] +// CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete] +// CHECK:STDOUT: %A.type.495: type = generic_interface_type @A [concrete] +// CHECK:STDOUT: %A.generic: %A.type.495 = struct_value () [concrete] +// CHECK:STDOUT: %A.type.c1c: type = facet_type <@A, @A(%T)> [symbolic] +// CHECK:STDOUT: %Self: %A.type.c1c = symbolic_binding Self, 1 [symbolic] +// CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] +// CHECK:STDOUT: %A.type.23f: type = facet_type <@A, @A(%empty_struct_type)> [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .A = %A.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %A.decl: %A.type.495 = interface_decl @A [concrete = constants.%A.generic] { +// CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] +// CHECK:STDOUT: %T.loc3_13.2: type = symbolic_binding T, 0 [symbolic = %T.loc3_13.1 (constants.%T)] +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic interface @A(%T.loc3_13.2: type) { +// CHECK:STDOUT: %T.loc3_13.1: type = symbolic_binding T, 0 [symbolic = %T.loc3_13.1 (constants.%T)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %A.type: type = facet_type <@A, @A(%T.loc3_13.1)> [symbolic = %A.type (constants.%A.type.c1c)] +// CHECK:STDOUT: %Self.loc3_23.2: @A.%A.type (%A.type.c1c) = symbolic_binding Self, 1 [symbolic = %Self.loc3_23.2 (constants.%Self)] +// CHECK:STDOUT: +// CHECK:STDOUT: interface { +// CHECK:STDOUT: %Self.loc3_23.1: @A.%A.type (%A.type.c1c) = symbolic_binding Self, 1 [symbolic = %Self.loc3_23.2 (constants.%Self)] +// CHECK:STDOUT: %A.require0.decl = require_decl @A.require0 [concrete] { +// CHECK:STDOUT: require %Self.as_type impls <@A, @A(constants.%empty_struct_type)> +// CHECK:STDOUT: } { +// CHECK:STDOUT: %Self.as_type: type = facet_access_type @A.%Self.loc3_23.1 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] +// CHECK:STDOUT: %A.ref: %A.type.495 = name_ref A, file.%A.decl [concrete = constants.%A.generic] +// CHECK:STDOUT: %.loc11_27: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] +// CHECK:STDOUT: %.loc11_28: type = converted %.loc11_27, constants.%empty_struct_type [concrete = constants.%empty_struct_type] +// CHECK:STDOUT: %A.type.loc11_28: type = facet_type <@A, @A(constants.%empty_struct_type)> [concrete = constants.%A.type.23f] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self.loc3_23.1 +// CHECK:STDOUT: .A = +// CHECK:STDOUT: witness = () +// CHECK:STDOUT: +// CHECK:STDOUT: !requires: +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic require @A.require0(@A.%T.loc3_13.2: type, @A.%Self.loc3_23.1: @A.%A.type (%A.type.c1c)) { +// CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] +// CHECK:STDOUT: %A.type.loc11_18: type = facet_type <@A, @A(%T)> [symbolic = %A.type.loc11_18 (constants.%A.type.c1c)] +// CHECK:STDOUT: %Self: @A.require0.%A.type.loc11_18 (%A.type.c1c) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self)] +// CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @A(constants.%T) { +// CHECK:STDOUT: %T.loc3_13.1 => constants.%T +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @A(constants.%empty_struct_type) { +// CHECK:STDOUT: %T.loc3_13.1 => constants.%empty_struct_type +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @A.require0(constants.%T, constants.%Self) { +// CHECK:STDOUT: %T => constants.%T +// CHECK:STDOUT: %A.type.loc11_18 => constants.%A.type.c1c +// CHECK:STDOUT: %Self => constants.%Self +// CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type +// CHECK:STDOUT: } +// CHECK:STDOUT: From 3939132e838349af8fbcbe24a4dd8c059b2acf0d Mon Sep 17 00:00:00 2001 From: danakj Date: Thu, 20 Nov 2025 16:31:12 -0500 Subject: [PATCH 24/31] wip --- .../check/testdata/impl/import_generic.carbon | 38 +++++++++++++++++++ ...c_const.carbon => use_assoc_entity.carbon} | 0 2 files changed, 38 insertions(+) rename toolchain/check/testdata/impl/{use_assoc_const.carbon => use_assoc_entity.carbon} (100%) diff --git a/toolchain/check/testdata/impl/import_generic.carbon b/toolchain/check/testdata/impl/import_generic.carbon index 587517317a79a..b274cbe84697e 100644 --- a/toolchain/check/testdata/impl/import_generic.carbon +++ b/toolchain/check/testdata/impl/import_generic.carbon @@ -12,6 +12,44 @@ // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/import_generic.carbon +// --- basic_import_generic_interface.carbon + +library "[[@TEST_NAME]]"; + +interface I(T:! type) {} +interface J(T:! type) { + extend require impls I(T); +} + +// --- basic_import_generic_interface.impl.carbon + +impl library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_import_generic.impl.carbon:[[@LINE+4]]:1: error: redeclaration of imported impl [RedeclImportedImpl] +// CHECK:STDERR: impl forall [T:! type] C as I(T); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +impl {} as J({}); + +// --- basic_import_generic_constraint.carbon + +library "[[@TEST_NAME]]"; + +interface I(T:! type) {} +constraint J(T:! type) { + extend require impls I(T); +} + +// --- basic_import_generic_constraint.impl.carbon + +impl library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_import_generic.impl.carbon:[[@LINE+4]]:1: error: redeclaration of imported impl [RedeclImportedImpl] +// CHECK:STDERR: impl forall [T:! type] C as I(T); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +impl {} as J({}); + // --- import_generic.carbon library "[[@TEST_NAME]]"; diff --git a/toolchain/check/testdata/impl/use_assoc_const.carbon b/toolchain/check/testdata/impl/use_assoc_entity.carbon similarity index 100% rename from toolchain/check/testdata/impl/use_assoc_const.carbon rename to toolchain/check/testdata/impl/use_assoc_entity.carbon From ec8e2d4fd32cc5aab972407a708f1047d30a8eee Mon Sep 17 00:00:00 2001 From: danakj Date: Thu, 20 Nov 2025 17:16:10 -0500 Subject: [PATCH 25/31] works --- .../check/testdata/impl/import_generic.carbon | 534 +++++++++++++++++- .../testdata/impl/use_assoc_entity.carbon | 4 +- 2 files changed, 520 insertions(+), 18 deletions(-) diff --git a/toolchain/check/testdata/impl/import_generic.carbon b/toolchain/check/testdata/impl/import_generic.carbon index b274cbe84697e..101e48fb09014 100644 --- a/toolchain/check/testdata/impl/import_generic.carbon +++ b/toolchain/check/testdata/impl/import_generic.carbon @@ -13,7 +13,6 @@ // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/import_generic.carbon // --- basic_import_generic_interface.carbon - library "[[@TEST_NAME]]"; interface I(T:! type) {} @@ -22,17 +21,11 @@ interface J(T:! type) { } // --- basic_import_generic_interface.impl.carbon - impl library "[[@TEST_NAME]]"; -// CHECK:STDERR: fail_import_generic.impl.carbon:[[@LINE+4]]:1: error: redeclaration of imported impl [RedeclImportedImpl] -// CHECK:STDERR: impl forall [T:! type] C as I(T); -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// CHECK:STDERR: -impl {} as J({}); +impl {} as J({}) {} // --- basic_import_generic_constraint.carbon - library "[[@TEST_NAME]]"; interface I(T:! type) {} @@ -41,14 +34,9 @@ constraint J(T:! type) { } // --- basic_import_generic_constraint.impl.carbon - impl library "[[@TEST_NAME]]"; -// CHECK:STDERR: fail_import_generic.impl.carbon:[[@LINE+4]]:1: error: redeclaration of imported impl [RedeclImportedImpl] -// CHECK:STDERR: impl forall [T:! type] C as I(T); -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// CHECK:STDERR: -impl {} as J({}); +impl {} as J({}) {} // --- import_generic.carbon @@ -195,6 +183,520 @@ impl forall [T:! type] D as N(T) {} // CHECK:STDERR: impl forall [T:! type] D as N(T*) {} +// CHECK:STDOUT: --- basic_import_generic_interface.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %type: type = facet_type [concrete] +// CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] +// CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] +// CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete] +// CHECK:STDOUT: %I.type.dac: type = generic_interface_type @I [concrete] +// CHECK:STDOUT: %I.generic: %I.type.dac = struct_value () [concrete] +// CHECK:STDOUT: %I.type.070: type = facet_type <@I, @I(%T)> [symbolic] +// CHECK:STDOUT: %Self.269: %I.type.070 = symbolic_binding Self, 1 [symbolic] +// CHECK:STDOUT: %J.type.2b8: type = generic_interface_type @J [concrete] +// CHECK:STDOUT: %J.generic: %J.type.2b8 = struct_value () [concrete] +// CHECK:STDOUT: %J.type.8ec: type = facet_type <@J, @J(%T)> [symbolic] +// CHECK:STDOUT: %Self.f68: %J.type.8ec = symbolic_binding Self, 1 [symbolic] +// CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.f68 [symbolic] +// CHECK:STDOUT: %require_complete: = require_complete_type %I.type.070 [symbolic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .I = %I.decl +// CHECK:STDOUT: .J = %J.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %I.decl: %I.type.dac = interface_decl @I [concrete = constants.%I.generic] { +// CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] +// CHECK:STDOUT: %T.loc3_13.2: type = symbolic_binding T, 0 [symbolic = %T.loc3_13.1 (constants.%T)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %J.decl: %J.type.2b8 = interface_decl @J [concrete = constants.%J.generic] { +// CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] +// CHECK:STDOUT: %T.loc4_13.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_13.1 (constants.%T)] +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic interface @I(%T.loc3_13.2: type) { +// CHECK:STDOUT: %T.loc3_13.1: type = symbolic_binding T, 0 [symbolic = %T.loc3_13.1 (constants.%T)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T.loc3_13.1)> [symbolic = %I.type (constants.%I.type.070)] +// CHECK:STDOUT: %Self.loc3_23.2: @I.%I.type (%I.type.070) = symbolic_binding Self, 1 [symbolic = %Self.loc3_23.2 (constants.%Self.269)] +// CHECK:STDOUT: +// CHECK:STDOUT: interface { +// CHECK:STDOUT: %Self.loc3_23.1: @I.%I.type (%I.type.070) = symbolic_binding Self, 1 [symbolic = %Self.loc3_23.2 (constants.%Self.269)] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self.loc3_23.1 +// CHECK:STDOUT: witness = () +// CHECK:STDOUT: +// CHECK:STDOUT: !requires: +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic interface @J(%T.loc4_13.2: type) { +// CHECK:STDOUT: %T.loc4_13.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_13.1 (constants.%T)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %J.type: type = facet_type <@J, @J(%T.loc4_13.1)> [symbolic = %J.type (constants.%J.type.8ec)] +// CHECK:STDOUT: %Self.loc4_23.2: @J.%J.type (%J.type.8ec) = symbolic_binding Self, 1 [symbolic = %Self.loc4_23.2 (constants.%Self.f68)] +// CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T.loc4_13.1)> [symbolic = %I.type (constants.%I.type.070)] +// CHECK:STDOUT: %require_complete: = require_complete_type %I.type [symbolic = %require_complete (constants.%require_complete)] +// CHECK:STDOUT: +// CHECK:STDOUT: interface { +// CHECK:STDOUT: %Self.loc4_23.1: @J.%J.type (%J.type.8ec) = symbolic_binding Self, 1 [symbolic = %Self.loc4_23.2 (constants.%Self.f68)] +// CHECK:STDOUT: %J.require0.decl = require_decl @J.require0 [concrete] { +// CHECK:STDOUT: require %Self.as_type impls <@I, @I(constants.%T)> +// CHECK:STDOUT: } { +// CHECK:STDOUT: %Self.as_type: type = facet_access_type @J.%Self.loc4_23.1 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] +// CHECK:STDOUT: %I.ref: %I.type.dac = name_ref I, file.%I.decl [concrete = constants.%I.generic] +// CHECK:STDOUT: %T.ref: type = name_ref T, @J.%T.loc4_13.2 [symbolic = %T (constants.%T)] +// CHECK:STDOUT: %I.type.loc5_27.1: type = facet_type <@I, @I(constants.%T)> [symbolic = %I.type.loc5_27.2 (constants.%I.type.070)] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self.loc4_23.1 +// CHECK:STDOUT: .I = +// CHECK:STDOUT: .T = +// CHECK:STDOUT: witness = () +// CHECK:STDOUT: +// CHECK:STDOUT: !requires: +// CHECK:STDOUT: @J.require0 { +// CHECK:STDOUT: require @J.require0.%Self.as_type impls <@I, @I(constants.%T)> +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic require @J.require0(@J.%T.loc4_13.2: type, @J.%Self.loc4_23.1: @J.%J.type (%J.type.8ec)) { +// CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] +// CHECK:STDOUT: %J.type: type = facet_type <@J, @J(%T)> [symbolic = %J.type (constants.%J.type.8ec)] +// CHECK:STDOUT: %Self: @J.require0.%J.type (%J.type.8ec) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.f68)] +// CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] +// CHECK:STDOUT: %I.type.loc5_27.2: type = facet_type <@I, @I(%T)> [symbolic = %I.type.loc5_27.2 (constants.%I.type.070)] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @I(constants.%T) { +// CHECK:STDOUT: %T.loc3_13.1 => constants.%T +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %I.type => constants.%I.type.070 +// CHECK:STDOUT: %Self.loc3_23.2 => constants.%Self.269 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @J(constants.%T) { +// CHECK:STDOUT: %T.loc4_13.1 => constants.%T +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @J.require0(constants.%T, constants.%Self.f68) { +// CHECK:STDOUT: %T => constants.%T +// CHECK:STDOUT: %J.type => constants.%J.type.8ec +// CHECK:STDOUT: %Self => constants.%Self.f68 +// CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type +// CHECK:STDOUT: %I.type.loc5_27.2 => constants.%I.type.070 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- basic_import_generic_interface.impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] +// CHECK:STDOUT: %J.type.2b8: type = generic_interface_type @J [concrete] +// CHECK:STDOUT: %J.generic: %J.type.2b8 = struct_value () [concrete] +// CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] +// CHECK:STDOUT: %J.type.8ec: type = facet_type <@J, @J(%T)> [symbolic] +// CHECK:STDOUT: %Self.f68: %J.type.8ec = symbolic_binding Self, 1 [symbolic] +// CHECK:STDOUT: %I.type.070: type = facet_type <@I, @I(%T)> [symbolic] +// CHECK:STDOUT: %Self.269: %I.type.070 = symbolic_binding Self, 1 [symbolic] +// CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.f68 [symbolic] +// CHECK:STDOUT: %require_complete: = require_complete_type %I.type.070 [symbolic] +// CHECK:STDOUT: %J.type.457: type = facet_type <@J, @J(%empty_struct_type)> [concrete] +// CHECK:STDOUT: %Self.07b: %J.type.457 = symbolic_binding Self, 1 [symbolic] +// CHECK:STDOUT: %I.type.399: type = facet_type <@I, @I(%empty_struct_type)> [concrete] +// CHECK:STDOUT: %Self.32d: %I.type.399 = symbolic_binding Self, 1 [symbolic] +// CHECK:STDOUT: %complete_type: = complete_type_witness %I.type.399 [concrete] +// CHECK:STDOUT: %J.impl_witness: = impl_witness file.%J.impl_witness_table [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Main.I = import_ref Main//basic_import_generic_interface, I, unloaded +// CHECK:STDOUT: %Main.J: %J.type.2b8 = import_ref Main//basic_import_generic_interface, J, loaded [concrete = constants.%J.generic] +// CHECK:STDOUT: %Main.import_ref.769 = import_ref Main//basic_import_generic_interface, loc3_23, unloaded +// CHECK:STDOUT: %Main.import_ref.efcd44.1: type = import_ref Main//basic_import_generic_interface, loc3_13, loaded [symbolic = @I.%T (constants.%T)] +// CHECK:STDOUT: %Main.import_ref.ce5: type = import_ref Main//basic_import_generic_interface, loc5_18, loaded [symbolic = constants.%Self.binding.as_type] +// CHECK:STDOUT: %Main.import_ref.efcd44.2: type = import_ref Main//basic_import_generic_interface, loc4_13, loaded [symbolic = @J.%T (constants.%T)] +// CHECK:STDOUT: %Main.import_ref.cc4: @J.%J.type (%J.type.8ec) = import_ref Main//basic_import_generic_interface, loc4_23, loaded [symbolic = @J.%Self (constants.%Self.f68)] +// CHECK:STDOUT: %Main.import_ref.b3b = import_ref Main//basic_import_generic_interface, loc4_23, unloaded +// CHECK:STDOUT: %Main.import_ref.efcd44.3: type = import_ref Main//basic_import_generic_interface, loc4_13, loaded [symbolic = @J.%T (constants.%T)] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .I = imports.%Main.I +// CHECK:STDOUT: .J = imports.%Main.J +// CHECK:STDOUT: } +// CHECK:STDOUT: %default.import.loc1_46.1 = import +// CHECK:STDOUT: %default.import.loc1_46.2 = import +// CHECK:STDOUT: impl_decl @empty_struct_type.as.J.impl [concrete] {} { +// CHECK:STDOUT: %.loc3_7.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] +// CHECK:STDOUT: %.loc3_7.2: type = converted %.loc3_7.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] +// CHECK:STDOUT: %J.ref: %J.type.2b8 = name_ref J, imports.%Main.J [concrete = constants.%J.generic] +// CHECK:STDOUT: %.loc3_15: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] +// CHECK:STDOUT: %.loc3_16: type = converted %.loc3_15, constants.%empty_struct_type [concrete = constants.%empty_struct_type] +// CHECK:STDOUT: %J.type: type = facet_type <@J, @J(constants.%empty_struct_type)> [concrete = constants.%J.type.457] +// CHECK:STDOUT: } +// CHECK:STDOUT: %J.impl_witness_table = impl_witness_table (), @empty_struct_type.as.J.impl [concrete] +// CHECK:STDOUT: %J.impl_witness: = impl_witness %J.impl_witness_table [concrete = constants.%J.impl_witness] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic interface @J(imports.%Main.import_ref.efcd44.3: type) [from "basic_import_generic_interface.carbon"] { +// CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %J.type: type = facet_type <@J, @J(%T)> [symbolic = %J.type (constants.%J.type.8ec)] +// CHECK:STDOUT: %Self: @J.%J.type (%J.type.8ec) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.f68)] +// CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T)> [symbolic = %I.type (constants.%I.type.070)] +// CHECK:STDOUT: %require_complete: = require_complete_type %I.type [symbolic = %require_complete (constants.%require_complete)] +// CHECK:STDOUT: +// CHECK:STDOUT: interface { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = imports.%Main.import_ref.b3b +// CHECK:STDOUT: witness = () +// CHECK:STDOUT: +// CHECK:STDOUT: !requires: +// CHECK:STDOUT: @J.require0 { +// CHECK:STDOUT: require imports.%Main.import_ref.ce5 impls <@I, @I(constants.%T)> +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic interface @I(imports.%Main.import_ref.efcd44.1: type) [from "basic_import_generic_interface.carbon"] { +// CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T)> [symbolic = %I.type (constants.%I.type.070)] +// CHECK:STDOUT: %Self: @I.%I.type (%I.type.070) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.269)] +// CHECK:STDOUT: +// CHECK:STDOUT: interface { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = imports.%Main.import_ref.769 +// CHECK:STDOUT: witness = () +// CHECK:STDOUT: +// CHECK:STDOUT: !requires: +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic require @J.require0(imports.%Main.import_ref.efcd44.2: type, imports.%Main.import_ref.cc4: @J.%J.type (%J.type.8ec)) [from "basic_import_generic_interface.carbon"] { +// CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] +// CHECK:STDOUT: %J.type: type = facet_type <@J, @J(%T)> [symbolic = %J.type (constants.%J.type.8ec)] +// CHECK:STDOUT: %Self: @J.require0.%J.type (%J.type.8ec) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.f68)] +// CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] +// CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T)> [symbolic = %I.type (constants.%I.type.070)] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: impl @empty_struct_type.as.J.impl: %.loc3_7.2 as %J.type { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: witness = file.%J.impl_witness +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @J(constants.%T) { +// CHECK:STDOUT: %T => constants.%T +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @I(constants.%T) { +// CHECK:STDOUT: %T => constants.%T +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %I.type => constants.%I.type.070 +// CHECK:STDOUT: %Self => constants.%Self.269 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @J.require0(constants.%T, constants.%Self.f68) { +// CHECK:STDOUT: %T => constants.%T +// CHECK:STDOUT: %J.type => constants.%J.type.8ec +// CHECK:STDOUT: %Self => constants.%Self.f68 +// CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type +// CHECK:STDOUT: %I.type => constants.%I.type.070 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @J(constants.%empty_struct_type) { +// CHECK:STDOUT: %T => constants.%empty_struct_type +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %J.type => constants.%J.type.457 +// CHECK:STDOUT: %Self => constants.%Self.07b +// CHECK:STDOUT: %I.type => constants.%I.type.399 +// CHECK:STDOUT: %require_complete => constants.%complete_type +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @I(constants.%empty_struct_type) { +// CHECK:STDOUT: %T => constants.%empty_struct_type +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %I.type => constants.%I.type.399 +// CHECK:STDOUT: %Self => constants.%Self.32d +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- basic_import_generic_constraint.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %type: type = facet_type [concrete] +// CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self] +// CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] +// CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete] +// CHECK:STDOUT: %I.type.dac: type = generic_interface_type @I [concrete] +// CHECK:STDOUT: %I.generic: %I.type.dac = struct_value () [concrete] +// CHECK:STDOUT: %I.type.070: type = facet_type <@I, @I(%T)> [symbolic] +// CHECK:STDOUT: %Self.269: %I.type.070 = symbolic_binding Self, 1 [symbolic] +// CHECK:STDOUT: %J.type.267: type = generic_named_constaint_type @J [concrete] +// CHECK:STDOUT: %empty_struct: %J.type.267 = struct_value () [concrete] +// CHECK:STDOUT: %J.type.b8d: type = facet_type <@J, @J(%T)> [symbolic] +// CHECK:STDOUT: %Self.aa1: %J.type.b8d = symbolic_binding Self, 1 [symbolic] +// CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.aa1 [symbolic] +// CHECK:STDOUT: %require_complete: = require_complete_type %I.type.070 [symbolic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .I = %I.decl +// CHECK:STDOUT: .J = %J.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %I.decl: %I.type.dac = interface_decl @I [concrete = constants.%I.generic] { +// CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] +// CHECK:STDOUT: %T.loc3_13.2: type = symbolic_binding T, 0 [symbolic = %T.loc3_13.1 (constants.%T)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %J.decl: %J.type.267 = constraint_decl @J [concrete = constants.%empty_struct] { +// CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] +// CHECK:STDOUT: %T.loc4_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_14.1 (constants.%T)] +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic interface @I(%T.loc3_13.2: type) { +// CHECK:STDOUT: %T.loc3_13.1: type = symbolic_binding T, 0 [symbolic = %T.loc3_13.1 (constants.%T)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T.loc3_13.1)> [symbolic = %I.type (constants.%I.type.070)] +// CHECK:STDOUT: %Self.loc3_23.2: @I.%I.type (%I.type.070) = symbolic_binding Self, 1 [symbolic = %Self.loc3_23.2 (constants.%Self.269)] +// CHECK:STDOUT: +// CHECK:STDOUT: interface { +// CHECK:STDOUT: %Self.loc3_23.1: @I.%I.type (%I.type.070) = symbolic_binding Self, 1 [symbolic = %Self.loc3_23.2 (constants.%Self.269)] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self.loc3_23.1 +// CHECK:STDOUT: witness = () +// CHECK:STDOUT: +// CHECK:STDOUT: !requires: +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic constraint @J(%T.loc4_14.2: type) { +// CHECK:STDOUT: %T.loc4_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_14.1 (constants.%T)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %J.type: type = facet_type <@J, @J(%T.loc4_14.1)> [symbolic = %J.type (constants.%J.type.b8d)] +// CHECK:STDOUT: %Self.loc4_24.2: @J.%J.type (%J.type.b8d) = symbolic_binding Self, 1 [symbolic = %Self.loc4_24.2 (constants.%Self.aa1)] +// CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T.loc4_14.1)> [symbolic = %I.type (constants.%I.type.070)] +// CHECK:STDOUT: %require_complete: = require_complete_type %I.type [symbolic = %require_complete (constants.%require_complete)] +// CHECK:STDOUT: +// CHECK:STDOUT: constraint { +// CHECK:STDOUT: %Self.loc4_24.1: @J.%J.type (%J.type.b8d) = symbolic_binding Self, 1 [symbolic = %Self.loc4_24.2 (constants.%Self.aa1)] +// CHECK:STDOUT: %J.require0.decl = require_decl @J.require0 [concrete] { +// CHECK:STDOUT: require %Self.as_type impls <@I, @I(constants.%T)> +// CHECK:STDOUT: } { +// CHECK:STDOUT: %Self.as_type: type = facet_access_type @J.%Self.loc4_24.1 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] +// CHECK:STDOUT: %I.ref: %I.type.dac = name_ref I, file.%I.decl [concrete = constants.%I.generic] +// CHECK:STDOUT: %T.ref: type = name_ref T, @J.%T.loc4_14.2 [symbolic = %T (constants.%T)] +// CHECK:STDOUT: %I.type.loc5_27.1: type = facet_type <@I, @I(constants.%T)> [symbolic = %I.type.loc5_27.2 (constants.%I.type.070)] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self.loc4_24.1 +// CHECK:STDOUT: .I = +// CHECK:STDOUT: .T = +// CHECK:STDOUT: +// CHECK:STDOUT: !requires: +// CHECK:STDOUT: @J.require0 { +// CHECK:STDOUT: require @J.require0.%Self.as_type impls <@I, @I(constants.%T)> +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic require @J.require0(@J.%T.loc4_14.2: type, @J.%Self.loc4_24.1: @J.%J.type (%J.type.b8d)) { +// CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] +// CHECK:STDOUT: %J.type: type = facet_type <@J, @J(%T)> [symbolic = %J.type (constants.%J.type.b8d)] +// CHECK:STDOUT: %Self: @J.require0.%J.type (%J.type.b8d) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.aa1)] +// CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] +// CHECK:STDOUT: %I.type.loc5_27.2: type = facet_type <@I, @I(%T)> [symbolic = %I.type.loc5_27.2 (constants.%I.type.070)] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @I(constants.%T) { +// CHECK:STDOUT: %T.loc3_13.1 => constants.%T +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %I.type => constants.%I.type.070 +// CHECK:STDOUT: %Self.loc3_23.2 => constants.%Self.269 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @J(constants.%T) { +// CHECK:STDOUT: %T.loc4_14.1 => constants.%T +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @J.require0(constants.%T, constants.%Self.aa1) { +// CHECK:STDOUT: %T => constants.%T +// CHECK:STDOUT: %J.type => constants.%J.type.b8d +// CHECK:STDOUT: %Self => constants.%Self.aa1 +// CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type +// CHECK:STDOUT: %I.type.loc5_27.2 => constants.%I.type.070 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- basic_import_generic_constraint.impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %empty_struct.a40: %empty_struct_type = struct_value () [concrete] +// CHECK:STDOUT: %J.type.267: type = generic_named_constaint_type @J [concrete] +// CHECK:STDOUT: %empty_struct.b6f: %J.type.267 = struct_value () [concrete] +// CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] +// CHECK:STDOUT: %J.type.b8d23b.1: type = facet_type <@J, @J(%T)> [symbolic] +// CHECK:STDOUT: %Self.aa1546.1: %J.type.b8d23b.1 = symbolic_binding Self, 1 [symbolic] +// CHECK:STDOUT: %I.type.070: type = facet_type <@I, @I(%T)> [symbolic] +// CHECK:STDOUT: %Self.269: %I.type.070 = symbolic_binding Self, 1 [symbolic] +// CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.aa1546.1 [symbolic] +// CHECK:STDOUT: %require_complete: = require_complete_type %I.type.070 [symbolic] +// CHECK:STDOUT: %J.type.b8d23b.2: type = facet_type <@J, @J(%empty_struct_type)> [concrete] +// CHECK:STDOUT: %Self.aa1546.2: %J.type.b8d23b.2 = symbolic_binding Self, 1 [symbolic] +// CHECK:STDOUT: %I.type.399: type = facet_type <@I, @I(%empty_struct_type)> [concrete] +// CHECK:STDOUT: %Self.32d: %I.type.399 = symbolic_binding Self, 1 [symbolic] +// CHECK:STDOUT: %complete_type: = complete_type_witness %I.type.399 [concrete] +// CHECK:STDOUT: %I.impl_witness: = impl_witness file.%I.impl_witness_table [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Main.I = import_ref Main//basic_import_generic_constraint, I, unloaded +// CHECK:STDOUT: %Main.J: %J.type.267 = import_ref Main//basic_import_generic_constraint, J, loaded [concrete = constants.%empty_struct.b6f] +// CHECK:STDOUT: %Main.import_ref.769 = import_ref Main//basic_import_generic_constraint, loc3_23, unloaded +// CHECK:STDOUT: %Main.import_ref.efcd44.1: type = import_ref Main//basic_import_generic_constraint, loc3_13, loaded [symbolic = @I.%T (constants.%T)] +// CHECK:STDOUT: %Main.import_ref.f92: type = import_ref Main//basic_import_generic_constraint, loc5_18, loaded [symbolic = constants.%Self.binding.as_type] +// CHECK:STDOUT: %Main.import_ref.efcd44.2: type = import_ref Main//basic_import_generic_constraint, loc4_14, loaded [symbolic = @J.%T (constants.%T)] +// CHECK:STDOUT: %Main.import_ref.d4d: @J.%J.type (%J.type.b8d23b.1) = import_ref Main//basic_import_generic_constraint, loc4_24, loaded [symbolic = @J.%Self (constants.%Self.aa1546.1)] +// CHECK:STDOUT: %Main.import_ref.388 = import_ref Main//basic_import_generic_constraint, loc4_24, unloaded +// CHECK:STDOUT: %Main.import_ref.efcd44.3: type = import_ref Main//basic_import_generic_constraint, loc4_14, loaded [symbolic = @J.%T (constants.%T)] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .I = imports.%Main.I +// CHECK:STDOUT: .J = imports.%Main.J +// CHECK:STDOUT: } +// CHECK:STDOUT: %default.import.loc1_47.1 = import +// CHECK:STDOUT: %default.import.loc1_47.2 = import +// CHECK:STDOUT: impl_decl @empty_struct_type.as.I.impl [concrete] {} { +// CHECK:STDOUT: %.loc3_7.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct.a40] +// CHECK:STDOUT: %.loc3_7.2: type = converted %.loc3_7.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] +// CHECK:STDOUT: %J.ref: %J.type.267 = name_ref J, imports.%Main.J [concrete = constants.%empty_struct.b6f] +// CHECK:STDOUT: %.loc3_15: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct.a40] +// CHECK:STDOUT: %.loc3_16: type = converted %.loc3_15, constants.%empty_struct_type [concrete = constants.%empty_struct_type] +// CHECK:STDOUT: %J.type: type = facet_type <@J, @J(constants.%empty_struct_type)> [concrete = constants.%J.type.b8d23b.2] +// CHECK:STDOUT: } +// CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @empty_struct_type.as.I.impl [concrete] +// CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic interface @I(imports.%Main.import_ref.efcd44.1: type) [from "basic_import_generic_constraint.carbon"] { +// CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T)> [symbolic = %I.type (constants.%I.type.070)] +// CHECK:STDOUT: %Self: @I.%I.type (%I.type.070) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.269)] +// CHECK:STDOUT: +// CHECK:STDOUT: interface { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = imports.%Main.import_ref.769 +// CHECK:STDOUT: witness = () +// CHECK:STDOUT: +// CHECK:STDOUT: !requires: +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic constraint @J(imports.%Main.import_ref.efcd44.3: type) [from "basic_import_generic_constraint.carbon"] { +// CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %J.type: type = facet_type <@J, @J(%T)> [symbolic = %J.type (constants.%J.type.b8d23b.1)] +// CHECK:STDOUT: %Self: @J.%J.type (%J.type.b8d23b.1) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.aa1546.1)] +// CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T)> [symbolic = %I.type (constants.%I.type.070)] +// CHECK:STDOUT: %require_complete: = require_complete_type %I.type [symbolic = %require_complete (constants.%require_complete)] +// CHECK:STDOUT: +// CHECK:STDOUT: constraint { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = imports.%Main.import_ref.388 +// CHECK:STDOUT: +// CHECK:STDOUT: !requires: +// CHECK:STDOUT: @J.require0 { +// CHECK:STDOUT: require imports.%Main.import_ref.f92 impls <@I, @I(constants.%T)> +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic require @J.require0(imports.%Main.import_ref.efcd44.2: type, imports.%Main.import_ref.d4d: @J.%J.type (%J.type.b8d23b.1)) [from "basic_import_generic_constraint.carbon"] { +// CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] +// CHECK:STDOUT: %J.type: type = facet_type <@J, @J(%T)> [symbolic = %J.type (constants.%J.type.b8d23b.1)] +// CHECK:STDOUT: %Self: @J.require0.%J.type (%J.type.b8d23b.1) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.aa1546.1)] +// CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)] +// CHECK:STDOUT: %I.type: type = facet_type <@I, @I(%T)> [symbolic = %I.type (constants.%I.type.070)] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: impl @empty_struct_type.as.I.impl: %.loc3_7.2 as %J.type { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: witness = file.%I.impl_witness +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @J(constants.%T) { +// CHECK:STDOUT: %T => constants.%T +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @I(constants.%T) { +// CHECK:STDOUT: %T => constants.%T +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %I.type => constants.%I.type.070 +// CHECK:STDOUT: %Self => constants.%Self.269 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @J.require0(constants.%T, constants.%Self.aa1546.1) { +// CHECK:STDOUT: %T => constants.%T +// CHECK:STDOUT: %J.type => constants.%J.type.b8d23b.1 +// CHECK:STDOUT: %Self => constants.%Self.aa1546.1 +// CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type +// CHECK:STDOUT: %I.type => constants.%I.type.070 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @J(constants.%empty_struct_type) { +// CHECK:STDOUT: %T => constants.%empty_struct_type +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %J.type => constants.%J.type.b8d23b.2 +// CHECK:STDOUT: %Self => constants.%Self.aa1546.2 +// CHECK:STDOUT: %I.type => constants.%I.type.399 +// CHECK:STDOUT: %require_complete => constants.%complete_type +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @I(constants.%empty_struct_type) { +// CHECK:STDOUT: %T => constants.%empty_struct_type +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %I.type => constants.%I.type.399 +// CHECK:STDOUT: %Self => constants.%Self.32d +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: --- import_generic.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { @@ -980,7 +1482,7 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %Main.import_ref.efcd44.2: type = import_ref Main//import_generic_with_different_specific, loc7_14, loaded [symbolic = @C.as.I.impl.f3e.%T (constants.%T)] // CHECK:STDOUT: %Main.import_ref.f92: type = import_ref Main//import_generic_with_different_specific, loc10_18, loaded [symbolic = constants.%Self.binding.as_type] // CHECK:STDOUT: %Main.import_ref.efcd44.3: type = import_ref Main//import_generic_with_different_specific, loc9_14, loaded [symbolic = @N.%T (constants.%T)] -// CHECK:STDOUT: %Main.import_ref.d4d: @N.%N.type (%N.type.b8d23b.1) = import_ref Main//import_generic_with_different_specific, loc9_24, loaded [symbolic = @N.%Self (constants.%Self.aa1)] +// CHECK:STDOUT: %Main.import_ref.d4d: @N.%N.type (%N.type.b8d23b.1) = import_ref Main//import_generic_with_different_specific, loc9_24, loaded [symbolic = @N.%Self (constants.%Self.aa1546.1)] // CHECK:STDOUT: %Main.import_ref.388 = import_ref Main//import_generic_with_different_specific, loc9_24, unloaded // CHECK:STDOUT: %Main.import_ref.efcd44.4: type = import_ref Main//import_generic_with_different_specific, loc9_14, loaded [symbolic = @N.%T (constants.%T)] // CHECK:STDOUT: } @@ -1427,7 +1929,7 @@ impl forall [T:! type] D as N(T*) {} // CHECK:STDOUT: %Main.import_ref.efcd44.3: type = import_ref Main//import_generic_decl, loc21_14, loaded [symbolic = @D.as.J.impl.265db6.1.%T (constants.%T)] // CHECK:STDOUT: %Main.import_ref.f92: type = import_ref Main//import_generic_decl, loc8_18, loaded [symbolic = constants.%Self.binding.as_type] // CHECK:STDOUT: %Main.import_ref.efcd44.4: type = import_ref Main//import_generic_decl, loc7_14, loaded [symbolic = @N.%T (constants.%T)] -// CHECK:STDOUT: %Main.import_ref.d4d: @N.%N.type (%N.type.b8d23b.1) = import_ref Main//import_generic_decl, loc7_24, loaded [symbolic = @N.%Self (constants.%Self.aa1)] +// CHECK:STDOUT: %Main.import_ref.d4d: @N.%N.type (%N.type.b8d23b.1) = import_ref Main//import_generic_decl, loc7_24, loaded [symbolic = @N.%Self (constants.%Self.aa1546.1)] // CHECK:STDOUT: %Main.import_ref.388 = import_ref Main//import_generic_decl, loc7_24, unloaded // CHECK:STDOUT: %Main.import_ref.efcd44.5: type = import_ref Main//import_generic_decl, loc7_14, loaded [symbolic = @N.%T (constants.%T)] // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/impl/use_assoc_entity.carbon b/toolchain/check/testdata/impl/use_assoc_entity.carbon index d89e5a5f630b4..3f115580b3151 100644 --- a/toolchain/check/testdata/impl/use_assoc_entity.carbon +++ b/toolchain/check/testdata/impl/use_assoc_entity.carbon @@ -8,9 +8,9 @@ // // AUTOUPDATE // TIP: To test this file alone, run: -// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/use_assoc_const.carbon +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/use_assoc_entity.carbon // TIP: To dump output, run: -// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/use_assoc_const.carbon +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/use_assoc_entity.carbon // --- associated_type_in_method_signature.carbon library "[[@TEST_NAME]]"; From e1fc9721cce0e4cd0ff93dfcb12978fed6e5ac56 Mon Sep 17 00:00:00 2001 From: danakj Date: Thu, 20 Nov 2025 17:17:52 -0500 Subject: [PATCH 26/31] yay --- toolchain/check/type_completion.cpp | 74 +++++++++++------------------ 1 file changed, 28 insertions(+), 46 deletions(-) diff --git a/toolchain/check/type_completion.cpp b/toolchain/check/type_completion.cpp index b2749be67b729..041be17e70286 100644 --- a/toolchain/check/type_completion.cpp +++ b/toolchain/check/type_completion.cpp @@ -76,58 +76,40 @@ static auto RequireCompleteFacetType(Context& context, SemIR::LocId loc_id, const SemIR::FacetType& facet_type, MakeDiagnosticBuilderFn diagnoser) -> bool { - struct SpecificForRequires { - SemIR::SpecificId specific_id; - SemIR::RequireImplsBlockId requires_block_id; - }; - llvm::SmallVector specifics; - - llvm::SmallVector work = {facet_type.facet_type_id}; - while (!work.empty()) { - auto next_facet_type_id = work.pop_back_val(); - const auto& facet_type_info = context.facet_types().Get(next_facet_type_id); + const auto& facet_type_info = + context.facet_types().Get(facet_type.facet_type_id); - for (auto extends : facet_type_info.extend_constraints) { - auto interface_id = extends.interface_id; - const auto& interface = context.interfaces().Get(interface_id); - if (!interface.is_complete()) { - if (diagnoser) { - auto builder = diagnoser(); - NoteIncompleteInterface(context, interface_id, builder); - builder.Emit(); - } - return false; - } - if (interface.generic_id.has_value()) { - specifics.push_back( - {extends.specific_id, interface.require_impls_block_id}); + for (auto extends : facet_type_info.extend_constraints) { + auto interface_id = extends.interface_id; + const auto& interface = context.interfaces().Get(interface_id); + if (!interface.is_complete()) { + if (diagnoser) { + auto builder = diagnoser(); + NoteIncompleteInterface(context, interface_id, builder); + builder.Emit(); } + return false; } - - for (auto extends : facet_type_info.extend_named_constraints) { - auto named_constraint_id = extends.named_constraint_id; - const auto& constraint = - context.named_constraints().Get(named_constraint_id); - if (!constraint.is_complete()) { - if (diagnoser) { - auto builder = diagnoser(); - NoteIncompleteNamedConstraint(context, named_constraint_id, builder); - builder.Emit(); - } - return false; - } - if (constraint.generic_id.has_value()) { - specifics.push_back( - {extends.specific_id, constraint.require_impls_block_id}); - } + if (interface.generic_id.has_value()) { + ResolveSpecificDefinition(context, loc_id, extends.specific_id); } } - // Specific definitions are resolved from the interfaces in the facet type - // down, each require target depends on the specific referring to it being - // resolved already. - for (auto [specific_id, _] : specifics) { - ResolveSpecificDefinition(context, loc_id, specific_id); + for (auto extends : facet_type_info.extend_named_constraints) { + auto named_constraint_id = extends.named_constraint_id; + const auto& constraint = + context.named_constraints().Get(named_constraint_id); + if (!constraint.is_complete()) { + if (diagnoser) { + auto builder = diagnoser(); + NoteIncompleteNamedConstraint(context, named_constraint_id, builder); + builder.Emit(); + } + return false; + } + if (constraint.generic_id.has_value()) { + ResolveSpecificDefinition(context, loc_id, extends.specific_id); + } } return true; From 00b9dc0d579d71804fe21bfa3b8d53138aac8d9d Mon Sep 17 00:00:00 2001 From: danakj Date: Thu, 20 Nov 2025 17:31:25 -0500 Subject: [PATCH 27/31] remove-todo-diagnostic --- toolchain/check/facet_type.cpp | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/toolchain/check/facet_type.cpp b/toolchain/check/facet_type.cpp index 02455cfe65aab..646eefa6f5294 100644 --- a/toolchain/check/facet_type.cpp +++ b/toolchain/check/facet_type.cpp @@ -55,18 +55,15 @@ static auto WitnessQueryMatchesInterface( static auto IncompleteFacetTypeDiagnosticBuilder( Context& context, SemIR::LocId loc_id, SemIR::TypeInstId facet_type_inst_id, bool is_definition) -> DiagnosticBuilder { - if (is_definition) { - CARBON_DIAGNOSTIC(ImplAsIncompleteFacetTypeDefinition, Error, - "definition of impl as incomplete facet type {0}", - InstIdAsType); - return context.emitter().Build(loc_id, ImplAsIncompleteFacetTypeDefinition, - facet_type_inst_id); - } else { - context.TODO(loc_id, - "declaration of impl as incomplete interface with a rewrite " - "constraint"); - return context.emitter().BuildSuppressed(); - } + // TODO: Remove this parameter. Facet types don't need to be complete for in + // declarations, unless there's a rewrite into `.Self`. But that completeness + // is checked/required by the member access of the rewrite. + CARBON_CHECK(is_definition); + CARBON_DIAGNOSTIC(ImplAsIncompleteFacetTypeDefinition, Error, + "definition of impl as incomplete facet type {0}", + InstIdAsType); + return context.emitter().Build(loc_id, ImplAsIncompleteFacetTypeDefinition, + facet_type_inst_id); } auto GetImplWitnessAccessWithoutSubstitution(Context& context, From fa4066e0cf34cf4afd6f38cd54507f5e7c846495 Mon Sep 17 00:00:00 2001 From: danakj Date: Fri, 21 Nov 2025 09:01:11 -0500 Subject: [PATCH 28/31] less-completing-impl-as --- toolchain/check/facet_type.cpp | 64 +++++++++++-------- .../check/testdata/impl/forward_decls.carbon | 3 - 2 files changed, 37 insertions(+), 30 deletions(-) diff --git a/toolchain/check/facet_type.cpp b/toolchain/check/facet_type.cpp index 646eefa6f5294..ee9a4a85250c1 100644 --- a/toolchain/check/facet_type.cpp +++ b/toolchain/check/facet_type.cpp @@ -53,12 +53,8 @@ static auto WitnessQueryMatchesInterface( } static auto IncompleteFacetTypeDiagnosticBuilder( - Context& context, SemIR::LocId loc_id, SemIR::TypeInstId facet_type_inst_id, - bool is_definition) -> DiagnosticBuilder { - // TODO: Remove this parameter. Facet types don't need to be complete for in - // declarations, unless there's a rewrite into `.Self`. But that completeness - // is checked/required by the member access of the rewrite. - CARBON_CHECK(is_definition); + Context& context, SemIR::LocId loc_id, SemIR::TypeInstId facet_type_inst_id) + -> DiagnosticBuilder { CARBON_DIAGNOSTIC(ImplAsIncompleteFacetTypeDefinition, Error, "definition of impl as incomplete facet type {0}", InstIdAsType); @@ -88,7 +84,18 @@ auto InitialFacetTypeImplWitness( const auto& facet_type_info = context.facet_types().Get(facet_type.facet_type_id); - if (!is_definition && facet_type_info.rewrite_constraints.empty()) { + auto rewrites_into_interface_to_witness = llvm::make_filter_range( + facet_type_info.rewrite_constraints, + [&](const SemIR::FacetTypeInfo::RewriteConstraint& rewrite) { + auto access = context.insts().GetAs( + GetImplWitnessAccessWithoutSubstitution(context, rewrite.lhs_id)); + return WitnessQueryMatchesInterface(context, access.witness_id, + interface_to_witness); + }); + + bool need_witness_table = + is_definition || !rewrites_into_interface_to_witness.empty(); + if (!need_witness_table) { auto witness_table_inst_id = AddInst( context, witness_loc_id, {.elements_id = context.inst_blocks().AddPlaceholder(), @@ -100,19 +107,27 @@ auto InitialFacetTypeImplWitness( .specific_id = self_specific_id}); } - // The presence of any rewrite constraints requires that we know how many - // entries to allocate in the witness table, which requires the entire facet - // type to be complete, even if this was a declaration. - if (!RequireCompleteType( - context, facet_type_id, SemIR::LocId(facet_type_inst_id), [&] { - return IncompleteFacetTypeDiagnosticBuilder( - context, witness_loc_id, facet_type_inst_id, is_definition); - })) { - return SemIR::ErrorInst::InstId; + if (is_definition) { + // The presence of any rewrite constraints requires that we know how many + // entries to allocate in the witness table, which requires the entire facet + // type to be complete, even if this was a declaration. + if (!RequireCompleteType(context, facet_type_id, + SemIR::LocId(facet_type_inst_id), [&] { + return IncompleteFacetTypeDiagnosticBuilder( + context, witness_loc_id, facet_type_inst_id); + })) { + return SemIR::ErrorInst::InstId; + } } const auto& interface = context.interfaces().Get(interface_to_witness.interface_id); + if (!interface.is_complete()) { + // There are rewrite constraints into `.Self` but the interface is not + // complete. Those rewrites would have been diagnosed as an error. + return SemIR::ErrorInst::InstId; + } + auto assoc_entities = context.inst_blocks().Get(interface.associated_entities_id); // TODO: When this function is used for things other than just impls, may want @@ -143,13 +158,9 @@ auto InitialFacetTypeImplWitness( .specific_id = self_specific_id}); } - for (auto rewrite : facet_type_info.rewrite_constraints) { + for (auto rewrite : rewrites_into_interface_to_witness) { auto access = context.insts().GetAs( GetImplWitnessAccessWithoutSubstitution(context, rewrite.lhs_id)); - if (!WitnessQueryMatchesInterface(context, access.witness_id, - interface_to_witness)) { - continue; - } auto& table_entry = table[access.index.index]; if (table_entry == SemIR::ErrorInst::InstId) { // Don't overwrite an error value. This prioritizes not generating @@ -248,12 +259,11 @@ auto RequireCompleteFacetTypeForImplDefinition( -> bool { auto facet_type_id = context.types().GetTypeIdForTypeInstId(facet_type_inst_id); - return RequireCompleteType( - context, facet_type_id, SemIR::LocId(facet_type_inst_id), [&] { - return IncompleteFacetTypeDiagnosticBuilder(context, loc_id, - facet_type_inst_id, - /*is_definition=*/true); - }); + return RequireCompleteType(context, facet_type_id, + SemIR::LocId(facet_type_inst_id), [&] { + return IncompleteFacetTypeDiagnosticBuilder( + context, loc_id, facet_type_inst_id); + }); } auto AllocateFacetTypeImplWitness(Context& context, diff --git a/toolchain/check/testdata/impl/forward_decls.carbon b/toolchain/check/testdata/impl/forward_decls.carbon index 56e5c4664ac35..f12abb5c10798 100644 --- a/toolchain/check/testdata/impl/forward_decls.carbon +++ b/toolchain/check/testdata/impl/forward_decls.carbon @@ -1928,7 +1928,6 @@ interface I { // CHECK:STDOUT: %I.lookup_impl_witness: = lookup_impl_witness %.Self, @I [symbolic_self] // CHECK:STDOUT: %impl.elem0: type = impl_witness_access %I.lookup_impl_witness, element0 [symbolic_self] // CHECK:STDOUT: %I_where.type: type = facet_type <@I where %impl.elem0 = %C> [symbolic] -// CHECK:STDOUT: %require_complete: = require_complete_type %I_where.type [symbolic] // CHECK:STDOUT: %I.impl_witness: = impl_witness @I.F.%I.impl_witness_table, @C.as.I.impl(%Self) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: @@ -1974,7 +1973,6 @@ interface I { // CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = %Self (constants.%Self)] // CHECK:STDOUT: %C: type = class_type @C, @C(%Self) [symbolic = %C (constants.%C)] // CHECK:STDOUT: %I_where.type: type = facet_type <@I where constants.%impl.elem0 = %C> [symbolic = %I_where.type (constants.%I_where.type)] -// CHECK:STDOUT: %require_complete: = require_complete_type %I_where.type [symbolic = %require_complete (constants.%require_complete)] // CHECK:STDOUT: %I.impl_witness: = impl_witness @I.F.%I.impl_witness_table, @C.as.I.impl(%Self) [symbolic = %I.impl_witness (constants.%I.impl_witness)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: @@ -2055,7 +2053,6 @@ interface I { // CHECK:STDOUT: %Self => constants.%Self // CHECK:STDOUT: %C => constants.%C // CHECK:STDOUT: %I_where.type => constants.%I_where.type -// CHECK:STDOUT: %require_complete => constants.%require_complete // CHECK:STDOUT: %I.impl_witness => constants.%I.impl_witness // CHECK:STDOUT: } // CHECK:STDOUT: From a79b70784713e54f7ede44105fcf6f532259f68e Mon Sep 17 00:00:00 2001 From: danakj Date: Fri, 21 Nov 2025 09:27:27 -0500 Subject: [PATCH 29/31] more --- toolchain/check/BUILD | 2 + toolchain/check/facet_type.cpp | 18 +- toolchain/check/handle_impl.cpp | 132 +++++-- toolchain/check/handle_require.cpp | 13 +- toolchain/check/impl.cpp | 364 ++++++++---------- toolchain/check/impl.h | 34 +- toolchain/check/name_scope.cpp | 28 ++ toolchain/check/name_scope.h | 27 ++ .../testdata/facet/require_invalid.carbon | 17 + .../impl/fail_extend_impl_forall.carbon | 1 - .../impl/fail_extend_impl_type_as.carbon | 7 +- .../impl/fail_extend_non_interface.carbon | 1 - ..._extend_partially_defined_interface.carbon | 1 - 13 files changed, 344 insertions(+), 301 deletions(-) create mode 100644 toolchain/check/name_scope.cpp create mode 100644 toolchain/check/name_scope.h diff --git a/toolchain/check/BUILD b/toolchain/check/BUILD index 04566875ed1ed..e22a1cc39c376 100644 --- a/toolchain/check/BUILD +++ b/toolchain/check/BUILD @@ -56,6 +56,7 @@ cc_library( "name_component.cpp", "name_lookup.cpp", "name_ref.cpp", + "name_scope.cpp", "operator.cpp", "pattern.cpp", "pattern_match.cpp", @@ -111,6 +112,7 @@ cc_library( "name_component.h", "name_lookup.h", "name_ref.h", + "name_scope.h", "operator.h", "param_and_arg_refs_stack.h", "pattern.h", diff --git a/toolchain/check/facet_type.cpp b/toolchain/check/facet_type.cpp index ee9a4a85250c1..d0b3c2929b7b8 100644 --- a/toolchain/check/facet_type.cpp +++ b/toolchain/check/facet_type.cpp @@ -107,24 +107,12 @@ auto InitialFacetTypeImplWitness( .specific_id = self_specific_id}); } - if (is_definition) { - // The presence of any rewrite constraints requires that we know how many - // entries to allocate in the witness table, which requires the entire facet - // type to be complete, even if this was a declaration. - if (!RequireCompleteType(context, facet_type_id, - SemIR::LocId(facet_type_inst_id), [&] { - return IncompleteFacetTypeDiagnosticBuilder( - context, witness_loc_id, facet_type_inst_id); - })) { - return SemIR::ErrorInst::InstId; - } - } - const auto& interface = context.interfaces().Get(interface_to_witness.interface_id); if (!interface.is_complete()) { - // There are rewrite constraints into `.Self` but the interface is not - // complete. Those rewrites would have been diagnosed as an error. + // This is a declaration with rewrite constraints into `.Self`, but the + // interface is not complete. Those rewrites would have been diagnosed as an + // error in their member access. return SemIR::ErrorInst::InstId; } diff --git a/toolchain/check/handle_impl.cpp b/toolchain/check/handle_impl.cpp index badc5891cbc27..d1bd240653487 100644 --- a/toolchain/check/handle_impl.cpp +++ b/toolchain/check/handle_impl.cpp @@ -14,16 +14,28 @@ #include "toolchain/check/inst.h" #include "toolchain/check/modifiers.h" #include "toolchain/check/name_lookup.h" +#include "toolchain/check/name_scope.h" #include "toolchain/check/pattern_match.h" #include "toolchain/check/type.h" #include "toolchain/check/type_completion.h" +#include "toolchain/parse/node_ids.h" #include "toolchain/parse/typed_nodes.h" #include "toolchain/sem_ir/generic.h" #include "toolchain/sem_ir/ids.h" +#include "toolchain/sem_ir/specific_interface.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { +// Returns the implicit `Self` type for an `impl` when it's in a `class` +// declaration. +static auto GetImplDefaultSelfType(Context& context, + const ClassScope& class_scope) + -> SemIR::TypeId { + // TODO: This is also valid in a mixin. + return context.classes().Get(class_scope.class_decl.class_id).self_type_id; +} + auto HandleParseNode(Context& context, Parse::ImplIntroducerId node_id) -> bool { // This might be a generic impl. @@ -56,14 +68,43 @@ auto HandleParseNode(Context& context, Parse::ForallId /*node_id*/) -> bool { auto HandleParseNode(Context& context, Parse::ImplTypeAsId node_id) -> bool { auto [self_node, self_id] = context.node_stack().PopExprWithNodeId(); - auto self_type_inst_id = ExprAsType(context, self_node, self_id).inst_id; - context.node_stack().Push(node_id, self_type_inst_id); + auto self_type = ExprAsType(context, self_node, self_id); + + const auto& introducer = context.decl_introducer_state_stack().innermost(); + if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend) && + self_type.type_id != SemIR::ErrorInst::TypeId) { + auto class_scope = + TryAsClassScope(context, context.decl_name_stack().PeekParentScopeId()); + if (class_scope) { + // If we're not inside a class at all, that will be diagnosed against the + // `extend` elsewhere. + auto extend_node = introducer.modifier_node_id(ModifierOrder::Extend); + CARBON_DIAGNOSTIC(ExtendImplSelfAs, Error, + "cannot `extend` an `impl` with an explicit self type"); + auto diag = context.emitter().Build(extend_node, ExtendImplSelfAs); + + if (self_type.type_id != GetImplDefaultSelfType(context, *class_scope)) { + // If the explicit self type is not the default, the self-type is an + // error. + diag.Emit(); + self_type.inst_id = SemIR::ErrorInst::TypeInstId; + } else { + // Otherwise, suggest removing the self-type, and continue as if there + // was not error. + CARBON_DIAGNOSTIC(ExtendImplSelfAsDefault, Note, + "remove the explicit `Self` type here"); + diag.Note(self_node, ExtendImplSelfAsDefault); + diag.Emit(); + } + } + } // Introduce `Self`. Note that we add this name lexically rather than adding // to the `NameScopeId` of the `impl`, because this happens before we enter // the `impl` scope or even identify which `impl` we're declaring. // TODO: Revisit this once #3714 is resolved. - AddNameToLookup(context, SemIR::NameId::SelfType, self_type_inst_id); + AddNameToLookup(context, SemIR::NameId::SelfType, self_type.inst_id); + context.node_stack().Push(node_id, self_type.inst_id); return true; } @@ -71,8 +112,10 @@ auto HandleParseNode(Context& context, Parse::ImplDefaultSelfAsId node_id) -> bool { auto self_inst_id = SemIR::TypeInstId::None; - if (auto self_type_id = GetImplDefaultSelfType(context); - self_type_id.has_value()) { + auto class_scope = + TryAsClassScope(context, context.decl_name_stack().PeekParentScopeId()); + if (class_scope) { + auto self_type_id = GetImplDefaultSelfType(context, *class_scope); // Build the implicit access to the enclosing `Self`. // TODO: Consider calling `HandleNameAsExpr` to build this implicit `Self` // expression. We've already done the work to check that the enclosing @@ -151,7 +194,7 @@ static auto PopImplIntroducerAndParamsAsNameComponent( // common logic shared by impl forward declarations and impl definitions. static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id, bool is_definition) - -> std::pair { + -> std::tuple { auto [constraint_node, constraint_id] = context.node_stack().PopExprWithNodeId(); auto [self_type_node, self_type_inst_id] = @@ -185,33 +228,50 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id, SemIR::ImplDecl{.impl_id = SemIR::ImplId::None, .decl_block_id = decl_block_id}); - SemIR::Impl impl_info = {name_context.MakeEntityWithParamsBase( - name, impl_decl_id, - /*is_extern=*/false, SemIR::LibraryNameId::None), - {.self_id = self_type_inst_id, - .constraint_id = constraint_type_inst_id, - // This requires that the facet type is identified. - .interface = CheckConstraintIsInterface( - context, impl_decl_id, constraint_type_inst_id), - .is_final = is_final}}; - - std::optional extend_impl; - if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend)) { - extend_impl = ExtendImplDecl{ - .self_type_node_id = self_type_node, - .constraint_type_id = constraint_type_id, - .extend_node_id = introducer.modifier_node_id(ModifierOrder::Extend), - }; + // This requires that the facet type is identified. It returns None if an + // error was diagnosed. + auto specific_interface = CheckConstraintIsInterface(context, impl_decl_id, + constraint_type_inst_id); + + auto impl_id = SemIR::ImplId::None; + { + SemIR::Impl impl_info = { + name_context.MakeEntityWithParamsBase(name, impl_decl_id, + /*is_extern=*/false, + SemIR::LibraryNameId::None), + {.self_id = self_type_inst_id, + .constraint_id = constraint_type_inst_id, + .interface = specific_interface, + .is_final = is_final}}; + auto extend_node = introducer.modifier_node_id(ModifierOrder::Extend); + impl_id = GetOrAddImpl(context, node_id, name.implicit_params_loc_id, + impl_info, is_definition, extend_node); } - return StartImplDecl(context, SemIR::LocId(node_id), - name.implicit_params_loc_id, impl_info, is_definition, - extend_impl); + // StartImplDecl either filled in the `impl_info` and returned a fresh ImplId, + // or if we're redeclaring a previous impl, returned an existing ImplId. Write + // that ImplId into the ImplDecl instruction and finish it. + auto impl_decl = context.insts().GetAs(impl_decl_id); + impl_decl.impl_id = impl_id; + ReplaceInstBeforeConstantUse(context, impl_decl_id, impl_decl); + + return {impl_id, impl_decl_id}; } auto HandleParseNode(Context& context, Parse::ImplDeclId node_id) -> bool { - BuildImplDecl(context, node_id, /*is_definition=*/false); + auto [impl_id, impl_decl_id] = + BuildImplDecl(context, node_id, /*is_definition=*/false); + auto& impl = context.impls().Get(impl_id); + context.decl_name_stack().PopScope(); + + // Impl definitions are required in the same file as the declaration. We skip + // this requirement if we've already issued an invalid redeclaration error, or + // there is an error that would prevent the impl from being legal to define. + if (impl.witness_id != SemIR::ErrorInst::InstId) { + context.definitions_required_by_decl().push_back(impl_decl_id); + } + return true; } @@ -219,20 +279,20 @@ auto HandleParseNode(Context& context, Parse::ImplDefinitionStartId node_id) -> bool { auto [impl_id, impl_decl_id] = BuildImplDecl(context, node_id, /*is_definition=*/true); - auto& impl_info = context.impls().Get(impl_id); + auto& impl = context.impls().Get(impl_id); - CARBON_CHECK(!impl_info.has_definition_started()); - impl_info.definition_id = impl_decl_id; - impl_info.scope_id = + CARBON_CHECK(!impl.has_definition_started()); + impl.definition_id = impl_decl_id; + impl.scope_id = context.name_scopes().Add(impl_decl_id, SemIR::NameId::None, context.decl_name_stack().PeekParentScopeId()); context.scope_stack().PushForEntity( - impl_decl_id, impl_info.scope_id, - context.generics().GetSelfSpecific(impl_info.generic_id)); - StartGenericDefinition(context, impl_info.generic_id); + impl_decl_id, impl.scope_id, + context.generics().GetSelfSpecific(impl.generic_id)); + StartGenericDefinition(context, impl.generic_id); // This requires that the facet type is complete. - ImplWitnessStartDefinition(context, impl_info); + ImplWitnessStartDefinition(context, impl); context.inst_block_stack().Push(); context.node_stack().Push(node_id, impl_id); @@ -245,7 +305,7 @@ auto HandleParseNode(Context& context, Parse::ImplDefinitionStartId node_id) // // We may need to track a list of instruction blocks here, as we do for a // function. - impl_info.body_block_id = context.inst_block_stack().PeekOrAdd(); + impl.body_block_id = context.inst_block_stack().PeekOrAdd(); return true; } diff --git a/toolchain/check/handle_require.cpp b/toolchain/check/handle_require.cpp index 50a6c9fd31620..b22d2899598c8 100644 --- a/toolchain/check/handle_require.cpp +++ b/toolchain/check/handle_require.cpp @@ -71,6 +71,7 @@ auto HandleParseNode(Context& context, Parse::RequireDefaultSelfImplsId node_id) auto self_type_id = context.insts().Get(self_inst_id).type_id(); CARBON_CHECK(context.types().Is(self_type_id)); + // TODO: We could simplify with a call to ExprAsType, like below? auto self_facet_as_type = AddTypeInst( context, node_id, {.type_id = SemIR::TypeType::TypeId, @@ -82,16 +83,20 @@ auto HandleParseNode(Context& context, Parse::RequireDefaultSelfImplsId node_id) auto HandleParseNode(Context& context, Parse::RequireTypeImplsId node_id) -> bool { auto [self_node_id, self_inst_id] = context.node_stack().PopExprWithNodeId(); + auto self_type = ExprAsType(context, self_node_id, self_inst_id); - auto introducer = context.decl_introducer_state_stack().innermost(); - if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend)) { + const auto& introducer = context.decl_introducer_state_stack().innermost(); + if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend) && + self_type.type_id != SemIR::ErrorInst::TypeId) { CARBON_DIAGNOSTIC(RequireImplsExtendWithExplicitSelf, Error, "`extend require impls` with explicit type"); + // TODO: If the explicit self-type matches a lookup of NameId::SelfType, add + // a note to the diagnostic: "remove the explicit `Self` type here", and + // continue without an ErrorInst. See ExtendImplSelfAsDefault. context.emitter().Emit(self_node_id, RequireImplsExtendWithExplicitSelf); - self_inst_id = SemIR::ErrorInst::InstId; + self_type.inst_id = SemIR::ErrorInst::TypeInstId; } - auto self_type = ExprAsType(context, self_node_id, self_inst_id); context.node_stack().Push(node_id, self_type.inst_id); return true; } diff --git a/toolchain/check/impl.cpp b/toolchain/check/impl.cpp index 9a529b4a5c5c4..62089693aee8d 100644 --- a/toolchain/check/impl.cpp +++ b/toolchain/check/impl.cpp @@ -16,6 +16,7 @@ #include "toolchain/check/interface.h" #include "toolchain/check/merge.h" #include "toolchain/check/name_lookup.h" +#include "toolchain/check/name_scope.h" #include "toolchain/check/thunk.h" #include "toolchain/check/type.h" #include "toolchain/check/type_completion.h" @@ -260,22 +261,6 @@ auto FillImplWitnessWithErrors(Context& context, SemIR::Impl& impl) -> void { impl.witness_id = SemIR::ErrorInst::InstId; } -auto AssignImplIdInWitness(Context& context, SemIR::ImplId impl_id, - SemIR::InstId witness_id) -> void { - if (witness_id == SemIR::ErrorInst::InstId) { - return; - } - auto witness = context.insts().GetAs(witness_id); - auto witness_table = - context.insts().GetAs(witness.witness_table_id); - witness_table.impl_id = impl_id; - // Note: The `ImplWitnessTable` instruction is `Unique`, so while this marks - // the instruction as being a dependent instruction of a generic impl, it will - // not be substituted into the eval block. - ReplaceInstBeforeConstantUse(context, witness.witness_table_id, - witness_table); -} - auto IsImplEffectivelyFinal(Context& context, const SemIR::Impl& impl) -> bool { return impl.is_final || (context.constant_values().Get(impl.self_id).is_concrete() && @@ -319,7 +304,8 @@ auto CheckConstraintIsInterface(Context& context, SemIR::InstId impl_decl_id, } // Returns true if impl redeclaration parameters match. -static auto CheckImplRedeclParamsMatch(Context& context, SemIR::Impl& new_impl, +static auto CheckImplRedeclParamsMatch(Context& context, + const SemIR::Impl& new_impl, SemIR::ImplId prev_impl_id) -> bool { auto& prev_impl = context.impls().Get(prev_impl_id); @@ -337,7 +323,7 @@ static auto CheckImplRedeclParamsMatch(Context& context, SemIR::Impl& new_impl, // Returns whether an impl can be redeclared. For example, defined impls // cannot be redeclared. -static auto IsValidImplRedecl(Context& context, SemIR::Impl& new_impl, +static auto IsValidImplRedecl(Context& context, const SemIR::Impl& new_impl, SemIR::ImplId prev_impl_id) -> bool { auto& prev_impl = context.impls().Get(prev_impl_id); @@ -376,62 +362,89 @@ static auto IsValidImplRedecl(Context& context, SemIR::Impl& new_impl, return true; } -static auto DiagnoseExtendImplOutsideClass(Context& context, - SemIR::LocId loc_id) -> void { - CARBON_DIAGNOSTIC(ExtendImplOutsideClass, Error, - "`extend impl` can only be used in a class"); - context.emitter().Emit(loc_id, ExtendImplOutsideClass); +// Sets the `ImplId` in the `ImplWitnessTable`. +static auto AssignImplIdInWitness(Context& context, SemIR::ImplId impl_id, + SemIR::InstId witness_id) -> void { + auto witness = context.insts().GetAs(witness_id); + auto witness_table = + context.insts().GetAs(witness.witness_table_id); + witness_table.impl_id = impl_id; + // Note: The `ImplWitnessTable` instruction is `Unique`, so while this marks + // the instruction as being a dependent instruction of a generic impl, it will + // not be substituted into the eval block. + ReplaceInstBeforeConstantUse(context, witness.witness_table_id, + witness_table); } -// If the specified name scope corresponds to a class, returns the corresponding -// class declaration. -// TODO: Should this be somewhere more central? -static auto TryAsClassScope(Context& context, SemIR::NameScopeId scope_id) - -> std::optional { - if (!scope_id.has_value()) { - return std::nullopt; +// Looks for any unused generic bindings. If one is found, it is diagnosed and +// false is returned. +static auto VerifyAllGenericBindingsUsed(Context& context, SemIR::LocId loc_id, + SemIR::LocId implicit_params_loc_id, + SemIR::Impl& impl) -> bool { + if (impl.witness_id == SemIR::ErrorInst::InstId) { + return true; } - auto& scope = context.name_scopes().Get(scope_id); - if (!scope.inst_id().has_value()) { - return std::nullopt; + if (!impl.generic_id.has_value()) { + return true; } - return context.insts().TryGetAs(scope.inst_id()); -} -auto GetImplDefaultSelfType(Context& context) -> SemIR::TypeId { - auto parent_scope_id = context.decl_name_stack().PeekParentScopeId(); - - if (auto class_decl = TryAsClassScope(context, parent_scope_id)) { - return context.classes().Get(class_decl->class_id).self_type_id; + if (impl.implicit_param_patterns_id.has_value()) { + for (auto inst_id : + context.inst_blocks().Get(impl.implicit_param_patterns_id)) { + if (inst_id == SemIR::ErrorInst::InstId) { + // An error was already diagnosed for a generic binding. + return true; + } + } } - // TODO: This is also valid in a mixin. + auto deduced_specific_id = DeduceImplArguments( + context, loc_id, impl, context.constant_values().Get(impl.self_id), + impl.interface.specific_id); + if (deduced_specific_id.has_value()) { + // Deduction succeeded, all bindings were used. + return true; + } - return SemIR::TypeId::None; + CARBON_DIAGNOSTIC(ImplUnusedBinding, Error, + "`impl` with unused generic binding"); + // TODO: This location may be incorrect, the binding may be inherited + // from an outer declaration. It would be nice to get the particular + // binding that was undeducible back from DeduceImplArguments here and + // use that. + auto diag_loc_id = + implicit_params_loc_id.has_value() ? implicit_params_loc_id : loc_id; + context.emitter().Emit(diag_loc_id, ImplUnusedBinding); + return false; } -// Process an `extend impl` declaration by extending the impl scope with the -// `impl`'s scope. -static auto ExtendImpl(Context& context, Parse::NodeId extend_node, - SemIR::LocId loc_id, SemIR::ImplId impl_id, - Parse::NodeId self_type_node_id, - SemIR::TypeId self_type_id, - SemIR::LocId implicit_params_loc_id, - SemIR::TypeInstId constraint_type_inst_id, - SemIR::TypeId constraint_type_id) -> bool { +// Apply an `extend impl` declaration by extending the parent scope with the +// `impl`. If there's an error it is diagnosed and false is returned. +static auto ApplyExtendImplAs(Context& context, SemIR::LocId loc_id, + const SemIR::Impl& impl, + Parse::NodeId extend_node, + SemIR::LocId implicit_params_loc_id) -> bool { auto parent_scope_id = context.decl_name_stack().PeekParentScopeId(); - if (!parent_scope_id.has_value()) { - DiagnoseExtendImplOutsideClass(context, loc_id); - return false; - } - // TODO: This is also valid in a mixin. - if (!TryAsClassScope(context, parent_scope_id)) { - DiagnoseExtendImplOutsideClass(context, loc_id); + + auto class_scope = TryAsClassScope(context, parent_scope_id); + if (!class_scope) { + if (impl.witness_id != SemIR::ErrorInst::InstId) { + CARBON_DIAGNOSTIC(ExtendImplOutsideClass, Error, + "`extend impl` can only be used in a class"); + context.emitter().Emit(loc_id, ExtendImplOutsideClass); + } return false; } auto& parent_scope = context.name_scopes().Get(parent_scope_id); + // An error was already diagnosed, but this is `extend impl as` inside a + // class, so propagate the error into the enclosing class scope. + if (impl.witness_id == SemIR::ErrorInst::InstId) { + parent_scope.set_has_error(); + return false; + } + if (implicit_params_loc_id.has_value()) { CARBON_DIAGNOSTIC(ExtendImplForall, Error, "cannot `extend` a parameterized `impl`"); @@ -440,96 +453,41 @@ static auto ExtendImpl(Context& context, Parse::NodeId extend_node, return false; } - const auto& impl = context.impls().Get(impl_id); - - if (context.parse_tree().node_kind(self_type_node_id) == - Parse::NodeKind::ImplTypeAs) { - CARBON_DIAGNOSTIC(ExtendImplSelfAs, Error, - "cannot `extend` an `impl` with an explicit self type"); - auto diag = context.emitter().Build(extend_node, ExtendImplSelfAs); - - // If the explicit self type is not the default, just bail out. - if (self_type_id != GetImplDefaultSelfType(context)) { - diag.Emit(); - parent_scope.set_has_error(); - return false; - } - - // The explicit self type is the same as the default self type, so suggest - // removing it and recover as if it were not present. - if (auto self_as = - context.parse_tree_and_subtrees().ExtractAs( - self_type_node_id)) { - CARBON_DIAGNOSTIC(ExtendImplSelfAsDefault, Note, - "remove the explicit `Self` type here"); - diag.Note(self_as->type_expr, ExtendImplSelfAsDefault); - } - diag.Emit(); + if (!RequireCompleteType( + context, context.types().GetTypeIdForTypeInstId(impl.constraint_id), + SemIR::LocId(impl.constraint_id), [&] { + CARBON_DIAGNOSTIC(ExtendImplAsIncomplete, Error, + "`extend impl as` incomplete facet type {0}", + InstIdAsType); + return context.emitter().Build(loc_id, ExtendImplAsIncomplete, + impl.constraint_id); + })) { + parent_scope.set_has_error(); + return false; } - if (impl.witness_id == SemIR::ErrorInst::InstId) { - parent_scope.set_has_error(); + if (!impl.generic_id.has_value()) { + parent_scope.AddExtendedScope(impl.constraint_id); } else { - bool is_complete = RequireCompleteType( - context, constraint_type_id, SemIR::LocId(constraint_type_inst_id), - [&] { - CARBON_DIAGNOSTIC(ExtendImplAsIncomplete, Error, - "`extend impl as` incomplete facet type {0}", - InstIdAsType); - return context.emitter().Build(impl.latest_decl_id(), - ExtendImplAsIncomplete, - constraint_type_inst_id); - }); - if (!is_complete) { - parent_scope.set_has_error(); - return false; - } + auto constraint_id_in_self_specific = AddTypeInst( + context, SemIR::LocId(impl.constraint_id), + {.type_id = SemIR::TypeType::TypeId, + .inst_id = impl.constraint_id, + .specific_id = context.generics().GetSelfSpecific(impl.generic_id)}); + parent_scope.AddExtendedScope(constraint_id_in_self_specific); } - parent_scope.AddExtendedScope(constraint_type_inst_id); return true; } -// Diagnoses when an impl has an unused binding. -static auto DiagnoseUnusedGenericBinding(Context& context, SemIR::LocId loc_id, - SemIR::LocId implicit_params_loc_id, - SemIR::ImplId impl_id) -> void { - auto& impl = context.impls().Get(impl_id); - if (!impl.generic_id.has_value() || - impl.witness_id == SemIR::ErrorInst::InstId) { - return; - } - - auto deduced_specific_id = DeduceImplArguments( - context, loc_id, impl, context.constant_values().Get(impl.self_id), - impl.interface.specific_id); - if (deduced_specific_id.has_value()) { - // Deduction succeeded, all bindings were used. - return; - } - - CARBON_DIAGNOSTIC(ImplUnusedBinding, Error, - "`impl` with unused generic binding"); - // TODO: This location may be incorrect, the binding may be inherited - // from an outer declaration. It would be nice to get the particular - // binding that was undeducible back from DeduceImplArguments here and - // use that. - auto diag_loc_id = - implicit_params_loc_id.has_value() ? implicit_params_loc_id : loc_id; - context.emitter().Emit(diag_loc_id, ImplUnusedBinding); - // Don't try to match the impl at all, save us work and possible future - // diagnostics. - FillImplWitnessWithErrors(context, context.impls().Get(impl_id)); -} - -auto StartImplDecl(Context& context, SemIR::LocId loc_id, - SemIR::LocId implicit_params_loc_id, SemIR::Impl impl, - bool is_definition, - std::optional extend_impl) - -> std::pair { +auto GetOrAddImpl(Context& context, SemIR::LocId loc_id, + SemIR::LocId implicit_params_loc_id, SemIR::Impl impl, + bool is_definition, Parse::NodeId extend_node) + -> SemIR::ImplId { auto impl_id = SemIR::ImplId::None; // Add the impl declaration. + bool invalid_redecl = false; auto lookup_bucket_ref = context.impls().GetOrAddLookupBucket(impl); // TODO: Detect two impl declarations with the same self type and interface, // and issue an error if they don't match. @@ -538,106 +496,86 @@ auto StartImplDecl(Context& context, SemIR::LocId loc_id, if (IsValidImplRedecl(context, impl, prev_impl_id)) { impl_id = prev_impl_id; } else { - // IsValidImplRedecl() has issued a diagnostic, avoid generating more - // diagnostics for this declaration. - impl.witness_id = SemIR::ErrorInst::InstId; + // IsValidImplRedecl() has issued a diagnostic, take care to avoid + // generating more diagnostics for this declaration. + invalid_redecl = true; } break; } } - // Create a new impl if this isn't a valid redeclaration. - if (!impl_id.has_value()) { + if (impl_id.has_value()) { + // This is a redeclaration of another impl, now held in `impl_id`. + auto& prev_impl = context.impls().Get(impl_id); + FinishGenericRedecl(context, prev_impl.generic_id); + } else { + // This is a new declaration (possibly with an attached definition). Create + // a new `impl_id`, filling the missing generic and witness in the provided + // `Impl`. impl.generic_id = BuildGeneric(context, impl.latest_decl_id()); - if (impl.witness_id != SemIR::ErrorInst::InstId) { - if (impl.interface.interface_id.has_value()) { - impl.witness_id = - ImplWitnessForDeclaration(context, impl, is_definition); - } else { + + // Due to lack of an instruction to set to ErrorInst, an `InterfaceId::None` + // indicates that the interface could not be identified and an error was + // diagnosed. If there's any error in the construction of the impl, then the + // witness can't be constructed. We set it to ErrorInt to make the impl + // unusable for impl lookup. + if (!impl.interface.interface_id.has_value() || invalid_redecl || + impl.self_id == SemIR::ErrorInst::TypeInstId || + impl.constraint_id == SemIR::ErrorInst::TypeInstId) { + impl.witness_id = SemIR::ErrorInst::InstId; + // TODO: We might also want to mark that the name scope for the impl has + // an error -- at least once we start making name lookups within the + // impl also look into the facet (eg, so you can name associated + // constants from within the impl). + } + + if (is_definition && impl.witness_id != SemIR::ErrorInst::InstId) { + if (!RequireCompleteFacetTypeForImplDefinition( + context, SemIR::LocId(impl.latest_decl_id()), + impl.constraint_id)) { impl.witness_id = SemIR::ErrorInst::InstId; - // TODO: We might also want to mark that the name scope for the impl has - // an error -- at least once we start making name lookups within the - // impl also look into the facet (eg, so you can name associated - // constants from within the impl). } } + + if (impl.witness_id != SemIR::ErrorInst::InstId) { + // This makes either a placeholder witness or a full witness table. The + // full witness table is deferred to the impl definition unless the + // declaration uses rewrite constraints to set values of associated + // constants in the interface. + impl.witness_id = ImplWitnessForDeclaration(context, impl, is_definition); + } + FinishGenericDecl(context, SemIR::LocId(impl.latest_decl_id()), impl.generic_id); // From here on, use the `Impl` from the `ImplStore` instead of `impl` // in order to make and see any changes to the `Impl`. impl_id = context.impls().Add(impl); lookup_bucket_ref.push_back(impl_id); - - AssignImplIdInWitness(context, impl_id, impl.witness_id); - - // Looking to see if there are any generic bindings on the `impl` - // declaration that are not deducible. If so, and the `impl` does not - // actually use all its generic bindings, and will never be matched. This - // should be diagnossed to the user. - bool has_error_in_implicit_pattern = false; - if (impl.implicit_param_patterns_id.has_value()) { - for (auto inst_id : - context.inst_blocks().Get(impl.implicit_param_patterns_id)) { - if (inst_id == SemIR::ErrorInst::InstId) { - has_error_in_implicit_pattern = true; - break; - } - } + if (impl.witness_id != SemIR::ErrorInst::InstId) { + AssignImplIdInWitness(context, impl_id, impl.witness_id); } - if (!has_error_in_implicit_pattern) { - DiagnoseUnusedGenericBinding(context, loc_id, implicit_params_loc_id, - impl_id); - } - } else { auto& stored_impl = context.impls().Get(impl_id); - FinishGenericRedecl(context, stored_impl.generic_id); - } - // Write the impl ID into the ImplDecl. - auto impl_decl = - context.insts().GetAs(impl.first_owning_decl_id); - CARBON_CHECK(!impl_decl.impl_id.has_value()); - impl_decl.impl_id = impl_id; - ReplaceInstBeforeConstantUse(context, impl.first_owning_decl_id, impl_decl); - - // For an `extend impl` declaration, mark the impl as extending this `impl`. - if (extend_impl) { - auto& stored_impl_info = context.impls().Get(impl_decl.impl_id); - auto self_type_id = - context.types().GetTypeIdForTypeInstId(stored_impl_info.self_id); - if (self_type_id != SemIR::ErrorInst::TypeId) { - auto constraint_id = impl.constraint_id; - if (stored_impl_info.generic_id.has_value()) { - constraint_id = AddTypeInst( - context, SemIR::LocId(constraint_id), - {.type_id = SemIR::TypeType::TypeId, - .inst_id = constraint_id, - .specific_id = context.generics().GetSelfSpecific( - stored_impl_info.generic_id)}); - } - if (!ExtendImpl(context, extend_impl->extend_node_id, loc_id, - impl_decl.impl_id, extend_impl->self_type_node_id, - self_type_id, implicit_params_loc_id, constraint_id, - extend_impl->constraint_type_id)) { - // Don't allow the invalid impl to be used. - FillImplWitnessWithErrors(context, stored_impl_info); - } + // Look to see if there are any generic bindings on the `impl` declaration + // that are not deducible. If so, and the `impl` does not actually use all + // its generic bindings, and will never be matched. This should be + // diagnossed to the user. + if (!VerifyAllGenericBindingsUsed(context, loc_id, implicit_params_loc_id, + stored_impl)) { + FillImplWitnessWithErrors(context, stored_impl); } - } - // Impl definitions are required in the same file as the declaration. We skip - // this requirement if we've already issued an invalid redeclaration error, or - // there is an error that would prevent the impl from being legal to define. - if (!is_definition) { - auto& stored_impl = context.impls().Get(impl_id); - if (stored_impl.witness_id != SemIR::ErrorInst::InstId) { - context.definitions_required_by_decl().push_back( - stored_impl.latest_decl_id()); + if (extend_node.has_value()) { + if (!ApplyExtendImplAs(context, loc_id, stored_impl, extend_node, + implicit_params_loc_id)) { + // Signal the erroneous impl should not be used in lookup. + FillImplWitnessWithErrors(context, stored_impl); + } } } - return {impl_id, impl.latest_decl_id()}; + return impl_id; } } // namespace Carbon::Check diff --git a/toolchain/check/impl.h b/toolchain/check/impl.h index 4d50ffa8b61a0..983daead1d7fb 100644 --- a/toolchain/check/impl.h +++ b/toolchain/check/impl.h @@ -27,10 +27,6 @@ auto FinishImplWitness(Context& context, SemIR::ImplId impl_id) -> void; // sets the witness id in the `Impl` to an error. auto FillImplWitnessWithErrors(Context& context, SemIR::Impl& impl) -> void; -// Sets the `ImplId` in the `ImplWitnessTable`. -auto AssignImplIdInWitness(Context& context, SemIR::ImplId impl_id, - SemIR::InstId witness_id) -> void; - // Returns whether the impl is either `final` explicitly, or implicitly due to // being concrete. auto IsImplEffectivelyFinal(Context& context, const SemIR::Impl& impl) -> bool; @@ -42,28 +38,16 @@ auto CheckConstraintIsInterface(Context& context, SemIR::InstId impl_decl_id, SemIR::TypeInstId constraint_id) -> SemIR::SpecificInterface; -// Returns the implicit `Self` type for an `impl` when it's in a `class` -// declaration. -auto GetImplDefaultSelfType(Context& context) -> SemIR::TypeId; - -// For `StartImplDecl`, additional details for an `extend impl` declaration. -struct ExtendImplDecl { - Parse::NodeId self_type_node_id; - SemIR::TypeId constraint_type_id; - Parse::NodeId extend_node_id; -}; - -// Starts an impl declaration. The caller is responsible for ensuring a generic -// declaration has been started. This returns the produced `ImplId` and -// `ImplDecl`'s `InstId`. +// Finds an existing `Impl` if the `impl` is a redeclaration. Otherwise, +// finishes construction of the `impl`, adds it to the ImplStore, and returns +// the new `ImplId`. This ensures all redeclarations share the same `ImplId`. // -// The `impl` should be constructed with a placeholder `ImplDecl` which this -// will add the `ImplId` to. -auto StartImplDecl(Context& context, SemIR::LocId loc_id, - SemIR::LocId implicit_params_loc_id, SemIR::Impl impl, - bool is_definition, - std::optional extend_impl) - -> std::pair; +// If the impl is modified with `extend` then the parent's scope is extended +// with it. +auto GetOrAddImpl(Context& context, SemIR::LocId loc_id, + SemIR::LocId implicit_params_loc_id, SemIR::Impl impl, + bool is_definition, Parse::NodeId extend_node) + -> SemIR::ImplId; } // namespace Carbon::Check diff --git a/toolchain/check/name_scope.cpp b/toolchain/check/name_scope.cpp new file mode 100644 index 0000000000000..6591973808b67 --- /dev/null +++ b/toolchain/check/name_scope.cpp @@ -0,0 +1,28 @@ +// 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 "toolchain/check/name_scope.h" + +namespace Carbon::Check { + +// If the specified name scope corresponds to a class, returns the corresponding +// class declaration. +auto TryAsClassScope(Context& context, SemIR::NameScopeId scope_id) + -> std::optional { + if (!scope_id.has_value()) { + return std::nullopt; + } + auto& scope = context.name_scopes().Get(scope_id); + if (!scope.inst_id().has_value()) { + return std::nullopt; + } + auto class_decl = context.insts().TryGetAs(scope.inst_id()); + if (!class_decl) { + return std::nullopt; + } + // TODO: This is also valid in a mixin. + return {{.class_decl = *class_decl}}; +} + +} // namespace Carbon::Check diff --git a/toolchain/check/name_scope.h b/toolchain/check/name_scope.h new file mode 100644 index 0000000000000..755e872272895 --- /dev/null +++ b/toolchain/check/name_scope.h @@ -0,0 +1,27 @@ +// 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 + +#ifndef CARBON_TOOLCHAIN_CHECK_NAME_SCOPE_H_ +#define CARBON_TOOLCHAIN_CHECK_NAME_SCOPE_H_ + +#include + +#include "toolchain/check/context.h" +#include "toolchain/sem_ir/ids.h" +#include "toolchain/sem_ir/typed_insts.h" + +namespace Carbon::Check { + +struct ClassScope { + SemIR::ClassDecl class_decl; +}; + +// If the specified name scope corresponds to a class, returns the corresponding +// class declaration. +auto TryAsClassScope(Context& context, SemIR::NameScopeId scope_id) + -> std::optional; + +} // namespace Carbon::Check + +#endif // CARBON_TOOLCHAIN_CHECK_NAME_SCOPE_H_ diff --git a/toolchain/check/testdata/facet/require_invalid.carbon b/toolchain/check/testdata/facet/require_invalid.carbon index a17d687e44c66..420a7945e2431 100644 --- a/toolchain/check/testdata/facet/require_invalid.carbon +++ b/toolchain/check/testdata/facet/require_invalid.carbon @@ -123,3 +123,20 @@ interface Z { // CHECK:STDERR: extend require () impls Y; } + +// --- fail_extend_require_with_invalid_type.carbon +library "[[@TEST_NAME]]"; + +interface Y {} + +interface Z { + // CHECK:STDERR: fail_extend_require_with_invalid_type.carbon:[[@LINE+8]]:18: error: name `Invalid` not found [NameNotFound] + // CHECK:STDERR: extend require Invalid* impls Y; + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + // CHECK:STDERR: fail_extend_require_with_invalid_type.carbon:[[@LINE+4]]:3: error: no `Self` reference found in `require` declaration; `Self` must appear in the self-type or as a generic parameter for each `interface` or `constraint` [RequireImplsMissingSelf] + // CHECK:STDERR: extend require Invalid* impls Y; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + extend require Invalid* impls Y; +} diff --git a/toolchain/check/testdata/impl/fail_extend_impl_forall.carbon b/toolchain/check/testdata/impl/fail_extend_impl_forall.carbon index a6a3623126032..223a09d41844f 100644 --- a/toolchain/check/testdata/impl/fail_extend_impl_forall.carbon +++ b/toolchain/check/testdata/impl/fail_extend_impl_forall.carbon @@ -139,7 +139,6 @@ class C { // CHECK:STDOUT: } // CHECK:STDOUT: %GenericInterface.impl_witness_table = impl_witness_table (), @C.as.GenericInterface.impl [concrete] // CHECK:STDOUT: %GenericInterface.impl_witness: = impl_witness %GenericInterface.impl_witness_table, @C.as.GenericInterface.impl(constants.%T) [symbolic = @C.as.GenericInterface.impl.%GenericInterface.impl_witness (constants.%GenericInterface.impl_witness)] -// CHECK:STDOUT: %.loc24: type = specific_constant @C.as.GenericInterface.impl.%GenericInterface.type.loc24_54.2, @C.as.GenericInterface.impl(constants.%T) [symbolic = constants.%GenericInterface.type.114] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: diff --git a/toolchain/check/testdata/impl/fail_extend_impl_type_as.carbon b/toolchain/check/testdata/impl/fail_extend_impl_type_as.carbon index 4528c12185dea..0516cba56a34f 100644 --- a/toolchain/check/testdata/impl/fail_extend_impl_type_as.carbon +++ b/toolchain/check/testdata/impl/fail_extend_impl_type_as.carbon @@ -58,7 +58,6 @@ class E { // CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] -// CHECK:STDOUT: %I.impl_witness.943: = impl_witness @C.%I.impl_witness_table [concrete] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] // CHECK:STDOUT: %complete_type.357: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: %D: type = class_type @D [concrete] @@ -101,7 +100,7 @@ class E { // CHECK:STDOUT: !requires: // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: impl @i32.as.I.impl: %i32 as %I.ref { +// CHECK:STDOUT: impl @.as.I.impl: as %I.ref { // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = // CHECK:STDOUT: } @@ -114,13 +113,11 @@ class E { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { -// CHECK:STDOUT: impl_decl @i32.as.I.impl [concrete] {} { +// CHECK:STDOUT: impl_decl @.as.I.impl [concrete] {} { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] // CHECK:STDOUT: } -// CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @i32.as.I.impl [concrete] -// CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table [concrete = constants.%I.impl_witness.943] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type.357] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: diff --git a/toolchain/check/testdata/impl/fail_extend_non_interface.carbon b/toolchain/check/testdata/impl/fail_extend_non_interface.carbon index c2ad8af8899e9..0e598c434f2c8 100644 --- a/toolchain/check/testdata/impl/fail_extend_non_interface.carbon +++ b/toolchain/check/testdata/impl/fail_extend_non_interface.carbon @@ -63,7 +63,6 @@ class C { // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C -// CHECK:STDOUT: extend @C.as..impl.%i32 // CHECK:STDOUT: has_error // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/impl/fail_extend_partially_defined_interface.carbon b/toolchain/check/testdata/impl/fail_extend_partially_defined_interface.carbon index 1d633b9cf7a92..2ef11a6e176b2 100644 --- a/toolchain/check/testdata/impl/fail_extend_partially_defined_interface.carbon +++ b/toolchain/check/testdata/impl/fail_extend_partially_defined_interface.carbon @@ -74,7 +74,6 @@ interface I { // CHECK:STDOUT: } // CHECK:STDOUT: %I.impl_witness_table = impl_witness_table (), @C.as.I.impl [concrete] // CHECK:STDOUT: %I.impl_witness: = impl_witness %I.impl_witness_table, @C.as.I.impl(constants.%Self) [symbolic = @C.as.I.impl.%I.impl_witness (constants.%I.impl_witness)] -// CHECK:STDOUT: %.loc24: type = specific_constant @C.as.I.impl.%I.ref, @C.as.I.impl(constants.%Self) [concrete = constants.%I.type] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: From 06634ada27bca99a19d1ae1e1db2337a17cb2b00 Mon Sep 17 00:00:00 2001 From: danakj Date: Fri, 21 Nov 2025 13:38:28 -0500 Subject: [PATCH 30/31] fix-extra-diag --- toolchain/check/handle_impl.cpp | 11 +++++--- toolchain/check/handle_require.cpp | 17 +++++++------ .../testdata/facet/require_invalid.carbon | 25 +++++++++++++------ .../check/testdata/impl/extend_impl.carbon | 14 +++++++++++ 4 files changed, 47 insertions(+), 20 deletions(-) diff --git a/toolchain/check/handle_impl.cpp b/toolchain/check/handle_impl.cpp index d1bd240653487..fcef1e7c94ff3 100644 --- a/toolchain/check/handle_impl.cpp +++ b/toolchain/check/handle_impl.cpp @@ -71,8 +71,7 @@ auto HandleParseNode(Context& context, Parse::ImplTypeAsId node_id) -> bool { auto self_type = ExprAsType(context, self_node, self_id); const auto& introducer = context.decl_introducer_state_stack().innermost(); - if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend) && - self_type.type_id != SemIR::ErrorInst::TypeId) { + if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend)) { auto class_scope = TryAsClassScope(context, context.decl_name_stack().PeekParentScopeId()); if (class_scope) { @@ -86,7 +85,9 @@ auto HandleParseNode(Context& context, Parse::ImplTypeAsId node_id) -> bool { if (self_type.type_id != GetImplDefaultSelfType(context, *class_scope)) { // If the explicit self type is not the default, the self-type is an // error. - diag.Emit(); + if (self_type.type_id != SemIR::ErrorInst::TypeId) { + diag.Emit(); + } self_type.inst_id = SemIR::ErrorInst::TypeInstId; } else { // Otherwise, suggest removing the self-type, and continue as if there @@ -94,7 +95,9 @@ auto HandleParseNode(Context& context, Parse::ImplTypeAsId node_id) -> bool { CARBON_DIAGNOSTIC(ExtendImplSelfAsDefault, Note, "remove the explicit `Self` type here"); diag.Note(self_node, ExtendImplSelfAsDefault); - diag.Emit(); + if (self_type.type_id != SemIR::ErrorInst::TypeId) { + diag.Emit(); + } } } } diff --git a/toolchain/check/handle_require.cpp b/toolchain/check/handle_require.cpp index b22d2899598c8..4d3901eb16c49 100644 --- a/toolchain/check/handle_require.cpp +++ b/toolchain/check/handle_require.cpp @@ -86,14 +86,15 @@ auto HandleParseNode(Context& context, Parse::RequireTypeImplsId node_id) auto self_type = ExprAsType(context, self_node_id, self_inst_id); const auto& introducer = context.decl_introducer_state_stack().innermost(); - if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend) && - self_type.type_id != SemIR::ErrorInst::TypeId) { - CARBON_DIAGNOSTIC(RequireImplsExtendWithExplicitSelf, Error, - "`extend require impls` with explicit type"); - // TODO: If the explicit self-type matches a lookup of NameId::SelfType, add - // a note to the diagnostic: "remove the explicit `Self` type here", and - // continue without an ErrorInst. See ExtendImplSelfAsDefault. - context.emitter().Emit(self_node_id, RequireImplsExtendWithExplicitSelf); + if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend)) { + if (self_type.type_id != SemIR::ErrorInst::TypeId) { + CARBON_DIAGNOSTIC(RequireImplsExtendWithExplicitSelf, Error, + "`extend require impls` with explicit type"); + // TODO: If the explicit self-type matches a lookup of NameId::SelfType, + // add a note to the diagnostic: "remove the explicit `Self` type here", + // and continue without an ErrorInst. See ExtendImplSelfAsDefault. + context.emitter().Emit(self_node_id, RequireImplsExtendWithExplicitSelf); + } self_type.inst_id = SemIR::ErrorInst::TypeInstId; } diff --git a/toolchain/check/testdata/facet/require_invalid.carbon b/toolchain/check/testdata/facet/require_invalid.carbon index 420a7945e2431..a5d5df4568fa4 100644 --- a/toolchain/check/testdata/facet/require_invalid.carbon +++ b/toolchain/check/testdata/facet/require_invalid.carbon @@ -124,19 +124,28 @@ interface Z { extend require () impls Y; } -// --- fail_extend_require_with_invalid_type.carbon +// --- fail_extend_require_with_nonexistant_type.carbon library "[[@TEST_NAME]]"; interface Y {} interface Z { - // CHECK:STDERR: fail_extend_require_with_invalid_type.carbon:[[@LINE+8]]:18: error: name `Invalid` not found [NameNotFound] - // CHECK:STDERR: extend require Invalid* impls Y; - // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: fail_extend_require_with_nonexistant_type.carbon:[[@LINE+4]]:18: error: name `nonexistant` not found [NameNotFound] + // CHECK:STDERR: extend require nonexistant impls Y; + // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: - // CHECK:STDERR: fail_extend_require_with_invalid_type.carbon:[[@LINE+4]]:3: error: no `Self` reference found in `require` declaration; `Self` must appear in the self-type or as a generic parameter for each `interface` or `constraint` [RequireImplsMissingSelf] - // CHECK:STDERR: extend require Invalid* impls Y; - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + extend require nonexistant impls Y; +} + +// --- fail_extend_require_with_nonexistant_type_pointer.carbon +library "[[@TEST_NAME]]"; + +interface Y {} + +interface Z { + // CHECK:STDERR: fail_extend_require_with_nonexistant_type_pointer.carbon:[[@LINE+4]]:18: error: name `nonexistant` not found [NameNotFound] + // CHECK:STDERR: extend require nonexistant* impls Y; + // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: - extend require Invalid* impls Y; + extend require nonexistant* impls Y; } diff --git a/toolchain/check/testdata/impl/extend_impl.carbon b/toolchain/check/testdata/impl/extend_impl.carbon index 021185d79b099..c28bc3553b8a8 100644 --- a/toolchain/check/testdata/impl/extend_impl.carbon +++ b/toolchain/check/testdata/impl/extend_impl.carbon @@ -39,6 +39,20 @@ interface I {} class C { // CHECK:STDERR: fail_extend_impl_nonexistent.carbon:[[@LINE+4]]:15: error: name `nonexistent` not found [NameNotFound] + // CHECK:STDERR: extend impl nonexistent as I {} + // CHECK:STDERR: ^~~~~~~~~~~ + // CHECK:STDERR: + extend impl nonexistent as I {} +} + +// --- fail_extend_impl_nonexistent_pointer.carbon + +library "[[@TEST_NAME]]"; + +interface I {} + +class C { + // CHECK:STDERR: fail_extend_impl_nonexistent_pointer.carbon:[[@LINE+4]]:15: error: name `nonexistent` not found [NameNotFound] // CHECK:STDERR: extend impl nonexistent* as I {} // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: From c713931bd535c888b01acc7075d6bfefa89b8cb4 Mon Sep 17 00:00:00 2001 From: danakj Date: Fri, 21 Nov 2025 16:00:16 -0500 Subject: [PATCH 31/31] early-out --- toolchain/check/impl.cpp | 113 ++++++++++++++++++++------------------- 1 file changed, 57 insertions(+), 56 deletions(-) diff --git a/toolchain/check/impl.cpp b/toolchain/check/impl.cpp index 62089693aee8d..eb010ff83559c 100644 --- a/toolchain/check/impl.cpp +++ b/toolchain/check/impl.cpp @@ -508,71 +508,72 @@ auto GetOrAddImpl(Context& context, SemIR::LocId loc_id, // This is a redeclaration of another impl, now held in `impl_id`. auto& prev_impl = context.impls().Get(impl_id); FinishGenericRedecl(context, prev_impl.generic_id); - } else { - // This is a new declaration (possibly with an attached definition). Create - // a new `impl_id`, filling the missing generic and witness in the provided - // `Impl`. - impl.generic_id = BuildGeneric(context, impl.latest_decl_id()); - - // Due to lack of an instruction to set to ErrorInst, an `InterfaceId::None` - // indicates that the interface could not be identified and an error was - // diagnosed. If there's any error in the construction of the impl, then the - // witness can't be constructed. We set it to ErrorInt to make the impl - // unusable for impl lookup. - if (!impl.interface.interface_id.has_value() || invalid_redecl || - impl.self_id == SemIR::ErrorInst::TypeInstId || - impl.constraint_id == SemIR::ErrorInst::TypeInstId) { + return impl_id; + } + + // This is a new declaration (possibly with an attached definition). Create a + // new `impl_id`, filling the missing generic and witness in the provided + // `Impl`. + + impl.generic_id = BuildGeneric(context, impl.latest_decl_id()); + + // Due to lack of an instruction to set to ErrorInst, an `InterfaceId::None` + // indicates that the interface could not be identified and an error was + // diagnosed. If there's any error in the construction of the impl, then the + // witness can't be constructed. We set it to ErrorInt to make the impl + // unusable for impl lookup. + if (!impl.interface.interface_id.has_value() || invalid_redecl || + impl.self_id == SemIR::ErrorInst::TypeInstId || + impl.constraint_id == SemIR::ErrorInst::TypeInstId) { + impl.witness_id = SemIR::ErrorInst::InstId; + // TODO: We might also want to mark that the name scope for the impl has an + // error -- at least once we start making name lookups within the impl also + // look into the facet (eg, so you can name associated constants from within + // the impl). + } + + if (is_definition && impl.witness_id != SemIR::ErrorInst::InstId) { + if (!RequireCompleteFacetTypeForImplDefinition( + context, SemIR::LocId(impl.latest_decl_id()), impl.constraint_id)) { impl.witness_id = SemIR::ErrorInst::InstId; - // TODO: We might also want to mark that the name scope for the impl has - // an error -- at least once we start making name lookups within the - // impl also look into the facet (eg, so you can name associated - // constants from within the impl). } + } - if (is_definition && impl.witness_id != SemIR::ErrorInst::InstId) { - if (!RequireCompleteFacetTypeForImplDefinition( - context, SemIR::LocId(impl.latest_decl_id()), - impl.constraint_id)) { - impl.witness_id = SemIR::ErrorInst::InstId; - } - } + if (impl.witness_id != SemIR::ErrorInst::InstId) { + // This makes either a placeholder witness or a full witness table. The full + // witness table is deferred to the impl definition unless the declaration + // uses rewrite constraints to set values of associated constants in the + // interface. + impl.witness_id = ImplWitnessForDeclaration(context, impl, is_definition); + } - if (impl.witness_id != SemIR::ErrorInst::InstId) { - // This makes either a placeholder witness or a full witness table. The - // full witness table is deferred to the impl definition unless the - // declaration uses rewrite constraints to set values of associated - // constants in the interface. - impl.witness_id = ImplWitnessForDeclaration(context, impl, is_definition); - } + FinishGenericDecl(context, SemIR::LocId(impl.latest_decl_id()), + impl.generic_id); + // From here on, use the `Impl` from the `ImplStore` instead of `impl` in + // order to make and see any changes to the `Impl`. + impl_id = context.impls().Add(impl); + lookup_bucket_ref.push_back(impl_id); + if (impl.witness_id != SemIR::ErrorInst::InstId) { + AssignImplIdInWitness(context, impl_id, impl.witness_id); + } - FinishGenericDecl(context, SemIR::LocId(impl.latest_decl_id()), - impl.generic_id); - // From here on, use the `Impl` from the `ImplStore` instead of `impl` - // in order to make and see any changes to the `Impl`. - impl_id = context.impls().Add(impl); - lookup_bucket_ref.push_back(impl_id); - if (impl.witness_id != SemIR::ErrorInst::InstId) { - AssignImplIdInWitness(context, impl_id, impl.witness_id); - } + auto& stored_impl = context.impls().Get(impl_id); - auto& stored_impl = context.impls().Get(impl_id); + // Look to see if there are any generic bindings on the `impl` declaration + // that are not deducible. If so, and the `impl` does not actually use all its + // generic bindings, and will never be matched. This should be diagnossed to + // the user. + if (!VerifyAllGenericBindingsUsed(context, loc_id, implicit_params_loc_id, + stored_impl)) { + FillImplWitnessWithErrors(context, stored_impl); + } - // Look to see if there are any generic bindings on the `impl` declaration - // that are not deducible. If so, and the `impl` does not actually use all - // its generic bindings, and will never be matched. This should be - // diagnossed to the user. - if (!VerifyAllGenericBindingsUsed(context, loc_id, implicit_params_loc_id, - stored_impl)) { + if (extend_node.has_value()) { + if (!ApplyExtendImplAs(context, loc_id, stored_impl, extend_node, + implicit_params_loc_id)) { + // Signal the erroneous impl should not be used in lookup. FillImplWitnessWithErrors(context, stored_impl); } - - if (extend_node.has_value()) { - if (!ApplyExtendImplAs(context, loc_id, stored_impl, extend_node, - implicit_params_loc_id)) { - // Signal the erroneous impl should not be used in lookup. - FillImplWitnessWithErrors(context, stored_impl); - } - } } return impl_id;