|
|
@@ -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.
|