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
@@ -666,6 +666,7 @@ class TypeCompleter {
|
||||
case SemIR::Call::Kind:
|
||||
case SemIR::ClassDeclaration::Kind:
|
||||
case SemIR::ClassFieldAccess::Kind:
|
||||
case SemIR::ClassInit::Kind:
|
||||
case SemIR::Dereference::Kind:
|
||||
case SemIR::Field::Kind:
|
||||
case SemIR::FunctionDeclaration::Kind:
|
||||
|
||||
+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.
|
||||
|
||||
@@ -41,7 +41,7 @@ struct ConversionTarget {
|
||||
// For an initializer, the object being initialized.
|
||||
SemIR::InstId init_id = SemIR::InstId::Invalid;
|
||||
// For an initializer, a block of pending instructions that are needed to
|
||||
// form the value of `target_id`, and that can be discarded if no
|
||||
// form the value of `init_id`, and that can be discarded if no
|
||||
// initialization is needed.
|
||||
PendingBlock* init_block = nullptr;
|
||||
|
||||
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// AUTOUPDATE
|
||||
|
||||
class Class {
|
||||
var a: i32;
|
||||
var b: i32;
|
||||
}
|
||||
|
||||
fn F() {
|
||||
// CHECK:STDERR: fail_init.carbon:[[@LINE+3]]:10: ERROR: Cannot initialize class with 2 field(s) from struct with 1 field(s).
|
||||
// CHECK:STDERR: {.a = 1} as Class;
|
||||
// CHECK:STDERR: ^
|
||||
{.a = 1} as Class;
|
||||
// CHECK:STDERR: fail_init.carbon:[[@LINE+3]]:18: ERROR: Missing value for field `b` in struct initialization.
|
||||
// CHECK:STDERR: {.a = 1, .c = 2} as Class;
|
||||
// CHECK:STDERR: ^
|
||||
{.a = 1, .c = 2} as Class;
|
||||
// CHECK:STDERR: fail_init.carbon:[[@LINE+3]]:26: ERROR: Cannot initialize class with 2 field(s) from struct with 3 field(s).
|
||||
// CHECK:STDERR: {.a = 1, .b = 2, .c = 3} as Class;
|
||||
// CHECK:STDERR: ^
|
||||
{.a = 1, .b = 2, .c = 3} as Class;
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: file "fail_init.carbon" {
|
||||
// CHECK:STDOUT: class_declaration @Class, ()
|
||||
// CHECK:STDOUT: %Class: type = class_type @Class
|
||||
// CHECK:STDOUT: %.loc10: type = struct_type {.a: i32, .b: i32}
|
||||
// CHECK:STDOUT: %F: <function> = fn_decl @F
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @Class {
|
||||
// CHECK:STDOUT: %.loc8_8.1: type = unbound_field_type Class, i32
|
||||
// CHECK:STDOUT: %.loc8_8.2: <unbound field of class Class> = field "a", member0
|
||||
// CHECK:STDOUT: %a: <unbound field of class Class> = bind_name "a", %.loc8_8.2
|
||||
// CHECK:STDOUT: %.loc9_8.1: type = unbound_field_type Class, i32
|
||||
// CHECK:STDOUT: %.loc9_8.2: <unbound field of class Class> = field "b", member1
|
||||
// CHECK:STDOUT: %b: <unbound field of class Class> = bind_name "b", %.loc9_8.2
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .a = %a
|
||||
// CHECK:STDOUT: .b = %b
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %.loc16_9: i32 = int_literal 1
|
||||
// CHECK:STDOUT: %.loc16_10.1: type = struct_type {.a: i32}
|
||||
// CHECK:STDOUT: %.loc16_10.2: {.a: i32} = struct_literal (%.loc16_9)
|
||||
// CHECK:STDOUT: %Class.ref.loc16: type = name_reference "Class", file.%Class
|
||||
// CHECK:STDOUT: %.loc10: type = ptr_type {.a: i32, .b: i32}
|
||||
// CHECK:STDOUT: %.loc16_10.3: ref Class = temporary_storage
|
||||
// CHECK:STDOUT: %.loc16_10.4: ref Class = temporary %.loc16_10.3, <error>
|
||||
// CHECK:STDOUT: %.loc20_9: i32 = int_literal 1
|
||||
// CHECK:STDOUT: %.loc20_17: i32 = int_literal 2
|
||||
// CHECK:STDOUT: %.loc20_18.1: type = struct_type {.a: i32, .c: i32}
|
||||
// CHECK:STDOUT: %.loc20_18.2: {.a: i32, .c: i32} = struct_literal (%.loc20_9, %.loc20_17)
|
||||
// CHECK:STDOUT: %Class.ref.loc20: type = name_reference "Class", file.%Class
|
||||
// CHECK:STDOUT: %.loc20_18.3: ref Class = temporary_storage
|
||||
// CHECK:STDOUT: %.loc20_18.4: ref i32 = class_field_access %.loc20_18.3, member0
|
||||
// CHECK:STDOUT: %.loc20_18.5: init i32 = initialize_from %.loc20_9 to %.loc20_18.4
|
||||
// CHECK:STDOUT: %.loc20_18.6: ref Class = temporary %.loc20_18.3, <error>
|
||||
// CHECK:STDOUT: %.loc24_9: i32 = int_literal 1
|
||||
// CHECK:STDOUT: %.loc24_17: i32 = int_literal 2
|
||||
// CHECK:STDOUT: %.loc24_25: i32 = int_literal 3
|
||||
// CHECK:STDOUT: %.loc24_26.1: type = struct_type {.a: i32, .b: i32, .c: i32}
|
||||
// CHECK:STDOUT: %.loc24_26.2: {.a: i32, .b: i32, .c: i32} = struct_literal (%.loc24_9, %.loc24_17, %.loc24_25)
|
||||
// CHECK:STDOUT: %Class.ref.loc24: type = name_reference "Class", file.%Class
|
||||
// CHECK:STDOUT: %.loc24_26.3: ref Class = temporary_storage
|
||||
// CHECK:STDOUT: %.loc24_26.4: ref Class = temporary %.loc24_26.3, <error>
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
@@ -0,0 +1,73 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// AUTOUPDATE
|
||||
|
||||
class Class {
|
||||
var a: i32;
|
||||
var b: i32;
|
||||
}
|
||||
|
||||
fn G(p: Class*);
|
||||
|
||||
fn F() {
|
||||
// TODO: This case should presumably work: `{...} as Class` should be an
|
||||
// initializing expression, not a value expression.
|
||||
//
|
||||
// CHECK:STDERR: fail_init_as_inplace.carbon:[[@LINE+3]]:33: ERROR: Cannot copy value of type `Class`.
|
||||
// CHECK:STDERR: var c: Class = {.a = 1, .b = 2} as Class;
|
||||
// CHECK:STDERR: ^
|
||||
var c: Class = {.a = 1, .b = 2} as Class;
|
||||
G(&c);
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: file "fail_init_as_inplace.carbon" {
|
||||
// CHECK:STDOUT: class_declaration @Class, ()
|
||||
// CHECK:STDOUT: %Class: type = class_type @Class
|
||||
// CHECK:STDOUT: %.loc10: type = struct_type {.a: i32, .b: i32}
|
||||
// CHECK:STDOUT: %G: <function> = fn_decl @G
|
||||
// CHECK:STDOUT: %F: <function> = fn_decl @F
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @Class {
|
||||
// CHECK:STDOUT: %.loc8_8.1: type = unbound_field_type Class, i32
|
||||
// CHECK:STDOUT: %.loc8_8.2: <unbound field of class Class> = field "a", member0
|
||||
// CHECK:STDOUT: %a: <unbound field of class Class> = bind_name "a", %.loc8_8.2
|
||||
// CHECK:STDOUT: %.loc9_8.1: type = unbound_field_type Class, i32
|
||||
// CHECK:STDOUT: %.loc9_8.2: <unbound field of class Class> = field "b", member1
|
||||
// CHECK:STDOUT: %b: <unbound field of class Class> = bind_name "b", %.loc9_8.2
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .a = %a
|
||||
// CHECK:STDOUT: .b = %b
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @G(%p: Class*);
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %Class.ref.loc21_10: type = name_reference "Class", file.%Class
|
||||
// CHECK:STDOUT: %.loc10: type = ptr_type {.a: i32, .b: i32}
|
||||
// CHECK:STDOUT: %c.var: ref Class = var "c"
|
||||
// CHECK:STDOUT: %c: ref Class = bind_name "c", %c.var
|
||||
// CHECK:STDOUT: %.loc21_24: i32 = int_literal 1
|
||||
// CHECK:STDOUT: %.loc21_32: i32 = int_literal 2
|
||||
// CHECK:STDOUT: %.loc21_33.1: {.a: i32, .b: i32} = struct_literal (%.loc21_24, %.loc21_32)
|
||||
// CHECK:STDOUT: %Class.ref.loc21_38: type = name_reference "Class", file.%Class
|
||||
// CHECK:STDOUT: %.loc21_33.2: ref Class = temporary_storage
|
||||
// CHECK:STDOUT: %.loc21_33.3: ref i32 = class_field_access %.loc21_33.2, member0
|
||||
// CHECK:STDOUT: %.loc21_33.4: init i32 = initialize_from %.loc21_24 to %.loc21_33.3
|
||||
// CHECK:STDOUT: %.loc21_33.5: ref i32 = class_field_access %.loc21_33.2, member1
|
||||
// CHECK:STDOUT: %.loc21_33.6: init i32 = initialize_from %.loc21_32 to %.loc21_33.5
|
||||
// CHECK:STDOUT: %.loc21_33.7: init Class = class_init %.loc21_33.1, (%.loc21_33.4, %.loc21_33.6)
|
||||
// CHECK:STDOUT: %.loc21_33.8: ref Class = temporary %.loc21_33.2, %.loc21_33.7
|
||||
// CHECK:STDOUT: %.loc21_33.9: Class = bind_value %.loc21_33.8
|
||||
// CHECK:STDOUT: assign %c.var, <error>
|
||||
// CHECK:STDOUT: %G.ref: <function> = name_reference "G", file.%G
|
||||
// CHECK:STDOUT: %c.ref: ref Class = name_reference "c", %c
|
||||
// CHECK:STDOUT: %.loc22_5: Class* = address_of %c.ref
|
||||
// CHECK:STDOUT: %.loc22_4.1: type = tuple_type ()
|
||||
// CHECK:STDOUT: %.loc22_4.2: init () = call %G.ref(%.loc22_5)
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// AUTOUPDATE
|
||||
|
||||
class Class {
|
||||
var n: i32;
|
||||
var next: Class*;
|
||||
}
|
||||
|
||||
fn Make(n: i32, next: Class*) -> Class {
|
||||
return {.n = n, .next = next};
|
||||
}
|
||||
|
||||
fn MakeReorder(n: i32, next: Class*) -> Class {
|
||||
return {.next = next, .n = n};
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: file "init.carbon" {
|
||||
// CHECK:STDOUT: class_declaration @Class, ()
|
||||
// CHECK:STDOUT: %Class: type = class_type @Class
|
||||
// CHECK:STDOUT: %.loc10_1.1: type = struct_type {.n: i32, .next: Class*}
|
||||
// CHECK:STDOUT: %.loc10_1.2: type = ptr_type {.n: i32, .next: Class*}
|
||||
// CHECK:STDOUT: %Make: <function> = fn_decl @Make
|
||||
// CHECK:STDOUT: %MakeReorder: <function> = fn_decl @MakeReorder
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @Class {
|
||||
// CHECK:STDOUT: %.loc8_8.1: type = unbound_field_type Class, i32
|
||||
// CHECK:STDOUT: %.loc8_8.2: <unbound field of class Class> = field "n", member0
|
||||
// CHECK:STDOUT: %n: <unbound field of class Class> = bind_name "n", %.loc8_8.2
|
||||
// CHECK:STDOUT: %Class.ref: type = name_reference "Class", file.%Class
|
||||
// CHECK:STDOUT: %.loc9_18: type = ptr_type Class
|
||||
// CHECK:STDOUT: %.loc9_11.1: type = unbound_field_type Class, Class*
|
||||
// CHECK:STDOUT: %.loc9_11.2: <unbound field of class Class> = field "next", member1
|
||||
// CHECK:STDOUT: %next: <unbound field of class Class> = bind_name "next", %.loc9_11.2
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .n = %n
|
||||
// CHECK:STDOUT: .next = %next
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @Make(%n: i32, %next: Class*) -> %return: Class {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %n.ref: i32 = name_reference "n", %n
|
||||
// CHECK:STDOUT: %next.ref: Class* = name_reference "next", %next
|
||||
// CHECK:STDOUT: %.loc13_31.1: {.n: i32, .next: Class*} = struct_literal (%n.ref, %next.ref)
|
||||
// CHECK:STDOUT: %.loc13_31.2: ref i32 = class_field_access %return, member0
|
||||
// CHECK:STDOUT: %.loc13_31.3: init i32 = initialize_from %n.ref to %.loc13_31.2
|
||||
// CHECK:STDOUT: %.loc13_31.4: ref Class* = class_field_access %return, member1
|
||||
// CHECK:STDOUT: %.loc13_31.5: init Class* = initialize_from %next.ref to %.loc13_31.4
|
||||
// CHECK:STDOUT: %.loc13_31.6: init Class = class_init %.loc13_31.1, (%.loc13_31.3, %.loc13_31.5)
|
||||
// CHECK:STDOUT: return %.loc13_31.6
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @MakeReorder(%n: i32, %next: Class*) -> %return: Class {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %next.ref: Class* = name_reference "next", %next
|
||||
// CHECK:STDOUT: %n.ref: i32 = name_reference "n", %n
|
||||
// CHECK:STDOUT: %.loc17_31.1: type = struct_type {.next: Class*, .n: i32}
|
||||
// CHECK:STDOUT: %.loc17_31.2: {.next: Class*, .n: i32} = struct_literal (%next.ref, %n.ref)
|
||||
// CHECK:STDOUT: %.loc17_31.3: ref i32 = class_field_access %return, member1
|
||||
// CHECK:STDOUT: %.loc17_31.4: init i32 = initialize_from %n.ref to %.loc17_31.3
|
||||
// CHECK:STDOUT: %.loc17_31.5: ref Class* = class_field_access %return, member0
|
||||
// CHECK:STDOUT: %.loc17_31.6: init Class* = initialize_from %next.ref to %.loc17_31.5
|
||||
// CHECK:STDOUT: %.loc17_31.7: init Class = class_init %.loc17_31.2, (%.loc17_31.4, %.loc17_31.6)
|
||||
// CHECK:STDOUT: return %.loc17_31.7
|
||||
// CHECK:STDOUT: }
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// AUTOUPDATE
|
||||
|
||||
class Class {
|
||||
var a: i32;
|
||||
var b: i32;
|
||||
}
|
||||
|
||||
fn F() -> i32 {
|
||||
return ({.a = 1, .b = 2} as Class).a;
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: file "init_as.carbon" {
|
||||
// CHECK:STDOUT: class_declaration @Class, ()
|
||||
// CHECK:STDOUT: %Class: type = class_type @Class
|
||||
// CHECK:STDOUT: %.loc10: type = struct_type {.a: i32, .b: i32}
|
||||
// CHECK:STDOUT: %F: <function> = fn_decl @F
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @Class {
|
||||
// CHECK:STDOUT: %.loc8_8.1: type = unbound_field_type Class, i32
|
||||
// CHECK:STDOUT: %.loc8_8.2: <unbound field of class Class> = field "a", member0
|
||||
// CHECK:STDOUT: %a: <unbound field of class Class> = bind_name "a", %.loc8_8.2
|
||||
// CHECK:STDOUT: %.loc9_8.1: type = unbound_field_type Class, i32
|
||||
// CHECK:STDOUT: %.loc9_8.2: <unbound field of class Class> = field "b", member1
|
||||
// CHECK:STDOUT: %b: <unbound field of class Class> = bind_name "b", %.loc9_8.2
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .a = %a
|
||||
// CHECK:STDOUT: .b = %b
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F() -> i32 {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %.loc13_17: i32 = int_literal 1
|
||||
// CHECK:STDOUT: %.loc13_25: i32 = int_literal 2
|
||||
// CHECK:STDOUT: %.loc13_26.1: {.a: i32, .b: i32} = struct_literal (%.loc13_17, %.loc13_25)
|
||||
// CHECK:STDOUT: %Class.ref: type = name_reference "Class", file.%Class
|
||||
// CHECK:STDOUT: %.loc10: type = ptr_type {.a: i32, .b: i32}
|
||||
// CHECK:STDOUT: %.loc13_26.2: ref Class = temporary_storage
|
||||
// CHECK:STDOUT: %.loc13_26.3: ref i32 = class_field_access %.loc13_26.2, member0
|
||||
// CHECK:STDOUT: %.loc13_26.4: init i32 = initialize_from %.loc13_17 to %.loc13_26.3
|
||||
// CHECK:STDOUT: %.loc13_26.5: ref i32 = class_field_access %.loc13_26.2, member1
|
||||
// CHECK:STDOUT: %.loc13_26.6: init i32 = initialize_from %.loc13_25 to %.loc13_26.5
|
||||
// CHECK:STDOUT: %.loc13_26.7: init Class = class_init %.loc13_26.1, (%.loc13_26.4, %.loc13_26.6)
|
||||
// CHECK:STDOUT: %.loc13_26.8: ref Class = temporary %.loc13_26.2, %.loc13_26.7
|
||||
// CHECK:STDOUT: %.loc13_37.1: ref i32 = class_field_access %.loc13_26.8, member0
|
||||
// CHECK:STDOUT: %.loc13_37.2: i32 = bind_value %.loc13_37.1
|
||||
// CHECK:STDOUT: return %.loc13_37.2
|
||||
// CHECK:STDOUT: }
|
||||
@@ -0,0 +1,79 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// AUTOUPDATE
|
||||
|
||||
class Inner {
|
||||
var a: i32;
|
||||
var b: i32;
|
||||
}
|
||||
|
||||
fn MakeInner() -> Inner;
|
||||
|
||||
class Outer {
|
||||
var c: Inner;
|
||||
var d: Inner;
|
||||
}
|
||||
|
||||
fn MakeOuter() -> Outer {
|
||||
return {.c = MakeInner(), .d = MakeInner()};
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: file "init_nested.carbon" {
|
||||
// CHECK:STDOUT: class_declaration @Inner, ()
|
||||
// CHECK:STDOUT: %Inner: type = class_type @Inner
|
||||
// CHECK:STDOUT: %.loc10_1.1: type = struct_type {.a: i32, .b: i32}
|
||||
// CHECK:STDOUT: %.loc10_1.2: type = ptr_type {.a: i32, .b: i32}
|
||||
// CHECK:STDOUT: %MakeInner: <function> = fn_decl @MakeInner
|
||||
// CHECK:STDOUT: class_declaration @Outer, ()
|
||||
// CHECK:STDOUT: %Outer: type = class_type @Outer
|
||||
// CHECK:STDOUT: %.loc17_1.1: type = struct_type {.c: Inner, .d: Inner}
|
||||
// CHECK:STDOUT: %.loc17_1.2: type = struct_type {.c: {.a: i32, .b: i32}*, .d: {.a: i32, .b: i32}*}
|
||||
// CHECK:STDOUT: %.loc17_1.3: type = ptr_type {.c: {.a: i32, .b: i32}*, .d: {.a: i32, .b: i32}*}
|
||||
// CHECK:STDOUT: %.loc14: type = ptr_type {.c: Inner, .d: Inner}
|
||||
// CHECK:STDOUT: %MakeOuter: <function> = fn_decl @MakeOuter
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @Inner {
|
||||
// CHECK:STDOUT: %.loc8_8.1: type = unbound_field_type Inner, i32
|
||||
// CHECK:STDOUT: %.loc8_8.2: <unbound field of class Inner> = field "a", member0
|
||||
// CHECK:STDOUT: %a: <unbound field of class Inner> = bind_name "a", %.loc8_8.2
|
||||
// CHECK:STDOUT: %.loc9_8.1: type = unbound_field_type Inner, i32
|
||||
// CHECK:STDOUT: %.loc9_8.2: <unbound field of class Inner> = field "b", member1
|
||||
// CHECK:STDOUT: %b: <unbound field of class Inner> = bind_name "b", %.loc9_8.2
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .a = %a
|
||||
// CHECK:STDOUT: .b = %b
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @Outer {
|
||||
// CHECK:STDOUT: %Inner.ref.loc15: type = name_reference "Inner", file.%Inner
|
||||
// CHECK:STDOUT: %.loc15_8.1: type = unbound_field_type Outer, Inner
|
||||
// CHECK:STDOUT: %.loc15_8.2: <unbound field of class Outer> = field "c", member0
|
||||
// CHECK:STDOUT: %c: <unbound field of class Outer> = bind_name "c", %.loc15_8.2
|
||||
// CHECK:STDOUT: %Inner.ref.loc16: type = name_reference "Inner", file.%Inner
|
||||
// CHECK:STDOUT: %.loc16_8.1: type = unbound_field_type Outer, Inner
|
||||
// CHECK:STDOUT: %.loc16_8.2: <unbound field of class Outer> = field "d", member1
|
||||
// CHECK:STDOUT: %d: <unbound field of class Outer> = bind_name "d", %.loc16_8.2
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .c = %c
|
||||
// CHECK:STDOUT: .d = %d
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @MakeInner() -> %return: Inner;
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @MakeOuter() -> %return: Outer {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %MakeInner.ref.loc20_16: <function> = name_reference "MakeInner", file.%MakeInner
|
||||
// CHECK:STDOUT: %.loc20_45.1: ref Inner = class_field_access %return, member0
|
||||
// CHECK:STDOUT: %.loc20_25: init Inner = call %MakeInner.ref.loc20_16() to %.loc20_45.1
|
||||
// CHECK:STDOUT: %MakeInner.ref.loc20_34: <function> = name_reference "MakeInner", file.%MakeInner
|
||||
// CHECK:STDOUT: %.loc20_45.2: ref Inner = class_field_access %return, member1
|
||||
// CHECK:STDOUT: %.loc20_43: init Inner = call %MakeInner.ref.loc20_34() to %.loc20_45.2
|
||||
// CHECK:STDOUT: %.loc20_45.3: {.c: Inner, .d: Inner} = struct_literal (%.loc20_25, %.loc20_43)
|
||||
// CHECK:STDOUT: %.loc20_45.4: init Outer = class_init %.loc20_45.3, (%.loc20_25, %.loc20_43)
|
||||
// CHECK:STDOUT: return %.loc20_45.4
|
||||
// CHECK:STDOUT: }
|
||||
@@ -4,7 +4,7 @@
|
||||
//
|
||||
// AUTOUPDATE
|
||||
|
||||
// CHECK:STDERR: fail_assign_empty.carbon:[[@LINE+3]]:21: ERROR: Cannot initialize struct of 1 element(s) from struct with 0 element(s).
|
||||
// CHECK:STDERR: fail_assign_empty.carbon:[[@LINE+3]]:21: ERROR: Cannot initialize struct with 1 field(s) from struct with 0 field(s).
|
||||
// CHECK:STDERR: var x: {.a: i32} = {};
|
||||
// CHECK:STDERR: ^
|
||||
var x: {.a: i32} = {};
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//
|
||||
// AUTOUPDATE
|
||||
|
||||
// CHECK:STDERR: fail_assign_to_empty.carbon:[[@LINE+3]]:20: ERROR: Cannot initialize struct of 0 element(s) from struct with 1 element(s).
|
||||
// CHECK:STDERR: fail_assign_to_empty.carbon:[[@LINE+3]]:20: ERROR: Cannot initialize struct with 0 field(s) from struct with 1 field(s).
|
||||
// CHECK:STDERR: var x: {} = {.a = 1};
|
||||
// CHECK:STDERR: ^
|
||||
var x: {} = {.a = 1};
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//
|
||||
// AUTOUPDATE
|
||||
|
||||
// CHECK:STDERR: fail_too_few_values.carbon:[[@LINE+3]]:36: ERROR: Cannot initialize struct of 2 element(s) from struct with 1 element(s).
|
||||
// CHECK:STDERR: fail_too_few_values.carbon:[[@LINE+3]]:36: ERROR: Cannot initialize struct with 2 field(s) from struct with 1 field(s).
|
||||
// CHECK:STDERR: var x: {.a: i32, .b: i32} = {.a = 1};
|
||||
// CHECK:STDERR: ^
|
||||
var x: {.a: i32, .b: i32} = {.a = 1};
|
||||
|
||||
+48
-43
@@ -280,6 +280,39 @@ auto HandleClassFieldAccess(FunctionContext& context, SemIR::InstId inst_id,
|
||||
inst.index)));
|
||||
}
|
||||
|
||||
static auto EmitAggregateInitializer(FunctionContext& context,
|
||||
SemIR::TypeId type_id,
|
||||
SemIR::InstBlockId refs_id,
|
||||
llvm::Twine name) -> llvm::Value* {
|
||||
auto* llvm_type = context.GetType(type_id);
|
||||
|
||||
switch (
|
||||
SemIR::GetInitializingRepresentation(context.sem_ir(), type_id).kind) {
|
||||
case SemIR::InitializingRepresentation::None:
|
||||
case SemIR::InitializingRepresentation::InPlace:
|
||||
// TODO: Add a helper to poison a value slot.
|
||||
return llvm::PoisonValue::get(llvm_type);
|
||||
|
||||
case SemIR::InitializingRepresentation::ByCopy: {
|
||||
auto refs = context.sem_ir().inst_blocks().Get(refs_id);
|
||||
CARBON_CHECK(refs.size() == 1)
|
||||
<< "Unexpected size for aggregate with by-copy value representation";
|
||||
// TODO: Remove the LLVM StructType wrapper in this case, so we don't
|
||||
// need this `insert_value` wrapping.
|
||||
return context.builder().CreateInsertValue(
|
||||
llvm::PoisonValue::get(llvm_type), context.GetLocal(refs[0]), {0},
|
||||
name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto HandleClassInit(FunctionContext& context, SemIR::InstId inst_id,
|
||||
SemIR::ClassInit inst) -> void {
|
||||
context.SetLocal(
|
||||
inst_id, EmitAggregateInitializer(context, inst.type_id, inst.elements_id,
|
||||
"class.init"));
|
||||
}
|
||||
|
||||
auto HandleDereference(FunctionContext& context, SemIR::InstId inst_id,
|
||||
SemIR::Dereference inst) -> void {
|
||||
context.SetLocal(inst_id, context.GetLocal(inst.pointer_id));
|
||||
@@ -413,10 +446,10 @@ auto HandleStructLiteral(FunctionContext& /*context*/,
|
||||
|
||||
// Emits the value representation for a struct or tuple whose elements are the
|
||||
// contents of `refs_id`.
|
||||
auto EmitStructOrTupleValueRepresentation(FunctionContext& context,
|
||||
SemIR::TypeId type_id,
|
||||
SemIR::InstBlockId refs_id,
|
||||
llvm::Twine name) -> llvm::Value* {
|
||||
auto EmitAggregateValueRepresentation(FunctionContext& context,
|
||||
SemIR::TypeId type_id,
|
||||
SemIR::InstBlockId refs_id,
|
||||
llvm::Twine name) -> llvm::Value* {
|
||||
auto value_rep = SemIR::GetValueRepresentation(context.sem_ir(), type_id);
|
||||
switch (value_rep.kind) {
|
||||
case SemIR::ValueRepresentation::Unknown:
|
||||
@@ -463,30 +496,16 @@ auto EmitStructOrTupleValueRepresentation(FunctionContext& context,
|
||||
|
||||
auto HandleStructInit(FunctionContext& context, SemIR::InstId inst_id,
|
||||
SemIR::StructInit inst) -> void {
|
||||
auto* llvm_type = context.GetType(inst.type_id);
|
||||
|
||||
switch (SemIR::GetInitializingRepresentation(context.sem_ir(), inst.type_id)
|
||||
.kind) {
|
||||
case SemIR::InitializingRepresentation::None:
|
||||
case SemIR::InitializingRepresentation::InPlace:
|
||||
// TODO: Add a helper to poison a value slot.
|
||||
context.SetLocal(inst_id, llvm::PoisonValue::get(llvm_type));
|
||||
break;
|
||||
|
||||
case SemIR::InitializingRepresentation::ByCopy: {
|
||||
context.SetLocal(
|
||||
inst_id, EmitStructOrTupleValueRepresentation(
|
||||
context, inst.type_id, inst.elements_id, "struct.init"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
context.SetLocal(
|
||||
inst_id, EmitAggregateInitializer(context, inst.type_id, inst.elements_id,
|
||||
"struct.init"));
|
||||
}
|
||||
|
||||
auto HandleStructValue(FunctionContext& context, SemIR::InstId inst_id,
|
||||
SemIR::StructValue inst) -> void {
|
||||
context.SetLocal(inst_id,
|
||||
EmitStructOrTupleValueRepresentation(
|
||||
context, inst.type_id, inst.elements_id, "struct"));
|
||||
context.SetLocal(
|
||||
inst_id, EmitAggregateValueRepresentation(context, inst.type_id,
|
||||
inst.elements_id, "struct"));
|
||||
}
|
||||
|
||||
auto HandleStructTypeField(FunctionContext& /*context*/,
|
||||
@@ -521,29 +540,15 @@ auto HandleTupleLiteral(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/,
|
||||
|
||||
auto HandleTupleInit(FunctionContext& context, SemIR::InstId inst_id,
|
||||
SemIR::TupleInit inst) -> void {
|
||||
auto* llvm_type = context.GetType(inst.type_id);
|
||||
|
||||
switch (SemIR::GetInitializingRepresentation(context.sem_ir(), inst.type_id)
|
||||
.kind) {
|
||||
case SemIR::InitializingRepresentation::None:
|
||||
case SemIR::InitializingRepresentation::InPlace:
|
||||
// TODO: Add a helper to poison a value slot.
|
||||
context.SetLocal(inst_id, llvm::PoisonValue::get(llvm_type));
|
||||
break;
|
||||
|
||||
case SemIR::InitializingRepresentation::ByCopy: {
|
||||
context.SetLocal(
|
||||
inst_id, EmitStructOrTupleValueRepresentation(
|
||||
context, inst.type_id, inst.elements_id, "tuple.init"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
context.SetLocal(
|
||||
inst_id, EmitAggregateInitializer(context, inst.type_id, inst.elements_id,
|
||||
"tuple.init"));
|
||||
}
|
||||
|
||||
auto HandleTupleValue(FunctionContext& context, SemIR::InstId inst_id,
|
||||
SemIR::TupleValue inst) -> void {
|
||||
context.SetLocal(
|
||||
inst_id, EmitStructOrTupleValueRepresentation(context, inst.type_id,
|
||||
context.SetLocal(inst_id,
|
||||
EmitAggregateValueRepresentation(context, inst.type_id,
|
||||
inst.elements_id, "tuple"));
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@ fn Run() -> i32 {
|
||||
// CHECK:STDOUT: %y = alloca { i32 }, align 8
|
||||
// CHECK:STDOUT: %a = getelementptr inbounds { i32 }, ptr %x, i32 0, i32 0
|
||||
// CHECK:STDOUT: %1 = load i32, ptr %a, align 4
|
||||
// CHECK:STDOUT: %2 = insertvalue { i32 } poison, i32 %1, 0
|
||||
// CHECK:STDOUT: store { i32 } %2, ptr %y, align 4
|
||||
// CHECK:STDOUT: %struct.init = insertvalue { i32 } poison, i32 %1, 0
|
||||
// CHECK:STDOUT: store { i32 } %struct.init, ptr %y, align 4
|
||||
// CHECK:STDOUT: ret i32 0
|
||||
// CHECK:STDOUT: }
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@ fn Run() -> i32 {
|
||||
// CHECK:STDOUT: %y = alloca { i32 }, align 8
|
||||
// CHECK:STDOUT: %tuple.elem = getelementptr inbounds { i32 }, ptr %x, i32 0, i32 0
|
||||
// CHECK:STDOUT: %1 = load i32, ptr %tuple.elem, align 4
|
||||
// CHECK:STDOUT: %2 = insertvalue { i32 } poison, i32 %1, 0
|
||||
// CHECK:STDOUT: store { i32 } %2, ptr %y, align 4
|
||||
// CHECK:STDOUT: %tuple.init = insertvalue { i32 } poison, i32 %1, 0
|
||||
// CHECK:STDOUT: store { i32 } %tuple.init, ptr %y, align 4
|
||||
// CHECK:STDOUT: ret i32 0
|
||||
// CHECK:STDOUT: }
|
||||
|
||||
@@ -198,6 +198,7 @@ static auto GetTypePrecedence(InstKind kind) -> int {
|
||||
case Call::Kind:
|
||||
case ClassDeclaration::Kind:
|
||||
case ClassFieldAccess::Kind:
|
||||
case ClassInit::Kind:
|
||||
case Dereference::Kind:
|
||||
case Field::Kind:
|
||||
case FunctionDeclaration::Kind:
|
||||
@@ -399,6 +400,7 @@ auto File::StringifyTypeExpression(InstId outer_inst_id,
|
||||
case Call::Kind:
|
||||
case ClassDeclaration::Kind:
|
||||
case ClassFieldAccess::Kind:
|
||||
case ClassInit::Kind:
|
||||
case CrossReference::Kind:
|
||||
case Dereference::Kind:
|
||||
case Field::Kind:
|
||||
@@ -567,6 +569,7 @@ auto GetExpressionCategory(const File& file, InstId inst_id)
|
||||
case ArrayInit::Kind:
|
||||
case Call::Kind:
|
||||
case InitializeFrom::Kind:
|
||||
case ClassInit::Kind:
|
||||
case StructInit::Kind:
|
||||
case TupleInit::Kind:
|
||||
return ExpressionCategory::Initializing;
|
||||
|
||||
@@ -35,6 +35,7 @@ CARBON_SEM_IR_INST_KIND(Builtin)
|
||||
CARBON_SEM_IR_INST_KIND(Call)
|
||||
CARBON_SEM_IR_INST_KIND(ClassDeclaration)
|
||||
CARBON_SEM_IR_INST_KIND(ClassFieldAccess)
|
||||
CARBON_SEM_IR_INST_KIND(ClassInit)
|
||||
CARBON_SEM_IR_INST_KIND(ClassType)
|
||||
CARBON_SEM_IR_INST_KIND(ConstType)
|
||||
CARBON_SEM_IR_INST_KIND(CrossReference)
|
||||
|
||||
@@ -218,6 +218,15 @@ struct ClassFieldAccess {
|
||||
MemberIndex index;
|
||||
};
|
||||
|
||||
struct ClassInit {
|
||||
static constexpr auto Kind = InstKind::ClassInit.Define("class_init");
|
||||
|
||||
Parse::Node parse_node;
|
||||
TypeId type_id;
|
||||
InstId src_id;
|
||||
InstBlockId elements_id;
|
||||
};
|
||||
|
||||
struct ClassType {
|
||||
static constexpr auto Kind = InstKind::ClassType.Define("class_type");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user