From cd8070f94f6865959949dbcec6e0e32cd88bb544 Mon Sep 17 00:00:00 2001 From: antlilja Date: Sat, 30 Jul 2022 11:39:49 +0200 Subject: Removed param_names from Fn inside Module.zig Removed the copy of param_names inside of Fn and changed to implementation of getParamName to fetch to parameter name from the ZIR. The signature of getParamName was also changed to take an additional *Module argument. --- src/Module.zig | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) (limited to 'src/Module.zig') diff --git a/src/Module.zig b/src/Module.zig index 4ac2775515..b80e00ad59 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1471,14 +1471,6 @@ pub const Fn = struct { /// TODO apply the same enhancement for param_names below to this field. anytype_args: [*]bool, - /// Prefer to use `getParamName` to access this because of the future improvement - /// we want to do mentioned in the TODO below. - /// Stored in gpa. - /// TODO: change param ZIR instructions to be embedded inside the function - /// ZIR instruction instead of before it, so that `zir_body_inst` can be used to - /// determine param names rather than redundantly storing them here. - param_names: []const [:0]const u8, - /// Precomputed hash for monomorphed_funcs. /// This is important because it may be accessed when resizing monomorphed_funcs /// while this Fn has already been added to the set, but does not have the @@ -1590,18 +1582,28 @@ pub const Fn = struct { gpa.destroy(node); it = next; } - - for (func.param_names) |param_name| { - gpa.free(param_name); - } - gpa.free(func.param_names); } - pub fn getParamName(func: Fn, index: u32) [:0]const u8 { - // TODO rework ZIR of parameters so that this function looks up - // param names in ZIR instead of redundantly saving them into Fn. - // const zir = func.owner_decl.getFileScope().zir; - return func.param_names[index]; + pub fn getParamName(func: Fn, mod: *Module, index: u32) [:0]const u8 { + const file = mod.declPtr(func.owner_decl).getFileScope(); + + const tags = file.zir.instructions.items(.tag); + const data = file.zir.instructions.items(.data); + + const param_body = file.zir.getParamBody(func.zir_body_inst); + const param = param_body[index]; + + return switch (tags[param]) { + .param, .param_comptime => blk: { + const extra = file.zir.extraData(Zir.Inst.Param, data[param].pl_tok.payload_index); + break :blk file.zir.nullTerminatedString(extra.data.name); + }, + .param_anytype, .param_anytype_comptime => blk: { + const param_data = data[param].str_tok; + break :blk param_data.get(file.zir); + }, + else => unreachable, + }; } pub fn hasInferredErrorSet(func: Fn, mod: *Module) bool { -- cgit v1.2.3 From ab3b614a335ffac9eac4f824ee18fba262ad988e Mon Sep 17 00:00:00 2001 From: antlilja Date: Sat, 30 Jul 2022 18:43:15 +0200 Subject: Removed anytype_args field from Fn anytype_args field was replaced with isAnytypeParam function. --- src/Module.zig | 21 ++++++++++++++++----- src/Sema.zig | 11 ++--------- 2 files changed, 18 insertions(+), 14 deletions(-) (limited to 'src/Module.zig') diff --git a/src/Module.zig b/src/Module.zig index b80e00ad59..625af7ca07 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1464,12 +1464,8 @@ pub const Fn = struct { /// These never have .generic_poison for the Type /// because the Type is needed to pass to `Type.eql` and for inserting comptime arguments /// into the inst_map when analyzing the body of a generic function instantiation. - /// Instead, the is_anytype knowledge is communicated via `anytype_args`. + /// Instead, the is_anytype knowledge is communicated via `isAnytypeParam`. comptime_args: ?[*]TypedValue, - /// When comptime_args is null, this is undefined. Otherwise, this flags each - /// parameter and tells whether it is anytype. - /// TODO apply the same enhancement for param_names below to this field. - anytype_args: [*]bool, /// Precomputed hash for monomorphed_funcs. /// This is important because it may be accessed when resizing monomorphed_funcs @@ -1584,6 +1580,21 @@ pub const Fn = struct { } } + pub fn isAnytypeParam(func: Fn, mod: *Module, index: u32) bool { + const file = mod.declPtr(func.owner_decl).getFileScope(); + + const tags = file.zir.instructions.items(.tag); + + const param_body = file.zir.getParamBody(func.zir_body_inst); + const param = param_body[index]; + + return switch (tags[param]) { + .param, .param_comptime => false, + .param_anytype, .param_anytype_comptime => true, + else => unreachable, + }; + } + pub fn getParamName(func: Fn, mod: *Module, index: u32) [:0]const u8 { const file = mod.declPtr(func.owner_decl).getFileScope(); diff --git a/src/Sema.zig b/src/Sema.zig index 07e81dc5be..1629b7711a 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5498,7 +5498,7 @@ const GenericCallAdapter = struct { const this_is_comptime = this_arg.val.tag() != .generic_poison; const other_is_comptime = other_arg.val.tag() != .generic_poison; const this_is_anytype = this_arg.ty.tag() != .generic_poison; - const other_is_anytype = other_key.anytype_args[i]; + const other_is_anytype = other_key.isAnytypeParam(ctx.module, @intCast(u32, i)); if (other_is_anytype != this_is_anytype) return false; if (other_is_comptime != this_is_comptime) return false; @@ -6379,12 +6379,9 @@ fn instantiateGenericCall( errdefer new_func.deinit(gpa); assert(new_func == new_module_func); - const anytype_args = try new_decl_arena_allocator.alloc(bool, func_ty_info.param_types.len); - new_func.anytype_args = anytype_args.ptr; arg_i = 0; for (fn_info.param_body) |inst| { var is_comptime = false; - var is_anytype = false; switch (zir_tags[inst]) { .param => { is_comptime = func_ty_info.paramIsComptime(arg_i); @@ -6393,11 +6390,9 @@ fn instantiateGenericCall( is_comptime = true; }, .param_anytype => { - is_anytype = true; is_comptime = func_ty_info.paramIsComptime(arg_i); }, .param_anytype_comptime => { - is_anytype = true; is_comptime = true; }, else => continue, @@ -6405,10 +6400,9 @@ fn instantiateGenericCall( // We populate the Type here regardless because it is needed by // `GenericCallAdapter.eql` as well as function body analysis. - // Whether it is anytype is communicated by `anytype_args`. + // Whether it is anytype is communicated by `isAnytypeParam`. const arg = child_sema.inst_map.get(inst).?; const copied_arg_ty = try child_sema.typeOf(arg).copy(new_decl_arena_allocator); - anytype_args[arg_i] = is_anytype; if (try sema.typeRequiresComptime(block, .unneeded, copied_arg_ty)) { is_comptime = true; @@ -7760,7 +7754,6 @@ fn funcCommon( .zir_body_inst = func_inst, .owner_decl = sema.owner_decl_index, .comptime_args = comptime_args, - .anytype_args = undefined, .hash = hash, .lbrace_line = src_locs.lbrace_line, .rbrace_line = src_locs.rbrace_line, -- cgit v1.2.3 From c76b5c1a31ebe09726cd29e7a6da69613eabfd10 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 3 Aug 2022 19:55:27 +0300 Subject: stage2: correct node offset of nested declarations --- src/AstGen.zig | 21 +++++-- src/Module.zig | 33 +++-------- src/Sema.zig | 69 ++++++++++------------ src/print_zir.zig | 16 +++++ src/type.zig | 44 -------------- .../non_constant_expression_in_array_size.zig | 2 +- test/cases/compile_errors/not_an_enum_type.zig | 2 +- ...ame_on_invalid_value_of_non-exhaustive_enum.zig | 2 +- test/cases/error_in_nested_declaration.zig | 31 ++++++++++ 9 files changed, 106 insertions(+), 114 deletions(-) create mode 100644 test/cases/error_in_nested_declaration.zig (limited to 'src/Module.zig') diff --git a/src/AstGen.zig b/src/AstGen.zig index ffe489a216..3a30ab7b9d 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -4278,7 +4278,7 @@ fn structDeclInner( var known_non_opv = false; var known_comptime_only = false; for (container_decl.ast.members) |member_node| { - const member = switch (try containerMember(gz, &namespace.base, &wip_members, member_node)) { + const member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) { .decl => continue, .field => |field| field, }; @@ -4445,7 +4445,7 @@ fn unionDeclInner( defer wip_members.deinit(); for (members) |member_node| { - const member = switch (try containerMember(gz, &namespace.base, &wip_members, member_node)) { + const member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) { .decl => continue, .field => |field| field, }; @@ -4732,7 +4732,7 @@ fn containerDecl( for (container_decl.ast.members) |member_node| { if (member_node == counts.nonexhaustive_node) continue; - const member = switch (try containerMember(gz, &namespace.base, &wip_members, member_node)) { + const member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) { .decl => continue, .field => |field| field, }; @@ -4810,13 +4810,26 @@ fn containerDecl( }; defer namespace.deinit(gpa); + astgen.advanceSourceCursorToNode(node); + var block_scope: GenZir = .{ + .parent = &namespace.base, + .decl_node_index = node, + .decl_line = astgen.source_line, + .astgen = astgen, + .force_comptime = true, + .in_defer = false, + .instructions = gz.instructions, + .instructions_top = gz.instructions.items.len, + }; + defer block_scope.unstack(); + const decl_count = try astgen.scanDecls(&namespace, container_decl.ast.members); var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, 0, 0, 0); defer wip_members.deinit(); for (container_decl.ast.members) |member_node| { - const res = try containerMember(gz, &namespace.base, &wip_members, member_node); + const res = try containerMember(&block_scope, &namespace.base, &wip_members, member_node); if (res == .field) { return astgen.failNode(member_node, "opaque types cannot have fields", .{}); } diff --git a/src/Module.zig b/src/Module.zig index ab394af0ad..8b195eff2d 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -853,8 +853,6 @@ pub const EmitH = struct { pub const ErrorSet = struct { /// The Decl that corresponds to the error set itself. owner_decl: Decl.Index, - /// Offset from Decl node index, points to the error set AST node. - node_offset: i32, /// The string bytes are stored in the owner Decl arena. /// These must be in sorted order. See sortNames. names: NameMap, @@ -866,7 +864,7 @@ pub const ErrorSet = struct { return .{ .file_scope = owner_decl.getFileScope(), .parent_decl_node = owner_decl.src_node, - .lazy = LazySrcLoc.nodeOffset(self.node_offset), + .lazy = LazySrcLoc.nodeOffset(0), }; } @@ -893,8 +891,6 @@ pub const Struct = struct { namespace: Namespace, /// The Decl that corresponds to the struct itself. owner_decl: Decl.Index, - /// 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, @@ -953,7 +949,7 @@ pub const Struct = struct { return .{ .file_scope = owner_decl.getFileScope(), .parent_decl_node = owner_decl.src_node, - .lazy = LazySrcLoc.nodeOffset(s.node_offset), + .lazy = LazySrcLoc.nodeOffset(0), }; } @@ -968,7 +964,7 @@ pub const Struct = struct { }); return s.srcLoc(mod); }; - const node = owner_decl.relativeToNodeIndex(s.node_offset); + const node = owner_decl.relativeToNodeIndex(0); const node_tags = tree.nodes.items(.tag); switch (node_tags[node]) { .container_decl, @@ -1060,8 +1056,6 @@ pub const Struct = struct { pub const EnumSimple = struct { /// The Decl that corresponds to the enum itself. owner_decl: Decl.Index, - /// Offset from `owner_decl`, points to the enum decl AST node. - node_offset: i32, /// Set of field names in declaration order. fields: NameMap, @@ -1072,7 +1066,7 @@ pub const EnumSimple = struct { return .{ .file_scope = owner_decl.getFileScope(), .parent_decl_node = owner_decl.src_node, - .lazy = LazySrcLoc.nodeOffset(self.node_offset), + .lazy = LazySrcLoc.nodeOffset(0), }; } }; @@ -1083,8 +1077,6 @@ pub const EnumSimple = struct { pub const EnumNumbered = struct { /// The Decl that corresponds to the enum itself. owner_decl: Decl.Index, - /// Offset from `owner_decl`, points to the enum decl AST node. - node_offset: i32, /// An integer type which is used for the numerical value of the enum. /// Whether zig chooses this type or the user specifies it, it is stored here. tag_ty: Type, @@ -1103,7 +1095,7 @@ pub const EnumNumbered = struct { return .{ .file_scope = owner_decl.getFileScope(), .parent_decl_node = owner_decl.src_node, - .lazy = LazySrcLoc.nodeOffset(self.node_offset), + .lazy = LazySrcLoc.nodeOffset(0), }; } }; @@ -1113,8 +1105,6 @@ pub const EnumNumbered = struct { pub const EnumFull = struct { /// The Decl that corresponds to the enum itself. owner_decl: Decl.Index, - /// Offset from `owner_decl`, points to the enum decl AST node. - node_offset: i32, /// An integer type which is used for the numerical value of the enum. /// Whether zig chooses this type or the user specifies it, it is stored here. tag_ty: Type, @@ -1137,7 +1127,7 @@ pub const EnumFull = struct { return .{ .file_scope = owner_decl.getFileScope(), .parent_decl_node = owner_decl.src_node, - .lazy = LazySrcLoc.nodeOffset(self.node_offset), + .lazy = LazySrcLoc.nodeOffset(0), }; } }; @@ -1155,8 +1145,6 @@ pub const Union = struct { namespace: Namespace, /// The Decl that corresponds to the union itself. owner_decl: Decl.Index, - /// 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, @@ -1203,7 +1191,7 @@ pub const Union = struct { return .{ .file_scope = owner_decl.getFileScope(), .parent_decl_node = owner_decl.src_node, - .lazy = LazySrcLoc.nodeOffset(self.node_offset), + .lazy = LazySrcLoc.nodeOffset(0), }; } @@ -1218,7 +1206,7 @@ pub const Union = struct { }); return u.srcLoc(mod); }; - const node = owner_decl.relativeToNodeIndex(u.node_offset); + const node = owner_decl.relativeToNodeIndex(0); const node_tags = tree.nodes.items(.tag); var buf: [2]Ast.Node.Index = undefined; switch (node_tags[node]) { @@ -1410,8 +1398,6 @@ pub const Union = struct { pub const Opaque = struct { /// The Decl that corresponds to the opaque itself. owner_decl: Decl.Index, - /// Offset from `owner_decl`, points to the opaque decl AST node. - node_offset: i32, /// Represents the declarations inside this opaque. namespace: Namespace, @@ -1420,7 +1406,7 @@ pub const Opaque = struct { return .{ .file_scope = owner_decl.getFileScope(), .parent_decl_node = owner_decl.src_node, - .lazy = LazySrcLoc.nodeOffset(self.node_offset), + .lazy = LazySrcLoc.nodeOffset(0), }; } @@ -4337,7 +4323,6 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void { struct_obj.* = .{ .owner_decl = undefined, // set below .fields = .{}, - .node_offset = 0, // it's the struct for the root file .zir_index = undefined, // set below .layout = .Auto, .status = .none, diff --git a/src/Sema.zig b/src/Sema.zig index c91f4cddb1..34d64ff919 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1818,10 +1818,10 @@ fn failWithInvalidComptimeFieldStore(sema: *Sema, block: *Block, init_src: LazyS const tree = try sema.getAstTree(block); const decl = sema.mod.declPtr(decl_index); - const field_src = enumFieldSrcLoc(decl, tree.*, container_ty.getNodeOffset(), field_index); + const field_src = enumFieldSrcLoc(decl, tree.*, 0, field_index); const default_value_src: LazySrcLoc = .{ .node_offset_field_default = field_src.node_offset.x }; - try sema.errNote(block, default_value_src, msg, "default value set here", .{}); + try sema.mod.errNoteNonLazy(default_value_src.toSrcLoc(decl), msg, "default value set here", .{}); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); @@ -1866,7 +1866,7 @@ fn addFieldErrNote( const decl_index = container_ty.getOwnerDecl(); const decl = mod.declPtr(decl_index); const tree = try sema.getAstTree(block); - const field_src = enumFieldSrcLoc(decl, tree.*, container_ty.getNodeOffset(), field_index); + const field_src = enumFieldSrcLoc(decl, tree.*, 0, field_index); try mod.errNoteNonLazy(field_src.toSrcLoc(decl), parent, format, args); } @@ -2262,7 +2262,7 @@ fn zirStructDecl( const struct_obj = try new_decl_arena_allocator.create(Module.Struct); const struct_ty = try Type.Tag.@"struct".create(new_decl_arena_allocator, struct_obj); const struct_val = try Value.Tag.ty.create(new_decl_arena_allocator, struct_ty); - const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ .ty = Type.type, .val = struct_val, }, small.name_strategy, "struct", inst); @@ -2272,7 +2272,6 @@ fn zirStructDecl( struct_obj.* = .{ .owner_decl = new_decl_index, .fields = .{}, - .node_offset = src.node_offset.x, .zir_index = inst, .layout = small.layout, .status = .none, @@ -2294,6 +2293,7 @@ fn zirStructDecl( fn createAnonymousDeclTypeNamed( sema: *Sema, block: *Block, + src: LazySrcLoc, typed_value: TypedValue, name_strategy: Zir.Inst.NameStrategy, anon_prefix: []const u8, @@ -2303,7 +2303,8 @@ fn createAnonymousDeclTypeNamed( const namespace = block.namespace; const src_scope = block.wip_capture_scope; const src_decl = mod.declPtr(block.src_decl); - const new_decl_index = try mod.allocateNewDecl(namespace, src_decl.src_node, src_scope); + const src_node = src_decl.relativeToNodeIndex(src.node_offset.x); + const new_decl_index = try mod.allocateNewDecl(namespace, src_node, src_scope); errdefer mod.destroyDecl(new_decl_index); switch (name_strategy) { @@ -2378,7 +2379,7 @@ fn createAnonymousDeclTypeNamed( }, else => {}, }; - return sema.createAnonymousDeclTypeNamed(block, typed_value, .anon, anon_prefix, null); + return sema.createAnonymousDeclTypeNamed(block, src, typed_value, .anon, anon_prefix, null); }, } } @@ -2442,7 +2443,7 @@ fn zirEnumDecl( }; const enum_ty = Type.initPayload(&enum_ty_payload.base); const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty); - const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ .ty = Type.type, .val = enum_val, }, small.name_strategy, "enum", inst); @@ -2456,7 +2457,6 @@ fn zirEnumDecl( .tag_ty_inferred = true, .fields = .{}, .values = .{}, - .node_offset = src.node_offset.x, .namespace = .{ .parent = block.namespace, .ty = enum_ty, @@ -2684,7 +2684,7 @@ fn zirUnionDecl( const union_ty = Type.initPayload(&union_payload.base); const union_val = try Value.Tag.ty.create(new_decl_arena_allocator, union_ty); const mod = sema.mod; - const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ .ty = Type.type, .val = union_val, }, small.name_strategy, "union", inst); @@ -2695,7 +2695,6 @@ fn zirUnionDecl( .owner_decl = new_decl_index, .tag_ty = Type.initTag(.@"null"), .fields = .{}, - .node_offset = src.node_offset.x, .zir_index = inst, .layout = small.layout, .status = .none, @@ -2753,7 +2752,7 @@ fn zirOpaqueDecl( }; const opaque_ty = Type.initPayload(&opaque_ty_payload.base); const opaque_val = try Value.Tag.ty.create(new_decl_arena_allocator, opaque_ty); - const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ .ty = Type.type, .val = opaque_val, }, small.name_strategy, "opaque", inst); @@ -2763,7 +2762,6 @@ fn zirOpaqueDecl( opaque_obj.* = .{ .owner_decl = new_decl_index, - .node_offset = src.node_offset.x, .namespace = .{ .parent = block.namespace, .ty = opaque_ty, @@ -2802,7 +2800,7 @@ fn zirErrorSetDecl( const error_set_ty = try Type.Tag.error_set.create(new_decl_arena_allocator, error_set); const error_set_val = try Value.Tag.ty.create(new_decl_arena_allocator, error_set_ty); const mod = sema.mod; - const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ .ty = Type.type, .val = error_set_val, }, name_strategy, "error", inst); @@ -2827,7 +2825,6 @@ fn zirErrorSetDecl( error_set.* = .{ .owner_decl = new_decl_index, - .node_offset = inst_data.src_node, .names = names, }; try new_decl.finalizeNewArena(&new_decl_arena); @@ -16336,7 +16333,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in }; const enum_ty = Type.initPayload(&enum_ty_payload.base); const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty); - const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ .ty = Type.type, .val = enum_val, }, name_strategy, "enum", inst); @@ -16350,7 +16347,6 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in .tag_ty_inferred = false, .fields = .{}, .values = .{}, - .node_offset = src.node_offset.x, .namespace = .{ .parent = block.namespace, .ty = enum_ty, @@ -16433,7 +16429,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in }; const opaque_ty = Type.initPayload(&opaque_ty_payload.base); const opaque_val = try Value.Tag.ty.create(new_decl_arena_allocator, opaque_ty); - const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ .ty = Type.type, .val = opaque_val, }, name_strategy, "opaque", inst); @@ -16443,7 +16439,6 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in opaque_obj.* = .{ .owner_decl = new_decl_index, - .node_offset = src.node_offset.x, .namespace = .{ .parent = block.namespace, .ty = opaque_ty, @@ -16492,7 +16487,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in }; const union_ty = Type.initPayload(&union_payload.base); const new_union_val = try Value.Tag.ty.create(new_decl_arena_allocator, union_ty); - const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ .ty = Type.type, .val = new_union_val, }, name_strategy, "union", inst); @@ -16503,7 +16498,6 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in .owner_decl = new_decl_index, .tag_ty = Type.initTag(.@"null"), .fields = .{}, - .node_offset = src.node_offset.x, .zir_index = inst, .layout = layout, .status = .have_field_types, @@ -16798,7 +16792,7 @@ fn reifyStruct( const struct_ty = try Type.Tag.@"struct".create(new_decl_arena_allocator, struct_obj); const new_struct_val = try Value.Tag.ty.create(new_decl_arena_allocator, struct_ty); const mod = sema.mod; - const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{ + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ .ty = Type.type, .val = new_struct_val, }, name_strategy, "struct", inst); @@ -16808,7 +16802,6 @@ fn reifyStruct( struct_obj.* = .{ .owner_decl = new_decl_index, .fields = .{}, - .node_offset = src.node_offset.x, .zir_index = inst, .layout = layout_val.toEnum(std.builtin.Type.ContainerLayout), .status = .have_field_types, @@ -27241,7 +27234,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small); var extra_index: usize = extended.operand; - const src = LazySrcLoc.nodeOffset(struct_obj.node_offset); + const src = LazySrcLoc.nodeOffset(0); extra_index += @boolToInt(small.has_src_node); const fields_len = if (small.has_fields_len) blk: { @@ -27360,12 +27353,12 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void if (gop.found_existing) { const msg = msg: { const tree = try sema.getAstTree(&block_scope); - const field_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, field_i); + const field_src = enumFieldSrcLoc(decl, tree.*, 0, field_i); const msg = try sema.errMsg(&block_scope, field_src, "duplicate struct field: '{s}'", .{field_name}); errdefer msg.destroy(gpa); const prev_field_index = struct_obj.fields.getIndex(field_name).?; - const prev_field_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, prev_field_index); + const prev_field_src = enumFieldSrcLoc(decl, tree.*, 0, prev_field_index); try sema.mod.errNoteNonLazy(prev_field_src.toSrcLoc(decl), msg, "other field here", .{}); try sema.errNote(&block_scope, src, msg, "struct declared here", .{}); break :msg msg; @@ -27422,7 +27415,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void if (field_ty.zigTypeTag() == .Opaque) { const msg = msg: { const tree = try sema.getAstTree(&block_scope); - const field_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, i); + const field_src = enumFieldSrcLoc(decl, tree.*, 0, i); const msg = try sema.errMsg(&block_scope, field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); errdefer msg.destroy(sema.gpa); @@ -27434,7 +27427,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void if (struct_obj.layout == .Extern and !sema.validateExternType(field.ty, .other)) { const msg = msg: { const tree = try sema.getAstTree(&block_scope); - const fields_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, i); + const fields_src = enumFieldSrcLoc(decl, tree.*, 0, i); const msg = try sema.errMsg(&block_scope, fields_src, "extern structs cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)}); errdefer msg.destroy(sema.gpa); @@ -27447,7 +27440,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void } else if (struct_obj.layout == .Packed and !(validatePackedType(field.ty))) { const msg = msg: { const tree = try sema.getAstTree(&block_scope); - const fields_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, i); + const fields_src = enumFieldSrcLoc(decl, tree.*, 0, i); const msg = try sema.errMsg(&block_scope, fields_src, "packed structs cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)}); errdefer msg.destroy(sema.gpa); @@ -27504,7 +27497,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { const small = @bitCast(Zir.Inst.UnionDecl.Small, extended.small); var extra_index: usize = extended.operand; - const src = LazySrcLoc.nodeOffset(union_obj.node_offset); + const src = LazySrcLoc.nodeOffset(0); extra_index += @boolToInt(small.has_src_node); const tag_type_ref: Zir.Inst.Ref = if (small.has_tag_type) blk: { @@ -27728,12 +27721,12 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { if (gop.found_existing) { const msg = msg: { const tree = try sema.getAstTree(&block_scope); - const field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i); + const field_src = enumFieldSrcLoc(decl, tree.*, 0, field_i); const msg = try sema.errMsg(&block_scope, field_src, "duplicate union field: '{s}'", .{field_name}); errdefer msg.destroy(gpa); const prev_field_index = union_obj.fields.getIndex(field_name).?; - const prev_field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, prev_field_index); + const prev_field_src = enumFieldSrcLoc(decl, tree.*, 0, prev_field_index); try sema.mod.errNoteNonLazy(prev_field_src.toSrcLoc(decl), msg, "other field here", .{}); try sema.errNote(&block_scope, src, msg, "union declared here", .{}); break :msg msg; @@ -27746,7 +27739,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { if (!enum_has_field) { const msg = msg: { const tree = try sema.getAstTree(&block_scope); - const field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i); + const field_src = enumFieldSrcLoc(decl, tree.*, 0, field_i); const msg = try sema.errMsg(&block_scope, field_src, "no field named '{s}' in enum '{}'", .{ field_name, union_obj.tag_ty.fmt(sema.mod) }); errdefer msg.destroy(sema.gpa); try sema.addDeclaredHereNote(msg, union_obj.tag_ty); @@ -27759,7 +27752,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { if (field_ty.zigTypeTag() == .Opaque) { const msg = msg: { const tree = try sema.getAstTree(&block_scope); - const field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i); + const field_src = enumFieldSrcLoc(decl, tree.*, 0, field_i); const msg = try sema.errMsg(&block_scope, field_src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{}); errdefer msg.destroy(sema.gpa); @@ -27771,7 +27764,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { if (union_obj.layout == .Extern and !sema.validateExternType(field_ty, .union_field)) { const msg = msg: { const tree = try sema.getAstTree(&block_scope); - const field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i); + const field_src = enumFieldSrcLoc(decl, tree.*, 0, field_i); const msg = try sema.errMsg(&block_scope, field_src, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)}); errdefer msg.destroy(sema.gpa); @@ -27784,7 +27777,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { } else if (union_obj.layout == .Packed and !(validatePackedType(field_ty))) { const msg = msg: { const tree = try sema.getAstTree(&block_scope); - const fields_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i); + const fields_src = enumFieldSrcLoc(decl, tree.*, 0, field_i); const msg = try sema.errMsg(&block_scope, fields_src, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)}); errdefer msg.destroy(sema.gpa); @@ -27876,7 +27869,6 @@ fn generateUnionTagTypeNumbered( .tag_ty = int_ty, .fields = .{}, .values = .{}, - .node_offset = 0, }; // Here we pre-allocate the maps using the decl arena. try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); @@ -27934,7 +27926,6 @@ fn generateUnionTagTypeSimple(sema: *Sema, block: *Block, fields_len: usize, may enum_obj.* = .{ .owner_decl = new_decl_index, .fields = .{}, - .node_offset = 0, }; // Here we pre-allocate the maps using the decl arena. try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); @@ -28245,7 +28236,7 @@ fn enumFieldSrcLoc( => tree.containerDeclArg(enum_node), // Container was constructed with `@Type`. - else => return LazySrcLoc.nodeOffset(node_offset), + else => return LazySrcLoc.nodeOffset(0), }; var it_index: usize = 0; for (container_decl.ast.members) |member_node| { diff --git a/src/print_zir.zig b/src/print_zir.zig index ca2368fe10..6503e029fd 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -1245,6 +1245,10 @@ const Writer = struct { if (decls_len == 0) { try stream.writeAll("{}, "); } else { + const prev_parent_decl_node = self.parent_decl_node; + if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); + defer self.parent_decl_node = prev_parent_decl_node; + try stream.writeAll("{\n"); self.indent += 2; extra_index = try self.writeDecls(stream, decls_len, extra_index); @@ -1415,6 +1419,10 @@ const Writer = struct { if (decls_len == 0) { try stream.writeAll("{}, "); } else { + const prev_parent_decl_node = self.parent_decl_node; + if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); + defer self.parent_decl_node = prev_parent_decl_node; + try stream.writeAll("{\n"); self.indent += 2; extra_index = try self.writeDecls(stream, decls_len, extra_index); @@ -1662,6 +1670,10 @@ const Writer = struct { if (decls_len == 0) { try stream.writeAll("{}, "); } else { + const prev_parent_decl_node = self.parent_decl_node; + if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); + defer self.parent_decl_node = prev_parent_decl_node; + try stream.writeAll("{\n"); self.indent += 2; extra_index = try self.writeDecls(stream, decls_len, extra_index); @@ -1755,6 +1767,10 @@ const Writer = struct { if (decls_len == 0) { try stream.writeAll("{})"); } else { + const prev_parent_decl_node = self.parent_decl_node; + if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); + defer self.parent_decl_node = prev_parent_decl_node; + try stream.writeAll("{\n"); self.indent += 2; _ = try self.writeDecls(stream, decls_len, extra_index); diff --git a/src/type.zig b/src/type.zig index d53158735a..85d77303c9 100644 --- a/src/type.zig +++ b/src/type.zig @@ -5771,50 +5771,6 @@ pub const Type = extern union { } } - pub fn getNodeOffset(ty: Type) i32 { - switch (ty.tag()) { - .enum_full, .enum_nonexhaustive => { - const enum_full = ty.cast(Payload.EnumFull).?.data; - return enum_full.node_offset; - }, - .enum_numbered => return ty.castTag(.enum_numbered).?.data.node_offset, - .enum_simple => { - const enum_simple = ty.castTag(.enum_simple).?.data; - return enum_simple.node_offset; - }, - .@"struct" => { - const struct_obj = ty.castTag(.@"struct").?.data; - return struct_obj.node_offset; - }, - .error_set => { - const error_set = ty.castTag(.error_set).?.data; - return error_set.node_offset; - }, - .@"union", .union_safety_tagged, .union_tagged => { - const union_obj = ty.cast(Payload.Union).?.data; - return union_obj.node_offset; - }, - .@"opaque" => { - const opaque_obj = ty.cast(Payload.Opaque).?.data; - return opaque_obj.node_offset; - }, - .atomic_order, - .atomic_rmw_op, - .calling_convention, - .address_space, - .float_mode, - .reduce_op, - .call_options, - .prefetch_options, - .export_options, - .extern_options, - .type_info, - => unreachable, // These need to be resolved earlier. - - else => unreachable, - } - } - /// This enum does not directly correspond to `std.builtin.TypeId` because /// it has extra enum tags in it, as a way of using less memory. For example, /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types diff --git a/test/cases/compile_errors/non_constant_expression_in_array_size.zig b/test/cases/compile_errors/non_constant_expression_in_array_size.zig index e702246001..1dc8b50464 100644 --- a/test/cases/compile_errors/non_constant_expression_in_array_size.zig +++ b/test/cases/compile_errors/non_constant_expression_in_array_size.zig @@ -11,4 +11,4 @@ export fn entry() usize { return @offsetOf(Foo, "y"); } // target=native // // :5:25: error: cannot load runtime value in comptime block -// :2:15: note: called from here +// :2:12: note: called from here diff --git a/test/cases/compile_errors/not_an_enum_type.zig b/test/cases/compile_errors/not_an_enum_type.zig index 063ee8a8d8..bdd03c8db7 100644 --- a/test/cases/compile_errors/not_an_enum_type.zig +++ b/test/cases/compile_errors/not_an_enum_type.zig @@ -17,4 +17,4 @@ const ExpectedVarDeclOrFn = struct {}; // target=native // // :4:9: error: expected type '@typeInfo(tmp.Error).Union.tag_type.?', found 'type' -// :8:1: note: enum declared here +// :8:15: note: enum declared here diff --git a/test/cases/compile_errors/tagName_on_invalid_value_of_non-exhaustive_enum.zig b/test/cases/compile_errors/tagName_on_invalid_value_of_non-exhaustive_enum.zig index e9e63f0d7a..1de0d1c145 100644 --- a/test/cases/compile_errors/tagName_on_invalid_value_of_non-exhaustive_enum.zig +++ b/test/cases/compile_errors/tagName_on_invalid_value_of_non-exhaustive_enum.zig @@ -9,4 +9,4 @@ test "enum" { // is_test=1 // // :3:9: error: no field with value '5' in enum 'test.enum.E' -// :1:1: note: declared here +// :2:15: note: declared here diff --git a/test/cases/error_in_nested_declaration.zig b/test/cases/error_in_nested_declaration.zig new file mode 100644 index 0000000000..3fff746909 --- /dev/null +++ b/test/cases/error_in_nested_declaration.zig @@ -0,0 +1,31 @@ +const S = struct { + b: u32, + c: i32, + a: struct { + pub fn str(_: @This(), extra: []u32) []i32 { + return @bitCast([]i32, extra); + } + }, +}; + +pub export fn entry() void { + var s: S = undefined; + _ = s.a.str(undefined); +} + +const S2 = struct { + a: [*c]anyopaque, +}; + +pub export fn entry2() void { + var s: S2 = undefined; + _ = s; +} + +// error +// backend=llvm +// target=native +// +// :17:12: error: C pointers cannot point to opaque types +// :6:29: error: cannot @bitCast to '[]i32' +// :6:29: note: use @ptrCast to cast from '[]u32' -- cgit v1.2.3 From 0d32b73078aa4579187f7d5c67343a6036eed277 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Mon, 8 Aug 2022 18:39:14 +0200 Subject: stage2: Implement explicit backing integers for packed structs Now the backing integer of a packed struct type may be explicitly specified with e.g. `packed struct(u32) { ... }`. --- lib/std/builtin.zig | 2 + lib/std/zig/Ast.zig | 2 +- lib/std/zig/parse.zig | 10 +- lib/std/zig/parser_test.zig | 7 + src/AstGen.zig | 56 +++++- src/Autodoc.zig | 11 ++ src/Module.zig | 21 +-- src/Sema.zig | 207 ++++++++++++++++++++- src/Zir.zig | 16 +- src/codegen/llvm.zig | 15 +- src/print_zir.zig | 25 ++- src/stage1/all_types.hpp | 1 + src/stage1/analyze.cpp | 6 + src/stage1/ir.cpp | 40 ++-- src/stage1/parser.cpp | 11 +- src/type.zig | 23 ++- test/behavior.zig | 1 + .../packed_struct_explicit_backing_int.zig | 53 ++++++ test/behavior/type_info.zig | 4 +- .../packed_struct_backing_int_wrong.zig | 55 ++++++ 20 files changed, 493 insertions(+), 73 deletions(-) create mode 100644 test/behavior/packed_struct_explicit_backing_int.zig create mode 100644 test/cases/compile_errors/packed_struct_backing_int_wrong.zig (limited to 'src/Module.zig') diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index ef716c6972..2c2bc92c96 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -294,6 +294,8 @@ pub const Type = union(enum) { /// therefore must be kept in sync with the compiler implementation. pub const Struct = struct { layout: ContainerLayout, + /// Only valid if layout is .Packed + backing_integer: ?type = null, fields: []const StructField, decls: []const Declaration, is_tuple: bool, diff --git a/lib/std/zig/Ast.zig b/lib/std/zig/Ast.zig index 9bffcb3df2..016cefb255 100644 --- a/lib/std/zig/Ast.zig +++ b/lib/std/zig/Ast.zig @@ -2967,7 +2967,7 @@ pub const Node = struct { /// Same as ContainerDeclTwo except there is known to be a trailing comma /// or semicolon before the rbrace. container_decl_two_trailing, - /// `union(lhs)` / `enum(lhs)`. `SubRange[rhs]`. + /// `struct(lhs)` / `union(lhs)` / `enum(lhs)`. `SubRange[rhs]`. container_decl_arg, /// Same as container_decl_arg but there is known to be a trailing /// comma or semicolon before the rbrace. diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index 2a7d2623ef..a03764a91c 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -3356,16 +3356,18 @@ const Parser = struct { } /// Caller must have already verified the first token. + /// ContainerDeclAuto <- ContainerDeclType LBRACE container_doc_comment? ContainerMembers RBRACE + /// /// ContainerDeclType - /// <- KEYWORD_struct + /// <- KEYWORD_struct (LPAREN Expr RPAREN)? + /// / KEYWORD_opaque /// / KEYWORD_enum (LPAREN Expr RPAREN)? /// / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)? - /// / KEYWORD_opaque fn parseContainerDeclAuto(p: *Parser) !Node.Index { const main_token = p.nextToken(); const arg_expr = switch (p.token_tags[main_token]) { - .keyword_struct, .keyword_opaque => null_node, - .keyword_enum => blk: { + .keyword_opaque => null_node, + .keyword_struct, .keyword_enum => blk: { if (p.eatToken(.l_paren)) |_| { const expr = try p.expectExpr(); _ = try p.expectToken(.r_paren); diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index a74d53f21c..bee9375b5a 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -3064,6 +3064,13 @@ test "zig fmt: struct declaration" { \\ c: u8, \\}; \\ + \\const Ps = packed struct(u32) { + \\ a: u1, + \\ b: u2, + \\ + \\ c: u29, + \\}; + \\ \\const Es = extern struct { \\ a: u8, \\ b: u8, diff --git a/src/AstGen.zig b/src/AstGen.zig index efa8690b55..af77cdacc4 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -152,6 +152,7 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir { 0, tree.containerDeclRoot(), .Auto, + 0, )) |struct_decl_ref| { assert(refToIndex(struct_decl_ref).? == 0); } else |err| switch (err) { @@ -4223,15 +4224,18 @@ fn structDeclInner( node: Ast.Node.Index, container_decl: Ast.full.ContainerDecl, layout: std.builtin.Type.ContainerLayout, + backing_int_node: Ast.Node.Index, ) InnerError!Zir.Inst.Ref { const decl_inst = try gz.reserveInstructionIndex(); - if (container_decl.ast.members.len == 0) { + if (container_decl.ast.members.len == 0 and backing_int_node == 0) { try gz.setStruct(decl_inst, .{ .src_node = node, .layout = layout, .fields_len = 0, .decls_len = 0, + .backing_int_ref = .none, + .backing_int_body_len = 0, .known_non_opv = false, .known_comptime_only = false, }); @@ -4266,6 +4270,35 @@ fn structDeclInner( }; defer block_scope.unstack(); + const scratch_top = astgen.scratch.items.len; + defer astgen.scratch.items.len = scratch_top; + + var backing_int_body_len: usize = 0; + const backing_int_ref: Zir.Inst.Ref = blk: { + if (backing_int_node != 0) { + if (layout != .Packed) { + return astgen.failNode(backing_int_node, "non-packed struct does not support backing integer type", .{}); + } else { + const backing_int_ref = try typeExpr(&block_scope, &namespace.base, backing_int_node); + if (!block_scope.isEmpty()) { + if (!block_scope.endsWithNoReturn()) { + _ = try block_scope.addBreak(.break_inline, decl_inst, backing_int_ref); + } + + const body = block_scope.instructionsSlice(); + const old_scratch_len = astgen.scratch.items.len; + try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body)); + appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body); + backing_int_body_len = astgen.scratch.items.len - old_scratch_len; + block_scope.instructions.items.len = block_scope.instructions_top; + } + break :blk backing_int_ref; + } + } else { + break :blk .none; + } + }; + const decl_count = try astgen.scanDecls(&namespace, container_decl.ast.members); const field_count = @intCast(u32, container_decl.ast.members.len - decl_count); @@ -4378,6 +4411,8 @@ fn structDeclInner( .layout = layout, .fields_len = field_count, .decls_len = decl_count, + .backing_int_ref = backing_int_ref, + .backing_int_body_len = @intCast(u32, backing_int_body_len), .known_non_opv = known_non_opv, .known_comptime_only = known_comptime_only, }); @@ -4386,7 +4421,9 @@ fn structDeclInner( const decls_slice = wip_members.declsSlice(); const fields_slice = wip_members.fieldsSlice(); const bodies_slice = astgen.scratch.items[bodies_start..]; - try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len + fields_slice.len + bodies_slice.len); + try astgen.extra.ensureUnusedCapacity(gpa, backing_int_body_len + + decls_slice.len + fields_slice.len + bodies_slice.len); + astgen.extra.appendSliceAssumeCapacity(astgen.scratch.items[scratch_top..][0..backing_int_body_len]); astgen.extra.appendSliceAssumeCapacity(decls_slice); astgen.extra.appendSliceAssumeCapacity(fields_slice); astgen.extra.appendSliceAssumeCapacity(bodies_slice); @@ -4582,9 +4619,7 @@ fn containerDecl( else => unreachable, } else std.builtin.Type.ContainerLayout.Auto; - assert(container_decl.ast.arg == 0); - - const result = try structDeclInner(gz, scope, node, container_decl, layout); + const result = try structDeclInner(gz, scope, node, container_decl, layout, container_decl.ast.arg); return rvalue(gz, rl, result, node); }, .keyword_union => { @@ -11254,6 +11289,8 @@ const GenZir = struct { src_node: Ast.Node.Index, fields_len: u32, decls_len: u32, + backing_int_ref: Zir.Inst.Ref, + backing_int_body_len: u32, layout: std.builtin.Type.ContainerLayout, known_non_opv: bool, known_comptime_only: bool, @@ -11261,7 +11298,7 @@ const GenZir = struct { const astgen = gz.astgen; const gpa = astgen.gpa; - try astgen.extra.ensureUnusedCapacity(gpa, 4); + try astgen.extra.ensureUnusedCapacity(gpa, 6); const payload_index = @intCast(u32, astgen.extra.items.len); if (args.src_node != 0) { @@ -11274,6 +11311,12 @@ const GenZir = struct { if (args.decls_len != 0) { astgen.extra.appendAssumeCapacity(args.decls_len); } + if (args.backing_int_ref != .none) { + astgen.extra.appendAssumeCapacity(args.backing_int_body_len); + if (args.backing_int_body_len == 0) { + astgen.extra.appendAssumeCapacity(@enumToInt(args.backing_int_ref)); + } + } astgen.instructions.set(inst, .{ .tag = .extended, .data = .{ .extended = .{ @@ -11282,6 +11325,7 @@ const GenZir = struct { .has_src_node = args.src_node != 0, .has_fields_len = args.fields_len != 0, .has_decls_len = args.decls_len != 0, + .has_backing_int = args.backing_int_ref != .none, .known_non_opv = args.known_non_opv, .known_comptime_only = args.known_comptime_only, .name_strategy = gz.anon_name_strategy, diff --git a/src/Autodoc.zig b/src/Autodoc.zig index 3fcc28d742..ef2579a8e4 100644 --- a/src/Autodoc.zig +++ b/src/Autodoc.zig @@ -2536,6 +2536,17 @@ fn walkInstruction( break :blk decls_len; } else 0; + // TODO: Expose explicit backing integer types in some way. + if (small.has_backing_int) { + const backing_int_body_len = file.zir.extra[extra_index]; + extra_index += 1; // backing_int_body_len + if (backing_int_body_len == 0) { + extra_index += 1; // backing_int_ref + } else { + extra_index += backing_int_body_len; // backing_int_body_inst + } + } + var decl_indexes: std.ArrayListUnmanaged(usize) = .{}; var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{}; diff --git a/src/Module.zig b/src/Module.zig index 8b195eff2d..7e877a2f4a 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -895,6 +895,11 @@ pub const Struct = struct { zir_index: Zir.Inst.Index, layout: std.builtin.Type.ContainerLayout, + /// If the layout is not packed, this is the noreturn type. + /// If the layout is packed, this is the backing integer type of the packed struct. + /// Whether zig chooses this type or the user specifies it, it is stored here. + /// This will be set to the noreturn type until status is `have_layout`. + backing_int_ty: Type = Type.initTag(.noreturn), status: enum { none, field_types_wip, @@ -1025,7 +1030,7 @@ pub const Struct = struct { pub fn packedFieldBitOffset(s: Struct, target: Target, index: usize) u16 { assert(s.layout == .Packed); - assert(s.haveFieldTypes()); + assert(s.haveLayout()); var bit_sum: u64 = 0; for (s.fields.values()) |field, i| { if (i == index) { @@ -1033,19 +1038,7 @@ pub const Struct = struct { } bit_sum += field.ty.bitSize(target); } - return @intCast(u16, bit_sum); - } - - pub fn packedIntegerBits(s: Struct, target: Target) u16 { - return s.packedFieldBitOffset(target, s.fields.count()); - } - - pub fn packedIntegerType(s: Struct, target: Target, buf: *Type.Payload.Bits) Type { - buf.* = .{ - .base = .{ .tag = .int_unsigned }, - .data = s.packedIntegerBits(target), - }; - return Type.initPayload(&buf.base); + unreachable; // index out of bounds } }; diff --git a/src/Sema.zig b/src/Sema.zig index 6d95b46c7c..faf1692653 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2239,6 +2239,16 @@ pub fn analyzeStructDecl( break :blk decls_len; } else 0; + if (small.has_backing_int) { + const backing_int_body_len = sema.code.extra[extra_index]; + extra_index += 1; // backing_int_body_len + if (backing_int_body_len == 0) { + extra_index += 1; // backing_int_ref + } else { + extra_index += backing_int_body_len; // backing_int_body_inst + } + } + _ = try sema.mod.scanNamespace(&struct_obj.namespace, extra_index, decls_len, new_decl); } @@ -14285,13 +14295,27 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, struct_ty.getNamespace()); - const field_values = try sema.arena.create([4]Value); + const backing_integer_val = blk: { + if (layout == .Packed) { + const struct_obj = struct_ty.castTag(.@"struct").?.data; + assert(struct_obj.haveLayout()); + assert(struct_obj.backing_int_ty.isInt()); + const backing_int_ty_val = try Value.Tag.ty.create(sema.arena, struct_obj.backing_int_ty); + break :blk try Value.Tag.opt_payload.create(sema.arena, backing_int_ty_val); + } else { + break :blk Value.initTag(.null_value); + } + }; + + const field_values = try sema.arena.create([5]Value); field_values.* = .{ // layout: ContainerLayout, try Value.Tag.enum_field_index.create( sema.arena, @enumToInt(layout), ), + // backing_integer: ?type, + backing_integer_val, // fields: []const StructField, fields_val, // decls: []const Declaration, @@ -16308,7 +16332,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in if (!try sema.intFitsInType(block, src, alignment_val, Type.u32, null)) { return sema.fail(block, src, "alignment must fit in 'u32'", .{}); } - const abi_align = @intCast(u29, alignment_val.toUnsignedInt(target)); + const abi_align = @intCast(u29, (try alignment_val.getUnsignedIntAdvanced(target, sema.kit(block, src))).?); var buffer: Value.ToTypeBuffer = undefined; const unresolved_elem_ty = child_val.toType(&buffer); @@ -16473,22 +16497,31 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in const struct_val = union_val.val.castTag(.aggregate).?.data; // layout: containerlayout, const layout_val = struct_val[0]; + // backing_int: ?type, + const backing_int_val = struct_val[1]; // fields: []const enumfield, - const fields_val = struct_val[1]; + const fields_val = struct_val[2]; // decls: []const declaration, - const decls_val = struct_val[2]; + const decls_val = struct_val[3]; // is_tuple: bool, - const is_tuple_val = struct_val[3]; + const is_tuple_val = struct_val[4]; + assert(struct_val.len == 5); + + const layout = layout_val.toEnum(std.builtin.Type.ContainerLayout); // Decls if (decls_val.sliceLen(mod) > 0) { return sema.fail(block, src, "reified structs must have no decls", .{}); } + if (layout != .Packed and !backing_int_val.isNull()) { + return sema.fail(block, src, "non-packed struct does not support backing integer type", .{}); + } + return if (is_tuple_val.toBool()) try sema.reifyTuple(block, src, fields_val) else - try sema.reifyStruct(block, inst, src, layout_val, fields_val, name_strategy); + try sema.reifyStruct(block, inst, src, layout, backing_int_val, fields_val, name_strategy); }, .Enum => { const struct_val = union_val.val.castTag(.aggregate).?.data; @@ -16981,7 +17014,8 @@ fn reifyStruct( block: *Block, inst: Zir.Inst.Index, src: LazySrcLoc, - layout_val: Value, + layout: std.builtin.Type.ContainerLayout, + backing_int_val: Value, fields_val: Value, name_strategy: Zir.Inst.NameStrategy, ) CompileError!Air.Inst.Ref { @@ -17004,7 +17038,7 @@ fn reifyStruct( .owner_decl = new_decl_index, .fields = .{}, .zir_index = inst, - .layout = layout_val.toEnum(std.builtin.Type.ContainerLayout), + .layout = layout, .status = .have_field_types, .known_non_opv = false, .namespace = .{ @@ -17070,6 +17104,41 @@ fn reifyStruct( }; } + if (layout == .Packed) { + struct_obj.status = .layout_wip; + + for (struct_obj.fields.values()) |field, index| { + sema.resolveTypeLayout(block, src, field.ty) catch |err| switch (err) { + error.AnalysisFail => { + const msg = sema.err orelse return err; + try sema.addFieldErrNote(block, struct_ty, index, msg, "while checking this field", .{}); + return err; + }, + else => return err, + }; + } + + var fields_bit_sum: u64 = 0; + for (struct_obj.fields.values()) |field| { + fields_bit_sum += field.ty.bitSize(target); + } + + if (backing_int_val.optionalValue()) |payload| { + var buf: Value.ToTypeBuffer = undefined; + const backing_int_ty = payload.toType(&buf); + try sema.checkBackingIntType(block, src, backing_int_ty, fields_bit_sum); + struct_obj.backing_int_ty = try backing_int_ty.copy(new_decl_arena_allocator); + } else { + var buf: Type.Payload.Bits = .{ + .base = .{ .tag = .int_unsigned }, + .data = @intCast(u16, fields_bit_sum), + }; + struct_obj.backing_int_ty = try Type.initPayload(&buf.base).copy(new_decl_arena_allocator); + } + + struct_obj.status = .have_layout; + } + try new_decl.finalizeNewArena(&new_decl_arena); return sema.analyzeDeclVal(block, src, new_decl_index); } @@ -27154,6 +27223,11 @@ fn resolveStructLayout( else => return err, }; } + + if (struct_obj.layout == .Packed) { + try semaBackingIntType(sema.mod, struct_obj); + } + struct_obj.status = .have_layout; // In case of querying the ABI alignment of this struct, we will ask @@ -27173,6 +27247,109 @@ fn resolveStructLayout( // otherwise it's a tuple; no need to resolve anything } +fn semaBackingIntType(mod: *Module, struct_obj: *Module.Struct) CompileError!void { + const gpa = mod.gpa; + const target = mod.getTarget(); + + var fields_bit_sum: u64 = 0; + for (struct_obj.fields.values()) |field| { + fields_bit_sum += field.ty.bitSize(target); + } + + const decl_index = struct_obj.owner_decl; + const decl = mod.declPtr(decl_index); + var decl_arena = decl.value_arena.?.promote(gpa); + defer decl.value_arena.?.* = decl_arena.state; + const decl_arena_allocator = decl_arena.allocator(); + + const zir = struct_obj.namespace.file_scope.zir; + const extended = zir.instructions.items(.data)[struct_obj.zir_index].extended; + assert(extended.opcode == .struct_decl); + const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small); + + if (small.has_backing_int) { + var extra_index: usize = extended.operand; + extra_index += @boolToInt(small.has_src_node); + extra_index += @boolToInt(small.has_fields_len); + extra_index += @boolToInt(small.has_decls_len); + + const backing_int_body_len = zir.extra[extra_index]; + extra_index += 1; + + var analysis_arena = std.heap.ArenaAllocator.init(gpa); + defer analysis_arena.deinit(); + + var sema: Sema = .{ + .mod = mod, + .gpa = gpa, + .arena = analysis_arena.allocator(), + .perm_arena = decl_arena_allocator, + .code = zir, + .owner_decl = decl, + .owner_decl_index = decl_index, + .func = null, + .fn_ret_ty = Type.void, + .owner_func = null, + }; + defer sema.deinit(); + + var wip_captures = try WipCaptureScope.init(gpa, decl_arena_allocator, decl.src_scope); + defer wip_captures.deinit(); + + var block: Block = .{ + .parent = null, + .sema = &sema, + .src_decl = decl_index, + .namespace = &struct_obj.namespace, + .wip_capture_scope = wip_captures.scope, + .instructions = .{}, + .inlining = null, + .is_comptime = true, + }; + defer { + assert(block.instructions.items.len == 0); + block.params.deinit(gpa); + } + + const backing_int_src: LazySrcLoc = .{ .node_offset_container_tag = 0 }; + const backing_int_ty = blk: { + if (backing_int_body_len == 0) { + const backing_int_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); + break :blk try sema.resolveType(&block, backing_int_src, backing_int_ref); + } else { + const body = zir.extra[extra_index..][0..backing_int_body_len]; + const ty_ref = try sema.resolveBody(&block, body, struct_obj.zir_index); + break :blk try sema.analyzeAsType(&block, backing_int_src, ty_ref); + } + }; + + try sema.checkBackingIntType(&block, backing_int_src, backing_int_ty, fields_bit_sum); + struct_obj.backing_int_ty = try backing_int_ty.copy(decl_arena_allocator); + } else { + var buf: Type.Payload.Bits = .{ + .base = .{ .tag = .int_unsigned }, + .data = @intCast(u16, fields_bit_sum), + }; + struct_obj.backing_int_ty = try Type.initPayload(&buf.base).copy(decl_arena_allocator); + } +} + +fn checkBackingIntType(sema: *Sema, block: *Block, src: LazySrcLoc, backing_int_ty: Type, fields_bit_sum: u64) CompileError!void { + const target = sema.mod.getTarget(); + + if (!backing_int_ty.isInt()) { + return sema.fail(block, src, "expected backing integer type, found '{}'", .{backing_int_ty.fmt(sema.mod)}); + } + if (backing_int_ty.bitSize(target) != fields_bit_sum) { + return sema.fail( + block, + src, + "backing integer type '{}' has bit size {} but the struct fields have a total bit size of {}", + .{ backing_int_ty.fmt(sema.mod), backing_int_ty.bitSize(target), fields_bit_sum }, + ); + } +} + fn resolveUnionLayout( sema: *Sema, block: *Block, @@ -27495,12 +27672,26 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void break :decls_len decls_len; } else 0; + // The backing integer cannot be handled until `resolveStructLayout()`. + if (small.has_backing_int) { + const backing_int_body_len = zir.extra[extra_index]; + extra_index += 1; // backing_int_body_len + if (backing_int_body_len == 0) { + extra_index += 1; // backing_int_ref + } else { + extra_index += backing_int_body_len; // backing_int_body_inst + } + } + // Skip over decls. var decls_it = zir.declIteratorInner(extra_index, decls_len); while (decls_it.next()) |_| {} extra_index = decls_it.extra_index; if (fields_len == 0) { + if (struct_obj.layout == .Packed) { + try semaBackingIntType(mod, struct_obj); + } struct_obj.status = .have_layout; return; } diff --git a/src/Zir.zig b/src/Zir.zig index c62e6d02bb..538ef6aaf8 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -3085,13 +3085,16 @@ pub const Inst = struct { /// 0. src_node: i32, // if has_src_node /// 1. fields_len: u32, // if has_fields_len /// 2. decls_len: u32, // if has_decls_len - /// 3. decl_bits: u32 // for every 8 decls + /// 3. backing_int_body_len: u32, // if has_backing_int + /// 4. backing_int_ref: Ref, // if has_backing_int and backing_int_body_len is 0 + /// 5. backing_int_body_inst: Inst, // if has_backing_int and backing_int_body_len is > 0 + /// 6. decl_bits: u32 // for every 8 decls /// - sets of 4 bits: /// 0b000X: whether corresponding decl is pub /// 0b00X0: whether corresponding decl is exported /// 0b0X00: whether corresponding decl has an align expression /// 0bX000: whether corresponding decl has a linksection or an address space expression - /// 4. decl: { // for every decls_len + /// 7. decl: { // for every decls_len /// src_hash: [4]u32, // hash of source bytes /// line: u32, // line number of decl, relative to parent /// name: u32, // null terminated string index @@ -3109,13 +3112,13 @@ pub const Inst = struct { /// address_space: Ref, /// } /// } - /// 5. flags: u32 // for every 8 fields + /// 8. flags: u32 // for every 8 fields /// - sets of 4 bits: /// 0b000X: whether corresponding field has an align expression /// 0b00X0: whether corresponding field has a default expression /// 0b0X00: whether corresponding field is comptime /// 0bX000: whether corresponding field has a type expression - /// 6. fields: { // for every fields_len + /// 9. fields: { // for every fields_len /// field_name: u32, /// doc_comment: u32, // 0 if no doc comment /// field_type: Ref, // if corresponding bit is not set. none means anytype. @@ -3123,7 +3126,7 @@ pub const Inst = struct { /// align_body_len: u32, // if corresponding bit is set /// init_body_len: u32, // if corresponding bit is set /// } - /// 7. bodies: { // for every fields_len + /// 10. bodies: { // for every fields_len /// field_type_body_inst: Inst, // for each field_type_body_len /// align_body_inst: Inst, // for each align_body_len /// init_body_inst: Inst, // for each init_body_len @@ -3133,11 +3136,12 @@ pub const Inst = struct { has_src_node: bool, has_fields_len: bool, has_decls_len: bool, + has_backing_int: bool, known_non_opv: bool, known_comptime_only: bool, name_strategy: NameStrategy, layout: std.builtin.Type.ContainerLayout, - _: u7 = undefined, + _: u6 = undefined, }; }; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 9c3efa18cd..6d2922ea3f 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1683,8 +1683,7 @@ pub const Object = struct { if (ty.castTag(.@"struct")) |payload| { const struct_obj = payload.data; if (struct_obj.layout == .Packed) { - var buf: Type.Payload.Bits = undefined; - const info = struct_obj.packedIntegerType(target, &buf).intInfo(target); + const info = struct_obj.backing_int_ty.intInfo(target); const dwarf_encoding: c_uint = switch (info.signedness) { .signed => DW.ATE.signed, .unsigned => DW.ATE.unsigned, @@ -2679,9 +2678,7 @@ pub const DeclGen = struct { const struct_obj = t.castTag(.@"struct").?.data; if (struct_obj.layout == .Packed) { - var buf: Type.Payload.Bits = undefined; - const int_ty = struct_obj.packedIntegerType(target, &buf); - const int_llvm_ty = try dg.lowerType(int_ty); + const int_llvm_ty = try dg.lowerType(struct_obj.backing_int_ty); gop.value_ptr.* = int_llvm_ty; return int_llvm_ty; } @@ -3330,8 +3327,8 @@ pub const DeclGen = struct { const struct_obj = tv.ty.castTag(.@"struct").?.data; if (struct_obj.layout == .Packed) { - const big_bits = struct_obj.packedIntegerBits(target); - const int_llvm_ty = dg.context.intType(big_bits); + const big_bits = struct_obj.backing_int_ty.bitSize(target); + const int_llvm_ty = dg.context.intType(@intCast(c_uint, big_bits)); const fields = struct_obj.fields.values(); comptime assert(Type.packed_struct_layout_version == 2); var running_int: *const llvm.Value = int_llvm_ty.constNull(); @@ -8243,8 +8240,8 @@ pub const FuncGen = struct { .Struct => { if (result_ty.containerLayout() == .Packed) { const struct_obj = result_ty.castTag(.@"struct").?.data; - const big_bits = struct_obj.packedIntegerBits(target); - const int_llvm_ty = self.dg.context.intType(big_bits); + const big_bits = struct_obj.backing_int_ty.bitSize(target); + const int_llvm_ty = self.dg.context.intType(@intCast(c_uint, big_bits)); const fields = struct_obj.fields.values(); comptime assert(Type.packed_struct_layout_version == 2); var running_int: *const llvm.Value = int_llvm_ty.constNull(); diff --git a/src/print_zir.zig b/src/print_zir.zig index 2b70799c36..4bc96c4259 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -1245,9 +1245,28 @@ const Writer = struct { try self.writeFlag(stream, "known_non_opv, ", small.known_non_opv); try self.writeFlag(stream, "known_comptime_only, ", small.known_comptime_only); - try stream.print("{s}, {s}, ", .{ - @tagName(small.name_strategy), @tagName(small.layout), - }); + + try stream.print("{s}, ", .{@tagName(small.name_strategy)}); + + if (small.layout == .Packed and small.has_backing_int) { + const backing_int_body_len = self.code.extra[extra_index]; + extra_index += 1; + try stream.writeAll("Packed("); + if (backing_int_body_len == 0) { + const backing_int_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); + extra_index += 1; + try self.writeInstRef(stream, backing_int_ref); + } else { + const body = self.code.extra[extra_index..][0..backing_int_body_len]; + extra_index += backing_int_body_len; + self.indent += 2; + try self.writeBracedDecl(stream, body); + self.indent -= 2; + } + try stream.writeAll("), "); + } else { + try stream.print("{s}, ", .{@tagName(small.layout)}); + } if (decls_len == 0) { try stream.writeAll("{}, "); diff --git a/src/stage1/all_types.hpp b/src/stage1/all_types.hpp index 4028c3872d..9f9a6151b8 100644 --- a/src/stage1/all_types.hpp +++ b/src/stage1/all_types.hpp @@ -1116,6 +1116,7 @@ struct AstNodeContainerDecl { ContainerLayout layout; bool auto_enum, is_root; // union(enum) + bool unsupported_explicit_backing_int; }; struct AstNodeErrorSetField { diff --git a/src/stage1/analyze.cpp b/src/stage1/analyze.cpp index 08aa8bbf06..90173f384e 100644 --- a/src/stage1/analyze.cpp +++ b/src/stage1/analyze.cpp @@ -3034,6 +3034,12 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) { AstNode *decl_node = struct_type->data.structure.decl_node; + if (decl_node->data.container_decl.unsupported_explicit_backing_int) { + add_node_error(g, decl_node, buf_create_from_str( + "the stage1 compiler does not support explicit backing integer types on packed structs")); + return ErrorSemanticAnalyzeFail; + } + if (struct_type->data.structure.resolve_loop_flag_zero_bits) { if (struct_type->data.structure.resolve_status != ResolveStatusInvalid) { struct_type->data.structure.resolve_status = ResolveStatusInvalid; diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp index e31715030c..a5428945a9 100644 --- a/src/stage1/ir.cpp +++ b/src/stage1/ir.cpp @@ -18640,7 +18640,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, Scope *scope, AstNode *sour result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Struct", nullptr); - ZigValue **fields = alloc_const_vals_ptrs(g, 4); + ZigValue **fields = alloc_const_vals_ptrs(g, 5); result->data.x_struct.fields = fields; // layout: ContainerLayout @@ -18648,8 +18648,17 @@ static Error ir_make_type_info_value(IrAnalyze *ira, Scope *scope, AstNode *sour fields[0]->special = ConstValSpecialStatic; fields[0]->type = ir_type_info_get_type(ira, "ContainerLayout", nullptr); bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.structure.layout); + + // backing_integer: ?type + ensure_field_index(result->type, "backing_integer", 1); + fields[1]->special = ConstValSpecialStatic; + fields[1]->type = get_optional_type(g, g->builtin_types.entry_type); + // This is always null in stage1, as stage1 does not support explicit backing integers + // for packed structs. + fields[1]->data.x_optional = nullptr; + // fields: []Type.StructField - ensure_field_index(result->type, "fields", 1); + ensure_field_index(result->type, "fields", 2); ZigType *type_info_struct_field_type = ir_type_info_get_type(ira, "StructField", nullptr); if ((err = type_resolve(g, type_info_struct_field_type, ResolveStatusSizeKnown))) { @@ -18663,7 +18672,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, Scope *scope, AstNode *sour struct_field_array->data.x_array.special = ConstArraySpecialNone; struct_field_array->data.x_array.data.s_none.elements = g->pass1_arena->allocate(struct_field_count); - init_const_slice(g, fields[1], struct_field_array, 0, struct_field_count, false, nullptr); + init_const_slice(g, fields[2], struct_field_array, 0, struct_field_count, false, nullptr); for (uint32_t struct_field_index = 0; struct_field_index < struct_field_count; struct_field_index++) { TypeStructField *struct_field = type_entry->data.structure.fields[struct_field_index]; @@ -18710,18 +18719,18 @@ static Error ir_make_type_info_value(IrAnalyze *ira, Scope *scope, AstNode *sour struct_field_val->parent.data.p_array.elem_index = struct_field_index; } // decls: []Type.Declaration - ensure_field_index(result->type, "decls", 2); - if ((err = ir_make_type_info_decls(ira, source_node, fields[2], + ensure_field_index(result->type, "decls", 3); + if ((err = ir_make_type_info_decls(ira, source_node, fields[3], type_entry->data.structure.decls_scope, false))) { return err; } // is_tuple: bool - ensure_field_index(result->type, "is_tuple", 3); - fields[3]->special = ConstValSpecialStatic; - fields[3]->type = g->builtin_types.entry_bool; - fields[3]->data.x_bool = is_tuple(type_entry); + ensure_field_index(result->type, "is_tuple", 4); + fields[4]->special = ConstValSpecialStatic; + fields[4]->type = g->builtin_types.entry_bool; + fields[4]->data.x_bool = is_tuple(type_entry); break; } @@ -19313,7 +19322,14 @@ static ZigType *type_info_to_type(IrAnalyze *ira, Scope *scope, AstNode *source_ assert(layout_value->type == ir_type_info_get_type(ira, "ContainerLayout", nullptr)); ContainerLayout layout = (ContainerLayout)bigint_as_u32(&layout_value->data.x_enum_tag); - ZigValue *fields_value = get_const_field(ira, source_node, payload, "fields", 1); + ZigType *tag_type = get_const_field_meta_type_optional(ira, source_node, payload, "backing_integer", 1); + if (tag_type != nullptr) { + ir_add_error_node(ira, source_node, buf_create_from_str( + "the stage1 compiler does not support explicit backing integer types on packed structs")); + return ira->codegen->invalid_inst_gen->value->type; + } + + ZigValue *fields_value = get_const_field(ira, source_node, payload, "fields", 2); if (fields_value == nullptr) return ira->codegen->invalid_inst_gen->value->type; assert(fields_value->special == ConstValSpecialStatic); @@ -19322,7 +19338,7 @@ static ZigType *type_info_to_type(IrAnalyze *ira, Scope *scope, AstNode *source_ ZigValue *fields_len_value = fields_value->data.x_struct.fields[slice_len_index]; size_t fields_len = bigint_as_usize(&fields_len_value->data.x_bigint); - ZigValue *decls_value = get_const_field(ira, source_node, payload, "decls", 2); + ZigValue *decls_value = get_const_field(ira, source_node, payload, "decls", 3); if (decls_value == nullptr) return ira->codegen->invalid_inst_gen->value->type; assert(decls_value->special == ConstValSpecialStatic); @@ -19335,7 +19351,7 @@ static ZigType *type_info_to_type(IrAnalyze *ira, Scope *scope, AstNode *source_ } bool is_tuple; - if ((err = get_const_field_bool(ira, source_node, payload, "is_tuple", 3, &is_tuple))) + if ((err = get_const_field_bool(ira, source_node, payload, "is_tuple", 4, &is_tuple))) return ira->codegen->invalid_inst_gen->value->type; ZigType *entry = new_type_table_entry(ZigTypeIdStruct); diff --git a/src/stage1/parser.cpp b/src/stage1/parser.cpp index fdc0777aff..bd778484cb 100644 --- a/src/stage1/parser.cpp +++ b/src/stage1/parser.cpp @@ -2902,16 +2902,25 @@ static AstNode *ast_parse_container_decl_auto(ParseContext *pc) { } // ContainerDeclType -// <- KEYWORD_struct +// <- KEYWORD_struct (LPAREN Expr RPAREN)? // / KEYWORD_enum (LPAREN Expr RPAREN)? // / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)? // / KEYWORD_opaque static AstNode *ast_parse_container_decl_type(ParseContext *pc) { TokenIndex first = eat_token_if(pc, TokenIdKeywordStruct); if (first != 0) { + bool explicit_backing_int = false; + if (eat_token_if(pc, TokenIdLParen) != 0) { + explicit_backing_int = true; + ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdRParen); + } AstNode *res = ast_create_node(pc, NodeTypeContainerDecl, first); res->data.container_decl.init_arg_expr = nullptr; res->data.container_decl.kind = ContainerKindStruct; + // We want this to be an error in semantic analysis not parsing to make sharing + // the test suite between stage1 and self hosted easier. + res->data.container_decl.unsupported_explicit_backing_int = explicit_backing_int; return res; } diff --git a/src/type.zig b/src/type.zig index 85d77303c9..1b71f4e9b1 100644 --- a/src/type.zig +++ b/src/type.zig @@ -3000,9 +3000,17 @@ pub const Type = extern union { .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) }, }; if (struct_obj.layout == .Packed) { - var buf: Type.Payload.Bits = undefined; - const int_ty = struct_obj.packedIntegerType(target, &buf); - return AbiAlignmentAdvanced{ .scalar = int_ty.abiAlignment(target) }; + switch (strat) { + .sema_kit => |sk| try sk.sema.resolveTypeLayout(sk.block, sk.src, ty), + .lazy => |arena| { + if (!struct_obj.haveLayout()) { + return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) }; + } + }, + .eager => {}, + } + assert(struct_obj.haveLayout()); + return AbiAlignmentAdvanced{ .scalar = struct_obj.backing_int_ty.abiAlignment(target) }; } const fields = ty.structFields(); @@ -3192,17 +3200,16 @@ pub const Type = extern union { .Packed => { const struct_obj = ty.castTag(.@"struct").?.data; switch (strat) { - .sema_kit => |sk| _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty), + .sema_kit => |sk| try sk.sema.resolveTypeLayout(sk.block, sk.src, ty), .lazy => |arena| { - if (!struct_obj.haveFieldTypes()) { + if (!struct_obj.haveLayout()) { return AbiSizeAdvanced{ .val = try Value.Tag.lazy_size.create(arena, ty) }; } }, .eager => {}, } - var buf: Type.Payload.Bits = undefined; - const int_ty = struct_obj.packedIntegerType(target, &buf); - return AbiSizeAdvanced{ .scalar = int_ty.abiSize(target) }; + assert(struct_obj.haveLayout()); + return AbiSizeAdvanced{ .scalar = struct_obj.backing_int_ty.abiSize(target) }; }, else => { switch (strat) { diff --git a/test/behavior.zig b/test/behavior.zig index fee61f5e09..e9c4ec779b 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -165,6 +165,7 @@ test { if (builtin.zig_backend != .stage1) { _ = @import("behavior/decltest.zig"); + _ = @import("behavior/packed_struct_explicit_backing_int.zig"); } if (builtin.os.tag != .wasi) { diff --git a/test/behavior/packed_struct_explicit_backing_int.zig b/test/behavior/packed_struct_explicit_backing_int.zig new file mode 100644 index 0000000000..165e94fd4e --- /dev/null +++ b/test/behavior/packed_struct_explicit_backing_int.zig @@ -0,0 +1,53 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const assert = std.debug.assert; +const expectEqual = std.testing.expectEqual; +const native_endian = builtin.cpu.arch.endian(); + +test "packed struct explicit backing integer" { + assert(builtin.zig_backend != .stage1); + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S1 = packed struct { a: u8, b: u8, c: u8 }; + + const S2 = packed struct(i24) { d: u8, e: u8, f: u8 }; + + const S3 = packed struct { x: S1, y: S2 }; + const S3Padded = packed struct(u64) { s3: S3, pad: u16 }; + + try expectEqual(48, @bitSizeOf(S3)); + try expectEqual(@sizeOf(u48), @sizeOf(S3)); + + try expectEqual(3, @offsetOf(S3, "y")); + try expectEqual(24, @bitOffsetOf(S3, "y")); + + if (native_endian == .Little) { + const s3 = @bitCast(S3Padded, @as(u64, 0xe952d5c71ff4)).s3; + try expectEqual(@as(u8, 0xf4), s3.x.a); + try expectEqual(@as(u8, 0x1f), s3.x.b); + try expectEqual(@as(u8, 0xc7), s3.x.c); + try expectEqual(@as(u8, 0xd5), s3.y.d); + try expectEqual(@as(u8, 0x52), s3.y.e); + try expectEqual(@as(u8, 0xe9), s3.y.f); + } + + const S4 = packed struct { a: i32, b: i8 }; + const S5 = packed struct(u80) { a: i32, b: i8, c: S4 }; + const S6 = packed struct(i80) { a: i32, b: S4, c: i8 }; + + const expectedBitSize = 80; + const expectedByteSize = @sizeOf(u80); + try expectEqual(expectedBitSize, @bitSizeOf(S5)); + try expectEqual(expectedByteSize, @sizeOf(S5)); + try expectEqual(expectedBitSize, @bitSizeOf(S6)); + try expectEqual(expectedByteSize, @sizeOf(S6)); + + try expectEqual(5, @offsetOf(S5, "c")); + try expectEqual(40, @bitOffsetOf(S5, "c")); + try expectEqual(9, @offsetOf(S6, "c")); + try expectEqual(72, @bitOffsetOf(S6, "c")); +} diff --git a/test/behavior/type_info.zig b/test/behavior/type_info.zig index b1012e69c8..968c3e7490 100644 --- a/test/behavior/type_info.zig +++ b/test/behavior/type_info.zig @@ -293,6 +293,7 @@ test "type info: struct info" { fn testStruct() !void { const unpacked_struct_info = @typeInfo(TestStruct); try expect(unpacked_struct_info.Struct.is_tuple == false); + try expect(unpacked_struct_info.Struct.backing_integer == null); try expect(unpacked_struct_info.Struct.fields[0].alignment == @alignOf(u32)); try expect(@ptrCast(*const u32, unpacked_struct_info.Struct.fields[0].default_value.?).* == 4); try expect(mem.eql(u8, "foobar", @ptrCast(*const *const [6:0]u8, unpacked_struct_info.Struct.fields[1].default_value.?).*)); @@ -315,6 +316,7 @@ fn testPackedStruct() !void { try expect(struct_info == .Struct); try expect(struct_info.Struct.is_tuple == false); try expect(struct_info.Struct.layout == .Packed); + try expect(struct_info.Struct.backing_integer == u128); try expect(struct_info.Struct.fields.len == 4); try expect(struct_info.Struct.fields[0].alignment == 0); try expect(struct_info.Struct.fields[2].field_type == f32); @@ -326,7 +328,7 @@ fn testPackedStruct() !void { } const TestPackedStruct = packed struct { - fieldA: usize, + fieldA: u64, fieldB: void, fieldC: f32, fieldD: u32 = 4, diff --git a/test/cases/compile_errors/packed_struct_backing_int_wrong.zig b/test/cases/compile_errors/packed_struct_backing_int_wrong.zig new file mode 100644 index 0000000000..cd1b4ec11c --- /dev/null +++ b/test/cases/compile_errors/packed_struct_backing_int_wrong.zig @@ -0,0 +1,55 @@ +export fn entry1() void { + _ = @sizeOf(packed struct(u32) { + x: u1, + y: u24, + z: u4, + }); +} +export fn entry2() void { + _ = @sizeOf(packed struct(i31) { + x: u4, + y: u24, + z: u4, + }); +} + +export fn entry3() void { + _ = @sizeOf(packed struct(void) { + x: void, + }); +} + +export fn entry4() void { + _ = @sizeOf(packed struct(void) {}); +} + +export fn entry5() void { + _ = @sizeOf(packed struct(noreturn) {}); +} + +export fn entry6() void { + _ = @sizeOf(packed struct(f64) { + x: u32, + y: f32, + }); +} + +export fn entry7() void { + _ = @sizeOf(packed struct(*u32) { + x: u4, + y: u24, + z: u4, + }); +} + +// error +// backend=llvm +// target=native +// +// :2:31: error: backing integer type 'u32' has bit size 32 but the struct fields have a total bit size of 29 +// :9:31: error: backing integer type 'i31' has bit size 31 but the struct fields have a total bit size of 32 +// :17:31: error: expected backing integer type, found 'void' +// :23:31: error: expected backing integer type, found 'void' +// :27:31: error: expected backing integer type, found 'noreturn' +// :31:31: error: expected backing integer type, found 'f64' +// :38:31: error: expected backing integer type, found '*u32' -- cgit v1.2.3 From 933436dc52b9be0a3e5d81b014fba6df2124fe20 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 10 Aug 2022 20:39:57 -0700 Subject: stage2: remove destroyed functions from maps This is likely the cause of the flaky test failures in master branch. Since we have some test coverage for incremental compilation, it's not OK to leave proper memory management of Fn objects as "TODO". --- src/Module.zig | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src/Module.zig') diff --git a/src/Module.zig b/src/Module.zig index 7e877a2f4a..995fdda7ea 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -84,7 +84,6 @@ string_literal_bytes: std.ArrayListUnmanaged(u8) = .{}, /// The set of all the generic function instantiations. This is used so that when a generic /// function is called twice with the same comptime parameter arguments, both calls dispatch /// to the same function. -/// TODO: remove functions from this set when they are destroyed. monomorphed_funcs: MonomorphedFuncsSet = .{}, /// The set of all comptime function calls that have been cached so that future calls /// with the same parameters will get the same return value. @@ -92,7 +91,6 @@ memoized_calls: MemoizedCallSet = .{}, /// Contains the values from `@setAlignStack`. A sparse table is used here /// instead of a field of `Fn` because usage of `@setAlignStack` is rare, while /// functions are many. -/// TODO: remove functions from this set when they are destroyed. align_stack_fns: std.AutoHashMapUnmanaged(*const Fn, SetAlignStack) = .{}, /// We optimize memory usage for a compilation with no compile errors by storing the @@ -560,6 +558,8 @@ pub const Decl = struct { gpa.destroy(extern_fn); } if (decl.getFunction()) |func| { + _ = mod.align_stack_fns.remove(func); + _ = mod.monomorphed_funcs.remove(func); func.deinit(gpa); gpa.destroy(func); } @@ -4094,6 +4094,12 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void { // The exports this Decl performs will be re-discovered, so we remove them here // prior to re-analysis. mod.deleteDeclExports(decl_index); + + // Similarly, `@setAlignStack` invocations will be re-discovered. + if (decl.getFunction()) |func| { + _ = mod.align_stack_fns.remove(func); + } + // Dependencies will be re-discovered, so we remove them here prior to re-analysis. for (decl.dependencies.keys()) |dep_index| { const dep = mod.declPtr(dep_index); -- cgit v1.2.3 From 6e313eb1107d4f5d7b0ada0a67c810ce90e79bf5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 16 Jul 2022 16:27:37 -0700 Subject: stage2: agree with LLVM that `@alignOf(u128)` is 8 on x86_64 and similar targets. --- lib/std/target.zig | 7 ++++--- src/Module.zig | 30 +++++++++++++++++++++++++----- src/Sema.zig | 5 +---- src/codegen/llvm.zig | 14 ++++++++------ src/type.zig | 13 +++++++++++-- test/behavior/align.zig | 29 +++++++++++++++++++++-------- 6 files changed, 70 insertions(+), 28 deletions(-) (limited to 'src/Module.zig') diff --git a/lib/std/target.zig b/lib/std/target.zig index f77f73bce0..155c59bdfc 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1806,9 +1806,9 @@ pub const Target = struct { else => 4, }, - // For x86_64, LLVMABIAlignmentOfType(i128) reports 8. However I think 16 - // is a better number for two reasons: - // 1. Better machine code when loading into SIMD register. + // For these, LLVMABIAlignmentOfType(i128) reports 8. Note that 16 + // is a relevant number in three cases: + // 1. Different machine code instruction when loading into SIMD register. // 2. The C ABI wants 16 for extern structs. // 3. 16-byte cmpxchg needs 16-byte alignment. // Same logic for riscv64, powerpc64, mips64, sparc64. @@ -1819,6 +1819,7 @@ pub const Target = struct { .mips64, .mips64el, .sparc64, + => 8, // Even LLVMABIAlignmentOfType(i128) agrees on these targets. .aarch64, diff --git a/src/Module.zig b/src/Module.zig index 995fdda7ea..6d2180e8e7 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -935,13 +935,33 @@ pub const Struct = struct { /// If true then `default_val` is the comptime field value. is_comptime: bool, - /// Returns the field alignment, assuming the struct is not packed. - pub fn normalAlignment(field: Field, target: Target) u32 { - if (field.abi_align == 0) { - return field.ty.abiAlignment(target); - } else { + /// Returns the field alignment. If the struct is packed, returns 0. + pub fn alignment( + field: Field, + target: Target, + layout: std.builtin.Type.ContainerLayout, + ) u32 { + if (field.abi_align != 0) { + assert(layout != .Packed); return field.abi_align; } + + switch (layout) { + .Packed => return 0, + .Auto => return field.ty.abiAlignment(target), + .Extern => { + // This logic is duplicated in Type.abiAlignmentAdvanced. + const ty_abi_align = field.ty.abiAlignment(target); + + if (field.ty.isAbiInt() and field.ty.intInfo(target).bits >= 128) { + // The C ABI requires 128 bit integer fields of structs + // to be 16-bytes aligned. + return @maximum(ty_abi_align, 16); + } + + return ty_abi_align; + }, + } } }; diff --git a/src/Sema.zig b/src/Sema.zig index d6ac3d0276..32b95a4c21 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -14380,10 +14380,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai else field.default_val; const default_val_ptr = try sema.optRefValue(block, src, field.ty, opt_default_val); - const alignment = switch (layout) { - .Auto, .Extern => field.normalAlignment(target), - .Packed => 0, - }; + const alignment = field.alignment(target, layout); struct_field_fields.* = .{ // name: []const u8, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 0586c99432..a9a343439d 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1841,6 +1841,7 @@ pub const Object = struct { } const fields = ty.structFields(); + const layout = ty.containerLayout(); var di_fields: std.ArrayListUnmanaged(*llvm.DIType) = .{}; defer di_fields.deinit(gpa); @@ -1854,7 +1855,7 @@ pub const Object = struct { if (field.is_comptime or !field.ty.hasRuntimeBitsIgnoreComptime()) continue; const field_size = field.ty.abiSize(target); - const field_align = field.normalAlignment(target); + const field_align = field.alignment(target, layout); const field_offset = std.mem.alignForwardGeneric(u64, offset, field_align); offset = field_offset + field_size; @@ -2499,7 +2500,7 @@ pub const DeclGen = struct { fn lowerType(dg: *DeclGen, t: Type) Allocator.Error!*const llvm.Type { const llvm_ty = try lowerTypeInner(dg, t); - if (std.debug.runtime_safety and false) check: { + if (std.debug.runtime_safety) check: { if (t.zigTypeTag() == .Opaque) break :check; if (!t.hasRuntimeBits()) break :check; if (!llvm_ty.isSized().toBool()) break :check; @@ -2757,7 +2758,7 @@ pub const DeclGen = struct { for (struct_obj.fields.values()) |field| { if (field.is_comptime or !field.ty.hasRuntimeBitsIgnoreComptime()) continue; - const field_align = field.normalAlignment(target); + const field_align = field.alignment(target, struct_obj.layout); const field_ty_align = field.ty.abiAlignment(target); any_underaligned_fields = any_underaligned_fields or field_align < field_ty_align; @@ -3433,7 +3434,7 @@ pub const DeclGen = struct { for (struct_obj.fields.values()) |field, i| { if (field.is_comptime or !field.ty.hasRuntimeBitsIgnoreComptime()) continue; - const field_align = field.normalAlignment(target); + const field_align = field.alignment(target, struct_obj.layout); big_align = @maximum(big_align, field_align); const prev_offset = offset; offset = std.mem.alignForwardGeneric(u64, offset, field_align); @@ -9376,13 +9377,14 @@ fn llvmFieldIndex( } return null; } - assert(ty.containerLayout() != .Packed); + const layout = ty.containerLayout(); + assert(layout != .Packed); var llvm_field_index: c_uint = 0; for (ty.structFields().values()) |field, i| { if (field.is_comptime or !field.ty.hasRuntimeBitsIgnoreComptime()) continue; - const field_align = field.normalAlignment(target); + const field_align = field.alignment(target, layout); big_align = @maximum(big_align, field_align); const prev_offset = offset; offset = std.mem.alignForwardGeneric(u64, offset, field_align); diff --git a/src/type.zig b/src/type.zig index 700f524556..4432ae9cf3 100644 --- a/src/type.zig +++ b/src/type.zig @@ -3017,6 +3017,15 @@ pub const Type = extern union { }, }; big_align = @maximum(big_align, field_align); + + // This logic is duplicated in Module.Struct.Field.alignment. + if (struct_obj.layout == .Extern) { + if (field.ty.isAbiInt() and field.ty.intInfo(target).bits >= 128) { + // The C ABI requires 128 bit integer fields of structs + // to be 16-bytes aligned. + big_align = @maximum(big_align, 16); + } + } } return AbiAlignmentAdvanced{ .scalar = big_align }; }, @@ -5490,7 +5499,7 @@ pub const Type = extern union { .@"struct" => { const struct_obj = ty.castTag(.@"struct").?.data; assert(struct_obj.layout != .Packed); - return struct_obj.fields.values()[index].normalAlignment(target); + return struct_obj.fields.values()[index].alignment(target, struct_obj.layout); }, .@"union", .union_safety_tagged, .union_tagged => { const union_obj = ty.cast(Payload.Union).?.data; @@ -5597,7 +5606,7 @@ pub const Type = extern union { if (!field.ty.hasRuntimeBits() or field.is_comptime) return FieldOffset{ .field = it.field, .offset = it.offset }; - const field_align = field.normalAlignment(it.target); + const field_align = field.alignment(it.target, it.struct_obj.layout); it.big_align = @maximum(it.big_align, field_align); it.offset = std.mem.alignForwardGeneric(u64, it.offset, field_align); defer it.offset += field.ty.abiSize(it.target); diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 26e3d91373..4a824bc9cf 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -143,6 +143,19 @@ test "alignment and size of structs with 128-bit fields" { .riscv64, .sparc64, .x86_64, + => .{ + .a_align = 8, + .a_size = 16, + + .b_align = 16, + .b_size = 32, + + .u128_align = 8, + .u128_size = 16, + .u129_align = 8, + .u129_size = 24, + }, + .aarch64, .aarch64_be, .aarch64_32, @@ -166,17 +179,17 @@ test "alignment and size of structs with 128-bit fields" { else => return error.SkipZigTest, }; comptime { - std.debug.assert(@alignOf(A) == expected.a_align); - std.debug.assert(@sizeOf(A) == expected.a_size); + assert(@alignOf(A) == expected.a_align); + assert(@sizeOf(A) == expected.a_size); - std.debug.assert(@alignOf(B) == expected.b_align); - std.debug.assert(@sizeOf(B) == expected.b_size); + assert(@alignOf(B) == expected.b_align); + assert(@sizeOf(B) == expected.b_size); - std.debug.assert(@alignOf(u128) == expected.u128_align); - std.debug.assert(@sizeOf(u128) == expected.u128_size); + assert(@alignOf(u128) == expected.u128_align); + assert(@sizeOf(u128) == expected.u128_size); - std.debug.assert(@alignOf(u129) == expected.u129_align); - std.debug.assert(@sizeOf(u129) == expected.u129_size); + assert(@alignOf(u129) == expected.u129_align); + assert(@sizeOf(u129) == expected.u129_size); } } -- cgit v1.2.3 From cee82c7ce4fb8beb39042f9dba16a1a391803fa4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 18 Aug 2022 20:34:36 -0700 Subject: improved ABI alignment/size for >= 128-bit integers * riscv64: adjust alignment and size of 128-bit integers. * take ofmt=c into account for ABI alignment of 128-bit integers and structs. * Type: make packed struct support intInfo * fix f80 alignment for i386-windows-msvc --- lib/std/target.zig | 9 ++++++--- src/Module.zig | 30 +++++++++++++++++++----------- src/type.zig | 16 ++++++++++++++-- test/behavior/align.zig | 46 ++++++++++++++++++++++++++++++---------------- 4 files changed, 69 insertions(+), 32 deletions(-) (limited to 'src/Module.zig') diff --git a/lib/std/target.zig b/lib/std/target.zig index 00553bb520..64f9f97809 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1808,20 +1808,23 @@ pub const Target = struct { // 1. Different machine code instruction when loading into SIMD register. // 2. The C ABI wants 16 for extern structs. // 3. 16-byte cmpxchg needs 16-byte alignment. - // Same logic for riscv64, powerpc64, mips64, sparc64. + // Same logic for powerpc64, mips64, sparc64. .x86_64, - .riscv64, .powerpc64, .powerpc64le, .mips64, .mips64el, .sparc64, - => 8, + => return switch (target.ofmt) { + .c => 16, + else => 8, + }, // Even LLVMABIAlignmentOfType(i128) agrees on these targets. .aarch64, .aarch64_be, .aarch64_32, + .riscv64, .bpfel, .bpfeb, .nvptx, diff --git a/src/Module.zig b/src/Module.zig index 6d2180e8e7..3ae61c264f 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -948,20 +948,28 @@ pub const Struct = struct { switch (layout) { .Packed => return 0, - .Auto => return field.ty.abiAlignment(target), - .Extern => { - // This logic is duplicated in Type.abiAlignmentAdvanced. - const ty_abi_align = field.ty.abiAlignment(target); - - if (field.ty.isAbiInt() and field.ty.intInfo(target).bits >= 128) { - // The C ABI requires 128 bit integer fields of structs - // to be 16-bytes aligned. - return @maximum(ty_abi_align, 16); + .Auto => { + if (target.ofmt == .c) { + return alignmentExtern(field, target); + } else { + return field.ty.abiAlignment(target); } - - return ty_abi_align; }, + .Extern => return alignmentExtern(field, target), + } + } + + pub fn alignmentExtern(field: Field, target: Target) u32 { + // This logic is duplicated in Type.abiAlignmentAdvanced. + const ty_abi_align = field.ty.abiAlignment(target); + + if (field.ty.isAbiInt() and field.ty.intInfo(target).bits >= 128) { + // The C ABI requires 128 bit integer fields of structs + // to be 16-bytes aligned. + return @maximum(ty_abi_align, 16); } + + return ty_abi_align; } }; diff --git a/src/type.zig b/src/type.zig index fb2ca1e3fc..6a66fb9d70 100644 --- a/src/type.zig +++ b/src/type.zig @@ -3019,7 +3019,7 @@ pub const Type = extern union { big_align = @maximum(big_align, field_align); // This logic is duplicated in Module.Struct.Field.alignment. - if (struct_obj.layout == .Extern) { + if (struct_obj.layout == .Extern or target.ofmt == .c) { if (field.ty.isAbiInt() and field.ty.intInfo(target).bits >= 128) { // The C ABI requires 128 bit integer fields of structs // to be 16-bytes aligned. @@ -3348,7 +3348,13 @@ pub const Type = extern union { .f128 => return AbiSizeAdvanced{ .scalar = 16 }, .f80 => switch (target.cpu.arch) { - .i386 => return AbiSizeAdvanced{ .scalar = 12 }, + .i386 => switch (target.os.tag) { + .windows => switch (target.abi) { + .msvc => return AbiSizeAdvanced{ .scalar = 16 }, + else => return AbiSizeAdvanced{ .scalar = 12 }, + }, + else => return AbiSizeAdvanced{ .scalar = 12 }, + }, .x86_64 => return AbiSizeAdvanced{ .scalar = 16 }, else => { var payload: Payload.Bits = .{ @@ -4559,6 +4565,12 @@ pub const Type = extern union { .vector => ty = ty.castTag(.vector).?.data.elem_type, + .@"struct" => { + const struct_obj = ty.castTag(.@"struct").?.data; + assert(struct_obj.layout == .Packed); + ty = struct_obj.backing_int_ty; + }, + else => unreachable, }; } diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 4a824bc9cf..ad857fb9c2 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -100,8 +100,8 @@ test "alignment and size of structs with 128-bit fields" { .a_align = 8, .a_size = 16, - .b_align = 8, - .b_size = 24, + .b_align = 16, + .b_size = 32, .u128_align = 8, .u128_size = 16, @@ -114,8 +114,8 @@ test "alignment and size of structs with 128-bit fields" { .a_align = 8, .a_size = 16, - .b_align = 8, - .b_size = 24, + .b_align = 16, + .b_size = 32, .u128_align = 8, .u128_size = 16, @@ -126,8 +126,8 @@ test "alignment and size of structs with 128-bit fields" { .a_align = 4, .a_size = 16, - .b_align = 4, - .b_size = 20, + .b_align = 16, + .b_size = 32, .u128_align = 4, .u128_size = 16, @@ -140,25 +140,39 @@ test "alignment and size of structs with 128-bit fields" { .mips64el, .powerpc64, .powerpc64le, - .riscv64, .sparc64, .x86_64, - => .{ - .a_align = 8, - .a_size = 16, + => switch (builtin.object_format) { + .c => .{ + .a_align = 16, + .a_size = 16, - .b_align = 16, - .b_size = 32, + .b_align = 16, + .b_size = 32, - .u128_align = 8, - .u128_size = 16, - .u129_align = 8, - .u129_size = 24, + .u128_align = 16, + .u128_size = 16, + .u129_align = 16, + .u129_size = 32, + }, + else => .{ + .a_align = 8, + .a_size = 16, + + .b_align = 16, + .b_size = 32, + + .u128_align = 8, + .u128_size = 16, + .u129_align = 8, + .u129_size = 24, + }, }, .aarch64, .aarch64_be, .aarch64_32, + .riscv64, .bpfel, .bpfeb, .nvptx, -- cgit v1.2.3 From 437311756d88984d9bf3057d9cd93986c15434fa Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 18 Aug 2022 10:49:09 -0700 Subject: LLVM: add DLL export attribute This was present in stage1 but missing from self-hosted. --- src/Compilation.zig | 2 +- src/Module.zig | 4 ++++ src/codegen/llvm.zig | 3 +++ src/codegen/llvm/bindings.zig | 9 +++++++++ 4 files changed, 17 insertions(+), 1 deletion(-) (limited to 'src/Module.zig') diff --git a/src/Compilation.zig b/src/Compilation.zig index 84dd273947..0914685c77 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1231,7 +1231,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { break :blk lm; } else default_link_mode; - const dll_export_fns = if (options.dll_export_fns) |explicit| explicit else is_dyn_lib or options.rdynamic; + const dll_export_fns = options.dll_export_fns orelse (is_dyn_lib or options.rdynamic); const libc_dirs = try detectLibCIncludeDirs( arena, diff --git a/src/Module.zig b/src/Module.zig index 3ae61c264f..3577115ded 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -6529,3 +6529,7 @@ pub fn addGlobalAssembly(mod: *Module, decl_index: Decl.Index, source: []const u mod.global_assembly.putAssumeCapacityNoClobber(decl_index, duped_source); } + +pub fn wantDllExports(mod: Module) bool { + return mod.comp.bin_file.options.dll_export_fns and mod.getTarget().os.tag == .windows; +} diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 0898c8fe87..d50b463606 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1103,6 +1103,7 @@ pub const Object = struct { } llvm_global.setUnnamedAddr(.False); llvm_global.setLinkage(.External); + if (module.wantDllExports()) llvm_global.setDLLStorageClass(.Default); if (self.di_map.get(decl)) |di_node| { if (try decl.isFunction()) { const di_func = @ptrCast(*llvm.DISubprogram, di_node); @@ -1128,6 +1129,7 @@ pub const Object = struct { const exp_name = exports[0].options.name; llvm_global.setValueName2(exp_name.ptr, exp_name.len); llvm_global.setUnnamedAddr(.False); + if (module.wantDllExports()) llvm_global.setDLLStorageClass(.DLLExport); if (self.di_map.get(decl)) |di_node| { if (try decl.isFunction()) { const di_func = @ptrCast(*llvm.DISubprogram, di_node); @@ -1187,6 +1189,7 @@ pub const Object = struct { defer module.gpa.free(fqn); llvm_global.setValueName2(fqn.ptr, fqn.len); llvm_global.setLinkage(.Internal); + if (module.wantDllExports()) llvm_global.setDLLStorageClass(.Default); llvm_global.setUnnamedAddr(.True); if (decl.val.castTag(.variable)) |variable| { const single_threaded = module.comp.bin_file.options.single_threaded; diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index 9daa96eb8f..38f794cfda 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -223,6 +223,9 @@ pub const Value = opaque { pub const setInitializer = LLVMSetInitializer; extern fn LLVMSetInitializer(GlobalVar: *const Value, ConstantVal: *const Value) void; + pub const setDLLStorageClass = LLVMSetDLLStorageClass; + extern fn LLVMSetDLLStorageClass(Global: *const Value, Class: DLLStorageClass) void; + pub const addCase = LLVMAddCase; extern fn LLVMAddCase(Switch: *const Value, OnVal: *const Value, Dest: *const BasicBlock) void; @@ -1482,6 +1485,12 @@ pub const CallAttr = enum(c_int) { AlwaysInline, }; +pub const DLLStorageClass = enum(c_uint) { + Default, + DLLImport, + DLLExport, +}; + pub const address_space = struct { pub const default: c_uint = 0; -- cgit v1.2.3 From b55a5007faad1de054e86e00bfdc9a58e5fc4ff8 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 21 Aug 2022 18:04:46 +0300 Subject: Sema: fix parameter of type 'T' must be comptime error Closes #12519 Closes #12505 --- doc/langref.html.in | 4 ++-- lib/std/fs/path.zig | 2 +- lib/std/math.zig | 2 +- lib/std/math/float.zig | 2 +- lib/std/zig/c_translation.zig | 2 +- lib/std/zig/parse.zig | 2 +- src/Module.zig | 8 ++++---- src/Sema.zig | 18 +++++++++++------ src/type.zig | 2 +- test/behavior/cast.zig | 2 +- test/behavior/error.zig | 2 +- test/behavior/fn.zig | 2 +- test/behavior/struct.zig | 2 +- .../comptime_parameter_not_declared_as_such.zig | 23 ++++++++++++++++++++++ 14 files changed, 51 insertions(+), 22 deletions(-) create mode 100644 test/cases/compile_errors/comptime_parameter_not_declared_as_such.zig (limited to 'src/Module.zig') diff --git a/doc/langref.html.in b/doc/langref.html.in index 9247d3bc43..04d736609a 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5023,8 +5023,8 @@ fn shiftLeftOne(a: u32) callconv(.Inline) u32 { // Another file can use @import and call sub2 pub fn sub2(a: i8, b: i8) i8 { return a - b; } -// Functions can be used as values and are equivalent to pointers. -const call2_op = fn (a: i8, b: i8) i8; +// Function pointers are prefixed with `*const `. +const call2_op = *const fn (a: i8, b: i8) i8; fn do_op(fn_call: call2_op, op1: i8, op2: i8) i8 { return fn_call(op1, op2); } diff --git a/lib/std/fs/path.zig b/lib/std/fs/path.zig index d5583dcc80..9dc3367688 100644 --- a/lib/std/fs/path.zig +++ b/lib/std/fs/path.zig @@ -42,7 +42,7 @@ pub fn isSep(byte: u8) bool { /// This is different from mem.join in that the separator will not be repeated if /// it is found at the end or beginning of a pair of consecutive paths. -fn joinSepMaybeZ(allocator: Allocator, separator: u8, sepPredicate: fn (u8) bool, paths: []const []const u8, zero: bool) ![]u8 { +fn joinSepMaybeZ(allocator: Allocator, separator: u8, comptime sepPredicate: fn (u8) bool, paths: []const []const u8, zero: bool) ![]u8 { if (paths.len == 0) return if (zero) try allocator.dupe(u8, &[1]u8{0}) else &[0]u8{}; // Find first non-empty path index. diff --git a/lib/std/math.zig b/lib/std/math.zig index 40b5eb9204..1ed9604612 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -1548,7 +1548,7 @@ test "boolMask" { } /// Return the mod of `num` with the smallest integer type -pub fn comptimeMod(num: anytype, denom: comptime_int) IntFittingRange(0, denom - 1) { +pub fn comptimeMod(num: anytype, comptime denom: comptime_int) IntFittingRange(0, denom - 1) { return @intCast(IntFittingRange(0, denom - 1), @mod(num, denom)); } diff --git a/lib/std/math/float.zig b/lib/std/math/float.zig index 30e456fcbd..768cc03285 100644 --- a/lib/std/math/float.zig +++ b/lib/std/math/float.zig @@ -8,7 +8,7 @@ inline fn mantissaOne(comptime T: type) comptime_int { } /// Creates floating point type T from an unbiased exponent and raw mantissa. -inline fn reconstructFloat(comptime T: type, exponent: comptime_int, mantissa: comptime_int) T { +inline fn reconstructFloat(comptime T: type, comptime exponent: comptime_int, comptime mantissa: comptime_int) T { const TBits = @Type(.{ .Int = .{ .signedness = .unsigned, .bits = @bitSizeOf(T) } }); const biased_exponent = @as(TBits, exponent + floatExponentMax(T)); return @bitCast(T, (biased_exponent << floatMantissaBits(T)) | @as(TBits, mantissa)); diff --git a/lib/std/zig/c_translation.zig b/lib/std/zig/c_translation.zig index 8a2086e9ad..348e3a7133 100644 --- a/lib/std/zig/c_translation.zig +++ b/lib/std/zig/c_translation.zig @@ -349,7 +349,7 @@ test "shuffleVectorIndex" { /// Constructs a [*c] pointer with the const and volatile annotations /// from SelfType for pointing to a C flexible array of ElementType. -pub fn FlexibleArrayType(comptime SelfType: type, ElementType: type) type { +pub fn FlexibleArrayType(comptime SelfType: type, comptime ElementType: type) type { switch (@typeInfo(SelfType)) { .Pointer => |ptr| { return @Type(.{ .Pointer = .{ diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index a03764a91c..fda6ad98b9 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -3670,7 +3670,7 @@ const Parser = struct { } /// KEYWORD_if LPAREN Expr RPAREN PtrPayload? Body (KEYWORD_else Payload? Body)? - fn parseIf(p: *Parser, bodyParseFn: fn (p: *Parser) Error!Node.Index) !Node.Index { + fn parseIf(p: *Parser, comptime bodyParseFn: fn (p: *Parser) Error!Node.Index) !Node.Index { const if_token = p.eatToken(.keyword_if) orelse return null_node; _ = try p.expectToken(.l_paren); const condition = try p.expectExpr(); diff --git a/src/Module.zig b/src/Module.zig index 3577115ded..45e0779c54 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -6072,17 +6072,17 @@ pub fn paramSrc( else => unreachable, }; var it = full.iterate(tree); - while (true) { - if (it.param_i == param_i) { - const param = it.next().?; + var i: usize = 0; + while (it.next()) |param| : (i += 1) { + if (i == param_i) { if (param.anytype_ellipsis3) |some| { const main_token = tree.nodes.items(.main_token)[decl.src_node]; return .{ .token_offset_param = @bitCast(i32, some) - @bitCast(i32, main_token) }; } return .{ .node_offset_param = decl.nodeIndexToRelative(param.type_expr) }; } - _ = it.next(); } + unreachable; } pub fn argSrc( diff --git a/src/Sema.zig b/src/Sema.zig index 582299bc9e..a3207b8539 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -76,6 +76,8 @@ types_to_resolve: std.ArrayListUnmanaged(Air.Inst.Ref) = .{}, post_hoc_blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, *LabeledBlock) = .{}, /// Populated with the last compile error created. err: ?*Module.ErrorMsg = null, +/// True when analyzing a generic instantiation. Used to suppress some errors. +is_generic_instantiation: bool = false, const std = @import("std"); const math = std.math; @@ -6495,6 +6497,7 @@ fn instantiateGenericCall( .comptime_args = try new_decl_arena_allocator.alloc(TypedValue, uncasted_args.len), .comptime_args_fn_inst = module_fn.zir_body_inst, .preallocated_new_func = new_module_func, + .is_generic_instantiation = true, }; defer child_sema.deinit(); @@ -7789,6 +7792,7 @@ fn funcCommon( &is_generic, is_extern, cc_workaround, + has_body, ) catch |err| switch (err) { error.NeededSourceLocation => { const decl = sema.mod.declPtr(block.src_decl); @@ -7802,6 +7806,7 @@ fn funcCommon( &is_generic, is_extern, cc_workaround, + has_body, ); return error.AnalysisFail; }, @@ -8005,6 +8010,7 @@ fn analyzeParameter( is_generic: *bool, is_extern: bool, cc: std.builtin.CallingConvention, + has_body: bool, ) !void { const requires_comptime = try sema.typeRequiresComptime(block, param_src, param.ty); comptime_params[i] = param.is_comptime or requires_comptime; @@ -8053,9 +8059,9 @@ fn analyzeParameter( }; return sema.failWithOwnedErrorMsg(msg); } - if (requires_comptime and !param.is_comptime) { + if (!sema.is_generic_instantiation and requires_comptime and !param.is_comptime and has_body) { const msg = msg: { - const msg = try sema.errMsg(block, param_src, "parametter of type '{}' must be declared comptime", .{ + const msg = try sema.errMsg(block, param_src, "parameter of type '{}' must be declared comptime", .{ param.ty.fmt(sema.mod), }); errdefer msg.destroy(sema.gpa); @@ -8153,7 +8159,7 @@ fn zirParam( try block.params.append(sema.gpa, .{ .ty = param_ty, - .is_comptime = is_comptime, + .is_comptime = comptime_syntax, .name = param_name, }); const result = try sema.addConstant(param_ty, Value.initTag(.generic_poison)); @@ -16318,7 +16324,7 @@ fn zirUnaryMath( block: *Block, inst: Zir.Inst.Index, air_tag: Air.Inst.Tag, - eval: fn (Value, Type, Allocator, std.Target) Allocator.Error!Value, + comptime eval: fn (Value, Type, Allocator, std.Target) Allocator.Error!Value, ) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); @@ -17777,7 +17783,7 @@ fn zirBitCount( block: *Block, inst: Zir.Inst.Index, air_tag: Air.Inst.Tag, - comptimeOp: fn (val: Value, ty: Type, target: std.Target) u64, + comptime comptimeOp: fn (val: Value, ty: Type, target: std.Target) u64, ) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); @@ -29491,7 +29497,7 @@ pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ => { const child_ty = ty.childType(); if (child_ty.zigTypeTag() == .Fn) { - return false; + return child_ty.fnInfo().is_generic; } else { return sema.typeRequiresComptime(block, src, child_ty); } diff --git a/src/type.zig b/src/type.zig index d2885f537f..1592dcf469 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2394,7 +2394,7 @@ pub const Type = extern union { if (ignore_comptime_only) { return true; } else if (ty.childType().zigTypeTag() == .Fn) { - return true; + return !ty.childType().fnInfo().is_generic; } else if (sema_kit) |sk| { return !(try sk.sema.typeRequiresComptime(sk.block, sk.src, ty)); } else { diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index ac3a4daeab..fa0877258c 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -1281,7 +1281,7 @@ test "*const [N]null u8 to ?[]const u8" { test "cast between [*c]T and ?[*:0]T on fn parameter" { const S = struct { const Handler = ?fn ([*c]const u8) callconv(.C) void; - fn addCallback(handler: Handler) void { + fn addCallback(comptime handler: Handler) void { _ = handler; } diff --git a/test/behavior/error.zig b/test/behavior/error.zig index 84b18a2738..b355c85819 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -168,7 +168,7 @@ fn entryPtr() void { fooPtr(ptr); } -fn foo2(f: fn () anyerror!void) void { +fn foo2(comptime f: fn () anyerror!void) void { const x = f(); x catch { @panic("fail"); diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig index 044e4ff049..d68cd89210 100644 --- a/test/behavior/fn.zig +++ b/test/behavior/fn.zig @@ -137,7 +137,7 @@ test "implicit cast function unreachable return" { wantsFnWithVoid(fnWithUnreachable); } -fn wantsFnWithVoid(f: fn () void) void { +fn wantsFnWithVoid(comptime f: fn () void) void { _ = f; } diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index eb29c9038d..12c874f8ba 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -147,7 +147,7 @@ test "fn call of struct field" { return 13; } - fn callStructField(foo: Foo) i32 { + fn callStructField(comptime foo: Foo) i32 { return foo.ptr(); } }; diff --git a/test/cases/compile_errors/comptime_parameter_not_declared_as_such.zig b/test/cases/compile_errors/comptime_parameter_not_declared_as_such.zig new file mode 100644 index 0000000000..e4d9eed079 --- /dev/null +++ b/test/cases/compile_errors/comptime_parameter_not_declared_as_such.zig @@ -0,0 +1,23 @@ +fn f(_: anytype) void {} +fn g(h: *const fn (anytype) void) void { + h({}); +} +pub export fn entry() void { + g(f); +} + +pub fn comptimeMod(num: anytype, denom: comptime_int) void { + _ = num; + _ = denom; +} + +pub export fn entry1() void { + _ = comptimeMod(1, 2); +} + +// error +// backend=stage2 +// target=native +// +// :2:6: error: parameter of type '*const fn(anytype) void' must be declared comptime +// :9:34: error: parameter of type 'comptime_int' must be declared comptime -- cgit v1.2.3 From af19909b9cde3d009f0306ac825f39912644bca6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 24 Aug 2022 16:41:10 -0700 Subject: stage2: fix generic function cleanup When removing generic function instantiations from monomorphed_funcs, we need to first make sure the function is generic, otherwise the hash map tries to access the `hash` field of the function which is undefined. closes #12614 --- src/Module.zig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/Module.zig') diff --git a/src/Module.zig b/src/Module.zig index 45e0779c54..34617ed3e2 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -559,7 +559,9 @@ pub const Decl = struct { } if (decl.getFunction()) |func| { _ = mod.align_stack_fns.remove(func); - _ = mod.monomorphed_funcs.remove(func); + if (func.comptime_args != null) { + _ = mod.monomorphed_funcs.remove(func); + } func.deinit(gpa); gpa.destroy(func); } @@ -1478,6 +1480,7 @@ pub const Fn = struct { /// This is important because it may be accessed when resizing monomorphed_funcs /// while this Fn has already been added to the set, but does not have the /// owner_decl, comptime_args, or other fields populated yet. + /// This field is undefined if comptime_args == null. hash: u64, /// Relative to owner Decl. -- cgit v1.2.3 From 7453f56e678c80928ababa2868c69cfe41647fed Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 24 Aug 2022 20:27:11 -0700 Subject: stage2: explicitly tagged enums no longer have one possible value Previously, Zig had inconsistent semantics for an enum like this: `enum(u8){zero = 0}` Although in theory this can only hold one possible value, the tag `zero`, Zig no longer will treat the type this way. It will do loads and stores, as if the type has runtime bits. Closes #12619 Tests passed locally: * test-behavior * test-cases --- lib/std/elf.zig | 14 ++++++------- src/Module.zig | 26 ++++++++++++----------- src/Sema.zig | 7 +++++++ src/arch/x86_64/CodeGen.zig | 4 ++-- src/codegen/llvm.zig | 8 +++---- src/type.zig | 51 +++++++++++++++++++++++++++++---------------- test/behavior.zig | 1 - test/behavior/bugs/1111.zig | 11 ---------- test/behavior/enum.zig | 42 +++++++++++++++++++++++++++++++++++++ test/behavior/union.zig | 9 ++++---- 10 files changed, 113 insertions(+), 60 deletions(-) delete mode 100644 test/behavior/bugs/1111.zig (limited to 'src/Module.zig') diff --git a/lib/std/elf.zig b/lib/std/elf.zig index 16581b7782..cc43e11a7d 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -3,7 +3,7 @@ const io = std.io; const os = std.os; const math = std.math; const mem = std.mem; -const debug = std.debug; +const assert = std.debug.assert; const File = std.fs.File; const native_endian = @import("builtin").target.cpu.arch.endian(); @@ -872,14 +872,14 @@ pub const Elf_MIPS_ABIFlags_v0 = extern struct { }; comptime { - debug.assert(@sizeOf(Elf32_Ehdr) == 52); - debug.assert(@sizeOf(Elf64_Ehdr) == 64); + assert(@sizeOf(Elf32_Ehdr) == 52); + assert(@sizeOf(Elf64_Ehdr) == 64); - debug.assert(@sizeOf(Elf32_Phdr) == 32); - debug.assert(@sizeOf(Elf64_Phdr) == 56); + assert(@sizeOf(Elf32_Phdr) == 32); + assert(@sizeOf(Elf64_Phdr) == 56); - debug.assert(@sizeOf(Elf32_Shdr) == 40); - debug.assert(@sizeOf(Elf64_Shdr) == 64); + assert(@sizeOf(Elf32_Shdr) == 40); + assert(@sizeOf(Elf64_Shdr) == 64); } pub const Auxv = switch (@sizeOf(usize)) { diff --git a/src/Module.zig b/src/Module.zig index 34617ed3e2..a92849e127 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1368,18 +1368,20 @@ pub const Union = struct { } } payload_align = @maximum(payload_align, 1); - if (!have_tag or fields.len <= 1) return .{ - .abi_size = std.mem.alignForwardGeneric(u64, payload_size, payload_align), - .abi_align = payload_align, - .most_aligned_field = most_aligned_field, - .most_aligned_field_size = most_aligned_field_size, - .biggest_field = biggest_field, - .payload_size = payload_size, - .payload_align = payload_align, - .tag_align = 0, - .tag_size = 0, - .padding = 0, - }; + if (!have_tag or !u.tag_ty.hasRuntimeBits()) { + return .{ + .abi_size = std.mem.alignForwardGeneric(u64, payload_size, payload_align), + .abi_align = payload_align, + .most_aligned_field = most_aligned_field, + .most_aligned_field_size = most_aligned_field_size, + .biggest_field = biggest_field, + .payload_size = payload_size, + .payload_align = payload_align, + .tag_align = 0, + .tag_size = 0, + .padding = 0, + }; + } // Put the tag before or after the payload depending on which one's // alignment is greater. const tag_size = u.tag_ty.abiSize(target); diff --git a/src/Sema.zig b/src/Sema.zig index b24a00150a..3e8005269f 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -28844,6 +28844,10 @@ pub fn typeHasOnePossibleValue( .enum_numbered => { const resolved_ty = try sema.resolveTypeFields(block, src, ty); const enum_obj = resolved_ty.castTag(.enum_numbered).?.data; + // An explicit tag type is always provided for enum_numbered. + if (enum_obj.tag_ty.hasRuntimeBits()) { + return null; + } if (enum_obj.fields.count() == 1) { if (enum_obj.values.count() == 0) { return Value.zero; // auto-numbered @@ -28857,6 +28861,9 @@ pub fn typeHasOnePossibleValue( .enum_full => { const resolved_ty = try sema.resolveTypeFields(block, src, ty); const enum_obj = resolved_ty.castTag(.enum_full).?.data; + if (enum_obj.tag_ty.hasRuntimeBits()) { + return null; + } if (enum_obj.fields.count() == 1) { if (enum_obj.values.count() == 0) { return Value.zero; // auto-numbered diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 0f6b6baee3..106d2feec0 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -6524,13 +6524,13 @@ fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void { const extra = self.air.extraData(Air.Block, ty_pl.payload); _ = ty_pl; _ = extra; - return self.fail("TODO implement airCmpxchg for {}", .{self.target.cpu.arch}); + return self.fail("TODO implement x86 airCmpxchg", .{}); // return self.finishAir(inst, result, .{ extra.ptr, extra.expected_value, extra.new_value }); } fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void { _ = inst; - return self.fail("TODO implement airCmpxchg for {}", .{self.target.cpu.arch}); + return self.fail("TODO implement x86 airAtomicRaw", .{}); } fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) !void { diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index cddbdc6822..d872057beb 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1860,7 +1860,7 @@ pub const Object = struct { var offset: u64 = 0; for (fields.values()) |field, i| { - if (field.is_comptime or !field.ty.hasRuntimeBitsIgnoreComptime()) continue; + if (field.is_comptime or !field.ty.hasRuntimeBits()) continue; const field_size = field.ty.abiSize(target); const field_align = field.alignment(target, layout); @@ -2764,7 +2764,7 @@ pub const DeclGen = struct { var any_underaligned_fields = false; for (struct_obj.fields.values()) |field| { - if (field.is_comptime or !field.ty.hasRuntimeBitsIgnoreComptime()) continue; + if (field.is_comptime or !field.ty.hasRuntimeBits()) continue; const field_align = field.alignment(target, struct_obj.layout); const field_ty_align = field.ty.abiAlignment(target); @@ -3443,7 +3443,7 @@ pub const DeclGen = struct { var need_unnamed = false; for (struct_obj.fields.values()) |field, i| { - if (field.is_comptime or !field.ty.hasRuntimeBitsIgnoreComptime()) continue; + if (field.is_comptime or !field.ty.hasRuntimeBits()) continue; const field_align = field.alignment(target, struct_obj.layout); big_align = @maximum(big_align, field_align); @@ -9477,7 +9477,7 @@ fn llvmFieldIndex( var llvm_field_index: c_uint = 0; for (ty.structFields().values()) |field, i| { - if (field.is_comptime or !field.ty.hasRuntimeBitsIgnoreComptime()) continue; + if (field.is_comptime or !field.ty.hasRuntimeBits()) continue; const field_align = field.alignment(target, layout); big_align = @maximum(big_align, field_align); diff --git a/src/type.zig b/src/type.zig index 1592dcf469..cc6e5706ee 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2310,6 +2310,8 @@ pub const Type = extern union { /// fields will count towards the ABI size. For example, `struct {T: type, x: i32}` /// hasRuntimeBits()=true and abiSize()=4 /// * the type has only one possible value, making its ABI size 0. + /// - an enum with an explicit tag type has the ABI size of the integer tag type, + /// making it one-possible-value only if the integer tag type has 0 bits. /// When `ignore_comptime_only` is true, then types that are comptime only /// may return false positives. pub fn hasRuntimeBitsAdvanced( @@ -2452,9 +2454,9 @@ pub const Type = extern union { _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty); } assert(struct_obj.haveFieldTypes()); - for (struct_obj.fields.values()) |value| { - if (value.is_comptime) continue; - if (try value.ty.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit)) + for (struct_obj.fields.values()) |field| { + if (field.is_comptime) continue; + if (try field.ty.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit)) return true; } else { return false; @@ -2463,7 +2465,7 @@ pub const Type = extern union { .enum_full => { const enum_full = ty.castTag(.enum_full).?.data; - return enum_full.fields.count() >= 2; + return enum_full.tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit); }, .enum_simple => { const enum_simple = ty.castTag(.enum_simple).?.data; @@ -2490,9 +2492,10 @@ pub const Type = extern union { }, .union_safety_tagged, .union_tagged => { const union_obj = ty.cast(Payload.Union).?.data; - if (union_obj.fields.count() > 0 and try union_obj.tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit)) { + if (try union_obj.tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit)) { return true; } + if (sema_kit) |sk| { _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty); } @@ -3125,7 +3128,11 @@ pub const Type = extern union { .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) }, }; if (union_obj.fields.count() == 0) { - return AbiAlignmentAdvanced{ .scalar = @boolToInt(union_obj.layout == .Extern) }; + if (have_tag) { + return abiAlignmentAdvanced(union_obj.tag_ty, target, strat); + } else { + return AbiAlignmentAdvanced{ .scalar = @boolToInt(union_obj.layout == .Extern) }; + } } var max_align: u32 = 0; @@ -4991,14 +4998,18 @@ pub const Type = extern union { .enum_numbered => { const enum_numbered = ty.castTag(.enum_numbered).?.data; - if (enum_numbered.fields.count() == 1) { - return enum_numbered.values.keys()[0]; - } else { + // An explicit tag type is always provided for enum_numbered. + if (enum_numbered.tag_ty.hasRuntimeBits()) { return null; } + assert(enum_numbered.fields.count() == 1); + return enum_numbered.values.keys()[0]; }, .enum_full => { const enum_full = ty.castTag(.enum_full).?.data; + if (enum_full.tag_ty.hasRuntimeBits()) { + return null; + } if (enum_full.fields.count() == 1) { if (enum_full.values.count() == 0) { return Value.zero; @@ -5333,7 +5344,8 @@ pub const Type = extern union { .enum_numbered => return ty.castTag(.enum_numbered).?.data.tag_ty, .enum_simple => { const enum_simple = ty.castTag(.enum_simple).?.data; - const bits = std.math.log2_int_ceil(usize, enum_simple.fields.count()); + const field_count = enum_simple.fields.count(); + const bits: u16 = if (field_count == 0) 0 else std.math.log2_int_ceil(usize, field_count); buffer.* = .{ .base = .{ .tag = .int_unsigned }, .data = bits, @@ -5653,19 +5665,22 @@ pub const Type = extern union { target: Target, pub fn next(it: *StructOffsetIterator) ?FieldOffset { - if (it.struct_obj.fields.count() <= it.field) + const i = it.field; + if (it.struct_obj.fields.count() <= i) return null; - const field = it.struct_obj.fields.values()[it.field]; - defer it.field += 1; - if (!field.ty.hasRuntimeBits() or field.is_comptime) - return FieldOffset{ .field = it.field, .offset = it.offset }; + const field = it.struct_obj.fields.values()[i]; + it.field += 1; + + if (field.is_comptime or !field.ty.hasRuntimeBits()) { + return FieldOffset{ .field = i, .offset = it.offset }; + } const field_align = field.alignment(it.target, it.struct_obj.layout); it.big_align = @maximum(it.big_align, field_align); - it.offset = std.mem.alignForwardGeneric(u64, it.offset, field_align); - defer it.offset += field.ty.abiSize(it.target); - return FieldOffset{ .field = it.field, .offset = it.offset }; + const field_offset = std.mem.alignForwardGeneric(u64, it.offset, field_align); + it.offset = field_offset + field.ty.abiSize(it.target); + return FieldOffset{ .field = i, .offset = field_offset }; } }; diff --git a/test/behavior.zig b/test/behavior.zig index ba8379cd72..12edd6f9a3 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -26,7 +26,6 @@ test { _ = @import("behavior/bugs/920.zig"); _ = @import("behavior/bugs/1025.zig"); _ = @import("behavior/bugs/1076.zig"); - _ = @import("behavior/bugs/1111.zig"); _ = @import("behavior/bugs/1277.zig"); _ = @import("behavior/bugs/1310.zig"); _ = @import("behavior/bugs/1381.zig"); diff --git a/test/behavior/bugs/1111.zig b/test/behavior/bugs/1111.zig deleted file mode 100644 index d274befaf3..0000000000 --- a/test/behavior/bugs/1111.zig +++ /dev/null @@ -1,11 +0,0 @@ -const Foo = enum(c_int) { - Bar = -1, -}; - -test "issue 1111 fixed" { - const v = Foo.Bar; - - switch (v) { - Foo.Bar => return, - } -} diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig index 709d30af33..9e96163cc0 100644 --- a/test/behavior/enum.zig +++ b/test/behavior/enum.zig @@ -1,6 +1,7 @@ const builtin = @import("builtin"); const std = @import("std"); const expect = std.testing.expect; +const assert = std.debug.assert; const mem = std.mem; const Tag = std.meta.Tag; @@ -1128,3 +1129,44 @@ test "tag name functions are unique" { _ = a; } } + +test "size of enum with only one tag which has explicit integer tag type" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + + const E = enum(u8) { nope = 10 }; + const S0 = struct { e: E }; + const S1 = extern struct { e: E }; + //const U = union(E) { nope: void }; + comptime assert(@sizeOf(E) == 1); + comptime assert(@sizeOf(S0) == 1); + comptime assert(@sizeOf(S1) == 1); + //comptime assert(@sizeOf(U) == 1); + + var s1: S1 = undefined; + s1.e = .nope; + try expect(s1.e == .nope); + const ptr = @ptrCast(*u8, &s1); + try expect(ptr.* == 10); + + var s0: S0 = undefined; + s0.e = .nope; + try expect(s0.e == .nope); +} + +test "switch on an extern enum with negative value" { + // TODO x86, wasm backends fail because they assume that enum tag types are unsigned + if (@import("builtin").zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (@import("builtin").zig_backend == .stage2_wasm) return error.SkipZigTest; + + const Foo = enum(c_int) { + Bar = -1, + }; + + const v = Foo.Bar; + + switch (v) { + Foo.Bar => return, + } +} diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 92f277b946..e2078c66df 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -1,6 +1,7 @@ const builtin = @import("builtin"); const std = @import("std"); const expect = std.testing.expect; +const assert = std.debug.assert; const expectEqual = std.testing.expectEqual; const Tag = std.meta.Tag; @@ -1065,6 +1066,8 @@ test "@unionInit on union with tag but no fields" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const S = struct { const Type = enum(u8) { no_op = 105 }; @@ -1079,11 +1082,7 @@ test "@unionInit on union with tag but no fields" { }; comptime { - if (builtin.zig_backend == .stage1) { - // stage1 gets the wrong answer here - } else { - std.debug.assert(@sizeOf(Data) == 0); - } + assert(@sizeOf(Data) == 1); } fn doTheTest() !void { -- cgit v1.2.3