Reimplement derived class thunk in terms of down-casting (#7322)

This helps us move away from the clone-with-modifications approach to
thunking, which gets unwieldy as signatures get more complex.

---------

Co-authored-by: Richard Smith <richard@metafoo.co.uk>
This commit is contained in:
Geoff Romer
2026-06-09 21:10:18 +00:00
committed by GitHub
co-authored by Richard Smith
parent 1a26b57732
commit 85c53fa00c
22 changed files with 630 additions and 307 deletions
+7 -7
View File
@@ -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: {
+77 -6
View File
@@ -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<std::pair<SemIR::InstId, SemIR::TypeId>>;
// 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<InheritanceEdge>;
// 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<SemIR::BaseDecl>(base_id);
value_id = AddInst<SemIR::ClassElementAccess>(
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<SemIR::BaseDecl>(edge.base_decl_id);
result_id = AddInst<SemIR::EnclosingClassAccess>(
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<SemIR::AcquireValue>(
context, loc_id,
{.type_id = GetQualifiedType(context, type_id, quals),
.value_id = result_id});
}
return result_id;
}
} // namespace Carbon::Check
// NOLINTEND(misc-no-recursion)
+7
View File
@@ -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_
+20 -4
View File
@@ -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<SemIR::CalleeFunction, const SemIR::Function&> {
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) {
@@ -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
+2 -4
View File
@@ -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,
+5 -10
View File
@@ -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,
+7 -22
View File
@@ -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 &&
+5 -8
View File
@@ -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,
+201 -60
View File
@@ -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 {.<vptr>: %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: <witness> = 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: <namespace> = 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: <elided>
// CHECK:STDOUT: } {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT: %vtable_decl: ref %ptr.454 = vtable_decl @Derived.vtable [concrete = constants.%Derived.vtable_decl]
// CHECK:STDOUT: %complete_type: <witness> = 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 = <poisoned>
// 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: <elided>
// 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> = 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: <elided>
// 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: <elided>
// CHECK:STDOUT: } {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT: <elided>
// 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 = <poisoned>
// 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: <elided>
// 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> = 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: <elided>
// 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: <elided>
// CHECK:STDOUT: } {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT: <elided>
// 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 = <poisoned>
// 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: <elided>
// 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> = 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> = 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: <elided>
// 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> = 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> = bound_method %Derived.F.call, %impl.elem0
// CHECK:STDOUT: <elided>
// 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> = 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> = 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: <elided>
// 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: <elided>
// CHECK:STDOUT: } {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: complete_type_witness = %complete_type
// CHECK:STDOUT: vtable_decl = %vtable_decl
@@ -1386,7 +1461,7 @@ class T2(G2:! type) {
// CHECK:STDOUT: .Base = <poisoned>
// CHECK:STDOUT: .T1 = <poisoned>
// 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: <elided>
// 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> = 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: <witness> = 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> = 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> = 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: <witness> = complete_type_witness %struct_type.base.619 [symbolic]
// CHECK:STDOUT: %require_complete.1e7: <witness> = require_complete_type %Derived [symbolic]
// CHECK:STDOUT: %require_complete.ef1: <witness> = require_complete_type %ptr.e8f [symbolic]
// CHECK:STDOUT: %Derived.F.specific_fn.822c1a.2: <specific function> = 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: <elided>
// 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: <elided>
// CHECK:STDOUT:
// CHECK:STDOUT: class {
// CHECK:STDOUT: <elided>
// 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: <elided>
// CHECK:STDOUT: } {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: complete_type_witness = %complete_type.loc12_1.1
@@ -1462,7 +1560,7 @@ class T2(G2:! type) {
// CHECK:STDOUT: .Base = <poisoned>
// CHECK:STDOUT: .T = <poisoned>
// 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: <witness> = require_complete_type %Derived [symbolic = %require_complete.loc10_28 (constants.%require_complete.1e7)]
// CHECK:STDOUT: %require_complete.loc10_44: <witness> = 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: <elided>
// CHECK:STDOUT:
// CHECK:STDOUT: !definition:
// CHECK:STDOUT: <elided>
// 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> = 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: <elided>
// 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> = bound_method %.loc10_50.3, %F.ref
// CHECK:STDOUT: %Derived.F.specific_fn.loc10_50.1: <specific function> = 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> = 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:
+42 -39
View File
@@ -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<SemIR::AnyBindingPattern>()) {
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<SemIR::ReturnSlotPattern>()) {
@@ -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<SemIR::FunctionId, SemIR::InstId> {
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<SemIR::InstId> param_pattern_ids,
llvm::ArrayRef<SemIR::InstId> 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<SemIR::InstId> 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.<callee_id>` 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<SemIR::InstId> param_pattern_ids,
llvm::ArrayRef<SemIR::InstId> call_arg_ids)
llvm::ArrayRef<SemIR::InstId> 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();
}
+13 -10
View File
@@ -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<SemIR::InstId> param_pattern_ids,
llvm::ArrayRef<SemIR::InstId> 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.
+22
View File
@@ -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<llvm::StructType>(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* {
+7
View File
@@ -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_
+2
View File
@@ -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);
}
+7
View File
@@ -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,
+80 -56
View File
@@ -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: }
@@ -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}
+67 -54
View File
@@ -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)
+1
View File
@@ -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)
+10 -2
View File
@@ -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<ThunkId, ThunkInfo, Tag<CheckIRId>>;
+14
View File
@@ -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<Parse::NodeId>(
{.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