| author | |
| committer | |
| log | a3104a4a78089f3260c0dd3f4a96012c6d73a63b |
| tree | 63d73840d9d48d09cd27c3fef5e52f051a2dc4bd |
| parent | 7f006287ae89b97ff0ca977f64744ea344ec94fd |
* ZIR: the `array_type_sentinel` now has a source node attached to it
for proper error reporting.
* Refactor: move `Module.arrayType` to `Type.array`
* Value: the `bytes` and `array` tags now include the sentinel, if the
type has one. This simplifies comptime evaluation logic.
* Sema: fix `zirStructInitEmpty` to properly handle when the type is
void or a sentinel-terminated array. This handles the syntax `void{}`
and `[0:X]T{}`.
* Sema: fix the logic for reporting "cannot store runtime value in
compile time variable" as well as for emitting a runtime store when a
pointer value is comptime known but it is a global variable.
* Sema: implement elemVal for double pointer to array. This can happen
with this code for example: `var a: *[1]u8 = undefined; _ = a[0];`
* Sema: Rework the `storePtrVal` function to properly handle nested
structs and arrays.
- Also it now handles comptime stores through a bitcasted pointer.
When the pointer element type and the type according to the Decl
don't match, the element value is bitcasted before storage.11 files changed, 610 insertions(+), 273 deletions(-)
src/AstGen.zig+25-31| ... | ... | @@ -1235,7 +1235,15 @@ fn arrayInitExpr( |
| 1235 | 1235 | }; |
| 1236 | 1236 | } else { |
| 1237 | 1237 | const sentinel = try comptimeExpr(gz, scope, .{ .ty = elem_type }, array_type.ast.sentinel); |
| 1238 | const array_type_inst = try gz.addArrayTypeSentinel(len_inst, elem_type, sentinel); | |
| 1238 | const array_type_inst = try gz.addPlNode( | |
| 1239 | .array_type_sentinel, | |
| 1240 | array_init.ast.type_expr, | |
| 1241 | Zir.Inst.ArrayTypeSentinel{ | |
| 1242 | .len = len_inst, | |
| 1243 | .elem_type = elem_type, | |
| 1244 | .sentinel = sentinel, | |
| 1245 | }, | |
| 1246 | ); | |
| 1239 | 1247 | break :inst .{ |
| 1240 | 1248 | .array = array_type_inst, |
| 1241 | 1249 | .elem = elem_type, |
| ... | ... | @@ -1425,7 +1433,15 @@ fn structInitExpr( |
| 1425 | 1433 | break :blk try gz.addBin(.array_type, .zero_usize, elem_type); |
| 1426 | 1434 | } else blk: { |
| 1427 | 1435 | const sentinel = try comptimeExpr(gz, scope, .{ .ty = elem_type }, array_type.ast.sentinel); |
| 1428 | break :blk try gz.addArrayTypeSentinel(.zero_usize, elem_type, sentinel); | |
| 1436 | break :blk try gz.addPlNode( | |
| 1437 | .array_type_sentinel, | |
| 1438 | struct_init.ast.type_expr, | |
| 1439 | Zir.Inst.ArrayTypeSentinel{ | |
| 1440 | .len = .zero_usize, | |
| 1441 | .elem_type = elem_type, | |
| 1442 | .sentinel = sentinel, | |
| 1443 | }, | |
| 1444 | ); | |
| 1429 | 1445 | }; |
| 1430 | 1446 | const result = try gz.addUnNode(.struct_init_empty, array_type_inst, node); |
| 1431 | 1447 | return rvalue(gz, rl, result, node); |
| ... | ... | @@ -2976,11 +2992,15 @@ fn arrayTypeSentinel(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.I |
| 2976 | 2992 | { |
| 2977 | 2993 | return astgen.failNode(len_node, "unable to infer array size", .{}); |
| 2978 | 2994 | } |
| 2979 | const len = try expr(gz, scope, .{ .coerced_ty = .usize_type }, len_node); | |
| 2995 | const len = try reachableExpr(gz, scope, .{ .coerced_ty = .usize_type }, len_node, node); | |
| 2980 | 2996 | const elem_type = try typeExpr(gz, scope, extra.elem_type); |
| 2981 | const sentinel = try expr(gz, scope, .{ .coerced_ty = elem_type }, extra.sentinel); | |
| 2997 | const sentinel = try reachableExpr(gz, scope, .{ .coerced_ty = elem_type }, extra.sentinel, node); | |
| 2982 | 2998 | |
| 2983 | const result = try gz.addArrayTypeSentinel(len, elem_type, sentinel); | |
| 2999 | const result = try gz.addPlNode(.array_type_sentinel, node, Zir.Inst.ArrayTypeSentinel{ | |
| 3000 | .len = len, | |
| 3001 | .elem_type = elem_type, | |
| 3002 | .sentinel = sentinel, | |
| 3003 | }); | |
| 2984 | 3004 | return rvalue(gz, rl, result, node); |
| 2985 | 3005 | } |
| 2986 | 3006 | |
| ... | ... | @@ -10017,32 +10037,6 @@ const GenZir = struct { |
| 10017 | 10037 | return indexToRef(new_index); |
| 10018 | 10038 | } |
| 10019 | 10039 | |
| 10020 | fn addArrayTypeSentinel( | |
| 10021 | gz: *GenZir, | |
| 10022 | len: Zir.Inst.Ref, | |
| 10023 | sentinel: Zir.Inst.Ref, | |
| 10024 | elem_type: Zir.Inst.Ref, | |
| 10025 | ) !Zir.Inst.Ref { | |
| 10026 | const gpa = gz.astgen.gpa; | |
| 10027 | try gz.instructions.ensureUnusedCapacity(gpa, 1); | |
| 10028 | try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); | |
| 10029 | ||
| 10030 | const payload_index = try gz.astgen.addExtra(Zir.Inst.ArrayTypeSentinel{ | |
| 10031 | .sentinel = sentinel, | |
| 10032 | .elem_type = elem_type, | |
| 10033 | }); | |
| 10034 | const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len); | |
| 10035 | gz.astgen.instructions.appendAssumeCapacity(.{ | |
| 10036 | .tag = .array_type_sentinel, | |
| 10037 | .data = .{ .array_type_sentinel = .{ | |
| 10038 | .len = len, | |
| 10039 | .payload_index = payload_index, | |
| 10040 | } }, | |
| 10041 | }); | |
| 10042 | gz.instructions.appendAssumeCapacity(new_index); | |
| 10043 | return indexToRef(new_index); | |
| 10044 | } | |
| 10045 | ||
| 10046 | 10040 | fn addUnTok( |
| 10047 | 10041 | gz: *GenZir, |
| 10048 | 10042 | tag: Zir.Inst.Tag, |
src/Module.zig+70-30| ... | ... | @@ -1885,6 +1885,55 @@ pub const SrcLoc = struct { |
| 1885 | 1885 | const token_starts = tree.tokens.items(.start); |
| 1886 | 1886 | return token_starts[tok_index]; |
| 1887 | 1887 | }, |
| 1888 | ||
| 1889 | .node_offset_array_type_len => |node_off| { | |
| 1890 | const tree = try src_loc.file_scope.getTree(gpa); | |
| 1891 | const node_tags = tree.nodes.items(.tag); | |
| 1892 | const parent_node = src_loc.declRelativeToNodeIndex(node_off); | |
| 1893 | ||
| 1894 | const full: Ast.full.ArrayType = switch (node_tags[parent_node]) { | |
| 1895 | .array_type => tree.arrayType(parent_node), | |
| 1896 | .array_type_sentinel => tree.arrayTypeSentinel(parent_node), | |
| 1897 | else => unreachable, | |
| 1898 | }; | |
| 1899 | const node = full.ast.elem_count; | |
| 1900 | const main_tokens = tree.nodes.items(.main_token); | |
| 1901 | const tok_index = main_tokens[node]; | |
| 1902 | const token_starts = tree.tokens.items(.start); | |
| 1903 | return token_starts[tok_index]; | |
| 1904 | }, | |
| 1905 | .node_offset_array_type_sentinel => |node_off| { | |
| 1906 | const tree = try src_loc.file_scope.getTree(gpa); | |
| 1907 | const node_tags = tree.nodes.items(.tag); | |
| 1908 | const parent_node = src_loc.declRelativeToNodeIndex(node_off); | |
| 1909 | ||
| 1910 | const full: Ast.full.ArrayType = switch (node_tags[parent_node]) { | |
| 1911 | .array_type => tree.arrayType(parent_node), | |
| 1912 | .array_type_sentinel => tree.arrayTypeSentinel(parent_node), | |
| 1913 | else => unreachable, | |
| 1914 | }; | |
| 1915 | const node = full.ast.sentinel; | |
| 1916 | const main_tokens = tree.nodes.items(.main_token); | |
| 1917 | const tok_index = main_tokens[node]; | |
| 1918 | const token_starts = tree.tokens.items(.start); | |
| 1919 | return token_starts[tok_index]; | |
| 1920 | }, | |
| 1921 | .node_offset_array_type_elem => |node_off| { | |
| 1922 | const tree = try src_loc.file_scope.getTree(gpa); | |
| 1923 | const node_tags = tree.nodes.items(.tag); | |
| 1924 | const parent_node = src_loc.declRelativeToNodeIndex(node_off); | |
| 1925 | ||
| 1926 | const full: Ast.full.ArrayType = switch (node_tags[parent_node]) { | |
| 1927 | .array_type => tree.arrayType(parent_node), | |
| 1928 | .array_type_sentinel => tree.arrayTypeSentinel(parent_node), | |
| 1929 | else => unreachable, | |
| 1930 | }; | |
| 1931 | const node = full.ast.elem_type; | |
| 1932 | const main_tokens = tree.nodes.items(.main_token); | |
| 1933 | const tok_index = main_tokens[node]; | |
| 1934 | const token_starts = tree.tokens.items(.start); | |
| 1935 | return token_starts[tok_index]; | |
| 1936 | }, | |
| 1888 | 1937 | } |
| 1889 | 1938 | } |
| 1890 | 1939 | |
| ... | ... | @@ -2085,6 +2134,24 @@ pub const LazySrcLoc = union(enum) { |
| 2085 | 2134 | /// expression AST node. Next, navigate to the string literal of the `extern "foo"`. |
| 2086 | 2135 | /// The Decl is determined contextually. |
| 2087 | 2136 | node_offset_lib_name: i32, |
| 2137 | /// The source location points to the len expression of an `[N:S]T` | |
| 2138 | /// expression, found by taking this AST node index offset from the containing | |
| 2139 | /// Decl AST node, which points to an `[N:S]T` expression AST node. Next, navigate | |
| 2140 | /// to the len expression. | |
| 2141 | /// The Decl is determined contextually. | |
| 2142 | node_offset_array_type_len: i32, | |
| 2143 | /// The source location points to the sentinel expression of an `[N:S]T` | |
| 2144 | /// expression, found by taking this AST node index offset from the containing | |
| 2145 | /// Decl AST node, which points to an `[N:S]T` expression AST node. Next, navigate | |
| 2146 | /// to the sentinel expression. | |
| 2147 | /// The Decl is determined contextually. | |
| 2148 | node_offset_array_type_sentinel: i32, | |
| 2149 | /// The source location points to the elem expression of an `[N:S]T` | |
| 2150 | /// expression, found by taking this AST node index offset from the containing | |
| 2151 | /// Decl AST node, which points to an `[N:S]T` expression AST node. Next, navigate | |
| 2152 | /// to the elem expression. | |
| 2153 | /// The Decl is determined contextually. | |
| 2154 | node_offset_array_type_elem: i32, | |
| 2088 | 2155 | |
| 2089 | 2156 | /// Upgrade to a `SrcLoc` based on the `Decl` provided. |
| 2090 | 2157 | pub fn toSrcLoc(lazy: LazySrcLoc, decl: *Decl) SrcLoc { |
| ... | ... | @@ -2130,6 +2197,9 @@ pub const LazySrcLoc = union(enum) { |
| 2130 | 2197 | .node_offset_fn_type_ret_ty, |
| 2131 | 2198 | .node_offset_anyframe_type, |
| 2132 | 2199 | .node_offset_lib_name, |
| 2200 | .node_offset_array_type_len, | |
| 2201 | .node_offset_array_type_sentinel, | |
| 2202 | .node_offset_array_type_elem, | |
| 2133 | 2203 | => .{ |
| 2134 | 2204 | .file_scope = decl.getFileScope(), |
| 2135 | 2205 | .parent_decl_node = decl.src_node, |
| ... | ... | @@ -4125,36 +4195,6 @@ pub fn optionalType(arena: *Allocator, child_type: Type) Allocator.Error!Type { |
| 4125 | 4195 | } |
| 4126 | 4196 | } |
| 4127 | 4197 | |
| 4128 | pub fn arrayType( | |
| 4129 | arena: *Allocator, | |
| 4130 | len: u64, | |
| 4131 | sentinel: ?Value, | |
| 4132 | elem_type: Type, | |
| 4133 | ) Allocator.Error!Type { | |
| 4134 | if (elem_type.eql(Type.initTag(.u8))) { | |
| 4135 | if (sentinel) |some| { | |
| 4136 | if (some.eql(Value.initTag(.zero), elem_type)) { | |
| 4137 | return Type.Tag.array_u8_sentinel_0.create(arena, len); | |
| 4138 | } | |
| 4139 | } else { | |
| 4140 | return Type.Tag.array_u8.create(arena, len); | |
| 4141 | } | |
| 4142 | } | |
| 4143 | ||
| 4144 | if (sentinel) |some| { | |
| 4145 | return Type.Tag.array_sentinel.create(arena, .{ | |
| 4146 | .len = len, | |
| 4147 | .sentinel = some, | |
| 4148 | .elem_type = elem_type, | |
| 4149 | }); | |
| 4150 | } | |
| 4151 | ||
| 4152 | return Type.Tag.array.create(arena, .{ | |
| 4153 | .len = len, | |
| 4154 | .elem_type = elem_type, | |
| 4155 | }); | |
| 4156 | } | |
| 4157 | ||
| 4158 | 4198 | pub fn errorUnionType( |
| 4159 | 4199 | arena: *Allocator, |
| 4160 | 4200 | error_set: Type, |
src/Sema.zig+309-86| ... | ... | @@ -1021,7 +1021,7 @@ fn resolveConstString( |
| 1021 | 1021 | const wanted_type = Type.initTag(.const_slice_u8); |
| 1022 | 1022 | const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); |
| 1023 | 1023 | const val = try sema.resolveConstValue(block, src, coerced_inst); |
| 1024 | return val.toAllocatedBytes(sema.arena); | |
| 1024 | return val.toAllocatedBytes(wanted_type, sema.arena); | |
| 1025 | 1025 | } |
| 1026 | 1026 | |
| 1027 | 1027 | pub fn resolveType(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type { |
| ... | ... | @@ -2436,10 +2436,10 @@ fn zirStr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins |
| 2436 | 2436 | var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); |
| 2437 | 2437 | errdefer new_decl_arena.deinit(); |
| 2438 | 2438 | |
| 2439 | const bytes = try new_decl_arena.allocator.dupe(u8, zir_bytes); | |
| 2439 | const bytes = try new_decl_arena.allocator.dupeZ(u8, zir_bytes); | |
| 2440 | 2440 | |
| 2441 | 2441 | const decl_ty = try Type.Tag.array_u8_sentinel_0.create(&new_decl_arena.allocator, bytes.len); |
| 2442 | const decl_val = try Value.Tag.bytes.create(&new_decl_arena.allocator, bytes); | |
| 2442 | const decl_val = try Value.Tag.bytes.create(&new_decl_arena.allocator, bytes[0 .. bytes.len + 1]); | |
| 2443 | 2443 | |
| 2444 | 2444 | const new_decl = try sema.mod.createAnonymousDecl(block, .{ |
| 2445 | 2445 | .ty = decl_ty, |
| ... | ... | @@ -3747,7 +3747,7 @@ fn analyzeCall( |
| 3747 | 3747 | .code = fn_zir, |
| 3748 | 3748 | .owner_decl = new_decl, |
| 3749 | 3749 | .func = null, |
| 3750 | .fn_ret_ty = Type.initTag(.void), | |
| 3750 | .fn_ret_ty = Type.void, | |
| 3751 | 3751 | .owner_func = null, |
| 3752 | 3752 | .comptime_args = try new_decl_arena.allocator.alloc(TypedValue, uncasted_args.len), |
| 3753 | 3753 | .comptime_args_fn_inst = module_fn.zir_body_inst, |
| ... | ... | @@ -4040,9 +4040,9 @@ fn zirArrayType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A |
| 4040 | 4040 | defer tracy.end(); |
| 4041 | 4041 | |
| 4042 | 4042 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; |
| 4043 | const len = try sema.resolveInt(block, .unneeded, bin_inst.lhs, Type.initTag(.usize)); | |
| 4043 | const len = try sema.resolveInt(block, .unneeded, bin_inst.lhs, Type.usize); | |
| 4044 | 4044 | const elem_type = try sema.resolveType(block, .unneeded, bin_inst.rhs); |
| 4045 | const array_ty = try Module.arrayType(sema.arena, len, null, elem_type); | |
| 4045 | const array_ty = try Type.array(sema.arena, len, null, elem_type); | |
| 4046 | 4046 | |
| 4047 | 4047 | return sema.addType(array_ty); |
| 4048 | 4048 | } |
| ... | ... | @@ -4051,14 +4051,17 @@ fn zirArrayTypeSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compil |
| 4051 | 4051 | const tracy = trace(@src()); |
| 4052 | 4052 | defer tracy.end(); |
| 4053 | 4053 | |
| 4054 | const inst_data = sema.code.instructions.items(.data)[inst].array_type_sentinel; | |
| 4055 | const len = try sema.resolveInt(block, .unneeded, inst_data.len, Type.initTag(.usize)); | |
| 4054 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | |
| 4056 | 4055 | const extra = sema.code.extraData(Zir.Inst.ArrayTypeSentinel, inst_data.payload_index).data; |
| 4057 | const elem_type = try sema.resolveType(block, .unneeded, extra.elem_type); | |
| 4056 | const len_src: LazySrcLoc = .{ .node_offset_array_type_len = inst_data.src_node }; | |
| 4057 | const sentinel_src: LazySrcLoc = .{ .node_offset_array_type_sentinel = inst_data.src_node }; | |
| 4058 | const elem_src: LazySrcLoc = .{ .node_offset_array_type_elem = inst_data.src_node }; | |
| 4059 | const len = try sema.resolveInt(block, len_src, extra.len, Type.usize); | |
| 4060 | const elem_type = try sema.resolveType(block, elem_src, extra.elem_type); | |
| 4058 | 4061 | const uncasted_sentinel = sema.resolveInst(extra.sentinel); |
| 4059 | const sentinel = try sema.coerce(block, elem_type, uncasted_sentinel, .unneeded); | |
| 4060 | const sentinel_val = try sema.resolveConstValue(block, .unneeded, sentinel); | |
| 4061 | const array_ty = try Module.arrayType(sema.arena, len, sentinel_val, elem_type); | |
| 4062 | const sentinel = try sema.coerce(block, elem_type, uncasted_sentinel, sentinel_src); | |
| 4063 | const sentinel_val = try sema.resolveConstValue(block, sentinel_src, sentinel); | |
| 4064 | const array_ty = try Type.array(sema.arena, len, sentinel_val, elem_type); | |
| 4062 | 4065 | |
| 4063 | 4066 | return sema.addType(array_ty); |
| 4064 | 4067 | } |
| ... | ... | @@ -4658,7 +4661,7 @@ fn funcCommon( |
| 4658 | 4661 | // the function as generic. |
| 4659 | 4662 | var is_generic = false; |
| 4660 | 4663 | const bare_return_type: Type = ret_ty: { |
| 4661 | if (ret_ty_body.len == 0) break :ret_ty Type.initTag(.void); | |
| 4664 | if (ret_ty_body.len == 0) break :ret_ty Type.void; | |
| 4662 | 4665 | |
| 4663 | 4666 | const err = err: { |
| 4664 | 4667 | // Make sure any nested param instructions don't clobber our work. |
| ... | ... | @@ -6560,13 +6563,14 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 6560 | 6563 | if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| { |
| 6561 | 6564 | if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| { |
| 6562 | 6565 | const final_len = lhs_info.len + rhs_info.len; |
| 6566 | const final_len_including_sent = final_len + @boolToInt(res_sent != null); | |
| 6563 | 6567 | const is_pointer = lhs_ty.zigTypeTag() == .Pointer; |
| 6564 | 6568 | var anon_decl = try block.startAnonDecl(); |
| 6565 | 6569 | defer anon_decl.deinit(); |
| 6566 | 6570 | |
| 6567 | 6571 | const lhs_sub_val = if (is_pointer) (try lhs_val.pointerDeref(anon_decl.arena())).? else lhs_val; |
| 6568 | 6572 | const rhs_sub_val = if (is_pointer) (try rhs_val.pointerDeref(anon_decl.arena())).? else rhs_val; |
| 6569 | const buf = try anon_decl.arena().alloc(Value, final_len); | |
| 6573 | const buf = try anon_decl.arena().alloc(Value, final_len_including_sent); | |
| 6570 | 6574 | { |
| 6571 | 6575 | var i: u64 = 0; |
| 6572 | 6576 | while (i < lhs_info.len) : (i += 1) { |
| ... | ... | @@ -6581,10 +6585,17 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 6581 | 6585 | buf[lhs_info.len + i] = try val.copy(anon_decl.arena()); |
| 6582 | 6586 | } |
| 6583 | 6587 | } |
| 6584 | const ty = if (res_sent) |rs| | |
| 6585 | try Type.Tag.array_sentinel.create(anon_decl.arena(), .{ .len = final_len, .elem_type = lhs_info.elem_type, .sentinel = rs }) | |
| 6586 | else | |
| 6587 | try Type.Tag.array.create(anon_decl.arena(), .{ .len = final_len, .elem_type = lhs_info.elem_type }); | |
| 6588 | const ty = if (res_sent) |rs| ty: { | |
| 6589 | buf[final_len] = try rs.copy(anon_decl.arena()); | |
| 6590 | break :ty try Type.Tag.array_sentinel.create(anon_decl.arena(), .{ | |
| 6591 | .len = final_len, | |
| 6592 | .elem_type = lhs_info.elem_type, | |
| 6593 | .sentinel = rs, | |
| 6594 | }); | |
| 6595 | } else try Type.Tag.array.create(anon_decl.arena(), .{ | |
| 6596 | .len = final_len, | |
| 6597 | .elem_type = lhs_info.elem_type, | |
| 6598 | }); | |
| 6588 | 6599 | const val = try Value.Tag.array.create(anon_decl.arena(), buf); |
| 6589 | 6600 | return if (is_pointer) |
| 6590 | 6601 | sema.analyzeDeclRef(try anon_decl.finish(ty, val)) |
| ... | ... | @@ -6623,20 +6634,31 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 6623 | 6634 | const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; |
| 6624 | 6635 | |
| 6625 | 6636 | // In `**` rhs has to be comptime-known, but lhs can be runtime-known |
| 6626 | const tomulby = try sema.resolveInt(block, rhs_src, extra.rhs, Type.initTag(.usize)); | |
| 6637 | const tomulby = try sema.resolveInt(block, rhs_src, extra.rhs, Type.usize); | |
| 6627 | 6638 | const mulinfo = getArrayCatInfo(lhs_ty) orelse |
| 6628 | 6639 | return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty}); |
| 6629 | 6640 | |
| 6630 | const final_len = std.math.mul(u64, mulinfo.len, tomulby) catch return sema.fail(block, rhs_src, "operation results in overflow", .{}); | |
| 6641 | const final_len = std.math.mul(u64, mulinfo.len, tomulby) catch | |
| 6642 | return sema.fail(block, rhs_src, "operation results in overflow", .{}); | |
| 6643 | const final_len_including_sent = final_len + @boolToInt(mulinfo.sentinel != null); | |
| 6644 | ||
| 6631 | 6645 | if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| { |
| 6632 | 6646 | var anon_decl = try block.startAnonDecl(); |
| 6633 | 6647 | defer anon_decl.deinit(); |
| 6648 | ||
| 6634 | 6649 | const lhs_sub_val = if (lhs_ty.zigTypeTag() == .Pointer) (try lhs_val.pointerDeref(anon_decl.arena())).? else lhs_val; |
| 6635 | 6650 | const final_ty = if (mulinfo.sentinel) |sent| |
| 6636 | try Type.Tag.array_sentinel.create(anon_decl.arena(), .{ .len = final_len, .elem_type = mulinfo.elem_type, .sentinel = sent }) | |
| 6651 | try Type.Tag.array_sentinel.create(anon_decl.arena(), .{ | |
| 6652 | .len = final_len, | |
| 6653 | .elem_type = mulinfo.elem_type, | |
| 6654 | .sentinel = sent, | |
| 6655 | }) | |
| 6637 | 6656 | else |
| 6638 | try Type.Tag.array.create(anon_decl.arena(), .{ .len = final_len, .elem_type = mulinfo.elem_type }); | |
| 6639 | const buf = try anon_decl.arena().alloc(Value, final_len); | |
| 6657 | try Type.Tag.array.create(anon_decl.arena(), .{ | |
| 6658 | .len = final_len, | |
| 6659 | .elem_type = mulinfo.elem_type, | |
| 6660 | }); | |
| 6661 | const buf = try anon_decl.arena().alloc(Value, final_len_including_sent); | |
| 6640 | 6662 | |
| 6641 | 6663 | // handles the optimisation where arr.len == 0 : [_]T { X } ** N |
| 6642 | 6664 | const val = if (mulinfo.len == 1) blk: { |
| ... | ... | @@ -6652,6 +6674,9 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 6652 | 6674 | buf[mulinfo.len * i + j] = try val.copy(anon_decl.arena()); |
| 6653 | 6675 | } |
| 6654 | 6676 | } |
| 6677 | if (mulinfo.sentinel) |sent| { | |
| 6678 | buf[final_len] = try sent.copy(anon_decl.arena()); | |
| 6679 | } | |
| 6655 | 6680 | break :blk try Value.Tag.array.create(anon_decl.arena(), buf); |
| 6656 | 6681 | }; |
| 6657 | 6682 | if (lhs_ty.zigTypeTag() == .Pointer) { |
| ... | ... | @@ -6760,7 +6785,7 @@ fn analyzeArithmetic( |
| 6760 | 6785 | }; |
| 6761 | 6786 | // TODO if the operand is comptime-known to be negative, or is a negative int, |
| 6762 | 6787 | // coerce to isize instead of usize. |
| 6763 | const casted_rhs = try sema.coerce(block, Type.initTag(.usize), rhs, rhs_src); | |
| 6788 | const casted_rhs = try sema.coerce(block, Type.usize, rhs, rhs_src); | |
| 6764 | 6789 | const runtime_src = runtime_src: { |
| 6765 | 6790 | if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| { |
| 6766 | 6791 | if (try sema.resolveDefinedValue(block, rhs_src, casted_rhs)) |rhs_val| { |
| ... | ... | @@ -8521,9 +8546,21 @@ fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE |
| 8521 | 8546 | |
| 8522 | 8547 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 8523 | 8548 | const src = inst_data.src(); |
| 8524 | const struct_type = try sema.resolveType(block, src, inst_data.operand); | |
| 8549 | const obj_ty = try sema.resolveType(block, src, inst_data.operand); | |
| 8525 | 8550 | |
| 8526 | return sema.addConstant(struct_type, Value.initTag(.empty_struct_value)); | |
| 8551 | switch (obj_ty.zigTypeTag()) { | |
| 8552 | .Struct => return sema.addConstant(obj_ty, Value.initTag(.empty_struct_value)), | |
| 8553 | .Array => { | |
| 8554 | if (obj_ty.sentinel()) |sentinel| { | |
| 8555 | const val = try Value.Tag.empty_array_sentinel.create(sema.arena, sentinel); | |
| 8556 | return sema.addConstant(obj_ty, val); | |
| 8557 | } else { | |
| 8558 | return sema.addConstant(obj_ty, Value.initTag(.empty_array)); | |
| 8559 | } | |
| 8560 | }, | |
| 8561 | .Void => return sema.addConstant(obj_ty, Value.void), | |
| 8562 | else => unreachable, | |
| 8563 | } | |
| 8527 | 8564 | } |
| 8528 | 8565 | |
| 8529 | 8566 | fn zirUnionInitPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | ... | @@ -8973,7 +9010,7 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 8973 | 9010 | |
| 8974 | 9011 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; |
| 8975 | 9012 | const operand_res = sema.resolveInst(extra.rhs); |
| 8976 | const operand_coerced = try sema.coerce(block, Type.initTag(.usize), operand_res, operand_src); | |
| 9013 | const operand_coerced = try sema.coerce(block, Type.usize, operand_res, operand_src); | |
| 8977 | 9014 | |
| 8978 | 9015 | const type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| 8979 | 9016 | const type_res = try sema.resolveType(block, src, extra.lhs); |
| ... | ... | @@ -9010,7 +9047,7 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 9010 | 9047 | .data = ptr_align - 1, |
| 9011 | 9048 | }; |
| 9012 | 9049 | const align_minus_1 = try sema.addConstant( |
| 9013 | Type.initTag(.usize), | |
| 9050 | Type.usize, | |
| 9014 | 9051 | Value.initPayload(&val_payload.base), |
| 9015 | 9052 | ); |
| 9016 | 9053 | const remainder = try block.addBinOp(.bit_and, operand_coerced, align_minus_1); |
| ... | ... | @@ -9295,6 +9332,48 @@ fn checkAtomicOperandType( |
| 9295 | 9332 | } |
| 9296 | 9333 | } |
| 9297 | 9334 | |
| 9335 | fn checkPtrIsNotComptimeMutable( | |
| 9336 | sema: *Sema, | |
| 9337 | block: *Block, | |
| 9338 | ptr_val: Value, | |
| 9339 | ptr_src: LazySrcLoc, | |
| 9340 | operand_src: LazySrcLoc, | |
| 9341 | ) CompileError!void { | |
| 9342 | _ = operand_src; | |
| 9343 | if (ptr_val.isComptimeMutablePtr()) { | |
| 9344 | return sema.fail(block, ptr_src, "cannot store runtime value in compile time variable", .{}); | |
| 9345 | } | |
| 9346 | } | |
| 9347 | ||
| 9348 | fn checkComptimeVarStore( | |
| 9349 | sema: *Sema, | |
| 9350 | block: *Block, | |
| 9351 | src: LazySrcLoc, | |
| 9352 | decl_ref_mut: Value.Payload.DeclRefMut.Data, | |
| 9353 | ) CompileError!void { | |
| 9354 | if (decl_ref_mut.runtime_index < block.runtime_index) { | |
| 9355 | if (block.runtime_cond) |cond_src| { | |
| 9356 | const msg = msg: { | |
| 9357 | const msg = try sema.errMsg(block, src, "store to comptime variable depends on runtime condition", .{}); | |
| 9358 | errdefer msg.destroy(sema.gpa); | |
| 9359 | try sema.errNote(block, cond_src, msg, "runtime condition here", .{}); | |
| 9360 | break :msg msg; | |
| 9361 | }; | |
| 9362 | return sema.failWithOwnedErrorMsg(msg); | |
| 9363 | } | |
| 9364 | if (block.runtime_loop) |loop_src| { | |
| 9365 | const msg = msg: { | |
| 9366 | const msg = try sema.errMsg(block, src, "cannot store to comptime variable in non-inline loop", .{}); | |
| 9367 | errdefer msg.destroy(sema.gpa); | |
| 9368 | try sema.errNote(block, loop_src, msg, "non-inline loop here", .{}); | |
| 9369 | break :msg msg; | |
| 9370 | }; | |
| 9371 | return sema.failWithOwnedErrorMsg(msg); | |
| 9372 | } | |
| 9373 | unreachable; | |
| 9374 | } | |
| 9375 | } | |
| 9376 | ||
| 9298 | 9377 | fn resolveExportOptions( |
| 9299 | 9378 | sema: *Sema, |
| 9300 | 9379 | block: *Block, |
| ... | ... | @@ -9313,8 +9392,9 @@ fn resolveExportOptions( |
| 9313 | 9392 | if (!fields[section_index].isNull()) { |
| 9314 | 9393 | return sema.fail(block, src, "TODO: implement exporting with linksection", .{}); |
| 9315 | 9394 | } |
| 9395 | const name_ty = Type.initTag(.const_slice_u8); | |
| 9316 | 9396 | return std.builtin.ExportOptions{ |
| 9317 | .name = try fields[name_index].toAllocatedBytes(sema.arena), | |
| 9397 | .name = try fields[name_index].toAllocatedBytes(name_ty, sema.arena), | |
| 9318 | 9398 | .linkage = fields[linkage_index].toEnum(std.builtin.GlobalLinkage), |
| 9319 | 9399 | .section = null, // TODO |
| 9320 | 9400 | }; |
| ... | ... | @@ -9547,7 +9627,12 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A |
| 9547 | 9627 | } |
| 9548 | 9628 | |
| 9549 | 9629 | const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { |
| 9550 | if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |operand_val| { | |
| 9630 | const maybe_operand_val = try sema.resolveMaybeUndefVal(block, operand_src, operand); | |
| 9631 | const operand_val = maybe_operand_val orelse { | |
| 9632 | try sema.checkPtrIsNotComptimeMutable(block, ptr_val, ptr_src, operand_src); | |
| 9633 | break :rs operand_src; | |
| 9634 | }; | |
| 9635 | if (ptr_val.isComptimeMutablePtr()) { | |
| 9551 | 9636 | const target = sema.mod.getTarget(); |
| 9552 | 9637 | const stored_val = (try ptr_val.pointerDeref(sema.arena)) orelse break :rs ptr_src; |
| 9553 | 9638 | const new_val = switch (op) { |
| ... | ... | @@ -9565,7 +9650,7 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A |
| 9565 | 9650 | }; |
| 9566 | 9651 | try sema.storePtrVal(block, src, ptr_val, new_val, operand_ty); |
| 9567 | 9652 | return sema.addConstant(operand_ty, stored_val); |
| 9568 | } else break :rs operand_src; | |
| 9653 | } else break :rs ptr_src; | |
| 9569 | 9654 | } else ptr_src; |
| 9570 | 9655 | |
| 9571 | 9656 | const flags: u32 = @as(u32, @enumToInt(order)) | (@as(u32, @enumToInt(op)) << 3); |
| ... | ... | @@ -9682,7 +9767,7 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void |
| 9682 | 9767 | .size = .Many, |
| 9683 | 9768 | }); |
| 9684 | 9769 | const src_ptr = try sema.coerce(block, wanted_src_ptr_ty, uncasted_src_ptr, src_src); |
| 9685 | const len = try sema.coerce(block, Type.initTag(.usize), sema.resolveInst(extra.byte_count), len_src); | |
| 9770 | const len = try sema.coerce(block, Type.usize, sema.resolveInst(extra.byte_count), len_src); | |
| 9686 | 9771 | |
| 9687 | 9772 | const maybe_dest_ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr); |
| 9688 | 9773 | const maybe_src_ptr_val = try sema.resolveDefinedValue(block, src_src, src_ptr); |
| ... | ... | @@ -9729,7 +9814,7 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void |
| 9729 | 9814 | } |
| 9730 | 9815 | const elem_ty = dest_ptr_ty.elemType2(); |
| 9731 | 9816 | const value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.byte), value_src); |
| 9732 | const len = try sema.coerce(block, Type.initTag(.usize), sema.resolveInst(extra.byte_count), len_src); | |
| 9817 | const len = try sema.coerce(block, Type.usize, sema.resolveInst(extra.byte_count), len_src); | |
| 9733 | 9818 | |
| 9734 | 9819 | const maybe_dest_ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr); |
| 9735 | 9820 | const maybe_len_val = try sema.resolveDefinedValue(block, len_src, len); |
| ... | ... | @@ -10270,7 +10355,7 @@ fn fieldVal( |
| 10270 | 10355 | try sema.requireRuntimeBlock(block, src); |
| 10271 | 10356 | return block.addTyOp(.slice_ptr, result_ty, object); |
| 10272 | 10357 | } else if (mem.eql(u8, field_name, "len")) { |
| 10273 | const result_ty = Type.initTag(.usize); | |
| 10358 | const result_ty = Type.usize; | |
| 10274 | 10359 | if (try sema.resolveMaybeUndefVal(block, object_src, object)) |val| { |
| 10275 | 10360 | if (val.isUndef()) return sema.addConstUndef(result_ty); |
| 10276 | 10361 | return sema.addConstant( |
| ... | ... | @@ -10955,7 +11040,7 @@ fn elemVal( |
| 10955 | 11040 | return block.addBinOp(.ptr_elem_val, array_maybe_ptr, elem_index); |
| 10956 | 11041 | }, |
| 10957 | 11042 | .One => { |
| 10958 | const indexable_ty = maybe_ptr_ty.elemType(); | |
| 11043 | const indexable_ty = maybe_ptr_ty.childType(); | |
| 10959 | 11044 | switch (indexable_ty.zigTypeTag()) { |
| 10960 | 11045 | .Pointer => switch (indexable_ty.ptrSize()) { |
| 10961 | 11046 | .Slice => { |
| ... | ... | @@ -10986,12 +11071,22 @@ fn elemVal( |
| 10986 | 11071 | try sema.requireRuntimeBlock(block, src); |
| 10987 | 11072 | return block.addBinOp(.ptr_ptr_elem_val, array_maybe_ptr, elem_index); |
| 10988 | 11073 | }, |
| 10989 | .One => return sema.fail( | |
| 10990 | block, | |
| 10991 | array_ptr_src, | |
| 10992 | "expected pointer, found '{}'", | |
| 10993 | .{indexable_ty.elemType()}, | |
| 10994 | ), | |
| 11074 | .One => { | |
| 11075 | const array_ty = indexable_ty.childType(); | |
| 11076 | if (array_ty.zigTypeTag() == .Array) { | |
| 11077 | // We have a double pointer to an array, and we want an element | |
| 11078 | // value. This can happen with this code for example: | |
| 11079 | // var a: *[1]u8 = undefined; _ = a[0]; | |
| 11080 | const array_ptr = try sema.analyzeLoad(block, src, array_maybe_ptr, array_ptr_src); | |
| 11081 | const ptr = try sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src); | |
| 11082 | return sema.analyzeLoad(block, src, ptr, elem_index_src); | |
| 11083 | } else return sema.fail( | |
| 11084 | block, | |
| 11085 | array_ptr_src, | |
| 11086 | "expected pointer, found '{}'", | |
| 11087 | .{array_ty}, | |
| 11088 | ); | |
| 11089 | }, | |
| 10995 | 11090 | }, |
| 10996 | 11091 | .Array => { |
| 10997 | 11092 | const ptr = try sema.elemPtr(block, src, array_maybe_ptr, elem_index, elem_index_src); |
| ... | ... | @@ -11463,13 +11558,15 @@ fn storePtr2( |
| 11463 | 11558 | return; |
| 11464 | 11559 | |
| 11465 | 11560 | const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { |
| 11466 | const operand_val = (try sema.resolveMaybeUndefVal(block, operand_src, operand)) orelse | |
| 11467 | return sema.fail(block, src, "cannot store runtime value in compile time variable", .{}); | |
| 11468 | if (ptr_val.tag() == .decl_ref_mut) { | |
| 11561 | const maybe_operand_val = try sema.resolveMaybeUndefVal(block, operand_src, operand); | |
| 11562 | const operand_val = maybe_operand_val orelse { | |
| 11563 | try sema.checkPtrIsNotComptimeMutable(block, ptr_val, ptr_src, operand_src); | |
| 11564 | break :rs operand_src; | |
| 11565 | }; | |
| 11566 | if (ptr_val.isComptimeMutablePtr()) { | |
| 11469 | 11567 | try sema.storePtrVal(block, src, ptr_val, operand_val, elem_ty); |
| 11470 | 11568 | return; |
| 11471 | } | |
| 11472 | break :rs operand_src; | |
| 11569 | } else break :rs ptr_src; | |
| 11473 | 11570 | } else ptr_src; |
| 11474 | 11571 | |
| 11475 | 11572 | // TODO handle if the element type requires comptime |
| ... | ... | @@ -11489,40 +11586,166 @@ fn storePtrVal( |
| 11489 | 11586 | operand_val: Value, |
| 11490 | 11587 | operand_ty: Type, |
| 11491 | 11588 | ) !void { |
| 11492 | if (ptr_val.castTag(.decl_ref_mut)) |decl_ref_mut| { | |
| 11493 | if (decl_ref_mut.data.runtime_index < block.runtime_index) { | |
| 11494 | if (block.runtime_cond) |cond_src| { | |
| 11495 | const msg = msg: { | |
| 11496 | const msg = try sema.errMsg(block, src, "store to comptime variable depends on runtime condition", .{}); | |
| 11497 | errdefer msg.destroy(sema.gpa); | |
| 11498 | try sema.errNote(block, cond_src, msg, "runtime condition here", .{}); | |
| 11499 | break :msg msg; | |
| 11500 | }; | |
| 11501 | return sema.failWithOwnedErrorMsg(msg); | |
| 11589 | var kit = try beginComptimePtrMutation(sema, block, src, ptr_val); | |
| 11590 | try sema.checkComptimeVarStore(block, src, kit.decl_ref_mut); | |
| 11591 | ||
| 11592 | const target = sema.mod.getTarget(); | |
| 11593 | const bitcasted_val = try operand_val.bitCast(operand_ty, kit.ty, target, sema.gpa, sema.arena); | |
| 11594 | ||
| 11595 | const arena = kit.beginArena(sema.gpa); | |
| 11596 | defer kit.finishArena(); | |
| 11597 | ||
| 11598 | kit.val.* = try bitcasted_val.copy(arena); | |
| 11599 | } | |
| 11600 | ||
| 11601 | const ComptimePtrMutationKit = struct { | |
| 11602 | decl_ref_mut: Value.Payload.DeclRefMut.Data, | |
| 11603 | val: *Value, | |
| 11604 | ty: Type, | |
| 11605 | decl_arena: std.heap.ArenaAllocator = undefined, | |
| 11606 | ||
| 11607 | fn beginArena(self: *ComptimePtrMutationKit, gpa: *Allocator) *Allocator { | |
| 11608 | self.decl_arena = self.decl_ref_mut.decl.value_arena.?.promote(gpa); | |
| 11609 | return &self.decl_arena.allocator; | |
| 11610 | } | |
| 11611 | ||
| 11612 | fn finishArena(self: *ComptimePtrMutationKit) void { | |
| 11613 | self.decl_ref_mut.decl.value_arena.?.* = self.decl_arena.state; | |
| 11614 | self.decl_arena = undefined; | |
| 11615 | } | |
| 11616 | }; | |
| 11617 | ||
| 11618 | fn beginComptimePtrMutation( | |
| 11619 | sema: *Sema, | |
| 11620 | block: *Block, | |
| 11621 | src: LazySrcLoc, | |
| 11622 | ptr_val: Value, | |
| 11623 | ) CompileError!ComptimePtrMutationKit { | |
| 11624 | switch (ptr_val.tag()) { | |
| 11625 | .decl_ref_mut => { | |
| 11626 | const decl_ref_mut = ptr_val.castTag(.decl_ref_mut).?.data; | |
| 11627 | return ComptimePtrMutationKit{ | |
| 11628 | .decl_ref_mut = decl_ref_mut, | |
| 11629 | .val = &decl_ref_mut.decl.val, | |
| 11630 | .ty = decl_ref_mut.decl.ty, | |
| 11631 | }; | |
| 11632 | }, | |
| 11633 | .elem_ptr => { | |
| 11634 | const elem_ptr = ptr_val.castTag(.elem_ptr).?.data; | |
| 11635 | var parent = try beginComptimePtrMutation(sema, block, src, elem_ptr.array_ptr); | |
| 11636 | const elem_ty = parent.ty.childType(); | |
| 11637 | switch (parent.val.tag()) { | |
| 11638 | .undef => { | |
| 11639 | // An array has been initialized to undefined at comptime and now we | |
| 11640 | // are for the first time setting an element. We must change the representation | |
| 11641 | // of the array from `undef` to `array`. | |
| 11642 | const arena = parent.beginArena(sema.gpa); | |
| 11643 | defer parent.finishArena(); | |
| 11644 | ||
| 11645 | const elems = try arena.alloc(Value, parent.ty.arrayLenIncludingSentinel()); | |
| 11646 | mem.set(Value, elems, Value.undef); | |
| 11647 | ||
| 11648 | parent.val.* = try Value.Tag.array.create(arena, elems); | |
| 11649 | ||
| 11650 | return ComptimePtrMutationKit{ | |
| 11651 | .decl_ref_mut = parent.decl_ref_mut, | |
| 11652 | .val = &elems[elem_ptr.index], | |
| 11653 | .ty = elem_ty, | |
| 11654 | }; | |
| 11655 | }, | |
| 11656 | .bytes => { | |
| 11657 | // An array is memory-optimized to store a slice of bytes, but we are about | |
| 11658 | // to modify an individual field and the representation has to change. | |
| 11659 | // If we wanted to avoid this, there would need to be special detection | |
| 11660 | // elsewhere to identify when writing a value to an array element that is stored | |
| 11661 | // using the `bytes` tag, and handle it without making a call to this function. | |
| 11662 | const arena = parent.beginArena(sema.gpa); | |
| 11663 | defer parent.finishArena(); | |
| 11664 | ||
| 11665 | const bytes = parent.val.castTag(.bytes).?.data; | |
| 11666 | assert(bytes.len == parent.ty.arrayLenIncludingSentinel()); | |
| 11667 | const elems = try arena.alloc(Value, bytes.len); | |
| 11668 | for (elems) |*elem, i| { | |
| 11669 | elem.* = try Value.Tag.int_u64.create(arena, bytes[i]); | |
| 11670 | } | |
| 11671 | ||
| 11672 | parent.val.* = try Value.Tag.array.create(arena, elems); | |
| 11673 | ||
| 11674 | return ComptimePtrMutationKit{ | |
| 11675 | .decl_ref_mut = parent.decl_ref_mut, | |
| 11676 | .val = &elems[elem_ptr.index], | |
| 11677 | .ty = elem_ty, | |
| 11678 | }; | |
| 11679 | }, | |
| 11680 | .repeated => { | |
| 11681 | // An array is memory-optimized to store only a single element value, and | |
| 11682 | // that value is understood to be the same for the entire length of the array. | |
| 11683 | // However, now we want to modify an individual field and so the | |
| 11684 | // representation has to change. If we wanted to avoid this, there would | |
| 11685 | // need to be special detection elsewhere to identify when writing a value to an | |
| 11686 | // array element that is stored using the `repeated` tag, and handle it | |
| 11687 | // without making a call to this function. | |
| 11688 | const arena = parent.beginArena(sema.gpa); | |
| 11689 | defer parent.finishArena(); | |
| 11690 | ||
| 11691 | const repeated_val = try parent.val.castTag(.repeated).?.data.copy(arena); | |
| 11692 | const elems = try arena.alloc(Value, parent.ty.arrayLenIncludingSentinel()); | |
| 11693 | mem.set(Value, elems, repeated_val); | |
| 11694 | ||
| 11695 | parent.val.* = try Value.Tag.array.create(arena, elems); | |
| 11696 | ||
| 11697 | return ComptimePtrMutationKit{ | |
| 11698 | .decl_ref_mut = parent.decl_ref_mut, | |
| 11699 | .val = &elems[elem_ptr.index], | |
| 11700 | .ty = elem_ty, | |
| 11701 | }; | |
| 11702 | }, | |
| 11703 | ||
| 11704 | .array => return ComptimePtrMutationKit{ | |
| 11705 | .decl_ref_mut = parent.decl_ref_mut, | |
| 11706 | .val = &parent.val.castTag(.array).?.data[elem_ptr.index], | |
| 11707 | .ty = elem_ty, | |
| 11708 | }, | |
| 11709 | ||
| 11710 | else => unreachable, | |
| 11502 | 11711 | } |
| 11503 | if (block.runtime_loop) |loop_src| { | |
| 11504 | const msg = msg: { | |
| 11505 | const msg = try sema.errMsg(block, src, "cannot store to comptime variable in non-inline loop", .{}); | |
| 11506 | errdefer msg.destroy(sema.gpa); | |
| 11507 | try sema.errNote(block, loop_src, msg, "non-inline loop here", .{}); | |
| 11508 | break :msg msg; | |
| 11509 | }; | |
| 11510 | return sema.failWithOwnedErrorMsg(msg); | |
| 11712 | }, | |
| 11713 | .field_ptr => { | |
| 11714 | const field_ptr = ptr_val.castTag(.field_ptr).?.data; | |
| 11715 | var parent = try beginComptimePtrMutation(sema, block, src, field_ptr.container_ptr); | |
| 11716 | const field_ty = parent.ty.structFieldType(field_ptr.field_index); | |
| 11717 | switch (parent.val.tag()) { | |
| 11718 | .undef => { | |
| 11719 | // A struct has been initialized to undefined at comptime and now we | |
| 11720 | // are for the first time setting a field. We must change the representation | |
| 11721 | // of the struct from `undef` to `struct`. | |
| 11722 | const arena = parent.beginArena(sema.gpa); | |
| 11723 | defer parent.finishArena(); | |
| 11724 | ||
| 11725 | const fields = try arena.alloc(Value, parent.ty.structFieldCount()); | |
| 11726 | mem.set(Value, fields, Value.undef); | |
| 11727 | ||
| 11728 | parent.val.* = try Value.Tag.@"struct".create(arena, fields); | |
| 11729 | ||
| 11730 | return ComptimePtrMutationKit{ | |
| 11731 | .decl_ref_mut = parent.decl_ref_mut, | |
| 11732 | .val = &fields[field_ptr.field_index], | |
| 11733 | .ty = field_ty, | |
| 11734 | }; | |
| 11735 | }, | |
| 11736 | .@"struct" => return ComptimePtrMutationKit{ | |
| 11737 | .decl_ref_mut = parent.decl_ref_mut, | |
| 11738 | .val = &parent.val.castTag(.@"struct").?.data[field_ptr.field_index], | |
| 11739 | .ty = field_ty, | |
| 11740 | }, | |
| 11741 | ||
| 11742 | else => unreachable, | |
| 11511 | 11743 | } |
| 11512 | unreachable; | |
| 11513 | } | |
| 11514 | var new_arena = std.heap.ArenaAllocator.init(sema.gpa); | |
| 11515 | errdefer new_arena.deinit(); | |
| 11516 | const new_ty = try operand_ty.copy(&new_arena.allocator); | |
| 11517 | const new_val = try operand_val.copy(&new_arena.allocator); | |
| 11518 | const decl = decl_ref_mut.data.decl; | |
| 11519 | var old_arena = decl.value_arena.?.promote(sema.gpa); | |
| 11520 | decl.value_arena = null; | |
| 11521 | try decl.finalizeNewArena(&new_arena); | |
| 11522 | decl.ty = new_ty; | |
| 11523 | decl.val = new_val; | |
| 11524 | old_arena.deinit(); | |
| 11525 | return; | |
| 11744 | }, | |
| 11745 | .eu_payload_ptr => return sema.fail(block, src, "TODO comptime store to eu_payload_ptr", .{}), | |
| 11746 | .opt_payload_ptr => return sema.fail(block, src, "TODO comptime store opt_payload_ptr", .{}), | |
| 11747 | .decl_ref => unreachable, // isComptimeMutablePtr() has been checked already | |
| 11748 | else => unreachable, | |
| 11526 | 11749 | } |
| 11527 | 11750 | } |
| 11528 | 11751 | |
| ... | ... | @@ -11672,7 +11895,7 @@ fn analyzeLoad( |
| 11672 | 11895 | ) CompileError!Air.Inst.Ref { |
| 11673 | 11896 | const ptr_ty = sema.typeOf(ptr); |
| 11674 | 11897 | const elem_ty = switch (ptr_ty.zigTypeTag()) { |
| 11675 | .Pointer => ptr_ty.elemType(), | |
| 11898 | .Pointer => ptr_ty.childType(), | |
| 11676 | 11899 | else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty}), |
| 11677 | 11900 | }; |
| 11678 | 11901 | if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { |
| ... | ... | @@ -11693,12 +11916,12 @@ fn analyzeSliceLen( |
| 11693 | 11916 | ) CompileError!Air.Inst.Ref { |
| 11694 | 11917 | if (try sema.resolveMaybeUndefVal(block, src, slice_inst)) |slice_val| { |
| 11695 | 11918 | if (slice_val.isUndef()) { |
| 11696 | return sema.addConstUndef(Type.initTag(.usize)); | |
| 11919 | return sema.addConstUndef(Type.usize); | |
| 11697 | 11920 | } |
| 11698 | 11921 | return sema.addIntUnsigned(Type.usize, slice_val.sliceLen()); |
| 11699 | 11922 | } |
| 11700 | 11923 | try sema.requireRuntimeBlock(block, src); |
| 11701 | return block.addTyOp(.slice_len, Type.initTag(.usize), slice_inst); | |
| 11924 | return block.addTyOp(.slice_len, Type.usize, slice_inst); | |
| 11702 | 11925 | } |
| 11703 | 11926 | |
| 11704 | 11927 | fn analyzeIsNull( |
| ... | ... | @@ -11806,7 +12029,7 @@ fn analyzeSlice( |
| 11806 | 12029 | array_type.sentinel() |
| 11807 | 12030 | else |
| 11808 | 12031 | slice_sentinel; |
| 11809 | return_elem_type = try Module.arrayType(sema.arena, len, array_sentinel, elem_type); | |
| 12032 | return_elem_type = try Type.array(sema.arena, len, array_sentinel, elem_type); | |
| 11810 | 12033 | return_ptr_size = .One; |
| 11811 | 12034 | } |
| 11812 | 12035 | } |
| ... | ... | @@ -12396,7 +12619,7 @@ fn semaStructFields( |
| 12396 | 12619 | .code = zir, |
| 12397 | 12620 | .owner_decl = decl, |
| 12398 | 12621 | .func = null, |
| 12399 | .fn_ret_ty = Type.initTag(.void), | |
| 12622 | .fn_ret_ty = Type.void, | |
| 12400 | 12623 | .owner_func = null, |
| 12401 | 12624 | }; |
| 12402 | 12625 | defer sema.deinit(); |
| ... | ... | @@ -12566,7 +12789,7 @@ fn semaUnionFields( |
| 12566 | 12789 | .code = zir, |
| 12567 | 12790 | .owner_decl = decl, |
| 12568 | 12791 | .func = null, |
| 12569 | .fn_ret_ty = Type.initTag(.void), | |
| 12792 | .fn_ret_ty = Type.void, | |
| 12570 | 12793 | .owner_func = null, |
| 12571 | 12794 | }; |
| 12572 | 12795 | defer sema.deinit(); |
| ... | ... | @@ -12677,7 +12900,7 @@ fn semaUnionFields( |
| 12677 | 12900 | } |
| 12678 | 12901 | |
| 12679 | 12902 | const field_ty: Type = if (!has_type) |
| 12680 | Type.initTag(.void) | |
| 12903 | Type.void | |
| 12681 | 12904 | else if (field_type_ref == .none) |
| 12682 | 12905 | Type.initTag(.noreturn) |
| 12683 | 12906 | else |
| ... | ... | @@ -12959,7 +13182,7 @@ fn typeHasOnePossibleValue( |
| 12959 | 13182 | }, |
| 12960 | 13183 | |
| 12961 | 13184 | .empty_struct, .empty_struct_literal => return Value.initTag(.empty_struct_value), |
| 12962 | .void => return Value.initTag(.void_value), | |
| 13185 | .void => return Value.void, | |
| 12963 | 13186 | .noreturn => return Value.initTag(.unreachable_value), |
| 12964 | 13187 | .@"null" => return Value.initTag(.null_value), |
| 12965 | 13188 | .@"undefined" => return Value.initTag(.undef), |
src/Zir.zig+4-9| ... | ... | @@ -210,8 +210,8 @@ pub const Inst = struct { |
| 210 | 210 | /// `[N]T` syntax. No source location provided. |
| 211 | 211 | /// Uses the `bin` union field. lhs is length, rhs is element type. |
| 212 | 212 | array_type, |
| 213 | /// `[N:S]T` syntax. No source location provided. | |
| 214 | /// Uses the `array_type_sentinel` field. | |
| 213 | /// `[N:S]T` syntax. Source location is the array type expression node. | |
| 214 | /// Uses the `pl_node` union field. Payload is `ArrayTypeSentinel`. | |
| 215 | 215 | array_type_sentinel, |
| 216 | 216 | /// `@Vector` builtin. |
| 217 | 217 | /// Uses the `pl_node` union field with `Bin` payload. |
| ... | ... | @@ -1256,7 +1256,7 @@ pub const Inst = struct { |
| 1256 | 1256 | .array_cat = .pl_node, |
| 1257 | 1257 | .array_mul = .pl_node, |
| 1258 | 1258 | .array_type = .bin, |
| 1259 | .array_type_sentinel = .array_type_sentinel, | |
| 1259 | .array_type_sentinel = .pl_node, | |
| 1260 | 1260 | .vector_type = .pl_node, |
| 1261 | 1261 | .elem_type = .un_node, |
| 1262 | 1262 | .indexable_ptr_len = .un_node, |
| ... | ... | @@ -2137,11 +2137,6 @@ pub const Inst = struct { |
| 2137 | 2137 | node: i32, |
| 2138 | 2138 | int: u64, |
| 2139 | 2139 | float: f64, |
| 2140 | array_type_sentinel: struct { | |
| 2141 | len: Ref, | |
| 2142 | /// index into extra, points to an `ArrayTypeSentinel` | |
| 2143 | payload_index: u32, | |
| 2144 | }, | |
| 2145 | 2140 | ptr_type_simple: struct { |
| 2146 | 2141 | is_allowzero: bool, |
| 2147 | 2142 | is_mutable: bool, |
| ... | ... | @@ -2245,7 +2240,6 @@ pub const Inst = struct { |
| 2245 | 2240 | node, |
| 2246 | 2241 | int, |
| 2247 | 2242 | float, |
| 2248 | array_type_sentinel, | |
| 2249 | 2243 | ptr_type_simple, |
| 2250 | 2244 | ptr_type, |
| 2251 | 2245 | int_type, |
| ... | ... | @@ -2427,6 +2421,7 @@ pub const Inst = struct { |
| 2427 | 2421 | }; |
| 2428 | 2422 | |
| 2429 | 2423 | pub const ArrayTypeSentinel = struct { |
| 2424 | len: Ref, | |
| 2430 | 2425 | sentinel: Ref, |
| 2431 | 2426 | elem_type: Ref, |
| 2432 | 2427 | }; |
src/codegen/llvm.zig+33-27| ... | ... | @@ -1031,54 +1031,60 @@ pub const DeclGen = struct { |
| 1031 | 1031 | }, |
| 1032 | 1032 | else => |tag| return self.todo("implement const of pointer type '{}' ({})", .{ tv.ty, tag }), |
| 1033 | 1033 | }, |
| 1034 | .Array => { | |
| 1035 | const gpa = self.gpa; | |
| 1036 | if (tv.val.castTag(.bytes)) |payload| { | |
| 1037 | const zero_sentinel = if (tv.ty.sentinel()) |sentinel| blk: { | |
| 1038 | if (sentinel.tag() == .zero) break :blk true; | |
| 1039 | return self.todo("handle other sentinel values", .{}); | |
| 1040 | } else false; | |
| 1041 | ||
| 1034 | .Array => switch (tv.val.tag()) { | |
| 1035 | .bytes => { | |
| 1036 | const bytes = tv.val.castTag(.bytes).?.data; | |
| 1042 | 1037 | return self.context.constString( |
| 1043 | payload.data.ptr, | |
| 1044 | @intCast(c_uint, payload.data.len), | |
| 1045 | llvm.Bool.fromBool(!zero_sentinel), | |
| 1038 | bytes.ptr, | |
| 1039 | @intCast(c_uint, bytes.len), | |
| 1040 | .True, // don't null terminate. bytes has the sentinel, if any. | |
| 1046 | 1041 | ); |
| 1047 | } | |
| 1048 | if (tv.val.castTag(.array)) |payload| { | |
| 1042 | }, | |
| 1043 | .array => { | |
| 1044 | const elem_vals = tv.val.castTag(.array).?.data; | |
| 1049 | 1045 | const elem_ty = tv.ty.elemType(); |
| 1050 | const elem_vals = payload.data; | |
| 1051 | const sento = tv.ty.sentinel(); | |
| 1052 | const llvm_elems = try gpa.alloc(*const llvm.Value, elem_vals.len + @boolToInt(sento != null)); | |
| 1046 | const gpa = self.gpa; | |
| 1047 | const llvm_elems = try gpa.alloc(*const llvm.Value, elem_vals.len); | |
| 1053 | 1048 | defer gpa.free(llvm_elems); |
| 1054 | 1049 | for (elem_vals) |elem_val, i| { |
| 1055 | 1050 | llvm_elems[i] = try self.genTypedValue(.{ .ty = elem_ty, .val = elem_val }); |
| 1056 | 1051 | } |
| 1057 | if (sento) |sent| llvm_elems[elem_vals.len] = try self.genTypedValue(.{ .ty = elem_ty, .val = sent }); | |
| 1058 | 1052 | const llvm_elem_ty = try self.llvmType(elem_ty); |
| 1059 | 1053 | return llvm_elem_ty.constArray( |
| 1060 | 1054 | llvm_elems.ptr, |
| 1061 | 1055 | @intCast(c_uint, llvm_elems.len), |
| 1062 | 1056 | ); |
| 1063 | } | |
| 1064 | if (tv.val.castTag(.repeated)) |payload| { | |
| 1065 | const val = payload.data; | |
| 1057 | }, | |
| 1058 | .repeated => { | |
| 1059 | const val = tv.val.castTag(.repeated).?.data; | |
| 1066 | 1060 | const elem_ty = tv.ty.elemType(); |
| 1061 | const sentinel = tv.ty.sentinel(); | |
| 1067 | 1062 | const len = tv.ty.arrayLen(); |
| 1068 | ||
| 1069 | const llvm_elems = try gpa.alloc(*const llvm.Value, len); | |
| 1063 | const len_including_sent = len + @boolToInt(sentinel != null); | |
| 1064 | const gpa = self.gpa; | |
| 1065 | const llvm_elems = try gpa.alloc(*const llvm.Value, len_including_sent); | |
| 1070 | 1066 | defer gpa.free(llvm_elems); |
| 1071 | var i: u64 = 0; | |
| 1072 | while (i < len) : (i += 1) { | |
| 1073 | llvm_elems[i] = try self.genTypedValue(.{ .ty = elem_ty, .val = val }); | |
| 1067 | for (llvm_elems[0..len]) |*elem| { | |
| 1068 | elem.* = try self.genTypedValue(.{ .ty = elem_ty, .val = val }); | |
| 1069 | } | |
| 1070 | if (sentinel) |sent| { | |
| 1071 | llvm_elems[len] = try self.genTypedValue(.{ .ty = elem_ty, .val = sent }); | |
| 1074 | 1072 | } |
| 1075 | 1073 | const llvm_elem_ty = try self.llvmType(elem_ty); |
| 1076 | 1074 | return llvm_elem_ty.constArray( |
| 1077 | 1075 | llvm_elems.ptr, |
| 1078 | 1076 | @intCast(c_uint, llvm_elems.len), |
| 1079 | 1077 | ); |
| 1080 | } | |
| 1081 | return self.todo("handle more array values", .{}); | |
| 1078 | }, | |
| 1079 | .empty_array_sentinel => { | |
| 1080 | const elem_ty = tv.ty.elemType(); | |
| 1081 | const sent_val = tv.ty.sentinel().?; | |
| 1082 | const sentinel = try self.genTypedValue(.{ .ty = elem_ty, .val = sent_val }); | |
| 1083 | const llvm_elems: [1]*const llvm.Value = .{sentinel}; | |
| 1084 | const llvm_elem_ty = try self.llvmType(elem_ty); | |
| 1085 | return llvm_elem_ty.constArray(&llvm_elems, llvm_elems.len); | |
| 1086 | }, | |
| 1087 | else => unreachable, | |
| 1082 | 1088 | }, |
| 1083 | 1089 | .Optional => { |
| 1084 | 1090 | var buf: Type.Payload.ElemType = undefined; |
src/codegen/llvm/bindings.zig+1-1| ... | ... | @@ -194,7 +194,7 @@ pub const Type = opaque { |
| 194 | 194 | extern fn LLVMConstReal(RealTy: *const Type, N: f64) *const Value; |
| 195 | 195 | |
| 196 | 196 | pub const constArray = LLVMConstArray; |
| 197 | extern fn LLVMConstArray(ElementTy: *const Type, ConstantVals: [*]*const Value, Length: c_uint) *const Value; | |
| 197 | extern fn LLVMConstArray(ElementTy: *const Type, ConstantVals: [*]const *const Value, Length: c_uint) *const Value; | |
| 198 | 198 | |
| 199 | 199 | pub const constNamedStruct = LLVMConstNamedStruct; |
| 200 | 200 | extern fn LLVMConstNamedStruct( |
src/print_zir.zig+4-3| ... | ... | @@ -542,14 +542,15 @@ const Writer = struct { |
| 542 | 542 | stream: anytype, |
| 543 | 543 | inst: Zir.Inst.Index, |
| 544 | 544 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { |
| 545 | const inst_data = self.code.instructions.items(.data)[inst].array_type_sentinel; | |
| 545 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 546 | 546 | const extra = self.code.extraData(Zir.Inst.ArrayTypeSentinel, inst_data.payload_index).data; |
| 547 | try self.writeInstRef(stream, inst_data.len); | |
| 547 | try self.writeInstRef(stream, extra.len); | |
| 548 | 548 | try stream.writeAll(", "); |
| 549 | 549 | try self.writeInstRef(stream, extra.sentinel); |
| 550 | 550 | try stream.writeAll(", "); |
| 551 | 551 | try self.writeInstRef(stream, extra.elem_type); |
| 552 | try stream.writeAll(")"); | |
| 552 | try stream.writeAll(") "); | |
| 553 | try self.writeSrc(stream, inst_data.src()); | |
| 553 | 554 | } |
| 554 | 555 | |
| 555 | 556 | fn writePtrTypeSimple( |
src/type.zig+56-8| ... | ... | @@ -1468,7 +1468,19 @@ pub const Type = extern union { |
| 1468 | 1468 | // TODO lazy types |
| 1469 | 1469 | .array, .vector => self.elemType().hasCodeGenBits() and self.arrayLen() != 0, |
| 1470 | 1470 | .array_u8 => self.arrayLen() != 0, |
| 1471 | .array_sentinel, .single_const_pointer, .single_mut_pointer, .many_const_pointer, .many_mut_pointer, .c_const_pointer, .c_mut_pointer, .const_slice, .mut_slice, .pointer => self.elemType().hasCodeGenBits(), | |
| 1471 | ||
| 1472 | .array_sentinel, | |
| 1473 | .single_const_pointer, | |
| 1474 | .single_mut_pointer, | |
| 1475 | .many_const_pointer, | |
| 1476 | .many_mut_pointer, | |
| 1477 | .c_const_pointer, | |
| 1478 | .c_mut_pointer, | |
| 1479 | .const_slice, | |
| 1480 | .mut_slice, | |
| 1481 | .pointer, | |
| 1482 | => self.childType().hasCodeGenBits(), | |
| 1483 | ||
| 1472 | 1484 | .int_signed, .int_unsigned => self.cast(Payload.Bits).?.data != 0, |
| 1473 | 1485 | |
| 1474 | 1486 | .error_union => { |
| ... | ... | @@ -2560,18 +2572,22 @@ pub const Type = extern union { |
| 2560 | 2572 | } |
| 2561 | 2573 | |
| 2562 | 2574 | /// Asserts the type is an array or vector. |
| 2563 | pub fn arrayLen(self: Type) u64 { | |
| 2564 | return switch (self.tag()) { | |
| 2565 | .vector => self.castTag(.vector).?.data.len, | |
| 2566 | .array => self.castTag(.array).?.data.len, | |
| 2567 | .array_sentinel => self.castTag(.array_sentinel).?.data.len, | |
| 2568 | .array_u8 => self.castTag(.array_u8).?.data, | |
| 2569 | .array_u8_sentinel_0 => self.castTag(.array_u8_sentinel_0).?.data, | |
| 2575 | pub fn arrayLen(ty: Type) u64 { | |
| 2576 | return switch (ty.tag()) { | |
| 2577 | .vector => ty.castTag(.vector).?.data.len, | |
| 2578 | .array => ty.castTag(.array).?.data.len, | |
| 2579 | .array_sentinel => ty.castTag(.array_sentinel).?.data.len, | |
| 2580 | .array_u8 => ty.castTag(.array_u8).?.data, | |
| 2581 | .array_u8_sentinel_0 => ty.castTag(.array_u8_sentinel_0).?.data, | |
| 2570 | 2582 | |
| 2571 | 2583 | else => unreachable, |
| 2572 | 2584 | }; |
| 2573 | 2585 | } |
| 2574 | 2586 | |
| 2587 | pub fn arrayLenIncludingSentinel(ty: Type) u64 { | |
| 2588 | return ty.arrayLen() + @boolToInt(ty.sentinel() != null); | |
| 2589 | } | |
| 2590 | ||
| 2575 | 2591 | /// Asserts the type is an array, pointer or vector. |
| 2576 | 2592 | pub fn sentinel(self: Type) ?Value { |
| 2577 | 2593 | return switch (self.tag()) { |
| ... | ... | @@ -3882,9 +3898,11 @@ pub const Type = extern union { |
| 3882 | 3898 | }; |
| 3883 | 3899 | }; |
| 3884 | 3900 | |
| 3901 | pub const @"u8" = initTag(.u8); | |
| 3885 | 3902 | pub const @"bool" = initTag(.bool); |
| 3886 | 3903 | pub const @"usize" = initTag(.usize); |
| 3887 | 3904 | pub const @"comptime_int" = initTag(.comptime_int); |
| 3905 | pub const @"void" = initTag(.void); | |
| 3888 | 3906 | |
| 3889 | 3907 | pub fn ptr(arena: *Allocator, d: Payload.Pointer.Data) !Type { |
| 3890 | 3908 | assert(d.host_size == 0 or d.bit_offset < d.host_size * 8); |
| ... | ... | @@ -3917,6 +3935,36 @@ pub const Type = extern union { |
| 3917 | 3935 | return Type.initPayload(&type_payload.base); |
| 3918 | 3936 | } |
| 3919 | 3937 | |
| 3938 | pub fn array( | |
| 3939 | arena: *Allocator, | |
| 3940 | len: u64, | |
| 3941 | sent: ?Value, | |
| 3942 | elem_type: Type, | |
| 3943 | ) Allocator.Error!Type { | |
| 3944 | if (elem_type.eql(Type.u8)) { | |
| 3945 | if (sent) |some| { | |
| 3946 | if (some.eql(Value.initTag(.zero), elem_type)) { | |
| 3947 | return Tag.array_u8_sentinel_0.create(arena, len); | |
| 3948 | } | |
| 3949 | } else { | |
| 3950 | return Tag.array_u8.create(arena, len); | |
| 3951 | } | |
| 3952 | } | |
| 3953 | ||
| 3954 | if (sent) |some| { | |
| 3955 | return Tag.array_sentinel.create(arena, .{ | |
| 3956 | .len = len, | |
| 3957 | .sentinel = some, | |
| 3958 | .elem_type = elem_type, | |
| 3959 | }); | |
| 3960 | } | |
| 3961 | ||
| 3962 | return Tag.array.create(arena, .{ | |
| 3963 | .len = len, | |
| 3964 | .elem_type = elem_type, | |
| 3965 | }); | |
| 3966 | } | |
| 3967 | ||
| 3920 | 3968 | pub fn smallestUnsignedBits(max: u64) u16 { |
| 3921 | 3969 | if (max == 0) return 0; |
| 3922 | 3970 | const base = std.math.log2(max); |
src/value.zig+80-51| ... | ... | @@ -112,7 +112,9 @@ pub const Value = extern union { |
| 112 | 112 | /// This Tag will never be seen by machine codegen backends. It is changed into a |
| 113 | 113 | /// `decl_ref` when a comptime variable goes out of scope. |
| 114 | 114 | decl_ref_mut, |
| 115 | /// Pointer to a specific element of an array. | |
| 115 | 116 | elem_ptr, |
| 117 | /// Pointer to a specific field of a struct. | |
| 116 | 118 | field_ptr, |
| 117 | 119 | /// A slice of u8 whose memory is managed externally. |
| 118 | 120 | bytes, |
| ... | ... | @@ -120,7 +122,11 @@ pub const Value = extern union { |
| 120 | 122 | /// is stored externally. |
| 121 | 123 | repeated, |
| 122 | 124 | /// Each element stored as a `Value`. |
| 125 | /// In the case of sentinel-terminated arrays, the sentinel value *is* stored, | |
| 126 | /// so the slice length will be one more than the type's array length. | |
| 123 | 127 | array, |
| 128 | /// An array with length 0 but it has a sentinel. | |
| 129 | empty_array_sentinel, | |
| 124 | 130 | /// Pointer and length as sub `Value` objects. |
| 125 | 131 | slice, |
| 126 | 132 | float_16, |
| ... | ... | @@ -255,6 +261,7 @@ pub const Value = extern union { |
| 255 | 261 | .eu_payload_ptr, |
| 256 | 262 | .opt_payload, |
| 257 | 263 | .opt_payload_ptr, |
| 264 | .empty_array_sentinel, | |
| 258 | 265 | => Payload.SubValue, |
| 259 | 266 | |
| 260 | 267 | .bytes, |
| ... | ... | @@ -486,6 +493,7 @@ pub const Value = extern union { |
| 486 | 493 | .eu_payload_ptr, |
| 487 | 494 | .opt_payload, |
| 488 | 495 | .opt_payload_ptr, |
| 496 | .empty_array_sentinel, | |
| 489 | 497 | => { |
| 490 | 498 | const payload = self.cast(Payload.SubValue).?; |
| 491 | 499 | const new_payload = try arena.create(Payload.SubValue); |
| ... | ... | @@ -697,6 +705,7 @@ pub const Value = extern union { |
| 697 | 705 | val = val.castTag(.repeated).?.data; |
| 698 | 706 | }, |
| 699 | 707 | .array => return out_stream.writeAll("(array)"), |
| 708 | .empty_array_sentinel => return out_stream.writeAll("(empty array with sentinel)"), | |
| 700 | 709 | .slice => return out_stream.writeAll("(slice)"), |
| 701 | 710 | .float_16 => return out_stream.print("{}", .{val.castTag(.float_16).?.data}), |
| 702 | 711 | .float_32 => return out_stream.print("{}", .{val.castTag(.float_32).?.data}), |
| ... | ... | @@ -731,22 +740,23 @@ pub const Value = extern union { |
| 731 | 740 | |
| 732 | 741 | /// Asserts that the value is representable as an array of bytes. |
| 733 | 742 | /// Copies the value into a freshly allocated slice of memory, which is owned by the caller. |
| 734 | pub fn toAllocatedBytes(self: Value, allocator: *Allocator) ![]u8 { | |
| 735 | if (self.castTag(.bytes)) |payload| { | |
| 736 | return std.mem.dupe(allocator, u8, payload.data); | |
| 737 | } | |
| 738 | if (self.castTag(.enum_literal)) |payload| { | |
| 739 | return std.mem.dupe(allocator, u8, payload.data); | |
| 740 | } | |
| 741 | if (self.castTag(.repeated)) |payload| { | |
| 742 | _ = payload; | |
| 743 | @panic("TODO implement toAllocatedBytes for this Value tag"); | |
| 744 | } | |
| 745 | if (self.castTag(.decl_ref)) |payload| { | |
| 746 | const val = try payload.data.value(); | |
| 747 | return val.toAllocatedBytes(allocator); | |
| 743 | pub fn toAllocatedBytes(val: Value, ty: Type, allocator: *Allocator) ![]u8 { | |
| 744 | switch (val.tag()) { | |
| 745 | .bytes => { | |
| 746 | const bytes = val.castTag(.bytes).?.data; | |
| 747 | const adjusted_len = bytes.len - @boolToInt(ty.sentinel() != null); | |
| 748 | const adjusted_bytes = bytes[0..adjusted_len]; | |
| 749 | return std.mem.dupe(allocator, u8, adjusted_bytes); | |
| 750 | }, | |
| 751 | .enum_literal => return std.mem.dupe(allocator, u8, val.castTag(.enum_literal).?.data), | |
| 752 | .repeated => @panic("TODO implement toAllocatedBytes for this Value tag"), | |
| 753 | .decl_ref => { | |
| 754 | const decl = val.castTag(.decl_ref).?.data; | |
| 755 | const decl_val = try decl.value(); | |
| 756 | return decl_val.toAllocatedBytes(decl.ty, allocator); | |
| 757 | }, | |
| 758 | else => unreachable, | |
| 748 | 759 | } |
| 749 | unreachable; | |
| 750 | 760 | } |
| 751 | 761 | |
| 752 | 762 | pub const ToTypeBuffer = Type.Payload.Bits; |
| ... | ... | @@ -965,6 +975,8 @@ pub const Value = extern union { |
| 965 | 975 | gpa: *Allocator, |
| 966 | 976 | arena: *Allocator, |
| 967 | 977 | ) !Value { |
| 978 | if (old_ty.eql(new_ty)) return val; | |
| 979 | ||
| 968 | 980 | // For types with well-defined memory layouts, we serialize them a byte buffer, |
| 969 | 981 | // then deserialize to the new type. |
| 970 | 982 | const buffer = try gpa.alloc(u8, old_ty.abiSize(target)); |
| ... | ... | @@ -1527,37 +1539,34 @@ pub const Value = extern union { |
| 1527 | 1539 | |
| 1528 | 1540 | /// Asserts the value is a pointer and dereferences it. |
| 1529 | 1541 | /// Returns error.AnalysisFail if the pointer points to a Decl that failed semantic analysis. |
| 1530 | pub fn pointerDeref( | |
| 1531 | self: Value, | |
| 1532 | allocator: *Allocator, | |
| 1533 | ) error{ AnalysisFail, OutOfMemory }!?Value { | |
| 1534 | const sub_val: Value = switch (self.tag()) { | |
| 1535 | .decl_ref_mut => val: { | |
| 1542 | pub fn pointerDeref(val: Value, arena: *Allocator) error{ AnalysisFail, OutOfMemory }!?Value { | |
| 1543 | const sub_val: Value = switch (val.tag()) { | |
| 1544 | .decl_ref_mut => sub_val: { | |
| 1536 | 1545 | // The decl whose value we are obtaining here may be overwritten with |
| 1537 | 1546 | // a different value, which would invalidate this memory. So we must |
| 1538 | 1547 | // copy here. |
| 1539 | const val = try self.castTag(.decl_ref_mut).?.data.decl.value(); | |
| 1540 | break :val try val.copy(allocator); | |
| 1548 | const sub_val = try val.castTag(.decl_ref_mut).?.data.decl.value(); | |
| 1549 | break :sub_val try sub_val.copy(arena); | |
| 1541 | 1550 | }, |
| 1542 | .decl_ref => try self.castTag(.decl_ref).?.data.value(), | |
| 1551 | .decl_ref => try val.castTag(.decl_ref).?.data.value(), | |
| 1543 | 1552 | .elem_ptr => blk: { |
| 1544 | const elem_ptr = self.castTag(.elem_ptr).?.data; | |
| 1545 | const array_val = (try elem_ptr.array_ptr.pointerDeref(allocator)) orelse return null; | |
| 1546 | break :blk try array_val.elemValue(allocator, elem_ptr.index); | |
| 1553 | const elem_ptr = val.castTag(.elem_ptr).?.data; | |
| 1554 | const array_val = (try elem_ptr.array_ptr.pointerDeref(arena)) orelse return null; | |
| 1555 | break :blk try array_val.elemValue(arena, elem_ptr.index); | |
| 1547 | 1556 | }, |
| 1548 | 1557 | .field_ptr => blk: { |
| 1549 | const field_ptr = self.castTag(.field_ptr).?.data; | |
| 1550 | const container_val = (try field_ptr.container_ptr.pointerDeref(allocator)) orelse return null; | |
| 1551 | break :blk try container_val.fieldValue(allocator, field_ptr.field_index); | |
| 1558 | const field_ptr = val.castTag(.field_ptr).?.data; | |
| 1559 | const container_val = (try field_ptr.container_ptr.pointerDeref(arena)) orelse return null; | |
| 1560 | break :blk try container_val.fieldValue(arena, field_ptr.field_index); | |
| 1552 | 1561 | }, |
| 1553 | 1562 | .eu_payload_ptr => blk: { |
| 1554 | const err_union_ptr = self.castTag(.eu_payload_ptr).?.data; | |
| 1555 | const err_union_val = (try err_union_ptr.pointerDeref(allocator)) orelse return null; | |
| 1563 | const err_union_ptr = val.castTag(.eu_payload_ptr).?.data; | |
| 1564 | const err_union_val = (try err_union_ptr.pointerDeref(arena)) orelse return null; | |
| 1556 | 1565 | break :blk err_union_val.castTag(.eu_payload).?.data; |
| 1557 | 1566 | }, |
| 1558 | 1567 | .opt_payload_ptr => blk: { |
| 1559 | const opt_ptr = self.castTag(.opt_payload_ptr).?.data; | |
| 1560 | const opt_val = (try opt_ptr.pointerDeref(allocator)) orelse return null; | |
| 1568 | const opt_ptr = val.castTag(.opt_payload_ptr).?.data; | |
| 1569 | const opt_val = (try opt_ptr.pointerDeref(arena)) orelse return null; | |
| 1561 | 1570 | break :blk opt_val.castTag(.opt_payload).?.data; |
| 1562 | 1571 | }, |
| 1563 | 1572 | |
| ... | ... | @@ -1582,24 +1591,33 @@ pub const Value = extern union { |
| 1582 | 1591 | return sub_val; |
| 1583 | 1592 | } |
| 1584 | 1593 | |
| 1594 | pub fn isComptimeMutablePtr(val: Value) bool { | |
| 1595 | return switch (val.tag()) { | |
| 1596 | .decl_ref_mut => true, | |
| 1597 | .elem_ptr => isComptimeMutablePtr(val.castTag(.elem_ptr).?.data.array_ptr), | |
| 1598 | .field_ptr => isComptimeMutablePtr(val.castTag(.field_ptr).?.data.container_ptr), | |
| 1599 | .eu_payload_ptr => isComptimeMutablePtr(val.castTag(.eu_payload_ptr).?.data), | |
| 1600 | .opt_payload_ptr => isComptimeMutablePtr(val.castTag(.opt_payload_ptr).?.data), | |
| 1601 | ||
| 1602 | else => false, | |
| 1603 | }; | |
| 1604 | } | |
| 1605 | ||
| 1585 | 1606 | /// Gets the decl referenced by this pointer. If the pointer does not point |
| 1586 | 1607 | /// to a decl, or if it points to some part of a decl (like field_ptr or element_ptr), |
| 1587 | 1608 | /// this function returns null. |
| 1588 | pub fn pointerDecl(self: Value) ?*Module.Decl { | |
| 1589 | return switch (self.tag()) { | |
| 1590 | .decl_ref_mut => self.castTag(.decl_ref_mut).?.data.decl, | |
| 1591 | .extern_fn, .decl_ref => self.cast(Payload.Decl).?.data, | |
| 1592 | .function => self.castTag(.function).?.data.owner_decl, | |
| 1593 | .variable => self.castTag(.variable).?.data.owner_decl, | |
| 1609 | pub fn pointerDecl(val: Value) ?*Module.Decl { | |
| 1610 | return switch (val.tag()) { | |
| 1611 | .decl_ref_mut => val.castTag(.decl_ref_mut).?.data.decl, | |
| 1612 | .extern_fn, .decl_ref => val.cast(Payload.Decl).?.data, | |
| 1613 | .function => val.castTag(.function).?.data.owner_decl, | |
| 1614 | .variable => val.castTag(.variable).?.data.owner_decl, | |
| 1594 | 1615 | else => null, |
| 1595 | 1616 | }; |
| 1596 | 1617 | } |
| 1597 | 1618 | |
| 1598 | 1619 | pub fn sliceLen(val: Value) u64 { |
| 1599 | 1620 | return switch (val.tag()) { |
| 1600 | .empty_array => 0, | |
| 1601 | .bytes => val.castTag(.bytes).?.data.len, | |
| 1602 | .array => val.castTag(.array).?.data.len, | |
| 1603 | 1621 | .slice => val.castTag(.slice).?.data.len.toUnsignedInt(), |
| 1604 | 1622 | .decl_ref => { |
| 1605 | 1623 | const decl = val.castTag(.decl_ref).?.data; |
| ... | ... | @@ -1615,17 +1633,23 @@ pub const Value = extern union { |
| 1615 | 1633 | |
| 1616 | 1634 | /// Asserts the value is a single-item pointer to an array, or an array, |
| 1617 | 1635 | /// or an unknown-length pointer, and returns the element value at the index. |
| 1618 | pub fn elemValue(self: Value, allocator: *Allocator, index: usize) error{OutOfMemory}!Value { | |
| 1619 | switch (self.tag()) { | |
| 1636 | pub fn elemValue(val: Value, arena: *Allocator, index: usize) error{OutOfMemory}!Value { | |
| 1637 | switch (val.tag()) { | |
| 1620 | 1638 | .empty_array => unreachable, // out of bounds array index |
| 1639 | .empty_struct_value => unreachable, // out of bounds array index | |
| 1640 | ||
| 1641 | .empty_array_sentinel => { | |
| 1642 | assert(index == 0); // The only valid index for an empty array with sentinel. | |
| 1643 | return val.castTag(.empty_array_sentinel).?.data; | |
| 1644 | }, | |
| 1621 | 1645 | |
| 1622 | .bytes => return Tag.int_u64.create(allocator, self.castTag(.bytes).?.data[index]), | |
| 1646 | .bytes => return Tag.int_u64.create(arena, val.castTag(.bytes).?.data[index]), | |
| 1623 | 1647 | |
| 1624 | 1648 | // No matter the index; all the elements are the same! |
| 1625 | .repeated => return self.castTag(.repeated).?.data, | |
| 1649 | .repeated => return val.castTag(.repeated).?.data, | |
| 1626 | 1650 | |
| 1627 | .array => return self.castTag(.array).?.data[index], | |
| 1628 | .slice => return self.castTag(.slice).?.data.ptr.elemValue(allocator, index), | |
| 1651 | .array => return val.castTag(.array).?.data[index], | |
| 1652 | .slice => return val.castTag(.slice).?.data.ptr.elemValue(arena, index), | |
| 1629 | 1653 | |
| 1630 | 1654 | else => unreachable, |
| 1631 | 1655 | } |
| ... | ... | @@ -2556,10 +2580,12 @@ pub const Value = extern union { |
| 2556 | 2580 | pub const base_tag = Tag.decl_ref_mut; |
| 2557 | 2581 | |
| 2558 | 2582 | base: Payload = Payload{ .tag = base_tag }, |
| 2559 | data: struct { | |
| 2583 | data: Data, | |
| 2584 | ||
| 2585 | pub const Data = struct { | |
| 2560 | 2586 | decl: *Module.Decl, |
| 2561 | 2587 | runtime_index: u32, |
| 2562 | }, | |
| 2588 | }; | |
| 2563 | 2589 | }; |
| 2564 | 2590 | |
| 2565 | 2591 | pub const ElemPtr = struct { |
| ... | ... | @@ -2584,6 +2610,7 @@ pub const Value = extern union { |
| 2584 | 2610 | |
| 2585 | 2611 | pub const Bytes = struct { |
| 2586 | 2612 | base: Payload, |
| 2613 | /// Includes the sentinel, if any. | |
| 2587 | 2614 | data: []const u8, |
| 2588 | 2615 | }; |
| 2589 | 2616 | |
| ... | ... | @@ -2706,6 +2733,8 @@ pub const Value = extern union { |
| 2706 | 2733 | pub const zero = initTag(.zero); |
| 2707 | 2734 | pub const one = initTag(.one); |
| 2708 | 2735 | pub const negative_one: Value = .{ .ptr_otherwise = &negative_one_payload.base }; |
| 2736 | pub const undef = initTag(.undef); | |
| 2737 | pub const @"void" = initTag(.void_value); | |
| 2709 | 2738 | }; |
| 2710 | 2739 | |
| 2711 | 2740 | var negative_one_payload: Value.Payload.I64 = .{ |
test/behavior/array.zig+28| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | |
| 2 | 3 | const testing = std.testing; |
| 3 | 4 | const mem = std.mem; |
| 4 | 5 | const expect = testing.expect; |
| ... | ... | @@ -76,3 +77,30 @@ test "array len field" { |
| 76 | 77 | try expect(ptr.len == 4); |
| 77 | 78 | comptime try expect(ptr.len == 4); |
| 78 | 79 | } |
| 80 | ||
| 81 | test "array with sentinels" { | |
| 82 | const S = struct { | |
| 83 | fn doTheTest(is_ct: bool) !void { | |
| 84 | if (is_ct or builtin.zig_is_stage2) { | |
| 85 | var zero_sized: [0:0xde]u8 = [_:0xde]u8{}; | |
| 86 | // Stage1 test coverage disabled at runtime because of | |
| 87 | // https://github.com/ziglang/zig/issues/4372 | |
| 88 | try expect(zero_sized[0] == 0xde); | |
| 89 | var reinterpreted = @ptrCast(*[1]u8, &zero_sized); | |
| 90 | try expect(reinterpreted[0] == 0xde); | |
| 91 | } | |
| 92 | var arr: [3:0x55]u8 = undefined; | |
| 93 | // Make sure the sentinel pointer is pointing after the last element. | |
| 94 | if (!is_ct) { | |
| 95 | const sentinel_ptr = @ptrToInt(&arr[3]); | |
| 96 | const last_elem_ptr = @ptrToInt(&arr[2]); | |
| 97 | try expect((sentinel_ptr - last_elem_ptr) == 1); | |
| 98 | } | |
| 99 | // Make sure the sentinel is writeable. | |
| 100 | arr[3] = 0x55; | |
| 101 | } | |
| 102 | }; | |
| 103 | ||
| 104 | try S.doTheTest(false); | |
| 105 | comptime try S.doTheTest(true); | |
| 106 | } |
test/behavior/array_stage1.zig-27| ... | ... | @@ -4,33 +4,6 @@ const mem = std.mem; |
| 4 | 4 | const expect = testing.expect; |
| 5 | 5 | const expectEqual = testing.expectEqual; |
| 6 | 6 | |
| 7 | test "array with sentinels" { | |
| 8 | const S = struct { | |
| 9 | fn doTheTest(is_ct: bool) !void { | |
| 10 | if (is_ct) { | |
| 11 | var zero_sized: [0:0xde]u8 = [_:0xde]u8{}; | |
| 12 | // Disabled at runtime because of | |
| 13 | // https://github.com/ziglang/zig/issues/4372 | |
| 14 | try expectEqual(@as(u8, 0xde), zero_sized[0]); | |
| 15 | var reinterpreted = @ptrCast(*[1]u8, &zero_sized); | |
| 16 | try expectEqual(@as(u8, 0xde), reinterpreted[0]); | |
| 17 | } | |
| 18 | var arr: [3:0x55]u8 = undefined; | |
| 19 | // Make sure the sentinel pointer is pointing after the last element | |
| 20 | if (!is_ct) { | |
| 21 | const sentinel_ptr = @ptrToInt(&arr[3]); | |
| 22 | const last_elem_ptr = @ptrToInt(&arr[2]); | |
| 23 | try expectEqual(@as(usize, 1), sentinel_ptr - last_elem_ptr); | |
| 24 | } | |
| 25 | // Make sure the sentinel is writeable | |
| 26 | arr[3] = 0x55; | |
| 27 | } | |
| 28 | }; | |
| 29 | ||
| 30 | try S.doTheTest(false); | |
| 31 | comptime try S.doTheTest(true); | |
| 32 | } | |
| 33 | ||
| 34 | 7 | test "void arrays" { |
| 35 | 8 | var array: [4]void = undefined; |
| 36 | 9 | array[0] = void{}; |