mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 12:59:04 +00:00
Sema: implement struct_decl instruction
This commit is contained in:
parent
aa4e9399db
commit
95b014caea
@ -501,7 +501,9 @@ pub const Struct = struct {
|
||||
layout: std.builtin.TypeInfo.ContainerLayout,
|
||||
status: enum {
|
||||
none,
|
||||
field_types_wip,
|
||||
have_field_types,
|
||||
layout_wip,
|
||||
have_layout,
|
||||
},
|
||||
|
||||
|
||||
158
src/Sema.zig
158
src/Sema.zig
@ -1163,7 +1163,6 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Ind
|
||||
// Maps field index to field_ptr index of where it was already initialized.
|
||||
const found_fields = try gpa.alloc(Zir.Inst.Index, struct_obj.fields.entries.items.len);
|
||||
defer gpa.free(found_fields);
|
||||
|
||||
mem.set(Zir.Inst.Index, found_fields, 0);
|
||||
|
||||
for (instrs) |field_ptr| {
|
||||
@ -1190,11 +1189,12 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Ind
|
||||
|
||||
var root_msg: ?*Module.ErrorMsg = null;
|
||||
|
||||
// TODO handle default struct field values
|
||||
for (found_fields) |field_ptr, i| {
|
||||
if (field_ptr != 0) continue;
|
||||
|
||||
const field_name = struct_obj.fields.entries.items[i].key;
|
||||
const template = "mising struct field: {s}";
|
||||
const template = "missing struct field: {s}";
|
||||
const args = .{field_name};
|
||||
if (root_msg) |msg| {
|
||||
try mod.errNote(&block.base, struct_init_src, msg, template, args);
|
||||
@ -4614,7 +4614,7 @@ fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
|
||||
}
|
||||
|
||||
fn zirTypeof(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
|
||||
const src = inst_data.src();
|
||||
const operand = try sema.resolveInst(inst_data.operand);
|
||||
return sema.mod.constType(sema.arena, src, operand.ty);
|
||||
@ -5052,9 +5052,112 @@ fn zirUnionInitPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inner
|
||||
}
|
||||
|
||||
fn zirStructInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref: bool) InnerError!*Inst {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const mod = sema.mod;
|
||||
const gpa = sema.gpa;
|
||||
const zir_datas = sema.code.instructions.items(.data);
|
||||
const inst_data = zir_datas[inst].pl_node;
|
||||
const extra = sema.code.extraData(Zir.Inst.StructInit, inst_data.payload_index);
|
||||
const src = inst_data.src();
|
||||
return sema.mod.fail(&block.base, src, "TODO: Sema.zirStructInit", .{});
|
||||
|
||||
const first_item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end).data;
|
||||
const first_field_type_data = zir_datas[first_item.field_type].pl_node;
|
||||
const first_field_type_extra = sema.code.extraData(Zir.Inst.FieldType, first_field_type_data.payload_index).data;
|
||||
const unresolved_struct_type = try sema.resolveType(block, src, first_field_type_extra.container_type);
|
||||
const struct_ty = try sema.resolveTypeFields(block, src, unresolved_struct_type);
|
||||
const struct_obj = struct_ty.castTag(.@"struct").?.data;
|
||||
|
||||
// Maps field index to field_type index of where it was already initialized.
|
||||
// For making sure all fields are accounted for and no fields are duplicated.
|
||||
const found_fields = try gpa.alloc(Zir.Inst.Index, struct_obj.fields.entries.items.len);
|
||||
defer gpa.free(found_fields);
|
||||
mem.set(Zir.Inst.Index, found_fields, 0);
|
||||
|
||||
// The init values to use for the struct instance.
|
||||
const field_inits = try gpa.alloc(*ir.Inst, struct_obj.fields.entries.items.len);
|
||||
defer gpa.free(field_inits);
|
||||
|
||||
var field_i: u32 = 0;
|
||||
var extra_index = extra.end;
|
||||
|
||||
while (field_i < extra.data.fields_len) : (field_i += 1) {
|
||||
const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra_index);
|
||||
extra_index = item.end;
|
||||
|
||||
const field_type_data = zir_datas[item.data.field_type].pl_node;
|
||||
const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_type_data.src_node };
|
||||
const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data;
|
||||
const field_name = sema.code.nullTerminatedString(field_type_extra.name_start);
|
||||
const field_index = struct_obj.fields.getIndex(field_name) orelse
|
||||
return sema.failWithBadFieldAccess(block, struct_obj, field_src, field_name);
|
||||
if (found_fields[field_index] != 0) {
|
||||
const other_field_type = found_fields[field_index];
|
||||
const other_field_type_data = zir_datas[other_field_type].pl_node;
|
||||
const other_field_src: LazySrcLoc = .{ .node_offset_back2tok = other_field_type_data.src_node };
|
||||
const msg = msg: {
|
||||
const msg = try mod.errMsg(&block.base, field_src, "duplicate field", .{});
|
||||
errdefer msg.destroy(gpa);
|
||||
try mod.errNote(&block.base, other_field_src, msg, "other field here", .{});
|
||||
break :msg msg;
|
||||
};
|
||||
return mod.failWithOwnedErrorMsg(&block.base, msg);
|
||||
}
|
||||
found_fields[field_index] = item.data.field_type;
|
||||
field_inits[field_index] = try sema.resolveInst(item.data.init);
|
||||
}
|
||||
|
||||
var root_msg: ?*Module.ErrorMsg = null;
|
||||
|
||||
for (found_fields) |field_type_inst, i| {
|
||||
if (field_type_inst != 0) continue;
|
||||
|
||||
// Check if the field has a default init.
|
||||
const field = struct_obj.fields.entries.items[i].value;
|
||||
if (field.default_val.tag() == .unreachable_value) {
|
||||
const field_name = struct_obj.fields.entries.items[i].key;
|
||||
const template = "missing struct field: {s}";
|
||||
const args = .{field_name};
|
||||
if (root_msg) |msg| {
|
||||
try mod.errNote(&block.base, src, msg, template, args);
|
||||
} else {
|
||||
root_msg = try mod.errMsg(&block.base, src, template, args);
|
||||
}
|
||||
} else {
|
||||
field_inits[i] = try mod.constInst(sema.arena, src, .{
|
||||
.ty = field.ty,
|
||||
.val = field.default_val,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (root_msg) |msg| {
|
||||
const fqn = try struct_obj.getFullyQualifiedName(gpa);
|
||||
defer gpa.free(fqn);
|
||||
try mod.errNoteNonLazy(
|
||||
struct_obj.srcLoc(),
|
||||
msg,
|
||||
"struct '{s}' declared here",
|
||||
.{fqn},
|
||||
);
|
||||
return mod.failWithOwnedErrorMsg(&block.base, msg);
|
||||
}
|
||||
|
||||
const is_comptime = for (field_inits) |field_init| {
|
||||
if (field_init.value() == null) {
|
||||
break false;
|
||||
}
|
||||
} else true;
|
||||
|
||||
if (is_comptime) {
|
||||
const values = try sema.arena.alloc(Value, field_inits.len);
|
||||
for (field_inits) |field_init, i| {
|
||||
values[i] = field_init.value().?;
|
||||
}
|
||||
return mod.constInst(sema.arena, src, .{
|
||||
.ty = struct_ty,
|
||||
.val = try Value.Tag.@"struct".create(sema.arena, values.ptr),
|
||||
});
|
||||
}
|
||||
|
||||
return mod.fail(&block.base, src, "TODO: Sema.zirStructInit for runtime-known struct values", .{});
|
||||
}
|
||||
|
||||
fn zirStructInitAnon(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref: bool) InnerError!*Inst {
|
||||
@ -6043,17 +6146,18 @@ fn coerce(
|
||||
if (inst.ty.zigTypeTag() == .EnumLiteral) {
|
||||
const val = try sema.resolveConstValue(block, inst_src, inst);
|
||||
const bytes = val.castTag(.enum_literal).?.data;
|
||||
const field_index = dest_type.enumFieldIndex(bytes) orelse {
|
||||
const resolved_dest_type = try sema.resolveTypeFields(block, inst_src, dest_type);
|
||||
const field_index = resolved_dest_type.enumFieldIndex(bytes) orelse {
|
||||
const msg = msg: {
|
||||
const msg = try mod.errMsg(
|
||||
&block.base,
|
||||
inst_src,
|
||||
"enum '{}' has no field named '{s}'",
|
||||
.{ dest_type, bytes },
|
||||
.{ resolved_dest_type, bytes },
|
||||
);
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
try mod.errNoteNonLazy(
|
||||
dest_type.declSrcLoc(),
|
||||
resolved_dest_type.declSrcLoc(),
|
||||
msg,
|
||||
"enum declared here",
|
||||
.{},
|
||||
@ -6063,7 +6167,7 @@ fn coerce(
|
||||
return mod.failWithOwnedErrorMsg(&block.base, msg);
|
||||
};
|
||||
return mod.constInst(arena, inst_src, .{
|
||||
.ty = dest_type,
|
||||
.ty = resolved_dest_type,
|
||||
.val = try Value.Tag.enum_field_index.create(arena, @intCast(u32, field_index)),
|
||||
});
|
||||
}
|
||||
@ -6698,23 +6802,41 @@ fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type
|
||||
const struct_obj = ty.castTag(.@"struct").?.data;
|
||||
switch (struct_obj.status) {
|
||||
.none => {},
|
||||
.have_field_types, .have_layout => return ty,
|
||||
.field_types_wip => {
|
||||
return sema.mod.fail(&block.base, src, "struct {} depends on itself", .{
|
||||
ty,
|
||||
});
|
||||
},
|
||||
.have_field_types, .have_layout, .layout_wip => return ty,
|
||||
}
|
||||
struct_obj.status = .field_types_wip;
|
||||
try sema.mod.analyzeStructFields(struct_obj);
|
||||
struct_obj.status = .have_field_types;
|
||||
return ty;
|
||||
},
|
||||
.extern_options => {
|
||||
const extern_options_ty = try sema.getBuiltinType(block, src, "ExternOptions");
|
||||
return sema.resolveTypeFields(block, src, extern_options_ty);
|
||||
},
|
||||
.export_options => {
|
||||
const export_options_ty = try sema.getBuiltinType(block, src, "ExportOptions");
|
||||
return sema.resolveTypeFields(block, src, export_options_ty);
|
||||
},
|
||||
.extern_options => return sema.resolveBuiltinTypeFields(block, src, ty, "ExternOptions"),
|
||||
.export_options => return sema.resolveBuiltinTypeFields(block, src, ty, "ExportOptions"),
|
||||
.atomic_ordering => return sema.resolveBuiltinTypeFields(block, src, ty, "AtomicOrdering"),
|
||||
.atomic_rmw_op => return sema.resolveBuiltinTypeFields(block, src, ty, "AtomicRmwOp"),
|
||||
.calling_convention => return sema.resolveBuiltinTypeFields(block, src, ty, "CallingConvention"),
|
||||
.float_mode => return sema.resolveBuiltinTypeFields(block, src, ty, "FloatMode"),
|
||||
.reduce_op => return sema.resolveBuiltinTypeFields(block, src, ty, "ReduceOp"),
|
||||
.call_options => return sema.resolveBuiltinTypeFields(block, src, ty, "CallOptions"),
|
||||
else => return ty,
|
||||
}
|
||||
}
|
||||
|
||||
fn resolveBuiltinTypeFields(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
src: LazySrcLoc,
|
||||
ty: Type,
|
||||
name: []const u8,
|
||||
) InnerError!Type {
|
||||
const resolved_ty = try sema.getBuiltinType(block, src, name);
|
||||
return sema.resolveTypeFields(block, src, resolved_ty);
|
||||
}
|
||||
|
||||
fn getBuiltinType(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
|
||||
@ -118,6 +118,8 @@ pub const Value = extern union {
|
||||
enum_field_index,
|
||||
@"error",
|
||||
error_union,
|
||||
/// An instance of a struct.
|
||||
@"struct",
|
||||
/// This is a special value that tracks a set of types that have been stored
|
||||
/// to an inferred allocation. It does not support any of the normal value queries.
|
||||
inferred_alloc,
|
||||
@ -225,6 +227,7 @@ pub const Value = extern union {
|
||||
.float_128 => Payload.Float_128,
|
||||
.@"error" => Payload.Error,
|
||||
.inferred_alloc => Payload.InferredAlloc,
|
||||
.@"struct" => Payload.Struct,
|
||||
};
|
||||
}
|
||||
|
||||
@ -442,6 +445,7 @@ pub const Value = extern union {
|
||||
};
|
||||
return Value{ .ptr_otherwise = &new_payload.base };
|
||||
},
|
||||
.@"struct" => @panic("TODO can't copy struct value without knowing the type"),
|
||||
|
||||
.inferred_alloc => unreachable,
|
||||
}
|
||||
@ -521,6 +525,9 @@ pub const Value = extern union {
|
||||
.abi_align_default => return out_stream.writeAll("(default ABI alignment)"),
|
||||
|
||||
.empty_struct_value => return out_stream.writeAll("struct {}{}"),
|
||||
.@"struct" => {
|
||||
return out_stream.writeAll("(struct value)");
|
||||
},
|
||||
.null_value => return out_stream.writeAll("null"),
|
||||
.undef => return out_stream.writeAll("undefined"),
|
||||
.zero => return out_stream.writeAll("0"),
|
||||
@ -701,6 +708,7 @@ pub const Value = extern union {
|
||||
.@"error",
|
||||
.error_union,
|
||||
.empty_struct_value,
|
||||
.@"struct",
|
||||
.inferred_alloc,
|
||||
.abi_align_default,
|
||||
=> unreachable,
|
||||
@ -1207,6 +1215,7 @@ pub const Value = extern union {
|
||||
.call_options_type,
|
||||
.export_options_type,
|
||||
.extern_options_type,
|
||||
.@"struct",
|
||||
=> @panic("TODO this hash function looks pretty broken. audit it"),
|
||||
}
|
||||
return hasher.final();
|
||||
@ -1394,6 +1403,7 @@ pub const Value = extern union {
|
||||
.@"error",
|
||||
.error_union,
|
||||
.empty_struct_value,
|
||||
.@"struct",
|
||||
.null_value,
|
||||
.abi_align_default,
|
||||
=> false,
|
||||
@ -1537,6 +1547,14 @@ pub const Value = extern union {
|
||||
stored_inst_list: std.ArrayListUnmanaged(*ir.Inst) = .{},
|
||||
},
|
||||
};
|
||||
|
||||
pub const Struct = struct {
|
||||
pub const base_tag = Tag.@"struct";
|
||||
|
||||
base: Payload = .{ .tag = base_tag },
|
||||
/// Field values. The number and type are according to the struct type.
|
||||
data: [*]Value,
|
||||
};
|
||||
};
|
||||
|
||||
/// Big enough to fit any non-BigInt value
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user