From 1d720dc0013ee2daf0a90237412410b9c88e6df0 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 14 Mar 2024 11:31:21 -0700 Subject: [PATCH] Basic support for looking up impl members when naming an associated entity. (#3776) When a member access names an interface member, perform impl lookup to find the impl and its corresponding member. --------- Co-authored-by: Jon Ross-Perkins --- toolchain/check/BUILD | 1 + toolchain/check/context.cpp | 1 + toolchain/check/eval.cpp | 3 +- toolchain/check/impl.cpp | 3 +- toolchain/check/member_access.cpp | 159 +++++++++++++++--- .../testdata/class/fail_incomplete.carbon | 2 +- .../check/testdata/class/fail_reorder.carbon | 2 +- toolchain/check/testdata/impl/basic.carbon | 4 +- toolchain/check/testdata/impl/empty.carbon | 4 +- ..._extend_impl.carbon => extend_impl.carbon} | 22 +-- .../impl/fail_extend_impl_scope.carbon | 4 +- .../impl/fail_extend_impl_type_as.carbon | 6 +- .../testdata/impl/fail_impl_as_scope.carbon | 4 +- .../impl/fail_impl_bad_assoc_fn.carbon | 26 +-- .../testdata/impl/fail_impl_bad_type.carbon | 4 +- .../testdata/impl/fail_redefinition.carbon | 2 +- .../impl/fail_todo_self_in_signature.carbon | 2 +- toolchain/check/testdata/impl/impl_as.carbon | 4 +- .../check/testdata/impl/impl_forall.carbon | 4 +- .../check/testdata/impl/lookup/alias.carbon | 105 ++++++++++++ .../lookup/fail_alias_impl_not_found.carbon | 84 +++++++++ .../lookup/fail_todo_undefined_impl.carbon | 98 +++++++++++ .../impl/lookup/instance_method.carbon | 109 ++++++++++++ .../check/testdata/impl/redeclaration.carbon | 4 +- .../interface/fail_lookup_undefined.carbon | 4 +- toolchain/diagnostics/diagnostic_kind.def | 3 + toolchain/lower/file_context.cpp | 6 + toolchain/lower/handle.cpp | 10 ++ .../lower/testdata/impl/assoc_fn_alias.carbon | 42 +++++ .../lower/testdata/impl/extend_impl.carbon | 44 +++++ .../testdata/impl/instance_method.carbon | 38 +++++ toolchain/sem_ir/file.cpp | 3 + toolchain/sem_ir/inst_kind.def | 1 + toolchain/sem_ir/typed_insts.h | 24 ++- 34 files changed, 749 insertions(+), 83 deletions(-) rename toolchain/check/testdata/impl/{fail_todo_extend_impl.carbon => extend_impl.carbon} (77%) create mode 100644 toolchain/check/testdata/impl/lookup/alias.carbon create mode 100644 toolchain/check/testdata/impl/lookup/fail_alias_impl_not_found.carbon create mode 100644 toolchain/check/testdata/impl/lookup/fail_todo_undefined_impl.carbon create mode 100644 toolchain/check/testdata/impl/lookup/instance_method.carbon create mode 100644 toolchain/lower/testdata/impl/assoc_fn_alias.carbon create mode 100644 toolchain/lower/testdata/impl/extend_impl.carbon create mode 100644 toolchain/lower/testdata/impl/instance_method.carbon diff --git a/toolchain/check/BUILD b/toolchain/check/BUILD index b20cdcc236eb..33a9e7617b60 100644 --- a/toolchain/check/BUILD +++ b/toolchain/check/BUILD @@ -199,6 +199,7 @@ cc_library( deps = [ ":context", "//common:check", + "//toolchain/diagnostics:diagnostic_emitter", "//toolchain/sem_ir:file", "//toolchain/sem_ir:ids", "//toolchain/sem_ir:inst", diff --git a/toolchain/check/context.cpp b/toolchain/check/context.cpp index 48acab570182..7a9c85d1e051 100644 --- a/toolchain/check/context.cpp +++ b/toolchain/check/context.cpp @@ -842,6 +842,7 @@ class TypeCompleter { case SemIR::InitializeFrom::Kind: case SemIR::InterfaceDecl::Kind: case SemIR::InterfaceWitness::Kind: + case SemIR::InterfaceWitnessAccess::Kind: case SemIR::IntLiteral::Kind: case SemIR::NameRef::Kind: case SemIR::Namespace::Kind: diff --git a/toolchain/check/eval.cpp b/toolchain/check/eval.cpp index f62b8b1dc7ea..533441f82491 100644 --- a/toolchain/check/eval.cpp +++ b/toolchain/check/eval.cpp @@ -330,7 +330,7 @@ auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst) &SemIR::BoundMethod::function_id); case SemIR::InterfaceWitness::Kind: return RebuildIfFieldsAreConstant(context, inst, - &SemIR::InterfaceWitness::table_id); + &SemIR::InterfaceWitness::elements_id); case SemIR::PointerType::Kind: return RebuildIfFieldsAreConstant(context, inst, &SemIR::PointerType::pointee_id); @@ -417,6 +417,7 @@ auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst) // The elements of a constant aggregate can be accessed. case SemIR::ClassElementAccess::Kind: + case SemIR::InterfaceWitnessAccess::Kind: case SemIR::StructAccess::Kind: case SemIR::TupleAccess::Kind: return PerformAggregateAccess(context, inst); diff --git a/toolchain/check/impl.cpp b/toolchain/check/impl.cpp index 62e40ad5842f..0ceff862eb0a 100644 --- a/toolchain/check/impl.cpp +++ b/toolchain/check/impl.cpp @@ -114,8 +114,7 @@ static auto BuildInterfaceWitness( auto table_id = context.inst_blocks().Add(table); return context.AddInst(SemIR::InterfaceWitness{ - context.GetBuiltinType(SemIR::BuiltinKind::WitnessType), interface_id, - table_id}); + context.GetBuiltinType(SemIR::BuiltinKind::WitnessType), table_id}); } auto BuildImplWitness(Context& context, SemIR::ImplId impl_id) diff --git a/toolchain/check/member_access.cpp b/toolchain/check/member_access.cpp index c4cc01e1d118..94325a4bcc93 100644 --- a/toolchain/check/member_access.cpp +++ b/toolchain/check/member_access.cpp @@ -5,6 +5,7 @@ #include "llvm/ADT/STLExtras.h" #include "toolchain/check/context.h" #include "toolchain/check/convert.h" +#include "toolchain/diagnostics/diagnostic_emitter.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/typed_insts.h" @@ -12,13 +13,9 @@ namespace Carbon::Check { // Returns the name 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, SemIR::InstId base_id) +static auto GetAsNameScope(Context& context, Parse::NodeId node_id, + SemIR::ConstantId base_const_id) -> std::optional { - auto base_const_id = context.constant_values().Get(base_id); - if (!base_const_id.is_constant()) { - // A name scope must be a constant. - return std::nullopt; - } auto base = context.insts().Get(base_const_id.inst_id()); if (auto base_as_namespace = base.TryAs()) { return base_as_namespace->name_scope_id; @@ -31,9 +28,9 @@ static auto GetAsNameScope(Context& context, SemIR::InstId base_id) CARBON_DIAGNOSTIC(QualifiedExprInIncompleteClassScope, Error, "Member access into incomplete class `{0}`.", std::string); - auto builder = - context.emitter().Build(base_id, QualifiedExprInIncompleteClassScope, - context.sem_ir().StringifyTypeExpr(base_id)); + auto builder = context.emitter().Build( + node_id, QualifiedExprInIncompleteClassScope, + context.sem_ir().StringifyTypeExpr(base_const_id.inst_id())); context.NoteIncompleteClass(base_as_class->class_id, builder); builder.Emit(); } @@ -47,8 +44,8 @@ static auto GetAsNameScope(Context& context, SemIR::InstId base_id) "Member access into undefined interface `{0}`.", std::string); auto builder = context.emitter().Build( - base_id, QualifiedExprInUndefinedInterfaceScope, - context.sem_ir().StringifyTypeExpr(base_id)); + node_id, QualifiedExprInUndefinedInterfaceScope, + context.sem_ir().StringifyTypeExpr(base_const_id.inst_id())); context.NoteUndefinedInterface(base_as_interface->interface_id, builder); builder.Emit(); } @@ -93,12 +90,111 @@ static auto IsInstanceMethod(const SemIR::File& sem_ir, return false; } -// Performs a member name lookup into the specified scope. If the scope is -// invalid, assume an error has already been diagnosed, and return BuiltinError. +// 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_id = context.name_scopes().GetInstIdIfValid(name_scope_id); + if (!inst_id.is_valid()) { + return false; + } + + auto inst = context.insts().Get(inst_id); + if (inst.Is()) { + // Don't perform impl lookup if an associated entity is named as a member of + // a facet type. + return false; + } + if (inst.Is()) { + // Don't perform impl lookup if an associated entity is named as a namespace + // member. + // TODO: This case is not yet listed in the design. + return false; + } + // Any other kind of scope is assumed to be a type that implements the + // interface containing the associated entity, and impl lookup is performed. + return true; +} + +// Given a type and an interface, searches for an impl that describes how that +// type implements that interface, and returns the corresponding witness. +// Returns an invalid InstId if no matching impl is found. +static auto LookupInterfaceWitness(Context& context, + SemIR::ConstantId type_const_id, + SemIR::InterfaceId interface_id) + -> SemIR::InstId { + // TODO: Add a better impl lookup system. At the very least, we should only be + // considering impls that are for the same interface we're querying. We can + // also skip impls that mention any types that aren't part of our impl query. + for (const auto& impl : context.impls().array_ref()) { + if (context.types().GetInstId(impl.self_id) != type_const_id.inst_id()) { + continue; + } + auto interface_type = + context.types().TryGetAs(impl.constraint_id); + if (!interface_type) { + // TODO: An impl of a constraint type should be treated as implementing + // the constraint's interfaces. + continue; + } + if (interface_type->interface_id != interface_id) { + continue; + } + // TODO: Diagnose if the impl isn't defined yet? + return impl.witness_id; + } + return SemIR::InstId::Invalid; +} + +// Performs impl lookup for a member name expression. This finds the relevant +// impl witness and extracts the corresponding impl member. +static auto PerformImplLookup(Context& context, SemIR::ConstantId type_const_id, + SemIR::AssociatedEntityType assoc_type, + SemIR::InstId member_id) -> SemIR::InstId { + auto witness_id = + LookupInterfaceWitness(context, type_const_id, assoc_type.interface_id); + if (!witness_id.is_valid()) { + CARBON_DIAGNOSTIC(MissingImplInMemberAccess, Error, + "Cannot access member of interface {0} in type {1} " + "that does not implement that interface.", + SemIR::NameId, std::string); + context.emitter().Emit( + member_id, MissingImplInMemberAccess, + context.interfaces().Get(assoc_type.interface_id).name_id, + context.sem_ir().StringifyTypeExpr(type_const_id.inst_id())); + return SemIR::InstId::BuiltinError; + } + + auto const_id = context.constant_values().Get(member_id); + if (!const_id.is_constant()) { + if (const_id != SemIR::ConstantId::Error) { + context.TODO(context.insts().GetNodeId(member_id), + "non-constant associated entity"); + } + return SemIR::InstId::BuiltinError; + } + + auto assoc_entity = + context.insts().TryGetAs(const_id.inst_id()); + if (!assoc_entity) { + context.TODO(context.insts().GetNodeId(member_id), + "unexpected value for associated entity"); + return SemIR::InstId::BuiltinError; + } + + // TODO: Substitute interface arguments and `Self` into `entity_type_id`. + return context.AddInst(SemIR::InterfaceWitnessAccess{ + assoc_type.entity_type_id, witness_id, assoc_entity->index}); +} + +// Performs a member name lookup into the specified scope, including performing +// impl lookup if necessary. If the scope is invalid, assume an error has +// already been diagnosed, and return BuiltinError. static auto LookupMemberNameInScope(Context& context, Parse::MemberAccessExprId 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( @@ -107,8 +203,24 @@ static auto LookupMemberNameInScope(Context& context, auto inst = context.insts().Get(inst_id); // TODO: Use a different kind of instruction that also references the // `base_id` so that `SemIR` consumers can find it. - return context.AddInst( + auto member_id = context.AddInst( {node_id, SemIR::NameRef{inst.type_id(), name_id, inst_id}}); + + // If member name lookup finds an associated entity name, and the scope is not + // a facet type, perform impl lookup. + // + // TODO: We need to do this as part of searching extended scopes, because a + // lookup that finds an associated entity and also finds the corresponding + // 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)) { + member_id = PerformImplLookup(context, name_scope_const_id, *assoc_type, + member_id); + } + } + + return member_id; } // Performs the instance binding step in member access. If the found member is a @@ -180,9 +292,12 @@ auto PerformMemberAccess(Context& context, Parse::MemberAccessExprId node_id, -> SemIR::InstId { // If the base is a name scope, such as a class or namespace, perform lookup // into that scope. - if (auto name_scope_id = GetAsNameScope(context, base_id)) { - return LookupMemberNameInScope(context, node_id, base_id, name_id, - *name_scope_id); + 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)) { + return LookupMemberNameInScope(context, node_id, base_id, name_id, + base_const_id, *name_scope_id); + } } // If the base isn't a scope, it must have a complete type. @@ -200,14 +315,14 @@ auto PerformMemberAccess(Context& context, Parse::MemberAccessExprId node_id, // Materialize a temporary for the base expression if necessary. base_id = ConvertToValueOrRefExpr(context, base_id); base_type_id = context.insts().Get(base_id).type_id(); - auto base_type_inst_id = context.types().GetInstId(base_type_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, base_type_inst_id); + auto name_scope_id = GetAsNameScope(context, node_id, base_type_const_id); if (!name_scope_id) { // The base type is not a name scope. Try some fallback options. - if (auto struct_type = - context.insts().TryGetAs(base_type_inst_id)) { + if (auto struct_type = context.insts().TryGetAs( + base_type_const_id.inst_id())) { // TODO: Do we need to optimize this with a lookup table for O(1)? for (auto [i, ref_id] : llvm::enumerate(context.inst_blocks().Get(struct_type->fields_id))) { @@ -239,7 +354,7 @@ auto PerformMemberAccess(Context& context, Parse::MemberAccessExprId node_id, // Perform lookup into the base type. auto member_id = LookupMemberNameInScope(context, node_id, base_id, name_id, - *name_scope_id); + base_type_const_id, *name_scope_id); // Perform instance binding if we found an instance member. member_id = PerformInstanceBinding(context, node_id, base_id, member_id); diff --git a/toolchain/check/testdata/class/fail_incomplete.carbon b/toolchain/check/testdata/class/fail_incomplete.carbon index af6741c5a618..1a4e7fcb6527 100644 --- a/toolchain/check/testdata/class/fail_incomplete.carbon +++ b/toolchain/check/testdata/class/fail_incomplete.carbon @@ -17,7 +17,7 @@ fn Class.Function() {} fn CallClassFunction() { // CHECK:STDERR: fail_incomplete.carbon:[[@LINE+6]]:3: ERROR: Member access into incomplete class `Class`. // CHECK:STDERR: Class.Function(); - // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_incomplete.carbon:[[@LINE-14]]:1: Class was forward declared here. // CHECK:STDERR: class Class; // CHECK:STDERR: ^~~~~~~~~~~~ diff --git a/toolchain/check/testdata/class/fail_reorder.carbon b/toolchain/check/testdata/class/fail_reorder.carbon index ebf944b1068c..4df0d6c6b9a4 100644 --- a/toolchain/check/testdata/class/fail_reorder.carbon +++ b/toolchain/check/testdata/class/fail_reorder.carbon @@ -10,7 +10,7 @@ class Class { // later. // CHECK:STDERR: fail_reorder.carbon:[[@LINE+9]]:12: ERROR: Member access into incomplete class `Class`. // CHECK:STDERR: return Class.F(); - // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_reorder.carbon:[[@LINE-7]]:1: Class is incomplete within its definition. // CHECK:STDERR: class Class { // CHECK:STDERR: ^~~~~~~~~~~~~ diff --git a/toolchain/check/testdata/impl/basic.carbon b/toolchain/check/testdata/impl/basic.carbon index 0cd275d655d6..72776bc07eaf 100644 --- a/toolchain/check/testdata/impl/basic.carbon +++ b/toolchain/check/testdata/impl/basic.carbon @@ -18,7 +18,7 @@ impl i32 as Simple { // CHECK:STDOUT: %.1: type = interface_type @Simple [template] // CHECK:STDOUT: %.2: type = assoc_entity_type @Simple, [template] // CHECK:STDOUT: %.3: in Simple> = assoc_entity element0, @Simple.%F [template] -// CHECK:STDOUT: %.4: = interface_witness @Simple, (@impl.%F) [template] +// CHECK:STDOUT: %.4: = interface_witness (@impl.%F) [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -44,7 +44,7 @@ impl i32 as Simple { // CHECK:STDOUT: // CHECK:STDOUT: impl @impl: i32 as Simple { // CHECK:STDOUT: %F: = fn_decl @F.2 [template] {} -// CHECK:STDOUT: %.1: = interface_witness @Simple, (%F) [template = constants.%.4] +// CHECK:STDOUT: %.1: = interface_witness (%F) [template = constants.%.4] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %F diff --git a/toolchain/check/testdata/impl/empty.carbon b/toolchain/check/testdata/impl/empty.carbon index 6020762c4c1e..4035e4422bcf 100644 --- a/toolchain/check/testdata/impl/empty.carbon +++ b/toolchain/check/testdata/impl/empty.carbon @@ -14,7 +14,7 @@ impl i32 as Empty { // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %.1: type = interface_type @Empty [template] -// CHECK:STDOUT: %.2: = interface_witness @Empty, () [template] +// CHECK:STDOUT: %.2: = interface_witness () [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -36,7 +36,7 @@ impl i32 as Empty { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @impl: i32 as Empty { -// CHECK:STDOUT: %.1: = interface_witness @Empty, () [template = constants.%.2] +// CHECK:STDOUT: %.1: = interface_witness () [template = constants.%.2] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %.1 diff --git a/toolchain/check/testdata/impl/fail_todo_extend_impl.carbon b/toolchain/check/testdata/impl/extend_impl.carbon similarity index 77% rename from toolchain/check/testdata/impl/fail_todo_extend_impl.carbon rename to toolchain/check/testdata/impl/extend_impl.carbon index 0209380808b9..24a094f9df09 100644 --- a/toolchain/check/testdata/impl/fail_todo_extend_impl.carbon +++ b/toolchain/check/testdata/impl/extend_impl.carbon @@ -15,26 +15,18 @@ class C { } fn G(c: C) { - // TODO: These should do impl lookup. Because that's not implemented yet, they - // refer to the interface members rather than to the impl members. - // CHECK:STDERR: fail_todo_extend_impl.carbon:[[@LINE+3]]:3: ERROR: Value of type ` in HasF>` is not callable. - // CHECK:STDERR: C.F(); - // CHECK:STDERR: ^~~~ C.F(); - // CHECK:STDERR: fail_todo_extend_impl.carbon:[[@LINE+3]]:3: ERROR: Value of type ` in HasF>` is not callable. - // CHECK:STDERR: c.F(); - // CHECK:STDERR: ^~~~ c.F(); } -// CHECK:STDOUT: --- fail_todo_extend_impl.carbon +// CHECK:STDOUT: --- extend_impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %.1: type = interface_type @HasF [template] // CHECK:STDOUT: %.2: type = assoc_entity_type @HasF, [template] // CHECK:STDOUT: %.3: in HasF> = assoc_entity element0, @HasF.%F [template] // CHECK:STDOUT: %C: type = class_type @C [template] -// CHECK:STDOUT: %.4: = interface_witness @HasF, (@impl.%F) [template] +// CHECK:STDOUT: %.4: = interface_witness (@impl.%F) [template] // CHECK:STDOUT: %.5: type = struct_type {} [template] // CHECK:STDOUT: %.6: type = tuple_type () [template] // CHECK:STDOUT: %.7: type = ptr_type {} [template] @@ -68,7 +60,7 @@ fn G(c: C) { // CHECK:STDOUT: // CHECK:STDOUT: impl @impl: C as HasF { // CHECK:STDOUT: %F: = fn_decl @F.2 [template] {} -// CHECK:STDOUT: %.1: = interface_witness @HasF, (%F) [template = constants.%.4] +// CHECK:STDOUT: %.1: = interface_witness (%F) [template = constants.%.4] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %F @@ -95,9 +87,13 @@ fn G(c: C) { // CHECK:STDOUT: fn @G(%c: C) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [template = constants.%C] -// CHECK:STDOUT: %F.ref.loc23: in HasF> = name_ref F, @HasF.%.loc8 [template = constants.%.3] +// CHECK:STDOUT: %F.ref.loc18: in HasF> = name_ref F, @HasF.%.loc8 [template = constants.%.3] +// CHECK:STDOUT: %.1: = interface_witness_access @impl.%.1, element0 [template = @impl.%F] +// CHECK:STDOUT: %.loc18: init () = call %.1() // CHECK:STDOUT: %c.ref: C = name_ref c, %c -// CHECK:STDOUT: %F.ref.loc27: in HasF> = name_ref F, @HasF.%.loc8 [template = constants.%.3] +// CHECK:STDOUT: %F.ref.loc19: in HasF> = name_ref F, @HasF.%.loc8 [template = constants.%.3] +// CHECK:STDOUT: %.2: = interface_witness_access @impl.%.1, element0 [template = @impl.%F] +// CHECK:STDOUT: %.loc19: init () = call %.2() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/impl/fail_extend_impl_scope.carbon b/toolchain/check/testdata/impl/fail_extend_impl_scope.carbon index 023c4a38b913..f01f9d89bd49 100644 --- a/toolchain/check/testdata/impl/fail_extend_impl_scope.carbon +++ b/toolchain/check/testdata/impl/fail_extend_impl_scope.carbon @@ -15,7 +15,7 @@ extend impl i32 as I {} // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %.1: type = interface_type @I [template] -// CHECK:STDOUT: %.2: = interface_witness @I, () [template] +// CHECK:STDOUT: %.2: = interface_witness () [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -37,7 +37,7 @@ extend impl i32 as I {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @impl: i32 as I { -// CHECK:STDOUT: %.1: = interface_witness @I, () [template = constants.%.2] +// CHECK:STDOUT: %.1: = interface_witness () [template = constants.%.2] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %.1 diff --git a/toolchain/check/testdata/impl/fail_extend_impl_type_as.carbon b/toolchain/check/testdata/impl/fail_extend_impl_type_as.carbon index ea5f7cd054e8..718f4bbbff4f 100644 --- a/toolchain/check/testdata/impl/fail_extend_impl_type_as.carbon +++ b/toolchain/check/testdata/impl/fail_extend_impl_type_as.carbon @@ -38,7 +38,7 @@ class E { // CHECK:STDOUT: constants { // CHECK:STDOUT: %.1: type = interface_type @I [template] // CHECK:STDOUT: %C: type = class_type @C [template] -// CHECK:STDOUT: %.2: = interface_witness @I, () [template] +// CHECK:STDOUT: %.2: = interface_witness () [template] // CHECK:STDOUT: %.3: type = struct_type {} [template] // CHECK:STDOUT: %D: type = class_type @D [template] // CHECK:STDOUT: %E: type = class_type @E [template] @@ -66,7 +66,7 @@ class E { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @impl.1: i32 as I { -// CHECK:STDOUT: %.1: = interface_witness @I, () [template = constants.%.2] +// CHECK:STDOUT: %.1: = interface_witness () [template = constants.%.2] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %.1 @@ -75,7 +75,7 @@ class E { // CHECK:STDOUT: impl @impl.2: D as I; // CHECK:STDOUT: // CHECK:STDOUT: impl @impl.3: E as I { -// CHECK:STDOUT: %.1: = interface_witness @I, () [template = constants.%.2] +// CHECK:STDOUT: %.1: = interface_witness () [template = constants.%.2] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %.1 diff --git a/toolchain/check/testdata/impl/fail_impl_as_scope.carbon b/toolchain/check/testdata/impl/fail_impl_as_scope.carbon index 4f860beb8f80..d15a4df7007c 100644 --- a/toolchain/check/testdata/impl/fail_impl_as_scope.carbon +++ b/toolchain/check/testdata/impl/fail_impl_as_scope.carbon @@ -21,7 +21,7 @@ impl as Simple { // CHECK:STDOUT: %.1: type = interface_type @Simple [template] // CHECK:STDOUT: %.2: type = assoc_entity_type @Simple, [template] // CHECK:STDOUT: %.3: in Simple> = assoc_entity element0, @Simple.%F [template] -// CHECK:STDOUT: %.4: = interface_witness @Simple, (@impl.%F) [template] +// CHECK:STDOUT: %.4: = interface_witness (@impl.%F) [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -47,7 +47,7 @@ impl as Simple { // CHECK:STDOUT: // CHECK:STDOUT: impl @impl: as Simple { // CHECK:STDOUT: %F: = fn_decl @F.2 [template] {} -// CHECK:STDOUT: %.1: = interface_witness @Simple, (%F) [template = constants.%.4] +// CHECK:STDOUT: %.1: = interface_witness (%F) [template = constants.%.4] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %F diff --git a/toolchain/check/testdata/impl/fail_impl_bad_assoc_fn.carbon b/toolchain/check/testdata/impl/fail_impl_bad_assoc_fn.carbon index a0341fcad5bd..c53b0be38829 100644 --- a/toolchain/check/testdata/impl/fail_impl_bad_assoc_fn.carbon +++ b/toolchain/check/testdata/impl/fail_impl_bad_assoc_fn.carbon @@ -259,7 +259,7 @@ class FDifferentParamName { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @impl.1: NoF as I { -// CHECK:STDOUT: %.1: = interface_witness @I, () [template = ] +// CHECK:STDOUT: %.1: = interface_witness () [template = ] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %.1 @@ -267,7 +267,7 @@ class FDifferentParamName { // CHECK:STDOUT: // CHECK:STDOUT: impl @impl.2: FNotFunction as I { // CHECK:STDOUT: %F.decl: type = class_decl @F.13 [template = constants.%F] {} -// CHECK:STDOUT: %.1: = interface_witness @I, () [template = ] +// CHECK:STDOUT: %.1: = interface_witness () [template = ] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %F.decl @@ -277,7 +277,7 @@ class FDifferentParamName { // CHECK:STDOUT: impl @impl.3: FAlias as I { // CHECK:STDOUT: %PossiblyF.ref: = name_ref PossiblyF, file.%PossiblyF [template = file.%PossiblyF] // CHECK:STDOUT: %F: = bind_alias F, file.%PossiblyF [template = file.%PossiblyF] -// CHECK:STDOUT: %.1: = interface_witness @I, () [template = ] +// CHECK:STDOUT: %.1: = interface_witness () [template = ] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %F @@ -289,7 +289,7 @@ class FDifferentParamName { // CHECK:STDOUT: %b.loc54_10.1: bool = param b // CHECK:STDOUT: %b.loc54_10.2: bool = bind_name b, %b.loc54_10.1 // CHECK:STDOUT: } -// CHECK:STDOUT: %.1: = interface_witness @I, () [template = ] +// CHECK:STDOUT: %.1: = interface_witness () [template = ] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %F @@ -302,7 +302,7 @@ class FDifferentParamName { // CHECK:STDOUT: %self.loc66_10.1: FExtraImplicitParam = param self // CHECK:STDOUT: %self.loc66_10.2: FExtraImplicitParam = bind_name self, %self.loc66_10.1 // CHECK:STDOUT: } -// CHECK:STDOUT: %.1: = interface_witness @I, () [template = ] +// CHECK:STDOUT: %.1: = interface_witness () [template = ] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %F @@ -313,7 +313,7 @@ class FDifferentParamName { // CHECK:STDOUT: %F: = fn_decl @F.4 [template] { // CHECK:STDOUT: %return.var: ref bool = var // CHECK:STDOUT: } -// CHECK:STDOUT: %.1: = interface_witness @I, () [template = ] +// CHECK:STDOUT: %.1: = interface_witness () [template = ] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %F @@ -326,7 +326,7 @@ class FDifferentParamName { // CHECK:STDOUT: %self.loc93_10.2: bool = bind_name self, %self.loc93_10.1 // CHECK:STDOUT: %return.var: ref bool = var // CHECK:STDOUT: } -// CHECK:STDOUT: %.1: = interface_witness @J, () [template = ] +// CHECK:STDOUT: %.1: = interface_witness () [template = ] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %F @@ -339,7 +339,7 @@ class FDifferentParamName { // CHECK:STDOUT: %b.loc105_10.2: bool = bind_name b, %b.loc105_10.1 // CHECK:STDOUT: %return.var: ref bool = var // CHECK:STDOUT: } -// CHECK:STDOUT: %.1: = interface_witness @J, () [template = ] +// CHECK:STDOUT: %.1: = interface_witness () [template = ] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %F @@ -353,7 +353,7 @@ class FDifferentParamName { // CHECK:STDOUT: %b.loc117_22.1: bool = param b // CHECK:STDOUT: %b.loc117_22.2: bool = bind_name b, %b.loc117_22.1 // CHECK:STDOUT: } -// CHECK:STDOUT: %.1: = interface_witness @J, () [template = ] +// CHECK:STDOUT: %.1: = interface_witness () [template = ] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %F @@ -369,7 +369,7 @@ class FDifferentParamName { // CHECK:STDOUT: %b.loc129_22.2: FDifferentParamType = bind_name b, %b.loc129_22.1 // CHECK:STDOUT: %return.var: ref bool = var // CHECK:STDOUT: } -// CHECK:STDOUT: %.1: = interface_witness @J, () [template = ] +// CHECK:STDOUT: %.1: = interface_witness () [template = ] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %F @@ -385,7 +385,7 @@ class FDifferentParamName { // CHECK:STDOUT: %b.loc141_22.2: bool = bind_name b, %b.loc141_22.1 // CHECK:STDOUT: %return.var: ref bool = var // CHECK:STDOUT: } -// CHECK:STDOUT: %.1: = interface_witness @J, () [template = ] +// CHECK:STDOUT: %.1: = interface_witness () [template = ] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %F @@ -401,7 +401,7 @@ class FDifferentParamName { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%FDifferentReturnType [template = constants.%FDifferentReturnType] // CHECK:STDOUT: %return.var: ref FDifferentReturnType = var // CHECK:STDOUT: } -// CHECK:STDOUT: %.1: = interface_witness @J, () [template = ] +// CHECK:STDOUT: %.1: = interface_witness () [template = ] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %F @@ -416,7 +416,7 @@ class FDifferentParamName { // CHECK:STDOUT: %not_b.loc166_22.2: bool = bind_name not_b, %not_b.loc166_22.1 // CHECK:STDOUT: %return.var: ref bool = var // CHECK:STDOUT: } -// CHECK:STDOUT: %.1: = interface_witness @J, () [template = ] +// CHECK:STDOUT: %.1: = interface_witness () [template = ] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %F diff --git a/toolchain/check/testdata/impl/fail_impl_bad_type.carbon b/toolchain/check/testdata/impl/fail_impl_bad_type.carbon index e16d9a3532f2..4fd4c8b5653c 100644 --- a/toolchain/check/testdata/impl/fail_impl_bad_type.carbon +++ b/toolchain/check/testdata/impl/fail_impl_bad_type.carbon @@ -16,7 +16,7 @@ impl true as I {} // CHECK:STDOUT: constants { // CHECK:STDOUT: %.1: type = interface_type @I [template] // CHECK:STDOUT: %.2: bool = bool_literal true [template] -// CHECK:STDOUT: %.3: = interface_witness @I, () [template] +// CHECK:STDOUT: %.3: = interface_witness () [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -39,7 +39,7 @@ impl true as I {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @impl: as I { -// CHECK:STDOUT: %.1: = interface_witness @I, () [template = constants.%.3] +// CHECK:STDOUT: %.1: = interface_witness () [template = constants.%.3] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %.1 diff --git a/toolchain/check/testdata/impl/fail_redefinition.carbon b/toolchain/check/testdata/impl/fail_redefinition.carbon index 5ad25e9e2b5b..ced8f75d5a48 100644 --- a/toolchain/check/testdata/impl/fail_redefinition.carbon +++ b/toolchain/check/testdata/impl/fail_redefinition.carbon @@ -20,7 +20,7 @@ impl i32 as I {} // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %.1: type = interface_type @I [template] -// CHECK:STDOUT: %.2: = interface_witness @I, () [template] +// CHECK:STDOUT: %.2: = interface_witness () [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { diff --git a/toolchain/check/testdata/impl/fail_todo_self_in_signature.carbon b/toolchain/check/testdata/impl/fail_todo_self_in_signature.carbon index 472e3a10cc45..87dad9eb4793 100644 --- a/toolchain/check/testdata/impl/fail_todo_self_in_signature.carbon +++ b/toolchain/check/testdata/impl/fail_todo_self_in_signature.carbon @@ -84,7 +84,7 @@ impl C as UseSelf { // CHECK:STDOUT: %C.ref.loc21_26: type = name_ref C, file.%C.decl [template = constants.%C] // CHECK:STDOUT: %return.var: ref C = var // CHECK:STDOUT: } -// CHECK:STDOUT: %.1: = interface_witness @UseSelf, () [template = ] +// CHECK:STDOUT: %.1: = interface_witness () [template = ] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %F diff --git a/toolchain/check/testdata/impl/impl_as.carbon b/toolchain/check/testdata/impl/impl_as.carbon index d5c30e43acf2..44cfdd28d1c5 100644 --- a/toolchain/check/testdata/impl/impl_as.carbon +++ b/toolchain/check/testdata/impl/impl_as.carbon @@ -21,7 +21,7 @@ class C { // CHECK:STDOUT: %.2: type = assoc_entity_type @Simple, [template] // CHECK:STDOUT: %.3: in Simple> = assoc_entity element0, @Simple.%F [template] // CHECK:STDOUT: %C: type = class_type @C [template] -// CHECK:STDOUT: %.4: = interface_witness @Simple, (@impl.%F) [template] +// CHECK:STDOUT: %.4: = interface_witness (@impl.%F) [template] // CHECK:STDOUT: %.5: type = struct_type {} [template] // CHECK:STDOUT: } // CHECK:STDOUT: @@ -47,7 +47,7 @@ class C { // CHECK:STDOUT: // CHECK:STDOUT: impl @impl: C as Simple { // CHECK:STDOUT: %F: = fn_decl @F.2 [template] {} -// CHECK:STDOUT: %.1: = interface_witness @Simple, (%F) [template = constants.%.4] +// CHECK:STDOUT: %.1: = interface_witness (%F) [template = constants.%.4] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %F diff --git a/toolchain/check/testdata/impl/impl_forall.carbon b/toolchain/check/testdata/impl/impl_forall.carbon index 5e07bd36dcdb..17201f991b12 100644 --- a/toolchain/check/testdata/impl/impl_forall.carbon +++ b/toolchain/check/testdata/impl/impl_forall.carbon @@ -18,7 +18,7 @@ impl forall [T:! type] T as Simple { // CHECK:STDOUT: %.1: type = interface_type @Simple [template] // CHECK:STDOUT: %.2: type = assoc_entity_type @Simple, [template] // CHECK:STDOUT: %.3: in Simple> = assoc_entity element0, @Simple.%F [template] -// CHECK:STDOUT: %.4: = interface_witness @Simple, (@impl.%F) [template] +// CHECK:STDOUT: %.4: = interface_witness (@impl.%F) [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -47,7 +47,7 @@ impl forall [T:! type] T as Simple { // CHECK:STDOUT: // CHECK:STDOUT: impl @impl: T as Simple { // CHECK:STDOUT: %F: = fn_decl @F.2 [template] {} -// CHECK:STDOUT: %.1: = interface_witness @Simple, (%F) [template = constants.%.4] +// CHECK:STDOUT: %.1: = interface_witness (%F) [template = constants.%.4] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .F = %F diff --git a/toolchain/check/testdata/impl/lookup/alias.carbon b/toolchain/check/testdata/impl/lookup/alias.carbon new file mode 100644 index 000000000000..1169f3958ca8 --- /dev/null +++ b/toolchain/check/testdata/impl/lookup/alias.carbon @@ -0,0 +1,105 @@ +// 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 + +interface HasF { + fn F(); +} + +class C { + alias G = HasF.F; +} + +impl C as HasF { + fn F() {} +} + +fn G(c: C) { + C.G(); + c.G(); +} + +// CHECK:STDOUT: --- alias.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: type = interface_type @HasF [template] +// CHECK:STDOUT: %.2: type = assoc_entity_type @HasF, [template] +// CHECK:STDOUT: %.3: in HasF> = assoc_entity element0, @HasF.%F [template] +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.4: type = struct_type {} [template] +// CHECK:STDOUT: %.5: = interface_witness (@impl.%F) [template] +// CHECK:STDOUT: %.6: type = tuple_type () [template] +// CHECK:STDOUT: %.7: type = ptr_type {} [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .HasF = %HasF.decl +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: .G = %G +// CHECK:STDOUT: } +// CHECK:STDOUT: %HasF.decl: type = interface_decl @HasF [template = constants.%.1] {} +// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} +// CHECK:STDOUT: impl_decl @impl { +// CHECK:STDOUT: %C.ref.loc15: type = name_ref C, %C.decl [template = constants.%C] +// CHECK:STDOUT: %HasF.ref: type = name_ref HasF, %HasF.decl [template = constants.%.1] +// CHECK:STDOUT: } +// CHECK:STDOUT: %G: = fn_decl @G [template] { +// CHECK:STDOUT: %C.ref.loc19: type = name_ref C, %C.decl [template = constants.%C] +// CHECK:STDOUT: %c.loc19_6.1: C = param c +// CHECK:STDOUT: @G.%c: C = bind_name c, %c.loc19_6.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @HasF { +// CHECK:STDOUT: %Self: HasF = bind_symbolic_name Self [symbolic] +// CHECK:STDOUT: %F: = fn_decl @F.1 [template] {} +// CHECK:STDOUT: %.loc8: in HasF> = assoc_entity element0, %F [template = constants.%.3] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .F = %.loc8 +// CHECK:STDOUT: witness = (%F) +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: impl @impl: C as HasF { +// CHECK:STDOUT: %F: = fn_decl @F.2 [template] {} +// CHECK:STDOUT: %.1: = interface_witness (%F) [template = constants.%.5] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: witness = %.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %HasF.ref: type = name_ref HasF, file.%HasF.decl [template = constants.%.1] +// CHECK:STDOUT: %F.ref: in HasF> = name_ref F, @HasF.%.loc8 [template = constants.%.3] +// CHECK:STDOUT: %G: in HasF> = bind_alias G, @HasF.%.loc8 [template = constants.%.3] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: .G = %G +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F.1(); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F.2() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G(%c: C) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [template = constants.%C] +// CHECK:STDOUT: %G.ref.loc20: in HasF> = name_ref G, @C.%G [template = constants.%.3] +// CHECK:STDOUT: %.1: = interface_witness_access @impl.%.1, element0 [template = @impl.%F] +// CHECK:STDOUT: %.loc20: init () = call %.1() +// CHECK:STDOUT: %c.ref: C = name_ref c, %c +// CHECK:STDOUT: %G.ref.loc21: in HasF> = name_ref G, @C.%G [template = constants.%.3] +// CHECK:STDOUT: %.2: = interface_witness_access @impl.%.1, element0 [template = @impl.%F] +// CHECK:STDOUT: %.loc21: init () = call %.2() +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/impl/lookup/fail_alias_impl_not_found.carbon b/toolchain/check/testdata/impl/lookup/fail_alias_impl_not_found.carbon new file mode 100644 index 000000000000..396a1d5f9e2a --- /dev/null +++ b/toolchain/check/testdata/impl/lookup/fail_alias_impl_not_found.carbon @@ -0,0 +1,84 @@ +// 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 + +interface I { + fn F(); +} + +class C { + alias F = I.F; +} + +fn F(c: C) { + // CHECK:STDERR: fail_alias_impl_not_found.carbon:[[@LINE+3]]:3: ERROR: Cannot access member of interface I in type C that does not implement that interface. + // CHECK:STDERR: C.F(); + // CHECK:STDERR: ^~~ + C.F(); + // CHECK:STDERR: fail_alias_impl_not_found.carbon:[[@LINE+3]]:3: ERROR: Cannot access member of interface I in type C that does not implement that interface. + // CHECK:STDERR: c.F(); + // CHECK:STDERR: ^~~ + c.F(); +} + +// CHECK:STDOUT: --- fail_alias_impl_not_found.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: type = interface_type @I [template] +// CHECK:STDOUT: %.2: type = assoc_entity_type @I, [template] +// CHECK:STDOUT: %.3: in I> = assoc_entity element0, @I.%F [template] +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.4: type = struct_type {} [template] +// CHECK:STDOUT: %.5: type = tuple_type () [template] +// CHECK:STDOUT: %.6: type = ptr_type {} [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .I = %I.decl +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: } +// CHECK:STDOUT: %I.decl: type = interface_decl @I [template = constants.%.1] {} +// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} +// CHECK:STDOUT: %F: = fn_decl @F.2 [template] { +// CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [template = constants.%C] +// CHECK:STDOUT: %c.loc15_6.1: C = param c +// CHECK:STDOUT: @F.2.%c: C = bind_name c, %c.loc15_6.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @I { +// CHECK:STDOUT: %Self: I = bind_symbolic_name Self [symbolic] +// CHECK:STDOUT: %F: = fn_decl @F.1 [template] {} +// CHECK:STDOUT: %.loc8: in I> = assoc_entity element0, %F [template = constants.%.3] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .F = %.loc8 +// CHECK:STDOUT: witness = (%F) +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [template = constants.%.1] +// CHECK:STDOUT: %F.ref: in I> = name_ref F, @I.%.loc8 [template = constants.%.3] +// CHECK:STDOUT: %F: in I> = bind_alias F, @I.%.loc8 [template = constants.%.3] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F.1(); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F.2(%c: C) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [template = constants.%C] +// CHECK:STDOUT: %F.ref.loc19: in I> = name_ref F, @C.%F [template = constants.%.3] +// CHECK:STDOUT: %c.ref: C = name_ref c, %c +// CHECK:STDOUT: %F.ref.loc23: in I> = name_ref F, @C.%F [template = constants.%.3] +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/impl/lookup/fail_todo_undefined_impl.carbon b/toolchain/check/testdata/impl/lookup/fail_todo_undefined_impl.carbon new file mode 100644 index 000000000000..62032245efc6 --- /dev/null +++ b/toolchain/check/testdata/impl/lookup/fail_todo_undefined_impl.carbon @@ -0,0 +1,98 @@ +// 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 + +interface I { + fn F(); +} + +class C { + extend impl as I; +} + +fn F() -> i32 { + // TODO: This should produce a more useful error message. + // CHECK:STDERR: fail_todo_undefined_impl.carbon:[[@LINE+3]]:10: ERROR: Cannot access member of interface I in type C that does not implement that interface. + // CHECK:STDERR: return C.F(); + // CHECK:STDERR: ^~~ + return C.F(); +} + +impl C as I { + fn F() {} +} + +// CHECK:STDOUT: --- fail_todo_undefined_impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: type = interface_type @I [template] +// CHECK:STDOUT: %.2: type = assoc_entity_type @I, [template] +// CHECK:STDOUT: %.3: in I> = assoc_entity element0, @I.%F [template] +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.4: type = struct_type {} [template] +// CHECK:STDOUT: %.5: = interface_witness (@impl.%F) [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .I = %I.decl +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: } +// CHECK:STDOUT: %I.decl: type = interface_decl @I [template = constants.%.1] {} +// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} +// CHECK:STDOUT: %F: = fn_decl @F.2 [template] { +// CHECK:STDOUT: %return.var: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: impl_decl @impl { +// CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [template = constants.%C] +// CHECK:STDOUT: %I.ref: type = name_ref I, %I.decl [template = constants.%.1] +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @I { +// CHECK:STDOUT: %Self: I = bind_symbolic_name Self [symbolic] +// CHECK:STDOUT: %F: = fn_decl @F.1 [template] {} +// CHECK:STDOUT: %.loc8: in I> = assoc_entity element0, %F [template = constants.%.3] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .F = %.loc8 +// CHECK:STDOUT: witness = (%F) +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: impl @impl: C as I { +// CHECK:STDOUT: %F: = fn_decl @F.3 [template] {} +// CHECK:STDOUT: %.1: = interface_witness (%F) [template = constants.%.5] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: witness = %.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: impl_decl @impl { +// CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [template = constants.%.1] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: extend name_scope1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F.1(); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F.2() -> i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [template = constants.%C] +// CHECK:STDOUT: %F.ref: in I> = name_ref F, @I.%.loc8 [template = constants.%.3] +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F.3() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/impl/lookup/instance_method.carbon b/toolchain/check/testdata/impl/lookup/instance_method.carbon new file mode 100644 index 000000000000..c02e8f7ac368 --- /dev/null +++ b/toolchain/check/testdata/impl/lookup/instance_method.carbon @@ -0,0 +1,109 @@ +// 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 + +class C; + +interface I { + // TODO: Use `Self` once we perform substitution for it. + fn F[self: C]() -> i32; +} + +class C { + extend impl as I { + fn F[self: C]() -> i32; + } +} + +fn F(c: C) -> i32 { + return c.F(); +} + +// CHECK:STDOUT: --- instance_method.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.1: type = interface_type @I [template] +// CHECK:STDOUT: %.2: type = assoc_entity_type @I, [template] +// CHECK:STDOUT: %.3: in I> = assoc_entity element0, @I.%F [template] +// CHECK:STDOUT: %.4: = interface_witness (@impl.%F) [template] +// CHECK:STDOUT: %.5: type = struct_type {} [template] +// CHECK:STDOUT: %.6: type = tuple_type () [template] +// CHECK:STDOUT: %.7: type = ptr_type {} [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %C.decl.loc7 +// CHECK:STDOUT: .I = %I.decl +// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl.loc7: type = class_decl @C [template = constants.%C] {} +// CHECK:STDOUT: %I.decl: type = interface_decl @I [template = constants.%.1] {} +// CHECK:STDOUT: %C.decl.loc14: type = class_decl @C [template = constants.%C] {} +// CHECK:STDOUT: %F: = fn_decl @F.3 [template] { +// CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl.loc7 [template = constants.%C] +// CHECK:STDOUT: %c.loc20_6.1: C = param c +// CHECK:STDOUT: @F.3.%c: C = bind_name c, %c.loc20_6.1 +// CHECK:STDOUT: %return.var: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @I { +// CHECK:STDOUT: %Self: I = bind_symbolic_name Self [symbolic] +// CHECK:STDOUT: %F: = fn_decl @F.1 [template] { +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl.loc7 [template = constants.%C] +// CHECK:STDOUT: %self.loc11_8.1: C = param self +// CHECK:STDOUT: %self.loc11_8.2: C = bind_name self, %self.loc11_8.1 +// CHECK:STDOUT: %return.var: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc11: in I> = assoc_entity element0, %F [template = constants.%.3] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .F = %.loc11 +// CHECK:STDOUT: witness = (%F) +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: impl @impl: C as I { +// CHECK:STDOUT: %F: = fn_decl @F.2 [template] { +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl.loc7 [template = constants.%C] +// CHECK:STDOUT: %self.loc16_10.1: C = param self +// CHECK:STDOUT: %self.loc16_10.2: C = bind_name self, %self.loc16_10.1 +// CHECK:STDOUT: %return.var: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %.1: = interface_witness (%F) [template = constants.%.4] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: witness = %.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: impl_decl @impl { +// CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [template = constants.%.1] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: extend name_scope1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F.1[@I.%self.loc11_8.2: C]() -> i32; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F.2[@impl.%self.loc16_10.2: C]() -> i32; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F.3(%c: C) -> i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %c.ref: C = name_ref c, %c +// CHECK:STDOUT: %F.ref: in I> = name_ref F, @I.%.loc11 [template = constants.%.3] +// CHECK:STDOUT: %.1: = interface_witness_access @impl.%.1, element0 [template = @impl.%F] +// CHECK:STDOUT: %.loc21_11: = bound_method %c.ref, %.1 +// CHECK:STDOUT: %.loc21_13.1: init i32 = call %.loc21_11(%c.ref) +// CHECK:STDOUT: %.loc21_15: i32 = value_of_initializer %.loc21_13.1 +// CHECK:STDOUT: %.loc21_13.2: i32 = converted %.loc21_13.1, %.loc21_15 +// CHECK:STDOUT: return %.loc21_13.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/impl/redeclaration.carbon b/toolchain/check/testdata/impl/redeclaration.carbon index ad29c44d5091..acce07898982 100644 --- a/toolchain/check/testdata/impl/redeclaration.carbon +++ b/toolchain/check/testdata/impl/redeclaration.carbon @@ -20,7 +20,7 @@ impl i32 as I {} // CHECK:STDOUT: %.1: type = interface_type @I [template] // CHECK:STDOUT: %X: type = class_type @X [template] // CHECK:STDOUT: %.2: type = struct_type {} [template] -// CHECK:STDOUT: %.3: = interface_witness @I, () [template] +// CHECK:STDOUT: %.3: = interface_witness () [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -47,7 +47,7 @@ impl i32 as I {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @impl: i32 as I { -// CHECK:STDOUT: %.1: = interface_witness @I, () [template = constants.%.3] +// CHECK:STDOUT: %.1: = interface_witness () [template = constants.%.3] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: witness = %.1 diff --git a/toolchain/check/testdata/interface/fail_lookup_undefined.carbon b/toolchain/check/testdata/interface/fail_lookup_undefined.carbon index 1ee051f23a4d..6a331b2244b1 100644 --- a/toolchain/check/testdata/interface/fail_lookup_undefined.carbon +++ b/toolchain/check/testdata/interface/fail_lookup_undefined.carbon @@ -17,7 +17,7 @@ fn Undefined.F(); fn Test() { // CHECK:STDERR: fail_lookup_undefined.carbon:[[@LINE+6]]:3: ERROR: Member access into undefined interface `Undefined`. // CHECK:STDERR: Undefined.G(); - // CHECK:STDERR: ^~~~~~~~~ + // CHECK:STDERR: ^~~~~~~~~~~ // CHECK:STDERR: fail_lookup_undefined.carbon:[[@LINE-14]]:1: Interface was forward declared here. // CHECK:STDERR: interface Undefined; // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ @@ -27,7 +27,7 @@ fn Test() { interface BeingDefined { // CHECK:STDERR: fail_lookup_undefined.carbon:[[@LINE+9]]:13: ERROR: Member access into undefined interface `BeingDefined`. // CHECK:STDERR: fn H() -> BeingDefined.T; - // CHECK:STDERR: ^~~~~~~~~~~~ + // CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_lookup_undefined.carbon:[[@LINE-4]]:1: Interface is currently being defined. // CHECK:STDERR: interface BeingDefined { // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 4c439bfd86b0..2d69e340d352 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -203,6 +203,9 @@ CARBON_DIAGNOSTIC_KIND(ImplFunctionWithNonFunction) CARBON_DIAGNOSTIC_KIND(ImplAssociatedFunctionHere) CARBON_DIAGNOSTIC_KIND(ImplOfUndefinedInterface) +// Impl lookup. +CARBON_DIAGNOSTIC_KIND(MissingImplInMemberAccess) + // Let declaration checking. CARBON_DIAGNOSTIC_KIND(ExpectedInitializerAfterLet) CARBON_DIAGNOSTIC_KIND(ExpectedSymbolicBindingInAssociatedConstant) diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index 77e788641f44..921100542c7b 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -63,6 +63,11 @@ auto FileContext::GetGlobal(SemIR::InstId inst_id) -> llvm::Value* { } auto target = sem_ir().insts().Get(inst_id); + while (auto alias = target.TryAs()) { + inst_id = alias->value_id; + target = sem_ir().insts().Get(inst_id); + } + if (auto function_decl = target.TryAs()) { return GetFunction(function_decl->function_id); } @@ -265,6 +270,7 @@ auto FileContext::BuildType(SemIR::InstId inst_id) -> llvm::Type* { case SemIR::BuiltinKind::FunctionType.AsInt(): case SemIR::BuiltinKind::BoundMethodType.AsInt(): case SemIR::BuiltinKind::NamespaceType.AsInt(): + case SemIR::BuiltinKind::WitnessType.AsInt(): // Return an empty struct as a placeholder. return llvm::StructType::get(*llvm_context_); default: diff --git a/toolchain/lower/handle.cpp b/toolchain/lower/handle.cpp index e2b9fdf7317a..acda3092c293 100644 --- a/toolchain/lower/handle.cpp +++ b/toolchain/lower/handle.cpp @@ -252,6 +252,16 @@ auto HandleInterfaceWitness(FunctionContext& /*context*/, FatalErrorIfEncountered(inst); } +auto HandleInterfaceWitnessAccess(FunctionContext& context, + SemIR::InstId inst_id, + SemIR::InterfaceWitnessAccess inst) -> void { + // TODO: Add general constant lowering. + auto const_id = context.sem_ir().constant_values().Get(inst_id); + CARBON_CHECK(const_id.is_constant()) + << "Lowering non-constant witness access " << inst; + context.SetLocal(inst_id, context.GetValue(const_id.inst_id())); +} + auto HandleIntLiteral(FunctionContext& context, SemIR::InstId inst_id, SemIR::IntLiteral inst) -> void { const llvm::APInt& i = context.sem_ir().ints().Get(inst.int_id); diff --git a/toolchain/lower/testdata/impl/assoc_fn_alias.carbon b/toolchain/lower/testdata/impl/assoc_fn_alias.carbon new file mode 100644 index 000000000000..c83d0400c7ec --- /dev/null +++ b/toolchain/lower/testdata/impl/assoc_fn_alias.carbon @@ -0,0 +1,42 @@ +// 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 + +class A; + +interface I { + fn F[self: A]() -> i32; +} + +class A { + alias F = I.F; + var n: i32; +} + +impl A as I { + fn F[self: A]() -> i32 { + return self.n; + } +} + +fn Call(a: A) -> i32 { + return a.F(); +} + +// CHECK:STDOUT: ; ModuleID = 'assoc_fn_alias.carbon' +// CHECK:STDOUT: source_filename = "assoc_fn_alias.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: declare i32 @F(ptr) +// CHECK:STDOUT: +// CHECK:STDOUT: define i32 @F.1(ptr %self) { +// CHECK:STDOUT: %n = getelementptr inbounds { i32 }, ptr %self, i32 0, i32 0 +// CHECK:STDOUT: %1 = load i32, ptr %n, align 4 +// CHECK:STDOUT: ret i32 %1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i32 @Call(ptr %a) { +// CHECK:STDOUT: %F.1 = call i32 @F.1(ptr %a) +// CHECK:STDOUT: ret i32 %F.1 +// CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/impl/extend_impl.carbon b/toolchain/lower/testdata/impl/extend_impl.carbon new file mode 100644 index 000000000000..f55e5079d071 --- /dev/null +++ b/toolchain/lower/testdata/impl/extend_impl.carbon @@ -0,0 +1,44 @@ +// 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 + +interface I { + fn F() -> i32; +} + +class A { + extend impl as I { + fn F() -> i32 { + return 0; + } + } +} + +fn TypeAccess() -> i32 { + return A.F(); +} + +fn InstanceAccess(a: A) -> i32 { + return a.F(); +} + +// CHECK:STDOUT: ; ModuleID = 'extend_impl.carbon' +// CHECK:STDOUT: source_filename = "extend_impl.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: declare i32 @F() +// CHECK:STDOUT: +// CHECK:STDOUT: define i32 @F.1() { +// CHECK:STDOUT: ret i32 0 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i32 @TypeAccess() { +// CHECK:STDOUT: %F.1 = call i32 @F.1() +// CHECK:STDOUT: ret i32 %F.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i32 @InstanceAccess(ptr %a) { +// CHECK:STDOUT: %F.1 = call i32 @F.1() +// CHECK:STDOUT: ret i32 %F.1 +// CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/impl/instance_method.carbon b/toolchain/lower/testdata/impl/instance_method.carbon new file mode 100644 index 000000000000..f324f34d59b9 --- /dev/null +++ b/toolchain/lower/testdata/impl/instance_method.carbon @@ -0,0 +1,38 @@ +// 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 + +class A; + +interface GetSelf { + // TODO: Use `Self` once that works. + fn Get[addr self: A*]() -> A*; +} + +class A { + extend impl as GetSelf { + fn Get[addr self: Self*]() -> Self* { + return self; + } + } +} + +fn Call(a: A*) -> A* { + return (*a).Get(); +} + +// CHECK:STDOUT: ; ModuleID = 'instance_method.carbon' +// CHECK:STDOUT: source_filename = "instance_method.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: declare ptr @Get(ptr) +// CHECK:STDOUT: +// CHECK:STDOUT: define ptr @Get.1(ptr %self) { +// CHECK:STDOUT: ret ptr %self +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define ptr @Call(ptr %a) { +// CHECK:STDOUT: %Get.1 = call ptr @Get.1(ptr %a) +// CHECK:STDOUT: ret ptr %Get.1 +// CHECK:STDOUT: } diff --git a/toolchain/sem_ir/file.cpp b/toolchain/sem_ir/file.cpp index 4f06ff33f0d2..9f1199c8b86c 100644 --- a/toolchain/sem_ir/file.cpp +++ b/toolchain/sem_ir/file.cpp @@ -239,6 +239,7 @@ static auto GetTypePrecedence(InstKind kind) -> int { case InitializeFrom::Kind: case InterfaceDecl::Kind: case InterfaceWitness::Kind: + case InterfaceWitnessAccess::Kind: case IntLiteral::Kind: case Namespace::Kind: case Param::Kind: @@ -487,6 +488,7 @@ static auto StringifyTypeExprImpl(const SemIR::File& outer_sem_ir, case InitializeFrom::Kind: case InterfaceDecl::Kind: case InterfaceWitness::Kind: + case InterfaceWitnessAccess::Kind: case IntLiteral::Kind: case Namespace::Kind: case Param::Kind: @@ -594,6 +596,7 @@ auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory { case InterfaceDecl::Kind: case InterfaceType::Kind: case InterfaceWitness::Kind: + case InterfaceWitnessAccess::Kind: case IntLiteral::Kind: case Param::Kind: case PointerType::Kind: diff --git a/toolchain/sem_ir/inst_kind.def b/toolchain/sem_ir/inst_kind.def index 7ed5f48444fd..2df9477926a4 100644 --- a/toolchain/sem_ir/inst_kind.def +++ b/toolchain/sem_ir/inst_kind.def @@ -57,6 +57,7 @@ CARBON_SEM_IR_INST_KIND(InitializeFrom) CARBON_SEM_IR_INST_KIND(InterfaceDecl) CARBON_SEM_IR_INST_KIND(InterfaceType) CARBON_SEM_IR_INST_KIND(InterfaceWitness) +CARBON_SEM_IR_INST_KIND(InterfaceWitnessAccess) CARBON_SEM_IR_INST_KIND(IntLiteral) CARBON_SEM_IR_INST_KIND(NameRef) CARBON_SEM_IR_INST_KIND(Namespace) diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index fc6e52f79084..bb7940bcbbd6 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -75,9 +75,9 @@ struct ArrayIndex { // Common representation for aggregate access nodes, which access a fixed // element of an aggregate. struct AnyAggregateAccess { - static constexpr InstKind Kinds[] = {InstKind::StructAccess, - InstKind::TupleAccess, - InstKind::ClassElementAccess}; + static constexpr InstKind Kinds[] = { + InstKind::StructAccess, InstKind::TupleAccess, + InstKind::ClassElementAccess, InstKind::InterfaceWitnessAccess}; InstKind kind; TypeId type_id; @@ -111,8 +111,8 @@ struct AnyAggregateInit { // Common representation for all kinds of aggregate value. struct AnyAggregateValue { - static constexpr InstKind Kinds[] = {InstKind::StructValue, - InstKind::TupleValue}; + static constexpr InstKind Kinds[] = { + InstKind::StructValue, InstKind::TupleValue, InstKind::InterfaceWitness}; InstKind kind; TypeId type_id; @@ -547,8 +547,18 @@ struct InterfaceWitness { "interface_witness"); TypeId type_id; - InterfaceId interface_id; - InstBlockId table_id; + InstBlockId elements_id; +}; + +// Accesses an element of an interface witness by index. +struct InterfaceWitnessAccess { + static constexpr auto Kind = + InstKind::InterfaceWitnessAccess.Define( + "interface_witness_access"); + + TypeId type_id; + InstId witness_id; + ElementIndex index; }; struct IntLiteral {