Skip to content

Commit 2bf0564

Browse files
committed
Switch generic type representation to be pessimistic.
Don't assume that generic types always have copy value representation and don't need a return slot. Instead, make a conservative assumption about the value representation, and assume a return slot might be needed. When lowering, if a return slot was not actually needed in a specific, don't use one.
1 parent d60900c commit 2bf0564

File tree

13 files changed

+340
-17
lines changed

13 files changed

+340
-17
lines changed

core/prelude/copy.carbon

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
package Core library "prelude/copy";
66

7+
import library "prelude/destroy";
8+
79
// Copying an object, which is a conversion from a value representation to an
810
// initializing representation.
911
interface Copy {
@@ -48,6 +50,7 @@ impl () as Copy {
4850
fn Op[self: Self]() -> Self = "no_op";
4951
}
5052

53+
// TODO: This uses `T as Destroy`, but should not.
5154
impl forall [T:! Copy] (T,) as Copy {
5255
fn Op[self: Self]() -> Self {
5356
return (self.0.Op(),);

toolchain/check/testdata/builtins/int/make_type_signed.carbon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ library "[[@TEST_NAME]]";
2323

2424
import library "types";
2525

26-
fn Copy[T:! type](x: T) -> T = "primitive_copy";
26+
fn Copy[N:! IntLiteral()](x: Int(N)) -> Int(N) = "primitive_copy";
2727

2828
fn F(n: Int(64)) ->
2929
//@dump-sem-ir-begin

toolchain/check/testdata/builtins/int/make_type_unsigned.carbon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ library "[[@TEST_NAME]]";
2323

2424
import library "types";
2525

26-
fn Copy[T:! type](x: T) -> T = "primitive_copy";
26+
fn Copy[N:! IntLiteral()](x: UInt(N)) -> UInt(N) = "primitive_copy";
2727

2828
fn F(n: UInt(64)) ->
2929
//@dump-sem-ir-begin

toolchain/check/type_completion.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ class TypeCompleter {
6666
// state, such as empty structs and tuples.
6767
auto MakeEmptyValueRepr() const -> SemIR::ValueRepr;
6868

69+
// Makes a dependent value representation, which is used for symbolic types.
70+
auto MakeDependentValueRepr(SemIR::TypeId type_id) const -> SemIR::ValueRepr;
71+
6972
// Makes a value representation that uses pass-by-copy, copying the given
7073
// type.
7174
auto MakeCopyValueRepr(SemIR::TypeId rep_id,
@@ -165,8 +168,10 @@ class TypeCompleter {
165168
requires(InstT::Kind.is_symbolic_when_type())
166169
auto BuildInfoForInst(SemIR::TypeId type_id, InstT /*inst*/) const
167170
-> SemIR::CompleteTypeInfo {
168-
// For symbolic types, we arbitrarily pick a copy representation.
169-
return {.value_repr = MakeCopyValueRepr(type_id)};
171+
// TODO: We're implicitly assuming here that dependent types are not
172+
// abstract. How do we enforce that dependent types are non-abstract when
173+
// they need to be?
174+
return {.value_repr = MakeDependentValueRepr(type_id)};
170175
}
171176

172177
// Builds and returns the `CompleteTypeInfo` for the given type. All nested
@@ -363,6 +368,11 @@ auto TypeCompleter::MakeEmptyValueRepr() const -> SemIR::ValueRepr {
363368
.type_id = GetTupleType(*context_, {})};
364369
}
365370

371+
auto TypeCompleter::MakeDependentValueRepr(SemIR::TypeId type_id) const
372+
-> SemIR::ValueRepr {
373+
return {.kind = SemIR::ValueRepr::Dependent, .type_id = type_id};
374+
}
375+
366376
auto TypeCompleter::MakeCopyValueRepr(
367377
SemIR::TypeId rep_id, SemIR::ValueRepr::AggregateKind aggregate_kind) const
368378
-> SemIR::ValueRepr {

toolchain/lower/file_context.cpp

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,8 @@ auto FileContext::BuildFunctionTypeInfo(const SemIR::Function& function,
357357
return {.type = llvm::FunctionType::get(
358358
llvm::Type::getVoidTy(llvm_context()),
359359
/*isVarArg=*/false)};
360+
case SemIR::ValueRepr::Dependent:
361+
CARBON_FATAL("Lowering function with dependent parameter type");
360362
case SemIR::ValueRepr::None:
361363
break;
362364
case SemIR::ValueRepr::Copy:
@@ -630,25 +632,29 @@ auto FileContext::BuildFunctionBody(SemIR::FunctionId function_id,
630632
function_lowering.SetLocal(param_id, param_value);
631633
};
632634

633-
// The subset of call_param_ids that is already in the order that the LLVM
634-
// calling convention expects.
635-
llvm::ArrayRef<SemIR::InstId> sequential_param_ids;
635+
// Lower the return slot parameter.
636636
if (declaration_function.return_slot_pattern_id.has_value()) {
637+
auto call_param_id = call_param_ids.consume_back();
637638
// The LLVM calling convention has the return slot first rather than last.
638639
// Note that this queries whether there is a return slot at the LLVM level,
639640
// whereas `function.return_slot_pattern_id.has_value()` queries whether
640641
// there is a return slot at the SemIR level.
641642
if (SemIR::ReturnTypeInfo::ForFunction(sem_ir(), declaration_function,
642643
specific_id)
643644
.has_return_slot()) {
644-
lower_param(call_param_ids.back());
645+
lower_param(call_param_id);
646+
} else {
647+
// The return slot might still be mentioned as a destination location, but
648+
// shouldn't actually be used for anything, so we can use a poison value
649+
// for it.
650+
function_lowering.SetLocal(call_param_id,
651+
llvm::PoisonValue::get(llvm::PointerType::get(
652+
llvm_context(), /*AddressSpace=*/0)));
645653
}
646-
sequential_param_ids = call_param_ids.drop_back();
647-
} else {
648-
sequential_param_ids = call_param_ids;
649654
}
650655

651-
for (auto param_id : sequential_param_ids) {
656+
// Lower the remaining call parameters.
657+
for (auto param_id : call_param_ids) {
652658
lower_param(param_id);
653659
}
654660

toolchain/lower/function_context.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,8 @@ auto FunctionContext::CopyValue(TypeInFile type, SemIR::InstId source_id,
368368
switch (GetValueRepr(type).repr.kind) {
369369
case SemIR::ValueRepr::Unknown:
370370
CARBON_FATAL("Attempt to copy incomplete type");
371+
case SemIR::ValueRepr::Dependent:
372+
CARBON_FATAL("Attempt to copy dependent type");
371373
case SemIR::ValueRepr::None:
372374
break;
373375
case SemIR::ValueRepr::Copy:

toolchain/lower/handle.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -232,10 +232,7 @@ auto HandleInst(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/,
232232

233233
auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
234234
SemIR::ReturnSlot inst) -> void {
235-
if (context.GetInitRepr(context.GetTypeIdOfInst(inst_id)).kind ==
236-
SemIR::InitRepr::InPlace) {
237-
context.SetLocal(inst_id, context.GetValue(inst.storage_id));
238-
}
235+
context.SetLocal(inst_id, context.GetValue(inst.storage_id));
239236
}
240237

241238
auto HandleInst(FunctionContext& context, SemIR::InstId /*inst_id*/,

toolchain/lower/handle_aggregates.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ static auto GetAggregateElement(FunctionContext& context,
7979
switch (value_repr.repr.kind) {
8080
case SemIR::ValueRepr::Unknown:
8181
CARBON_FATAL("Lowering access to incomplete aggregate type");
82+
case SemIR::ValueRepr::Dependent:
83+
CARBON_FATAL("Lowering access to dependent aggregate type");
8284
case SemIR::ValueRepr::None:
8385
return aggr_value;
8486
case SemIR::ValueRepr::Copy:
@@ -241,7 +243,10 @@ static auto EmitAggregateValueRepr(FunctionContext& context,
241243
auto value_type = value_repr.type();
242244
switch (value_repr.repr.kind) {
243245
case SemIR::ValueRepr::Unknown:
244-
CARBON_FATAL("Incomplete aggregate type in lowering");
246+
CARBON_FATAL("Lowering value of incomplete aggregate type");
247+
248+
case SemIR::ValueRepr::Dependent:
249+
CARBON_FATAL("Lowering value of dependent aggregate type");
245250

246251
case SemIR::ValueRepr::None:
247252
// TODO: Add a helper to get a "no value representation" value.

toolchain/lower/handle_call.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,8 +517,16 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
517517
std::vector<llvm::Value*> args;
518518

519519
auto inst_type = context.GetTypeIdOfInst(inst_id);
520+
bool call_has_return_slot =
521+
SemIR::ReturnTypeInfo::ForType(context.sem_ir(), inst.type_id)
522+
.has_return_slot();
520523
if (context.GetReturnTypeInfo(inst_type).info.has_return_slot()) {
524+
CARBON_CHECK(call_has_return_slot);
521525
args.push_back(context.GetValue(arg_ids.consume_back()));
526+
} else if (call_has_return_slot) {
527+
// Call instruction has a return slot but this specific callee does not.
528+
// Just ignore it.
529+
arg_ids.consume_back();
522530
}
523531

524532
for (auto arg_id : arg_ids) {

toolchain/lower/handle_expr_category.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
1515
case SemIR::ValueRepr::Unknown:
1616
CARBON_FATAL(
1717
"Value binding for type with incomplete value representation");
18+
case SemIR::ValueRepr::Dependent:
19+
CARBON_FATAL(
20+
"Value binding for type with dependent value representation");
1821
case SemIR::ValueRepr::None:
1922
// Nothing should use this value, but StubRef needs a value to
2023
// propagate.
@@ -51,6 +54,8 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
5154
switch (context.GetValueRepr(type).repr.kind) {
5255
case SemIR::ValueRepr::Unknown:
5356
CARBON_FATAL("Unexpected incomplete type");
57+
case SemIR::ValueRepr::Dependent:
58+
CARBON_FATAL("Unexpected dependent type");
5459
case SemIR::ValueRepr::None:
5560
case SemIR::ValueRepr::Pointer:
5661
break;

0 commit comments

Comments
 (0)