aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Module.zig154
-rw-r--r--src/Sema.zig203
-rw-r--r--src/Zir.zig4
3 files changed, 225 insertions, 136 deletions
diff --git a/src/Module.zig b/src/Module.zig
index 57bb2435fb..f780dfb9f6 100644
--- a/src/Module.zig
+++ b/src/Module.zig
@@ -493,9 +493,10 @@ pub const Struct = struct {
fields: std.StringArrayHashMapUnmanaged(Field),
/// Represents the declarations inside this struct.
namespace: Scope.Namespace,
-
/// Offset from `owner_decl`, points to the struct AST node.
node_offset: i32,
+ /// Index of the struct_decl ZIR instruction.
+ zir_index: Zir.Inst.Index,
layout: std.builtin.TypeInfo.ContainerLayout,
status: enum {
@@ -2406,6 +2407,8 @@ pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void {
}
assert(file.zir_loaded);
+ const main_struct_inst = file.zir.extra[@enumToInt(Zir.ExtraIndex.main_struct)] -
+ @intCast(u32, Zir.Inst.Ref.typed_value_map.len);
const gpa = mod.gpa;
var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
@@ -2418,6 +2421,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void {
.owner_decl = undefined, // set below
.fields = .{},
.node_offset = 0, // it's the struct for the root file
+ .zir_index = main_struct_inst,
.layout = .Auto,
.status = .none,
.namespace = .{
@@ -2468,8 +2472,6 @@ pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void {
};
defer block_scope.instructions.deinit(gpa);
- const main_struct_inst = file.zir.extra[@enumToInt(Zir.ExtraIndex.main_struct)] -
- @intCast(u32, Zir.Inst.Ref.typed_value_map.len);
try sema.analyzeStructDecl(new_decl, main_struct_inst, struct_obj);
try new_decl.finalizeNewArena(&new_decl_arena);
@@ -4060,3 +4062,149 @@ pub const SwitchProngSrc = union(enum) {
} else unreachable;
}
};
+
+pub fn analyzeStructFields(mod: *Module, struct_obj: *Module.Struct) InnerError!void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const gpa = mod.gpa;
+ const zir = struct_obj.owner_decl.namespace.file_scope.zir;
+ const inst_data = zir.instructions.items(.data)[struct_obj.zir_index].pl_node;
+ const src = inst_data.src();
+ const extra = zir.extraData(Zir.Inst.StructDecl, inst_data.payload_index);
+ const fields_len = extra.data.fields_len;
+ const decls_len = extra.data.decls_len;
+
+ // Skip over decls.
+ var extra_index = extra.end;
+ {
+ const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable;
+ var bit_bag_index: usize = extra_index;
+ extra_index += bit_bags_count;
+ var cur_bit_bag: u32 = undefined;
+ var decl_i: u32 = 0;
+ while (decl_i < decls_len) : (decl_i += 1) {
+ if (decl_i % 8 == 0) {
+ cur_bit_bag = zir.extra[bit_bag_index];
+ bit_bag_index += 1;
+ }
+ const flags = @truncate(u4, cur_bit_bag);
+ cur_bit_bag >>= 4;
+
+ extra_index += 7; // src_hash(4) + line(1) + name(1) + value(1)
+ extra_index += @truncate(u1, flags >> 2);
+ extra_index += @truncate(u1, flags >> 3);
+ }
+ }
+
+ const body = zir.extra[extra_index..][0..extra.data.body_len];
+ if (fields_len == 0) {
+ assert(body.len == 0);
+ return;
+ }
+ extra_index += body.len;
+
+ var decl_arena = struct_obj.owner_decl.value_arena.?.promote(gpa);
+ defer struct_obj.owner_decl.value_arena.?.* = decl_arena.state;
+
+ try struct_obj.fields.ensureCapacity(&decl_arena.allocator, fields_len);
+
+ // We create a block for the field type instructions because they
+ // may need to reference Decls from inside the struct namespace.
+ // Within the field type, default value, and alignment expressions, the "owner decl"
+ // should be the struct itself. Thus we need a new Sema.
+ var sema: Sema = .{
+ .mod = mod,
+ .gpa = gpa,
+ .arena = &decl_arena.allocator,
+ .code = zir,
+ .inst_map = try gpa.alloc(*ir.Inst, zir.instructions.len),
+ .owner_decl = struct_obj.owner_decl,
+ .namespace = &struct_obj.namespace,
+ .owner_func = null,
+ .func = null,
+ .param_inst_list = &.{},
+ };
+ defer gpa.free(sema.inst_map);
+
+ var block: Scope.Block = .{
+ .parent = null,
+ .sema = &sema,
+ .src_decl = struct_obj.owner_decl,
+ .instructions = .{},
+ .inlining = null,
+ .is_comptime = true,
+ };
+ defer assert(block.instructions.items.len == 0); // should all be comptime instructions
+
+ _ = try sema.analyzeBody(&block, body);
+
+ const bits_per_field = 4;
+ const fields_per_u32 = 32 / bits_per_field;
+ const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
+ var bit_bag_index: usize = extra_index;
+ extra_index += bit_bags_count;
+ var cur_bit_bag: u32 = undefined;
+ var field_i: u32 = 0;
+ while (field_i < fields_len) : (field_i += 1) {
+ if (field_i % fields_per_u32 == 0) {
+ cur_bit_bag = zir.extra[bit_bag_index];
+ bit_bag_index += 1;
+ }
+ const has_align = @truncate(u1, cur_bit_bag) != 0;
+ cur_bit_bag >>= 1;
+ const has_default = @truncate(u1, cur_bit_bag) != 0;
+ cur_bit_bag >>= 1;
+ const is_comptime = @truncate(u1, cur_bit_bag) != 0;
+ cur_bit_bag >>= 1;
+ const unused = @truncate(u1, cur_bit_bag) != 0;
+ cur_bit_bag >>= 1;
+
+ _ = unused;
+
+ const field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]);
+ extra_index += 1;
+ const field_type_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
+ extra_index += 1;
+
+ // This string needs to outlive the ZIR code.
+ const field_name = try decl_arena.allocator.dupe(u8, field_name_zir);
+ if (field_type_ref == .none) {
+ return mod.fail(&block.base, src, "TODO: implement anytype struct field", .{});
+ }
+ const field_ty: Type = if (field_type_ref == .none)
+ Type.initTag(.noreturn)
+ else
+ // TODO: if we need to report an error here, use a source location
+ // that points to this type expression rather than the struct.
+ // But only resolve the source location if we need to emit a compile error.
+ try sema.resolveType(&block, src, field_type_ref);
+
+ const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name);
+ assert(!gop.found_existing);
+ gop.entry.value = .{
+ .ty = field_ty,
+ .abi_align = Value.initTag(.abi_align_default),
+ .default_val = Value.initTag(.unreachable_value),
+ .is_comptime = is_comptime,
+ .offset = undefined,
+ };
+
+ if (has_align) {
+ const align_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
+ extra_index += 1;
+ // TODO: if we need to report an error here, use a source location
+ // that points to this alignment expression rather than the struct.
+ // But only resolve the source location if we need to emit a compile error.
+ gop.entry.value.abi_align = (try sema.resolveInstConst(&block, src, align_ref)).val;
+ }
+ if (has_default) {
+ const default_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
+ extra_index += 1;
+ // TODO: if we need to report an error here, use a source location
+ // that points to this default value expression rather than the struct.
+ // But only resolve the source location if we need to emit a compile error.
+ gop.entry.value.default_val = (try sema.resolveInstConst(&block, src, default_ref)).val;
+ }
+ }
+}
diff --git a/src/Sema.zig b/src/Sema.zig
index 178de4f9bf..6a8a820c98 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -572,8 +572,12 @@ fn resolveConstString(
return val.toAllocatedBytes(sema.arena);
}
-fn resolveType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type {
+pub fn resolveType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type {
const air_inst = try sema.resolveInst(zir_ref);
+ return sema.resolveAirAsType(block, src, air_inst);
+}
+
+fn resolveAirAsType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, air_inst: *ir.Inst) !Type {
const wanted_type = Type.initTag(.@"type");
const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
const val = try sema.resolveConstValue(block, src, coerced_inst);
@@ -675,135 +679,6 @@ pub fn analyzeStructDecl(
_ = try sema.mod.scanNamespace(&struct_obj.namespace, extra.end, decls_len, new_decl);
}
-pub fn analyzeStructFields(
- sema: *Sema,
- block: *Scope.Block,
- new_decl_arena: *std.heap.ArenaAllocator,
-) InnerError!void {
- const tracy = trace(@src());
- defer tracy.end();
-
- const mod = sema.mod;
- const gpa = sema.gpa;
- const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
- const src = inst_data.src();
- const extra = sema.code.extraData(Zir.Inst.StructDecl, inst_data.payload_index);
- const fields_len = extra.data.fields_len;
- const decls_len = extra.data.decls_len;
-
- const body = sema.code.extra[extra_index..][0..extra.data.body_len];
- if (fields_len == 0) {
- assert(body.len == 0);
- return;
- }
- extra_index += body.len;
-
- try struct_obj.fields.ensureCapacity(&new_decl_arena.allocator, fields_len);
-
- {
- // We create a block for the field type instructions because they
- // may need to reference Decls from inside the struct namespace.
- // Within the field type, default value, and alignment expressions, the "owner decl"
- // should be the struct itself. Thus we need a new Sema.
- var struct_sema: Sema = .{
- .mod = mod,
- .gpa = gpa,
- .arena = &new_decl_arena.allocator,
- .code = sema.code,
- .inst_map = sema.inst_map,
- .owner_decl = new_decl,
- .namespace = &struct_obj.namespace,
- .owner_func = null,
- .func = null,
- .param_inst_list = &.{},
- .branch_quota = sema.branch_quota,
- .branch_count = sema.branch_count,
- };
-
- var struct_block: Scope.Block = .{
- .parent = null,
- .sema = &struct_sema,
- .src_decl = new_decl,
- .instructions = .{},
- .inlining = null,
- .is_comptime = true,
- };
- defer assert(struct_block.instructions.items.len == 0); // should all be comptime instructions
-
- _ = try struct_sema.analyzeBody(&struct_block, body);
-
- sema.branch_count = struct_sema.branch_count;
- sema.branch_quota = struct_sema.branch_quota;
- }
- const bits_per_field = 4;
- const fields_per_u32 = 32 / bits_per_field;
- const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
- var bit_bag_index: usize = extra_index;
- extra_index += bit_bags_count;
- var cur_bit_bag: u32 = undefined;
- var field_i: u32 = 0;
- while (field_i < fields_len) : (field_i += 1) {
- if (field_i % fields_per_u32 == 0) {
- cur_bit_bag = sema.code.extra[bit_bag_index];
- bit_bag_index += 1;
- }
- const has_align = @truncate(u1, cur_bit_bag) != 0;
- cur_bit_bag >>= 1;
- const has_default = @truncate(u1, cur_bit_bag) != 0;
- cur_bit_bag >>= 1;
- const is_comptime = @truncate(u1, cur_bit_bag) != 0;
- cur_bit_bag >>= 1;
- const unused = @truncate(u1, cur_bit_bag) != 0;
- cur_bit_bag >>= 1;
-
- _ = unused;
-
- const field_name_zir = sema.code.nullTerminatedString(sema.code.extra[extra_index]);
- extra_index += 1;
- const field_type_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
- extra_index += 1;
-
- // This string needs to outlive the ZIR code.
- const field_name = try new_decl_arena.allocator.dupe(u8, field_name_zir);
- if (field_type_ref == .none) {
- return mod.fail(&block.base, src, "TODO: implement anytype struct field", .{});
- }
- const field_ty: Type = if (field_type_ref == .none)
- Type.initTag(.noreturn)
- else
- // TODO: if we need to report an error here, use a source location
- // that points to this type expression rather than the struct.
- // But only resolve the source location if we need to emit a compile error.
- try sema.resolveType(block, src, field_type_ref);
-
- const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name);
- assert(!gop.found_existing);
- gop.entry.value = .{
- .ty = field_ty,
- .abi_align = Value.initTag(.abi_align_default),
- .default_val = Value.initTag(.unreachable_value),
- .is_comptime = is_comptime,
- };
-
- if (has_align) {
- const align_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
- extra_index += 1;
- // TODO: if we need to report an error here, use a source location
- // that points to this alignment expression rather than the struct.
- // But only resolve the source location if we need to emit a compile error.
- gop.entry.value.abi_align = (try sema.resolveInstConst(block, src, align_ref)).val;
- }
- if (has_default) {
- const default_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
- extra_index += 1;
- // TODO: if we need to report an error here, use a source location
- // that points to this default value expression rather than the struct.
- // But only resolve the source location if we need to emit a compile error.
- gop.entry.value.default_val = (try sema.resolveInstConst(block, src, default_ref)).val;
- }
- }
-}
-
fn zirStructDecl(
sema: *Sema,
block: *Scope.Block,
@@ -826,6 +701,7 @@ fn zirStructDecl(
.owner_decl = sema.owner_decl,
.fields = .{},
.node_offset = inst_data.src_node,
+ .zir_index = inst,
.layout = layout,
.status = .none,
.namespace = .{
@@ -5207,8 +5083,23 @@ fn zirFieldTypeRef(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inner
fn zirFieldType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+ const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data;
const src = inst_data.src();
- return sema.mod.fail(&block.base, src, "TODO: Sema.zirFieldType", .{});
+ const field_name = sema.code.nullTerminatedString(extra.name_start);
+ const unresolved_struct_type = try sema.resolveType(block, src, extra.container_type);
+ if (unresolved_struct_type.zigTypeTag() != .Struct) {
+ return sema.mod.fail(&block.base, src, "expected struct; found '{}'", .{
+ unresolved_struct_type,
+ });
+ }
+ const struct_ty = try sema.resolveTypeFields(block, src, unresolved_struct_type);
+ const struct_obj = struct_ty.castTag(.@"struct").?.data;
+ const field = struct_obj.fields.get(field_name) orelse {
+ return sema.mod.fail(&block.base, src, "no field named '{s}' in struct '{}'", .{
+ field_name, struct_ty,
+ });
+ };
+ return sema.mod.constType(sema.arena, src, field.ty);
}
fn zirErrorReturnTrace(
@@ -6800,3 +6691,53 @@ fn resolvePeerTypes(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, instructi
return chosen.ty;
}
+
+fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) InnerError!Type {
+ switch (ty.tag()) {
+ .@"struct" => {
+ const struct_obj = ty.castTag(.@"struct").?.data;
+ switch (struct_obj.status) {
+ .none => {},
+ .have_field_types, .have_layout => return ty,
+ }
+ try sema.mod.analyzeStructFields(struct_obj);
+ 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);
+ },
+ else => return ty,
+ }
+}
+
+fn getBuiltinType(
+ sema: *Sema,
+ block: *Scope.Block,
+ src: LazySrcLoc,
+ name: []const u8,
+) InnerError!Type {
+ const mod = sema.mod;
+ const std_pkg = mod.root_pkg.table.get("std").?;
+ const std_file = (mod.importPkg(mod.root_pkg, std_pkg) catch unreachable).file;
+ const opt_builtin_inst = try sema.analyzeNamespaceLookup(
+ block,
+ src,
+ std_file.namespace,
+ "builtin",
+ );
+ const builtin_inst = try sema.analyzeLoad(block, src, opt_builtin_inst.?, src);
+ const builtin_ty = try sema.resolveAirAsType(block, src, builtin_inst);
+ const opt_ty_inst = try sema.analyzeNamespaceLookup(
+ block,
+ src,
+ builtin_ty.getNamespace().?,
+ name,
+ );
+ const ty_inst = try sema.analyzeLoad(block, src, opt_ty_inst.?, src);
+ return sema.resolveAirAsType(block, src, ty_inst);
+}
diff --git a/src/Zir.zig b/src/Zir.zig
index ec4c97f0f7..8b67618cf1 100644
--- a/src/Zir.zig
+++ b/src/Zir.zig
@@ -670,10 +670,10 @@ pub const Inst = struct {
/// A struct literal with a specified type, with no fields.
/// Uses the `un_node` field.
struct_init_empty,
- /// Given a struct, union, enum, or opaque and a field name as a string index,
+ /// Given a struct, union, or enum, and a field name as a string index,
/// returns the field type. Uses the `pl_node` field. Payload is `FieldType`.
field_type,
- /// Given a struct, union, enum, or opaque and a field name as a Ref,
+ /// Given a struct, union, or enum, and a field name as a Ref,
/// returns the field type. Uses the `pl_node` field. Payload is `FieldTypeRef`.
field_type_ref,
/// Finalizes a typed struct initialization, performs validation, and returns the