diff options
Diffstat (limited to 'src/Module.zig')
| -rw-r--r-- | src/Module.zig | 238 |
1 files changed, 216 insertions, 22 deletions
diff --git a/src/Module.zig b/src/Module.zig index df2c80720a..d84b4e2f35 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -456,6 +456,16 @@ pub const Decl = struct { return struct_obj; } + /// If the Decl has a value and it is a union, return it, + /// otherwise null. + pub fn getUnion(decl: *Decl) ?*Union { + if (!decl.has_tv) return null; + const ty = (decl.val.castTag(.ty) orelse return null).data; + const union_obj = (ty.cast(Type.Payload.Union) orelse return null).data; + if (union_obj.owner_decl != decl) return null; + return union_obj; + } + /// If the Decl has a value and it is a function, return it, /// otherwise null. pub fn getFunction(decl: *Decl) ?*Fn { @@ -571,6 +581,18 @@ pub const Struct = struct { .lazy = .{ .node_offset = s.node_offset }, }; } + + pub fn haveFieldTypes(s: Struct) bool { + return switch (s.status) { + .none, + .field_types_wip, + => false, + .have_field_types, + .layout_wip, + .have_layout, + => true, + }; + } }; /// Represents the data that an enum declaration provides, when the fields @@ -624,6 +646,52 @@ pub const EnumFull = struct { } }; +pub const Union = struct { + /// The Decl that corresponds to the union itself. + owner_decl: *Decl, + /// An enum type which is used for the tag of the union. + /// This type is created even for untagged unions, even when the memory + /// layout does not store the tag. + /// Whether zig chooses this type or the user specifies it, it is stored here. + /// This will be set to the null type until status is `have_field_types`. + tag_ty: Type, + /// Set of field names in declaration order. + fields: std.StringArrayHashMapUnmanaged(Field), + /// Represents the declarations inside this union. + namespace: Scope.Namespace, + /// Offset from `owner_decl`, points to the union decl AST node. + node_offset: i32, + /// Index of the union_decl ZIR instruction. + zir_index: Zir.Inst.Index, + + layout: std.builtin.TypeInfo.ContainerLayout, + status: enum { + none, + field_types_wip, + have_field_types, + layout_wip, + have_layout, + }, + + pub const Field = struct { + /// undefined until `status` is `have_field_types` or `have_layout`. + ty: Type, + abi_align: Value, + }; + + pub fn getFullyQualifiedName(s: *Union, gpa: *Allocator) ![]u8 { + return s.owner_decl.getFullyQualifiedName(gpa); + } + + pub fn srcLoc(self: Union) SrcLoc { + return .{ + .file_scope = self.owner_decl.getFileScope(), + .parent_decl_node = self.owner_decl.src_node, + .lazy = .{ .node_offset = self.node_offset }, + }; + } +}; + /// Some Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator. /// Extern functions do not have this data structure; they are represented by /// the `Decl` only, with a `Value` tag of `extern_fn`. @@ -2401,6 +2469,7 @@ pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node /// Patch ups: /// * Struct.zir_index +/// * Decl.zir_index /// * Fn.zir_body_inst /// * Decl.zir_decl_index /// * Decl.name @@ -2479,6 +2548,13 @@ fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !void { }; } + if (decl.getUnion()) |union_obj| { + union_obj.zir_index = inst_map.get(union_obj.zir_index) orelse { + try file.deleted_decls.append(gpa, decl); + continue; + }; + } + if (decl.getFunction()) |func| { func.zir_body_inst = inst_map.get(func.zir_body_inst) orelse { try file.deleted_decls.append(gpa, decl); @@ -2769,7 +2845,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { }; if (decl.isRoot()) { - log.debug("semaDecl root {*} ({s})", .{decl, decl.name}); + log.debug("semaDecl root {*} ({s})", .{ decl, decl.name }); const main_struct_inst = zir.getMainStruct(); const struct_obj = decl.getStruct().?; try sema.analyzeStructDecl(decl, main_struct_inst, struct_obj); @@ -4271,7 +4347,7 @@ pub const SwitchProngSrc = union(enum) { } }; -pub fn analyzeStructFields(mod: *Module, struct_obj: *Module.Struct) InnerError!void { +pub fn analyzeStructFields(mod: *Module, struct_obj: *Struct) InnerError!void { const tracy = trace(@src()); defer tracy.end(); @@ -4284,26 +4360,9 @@ pub fn analyzeStructFields(mod: *Module, struct_obj: *Module.Struct) InnerError! 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); - } - } + var decls_it = zir.declIterator(struct_obj.zir_index); + while (decls_it.next()) |_| {} + var extra_index = decls_it.extra_index; const body = zir.extra[extra_index..][0..extra.data.body_len]; if (fields_len == 0) { @@ -4417,6 +4476,141 @@ pub fn analyzeStructFields(mod: *Module, struct_obj: *Module.Struct) InnerError! } } +pub fn analyzeUnionFields(mod: *Module, union_obj: *Union) InnerError!void { + const tracy = trace(@src()); + defer tracy.end(); + + const gpa = mod.gpa; + const zir = union_obj.owner_decl.namespace.file_scope.zir; + const inst_data = zir.instructions.items(.data)[union_obj.zir_index].pl_node; + const src = inst_data.src(); + const extra = zir.extraData(Zir.Inst.UnionDecl, inst_data.payload_index); + const fields_len = extra.data.fields_len; + const decls_len = extra.data.decls_len; + + // Skip over decls. + var decls_it = zir.declIterator(union_obj.zir_index); + while (decls_it.next()) |_| {} + var extra_index = decls_it.extra_index; + + 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 = union_obj.owner_decl.value_arena.?.promote(gpa); + defer union_obj.owner_decl.value_arena.?.* = decl_arena.state; + + try union_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 = union_obj.owner_decl, + .namespace = &union_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 = union_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); + + var auto_enum_tag: ?bool = null; + + 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_type = @truncate(u1, cur_bit_bag) != 0; + cur_bit_bag >>= 1; + const has_align = @truncate(u1, cur_bit_bag) != 0; + cur_bit_bag >>= 1; + const has_tag = @truncate(u1, cur_bit_bag) != 0; + cur_bit_bag >>= 1; + const unused = @truncate(u1, cur_bit_bag) != 0; + cur_bit_bag >>= 1; + + if (auto_enum_tag == null) { + auto_enum_tag = unused; + } + + const field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]); + extra_index += 1; + + const field_type_ref: Zir.Inst.Ref = if (has_type) blk: { + const field_type_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); + extra_index += 1; + break :blk field_type_ref; + } else .none; + + const align_ref: Zir.Inst.Ref = if (has_align) blk: { + const align_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); + extra_index += 1; + break :blk align_ref; + } else .none; + + const tag_ref: Zir.Inst.Ref = if (has_tag) blk: { + const tag_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); + extra_index += 1; + break :blk tag_ref; + } else .none; + + // This string needs to outlive the ZIR code. + const field_name = try decl_arena.allocator.dupe(u8, field_name_zir); + const field_ty: Type = if (field_type_ref == .none) + Type.initTag(.void) + else + // TODO: if we need to report an error here, use a source location + // that points to this type expression rather than the union. + // But only resolve the source location if we need to emit a compile error. + try sema.resolveType(&block, src, field_type_ref); + + const gop = union_obj.fields.getOrPutAssumeCapacity(field_name); + assert(!gop.found_existing); + gop.entry.value = .{ + .ty = field_ty, + .abi_align = Value.initTag(.abi_align_default), + }; + + if (align_ref != .none) { + // 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; + } + } + + // TODO resolve the union tag type +} + /// Called from `performAllTheWork`, after all AstGen workers have finished, /// and before the main semantic analysis loop begins. pub fn processOutdatedAndDeletedDecls(mod: *Module) !void { |
