Skip to content

Commit e0de9dd

Browse files
authored
Implement initialization for C++ thunk parameters. (#5938)
When initializing a C++ thunk parameter: * If we have an initializing expression, materialize a temporary and pass its address. * If we have a reference expression, pass its address directly. * If we have a value expression with a pointer value representation, pass the pointer. * Otherwise, create a new temporary and initialize it with a copy of the argument, and pass its address.
1 parent 2e22733 commit e0de9dd

File tree

10 files changed

+665
-446
lines changed

10 files changed

+665
-446
lines changed

toolchain/check/convert.cpp

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,21 @@ static auto MarkInitializerFor(SemIR::File& sem_ir, SemIR::InstId init_id,
5555
}
5656
}
5757

58+
// For a value or initializing expression using a copy value representation,
59+
// copy the value into a temporary object.
60+
static auto CopyValueToTemporary(Context& context, SemIR::InstId init_id)
61+
-> SemIR::InstId {
62+
// TODO: Consider using `None` to mean that we immediately materialize and
63+
// initialize a temporary, rather than two separate instructions.
64+
auto init = context.sem_ir().insts().Get(init_id);
65+
auto temporary_id = AddInstWithCleanup<SemIR::TemporaryStorage>(
66+
context, SemIR::LocId(init_id), {.type_id = init.type_id()});
67+
return AddInst<SemIR::Temporary>(context, SemIR::LocId(init_id),
68+
{.type_id = init.type_id(),
69+
.storage_id = temporary_id,
70+
.init_id = init_id});
71+
}
72+
5873
// Commits to using a temporary to store the result of the initializing
5974
// expression described by `init_id`, and returns the location of the
6075
// temporary. If `discarded` is `true`, the result is discarded, and no
@@ -85,15 +100,7 @@ static auto FinalizeTemporary(Context& context, SemIR::InstId init_id,
85100

86101
// The initializer has no return slot, but we want to produce a temporary
87102
// object. Materialize one now.
88-
// TODO: Consider using `None` to mean that we immediately materialize and
89-
// initialize a temporary, rather than two separate instructions.
90-
auto init = sem_ir.insts().Get(init_id);
91-
auto temporary_id = AddInstWithCleanup<SemIR::TemporaryStorage>(
92-
context, SemIR::LocId(init_id), {.type_id = init.type_id()});
93-
return AddInst<SemIR::Temporary>(context, SemIR::LocId(init_id),
94-
{.type_id = init.type_id(),
95-
.storage_id = temporary_id,
96-
.init_id = init_id});
103+
return CopyValueToTemporary(context, init_id);
97104
}
98105

99106
// Materialize a temporary to hold the result of the given expression if it is
@@ -717,6 +724,8 @@ static auto IsValidExprCategoryForConversionTarget(
717724
category == SemIR::ExprCategory::Initializing;
718725
case ConversionTarget::DurableRef:
719726
return category == SemIR::ExprCategory::DurableRef;
727+
case ConversionTarget::CppThunkRef:
728+
return category == SemIR::ExprCategory::EphemeralRef;
720729
case ConversionTarget::ExplicitAs:
721730
return true;
722731
case ConversionTarget::Initializer:
@@ -1173,6 +1182,34 @@ static auto PerformCopy(Context& context, SemIR::InstId expr_id, bool diagnose)
11731182
return SemIR::ErrorInst::InstId;
11741183
}
11751184

