| author | |
| committer | |
| log | e0178890ba5ad76fdf5ba955f479ccf6f05a3d49 |
| tree | 97388d19f25afa268f68862225a3a1a478feb3f6 |
| parent | 45c444ff18b43d30a7277e346174ba6eca4a6193 |
| parent | 0d32b73078aa4579187f7d5c67343a6036eed277 |
| signature |
stage2: Implement explicit backing integers for packed structs22 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 comma | 2967 | /// 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 trailing | 2972 | /// 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 | } |
| 3357 | 3357 | ||
| 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 | /// ContainerDeclType | 3361 | /// ContainerDeclType |
| 3360 | /// <- KEYWORD_struct | 3362 | /// <- 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(); |
| 4228 | 4230 | ||
| 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(); |
| 4268 | 4272 | ||
| 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); |
| 4271 | 4304 | ||
| ... | @@ -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; |
| 4584 | 4621 | ||
| 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; |
| 11263 | 11300 | ||
| 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); |
| 11266 | 11303 | ||
| 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; |
| 2538 | 2538 | ||
| 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) = .{}; |
| 2541 | 2552 |
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, |
| 896 | 896 | ||
| 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 { |
| 1025 | 1030 | ||
| 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 | }; |
| 1051 | 1044 |
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) = .{}, |
| 78 | err: ?*Module.ErrorMsg = null, | 78 | err: ?*Module.ErrorMsg = null, |
| 79 | 79 | ||
| 80 | const std = @import("std"); | 80 | const std = @import("std"); |
| 81 | const math = std.math; | ||
| 81 | const mem = std.mem; | 82 | const mem = std.mem; |
| 82 | const Allocator = std.mem.Allocator; | 83 | const Allocator = std.mem.Allocator; |
| 83 | const assert = std.debug.assert; | 84 | const 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; |
| 2240 | 2241 | ||
| 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 | } |
| 2243 | 2254 | ||
| ... | @@ -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))) and | 11839 | 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 | } |
| 11886 | 11897 | ||
| 11898 | fn 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 | |||
| 11917 | fn 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 | |||
| 11887 | fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 11952 | fn 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 |
| 14228 | 14293 | ||
| 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()); |
| 14230 | 14295 | ||
| 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))).?); |
| 16255 | 16334 | ||
| 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); | ||
| 16425 | 16509 | ||
| 16426 | // Decls | 16510 | // 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 | } |
| 16430 | 16514 | ||
| 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 | else | 16521 | 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 | } |
| 17015 | 17104 | ||
| 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; |
| 27113 | 27242 | ||
| 27114 | // In case of querying the ABI alignment of this struct, we will ask | 27243 | // 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 anything | 27257 | // otherwise it's a tuple; no need to resolve anything |
| 27129 | } | 27258 | } |
| 27130 | 27259 | ||
| 27260 | fn 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 | |||
| 27347 | fn 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 | |||
| 27131 | fn resolveUnionLayout( | 27363 | fn 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; |
| 27452 | 27684 | ||
| 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; |
| 27457 | 27700 | ||
| 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_node | 3085 | /// 0. src_node: i32, // if has_src_node |
| 3086 | /// 1. fields_len: u32, // if has_fields_len | 3086 | /// 1. fields_len: u32, // if has_fields_len |
| 3087 | /// 2. decls_len: u32, // if has_decls_len | 3087 | /// 2. decls_len: u32, // if has_decls_len |
| 3088 | /// 3. decl_bits: u32 // for every 8 decls | 3088 | /// 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 pub | 3093 | /// 0b000X: whether corresponding decl is pub |
| 3091 | /// 0b00X0: whether corresponding decl is exported | 3094 | /// 0b00X0: whether corresponding decl is exported |
| 3092 | /// 0b0X00: whether corresponding decl has an align expression | 3095 | /// 0b0X00: whether corresponding decl has an align expression |
| 3093 | /// 0bX000: whether corresponding decl has a linksection or an address space expression | 3096 | /// 0bX000: whether corresponding decl has a linksection or an address space expression |
| 3094 | /// 4. decl: { // for every decls_len | 3097 | /// 7. decl: { // for every decls_len |
| 3095 | /// src_hash: [4]u32, // hash of source bytes | 3098 | /// src_hash: [4]u32, // hash of source bytes |
| 3096 | /// line: u32, // line number of decl, relative to parent | 3099 | /// line: u32, // line number of decl, relative to parent |
| 3097 | /// name: u32, // null terminated string index | 3100 | /// 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 fields | 3115 | /// 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 expression | 3117 | /// 0b000X: whether corresponding field has an align expression |
| 3115 | /// 0b00X0: whether corresponding field has a default expression | 3118 | /// 0b00X0: whether corresponding field has a default expression |
| 3116 | /// 0b0X00: whether corresponding field is comptime | 3119 | /// 0b0X00: whether corresponding field is comptime |
| 3117 | /// 0bX000: whether corresponding field has a type expression | 3120 | /// 0bX000: whether corresponding field has a type expression |
| 3118 | /// 6. fields: { // for every fields_len | 3121 | /// 9. fields: { // for every fields_len |
| 3119 | /// field_name: u32, | 3122 | /// field_name: u32, |
| 3120 | /// doc_comment: u32, // 0 if no doc comment | 3123 | /// 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 set | 3126 | /// align_body_len: u32, // if corresponding bit is set |
| 3124 | /// init_body_len: u32, // if corresponding bit is set | 3127 | /// init_body_len: u32, // if corresponding bit is set |
| 3125 | /// } | 3128 | /// } |
| 3126 | /// 7. bodies: { // for every fields_len | 3129 | /// 10. bodies: { // for every fields_len |
| 3127 | /// field_type_body_inst: Inst, // for each field_type_body_len | 3130 | /// field_type_body_inst: Inst, // for each field_type_body_len |
| 3128 | /// align_body_inst: Inst, // for each align_body_len | 3131 | /// align_body_inst: Inst, // for each align_body_len |
| 3129 | /// init_body_inst: Inst, // for each init_body_len | 3132 | /// 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 | }; |
| 3143 | 3147 |
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; |
| 2680 | 2679 | ||
| 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; |
| 3331 | 3328 | ||
| 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 { |
| 1245 | 1245 | ||
| 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 | } | ||
| 1251 | 1270 | ||
| 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; |
| 1117 | 1117 | ||
| 1118 | bool auto_enum, is_root; // union(enum) | 1118 | bool auto_enum, is_root; // union(enum) |
| 1119 | bool unsupported_explicit_backing_int; | ||
| 1119 | }; | 1120 | }; |
| 1120 | 1121 | ||
| 1121 | struct AstNodeErrorSetField { | 1122 | struct 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) { |
| 3034 | 3034 | ||
| 3035 | AstNode *decl_node = struct_type->data.structure.decl_node; | 3035 | AstNode *decl_node = struct_type->data.structure.decl_node; |
| 3036 | 3036 | ||
| 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); |
| 18642 | 18642 | ||
| 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; |
| 18645 | 18645 | ||
| 18646 | // layout: ContainerLayout | 18646 | // 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.StructField | 18660 | // fields: []Type.StructField |
| 18652 | ensure_field_index(result->type, "fields", 1); | 18661 | ensure_field_index(result->type, "fields", 2); |
| 18653 | 18662 | ||
| 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); |
| 18665 | 18674 | ||
| 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); |
| 18667 | 18676 | ||
| 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.Declaration | 18721 | // 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 | } |
| 18719 | 18728 | ||
| 18720 | // is_tuple: bool | 18729 | // 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); |
| 18725 | 18734 | ||
| 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); |
| 19315 | 19324 | ||
| 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); |
| 19324 | 19340 | ||
| 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 | } |
| 19336 | 19352 | ||
| 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; |
| 19340 | 19356 | ||
| 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 | } |
| 2903 | 2903 | ||
| 2904 | // ContainerDeclType | 2904 | // ContainerDeclType |
| 2905 | // <- KEYWORD_struct | 2905 | // <- 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_opaque | 2908 | // / KEYWORD_opaque |
| 2909 | static AstNode *ast_parse_container_decl_type(ParseContext *pc) { | 2909 | static 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 | } |
| 2917 | 2926 |
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 | } |
| 3007 | 3015 | ||
| 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 | } |
| 3474 | 3474 | ||
| 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 { |
| 165 | 165 | ||
| 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 | } |
| 169 | 170 | ||
| 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 { |
| 1721 | fn testAbsFloatOne(in: f32, out: f32) !void { | 1721 | fn 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 | |||
| 1725 | test "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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const assert = std.debug.assert; | ||
| 4 | const expectEqual = std.testing.expectEqual; | ||
| 5 | const native_endian = builtin.cpu.arch.endian(); | ||
| 6 | |||
| 7 | test "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" { |
| 293 | fn testStruct() !void { | 293 | fn 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 | } |
| 327 | 329 | ||
| 328 | const TestPackedStruct = packed struct { | 330 | const 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 @@ | ||
| 1 | export fn entry1() void { | ||
| 2 | _ = @sizeOf(packed struct(u32) { | ||
| 3 | x: u1, | ||
| 4 | y: u24, | ||
| 5 | z: u4, | ||
| 6 | }); | ||
| 7 | } | ||
| 8 | export fn entry2() void { | ||
| 9 | _ = @sizeOf(packed struct(i31) { | ||
| 10 | x: u4, | ||
| 11 | y: u24, | ||
| 12 | z: u4, | ||
| 13 | }); | ||
| 14 | } | ||
| 15 | |||
| 16 | export fn entry3() void { | ||
| 17 | _ = @sizeOf(packed struct(void) { | ||
| 18 | x: void, | ||
| 19 | }); | ||
| 20 | } | ||
| 21 | |||
| 22 | export fn entry4() void { | ||
| 23 | _ = @sizeOf(packed struct(void) {}); | ||
| 24 | } | ||
| 25 | |||
| 26 | export fn entry5() void { | ||
| 27 | _ = @sizeOf(packed struct(noreturn) {}); | ||
| 28 | } | ||
| 29 | |||
| 30 | export fn entry6() void { | ||
| 31 | _ = @sizeOf(packed struct(f64) { | ||
| 32 | x: u32, | ||
| 33 | y: f32, | ||
| 34 | }); | ||
| 35 | } | ||
| 36 | |||
| 37 | export 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' | ||