diff --git a/toolchain/check/BUILD b/toolchain/check/BUILD index 33a9e7617b60..834e5e841283 100644 --- a/toolchain/check/BUILD +++ b/toolchain/check/BUILD @@ -142,6 +142,7 @@ cc_library( hdrs = ["function.h"], deps = [ ":context", + ":subst", "//common:check", "//toolchain/sem_ir:file", ], @@ -154,6 +155,7 @@ cc_library( deps = [ ":context", ":function", + ":subst", "//common:check", "//toolchain/diagnostics:diagnostic_emitter", "//toolchain/sem_ir:file", @@ -196,6 +198,23 @@ cc_library( name = "member_access", srcs = ["member_access.cpp"], hdrs = ["member_access.h"], + deps = [ + ":context", + ":subst", + "//common:check", + "//toolchain/diagnostics:diagnostic_emitter", + "//toolchain/sem_ir:file", + "//toolchain/sem_ir:ids", + "//toolchain/sem_ir:inst", + "//toolchain/sem_ir:inst_kind", + "@llvm-project//llvm:Support", + ], +) + +cc_library( + name = "subst", + srcs = ["subst.cpp"], + hdrs = ["subst.h"], deps = [ ":context", "//common:check", diff --git a/toolchain/check/function.cpp b/toolchain/check/function.cpp index 0f2f82de0785..add864c238e3 100644 --- a/toolchain/check/function.cpp +++ b/toolchain/check/function.cpp @@ -4,6 +4,8 @@ #include "toolchain/check/function.h" +#include "toolchain/check/subst.h" + namespace Carbon::Check { // Returns true if there was an error in declaring the function, which will have @@ -31,7 +33,8 @@ static auto CheckRedeclParam(Context& context, llvm::StringLiteral param_diag_label, int32_t param_index, SemIR::InstId new_param_ref_id, - SemIR::InstId prev_param_ref_id) -> bool { + SemIR::InstId prev_param_ref_id, + Substitutions substitutions) -> bool { // TODO: Consider differentiating between type and name mistakes. For now, // taking the simpler approach because I also think we may want to refactor // params. @@ -52,7 +55,8 @@ static auto CheckRedeclParam(Context& context, auto new_param_ref = context.insts().Get(new_param_ref_id); auto prev_param_ref = context.insts().Get(prev_param_ref_id); if (new_param_ref.kind() != prev_param_ref.kind() || - new_param_ref.type_id() != prev_param_ref.type_id()) { + new_param_ref.type_id() != + SubstType(context, prev_param_ref.type_id(), substitutions)) { diagnose(); return false; } @@ -90,7 +94,8 @@ static auto CheckRedeclParams(Context& context, SemIR::InstId new_decl_id, SemIR::InstBlockId new_param_refs_id, SemIR::InstId prev_decl_id, SemIR::InstBlockId prev_param_refs_id, - llvm::StringLiteral param_diag_label) -> bool { + llvm::StringLiteral param_diag_label, + Substitutions substitutions) -> bool { // This will often occur for empty params. if (new_param_refs_id == prev_param_refs_id) { return true; @@ -116,7 +121,7 @@ static auto CheckRedeclParams(Context& context, SemIR::InstId new_decl_id, for (auto [index, new_param_ref_id, prev_param_ref_id] : llvm::enumerate(new_param_ref_ids, prev_param_ref_ids)) { if (!CheckRedeclParam(context, param_diag_label, index, new_param_ref_id, - prev_param_ref_id)) { + prev_param_ref_id, substitutions)) { return false; } } @@ -125,21 +130,26 @@ static auto CheckRedeclParams(Context& context, SemIR::InstId new_decl_id, // Returns false if the provided function declarations differ. static auto CheckRedecl(Context& context, const SemIR::Function& new_function, - const SemIR::Function& prev_function) -> bool { + const SemIR::Function& prev_function, + Substitutions substitutions) -> bool { if (FunctionDeclHasError(context, new_function) || FunctionDeclHasError(context, prev_function)) { return false; } - if (!CheckRedeclParams(context, new_function.decl_id, - new_function.implicit_param_refs_id, - prev_function.decl_id, - prev_function.implicit_param_refs_id, "implicit ") || + if (!CheckRedeclParams( + context, new_function.decl_id, new_function.implicit_param_refs_id, + prev_function.decl_id, prev_function.implicit_param_refs_id, + "implicit ", substitutions) || !CheckRedeclParams(context, new_function.decl_id, new_function.param_refs_id, prev_function.decl_id, - prev_function.param_refs_id, "")) { + prev_function.param_refs_id, "", substitutions)) { return false; } - if (new_function.return_type_id != prev_function.return_type_id) { + auto prev_return_type_id = + prev_function.return_type_id.is_valid() + ? SubstType(context, prev_function.return_type_id, substitutions) + : SemIR::TypeId::Invalid; + if (new_function.return_type_id != prev_return_type_id) { CARBON_DIAGNOSTIC( FunctionRedeclReturnTypeDiffers, Error, "Function redeclaration differs because return type is `{0}`.", @@ -154,12 +164,12 @@ static auto CheckRedecl(Context& context, const SemIR::Function& new_function, new_function.return_type_id) : context.emitter().Build(new_function.decl_id, FunctionRedeclReturnTypeDiffersNoReturn); - if (prev_function.return_type_id.is_valid()) { + if (prev_return_type_id.is_valid()) { CARBON_DIAGNOSTIC(FunctionRedeclReturnTypePrevious, Note, "Previously declared with return type `{0}`.", SemIR::TypeId); diag.Note(prev_function.decl_id, FunctionRedeclReturnTypePrevious, - prev_function.return_type_id); + prev_return_type_id); } else { CARBON_DIAGNOSTIC(FunctionRedeclReturnTypePreviousNoReturn, Note, "Previously declared with no return type."); @@ -173,10 +183,12 @@ static auto CheckRedecl(Context& context, const SemIR::Function& new_function, return true; } -auto CheckFunctionRedecl(Context& context, SemIR::FunctionId new_function_id, - SemIR::FunctionId prev_function_id) -> bool { +auto CheckFunctionTypeMatches(Context& context, + SemIR::FunctionId new_function_id, + SemIR::FunctionId prev_function_id, + Substitutions substitutions) -> bool { return CheckRedecl(context, context.functions().Get(new_function_id), - context.functions().Get(prev_function_id)); + context.functions().Get(prev_function_id), substitutions); } auto MergeFunctionRedecl(Context& context, Parse::NodeId node_id, @@ -186,7 +198,7 @@ auto MergeFunctionRedecl(Context& context, Parse::NodeId node_id, auto& prev_function = context.functions().Get(prev_function_id); // TODO: Disallow redeclarations within classes? - if (!CheckRedecl(context, new_function, prev_function)) { + if (!CheckRedecl(context, new_function, prev_function, {})) { return false; } diff --git a/toolchain/check/function.h b/toolchain/check/function.h index c42ba5716ad2..f6add8cd2120 100644 --- a/toolchain/check/function.h +++ b/toolchain/check/function.h @@ -6,14 +6,20 @@ #define CARBON_TOOLCHAIN_CHECK_FUNCTION_H_ #include "toolchain/check/context.h" +#include "toolchain/check/subst.h" #include "toolchain/sem_ir/function.h" namespace Carbon::Check { -// Checks that `new_function_id` does not differ from `prev_function_id`. -// Prints a suitable diagnostic and returns false if not. -auto CheckFunctionRedecl(Context& context, SemIR::FunctionId new_function_id, - SemIR::FunctionId prev_function_id) -> bool; +// Checks that `new_function_id` has the same parameter types and return type as +// `prev_function_id`, applying the specified set of substitutions to the +// previous function. Prints a suitable diagnostic and returns false if not. +// Note that this doesn't include the syntactic check that's performed for +// redeclarations. +auto CheckFunctionTypeMatches(Context& context, + SemIR::FunctionId new_function_id, + SemIR::FunctionId prev_function_id, + Substitutions substitutions) -> bool; // Tries to merge new_function into prev_function_id. Since new_function won't // have a definition even if one is upcoming, set is_definition to indicate the diff --git a/toolchain/check/impl.cpp b/toolchain/check/impl.cpp index 0ceff862eb0a..b31fa75b63ae 100644 --- a/toolchain/check/impl.cpp +++ b/toolchain/check/impl.cpp @@ -6,6 +6,7 @@ #include "toolchain/check/context.h" #include "toolchain/check/function.h" +#include "toolchain/check/subst.h" #include "toolchain/diagnostics/diagnostic_emitter.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/impl.h" @@ -29,7 +30,7 @@ static auto NoteAssociatedFunction(Context& context, // `BuiltinError` if the function is not usable. static auto CheckAssociatedFunctionImplementation( Context& context, SemIR::FunctionId interface_function_id, - SemIR::InstId impl_decl_id) -> SemIR::InstId { + SemIR::InstId impl_decl_id, Substitutions substitutions) -> SemIR::InstId { auto impl_function_decl = context.insts().TryGetAs(impl_decl_id); if (!impl_function_decl) { @@ -49,8 +50,8 @@ static auto CheckAssociatedFunctionImplementation( // before checking. Also, this should be a semantic check rather than a // syntactic one. The functions should be allowed to have different signatures // as long as we can synthesize a suitable thunk. - if (!CheckFunctionRedecl(context, impl_function_decl->function_id, - interface_function_id)) { + if (!CheckFunctionTypeMatches(context, impl_function_decl->function_id, + interface_function_id, substitutions)) { return SemIR::InstId::BuiltinError; } return impl_decl_id; @@ -79,6 +80,11 @@ static auto BuildInterfaceWitness( context.inst_blocks().Get(interface.associated_entities_id); table.reserve(assoc_entities.size()); + // Substitute `Self` with the impl's self type when associated functions. + Substitution substitutions[1] = { + {.bind_id = interface.self_param_id, + .replacement_id = context.types().GetConstantId(impl.self_id)}}; + for (auto decl_id : assoc_entities) { auto decl = context.insts().Get(decl_id); if (auto fn_decl = decl.TryAs()) { @@ -88,7 +94,7 @@ static auto BuildInterfaceWitness( if (impl_decl_id.is_valid()) { used_decl_ids.push_back(impl_decl_id); table.push_back(CheckAssociatedFunctionImplementation( - context, fn_decl->function_id, impl_decl_id)); + context, fn_decl->function_id, impl_decl_id, substitutions)); } else { CARBON_DIAGNOSTIC( ImplMissingFunction, Error, diff --git a/toolchain/check/member_access.cpp b/toolchain/check/member_access.cpp index a1a7cee2ad4b..1a8963e2dd5d 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/check/subst.h" #include "toolchain/diagnostics/diagnostic_emitter.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/typed_insts.h" @@ -151,6 +152,7 @@ static auto LookupInterfaceWitness(Context& context, static auto PerformImplLookup(Context& context, SemIR::ConstantId type_const_id, SemIR::AssociatedEntityType assoc_type, SemIR::InstId member_id) -> SemIR::InstId { + auto& interface = context.interfaces().Get(assoc_type.interface_id); auto witness_id = LookupInterfaceWitness(context, type_const_id, assoc_type.interface_id); if (!witness_id.is_valid()) { @@ -159,8 +161,7 @@ static auto PerformImplLookup(Context& context, SemIR::ConstantId type_const_id, "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, + member_id, MissingImplInMemberAccess, interface.name_id, context.sem_ir().StringifyTypeExpr(type_const_id.inst_id())); return SemIR::InstId::BuiltinError; } @@ -180,9 +181,14 @@ static auto PerformImplLookup(Context& context, SemIR::ConstantId type_const_id, return SemIR::InstId::BuiltinError; } - // TODO: Substitute interface arguments and `Self` into `entity_type_id`. + // Substitute into the type declared in the interface. + Substitution substitutions[1] = { + {.bind_id = interface.self_param_id, .replacement_id = type_const_id}}; + auto subst_type_id = + SubstType(context, assoc_type.entity_type_id, substitutions); + return context.AddInst(SemIR::InterfaceWitnessAccess{ - assoc_type.entity_type_id, witness_id, assoc_entity->index}); + subst_type_id, witness_id, assoc_entity->index}); } // Performs a member name lookup into the specified scope, including performing diff --git a/toolchain/check/subst.cpp b/toolchain/check/subst.cpp new file mode 100644 index 000000000000..935cfc8bc8f7 --- /dev/null +++ b/toolchain/check/subst.cpp @@ -0,0 +1,250 @@ +// 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 + +#include "toolchain/check/subst.h" + +#include "toolchain/check/eval.h" +#include "toolchain/sem_ir/copy_on_write_block.h" +#include "toolchain/sem_ir/ids.h" + +namespace Carbon::Check { + +namespace { + +// Information about an instruction that we are substituting into. +struct WorklistItem { + // The instruction that we are substituting into. + SemIR::InstId inst_id; + // Whether the operands of this instruction have been added to the worklist. + bool is_expanded : 1; + // The index of the worklist item to process after we finish updating this + // one. For the final child of an instruction, this is the parent. For any + // other child, this is the index of the next child of the parent. For the + // root, this is -1. + int next_index : 31; +}; + +// A list of instructions that we're currently in the process of substituting +// into. For details of the algorithm used here, see `SubstConstant`. +class Worklist { + public: + explicit Worklist(SemIR::InstId root_id) { + worklist_.push_back( + {.inst_id = root_id, .is_expanded = false, .next_index = -1}); + } + + auto operator[](int index) -> WorklistItem& { return worklist_[index]; } + auto size() -> int { return worklist_.size(); } + auto back() -> WorklistItem& { return worklist_.back(); } + + auto Push(SemIR::InstId inst_id) -> void { + worklist_.push_back({.inst_id = inst_id, + .is_expanded = false, + .next_index = static_cast(worklist_.size() + 1)}); + CARBON_CHECK(worklist_.back().next_index > 0) << "Constant too large."; + } + auto Pop() -> SemIR::InstId { return worklist_.pop_back_val().inst_id; } + + private: + // Constants can get pretty large, so use a large worklist. This should be + // about 4KiB, which should be small enough to comfortably fit on the stack, + // but large enough that it's unlikely that we'll need a heap allocation. + llvm::SmallVector worklist_; +}; + +} // namespace + +// Pushes the specified operand onto the worklist. +static auto PushOperand(Context& context, Worklist& worklist, + SemIR::IdKind kind, int32_t arg) -> void { + switch (kind) { + case SemIR::IdKind::For: + worklist.Push(static_cast(arg)); + break; + case SemIR::IdKind::For: + worklist.Push(context.types().GetInstId(static_cast(arg))); + break; + case SemIR::IdKind::For: + for (auto inst_id : + context.inst_blocks().Get(static_cast(arg))) { + worklist.Push(inst_id); + } + break; + case SemIR::IdKind::For: + for (auto type_id : + context.type_blocks().Get(static_cast(arg))) { + worklist.Push(context.types().GetInstId(type_id)); + } + break; + default: + break; + } +} + +// Converts the operands of this instruction into `InstId`s and pushes them onto +// the worklist. +static auto ExpandOperands(Context& context, Worklist& worklist, + SemIR::InstId inst_id) -> void { + auto inst = context.insts().Get(inst_id); + auto kinds = inst.ArgKinds(); + PushOperand(context, worklist, kinds.first, inst.arg0()); + PushOperand(context, worklist, kinds.second, inst.arg1()); +} + +// Pops the specified operand from the worklist and returns it. +static auto PopOperand(Context& context, Worklist& worklist, SemIR::IdKind kind, + int32_t arg) -> int32_t { + switch (kind) { + case SemIR::IdKind::For: + return worklist.Pop().index; + case SemIR::IdKind::For: + return context.GetTypeIdForTypeInst(worklist.Pop()).index; + case SemIR::IdKind::For: { + auto old_inst_block_id = static_cast(arg); + auto size = context.inst_blocks().Get(old_inst_block_id).size(); + SemIR::CopyOnWriteInstBlock new_inst_block(context.sem_ir(), + old_inst_block_id); + for (auto i : llvm::reverse(llvm::seq(size))) { + new_inst_block.Set(i, worklist.Pop()); + } + return new_inst_block.id().index; + } + case SemIR::IdKind::For: { + auto old_type_block_id = static_cast(arg); + auto size = context.type_blocks().Get(old_type_block_id).size(); + SemIR::CopyOnWriteTypeBlock new_type_block(context.sem_ir(), + old_type_block_id); + for (auto i : llvm::index_range(0, size)) { + new_type_block.Set(size - i - 1, + context.GetTypeIdForTypeInst(worklist.Pop())); + } + return new_type_block.id().index; + } + default: + return arg; + } +} + +// Pops the operands of the specified instruction off the worklist and rebuilds +// the instruction with the updated operands. +static auto Rebuild(Context& context, Worklist& worklist, SemIR::InstId inst_id) + -> SemIR::InstId { + auto inst = context.insts().Get(inst_id); + auto kinds = inst.ArgKinds(); + + // Note that we pop in reverse order because we pushed them in forwards order. + int32_t arg1 = PopOperand(context, worklist, kinds.second, inst.arg1()); + int32_t arg0 = PopOperand(context, worklist, kinds.first, inst.arg0()); + if (arg0 == inst.arg0() && arg1 == inst.arg1()) { + return inst_id; + } + + // TODO: Updating the arguments might result in the instruction having a + // different type. We should consider either recomputing the type or + // substituting into it. In the latter case, consider caching, as we may + // substitute into related types repeatedly. + inst.SetArgs(arg0, arg1); + auto result_id = TryEvalInst(context, SemIR::InstId::Invalid, inst); + CARBON_CHECK(result_id.is_constant()) + << "Substitution into constant produced non-constant"; + return result_id.inst_id(); +} + +auto SubstConstant(Context& context, SemIR::ConstantId const_id, + Substitutions substitutions) -> SemIR::ConstantId { + CARBON_CHECK(const_id.is_constant()) << "Substituting into non-constant"; + + if (substitutions.empty()) { + // Nothing to substitute. + return const_id; + } + + if (!const_id.is_symbolic()) { + // A template constant can't contain a reference to a symbolic binding. + return const_id; + } + + Worklist worklist(const_id.inst_id()); + + // For each instruction that forms part of the constant, we will visit it + // twice: + // + // - First, we visit it with `is_expanded == false`, we add all of its + // operands onto the worklist, and process them by following this same + // process. + // - Then, once all operands are processed, we visit the instruction with + // `is_expanded == true`, pop the operands back off the worklist, and if any + // of them changed, rebuild this instruction. + // + // The second step is skipped if we can detect in the first step that the + // instruction will not need to be rebuilt. + int index = 0; + while (index != -1) { + auto& item = worklist[index]; + + if (item.is_expanded) { + // Rebuild this item if necessary. Note that this might pop items from the + // worklist but does not reallocate, so does not invalidate `item`. + item.inst_id = Rebuild(context, worklist, item.inst_id); + index = item.next_index; + continue; + } + + if (context.constant_values().Get(item.inst_id).is_template()) { + // This instruction is a template constant, so can't contain any + // bindings that need to be substituted. + index = item.next_index; + continue; + } + + if (context.insts().Is(item.inst_id)) { + // This is a symbolic binding. Check if we're substituting it. + + // TODO: Consider building a hash map for substitutions. We might have a + // lot of them. + for (auto [bind_id, replacement_id] : substitutions) { + if (item.inst_id == bind_id) { + // This is the binding we're replacing. Perform substitution. + item.inst_id = replacement_id.inst_id(); + break; + } + } + + // If it's not being substituted, don't look through it. Its constant + // value doesn't depend on its operand. + index = item.next_index; + continue; + } + + // Extract the operands of this item into the worklist. Note that this + // modifies the worklist, so it's not safe to use `item` after + // `ExpandOperands` returns. + item.is_expanded = true; + int first_operand = worklist.size(); + int next_index = item.next_index; + ExpandOperands(context, worklist, item.inst_id); + + // If there are any operands, go and update them before rebuilding this + // item. + if (worklist.size() > first_operand) { + worklist.back().next_index = index; + index = first_operand; + } else { + // No need to rebuild this instruction. + index = next_index; + } + } + + CARBON_CHECK(worklist.size() == 1) + << "Unexpected data left behind in work list"; + return context.constant_values().Get(worklist.back().inst_id); +} + +auto SubstType(Context& context, SemIR::TypeId type_id, + Substitutions substitutions) -> SemIR::TypeId { + return context.GetTypeIdForTypeConstant(SubstConstant( + context, context.types().GetConstantId(type_id), substitutions)); +} + +} // namespace Carbon::Check diff --git a/toolchain/check/subst.h b/toolchain/check/subst.h new file mode 100644 index 000000000000..c7ba945288a0 --- /dev/null +++ b/toolchain/check/subst.h @@ -0,0 +1,35 @@ +// 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 + +#ifndef CARBON_TOOLCHAIN_CHECK_SUBST_H_ +#define CARBON_TOOLCHAIN_CHECK_SUBST_H_ + +#include "toolchain/check/context.h" +#include "toolchain/sem_ir/ids.h" + +namespace Carbon::Check { + +// A substitution that is being performed. +struct Substitution { + // The ID of a `BindSymbolicName` instruction that is being replaced. + SemIR::InstId bind_id; + // The replacement constant value to substitute. + SemIR::ConstantId replacement_id; +}; + +using Substitutions = llvm::ArrayRef; + +// Replaces the `BindSymbolicName` instruction `bind_id` with `replacement_id` +// throughout the constant `const_id`, and returns the substituted value. +auto SubstConstant(Context& context, SemIR::ConstantId const_id, + Substitutions substitutions) -> SemIR::ConstantId; + +// Replaces the `BindSymbolicName` instruction `bind_id` with `replacement_id` +// throughout the type `type_id`, and returns the substituted value. +auto SubstType(Context& context, SemIR::TypeId type_id, + Substitutions substitutions) -> SemIR::TypeId; + +} // namespace Carbon::Check + +#endif // CARBON_TOOLCHAIN_CHECK_SUBST_H_ 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 c53b0be38829..ec43e36a202e 100644 --- a/toolchain/check/testdata/impl/fail_impl_bad_assoc_fn.carbon +++ b/toolchain/check/testdata/impl/fail_impl_bad_assoc_fn.carbon @@ -167,6 +167,34 @@ class FDifferentParamName { } } +interface SelfNested { + fn F(x: (Self*, {.x: Self, .y: i32})) -> [Self; 4]; +} + +class SelfNestedBadParam { + impl as SelfNested { + // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+6]]:56: ERROR: Function returns incomplete type `[SelfNestedBadParam; 4]`. + // CHECK:STDERR: fn F(x: (SelfNestedBadParam*, {.x: i32, .y: i32})) -> [SelfNestedBadParam; 4]; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-5]]:1: Class is incomplete within its definition. + // CHECK:STDERR: class SelfNestedBadParam { + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~ + fn F(x: (SelfNestedBadParam*, {.x: i32, .y: i32})) -> [SelfNestedBadParam; 4]; + } +} + +class SelfNestedBadReturnType { + impl as SelfNested { + // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE+6]]:5: ERROR: Function redeclaration differs because return type is `[SelfNestedBadParam; 4]`. + // CHECK:STDERR: fn F(x: (SelfNestedBadReturnType*, {.x: SelfNestedBadReturnType, .y: i32})) -> [SelfNestedBadParam; 4]; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: fail_impl_bad_assoc_fn.carbon:[[@LINE-20]]:3: Previously declared with return type `[SelfNestedBadReturnType; 4]`. + // CHECK:STDERR: fn F(x: (Self*, {.x: Self, .y: i32})) -> [Self; 4]; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + fn F(x: (SelfNestedBadReturnType*, {.x: SelfNestedBadReturnType, .y: i32})) -> [SelfNestedBadParam; 4]; + } +} + // CHECK:STDOUT: --- fail_impl_bad_assoc_fn.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { @@ -176,7 +204,7 @@ class FDifferentParamName { // CHECK:STDOUT: %NoF: type = class_type @NoF [template] // CHECK:STDOUT: %.4: type = struct_type {} [template] // CHECK:STDOUT: %FNotFunction: type = class_type @FNotFunction [template] -// CHECK:STDOUT: %F: type = class_type @F.13 [template] +// CHECK:STDOUT: %F: type = class_type @F.16 [template] // CHECK:STDOUT: %FAlias: type = class_type @FAlias [template] // CHECK:STDOUT: %FExtraParam: type = class_type @FExtraParam [template] // CHECK:STDOUT: %FExtraImplicitParam: type = class_type @FExtraImplicitParam [template] @@ -191,6 +219,29 @@ class FDifferentParamName { // CHECK:STDOUT: %FDifferentImplicitParamType: type = class_type @FDifferentImplicitParamType [template] // CHECK:STDOUT: %FDifferentReturnType: type = class_type @FDifferentReturnType [template] // CHECK:STDOUT: %FDifferentParamName: type = class_type @FDifferentParamName [template] +// CHECK:STDOUT: %.8: type = interface_type @SelfNested [template] +// CHECK:STDOUT: %.9: type = ptr_type Self [symbolic] +// CHECK:STDOUT: %.10: type = struct_type {.x: Self, .y: i32} [symbolic] +// CHECK:STDOUT: %.11: type = tuple_type (type, type) [template] +// CHECK:STDOUT: %.12: type = tuple_type (Self*, {.x: Self, .y: i32}) [symbolic] +// CHECK:STDOUT: %.13: i32 = int_literal 4 [template] +// CHECK:STDOUT: %.14: type = array_type %.13, Self [symbolic] +// CHECK:STDOUT: %.15: type = ptr_type [Self; 4] [symbolic] +// CHECK:STDOUT: %.16: type = assoc_entity_type @SelfNested, [template] +// CHECK:STDOUT: %.17: in SelfNested> = assoc_entity element0, @SelfNested.%F [template] +// CHECK:STDOUT: %SelfNestedBadParam: type = class_type @SelfNestedBadParam [template] +// CHECK:STDOUT: %.18: type = ptr_type SelfNestedBadParam [template] +// CHECK:STDOUT: %.19: type = struct_type {.x: i32, .y: i32} [template] +// CHECK:STDOUT: %.20: type = tuple_type (SelfNestedBadParam*, {.x: i32, .y: i32}) [template] +// CHECK:STDOUT: %.21: type = array_type %.13, SelfNestedBadParam [template] +// CHECK:STDOUT: %SelfNestedBadReturnType: type = class_type @SelfNestedBadReturnType [template] +// CHECK:STDOUT: %.22: type = ptr_type SelfNestedBadReturnType [template] +// CHECK:STDOUT: %.23: type = struct_type {.x: SelfNestedBadReturnType, .y: i32} [template] +// CHECK:STDOUT: %.24: type = tuple_type (SelfNestedBadReturnType*, {.x: SelfNestedBadReturnType, .y: i32}) [template] +// CHECK:STDOUT: %.25: type = tuple_type () [template] +// CHECK:STDOUT: %.26: type = ptr_type {} [template] +// CHECK:STDOUT: %.27: type = ptr_type [SelfNestedBadParam; 4] [template] +// CHECK:STDOUT: %.28: type = array_type %.13, SelfNestedBadReturnType [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -211,6 +262,9 @@ class FDifferentParamName { // CHECK:STDOUT: .FDifferentImplicitParamType = %FDifferentImplicitParamType.decl // CHECK:STDOUT: .FDifferentReturnType = %FDifferentReturnType.decl // CHECK:STDOUT: .FDifferentParamName = %FDifferentParamName.decl +// CHECK:STDOUT: .SelfNested = %SelfNested.decl +// CHECK:STDOUT: .SelfNestedBadParam = %SelfNestedBadParam.decl +// CHECK:STDOUT: .SelfNestedBadReturnType = %SelfNestedBadReturnType.decl // CHECK:STDOUT: } // CHECK:STDOUT: %I.decl: type = interface_decl @I [template = constants.%.1] {} // CHECK:STDOUT: %NoF.decl: type = class_decl @NoF [template = constants.%NoF] {} @@ -228,6 +282,9 @@ class FDifferentParamName { // CHECK:STDOUT: %FDifferentImplicitParamType.decl: type = class_decl @FDifferentImplicitParamType [template = constants.%FDifferentImplicitParamType] {} // CHECK:STDOUT: %FDifferentReturnType.decl: type = class_decl @FDifferentReturnType [template = constants.%FDifferentReturnType] {} // CHECK:STDOUT: %FDifferentParamName.decl: type = class_decl @FDifferentParamName [template = constants.%FDifferentParamName] {} +// CHECK:STDOUT: %SelfNested.decl: type = interface_decl @SelfNested [template = constants.%.8] {} +// CHECK:STDOUT: %SelfNestedBadParam.decl: type = class_decl @SelfNestedBadParam [template = constants.%SelfNestedBadParam] {} +// CHECK:STDOUT: %SelfNestedBadReturnType.decl: type = class_decl @SelfNestedBadReturnType [template = constants.%SelfNestedBadReturnType] {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: interface @I { @@ -258,6 +315,36 @@ class FDifferentParamName { // CHECK:STDOUT: witness = (%F) // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: interface @SelfNested { +// CHECK:STDOUT: %Self: SelfNested = bind_symbolic_name Self [symbolic] +// CHECK:STDOUT: %F: = fn_decl @F.13 [template] { +// CHECK:STDOUT: %Self.ref.loc171_12: SelfNested = name_ref Self, %Self [symbolic = %Self] +// CHECK:STDOUT: %.loc171_16.1: type = facet_type_access %Self.ref.loc171_12 [symbolic = %Self] +// CHECK:STDOUT: %.loc171_12: type = converted %Self.ref.loc171_12, %.loc171_16.1 [symbolic = %Self] +// CHECK:STDOUT: %.loc171_16.2: type = ptr_type Self [symbolic = constants.%.9] +// CHECK:STDOUT: %Self.ref.loc171_24: SelfNested = name_ref Self, %Self [symbolic = %Self] +// CHECK:STDOUT: %.loc171_24.1: type = facet_type_access %Self.ref.loc171_24 [symbolic = %Self] +// CHECK:STDOUT: %.loc171_24.2: type = converted %Self.ref.loc171_24, %.loc171_24.1 [symbolic = %Self] +// CHECK:STDOUT: %.loc171_37: type = struct_type {.x: Self, .y: i32} [symbolic = constants.%.10] +// CHECK:STDOUT: %.loc171_38.1: (type, type) = tuple_literal (%.loc171_16.2, %.loc171_37) +// CHECK:STDOUT: %.loc171_38.2: type = converted %.loc171_38.1, constants.%.12 [symbolic = constants.%.12] +// CHECK:STDOUT: %x.loc171_8.1: (Self*, {.x: Self, .y: i32}) = param x +// CHECK:STDOUT: %x.loc171_8.2: (Self*, {.x: Self, .y: i32}) = bind_name x, %x.loc171_8.1 +// CHECK:STDOUT: %Self.ref.loc171_45: SelfNested = name_ref Self, %Self [symbolic = %Self] +// CHECK:STDOUT: %.loc171_51: i32 = int_literal 4 [template = constants.%.13] +// CHECK:STDOUT: %.loc171_45.1: type = facet_type_access %Self.ref.loc171_45 [symbolic = %Self] +// CHECK:STDOUT: %.loc171_45.2: type = converted %Self.ref.loc171_45, %.loc171_45.1 [symbolic = %Self] +// CHECK:STDOUT: %.loc171_52: type = array_type %.loc171_51, Self [symbolic = constants.%.14] +// CHECK:STDOUT: %return.var: ref [Self; 4] = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc171_53: in SelfNested> = assoc_entity element0, %F [template = constants.%.17] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .F = %.loc171_53 +// CHECK:STDOUT: witness = (%F) +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: impl @impl.1: NoF as I { // CHECK:STDOUT: %.1: = interface_witness () [template = ] // CHECK:STDOUT: @@ -266,7 +353,7 @@ class FDifferentParamName { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: impl @impl.2: FNotFunction as I { -// CHECK:STDOUT: %F.decl: type = class_decl @F.13 [template = constants.%F] {} +// CHECK:STDOUT: %F.decl: type = class_decl @F.16 [template = constants.%F] {} // CHECK:STDOUT: %.1: = interface_witness () [template = ] // CHECK:STDOUT: // CHECK:STDOUT: !members: @@ -423,6 +510,49 @@ class FDifferentParamName { // CHECK:STDOUT: witness = %.1 // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: impl @impl.14: SelfNestedBadParam as SelfNested { +// CHECK:STDOUT: %F: = fn_decl @F.14 [template] { +// CHECK:STDOUT: %SelfNestedBadParam.ref.loc182_14: type = name_ref SelfNestedBadParam, file.%SelfNestedBadParam.decl [template = constants.%SelfNestedBadParam] +// CHECK:STDOUT: %.loc182_32: type = ptr_type SelfNestedBadParam [template = constants.%.18] +// CHECK:STDOUT: %.loc182_52: type = struct_type {.x: i32, .y: i32} [template = constants.%.19] +// CHECK:STDOUT: %.loc182_53.1: (type, type) = tuple_literal (%.loc182_32, %.loc182_52) +// CHECK:STDOUT: %.loc182_53.2: type = converted %.loc182_53.1, constants.%.20 [template = constants.%.20] +// CHECK:STDOUT: %x.loc182_10.1: (SelfNestedBadParam*, {.x: i32, .y: i32}) = param x +// CHECK:STDOUT: %x.loc182_10.2: (SelfNestedBadParam*, {.x: i32, .y: i32}) = bind_name x, %x.loc182_10.1 +// CHECK:STDOUT: %SelfNestedBadParam.ref.loc182_60: type = name_ref SelfNestedBadParam, file.%SelfNestedBadParam.decl [template = constants.%SelfNestedBadParam] +// CHECK:STDOUT: %.loc182_80: i32 = int_literal 4 [template = constants.%.13] +// CHECK:STDOUT: %.loc182_81: type = array_type %.loc182_80, SelfNestedBadParam [template = constants.%.21] +// CHECK:STDOUT: %return.var: ref [SelfNestedBadParam; 4] = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %.1: = interface_witness () [template = ] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: witness = %.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: impl @impl.15: SelfNestedBadReturnType as SelfNested { +// CHECK:STDOUT: %F: = fn_decl @F.15 [template] { +// CHECK:STDOUT: %SelfNestedBadReturnType.ref.loc194_14: type = name_ref SelfNestedBadReturnType, file.%SelfNestedBadReturnType.decl [template = constants.%SelfNestedBadReturnType] +// CHECK:STDOUT: %.loc194_37: type = ptr_type SelfNestedBadReturnType [template = constants.%.22] +// CHECK:STDOUT: %SelfNestedBadReturnType.ref.loc194_45: type = name_ref SelfNestedBadReturnType, file.%SelfNestedBadReturnType.decl [template = constants.%SelfNestedBadReturnType] +// CHECK:STDOUT: %.loc194_77: type = struct_type {.x: SelfNestedBadReturnType, .y: i32} [template = constants.%.23] +// CHECK:STDOUT: %.loc194_78.1: (type, type) = tuple_literal (%.loc194_37, %.loc194_77) +// CHECK:STDOUT: %.loc194_78.2: type = converted %.loc194_78.1, constants.%.24 [template = constants.%.24] +// CHECK:STDOUT: %x.loc194_10.1: (SelfNestedBadReturnType*, {.x: SelfNestedBadReturnType, .y: i32}) = param x +// CHECK:STDOUT: %x.loc194_10.2: (SelfNestedBadReturnType*, {.x: SelfNestedBadReturnType, .y: i32}) = bind_name x, %x.loc194_10.1 +// CHECK:STDOUT: %SelfNestedBadParam.ref: type = name_ref SelfNestedBadParam, file.%SelfNestedBadParam.decl [template = constants.%SelfNestedBadParam] +// CHECK:STDOUT: %.loc194_105: i32 = int_literal 4 [template = constants.%.13] +// CHECK:STDOUT: %.loc194_106: type = array_type %.loc194_105, SelfNestedBadParam [template = constants.%.21] +// CHECK:STDOUT: %return.var: ref [SelfNestedBadParam; 4] = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %.1: = interface_witness () [template = ] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: witness = %.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: class @NoF { // CHECK:STDOUT: impl_decl @impl.1 { // CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [template = constants.%.1] @@ -441,7 +571,7 @@ class FDifferentParamName { // CHECK:STDOUT: .Self = constants.%FNotFunction // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: class @F.13; +// CHECK:STDOUT: class @F.16; // CHECK:STDOUT: // CHECK:STDOUT: class @FAlias { // CHECK:STDOUT: impl_decl @impl.3 { @@ -542,6 +672,24 @@ class FDifferentParamName { // CHECK:STDOUT: .Self = constants.%FDifferentParamName // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: class @SelfNestedBadParam { +// CHECK:STDOUT: impl_decl @impl.14 { +// CHECK:STDOUT: %SelfNested.ref: type = name_ref SelfNested, file.%SelfNested.decl [template = constants.%.8] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%SelfNestedBadParam +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @SelfNestedBadReturnType { +// CHECK:STDOUT: impl_decl @impl.15 { +// CHECK:STDOUT: %SelfNested.ref: type = name_ref SelfNested, file.%SelfNested.decl [template = constants.%.8] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%SelfNestedBadReturnType +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: fn @F.1(); // CHECK:STDOUT: // CHECK:STDOUT: fn @PossiblyF(); @@ -568,3 +716,9 @@ class FDifferentParamName { // CHECK:STDOUT: // CHECK:STDOUT: fn @F.12[@impl.13.%self.loc166_10.2: bool](@impl.13.%not_b.loc166_22.2: bool) -> bool; // CHECK:STDOUT: +// CHECK:STDOUT: fn @F.13(@SelfNested.%x.loc171_8.2: (Self*, {.x: Self, .y: i32})) -> @SelfNested.%return.var: [Self; 4]; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F.14(@impl.14.%x.loc182_10.2: (SelfNestedBadParam*, {.x: i32, .y: i32})) -> ; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F.15(@impl.15.%x.loc194_10.2: (SelfNestedBadReturnType*, {.x: SelfNestedBadReturnType, .y: i32})) -> @impl.15.%return.var: [SelfNestedBadParam; 4]; +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/impl/fail_todo_self_in_signature.carbon b/toolchain/check/testdata/impl/fail_todo_self_in_signature.carbon deleted file mode 100644 index 87dad9eb4793..000000000000 --- a/toolchain/check/testdata/impl/fail_todo_self_in_signature.carbon +++ /dev/null @@ -1,108 +0,0 @@ -// 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 UseSelf { - fn F[self: Self](x: Self) -> Self; -} - -class C {} - -impl C as UseSelf { - // TODO: Use `Self` below once it's supported. - // CHECK:STDERR: fail_todo_self_in_signature.carbon:[[@LINE+6]]:8: ERROR: Function redeclaration differs at implicit parameter 1. - // CHECK:STDERR: fn F[self: C](x: C) -> C { return {}; } - // CHECK:STDERR: ^~~~ - // CHECK:STDERR: fail_todo_self_in_signature.carbon:[[@LINE-10]]:8: Previous declaration's corresponding implicit parameter here. - // CHECK:STDERR: fn F[self: Self](x: Self) -> Self; - // CHECK:STDERR: ^~~~ - fn F[self: C](x: C) -> C { return {}; } -} - -// CHECK:STDOUT: --- fail_todo_self_in_signature.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %.1: type = interface_type @UseSelf [template] -// CHECK:STDOUT: %.2: type = assoc_entity_type @UseSelf, [template] -// CHECK:STDOUT: %.3: in UseSelf> = assoc_entity element0, @UseSelf.%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: %.7: C = struct_value () [template] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [template] { -// CHECK:STDOUT: .UseSelf = %UseSelf.decl -// CHECK:STDOUT: .C = %C.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %UseSelf.decl: type = interface_decl @UseSelf [template = constants.%.1] {} -// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} -// CHECK:STDOUT: impl_decl @impl { -// CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [template = constants.%C] -// CHECK:STDOUT: %UseSelf.ref: type = name_ref UseSelf, %UseSelf.decl [template = constants.%.1] -// CHECK:STDOUT: } -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: interface @UseSelf { -// CHECK:STDOUT: %Self: UseSelf = bind_symbolic_name Self [symbolic] -// CHECK:STDOUT: %F: = fn_decl @F.1 [template] { -// CHECK:STDOUT: %Self.ref.loc8_14: UseSelf = name_ref Self, %Self [symbolic = %Self] -// CHECK:STDOUT: %.loc8_14.1: type = facet_type_access %Self.ref.loc8_14 [symbolic = %Self] -// CHECK:STDOUT: %.loc8_14.2: type = converted %Self.ref.loc8_14, %.loc8_14.1 [symbolic = %Self] -// CHECK:STDOUT: %self.loc8_8.1: Self = param self -// CHECK:STDOUT: %self.loc8_8.2: Self = bind_name self, %self.loc8_8.1 -// CHECK:STDOUT: %Self.ref.loc8_23: UseSelf = name_ref Self, %Self [symbolic = %Self] -// CHECK:STDOUT: %.loc8_23.1: type = facet_type_access %Self.ref.loc8_23 [symbolic = %Self] -// CHECK:STDOUT: %.loc8_23.2: type = converted %Self.ref.loc8_23, %.loc8_23.1 [symbolic = %Self] -// CHECK:STDOUT: %x.loc8_20.1: Self = param x -// CHECK:STDOUT: %x.loc8_20.2: Self = bind_name x, %x.loc8_20.1 -// CHECK:STDOUT: %Self.ref.loc8_32: UseSelf = name_ref Self, %Self [symbolic = %Self] -// CHECK:STDOUT: %.loc8_32.1: type = facet_type_access %Self.ref.loc8_32 [symbolic = %Self] -// CHECK:STDOUT: %.loc8_32.2: type = converted %Self.ref.loc8_32, %.loc8_32.1 [symbolic = %Self] -// CHECK:STDOUT: %return.var: ref Self = var -// CHECK:STDOUT: } -// CHECK:STDOUT: %.loc8_36: in UseSelf> = assoc_entity element0, %F [template = constants.%.3] -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = %Self -// CHECK:STDOUT: .F = %.loc8_36 -// CHECK:STDOUT: witness = (%F) -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: impl @impl: C as UseSelf { -// CHECK:STDOUT: %F: = fn_decl @F.2 [template] { -// CHECK:STDOUT: %C.ref.loc21_14: type = name_ref C, file.%C.decl [template = constants.%C] -// CHECK:STDOUT: %self.loc21_8.1: C = param self -// CHECK:STDOUT: %self.loc21_8.2: C = bind_name self, %self.loc21_8.1 -// CHECK:STDOUT: %C.ref.loc21_20: type = name_ref C, file.%C.decl [template = constants.%C] -// CHECK:STDOUT: %x.loc21_17.1: C = param x -// CHECK:STDOUT: %x.loc21_17.2: C = bind_name x, %x.loc21_17.1 -// 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 () [template = ] -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .F = %F -// CHECK:STDOUT: witness = %.1 -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: class @C { -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = constants.%C -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @F.1[@UseSelf.%self.loc8_8.2: Self](@UseSelf.%x.loc8_20.2: Self) -> Self; -// CHECK:STDOUT: -// CHECK:STDOUT: fn @F.2[@impl.%self.loc21_8.2: C](@impl.%x.loc21_17.2: C) -> @impl.%return.var: C { -// CHECK:STDOUT: !entry: -// CHECK:STDOUT: %.loc21_38.1: {} = struct_literal () -// CHECK:STDOUT: %.loc21_38.2: init C = class_init (), @impl.%return.var [template = constants.%.7] -// CHECK:STDOUT: %.loc21_38.3: init C = converted %.loc21_38.1, %.loc21_38.2 [template = constants.%.7] -// CHECK:STDOUT: return %.loc21_38.3 -// CHECK:STDOUT: } -// CHECK:STDOUT: diff --git a/toolchain/check/testdata/impl/lookup/instance_method.carbon b/toolchain/check/testdata/impl/lookup/instance_method.carbon index c02e8f7ac368..2fa96a6de97b 100644 --- a/toolchain/check/testdata/impl/lookup/instance_method.carbon +++ b/toolchain/check/testdata/impl/lookup/instance_method.carbon @@ -7,13 +7,12 @@ class C; interface I { - // TODO: Use `Self` once we perform substitution for it. - fn F[self: C]() -> i32; + fn F[self: Self]() -> i32; } class C { extend impl as I { - fn F[self: C]() -> i32; + fn F[self: Self]() -> i32; } } @@ -42,11 +41,11 @@ fn F(c: C) -> i32 { // 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: %C.decl.loc13: 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: %c.loc19_6.1: C = param c +// CHECK:STDOUT: @F.3.%c: C = bind_name c, %c.loc19_6.1 // CHECK:STDOUT: %return.var: ref i32 = var // CHECK:STDOUT: } // CHECK:STDOUT: } @@ -54,24 +53,26 @@ fn F(c: C) -> i32 { // 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: %Self.ref: I = name_ref Self, %Self [symbolic = %Self] +// CHECK:STDOUT: %.loc10_14.1: type = facet_type_access %Self.ref [symbolic = %Self] +// CHECK:STDOUT: %.loc10_14.2: type = converted %Self.ref, %.loc10_14.1 [symbolic = %Self] +// CHECK:STDOUT: %self.loc10_8.1: Self = param self +// CHECK:STDOUT: %self.loc10_8.2: Self = bind_name self, %self.loc10_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: %.loc10_28: in I> = assoc_entity element0, %F [template = constants.%.3] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self -// CHECK:STDOUT: .F = %.loc11 +// CHECK:STDOUT: .F = %.loc10_28 // 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: %Self.ref: type = name_ref Self, constants.%C [template = constants.%C] +// CHECK:STDOUT: %self.loc15_10.1: C = param self +// CHECK:STDOUT: %self.loc15_10.2: C = bind_name self, %self.loc15_10.1 // CHECK:STDOUT: %return.var: ref i32 = var // CHECK:STDOUT: } // CHECK:STDOUT: %.1: = interface_witness (%F) [template = constants.%.4] @@ -91,19 +92,19 @@ fn F(c: C) -> i32 { // CHECK:STDOUT: extend name_scope1 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: fn @F.1[@I.%self.loc11_8.2: C]() -> i32; +// CHECK:STDOUT: fn @F.1[@I.%self.loc10_8.2: Self]() -> i32; // CHECK:STDOUT: -// CHECK:STDOUT: fn @F.2[@impl.%self.loc16_10.2: C]() -> i32; +// CHECK:STDOUT: fn @F.2[@impl.%self.loc15_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: %F.ref: in I> = name_ref F, @I.%.loc10_28 [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: %.loc20_11: = bound_method %c.ref, %.1 +// CHECK:STDOUT: %.loc20_13.1: init i32 = call %.loc20_11(%c.ref) +// CHECK:STDOUT: %.loc20_15: i32 = value_of_initializer %.loc20_13.1 +// CHECK:STDOUT: %.loc20_13.2: i32 = converted %.loc20_13.1, %.loc20_15 +// CHECK:STDOUT: return %.loc20_13.2 // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/impl/self_in_signature.carbon b/toolchain/check/testdata/impl/self_in_signature.carbon new file mode 100644 index 000000000000..a87608e298b1 --- /dev/null +++ b/toolchain/check/testdata/impl/self_in_signature.carbon @@ -0,0 +1,174 @@ +// 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 UseSelf { + fn F[self: Self](x: Self) -> Self; +} + +class C {} + +impl C as UseSelf { + // TODO: Use `Self` below once it's supported. + fn F[self: C](x: C) -> C { return {}; } +} + +interface SelfNested { + fn F(x: (Self*, {.x: Self, .y: i32})); +} + +impl C as SelfNested { + fn F(x: (C*, {.x: C, .y: i32})); +} + +// CHECK:STDOUT: --- self_in_signature.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: type = interface_type @UseSelf [template] +// CHECK:STDOUT: %.2: type = assoc_entity_type @UseSelf, [template] +// CHECK:STDOUT: %.3: in UseSelf> = assoc_entity element0, @UseSelf.%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: %.7: C = struct_value () [template] +// CHECK:STDOUT: %.8: = interface_witness (@impl.1.%F) [template] +// CHECK:STDOUT: %.9: type = interface_type @SelfNested [template] +// CHECK:STDOUT: %.10: type = ptr_type Self [symbolic] +// CHECK:STDOUT: %.11: type = struct_type {.x: Self, .y: i32} [symbolic] +// CHECK:STDOUT: %.12: type = tuple_type (type, type) [template] +// CHECK:STDOUT: %.13: type = tuple_type (Self*, {.x: Self, .y: i32}) [symbolic] +// CHECK:STDOUT: %.14: type = assoc_entity_type @SelfNested, [template] +// CHECK:STDOUT: %.15: in SelfNested> = assoc_entity element0, @SelfNested.%F [template] +// CHECK:STDOUT: %.16: type = ptr_type C [template] +// CHECK:STDOUT: %.17: type = struct_type {.x: C, .y: i32} [template] +// CHECK:STDOUT: %.18: type = tuple_type (C*, {.x: C, .y: i32}) [template] +// CHECK:STDOUT: %.19: = interface_witness (@impl.2.%F) [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .UseSelf = %UseSelf.decl +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: .SelfNested = %SelfNested.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %UseSelf.decl: type = interface_decl @UseSelf [template = constants.%.1] {} +// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} +// CHECK:STDOUT: impl_decl @impl.1 { +// CHECK:STDOUT: %C.ref.loc13: type = name_ref C, %C.decl [template = constants.%C] +// CHECK:STDOUT: %UseSelf.ref: type = name_ref UseSelf, %UseSelf.decl [template = constants.%.1] +// CHECK:STDOUT: } +// CHECK:STDOUT: %SelfNested.decl: type = interface_decl @SelfNested [template = constants.%.9] {} +// CHECK:STDOUT: impl_decl @impl.2 { +// CHECK:STDOUT: %C.ref.loc22: type = name_ref C, %C.decl [template = constants.%C] +// CHECK:STDOUT: %SelfNested.ref: type = name_ref SelfNested, %SelfNested.decl [template = constants.%.9] +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @UseSelf { +// CHECK:STDOUT: %Self: UseSelf = bind_symbolic_name Self [symbolic] +// CHECK:STDOUT: %F: = fn_decl @F.1 [template] { +// CHECK:STDOUT: %Self.ref.loc8_14: UseSelf = name_ref Self, %Self [symbolic = %Self] +// CHECK:STDOUT: %.loc8_14.1: type = facet_type_access %Self.ref.loc8_14 [symbolic = %Self] +// CHECK:STDOUT: %.loc8_14.2: type = converted %Self.ref.loc8_14, %.loc8_14.1 [symbolic = %Self] +// CHECK:STDOUT: %self.loc8_8.1: Self = param self +// CHECK:STDOUT: %self.loc8_8.2: Self = bind_name self, %self.loc8_8.1 +// CHECK:STDOUT: %Self.ref.loc8_23: UseSelf = name_ref Self, %Self [symbolic = %Self] +// CHECK:STDOUT: %.loc8_23.1: type = facet_type_access %Self.ref.loc8_23 [symbolic = %Self] +// CHECK:STDOUT: %.loc8_23.2: type = converted %Self.ref.loc8_23, %.loc8_23.1 [symbolic = %Self] +// CHECK:STDOUT: %x.loc8_20.1: Self = param x +// CHECK:STDOUT: %x.loc8_20.2: Self = bind_name x, %x.loc8_20.1 +// CHECK:STDOUT: %Self.ref.loc8_32: UseSelf = name_ref Self, %Self [symbolic = %Self] +// CHECK:STDOUT: %.loc8_32.1: type = facet_type_access %Self.ref.loc8_32 [symbolic = %Self] +// CHECK:STDOUT: %.loc8_32.2: type = converted %Self.ref.loc8_32, %.loc8_32.1 [symbolic = %Self] +// CHECK:STDOUT: %return.var: ref Self = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc8_36: in UseSelf> = assoc_entity element0, %F [template = constants.%.3] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .F = %.loc8_36 +// CHECK:STDOUT: witness = (%F) +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @SelfNested { +// CHECK:STDOUT: %Self: SelfNested = bind_symbolic_name Self [symbolic] +// CHECK:STDOUT: %F: = fn_decl @F.3 [template] { +// CHECK:STDOUT: %Self.ref.loc19_12: SelfNested = name_ref Self, %Self [symbolic = %Self] +// CHECK:STDOUT: %.loc19_16.1: type = facet_type_access %Self.ref.loc19_12 [symbolic = %Self] +// CHECK:STDOUT: %.loc19_12: type = converted %Self.ref.loc19_12, %.loc19_16.1 [symbolic = %Self] +// CHECK:STDOUT: %.loc19_16.2: type = ptr_type Self [symbolic = constants.%.10] +// CHECK:STDOUT: %Self.ref.loc19_24: SelfNested = name_ref Self, %Self [symbolic = %Self] +// CHECK:STDOUT: %.loc19_24.1: type = facet_type_access %Self.ref.loc19_24 [symbolic = %Self] +// CHECK:STDOUT: %.loc19_24.2: type = converted %Self.ref.loc19_24, %.loc19_24.1 [symbolic = %Self] +// CHECK:STDOUT: %.loc19_37: type = struct_type {.x: Self, .y: i32} [symbolic = constants.%.11] +// CHECK:STDOUT: %.loc19_38.1: (type, type) = tuple_literal (%.loc19_16.2, %.loc19_37) +// CHECK:STDOUT: %.loc19_38.2: type = converted %.loc19_38.1, constants.%.13 [symbolic = constants.%.13] +// CHECK:STDOUT: %x.loc19_8.1: (Self*, {.x: Self, .y: i32}) = param x +// CHECK:STDOUT: %x.loc19_8.2: (Self*, {.x: Self, .y: i32}) = bind_name x, %x.loc19_8.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc19_40: in SelfNested> = assoc_entity element0, %F [template = constants.%.15] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .F = %.loc19_40 +// CHECK:STDOUT: witness = (%F) +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: impl @impl.1: C as UseSelf { +// CHECK:STDOUT: %F: = fn_decl @F.2 [template] { +// CHECK:STDOUT: %C.ref.loc15_14: type = name_ref C, file.%C.decl [template = constants.%C] +// CHECK:STDOUT: %self.loc15_8.1: C = param self +// CHECK:STDOUT: %self.loc15_8.2: C = bind_name self, %self.loc15_8.1 +// CHECK:STDOUT: %C.ref.loc15_20: type = name_ref C, file.%C.decl [template = constants.%C] +// CHECK:STDOUT: %x.loc15_17.1: C = param x +// CHECK:STDOUT: %x.loc15_17.2: C = bind_name x, %x.loc15_17.1 +// CHECK:STDOUT: %C.ref.loc15_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 (%F) [template = constants.%.8] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: witness = %.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: impl @impl.2: C as SelfNested { +// CHECK:STDOUT: %F: = fn_decl @F.4 [template] { +// CHECK:STDOUT: %C.ref.loc23_12: type = name_ref C, file.%C.decl [template = constants.%C] +// CHECK:STDOUT: %.loc23_13: type = ptr_type C [template = constants.%.16] +// CHECK:STDOUT: %C.ref.loc23_21: type = name_ref C, file.%C.decl [template = constants.%C] +// CHECK:STDOUT: %.loc23_31: type = struct_type {.x: C, .y: i32} [template = constants.%.17] +// CHECK:STDOUT: %.loc23_32.1: (type, type) = tuple_literal (%.loc23_13, %.loc23_31) +// CHECK:STDOUT: %.loc23_32.2: type = converted %.loc23_32.1, constants.%.18 [template = constants.%.18] +// CHECK:STDOUT: %x.loc23_8.1: (C*, {.x: C, .y: i32}) = param x +// CHECK:STDOUT: %x.loc23_8.2: (C*, {.x: C, .y: i32}) = bind_name x, %x.loc23_8.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: %.1: = interface_witness (%F) [template = constants.%.19] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: witness = %.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F.1[@UseSelf.%self.loc8_8.2: Self](@UseSelf.%x.loc8_20.2: Self) -> Self; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F.2[@impl.1.%self.loc15_8.2: C](@impl.1.%x.loc15_17.2: C) -> @impl.1.%return.var: C { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc15_38.1: {} = struct_literal () +// CHECK:STDOUT: %.loc15_38.2: init C = class_init (), @impl.1.%return.var [template = constants.%.7] +// CHECK:STDOUT: %.loc15_38.3: init C = converted %.loc15_38.1, %.loc15_38.2 [template = constants.%.7] +// CHECK:STDOUT: return %.loc15_38.3 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F.3(@SelfNested.%x.loc19_8.2: (Self*, {.x: Self, .y: i32})); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F.4(@impl.2.%x.loc23_8.2: (C*, {.x: C, .y: i32})); +// CHECK:STDOUT: diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index 921100542c7b..573f2fee9d05 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -87,6 +87,15 @@ auto FileContext::GetGlobal(SemIR::InstId inst_id) -> llvm::Value* { auto FileContext::BuildFunctionDecl(SemIR::FunctionId function_id) -> llvm::Function* { const auto& function = sem_ir().functions().Get(function_id); + + // Don't lower associated functions. + // TODO: We shouldn't lower any function that has generic parameters. + if (sem_ir().insts().Is( + sem_ir().name_scopes().GetInstIdIfValid( + function.enclosing_scope_id))) { + return nullptr; + } + const bool has_return_slot = function.return_slot_id.is_valid(); auto implicit_param_refs = sem_ir().inst_blocks().Get(function.implicit_param_refs_id); diff --git a/toolchain/lower/testdata/impl/assoc_fn_alias.carbon b/toolchain/lower/testdata/impl/assoc_fn_alias.carbon index c83d0400c7ec..7ba45c3e54a8 100644 --- a/toolchain/lower/testdata/impl/assoc_fn_alias.carbon +++ b/toolchain/lower/testdata/impl/assoc_fn_alias.carbon @@ -4,10 +4,8 @@ // // AUTOUPDATE -class A; - interface I { - fn F[self: A]() -> i32; + fn F[self: Self]() -> i32; } class A { @@ -28,15 +26,13 @@ fn Call(a: A) -> i32 { // 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: define i32 @F(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: %F = call i32 @F(ptr %a) +// CHECK:STDOUT: ret i32 %F // CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/impl/extend_impl.carbon b/toolchain/lower/testdata/impl/extend_impl.carbon index f55e5079d071..661238fe92dc 100644 --- a/toolchain/lower/testdata/impl/extend_impl.carbon +++ b/toolchain/lower/testdata/impl/extend_impl.carbon @@ -27,18 +27,16 @@ fn InstanceAccess(a: A) -> i32 { // 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: define i32 @F() { // 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: %F = call i32 @F() +// CHECK:STDOUT: ret i32 %F // 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: %F = call i32 @F() +// CHECK:STDOUT: ret i32 %F // CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/impl/instance_method.carbon b/toolchain/lower/testdata/impl/instance_method.carbon index f324f34d59b9..0714dc963763 100644 --- a/toolchain/lower/testdata/impl/instance_method.carbon +++ b/toolchain/lower/testdata/impl/instance_method.carbon @@ -7,8 +7,7 @@ class A; interface GetSelf { - // TODO: Use `Self` once that works. - fn Get[addr self: A*]() -> A*; + fn Get[addr self: Self*]() -> Self*; } class A { @@ -26,13 +25,11 @@ fn Call(a: A*) -> A* { // 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: define ptr @Get(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: %Get = call ptr @Get(ptr %a) +// CHECK:STDOUT: ret ptr %Get // CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/interface/assoc.carbon b/toolchain/lower/testdata/interface/assoc.carbon index ae427a84623e..7585a28dc4e3 100644 --- a/toolchain/lower/testdata/interface/assoc.carbon +++ b/toolchain/lower/testdata/interface/assoc.carbon @@ -13,8 +13,6 @@ fn F() { I.Assoc; } // CHECK:STDOUT: ; ModuleID = 'assoc.carbon' // CHECK:STDOUT: source_filename = "assoc.carbon" // CHECK:STDOUT: -// CHECK:STDOUT: declare void @Assoc() -// CHECK:STDOUT: // CHECK:STDOUT: define void @F() { // CHECK:STDOUT: ret void // CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/interface/basic.carbon b/toolchain/lower/testdata/interface/basic.carbon index 9fb909e13883..adddde866517 100644 --- a/toolchain/lower/testdata/interface/basic.carbon +++ b/toolchain/lower/testdata/interface/basic.carbon @@ -20,8 +20,6 @@ fn G(T: J) {} // CHECK:STDOUT: ; ModuleID = 'basic.carbon' // CHECK:STDOUT: source_filename = "basic.carbon" // CHECK:STDOUT: -// CHECK:STDOUT: declare void @Assoc() -// CHECK:STDOUT: // CHECK:STDOUT: define void @F() { // CHECK:STDOUT: ret void // CHECK:STDOUT: } diff --git a/toolchain/sem_ir/id_kind.h b/toolchain/sem_ir/id_kind.h index 14bc2a753a61..266c310466de 100644 --- a/toolchain/sem_ir/id_kind.h +++ b/toolchain/sem_ir/id_kind.h @@ -20,8 +20,15 @@ class TypeEnum { static_assert(NumValues <= 256, "Too many types for raw enum."); +// TODO: Works around a clang-format bug: +// https://github.com/llvm/llvm-project/issues/85476 +#define CARBON_OPEN_ENUM [[clang::enum_extensibility(open)]] + // The underlying raw enumeration type. - enum class RawEnumType : uint8_t { + // + // The enum_extensibility attribute indicates that this enum is intended to + // take values that do not correspond to its declared enumerators. + enum class CARBON_OPEN_ENUM RawEnumType : uint8_t { // The first sizeof...(Types) values correspond to the types. // An explicitly invalid value. @@ -33,6 +40,8 @@ class TypeEnum { None, }; +#undef CARBON_OPEN_ENUM + // Accesses the type given an enum value. template requires(K != RawEnumType::Invalid) diff --git a/toolchain/sem_ir/inst.h b/toolchain/sem_ir/inst.h index 07b239447fc7..3563b92ba6b8 100644 --- a/toolchain/sem_ir/inst.h +++ b/toolchain/sem_ir/inst.h @@ -230,6 +230,12 @@ class Inst : public Printable { // such argument. auto arg1() const -> int32_t { return arg1_; } + // Sets the arguments of this instruction. + auto SetArgs(int32_t arg0, int32_t arg1) { + arg0_ = arg0; + arg1_ = arg1; + } + auto Print(llvm::raw_ostream& out) const -> void; private: