From 6d3c915bbfa05d20b768735d20e9676cdcd7f2b5 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 10 Jul 2024 11:35:55 -0700 Subject: [PATCH] When performing name lookup, determine the generic instance within which the lookup result was found. (#4118) Require types into which qualified lookup is performed to be completely defined. Eventually this will trigger substitution into the definition for generic types. --- toolchain/check/context.cpp | 89 ++++++-- toolchain/check/context.h | 35 ++- toolchain/check/generic.cpp | 7 + toolchain/check/generic.h | 9 + toolchain/check/handle_name.cpp | 12 +- toolchain/check/member_access.cpp | 95 ++++---- toolchain/check/operator.cpp | 17 +- toolchain/check/testdata/class/basic.carbon | 5 +- .../testdata/class/fail_base_unbound.carbon | 2 + .../check/testdata/class/fail_method.carbon | 2 +- .../testdata/class/fail_unbound_field.carbon | 1 + .../check/testdata/class/generic/field.carbon | 214 ++++++++++++++++++ .../check/testdata/class/nested_name.carbon | 4 +- .../no_prelude/indirect_import_member.carbon | 5 + .../check/testdata/class/reenter_scope.carbon | 1 + toolchain/check/testdata/class/reorder.carbon | 5 +- toolchain/check/testdata/class/scope.carbon | 1 + .../lookup/fail_todo_undefined_impl.carbon | 5 +- .../fail_assoc_const_not_constant.carbon | 1 + .../check/testdata/package_expr/syntax.carbon | 1 + 20 files changed, 422 insertions(+), 89 deletions(-) create mode 100644 toolchain/check/testdata/class/generic/field.carbon diff --git a/toolchain/check/context.cpp b/toolchain/check/context.cpp index dd1eb8870ca40..431603f453f1f 100644 --- a/toolchain/check/context.cpp +++ b/toolchain/check/context.cpp @@ -263,7 +263,7 @@ auto Context::LookupNameInDecl(SemIR::LocId loc_id, SemIR::NameId name_id, } auto Context::LookupUnqualifiedName(Parse::NodeId node_id, - SemIR::NameId name_id) -> SemIR::InstId { + SemIR::NameId name_id) -> LookupResult { // TODO: Check for shadowed lookup results. // Find the results from ancestor lexical scopes. These will be combined with @@ -272,22 +272,33 @@ 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, name_scope_id] : llvm::reverse(non_lexical_scopes)) { - if (auto non_lexical_result = - LookupQualifiedName(node_id, name_id, name_scope_id, - /*required=*/false); - non_lexical_result.is_valid()) { + 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. + if (auto non_lexical_result = LookupQualifiedName( + node_id, name_id, + {.name_scope_id = lookup_scope_id, + .instance_id = SemIR::GenericInstanceId::Invalid}, + /*required=*/false); + non_lexical_result.inst_id.is_valid()) { return non_lexical_result; } } if (lexical_result.is_valid()) { - return lexical_result; + // A lexical scope never needs an associated generic instance. If there's a + // lexically enclosing generic, then it also encloses the point of use of + // the name. + return {.instance_id = SemIR::GenericInstanceId::Invalid, + .inst_id = lexical_result}; } // We didn't find anything at all. DiagnoseNameNotFound(node_id, name_id); - return SemIR::InstId::BuiltinError; + return {.instance_id = SemIR::GenericInstanceId::Invalid, + .inst_id = SemIR::InstId::BuiltinError}; } // Handles lookup through the import_ir_scopes for LookupNameInExactScope. @@ -391,15 +402,16 @@ auto Context::LookupNameInExactScope(SemIRLoc loc, SemIR::NameId name_id, } auto Context::LookupQualifiedName(Parse::NodeId node_id, SemIR::NameId name_id, - SemIR::NameScopeId scope_id, bool required) - -> SemIR::InstId { - llvm::SmallVector scope_ids = {scope_id}; - auto result_id = SemIR::InstId::Invalid; + LookupScope scope, bool required) + -> LookupResult { + llvm::SmallVector scopes = {scope}; + LookupResult result = {.instance_id = SemIR::GenericInstanceId::Invalid, + .inst_id = SemIR::InstId::Invalid}; bool has_error = false; // Walk this scope and, if nothing is found here, the scopes it extends. - while (!scope_ids.empty()) { - auto scope_id = scope_ids.pop_back_val(); + while (!scopes.empty()) { + auto [scope_id, instance_id] = scopes.pop_back_val(); const auto& scope = name_scopes().Get(scope_id); has_error |= scope.has_error; @@ -407,13 +419,19 @@ auto Context::LookupQualifiedName(Parse::NodeId node_id, SemIR::NameId name_id, LookupNameInExactScope(node_id, name_id, scope_id, scope); if (!scope_result_id.is_valid()) { // Nothing found in this scope: also look in its extended scopes. - auto extended = llvm::reverse(scope.extended_scopes); - scope_ids.append(extended.begin(), extended.end()); + auto extended = scope.extended_scopes; + scopes.reserve(scopes.size() + extended.size()); + for (auto extended_id : llvm::reverse(extended)) { + // TODO: Track a constant describing the extended scope, and substitute + // into it to determine its corresponding generic instance. + scopes.push_back({.name_scope_id = extended_id, + .instance_id = SemIR::GenericInstanceId::Invalid}); + } continue; } // If this is our second lookup result, diagnose an ambiguity. - if (result_id.is_valid()) { + if (result.inst_id.is_valid()) { // TODO: This is currently not reachable because the only scope that can // extend is a class scope, and it can only extend a single base class. // Add test coverage once this is possible. @@ -423,20 +441,23 @@ auto Context::LookupQualifiedName(Parse::NodeId node_id, SemIR::NameId name_id, SemIR::NameId); emitter_->Emit(node_id, NameAmbiguousDueToExtend, name_id); // TODO: Add notes pointing to the scopes. - return SemIR::InstId::BuiltinError; + return {.instance_id = SemIR::GenericInstanceId::Invalid, + .inst_id = SemIR::InstId::BuiltinError}; } - result_id = scope_result_id; + result.inst_id = scope_result_id; + result.instance_id = instance_id; } - if (required && !result_id.is_valid()) { + if (required && !result.inst_id.is_valid()) { if (!has_error) { DiagnoseNameNotFound(node_id, name_id); } - return SemIR::InstId::BuiltinError; + return {.instance_id = SemIR::GenericInstanceId::Invalid, + .inst_id = SemIR::InstId::BuiltinError}; } - return result_id; + return result; } // Returns the scope of the Core package, or Invalid if it's not found. @@ -779,6 +800,7 @@ class TypeCompleter { } return false; } + // TODO: Trigger generic resolution here for a generic class. Push(class_info.object_repr_id); break; } @@ -1042,6 +1064,29 @@ auto Context::TryToCompleteType( return TypeCompleter(*this, diagnoser).Complete(type_id); } +auto Context::TryToDefineType( + SemIR::TypeId type_id, + std::optionalDiagnosticBuilder>> diagnoser) + -> bool { + if (!TryToCompleteType(type_id, diagnoser)) { + return false; + } + + if (auto interface = types().TryGetAs(type_id)) { + auto interface_id = interface->interface_id; + if (!interfaces().Get(interface_id).is_defined()) { + auto builder = (*diagnoser)(); + NoteUndefinedInterface(interface_id, builder); + builder.Emit(); + return false; + } + + // TODO: Trigger generic resolution here for a generic instance. + } + + return true; +} + auto Context::GetTypeIdForTypeConstant(SemIR::ConstantId constant_id) -> SemIR::TypeId { CARBON_CHECK(constant_id.is_constant()) diff --git a/toolchain/check/context.h b/toolchain/check/context.h index b15ff3ac0df3f..0482990a46764 100644 --- a/toolchain/check/context.h +++ b/toolchain/check/context.h @@ -26,6 +26,24 @@ namespace Carbon::Check { +// Information about a scope in which we can perform name lookup. +struct LookupScope { + // The name scope in which names are searched. + SemIR::NameScopeId name_scope_id; + // The generic instance for the name scope, or `Invalid` if the name scope is + // not an instance of a generic. + SemIR::GenericInstanceId instance_id; +}; + +// A result produced by name lookup. +struct LookupResult { + // The generic instance in which the lookup result was found. `Invalid` if the + // result was not found in a generic instance. + SemIR::GenericInstanceId instance_id; + // The declaration that was found by name lookup. + SemIR::InstId inst_id; +}; + // Context and shared functionality for semantics handlers. class Context { public: @@ -122,7 +140,7 @@ class Context { // Performs an unqualified name lookup, returning the referenced instruction. auto LookupUnqualifiedName(Parse::NodeId node_id, SemIR::NameId name_id) - -> SemIR::InstId; + -> LookupResult; // Performs a name lookup in a specified scope, returning the referenced // instruction. Does not look into extended scopes. Returns an invalid @@ -134,8 +152,8 @@ class Context { // Performs a qualified name lookup in a specified scope and in scopes that // it extends, returning the referenced instruction. auto LookupQualifiedName(Parse::NodeId node_id, SemIR::NameId name_id, - SemIR::NameScopeId scope_id, bool required = true) - -> SemIR::InstId; + LookupScope scope, bool required = true) + -> LookupResult; // Returns the instruction corresponding to a name in the core package, or // BuiltinError if not found. @@ -239,6 +257,17 @@ class Context { std::optionalDiagnosticBuilder>> diagnoser = std::nullopt) -> bool; + // Attempts to complete and define the type `type_id`. Returns `true` if the + // type is defined, or `false` if no definition is available. A defined type + // has known members. + // + // This is the same as `TryToCompleteType` except for interfaces, which are + // complete before they are fully defined. + auto TryToDefineType( + SemIR::TypeId type_id, + std::optionalDiagnosticBuilder>> diagnoser = + std::nullopt) -> bool; + // Returns the type `type_id` as a complete type, or produces an incomplete // type error and returns an error type. This is a convenience wrapper around // TryToCompleteType. diff --git a/toolchain/check/generic.cpp b/toolchain/check/generic.cpp index 4f96b1f4a645b..b921da2c2926a 100644 --- a/toolchain/check/generic.cpp +++ b/toolchain/check/generic.cpp @@ -281,6 +281,13 @@ auto GetConstantInInstance(Context& context, 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); diff --git a/toolchain/check/generic.h b/toolchain/check/generic.h index 8a028cb1af411..0f172f833e8f5 100644 --- a/toolchain/check/generic.h +++ b/toolchain/check/generic.h @@ -54,6 +54,15 @@ 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_name.cpp b/toolchain/check/handle_name.cpp index 2149a01e58c35..2623b05bcae65 100644 --- a/toolchain/check/handle_name.cpp +++ b/toolchain/check/handle_name.cpp @@ -80,15 +80,15 @@ static auto GetIdentifierAsName(Context& context, Parse::NodeId node_id) // lookup. static auto HandleNameAsExpr(Context& context, Parse::NodeId node_id, SemIR::NameId name_id) -> bool { - auto value_id = context.LookupUnqualifiedName(node_id, name_id); - // TODO: Lookup should produce this. - auto instance_id = SemIR::GenericInstanceId::Invalid; - auto value = context.insts().Get(value_id); - auto type_id = GetTypeInInstance(context, instance_id, value.type_id()); + auto result = context.LookupUnqualifiedName(node_id, name_id); + auto value = context.insts().Get(result.inst_id); + auto type_id = + GetTypeInInstance(context, result.instance_id, value.type_id()); CARBON_CHECK(type_id.is_valid()) << "Missing type for " << value; context.AddInstAndPush( - node_id, {.type_id = type_id, .name_id = name_id, .value_id = value_id}); + node_id, + {.type_id = type_id, .name_id = name_id, .value_id = result.inst_id}); return true; } diff --git a/toolchain/check/member_access.cpp b/toolchain/check/member_access.cpp index 3fcd8a635833e..c9cb4ace72880 100644 --- a/toolchain/check/member_access.cpp +++ b/toolchain/check/member_access.cpp @@ -8,54 +8,57 @@ #include "toolchain/base/kind_switch.h" #include "toolchain/check/context.h" #include "toolchain/check/convert.h" +#include "toolchain/check/generic.h" #include "toolchain/check/import_ref.h" #include "toolchain/check/subst.h" #include "toolchain/diagnostics/diagnostic_emitter.h" +#include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { -// Returns the name scope corresponding to base_id, or nullopt if not a scope. +// Returns the lookup scope corresponding to base_id, or nullopt if not a scope. // On invalid scopes, prints a diagnostic and still returns the scope. -static auto GetAsNameScope(Context& context, Parse::NodeId node_id, - SemIR::ConstantId base_const_id) - -> std::optional { +static auto GetAsLookupScope(Context& context, Parse::NodeId node_id, + SemIR::ConstantId base_const_id) + -> std::optional { auto base_id = context.constant_values().GetInstId(base_const_id); auto base = context.insts().Get(base_id); if (auto base_as_namespace = base.TryAs()) { - return base_as_namespace->name_scope_id; + return LookupScope{.name_scope_id = base_as_namespace->name_scope_id, + .instance_id = SemIR::GenericInstanceId::Invalid}; } // TODO: Consider refactoring the near-identical class and interface support // below. if (auto base_as_class = base.TryAs()) { + context.TryToDefineType( + context.GetTypeIdForTypeConstant(base_const_id), [&] { + CARBON_DIAGNOSTIC(QualifiedExprInIncompleteClassScope, Error, + "Member access into incomplete class `{0}`.", + std::string); + return context.emitter().Build( + node_id, QualifiedExprInIncompleteClassScope, + context.sem_ir().StringifyType(base_const_id)); + }); auto& class_info = context.classes().Get(base_as_class->class_id); - if (!class_info.is_defined()) { - CARBON_DIAGNOSTIC(QualifiedExprInIncompleteClassScope, Error, - "Member access into incomplete class `{0}`.", - std::string); - auto builder = context.emitter().Build( - node_id, QualifiedExprInIncompleteClassScope, - context.sem_ir().StringifyType(base_const_id)); - context.NoteIncompleteClass(base_as_class->class_id, builder); - builder.Emit(); - } - return class_info.scope_id; + return LookupScope{.name_scope_id = class_info.scope_id, + .instance_id = base_as_class->instance_id}; } if (auto base_as_interface = base.TryAs()) { + context.TryToDefineType( + context.GetTypeIdForTypeConstant(base_const_id), [&] { + CARBON_DIAGNOSTIC(QualifiedExprInUndefinedInterfaceScope, Error, + "Member access into undefined interface `{0}`.", + std::string); + return context.emitter().Build( + node_id, QualifiedExprInUndefinedInterfaceScope, + context.sem_ir().StringifyType(base_const_id)); + }); auto& interface_info = context.interfaces().Get(base_as_interface->interface_id); - if (!interface_info.is_defined()) { - CARBON_DIAGNOSTIC(QualifiedExprInUndefinedInterfaceScope, Error, - "Member access into undefined interface `{0}`.", - std::string); - auto builder = context.emitter().Build( - node_id, QualifiedExprInUndefinedInterfaceScope, - context.sem_ir().StringifyType(base_const_id)); - context.NoteUndefinedInterface(base_as_interface->interface_id, builder); - builder.Emit(); - } - return interface_info.scope_id; + return LookupScope{.name_scope_id = interface_info.scope_id, + .instance_id = base_as_interface->instance_id}; } // TODO: Per the design, if `base_id` is any kind of type, then lookup should // treat it as a name scope, even if it doesn't have members. For example, @@ -96,11 +99,10 @@ static auto IsInstanceMethod(const SemIR::File& sem_ir, return false; } -// Returns whether `name_scope_id` is a scope for which impl lookup should be -// performed if we find an associated entity. -static auto ScopeNeedsImplLookup(Context& context, - SemIR::NameScopeId name_scope_id) -> bool { - auto [_, inst] = context.name_scopes().GetInstIfValid(name_scope_id); +// Returns whether `scope` is a scope for which impl lookup should be performed +// if we find an associated entity. +static auto ScopeNeedsImplLookup(Context& context, LookupScope scope) -> bool { + auto [_, inst] = context.name_scopes().GetInstIfValid(scope.name_scope_id); if (!inst) { return false; } @@ -214,17 +216,20 @@ static auto LookupMemberNameInScope(Context& context, Parse::NodeId node_id, SemIR::InstId /*base_id*/, SemIR::NameId name_id, SemIR::ConstantId name_scope_const_id, - SemIR::NameScopeId name_scope_id) - -> SemIR::InstId { - auto inst_id = name_scope_id.is_valid() ? context.LookupQualifiedName( - node_id, name_id, name_scope_id) - : SemIR::InstId::BuiltinError; - auto inst = context.insts().Get(inst_id); + LookupScope lookup_scope) -> SemIR::InstId { + LookupResult result = {.instance_id = SemIR::GenericInstanceId::Invalid, + .inst_id = SemIR::InstId::BuiltinError}; + if (lookup_scope.name_scope_id.is_valid()) { + result = context.LookupQualifiedName(node_id, name_id, lookup_scope); + } + + auto inst = context.insts().Get(result.inst_id); + auto type_id = GetTypeInInstance(context, result.instance_id, inst.type_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( node_id, - {.type_id = inst.type_id(), .name_id = name_id, .value_id = inst_id}); + {.type_id = type_id, .name_id = name_id, .value_id = result.inst_id}); // If member name lookup finds an associated entity name, and the scope is not // a facet type, perform impl lookup. @@ -234,7 +239,7 @@ static auto LookupMemberNameInScope(Context& context, Parse::NodeId node_id, // impl member is not supposed to be treated as ambiguous. if (auto assoc_type = context.types().TryGetAs( inst.type_id())) { - if (ScopeNeedsImplLookup(context, name_scope_id)) { + if (ScopeNeedsImplLookup(context, lookup_scope)) { member_id = PerformImplLookup(context, node_id, name_scope_const_id, *assoc_type, member_id); } @@ -303,9 +308,9 @@ auto PerformMemberAccess(Context& context, Parse::NodeId node_id, // into that scope. if (auto base_const_id = context.constant_values().Get(base_id); base_const_id.is_constant()) { - if (auto name_scope_id = GetAsNameScope(context, node_id, base_const_id)) { + if (auto lookup_scope = GetAsLookupScope(context, node_id, base_const_id)) { return LookupMemberNameInScope(context, node_id, base_id, name_id, - base_const_id, *name_scope_id); + base_const_id, *lookup_scope); } } @@ -327,8 +332,8 @@ auto PerformMemberAccess(Context& context, Parse::NodeId node_id, auto base_type_const_id = context.types().GetConstantId(base_type_id); // Find the scope corresponding to the base type. - auto name_scope_id = GetAsNameScope(context, node_id, base_type_const_id); - if (!name_scope_id) { + auto lookup_scope = GetAsLookupScope(context, node_id, base_type_const_id); + if (!lookup_scope) { // The base type is not a name scope. Try some fallback options. if (auto struct_type = context.insts().TryGetAs( context.constant_values().GetInstId(base_type_const_id))) { @@ -364,7 +369,7 @@ auto PerformMemberAccess(Context& context, Parse::NodeId node_id, // Perform lookup into the base type. auto member_id = LookupMemberNameInScope(context, node_id, base_id, name_id, - base_type_const_id, *name_scope_id); + base_type_const_id, *lookup_scope); // Perform instance binding if we found an instance member. member_id = PerformInstanceBinding(context, node_id, base_id, member_id); diff --git a/toolchain/check/operator.cpp b/toolchain/check/operator.cpp index d4d123183af87..d23dff1a85ba1 100644 --- a/toolchain/check/operator.cpp +++ b/toolchain/check/operator.cpp @@ -6,6 +6,7 @@ #include "toolchain/check/call.h" #include "toolchain/check/context.h" +#include "toolchain/check/generic.h" #include "toolchain/check/member_access.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/typed_insts.h" @@ -37,17 +38,25 @@ static auto GetOperatorOpFunction(Context& context, Parse::AnyExprId node_id, return SemIR::InstId::Invalid; } + // TODO: For a parameterized interface, find the corresponding generic + // instance. + LookupScope scope = {.name_scope_id = interface_scope_id, + .instance_id = SemIR::GenericInstanceId::Invalid}; + // Lookup `Interface.Op`. auto op_ident_id = context.identifiers().Add(op.op_name); - auto op_id = context.LookupQualifiedName( - node_id, SemIR::NameId::ForIdentifier(op_ident_id), interface_scope_id, + auto op_result = context.LookupQualifiedName( + node_id, SemIR::NameId::ForIdentifier(op_ident_id), scope, /*required=*/false); - if (!op_id.is_valid()) { + if (!op_result.inst_id.is_valid()) { return SemIR::InstId::Invalid; } // Look through import_refs and aliases. - op_id = context.constant_values().GetConstantInstId(op_id); + auto op_const_id = + GetConstantInInstance(context, op_result.instance_id, + context.constant_values().Get(op_result.inst_id)); + auto op_id = context.constant_values().GetInstId(op_const_id); // We expect it to be an associated function. if (context.insts().Is(op_id)) { diff --git a/toolchain/check/testdata/class/basic.carbon b/toolchain/check/testdata/class/basic.carbon index 483e42c5dfdbe..7a7d250d349f7 100644 --- a/toolchain/check/testdata/class/basic.carbon +++ b/toolchain/check/testdata/class/basic.carbon @@ -41,7 +41,8 @@ fn Run() -> i32 { // CHECK:STDOUT: %.3: type = struct_type {.k: i32} [template] // CHECK:STDOUT: %Run.type: type = fn_type @Run [template] // CHECK:STDOUT: %Run: %Run.type = struct_value () [template] -// CHECK:STDOUT: %.4: i32 = int_literal 4 [template] +// CHECK:STDOUT: %.4: type = ptr_type %.3 [template] +// CHECK:STDOUT: %.5: i32 = int_literal 4 [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { @@ -136,7 +137,7 @@ fn Run() -> i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %Class.ref: type = name_ref Class, file.%Class.decl [template = constants.%Class] // CHECK:STDOUT: %F.ref: %F.type = name_ref F, @Class.%F.decl [template = constants.%F] -// CHECK:STDOUT: %.loc26_18: i32 = int_literal 4 [template = constants.%.4] +// CHECK:STDOUT: %.loc26_18: i32 = int_literal 4 [template = constants.%.5] // CHECK:STDOUT: %F.call: init i32 = call %F.ref(%.loc26_18) // CHECK:STDOUT: %.loc26_20.1: i32 = value_of_initializer %F.call // CHECK:STDOUT: %.loc26_20.2: i32 = converted %F.call, %.loc26_20.1 diff --git a/toolchain/check/testdata/class/fail_base_unbound.carbon b/toolchain/check/testdata/class/fail_base_unbound.carbon index 8f972133279ab..d2688bd21bdb6 100644 --- a/toolchain/check/testdata/class/fail_base_unbound.carbon +++ b/toolchain/check/testdata/class/fail_base_unbound.carbon @@ -29,6 +29,8 @@ let b: B = C.base; // CHECK:STDOUT: %.3: type = ptr_type %.1 [template] // CHECK:STDOUT: %.4: type = unbound_element_type %C, %B [template] // CHECK:STDOUT: %.5: type = struct_type {.base: %B} [template] +// CHECK:STDOUT: %.6: type = struct_type {.base: %.3} [template] +// CHECK:STDOUT: %.7: type = ptr_type %.5 [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { diff --git a/toolchain/check/testdata/class/fail_method.carbon b/toolchain/check/testdata/class/fail_method.carbon index edf841efe68eb..e5ed9bae46727 100644 --- a/toolchain/check/testdata/class/fail_method.carbon +++ b/toolchain/check/testdata/class/fail_method.carbon @@ -56,9 +56,9 @@ fn F(c: Class) { // CHECK:STDOUT: %WithSelf.type: type = fn_type @WithSelf [template] // CHECK:STDOUT: %WithSelf: %WithSelf.type = struct_value () [template] // CHECK:STDOUT: %.2: type = struct_type {} [template] +// CHECK:STDOUT: %.3: type = ptr_type %.2 [template] // CHECK:STDOUT: %F.type: type = fn_type @F [template] // CHECK:STDOUT: %F: %F.type = struct_value () [template] -// CHECK:STDOUT: %.3: type = ptr_type %.2 [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { diff --git a/toolchain/check/testdata/class/fail_unbound_field.carbon b/toolchain/check/testdata/class/fail_unbound_field.carbon index b3b343cffd84c..a05e208ff2c2a 100644 --- a/toolchain/check/testdata/class/fail_unbound_field.carbon +++ b/toolchain/check/testdata/class/fail_unbound_field.carbon @@ -39,6 +39,7 @@ fn G() -> i32 { // CHECK:STDOUT: %.3: type = struct_type {.field: i32} [template] // CHECK:STDOUT: %G.type: type = fn_type @G [template] // CHECK:STDOUT: %G: %G.type = struct_value () [template] +// CHECK:STDOUT: %.4: type = ptr_type %.3 [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { diff --git a/toolchain/check/testdata/class/generic/field.carbon b/toolchain/check/testdata/class/generic/field.carbon new file mode 100644 index 0000000000000..77694354aba7d --- /dev/null +++ b/toolchain/check/testdata/class/generic/field.carbon @@ -0,0 +1,214 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/generic/field.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/generic/field.carbon +// CHECK:STDERR: fail_todo_field.carbon: ERROR: Main//default previously provided by `field.carbon`. +// CHECK:STDERR: + +// --- field.carbon + +class Class(T:! type) { + var x: T; +} + +// TODO: This case only works because the parameters of Class and G have the +// same name and index, so canonicalize to the same type. +fn G(T:! type, c: Class(T)) -> T { + return c.x; +} + +// --- fail_todo_field.carbon + +class Class(T:! type) { + var x: T; +} + +fn F(c: Class(i32)) -> i32 { + // CHECK:STDERR: fail_todo_field.carbon:[[@LINE+8]]:3: ERROR: Cannot implicitly convert from `T` to `i32`. + // CHECK:STDERR: return c.x; + // CHECK:STDERR: ^~~~~~~~~~~ + // CHECK:STDERR: + // CHECK:STDERR: fail_todo_field.carbon:[[@LINE+4]]:10: ERROR: Cannot implicitly convert from `Class` to `Class`. + // CHECK:STDERR: return c.x; + // CHECK:STDERR: ^~~ + // CHECK:STDERR: + return c.x; +} + +fn H(U:! type, c: Class(U)) -> U { + // CHECK:STDERR: fail_todo_field.carbon:[[@LINE+7]]:3: ERROR: Cannot implicitly convert from `T` to `U`. + // CHECK:STDERR: return c.x; + // CHECK:STDERR: ^~~~~~~~~~~ + // CHECK:STDERR: + // CHECK:STDERR: fail_todo_field.carbon:[[@LINE+3]]:10: ERROR: Cannot implicitly convert from `Class` to `Class`. + // CHECK:STDERR: return c.x; + // CHECK:STDERR: ^~~ + return c.x; +} + +// CHECK:STDOUT: --- field.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %T: type = bind_symbolic_name T 0 [symbolic] +// CHECK:STDOUT: %Class.type: type = generic_class_type @Class [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %Class.1: %Class.type = struct_value () [template] +// CHECK:STDOUT: %Class.2: type = class_type @Class, (%T) [symbolic] +// CHECK:STDOUT: %.2: type = unbound_element_type %Class.2, %T [symbolic] +// CHECK:STDOUT: %.3: type = struct_type {.x: %T} [symbolic] +// CHECK:STDOUT: %G.type: type = fn_type @G [template] +// CHECK:STDOUT: %G: %G.type = struct_value () [template] +// CHECK:STDOUT: %.4: type = ptr_type %.3 [symbolic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .Class = %Class.decl +// CHECK:STDOUT: .G = %G.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %Core: = namespace %Core.import, [template] {} +// CHECK:STDOUT: %Class.decl: %Class.type = class_decl @Class [template = constants.%Class.1] { +// CHECK:STDOUT: %T.loc2_13.1: type = param T +// CHECK:STDOUT: %T.loc2_13.2: type = bind_symbolic_name T 0, %T.loc2_13.1 [symbolic = %T.loc2_13.2 (constants.%T)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [template = constants.%G] { +// CHECK:STDOUT: %T.loc8_6.1: type = param T +// CHECK:STDOUT: @G.%T: type = bind_symbolic_name T 0, %T.loc8_6.1 [symbolic = @G.%T (constants.%T)] +// CHECK:STDOUT: %Class.ref: %Class.type = name_ref Class, %Class.decl [template = constants.%Class.1] +// CHECK:STDOUT: %T.ref.loc8_25: type = name_ref T, @G.%T [symbolic = @G.%T (constants.%T)] +// CHECK:STDOUT: %.loc8_24: init type = call %Class.ref(%T.ref.loc8_25) [symbolic = %.loc8_24 (constants.%Class.2)] +// CHECK:STDOUT: %.loc8_26.1: type = value_of_initializer %.loc8_24 [symbolic = %.loc8_24 (constants.%Class.2)] +// CHECK:STDOUT: %.loc8_26.2: type = converted %.loc8_24, %.loc8_26.1 [symbolic = %.loc8_24 (constants.%Class.2)] +// CHECK:STDOUT: %c.loc8_16.1: file.%.loc8_24 (%Class.2) = param c +// CHECK:STDOUT: @G.%c: file.%.loc8_24 (%Class.2) = bind_name c, %c.loc8_16.1 +// CHECK:STDOUT: %T.ref.loc8_32: type = name_ref T, @G.%T [symbolic = @G.%T (constants.%T)] +// CHECK:STDOUT: @G.%return: ref %T = var +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Class +// CHECK:STDOUT: generic [file.%T.loc2_13.2: type] { +// CHECK:STDOUT: %T.ref: type = name_ref T, file.%T.loc2_13.2 [symbolic = constants.%T] +// CHECK:STDOUT: %.loc3: %.2 = field_decl x, element0 [template] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%Class.2 +// CHECK:STDOUT: .x = %.loc3 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G(%T: type, %c: file.%.loc8_24 (%Class.2)) -> %T +// CHECK:STDOUT: generic [%T: type] { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %c.ref: %Class.2 = name_ref c, %c +// CHECK:STDOUT: %x.ref: %.2 = name_ref x, @Class.%.loc3 [template = @Class.%.loc3] +// CHECK:STDOUT: %.loc9_11.1: ref %T = class_element_access %c.ref, element0 +// CHECK:STDOUT: %.loc9_11.2: %T = bind_value %.loc9_11.1 +// CHECK:STDOUT: return %.loc9_11.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_todo_field.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %T: type = bind_symbolic_name T 0 [symbolic] +// CHECK:STDOUT: %Class.type: type = generic_class_type @Class [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %Class.1: %Class.type = struct_value () [template] +// CHECK:STDOUT: %Class.2: type = class_type @Class, (%T) [symbolic] +// CHECK:STDOUT: %.2: type = unbound_element_type %Class.2, %T [symbolic] +// CHECK:STDOUT: %.3: type = struct_type {.x: %T} [symbolic] +// CHECK:STDOUT: %Int32.type: type = fn_type @Int32 [template] +// CHECK:STDOUT: %Int32: %Int32.type = struct_value () [template] +// CHECK:STDOUT: %Class.3: type = class_type @Class, (i32) [template] +// CHECK:STDOUT: %F.type: type = fn_type @F [template] +// CHECK:STDOUT: %F: %F.type = struct_value () [template] +// CHECK:STDOUT: %.4: type = ptr_type %.3 [symbolic] +// CHECK:STDOUT: %U: type = bind_symbolic_name U 0 [symbolic] +// CHECK:STDOUT: %Class.4: type = class_type @Class, (%U) [symbolic] +// CHECK:STDOUT: %H.type: type = fn_type @H [template] +// CHECK:STDOUT: %H: %H.type = struct_value () [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %import_ref.1: %Int32.type = import_ref ir3, inst+4, loaded [template = constants.%Int32] +// CHECK:STDOUT: %import_ref.2: %Int32.type = import_ref ir3, inst+4, loaded [template = constants.%Int32] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .Class = %Class.decl +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: .H = %H.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %Core: = namespace %Core.import, [template] {} +// CHECK:STDOUT: %Class.decl: %Class.type = class_decl @Class [template = constants.%Class.1] { +// CHECK:STDOUT: %T.loc2_13.1: type = param T +// CHECK:STDOUT: %T.loc2_13.2: type = bind_symbolic_name T 0, %T.loc2_13.1 [symbolic = %T.loc2_13.2 (constants.%T)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [template = constants.%F] { +// CHECK:STDOUT: %Class.ref.loc6: %Class.type = name_ref Class, %Class.decl [template = constants.%Class.1] +// CHECK:STDOUT: %int.make_type_32.loc6_15: init type = call constants.%Int32() [template = i32] +// CHECK:STDOUT: %.loc6_14.1: type = value_of_initializer %int.make_type_32.loc6_15 [template = i32] +// CHECK:STDOUT: %.loc6_14.2: type = converted %int.make_type_32.loc6_15, %.loc6_14.1 [template = i32] +// CHECK:STDOUT: %.loc6_14.3: init type = call %Class.ref.loc6(%.loc6_14.2) [template = constants.%Class.3] +// CHECK:STDOUT: %.loc6_18.1: type = value_of_initializer %.loc6_14.3 [template = constants.%Class.3] +// CHECK:STDOUT: %.loc6_18.2: type = converted %.loc6_14.3, %.loc6_18.1 [template = constants.%Class.3] +// CHECK:STDOUT: %c.loc6_6.1: %Class.3 = param c +// CHECK:STDOUT: @F.%c: %Class.3 = bind_name c, %c.loc6_6.1 +// CHECK:STDOUT: %int.make_type_32.loc6_24: init type = call constants.%Int32() [template = i32] +// CHECK:STDOUT: %.loc6_24.1: type = value_of_initializer %int.make_type_32.loc6_24 [template = i32] +// CHECK:STDOUT: %.loc6_24.2: type = converted %int.make_type_32.loc6_24, %.loc6_24.1 [template = i32] +// CHECK:STDOUT: @F.%return: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %H.decl: %H.type = fn_decl @H [template = constants.%H] { +// CHECK:STDOUT: %U.loc18_6.1: type = param U +// CHECK:STDOUT: @H.%U: type = bind_symbolic_name U 0, %U.loc18_6.1 [symbolic = @H.%U (constants.%U)] +// CHECK:STDOUT: %Class.ref.loc18: %Class.type = name_ref Class, %Class.decl [template = constants.%Class.1] +// CHECK:STDOUT: %U.ref.loc18_25: type = name_ref U, @H.%U [symbolic = @H.%U (constants.%U)] +// CHECK:STDOUT: %.loc18_24: init type = call %Class.ref.loc18(%U.ref.loc18_25) [symbolic = %.loc18_24 (constants.%Class.4)] +// CHECK:STDOUT: %.loc18_26.1: type = value_of_initializer %.loc18_24 [symbolic = %.loc18_24 (constants.%Class.4)] +// CHECK:STDOUT: %.loc18_26.2: type = converted %.loc18_24, %.loc18_26.1 [symbolic = %.loc18_24 (constants.%Class.4)] +// CHECK:STDOUT: %c.loc18_16.1: file.%.loc18_24 (%Class.4) = param c +// CHECK:STDOUT: @H.%c: file.%.loc18_24 (%Class.4) = bind_name c, %c.loc18_16.1 +// CHECK:STDOUT: %U.ref.loc18_32: type = name_ref U, @H.%U [symbolic = @H.%U (constants.%U)] +// CHECK:STDOUT: @H.%return: ref %U = var +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Class +// CHECK:STDOUT: generic [file.%T.loc2_13.2: type] { +// CHECK:STDOUT: %T.ref: type = name_ref T, file.%T.loc2_13.2 [symbolic = constants.%T] +// CHECK:STDOUT: %.loc3: %.2 = field_decl x, element0 [template] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%Class.2 +// CHECK:STDOUT: .x = %.loc3 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Int32() -> type = "int.make_type_32"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(%c: %Class.3) -> i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %c.ref: %Class.3 = name_ref c, %c +// CHECK:STDOUT: %x.ref: %.2 = name_ref x, @Class.%.loc3 [template = @Class.%.loc3] +// CHECK:STDOUT: %.loc15: %T = class_element_access , element0 [template = ] +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @H(%U: type, %c: file.%.loc18_24 (%Class.4)) -> %U +// CHECK:STDOUT: generic [%U: type] { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %c.ref: %Class.4 = name_ref c, %c +// CHECK:STDOUT: %x.ref: %.2 = name_ref x, @Class.%.loc3 [template = @Class.%.loc3] +// CHECK:STDOUT: %.loc26: %T = class_element_access , element0 [template = ] +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/nested_name.carbon b/toolchain/check/testdata/class/nested_name.carbon index 0a412c46f7ff5..e7f1ce1862f4c 100644 --- a/toolchain/check/testdata/class/nested_name.carbon +++ b/toolchain/check/testdata/class/nested_name.carbon @@ -33,12 +33,12 @@ fn G(o: Outer) { // CHECK:STDOUT: %.2: type = unbound_element_type %Inner, i32 [template] // CHECK:STDOUT: %.3: type = struct_type {.n: i32} [template] // CHECK:STDOUT: %.4: type = struct_type {} [template] +// CHECK:STDOUT: %.5: type = ptr_type %.4 [template] // CHECK:STDOUT: %F.type: type = fn_type @F [template] // CHECK:STDOUT: %F: %F.type = struct_value () [template] -// CHECK:STDOUT: %.5: type = ptr_type %.3 [template] +// CHECK:STDOUT: %.6: type = ptr_type %.3 [template] // CHECK:STDOUT: %G.type: type = fn_type @G [template] // CHECK:STDOUT: %G: %G.type = struct_value () [template] -// CHECK:STDOUT: %.6: type = ptr_type %.4 [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { diff --git a/toolchain/check/testdata/class/no_prelude/indirect_import_member.carbon b/toolchain/check/testdata/class/no_prelude/indirect_import_member.carbon index bc51a1ab827c1..6abf783c0d294 100644 --- a/toolchain/check/testdata/class/no_prelude/indirect_import_member.carbon +++ b/toolchain/check/testdata/class/no_prelude/indirect_import_member.carbon @@ -240,6 +240,7 @@ var x: () = D.C.F(); // CHECK:STDOUT: %.1: type = tuple_type () [template] // CHECK:STDOUT: %C: type = class_type @C [template] // CHECK:STDOUT: %.2: type = struct_type {} [template] +// CHECK:STDOUT: %.3: type = ptr_type %.2 [template] // CHECK:STDOUT: %F.type: type = fn_type @F [template] // CHECK:STDOUT: %F: %F.type = struct_value () [template] // CHECK:STDOUT: } @@ -285,6 +286,7 @@ var x: () = D.C.F(); // CHECK:STDOUT: %.1: type = tuple_type () [template] // CHECK:STDOUT: %C: type = class_type @C [template] // CHECK:STDOUT: %.2: type = struct_type {} [template] +// CHECK:STDOUT: %.3: type = ptr_type %.2 [template] // CHECK:STDOUT: %F.type: type = fn_type @F [template] // CHECK:STDOUT: %F: %F.type = struct_value () [template] // CHECK:STDOUT: } @@ -330,6 +332,7 @@ var x: () = D.C.F(); // CHECK:STDOUT: %.1: type = tuple_type () [template] // CHECK:STDOUT: %C: type = class_type @C [template] // CHECK:STDOUT: %.2: type = struct_type {} [template] +// CHECK:STDOUT: %.3: type = ptr_type %.2 [template] // CHECK:STDOUT: %F.type: type = fn_type @F [template] // CHECK:STDOUT: %F: %F.type = struct_value () [template] // CHECK:STDOUT: } @@ -375,6 +378,7 @@ var x: () = D.C.F(); // CHECK:STDOUT: %.1: type = tuple_type () [template] // CHECK:STDOUT: %D: type = class_type @D [template] // CHECK:STDOUT: %.2: type = struct_type {} [template] +// CHECK:STDOUT: %.3: type = ptr_type %.2 [template] // CHECK:STDOUT: %C: type = class_type @C [template] // CHECK:STDOUT: %F.type: type = fn_type @F [template] // CHECK:STDOUT: %F: %F.type = struct_value () [template] @@ -430,6 +434,7 @@ var x: () = D.C.F(); // CHECK:STDOUT: %.1: type = tuple_type () [template] // CHECK:STDOUT: %D: type = class_type @D [template] // CHECK:STDOUT: %.2: type = struct_type {} [template] +// CHECK:STDOUT: %.3: type = ptr_type %.2 [template] // CHECK:STDOUT: %C: type = class_type @C [template] // CHECK:STDOUT: %F.type: type = fn_type @F [template] // CHECK:STDOUT: %F: %F.type = struct_value () [template] diff --git a/toolchain/check/testdata/class/reenter_scope.carbon b/toolchain/check/testdata/class/reenter_scope.carbon index ac47f9edeacc5..105352fced7c9 100644 --- a/toolchain/check/testdata/class/reenter_scope.carbon +++ b/toolchain/check/testdata/class/reenter_scope.carbon @@ -30,6 +30,7 @@ fn Class.F() -> i32 { // CHECK:STDOUT: %G.type: type = fn_type @G [template] // CHECK:STDOUT: %G: %G.type = struct_value () [template] // CHECK:STDOUT: %.2: type = struct_type {} [template] +// CHECK:STDOUT: %.3: type = ptr_type %.2 [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { diff --git a/toolchain/check/testdata/class/reorder.carbon b/toolchain/check/testdata/class/reorder.carbon index a66f16ece7b07..cb64c5ccb63e6 100644 --- a/toolchain/check/testdata/class/reorder.carbon +++ b/toolchain/check/testdata/class/reorder.carbon @@ -30,7 +30,8 @@ class Class { // CHECK:STDOUT: %F.type: type = fn_type @F [template] // CHECK:STDOUT: %F: %F.type = struct_value () [template] // CHECK:STDOUT: %.2: type = struct_type {} [template] -// CHECK:STDOUT: %.3: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.3: type = ptr_type %.2 [template] +// CHECK:STDOUT: %.4: i32 = int_literal 1 [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { @@ -82,7 +83,7 @@ class Class { // CHECK:STDOUT: // CHECK:STDOUT: fn @F() -> i32 { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %.loc17: i32 = int_literal 1 [template = constants.%.3] +// CHECK:STDOUT: %.loc17: i32 = int_literal 1 [template = constants.%.4] // CHECK:STDOUT: return %.loc17 // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/scope.carbon b/toolchain/check/testdata/class/scope.carbon index 8bdfbaa9a07fd..5b6cd2cbbce23 100644 --- a/toolchain/check/testdata/class/scope.carbon +++ b/toolchain/check/testdata/class/scope.carbon @@ -45,6 +45,7 @@ fn Run() { // CHECK:STDOUT: %.4: i32 = int_literal 2 [template] // CHECK:STDOUT: %Run.type: type = fn_type @Run [template] // CHECK:STDOUT: %Run: %Run.type = struct_value () [template] +// CHECK:STDOUT: %.5: type = ptr_type %.2 [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { diff --git a/toolchain/check/testdata/impl/lookup/fail_todo_undefined_impl.carbon b/toolchain/check/testdata/impl/lookup/fail_todo_undefined_impl.carbon index 2ea16de994b2e..93f5aeb8a89a9 100644 --- a/toolchain/check/testdata/impl/lookup/fail_todo_undefined_impl.carbon +++ b/toolchain/check/testdata/impl/lookup/fail_todo_undefined_impl.carbon @@ -44,9 +44,10 @@ impl C as I { // CHECK:STDOUT: %Int32: %Int32.type = struct_value () [template] // CHECK:STDOUT: %F.type.2: type = fn_type @F.2 [template] // CHECK:STDOUT: %F.2: %F.type.2 = struct_value () [template] +// CHECK:STDOUT: %.6: type = ptr_type %.5 [template] // CHECK:STDOUT: %F.type.3: type = fn_type @F.3 [template] // CHECK:STDOUT: %F.3: %F.type.3 = struct_value () [template] -// CHECK:STDOUT: %.6: = interface_witness (%F.3) [template] +// CHECK:STDOUT: %.7: = interface_witness (%F.3) [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { @@ -89,7 +90,7 @@ impl C as I { // CHECK:STDOUT: // CHECK:STDOUT: impl @impl: %C as %.1 { // CHECK:STDOUT: %F.decl: %F.type.3 = fn_decl @F.3 [template = constants.%F.3] {} -// CHECK:STDOUT: %.1: = interface_witness (%F.decl) [template = constants.%.6] +// CHECK:STDOUT: %.1: = interface_witness (%F.decl) [template = constants.%.7] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %F.decl diff --git a/toolchain/check/testdata/interface/no_prelude/fail_assoc_const_not_constant.carbon b/toolchain/check/testdata/interface/no_prelude/fail_assoc_const_not_constant.carbon index 0fcda517fb2a9..5d8924241549b 100644 --- a/toolchain/check/testdata/interface/no_prelude/fail_assoc_const_not_constant.carbon +++ b/toolchain/check/testdata/interface/no_prelude/fail_assoc_const_not_constant.carbon @@ -30,6 +30,7 @@ alias UseOther = I.other; // CHECK:STDOUT: constants { // CHECK:STDOUT: %.1: type = interface_type @I [template] // CHECK:STDOUT: %Self: %.1 = bind_symbolic_name Self 0 [symbolic] +// CHECK:STDOUT: %.2: type = tuple_type () [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { diff --git a/toolchain/check/testdata/package_expr/syntax.carbon b/toolchain/check/testdata/package_expr/syntax.carbon index 88f2223155a27..9a9bec14c21b8 100644 --- a/toolchain/check/testdata/package_expr/syntax.carbon +++ b/toolchain/check/testdata/package_expr/syntax.carbon @@ -162,6 +162,7 @@ fn Main() { // CHECK:STDOUT: %.2: type = struct_type {} [template] // CHECK:STDOUT: %Main.type: type = fn_type @Main [template] // CHECK:STDOUT: %Main: %Main.type = struct_value () [template] +// CHECK:STDOUT: %.3: type = ptr_type %.2 [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file {