1185+
// Convert a value expression so that it can be used to initialize a C++ thunk
1186+
// parameter.
1187+
static auto ConvertValueForCppThunkRef(Context& context, SemIR::InstId expr_id,
1188+
bool diagnose) -> SemIR::InstId {
1189+
auto expr = context.insts().Get(expr_id);
1190+
1191+
// If the expression has a pointer value representation, extract that and use
1192+
// it directly.
1193+
if (SemIR::ValueRepr::ForType(context.sem_ir(), expr.type_id()).kind ==
1194+
SemIR::ValueRepr::Pointer) {
1195+
return AddInst<SemIR::ValueAsRef>(
1196+
context, SemIR::LocId(expr_id),
1197+
{.type_id = expr.type_id(), .value_id = expr_id});
1198+
}
1199+
1200+
// Otherwise, we need a temporary to pass as the thunk argument. Create a copy
1201+
// and initialize a temporary from it.
1202+
expr_id = PerformCopy(context, expr_id, diagnose);
1203+
if (SemIR::GetExprCategory(context.sem_ir(), expr_id) ==
1204+
SemIR::ExprCategory::Value) {
1205+
// If we still have a value expression, then it's a value expression
1206+
// whose value is being used directly to initialize the object. Copy
1207+
// it into a temporary to form an ephemeral reference.
1208+
expr_id = CopyValueToTemporary(context, expr_id);
1209+
}
1210+
return expr_id;
1211+
}
1212+
11761213
auto PerformAction(Context& context, SemIR::LocId loc_id,
11771214
SemIR::ConvertToValueAction action) -> SemIR::InstId {
11781215
return Convert(context, loc_id, action.inst_id,
@@ -1377,7 +1414,8 @@ auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
13771414
case SemIR::ExprCategory::EphemeralRef:
13781415
// If a reference expression is an acceptable result, we're done.
13791416
if (target.kind == ConversionTarget::ValueOrRef ||
1380-
target.kind == ConversionTarget::Discarded) {
1417+
target.kind == ConversionTarget::Discarded ||
1418+
target.kind == ConversionTarget::CppThunkRef) {
13811419
break;
13821420
}
13831421

@@ -1406,6 +1444,13 @@ auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
14061444
if (target.is_initializer()) {
14071445
expr_id = PerformCopy(context, expr_id, target.diagnose);
14081446
}
1447+
1448+
// When initializing a C++ thunk parameter, form a reference, creating a
1449+
// temporary if needed.
1450+
if (target.kind == ConversionTarget::CppThunkRef) {
1451+
expr_id = ConvertValueForCppThunkRef(context, expr_id, target.diagnose);
1452+
}
1453+
14091454
break;
14101455
}
14111456

toolchain/check/convert.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ struct ConversionTarget {
2121
ValueOrRef,
2222
// Convert to a durable reference of type `type_id`.
2323
DurableRef,
24+
// Convert to a reference of type `type_id`, for use as the argument to a
25+
// C++ thunk.
26+
CppThunkRef,
2427
// Convert for an explicit `as` cast. This allows any expression category
2528
// as the result, and uses the `As` interface instead of the `ImplicitAs`
2629
// interface.

toolchain/check/cpp_thunk.cpp

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "toolchain/check/call.h"
1111
#include "toolchain/check/context.h"
1212
#include "toolchain/check/control_flow.h"
13+
#include "toolchain/check/convert.h"
1314
#include "toolchain/check/literal.h"
1415
#include "toolchain/check/type.h"
1516
#include "toolchain/check/type_completion.h"
@@ -237,7 +238,10 @@ static auto BuildCalleeArgs(clang::Sema& sema,
237238
clang::Expr* call_arg = sema.BuildDeclRefExpr(
238239
thunk_param, thunk_param->getType(), clang::VK_LValue, clang_loc);
239240
if (param_type_changed[i]) {
240-
// TODO: Insert a cast to an rvalue.
241+
// TODO: Consider inserting a cast to an rvalue. Note that we currently
242+
// pass pointers to non-temporary objects as the argument when calling a
243+
// thunk, so we'll need to either change that or generate different thunks
244+
// depending on whether we're moving from each parameter.
241245
clang::ExprResult deref_result =
242246
sema.BuildUnaryOp(nullptr, clang_loc, clang::UO_Deref, call_arg);
243247
CARBON_CHECK(deref_result.isUsable());
@@ -341,22 +345,12 @@ auto PerformCppThunkCall(Context& context, SemIR::LocId loc_id,
341345
GetPointerType(context, context.types().GetInstId(
342346
callee_param_type_id)));
343347

344-
// TODO: Don't create storage if it's already in a storage (depends on
345-
// expression category).
346-
SemIR::InstId temporary_storage_inst_id = AddInstWithCleanup(
348+
arg_id = Convert(context, loc_id, arg_id,
349+
{.kind = ConversionTarget::CppThunkRef,
350+
.type_id = callee_param_type_id});
351+
arg_id = AddInst<SemIR::AddrOf>(
347352
context, loc_id,
348-
SemIR::TemporaryStorage{.type_id = callee_param_type_id});
349-
AddInst(context, loc_id,
350-
SemIR::InitializeFrom{.type_id = callee_param_type_id,
351-
.src_id = arg_id,
352-
.dest_id = temporary_storage_inst_id});
353-
354-
// TODO: Do not use `InitializeFrom` directly. Use the `Initialize`
355-
// machinery. See
356-
// https://github.com/carbon-language/carbon-lang/pull/5850/files#r2249030529.
357-
arg_id = AddInst(context, loc_id,
358-
SemIR::AddrOf{.type_id = thunk_param_type_id,
359-
.lvalue_id = temporary_storage_inst_id});
353+
{.type_id = thunk_param_type_id, .lvalue_id = arg_id});
360354
}
361355
thunk_arg_ids.push_back(arg_id);
362356
}

0 commit comments

Comments
 (0)