diff --git a/toolchain/check/call.cpp b/toolchain/check/call.cpp index 2146045d5548..6b58f1c48e34 100644 --- a/toolchain/check/call.cpp +++ b/toolchain/check/call.cpp @@ -263,13 +263,12 @@ auto PerformCallToFunction(Context& context, SemIR::LocId loc_id, switch (callee.special_function_kind) { case SemIR::Function::SpecialFunctionKind::Thunk: { // If we're about to form a direct call to a thunk, inline it. - auto callee_inst_id = - context.sem_ir().thunks().Get(callee.thunk_id()).callee_id; - LoadImportRef(context, callee_inst_id); + const auto& thunk_info = context.sem_ir().thunks().Get(callee.thunk_id()); + LoadImportRef(context, thunk_info.callee_id); // Name the thunk target within the enclosing scope of the thunk. auto thunk_ref_id = - BuildNameRef(context, loc_id, callee.name_id, callee_inst_id, + BuildNameRef(context, loc_id, callee.name_id, thunk_info.callee_id, callee_function.enclosing_specific_id); auto param_pattern_ids = @@ -279,9 +278,10 @@ auto PerformCallToFunction(Context& context, SemIR::LocId loc_id, // This recurses back into `PerformCall`. However, we never form a thunk // to a thunk, so we only recurse once. - return PerformThunkCall( - context, loc_id, callee_function.function_id, param_pattern_ids, - context.inst_blocks().Get(converted_args_id), thunk_ref_id); + return PerformThunkCall(context, loc_id, callee_function.function_id, + param_pattern_ids, + context.inst_blocks().Get(converted_args_id), + thunk_ref_id, thunk_info.override_self_type_id); } case SemIR::Function::SpecialFunctionKind::HasCppThunk: { diff --git a/toolchain/check/convert.cpp b/toolchain/check/convert.cpp index ec25595e7f8a..370e654eddce 100644 --- a/toolchain/check/convert.cpp +++ b/toolchain/check/convert.cpp @@ -929,10 +929,19 @@ static auto ConvertStructToClass(Context& context, SemIR::StructType src_type, is_partial ? nullptr : &dest_type); } -// An inheritance path is a sequence of `BaseDecl`s and corresponding base types -// in order from derived to base. -using InheritancePath = - llvm::SmallVector>; +// Represents an edge in the inheritance graph, created by a `base` declaration. +struct InheritanceEdge { + // The class type of the class containing the `base` declaration. + SemIR::TypeId derived_type_id; + // The `base` declaration itself. + SemIR::InstId base_decl_id; + // The type of the `base` declaration. + SemIR::TypeId base_type_id; +}; + +// An inheritance path is a sequence of `InheritanceEdge`s, in order from +// derived to base. +using InheritancePath = llvm::SmallVector; // Computes the inheritance path from class `derived_id` to class `base_id`. // Returns nullopt if `derived_id` is not a class derived from `base_id`. @@ -964,7 +973,9 @@ static auto ComputeInheritancePath(Context& context, SemIR::LocId loc_id, result = std::nullopt; break; } - result->push_back({derived_class.base_id, base_type_id}); + result->push_back({.derived_type_id = derived_id, + .base_decl_id = derived_class.base_id, + .base_type_id = base_type_id}); derived_id = base_type_id; } return result; @@ -985,7 +996,7 @@ static auto ConvertDerivedToBase(Context& context, SemIR::LocId loc_id, .second; // Add a series of `.base` accesses. - for (auto [base_id, base_type_id] : path) { + for (auto [_, base_id, base_type_id] : path) { auto base_decl = context.insts().GetAs(base_id); value_id = AddInst( context, loc_id, @@ -2360,6 +2371,66 @@ auto DiscardExpr(Context& context, SemIR::InstId expr_id) -> void { // TODO: This will eventually need to do some "do not discard" analysis. } +auto UnsafeUndoConvert(Context& context, SemIR::LocId loc_id, + SemIR::InstId expr_id, SemIR::TypeId type_id) + -> SemIR::InstId { + auto source_type_id = context.insts().Get(expr_id).type_id(); + // Preserve type qualifiers. + auto quals = + context.types().GetUnqualifiedTypeAndQualifiers(source_type_id).second; + auto path = ComputeInheritancePath(context, loc_id, type_id, source_type_id); + CARBON_CHECK(path.has_value(), + "only derived-to-base conversions can currently be undone"); + + if (path->empty()) { + // No-op conversion. + return expr_id; + } + + auto source_category = SemIR::GetExprCategory(context.sem_ir(), expr_id); + switch (source_category) { + case SemIR::ExprCategory::NotExpr: + case SemIR::ExprCategory::Error: + case SemIR::ExprCategory::Pattern: + case SemIR::ExprCategory::ReprInitializing: + case SemIR::ExprCategory::InPlaceInitializing: + case SemIR::ExprCategory::Mixed: + case SemIR::ExprCategory::Dependent: + case SemIR::ExprCategory::RefTagged: + CARBON_FATAL("Unsupported category {0} for base-to-derived conversion", + source_category); + case SemIR::ExprCategory::EphemeralRef: + case SemIR::ExprCategory::DurableRef: + break; + case SemIR::ExprCategory::Value: + if (auto ref_expr_id = TryMakeValueAsRef(context, expr_id); + ref_expr_id.has_value()) { + expr_id = ref_expr_id; + } else { + CARBON_FATAL("Types must have pointer value-representation"); + } + break; + } + + auto result_id = expr_id; + for (auto edge : llvm::reverse(*path)) { + auto base_decl = context.insts().GetAs(edge.base_decl_id); + result_id = AddInst( + context, loc_id, + {.type_id = GetQualifiedType(context, edge.derived_type_id, quals), + .element_id = result_id, + .index = base_decl.index}); + } + + if (source_category == SemIR::ExprCategory::Value) { + result_id = AddInst( + context, loc_id, + {.type_id = GetQualifiedType(context, type_id, quals), + .value_id = result_id}); + } + return result_id; +} + } // namespace Carbon::Check // NOLINTEND(misc-no-recursion) diff --git a/toolchain/check/convert.h b/toolchain/check/convert.h index 56447349a1d6..bb3a14569fdc 100644 --- a/toolchain/check/convert.h +++ b/toolchain/check/convert.h @@ -265,6 +265,13 @@ auto ReturnExprAsForm(Context& context, SemIR::LocId loc_id, // Handles an expression whose result value is unused. auto DiscardExpr(Context& context, SemIR::InstId expr_id) -> void; +// Given that `expr_id` is the result of a conversion from an expression of +// type `type_id`, this undoes that conversion. Currently, this only supports +// the case where the original conversion was a derived-to-base conversion. +auto UnsafeUndoConvert(Context& context, SemIR::LocId loc_id, + SemIR::InstId expr_id, SemIR::TypeId type_id) + -> SemIR::InstId; + } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_CONVERT_H_ diff --git a/toolchain/check/cpp/generate_ast.cpp b/toolchain/check/cpp/generate_ast.cpp index d932b5f23dff..5737d203ffa9 100644 --- a/toolchain/check/cpp/generate_ast.cpp +++ b/toolchain/check/cpp/generate_ast.cpp @@ -697,10 +697,26 @@ auto CarbonExternalASTSource::CompleteType(clang::TagDecl* tag_decl) -> void { continue; } - auto callee_function = - GetCalleeAsFunction(context_->sem_ir(), vtable_entry_id); - const SemIR::Function& function = - context_->functions().Get(callee_function.function_id); + // The Carbon vtable entry for a function override is a thunk, which wraps + // the function declaration to expose the signature of the overridden + // virtual function. Here we want to generate a C++ method declaration for + // the override, so we need to look through the thunk wrapping. + const auto [callee_function, function] = + [&]() -> std::pair { + auto vtable_callee_function = + GetCalleeAsFunction(context_->sem_ir(), vtable_entry_id); + const SemIR::Function& vtable_function = + context_->functions().Get(vtable_callee_function.function_id); + if (!vtable_function.thunk_id().has_value()) { + return {vtable_callee_function, vtable_function}; + } + auto vtable_thunk = + context_->sem_ir().thunks().Get(vtable_function.thunk_id()); + auto callee_function = + GetCalleeAsFunction(context_->sem_ir(), vtable_thunk.callee_id); + return {callee_function, + context_->functions().Get(callee_function.function_id)}; + }(); // If this is a member of a base class, nothing to do here. if (function.parent_scope_id != class_info.scope_id) { diff --git a/toolchain/check/deferred_definition_worklist.h b/toolchain/check/deferred_definition_worklist.h index ca6acd80dbe7..41ba19ce75e4 100644 --- a/toolchain/check/deferred_definition_worklist.h +++ b/toolchain/check/deferred_definition_worklist.h @@ -51,6 +51,7 @@ class DeferredDefinitionWorklist { SemIR::FunctionId function_id; SemIR::InstId decl_id; SemIR::InstId callee_id; + SemIR::TypeId override_self_type_id; }; // A worklist task that indicates we should define a thunk that was previously diff --git a/toolchain/check/function.cpp b/toolchain/check/function.cpp index 2138663e3cc5..7d284fdeb3c1 100644 --- a/toolchain/check/function.cpp +++ b/toolchain/check/function.cpp @@ -304,12 +304,10 @@ auto CheckFunctionTypeMatches(Context& context, const SemIR::Function& new_function, const SemIR::Function& prev_function, SemIR::SpecificId prev_specific_id, - bool check_syntax, - SemIR::TypeId self_type_override_id, - bool diagnose) -> bool { + bool check_syntax, bool diagnose) -> bool { if (!CheckRedeclParamsMatch(context, DeclParams(new_function), DeclParams(prev_function), prev_specific_id, - diagnose, check_syntax, self_type_override_id)) { + diagnose, check_syntax)) { return false; } if (!CheckFunctionReturnTypeMatches(context, new_function, prev_function, diff --git a/toolchain/check/function.h b/toolchain/check/function.h index f6ed7c75d114..bab2bc73b964 100644 --- a/toolchain/check/function.h +++ b/toolchain/check/function.h @@ -71,16 +71,11 @@ auto CheckFunctionReturnTypeMatches(Context& context, // // `check_syntax` is false if the redeclaration can be called via a thunk with // implicit conversions from the original declaration. -// -// If `self_type_override_id` is specified, the self type is checked against -// that type instead of the type from `prev_function`. This is used to check -// virtual function overrides. -auto CheckFunctionTypeMatches( - Context& context, const SemIR::Function& new_function, - const SemIR::Function& prev_function, SemIR::SpecificId prev_specific_id, - bool check_syntax, - SemIR::TypeId self_type_override_id = SemIR::TypeId::None, - bool diagnose = true) -> bool; +auto CheckFunctionTypeMatches(Context& context, + const SemIR::Function& new_function, + const SemIR::Function& prev_function, + SemIR::SpecificId prev_specific_id, + bool check_syntax, bool diagnose = true) -> bool; inline auto CheckFunctionTypeMatches(Context& context, const SemIR::Function& new_function, diff --git a/toolchain/check/merge.cpp b/toolchain/check/merge.cpp index 1e3fed25ee5f..95712c01c235 100644 --- a/toolchain/check/merge.cpp +++ b/toolchain/check/merge.cpp @@ -206,8 +206,7 @@ static auto CheckRedeclParam(Context& context, bool is_implicit_param, SemIR::InstId orig_new_param_pattern_id, SemIR::InstId orig_prev_param_pattern_id, SemIR::SpecificId prev_specific_id, bool diagnose, - bool check_syntax, - SemIR::TypeId self_type_override_id) -> bool { + bool check_syntax) -> bool { CARBON_DIAGNOSTIC( RedeclParamPrevious, Note, "previous declaration's corresponding {0:implicit |}parameter here", @@ -304,15 +303,6 @@ static auto CheckRedeclParam(Context& context, bool is_implicit_param, .Get(prev_any_binding_pattern.entity_name_id) .name_id; - // If this is the self parameter, and we have a type override for it, - // check against that type instead. - if (new_name_id == SemIR::NameId::SelfValue && - prev_name_id == SemIR::NameId::SelfValue && - self_type_override_id.has_value()) { - check_type = false; - check_for_type_mismatch_with(self_type_override_id); - } - if (new_any_binding_pattern.kind == SemIR::WrapperBindingPattern::Kind) { // The subpattern handling will take care of checking for type @@ -347,8 +337,7 @@ static auto CheckRedeclParams(Context& context, SemIR::LocId new_decl_loc_id, SemIR::InstBlockId prev_param_patterns_id, bool is_implicit_param, SemIR::SpecificId prev_specific_id, bool diagnose, - bool check_syntax, - SemIR::TypeId self_type_override_id) -> bool { + bool check_syntax) -> bool { // This will often occur for empty params. if (new_param_patterns_id == prev_param_patterns_id) { return true; @@ -406,8 +395,7 @@ static auto CheckRedeclParams(Context& context, SemIR::LocId new_decl_loc_id, llvm::enumerate(new_param_pattern_ids, prev_param_pattern_ids)) { if (!CheckRedeclParam(context, is_implicit_param, index, new_param_pattern_id, prev_param_pattern_id, - prev_specific_id, diagnose, check_syntax, - self_type_override_id)) { + prev_specific_id, diagnose, check_syntax)) { return false; } } @@ -530,8 +518,7 @@ static auto CheckRedeclParamSyntax(Context& context, auto CheckRedeclParamsMatch(Context& context, const DeclParams& new_entity, const DeclParams& prev_entity, SemIR::SpecificId prev_specific_id, bool diagnose, - bool check_syntax, - SemIR::TypeId self_type_override_id) -> bool { + bool check_syntax) -> bool { if (EntityHasParamError(context, new_entity) || EntityHasParamError(context, prev_entity)) { return false; @@ -539,17 +526,15 @@ auto CheckRedeclParamsMatch(Context& context, const DeclParams& new_entity, if (!CheckRedeclParams( context, new_entity.loc_id, new_entity.implicit_param_patterns_id, prev_entity.loc_id, prev_entity.implicit_param_patterns_id, - /*is_implicit_param=*/true, prev_specific_id, diagnose, check_syntax, - self_type_override_id)) { + /*is_implicit_param=*/true, prev_specific_id, diagnose, + check_syntax)) { return false; } - // Don't forward `self_type_override_id` here because it's extra cost, and - // `self` is only allowed in implicit params. if (!CheckRedeclParams(context, new_entity.loc_id, new_entity.param_patterns_id, prev_entity.loc_id, prev_entity.param_patterns_id, /*is_implicit_param=*/false, prev_specific_id, - diagnose, check_syntax, SemIR::TypeId::None)) { + diagnose, check_syntax)) { return false; } if (check_syntax && diff --git a/toolchain/check/merge.h b/toolchain/check/merge.h index d2809ca08f77..5b4a6254f809 100644 --- a/toolchain/check/merge.h +++ b/toolchain/check/merge.h @@ -95,14 +95,11 @@ struct DeclParams { // Checks that the parameters in a redeclaration of an entity match the // parameters in the prior declaration. If not, produces a diagnostic if -// `diagnose` is true, and returns false. If `self_type_override_id` is -// specified, the type of `self` will be compared against that type instead of -// the `self` type from `prev_entity`. -auto CheckRedeclParamsMatch( - Context& context, const DeclParams& new_entity, - const DeclParams& prev_entity, SemIR::SpecificId prev_specific_id, - bool diagnose, bool check_syntax, - SemIR::TypeId self_type_override_id = SemIR::TypeId::None) -> bool; +// `diagnose` is true, and returns false. +auto CheckRedeclParamsMatch(Context& context, const DeclParams& new_entity, + const DeclParams& prev_entity, + SemIR::SpecificId prev_specific_id, bool diagnose, + bool check_syntax) -> bool; inline auto CheckRedeclParamsMatch(Context& context, const DeclParams& new_entity, diff --git a/toolchain/check/testdata/class/method/virtual.carbon b/toolchain/check/testdata/class/method/virtual.carbon index 243ce2379143..09ee24d92a26 100644 --- a/toolchain/check/testdata/class/method/virtual.carbon +++ b/toolchain/check/testdata/class/method/virtual.carbon @@ -309,18 +309,18 @@ class T2 { library "[[@TEST_NAME]]"; base class T1 { - // CHECK:STDERR: fail_ref_self_mismatch.carbon:[[@LINE+3]]:17: error: value expression passed to reference parameter [ValueForRefParam] - // CHECK:STDERR: virtual fn F1[self: Self](); - // CHECK:STDERR: ^~~~~~~~~~ virtual fn F1[self: Self](); } class T2 { extend base: T1; + // CHECK:STDERR: fail_ref_self_mismatch.carbon:[[@LINE+10]]:3: error: value expression passed to reference parameter [ValueForRefParam] + // CHECK:STDERR: override fn F1[ref self: Self](); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_ref_self_mismatch.carbon:[[@LINE+7]]:18: note: initializing function parameter [InCallToFunctionParam] // CHECK:STDERR: override fn F1[ref self: Self](); // CHECK:STDERR: ^~~~~~~~~~~~~~ - // CHECK:STDERR: fail_ref_self_mismatch.carbon:[[@LINE-8]]:3: note: while building thunk to match the signature of this function [ThunkSignature] + // CHECK:STDERR: fail_ref_self_mismatch.carbon:[[@LINE-11]]:3: note: while building thunk to match the signature of this function [ThunkSignature] // CHECK:STDERR: virtual fn F1[self: Self](); // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: @@ -648,11 +648,13 @@ class T2(G2:! type) { // CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete] // CHECK:STDOUT: %Derived.elem: type = unbound_element_type %Derived, %Base [concrete] // CHECK:STDOUT: %pattern_type.746: type = pattern_type %Derived [concrete] -// CHECK:STDOUT: %Derived.H.type: type = fn_type @Derived.H [concrete] +// CHECK:STDOUT: %Derived.H.type.1f42d4.1: type = fn_type @Derived.H.loc9_30.1 [concrete] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] -// CHECK:STDOUT: %Derived.H: %Derived.H.type = struct_value () [concrete] +// CHECK:STDOUT: %Derived.H.a6796b.1: %Derived.H.type.1f42d4.1 = struct_value () [concrete] // CHECK:STDOUT: %Base.H.type: type = fn_type @Base.H [concrete] // CHECK:STDOUT: %Base.H: %Base.H.type = struct_value () [concrete] +// CHECK:STDOUT: %Derived.H.type.1f42d4.2: type = fn_type @Derived.H.loc9_30.2 [concrete] +// CHECK:STDOUT: %Derived.H.a6796b.2: %Derived.H.type.1f42d4.2 = struct_value () [concrete] // CHECK:STDOUT: %Derived.vtable_decl: ref %ptr.454 = vtable_decl @Derived.vtable [concrete] // CHECK:STDOUT: %struct_type.base.507: type = struct_type {.base: %Base} [concrete] // CHECK:STDOUT: %complete_type.d6b: = complete_type_witness %struct_type.base.507 [concrete] @@ -676,6 +678,7 @@ class T2(G2:! type) { // CHECK:STDOUT: import Modifiers//default // CHECK:STDOUT: } // CHECK:STDOUT: %Modifiers.Base: type = import_ref Modifiers//default, Base, loaded [concrete = constants.%Base] +// CHECK:STDOUT: %Base.H.decl: %Base.H.type = fn_decl @Base.H [concrete = constants.%Base.H] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -686,7 +689,7 @@ class T2(G2:! type) { // CHECK:STDOUT: %Modifiers.ref: = name_ref Modifiers, imports.%Modifiers [concrete = imports.%Modifiers] // CHECK:STDOUT: %Base.ref: type = name_ref Base, imports.%Modifiers.Base [concrete = constants.%Base] // CHECK:STDOUT: %.loc8: %Derived.elem = base_decl %Base.ref, element0 [concrete] -// CHECK:STDOUT: %Derived.H.decl: %Derived.H.type = fn_decl @Derived.H [concrete = constants.%Derived.H] { +// CHECK:STDOUT: %Derived.H.decl.loc9_30.1: %Derived.H.type.1f42d4.1 = fn_decl @Derived.H.loc9_30.1 [concrete = constants.%Derived.H.a6796b.1] { // CHECK:STDOUT: %self.param_patt: %pattern_type.746 = value_param_pattern [concrete] // CHECK:STDOUT: %self.patt: %pattern_type.746 = at_binding_pattern self, %self.param_patt [concrete] // CHECK:STDOUT: } { @@ -694,6 +697,11 @@ class T2(G2:! type) { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Derived [concrete = constants.%Derived] // CHECK:STDOUT: %self: %Derived = value_binding self, %self.param // CHECK:STDOUT: } +// CHECK:STDOUT: %Derived.H.decl.loc9_30.2: %Derived.H.type.1f42d4.2 = fn_decl @Derived.H.loc9_30.2 [concrete = constants.%Derived.H.a6796b.2] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } // CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @Derived.vtable [concrete = constants.%Derived.vtable_decl] // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%struct_type.base.507 [concrete = constants.%complete_type.d6b] // CHECK:STDOUT: complete_type_witness = %complete_type @@ -703,7 +711,7 @@ class T2(G2:! type) { // CHECK:STDOUT: .Self = constants.%Derived // CHECK:STDOUT: .Modifiers = // CHECK:STDOUT: .base = %.loc8 -// CHECK:STDOUT: .H [hidden] = %Derived.H.decl +// CHECK:STDOUT: .H [hidden] = %Derived.H.decl.loc9_30.1 // CHECK:STDOUT: extend %Base.ref // CHECK:STDOUT: } // CHECK:STDOUT: @@ -712,10 +720,21 @@ class T2(G2:! type) { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Derived.vtable { -// CHECK:STDOUT: @Derived.%Derived.H.decl +// CHECK:STDOUT: @Derived.%Derived.H.decl.loc9_30.2 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: override fn @Derived.H(%self.param: %Derived); +// CHECK:STDOUT: override fn @Derived.H.loc9_30.1(%self.param: %Derived); +// CHECK:STDOUT: +// CHECK:STDOUT: override fn @Derived.H.loc9_30.2(%self.param: %Base) [thunk @Derived.%Derived.H.decl.loc9_30.1 for imports.%Base.H.decl] { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %H.ref: %Derived.H.type.1f42d4.1 = name_ref H, @Derived.%Derived.H.decl.loc9_30.1 [concrete = constants.%Derived.H.a6796b.1] +// CHECK:STDOUT: +// CHECK:STDOUT: %.loc9_30.1: ref %Derived = enclosing_class_access %.1, element0 +// CHECK:STDOUT: %.loc9_30.2: %Derived = acquire_value %.loc9_30.1 +// CHECK:STDOUT: %Derived.H.bound: = bound_method %.loc9_30.2, %H.ref +// CHECK:STDOUT: %Derived.H.call: init %empty_tuple.type = call %Derived.H.bound(%.loc9_30.2) +// CHECK:STDOUT: return +// CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Use() { // CHECK:STDOUT: !entry: @@ -994,17 +1013,22 @@ class T2(G2:! type) { // CHECK:STDOUT: --- abstract_impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { +// CHECK:STDOUT: %AbstractBase: type = class_type @AbstractBase [concrete] // CHECK:STDOUT: %AbstractBase.F.type: type = fn_type @AbstractBase.F [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %AbstractBase.F: %AbstractBase.F.type = struct_value () [concrete] +// CHECK:STDOUT: %AbstractIntermediate: type = class_type @AbstractIntermediate [concrete] // CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] // CHECK:STDOUT: %pattern_type.746: type = pattern_type %Derived [concrete] -// CHECK:STDOUT: %Derived.F.type: type = fn_type @Derived.F [concrete] -// CHECK:STDOUT: %Derived.F: %Derived.F.type = struct_value () [concrete] +// CHECK:STDOUT: %Derived.F.type.48cdab.1: type = fn_type @Derived.F.loc15_30.1 [concrete] +// CHECK:STDOUT: %Derived.F.bd5be1.1: %Derived.F.type.48cdab.1 = struct_value () [concrete] +// CHECK:STDOUT: %Derived.F.type.48cdab.2: type = fn_type @Derived.F.loc15_30.2 [concrete] +// CHECK:STDOUT: %Derived.F.bd5be1.2: %Derived.F.type.48cdab.2 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: -// CHECK:STDOUT: %Derived.F.decl: %Derived.F.type = fn_decl @Derived.F [concrete = constants.%Derived.F] { +// CHECK:STDOUT: %Derived.F.decl.loc15_30.1: %Derived.F.type.48cdab.1 = fn_decl @Derived.F.loc15_30.1 [concrete = constants.%Derived.F.bd5be1.1] { // CHECK:STDOUT: %self.param_patt: %pattern_type.746 = value_param_pattern [concrete] // CHECK:STDOUT: %self.patt: %pattern_type.746 = at_binding_pattern self, %self.param_patt [concrete] // CHECK:STDOUT: } { @@ -1012,6 +1036,11 @@ class T2(G2:! type) { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Derived [concrete = constants.%Derived] // CHECK:STDOUT: %self: %Derived = value_binding self, %self.param // CHECK:STDOUT: } +// CHECK:STDOUT: %Derived.F.decl.loc15_30.2: %Derived.F.type.48cdab.2 = fn_decl @Derived.F.loc15_30.2 [concrete = constants.%Derived.F.bd5be1.2] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl @@ -1020,7 +1049,7 @@ class T2(G2:! type) { // CHECK:STDOUT: .Self = constants.%Derived // CHECK:STDOUT: .AbstractIntermediate = // CHECK:STDOUT: .base = %.loc13 -// CHECK:STDOUT: .F [hidden] = %Derived.F.decl +// CHECK:STDOUT: .F [hidden] = %Derived.F.decl.loc15_30.1 // CHECK:STDOUT: extend %AbstractIntermediate.ref // CHECK:STDOUT: } // CHECK:STDOUT: @@ -1033,25 +1062,42 @@ class T2(G2:! type) { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Derived.vtable { -// CHECK:STDOUT: @Derived.%Derived.F.decl +// CHECK:STDOUT: @Derived.%Derived.F.decl.loc15_30.2 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: override fn @Derived.F(%self.param: %Derived); +// CHECK:STDOUT: override fn @Derived.F.loc15_30.1(%self.param: %Derived); +// CHECK:STDOUT: +// CHECK:STDOUT: override fn @Derived.F.loc15_30.2(%self.param: %AbstractBase) [thunk @Derived.%Derived.F.decl.loc15_30.1 for @AbstractBase.%AbstractBase.F.decl] { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %F.ref: %Derived.F.type.48cdab.1 = name_ref F, @Derived.%Derived.F.decl.loc15_30.1 [concrete = constants.%Derived.F.bd5be1.1] +// CHECK:STDOUT: +// CHECK:STDOUT: %.loc15_30.1: ref %AbstractIntermediate = enclosing_class_access %.loc5, element0 +// CHECK:STDOUT: %.loc15_30.2: ref %Derived = enclosing_class_access %.loc15_30.1, element0 +// CHECK:STDOUT: %.loc15_30.3: %Derived = acquire_value %.loc15_30.2 +// CHECK:STDOUT: %Derived.F.bound: = bound_method %.loc15_30.3, %F.ref +// CHECK:STDOUT: %Derived.F.call: init %empty_tuple.type = call %Derived.F.bound(%.loc15_30.3) +// CHECK:STDOUT: return +// CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- virtual_impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { +// CHECK:STDOUT: %VirtualBase: type = class_type @VirtualBase [concrete] // CHECK:STDOUT: %VirtualBase.F.type: type = fn_type @VirtualBase.F [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %VirtualBase.F: %VirtualBase.F.type = struct_value () [concrete] +// CHECK:STDOUT: %VirtualIntermediate: type = class_type @VirtualIntermediate [concrete] // CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] // CHECK:STDOUT: %pattern_type.746: type = pattern_type %Derived [concrete] -// CHECK:STDOUT: %Derived.F.type: type = fn_type @Derived.F [concrete] -// CHECK:STDOUT: %Derived.F: %Derived.F.type = struct_value () [concrete] +// CHECK:STDOUT: %Derived.F.type.48cdab.1: type = fn_type @Derived.F.loc15_30.1 [concrete] +// CHECK:STDOUT: %Derived.F.bd5be1.1: %Derived.F.type.48cdab.1 = struct_value () [concrete] +// CHECK:STDOUT: %Derived.F.type.48cdab.2: type = fn_type @Derived.F.loc15_30.2 [concrete] +// CHECK:STDOUT: %Derived.F.bd5be1.2: %Derived.F.type.48cdab.2 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: -// CHECK:STDOUT: %Derived.F.decl: %Derived.F.type = fn_decl @Derived.F [concrete = constants.%Derived.F] { +// CHECK:STDOUT: %Derived.F.decl.loc15_30.1: %Derived.F.type.48cdab.1 = fn_decl @Derived.F.loc15_30.1 [concrete = constants.%Derived.F.bd5be1.1] { // CHECK:STDOUT: %self.param_patt: %pattern_type.746 = value_param_pattern [concrete] // CHECK:STDOUT: %self.patt: %pattern_type.746 = at_binding_pattern self, %self.param_patt [concrete] // CHECK:STDOUT: } { @@ -1059,6 +1105,11 @@ class T2(G2:! type) { // CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Derived [concrete = constants.%Derived] // CHECK:STDOUT: %self: %Derived = value_binding self, %self.param // CHECK:STDOUT: } +// CHECK:STDOUT: %Derived.F.decl.loc15_30.2: %Derived.F.type.48cdab.2 = fn_decl @Derived.F.loc15_30.2 [concrete = constants.%Derived.F.bd5be1.2] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl @@ -1067,7 +1118,7 @@ class T2(G2:! type) { // CHECK:STDOUT: .Self = constants.%Derived // CHECK:STDOUT: .VirtualIntermediate = // CHECK:STDOUT: .base = %.loc13 -// CHECK:STDOUT: .F [hidden] = %Derived.F.decl +// CHECK:STDOUT: .F [hidden] = %Derived.F.decl.loc15_30.1 // CHECK:STDOUT: extend %VirtualIntermediate.ref // CHECK:STDOUT: } // CHECK:STDOUT: @@ -1080,10 +1131,22 @@ class T2(G2:! type) { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @Derived.vtable { -// CHECK:STDOUT: @Derived.%Derived.F.decl +// CHECK:STDOUT: @Derived.%Derived.F.decl.loc15_30.2 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: override fn @Derived.F(%self.param: %Derived); +// CHECK:STDOUT: override fn @Derived.F.loc15_30.1(%self.param: %Derived); +// CHECK:STDOUT: +// CHECK:STDOUT: override fn @Derived.F.loc15_30.2(%self.param: %VirtualBase) [thunk @Derived.%Derived.F.decl.loc15_30.1 for @VirtualBase.%VirtualBase.F.decl] { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %F.ref: %Derived.F.type.48cdab.1 = name_ref F, @Derived.%Derived.F.decl.loc15_30.1 [concrete = constants.%Derived.F.bd5be1.1] +// CHECK:STDOUT: +// CHECK:STDOUT: %.loc15_30.1: ref %VirtualIntermediate = enclosing_class_access %.loc5, element0 +// CHECK:STDOUT: %.loc15_30.2: ref %Derived = enclosing_class_access %.loc15_30.1, element0 +// CHECK:STDOUT: %.loc15_30.3: %Derived = acquire_value %.loc15_30.2 +// CHECK:STDOUT: %Derived.F.bound: = bound_method %.loc15_30.3, %F.ref +// CHECK:STDOUT: %Derived.F.call: init %empty_tuple.type = call %Derived.F.bound(%.loc15_30.3) +// CHECK:STDOUT: return +// CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: --- impl_conversion.carbon // CHECK:STDOUT: @@ -1098,6 +1161,7 @@ class T2(G2:! type) { // CHECK:STDOUT: %T2.as.ImplicitAs.impl.Convert: %T2.as.ImplicitAs.impl.Convert.type = struct_value () [concrete] // CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.a74 = facet_value %T2, (%ImplicitAs.impl_witness) [concrete] // CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.00a: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%T1, %ImplicitAs.facet) [concrete] +// CHECK:STDOUT: %Base: type = class_type @Base [concrete] // CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] // CHECK:STDOUT: %.617: Core.Form = init_form %T2 [concrete] // CHECK:STDOUT: %Derived.F.type.48cdab.1: type = fn_type @Derived.F.loc27 [concrete] @@ -1149,22 +1213,25 @@ class T2(G2:! type) { // CHECK:STDOUT: @Derived.%Derived.F.decl.loc23_36.2 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: override fn @Derived.F.loc23(%self.param: %Derived) -> out %return.param: %T1 [thunk @Derived.%Derived.F.decl.loc23_36.1 for @Base.%Base.F.decl] { +// CHECK:STDOUT: override fn @Derived.F.loc23(%self.param: %Base) -> out %return.param: %T1 [thunk @Derived.%Derived.F.decl.loc23_36.1 for @Base.%Base.F.decl] { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %Derived.F.type.48cdab.1 = name_ref F, @Derived.%Derived.F.decl.loc23_36.1 [concrete = constants.%Derived.F.bd5be1.1] -// CHECK:STDOUT: %Derived.F.bound: = bound_method %self.param, %F.ref -// CHECK:STDOUT: %.loc23_36.1: ref %T2 = temporary_storage -// CHECK:STDOUT: %Derived.F.call: init %T2 to %.loc23_36.1 = call %Derived.F.bound(%self.param) +// CHECK:STDOUT: +// CHECK:STDOUT: %.loc23_36.1: ref %Derived = enclosing_class_access %.loc17_20, element0 +// CHECK:STDOUT: %.loc23_36.2: %Derived = acquire_value %.loc23_36.1 +// CHECK:STDOUT: %Derived.F.bound: = bound_method %.loc23_36.2, %F.ref +// CHECK:STDOUT: %.loc23_36.3: ref %T2 = temporary_storage +// CHECK:STDOUT: %Derived.F.call: init %T2 to %.loc23_36.3 = call %Derived.F.bound(%.loc23_36.2) // CHECK:STDOUT: %impl.elem0: %.fd2 = impl_witness_access constants.%ImplicitAs.impl_witness, element0 [concrete = constants.%T2.as.ImplicitAs.impl.Convert] // CHECK:STDOUT: %bound_method: = bound_method %Derived.F.call, %impl.elem0 // CHECK:STDOUT: -// CHECK:STDOUT: %.loc23_36.2: ref %T2 = temporary %.loc23_36.1, %Derived.F.call -// CHECK:STDOUT: %.loc23_36.3: %T2 = acquire_value %.loc23_36.2 -// CHECK:STDOUT: %T2.as.ImplicitAs.impl.Convert.call: init %T1 to %.loc17 = call %bound_method(%.loc23_36.3) -// CHECK:STDOUT: %.loc23_36.4: init %T1 = converted %Derived.F.call, %T2.as.ImplicitAs.impl.Convert.call -// CHECK:STDOUT: %Destroy.Op.bound: = bound_method %.loc23_36.2, constants.%Destroy.Op.1a2547.2 -// CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc23_36.2) -// CHECK:STDOUT: return %.loc23_36.4 to %return.param +// CHECK:STDOUT: %.loc23_36.4: ref %T2 = temporary %.loc23_36.3, %Derived.F.call +// CHECK:STDOUT: %.loc23_36.5: %T2 = acquire_value %.loc23_36.4 +// CHECK:STDOUT: %T2.as.ImplicitAs.impl.Convert.call: init %T1 to %.loc17_33 = call %bound_method(%.loc23_36.5) +// CHECK:STDOUT: %.loc23_36.6: init %T1 = converted %Derived.F.call, %T2.as.ImplicitAs.impl.Convert.call +// CHECK:STDOUT: %Destroy.Op.bound: = bound_method %.loc23_36.4, constants.%Destroy.Op.1a2547.2 +// CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc23_36.4) +// CHECK:STDOUT: return %.loc23_36.6 to %return.param // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Destroy.Op.loc23_36.1(%self.param: ref %empty_struct_type) = "no_op"; @@ -1346,6 +1413,7 @@ class T2(G2:! type) { // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %Base.type: type = generic_class_type @Base [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Base.generic: %Base.type = struct_value () [concrete] // CHECK:STDOUT: %T1: type = class_type @T1 [concrete] // CHECK:STDOUT: %D1: type = class_type @D1 [concrete] @@ -1353,13 +1421,15 @@ class T2(G2:! type) { // CHECK:STDOUT: %ptr.2ee: type = ptr_type %Base.5b2 [concrete] // CHECK:STDOUT: %pattern_type.e0f: type = pattern_type %ptr.2ee [concrete] // CHECK:STDOUT: %pattern_type.c35: type = pattern_type %D1 [concrete] -// CHECK:STDOUT: %D1.F.type: type = fn_type @D1.F [concrete] -// CHECK:STDOUT: %D1.F: %D1.F.type = struct_value () [concrete] +// CHECK:STDOUT: %D1.F.type.48cdab.1: type = fn_type @D1.F.loc11_57.1 [concrete] +// CHECK:STDOUT: %D1.F.bd5be1.1: %D1.F.type.48cdab.1 = struct_value () [concrete] +// CHECK:STDOUT: %D1.F.type.48cdab.2: type = fn_type @D1.F.loc11_57.2 [concrete] +// CHECK:STDOUT: %D1.F.bd5be1.2: %D1.F.type.48cdab.2 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @D1 { // CHECK:STDOUT: -// CHECK:STDOUT: %D1.F.decl: %D1.F.type = fn_decl @D1.F [concrete = constants.%D1.F] { +// CHECK:STDOUT: %D1.F.decl.loc11_57.1: %D1.F.type.48cdab.1 = fn_decl @D1.F.loc11_57.1 [concrete = constants.%D1.F.bd5be1.1] { // CHECK:STDOUT: %self.param_patt: %pattern_type.c35 = value_param_pattern [concrete] // CHECK:STDOUT: %self.patt: %pattern_type.c35 = at_binding_pattern self, %self.param_patt [concrete] // CHECK:STDOUT: %t.param_patt: %pattern_type.e0f = value_param_pattern [concrete] @@ -1377,6 +1447,11 @@ class T2(G2:! type) { // CHECK:STDOUT: } // CHECK:STDOUT: %t: %ptr.2ee = value_binding t, %t.param // CHECK:STDOUT: } +// CHECK:STDOUT: %D1.F.decl.loc11_57.2: %D1.F.type.48cdab.2 = fn_decl @D1.F.loc11_57.2 [concrete = constants.%D1.F.bd5be1.2] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: vtable_decl = %vtable_decl @@ -1386,7 +1461,7 @@ class T2(G2:! type) { // CHECK:STDOUT: .Base = // CHECK:STDOUT: .T1 = // CHECK:STDOUT: .base = %.loc9 -// CHECK:STDOUT: .F [hidden] = %D1.F.decl +// CHECK:STDOUT: .F [hidden] = %D1.F.decl.loc11_57.1 // CHECK:STDOUT: extend %Base // CHECK:STDOUT: } // CHECK:STDOUT: @@ -1395,32 +1470,48 @@ class T2(G2:! type) { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: vtable @D1.vtable { -// CHECK:STDOUT: @D1.%D1.F.decl +// CHECK:STDOUT: @D1.%D1.F.decl.loc11_57.2 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: override fn @D1.F(%self.param: %D1, %t.param: %ptr.2ee) { +// CHECK:STDOUT: override fn @D1.F.loc11_57.1(%self.param: %D1, %t.param: %ptr.2ee) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: override fn @D1.F.loc11_57.2(%self.param: %Base.5b2, %t.param: %ptr.2ee) [thunk @D1.%D1.F.decl.loc11_57.1 for @Base.%Base.F.decl, @Base.F(constants.%T1)] { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %F.ref: %D1.F.type.48cdab.1 = name_ref F, @D1.%D1.F.decl.loc11_57.1 [concrete = constants.%D1.F.bd5be1.1] +// CHECK:STDOUT: +// CHECK:STDOUT: %.loc11_57.1: ref %D1 = enclosing_class_access %.loc5, element0 +// CHECK:STDOUT: %.loc11_57.2: %D1 = acquire_value %.loc11_57.1 +// CHECK:STDOUT: %D1.F.bound: = bound_method %.loc11_57.2, %F.ref +// CHECK:STDOUT: %D1.F.call: init %empty_tuple.type = call %D1.F.bound(%.loc11_57.2, %t.param) +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: --- impl_generic_generic.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %Derived: type = class_type @Derived, @Derived(%T) [symbolic] // CHECK:STDOUT: %ptr.e8f: type = ptr_type %T [symbolic] // CHECK:STDOUT: %Base.99a: type = class_type @Base, @Base(%ptr.e8f) [symbolic] +// CHECK:STDOUT: %pattern_type.bdc: type = pattern_type %Base.99a [symbolic] // CHECK:STDOUT: %pattern_type.4f4: type = pattern_type %ptr.e8f [symbolic] // CHECK:STDOUT: %require_complete.29f: = require_complete_type %Base.99a [symbolic] // CHECK:STDOUT: %Derived.elem: type = unbound_element_type %Derived, %Base.99a [symbolic] // CHECK:STDOUT: %pattern_type.839: type = pattern_type %Derived [symbolic] -// CHECK:STDOUT: %Derived.F.type: type = fn_type @Derived.F, @Derived(%T) [symbolic] -// CHECK:STDOUT: %Derived.F: %Derived.F.type = struct_value () [symbolic] -// CHECK:STDOUT: %Derived.F.specific_fn: = specific_function %Derived.F, @Derived.F(%T) [symbolic] +// CHECK:STDOUT: %Derived.F.type.77405e.1: type = fn_type @Derived.F.loc10_50.1, @Derived(%T) [symbolic] +// CHECK:STDOUT: %Derived.F.d85493.1: %Derived.F.type.77405e.1 = struct_value () [symbolic] +// CHECK:STDOUT: %Derived.F.type.77405e.2: type = fn_type @Derived.F.loc10_50.2, @Derived(%T) [symbolic] +// CHECK:STDOUT: %Derived.F.d85493.2: %Derived.F.type.77405e.2 = struct_value () [symbolic] +// CHECK:STDOUT: %Derived.F.specific_fn.822c1a.1: = specific_function %Derived.F.d85493.2, @Derived.F.loc10_50.2(%T) [symbolic] // CHECK:STDOUT: %struct_type.base.619: type = struct_type {.base: %Base.99a} [symbolic] // CHECK:STDOUT: %complete_type.db5: = complete_type_witness %struct_type.base.619 [symbolic] // CHECK:STDOUT: %require_complete.1e7: = require_complete_type %Derived [symbolic] // CHECK:STDOUT: %require_complete.ef1: = require_complete_type %ptr.e8f [symbolic] +// CHECK:STDOUT: %Derived.F.specific_fn.822c1a.2: = specific_function %Derived.F.d85493.1, @Derived.F.loc10_50.1(%T) [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: generic class @Derived(%T.loc7_16.2: type) { @@ -1428,30 +1519,37 @@ class T2(G2:! type) { // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: -// CHECK:STDOUT: %Derived.F.type: type = fn_type @Derived.F, @Derived(%T.loc7_16.1) [symbolic = %Derived.F.type (constants.%Derived.F.type)] -// CHECK:STDOUT: %Derived.F: @Derived.%Derived.F.type (%Derived.F.type) = struct_value () [symbolic = %Derived.F (constants.%Derived.F)] +// CHECK:STDOUT: %Derived.F.type.loc10_50.1: type = fn_type @Derived.F.loc10_50.1, @Derived(%T.loc7_16.1) [symbolic = %Derived.F.type.loc10_50.1 (constants.%Derived.F.type.77405e.1)] +// CHECK:STDOUT: %Derived.F.loc10_50.1: @Derived.%Derived.F.type.loc10_50.1 (%Derived.F.type.77405e.1) = struct_value () [symbolic = %Derived.F.loc10_50.1 (constants.%Derived.F.d85493.1)] +// CHECK:STDOUT: %Derived.F.type.loc10_50.2: type = fn_type @Derived.F.loc10_50.2, @Derived(%T.loc7_16.1) [symbolic = %Derived.F.type.loc10_50.2 (constants.%Derived.F.type.77405e.2)] +// CHECK:STDOUT: %Derived.F.loc10_50.2: @Derived.%Derived.F.type.loc10_50.2 (%Derived.F.type.77405e.2) = struct_value () [symbolic = %Derived.F.loc10_50.2 (constants.%Derived.F.d85493.2)] // CHECK:STDOUT: // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: -// CHECK:STDOUT: %Derived.F.decl: @Derived.%Derived.F.type (%Derived.F.type) = fn_decl @Derived.F [symbolic = @Derived.%Derived.F (constants.%Derived.F)] { -// CHECK:STDOUT: %self.param_patt: @Derived.F.%pattern_type.loc10_28 (%pattern_type.839) = value_param_pattern [concrete] -// CHECK:STDOUT: %self.patt: @Derived.F.%pattern_type.loc10_28 (%pattern_type.839) = at_binding_pattern self, %self.param_patt [concrete] -// CHECK:STDOUT: %t.param_patt: @Derived.F.%pattern_type.loc10_44 (%pattern_type.4f4) = value_param_pattern [concrete] -// CHECK:STDOUT: %t.patt: @Derived.F.%pattern_type.loc10_44 (%pattern_type.4f4) = at_binding_pattern t, %t.param_patt [concrete] +// CHECK:STDOUT: %Derived.F.decl.loc10_50.1: @Derived.%Derived.F.type.loc10_50.1 (%Derived.F.type.77405e.1) = fn_decl @Derived.F.loc10_50.1 [symbolic = @Derived.%Derived.F.loc10_50.1 (constants.%Derived.F.d85493.1)] { +// CHECK:STDOUT: %self.param_patt: @Derived.F.loc10_50.1.%pattern_type.loc10_28 (%pattern_type.839) = value_param_pattern [concrete] +// CHECK:STDOUT: %self.patt: @Derived.F.loc10_50.1.%pattern_type.loc10_28 (%pattern_type.839) = at_binding_pattern self, %self.param_patt [concrete] +// CHECK:STDOUT: %t.param_patt: @Derived.F.loc10_50.1.%pattern_type.loc10_44 (%pattern_type.4f4) = value_param_pattern [concrete] +// CHECK:STDOUT: %t.patt: @Derived.F.loc10_50.1.%pattern_type.loc10_44 (%pattern_type.4f4) = at_binding_pattern t, %t.param_patt [concrete] // CHECK:STDOUT: } { -// CHECK:STDOUT: %self.param: @Derived.F.%Derived (%Derived) = value_param call_param0 +// CHECK:STDOUT: %self.param: @Derived.F.loc10_50.1.%Derived (%Derived) = value_param call_param0 // CHECK:STDOUT: %.loc10_30.1: type = splice_block %Self.ref [symbolic = %Derived (constants.%Derived)] { // CHECK:STDOUT: %.loc10_30.2: type = specific_constant constants.%Derived, @Derived(constants.%T) [symbolic = %Derived (constants.%Derived)] // CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc10_30.2 [symbolic = %Derived (constants.%Derived)] // CHECK:STDOUT: } -// CHECK:STDOUT: %self: @Derived.F.%Derived (%Derived) = value_binding self, %self.param -// CHECK:STDOUT: %t.param: @Derived.F.%ptr.loc10_47.1 (%ptr.e8f) = value_param call_param1 +// CHECK:STDOUT: %self: @Derived.F.loc10_50.1.%Derived (%Derived) = value_binding self, %self.param +// CHECK:STDOUT: %t.param: @Derived.F.loc10_50.1.%ptr.loc10_47.1 (%ptr.e8f) = value_param call_param1 // CHECK:STDOUT: %.loc10_47: type = splice_block %ptr.loc10_47.2 [symbolic = %ptr.loc10_47.1 (constants.%ptr.e8f)] { // CHECK:STDOUT: %T.ref: type = name_ref T, @Derived.%T.loc7_16.2 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %ptr.loc10_47.2: type = ptr_type %T.ref [symbolic = %ptr.loc10_47.1 (constants.%ptr.e8f)] // CHECK:STDOUT: } -// CHECK:STDOUT: %t: @Derived.F.%ptr.loc10_47.1 (%ptr.e8f) = value_binding t, %t.param +// CHECK:STDOUT: %t: @Derived.F.loc10_50.1.%ptr.loc10_47.1 (%ptr.e8f) = value_binding t, %t.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %Derived.F.decl.loc10_50.2: @Derived.%Derived.F.type.loc10_50.2 (%Derived.F.type.77405e.2) = fn_decl @Derived.F.loc10_50.2 [symbolic = @Derived.%Derived.F.loc10_50.2 (constants.%Derived.F.d85493.2)] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: complete_type_witness = %complete_type.loc12_1.1 @@ -1462,7 +1560,7 @@ class T2(G2:! type) { // CHECK:STDOUT: .Base = // CHECK:STDOUT: .T = // CHECK:STDOUT: .base = %.loc8 -// CHECK:STDOUT: .F [hidden] = %Derived.F.decl +// CHECK:STDOUT: .F [hidden] = %Derived.F.decl.loc10_50.1 // CHECK:STDOUT: extend %Base.loc8_23.1 // CHECK:STDOUT: } // CHECK:STDOUT: } @@ -1475,7 +1573,7 @@ class T2(G2:! type) { // CHECK:STDOUT: @Derived.%Derived.F.specific_fn.loc12_1.1 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: generic override fn @Derived.F(@Derived.%T.loc7_16.2: type) { +// CHECK:STDOUT: generic override fn @Derived.F.loc10_50.1(@Derived.%T.loc7_16.2: type) { // CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %Derived: type = class_type @Derived, @Derived(%T) [symbolic = %Derived (constants.%Derived)] // CHECK:STDOUT: %pattern_type.loc10_28: type = pattern_type %Derived [symbolic = %pattern_type.loc10_28 (constants.%pattern_type.839)] @@ -1486,12 +1584,37 @@ class T2(G2:! type) { // CHECK:STDOUT: %require_complete.loc10_28: = require_complete_type %Derived [symbolic = %require_complete.loc10_28 (constants.%require_complete.1e7)] // CHECK:STDOUT: %require_complete.loc10_44: = require_complete_type %ptr.loc10_47.1 [symbolic = %require_complete.loc10_44 (constants.%require_complete.ef1)] // CHECK:STDOUT: -// CHECK:STDOUT: override fn(%self.param: @Derived.F.%Derived (%Derived), %t.param: @Derived.F.%ptr.loc10_47.1 (%ptr.e8f)) { +// CHECK:STDOUT: override fn(%self.param: @Derived.F.loc10_50.1.%Derived (%Derived), %t.param: @Derived.F.loc10_50.1.%ptr.loc10_47.1 (%ptr.e8f)) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: generic override fn @Derived.F.loc10_50.2(@Derived.%T.loc7_16.2: type) { +// CHECK:STDOUT: +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: +// CHECK:STDOUT: %Derived.F.type: type = fn_type @Derived.F.loc10_50.1, @Derived(%T) [symbolic = %Derived.F.type (constants.%Derived.F.type.77405e.1)] +// CHECK:STDOUT: %Derived.F: @Derived.F.loc10_50.2.%Derived.F.type (%Derived.F.type.77405e.1) = struct_value () [symbolic = %Derived.F (constants.%Derived.F.d85493.1)] +// CHECK:STDOUT: %Derived: type = class_type @Derived, @Derived(%T) [symbolic = %Derived (constants.%Derived)] +// CHECK:STDOUT: %Derived.F.specific_fn.loc10_50.2: = specific_function %Derived.F, @Derived.F.loc10_50.1(%T) [symbolic = %Derived.F.specific_fn.loc10_50.2 (constants.%Derived.F.specific_fn.822c1a.2)] +// CHECK:STDOUT: +// CHECK:STDOUT: override fn(%self.param: @Derived.F.loc10_50.2.%Base (%Base.99a), %t.param: @Derived.F.loc10_50.2.%ptr (%ptr.e8f)) [thunk @Derived.%Derived.F.decl.loc10_50.1 for @Base.%Base.F.decl, @Base.F(constants.%ptr.e8f)] { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc10_50.1: @Derived.F.loc10_50.2.%Derived.F.type (%Derived.F.type.77405e.1) = specific_constant @Derived.%Derived.F.decl.loc10_50.1, @Derived(constants.%T) [symbolic = %Derived.F (constants.%Derived.F.d85493.1)] +// CHECK:STDOUT: %F.ref: @Derived.F.loc10_50.2.%Derived.F.type (%Derived.F.type.77405e.1) = name_ref F, %.loc10_50.1 [symbolic = %Derived.F (constants.%Derived.F.d85493.1)] +// CHECK:STDOUT: +// CHECK:STDOUT: %.loc10_50.2: ref @Derived.F.loc10_50.2.%Derived (%Derived) = enclosing_class_access %.loc5, element0 +// CHECK:STDOUT: %.loc10_50.3: @Derived.F.loc10_50.2.%Derived (%Derived) = acquire_value %.loc10_50.2 +// CHECK:STDOUT: %Derived.F.bound: = bound_method %.loc10_50.3, %F.ref +// CHECK:STDOUT: %Derived.F.specific_fn.loc10_50.1: = specific_function %F.ref, @Derived.F.loc10_50.1(constants.%T) [symbolic = %Derived.F.specific_fn.loc10_50.2 (constants.%Derived.F.specific_fn.822c1a.2)] +// CHECK:STDOUT: %bound_method: = bound_method %.loc10_50.3, %Derived.F.specific_fn.loc10_50.1 +// CHECK:STDOUT: %Derived.F.call: init %empty_tuple.type = call %bound_method(%.loc10_50.3, %t.param) +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: specific @Derived(constants.%T) { // CHECK:STDOUT: %T.loc7_16.1 => constants.%T // CHECK:STDOUT: @@ -1501,14 +1624,16 @@ class T2(G2:! type) { // CHECK:STDOUT: %require_complete => constants.%require_complete.29f // CHECK:STDOUT: %Derived => constants.%Derived // CHECK:STDOUT: %Derived.elem => constants.%Derived.elem -// CHECK:STDOUT: %Derived.F.type => constants.%Derived.F.type -// CHECK:STDOUT: %Derived.F => constants.%Derived.F -// CHECK:STDOUT: %Derived.F.specific_fn.loc12_1.2 => constants.%Derived.F.specific_fn +// CHECK:STDOUT: %Derived.F.type.loc10_50.1 => constants.%Derived.F.type.77405e.1 +// CHECK:STDOUT: %Derived.F.loc10_50.1 => constants.%Derived.F.d85493.1 +// CHECK:STDOUT: %Derived.F.type.loc10_50.2 => constants.%Derived.F.type.77405e.2 +// CHECK:STDOUT: %Derived.F.loc10_50.2 => constants.%Derived.F.d85493.2 +// CHECK:STDOUT: %Derived.F.specific_fn.loc12_1.2 => constants.%Derived.F.specific_fn.822c1a.1 // CHECK:STDOUT: %struct_type.base => constants.%struct_type.base.619 // CHECK:STDOUT: %complete_type.loc12_1.2 => constants.%complete_type.db5 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: specific @Derived.F(constants.%T) { +// CHECK:STDOUT: specific @Derived.F.loc10_50.1(constants.%T) { // CHECK:STDOUT: %T => constants.%T // CHECK:STDOUT: %Derived => constants.%Derived // CHECK:STDOUT: %pattern_type.loc10_28 => constants.%pattern_type.839 @@ -1520,3 +1645,19 @@ class T2(G2:! type) { // CHECK:STDOUT: %require_complete.loc10_44 => constants.%require_complete.ef1 // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: specific @Derived.F.loc10_50.2(constants.%T) { +// CHECK:STDOUT: %T => constants.%T +// CHECK:STDOUT: %ptr => constants.%ptr.e8f +// CHECK:STDOUT: %Base => constants.%Base.99a +// CHECK:STDOUT: %pattern_type.loc5_27 => constants.%pattern_type.bdc +// CHECK:STDOUT: %pattern_type.loc5_43 => constants.%pattern_type.4f4 +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %require_complete.loc5_27 => constants.%require_complete.29f +// CHECK:STDOUT: %require_complete.loc5_43 => constants.%require_complete.ef1 +// CHECK:STDOUT: %Derived.F.type => constants.%Derived.F.type.77405e.1 +// CHECK:STDOUT: %Derived.F => constants.%Derived.F.d85493.1 +// CHECK:STDOUT: %Derived => constants.%Derived +// CHECK:STDOUT: %Derived.F.specific_fn.loc10_50.2 => constants.%Derived.F.specific_fn.822c1a.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/thunk.cpp b/toolchain/check/thunk.cpp index d95f17778b1a..0da0b57ffbda 100644 --- a/toolchain/check/thunk.cpp +++ b/toolchain/check/thunk.cpp @@ -100,10 +100,8 @@ static auto CloneBindingPattern(Context& context, SemIR::InstId pattern_id, // Makes a copy of the given pattern instruction, substituting values from a // specific as needed. The resulting pattern behaves like a newly-created // pattern, so is suitable for running `CalleePatternMatch` against. -static auto ClonePattern( - Context& context, SemIR::SpecificId specific_id, SemIR::InstId pattern_id, - SemIR::TypeId self_type_override_id = SemIR::TypeId::None) - -> SemIR::InstId { +static auto ClonePattern(Context& context, SemIR::SpecificId specific_id, + SemIR::InstId pattern_id) -> SemIR::InstId { if (!pattern_id.has_value()) { return SemIR::InstId::None; } @@ -126,11 +124,6 @@ static auto ClonePattern( auto new_pattern_id = SemIR::InstId::None; if (auto binding = pattern.TryAs()) { auto type_id = get_type(pattern_id); - if (self_type_override_id.has_value() && - context.entity_names().Get(binding->entity_name_id).name_id == - SemIR::NameId::SelfValue) { - type_id = GetPatternType(context, self_type_override_id); - } new_pattern_id = CloneBindingPattern(context, pattern_id, *binding, type_id); } else if (auto return_slot = pattern.TryAs()) { @@ -178,16 +171,14 @@ static auto ClonePattern( } static auto ClonePatternBlock(Context& context, SemIR::SpecificId specific_id, - SemIR::InstBlockId inst_block_id, - SemIR::TypeId self_type_override_id = - SemIR::TypeId::None) -> SemIR::InstBlockId { + SemIR::InstBlockId inst_block_id) + -> SemIR::InstBlockId { if (!inst_block_id.has_value()) { return SemIR::InstBlockId::None; } return context.inst_blocks().Transform( inst_block_id, [&](SemIR::InstId inst_id) { - return ClonePattern(context, specific_id, inst_id, - self_type_override_id); + return ClonePattern(context, specific_id, inst_id); }); } @@ -217,7 +208,6 @@ static auto CloneTypeInstId(Context& context, SemIR::SpecificId specific_id, static auto CloneFunctionDecl(Context& context, SemIR::LocId loc_id, SemIR::FunctionId signature_id, SemIR::SpecificId signature_specific_id, - SemIR::TypeId signature_self_type_override_id, SemIR::FunctionId callee_id) -> std::pair { StartGenericDecl(context); @@ -227,8 +217,7 @@ static auto CloneFunctionDecl(Context& context, SemIR::LocId loc_id, // Clone the signature. context.pattern_block_stack().Push(); auto implicit_param_patterns_id = ClonePatternBlock( - context, signature_specific_id, signature.implicit_param_patterns_id, - signature_self_type_override_id); + context, signature_specific_id, signature.implicit_param_patterns_id); auto param_patterns_id = ClonePatternBlock(context, signature_specific_id, signature.param_patterns_id); auto return_pattern_id = @@ -293,13 +282,19 @@ auto PerformThunkCall(Context& context, SemIR::LocId loc_id, SemIR::FunctionId function_id, llvm::ArrayRef param_pattern_ids, llvm::ArrayRef call_arg_ids, - SemIR::InstId callee_id) -> SemIR::InstId { + SemIR::InstId callee_id, + SemIR::TypeId override_self_type_id) -> SemIR::InstId { auto& function = context.functions().Get(function_id); auto [args_vec, ignored_call_args] = ThunkPatternMatch( context, function.self_param_id, param_pattern_ids, call_arg_ids); llvm::ArrayRef args = args_vec; + if (override_self_type_id.has_value()) { + args_vec.front() = UnsafeUndoConvert(context, loc_id, args_vec.front(), + override_self_type_id); + } + // If we have a self parameter, form `self.` if needed. // When calling a C++ constructor to implement `Copy`, or calling a C++ // non-method operator to implement a Carbon operator, the interface has a @@ -318,11 +313,15 @@ auto PerformThunkCall(Context& context, SemIR::LocId loc_id, } // Build a call to a function that forwards the arguments of the enclosing -// function, for use when constructing a thunk. +// function, for use when constructing a thunk. `param_pattern_ids` contains the +// parameter patterns of `function_id`. If `override_self_type_id` is not +// `None`, `callee_id` refers to a virtual function override declared in the +// given type. static auto BuildThunkCall(Context& context, SemIR::FunctionId function_id, SemIR::InstId callee_id, llvm::ArrayRef param_pattern_ids, - llvm::ArrayRef call_arg_ids) + llvm::ArrayRef call_arg_ids, + SemIR::TypeId override_self_type_id) -> SemIR::InstId { auto& function = context.functions().Get(function_id); @@ -334,7 +333,7 @@ static auto BuildThunkCall(Context& context, SemIR::FunctionId function_id, callee_type.specific_id); return PerformThunkCall(context, loc_id, function_id, param_pattern_ids, - call_arg_ids, callee_id); + call_arg_ids, callee_id, override_self_type_id); } static auto StartThunkFunctionDefinition(Context& context, @@ -359,7 +358,8 @@ static auto BuildThunkDefinition(Context& context, SemIR::FunctionId signature_id, SemIR::FunctionId function_id, SemIR::InstId thunk_id, - SemIR::InstId callee_id) -> void { + SemIR::InstId callee_id, + SemIR::TypeId override_self_type_id) -> void { // TODO: Improve the diagnostics produced here. Specifically, it would likely // be better for the primary error message to be that we tried to produce a // thunk because of a type mismatch, but couldn't, with notes explaining @@ -386,8 +386,9 @@ static auto BuildThunkDefinition(Context& context, } auto call_param_ids = context.inst_blocks().Get(function.call_params_id); - auto call_id = BuildThunkCall(context, function_id, callee_id, - param_pattern_ids, call_param_ids); + auto call_id = + BuildThunkCall(context, function_id, callee_id, param_pattern_ids, + call_param_ids, override_self_type_id); if (HasDeclaredReturnType(context, function_id)) { BuildReturnWithExpr(context, SemIR::LocId(callee_id), call_id); } else { @@ -425,7 +426,8 @@ auto BuildThunkDefinitionForExport(Context& context, } auto call_id = BuildThunkCall(context, thunk_function_id, callee_id, - param_pattern_ids, call_param_ids); + param_pattern_ids, call_param_ids, + /*override_self_type_id=*/SemIR::TypeId::None); if (thunk_has_return_param) { auto out_param_id = context.inst_blocks().Get(thunk_function.call_params_id).back(); @@ -453,16 +455,16 @@ auto BuildThunkDefinition(Context& context, context.scope_stack().Restore(std::move(task.scope)); BuildThunkDefinition(context, task.info.signature_id, task.info.function_id, - task.info.decl_id, task.info.callee_id); + task.info.decl_id, task.info.callee_id, + task.info.override_self_type_id); context.scope_stack().Pop(); } auto BuildThunk(Context& context, SemIR::FunctionId signature_id, SemIR::SpecificId signature_specific_id, - SemIR::TypeId signature_self_type_override_id, - SemIR::InstId callee_id, bool defer_definition) - -> SemIR::InstId { + SemIR::TypeId override_self_type_id, SemIR::InstId callee_id, + bool defer_definition) -> SemIR::InstId { auto callee = SemIR::GetCalleeAsFunction(context.sem_ir(), callee_id); // Check whether we can use the given function without a thunk. @@ -473,8 +475,7 @@ auto BuildThunk(Context& context, SemIR::FunctionId signature_id, CheckFunctionTypeMatches( context, context.functions().Get(callee.function_id), context.functions().Get(signature_id), signature_specific_id, - /*check_syntax=*/false, signature_self_type_override_id, - /*diagnose=*/false)) { + /*check_syntax=*/false, /*diagnose=*/false)) { return callee_id; } @@ -504,14 +505,15 @@ auto BuildThunk(Context& context, SemIR::FunctionId signature_id, // We can't use the function directly. Build a thunk. // TODO: Check for and diagnose obvious reasons why this will fail, such as // arity mismatch, before trying to build the thunk. - auto [function_id, thunk_inst_id] = CloneFunctionDecl( - context, SemIR::LocId(callee_id), signature_id, signature_specific_id, - signature_self_type_override_id, callee.function_id); + auto [function_id, thunk_inst_id] = + CloneFunctionDecl(context, SemIR::LocId(callee_id), signature_id, + signature_specific_id, callee.function_id); - auto thunk_id = - context.sem_ir().thunks().Add({.callee_id = callee_id, - .signature_id = signature_id, - .specific_id = signature_specific_id}); + auto thunk_id = context.sem_ir().thunks().Add( + {.callee_id = callee_id, + .signature_id = signature_id, + .specific_id = signature_specific_id, + .override_self_type_id = override_self_type_id}); // Track that this function is a thunk. context.functions().Get(function_id).SetThunk(thunk_id); @@ -526,10 +528,11 @@ auto BuildThunk(Context& context, SemIR::FunctionId signature_id, .function_id = function_id, .decl_id = thunk_inst_id, .callee_id = callee_id, + .override_self_type_id = override_self_type_id, }); } else { BuildThunkDefinition(context, signature_id, function_id, thunk_inst_id, - callee_id); + callee_id, override_self_type_id); context.scope_stack().Pop(); } diff --git a/toolchain/check/thunk.h b/toolchain/check/thunk.h index 5afd915d97d9..c90d02c3935c 100644 --- a/toolchain/check/thunk.h +++ b/toolchain/check/thunk.h @@ -12,23 +12,26 @@ namespace Carbon::Check { // Given a function signature and a callee function, build a thunk that matches -// the given signature and calls the specified callee. Returns the callee -// unchanged if it can be used directly. +// the given signature and calls the specified callee. If +// `override_self_type_id` is not `None`, this thunk wraps a virtual function +// override declared in the given type. Returns the callee unchanged if it can +// be used directly. auto BuildThunk(Context& context, SemIR::FunctionId signature_id, SemIR::SpecificId signature_specific_id, - SemIR::TypeId signature_self_type_override_id, - SemIR::InstId callee_id, bool defer_definition) - -> SemIR::InstId; + SemIR::TypeId override_self_type_id, SemIR::InstId callee_id, + bool defer_definition) -> SemIR::InstId; -// Builds a call to a function that forwards a call argument list built for -// `function_id` to a call to `callee_id`, for use when building a call from a -// thunk to its target. This is like `PerformCall`, except that it takes a list -// of call arguments for `function_id`, not a syntactic argument list. +// Builds a call to a function that forwards a call argument list `call_arg_ids` +// built for `function_id` to a call to `callee_id`, for use when building a +// call from a thunk to its target. `param_pattern_ids` contains the parameter +// patterns of `function_id`. If `override_self_type_id` is not `None`, +// `callee_id` refers to a virtual function override declared in the given type. auto PerformThunkCall(Context& context, SemIR::LocId loc_id, SemIR::FunctionId function_id, llvm::ArrayRef param_pattern_ids, llvm::ArrayRef call_arg_ids, - SemIR::InstId callee_id) -> SemIR::InstId; + SemIR::InstId callee_id, + SemIR::TypeId override_self_type_id) -> SemIR::InstId; // Builds the definition for a thunk whose definition was deferred until the end // of the enclosing scope. diff --git a/toolchain/lower/aggregate.cpp b/toolchain/lower/aggregate.cpp index 5c6d410d8af7..ea7077330c92 100644 --- a/toolchain/lower/aggregate.cpp +++ b/toolchain/lower/aggregate.cpp @@ -124,6 +124,28 @@ auto GetAggregateElement(FunctionContext& context, SemIR::InstId aggr_inst_id, } } +auto GetEnclosingAggregate(FunctionContext& context, + FunctionContext::TypeInFile aggr_type, + SemIR::InstId element_id, SemIR::ElementIndex index) + -> llvm::Value* { + auto object_repr = FunctionContext::TypeInFile{ + .file = aggr_type.file, + .type_id = aggr_type.file->types().GetObjectRepr(aggr_type.type_id)}; + auto* llvm_object_type = + llvm::cast(context.GetType(object_repr)); + const auto* object_layout = + context.llvm_module().getDataLayout().getStructLayout(llvm_object_type); + llvm::APInt offset( + 64, object_layout->getElementOffset(GetElementIndex(object_repr, index)), + /*isSigned=*/true); + + auto* element_addr = context.GetValue(element_id); + if (offset == 0) { + return element_addr; + } + CARBON_FATAL("TODO: emit a getelementptr with a negative offset"); +} + auto EmitAggregateValueRepr(FunctionContext& context, SemIR::InstId value_inst_id, SemIR::InstBlockId refs_id) -> llvm::Value* { diff --git a/toolchain/lower/aggregate.h b/toolchain/lower/aggregate.h index 6d7b07aaa330..b25b7e666c27 100644 --- a/toolchain/lower/aggregate.h +++ b/toolchain/lower/aggregate.h @@ -31,6 +31,13 @@ auto EmitAggregateInitializer(FunctionContext& context, SemIR::InstBlockId refs_id, llvm::Twine name) -> llvm::Value*; +// Given that `element_id` is the `index` element of an aggregate of type +// `aggr_type`, returns a reference to that aggregate. +auto GetEnclosingAggregate(FunctionContext& context, + FunctionContext::TypeInFile aggr_type, + SemIR::InstId element_id, SemIR::ElementIndex index) + -> llvm::Value*; + } // namespace Carbon::Lower #endif // CARBON_TOOLCHAIN_LOWER_AGGREGATE_H_ diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index ffbe03164b00..5fd5ed268eba 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -583,6 +583,8 @@ auto FileContext::BuildFunctionBody(SemIR::FunctionId function_id, // Otherwise, always inline thunks. if (definition_function.special_function_kind == SemIR::Function::SpecialFunctionKind::Thunk) { + // TODO: emit the thunk as an alias instead of a separate function, + // when that would be correct. attr_builder.addAttribute(llvm::Attribute::AlwaysInline); } diff --git a/toolchain/lower/handle_aggregates.cpp b/toolchain/lower/handle_aggregates.cpp index 81df336d812e..7f23ff984b49 100644 --- a/toolchain/lower/handle_aggregates.cpp +++ b/toolchain/lower/handle_aggregates.cpp @@ -48,6 +48,13 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, GetStructFieldName(object_repr, inst.index))); } +auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, + SemIR::EnclosingClassAccess inst) -> void { + auto aggr_type = context.GetTypeIdOfInst(inst_id); + context.SetLocal(inst_id, GetEnclosingAggregate(context, aggr_type, + inst.element_id, inst.index)); +} + auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, SemIR::ClassInit inst) -> void { context.SetLocal(inst_id, diff --git a/toolchain/lower/testdata/class/virtual.carbon b/toolchain/lower/testdata/class/virtual.carbon index 7104cc9c4b6b..52f05c30976e 100644 --- a/toolchain/lower/testdata/class/virtual.carbon +++ b/toolchain/lower/testdata/class/virtual.carbon @@ -216,7 +216,7 @@ fn Use() { // CHECK:STDOUT: source_filename = "classes.carbon" // CHECK:STDOUT: // CHECK:STDOUT: @"_CIntermediate.Classes.$vtable" = unnamed_addr constant [1 x i32] [i32 trunc (i64 sub (i64 ptrtoint (ptr @_CFn.Intermediate.Classes to i64), i64 ptrtoint (ptr @"_CIntermediate.Classes.$vtable" to i64)) to i32)] -// CHECK:STDOUT: @"_CDerived.Classes.$vtable" = unnamed_addr constant [1 x i32] [i32 trunc (i64 sub (i64 ptrtoint (ptr @_CFn.Derived.Classes to i64), i64 ptrtoint (ptr @"_CDerived.Classes.$vtable" to i64)) to i32)] +// CHECK:STDOUT: @"_CDerived.Classes.$vtable" = unnamed_addr constant [1 x i32] [i32 trunc (i64 sub (i64 ptrtoint (ptr @"_CFn:thunk:Intermediate.Classes:Derived.Classes" to i64), i64 ptrtoint (ptr @"_CDerived.Classes.$vtable" to i64)) to i32)] // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind // CHECK:STDOUT: define void @_CFn.Intermediate.Classes(ptr %self) #0 !dbg !4 { @@ -230,7 +230,15 @@ fn Use() { // CHECK:STDOUT: ret void, !dbg !14 // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: alwaysinline nounwind +// CHECK:STDOUT: define void @"_CFn:thunk:Intermediate.Classes:Derived.Classes"(ptr %self) #1 !dbg !15 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @_CFn.Derived.Classes(ptr %self), !dbg !18 +// CHECK:STDOUT: ret void, !dbg !18 +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: attributes #0 = { nounwind } +// CHECK:STDOUT: attributes #1 = { alwaysinline nounwind } // CHECK:STDOUT: // CHECK:STDOUT: !llvm.module.flags = !{!0, !1} // CHECK:STDOUT: !llvm.dbg.cu = !{!2} @@ -250,6 +258,10 @@ fn Use() { // CHECK:STDOUT: !12 = !{!13} // CHECK:STDOUT: !13 = !DILocalVariable(arg: 1, scope: !11, type: !7) // CHECK:STDOUT: !14 = !DILocation(line: 14, column: 3, scope: !11) +// CHECK:STDOUT: !15 = distinct !DISubprogram(name: "Fn", linkageName: "_CFn:thunk:Intermediate.Classes:Derived.Classes", scope: null, file: !3, line: 14, type: !5, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !16) +// CHECK:STDOUT: !16 = !{!17} +// CHECK:STDOUT: !17 = !DILocalVariable(arg: 1, scope: !15, type: !7) +// CHECK:STDOUT: !18 = !DILocation(line: 14, column: 3, scope: !15) // CHECK:STDOUT: ; ModuleID = 'create.carbon' // CHECK:STDOUT: source_filename = "create.carbon" // CHECK:STDOUT: @@ -290,7 +302,7 @@ fn Use() { // CHECK:STDOUT: // CHECK:STDOUT: declare void @_CFn.Intermediate.Classes(ptr) // CHECK:STDOUT: -// CHECK:STDOUT: declare void @_CFn.Derived.Classes(ptr) +// CHECK:STDOUT: declare void @"_CFn:thunk:Intermediate.Classes:Derived.Classes"(ptr) // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind // CHECK:STDOUT: define weak_odr void @"_COp.599adb417fbfaec7:core.Destroy.Core"(ptr %self) #0 !dbg !15 { @@ -448,7 +460,7 @@ fn Use() { // CHECK:STDOUT: ret void, !dbg !22 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: declare void @_CFn.Derived.Classes(ptr) +// CHECK:STDOUT: declare void @"_CFn:thunk:Intermediate.Classes:Derived.Classes"(ptr) // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind // CHECK:STDOUT: define void @_CMakeDerivedFromNonPartial.Main(ptr sret({ { ptr, {} } }) %return) #0 !dbg !23 { @@ -737,68 +749,76 @@ fn Use() { // CHECK:STDOUT: source_filename = "call_impl.carbon" // CHECK:STDOUT: // CHECK:STDOUT: @"_CBase.Main.$vtable" = unnamed_addr constant [1 x i32] [i32 trunc (i64 sub (i64 ptrtoint (ptr @_CF.Base.Main to i64), i64 ptrtoint (ptr @"_CBase.Main.$vtable" to i64)) to i32)] -// CHECK:STDOUT: @"_CDerived.Main.$vtable" = unnamed_addr constant [1 x i32] [i32 trunc (i64 sub (i64 ptrtoint (ptr @_CF.Derived.Main to i64), i64 ptrtoint (ptr @"_CDerived.Main.$vtable" to i64)) to i32)] +// CHECK:STDOUT: @"_CDerived.Main.$vtable" = unnamed_addr constant [1 x i32] [i32 trunc (i64 sub (i64 ptrtoint (ptr @"_CF:thunk:Base.Main:Derived.Main" to i64), i64 ptrtoint (ptr @"_CDerived.Main.$vtable" to i64)) to i32)] // CHECK:STDOUT: @Derived.val.loc14_32.5 = internal constant { { ptr } } poison // CHECK:STDOUT: // CHECK:STDOUT: declare void @_CF.Base.Main(ptr) // CHECK:STDOUT: // CHECK:STDOUT: declare void @_CF.Derived.Main(ptr) // CHECK:STDOUT: -// CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define void @_CUse.Main() #0 !dbg !4 { +// CHECK:STDOUT: ; Function Attrs: alwaysinline nounwind +// CHECK:STDOUT: define void @"_CF:thunk:Base.Main:Derived.Main"(ptr %self) #0 !dbg !4 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: %v.var = alloca { { ptr } }, align 8, !dbg !7 -// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %v.var), !dbg !7 -// CHECK:STDOUT: %.loc14_32.2.base = getelementptr inbounds nuw { { ptr } }, ptr %v.var, i32 0, i32 0, !dbg !8 -// CHECK:STDOUT: %.loc14_31.2.vptr = getelementptr inbounds nuw { ptr }, ptr %.loc14_32.2.base, i32 0, i32 0, !dbg !9 -// CHECK:STDOUT: %.loc14_32.6.base = getelementptr inbounds nuw { { ptr } }, ptr %v.var, i32 0, i32 0, !dbg !8 -// CHECK:STDOUT: %.loc14_32.7.vptr = getelementptr inbounds nuw { ptr }, ptr %.loc14_32.6.base, i32 0, i32 0, !dbg !8 -// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %v.var, ptr align 8 @Derived.val.loc14_32.5, i64 8, i1 false), !dbg !8 -// CHECK:STDOUT: store ptr @"_CDerived.Main.$vtable", ptr %.loc14_32.7.vptr, align 8, !dbg !8 -// CHECK:STDOUT: %.loc15_3.1.base = getelementptr inbounds nuw { { ptr } }, ptr %v.var, i32 0, i32 0, !dbg !10 -// CHECK:STDOUT: %Base.F.call.vtable = load ptr, ptr %.loc15_3.1.base, align 8, !dbg !10 -// CHECK:STDOUT: %Base.F.call = call ptr @llvm.load.relative.i32(ptr %Base.F.call.vtable, i32 0), !dbg !10 -// CHECK:STDOUT: call void %Base.F.call(ptr %.loc15_3.1.base), !dbg !10 -// CHECK:STDOUT: call void @"_COp.8185e3504351afa8:core.Destroy.Core"(ptr %v.var), !dbg !7 -// CHECK:STDOUT: ret void, !dbg !11 +// CHECK:STDOUT: call void @_CF.Derived.Main(ptr %self), !dbg !10 +// CHECK:STDOUT: ret void, !dbg !10 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define weak_odr void @"_COp.a8f528cd70a29ff8:core.Destroy.Core"(ptr %self) #0 !dbg !12 { +// CHECK:STDOUT: define void @_CUse.Main() #1 !dbg !11 { // CHECK:STDOUT: entry: +// CHECK:STDOUT: %v.var = alloca { { ptr } }, align 8, !dbg !14 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %v.var), !dbg !14 +// CHECK:STDOUT: %.loc14_32.2.base = getelementptr inbounds nuw { { ptr } }, ptr %v.var, i32 0, i32 0, !dbg !15 +// CHECK:STDOUT: %.loc14_31.2.vptr = getelementptr inbounds nuw { ptr }, ptr %.loc14_32.2.base, i32 0, i32 0, !dbg !16 +// CHECK:STDOUT: %.loc14_32.6.base = getelementptr inbounds nuw { { ptr } }, ptr %v.var, i32 0, i32 0, !dbg !15 +// CHECK:STDOUT: %.loc14_32.7.vptr = getelementptr inbounds nuw { ptr }, ptr %.loc14_32.6.base, i32 0, i32 0, !dbg !15 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %v.var, ptr align 8 @Derived.val.loc14_32.5, i64 8, i1 false), !dbg !15 +// CHECK:STDOUT: store ptr @"_CDerived.Main.$vtable", ptr %.loc14_32.7.vptr, align 8, !dbg !15 +// CHECK:STDOUT: %.loc15_3.1.base = getelementptr inbounds nuw { { ptr } }, ptr %v.var, i32 0, i32 0, !dbg !17 +// CHECK:STDOUT: %Base.F.call.vtable = load ptr, ptr %.loc15_3.1.base, align 8, !dbg !17 +// CHECK:STDOUT: %Base.F.call = call ptr @llvm.load.relative.i32(ptr %Base.F.call.vtable, i32 0), !dbg !17 +// CHECK:STDOUT: call void %Base.F.call(ptr %.loc15_3.1.base), !dbg !17 +// CHECK:STDOUT: call void @"_COp.8185e3504351afa8:core.Destroy.Core"(ptr %v.var), !dbg !14 // CHECK:STDOUT: ret void, !dbg !18 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define weak_odr void @"_COp.a45b619846c20518:core.Destroy.Core"(ptr %self) #0 !dbg !19 { +// CHECK:STDOUT: define weak_odr void @"_COp.a8f528cd70a29ff8:core.Destroy.Core"(ptr %self) #1 !dbg !19 { // CHECK:STDOUT: entry: // CHECK:STDOUT: ret void, !dbg !22 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define weak_odr void @"_COp.4e1364ec5889efef:core.Destroy.Core"(ptr %self) #0 !dbg !23 { +// CHECK:STDOUT: define weak_odr void @"_COp.a45b619846c20518:core.Destroy.Core"(ptr %self) #1 !dbg !23 { // CHECK:STDOUT: entry: // CHECK:STDOUT: ret void, !dbg !26 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define weak_odr void @"_COp.8185e3504351afa8:core.Destroy.Core"(ptr %self) #0 !dbg !27 { +// CHECK:STDOUT: define weak_odr void @"_COp.4e1364ec5889efef:core.Destroy.Core"(ptr %self) #1 !dbg !27 { // CHECK:STDOUT: entry: // CHECK:STDOUT: ret void, !dbg !30 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) -// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #1 +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.8185e3504351afa8:core.Destroy.Core"(ptr %self) #1 !dbg !31 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !34 +// CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) -// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i64, i1 immarg) #1 +// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #2 +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i64, i1 immarg) #2 // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: read) -// CHECK:STDOUT: declare ptr @llvm.load.relative.i32(ptr, i32) #2 +// CHECK:STDOUT: declare ptr @llvm.load.relative.i32(ptr, i32) #3 // CHECK:STDOUT: -// CHECK:STDOUT: attributes #0 = { nounwind } -// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } -// CHECK:STDOUT: attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: read) } +// CHECK:STDOUT: attributes #0 = { alwaysinline nounwind } +// CHECK:STDOUT: attributes #1 = { nounwind } +// CHECK:STDOUT: attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: attributes #3 = { nocallback nofree nosync nounwind willreturn memory(argmem: read) } // CHECK:STDOUT: // CHECK:STDOUT: !llvm.module.flags = !{!0, !1} // CHECK:STDOUT: !llvm.dbg.cu = !{!2} @@ -807,33 +827,37 @@ fn Use() { // CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} // CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) // CHECK:STDOUT: !3 = !DIFile(filename: "call_impl.carbon", directory: "") -// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "Use", linkageName: "_CUse.Main", scope: null, file: !3, line: 13, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "F", linkageName: "_CF:thunk:Base.Main:Derived.Main", scope: null, file: !3, line: 10, type: !5, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !8) // CHECK:STDOUT: !5 = !DISubroutineType(types: !6) -// CHECK:STDOUT: !6 = !{null} -// CHECK:STDOUT: !7 = !DILocation(line: 14, column: 3, scope: !4) -// CHECK:STDOUT: !8 = !DILocation(line: 14, column: 21, scope: !4) -// CHECK:STDOUT: !9 = !DILocation(line: 14, column: 30, scope: !4) -// CHECK:STDOUT: !10 = !DILocation(line: 15, column: 3, scope: !4) -// CHECK:STDOUT: !11 = !DILocation(line: 13, column: 1, scope: !4) -// CHECK:STDOUT: !12 = distinct !DISubprogram(name: "Op", linkageName: "_COp.a8f528cd70a29ff8:core.Destroy.Core", scope: null, file: !3, line: 14, type: !13, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !16) -// CHECK:STDOUT: !13 = !DISubroutineType(types: !14) -// CHECK:STDOUT: !14 = !{null, !15} -// CHECK:STDOUT: !15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) -// CHECK:STDOUT: !16 = !{!17} -// CHECK:STDOUT: !17 = !DILocalVariable(arg: 1, scope: !12, type: !15) -// CHECK:STDOUT: !18 = !DILocation(line: 14, column: 3, scope: !12) -// CHECK:STDOUT: !19 = distinct !DISubprogram(name: "Op", linkageName: "_COp.a45b619846c20518:core.Destroy.Core", scope: null, file: !3, line: 14, type: !13, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !20) +// CHECK:STDOUT: !6 = !{null, !7} +// CHECK:STDOUT: !7 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +// CHECK:STDOUT: !8 = !{!9} +// CHECK:STDOUT: !9 = !DILocalVariable(arg: 1, scope: !4, type: !7) +// CHECK:STDOUT: !10 = !DILocation(line: 10, column: 3, scope: !4) +// CHECK:STDOUT: !11 = distinct !DISubprogram(name: "Use", linkageName: "_CUse.Main", scope: null, file: !3, line: 13, type: !12, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !12 = !DISubroutineType(types: !13) +// CHECK:STDOUT: !13 = !{null} +// CHECK:STDOUT: !14 = !DILocation(line: 14, column: 3, scope: !11) +// CHECK:STDOUT: !15 = !DILocation(line: 14, column: 21, scope: !11) +// CHECK:STDOUT: !16 = !DILocation(line: 14, column: 30, scope: !11) +// CHECK:STDOUT: !17 = !DILocation(line: 15, column: 3, scope: !11) +// CHECK:STDOUT: !18 = !DILocation(line: 13, column: 1, scope: !11) +// CHECK:STDOUT: !19 = distinct !DISubprogram(name: "Op", linkageName: "_COp.a8f528cd70a29ff8:core.Destroy.Core", scope: null, file: !3, line: 14, type: !5, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !20) // CHECK:STDOUT: !20 = !{!21} -// CHECK:STDOUT: !21 = !DILocalVariable(arg: 1, scope: !19, type: !15) +// CHECK:STDOUT: !21 = !DILocalVariable(arg: 1, scope: !19, type: !7) // CHECK:STDOUT: !22 = !DILocation(line: 14, column: 3, scope: !19) -// CHECK:STDOUT: !23 = distinct !DISubprogram(name: "Op", linkageName: "_COp.4e1364ec5889efef:core.Destroy.Core", scope: null, file: !3, line: 14, type: !13, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !24) +// CHECK:STDOUT: !23 = distinct !DISubprogram(name: "Op", linkageName: "_COp.a45b619846c20518:core.Destroy.Core", scope: null, file: !3, line: 14, type: !5, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !24) // CHECK:STDOUT: !24 = !{!25} -// CHECK:STDOUT: !25 = !DILocalVariable(arg: 1, scope: !23, type: !15) +// CHECK:STDOUT: !25 = !DILocalVariable(arg: 1, scope: !23, type: !7) // CHECK:STDOUT: !26 = !DILocation(line: 14, column: 3, scope: !23) -// CHECK:STDOUT: !27 = distinct !DISubprogram(name: "Op", linkageName: "_COp.8185e3504351afa8:core.Destroy.Core", scope: null, file: !3, line: 14, type: !13, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !28) +// CHECK:STDOUT: !27 = distinct !DISubprogram(name: "Op", linkageName: "_COp.4e1364ec5889efef:core.Destroy.Core", scope: null, file: !3, line: 14, type: !5, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !28) // CHECK:STDOUT: !28 = !{!29} -// CHECK:STDOUT: !29 = !DILocalVariable(arg: 1, scope: !27, type: !15) +// CHECK:STDOUT: !29 = !DILocalVariable(arg: 1, scope: !27, type: !7) // CHECK:STDOUT: !30 = !DILocation(line: 14, column: 3, scope: !27) +// CHECK:STDOUT: !31 = distinct !DISubprogram(name: "Op", linkageName: "_COp.8185e3504351afa8:core.Destroy.Core", scope: null, file: !3, line: 14, type: !5, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !32) +// CHECK:STDOUT: !32 = !{!33} +// CHECK:STDOUT: !33 = !DILocalVariable(arg: 1, scope: !31, type: !7) +// CHECK:STDOUT: !34 = !DILocation(line: 14, column: 3, scope: !31) // CHECK:STDOUT: ; ModuleID = 'generic_noop.carbon' // CHECK:STDOUT: source_filename = "generic_noop.carbon" // CHECK:STDOUT: @@ -1095,14 +1119,14 @@ fn Use() { // CHECK:STDOUT: ; Function Attrs: alwaysinline nounwind // CHECK:STDOUT: define void @"_CF:thunk:Base.Main:Derived.Main"(ptr sret({}) %return, ptr %self, ptr %n) #1 !dbg !12 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: %.loc17_58.1.temp = alloca {}, align 1, !dbg !16 +// CHECK:STDOUT: %.loc17_58.2.temp = alloca {}, align 1, !dbg !16 // CHECK:STDOUT: %.loc12_33.1.temp = alloca {}, align 1, !dbg !17 -// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc17_58.1.temp), !dbg !16 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc17_58.2.temp), !dbg !16 // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc12_33.1.temp), !dbg !17 // CHECK:STDOUT: call void @"_CConvert.From.Main:ImplicitAs.a3bb440f6426a7d6.Core"(ptr %.loc12_33.1.temp, ptr %n), !dbg !17 -// CHECK:STDOUT: call void @_CF.Derived.Main(ptr %.loc17_58.1.temp, ptr %self, ptr %.loc12_33.1.temp), !dbg !16 -// CHECK:STDOUT: call void @"_CConvert.From.Main:ImplicitAs.a3bb440f6426a7d6.Core"(ptr %return, ptr %.loc17_58.1.temp), !dbg !16 -// CHECK:STDOUT: call void @"_COp.419398171642b7b7:core.Destroy.Core"(ptr %.loc17_58.1.temp), !dbg !16 +// CHECK:STDOUT: call void @_CF.Derived.Main(ptr %.loc17_58.2.temp, ptr %self, ptr %.loc12_33.1.temp), !dbg !16 +// CHECK:STDOUT: call void @"_CConvert.From.Main:ImplicitAs.a3bb440f6426a7d6.Core"(ptr %return, ptr %.loc17_58.2.temp), !dbg !16 +// CHECK:STDOUT: call void @"_COp.419398171642b7b7:core.Destroy.Core"(ptr %.loc17_58.2.temp), !dbg !16 // CHECK:STDOUT: call void @"_COp.41ca6508118965ec:core.Destroy.Core"(ptr %.loc12_33.1.temp), !dbg !17 // CHECK:STDOUT: ret void, !dbg !16 // CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/interop/cpp/class/import/dynamic.carbon b/toolchain/lower/testdata/interop/cpp/class/import/dynamic.carbon index d7024540e0ca..8fc39ce14375 100644 --- a/toolchain/lower/testdata/interop/cpp/class/import/dynamic.carbon +++ b/toolchain/lower/testdata/interop/cpp/class/import/dynamic.carbon @@ -164,42 +164,47 @@ auto DoThing() -> void { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: alwaysinline nounwind -// CHECK:STDOUT: define void @"_C__destroy_thunk:thunk.Derived.Main"(ptr %self) #6 !dbg !29 { +// CHECK:STDOUT: define void @"_Cfunc:thunk:Base.Cpp:Derived.Main"(ptr %self) #6 !dbg !29 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: call void @"_COp.8185e3504351afa8:core.Destroy.Core"(ptr %self), !dbg !32 +// CHECK:STDOUT: call void @_Cfunc.Derived.Main(ptr %self), !dbg !32 // CHECK:STDOUT: ret void, !dbg !32 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define weak_odr void @"_COp.13b019890e86b6be:core.Destroy.Core"(ptr %self) #5 !dbg !33 { +// CHECK:STDOUT: ; Function Attrs: alwaysinline nounwind +// CHECK:STDOUT: define void @"_C__destroy_thunk:thunk.Derived.Main"(ptr %self) #6 !dbg !33 { // CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @"_COp.8185e3504351afa8:core.Destroy.Core"(ptr %self), !dbg !36 // CHECK:STDOUT: ret void, !dbg !36 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define weak_odr void @"_COp.8185e3504351afa8:core.Destroy.Core"(ptr %self) #5 !dbg !37 { +// CHECK:STDOUT: define weak_odr void @"_COp.13b019890e86b6be:core.Destroy.Core"(ptr %self) #5 !dbg !37 { // CHECK:STDOUT: entry: // CHECK:STDOUT: ret void, !dbg !40 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define void @_Cfunc__carbon_thunk.Derived.Main(ptr %self) #5 !dbg !41 { +// CHECK:STDOUT: define weak_odr void @"_COp.8185e3504351afa8:core.Destroy.Core"(ptr %self) #5 !dbg !41 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: %Derived.func.call.vtable = load ptr, ptr %self, align 8, !dbg !44 -// CHECK:STDOUT: %Derived.func.call = call ptr @llvm.load.relative.i32(ptr %Derived.func.call.vtable, i32 4), !dbg !44 -// CHECK:STDOUT: call void %Derived.func.call(ptr %self), !dbg !44 // CHECK:STDOUT: ret void, !dbg !44 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define void @_Cother_func__carbon_thunk.Derived.Main(ptr %self) #5 !dbg !45 { +// CHECK:STDOUT: define void @_Cfunc__carbon_thunk.Derived.Main(ptr %self) #5 !dbg !45 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: %Derived.other_func.call.vtable = load ptr, ptr %self, align 8, !dbg !48 -// CHECK:STDOUT: %Derived.other_func.call = call ptr @llvm.load.relative.i32(ptr %Derived.other_func.call.vtable, i32 8), !dbg !48 -// CHECK:STDOUT: call void %Derived.other_func.call(ptr %self), !dbg !48 +// CHECK:STDOUT: call void @_Cfunc.Derived.Main(ptr %self), !dbg !48 // CHECK:STDOUT: ret void, !dbg !48 // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_Cother_func__carbon_thunk.Derived.Main(ptr %self) #5 !dbg !49 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %Derived.other_func.call.vtable = load ptr, ptr %self, align 8, !dbg !52 +// CHECK:STDOUT: %Derived.other_func.call = call ptr @llvm.load.relative.i32(ptr %Derived.other_func.call.vtable, i32 8), !dbg !52 +// CHECK:STDOUT: call void %Derived.other_func.call(ptr %self), !dbg !52 +// CHECK:STDOUT: ret void, !dbg !52 +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: read) // CHECK:STDOUT: declare ptr @llvm.load.relative.i32(ptr, i32) #7 // CHECK:STDOUT: @@ -238,7 +243,7 @@ auto DoThing() -> void { // CHECK:STDOUT: define linkonce_odr dso_local void @_ZN11FurtherBaseC2Ev(ptr noundef nonnull align 8 dereferenceable(8) %this) unnamed_addr #3 comdat align 2 { // CHECK:STDOUT: entry: // CHECK:STDOUT: %this.addr = alloca ptr, align 8 -// CHECK:STDOUT: store ptr %this, ptr %this.addr, align 8, !tbaa !49 +// CHECK:STDOUT: store ptr %this, ptr %this.addr, align 8, !tbaa !53 // CHECK:STDOUT: %this1 = load ptr, ptr %this.addr, align 8 // CHECK:STDOUT: store ptr getelementptr inbounds inrange(-16, 8) ({ [3 x ptr] }, ptr @_ZTV11FurtherBase, i32 0, i32 0, i32 2), ptr %this1, align 8, !tbaa !16 // CHECK:STDOUT: ret void @@ -248,7 +253,6 @@ auto DoThing() -> void { // CHECK:STDOUT: uselistorder ptr @_ZN6Carbon7DerivedC2Ev, { 1, 0 } // CHECK:STDOUT: uselistorder ptr @_ZN6Carbon7DerivedD2Ev, { 1, 0 } // CHECK:STDOUT: uselistorder ptr @_ZN11FurtherBase17further_base_funcEv, { 2, 1, 0 } -// CHECK:STDOUT: uselistorder ptr @llvm.load.relative.i32, { 1, 0 } // CHECK:STDOUT: // CHECK:STDOUT: attributes #0 = { mustprogress noinline nounwind optnone uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } // CHECK:STDOUT: attributes #1 = { mustprogress nounwind uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } @@ -293,25 +297,29 @@ auto DoThing() -> void { // CHECK:STDOUT: !26 = !{!27} // CHECK:STDOUT: !27 = !DILocalVariable(arg: 1, scope: !25, type: !21) // CHECK:STDOUT: !28 = !DILocation(line: 26, column: 3, scope: !25) -// CHECK:STDOUT: !29 = distinct !DISubprogram(name: "__destroy_thunk", linkageName: "_C__destroy_thunk:thunk.Derived.Main", scope: null, file: !6, line: 22, type: !19, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !30) +// CHECK:STDOUT: !29 = distinct !DISubprogram(name: "func", linkageName: "_Cfunc:thunk:Base.Cpp:Derived.Main", scope: null, file: !6, line: 26, type: !19, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !30) // CHECK:STDOUT: !30 = !{!31} // CHECK:STDOUT: !31 = !DILocalVariable(arg: 1, scope: !29, type: !21) -// CHECK:STDOUT: !32 = !DILocation(line: 22, column: 1, scope: !29) -// CHECK:STDOUT: !33 = distinct !DISubprogram(name: "Op", linkageName: "_COp.13b019890e86b6be:core.Destroy.Core", scope: null, file: !6, line: 22, type: !19, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !34) +// CHECK:STDOUT: !32 = !DILocation(line: 26, column: 3, scope: !29) +// CHECK:STDOUT: !33 = distinct !DISubprogram(name: "__destroy_thunk", linkageName: "_C__destroy_thunk:thunk.Derived.Main", scope: null, file: !6, line: 22, type: !19, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !34) // CHECK:STDOUT: !34 = !{!35} // CHECK:STDOUT: !35 = !DILocalVariable(arg: 1, scope: !33, type: !21) // CHECK:STDOUT: !36 = !DILocation(line: 22, column: 1, scope: !33) -// CHECK:STDOUT: !37 = distinct !DISubprogram(name: "Op", linkageName: "_COp.8185e3504351afa8:core.Destroy.Core", scope: null, file: !6, line: 22, type: !19, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !38) +// CHECK:STDOUT: !37 = distinct !DISubprogram(name: "Op", linkageName: "_COp.13b019890e86b6be:core.Destroy.Core", scope: null, file: !6, line: 22, type: !19, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !38) // CHECK:STDOUT: !38 = !{!39} // CHECK:STDOUT: !39 = !DILocalVariable(arg: 1, scope: !37, type: !21) // CHECK:STDOUT: !40 = !DILocation(line: 22, column: 1, scope: !37) -// CHECK:STDOUT: !41 = distinct !DISubprogram(name: "func__carbon_thunk", linkageName: "_Cfunc__carbon_thunk.Derived.Main", scope: null, file: !6, line: 26, type: !19, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !42) +// CHECK:STDOUT: !41 = distinct !DISubprogram(name: "Op", linkageName: "_COp.8185e3504351afa8:core.Destroy.Core", scope: null, file: !6, line: 22, type: !19, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !42) // CHECK:STDOUT: !42 = !{!43} // CHECK:STDOUT: !43 = !DILocalVariable(arg: 1, scope: !41, type: !21) -// CHECK:STDOUT: !44 = !DILocation(line: 26, column: 3, scope: !41) -// CHECK:STDOUT: !45 = distinct !DISubprogram(name: "other_func__carbon_thunk", linkageName: "_Cother_func__carbon_thunk.Derived.Main", scope: null, file: !6, line: 24, type: !19, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !46) +// CHECK:STDOUT: !44 = !DILocation(line: 22, column: 1, scope: !41) +// CHECK:STDOUT: !45 = distinct !DISubprogram(name: "func__carbon_thunk", linkageName: "_Cfunc__carbon_thunk.Derived.Main", scope: null, file: !6, line: 26, type: !19, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !46) // CHECK:STDOUT: !46 = !{!47} // CHECK:STDOUT: !47 = !DILocalVariable(arg: 1, scope: !45, type: !21) -// CHECK:STDOUT: !48 = !DILocation(line: 24, column: 3, scope: !45) -// CHECK:STDOUT: !49 = !{!50, !50, i64 0} -// CHECK:STDOUT: !50 = !{!"p1 _ZTS11FurtherBase", !13, i64 0} +// CHECK:STDOUT: !48 = !DILocation(line: 26, column: 3, scope: !45) +// CHECK:STDOUT: !49 = distinct !DISubprogram(name: "other_func__carbon_thunk", linkageName: "_Cother_func__carbon_thunk.Derived.Main", scope: null, file: !6, line: 24, type: !19, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !50) +// CHECK:STDOUT: !50 = !{!51} +// CHECK:STDOUT: !51 = !DILocalVariable(arg: 1, scope: !49, type: !21) +// CHECK:STDOUT: !52 = !DILocation(line: 24, column: 3, scope: !49) +// CHECK:STDOUT: !53 = !{!54, !54, i64 0} +// CHECK:STDOUT: !54 = !{!"p1 _ZTS11FurtherBase", !13, i64 0} diff --git a/toolchain/lower/testdata/interop/cpp/class/virtual_fn.carbon b/toolchain/lower/testdata/interop/cpp/class/virtual_fn.carbon index 47bf0b532c52..278abeed612d 100644 --- a/toolchain/lower/testdata/interop/cpp/class/virtual_fn.carbon +++ b/toolchain/lower/testdata/interop/cpp/class/virtual_fn.carbon @@ -95,7 +95,7 @@ void delete_new_Base() { // CHECK:STDOUT: entry: // CHECK:STDOUT: %p = alloca ptr, align 8 // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %p) #8 -// CHECK:STDOUT: %call = call noalias noundef nonnull ptr @_Znwm(i64 noundef 8) #11 +// CHECK:STDOUT: %call = call noalias noundef nonnull ptr @_Znwm(i64 noundef 8) #10 // CHECK:STDOUT: call void @_ZN6Carbon5FinalC2Ev(ptr noundef nonnull align 8 dereferenceable(8) %call) #8 // CHECK:STDOUT: store ptr %call, ptr %p, align 8, !tbaa !11 // CHECK:STDOUT: %0 = load ptr, ptr %p, align 8, !tbaa !11 @@ -104,7 +104,7 @@ void delete_new_Base() { // CHECK:STDOUT: // CHECK:STDOUT: delete.notnull: ; preds = %entry // CHECK:STDOUT: call void @_ZN6Carbon5FinalD2Ev(ptr noundef nonnull align 8 dead_on_return(8) dereferenceable(8) %0) #8 -// CHECK:STDOUT: call void @_ZdlPvm(ptr noundef %0, i64 noundef 8) #12 +// CHECK:STDOUT: call void @_ZdlPvm(ptr noundef %0, i64 noundef 8) #11 // CHECK:STDOUT: br label %delete.end // CHECK:STDOUT: // CHECK:STDOUT: delete.end: ; preds = %delete.notnull, %entry @@ -151,7 +151,7 @@ void delete_new_Base() { // CHECK:STDOUT: entry: // CHECK:STDOUT: %p = alloca ptr, align 8 // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %p) #8 -// CHECK:STDOUT: %call = call noalias noundef nonnull ptr @_Znwm(i64 noundef 8) #11 +// CHECK:STDOUT: %call = call noalias noundef nonnull ptr @_Znwm(i64 noundef 8) #10 // CHECK:STDOUT: call void @_ZN6Carbon4BaseC2Ev(ptr noundef nonnull align 8 dereferenceable(8) %call) #8 // CHECK:STDOUT: store ptr %call, ptr %p, align 8, !tbaa !16 // CHECK:STDOUT: %0 = load ptr, ptr %p, align 8, !tbaa !16 @@ -196,63 +196,70 @@ void delete_new_Base() { // CHECK:STDOUT: // CHECK:STDOUT: declare void @_CF.Final.Main(ptr) // CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: alwaysinline nounwind +// CHECK:STDOUT: define void @"_CF:thunk:A.Cpp:Final.Main"(ptr %self) #6 !dbg !22 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @_CF.Final.Main(ptr %self), !dbg !28 +// CHECK:STDOUT: ret void, !dbg !28 +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: declare void @_CF.Base.Main(ptr) // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: alwaysinline nounwind -// CHECK:STDOUT: define void @"_C__destroy_thunk:thunk.Final.Main"(ptr %self) #6 !dbg !22 { +// CHECK:STDOUT: define void @"_CF:thunk:A.Cpp:Base.Main"(ptr %self) #6 !dbg !29 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: call void @"_COp.6622c02ec7a967c3:core.Destroy.Core"(ptr %self), !dbg !28 -// CHECK:STDOUT: ret void, !dbg !28 +// CHECK:STDOUT: call void @_CF.Base.Main(ptr %self), !dbg !32 +// CHECK:STDOUT: ret void, !dbg !32 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: alwaysinline nounwind +// CHECK:STDOUT: define void @"_C__destroy_thunk:thunk.Final.Main"(ptr %self) #6 !dbg !33 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @"_COp.6622c02ec7a967c3:core.Destroy.Core"(ptr %self), !dbg !36 +// CHECK:STDOUT: ret void, !dbg !36 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind // CHECK:STDOUT: declare void @_ZN1AD1Ev(ptr noundef nonnull align 8 dead_on_return(8) dereferenceable(8)) unnamed_addr #7 // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define weak_odr void @"_COp.4fc2347fc019a955:core.Destroy.Core"(ptr %self) #8 !dbg !29 { +// CHECK:STDOUT: define weak_odr void @"_COp.4fc2347fc019a955:core.Destroy.Core"(ptr %self) #8 !dbg !37 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: ret void, !dbg !32 -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define weak_odr void @"_COp.6622c02ec7a967c3:core.Destroy.Core"(ptr %self) #8 !dbg !33 { -// CHECK:STDOUT: entry: -// CHECK:STDOUT: ret void, !dbg !36 -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define void @_CF__carbon_thunk.Final.Main(ptr %self) #8 !dbg !37 { -// CHECK:STDOUT: entry: -// CHECK:STDOUT: %Final.F.call.vtable = load ptr, ptr %self, align 8, !dbg !40 -// CHECK:STDOUT: %Final.F.call = call ptr @llvm.load.relative.i32(ptr %Final.F.call.vtable, i32 8), !dbg !40 -// CHECK:STDOUT: call void %Final.F.call(ptr %self), !dbg !40 // CHECK:STDOUT: ret void, !dbg !40 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: ; Function Attrs: alwaysinline nounwind -// CHECK:STDOUT: define void @"_C__destroy_thunk:thunk.Base.Main"(ptr %self) #6 !dbg !41 { +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.6622c02ec7a967c3:core.Destroy.Core"(ptr %self) #8 !dbg !41 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: call void @"_COp.a45b619846c20518:core.Destroy.Core"(ptr %self), !dbg !44 // CHECK:STDOUT: ret void, !dbg !44 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define weak_odr void @"_COp.a45b619846c20518:core.Destroy.Core"(ptr %self) #8 !dbg !45 { +// CHECK:STDOUT: define void @_CF__carbon_thunk.Final.Main(ptr %self) #8 !dbg !45 { // CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @_CF.Final.Main(ptr %self), !dbg !48 // CHECK:STDOUT: ret void, !dbg !48 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define void @_CF__carbon_thunk.Base.Main(ptr %self) #8 !dbg !49 { +// CHECK:STDOUT: ; Function Attrs: alwaysinline nounwind +// CHECK:STDOUT: define void @"_C__destroy_thunk:thunk.Base.Main"(ptr %self) #6 !dbg !49 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: %Base.F.call.vtable = load ptr, ptr %self, align 8, !dbg !52 -// CHECK:STDOUT: %Base.F.call = call ptr @llvm.load.relative.i32(ptr %Base.F.call.vtable, i32 8), !dbg !52 -// CHECK:STDOUT: call void %Base.F.call(ptr %self), !dbg !52 +// CHECK:STDOUT: call void @"_COp.a45b619846c20518:core.Destroy.Core"(ptr %self), !dbg !52 // CHECK:STDOUT: ret void, !dbg !52 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: read) -// CHECK:STDOUT: declare ptr @llvm.load.relative.i32(ptr, i32) #9 +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.a45b619846c20518:core.Destroy.Core"(ptr %self) #8 !dbg !53 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !56 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CF__carbon_thunk.Base.Main(ptr %self) #8 !dbg !57 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @_CF.Base.Main(ptr %self), !dbg !60 +// CHECK:STDOUT: ret void, !dbg !60 +// CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: inlinehint mustprogress nounwind uwtable // CHECK:STDOUT: define linkonce_odr dso_local void @_ZN1AC2Ev(ptr noundef nonnull align 8 dereferenceable(8) %this) unnamed_addr #3 comdat align 2 { @@ -271,11 +278,11 @@ void delete_new_Base() { // CHECK:STDOUT: store ptr %this, ptr %this.addr, align 8, !tbaa !11 // CHECK:STDOUT: %this1 = load ptr, ptr %this.addr, align 8 // CHECK:STDOUT: call void @_ZN6Carbon5FinalD2Ev(ptr noundef nonnull align 8 dead_on_return(8) dereferenceable(8) %this1) #8 -// CHECK:STDOUT: call void @_ZdlPvm(ptr noundef %this1, i64 noundef 8) #12 +// CHECK:STDOUT: call void @_ZdlPvm(ptr noundef %this1, i64 noundef 8) #11 // CHECK:STDOUT: ret void // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: declare void @_ZNK1A1FEv(ptr noundef nonnull align 8 dereferenceable(8)) unnamed_addr #10 +// CHECK:STDOUT: declare void @_ZNK1A1FEv(ptr noundef nonnull align 8 dereferenceable(8)) unnamed_addr #9 // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress nounwind uwtable // CHECK:STDOUT: define internal void @_ZN6Carbon5Final1FEv(ptr noundef nonnull align 8 dereferenceable(8) %this) unnamed_addr #5 align 2 { @@ -311,7 +318,7 @@ void delete_new_Base() { // CHECK:STDOUT: store ptr %this, ptr %this.addr, align 8, !tbaa !16 // CHECK:STDOUT: %this1 = load ptr, ptr %this.addr, align 8 // CHECK:STDOUT: call void @_ZN6Carbon4BaseD2Ev(ptr noundef nonnull align 8 dead_on_return(8) dereferenceable(8) %this1) #8 -// CHECK:STDOUT: call void @_ZdlPvm(ptr noundef %this1, i64 noundef 8) #12 +// CHECK:STDOUT: call void @_ZdlPvm(ptr noundef %this1, i64 noundef 8) #11 // CHECK:STDOUT: ret void // CHECK:STDOUT: } // CHECK:STDOUT: @@ -327,7 +334,6 @@ void delete_new_Base() { // CHECK:STDOUT: // CHECK:STDOUT: ; uselistorder directives // CHECK:STDOUT: uselistorder ptr @_ZN6Carbon5FinalD2Ev, { 2, 1, 0 } -// CHECK:STDOUT: uselistorder ptr @llvm.load.relative.i32, { 1, 0 } // CHECK:STDOUT: uselistorder ptr @_ZN1AC2Ev, { 1, 0 } // CHECK:STDOUT: uselistorder ptr @_ZNK1A1FEv, { 2, 1, 0 } // CHECK:STDOUT: uselistorder ptr @__cxa_pure_virtual, { 1, 0 } @@ -342,10 +348,9 @@ void delete_new_Base() { // CHECK:STDOUT: attributes #6 = { alwaysinline nounwind } // CHECK:STDOUT: attributes #7 = { nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } // CHECK:STDOUT: attributes #8 = { nounwind } -// CHECK:STDOUT: attributes #9 = { nocallback nofree nosync nounwind willreturn memory(argmem: read) } -// CHECK:STDOUT: attributes #10 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } -// CHECK:STDOUT: attributes #11 = { builtin allocsize(0) } -// CHECK:STDOUT: attributes #12 = { builtin nounwind } +// CHECK:STDOUT: attributes #9 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +// CHECK:STDOUT: attributes #10 = { builtin allocsize(0) } +// CHECK:STDOUT: attributes #11 = { builtin nounwind } // CHECK:STDOUT: // CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4} // CHECK:STDOUT: !llvm.dbg.cu = !{!5} @@ -373,34 +378,42 @@ void delete_new_Base() { // CHECK:STDOUT: !19 = !{!"p1 _ZTS1A", !13, i64 0} // CHECK:STDOUT: !20 = !{} // CHECK:STDOUT: !21 = !{i64 8} -// CHECK:STDOUT: !22 = distinct !DISubprogram(name: "__destroy_thunk", linkageName: "_C__destroy_thunk:thunk.Final.Main", scope: null, file: !6, line: 12, type: !23, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !26) +// CHECK:STDOUT: !22 = distinct !DISubprogram(name: "F", linkageName: "_CF:thunk:A.Cpp:Final.Main", scope: null, file: !6, line: 14, type: !23, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !26) // CHECK:STDOUT: !23 = !DISubroutineType(types: !24) // CHECK:STDOUT: !24 = !{null, !25} // CHECK:STDOUT: !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) // CHECK:STDOUT: !26 = !{!27} // CHECK:STDOUT: !27 = !DILocalVariable(arg: 1, scope: !22, type: !25) -// CHECK:STDOUT: !28 = !DILocation(line: 12, column: 1, scope: !22) -// CHECK:STDOUT: !29 = distinct !DISubprogram(name: "Op", linkageName: "_COp.4fc2347fc019a955:core.Destroy.Core", scope: null, file: !6, line: 12, type: !23, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !30) +// CHECK:STDOUT: !28 = !DILocation(line: 14, column: 3, scope: !22) +// CHECK:STDOUT: !29 = distinct !DISubprogram(name: "F", linkageName: "_CF:thunk:A.Cpp:Base.Main", scope: null, file: !6, line: 19, type: !23, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !30) // CHECK:STDOUT: !30 = !{!31} // CHECK:STDOUT: !31 = !DILocalVariable(arg: 1, scope: !29, type: !25) -// CHECK:STDOUT: !32 = !DILocation(line: 12, column: 1, scope: !29) -// CHECK:STDOUT: !33 = distinct !DISubprogram(name: "Op", linkageName: "_COp.6622c02ec7a967c3:core.Destroy.Core", scope: null, file: !6, line: 12, type: !23, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !34) +// CHECK:STDOUT: !32 = !DILocation(line: 19, column: 3, scope: !29) +// CHECK:STDOUT: !33 = distinct !DISubprogram(name: "__destroy_thunk", linkageName: "_C__destroy_thunk:thunk.Final.Main", scope: null, file: !6, line: 12, type: !23, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !34) // CHECK:STDOUT: !34 = !{!35} // CHECK:STDOUT: !35 = !DILocalVariable(arg: 1, scope: !33, type: !25) // CHECK:STDOUT: !36 = !DILocation(line: 12, column: 1, scope: !33) -// CHECK:STDOUT: !37 = distinct !DISubprogram(name: "F__carbon_thunk", linkageName: "_CF__carbon_thunk.Final.Main", scope: null, file: !6, line: 14, type: !23, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !38) +// CHECK:STDOUT: !37 = distinct !DISubprogram(name: "Op", linkageName: "_COp.4fc2347fc019a955:core.Destroy.Core", scope: null, file: !6, line: 12, type: !23, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !38) // CHECK:STDOUT: !38 = !{!39} // CHECK:STDOUT: !39 = !DILocalVariable(arg: 1, scope: !37, type: !25) -// CHECK:STDOUT: !40 = !DILocation(line: 14, column: 3, scope: !37) -// CHECK:STDOUT: !41 = distinct !DISubprogram(name: "__destroy_thunk", linkageName: "_C__destroy_thunk:thunk.Base.Main", scope: null, file: !6, line: 17, type: !23, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !42) +// CHECK:STDOUT: !40 = !DILocation(line: 12, column: 1, scope: !37) +// CHECK:STDOUT: !41 = distinct !DISubprogram(name: "Op", linkageName: "_COp.6622c02ec7a967c3:core.Destroy.Core", scope: null, file: !6, line: 12, type: !23, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !42) // CHECK:STDOUT: !42 = !{!43} // CHECK:STDOUT: !43 = !DILocalVariable(arg: 1, scope: !41, type: !25) -// CHECK:STDOUT: !44 = !DILocation(line: 17, column: 1, scope: !41) -// CHECK:STDOUT: !45 = distinct !DISubprogram(name: "Op", linkageName: "_COp.a45b619846c20518:core.Destroy.Core", scope: null, file: !6, line: 17, type: !23, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !46) +// CHECK:STDOUT: !44 = !DILocation(line: 12, column: 1, scope: !41) +// CHECK:STDOUT: !45 = distinct !DISubprogram(name: "F__carbon_thunk", linkageName: "_CF__carbon_thunk.Final.Main", scope: null, file: !6, line: 14, type: !23, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !46) // CHECK:STDOUT: !46 = !{!47} // CHECK:STDOUT: !47 = !DILocalVariable(arg: 1, scope: !45, type: !25) -// CHECK:STDOUT: !48 = !DILocation(line: 17, column: 1, scope: !45) -// CHECK:STDOUT: !49 = distinct !DISubprogram(name: "F__carbon_thunk", linkageName: "_CF__carbon_thunk.Base.Main", scope: null, file: !6, line: 19, type: !23, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !50) +// CHECK:STDOUT: !48 = !DILocation(line: 14, column: 3, scope: !45) +// CHECK:STDOUT: !49 = distinct !DISubprogram(name: "__destroy_thunk", linkageName: "_C__destroy_thunk:thunk.Base.Main", scope: null, file: !6, line: 17, type: !23, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !50) // CHECK:STDOUT: !50 = !{!51} // CHECK:STDOUT: !51 = !DILocalVariable(arg: 1, scope: !49, type: !25) -// CHECK:STDOUT: !52 = !DILocation(line: 19, column: 3, scope: !49) +// CHECK:STDOUT: !52 = !DILocation(line: 17, column: 1, scope: !49) +// CHECK:STDOUT: !53 = distinct !DISubprogram(name: "Op", linkageName: "_COp.a45b619846c20518:core.Destroy.Core", scope: null, file: !6, line: 17, type: !23, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !54) +// CHECK:STDOUT: !54 = !{!55} +// CHECK:STDOUT: !55 = !DILocalVariable(arg: 1, scope: !53, type: !25) +// CHECK:STDOUT: !56 = !DILocation(line: 17, column: 1, scope: !53) +// CHECK:STDOUT: !57 = distinct !DISubprogram(name: "F__carbon_thunk", linkageName: "_CF__carbon_thunk.Base.Main", scope: null, file: !6, line: 19, type: !23, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !58) +// CHECK:STDOUT: !58 = !{!59} +// CHECK:STDOUT: !59 = !DILocalVariable(arg: 1, scope: !57, type: !25) +// CHECK:STDOUT: !60 = !DILocation(line: 19, column: 3, scope: !57) diff --git a/toolchain/sem_ir/inst_kind.def b/toolchain/sem_ir/inst_kind.def index 0cc514a1d04f..5ce7c6deb24d 100644 --- a/toolchain/sem_ir/inst_kind.def +++ b/toolchain/sem_ir/inst_kind.def @@ -61,6 +61,7 @@ CARBON_SEM_IR_INST_KIND(CppTemplateNameType) CARBON_SEM_IR_INST_KIND(CustomLayoutType) CARBON_SEM_IR_INST_KIND(CustomWitness) CARBON_SEM_IR_INST_KIND(Deref) +CARBON_SEM_IR_INST_KIND(EnclosingClassAccess) CARBON_SEM_IR_INST_KIND(ErrorInst) CARBON_SEM_IR_INST_KIND(ExportDecl) CARBON_SEM_IR_INST_KIND(ExprPattern) diff --git a/toolchain/sem_ir/thunk.h b/toolchain/sem_ir/thunk.h index a0844f3c547f..5ecd1c7862bf 100644 --- a/toolchain/sem_ir/thunk.h +++ b/toolchain/sem_ir/thunk.h @@ -10,12 +10,20 @@ namespace Carbon::SemIR { -// Metadata tracking information about a thunk's signature, callee, and specific -// generic parameters. +// Metadata about a thunk. struct ThunkInfo { + // The callee wrapped by the thunk. InstId callee_id; + + // The signature of the thunk. FunctionId signature_id = FunctionId::None; + + // If `signature_id` is generic, the specific that this thunk is for. SpecificId specific_id = SpecificId::None; + + // If this is a thunk for a virtual function override, the type of the + // `self` parameter of the override. + TypeId override_self_type_id = TypeId::None; }; using ThunkStore = ValueStore>; diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index b94755bc6c1f..8d91e182b060 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -623,6 +623,20 @@ struct Deref { InstId pointer_id; }; +// A reference to the class instance which has `element_id` at index `index`. +struct EnclosingClassAccess { + static constexpr auto Kind = + // This is created from other insts, not from a node. + InstKind::EnclosingClassAccess.Define( + {.ir_name = "enclosing_class_access", + .expr_category = ComputedExprCategory::SameAsFirstOperand, + .constant_kind = InstConstantKind::Never}); + + TypeId type_id; + InstId element_id; + ElementIndex index; +}; + // Used when a semantic error has been detected, and a SemIR InstId is still // required. For example, when there is a type checking issue, this will be used // in the type_id. It's typically used as a cue that semantic checking doesn't