mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
Support initializing a class from a struct. (#3358)
Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
This commit is contained in:
co-authored by
Jon Ross-Perkins
parent
737162cc8f
commit
545a5b3679
+93
-22
@@ -29,6 +29,7 @@ static auto FindReturnSlotForInitializer(SemIR::File& sem_ir,
|
||||
default:
|
||||
CARBON_FATAL() << "Initialization from unexpected inst " << init;
|
||||
|
||||
case SemIR::ClassInit::Kind:
|
||||
case SemIR::StructInit::Kind:
|
||||
case SemIR::TupleInit::Kind:
|
||||
// TODO: Track a return slot for these initializers.
|
||||
@@ -228,8 +229,9 @@ class CopyOnWriteBlock {
|
||||
};
|
||||
} // namespace
|
||||
|
||||
// Performs a conversion from a tuple to an array type. Does not perform a
|
||||
// final conversion to the requested expression category.
|
||||
// Performs a conversion from a tuple to an array type. This function only
|
||||
// converts the type, and does not perform a final conversion to the requested
|
||||
// expression category.
|
||||
static auto ConvertTupleToArray(Context& context, SemIR::TupleType tuple_type,
|
||||
SemIR::ArrayType array_type,
|
||||
SemIR::InstId value_id, ConversionTarget target)
|
||||
@@ -310,8 +312,9 @@ static auto ConvertTupleToArray(Context& context, SemIR::TupleType tuple_type,
|
||||
sem_ir.inst_blocks().Add(inits)});
|
||||
}
|
||||
|
||||
// Performs a conversion from a tuple to a tuple type. Does not perform a
|
||||
// final conversion to the requested expression category.
|
||||
// Performs a conversion from a tuple to a tuple type. This function only
|
||||
// converts the type, and does not perform a final conversion to the requested
|
||||
// expression category.
|
||||
static auto ConvertTupleToTuple(Context& context, SemIR::TupleType src_type,
|
||||
SemIR::TupleType dest_type,
|
||||
SemIR::InstId value_id, ConversionTarget target)
|
||||
@@ -383,12 +386,14 @@ static auto ConvertTupleToTuple(Context& context, SemIR::TupleType src_type,
|
||||
new_block.id()});
|
||||
}
|
||||
|
||||
// Performs a conversion from a struct to a struct type. Does not perform a
|
||||
// final conversion to the requested expression category.
|
||||
static auto ConvertStructToStruct(Context& context, SemIR::StructType src_type,
|
||||
SemIR::StructType dest_type,
|
||||
SemIR::InstId value_id,
|
||||
ConversionTarget target) -> SemIR::InstId {
|
||||
// Common implementation for ConvertStructToStruct and ConvertStructToClass.
|
||||
template <typename TargetAccessInstT>
|
||||
static auto ConvertStructToStructOrClass(Context& context,
|
||||
SemIR::StructType src_type,
|
||||
SemIR::StructType dest_type,
|
||||
SemIR::InstId value_id,
|
||||
ConversionTarget target, bool is_class)
|
||||
-> SemIR::InstId {
|
||||
auto& sem_ir = context.sem_ir();
|
||||
auto src_elem_fields = sem_ir.inst_blocks().Get(src_type.fields_id);
|
||||
auto dest_elem_fields = sem_ir.inst_blocks().Get(dest_type.fields_id);
|
||||
@@ -412,11 +417,13 @@ static auto ConvertStructToStruct(Context& context, SemIR::StructType src_type,
|
||||
// exist in the destination or vice versa in the diagnostic.
|
||||
if (src_elem_fields.size() != dest_elem_fields.size()) {
|
||||
CARBON_DIAGNOSTIC(StructInitElementCountMismatch, Error,
|
||||
"Cannot initialize struct of {0} element(s) from struct "
|
||||
"with {1} element(s).",
|
||||
size_t, size_t);
|
||||
context.emitter().Emit(value.parse_node(), StructInitElementCountMismatch,
|
||||
dest_elem_fields.size(), src_elem_fields.size());
|
||||
"Cannot initialize {0} with {1} field(s) from struct "
|
||||
"with {2} field(s).",
|
||||
llvm::StringLiteral, size_t, size_t);
|
||||
context.emitter().Emit(
|
||||
value.parse_node(), StructInitElementCountMismatch,
|
||||
is_class ? llvm::StringLiteral("class") : llvm::StringLiteral("struct"),
|
||||
dest_elem_fields.size(), src_elem_fields.size());
|
||||
return SemIR::InstId::BuiltinError;
|
||||
}
|
||||
|
||||
@@ -484,7 +491,7 @@ static auto ConvertStructToStruct(Context& context, SemIR::StructType src_type,
|
||||
// TODO: This call recurses back into conversion. Switch to an iterative
|
||||
// approach.
|
||||
auto init_id =
|
||||
ConvertAggregateElement<SemIR::StructAccess, SemIR::StructAccess>(
|
||||
ConvertAggregateElement<SemIR::StructAccess, TargetAccessInstT>(
|
||||
context, value.parse_node(), value_id, src_field.field_type_id,
|
||||
literal_elems, inner_kind, target.init_id, dest_field.field_type_id,
|
||||
target.init_block, src_field_index);
|
||||
@@ -494,12 +501,63 @@ static auto ConvertStructToStruct(Context& context, SemIR::StructType src_type,
|
||||
new_block.Set(i, init_id);
|
||||
}
|
||||
|
||||
return is_init ? context.AddInst(SemIR::StructInit{value.parse_node(),
|
||||
target.type_id, value_id,
|
||||
new_block.id()})
|
||||
: context.AddInst(SemIR::StructValue{value.parse_node(),
|
||||
target.type_id, value_id,
|
||||
new_block.id()});
|
||||
if (is_class) {
|
||||
CARBON_CHECK(is_init)
|
||||
<< "Converting directly to a class value is not supported";
|
||||
return context.AddInst(SemIR::ClassInit{value.parse_node(), target.type_id,
|
||||
value_id, new_block.id()});
|
||||
} else if (is_init) {
|
||||
return context.AddInst(SemIR::StructInit{value.parse_node(), target.type_id,
|
||||
value_id, new_block.id()});
|
||||
} else {
|
||||
return context.AddInst(SemIR::StructValue{
|
||||
value.parse_node(), target.type_id, value_id, new_block.id()});
|
||||
}
|
||||
}
|
||||
|
||||
// Performs a conversion from a struct to a struct type. This function only
|
||||
// converts the type, and does not perform a final conversion to the requested
|
||||
// expression category.
|
||||
static auto ConvertStructToStruct(Context& context, SemIR::StructType src_type,
|
||||
SemIR::StructType dest_type,
|
||||
SemIR::InstId value_id,
|
||||
ConversionTarget target) -> SemIR::InstId {
|
||||
return ConvertStructToStructOrClass<SemIR::StructAccess>(
|
||||
context, src_type, dest_type, value_id, target, /*is_class=*/false);
|
||||
}
|
||||
|
||||
// Performs a conversion from a struct to a class type. This function only
|
||||
// converts the type, and does not perform a final conversion to the requested
|
||||
// expression category.
|
||||
static auto ConvertStructToClass(Context& context, SemIR::StructType src_type,
|
||||
SemIR::ClassType dest_type,
|
||||
SemIR::InstId value_id,
|
||||
ConversionTarget target) -> SemIR::InstId {
|
||||
PendingBlock target_block(context);
|
||||
auto dest_struct_type = context.insts().GetAs<SemIR::StructType>(
|
||||
context.sem_ir().GetTypeAllowBuiltinTypes(
|
||||
context.classes().Get(dest_type.class_id).object_representation_id));
|
||||
|
||||
// If we're trying to create a class value, form a temporary for the value to
|
||||
// point to.
|
||||
bool need_temporary = !target.is_initializer();
|
||||
if (need_temporary) {
|
||||
target.kind = ConversionTarget::Initializer;
|
||||
target.init_block = &target_block;
|
||||
target.init_id = target_block.AddInst(SemIR::TemporaryStorage{
|
||||
context.insts().Get(value_id).parse_node(), target.type_id});
|
||||
}
|
||||
|
||||
auto result_id = ConvertStructToStructOrClass<SemIR::ClassFieldAccess>(
|
||||
context, src_type, dest_struct_type, value_id, target, /*is_class=*/true);
|
||||
|
||||
if (need_temporary) {
|
||||
target_block.InsertHere();
|
||||
result_id = context.AddInst(
|
||||
SemIR::Temporary{context.insts().Get(value_id).parse_node(),
|
||||
target.type_id, target.init_id, result_id});
|
||||
}
|
||||
return result_id;
|
||||
}
|
||||
|
||||
// Returns whether `category` is a valid expression category to produce as a
|
||||
@@ -622,6 +680,19 @@ static auto PerformBuiltinConversion(Context& context, Parse::Node parse_node,
|
||||
}
|
||||
}
|
||||
|
||||
// A struct {.f_1: T_1, .f_2: T_2, ..., .f_n: T_n} converts to a class type
|
||||
// if it converts to the struct type that is the class's representation type
|
||||
// (a struct with the same fields as the class, plus a base field where
|
||||
// relevant).
|
||||
if (auto target_class_type = target_type_inst.TryAs<SemIR::ClassType>()) {
|
||||
auto value_type_inst =
|
||||
sem_ir.insts().Get(sem_ir.GetTypeAllowBuiltinTypes(value_type_id));
|
||||
if (auto src_struct_type = value_type_inst.TryAs<SemIR::StructType>()) {
|
||||
return ConvertStructToClass(context, *src_struct_type, *target_class_type,
|
||||
value_id, target);
|
||||
}
|
||||
}
|
||||
|
||||
if (target.type_id == SemIR::TypeId::TypeType) {
|
||||
// A tuple of types converts to type `type`.
|
||||
// TODO: This should apply even for non-literal tuples.
|
||||
|
||||
Reference in New Issue
Block a user