authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-08-10 19:13:29-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-08-10 19:13:29-04:00
loge0178890ba5ad76fdf5ba955f479ccf6f05a3d49
tree97388d19f25afa268f68862225a3a1a478feb3f6
parent45c444ff18b43d30a7277e346174ba6eca4a6193
parent0d32b73078aa4579187f7d5c67343a6036eed277
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #12379 from ifreund/packed-struct-explicit-backing-int

stage2: Implement explicit backing integers for packed structs

22 files changed, 565 insertions(+), 113 deletions(-)

lib/std/builtin.zig+2
...@@ -294,6 +294,8 @@ pub const Type = union(enum) {...@@ -294,6 +294,8 @@ pub const Type = union(enum) {
294 /// therefore must be kept in sync with the compiler implementation.294 /// therefore must be kept in sync with the compiler implementation.
295 pub const Struct = struct {295 pub const Struct = struct {
296 layout: ContainerLayout,296 layout: ContainerLayout,
297 /// Only valid if layout is .Packed
298 backing_integer: ?type = null,
297 fields: []const StructField,299 fields: []const StructField,
298 decls: []const Declaration,300 decls: []const Declaration,
299 is_tuple: bool,301 is_tuple: bool,
lib/std/zig/Ast.zig+1-1
...@@ -2967,7 +2967,7 @@ pub const Node = struct {...@@ -2967,7 +2967,7 @@ pub const Node = struct {
2967 /// Same as ContainerDeclTwo except there is known to be a trailing comma2967 /// Same as ContainerDeclTwo except there is known to be a trailing comma
2968 /// or semicolon before the rbrace.2968 /// or semicolon before the rbrace.
2969 container_decl_two_trailing,2969 container_decl_two_trailing,
2970 /// `union(lhs)` / `enum(lhs)`. `SubRange[rhs]`.2970 /// `struct(lhs)` / `union(lhs)` / `enum(lhs)`. `SubRange[rhs]`.
2971 container_decl_arg,2971 container_decl_arg,
2972 /// Same as container_decl_arg but there is known to be a trailing2972 /// Same as container_decl_arg but there is known to be a trailing
2973 /// comma or semicolon before the rbrace.2973 /// comma or semicolon before the rbrace.
lib/std/zig/parse.zig+6-4
...@@ -3356,16 +3356,18 @@ const Parser = struct {...@@ -3356,16 +3356,18 @@ const Parser = struct {
3356 }3356 }
33573357
3358 /// Caller must have already verified the first token.3358 /// Caller must have already verified the first token.
3359 /// ContainerDeclAuto <- ContainerDeclType LBRACE container_doc_comment? ContainerMembers RBRACE
3360 ///
3359 /// ContainerDeclType3361 /// ContainerDeclType
3360 /// <- KEYWORD_struct3362 /// <- KEYWORD_struct (LPAREN Expr RPAREN)?
3363 /// / KEYWORD_opaque
3361 /// / KEYWORD_enum (LPAREN Expr RPAREN)?3364 /// / KEYWORD_enum (LPAREN Expr RPAREN)?
3362 /// / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)?3365 /// / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)?
3363 /// / KEYWORD_opaque
3364 fn parseContainerDeclAuto(p: *Parser) !Node.Index {3366 fn parseContainerDeclAuto(p: *Parser) !Node.Index {
3365 const main_token = p.nextToken();3367 const main_token = p.nextToken();
3366 const arg_expr = switch (p.token_tags[main_token]) {3368 const arg_expr = switch (p.token_tags[main_token]) {
3367 .keyword_struct, .keyword_opaque => null_node,3369 .keyword_opaque => null_node,
3368 .keyword_enum => blk: {3370 .keyword_struct, .keyword_enum => blk: {
3369 if (p.eatToken(.l_paren)) |_| {3371 if (p.eatToken(.l_paren)) |_| {
3370 const expr = try p.expectExpr();3372 const expr = try p.expectExpr();
3371 _ = try p.expectToken(.r_paren);3373 _ = try p.expectToken(.r_paren);
lib/std/zig/parser_test.zig+7
...@@ -3064,6 +3064,13 @@ test "zig fmt: struct declaration" {...@@ -3064,6 +3064,13 @@ test "zig fmt: struct declaration" {
3064 \\ c: u8,3064 \\ c: u8,
3065 \\};3065 \\};
3066 \\3066 \\
3067 \\const Ps = packed struct(u32) {
3068 \\ a: u1,
3069 \\ b: u2,
3070 \\
3071 \\ c: u29,
3072 \\};
3073 \\
3067 \\const Es = extern struct {3074 \\const Es = extern struct {
3068 \\ a: u8,3075 \\ a: u8,
3069 \\ b: u8,3076 \\ b: u8,
src/AstGen.zig+50-6
...@@ -152,6 +152,7 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {...@@ -152,6 +152,7 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
152 0,152 0,
153 tree.containerDeclRoot(),153 tree.containerDeclRoot(),
154 .Auto,154 .Auto,
155 0,
155 )) |struct_decl_ref| {156 )) |struct_decl_ref| {
156 assert(refToIndex(struct_decl_ref).? == 0);157 assert(refToIndex(struct_decl_ref).? == 0);
157 } else |err| switch (err) {158 } else |err| switch (err) {
...@@ -4223,15 +4224,18 @@ fn structDeclInner(...@@ -4223,15 +4224,18 @@ fn structDeclInner(
4223 node: Ast.Node.Index,4224 node: Ast.Node.Index,
4224 container_decl: Ast.full.ContainerDecl,4225 container_decl: Ast.full.ContainerDecl,
4225 layout: std.builtin.Type.ContainerLayout,4226 layout: std.builtin.Type.ContainerLayout,
4227 backing_int_node: Ast.Node.Index,
4226) InnerError!Zir.Inst.Ref {4228) InnerError!Zir.Inst.Ref {
4227 const decl_inst = try gz.reserveInstructionIndex();4229 const decl_inst = try gz.reserveInstructionIndex();
42284230
4229 if (container_decl.ast.members.len == 0) {4231 if (container_decl.ast.members.len == 0 and backing_int_node == 0) {
4230 try gz.setStruct(decl_inst, .{4232 try gz.setStruct(decl_inst, .{
4231 .src_node = node,4233 .src_node = node,
4232 .layout = layout,4234 .layout = layout,
4233 .fields_len = 0,4235 .fields_len = 0,
4234 .decls_len = 0,4236 .decls_len = 0,
4237 .backing_int_ref = .none,
4238 .backing_int_body_len = 0,
4235 .known_non_opv = false,4239 .known_non_opv = false,
4236 .known_comptime_only = false,4240 .known_comptime_only = false,
4237 });4241 });
...@@ -4266,6 +4270,35 @@ fn structDeclInner(...@@ -4266,6 +4270,35 @@ fn structDeclInner(
4266 };4270 };
4267 defer block_scope.unstack();4271 defer block_scope.unstack();
42684272
4273 const scratch_top = astgen.scratch.items.len;
4274 defer astgen.scratch.items.len = scratch_top;
4275
4276 var backing_int_body_len: usize = 0;
4277 const backing_int_ref: Zir.Inst.Ref = blk: {
4278 if (backing_int_node != 0) {
4279 if (layout != .Packed) {
4280 return astgen.failNode(backing_int_node, "non-packed struct does not support backing integer type", .{});
4281 } else {
4282 const backing_int_ref = try typeExpr(&block_scope, &namespace.base, backing_int_node);
4283 if (!block_scope.isEmpty()) {
4284 if (!block_scope.endsWithNoReturn()) {
4285 _ = try block_scope.addBreak(.break_inline, decl_inst, backing_int_ref);
4286 }
4287
4288 const body = block_scope.instructionsSlice();
4289 const old_scratch_len = astgen.scratch.items.len;
4290 try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
4291 appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
4292 backing_int_body_len = astgen.scratch.items.len - old_scratch_len;
4293 block_scope.instructions.items.len = block_scope.instructions_top;
4294 }
4295 break :blk backing_int_ref;
4296 }
4297 } else {
4298 break :blk .none;
4299 }
4300 };
4301
4269 const decl_count = try astgen.scanDecls(&namespace, container_decl.ast.members);4302 const decl_count = try astgen.scanDecls(&namespace, container_decl.ast.members);
4270 const field_count = @intCast(u32, container_decl.ast.members.len - decl_count);4303 const field_count = @intCast(u32, container_decl.ast.members.len - decl_count);
42714304
...@@ -4378,6 +4411,8 @@ fn structDeclInner(...@@ -4378,6 +4411,8 @@ fn structDeclInner(
4378 .layout = layout,4411 .layout = layout,
4379 .fields_len = field_count,4412 .fields_len = field_count,
4380 .decls_len = decl_count,4413 .decls_len = decl_count,
4414 .backing_int_ref = backing_int_ref,
4415 .backing_int_body_len = @intCast(u32, backing_int_body_len),
4381 .known_non_opv = known_non_opv,4416 .known_non_opv = known_non_opv,
4382 .known_comptime_only = known_comptime_only,4417 .known_comptime_only = known_comptime_only,
4383 });4418 });
...@@ -4386,7 +4421,9 @@ fn structDeclInner(...@@ -4386,7 +4421,9 @@ fn structDeclInner(
4386 const decls_slice = wip_members.declsSlice();4421 const decls_slice = wip_members.declsSlice();
4387 const fields_slice = wip_members.fieldsSlice();4422 const fields_slice = wip_members.fieldsSlice();
4388 const bodies_slice = astgen.scratch.items[bodies_start..];4423 const bodies_slice = astgen.scratch.items[bodies_start..];
4389 try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len + fields_slice.len + bodies_slice.len);4424 try astgen.extra.ensureUnusedCapacity(gpa, backing_int_body_len +
4425 decls_slice.len + fields_slice.len + bodies_slice.len);
4426 astgen.extra.appendSliceAssumeCapacity(astgen.scratch.items[scratch_top..][0..backing_int_body_len]);
4390 astgen.extra.appendSliceAssumeCapacity(decls_slice);4427 astgen.extra.appendSliceAssumeCapacity(decls_slice);
4391 astgen.extra.appendSliceAssumeCapacity(fields_slice);4428 astgen.extra.appendSliceAssumeCapacity(fields_slice);
4392 astgen.extra.appendSliceAssumeCapacity(bodies_slice);4429 astgen.extra.appendSliceAssumeCapacity(bodies_slice);
...@@ -4582,9 +4619,7 @@ fn containerDecl(...@@ -4582,9 +4619,7 @@ fn containerDecl(
4582 else => unreachable,4619 else => unreachable,
4583 } else std.builtin.Type.ContainerLayout.Auto;4620 } else std.builtin.Type.ContainerLayout.Auto;
45844621
4585 assert(container_decl.ast.arg == 0);4622 const result = try structDeclInner(gz, scope, node, container_decl, layout, container_decl.ast.arg);
4586
4587 const result = try structDeclInner(gz, scope, node, container_decl, layout);
4588 return rvalue(gz, rl, result, node);4623 return rvalue(gz, rl, result, node);
4589 },4624 },
4590 .keyword_union => {4625 .keyword_union => {
...@@ -11254,6 +11289,8 @@ const GenZir = struct {...@@ -11254,6 +11289,8 @@ const GenZir = struct {
11254 src_node: Ast.Node.Index,11289 src_node: Ast.Node.Index,
11255 fields_len: u32,11290 fields_len: u32,
11256 decls_len: u32,11291 decls_len: u32,
11292 backing_int_ref: Zir.Inst.Ref,
11293 backing_int_body_len: u32,
11257 layout: std.builtin.Type.ContainerLayout,11294 layout: std.builtin.Type.ContainerLayout,
11258 known_non_opv: bool,11295 known_non_opv: bool,
11259 known_comptime_only: bool,11296 known_comptime_only: bool,
...@@ -11261,7 +11298,7 @@ const GenZir = struct {...@@ -11261,7 +11298,7 @@ const GenZir = struct {
11261 const astgen = gz.astgen;11298 const astgen = gz.astgen;
11262 const gpa = astgen.gpa;11299 const gpa = astgen.gpa;
1126311300
11264 try astgen.extra.ensureUnusedCapacity(gpa, 4);11301 try astgen.extra.ensureUnusedCapacity(gpa, 6);
11265 const payload_index = @intCast(u32, astgen.extra.items.len);11302 const payload_index = @intCast(u32, astgen.extra.items.len);
1126611303
11267 if (args.src_node != 0) {11304 if (args.src_node != 0) {
...@@ -11274,6 +11311,12 @@ const GenZir = struct {...@@ -11274,6 +11311,12 @@ const GenZir = struct {
11274 if (args.decls_len != 0) {11311 if (args.decls_len != 0) {
11275 astgen.extra.appendAssumeCapacity(args.decls_len);11312 astgen.extra.appendAssumeCapacity(args.decls_len);
11276 }11313 }
11314 if (args.backing_int_ref != .none) {
11315 astgen.extra.appendAssumeCapacity(args.backing_int_body_len);
11316 if (args.backing_int_body_len == 0) {
11317 astgen.extra.appendAssumeCapacity(@enumToInt(args.backing_int_ref));
11318 }
11319 }
11277 astgen.instructions.set(inst, .{11320 astgen.instructions.set(inst, .{
11278 .tag = .extended,11321 .tag = .extended,
11279 .data = .{ .extended = .{11322 .data = .{ .extended = .{
...@@ -11282,6 +11325,7 @@ const GenZir = struct {...@@ -11282,6 +11325,7 @@ const GenZir = struct {
11282 .has_src_node = args.src_node != 0,11325 .has_src_node = args.src_node != 0,
11283 .has_fields_len = args.fields_len != 0,11326 .has_fields_len = args.fields_len != 0,
11284 .has_decls_len = args.decls_len != 0,11327 .has_decls_len = args.decls_len != 0,
11328 .has_backing_int = args.backing_int_ref != .none,
11285 .known_non_opv = args.known_non_opv,11329 .known_non_opv = args.known_non_opv,
11286 .known_comptime_only = args.known_comptime_only,11330 .known_comptime_only = args.known_comptime_only,
11287 .name_strategy = gz.anon_name_strategy,11331 .name_strategy = gz.anon_name_strategy,
src/Autodoc.zig+11
...@@ -2536,6 +2536,17 @@ fn walkInstruction(...@@ -2536,6 +2536,17 @@ fn walkInstruction(
2536 break :blk decls_len;2536 break :blk decls_len;
2537 } else 0;2537 } else 0;
25382538
2539 // TODO: Expose explicit backing integer types in some way.
2540 if (small.has_backing_int) {
2541 const backing_int_body_len = file.zir.extra[extra_index];
2542 extra_index += 1; // backing_int_body_len
2543 if (backing_int_body_len == 0) {
2544 extra_index += 1; // backing_int_ref
2545 } else {
2546 extra_index += backing_int_body_len; // backing_int_body_inst
2547 }
2548 }
2549
2539 var decl_indexes: std.ArrayListUnmanaged(usize) = .{};2550 var decl_indexes: std.ArrayListUnmanaged(usize) = .{};
2540 var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{};2551 var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{};
25412552
src/Module.zig+7-14
...@@ -895,6 +895,11 @@ pub const Struct = struct {...@@ -895,6 +895,11 @@ pub const Struct = struct {
895 zir_index: Zir.Inst.Index,895 zir_index: Zir.Inst.Index,
896896
897 layout: std.builtin.Type.ContainerLayout,897 layout: std.builtin.Type.ContainerLayout,
898 /// If the layout is not packed, this is the noreturn type.
899 /// If the layout is packed, this is the backing integer type of the packed struct.
900 /// Whether zig chooses this type or the user specifies it, it is stored here.
901 /// This will be set to the noreturn type until status is `have_layout`.
902 backing_int_ty: Type = Type.initTag(.noreturn),
898 status: enum {903 status: enum {
899 none,904 none,
900 field_types_wip,905 field_types_wip,
...@@ -1025,7 +1030,7 @@ pub const Struct = struct {...@@ -1025,7 +1030,7 @@ pub const Struct = struct {
10251030
1026 pub fn packedFieldBitOffset(s: Struct, target: Target, index: usize) u16 {1031 pub fn packedFieldBitOffset(s: Struct, target: Target, index: usize) u16 {
1027 assert(s.layout == .Packed);1032 assert(s.layout == .Packed);
1028 assert(s.haveFieldTypes());1033 assert(s.haveLayout());
1029 var bit_sum: u64 = 0;1034 var bit_sum: u64 = 0;
1030 for (s.fields.values()) |field, i| {1035 for (s.fields.values()) |field, i| {
1031 if (i == index) {1036 if (i == index) {
...@@ -1033,19 +1038,7 @@ pub const Struct = struct {...@@ -1033,19 +1038,7 @@ pub const Struct = struct {
1033 }1038 }
1034 bit_sum += field.ty.bitSize(target);1039 bit_sum += field.ty.bitSize(target);
1035 }1040 }
1036 return @intCast(u16, bit_sum);1041 unreachable; // index out of bounds
1037 }
1038
1039 pub fn packedIntegerBits(s: Struct, target: Target) u16 {
1040 return s.packedFieldBitOffset(target, s.fields.count());
1041 }
1042
1043 pub fn packedIntegerType(s: Struct, target: Target, buf: *Type.Payload.Bits) Type {
1044 buf.* = .{
1045 .base = .{ .tag = .int_unsigned },
1046 .data = s.packedIntegerBits(target),
1047 };
1048 return Type.initPayload(&buf.base);
1049 }1042 }
1050};1043};
10511044
src/Sema.zig+256-10
...@@ -78,6 +78,7 @@ post_hoc_blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, *LabeledBlock) = .{},...@@ -78,6 +78,7 @@ post_hoc_blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, *LabeledBlock) = .{},
78err: ?*Module.ErrorMsg = null,78err: ?*Module.ErrorMsg = null,
7979
80const std = @import("std");80const std = @import("std");
81const math = std.math;
81const mem = std.mem;82const mem = std.mem;
82const Allocator = std.mem.Allocator;83const Allocator = std.mem.Allocator;
83const assert = std.debug.assert;84const assert = std.debug.assert;
...@@ -2238,6 +2239,16 @@ pub fn analyzeStructDecl(...@@ -2238,6 +2239,16 @@ pub fn analyzeStructDecl(
2238 break :blk decls_len;2239 break :blk decls_len;
2239 } else 0;2240 } else 0;
22402241
2242 if (small.has_backing_int) {
2243 const backing_int_body_len = sema.code.extra[extra_index];
2244 extra_index += 1; // backing_int_body_len
2245 if (backing_int_body_len == 0) {
2246 extra_index += 1; // backing_int_ref
2247 } else {
2248 extra_index += backing_int_body_len; // backing_int_body_inst
2249 }
2250 }
2251
2241 _ = try sema.mod.scanNamespace(&struct_obj.namespace, extra_index, decls_len, new_decl);2252 _ = try sema.mod.scanNamespace(&struct_obj.namespace, extra_index, decls_len, new_decl);
2242}2253}
22432254
...@@ -11822,7 +11833,7 @@ fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air....@@ -11822,7 +11833,7 @@ fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
11822 return sema.failWithDivideByZero(block, rhs_src);11833 return sema.failWithDivideByZero(block, rhs_src);
11823 }11834 }
11824 if (maybe_lhs_val) |lhs_val| {11835 if (maybe_lhs_val) |lhs_val| {
11825 const rem_result = try lhs_val.intRem(rhs_val, resolved_type, sema.arena, target);11836 const rem_result = try sema.intRem(block, resolved_type, lhs_val, lhs_src, rhs_val, rhs_src);
11826 // If this answer could possibly be different by doing `intMod`,11837 // If this answer could possibly be different by doing `intMod`,
11827 // we must emit a compile error. Otherwise, it's OK.11838 // we must emit a compile error. Otherwise, it's OK.
11828 if ((try rhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src))) != (try lhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src))) and11839 if ((try rhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src))) != (try lhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src))) and
...@@ -11884,6 +11895,60 @@ fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air....@@ -11884,6 +11895,60 @@ fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
11884 return block.addBinOp(air_tag, casted_lhs, casted_rhs);11895 return block.addBinOp(air_tag, casted_lhs, casted_rhs);
11885}11896}
1188611897
11898fn intRem(
11899 sema: *Sema,
11900 block: *Block,
11901 ty: Type,
11902 lhs: Value,
11903 lhs_src: LazySrcLoc,
11904 rhs: Value,
11905 rhs_src: LazySrcLoc,
11906) CompileError!Value {
11907 if (ty.zigTypeTag() == .Vector) {
11908 const result_data = try sema.arena.alloc(Value, ty.vectorLen());
11909 for (result_data) |*scalar, i| {
11910 scalar.* = try sema.intRemScalar(block, lhs.indexVectorlike(i), lhs_src, rhs.indexVectorlike(i), rhs_src);
11911 }
11912 return Value.Tag.aggregate.create(sema.arena, result_data);
11913 }
11914 return sema.intRemScalar(block, lhs, lhs_src, rhs, rhs_src);
11915}
11916
11917fn intRemScalar(
11918 sema: *Sema,
11919 block: *Block,
11920 lhs: Value,
11921 lhs_src: LazySrcLoc,
11922 rhs: Value,
11923 rhs_src: LazySrcLoc,
11924) CompileError!Value {
11925 const target = sema.mod.getTarget();
11926 // TODO is this a performance issue? maybe we should try the operation without
11927 // resorting to BigInt first.
11928 var lhs_space: Value.BigIntSpace = undefined;
11929 var rhs_space: Value.BigIntSpace = undefined;
11930 const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, target, sema.kit(block, lhs_src));
11931 const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, target, sema.kit(block, rhs_src));
11932 const limbs_q = try sema.arena.alloc(
11933 math.big.Limb,
11934 lhs_bigint.limbs.len,
11935 );
11936 const limbs_r = try sema.arena.alloc(
11937 math.big.Limb,
11938 // TODO: consider reworking Sema to re-use Values rather than
11939 // always producing new Value objects.
11940 rhs_bigint.limbs.len,
11941 );
11942 const limbs_buffer = try sema.arena.alloc(
11943 math.big.Limb,
11944 math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
11945 );
11946 var result_q = math.big.int.Mutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
11947 var result_r = math.big.int.Mutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
11948 result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);
11949 return Value.fromBigInt(sema.arena, result_r.toConst());
11950}
11951
11887fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {11952fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
11888 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;11953 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
11889 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };11954 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
...@@ -12048,7 +12113,7 @@ fn zirRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins...@@ -12048,7 +12113,7 @@ fn zirRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
12048 if (maybe_lhs_val) |lhs_val| {12113 if (maybe_lhs_val) |lhs_val| {
12049 return sema.addConstant(12114 return sema.addConstant(
12050 resolved_type,12115 resolved_type,
12051 try lhs_val.intRem(rhs_val, resolved_type, sema.arena, target),12116 try sema.intRem(block, resolved_type, lhs_val, lhs_src, rhs_val, rhs_src),
12052 );12117 );
12053 }12118 }
12054 break :rs lhs_src;12119 break :rs lhs_src;
...@@ -14228,13 +14293,27 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -14228,13 +14293,27 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1422814293
14229 const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, struct_ty.getNamespace());14294 const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, struct_ty.getNamespace());
1423014295
14231 const field_values = try sema.arena.create([4]Value);14296 const backing_integer_val = blk: {
14297 if (layout == .Packed) {
14298 const struct_obj = struct_ty.castTag(.@"struct").?.data;
14299 assert(struct_obj.haveLayout());
14300 assert(struct_obj.backing_int_ty.isInt());
14301 const backing_int_ty_val = try Value.Tag.ty.create(sema.arena, struct_obj.backing_int_ty);
14302 break :blk try Value.Tag.opt_payload.create(sema.arena, backing_int_ty_val);
14303 } else {
14304 break :blk Value.initTag(.null_value);
14305 }
14306 };
14307
14308 const field_values = try sema.arena.create([5]Value);
14232 field_values.* = .{14309 field_values.* = .{
14233 // layout: ContainerLayout,14310 // layout: ContainerLayout,
14234 try Value.Tag.enum_field_index.create(14311 try Value.Tag.enum_field_index.create(
14235 sema.arena,14312 sema.arena,
14236 @enumToInt(layout),14313 @enumToInt(layout),
14237 ),14314 ),
14315 // backing_integer: ?type,
14316 backing_integer_val,
14238 // fields: []const StructField,14317 // fields: []const StructField,
14239 fields_val,14318 fields_val,
14240 // decls: []const Declaration,14319 // decls: []const Declaration,
...@@ -16251,7 +16330,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in...@@ -16251,7 +16330,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in
16251 if (!try sema.intFitsInType(block, src, alignment_val, Type.u32, null)) {16330 if (!try sema.intFitsInType(block, src, alignment_val, Type.u32, null)) {
16252 return sema.fail(block, src, "alignment must fit in 'u32'", .{});16331 return sema.fail(block, src, "alignment must fit in 'u32'", .{});
16253 }16332 }
16254 const abi_align = @intCast(u29, alignment_val.toUnsignedInt(target));16333 const abi_align = @intCast(u29, (try alignment_val.getUnsignedIntAdvanced(target, sema.kit(block, src))).?);
1625516334
16256 var buffer: Value.ToTypeBuffer = undefined;16335 var buffer: Value.ToTypeBuffer = undefined;
16257 const unresolved_elem_ty = child_val.toType(&buffer);16336 const unresolved_elem_ty = child_val.toType(&buffer);
...@@ -16416,22 +16495,31 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in...@@ -16416,22 +16495,31 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in
16416 const struct_val = union_val.val.castTag(.aggregate).?.data;16495 const struct_val = union_val.val.castTag(.aggregate).?.data;
16417 // layout: containerlayout,16496 // layout: containerlayout,
16418 const layout_val = struct_val[0];16497 const layout_val = struct_val[0];
16498 // backing_int: ?type,
16499 const backing_int_val = struct_val[1];
16419 // fields: []const enumfield,16500 // fields: []const enumfield,
16420 const fields_val = struct_val[1];16501 const fields_val = struct_val[2];
16421 // decls: []const declaration,16502 // decls: []const declaration,
16422 const decls_val = struct_val[2];16503 const decls_val = struct_val[3];
16423 // is_tuple: bool,16504 // is_tuple: bool,
16424 const is_tuple_val = struct_val[3];16505 const is_tuple_val = struct_val[4];
16506 assert(struct_val.len == 5);
16507
16508 const layout = layout_val.toEnum(std.builtin.Type.ContainerLayout);
1642516509
16426 // Decls16510 // Decls
16427 if (decls_val.sliceLen(mod) > 0) {16511 if (decls_val.sliceLen(mod) > 0) {
16428 return sema.fail(block, src, "reified structs must have no decls", .{});16512 return sema.fail(block, src, "reified structs must have no decls", .{});
16429 }16513 }
1643016514
16515 if (layout != .Packed and !backing_int_val.isNull()) {
16516 return sema.fail(block, src, "non-packed struct does not support backing integer type", .{});
16517 }
16518
16431 return if (is_tuple_val.toBool())16519 return if (is_tuple_val.toBool())
16432 try sema.reifyTuple(block, src, fields_val)16520 try sema.reifyTuple(block, src, fields_val)
16433 else16521 else
16434 try sema.reifyStruct(block, inst, src, layout_val, fields_val, name_strategy);16522 try sema.reifyStruct(block, inst, src, layout, backing_int_val, fields_val, name_strategy);
16435 },16523 },
16436 .Enum => {16524 .Enum => {
16437 const struct_val = union_val.val.castTag(.aggregate).?.data;16525 const struct_val = union_val.val.castTag(.aggregate).?.data;
...@@ -16924,7 +17012,8 @@ fn reifyStruct(...@@ -16924,7 +17012,8 @@ fn reifyStruct(
16924 block: *Block,17012 block: *Block,
16925 inst: Zir.Inst.Index,17013 inst: Zir.Inst.Index,
16926 src: LazySrcLoc,17014 src: LazySrcLoc,
16927 layout_val: Value,17015 layout: std.builtin.Type.ContainerLayout,
17016 backing_int_val: Value,
16928 fields_val: Value,17017 fields_val: Value,
16929 name_strategy: Zir.Inst.NameStrategy,17018 name_strategy: Zir.Inst.NameStrategy,
16930) CompileError!Air.Inst.Ref {17019) CompileError!Air.Inst.Ref {
...@@ -16947,7 +17036,7 @@ fn reifyStruct(...@@ -16947,7 +17036,7 @@ fn reifyStruct(
16947 .owner_decl = new_decl_index,17036 .owner_decl = new_decl_index,
16948 .fields = .{},17037 .fields = .{},
16949 .zir_index = inst,17038 .zir_index = inst,
16950 .layout = layout_val.toEnum(std.builtin.Type.ContainerLayout),17039 .layout = layout,
16951 .status = .have_field_types,17040 .status = .have_field_types,
16952 .known_non_opv = false,17041 .known_non_opv = false,
16953 .namespace = .{17042 .namespace = .{
...@@ -17013,6 +17102,41 @@ fn reifyStruct(...@@ -17013,6 +17102,41 @@ fn reifyStruct(
17013 };17102 };
17014 }17103 }
1701517104
17105 if (layout == .Packed) {
17106 struct_obj.status = .layout_wip;
17107
17108 for (struct_obj.fields.values()) |field, index| {
17109 sema.resolveTypeLayout(block, src, field.ty) catch |err| switch (err) {
17110 error.AnalysisFail => {
17111 const msg = sema.err orelse return err;
17112 try sema.addFieldErrNote(block, struct_ty, index, msg, "while checking this field", .{});
17113 return err;
17114 },
17115 else => return err,
17116 };
17117 }
17118
17119 var fields_bit_sum: u64 = 0;
17120 for (struct_obj.fields.values()) |field| {
17121 fields_bit_sum += field.ty.bitSize(target);
17122 }
17123
17124 if (backing_int_val.optionalValue()) |payload| {
17125 var buf: Value.ToTypeBuffer = undefined;
17126 const backing_int_ty = payload.toType(&buf);
17127 try sema.checkBackingIntType(block, src, backing_int_ty, fields_bit_sum);
17128 struct_obj.backing_int_ty = try backing_int_ty.copy(new_decl_arena_allocator);
17129 } else {
17130 var buf: Type.Payload.Bits = .{
17131 .base = .{ .tag = .int_unsigned },
17132 .data = @intCast(u16, fields_bit_sum),
17133 };
17134 struct_obj.backing_int_ty = try Type.initPayload(&buf.base).copy(new_decl_arena_allocator);
17135 }
17136
17137 struct_obj.status = .have_layout;
17138 }
17139
17016 try new_decl.finalizeNewArena(&new_decl_arena);17140 try new_decl.finalizeNewArena(&new_decl_arena);
17017 return sema.analyzeDeclVal(block, src, new_decl_index);17141 return sema.analyzeDeclVal(block, src, new_decl_index);
17018}17142}
...@@ -27109,6 +27233,11 @@ fn resolveStructLayout(...@@ -27109,6 +27233,11 @@ fn resolveStructLayout(
27109 else => return err,27233 else => return err,
27110 };27234 };
27111 }27235 }
27236
27237 if (struct_obj.layout == .Packed) {
27238 try semaBackingIntType(sema.mod, struct_obj);
27239 }
27240
27112 struct_obj.status = .have_layout;27241 struct_obj.status = .have_layout;
2711327242
27114 // In case of querying the ABI alignment of this struct, we will ask27243 // In case of querying the ABI alignment of this struct, we will ask
...@@ -27128,6 +27257,109 @@ fn resolveStructLayout(...@@ -27128,6 +27257,109 @@ fn resolveStructLayout(
27128 // otherwise it's a tuple; no need to resolve anything27257 // otherwise it's a tuple; no need to resolve anything
27129}27258}
2713027259
27260fn semaBackingIntType(mod: *Module, struct_obj: *Module.Struct) CompileError!void {
27261 const gpa = mod.gpa;
27262 const target = mod.getTarget();
27263
27264 var fields_bit_sum: u64 = 0;
27265 for (struct_obj.fields.values()) |field| {
27266 fields_bit_sum += field.ty.bitSize(target);
27267 }
27268
27269 const decl_index = struct_obj.owner_decl;
27270 const decl = mod.declPtr(decl_index);
27271 var decl_arena = decl.value_arena.?.promote(gpa);
27272 defer decl.value_arena.?.* = decl_arena.state;
27273 const decl_arena_allocator = decl_arena.allocator();
27274
27275 const zir = struct_obj.namespace.file_scope.zir;
27276 const extended = zir.instructions.items(.data)[struct_obj.zir_index].extended;
27277 assert(extended.opcode == .struct_decl);
27278 const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small);
27279
27280 if (small.has_backing_int) {
27281 var extra_index: usize = extended.operand;
27282 extra_index += @boolToInt(small.has_src_node);
27283 extra_index += @boolToInt(small.has_fields_len);
27284 extra_index += @boolToInt(small.has_decls_len);
27285
27286 const backing_int_body_len = zir.extra[extra_index];
27287 extra_index += 1;
27288
27289 var analysis_arena = std.heap.ArenaAllocator.init(gpa);
27290 defer analysis_arena.deinit();
27291
27292 var sema: Sema = .{
27293 .mod = mod,
27294 .gpa = gpa,
27295 .arena = analysis_arena.allocator(),
27296 .perm_arena = decl_arena_allocator,
27297 .code = zir,
27298 .owner_decl = decl,
27299 .owner_decl_index = decl_index,
27300 .func = null,
27301 .fn_ret_ty = Type.void,
27302 .owner_func = null,
27303 };
27304 defer sema.deinit();
27305
27306 var wip_captures = try WipCaptureScope.init(gpa, decl_arena_allocator, decl.src_scope);
27307 defer wip_captures.deinit();
27308
27309 var block: Block = .{
27310 .parent = null,
27311 .sema = &sema,
27312 .src_decl = decl_index,
27313 .namespace = &struct_obj.namespace,
27314 .wip_capture_scope = wip_captures.scope,
27315 .instructions = .{},
27316 .inlining = null,
27317 .is_comptime = true,
27318 };
27319 defer {
27320 assert(block.instructions.items.len == 0);
27321 block.params.deinit(gpa);
27322 }
27323
27324 const backing_int_src: LazySrcLoc = .{ .node_offset_container_tag = 0 };
27325 const backing_int_ty = blk: {
27326 if (backing_int_body_len == 0) {
27327 const backing_int_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
27328 break :blk try sema.resolveType(&block, backing_int_src, backing_int_ref);
27329 } else {
27330 const body = zir.extra[extra_index..][0..backing_int_body_len];
27331 const ty_ref = try sema.resolveBody(&block, body, struct_obj.zir_index);
27332 break :blk try sema.analyzeAsType(&block, backing_int_src, ty_ref);
27333 }
27334 };
27335
27336 try sema.checkBackingIntType(&block, backing_int_src, backing_int_ty, fields_bit_sum);
27337 struct_obj.backing_int_ty = try backing_int_ty.copy(decl_arena_allocator);
27338 } else {
27339 var buf: Type.Payload.Bits = .{
27340 .base = .{ .tag = .int_unsigned },
27341 .data = @intCast(u16, fields_bit_sum),
27342 };
27343 struct_obj.backing_int_ty = try Type.initPayload(&buf.base).copy(decl_arena_allocator);
27344 }
27345}
27346
27347fn checkBackingIntType(sema: *Sema, block: *Block, src: LazySrcLoc, backing_int_ty: Type, fields_bit_sum: u64) CompileError!void {
27348 const target = sema.mod.getTarget();
27349
27350 if (!backing_int_ty.isInt()) {
27351 return sema.fail(block, src, "expected backing integer type, found '{}'", .{backing_int_ty.fmt(sema.mod)});
27352 }
27353 if (backing_int_ty.bitSize(target) != fields_bit_sum) {
27354 return sema.fail(
27355 block,
27356 src,
27357 "backing integer type '{}' has bit size {} but the struct fields have a total bit size of {}",
27358 .{ backing_int_ty.fmt(sema.mod), backing_int_ty.bitSize(target), fields_bit_sum },
27359 );
27360 }
27361}
27362
27131fn resolveUnionLayout(27363fn resolveUnionLayout(
27132 sema: *Sema,27364 sema: *Sema,
27133 block: *Block,27365 block: *Block,
...@@ -27450,12 +27682,26 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void...@@ -27450,12 +27682,26 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void
27450 break :decls_len decls_len;27682 break :decls_len decls_len;
27451 } else 0;27683 } else 0;
2745227684
27685 // The backing integer cannot be handled until `resolveStructLayout()`.
27686 if (small.has_backing_int) {
27687 const backing_int_body_len = zir.extra[extra_index];
27688 extra_index += 1; // backing_int_body_len
27689 if (backing_int_body_len == 0) {
27690 extra_index += 1; // backing_int_ref
27691 } else {
27692 extra_index += backing_int_body_len; // backing_int_body_inst
27693 }
27694 }
27695
27453 // Skip over decls.27696 // Skip over decls.
27454 var decls_it = zir.declIteratorInner(extra_index, decls_len);27697 var decls_it = zir.declIteratorInner(extra_index, decls_len);
27455 while (decls_it.next()) |_| {}27698 while (decls_it.next()) |_| {}
27456 extra_index = decls_it.extra_index;27699 extra_index = decls_it.extra_index;
2745727700
27458 if (fields_len == 0) {27701 if (fields_len == 0) {
27702 if (struct_obj.layout == .Packed) {
27703 try semaBackingIntType(mod, struct_obj);
27704 }
27459 struct_obj.status = .have_layout;27705 struct_obj.status = .have_layout;
27460 return;27706 return;
27461 }27707 }
src/Zir.zig+10-6
...@@ -3085,13 +3085,16 @@ pub const Inst = struct {...@@ -3085,13 +3085,16 @@ pub const Inst = struct {
3085 /// 0. src_node: i32, // if has_src_node3085 /// 0. src_node: i32, // if has_src_node
3086 /// 1. fields_len: u32, // if has_fields_len3086 /// 1. fields_len: u32, // if has_fields_len
3087 /// 2. decls_len: u32, // if has_decls_len3087 /// 2. decls_len: u32, // if has_decls_len
3088 /// 3. decl_bits: u32 // for every 8 decls3088 /// 3. backing_int_body_len: u32, // if has_backing_int
3089 /// 4. backing_int_ref: Ref, // if has_backing_int and backing_int_body_len is 0
3090 /// 5. backing_int_body_inst: Inst, // if has_backing_int and backing_int_body_len is > 0
3091 /// 6. decl_bits: u32 // for every 8 decls
3089 /// - sets of 4 bits:3092 /// - sets of 4 bits:
3090 /// 0b000X: whether corresponding decl is pub3093 /// 0b000X: whether corresponding decl is pub
3091 /// 0b00X0: whether corresponding decl is exported3094 /// 0b00X0: whether corresponding decl is exported
3092 /// 0b0X00: whether corresponding decl has an align expression3095 /// 0b0X00: whether corresponding decl has an align expression
3093 /// 0bX000: whether corresponding decl has a linksection or an address space expression3096 /// 0bX000: whether corresponding decl has a linksection or an address space expression
3094 /// 4. decl: { // for every decls_len3097 /// 7. decl: { // for every decls_len
3095 /// src_hash: [4]u32, // hash of source bytes3098 /// src_hash: [4]u32, // hash of source bytes
3096 /// line: u32, // line number of decl, relative to parent3099 /// line: u32, // line number of decl, relative to parent
3097 /// name: u32, // null terminated string index3100 /// name: u32, // null terminated string index
...@@ -3109,13 +3112,13 @@ pub const Inst = struct {...@@ -3109,13 +3112,13 @@ pub const Inst = struct {
3109 /// address_space: Ref,3112 /// address_space: Ref,
3110 /// }3113 /// }
3111 /// }3114 /// }
3112 /// 5. flags: u32 // for every 8 fields3115 /// 8. flags: u32 // for every 8 fields
3113 /// - sets of 4 bits:3116 /// - sets of 4 bits:
3114 /// 0b000X: whether corresponding field has an align expression3117 /// 0b000X: whether corresponding field has an align expression
3115 /// 0b00X0: whether corresponding field has a default expression3118 /// 0b00X0: whether corresponding field has a default expression
3116 /// 0b0X00: whether corresponding field is comptime3119 /// 0b0X00: whether corresponding field is comptime
3117 /// 0bX000: whether corresponding field has a type expression3120 /// 0bX000: whether corresponding field has a type expression
3118 /// 6. fields: { // for every fields_len3121 /// 9. fields: { // for every fields_len
3119 /// field_name: u32,3122 /// field_name: u32,
3120 /// doc_comment: u32, // 0 if no doc comment3123 /// doc_comment: u32, // 0 if no doc comment
3121 /// field_type: Ref, // if corresponding bit is not set. none means anytype.3124 /// field_type: Ref, // if corresponding bit is not set. none means anytype.
...@@ -3123,7 +3126,7 @@ pub const Inst = struct {...@@ -3123,7 +3126,7 @@ pub const Inst = struct {
3123 /// align_body_len: u32, // if corresponding bit is set3126 /// align_body_len: u32, // if corresponding bit is set
3124 /// init_body_len: u32, // if corresponding bit is set3127 /// init_body_len: u32, // if corresponding bit is set
3125 /// }3128 /// }
3126 /// 7. bodies: { // for every fields_len3129 /// 10. bodies: { // for every fields_len
3127 /// field_type_body_inst: Inst, // for each field_type_body_len3130 /// field_type_body_inst: Inst, // for each field_type_body_len
3128 /// align_body_inst: Inst, // for each align_body_len3131 /// align_body_inst: Inst, // for each align_body_len
3129 /// init_body_inst: Inst, // for each init_body_len3132 /// init_body_inst: Inst, // for each init_body_len
...@@ -3133,11 +3136,12 @@ pub const Inst = struct {...@@ -3133,11 +3136,12 @@ pub const Inst = struct {
3133 has_src_node: bool,3136 has_src_node: bool,
3134 has_fields_len: bool,3137 has_fields_len: bool,
3135 has_decls_len: bool,3138 has_decls_len: bool,
3139 has_backing_int: bool,
3136 known_non_opv: bool,3140 known_non_opv: bool,
3137 known_comptime_only: bool,3141 known_comptime_only: bool,
3138 name_strategy: NameStrategy,3142 name_strategy: NameStrategy,
3139 layout: std.builtin.Type.ContainerLayout,3143 layout: std.builtin.Type.ContainerLayout,
3140 _: u7 = undefined,3144 _: u6 = undefined,
3141 };3145 };
3142 };3146 };
31433147
src/codegen/llvm.zig+6-9
...@@ -1683,8 +1683,7 @@ pub const Object = struct {...@@ -1683,8 +1683,7 @@ pub const Object = struct {
1683 if (ty.castTag(.@"struct")) |payload| {1683 if (ty.castTag(.@"struct")) |payload| {
1684 const struct_obj = payload.data;1684 const struct_obj = payload.data;
1685 if (struct_obj.layout == .Packed) {1685 if (struct_obj.layout == .Packed) {
1686 var buf: Type.Payload.Bits = undefined;1686 const info = struct_obj.backing_int_ty.intInfo(target);
1687 const info = struct_obj.packedIntegerType(target, &buf).intInfo(target);
1688 const dwarf_encoding: c_uint = switch (info.signedness) {1687 const dwarf_encoding: c_uint = switch (info.signedness) {
1689 .signed => DW.ATE.signed,1688 .signed => DW.ATE.signed,
1690 .unsigned => DW.ATE.unsigned,1689 .unsigned => DW.ATE.unsigned,
...@@ -2679,9 +2678,7 @@ pub const DeclGen = struct {...@@ -2679,9 +2678,7 @@ pub const DeclGen = struct {
2679 const struct_obj = t.castTag(.@"struct").?.data;2678 const struct_obj = t.castTag(.@"struct").?.data;
26802679
2681 if (struct_obj.layout == .Packed) {2680 if (struct_obj.layout == .Packed) {
2682 var buf: Type.Payload.Bits = undefined;2681 const int_llvm_ty = try dg.lowerType(struct_obj.backing_int_ty);
2683 const int_ty = struct_obj.packedIntegerType(target, &buf);
2684 const int_llvm_ty = try dg.lowerType(int_ty);
2685 gop.value_ptr.* = int_llvm_ty;2682 gop.value_ptr.* = int_llvm_ty;
2686 return int_llvm_ty;2683 return int_llvm_ty;
2687 }2684 }
...@@ -3330,8 +3327,8 @@ pub const DeclGen = struct {...@@ -3330,8 +3327,8 @@ pub const DeclGen = struct {
3330 const struct_obj = tv.ty.castTag(.@"struct").?.data;3327 const struct_obj = tv.ty.castTag(.@"struct").?.data;
33313328
3332 if (struct_obj.layout == .Packed) {3329 if (struct_obj.layout == .Packed) {
3333 const big_bits = struct_obj.packedIntegerBits(target);3330 const big_bits = struct_obj.backing_int_ty.bitSize(target);
3334 const int_llvm_ty = dg.context.intType(big_bits);3331 const int_llvm_ty = dg.context.intType(@intCast(c_uint, big_bits));
3335 const fields = struct_obj.fields.values();3332 const fields = struct_obj.fields.values();
3336 comptime assert(Type.packed_struct_layout_version == 2);3333 comptime assert(Type.packed_struct_layout_version == 2);
3337 var running_int: *const llvm.Value = int_llvm_ty.constNull();3334 var running_int: *const llvm.Value = int_llvm_ty.constNull();
...@@ -8243,8 +8240,8 @@ pub const FuncGen = struct {...@@ -8243,8 +8240,8 @@ pub const FuncGen = struct {
8243 .Struct => {8240 .Struct => {
8244 if (result_ty.containerLayout() == .Packed) {8241 if (result_ty.containerLayout() == .Packed) {
8245 const struct_obj = result_ty.castTag(.@"struct").?.data;8242 const struct_obj = result_ty.castTag(.@"struct").?.data;
8246 const big_bits = struct_obj.packedIntegerBits(target);8243 const big_bits = struct_obj.backing_int_ty.bitSize(target);
8247 const int_llvm_ty = self.dg.context.intType(big_bits);8244 const int_llvm_ty = self.dg.context.intType(@intCast(c_uint, big_bits));
8248 const fields = struct_obj.fields.values();8245 const fields = struct_obj.fields.values();
8249 comptime assert(Type.packed_struct_layout_version == 2);8246 comptime assert(Type.packed_struct_layout_version == 2);
8250 var running_int: *const llvm.Value = int_llvm_ty.constNull();8247 var running_int: *const llvm.Value = int_llvm_ty.constNull();
src/print_zir.zig+22-3
...@@ -1245,9 +1245,28 @@ const Writer = struct {...@@ -1245,9 +1245,28 @@ const Writer = struct {
12451245
1246 try self.writeFlag(stream, "known_non_opv, ", small.known_non_opv);1246 try self.writeFlag(stream, "known_non_opv, ", small.known_non_opv);
1247 try self.writeFlag(stream, "known_comptime_only, ", small.known_comptime_only);1247 try self.writeFlag(stream, "known_comptime_only, ", small.known_comptime_only);
1248 try stream.print("{s}, {s}, ", .{1248
1249 @tagName(small.name_strategy), @tagName(small.layout),1249 try stream.print("{s}, ", .{@tagName(small.name_strategy)});
1250 });1250
1251 if (small.layout == .Packed and small.has_backing_int) {
1252 const backing_int_body_len = self.code.extra[extra_index];
1253 extra_index += 1;
1254 try stream.writeAll("Packed(");
1255 if (backing_int_body_len == 0) {
1256 const backing_int_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
1257 extra_index += 1;
1258 try self.writeInstRef(stream, backing_int_ref);
1259 } else {
1260 const body = self.code.extra[extra_index..][0..backing_int_body_len];
1261 extra_index += backing_int_body_len;
1262 self.indent += 2;
1263 try self.writeBracedDecl(stream, body);
1264 self.indent -= 2;
1265 }
1266 try stream.writeAll("), ");
1267 } else {
1268 try stream.print("{s}, ", .{@tagName(small.layout)});
1269 }
12511270
1252 if (decls_len == 0) {1271 if (decls_len == 0) {
1253 try stream.writeAll("{}, ");1272 try stream.writeAll("{}, ");
src/stage1/all_types.hpp+1
...@@ -1116,6 +1116,7 @@ struct AstNodeContainerDecl {...@@ -1116,6 +1116,7 @@ struct AstNodeContainerDecl {
1116 ContainerLayout layout;1116 ContainerLayout layout;
11171117
1118 bool auto_enum, is_root; // union(enum)1118 bool auto_enum, is_root; // union(enum)
1119 bool unsupported_explicit_backing_int;
1119};1120};
11201121
1121struct AstNodeErrorSetField {1122struct AstNodeErrorSetField {
src/stage1/analyze.cpp+6
...@@ -3034,6 +3034,12 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) {...@@ -3034,6 +3034,12 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) {
30343034
3035 AstNode *decl_node = struct_type->data.structure.decl_node;3035 AstNode *decl_node = struct_type->data.structure.decl_node;
30363036
3037 if (decl_node->data.container_decl.unsupported_explicit_backing_int) {
3038 add_node_error(g, decl_node, buf_create_from_str(
3039 "the stage1 compiler does not support explicit backing integer types on packed structs"));
3040 return ErrorSemanticAnalyzeFail;
3041 }
3042
3037 if (struct_type->data.structure.resolve_loop_flag_zero_bits) {3043 if (struct_type->data.structure.resolve_loop_flag_zero_bits) {
3038 if (struct_type->data.structure.resolve_status != ResolveStatusInvalid) {3044 if (struct_type->data.structure.resolve_status != ResolveStatusInvalid) {
3039 struct_type->data.structure.resolve_status = ResolveStatusInvalid;3045 struct_type->data.structure.resolve_status = ResolveStatusInvalid;
src/stage1/ir.cpp+28-12
...@@ -18640,7 +18640,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, Scope *scope, AstNode *sour...@@ -18640,7 +18640,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, Scope *scope, AstNode *sour
18640 result->special = ConstValSpecialStatic;18640 result->special = ConstValSpecialStatic;
18641 result->type = ir_type_info_get_type(ira, "Struct", nullptr);18641 result->type = ir_type_info_get_type(ira, "Struct", nullptr);
1864218642
18643 ZigValue **fields = alloc_const_vals_ptrs(g, 4);18643 ZigValue **fields = alloc_const_vals_ptrs(g, 5);
18644 result->data.x_struct.fields = fields;18644 result->data.x_struct.fields = fields;
1864518645
18646 // layout: ContainerLayout18646 // layout: ContainerLayout
...@@ -18648,8 +18648,17 @@ static Error ir_make_type_info_value(IrAnalyze *ira, Scope *scope, AstNode *sour...@@ -18648,8 +18648,17 @@ static Error ir_make_type_info_value(IrAnalyze *ira, Scope *scope, AstNode *sour
18648 fields[0]->special = ConstValSpecialStatic;18648 fields[0]->special = ConstValSpecialStatic;
18649 fields[0]->type = ir_type_info_get_type(ira, "ContainerLayout", nullptr);18649 fields[0]->type = ir_type_info_get_type(ira, "ContainerLayout", nullptr);
18650 bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.structure.layout);18650 bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.structure.layout);
18651
18652 // backing_integer: ?type
18653 ensure_field_index(result->type, "backing_integer", 1);
18654 fields[1]->special = ConstValSpecialStatic;
18655 fields[1]->type = get_optional_type(g, g->builtin_types.entry_type);
18656 // This is always null in stage1, as stage1 does not support explicit backing integers
18657 // for packed structs.
18658 fields[1]->data.x_optional = nullptr;
18659
18651 // fields: []Type.StructField18660 // fields: []Type.StructField
18652 ensure_field_index(result->type, "fields", 1);18661 ensure_field_index(result->type, "fields", 2);
1865318662
18654 ZigType *type_info_struct_field_type = ir_type_info_get_type(ira, "StructField", nullptr);18663 ZigType *type_info_struct_field_type = ir_type_info_get_type(ira, "StructField", nullptr);
18655 if ((err = type_resolve(g, type_info_struct_field_type, ResolveStatusSizeKnown))) {18664 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...@@ -18663,7 +18672,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, Scope *scope, AstNode *sour
18663 struct_field_array->data.x_array.special = ConstArraySpecialNone;18672 struct_field_array->data.x_array.special = ConstArraySpecialNone;
18664 struct_field_array->data.x_array.data.s_none.elements = g->pass1_arena->allocate<ZigValue>(struct_field_count);18673 struct_field_array->data.x_array.data.s_none.elements = g->pass1_arena->allocate<ZigValue>(struct_field_count);
1866518674
18666 init_const_slice(g, fields[1], struct_field_array, 0, struct_field_count, false, nullptr);18675 init_const_slice(g, fields[2], struct_field_array, 0, struct_field_count, false, nullptr);
1866718676
18668 for (uint32_t struct_field_index = 0; struct_field_index < struct_field_count; struct_field_index++) {18677 for (uint32_t struct_field_index = 0; struct_field_index < struct_field_count; struct_field_index++) {
18669 TypeStructField *struct_field = type_entry->data.structure.fields[struct_field_index];18678 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...@@ -18710,18 +18719,18 @@ static Error ir_make_type_info_value(IrAnalyze *ira, Scope *scope, AstNode *sour
18710 struct_field_val->parent.data.p_array.elem_index = struct_field_index;18719 struct_field_val->parent.data.p_array.elem_index = struct_field_index;
18711 }18720 }
18712 // decls: []Type.Declaration18721 // decls: []Type.Declaration
18713 ensure_field_index(result->type, "decls", 2);18722 ensure_field_index(result->type, "decls", 3);
18714 if ((err = ir_make_type_info_decls(ira, source_node, fields[2],18723 if ((err = ir_make_type_info_decls(ira, source_node, fields[3],
18715 type_entry->data.structure.decls_scope, false)))18724 type_entry->data.structure.decls_scope, false)))
18716 {18725 {
18717 return err;18726 return err;
18718 }18727 }
1871918728
18720 // is_tuple: bool18729 // is_tuple: bool
18721 ensure_field_index(result->type, "is_tuple", 3);18730 ensure_field_index(result->type, "is_tuple", 4);
18722 fields[3]->special = ConstValSpecialStatic;18731 fields[4]->special = ConstValSpecialStatic;
18723 fields[3]->type = g->builtin_types.entry_bool;18732 fields[4]->type = g->builtin_types.entry_bool;
18724 fields[3]->data.x_bool = is_tuple(type_entry);18733 fields[4]->data.x_bool = is_tuple(type_entry);
1872518734
18726 break;18735 break;
18727 }18736 }
...@@ -19313,7 +19322,14 @@ static ZigType *type_info_to_type(IrAnalyze *ira, Scope *scope, AstNode *source_...@@ -19313,7 +19322,14 @@ static ZigType *type_info_to_type(IrAnalyze *ira, Scope *scope, AstNode *source_
19313 assert(layout_value->type == ir_type_info_get_type(ira, "ContainerLayout", nullptr));19322 assert(layout_value->type == ir_type_info_get_type(ira, "ContainerLayout", nullptr));
19314 ContainerLayout layout = (ContainerLayout)bigint_as_u32(&layout_value->data.x_enum_tag);19323 ContainerLayout layout = (ContainerLayout)bigint_as_u32(&layout_value->data.x_enum_tag);
1931519324
19316 ZigValue *fields_value = get_const_field(ira, source_node, payload, "fields", 1);19325 ZigType *tag_type = get_const_field_meta_type_optional(ira, source_node, payload, "backing_integer", 1);
19326 if (tag_type != nullptr) {
19327 ir_add_error_node(ira, source_node, buf_create_from_str(
19328 "the stage1 compiler does not support explicit backing integer types on packed structs"));
19329 return ira->codegen->invalid_inst_gen->value->type;
19330 }
19331
19332 ZigValue *fields_value = get_const_field(ira, source_node, payload, "fields", 2);
19317 if (fields_value == nullptr)19333 if (fields_value == nullptr)
19318 return ira->codegen->invalid_inst_gen->value->type;19334 return ira->codegen->invalid_inst_gen->value->type;
19319 assert(fields_value->special == ConstValSpecialStatic);19335 assert(fields_value->special == ConstValSpecialStatic);
...@@ -19322,7 +19338,7 @@ static ZigType *type_info_to_type(IrAnalyze *ira, Scope *scope, AstNode *source_...@@ -19322,7 +19338,7 @@ static ZigType *type_info_to_type(IrAnalyze *ira, Scope *scope, AstNode *source_
19322 ZigValue *fields_len_value = fields_value->data.x_struct.fields[slice_len_index];19338 ZigValue *fields_len_value = fields_value->data.x_struct.fields[slice_len_index];
19323 size_t fields_len = bigint_as_usize(&fields_len_value->data.x_bigint);19339 size_t fields_len = bigint_as_usize(&fields_len_value->data.x_bigint);
1932419340
19325 ZigValue *decls_value = get_const_field(ira, source_node, payload, "decls", 2);19341 ZigValue *decls_value = get_const_field(ira, source_node, payload, "decls", 3);
19326 if (decls_value == nullptr)19342 if (decls_value == nullptr)
19327 return ira->codegen->invalid_inst_gen->value->type;19343 return ira->codegen->invalid_inst_gen->value->type;
19328 assert(decls_value->special == ConstValSpecialStatic);19344 assert(decls_value->special == ConstValSpecialStatic);
...@@ -19335,7 +19351,7 @@ static ZigType *type_info_to_type(IrAnalyze *ira, Scope *scope, AstNode *source_...@@ -19335,7 +19351,7 @@ static ZigType *type_info_to_type(IrAnalyze *ira, Scope *scope, AstNode *source_
19335 }19351 }
1933619352
19337 bool is_tuple;19353 bool is_tuple;
19338 if ((err = get_const_field_bool(ira, source_node, payload, "is_tuple", 3, &is_tuple)))19354 if ((err = get_const_field_bool(ira, source_node, payload, "is_tuple", 4, &is_tuple)))
19339 return ira->codegen->invalid_inst_gen->value->type;19355 return ira->codegen->invalid_inst_gen->value->type;
1934019356
19341 ZigType *entry = new_type_table_entry(ZigTypeIdStruct);19357 ZigType *entry = new_type_table_entry(ZigTypeIdStruct);
src/stage1/parser.cpp+10-1
...@@ -2902,16 +2902,25 @@ static AstNode *ast_parse_container_decl_auto(ParseContext *pc) {...@@ -2902,16 +2902,25 @@ static AstNode *ast_parse_container_decl_auto(ParseContext *pc) {
2902}2902}
29032903
2904// ContainerDeclType2904// ContainerDeclType
2905// <- KEYWORD_struct2905// <- KEYWORD_struct (LPAREN Expr RPAREN)?
2906// / KEYWORD_enum (LPAREN Expr RPAREN)?2906// / KEYWORD_enum (LPAREN Expr RPAREN)?
2907// / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)?2907// / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)?
2908// / KEYWORD_opaque2908// / KEYWORD_opaque
2909static AstNode *ast_parse_container_decl_type(ParseContext *pc) {2909static AstNode *ast_parse_container_decl_type(ParseContext *pc) {
2910 TokenIndex first = eat_token_if(pc, TokenIdKeywordStruct);2910 TokenIndex first = eat_token_if(pc, TokenIdKeywordStruct);
2911 if (first != 0) {2911 if (first != 0) {
2912 bool explicit_backing_int = false;
2913 if (eat_token_if(pc, TokenIdLParen) != 0) {
2914 explicit_backing_int = true;
2915 ast_expect(pc, ast_parse_expr);
2916 expect_token(pc, TokenIdRParen);
2917 }
2912 AstNode *res = ast_create_node(pc, NodeTypeContainerDecl, first);2918 AstNode *res = ast_create_node(pc, NodeTypeContainerDecl, first);
2913 res->data.container_decl.init_arg_expr = nullptr;2919 res->data.container_decl.init_arg_expr = nullptr;
2914 res->data.container_decl.kind = ContainerKindStruct;2920 res->data.container_decl.kind = ContainerKindStruct;
2921 // We want this to be an error in semantic analysis not parsing to make sharing
2922 // the test suite between stage1 and self hosted easier.
2923 res->data.container_decl.unsupported_explicit_backing_int = explicit_backing_int;
2915 return res;2924 return res;
2916 }2925 }
29172926
src/type.zig+15-8
...@@ -3000,9 +3000,17 @@ pub const Type = extern union {...@@ -3000,9 +3000,17 @@ pub const Type = extern union {
3000 .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) },3000 .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) },
3001 };3001 };
3002 if (struct_obj.layout == .Packed) {3002 if (struct_obj.layout == .Packed) {
3003 var buf: Type.Payload.Bits = undefined;3003 switch (strat) {
3004 const int_ty = struct_obj.packedIntegerType(target, &buf);3004 .sema_kit => |sk| try sk.sema.resolveTypeLayout(sk.block, sk.src, ty),
3005 return AbiAlignmentAdvanced{ .scalar = int_ty.abiAlignment(target) };3005 .lazy => |arena| {
3006 if (!struct_obj.haveLayout()) {
3007 return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) };
3008 }
3009 },
3010 .eager => {},
3011 }
3012 assert(struct_obj.haveLayout());
3013 return AbiAlignmentAdvanced{ .scalar = struct_obj.backing_int_ty.abiAlignment(target) };
3006 }3014 }
30073015
3008 const fields = ty.structFields();3016 const fields = ty.structFields();
...@@ -3192,17 +3200,16 @@ pub const Type = extern union {...@@ -3192,17 +3200,16 @@ pub const Type = extern union {
3192 .Packed => {3200 .Packed => {
3193 const struct_obj = ty.castTag(.@"struct").?.data;3201 const struct_obj = ty.castTag(.@"struct").?.data;
3194 switch (strat) {3202 switch (strat) {
3195 .sema_kit => |sk| _ = try sk.sema.resolveTypeFields(sk.block, sk.src, ty),3203 .sema_kit => |sk| try sk.sema.resolveTypeLayout(sk.block, sk.src, ty),
3196 .lazy => |arena| {3204 .lazy => |arena| {
3197 if (!struct_obj.haveFieldTypes()) {3205 if (!struct_obj.haveLayout()) {
3198 return AbiSizeAdvanced{ .val = try Value.Tag.lazy_size.create(arena, ty) };3206 return AbiSizeAdvanced{ .val = try Value.Tag.lazy_size.create(arena, ty) };
3199 }3207 }
3200 },3208 },
3201 .eager => {},3209 .eager => {},
3202 }3210 }
3203 var buf: Type.Payload.Bits = undefined;3211 assert(struct_obj.haveLayout());
3204 const int_ty = struct_obj.packedIntegerType(target, &buf);3212 return AbiSizeAdvanced{ .scalar = struct_obj.backing_int_ty.abiSize(target) };
3205 return AbiSizeAdvanced{ .scalar = int_ty.abiSize(target) };
3206 },3213 },
3207 else => {3214 else => {
3208 switch (strat) {3215 switch (strat) {
src/value.zig-38
...@@ -3472,44 +3472,6 @@ pub const Value = extern union {...@@ -3472,44 +3472,6 @@ pub const Value = extern union {
3472 return fromBigInt(allocator, result_q.toConst());3472 return fromBigInt(allocator, result_q.toConst());
3473 }3473 }
34743474
3475 pub fn intRem(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, target: Target) !Value {
3476 if (ty.zigTypeTag() == .Vector) {
3477 const result_data = try allocator.alloc(Value, ty.vectorLen());
3478 for (result_data) |*scalar, i| {
3479 scalar.* = try intRemScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator, target);
3480 }
3481 return Value.Tag.aggregate.create(allocator, result_data);
3482 }
3483 return intRemScalar(lhs, rhs, allocator, target);
3484 }
3485
3486 pub fn intRemScalar(lhs: Value, rhs: Value, allocator: Allocator, target: Target) !Value {
3487 // TODO is this a performance issue? maybe we should try the operation without
3488 // resorting to BigInt first.
3489 var lhs_space: Value.BigIntSpace = undefined;
3490 var rhs_space: Value.BigIntSpace = undefined;
3491 const lhs_bigint = lhs.toBigInt(&lhs_space, target);
3492 const rhs_bigint = rhs.toBigInt(&rhs_space, target);
3493 const limbs_q = try allocator.alloc(
3494 std.math.big.Limb,
3495 lhs_bigint.limbs.len,
3496 );
3497 const limbs_r = try allocator.alloc(
3498 std.math.big.Limb,
3499 // TODO: consider reworking Sema to re-use Values rather than
3500 // always producing new Value objects.
3501 rhs_bigint.limbs.len,
3502 );
3503 const limbs_buffer = try allocator.alloc(
3504 std.math.big.Limb,
3505 std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
3506 );
3507 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
3508 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
3509 result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);
3510 return fromBigInt(allocator, result_r.toConst());
3511 }
3512
3513 pub fn intMod(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, target: Target) !Value {3475 pub fn intMod(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, target: Target) !Value {
3514 if (ty.zigTypeTag() == .Vector) {3476 if (ty.zigTypeTag() == .Vector) {
3515 const result_data = try allocator.alloc(Value, ty.vectorLen());3477 const result_data = try allocator.alloc(Value, ty.vectorLen());
test/behavior.zig+1
...@@ -165,6 +165,7 @@ test {...@@ -165,6 +165,7 @@ test {
165165
166 if (builtin.zig_backend != .stage1) {166 if (builtin.zig_backend != .stage1) {
167 _ = @import("behavior/decltest.zig");167 _ = @import("behavior/decltest.zig");
168 _ = @import("behavior/packed_struct_explicit_backing_int.zig");
168 }169 }
169170
170 if (builtin.os.tag != .wasi) {171 if (builtin.os.tag != .wasi) {
test/behavior/math.zig+15
...@@ -1721,3 +1721,18 @@ fn testAbsFloat() !void {...@@ -1721,3 +1721,18 @@ fn testAbsFloat() !void {
1721fn testAbsFloatOne(in: f32, out: f32) !void {1721fn testAbsFloatOne(in: f32, out: f32) !void {
1722 try expect(@fabs(@as(f32, in)) == @as(f32, out));1722 try expect(@fabs(@as(f32, in)) == @as(f32, out));
1723}1723}
1724
1725test "mod lazy values" {
1726 {
1727 const X = struct { x: u32 };
1728 const x = @sizeOf(X);
1729 const y = 1 % x;
1730 _ = y;
1731 }
1732 {
1733 const X = struct { x: u32 };
1734 const x = @sizeOf(X);
1735 const y = x % 1;
1736 _ = y;
1737 }
1738}
test/behavior/packed_struct_explicit_backing_int.zig created+53
...@@ -0,0 +1,53 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const assert = std.debug.assert;
4const expectEqual = std.testing.expectEqual;
5const native_endian = builtin.cpu.arch.endian();
6
7test "packed struct explicit backing integer" {
8 assert(builtin.zig_backend != .stage1);
9 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
10 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
11 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
12 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
13 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
14
15 const S1 = packed struct { a: u8, b: u8, c: u8 };
16
17 const S2 = packed struct(i24) { d: u8, e: u8, f: u8 };
18
19 const S3 = packed struct { x: S1, y: S2 };
20 const S3Padded = packed struct(u64) { s3: S3, pad: u16 };
21
22 try expectEqual(48, @bitSizeOf(S3));
23 try expectEqual(@sizeOf(u48), @sizeOf(S3));
24
25 try expectEqual(3, @offsetOf(S3, "y"));
26 try expectEqual(24, @bitOffsetOf(S3, "y"));
27
28 if (native_endian == .Little) {
29 const s3 = @bitCast(S3Padded, @as(u64, 0xe952d5c71ff4)).s3;
30 try expectEqual(@as(u8, 0xf4), s3.x.a);
31 try expectEqual(@as(u8, 0x1f), s3.x.b);
32 try expectEqual(@as(u8, 0xc7), s3.x.c);
33 try expectEqual(@as(u8, 0xd5), s3.y.d);
34 try expectEqual(@as(u8, 0x52), s3.y.e);
35 try expectEqual(@as(u8, 0xe9), s3.y.f);
36 }
37
38 const S4 = packed struct { a: i32, b: i8 };
39 const S5 = packed struct(u80) { a: i32, b: i8, c: S4 };
40 const S6 = packed struct(i80) { a: i32, b: S4, c: i8 };
41
42 const expectedBitSize = 80;
43 const expectedByteSize = @sizeOf(u80);
44 try expectEqual(expectedBitSize, @bitSizeOf(S5));
45 try expectEqual(expectedByteSize, @sizeOf(S5));
46 try expectEqual(expectedBitSize, @bitSizeOf(S6));
47 try expectEqual(expectedByteSize, @sizeOf(S6));
48
49 try expectEqual(5, @offsetOf(S5, "c"));
50 try expectEqual(40, @bitOffsetOf(S5, "c"));
51 try expectEqual(9, @offsetOf(S6, "c"));
52 try expectEqual(72, @bitOffsetOf(S6, "c"));
53}
test/behavior/type_info.zig+3-1
...@@ -293,6 +293,7 @@ test "type info: struct info" {...@@ -293,6 +293,7 @@ test "type info: struct info" {
293fn testStruct() !void {293fn testStruct() !void {
294 const unpacked_struct_info = @typeInfo(TestStruct);294 const unpacked_struct_info = @typeInfo(TestStruct);
295 try expect(unpacked_struct_info.Struct.is_tuple == false);295 try expect(unpacked_struct_info.Struct.is_tuple == false);
296 try expect(unpacked_struct_info.Struct.backing_integer == null);
296 try expect(unpacked_struct_info.Struct.fields[0].alignment == @alignOf(u32));297 try expect(unpacked_struct_info.Struct.fields[0].alignment == @alignOf(u32));
297 try expect(@ptrCast(*const u32, unpacked_struct_info.Struct.fields[0].default_value.?).* == 4);298 try expect(@ptrCast(*const u32, unpacked_struct_info.Struct.fields[0].default_value.?).* == 4);
298 try expect(mem.eql(u8, "foobar", @ptrCast(*const *const [6:0]u8, unpacked_struct_info.Struct.fields[1].default_value.?).*));299 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 {...@@ -315,6 +316,7 @@ fn testPackedStruct() !void {
315 try expect(struct_info == .Struct);316 try expect(struct_info == .Struct);
316 try expect(struct_info.Struct.is_tuple == false);317 try expect(struct_info.Struct.is_tuple == false);
317 try expect(struct_info.Struct.layout == .Packed);318 try expect(struct_info.Struct.layout == .Packed);
319 try expect(struct_info.Struct.backing_integer == u128);
318 try expect(struct_info.Struct.fields.len == 4);320 try expect(struct_info.Struct.fields.len == 4);
319 try expect(struct_info.Struct.fields[0].alignment == 0);321 try expect(struct_info.Struct.fields[0].alignment == 0);
320 try expect(struct_info.Struct.fields[2].field_type == f32);322 try expect(struct_info.Struct.fields[2].field_type == f32);
...@@ -326,7 +328,7 @@ fn testPackedStruct() !void {...@@ -326,7 +328,7 @@ fn testPackedStruct() !void {
326}328}
327329
328const TestPackedStruct = packed struct {330const TestPackedStruct = packed struct {
329 fieldA: usize,331 fieldA: u64,
330 fieldB: void,332 fieldB: void,
331 fieldC: f32,333 fieldC: f32,
332 fieldD: u32 = 4,334 fieldD: u32 = 4,
test/cases/compile_errors/packed_struct_backing_int_wrong.zig created+55
...@@ -0,0 +1,55 @@
1export fn entry1() void {
2 _ = @sizeOf(packed struct(u32) {
3 x: u1,
4 y: u24,
5 z: u4,
6 });
7}
8export fn entry2() void {
9 _ = @sizeOf(packed struct(i31) {
10 x: u4,
11 y: u24,
12 z: u4,
13 });
14}
15
16export fn entry3() void {
17 _ = @sizeOf(packed struct(void) {
18 x: void,
19 });
20}
21
22export fn entry4() void {
23 _ = @sizeOf(packed struct(void) {});
24}
25
26export fn entry5() void {
27 _ = @sizeOf(packed struct(noreturn) {});
28}
29
30export fn entry6() void {
31 _ = @sizeOf(packed struct(f64) {
32 x: u32,
33 y: f32,
34 });
35}
36
37export fn entry7() void {
38 _ = @sizeOf(packed struct(*u32) {
39 x: u4,
40 y: u24,
41 z: u4,
42 });
43}
44
45// error
46// backend=llvm
47// target=native
48//
49// :2:31: error: backing integer type 'u32' has bit size 32 but the struct fields have a total bit size of 29
50// :9:31: error: backing integer type 'i31' has bit size 31 but the struct fields have a total bit size of 32
51// :17:31: error: expected backing integer type, found 'void'
52// :23:31: error: expected backing integer type, found 'void'
53// :27:31: error: expected backing integer type, found 'noreturn'
54// :31:31: error: expected backing integer type, found 'f64'
55// :38:31: error: expected backing integer type, found '*u32'