Skip to content

Commit b44ba47

Browse files
authored
Don't treat dependent types as having a copy value representation. (#6055)
Add `Dependent` value and initializing representations for types whose representations are unknown because they are dependent. When generating SemIR in such cases, use a worst-case initializing representation that both provides a destination address and also propagates a potential result value. Use this to fix incorrect lowering and lowering crashes for specific functions involving generic types that don't use a copy value representation. In lowering, be careful to distinguish between whether the initializing representation for the generic return type uses a return slot (which affects whether the SemIR declaration and call have one) and whether the initializing representation for the specific return type uses a return slot (which affects whether the LLVM IR declaration and call have one).
1 parent ca40e9d commit b44ba47

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+1376
-671
lines changed

toolchain/check/call.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ static auto PerformCallToFunction(Context& context, SemIR::LocId loc_id,
229229
SemIR::InstId return_slot_arg_id = SemIR::InstId::None;
230230
switch (return_info.init_repr.kind) {
231231
case SemIR::InitRepr::InPlace:
232+
case SemIR::InitRepr::Dependent:
232233
// Tentatively put storage for a temporary in the function's return slot.
233234
// This will be replaced if necessary when we perform initialization.
234235
return_slot_arg_id = AddInst<SemIR::TemporaryStorage>(

toolchain/check/convert.cpp

Lines changed: 54 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ namespace Carbon::Check {
4242

4343
// Marks the initializer `init_id` as initializing `target_id`.
4444
static auto MarkInitializerFor(SemIR::File& sem_ir, SemIR::InstId init_id,
45-
SemIR::InstId target_id,
46-
PendingBlock& target_block) -> void {
45+
ConversionTarget& target) -> void {
46+
CARBON_CHECK(target.is_initializer());
4747
auto return_slot_arg_id = FindReturnSlotArgForInitializer(sem_ir, init_id);
4848
if (return_slot_arg_id.has_value()) {
4949
// Replace the temporary in the return slot with a reference to our target.
@@ -52,7 +52,8 @@ static auto MarkInitializerFor(SemIR::File& sem_ir, SemIR::InstId init_id,
5252
"Return slot for initializer does not contain a temporary; "
5353
"initialized multiple times? Have {0}",
5454
sem_ir.insts().Get(return_slot_arg_id));
55-
target_block.MergeReplacing(return_slot_arg_id, target_id);
55+
target.init_id =
56+
target.init_block->MergeReplacing(return_slot_arg_id, target.init_id);
5657
}
5758
}
5859

@@ -146,6 +147,29 @@ static auto MakeElementAccessInst(Context& context, SemIR::LocId loc_id,
146147
}
147148
}
148149

150+
// Get the conversion target kind to use when initializing an element of an
151+
// aggregate.
152+
static auto GetAggregateElementConversionTargetKind(SemIR::File& sem_ir,
153+
ConversionTarget target)
154+
-> ConversionTarget::Kind {
155+
// If we're forming an initializer, then we want an initializer for each
156+
// element.
157+
if (target.is_initializer()) {
158+
// Perform a final destination store if we're performing an in-place
159+
// initialization.
160+
auto init_repr = SemIR::InitRepr::ForType(sem_ir, target.type_id);
161+
CARBON_CHECK(init_repr.kind != SemIR::InitRepr::Dependent,
162+
"Aggregate should not have dependent init kind");
163+
if (init_repr.kind == SemIR::InitRepr::InPlace) {
164+
return ConversionTarget::FullInitializer;
165+
}
166+
return ConversionTarget::Initializer;
167+
}
168+
169+
// Otherwise, we want a value representation for each element.
170+
return ConversionTarget::Value;
171+
}
172+
149173
// Converts an element of one aggregate so that it can be used as an element of
150174
// another aggregate.
151175
//
@@ -336,17 +360,8 @@ static auto ConvertTupleToTuple(Context& context, SemIR::TupleType src_type,
336360
return SemIR::ErrorInst::InstId;
337361
}
338362

339-
// If we're forming an initializer, then we want an initializer for each
340-
// element. Otherwise, we want a value representation for each element.
341-
// Perform a final destination store if we're performing an in-place
342-
// initialization.
343-
bool is_init = target.is_initializer();
344363
ConversionTarget::Kind inner_kind =
345-
!is_init ? ConversionTarget::Value
346-
: SemIR::InitRepr::ForType(sem_ir, target.type_id).kind ==
347-
SemIR::InitRepr::InPlace
348-
? ConversionTarget::FullInitializer
349-
: ConversionTarget::Initializer;
364+
GetAggregateElementConversionTargetKind(sem_ir, target);
350365

351366
// Initialize each element of the destination from the corresponding element
352367
// of the source.
@@ -373,7 +388,7 @@ static auto ConvertTupleToTuple(Context& context, SemIR::TupleType src_type,
373388
new_block.Set(i, init_id);
374389
}
375390

376-
if (is_init) {
391+
if (target.is_initializer()) {
377392
target.init_block->InsertHere();
378393
return AddInst<SemIR::TupleInit>(context, value_loc_id,
379394
{.type_id = target.type_id,
@@ -447,17 +462,8 @@ static auto ConvertStructToStructOrClass(
447462
}
448463
}
449464

450-
// If we're forming an initializer, then we want an initializer for each
451-
// element. Otherwise, we want a value representation for each element.
452-
// Perform a final destination store if we're performing an in-place
453-
// initialization.
454-
bool is_init = target.is_initializer();
455465
ConversionTarget::Kind inner_kind =
456-
!is_init ? ConversionTarget::Value
457-
: SemIR::InitRepr::ForType(sem_ir, target.type_id).kind ==
458-
SemIR::InitRepr::InPlace
459-
? ConversionTarget::FullInitializer
460-
: ConversionTarget::Initializer;
466+
GetAggregateElementConversionTargetKind(sem_ir, target);
461467

462468
// Initialize each element of the destination from the corresponding element
463469
// of the source.
@@ -545,6 +551,7 @@ static auto ConvertStructToStructOrClass(
545551
new_block.Set(i, init_id);
546552
}
547553

554+
bool is_init = target.is_initializer();
548555
if (ToClass) {
549556
target.init_block->InsertHere();
550557
CARBON_CHECK(is_init,
@@ -1282,30 +1289,33 @@ static auto IsCppEnum(Context& context, SemIR::TypeId type_id) -> bool {
12821289
}
12831290

12841291
// Given a value expression, form a corresponding initializer that copies from
1285-
// that value, if it is possible to do so.
1286-
static auto PerformCopy(Context& context, SemIR::InstId expr_id, bool diagnose)
1287-
-> SemIR::InstId {
1292+
// that value to the specified target, if it is possible to do so.
1293+
static auto PerformCopy(Context& context, SemIR::InstId expr_id,
1294+
ConversionTarget& target) -> SemIR::InstId {
12881295
// TODO: We don't have a mechanism yet to generate `Copy` impls for each enum
12891296
// type imported from C++. For now we fake it by providing a direct copy.
1290-
if (IsCppEnum(context, context.insts().Get(expr_id).type_id())) {
1291-
return CopyValueToTemporary(context, expr_id);
1297+
auto type_id = context.insts().Get(expr_id).type_id();
1298+
if (IsCppEnum(context, type_id)) {
1299+
return expr_id;
12921300
}
12931301

1294-
return BuildUnaryOperator(
1302+
auto copy_id = BuildUnaryOperator(
12951303
context, SemIR::LocId(expr_id), {"Copy"}, expr_id, [&] {
1296-
if (!diagnose) {
1304+
if (!target.diagnose) {
12971305
return context.emitter().BuildSuppressed();
12981306
}
12991307
CARBON_DIAGNOSTIC(CopyOfUncopyableType, Error,
13001308
"cannot copy value of type {0}", TypeOfInstId);
13011309
return context.emitter().Build(expr_id, CopyOfUncopyableType, expr_id);
13021310
});
1311+
MarkInitializerFor(context.sem_ir(), copy_id, target);
1312+
return copy_id;
13031313
}
13041314

13051315
// Convert a value expression so that it can be used to initialize a C++ thunk
13061316
// parameter.
1307-
static auto ConvertValueForCppThunkRef(Context& context, SemIR::InstId expr_id,
1308-
bool diagnose) -> SemIR::InstId {
1317+
static auto ConvertValueForCppThunkRef(Context& context, SemIR::InstId expr_id)
1318+
-> SemIR::InstId {
13091319
auto expr = context.insts().Get(expr_id);
13101320

13111321
// If the expression has a pointer value representation, extract that and use
@@ -1319,8 +1329,13 @@ static auto ConvertValueForCppThunkRef(Context& context, SemIR::InstId expr_id,
13191329

13201330
// Otherwise, we need a temporary to pass as the thunk argument. Create a copy
13211331
// and initialize a temporary from it.
1322-
expr_id = PerformCopy(context, expr_id, diagnose);
1323-
return MaterializeIfInitializing(context, expr_id);
1332+
auto temporary_id = AddInst<SemIR::TemporaryStorage>(
1333+
context, SemIR::LocId(expr_id), {.type_id = expr.type_id()});
1334+
expr_id = Initialize(context, SemIR::LocId(expr_id), temporary_id, expr_id);
1335+
return AddInstWithCleanup<SemIR::Temporary>(context, SemIR::LocId(expr_id),
1336+
{.type_id = expr.type_id(),
1337+
.storage_id = temporary_id,
1338+
.init_id = expr_id});
13241339
}
13251340

13261341
// Returns the Core interface name to use for a given kind of conversion.
@@ -1524,8 +1539,7 @@ auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
15241539
// a conversion. In that case, we will have created it with the
15251540
// target already set.
15261541
// TODO: Find a better way to track whether we need to do this.
1527-
MarkInitializerFor(sem_ir, expr_id, target.init_id,
1528-
*target.init_block);
1542+
MarkInitializerFor(sem_ir, expr_id, target);
15291543
}
15301544
break;
15311545
}
@@ -1577,13 +1591,13 @@ auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
15771591

15781592
// When initializing from a value, perform a copy.
15791593
if (target.is_initializer()) {
1580-
expr_id = PerformCopy(context, expr_id, target.diagnose);
1594+
expr_id = PerformCopy(context, expr_id, target);
15811595
}
15821596

15831597
// When initializing a C++ thunk parameter, form a reference, creating a
15841598
// temporary if needed.
15851599
if (target.kind == ConversionTarget::CppThunkRef) {
1586-
expr_id = ConvertValueForCppThunkRef(context, expr_id, target.diagnose);
1600+
expr_id = ConvertValueForCppThunkRef(context, expr_id);
15871601
}
15881602

15891603
break;
@@ -1592,7 +1606,7 @@ auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
15921606
// Perform a final destination store, if necessary.
15931607
if (target.kind == ConversionTarget::FullInitializer) {
15941608
if (auto init_rep = SemIR::InitRepr::ForType(sem_ir, target.type_id);
1595-
init_rep.kind == SemIR::InitRepr::ByCopy) {
1609+
init_rep.MightBeByCopy()) {
15961610
target.init_block->InsertHere();
15971611
expr_id = AddInst<SemIR::InitializeFrom>(context, loc_id,
15981612
{.type_id = target.type_id,

toolchain/check/pending_block.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,17 @@ class PendingBlock {
6868
}
6969

7070
// Replace the instruction at target_id with the instructions in this block.
71-
// The new value for target_id should be value_id.
72-
auto MergeReplacing(SemIR::InstId target_id, SemIR::InstId value_id) -> void {
71+
// The new value for target_id should be value_id. Returns the InstId that
72+
// should be used to refer to the result from now on.
73+
auto MergeReplacing(SemIR::InstId target_id, SemIR::InstId value_id)
74+
-> SemIR::InstId {
7375
SemIR::LocIdAndInst value = context_->insts().GetWithLocId(value_id);
7476

77+
auto result_id = value_id;
7578
if (insts_.size() == 1 && insts_[0] == value_id) {
7679
// The block is {value_id}. Replace `target_id` with the instruction
7780
// referred to by `value_id`. This is intended to be the common case.
81+
result_id = target_id;
7882
} else {
7983
// Anything else: splice it into the IR, replacing `target_id`. This
8084
// includes empty blocks, which `Add` handles.
@@ -88,6 +92,7 @@ class PendingBlock {
8892

8993
// Prepare to stash more pending instructions.
9094
insts_.clear();
95+
return result_id;
9196
}
9297

9398
private:

0 commit comments

Comments
 (0)