From 50d56aa7c9d00d5532d7b590b787306c05d0ac4c Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Fri, 12 Jul 2024 07:59:01 -0700 Subject: [PATCH] Add an instruction to represent a use of a dependent value from a generic instance. (#4122) We can't use the instruction from the generic directly, because it doesn't have the right constant value. Instead add an instruction that models the transition from the constant value in the generic to the constant value in the generic instance. Also start associating the self generic instance with unqualified lookups that find results in an enclosing generic, so that we track the information necessary to create the new instruction. --------- Co-authored-by: Jon Ross-Perkins --- toolchain/check/check.cpp | 1 + toolchain/check/context.cpp | 10 ++--- toolchain/check/context.h | 4 +- toolchain/check/decl_name_stack.cpp | 35 ++++++++++------ toolchain/check/decl_name_stack.h | 3 +- toolchain/check/eval.cpp | 13 ++++-- toolchain/check/generic.cpp | 41 +++++-------------- toolchain/check/generic.h | 21 +--------- toolchain/check/handle_class.cpp | 6 ++- toolchain/check/handle_interface.cpp | 6 ++- toolchain/check/handle_name.cpp | 10 +++++ toolchain/check/member_access.cpp | 13 ++++++ toolchain/check/operator.cpp | 5 +-- toolchain/check/scope_stack.cpp | 19 +++++++-- toolchain/check/scope_stack.h | 14 +++++-- .../testdata/basics/no_prelude/raw_ir.carbon | 21 ++++++---- .../testdata/class/fail_generic_method.carbon | 9 ++-- .../check/testdata/class/generic/basic.carbon | 12 +++--- .../class/generic/fail_todo_use.carbon | 3 +- .../class/generic/member_out_of_line.carbon | 10 +++-- .../check/testdata/class/generic/self.carbon | 10 +++-- .../testdata/class/generic_method.carbon | 10 +++-- .../fail_todo_generic_default_fn.carbon | 40 ++++++++++-------- toolchain/lower/constant.cpp | 2 +- toolchain/sem_ir/file.cpp | 6 +++ toolchain/sem_ir/file.h | 6 +-- toolchain/sem_ir/generic.cpp | 28 +++++++++++++ toolchain/sem_ir/generic.h | 28 +++++++++++++ toolchain/sem_ir/inst_kind.def | 1 + toolchain/sem_ir/typed_insts.h | 16 ++++++++ 30 files changed, 259 insertions(+), 144 deletions(-) diff --git a/toolchain/check/check.cpp b/toolchain/check/check.cpp index ad2fc701e9ba..545cf264ee65 100644 --- a/toolchain/check/check.cpp +++ b/toolchain/check/check.cpp @@ -207,6 +207,7 @@ static auto ImportCurrentPackage(Context& context, UnitInfo& unit_info, context.scope_stack().Push( package_inst_id, SemIR::NameScopeId::Package, + SemIR::GenericInstanceId::Invalid, context.name_scopes().Get(SemIR::NameScopeId::Package).has_error); } diff --git a/toolchain/check/context.cpp b/toolchain/check/context.cpp index 8963ba622596..7c80610035ed 100644 --- a/toolchain/check/context.cpp +++ b/toolchain/check/context.cpp @@ -273,15 +273,11 @@ auto Context::LookupUnqualifiedName(Parse::NodeId node_id, scope_stack().LookupInLexicalScopes(name_id); // Walk the non-lexical scopes and perform lookups into each of them. - for (auto [index, lookup_scope_id] : llvm::reverse(non_lexical_scopes)) { - // Enclosing non-lexical scopes cannot correspond to an instance of a - // generic, so it's always OK to pass an invalid generic instance here. - // Note that the lookup result might still be found in an extended scope, so - // it can be in a generic instance. + for (auto [index, lookup_scope_id, instance_id] : + llvm::reverse(non_lexical_scopes)) { if (auto non_lexical_result = LookupQualifiedName( node_id, name_id, - {.name_scope_id = lookup_scope_id, - .instance_id = SemIR::GenericInstanceId::Invalid}, + {.name_scope_id = lookup_scope_id, .instance_id = instance_id}, /*required=*/false); non_lexical_result.inst_id.is_valid()) { return non_lexical_result; diff --git a/toolchain/check/context.h b/toolchain/check/context.h index 3553b517e3a0..8e9805198e01 100644 --- a/toolchain/check/context.h +++ b/toolchain/check/context.h @@ -419,9 +419,7 @@ class Context { return sem_ir().interfaces(); } auto impls() -> SemIR::ImplStore& { return sem_ir().impls(); } - auto generics() -> ValueStore& { - return sem_ir().generics(); - } + auto generics() -> SemIR::GenericStore& { return sem_ir().generics(); } auto generic_instances() -> SemIR::GenericInstanceStore& { return sem_ir().generic_instances(); } diff --git a/toolchain/check/decl_name_stack.cpp b/toolchain/check/decl_name_stack.cpp index 34165d84d6e6..6b121338dda0 100644 --- a/toolchain/check/decl_name_stack.cpp +++ b/toolchain/check/decl_name_stack.cpp @@ -7,6 +7,7 @@ #include "toolchain/base/kind_switch.h" #include "toolchain/check/context.h" #include "toolchain/check/diagnostic_helpers.h" +#include "toolchain/check/generic.h" #include "toolchain/check/merge.h" #include "toolchain/check/name_component.h" #include "toolchain/diagnostics/diagnostic.h" @@ -199,12 +200,13 @@ auto DeclNameStack::LookupOrAddName(NameContext name_context, static auto PushNameQualifierScope(Context& context, SemIR::InstId scope_inst_id, SemIR::NameScopeId scope_id, + SemIR::GenericInstanceId instance_id, bool has_error = false) -> void { // If the qualifier has no parameters, we don't need to keep around a // parameter scope. context.scope_stack().PopIfEmpty(); - context.scope_stack().Push(scope_inst_id, scope_id, has_error); + context.scope_stack().Push(scope_inst_id, scope_id, instance_id, has_error); // Enter a parameter scope in case the qualified name itself has parameters. context.scope_stack().Push(); @@ -216,9 +218,10 @@ auto DeclNameStack::ApplyNameQualifier(const NameComponent& name) -> void { name_context.has_qualifiers = true; // Resolve the qualifier as a scope and enter the new scope. - auto scope_id = ResolveAsScope(name_context, name); + auto [scope_id, instance_id] = ResolveAsScope(name_context, name); if (scope_id.is_valid()) { PushNameQualifierScope(*context_, name_context.resolved_inst_id, scope_id, + instance_id, context_->name_scopes().Get(scope_id).has_error); name_context.parent_scope_id = scope_id; } else { @@ -349,9 +352,13 @@ static auto DiagnoseQualifiedDeclInNonScope(Context& context, SemIRLoc use_loc, auto DeclNameStack::ResolveAsScope(const NameContext& name_context, const NameComponent& name) const - -> SemIR::NameScopeId { + -> std::pair { + constexpr std::pair + InvalidResult = {SemIR::NameScopeId::Invalid, + SemIR::GenericInstanceId::Invalid}; + if (!CheckQualifierIsResolved(*context_, name_context)) { - return SemIR::NameScopeId::Invalid; + return InvalidResult; } auto new_params = @@ -363,29 +370,31 @@ auto DeclNameStack::ResolveAsScope(const NameContext& name_context, const auto& class_info = context_->classes().Get(class_decl.class_id); if (!CheckRedeclParamsMatch(*context_, new_params, DeclParams(class_info))) { - return SemIR::NameScopeId::Invalid; + return InvalidResult; } if (!class_info.is_defined()) { DiagnoseQualifiedDeclInIncompleteClassScope( *context_, name_context.loc_id, class_decl.class_id); - return SemIR::NameScopeId::Invalid; + return InvalidResult; } - return class_info.scope_id; + return {class_info.scope_id, + context_->generics().GetSelfInstance(class_info.generic_id)}; } case CARBON_KIND(SemIR::InterfaceDecl interface_decl): { const auto& interface_info = context_->interfaces().Get(interface_decl.interface_id); if (!CheckRedeclParamsMatch(*context_, new_params, DeclParams(interface_info))) { - return SemIR::NameScopeId::Invalid; + return InvalidResult; } if (!interface_info.is_defined()) { DiagnoseQualifiedDeclInUndefinedInterfaceScope( *context_, name_context.loc_id, interface_decl.interface_id, name_context.resolved_inst_id); - return SemIR::NameScopeId::Invalid; + return InvalidResult; } - return interface_info.scope_id; + return {interface_info.scope_id, + context_->generics().GetSelfInstance(interface_info.generic_id)}; } case CARBON_KIND(SemIR::Namespace resolved_inst): { auto scope_id = resolved_inst.name_scope_id; @@ -394,7 +403,7 @@ auto DeclNameStack::ResolveAsScope(const NameContext& name_context, DeclParams(name_context.resolved_inst_id, SemIR::InstBlockId::Invalid, SemIR::InstBlockId::Invalid))) { - return SemIR::NameScopeId::Invalid; + return InvalidResult; } if (scope.is_closed_import) { DiagnoseQualifiedDeclInImportedPackage(*context_, name_context.loc_id, @@ -403,12 +412,12 @@ auto DeclNameStack::ResolveAsScope(const NameContext& name_context, // be used as a name qualifier. scope.is_closed_import = false; } - return scope_id; + return {scope_id, SemIR::GenericInstanceId::Invalid}; } default: { DiagnoseQualifiedDeclInNonScope(*context_, name_context.loc_id, name_context.resolved_inst_id); - return SemIR::NameScopeId::Invalid; + return InvalidResult; } } } diff --git a/toolchain/check/decl_name_stack.h b/toolchain/check/decl_name_stack.h index eaa8da479605..e3f0c838fe2f 100644 --- a/toolchain/check/decl_name_stack.h +++ b/toolchain/check/decl_name_stack.h @@ -233,7 +233,8 @@ class DeclNameStack { // corresponding scope. Issues a suitable diagnostic and returns Invalid if // the name doesn't resolve to a scope. auto ResolveAsScope(const NameContext& name_context, - const NameComponent& name) const -> SemIR::NameScopeId; + const NameComponent& name) const + -> std::pair; // The linked context. Context* context_; diff --git a/toolchain/check/eval.cpp b/toolchain/check/eval.cpp index 80e3a23edd3b..dd98b5252f9b 100644 --- a/toolchain/check/eval.cpp +++ b/toolchain/check/eval.cpp @@ -10,6 +10,7 @@ #include "toolchain/diagnostics/diagnostic_emitter.h" #include "toolchain/sem_ir/builtin_function_kind.h" #include "toolchain/sem_ir/function.h" +#include "toolchain/sem_ir/generic.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst_kind.h" #include "toolchain/sem_ir/typed_insts.h" @@ -1122,6 +1123,12 @@ auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst) Phase::Template); } + case CARBON_KIND(SemIR::SpecificConstant instance): { + // Pull the instance-specific constant value out of the generic instance. + return SemIR::GetConstantValueInInstance( + context.sem_ir(), instance.instance_id, instance.inst_id); + } + // These cases are treated as being the unique canonical definition of the // corresponding constant value. // TODO: This doesn't properly handle redeclarations. Consider adding a @@ -1187,9 +1194,9 @@ auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst) // Map from an instance-specific constant value to the canonical value. // TODO: Remove this once we properly model instructions with // instance-dependent constant values. - return GetConstantInInstance( - context, SemIR::GenericInstanceId::Invalid, - context.constant_values().Get(typed_inst.value_id)); + return GetConstantValueInInstance(context.sem_ir(), + SemIR::GenericInstanceId::Invalid, + typed_inst.value_id); } case CARBON_KIND(SemIR::Converted typed_inst): { return context.constant_values().Get(typed_inst.result_id); diff --git a/toolchain/check/generic.cpp b/toolchain/check/generic.cpp index b921da2c2926..1404a3c2ef29 100644 --- a/toolchain/check/generic.cpp +++ b/toolchain/check/generic.cpp @@ -196,13 +196,19 @@ auto FinishGenericDecl(Context& context, SemIR::InstId decl_id) auto bindings_id = context.inst_blocks().Add(all_bindings); auto generic_id = context.generics().Add( - SemIR::Generic{.decl_id = decl_id, .bindings_id = bindings_id}); + SemIR::Generic{.decl_id = decl_id, + .bindings_id = bindings_id, + .self_instance_id = SemIR::GenericInstanceId::Invalid}); auto decl_block_id = MakeGenericEvalBlock( context, generic_id, SemIR::GenericInstIndex::Region::Declaration); context.generic_region_stack().Pop(); - context.generics().Get(generic_id).decl_block_id = decl_block_id; + auto self_instance_id = MakeGenericSelfInstance(context, generic_id); + + auto& generic_info = context.generics().Get(generic_id); + generic_info.decl_block_id = decl_block_id; + generic_info.self_instance_id = self_instance_id; return generic_id; } @@ -238,7 +244,6 @@ auto MakeGenericInstance(Context& context, SemIR::GenericId generic_id, auto MakeGenericSelfInstance(Context& context, SemIR::GenericId generic_id) -> SemIR::GenericInstanceId { - // TODO: Remove this once we import generics properly. if (!generic_id.is_valid()) { return SemIR::GenericInstanceId::Invalid; } @@ -261,37 +266,11 @@ auto MakeGenericSelfInstance(Context& context, SemIR::GenericId generic_id) return MakeGenericInstance(context, generic_id, args_id); } -auto GetConstantInInstance(Context& context, - SemIR::GenericInstanceId /*instance_id*/, - SemIR::ConstantId const_id) -> SemIR::ConstantId { - if (!const_id.is_symbolic()) { - // Type does not depend on a generic parameter. - return const_id; - } - - const auto& symbolic = - context.constant_values().GetSymbolicConstant(const_id); - if (!symbolic.generic_id.is_valid()) { - // Constant is an abstract symbolic constant, not an instance-specific one. - return const_id; - } - - // TODO: Look up the value in the generic instance. For now, return the - // canonical constant value. - return context.constant_values().Get(symbolic.inst_id); -} - -auto GetConstantValueInInstance(Context& context, - SemIR::GenericInstanceId instance_id, - SemIR::InstId inst_id) -> SemIR::ConstantId { - return GetConstantInInstance(context, instance_id, - context.constant_values().Get(inst_id)); -} - auto GetTypeInInstance(Context& context, SemIR::GenericInstanceId instance_id, SemIR::TypeId type_id) -> SemIR::TypeId { auto const_id = context.types().GetConstantId(type_id); - auto inst_const_id = GetConstantInInstance(context, instance_id, const_id); + auto inst_const_id = + GetConstantInInstance(context.sem_ir(), instance_id, const_id); if (inst_const_id == const_id) { // Common case: not an instance constant. return type_id; diff --git a/toolchain/check/generic.h b/toolchain/check/generic.h index 0f172f833e8f..10b8b537d845 100644 --- a/toolchain/check/generic.h +++ b/toolchain/check/generic.h @@ -41,28 +41,11 @@ auto MakeGenericInstance(Context& context, SemIR::GenericId generic_id, -> SemIR::GenericInstanceId; // Builds the generic instance corresponding to the generic itself. For example, -// for a generic `G(T:! type)`, this is `G(T)`. +// for a generic `G(T:! type)`, this is `G(T)`. For an invalid `generic_id`, +// returns an invalid instance ID. auto MakeGenericSelfInstance(Context& context, SemIR::GenericId generic_id) -> SemIR::GenericInstanceId; -// Gets the substituted value of a constant within a specified instance of a -// generic. Note that this does not perform substitution, and will return -// `Invalid` if the substituted constant value is not yet known. -// -// TODO: Move this to sem_ir so that lowering can use it. -auto GetConstantInInstance(Context& context, - SemIR::GenericInstanceId instance_id, - SemIR::ConstantId const_id) -> SemIR::ConstantId; - -// Gets the substituted constant value of an instruction within a specified -// instance of a generic. Note that this does not perform substitution, and will -// return `Invalid` if the substituted constant value is not yet known. -// -// TODO: Move this to sem_ir so that lowering can use it. -auto GetConstantValueInInstance(Context& context, - SemIR::GenericInstanceId instance_id, - SemIR::InstId inst_id) -> SemIR::ConstantId; - // Gets the substituted value of a type within a specified instance of a // generic. Note that this does not perform substitution, and will return // `Invalid` if the substituted type is not yet known. diff --git a/toolchain/check/handle_class.cpp b/toolchain/check/handle_class.cpp index e1fd1615e1e9..d7116a5261c3 100644 --- a/toolchain/check/handle_class.cpp +++ b/toolchain/check/handle_class.cpp @@ -261,7 +261,7 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id, auto& class_info = context.classes().Get(class_decl.class_id); if (class_info.is_generic()) { auto instance_id = - MakeGenericSelfInstance(context, class_info.generic_id); + context.generics().GetSelfInstance(class_info.generic_id); class_info.self_type_id = context.GetTypeIdForTypeConstant( TryEvalInst(context, SemIR::InstId::Invalid, SemIR::ClassType{.type_id = SemIR::TypeId::TypeType, @@ -299,7 +299,9 @@ auto HandleClassDefinitionStart(Context& context, } // Enter the class scope. - context.scope_stack().Push(class_decl_id, class_info.scope_id); + context.scope_stack().Push( + class_decl_id, class_info.scope_id, + context.generics().GetSelfInstance(class_info.generic_id)); StartGenericDefinition(context); // Introduce `Self`. diff --git a/toolchain/check/handle_interface.cpp b/toolchain/check/handle_interface.cpp index f4615af66845..dbec1cf3bafc 100644 --- a/toolchain/check/handle_interface.cpp +++ b/toolchain/check/handle_interface.cpp @@ -141,7 +141,9 @@ auto HandleInterfaceDefinitionStart(Context& context, } // Enter the interface scope. - context.scope_stack().Push(interface_decl_id, interface_info.scope_id); + context.scope_stack().Push( + interface_decl_id, interface_info.scope_id, + context.generics().GetSelfInstance(interface_info.generic_id)); StartGenericDefinition(context); context.inst_block_stack().Push(); @@ -155,7 +157,7 @@ auto HandleInterfaceDefinitionStart(Context& context, SemIR::TypeId self_type_id = SemIR::TypeId::Invalid; if (interface_info.is_generic()) { auto instance_id = - MakeGenericSelfInstance(context, interface_info.generic_id); + context.generics().GetSelfInstance(interface_info.generic_id); self_type_id = context.GetTypeIdForTypeConstant( TryEvalInst(context, SemIR::InstId::Invalid, SemIR::InterfaceType{.type_id = SemIR::TypeId::TypeType, diff --git a/toolchain/check/handle_name.cpp b/toolchain/check/handle_name.cpp index 890b17b49521..c3fa09299a9e 100644 --- a/toolchain/check/handle_name.cpp +++ b/toolchain/check/handle_name.cpp @@ -86,6 +86,16 @@ static auto HandleNameAsExpr(Context& context, Parse::NodeId node_id, GetTypeInInstance(context, result.instance_id, value.type_id()); CARBON_CHECK(type_id.is_valid()) << "Missing type for " << value; + // If the named entity has a constant value that depends on its generic + // instance, store the instance too. + if (result.instance_id.is_valid() && + context.constant_values().Get(result.inst_id).is_symbolic()) { + result.inst_id = context.AddInst( + node_id, {.type_id = type_id, + .inst_id = result.inst_id, + .instance_id = result.instance_id}); + } + context.AddInstAndPush( node_id, {.type_id = type_id, .name_id = name_id, .value_id = result.inst_id}); diff --git a/toolchain/check/member_access.cpp b/toolchain/check/member_access.cpp index db9105c6b22e..d5e4af2603ca 100644 --- a/toolchain/check/member_access.cpp +++ b/toolchain/check/member_access.cpp @@ -223,8 +223,21 @@ static auto LookupMemberNameInScope(Context& context, Parse::NodeId node_id, result = context.LookupQualifiedName(node_id, name_id, lookup_scope); } + // TODO: This duplicates the work that HandleNameAsExpr does. Factor this out. auto inst = context.insts().Get(result.inst_id); auto type_id = GetTypeInInstance(context, result.instance_id, inst.type_id()); + CARBON_CHECK(type_id.is_valid()) << "Missing type for member " << inst; + + // If the named entity has a constant value that depends on its generic + // instance, store the instance too. + if (result.instance_id.is_valid() && + context.constant_values().Get(result.inst_id).is_symbolic()) { + result.inst_id = context.AddInst( + node_id, {.type_id = type_id, + .inst_id = result.inst_id, + .instance_id = result.instance_id}); + } + // TODO: Use a different kind of instruction that also references the // `base_id` so that `SemIR` consumers can find it. auto member_id = context.AddInst( diff --git a/toolchain/check/operator.cpp b/toolchain/check/operator.cpp index d23dff1a85ba..4bae63e6d0a2 100644 --- a/toolchain/check/operator.cpp +++ b/toolchain/check/operator.cpp @@ -53,9 +53,8 @@ static auto GetOperatorOpFunction(Context& context, Parse::AnyExprId node_id, } // Look through import_refs and aliases. - auto op_const_id = - GetConstantInInstance(context, op_result.instance_id, - context.constant_values().Get(op_result.inst_id)); + auto op_const_id = GetConstantValueInInstance( + context.sem_ir(), op_result.instance_id, op_result.inst_id); auto op_id = context.constant_values().GetInstId(op_const_id); // We expect it to be an associated function. diff --git a/toolchain/check/scope_stack.cpp b/toolchain/check/scope_stack.cpp index 75a105d68dc3..ebb94fb5279b 100644 --- a/toolchain/check/scope_stack.cpp +++ b/toolchain/check/scope_stack.cpp @@ -14,19 +14,28 @@ auto ScopeStack::VerifyOnFinish() -> void { } auto ScopeStack::Push(SemIR::InstId scope_inst_id, SemIR::NameScopeId scope_id, + SemIR::GenericInstanceId instance_id, bool lexical_lookup_has_load_error) -> void { compile_time_binding_stack_.PushArray(); scope_stack_.push_back( {.index = next_scope_index_, .scope_inst_id = scope_inst_id, .scope_id = scope_id, + .instance_id = instance_id, .next_compile_time_bind_index = SemIR::CompileTimeBindIndex( compile_time_binding_stack_.all_values_size()), .lexical_lookup_has_load_error = LexicalLookupHasLoadError() || lexical_lookup_has_load_error}); if (scope_id.is_valid()) { - non_lexical_scope_stack_.push_back( - {.scope_index = next_scope_index_, .name_scope_id = scope_id}); + non_lexical_scope_stack_.push_back({.scope_index = next_scope_index_, + .name_scope_id = scope_id, + .instance_id = instance_id}); + } else { + // For lexical lookups, unqualified lookup doesn't know how to find the + // associated generic instance, so if we start adding lexical scopes with + // generic instances, we'll need to somehow track them in lookup. + CARBON_CHECK(!instance_id.is_valid()) + << "Lexical scope should not have an associated generic instance."; } // TODO: Handle this case more gracefully. @@ -207,8 +216,10 @@ auto ScopeStack::Restore(SuspendedScope scope) -> void { << scope.entry.next_compile_time_bind_index.index; if (scope.entry.scope_id.is_valid()) { - non_lexical_scope_stack_.push_back({.scope_index = scope.entry.index, - .name_scope_id = scope.entry.scope_id}); + non_lexical_scope_stack_.push_back( + {.scope_index = scope.entry.index, + .name_scope_id = scope.entry.scope_id, + .instance_id = scope.entry.instance_id}); } scope_stack_.push_back(std::move(scope.entry)); } diff --git a/toolchain/check/scope_stack.h b/toolchain/check/scope_stack.h index 8d26383e35a9..a0a3fdea3724 100644 --- a/toolchain/check/scope_stack.h +++ b/toolchain/check/scope_stack.h @@ -46,6 +46,9 @@ class ScopeStack { // The corresponding name scope. SemIR::NameScopeId name_scope_id; + + // The corresponding generic instance. + SemIR::GenericInstanceId instance_id; }; // Information about a scope that has been temporarily removed from the stack. @@ -55,9 +58,11 @@ class ScopeStack { // scopes. lexical_lookup_has_load_error is used to limit diagnostics when a // given namespace may contain a mix of both successful and failed name // imports. - auto Push(SemIR::InstId scope_inst_id = SemIR::InstId::Invalid, - SemIR::NameScopeId scope_id = SemIR::NameScopeId::Invalid, - bool lexical_lookup_has_load_error = false) -> void; + auto Push( + SemIR::InstId scope_inst_id = SemIR::InstId::Invalid, + SemIR::NameScopeId scope_id = SemIR::NameScopeId::Invalid, + SemIR::GenericInstanceId instance_id = SemIR::GenericInstanceId::Invalid, + bool lexical_lookup_has_load_error = false) -> void; // Pops the top scope from scope_stack_, cleaning up names from // lexical_lookup_. @@ -168,6 +173,9 @@ class ScopeStack { // The name scope associated with this entry, if any. SemIR::NameScopeId scope_id; + // The generic instance associated with this entry, if any. + SemIR::GenericInstanceId instance_id; + // The next compile-time binding index to allocate in this scope. SemIR::CompileTimeBindIndex next_compile_time_bind_index; diff --git a/toolchain/check/testdata/basics/no_prelude/raw_ir.carbon b/toolchain/check/testdata/basics/no_prelude/raw_ir.carbon index 84461a1c63f6..d620a6ae3cf6 100644 --- a/toolchain/check/testdata/basics/no_prelude/raw_ir.carbon +++ b/toolchain/check/testdata/basics/no_prelude/raw_ir.carbon @@ -28,11 +28,12 @@ fn Foo[T:! type](n: T) -> (T, ()) { // CHECK:STDOUT: bind_name0: {name: name1, parent_scope: name_scope, index: comp_time_bind0} // CHECK:STDOUT: bind_name1: {name: name2, parent_scope: name_scope, index: comp_time_bind} // CHECK:STDOUT: functions: -// CHECK:STDOUT: function0: {name: name0, parent_scope: name_scope0, param_refs: block5, return_storage: inst+15, return_slot: present, body: [block10]} +// CHECK:STDOUT: function0: {name: name0, parent_scope: name_scope0, param_refs: block5, return_storage: inst+15, return_slot: present, body: [block11]} // CHECK:STDOUT: classes: {} // CHECK:STDOUT: generics: // CHECK:STDOUT: generic0: {decl: inst+16, bindings: block8} -// CHECK:STDOUT: generic_instances: {} +// CHECK:STDOUT: generic_instances: +// CHECK:STDOUT: genericInstance0: {generic: generic0, args: block10} // CHECK:STDOUT: types: // CHECK:STDOUT: type0: {constant: template instNamespaceType, value_rep: {kind: copy, type: type0}} // CHECK:STDOUT: type1: {constant: symbolic 0, value_rep: {kind: copy, type: type1}} @@ -74,14 +75,14 @@ fn Foo[T:! type](n: T) -> (T, ()) { // CHECK:STDOUT: 'inst+19': {kind: PointerType, arg0: type4, type: typeTypeType} // CHECK:STDOUT: 'inst+20': {kind: NameRef, arg0: name2, arg1: inst+6, type: type1} // CHECK:STDOUT: 'inst+21': {kind: TupleLiteral, arg0: empty, type: type2} -// CHECK:STDOUT: 'inst+22': {kind: TupleLiteral, arg0: block11, type: type4} +// CHECK:STDOUT: 'inst+22': {kind: TupleLiteral, arg0: block12, type: type4} // CHECK:STDOUT: 'inst+23': {kind: TupleAccess, arg0: inst+15, arg1: element0, type: type1} // CHECK:STDOUT: 'inst+24': {kind: InitializeFrom, arg0: inst+20, arg1: inst+23, type: type1} // CHECK:STDOUT: 'inst+25': {kind: TupleAccess, arg0: inst+15, arg1: element1, type: type2} // CHECK:STDOUT: 'inst+26': {kind: TupleInit, arg0: empty, arg1: inst+25, type: type2} -// CHECK:STDOUT: 'inst+27': {kind: TupleValue, arg0: block13, type: type2} +// CHECK:STDOUT: 'inst+27': {kind: TupleValue, arg0: block14, type: type2} // CHECK:STDOUT: 'inst+28': {kind: Converted, arg0: inst+21, arg1: inst+26, type: type2} -// CHECK:STDOUT: 'inst+29': {kind: TupleInit, arg0: block12, arg1: inst+15, type: type4} +// CHECK:STDOUT: 'inst+29': {kind: TupleInit, arg0: block13, arg1: inst+15, type: type4} // CHECK:STDOUT: 'inst+30': {kind: Converted, arg0: inst+22, arg1: inst+29, type: type4} // CHECK:STDOUT: 'inst+31': {kind: ReturnExpr, arg0: inst+30, arg1: inst+15} // CHECK:STDOUT: constant_values: @@ -133,6 +134,8 @@ fn Foo[T:! type](n: T) -> (T, ()) { // CHECK:STDOUT: 0: inst+2 // CHECK:STDOUT: 1: inst+14 // CHECK:STDOUT: block10: +// CHECK:STDOUT: 0: inst+3 +// CHECK:STDOUT: block11: // CHECK:STDOUT: 0: inst+20 // CHECK:STDOUT: 1: inst+21 // CHECK:STDOUT: 2: inst+22 @@ -144,14 +147,14 @@ fn Foo[T:! type](n: T) -> (T, ()) { // CHECK:STDOUT: 8: inst+29 // CHECK:STDOUT: 9: inst+30 // CHECK:STDOUT: 10: inst+31 -// CHECK:STDOUT: block11: +// CHECK:STDOUT: block12: // CHECK:STDOUT: 0: inst+20 // CHECK:STDOUT: 1: inst+21 -// CHECK:STDOUT: block12: +// CHECK:STDOUT: block13: // CHECK:STDOUT: 0: inst+24 // CHECK:STDOUT: 1: inst+28 -// CHECK:STDOUT: block13: {} -// CHECK:STDOUT: block14: +// CHECK:STDOUT: block14: {} +// CHECK:STDOUT: block15: // CHECK:STDOUT: 0: inst+0 // CHECK:STDOUT: 1: inst+16 // CHECK:STDOUT: ... diff --git a/toolchain/check/testdata/class/fail_generic_method.carbon b/toolchain/check/testdata/class/fail_generic_method.carbon index a0c5c3fb6056..ab0686f9e4a4 100644 --- a/toolchain/check/testdata/class/fail_generic_method.carbon +++ b/toolchain/check/testdata/class/fail_generic_method.carbon @@ -85,9 +85,10 @@ fn Class(N:! i32).F[self: Self](n: T) {} // CHECK:STDOUT: %T.ref.loc12: type = name_ref T, file.%T.loc11_13.2 [symbolic = constants.%T] // CHECK:STDOUT: %.loc12: %.2 = field_decl a, element0 [template] // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [template = constants.%F] { -// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Class.2 [symbolic = %Self.ref (constants.%Class.2)] -// CHECK:STDOUT: %self.loc13_8.1: @Class.%Self.ref (%Class.2) = param self -// CHECK:STDOUT: %self.loc13_8.2: @Class.%Self.ref (%Class.2) = bind_name self, %self.loc13_8.1 +// CHECK:STDOUT: %.loc13: type = specific_constant constants.%Class.2, (constants.%T) [symbolic = %.loc13 (constants.%Class.2)] +// CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc13 [symbolic = %.loc13 (constants.%Class.2)] +// CHECK:STDOUT: %self.loc13_8.1: @Class.%.loc13 (%Class.2) = param self +// CHECK:STDOUT: %self.loc13_8.2: @Class.%.loc13 (%Class.2) = bind_name self, %self.loc13_8.1 // CHECK:STDOUT: %T.ref.loc13: type = name_ref T, file.%T.loc11_13.2 [symbolic = %T.ref.loc13 (constants.%T)] // CHECK:STDOUT: %n.loc13_20.1: @Class.%T.ref.loc13 (%T) = param n // CHECK:STDOUT: %n.loc13_20.2: @Class.%T.ref.loc13 (%T) = bind_name n, %n.loc13_20.1 @@ -99,7 +100,7 @@ fn Class(N:! i32).F[self: Self](n: T) {} // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: fn @F[@Class.%self.loc13_8.2: @Class.%Self.ref (%Class.2)](@Class.%n.loc13_20.2: @Class.%T.ref.loc13 (%T)) +// CHECK:STDOUT: fn @F[@Class.%self.loc13_8.2: @Class.%.loc13 (%Class.2)](@Class.%n.loc13_20.2: @Class.%T.ref.loc13 (%T)) // CHECK:STDOUT: generic [file.%T.loc11_13.2: type]; // CHECK:STDOUT: // CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32"; diff --git a/toolchain/check/testdata/class/generic/basic.carbon b/toolchain/check/testdata/class/generic/basic.carbon index 82ae63d9b3b4..df253041af6a 100644 --- a/toolchain/check/testdata/class/generic/basic.carbon +++ b/toolchain/check/testdata/class/generic/basic.carbon @@ -57,7 +57,8 @@ class Class(T:! type) { // CHECK:STDOUT: class @Class // CHECK:STDOUT: generic [file.%T.loc11_13.2: type] { // CHECK:STDOUT: %GetAddr.decl: %GetAddr.type = fn_decl @GetAddr [template = constants.%GetAddr] { -// CHECK:STDOUT: %Self.ref.loc12: type = name_ref Self, constants.%Class.2 [symbolic = %Self.ref.loc12 (constants.%Class.2)] +// CHECK:STDOUT: %.loc12_25: type = specific_constant constants.%Class.2, (constants.%T) [symbolic = %.loc12_25 (constants.%Class.2)] +// CHECK:STDOUT: %Self.ref.loc12: type = name_ref Self, %.loc12_25 [symbolic = %.loc12_25 (constants.%Class.2)] // CHECK:STDOUT: %.loc12_29: type = ptr_type %Class.2 [symbolic = %.loc12_29 (constants.%.2)] // CHECK:STDOUT: %self.loc12_19.1: @Class.%.loc12_29 (%.2) = param self // CHECK:STDOUT: %self.loc12_19.3: @Class.%.loc12_29 (%.2) = bind_name self, %self.loc12_19.1 @@ -67,9 +68,10 @@ class Class(T:! type) { // CHECK:STDOUT: %return.var.loc12: ref %.3 = var // CHECK:STDOUT: } // CHECK:STDOUT: %GetValue.decl: %GetValue.type = fn_decl @GetValue [template = constants.%GetValue] { -// CHECK:STDOUT: %Self.ref.loc17: type = name_ref Self, constants.%Class.2 [symbolic = %Self.ref.loc17 (constants.%Class.2)] -// CHECK:STDOUT: %self.loc17_15.1: @Class.%Self.ref.loc17 (%Class.2) = param self -// CHECK:STDOUT: %self.loc17_15.2: @Class.%Self.ref.loc17 (%Class.2) = bind_name self, %self.loc17_15.1 +// CHECK:STDOUT: %.loc17: type = specific_constant constants.%Class.2, (constants.%T) [symbolic = %.loc17 (constants.%Class.2)] +// CHECK:STDOUT: %Self.ref.loc17: type = name_ref Self, %.loc17 [symbolic = %.loc17 (constants.%Class.2)] +// CHECK:STDOUT: %self.loc17_15.1: @Class.%.loc17 (%Class.2) = param self +// CHECK:STDOUT: %self.loc17_15.2: @Class.%.loc17 (%Class.2) = bind_name self, %self.loc17_15.1 // CHECK:STDOUT: %T.ref.loc17: type = name_ref T, file.%T.loc11_13.2 [symbolic = %T.ref.loc17 (constants.%T)] // CHECK:STDOUT: %return.var.loc17: ref %T = var // CHECK:STDOUT: } @@ -94,7 +96,7 @@ class Class(T:! type) { // CHECK:STDOUT: return %.loc13_12 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: fn @GetValue[@Class.%self.loc17_15.2: @Class.%Self.ref.loc17 (%Class.2)]() -> %T +// CHECK:STDOUT: fn @GetValue[@Class.%self.loc17_15.2: @Class.%.loc17 (%Class.2)]() -> %T // CHECK:STDOUT: generic [file.%T.loc11_13.2: type] { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref: %Class.2 = name_ref self, @Class.%self.loc17_15.2 diff --git a/toolchain/check/testdata/class/generic/fail_todo_use.carbon b/toolchain/check/testdata/class/generic/fail_todo_use.carbon index 693ad7cb1fc4..a5522e5215f3 100644 --- a/toolchain/check/testdata/class/generic/fail_todo_use.carbon +++ b/toolchain/check/testdata/class/generic/fail_todo_use.carbon @@ -89,7 +89,8 @@ fn Run() -> i32 { // CHECK:STDOUT: class @Class // CHECK:STDOUT: generic [file.%T.loc11_13.2: type] { // CHECK:STDOUT: %Get.decl: %Get.type = fn_decl @Get [template = constants.%Get] { -// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Class.2 [symbolic = %Self.ref (constants.%Class.2)] +// CHECK:STDOUT: %.loc12_21: type = specific_constant constants.%Class.2, (constants.%T) [symbolic = %.loc12_21 (constants.%Class.2)] +// CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc12_21 [symbolic = %.loc12_21 (constants.%Class.2)] // CHECK:STDOUT: %.loc12_25: type = ptr_type %Class.2 [symbolic = %.loc12_25 (constants.%.2)] // CHECK:STDOUT: %self.loc12_15.1: @Class.%.loc12_25 (%.2) = param self // CHECK:STDOUT: %self.loc12_15.3: @Class.%.loc12_25 (%.2) = bind_name self, %self.loc12_15.1 diff --git a/toolchain/check/testdata/class/generic/member_out_of_line.carbon b/toolchain/check/testdata/class/generic/member_out_of_line.carbon index acec2aaae175..60626bdd51ea 100644 --- a/toolchain/check/testdata/class/generic/member_out_of_line.carbon +++ b/toolchain/check/testdata/class/generic/member_out_of_line.carbon @@ -191,7 +191,8 @@ fn Generic(T:! ()).WrongType() {} // CHECK:STDOUT: %T.ref.loc10_22: type = name_ref T, %T.loc10_6.2 [symbolic = constants.%T] // CHECK:STDOUT: %N.loc10_18.1: %T = param N // CHECK:STDOUT: %N.loc10_18.2: %T = bind_symbolic_name N 1, %N.loc10_18.1 [symbolic = constants.%N] -// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%B.2 [symbolic = constants.%B.2] +// CHECK:STDOUT: %.loc10: type = specific_constant constants.%B.2, (constants.%T, constants.%N) [symbolic = constants.%B.2] +// CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc10 [symbolic = constants.%B.2] // CHECK:STDOUT: %self.loc10_27.1: %B.2 = param self // CHECK:STDOUT: @F.%self: %B.2 = bind_name self, %self.loc10_27.1 // CHECK:STDOUT: %T.ref.loc10_42: type = name_ref T, %T.loc10_6.2 [symbolic = constants.%T] @@ -216,9 +217,10 @@ fn Generic(T:! ()).WrongType() {} // CHECK:STDOUT: class @B // CHECK:STDOUT: generic [file.%T.loc4_9.2: type, @A.%N.loc5_11.2: @A.%T.ref (%T)] { // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [template = constants.%F] { -// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%B.2 [symbolic = %Self.ref (constants.%B.2)] -// CHECK:STDOUT: %self.loc6_10.1: @B.%Self.ref (%B.2) = param self -// CHECK:STDOUT: %self.loc6_10.2: @B.%Self.ref (%B.2) = bind_name self, %self.loc6_10.1 +// CHECK:STDOUT: %.loc6: type = specific_constant constants.%B.2, (constants.%T, constants.%N) [symbolic = %.loc6 (constants.%B.2)] +// CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc6 [symbolic = %.loc6 (constants.%B.2)] +// CHECK:STDOUT: %self.loc6_10.1: @B.%.loc6 (%B.2) = param self +// CHECK:STDOUT: %self.loc6_10.2: @B.%.loc6 (%B.2) = bind_name self, %self.loc6_10.1 // CHECK:STDOUT: %T.ref: type = name_ref T, file.%T.loc4_9.2 [symbolic = %T.ref (constants.%T)] // CHECK:STDOUT: %a.loc6_22.1: @B.%T.ref (%T) = param a // CHECK:STDOUT: %a.loc6_22.2: @B.%T.ref (%T) = bind_name a, %a.loc6_22.1 diff --git a/toolchain/check/testdata/class/generic/self.carbon b/toolchain/check/testdata/class/generic/self.carbon index adf22b1f98de..1f7e9c2fb7f6 100644 --- a/toolchain/check/testdata/class/generic/self.carbon +++ b/toolchain/check/testdata/class/generic/self.carbon @@ -53,7 +53,8 @@ class Class(T:! type) { // CHECK:STDOUT: class @Class // CHECK:STDOUT: generic [file.%T.loc11_13.2: type] { // CHECK:STDOUT: %MakeSelf.decl: %MakeSelf.type = fn_decl @MakeSelf [template = constants.%MakeSelf] { -// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Class.2 [symbolic = %Self.ref (constants.%Class.2)] +// CHECK:STDOUT: %.loc14: type = specific_constant constants.%Class.2, (constants.%T) [symbolic = %.loc14 (constants.%Class.2)] +// CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc14 [symbolic = %.loc14 (constants.%Class.2)] // CHECK:STDOUT: %return.var.loc14: ref %Class.2 = var // CHECK:STDOUT: } // CHECK:STDOUT: %MakeClass.decl: %MakeClass.type = fn_decl @MakeClass [template = constants.%MakeClass] { @@ -93,12 +94,13 @@ class Class(T:! type) { // CHECK:STDOUT: %.loc17_9: ref %Class.2 = splice_block %c.var {} // CHECK:STDOUT: %MakeSelf.call: init %Class.2 = call %MakeSelf.ref() to %.loc17_9 // CHECK:STDOUT: assign %c.var, %MakeSelf.call -// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Class.2 [symbolic = constants.%Class.2] +// CHECK:STDOUT: %.loc18_12: type = specific_constant constants.%Class.2, (constants.%T) [symbolic = constants.%Class.2] +// CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc18_12 [symbolic = constants.%Class.2] // CHECK:STDOUT: %s.var: ref %Class.2 = var s // CHECK:STDOUT: %s: ref %Class.2 = bind_name s, %s.var // CHECK:STDOUT: %MakeClass.ref: %MakeClass.type = name_ref MakeClass, @Class.%MakeClass.decl [template = constants.%MakeClass] -// CHECK:STDOUT: %.loc18: ref %Class.2 = splice_block %s.var {} -// CHECK:STDOUT: %MakeClass.call: init %Class.2 = call %MakeClass.ref() to %.loc18 +// CHECK:STDOUT: %.loc18_9: ref %Class.2 = splice_block %s.var {} +// CHECK:STDOUT: %MakeClass.call: init %Class.2 = call %MakeClass.ref() to %.loc18_9 // CHECK:STDOUT: assign %s.var, %MakeClass.call // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/class/generic_method.carbon b/toolchain/check/testdata/class/generic_method.carbon index d3fef58a5ad8..2b4bf66d5112 100644 --- a/toolchain/check/testdata/class/generic_method.carbon +++ b/toolchain/check/testdata/class/generic_method.carbon @@ -44,7 +44,8 @@ fn Class(T:! type).F[self: Self](n: T) {} // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [template = constants.%F] { // CHECK:STDOUT: %T.loc16_10.1: type = param T // CHECK:STDOUT: %T.loc16_10.2: type = bind_symbolic_name T 0, %T.loc16_10.1 [symbolic = constants.%T] -// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Class.2 [symbolic = constants.%Class.2] +// CHECK:STDOUT: %.loc16: type = specific_constant constants.%Class.2, (constants.%T) [symbolic = constants.%Class.2] +// CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc16 [symbolic = constants.%Class.2] // CHECK:STDOUT: %self.loc16_22.1: %Class.2 = param self // CHECK:STDOUT: @F.%self: %Class.2 = bind_name self, %self.loc16_22.1 // CHECK:STDOUT: %T.ref: type = name_ref T, %T.loc16_10.2 [symbolic = constants.%T] @@ -58,9 +59,10 @@ fn Class(T:! type).F[self: Self](n: T) {} // CHECK:STDOUT: %T.ref.loc12: type = name_ref T, file.%T.loc11_13.2 [symbolic = constants.%T] // CHECK:STDOUT: %.loc12: %.2 = field_decl a, element0 [template] // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [template = constants.%F] { -// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Class.2 [symbolic = %Self.ref (constants.%Class.2)] -// CHECK:STDOUT: %self.loc13_8.1: @Class.%Self.ref (%Class.2) = param self -// CHECK:STDOUT: %self.loc13_8.2: @Class.%Self.ref (%Class.2) = bind_name self, %self.loc13_8.1 +// CHECK:STDOUT: %.loc13: type = specific_constant constants.%Class.2, (constants.%T) [symbolic = %.loc13 (constants.%Class.2)] +// CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc13 [symbolic = %.loc13 (constants.%Class.2)] +// CHECK:STDOUT: %self.loc13_8.1: @Class.%.loc13 (%Class.2) = param self +// CHECK:STDOUT: %self.loc13_8.2: @Class.%.loc13 (%Class.2) = bind_name self, %self.loc13_8.1 // CHECK:STDOUT: %T.ref.loc13: type = name_ref T, file.%T.loc11_13.2 [symbolic = %T.ref.loc13 (constants.%T)] // CHECK:STDOUT: %n.loc13_20.1: @Class.%T.ref.loc13 (%T) = param n // CHECK:STDOUT: %n.loc13_20.2: @Class.%T.ref.loc13 (%T) = bind_name n, %n.loc13_20.1 diff --git a/toolchain/check/testdata/interface/no_prelude/fail_todo_generic_default_fn.carbon b/toolchain/check/testdata/interface/no_prelude/fail_todo_generic_default_fn.carbon index a8aef588092a..332e08813865 100644 --- a/toolchain/check/testdata/interface/no_prelude/fail_todo_generic_default_fn.carbon +++ b/toolchain/check/testdata/interface/no_prelude/fail_todo_generic_default_fn.carbon @@ -49,14 +49,16 @@ fn I(T:! type).F[self: Self]() -> Self { return self; } // CHECK:STDOUT: %.decl: %.type = fn_decl @.1 [template = constants.%.5] { // CHECK:STDOUT: %T.loc22_6.1: type = param T // CHECK:STDOUT: %T.loc22_6.2: type = bind_symbolic_name T 0, %T.loc22_6.1 [symbolic = %T.loc22_6.2 (constants.%T)] -// CHECK:STDOUT: %Self.ref.loc22_24: (%.2) = name_ref Self, @I.%Self [symbolic = %Self.ref.loc22_24 (constants.%Self)] -// CHECK:STDOUT: %.loc22_24.1: type = facet_type_access %Self.ref.loc22_24 [symbolic = %Self.ref.loc22_24 (constants.%Self)] -// CHECK:STDOUT: %.loc22_24.2: type = converted %Self.ref.loc22_24, %.loc22_24.1 [symbolic = %Self.ref.loc22_24 (constants.%Self)] -// CHECK:STDOUT: %self.loc22_18.1: file.%Self.ref.loc22_24 (%Self) = param self -// CHECK:STDOUT: @.1.%self: file.%Self.ref.loc22_24 (%Self) = bind_name self, %self.loc22_18.1 -// CHECK:STDOUT: %Self.ref.loc22_35: (%.2) = name_ref Self, @I.%Self [symbolic = %Self.ref.loc22_24 (constants.%Self)] -// CHECK:STDOUT: %.loc22_35.1: type = facet_type_access %Self.ref.loc22_35 [symbolic = %Self.ref.loc22_24 (constants.%Self)] -// CHECK:STDOUT: %.loc22_35.2: type = converted %Self.ref.loc22_35, %.loc22_35.1 [symbolic = %Self.ref.loc22_24 (constants.%Self)] +// CHECK:STDOUT: %.loc22_24.1: (%.2) = specific_constant @I.%Self, (constants.%T) [symbolic = %.loc22_24.1 (constants.%Self)] +// CHECK:STDOUT: %Self.ref.loc22_24: (%.2) = name_ref Self, %.loc22_24.1 [symbolic = %.loc22_24.1 (constants.%Self)] +// CHECK:STDOUT: %.loc22_24.2: type = facet_type_access %Self.ref.loc22_24 [symbolic = %.loc22_24.1 (constants.%Self)] +// CHECK:STDOUT: %.loc22_24.3: type = converted %Self.ref.loc22_24, %.loc22_24.2 [symbolic = %.loc22_24.1 (constants.%Self)] +// CHECK:STDOUT: %self.loc22_18.1: file.%.loc22_24.1 (%Self) = param self +// CHECK:STDOUT: @.1.%self: file.%.loc22_24.1 (%Self) = bind_name self, %self.loc22_18.1 +// CHECK:STDOUT: %.loc22_35.1: (%.2) = specific_constant @I.%Self, (constants.%T) [symbolic = %.loc22_24.1 (constants.%Self)] +// CHECK:STDOUT: %Self.ref.loc22_35: (%.2) = name_ref Self, %.loc22_35.1 [symbolic = %.loc22_24.1 (constants.%Self)] +// CHECK:STDOUT: %.loc22_35.2: type = facet_type_access %Self.ref.loc22_35 [symbolic = %.loc22_24.1 (constants.%Self)] +// CHECK:STDOUT: %.loc22_35.3: type = converted %Self.ref.loc22_35, %.loc22_35.2 [symbolic = %.loc22_24.1 (constants.%Self)] // CHECK:STDOUT: @.1.%return: ref %Self = var // CHECK:STDOUT: } // CHECK:STDOUT: } @@ -65,14 +67,16 @@ fn I(T:! type).F[self: Self]() -> Self { return self; } // CHECK:STDOUT: generic [file.%T.loc11_13.2: type] { // CHECK:STDOUT: %Self: %.2 = bind_symbolic_name Self 1 [symbolic = constants.%Self] // CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [template = constants.%F] { -// CHECK:STDOUT: %Self.ref.loc13_14: %.2 = name_ref Self, %Self [symbolic = %Self.ref.loc13_14 (constants.%Self)] -// CHECK:STDOUT: %.loc13_14.1: type = facet_type_access %Self.ref.loc13_14 [symbolic = %Self.ref.loc13_14 (constants.%Self)] -// CHECK:STDOUT: %.loc13_14.2: type = converted %Self.ref.loc13_14, %.loc13_14.1 [symbolic = %Self.ref.loc13_14 (constants.%Self)] -// CHECK:STDOUT: %self.loc13_8.1: @I.%Self.ref.loc13_14 (%Self) = param self -// CHECK:STDOUT: %self.loc13_8.2: @I.%Self.ref.loc13_14 (%Self) = bind_name self, %self.loc13_8.1 -// CHECK:STDOUT: %Self.ref.loc13_25: %.2 = name_ref Self, %Self [symbolic = %Self.ref.loc13_14 (constants.%Self)] -// CHECK:STDOUT: %.loc13_25.1: type = facet_type_access %Self.ref.loc13_25 [symbolic = %Self.ref.loc13_14 (constants.%Self)] -// CHECK:STDOUT: %.loc13_25.2: type = converted %Self.ref.loc13_25, %.loc13_25.1 [symbolic = %Self.ref.loc13_14 (constants.%Self)] +// CHECK:STDOUT: %.loc13_14.1: %.2 = specific_constant %Self, (constants.%T) [symbolic = %.loc13_14.1 (constants.%Self)] +// CHECK:STDOUT: %Self.ref.loc13_14: %.2 = name_ref Self, %.loc13_14.1 [symbolic = %.loc13_14.1 (constants.%Self)] +// CHECK:STDOUT: %.loc13_14.2: type = facet_type_access %Self.ref.loc13_14 [symbolic = %.loc13_14.1 (constants.%Self)] +// CHECK:STDOUT: %.loc13_14.3: type = converted %Self.ref.loc13_14, %.loc13_14.2 [symbolic = %.loc13_14.1 (constants.%Self)] +// CHECK:STDOUT: %self.loc13_8.1: @I.%.loc13_14.1 (%Self) = param self +// CHECK:STDOUT: %self.loc13_8.2: @I.%.loc13_14.1 (%Self) = bind_name self, %self.loc13_8.1 +// CHECK:STDOUT: %.loc13_25.1: %.2 = specific_constant %Self, (constants.%T) [symbolic = %.loc13_14.1 (constants.%Self)] +// CHECK:STDOUT: %Self.ref.loc13_25: %.2 = name_ref Self, %.loc13_25.1 [symbolic = %.loc13_14.1 (constants.%Self)] +// CHECK:STDOUT: %.loc13_25.2: type = facet_type_access %Self.ref.loc13_25 [symbolic = %.loc13_14.1 (constants.%Self)] +// CHECK:STDOUT: %.loc13_25.3: type = converted %Self.ref.loc13_25, %.loc13_25.2 [symbolic = %.loc13_14.1 (constants.%Self)] // CHECK:STDOUT: %return.var: ref %Self = var // CHECK:STDOUT: } // CHECK:STDOUT: %.loc13_29: %.3 = assoc_entity element0, %F.decl [template = constants.%.4] @@ -83,10 +87,10 @@ fn I(T:! type).F[self: Self]() -> Self { return self; } // CHECK:STDOUT: witness = (%F.decl) // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: fn @F[@I.%self.loc13_8.2: @I.%Self.ref.loc13_14 (%Self)]() -> %Self +// CHECK:STDOUT: fn @F[@I.%self.loc13_8.2: @I.%.loc13_14.1 (%Self)]() -> %Self // CHECK:STDOUT: generic [file.%T.loc11_13.2: type, @I.%Self: %.2]; // CHECK:STDOUT: -// CHECK:STDOUT: fn @.1[%self: file.%Self.ref.loc22_24 (%Self)]() -> %Self +// CHECK:STDOUT: fn @.1[%self: file.%.loc22_24.1 (%Self)]() -> %Self // CHECK:STDOUT: generic [file.%T.loc22_6.2: type] { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %self.ref: %Self = name_ref self, %self diff --git a/toolchain/lower/constant.cpp b/toolchain/lower/constant.cpp index 0ec0f4cb2019..13376fe8cd5e 100644 --- a/toolchain/lower/constant.cpp +++ b/toolchain/lower/constant.cpp @@ -5,11 +5,11 @@ #include "toolchain/lower/constant.h" #include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/StringRef.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Value.h" #include "toolchain/base/kind_switch.h" #include "toolchain/lower/file_context.h" +#include "toolchain/sem_ir/generic.h" #include "toolchain/sem_ir/inst.h" namespace Carbon::Lower { diff --git a/toolchain/sem_ir/file.cpp b/toolchain/sem_ir/file.cpp index 274bbfe5b94f..560cc038d418 100644 --- a/toolchain/sem_ir/file.cpp +++ b/toolchain/sem_ir/file.cpp @@ -442,6 +442,7 @@ static auto StringifyTypeExprImpl(const SemIR::File& outer_sem_ir, case ImportRefLoaded::Kind: case ImportRefUnloaded::Kind: case InitializeFrom::Kind: + case SpecificConstant::Kind: case InterfaceDecl::Kind: case InterfaceWitness::Kind: case InterfaceWitnessAccess::Kind: @@ -548,6 +549,11 @@ auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory { continue; } + case CARBON_KIND(SpecificConstant inst): { + inst_id = inst.inst_id; + continue; + } + case AddrOf::Kind: case AddrPattern::Kind: case ArrayType::Kind: diff --git a/toolchain/sem_ir/file.h b/toolchain/sem_ir/file.h index 1c5cd4a235f9..c74221166372 100644 --- a/toolchain/sem_ir/file.h +++ b/toolchain/sem_ir/file.h @@ -129,8 +129,8 @@ class File : public Printable { } auto impls() -> ImplStore& { return impls_; } auto impls() const -> const ImplStore& { return impls_; } - auto generics() -> ValueStore& { return generics_; } - auto generics() const -> const ValueStore& { return generics_; } + auto generics() -> GenericStore& { return generics_; } + auto generics() const -> const GenericStore& { return generics_; } auto generic_instances() -> GenericInstanceStore& { return generic_instances_; } @@ -218,7 +218,7 @@ class File : public Printable { ImplStore impls_; // Storage for generics. - ValueStore generics_; + GenericStore generics_; // Storage for instances of generics. GenericInstanceStore generic_instances_; diff --git a/toolchain/sem_ir/generic.cpp b/toolchain/sem_ir/generic.cpp index dc121010d630..951861761110 100644 --- a/toolchain/sem_ir/generic.cpp +++ b/toolchain/sem_ir/generic.cpp @@ -4,6 +4,8 @@ #include "toolchain/sem_ir/generic.h" +#include "toolchain/sem_ir/file.h" + namespace Carbon::SemIR { class GenericInstanceStore::KeyContext @@ -42,4 +44,30 @@ auto GenericInstanceStore::GetOrAdd(GenericId generic_id, InstBlockId args_id) .key(); } +auto GetConstantInInstance(const File& sem_ir, + GenericInstanceId /*instance_id*/, + ConstantId const_id) -> ConstantId { + if (!const_id.is_symbolic()) { + // Type does not depend on a generic parameter. + return const_id; + } + + const auto& symbolic = sem_ir.constant_values().GetSymbolicConstant(const_id); + if (!symbolic.generic_id.is_valid()) { + // Constant is an abstract symbolic constant, not an instance-specific one. + return const_id; + } + + // TODO: Look up the value in the generic instance. For now, return the + // canonical constant value. + return sem_ir.constant_values().Get(symbolic.inst_id); +} + +auto GetConstantValueInInstance(const File& sem_ir, + GenericInstanceId instance_id, InstId inst_id) + -> ConstantId { + return GetConstantInInstance(sem_ir, instance_id, + sem_ir.constant_values().Get(inst_id)); +} + } // namespace Carbon::SemIR diff --git a/toolchain/sem_ir/generic.h b/toolchain/sem_ir/generic.h index 48cbadfb4d0d..437a84eb61b3 100644 --- a/toolchain/sem_ir/generic.h +++ b/toolchain/sem_ir/generic.h @@ -28,6 +28,10 @@ struct Generic : public Printable { // The index in this block will match the `bind_index` in the name binding // instruction's `BindNameInfo`. InstBlockId bindings_id; + // The self instance of this generic, which is an instance where every generic + // parameter's argument is that same parameter. For example, the self instance + // of `Vector(T:! type)` is `Vector(T)`. + GenericInstanceId self_instance_id; // The following members are set at the end of the corresponding region of the // generic. @@ -37,6 +41,17 @@ struct Generic : public Printable { InstBlockId decl_block_id = InstBlockId::Invalid; }; +// Provides storage for generics. +class GenericStore : public ValueStore { + public: + // Get the self-instance for a generic, or an invalid instance for an invalid + // generic ID. + auto GetSelfInstance(GenericId id) -> GenericInstanceId { + return id.is_valid() ? Get(id).self_instance_id + : GenericInstanceId::Invalid; + } +}; + // An instance of a generic entity, such as an instance of a generic function. // For each construct that depends on a compile-time parameter in the generic // entity, this contains the corresponding non-generic value. This includes @@ -92,6 +107,19 @@ class GenericInstanceStore : public Yaml::Printable { Carbon::Set lookup_table_; }; +// Gets the substituted value of a constant within a specified instance of a +// generic. Note that this does not perform substitution, and will return +// `Invalid` if the substituted constant value is not yet known. +auto GetConstantInInstance(const File& sem_ir, GenericInstanceId instance_id, + ConstantId const_id) -> ConstantId; + +// Gets the substituted constant value of an instruction within a specified +// instance of a generic. Note that this does not perform substitution, and will +// return `Invalid` if the substituted constant value is not yet known. +auto GetConstantValueInInstance(const File& sem_ir, + GenericInstanceId instance_id, InstId inst_id) + -> ConstantId; + } // namespace Carbon::SemIR #endif // CARBON_TOOLCHAIN_SEM_IR_GENERIC_H_ diff --git a/toolchain/sem_ir/inst_kind.def b/toolchain/sem_ir/inst_kind.def index d12fd01ada0a..097ebd31b6ee 100644 --- a/toolchain/sem_ir/inst_kind.def +++ b/toolchain/sem_ir/inst_kind.def @@ -62,6 +62,7 @@ CARBON_SEM_IR_INST_KIND(ImportDecl) CARBON_SEM_IR_INST_KIND(ImportRefUnloaded) CARBON_SEM_IR_INST_KIND(ImportRefLoaded) CARBON_SEM_IR_INST_KIND(InitializeFrom) +CARBON_SEM_IR_INST_KIND(SpecificConstant) CARBON_SEM_IR_INST_KIND(InterfaceDecl) CARBON_SEM_IR_INST_KIND(InterfaceType) CARBON_SEM_IR_INST_KIND(InterfaceWitness) diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index a5e9e93c7d2d..f9a10f74049a 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -821,6 +821,22 @@ struct ReturnExpr { InstId dest_id; }; +// Given an instruction with a constant value that depends on a generic +// parameter, selects an instance of that instruction with the constant value +// corresponding to a particular specific. +// +// TODO: We only form these as the instruction referenced by a `NameRef`. +// Consider merging an `SpecificConstant` + `NameRef` into a new form of +// instruction in order to give a more compact representation. +struct SpecificConstant { + static constexpr auto Kind = InstKind::SpecificConstant.Define( + {.ir_name = "specific_constant", .is_lowered = false}); + + TypeId type_id; + InstId inst_id; + GenericInstanceId instance_id; +}; + // Splices a block into the location where this appears. This may be an // expression, producing a result with a given type. For example, when // constructing from aggregates we may figure out which conversions are required