diff --git a/toolchain/check/handle_require.cpp b/toolchain/check/handle_require.cpp index 0329dbd3855f..20ab48063ba9 100644 --- a/toolchain/check/handle_require.cpp +++ b/toolchain/check/handle_require.cpp @@ -13,6 +13,7 @@ #include "toolchain/check/subst.h" #include "toolchain/check/type.h" #include "toolchain/check/type_completion.h" +#include "toolchain/diagnostics/diagnostic.h" #include "toolchain/parse/node_ids.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/named_constraint.h" @@ -81,6 +82,15 @@ 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 introducer = context.decl_introducer_state_stack().innermost(); + if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend)) { + CARBON_DIAGNOSTIC(RequireImplsExtendWithExplicitSelf, Error, + "`extend require impls` with explicit type"); + context.emitter().Emit(self_node_id, RequireImplsExtendWithExplicitSelf); + self_inst_id = SemIR::ErrorInst::InstId; + } + auto self_type = ExprAsType(context, self_node_id, self_inst_id); context.node_stack().Push(node_id, self_type.inst_id); return true; @@ -241,6 +251,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, @@ -251,6 +263,7 @@ auto HandleParseNode(Context& context, Parse::RequireDeclId node_id) -> bool { .facet_type_inst_id = context.types().GetAsTypeInstId(constraint_inst_id), .facet_type_id = constraint_facet_type.facet_type_id, + .extend_self = extend, .decl_id = decl_id, .parent_scope_id = context.scope_stack().PeekNameScopeId(), .generic_id = BuildGenericDecl(context, decl_id)}); diff --git a/toolchain/check/impl.cpp b/toolchain/check/impl.cpp index bf11e2e157ac..86ec08306325 100644 --- a/toolchain/check/impl.cpp +++ b/toolchain/check/impl.cpp @@ -121,6 +121,7 @@ auto ImplWitnessStartDefinition(Context& context, SemIR::Impl& impl) -> void { witness_block.empty()) { if (!RequireCompleteFacetTypeForImplDefinition( context, SemIR::LocId(impl.latest_decl_id()), impl.constraint_id)) { + FillImplWitnessWithErrors(context, impl); return; } diff --git a/toolchain/check/impl_lookup.cpp b/toolchain/check/impl_lookup.cpp index 89f476cf8384..4482f2708974 100644 --- a/toolchain/check/impl_lookup.cpp +++ b/toolchain/check/impl_lookup.cpp @@ -203,10 +203,10 @@ static auto GetInterfacesFromConstantId( context.facet_types().Get(facet_type_inst.facet_type_id); auto identified_id = RequireIdentifiedFacetType(context, facet_type_inst, [&] { - CARBON_DIAGNOSTIC(ImplLookupInIncompleteFacetType, Error, - "facet type {0} is incomplete", InstIdAsType); - return context.emitter().Build(loc_id, ImplLookupInIncompleteFacetType, - facet_type_inst_id); + CARBON_DIAGNOSTIC(ImplLookupInUnidentifiedFacetType, Error, + "facet type {0} can not be identified", InstIdAsType); + return context.emitter().Build( + loc_id, ImplLookupInUnidentifiedFacetType, facet_type_inst_id); }); if (!identified_id.has_value()) { return std::nullopt; diff --git a/toolchain/check/import_ref.cpp b/toolchain/check/import_ref.cpp index ad21a9df2965..a3c927299241 100644 --- a/toolchain/check/import_ref.cpp +++ b/toolchain/check/import_ref.cpp @@ -2468,6 +2468,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver, resolver.local_insts().GetAs( new_canonical_facet_type_inst_id); new_require.facet_type_id = new_canonical_facet_type.facet_type_id; + new_require.extend_self = import_require.extend_self; new_require.parent_scope_id = parent_scope_id; SetGenericData(resolver, import_require.generic_id, new_require.generic_id, diff --git a/toolchain/check/testdata/facet/fail_incomplete.carbon b/toolchain/check/testdata/facet/fail_incomplete.carbon index 43d72d227aaf..53495105b060 100644 --- a/toolchain/check/testdata/facet/fail_incomplete.carbon +++ b/toolchain/check/testdata/facet/fail_incomplete.carbon @@ -2,7 +2,7 @@ // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon // // AUTOUPDATE // TIP: To test this file alone, run: @@ -13,95 +13,114 @@ // --- fail_incomplete_interface.carbon library "[[@TEST_NAME]]"; -interface A; -interface B {} +interface X; class C {} -fn G[T:! A](t: T) {} -fn H[T:! A & B](t: T) {} +// Requires X identified. +impl C as X; -fn F() { - // CHECK:STDERR: fail_incomplete_interface.carbon:[[@LINE+4]]:3: error: cannot convert type `C` into type implementing `A` [ConversionFailureTypeToFacet] - // CHECK:STDERR: C as A; - // CHECK:STDERR: ^~~~~~ - // CHECK:STDERR: - C as A; +// 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 {} - // CHECK:STDERR: fail_incomplete_interface.carbon:[[@LINE+4]]:3: error: cannot convert type `C` into type implementing `A & B` [ConversionFailureTypeToFacet] - // CHECK:STDERR: C as (A & B); - // CHECK:STDERR: ^~~~~~~~~~~~ - // CHECK:STDERR: - C as (A & B); - - // CHECK:STDERR: fail_incomplete_interface.carbon:[[@LINE+7]]:3: error: cannot convert type `C` into type implementing `A` [ConversionFailureTypeToFacet] - // CHECK:STDERR: G({} as C); - // CHECK:STDERR: ^~~~~~~~~~ - // CHECK:STDERR: fail_incomplete_interface.carbon:[[@LINE-19]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] - // CHECK:STDERR: fn G[T:! A](t: T) {} - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: - G({} as C); - - // CHECK:STDERR: fail_incomplete_interface.carbon:[[@LINE+7]]:3: error: cannot convert type `C` into type implementing `A & B` [ConversionFailureTypeToFacet] - // CHECK:STDERR: H({} as C); - // CHECK:STDERR: ^~~~~~~~~~ - // CHECK:STDERR: fail_incomplete_interface.carbon:[[@LINE-27]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] - // CHECK:STDERR: fn H[T:! A & B](t: T) {} - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: - H({} as C); -} - -// --- fail_incomplete_constraint.carbon +// --- fail_incomplete_interface_without_forward_decl.carbon library "[[@TEST_NAME]]"; -constraint A; -interface B {} +interface X; class C {} -fn G[T:! A](t: T) {} -fn H[T:! A & B](t: T) {} +// 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_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]]"; + +constraint Z; + +fn AsZ(T:! Z) {} fn F() { - // CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE+7]]:3: error: facet type `A` is incomplete [ImplLookupInIncompleteFacetType] - // CHECK:STDERR: C as A; - // CHECK:STDERR: ^~~~~~ - // CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE-11]]:1: note: constraint was forward declared here [NamedConstraintForwardDeclaredHere] - // CHECK:STDERR: constraint A; + // Requires Z identified. + // CHECK:STDERR: fail_impl_lookup_incomplete.carbon:[[@LINE+10]]:3: error: facet type `Z` can not be identified [ImplLookupInUnidentifiedFacetType] + // CHECK:STDERR: AsZ(()); + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: fail_impl_lookup_incomplete.carbon:[[@LINE-9]]:1: note: constraint was forward declared here [NamedConstraintForwardDeclaredHere] + // CHECK:STDERR: constraint Z; // CHECK:STDERR: ^~~~~~~~~~~~~ + // CHECK:STDERR: fail_impl_lookup_incomplete.carbon:[[@LINE-10]]:8: note: initializing generic parameter `T` declared here [InitializingGenericParam] + // CHECK:STDERR: fn AsZ(T:! Z) {} + // CHECK:STDERR: ^ // CHECK:STDERR: - C as A; - - // CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE+7]]:3: error: facet type `B & A` is incomplete [ImplLookupInIncompleteFacetType] - // CHECK:STDERR: C as (A & B); - // CHECK:STDERR: ^~~~~~~~~~~~ - // CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE-20]]:1: note: constraint was forward declared here [NamedConstraintForwardDeclaredHere] - // CHECK:STDERR: constraint A; - // CHECK:STDERR: ^~~~~~~~~~~~~ - // CHECK:STDERR: - C as (A & B); - - // CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE+10]]:3: error: facet type `A` is incomplete [ImplLookupInIncompleteFacetType] - // CHECK:STDERR: G({} as C); - // CHECK:STDERR: ^~~~~~~~~~ - // CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE-29]]:1: note: constraint was forward declared here [NamedConstraintForwardDeclaredHere] - // CHECK:STDERR: constraint A; - // CHECK:STDERR: ^~~~~~~~~~~~~ - // CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE-28]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] - // CHECK:STDERR: fn G[T:! A](t: T) {} - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: - G({} as C); - - // CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE+10]]:3: error: facet type `B & A` is incomplete [ImplLookupInIncompleteFacetType] - // CHECK:STDERR: H({} as C); - // CHECK:STDERR: ^~~~~~~~~~ - // CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE-41]]:1: note: constraint was forward declared here [NamedConstraintForwardDeclaredHere] - // CHECK:STDERR: constraint A; - // CHECK:STDERR: ^~~~~~~~~~~~~ - // CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE-39]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere] - // CHECK:STDERR: fn H[T:! A & B](t: T) {} - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: - H({} as C); + AsZ(()); } diff --git a/toolchain/check/testdata/facet/require_invalid.carbon b/toolchain/check/testdata/facet/require_invalid.carbon index aa791ba75e69..a17d687e44c6 100644 --- a/toolchain/check/testdata/facet/require_invalid.carbon +++ b/toolchain/check/testdata/facet/require_invalid.carbon @@ -110,3 +110,16 @@ class C { // CHECK:STDERR: require impls Y; } + +// --- fail_extend_require_type.carbon +library "[[@TEST_NAME]]"; + +interface Y {} + +interface Z { + // CHECK:STDERR: fail_extend_require_type.carbon:[[@LINE+4]]:18: error: `extend require impls` with explicit type [RequireImplsExtendWithExplicitSelf] + // CHECK:STDERR: extend require () impls Y; + // CHECK:STDERR: ^~ + // CHECK:STDERR: + extend require () impls Y; +} diff --git a/toolchain/check/testdata/impl/impl_as_named_constraint.carbon b/toolchain/check/testdata/impl/impl_as_named_constraint.carbon index c84d3c08388b..6820f2ab7800 100644 --- a/toolchain/check/testdata/impl/impl_as_named_constraint.carbon +++ b/toolchain/check/testdata/impl/impl_as_named_constraint.carbon @@ -10,34 +10,16 @@ // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/impl_as_named_constraint.carbon -// --- fail_incomplete_constraint.carbon -library "[[@TEST_NAME]]"; - -constraint A; - -class C {} - -// CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE+7]]:1: error: facet type `A` cannot be identified in `impl as` [ImplOfUnidentifiedFacetType] -// CHECK:STDERR: impl C as A {} -// CHECK:STDERR: ^~~~~~~~~~~~~ -// CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE-7]]:1: note: constraint was forward declared here [NamedConstraintForwardDeclaredHere] -// CHECK:STDERR: constraint A; -// CHECK:STDERR: ^~~~~~~~~~~~~ -// CHECK:STDERR: -impl C as A {} - // --- fail_empty_constraint.carbon library "[[@TEST_NAME]]"; constraint A {} -class C {} - // CHECK:STDERR: fail_empty_constraint.carbon:[[@LINE+4]]:1: error: impl as 0 interfaces, expected 1 [ImplOfNotOneInterface] -// CHECK:STDERR: impl C as A {} -// CHECK:STDERR: ^~~~~~~~~~~~~ +// CHECK:STDERR: impl () as A {} +// CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: -impl C as A {} +impl () as A {} // --- fail_too_many_interfaces_in_constraint.carbon library "[[@TEST_NAME]]"; @@ -45,50 +27,109 @@ library "[[@TEST_NAME]]"; interface A1; interface A2; constraint B { - require impls A1; - require impls A2; + extend require impls A1; + extend require impls A2; } -class C {} - -// TODO: This should fail since B does not name a single interface, it names -// more than one. -// CHECK:STDERR: fail_too_many_interfaces_in_constraint.carbon:[[@LINE+4]]:1: error: impl as 0 interfaces, expected 1 [ImplOfNotOneInterface] -// CHECK:STDERR: impl C as B {} -// CHECK:STDERR: ^~~~~~~~~~~~~ +// CHECK:STDERR: fail_too_many_interfaces_in_constraint.carbon:[[@LINE+4]]:1: error: impl as 2 interfaces, expected 1 [ImplOfNotOneInterface] +// CHECK:STDERR: impl () as B {} +// CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: -impl C as B {} +impl () as B {} -// --- fail_todo_one_declared_interface_in_constraint.carbon +// --- one_extend_impls_interface_in_constraint.carbon library "[[@TEST_NAME]]"; -// TODO: This should work since B can be identified to have one interface. +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 +library "[[@TEST_NAME]]"; interface A; constraint B { require impls A; } -class C {} -// CHECK:STDERR: fail_todo_one_declared_interface_in_constraint.carbon:[[@LINE+4]]:1: error: impl as 0 interfaces, expected 1 [ImplOfNotOneInterface] -// CHECK:STDERR: impl C as B {} -// CHECK:STDERR: ^~~~~~~~~~~~~ +// CHECK:STDERR: fail_one_impls_interface_in_constraint.carbon:[[@LINE+4]]:1: error: impl as 0 interfaces, expected 1 [ImplOfNotOneInterface] +// CHECK:STDERR: impl () as B {} +// CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: -impl C as B {} +impl () as B {} -// --- fail_todo_one_defined_interface_in_constraint.carbon +// --- nested_constraints.carbon library "[[@TEST_NAME]]"; -// TODO: This should work since B can be identified to have one interface. - interface A {} constraint B { - require impls A; + extend require impls A; +} +constraint C { + extend require impls B; } -class C {} -// CHECK:STDERR: fail_todo_one_defined_interface_in_constraint.carbon:[[@LINE+4]]:1: error: impl as 0 interfaces, expected 1 [ImplOfNotOneInterface] -// CHECK:STDERR: impl C as B {} -// CHECK:STDERR: ^~~~~~~~~~~~~ +impl () as C {} + +// --- fail_nested_constraints_not_extend_outer.carbon +library "[[@TEST_NAME]]"; + +interface A {} + +constraint B { + extend require impls A; +} +constraint C { + require impls B; +} + +// CHECK:STDERR: fail_nested_constraints_not_extend_outer.carbon:[[@LINE+4]]:1: error: impl as 0 interfaces, expected 1 [ImplOfNotOneInterface] +// CHECK:STDERR: impl () as C {} +// CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: -impl C as B {} +impl () as C {} + +// --- fail_nested_constraints_not_extend_inner.carbon +library "[[@TEST_NAME]]"; + +interface A {} + +constraint B { + require impls A; +} +constraint C { + extend require impls B; +} + +// CHECK:STDERR: fail_nested_constraints_not_extend_inner.carbon:[[@LINE+4]]:1: error: impl as 0 interfaces, expected 1 [ImplOfNotOneInterface] +// CHECK:STDERR: impl () as C {} +// CHECK:STDERR: ^~~~~~~~~~~~~~ +// CHECK:STDERR: +impl () as C {} + +// --- fail_nested_constraints_not_extend_both.carbon +library "[[@TEST_NAME]]"; + +interface A {} + +constraint B { + require impls A; +} +constraint C { + require impls B; +} + +// CHECK:STDERR: fail_nested_constraints_not_extend_both.carbon:[[@LINE+4]]:1: error: impl as 0 interfaces, expected 1 [ImplOfNotOneInterface] +// CHECK:STDERR: impl () as C {} +// CHECK:STDERR: ^~~~~~~~~~~~~~ +// CHECK:STDERR: +impl () as C {} diff --git a/toolchain/check/testdata/named_constraint/import_constraint_decl.carbon b/toolchain/check/testdata/named_constraint/import_constraint_decl.carbon index cc95ee8e01d5..3ae435761269 100644 --- a/toolchain/check/testdata/named_constraint/import_constraint_decl.carbon +++ b/toolchain/check/testdata/named_constraint/import_constraint_decl.carbon @@ -49,7 +49,7 @@ class C {} // CHECK:STDERR: impl C as A; -// --- fail_todo_import_complete.carbon +// --- import_complete.carbon library "[[@TEST_NAME]]"; import library "b"; @@ -57,19 +57,9 @@ import library "b"; class C {} // This requires that B is identified. -// TODO: This should work. -// CHECK:STDERR: fail_todo_import_complete.carbon:[[@LINE+4]]:1: error: impl as 0 interfaces, expected 1 [ImplOfNotOneInterface] -// CHECK:STDERR: impl C as B; -// CHECK:STDERR: ^~~~~~~~~~~~ -// CHECK:STDERR: impl C as B; // This requires that B is complete. -// TODO: This should work. -// CHECK:STDERR: fail_todo_import_complete.carbon:[[@LINE+4]]:1: error: impl as 0 interfaces, expected 1 [ImplOfNotOneInterface] -// CHECK:STDERR: impl C as B {} -// CHECK:STDERR: ^~~~~~~~~~~~~ -// CHECK:STDERR: impl C as B {} // CHECK:STDOUT: --- a.impl.carbon diff --git a/toolchain/check/testdata/named_constraint/require.carbon b/toolchain/check/testdata/named_constraint/require.carbon index 422417b04c81..eff001f435f4 100644 --- a/toolchain/check/testdata/named_constraint/require.carbon +++ b/toolchain/check/testdata/named_constraint/require.carbon @@ -29,14 +29,10 @@ fn F(T:! Z) { // CHECK:STDERR: ^~~~ // CHECK:STDERR: T.YY(); - // CHECK:STDERR: fail_todo_extend.carbon:[[@LINE+4]]:3: error: cannot convert type `T` that implements `Z` into type implementing `Y` [ConversionFailureFacetToFacet] - // CHECK:STDERR: T.(Y.YY)(); - // CHECK:STDERR: ^~~~~~~~ - // CHECK:STDERR: T.(Y.YY)(); } -// --- fail_todo_implicit_self_impls.carbon +// --- implicit_self_impls.carbon library "[[@TEST_NAME]]"; interface Y { @@ -50,14 +46,10 @@ constraint Z { //@dump-sem-ir-end fn F(T:! Z) { - // CHECK:STDERR: fail_todo_implicit_self_impls.carbon:[[@LINE+4]]:3: error: cannot convert type `T` that implements `Z` into type implementing `Y` [ConversionFailureFacetToFacet] - // CHECK:STDERR: T.(Y.YY)(); - // CHECK:STDERR: ^~~~~~~~ - // CHECK:STDERR: T.(Y.YY)(); } -// --- fail_todo_explicit_self_impls.carbon +// --- explicit_self_impls.carbon library "[[@TEST_NAME]]"; interface Y { @@ -71,10 +63,6 @@ constraint Z { //@dump-sem-ir-end fn F(T:! Z) { - // CHECK:STDERR: fail_todo_explicit_self_impls.carbon:[[@LINE+4]]:3: error: cannot convert type `T` that implements `Z` into type implementing `Y` [ConversionFailureFacetToFacet] - // CHECK:STDERR: T.(Y.YY)(); - // CHECK:STDERR: ^~~~~~~~ - // CHECK:STDERR: T.(Y.YY)(); } @@ -342,7 +330,7 @@ constraint Z { // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_implicit_self_impls.carbon +// CHECK:STDOUT: --- implicit_self_impls.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Y.type: type = facet_type <@Y> [concrete] @@ -387,7 +375,7 @@ constraint Z { // CHECK:STDOUT: %Self.binding.as_type => constants.%Self.binding.as_type // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_explicit_self_impls.carbon +// CHECK:STDOUT: --- explicit_self_impls.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Y.type: type = facet_type <@Y> [concrete] diff --git a/toolchain/check/type_completion.cpp b/toolchain/check/type_completion.cpp index eb48183aa3fb..1293c5dfd143 100644 --- a/toolchain/check/type_completion.cpp +++ b/toolchain/check/type_completion.cpp @@ -4,6 +4,7 @@ #include "toolchain/check/type_completion.h" +#include "common/concepts.h" #include "llvm/ADT/SmallVector.h" #include "toolchain/base/kind_switch.h" #include "toolchain/check/cpp/import.h" @@ -69,6 +70,18 @@ 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); + } +} + namespace { // Worklist-based type completion mechanism. // @@ -397,15 +410,15 @@ auto TypeCompleter::AddNestedIncompleteTypes(SemIR::Inst type_inst) -> bool { break; } case CARBON_KIND(SemIR::FacetType inst): { - // TODO: Get the complete facet type here. auto identified_id = RequireIdentifiedFacetType(*context_, inst, diagnoser_); if (!identified_id.has_value()) { return false; } + const auto& identified = context_->identified_facet_types().Get(identified_id); - // Every mentioned interface needs to be complete. + for (auto req_interface : identified.required_interfaces()) { auto interface_id = req_interface.interface_id; const auto& interface = context_->interfaces().Get(interface_id); @@ -771,18 +784,11 @@ auto RequireConcreteType(Context& context, SemIR::TypeId type_id, return true; } -auto RequireIdentifiedFacetType(Context& context, - const SemIR::FacetType& facet_type, - MakeDiagnosticBuilderFn diagnoser) - -> SemIR::IdentifiedFacetTypeId { - if (auto identified_id = - context.identified_facet_types().TryGetId(facet_type.facet_type_id); - identified_id.has_value()) { - return identified_id; - } - const auto& facet_type_info = - context.facet_types().Get(facet_type.facet_type_id); - +// Require all named constraints in the facet type are identified. For a named +// constraint, this means the constraint definition is complete. +static auto RequireIdentifiedNamedConstraints( + Context& context, const SemIR::FacetTypeInfo& facet_type_info, + MakeDiagnosticBuilderFn diagnoser) -> bool { auto named_constraint_ids = llvm::map_range( llvm::concat( facet_type_info.extend_named_constraints, @@ -797,15 +803,84 @@ auto RequireIdentifiedFacetType(Context& context, NoteIncompleteNamedConstraint(context, named_constraint_id, builder); builder.Emit(); } + return false; + } + } + return true; +} + +auto RequireIdentifiedFacetType(Context& context, + const SemIR::FacetType& facet_type, + MakeDiagnosticBuilderFn diagnoser) + -> SemIR::IdentifiedFacetTypeId { + if (auto identified_id = + context.identified_facet_types().TryGetId(facet_type.facet_type_id); + identified_id.has_value()) { + return identified_id; + } + + // Work queue. + llvm::SmallVector extend_facet_types = { + facet_type.facet_type_id}; + llvm::SmallVector impls_facet_types; + + // Outputs for the IdentifiedFacetType. + llvm::SmallVector extends; + llvm::SmallVector self_impls; + + while (true) { + auto next_facet_type_id = SemIR::FacetTypeId::None; + bool facet_type_extends = false; + if (!extend_facet_types.empty()) { + next_facet_type_id = extend_facet_types.pop_back_val(); + facet_type_extends = true; + } else if (!impls_facet_types.empty()) { + next_facet_type_id = impls_facet_types.pop_back_val(); + facet_type_extends = false; + } else { + break; + } + + const auto& facet_type_info = context.facet_types().Get(next_facet_type_id); + + if (!RequireIdentifiedNamedConstraints(context, facet_type_info, + diagnoser)) { return SemIR::IdentifiedFacetTypeId::None; } + + if (facet_type_extends) { + llvm::append_range(extends, facet_type_info.extend_constraints); + } else { + llvm::append_range(self_impls, facet_type_info.extend_constraints); + } + llvm::append_range(self_impls, facet_type_info.self_impls_constraints); + + 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 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); + }); + } } - // TODO: expand named constraints // TODO: Process other kinds of requirements. - return context.identified_facet_types().Add( - facet_type.facet_type_id, {facet_type_info.extend_constraints, - facet_type_info.self_impls_constraints}); + return context.identified_facet_types().Add(facet_type.facet_type_id, + {extends, self_impls}); } auto AsCompleteType(Context& context, SemIR::TypeId type_id, diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index b780dd679be7..7c21fd5b9e5d 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -360,13 +360,14 @@ CARBON_DIAGNOSTIC_KIND(MissingImplInMemberAccess) CARBON_DIAGNOSTIC_KIND(MissingImplInMemberAccessNote) CARBON_DIAGNOSTIC_KIND(ImplLookupCycle) CARBON_DIAGNOSTIC_KIND(ImplLookupCycleNote) -CARBON_DIAGNOSTIC_KIND(ImplLookupInIncompleteFacetType) +CARBON_DIAGNOSTIC_KIND(ImplLookupInUnidentifiedFacetType) // Require checking. +CARBON_DIAGNOSTIC_KIND(RequireImplsExtendWithExplicitSelf) CARBON_DIAGNOSTIC_KIND(RequireImplsMissingFacetType) CARBON_DIAGNOSTIC_KIND(RequireImplsMissingSelf) -CARBON_DIAGNOSTIC_KIND(RequireInWrongScope) CARBON_DIAGNOSTIC_KIND(RequireImplsUnidentifiedFacetType) +CARBON_DIAGNOSTIC_KIND(RequireInWrongScope) // Let declaration checking. CARBON_DIAGNOSTIC_KIND(ExpectedInitializerAfterLet) diff --git a/toolchain/sem_ir/inst_fingerprinter.cpp b/toolchain/sem_ir/inst_fingerprinter.cpp index baf2ae32230c..773b939373af 100644 --- a/toolchain/sem_ir/inst_fingerprinter.cpp +++ b/toolchain/sem_ir/inst_fingerprinter.cpp @@ -246,8 +246,10 @@ struct Worklist { auto Add(RequireImplsId require_id) -> void { CARBON_CHECK(require_id.has_value()); const auto& require = sem_ir->require_impls().Get(require_id); - Add(require.self_id); - Add(require.facet_type_id); + Add(sem_ir->constant_values().Get(require.self_id)); + Add(sem_ir->constant_values().Get(require.facet_type_inst_id)); + contents.push_back(require.extend_self); + Add(require.parent_scope_id); } auto Add(AssociatedConstantId assoc_const_id) -> void { diff --git a/toolchain/sem_ir/require_impls.h b/toolchain/sem_ir/require_impls.h index b50e17846ae8..cb95f491b088 100644 --- a/toolchain/sem_ir/require_impls.h +++ b/toolchain/sem_ir/require_impls.h @@ -24,6 +24,8 @@ struct RequireImpls : Printable { TypeInstId facet_type_inst_id; // The `FacetTypeInfo` derived from the `facet_type_inst_id` instruction. FacetTypeId facet_type_id; + // If the facet type extends `Self`. When true, the `self_id` will be `Self`. + bool extend_self; // The location of the `require` declaration. InstId decl_id;