From cf57c85545703f9adeb49ce4cf5eb6fad32796b4 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Thu, 10 Apr 2025 18:59:02 -0400 Subject: [PATCH] Introduce TypeInstId (#5288) TypeInstId is an InstId whose constant value has a type of TypeType. This includes: - Type value instructions, the `ClassType` or `IntLiteralType` instructions. - Constraint value instructions, which are the `FacetType` and `TypeType` instructions, each of which also have type TypeType. TypeInstId encodes in the type system that it is safe to convert the instruction's value to a TypeId, and CHECKs at construction that this invariant is maintained. --------- Co-authored-by: Richard Smith --- toolchain/check/convert.cpp | 14 ++--- toolchain/check/convert.h | 2 +- toolchain/check/deduce.cpp | 4 ++ toolchain/check/eval.cpp | 10 ++++ toolchain/check/facet_type.cpp | 8 +-- toolchain/check/facet_type.h | 6 +-- toolchain/check/handle_binding_pattern.cpp | 2 +- toolchain/check/handle_class.cpp | 18 +++---- toolchain/check/handle_function.cpp | 7 +-- toolchain/check/handle_impl.cpp | 52 +++++++++---------- toolchain/check/handle_where.cpp | 2 +- toolchain/check/impl_lookup.cpp | 6 +++ toolchain/check/import_cpp.cpp | 2 +- toolchain/check/import_ref.cpp | 39 ++++++++------ toolchain/check/node_stack.h | 2 +- toolchain/check/subst.cpp | 12 +++++ toolchain/check/type.cpp | 4 +- toolchain/check/type.h | 4 +- .../compile/raw_and_textual_ir.carbon | 4 +- .../driver/testdata/compile/raw_ir.carbon | 4 +- toolchain/sem_ir/formatter.cpp | 4 ++ toolchain/sem_ir/id_kind.h | 3 +- toolchain/sem_ir/ids.h | 20 +++++++ toolchain/sem_ir/impl.h | 4 +- toolchain/sem_ir/type.cpp | 47 +++++++++++------ toolchain/sem_ir/type.h | 13 +++-- toolchain/sem_ir/typed_insts.h | 20 +++---- 27 files changed, 202 insertions(+), 111 deletions(-) diff --git a/toolchain/check/convert.cpp b/toolchain/check/convert.cpp index fea81bcb4b26..3b6344b9943e 100644 --- a/toolchain/check/convert.cpp +++ b/toolchain/check/convert.cpp @@ -1076,16 +1076,16 @@ static auto PerformBuiltinConversion( // `FacetValue`, which requires an instruction of type `TypeType`. So if // we are converting from a facet value, we get its `type` via an extra // `FacetAccessType` instruction. - auto type_inst_id = SemIR::InstId::None; + auto type_inst_id = SemIR::TypeInstId::None; if (sem_ir.types().Is(value_type_id)) { - type_inst_id = + type_inst_id = context.types().GetAsTypeInstId( AddInst(context, loc_id, SemIR::FacetAccessType{ .type_id = SemIR::TypeType::SingletonTypeId, .facet_value_inst_id = const_value_id, - }); + })); } else { - type_inst_id = const_value_id; + type_inst_id = context.types().GetAsTypeInstId(const_value_id); } return AddInst( context, loc_id, @@ -1470,7 +1470,7 @@ auto ExprAsType(Context& context, SemIR::LocId loc_id, SemIR::InstId value_id, auto type_inst_id = ConvertToValueOfType(context, loc_id, value_id, SemIR::TypeType::SingletonTypeId); if (type_inst_id == SemIR::ErrorInst::SingletonInstId) { - return {.inst_id = type_inst_id, + return {.inst_id = SemIR::ErrorInst::SingletonTypeInstId, .type_id = SemIR::ErrorInst::SingletonTypeId}; } @@ -1481,11 +1481,11 @@ auto ExprAsType(Context& context, SemIR::LocId loc_id, SemIR::InstId value_id, "cannot evaluate type expression"); context.emitter().Emit(loc_id, TypeExprEvaluationFailure); } - return {.inst_id = SemIR::ErrorInst::SingletonInstId, + return {.inst_id = SemIR::ErrorInst::SingletonTypeInstId, .type_id = SemIR::ErrorInst::SingletonTypeId}; } - return {.inst_id = type_inst_id, + return {.inst_id = context.types().GetAsTypeInstId(type_inst_id), .type_id = context.types().GetTypeIdForTypeConstantId(type_const_id)}; } diff --git a/toolchain/check/convert.h b/toolchain/check/convert.h index cc94f998d29d..a3f425daa386 100644 --- a/toolchain/check/convert.h +++ b/toolchain/check/convert.h @@ -120,7 +120,7 @@ auto ConvertCallArgs(Context& context, SemIR::LocId call_loc_id, // A type that has been converted for use as a type expression. struct TypeExpr { // The converted expression of type `type`, or `ErrorInst::SingletonInstId`. - SemIR::InstId inst_id; + SemIR::TypeInstId inst_id; // The corresponding type, or `ErrorInst::SingletonTypeId`. SemIR::TypeId type_id; }; diff --git a/toolchain/check/deduce.cpp b/toolchain/check/deduce.cpp index 644a4a6c36f7..299620b22164 100644 --- a/toolchain/check/deduce.cpp +++ b/toolchain/check/deduce.cpp @@ -130,6 +130,10 @@ class DeductionWorklist { Add(inst_id, SemIR::InstId(arg), needs_substitution); break; } + case CARBON_KIND(SemIR::TypeInstId inst_id): { + Add(inst_id, SemIR::InstId(arg), needs_substitution); + break; + } case CARBON_KIND(SemIR::StructTypeFieldsId fields_id): { AddAll(fields_id, SemIR::StructTypeFieldsId(arg), needs_substitution); break; diff --git a/toolchain/check/eval.cpp b/toolchain/check/eval.cpp index 553deffcf1ee..cb3946231fc7 100644 --- a/toolchain/check/eval.cpp +++ b/toolchain/check/eval.cpp @@ -421,6 +421,16 @@ static auto GetConstantValue(EvalContext& eval_context, return inst_id; } +static auto GetConstantValue(EvalContext& eval_context, + SemIR::TypeInstId inst_id, Phase* phase) + -> SemIR::TypeInstId { + // The input instruction is a TypeInstId, and eval does not change concrete + // types (like TypeType which TypeInstId implies), so the result is also a + // valid TypeInstId. + return SemIR::TypeInstId::UnsafeMake(GetConstantValue( + eval_context, static_cast(inst_id), phase)); +} + // Explicitly discard a `DestInstId`, because we should not be using the // destination as part of evaluation. static auto GetConstantValue(EvalContext& /*eval_context*/, diff --git a/toolchain/check/facet_type.cpp b/toolchain/check/facet_type.cpp index f1be8eb410da..87b79eff5a57 100644 --- a/toolchain/check/facet_type.cpp +++ b/toolchain/check/facet_type.cpp @@ -39,7 +39,7 @@ static auto WitnessAccessMatchesInterface( } static auto IncompleteFacetTypeDiagnosticBuilder( - Context& context, SemIRLoc loc, SemIR::InstId facet_type_inst_id, + Context& context, SemIRLoc loc, SemIR::TypeInstId facet_type_inst_id, bool is_definition) -> DiagnosticBuilder { if (is_definition) { CARBON_DIAGNOSTIC(ImplAsIncompleteFacetTypeDefinition, Error, @@ -59,7 +59,7 @@ static auto IncompleteFacetTypeDiagnosticBuilder( auto InitialFacetTypeImplWitness( Context& context, SemIR::LocId witness_loc_id, - SemIR::InstId facet_type_inst_id, SemIR::InstId self_type_inst_id, + SemIR::TypeInstId facet_type_inst_id, SemIR::TypeInstId self_type_inst_id, const SemIR::SpecificInterface& interface_to_witness, SemIR::SpecificId self_specific_id, bool is_definition) -> SemIR::InstId { // TODO: Finish facet type resolution. This code currently only handles @@ -233,8 +233,8 @@ auto InitialFacetTypeImplWitness( return witness_inst_id; } -auto RequireCompleteFacetTypeForImplDefinition(Context& context, SemIRLoc loc, - SemIR::InstId facet_type_inst_id) +auto RequireCompleteFacetTypeForImplDefinition( + Context& context, SemIRLoc loc, SemIR::TypeInstId facet_type_inst_id) -> bool { auto facet_type_id = context.types().GetTypeIdForTypeInstId(facet_type_inst_id); diff --git a/toolchain/check/facet_type.h b/toolchain/check/facet_type.h index 9c9aa30ff8fb..609e98a08e83 100644 --- a/toolchain/check/facet_type.h +++ b/toolchain/check/facet_type.h @@ -41,14 +41,14 @@ auto FacetTypeFromInterface(Context& context, SemIR::InterfaceId interface_id, // `Self` type. auto InitialFacetTypeImplWitness( Context& context, SemIR::LocId witness_loc_id, - SemIR::InstId facet_type_inst_id, SemIR::InstId self_type_inst_id, + SemIR::TypeInstId facet_type_inst_id, SemIR::TypeInstId self_type_inst_id, const SemIR::SpecificInterface& interface_to_witness, SemIR::SpecificId self_specific_id, bool is_definition) -> SemIR::InstId; // Returns `true` if the facet type is complete. Otherwise issues a diagnostic // and returns `false`. -auto RequireCompleteFacetTypeForImplDefinition(Context& context, SemIRLoc loc, - SemIR::InstId facet_type_inst_id) +auto RequireCompleteFacetTypeForImplDefinition( + Context& context, SemIRLoc loc, SemIR::TypeInstId facet_type_inst_id) -> bool; // Replaces the placeholder created by `InitialFacetTypeImplWitness` with an diff --git a/toolchain/check/handle_binding_pattern.cpp b/toolchain/check/handle_binding_pattern.cpp index 79777f61205a..7958eab46dc9 100644 --- a/toolchain/check/handle_binding_pattern.cpp +++ b/toolchain/check/handle_binding_pattern.cpp @@ -143,7 +143,7 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id, cast_type_id); }); if (cast_type_id == SemIR::ErrorInst::SingletonTypeId) { - cast_type_inst_id = SemIR::ErrorInst::SingletonInstId; + cast_type_inst_id = SemIR::ErrorInst::SingletonTypeInstId; } auto binding_id = context.parse_tree().As(node_id); diff --git a/toolchain/check/handle_class.cpp b/toolchain/check/handle_class.cpp index c67b421d339c..7cce7243c025 100644 --- a/toolchain/check/handle_class.cpp +++ b/toolchain/check/handle_class.cpp @@ -370,7 +370,7 @@ auto HandleParseNode(Context& context, Parse::AdaptDeclId node_id) -> bool { return true; } - auto [adapted_inst_id, adapted_type_id] = + auto [adapted_type_inst_id, adapted_type_id] = ExprAsType(context, node_id, adapted_type_expr_id); adapted_type_id = AsConcreteType( context, adapted_type_id, node_id, @@ -379,26 +379,26 @@ auto HandleParseNode(Context& context, Parse::AdaptDeclId node_id) -> bool { "adapted type {0} is an incomplete type", InstIdAsType); return context.emitter().Build(node_id, IncompleteTypeInAdaptDecl, - adapted_inst_id); + adapted_type_inst_id); }, [&] { CARBON_DIAGNOSTIC(AbstractTypeInAdaptDecl, Error, "adapted type {0} is an abstract type", InstIdAsType); return context.emitter().Build(node_id, AbstractTypeInAdaptDecl, - adapted_inst_id); + adapted_type_inst_id); }); if (adapted_type_id == SemIR::ErrorInst::SingletonTypeId) { - adapted_inst_id = SemIR::ErrorInst::SingletonInstId; + adapted_type_inst_id = SemIR::ErrorInst::SingletonTypeInstId; } // Build a SemIR representation for the declaration. class_info.adapt_id = AddInst( - context, node_id, {.adapted_type_inst_id = adapted_inst_id}); + context, node_id, {.adapted_type_inst_id = adapted_type_inst_id}); // Extend the class scope with the adapted type's scope if requested. if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend)) { auto& class_scope = context.name_scopes().Get(class_info.scope_id); - class_scope.AddExtendedScope(adapted_inst_id); + class_scope.AddExtendedScope(adapted_type_inst_id); } return true; } @@ -422,17 +422,17 @@ struct BaseInfo { SemIR::TypeId type_id; SemIR::NameScopeId scope_id; - SemIR::InstId inst_id; + SemIR::TypeInstId inst_id; }; constexpr BaseInfo BaseInfo::Error = { .type_id = SemIR::ErrorInst::SingletonTypeId, .scope_id = SemIR::NameScopeId::None, - .inst_id = SemIR::ErrorInst::SingletonInstId}; + .inst_id = SemIR::ErrorInst::SingletonTypeInstId}; } // namespace // Diagnoses an attempt to derive from a final type. static auto DiagnoseBaseIsFinal(Context& context, Parse::NodeId node_id, - SemIR::InstId base_type_inst_id) -> void { + SemIR::TypeInstId base_type_inst_id) -> void { CARBON_DIAGNOSTIC(BaseIsFinal, Error, "deriving from final type {0}; base type must be an " "`abstract` or `base` class", diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index 44bb853a97bb..cef48efa88e7 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -53,7 +53,7 @@ auto HandleParseNode(Context& context, Parse::FunctionIntroducerId node_id) auto HandleParseNode(Context& context, Parse::ReturnTypeId node_id) -> bool { // Propagate the type expression. auto [type_node_id, type_inst_id] = context.node_stack().PopExprWithNodeId(); - auto type_id = ExprAsType(context, type_node_id, type_inst_id).type_id; + auto as_type = ExprAsType(context, type_node_id, type_inst_id); // If the previous node was `IdentifierNameBeforeParams`, then it would have // caused these entries to be pushed to the pattern stacks. But it's possible @@ -69,10 +69,11 @@ auto HandleParseNode(Context& context, Parse::ReturnTypeId node_id) -> bool { } auto return_slot_pattern_id = AddPatternInst( - context, node_id, {.type_id = type_id, .type_inst_id = type_inst_id}); + context, node_id, + {.type_id = as_type.type_id, .type_inst_id = as_type.inst_id}); auto param_pattern_id = AddPatternInst( context, node_id, - {.type_id = type_id, + {.type_id = as_type.type_id, .subpattern_id = return_slot_pattern_id, .index = SemIR::CallParamIndex::None}); context.node_stack().Push(node_id, param_pattern_id); diff --git a/toolchain/check/handle_impl.cpp b/toolchain/check/handle_impl.cpp index 09a42b584ab2..49eb713d3f5b 100644 --- a/toolchain/check/handle_impl.cpp +++ b/toolchain/check/handle_impl.cpp @@ -55,14 +55,14 @@ auto HandleParseNode(Context& context, Parse::ForallId /*node_id*/) -> bool { auto HandleParseNode(Context& context, Parse::TypeImplAsId node_id) -> bool { auto [self_node, self_id] = context.node_stack().PopExprWithNodeId(); - self_id = ExprAsType(context, self_node, self_id).inst_id; - context.node_stack().Push(node_id, self_id); + auto self_type_inst_id = ExprAsType(context, self_node, self_id).inst_id; + context.node_stack().Push(node_id, self_type_inst_id); // 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_id); + AddNameToLookup(context, SemIR::NameId::SelfType, self_type_inst_id); return true; } @@ -109,11 +109,11 @@ auto HandleParseNode(Context& context, Parse::DefaultSelfImplAsId node_id) // is a class and found its `Self`, so additionally performing an unqualified // name lookup would be redundant work, but would avoid duplicating the // handling of the `Self` expression. - auto self_inst_id = AddInst( + auto self_inst_id = context.types().GetAsTypeInstId(AddInst( context, node_id, SemIR::NameRef{.type_id = SemIR::TypeType::SingletonTypeId, .name_id = SemIR::NameId::SelfType, - .value_id = context.types().GetInstId(self_type_id)}); + .value_id = context.types().GetInstId(self_type_id)})); // There's no need to push `Self` into scope here, because we can find it in // the parent class scope. @@ -135,8 +135,8 @@ static auto ExtendImpl(Context& context, Parse::NodeId extend_node, Parse::AnyImplDeclId node_id, SemIR::ImplId impl_id, Parse::NodeId self_type_node, SemIR::TypeId self_type_id, Parse::NodeId params_node, - SemIR::InstId constraint_inst_id, - SemIR::TypeId constraint_id) -> bool { + SemIR::TypeInstId constraint_type_inst_id, + SemIR::TypeId constraint_type_id) -> bool { auto parent_scope_id = context.decl_name_stack().PeekParentScopeId(); if (!parent_scope_id.has_value()) { DiagnoseExtendImplOutsideClass(context, node_id); @@ -188,14 +188,14 @@ static auto ExtendImpl(Context& context, Parse::NodeId extend_node, parent_scope.set_has_error(); } else { bool is_complete = RequireCompleteType( - context, constraint_id, context.insts().GetLocId(constraint_inst_id), - [&] { + context, constraint_type_id, + context.insts().GetLocId(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_inst_id); + constraint_type_inst_id); }); if (!is_complete) { parent_scope.set_has_error(); @@ -203,7 +203,7 @@ static auto ExtendImpl(Context& context, Parse::NodeId extend_node, } } - parent_scope.AddExtendedScope(constraint_inst_id); + parent_scope.AddExtendedScope(constraint_type_inst_id); return true; } @@ -318,7 +318,7 @@ static auto IsValidImplRedecl(Context& context, SemIR::Impl& new_impl, // and returns `None`. static auto CheckConstraintIsInterface(Context& context, SemIR::InstId impl_decl_id, - SemIR::InstId constraint_id) + SemIR::TypeInstId constraint_id) -> SemIR::SpecificInterface { auto facet_type_id = context.types().GetTypeIdForTypeInstId(constraint_id); if (facet_type_id == SemIR::ErrorInst::SingletonTypeId) { @@ -351,15 +351,15 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id, -> std::pair { auto [constraint_node, constraint_id] = context.node_stack().PopExprWithNodeId(); - auto [self_type_node, self_inst_id] = + auto [self_type_node, self_type_inst_id] = context.node_stack().PopWithNodeId(); - auto self_type_id = context.types().GetTypeIdForTypeInstId(self_inst_id); + auto self_type_id = context.types().GetTypeIdForTypeInstId(self_type_inst_id); // Pop the `impl` introducer and any `forall` parameters as a "name". auto name = PopImplIntroducerAndParamsAsNameComponent(context, node_id); auto decl_block_id = context.inst_block_stack().Pop(); // Convert the constraint expression to a type. - auto [constraint_inst_id, constraint_type_id] = + auto [constraint_type_inst_id, constraint_type_id] = ExprAsType(context, constraint_node, constraint_id); // Process modifiers. @@ -385,10 +385,10 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id, SemIR::Impl impl_info = {name_context.MakeEntityWithParamsBase( name, impl_decl_id, /*is_extern=*/false, SemIR::LibraryNameId::None), - {.self_id = self_inst_id, - .constraint_id = constraint_inst_id, + {.self_id = self_type_inst_id, + .constraint_id = constraint_type_inst_id, .interface = CheckConstraintIsInterface( - context, impl_decl_id, constraint_inst_id), + context, impl_decl_id, constraint_type_inst_id), .is_final = is_final}}; // Add the impl declaration. bool invalid_redeclaration = false; @@ -486,17 +486,17 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id, introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend)) { auto extend_node = introducer.modifier_node_id(ModifierOrder::Decl); if (impl_info.generic_id.has_value()) { - SemIR::TypeId type_id = context.insts().Get(constraint_inst_id).type_id(); - constraint_inst_id = AddInst( - context, context.insts().GetLocId(constraint_inst_id), - {.type_id = type_id, - .inst_id = constraint_inst_id, - .specific_id = - context.generics().GetSelfSpecific(impl_info.generic_id)}); + constraint_type_inst_id = + context.types().GetAsTypeInstId(AddInst( + context, context.insts().GetLocId(constraint_type_inst_id), + {.type_id = SemIR::TypeType::SingletonTypeId, + .inst_id = constraint_type_inst_id, + .specific_id = + context.generics().GetSelfSpecific(impl_info.generic_id)})); } if (!ExtendImpl(context, extend_node, node_id, impl_decl.impl_id, self_type_node, self_type_id, name.implicit_params_loc_id, - constraint_inst_id, constraint_type_id)) { + constraint_type_inst_id, constraint_type_id)) { // Don't allow the invalid impl to be used. FillImplWitnessWithErrors(context, impl_info); context.impls().Get(impl_decl.impl_id).witness_id = diff --git a/toolchain/check/handle_where.cpp b/toolchain/check/handle_where.cpp index ae3331c0e1ed..6666313da8ce 100644 --- a/toolchain/check/handle_where.cpp +++ b/toolchain/check/handle_where.cpp @@ -111,7 +111,7 @@ auto HandleParseNode(Context& context, Parse::RequirementImplsId node_id) ImplsOnNonFacetType, Error, "right argument of `impls` requirement must be a facet type"); context.emitter().Emit(rhs_node, ImplsOnNonFacetType); - rhs_as_type.inst_id = SemIR::ErrorInst::SingletonInstId; + rhs_as_type.inst_id = SemIR::ErrorInst::SingletonTypeInstId; } // TODO: Require that at least one side uses a designator. // TODO: For things like `HashSet(.T) as type`, add an implied constraint diff --git a/toolchain/check/impl_lookup.cpp b/toolchain/check/impl_lookup.cpp index 94eec309c01e..835cbb6833dd 100644 --- a/toolchain/check/impl_lookup.cpp +++ b/toolchain/check/impl_lookup.cpp @@ -80,6 +80,12 @@ static auto FindAssociatedImportIRs(Context& context, } break; } + case CARBON_KIND(SemIR::TypeInstId inst_id): { + if (inst_id.has_value()) { + worklist.push_back(inst_id); + } + break; + } case CARBON_KIND(SemIR::InstBlockId inst_block_id): { push_block(inst_block_id); break; diff --git a/toolchain/check/import_cpp.cpp b/toolchain/check/import_cpp.cpp index 4db97d36759b..0c029297b1f9 100644 --- a/toolchain/check/import_cpp.cpp +++ b/toolchain/check/import_cpp.cpp @@ -272,7 +272,7 @@ static auto MapType(Context& context, clang::QualType type) -> TypeExpr { context.ast_context().getTypeSize(type) == 32) { return MakeIntType(context, context.ints().Add(32)); } - return {.inst_id = SemIR::ErrorInst::SingletonInstId, + return {.inst_id = SemIR::ErrorInst::SingletonTypeInstId, .type_id = SemIR::ErrorInst::SingletonTypeId}; } diff --git a/toolchain/check/import_ref.cpp b/toolchain/check/import_ref.cpp index c0a5bdfa2011..02b18a93a3e9 100644 --- a/toolchain/check/import_ref.cpp +++ b/toolchain/check/import_ref.cpp @@ -699,6 +699,15 @@ static auto GetLocalConstantInstId(ImportRefResolver& resolver, return resolver.local_constant_values().GetInstIdIfValid(const_id); } +// Returns the local constant InstId for an imported InstId. +static auto GetLocalTypeInstId(ImportRefResolver& resolver, + SemIR::TypeInstId inst_id) -> SemIR::TypeInstId { + // The input instruction is a TypeInstId, and import does not change the type + // of instructions, so the result is also a valid TypeInstId. + return SemIR::TypeInstId::UnsafeMake( + GetLocalConstantInstId(resolver, static_cast(inst_id))); +} + // Returns the ConstantId for a TypeId. Adds unresolved constants to // work_stack_. static auto GetLocalConstantId(ImportRefResolver& resolver, @@ -1165,7 +1174,7 @@ static auto GetLocalReturnSlotPatternId( MakeImportedLocIdAndInst( context.local_context(), AddImportIRInst(context, param_pattern.subpattern_id), - {.type_id = type_id, .type_inst_id = SemIR::InstId::None})); + {.type_id = type_id, .type_inst_id = SemIR::TypeInstId::None})); return AddInstInNoBlock( context.local_context(), MakeImportedLocIdAndInst( @@ -1400,9 +1409,9 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver, return ResolveResult::Retry(); } - auto adapted_type_inst_id = + auto adapted_type_inst_id = resolver.local_ir().types().GetAsTypeInstId( AddLoadedImportRef(resolver, SemIR::TypeType::SingletonTypeId, - inst.adapted_type_inst_id, adapted_type_const_id); + inst.adapted_type_inst_id, adapted_type_const_id)); // Create a corresponding instruction to represent the declaration. auto inst_id = AddInstInNoBlock( @@ -1418,7 +1427,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::ArrayType inst) -> ResolveResult { CARBON_CHECK(inst.type_id == SemIR::TypeType::SingletonTypeId); auto element_type_inst_id = - GetLocalConstantInstId(resolver, inst.element_type_inst_id); + GetLocalTypeInstId(resolver, inst.element_type_inst_id); auto bound_id = GetLocalConstantInstId(resolver, inst.bound_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); @@ -1559,9 +1568,9 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver, return ResolveResult::Retry(); } - auto base_type_inst_id = + auto base_type_inst_id = resolver.local_ir().types().GetAsTypeInstId( AddLoadedImportRef(resolver, SemIR::TypeType::SingletonTypeId, - inst.base_type_inst_id, base_type_const_id); + inst.base_type_inst_id, base_type_const_id)); // Create a corresponding instruction to represent the declaration. auto inst_id = AddInstInNoBlock( @@ -2130,8 +2139,8 @@ static auto MakeImplDeclaration(ImportContext& context, AddImportIRInst(context, import_impl.latest_decl_id()), impl_decl)); impl_decl.impl_id = context.local_impls().Add( {GetIncompleteLocalEntityBase(context, impl_decl_id, import_impl), - {.self_id = SemIR::InstId::None, - .constraint_id = SemIR::InstId::None, + {.self_id = SemIR::TypeInstId::None, + .constraint_id = SemIR::TypeInstId::None, .interface = SemIR::SpecificInterface::None, .witness_id = witness_id}}); @@ -2226,12 +2235,12 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver, // Create instructions for self and constraint to hold the symbolic constant // value for a generic impl. - new_impl.self_id = + new_impl.self_id = resolver.local_ir().types().GetAsTypeInstId( AddLoadedImportRef(resolver, SemIR::TypeType::SingletonTypeId, - import_impl.self_id, self_const_id); - new_impl.constraint_id = + import_impl.self_id, self_const_id)); + new_impl.constraint_id = resolver.local_ir().types().GetAsTypeInstId( AddLoadedImportRef(resolver, SemIR::TypeType::SingletonTypeId, - import_impl.constraint_id, constraint_const_id); + import_impl.constraint_id, constraint_const_id)); new_impl.interface = GetLocalSpecificInterface( resolver, import_impl.interface, specific_interface_data); if (import_impl.is_complete()) { @@ -2499,7 +2508,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver, static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::FacetValue inst) -> ResolveResult { auto type_id = GetLocalConstantId(resolver, inst.type_id); - auto type_inst_id = GetLocalConstantInstId(resolver, inst.type_inst_id); + auto type_inst_id = GetLocalTypeInstId(resolver, inst.type_inst_id); auto witnesses = GetLocalInstBlockContents(resolver, inst.witnesses_block_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); @@ -2808,9 +2817,9 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver, -> ResolveResult { CARBON_CHECK(inst.type_id == SemIR::TypeType::SingletonTypeId); auto class_const_inst_id = - GetLocalConstantInstId(resolver, inst.class_type_inst_id); + GetLocalTypeInstId(resolver, inst.class_type_inst_id); auto elem_const_inst_id = - GetLocalConstantInstId(resolver, inst.element_type_inst_id); + GetLocalTypeInstId(resolver, inst.element_type_inst_id); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } diff --git a/toolchain/check/node_stack.h b/toolchain/check/node_stack.h index 89b056389a63..9a6f5faf0cb2 100644 --- a/toolchain/check/node_stack.h +++ b/toolchain/check/node_stack.h @@ -384,7 +384,7 @@ class NodeStack { Parse::NodeCategory::MemberName | Parse::NodeCategory::NonExprName, Id::KindFor()); set_id_if_category_is(Parse::NodeCategory::ImplAs, - Id::KindFor()); + Id::KindFor()); set_id_if_category_is(Parse::NodeCategory::Decl | Parse::NodeCategory::Statement | Parse::NodeCategory::Modifier, diff --git a/toolchain/check/subst.cpp b/toolchain/check/subst.cpp index 3a3c2614ca9a..f90b9eba32f3 100644 --- a/toolchain/check/subst.cpp +++ b/toolchain/check/subst.cpp @@ -89,6 +89,12 @@ static auto PushOperand(Context& context, Worklist& worklist, } break; } + case CARBON_KIND(SemIR::TypeInstId inst_id): { + if (inst_id.has_value()) { + worklist.Push(inst_id); + } + break; + } case CARBON_KIND(SemIR::InstBlockId inst_block_id): { push_block(inst_block_id); break; @@ -175,6 +181,12 @@ static auto PopOperand(Context& context, Worklist& worklist, } return worklist.Pop().index; } + case CARBON_KIND(SemIR::TypeInstId inst_id): { + if (!inst_id.has_value()) { + return arg.value(); + } + return worklist.Pop().index; + } case CARBON_KIND(SemIR::InstBlockId inst_block_id): { return pop_block_id(inst_block_id).index; } diff --git a/toolchain/check/type.cpp b/toolchain/check/type.cpp index a1351a7f443f..54742171e35b 100644 --- a/toolchain/check/type.cpp +++ b/toolchain/check/type.cpp @@ -157,8 +157,8 @@ auto GetPointerType(Context& context, SemIR::InstId pointee_type_id) return GetTypeImpl(context, pointee_type_id); } -auto GetUnboundElementType(Context& context, SemIR::InstId class_type_id, - SemIR::InstId element_type_id) -> SemIR::TypeId { +auto GetUnboundElementType(Context& context, SemIR::TypeInstId class_type_id, + SemIR::TypeInstId element_type_id) -> SemIR::TypeId { return GetTypeImpl(context, class_type_id, element_type_id); } diff --git a/toolchain/check/type.h b/toolchain/check/type.h index 831135c39e5d..11b21a622465 100644 --- a/toolchain/check/type.h +++ b/toolchain/check/type.h @@ -83,8 +83,8 @@ auto GetTupleType(Context& context, llvm::ArrayRef type_inst_ids) -> SemIR::TypeId; // Returns an unbound element type. -auto GetUnboundElementType(Context& context, SemIR::InstId class_type_id, - SemIR::InstId element_type_id) -> SemIR::TypeId; +auto GetUnboundElementType(Context& context, SemIR::TypeInstId class_type_id, + SemIR::TypeInstId element_type_id) -> SemIR::TypeId; } // namespace Carbon::Check diff --git a/toolchain/driver/testdata/compile/raw_and_textual_ir.carbon b/toolchain/driver/testdata/compile/raw_and_textual_ir.carbon index c80fade2f15c..b484200a1b9b 100644 --- a/toolchain/driver/testdata/compile/raw_and_textual_ir.carbon +++ b/toolchain/driver/testdata/compile/raw_and_textual_ir.carbon @@ -63,12 +63,12 @@ fn Foo(n: ()) -> ((), ()) { // CHECK:STDOUT: inst25: {kind: Converted, arg0: inst21, arg1: inst15, type: type(TypeType)} // CHECK:STDOUT: inst26: {kind: Converted, arg0: inst22, arg1: inst15, type: type(TypeType)} // CHECK:STDOUT: inst27: {kind: Converted, arg0: inst24, arg1: inst23, type: type(TypeType)} -// CHECK:STDOUT: inst28: {kind: ReturnSlotPattern, arg0: inst24, type: type(inst23)} +// CHECK:STDOUT: inst28: {kind: ReturnSlotPattern, arg0: inst27, type: type(inst23)} // CHECK:STDOUT: inst29: {kind: OutParamPattern, arg0: inst28, arg1: call_param1, type: type(inst23)} // CHECK:STDOUT: inst30: {kind: ValueParam, arg0: call_param0, arg1: name1, type: type(inst15)} // CHECK:STDOUT: inst31: {kind: SpliceBlock, arg0: inst_block4, arg1: inst17, type: type(TypeType)} // CHECK:STDOUT: inst32: {kind: OutParam, arg0: call_param1, arg1: name(ReturnSlot), type: type(inst23)} -// CHECK:STDOUT: inst33: {kind: ReturnSlot, arg0: inst24, arg1: inst32, type: type(inst23)} +// CHECK:STDOUT: inst33: {kind: ReturnSlot, arg0: inst27, arg1: inst32, type: type(inst23)} // CHECK:STDOUT: inst34: {kind: FunctionDecl, arg0: function0, arg1: inst_block11, type: type(inst35)} // CHECK:STDOUT: inst35: {kind: FunctionType, arg0: function0, arg1: specific, type: type(TypeType)} // CHECK:STDOUT: inst36: {kind: StructValue, arg0: inst_block_empty, type: type(inst35)} diff --git a/toolchain/driver/testdata/compile/raw_ir.carbon b/toolchain/driver/testdata/compile/raw_ir.carbon index d6309d4c660a..c2d185e599bf 100644 --- a/toolchain/driver/testdata/compile/raw_ir.carbon +++ b/toolchain/driver/testdata/compile/raw_ir.carbon @@ -77,11 +77,11 @@ fn Foo[T:! type](n: T) -> (T, ()) { // CHECK:STDOUT: inst28: {kind: Converted, arg0: inst25, arg1: inst24, type: type(TypeType)} // CHECK:STDOUT: inst29: {kind: TupleType, arg0: inst_block11, type: type(TypeType)} // CHECK:STDOUT: inst30: {kind: Converted, arg0: inst27, arg1: inst29, type: type(TypeType)} -// CHECK:STDOUT: inst31: {kind: ReturnSlotPattern, arg0: inst27, type: type(symbolic_constant5)} +// CHECK:STDOUT: inst31: {kind: ReturnSlotPattern, arg0: inst30, type: type(symbolic_constant5)} // CHECK:STDOUT: inst32: {kind: OutParamPattern, arg0: inst31, arg1: call_param1, type: type(symbolic_constant5)} // CHECK:STDOUT: inst33: {kind: ValueParam, arg0: call_param0, arg1: name2, type: type(symbolic_constant3)} // CHECK:STDOUT: inst34: {kind: OutParam, arg0: call_param1, arg1: name(ReturnSlot), type: type(symbolic_constant5)} -// CHECK:STDOUT: inst35: {kind: ReturnSlot, arg0: inst27, arg1: inst34, type: type(symbolic_constant5)} +// CHECK:STDOUT: inst35: {kind: ReturnSlot, arg0: inst30, arg1: inst34, type: type(symbolic_constant5)} // CHECK:STDOUT: inst36: {kind: FunctionDecl, arg0: function0, arg1: inst_block14, type: type(inst40)} // CHECK:STDOUT: inst37: {kind: BindSymbolicName, arg0: entity_name0, arg1: inst, type: type(TypeType)} // CHECK:STDOUT: inst38: {kind: SymbolicBindingPattern, arg0: entity_name0, type: type(TypeType)} diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index 922e3e109e97..a07c8ca594c0 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -1398,6 +1398,10 @@ class FormatterImpl { } } + auto FormatName(TypeInstId id) -> void { + FormatName(static_cast(id)); + } + auto FormatLabel(InstBlockId id) -> void { out_ << inst_namer_->GetLabelFor(scope_, id); } diff --git a/toolchain/sem_ir/id_kind.h b/toolchain/sem_ir/id_kind.h index 1dae39f8f890..f5412ed94fe3 100644 --- a/toolchain/sem_ir/id_kind.h +++ b/toolchain/sem_ir/id_kind.h @@ -179,7 +179,8 @@ using IdKind = TypeEnum< NameScopeId, SpecificId, SpecificInterfaceId, - StructTypeFieldsId>; + StructTypeFieldsId, + TypeInstId>; // clang-format on } // namespace Carbon::SemIR diff --git a/toolchain/sem_ir/ids.h b/toolchain/sem_ir/ids.h index 2898a4280957..8700869bb904 100644 --- a/toolchain/sem_ir/ids.h +++ b/toolchain/sem_ir/ids.h @@ -52,6 +52,26 @@ struct InstId : public IdBase { constexpr InstId InstId::InitTombstone = InstId(NoneIndex - 1); +// And InstId whose value is a type. The fact it's a type is CHECKed on +// construction, and this allows that check to be represented in the type +// system. +struct TypeInstId : public InstId { + static const TypeInstId None; + + using InstId::InstId; + + static constexpr auto UnsafeMake(InstId id) -> TypeInstId { + return TypeInstId(UnsafeCtor(), id); + } + + private: + struct UnsafeCtor {}; + explicit constexpr TypeInstId(UnsafeCtor /*unsafe*/, InstId id) + : InstId(id) {} +}; + +constexpr TypeInstId TypeInstId::None = TypeInstId::UnsafeMake(InstId::None); + // An ID of an instruction that is referenced absolutely by another instruction. // This should only be used as the type of a field within a typed instruction // class. diff --git a/toolchain/sem_ir/impl.h b/toolchain/sem_ir/impl.h index 7681298a39ed..1a26bbdc7d37 100644 --- a/toolchain/sem_ir/impl.h +++ b/toolchain/sem_ir/impl.h @@ -17,9 +17,9 @@ struct ImplFields { // This following members always have values and do not change. // The type for which the impl is implementing a constraint. - InstId self_id; + TypeInstId self_id; // The constraint that the impl implements. - InstId constraint_id; + TypeInstId constraint_id; // The single interface to implement from `constraint_id`. // The members are `None` if `constraint_id` isn't complete or doesn't diff --git a/toolchain/sem_ir/type.cpp b/toolchain/sem_ir/type.cpp index 9581df5a2289..9ae6de37fe3b 100644 --- a/toolchain/sem_ir/type.cpp +++ b/toolchain/sem_ir/type.cpp @@ -8,28 +8,45 @@ namespace Carbon::SemIR { -auto TypeStore::GetTypeIdForTypeConstantId(SemIR::ConstantId constant_id) const - -> SemIR::TypeId { +// Verify that the constant value's type is `TypeType` (or an error). +static void CheckTypeOfConstantIsTypeType(File& file, ConstantId constant_id) { CARBON_CHECK(constant_id.is_constant(), "Canonicalizing non-constant type: {0}", constant_id); - auto type_id = file_->insts() - .Get(file_->constant_values().GetInstId(constant_id)) - .type_id(); - CARBON_CHECK(type_id == SemIR::TypeType::SingletonTypeId || - constant_id == SemIR::ErrorInst::SingletonConstantId, + auto type_id = + file.insts().Get(file.constant_values().GetInstId(constant_id)).type_id(); + CARBON_CHECK(type_id == TypeType::SingletonTypeId || + constant_id == ErrorInst::SingletonConstantId, "Forming type ID for non-type constant of type {0}", - GetAsInst(type_id)); - - return SemIR::TypeId::ForTypeConstant(constant_id); + file.types().GetAsInst(type_id)); } -auto TypeStore::GetTypeIdForTypeInstId(SemIR::InstId inst_id) const - -> SemIR::TypeId { - return GetTypeIdForTypeConstantId(file_->constant_values().Get(inst_id)); +auto TypeStore::GetTypeIdForTypeConstantId(ConstantId constant_id) const + -> TypeId { + CheckTypeOfConstantIsTypeType(*file_, constant_id); + return TypeId::ForTypeConstant(constant_id); } -auto TypeStore::GetInstId(TypeId type_id) const -> InstId { - return file_->constant_values().GetInstId(GetConstantId(type_id)); +auto TypeStore::GetTypeIdForTypeInstId(InstId inst_id) const -> TypeId { + auto constant_id = file_->constant_values().Get(inst_id); + CheckTypeOfConstantIsTypeType(*file_, constant_id); + return TypeId::ForTypeConstant(constant_id); +} + +auto TypeStore::GetTypeIdForTypeInstId(TypeInstId inst_id) const -> TypeId { + auto constant_id = file_->constant_values().Get(inst_id); + return TypeId::ForTypeConstant(constant_id); +} + +auto TypeStore::GetAsTypeInstId(InstId inst_id) const -> TypeInstId { + auto constant_id = file_->constant_values().Get(inst_id); + CheckTypeOfConstantIsTypeType(*file_, constant_id); + return TypeInstId::UnsafeMake(inst_id); +} + +auto TypeStore::GetInstId(TypeId type_id) const -> TypeInstId { + // The instruction for a TypeId has a value of that TypeId. + return TypeInstId::UnsafeMake( + file_->constant_values().GetInstId(GetConstantId(type_id))); } auto TypeStore::GetAsInst(TypeId type_id) const -> Inst { diff --git a/toolchain/sem_ir/type.h b/toolchain/sem_ir/type.h index a2cf2d5e6304..9052cf9cf78b 100644 --- a/toolchain/sem_ir/type.h +++ b/toolchain/sem_ir/type.h @@ -39,8 +39,7 @@ class TypeStore : public Yaml::Printable { // Facet values are of the same typishness as types, but are not themselves // types, so they can not be passed here. They should be converted to a type // through an `as type` conversion, that is, to a value of type `TypeType`. - auto GetTypeIdForTypeConstantId(SemIR::ConstantId constant_id) const - -> SemIR::TypeId; + auto GetTypeIdForTypeConstantId(ConstantId constant_id) const -> TypeId; // Returns the type ID for an instruction whose constant value is a type // value, i.e. it is a value of type `TypeType`. @@ -50,10 +49,16 @@ class TypeStore : public Yaml::Printable { // so they can not be passed here. They should be converted to a type through // an `as type` conversion, such as to a `FacetAccessType` instruction whose // value is of type `TypeType`. - auto GetTypeIdForTypeInstId(SemIR::InstId inst_id) const -> SemIR::TypeId; + auto GetTypeIdForTypeInstId(InstId inst_id) const -> TypeId; + auto GetTypeIdForTypeInstId(TypeInstId inst_id) const -> TypeId; + + // Converts an `InstId` to a `TypeInstId` of the same id value. This process + // involves checking that the type of the instruction's value is `TypeType`, + // and then this check is encoded in the type system via `TypeInstId`. + auto GetAsTypeInstId(InstId inst_id) const -> TypeInstId; // Returns the ID of the instruction used to define the specified type. - auto GetInstId(TypeId type_id) const -> InstId; + auto GetInstId(TypeId type_id) const -> TypeInstId; // Returns the instruction used to define the specified type. auto GetAsInst(TypeId type_id) const -> Inst; diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index ad7002b28428..d8018b24337e 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -81,7 +81,7 @@ struct AnyFoundationDecl { static constexpr InstKind Kinds[] = {InstKind::AdaptDecl, InstKind::BaseDecl}; InstKind kind; - InstId foundation_type_inst_id; + TypeInstId foundation_type_inst_id; // Kind-specific data. AnyRawId arg1; }; @@ -94,7 +94,7 @@ struct AdaptDecl { .is_lowered = false}); // No type_id; this is not a value. - InstId adapted_type_inst_id; + TypeInstId adapted_type_inst_id; }; // Takes the address of a reference expression, such as for the `&` address-of @@ -193,7 +193,7 @@ struct ArrayType { TypeId type_id; InstId bound_id; - InstId element_type_inst_id; + TypeInstId element_type_inst_id; }; // Perform a no-op conversion to a compatible type. @@ -287,7 +287,7 @@ struct BaseDecl { {.ir_name = "base_decl", .constant_kind = InstConstantKind::Unique}); TypeId type_id; - InstId base_type_inst_id; + TypeInstId base_type_inst_id; ElementIndex index; }; @@ -665,6 +665,8 @@ struct ErrorInst { ConstantId::ForConcreteConstant(SingletonInstId); static constexpr auto SingletonTypeId = TypeId::ForTypeConstant(SingletonConstantId); + static constexpr auto SingletonTypeInstId = + TypeInstId::UnsafeMake(SingletonInstId); TypeId type_id; }; @@ -736,7 +738,7 @@ struct FacetValue { // A `FacetType`. TypeId type_id; // The type that you will get if you cast this value to `type`. - InstId type_inst_id; + TypeInstId type_inst_id; // The set of `ImplWitness` instructions for a `FacetType`. The witnesses are // in the same order as the set of `required_interfaces` in the // `IdentifiedFacetType` of the `FacetType` from `type_id`, so that an index @@ -1387,7 +1389,7 @@ struct ReturnSlot { // The function return type as originally written by the user. For diagnostics // only; this has no semantic significance, and is not preserved across // imports. - InstId type_inst_id; + TypeInstId type_inst_id; // The storage that will be initialized by the function. InstId storage_id; @@ -1410,7 +1412,7 @@ struct ReturnSlotPattern { // The function return type as originally written by the user. For diagnostics // only; this has no semantic significance, and is not preserved across // imports. - InstId type_inst_id; + TypeInstId type_inst_id; }; // An `expr == expr` clause in a `where` expression or `require` declaration. @@ -1782,9 +1784,9 @@ struct UnboundElementType { TypeId type_id; // The `ClassType` that a value of this type is an element of. - InstId class_type_inst_id; + TypeInstId class_type_inst_id; // The type of the element. - InstId element_type_inst_id; + TypeInstId element_type_inst_id; }; // Converts from a value expression to an ephemeral reference expression, in