diff --git a/toolchain/check/import_ref.cpp b/toolchain/check/import_ref.cpp index 66e93d005c18..4b3cf85bc41d 100644 --- a/toolchain/check/import_ref.cpp +++ b/toolchain/check/import_ref.cpp @@ -264,6 +264,9 @@ class ImportContext { auto import_interfaces() -> const SemIR::InterfaceStore& { return import_ir().interfaces(); } + auto import_named_constraints() -> const SemIR::NamedConstraintStore& { + return import_ir().named_constraints(); + } auto import_ints() -> const SharedValueStores::IntStore& { return import_ir().ints(); } @@ -337,6 +340,9 @@ class ImportContext { auto local_interfaces() -> SemIR::InterfaceStore& { return local_ir().interfaces(); } + auto local_named_constraints() -> SemIR::NamedConstraintStore& { + return local_ir().named_constraints(); + } auto local_ints() -> SharedValueStores::IntStore& { return local_ir().ints(); } @@ -2521,7 +2527,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver, const auto& import_interface = resolver.import_interfaces().Get(inst.interface_id); - SemIR::InterfaceId interface_id = SemIR::InterfaceId::None; + auto interface_id = SemIR::InterfaceId::None; if (!interface_const_id.has_value()) { auto import_specific_id = SemIR::SpecificId::None; if (auto import_generic_interface_type = @@ -2598,6 +2604,162 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver, return ResolveResult::Done(interface_const_id, new_interface.first_decl_id()); } +// Make a declaration of a named constraint. This is done as a separate step +// from importing the constraint definition in order to resolve cycles. +static auto MakeNamedConstraintDecl( + ImportContext& context, + const SemIR::NamedConstraint& import_named_constraint, + SemIR::SpecificId enclosing_specific_id) + -> std::pair { + SemIR::NamedConstraintDecl named_constraint_decl = { + .type_id = SemIR::TypeType::TypeId, + .named_constraint_id = SemIR::NamedConstraintId::None, + .decl_block_id = SemIR::InstBlockId::Empty}; + auto named_constraint_decl_id = AddPlaceholderImportedInst( + context, import_named_constraint.first_owning_decl_id, + named_constraint_decl); + + // Start with an incomplete interface. + named_constraint_decl.named_constraint_id = + context.local_named_constraints().Add( + {GetIncompleteLocalEntityBase(context, named_constraint_decl_id, + import_named_constraint), + {.scope_id = import_named_constraint.is_complete() + ? AddPlaceholderNameScope(context) + : SemIR::NameScopeId::None}}); + + if (import_named_constraint.has_parameters()) { + named_constraint_decl.type_id = GetGenericNamedConstraintType( + context.local_context(), named_constraint_decl.named_constraint_id, + enclosing_specific_id); + } + + // Write the interface ID into the InterfaceDecl. + auto interface_const_id = ReplacePlaceholderImportedInst( + context, named_constraint_decl_id, named_constraint_decl); + return {named_constraint_decl.named_constraint_id, interface_const_id}; +} + +// Imports the definition for a named constraint that has been imported as a +// forward declaration. +static auto AddNamedConstraintDefinition( + ImportContext& context, + const SemIR::NamedConstraint& import_named_constraint, + SemIR::NamedConstraint& new_named_constraint, SemIR::InstId self_param_id) + -> void { + auto& new_scope = + context.local_name_scopes().Get(new_named_constraint.scope_id); + const auto& import_scope = + context.import_name_scopes().Get(import_named_constraint.scope_id); + + // Push a block so that we can add scoped instructions to it. + context.local_context().inst_block_stack().Push(); + InitializeNameScopeAndImportRefs(context, import_scope, new_scope, + new_named_constraint.first_owning_decl_id, + SemIR::NameId::None, + new_named_constraint.parent_scope_id); + new_scope.set_is_interface_definition(); + new_named_constraint.body_block_id = + context.local_context().inst_block_stack().Pop(); + new_named_constraint.self_param_id = self_param_id; + + CARBON_CHECK(import_scope.extended_scopes().empty(), + "Interfaces don't currently have extended scopes to support."); +} + +static auto TryResolveTypedInst(ImportRefResolver& resolver, + SemIR::NamedConstraintDecl inst, + SemIR::ConstantId named_constraint_const_id) + -> ResolveResult { + const auto& import_named_constraint = + resolver.import_named_constraints().Get(inst.named_constraint_id); + + auto named_constraint_id = SemIR::NamedConstraintId::None; + if (!named_constraint_const_id.has_value()) { + auto import_specific_id = SemIR::SpecificId::None; + if (auto import_generic_named_constraint_type = + resolver.import_types().TryGetAs( + inst.type_id)) { + import_specific_id = + import_generic_named_constraint_type->enclosing_specific_id; + } + auto specific_data = GetLocalSpecificData(resolver, import_specific_id); + if (resolver.HasNewWork()) { + // This is the end of the first phase. Don't make a new interface yet if + // we already have new work. + return ResolveResult::Retry(); + } + + // On the second phase, create a forward declaration of the interface. + auto enclosing_specific_id = + GetOrAddLocalSpecific(resolver, import_specific_id, specific_data); + std::tie(named_constraint_id, named_constraint_const_id) = + MakeNamedConstraintDecl(resolver, import_named_constraint, + enclosing_specific_id); + } else { + // On the third phase, compute the interface ID from the constant value of + // the declaration. + auto named_constraint_const_inst = resolver.local_insts().Get( + resolver.local_constant_values().GetInstId(named_constraint_const_id)); + if (auto facet_type = + named_constraint_const_inst.TryAs()) { + const SemIR::FacetTypeInfo& facet_type_info = + resolver.local_facet_types().Get(facet_type->facet_type_id); + CARBON_CHECK(facet_type_info.extend_named_constraints.size() == 1); + CARBON_CHECK(facet_type_info.extend_constraints.empty()); + CARBON_CHECK(facet_type_info.self_impls_constraints.empty()); + CARBON_CHECK(facet_type_info.self_impls_named_constraints.empty()); + named_constraint_id = + facet_type_info.extend_named_constraints.front().named_constraint_id; + } else { + auto generic_named_constraint_type = + resolver.local_types().GetAs( + named_constraint_const_inst.type_id()); + named_constraint_id = generic_named_constraint_type.named_constraint_id; + } + } + + auto parent_scope_id = + GetLocalNameScopeId(resolver, import_named_constraint.parent_scope_id); + auto implicit_param_patterns = GetLocalInstBlockContents( + resolver, import_named_constraint.implicit_param_patterns_id); + auto param_patterns = GetLocalInstBlockContents( + resolver, import_named_constraint.param_patterns_id); + auto generic_data = + GetLocalGenericData(resolver, import_named_constraint.generic_id); + + std::optional self_param_id; + if (import_named_constraint.is_complete()) { + self_param_id = + GetLocalConstantInstId(resolver, import_named_constraint.self_param_id); + } + auto& new_named_constraint = + resolver.local_named_constraints().Get(named_constraint_id); + + if (resolver.HasNewWork()) { + return ResolveResult::Retry(named_constraint_const_id, + new_named_constraint.first_decl_id()); + } + + new_named_constraint.parent_scope_id = parent_scope_id; + new_named_constraint.implicit_param_patterns_id = + GetLocalCanonicalInstBlockId( + resolver, import_named_constraint.implicit_param_patterns_id, + implicit_param_patterns); + new_named_constraint.param_patterns_id = GetLocalCanonicalInstBlockId( + resolver, import_named_constraint.param_patterns_id, param_patterns); + SetGenericData(resolver, import_named_constraint.generic_id, + import_named_constraint.generic_id, generic_data); + + if (import_named_constraint.is_complete()) { + CARBON_CHECK(self_param_id); + AddNamedConstraintDefinition(resolver, import_named_constraint, + new_named_constraint, *self_param_id); + } + return ResolveResult::Done(named_constraint_const_id, + new_named_constraint.first_decl_id()); +} + static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::FacetAccessType inst) -> ResolveResult { auto facet_value_inst_id = @@ -3187,8 +3349,9 @@ static auto TryResolveInstCanonical(ImportRefResolver& resolver, case CARBON_KIND(SemIR::InterfaceDecl inst): { return TryResolveTypedInst(resolver, inst, const_id); } - // TODO: Import NamedConstraintDecl once its FacetType constant value - // contains the NamedConstraintId. + case CARBON_KIND(SemIR::NamedConstraintDecl inst): { + return TryResolveTypedInst(resolver, inst, const_id); + } default: break; } diff --git a/toolchain/check/testdata/named_constraint/import_constraint_decl.carbon b/toolchain/check/testdata/named_constraint/import_constraint_decl.carbon index 04277add39ec..96a3a56518a3 100644 --- a/toolchain/check/testdata/named_constraint/import_constraint_decl.carbon +++ b/toolchain/check/testdata/named_constraint/import_constraint_decl.carbon @@ -49,14 +49,16 @@ fn F(T:! B) {} // 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: %B.type: type = facet_type <@B> [concrete] +// CHECK:STDOUT: %T: %B.type = symbolic_binding T, 0 [symbolic] +// CHECK:STDOUT: %pattern_type: type = pattern_type %B.type [concrete] // CHECK:STDOUT: %F.type: type = fn_type @F [concrete] // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { -// CHECK:STDOUT: %Main.B: type = import_ref Main//b, B, loaded [concrete = constants.%type] +// CHECK:STDOUT: %Main.B: type = import_ref Main//b, B, loaded [concrete = constants.%B.type] +// CHECK:STDOUT: %Main.import_ref = import_ref Main//b, loc3_14, unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -69,16 +71,21 @@ fn F(T:! B) {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { // CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete] // CHECK:STDOUT: } { -// CHECK:STDOUT: %.loc4: type = splice_block %B.ref [concrete = constants.%type] { +// CHECK:STDOUT: %.loc4: type = splice_block %B.ref [concrete = constants.%B.type] { // CHECK:STDOUT: %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self] -// CHECK:STDOUT: %B.ref: type = name_ref B, imports.%Main.B [concrete = constants.%type] +// CHECK:STDOUT: %B.ref: type = name_ref B, imports.%Main.B [concrete = constants.%B.type] // CHECK:STDOUT: } -// CHECK:STDOUT: %T.loc4_6.2: %type = symbolic_binding T, 0 [symbolic = %T.loc4_6.1 (constants.%T)] +// CHECK:STDOUT: %T.loc4_6.2: %B.type = symbolic_binding T, 0 [symbolic = %T.loc4_6.1 (constants.%T)] // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: generic fn @F(%T.loc4_6.2: %type) { -// CHECK:STDOUT: %T.loc4_6.1: %type = symbolic_binding T, 0 [symbolic = %T.loc4_6.1 (constants.%T)] +// CHECK:STDOUT: constraint @B [from "b.carbon"] { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = imports.%Main.import_ref +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @F(%T.loc4_6.2: %B.type) { +// CHECK:STDOUT: %T.loc4_6.1: %B.type = symbolic_binding T, 0 [symbolic = %T.loc4_6.1 (constants.%T)] // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: