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