Import the full NamedConstraint from its decl (#6311)

This imports the entire `NamedConstraint` structure when importing
`NamedConstraintDecl`. This will be required to identify a facet type
that contains a named constraint, as we will need to pull the `require`
decls out of the `NamedConstraint` structure to do so.

I tried making the `InterfaceDecl` code path
[templated](https://github.com/carbon-language/carbon-lang/pull/6308#discussion_r2482655927)
to reuse it, but it was a lot of template parameters including field
pointers into `InterfaceDecl`, `GenericInterfaceType`,
`SpecificInterface`, and it was very hard to read so I gave up on that
approach here.
This commit is contained in:
Dana Jansens
2025-11-03 16:25:41 +00:00
committed by GitHub
parent 94bb6be185
commit 30c3a35776
2 changed files with 181 additions and 11 deletions
+166 -3
View File
@@ -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::NamedConstraintId, SemIR::ConstantId> {
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<SemIR::GenericNamedConstraintType>(
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<SemIR::FacetType>()) {
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<SemIR::GenericNamedConstraintType>(
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<SemIR::InstId> 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;
}
@@ -49,14 +49,16 @@ fn F(T:! B) {}
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %type: type = facet_type <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: