| 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,7 +1235,15 @@ fn arrayInitExpr( |
| 1235 | }; | 1235 | }; |
| 1236 | } else { | 1236 | } else { |
| 1237 | const sentinel = try comptimeExpr(gz, scope, .{ .ty = elem_type }, array_type.ast.sentinel); | 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 | break :inst .{ | 1247 | break :inst .{ |
| 1240 | .array = array_type_inst, | 1248 | .array = array_type_inst, |
| 1241 | .elem = elem_type, | 1249 | .elem = elem_type, |
| ... | @@ -1425,7 +1433,15 @@ fn structInitExpr( | ... | @@ -1425,7 +1433,15 @@ fn structInitExpr( |
| 1425 | break :blk try gz.addBin(.array_type, .zero_usize, elem_type); | 1433 | break :blk try gz.addBin(.array_type, .zero_usize, elem_type); |
| 1426 | } else blk: { | 1434 | } else blk: { |
| 1427 | const sentinel = try comptimeExpr(gz, scope, .{ .ty = elem_type }, array_type.ast.sentinel); | 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 | const result = try gz.addUnNode(.struct_init_empty, array_type_inst, node); | 1446 | const result = try gz.addUnNode(.struct_init_empty, array_type_inst, node); |
| 1431 | return rvalue(gz, rl, result, node); | 1447 | return rvalue(gz, rl, result, node); |
| ... | @@ -2976,11 +2992,15 @@ fn arrayTypeSentinel(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.I | ... | @@ -2976,11 +2992,15 @@ fn arrayTypeSentinel(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.I |
| 2976 | { | 2992 | { |
| 2977 | return astgen.failNode(len_node, "unable to infer array size", .{}); | 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 | const elem_type = try typeExpr(gz, scope, extra.elem_type); | 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 | return rvalue(gz, rl, result, node); | 3004 | return rvalue(gz, rl, result, node); |
| 2985 | } | 3005 | } |
| 2986 | 3006 | ||
| ... | @@ -10017,32 +10037,6 @@ const GenZir = struct { | ... | @@ -10017,32 +10037,6 @@ const GenZir = struct { |
| 10017 | return indexToRef(new_index); | 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 | fn addUnTok( | 10040 | fn addUnTok( |
| 10047 | gz: *GenZir, | 10041 | gz: *GenZir, |
| 10048 | tag: Zir.Inst.Tag, | 10042 | tag: Zir.Inst.Tag, |
src/Module.zig+70-30| ... | @@ -1885,6 +1885,55 @@ pub const SrcLoc = struct { | ... | @@ -1885,6 +1885,55 @@ pub const SrcLoc = struct { |
| 1885 | const token_starts = tree.tokens.items(.start); | 1885 | const token_starts = tree.tokens.items(.start); |
| 1886 | return token_starts[tok_index]; | 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,6 +2134,24 @@ pub const LazySrcLoc = union(enum) { |
| 2085 | /// expression AST node. Next, navigate to the string literal of the `extern "foo"`. | 2134 | /// expression AST node. Next, navigate to the string literal of the `extern "foo"`. |
| 2086 | /// The Decl is determined contextually. | 2135 | /// The Decl is determined contextually. |
| 2087 | node_offset_lib_name: i32, | 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 | /// Upgrade to a `SrcLoc` based on the `Decl` provided. | 2156 | /// Upgrade to a `SrcLoc` based on the `Decl` provided. |
| 2090 | pub fn toSrcLoc(lazy: LazySrcLoc, decl: *Decl) SrcLoc { | 2157 | pub fn toSrcLoc(lazy: LazySrcLoc, decl: *Decl) SrcLoc { |
| ... | @@ -2130,6 +2197,9 @@ pub const LazySrcLoc = union(enum) { | ... | @@ -2130,6 +2197,9 @@ pub const LazySrcLoc = union(enum) { |
| 2130 | .node_offset_fn_type_ret_ty, | 2197 | .node_offset_fn_type_ret_ty, |
| 2131 | .node_offset_anyframe_type, | 2198 | .node_offset_anyframe_type, |
| 2132 | .node_offset_lib_name, | 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 | .file_scope = decl.getFileScope(), | 2204 | .file_scope = decl.getFileScope(), |
| 2135 | .parent_decl_node = decl.src_node, | 2205 | .parent_decl_node = decl.src_node, |
| ... | @@ -4125,36 +4195,6 @@ pub fn optionalType(arena: *Allocator, child_type: Type) Allocator.Error!Type { | ... | @@ -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 | pub fn errorUnionType( | 4198 | pub fn errorUnionType( |
| 4159 | arena: *Allocator, | 4199 | arena: *Allocator, |
| 4160 | error_set: Type, | 4200 | error_set: Type, |
src/Sema.zig+309-86| ... | @@ -1021,7 +1021,7 @@ fn resolveConstString( | ... | @@ -1021,7 +1021,7 @@ fn resolveConstString( |
| 1021 | const wanted_type = Type.initTag(.const_slice_u8); | 1021 | const wanted_type = Type.initTag(.const_slice_u8); |
| 1022 | const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); | 1022 | const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); |
| 1023 | const val = try sema.resolveConstValue(block, src, coerced_inst); | 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 | pub fn resolveType(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type { | 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,10 +2436,10 @@ fn zirStr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins |
| 2436 | var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); | 2436 | var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); |
| 2437 | errdefer new_decl_arena.deinit(); | 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 | const decl_ty = try Type.Tag.array_u8_sentinel_0.create(&new_decl_arena.allocator, bytes.len); | 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 | const new_decl = try sema.mod.createAnonymousDecl(block, .{ | 2444 | const new_decl = try sema.mod.createAnonymousDecl(block, .{ |
| 2445 | .ty = decl_ty, | 2445 | .ty = decl_ty, |
| ... | @@ -3747,7 +3747,7 @@ fn analyzeCall( | ... | @@ -3747,7 +3747,7 @@ fn analyzeCall( |
| 3747 | .code = fn_zir, | 3747 | .code = fn_zir, |
| 3748 | .owner_decl = new_decl, | 3748 | .owner_decl = new_decl, |
| 3749 | .func = null, | 3749 | .func = null, |
| 3750 | .fn_ret_ty = Type.initTag(.void), | 3750 | .fn_ret_ty = Type.void, |
| 3751 | .owner_func = null, | 3751 | .owner_func = null, |
| 3752 | .comptime_args = try new_decl_arena.allocator.alloc(TypedValue, uncasted_args.len), | 3752 | .comptime_args = try new_decl_arena.allocator.alloc(TypedValue, uncasted_args.len), |
| 3753 | .comptime_args_fn_inst = module_fn.zir_body_inst, | 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,9 +4040,9 @@ fn zirArrayType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A |
| 4040 | defer tracy.end(); | 4040 | defer tracy.end(); |
| 4041 | 4041 | ||
| 4042 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | 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 | const elem_type = try sema.resolveType(block, .unneeded, bin_inst.rhs); | 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 | return sema.addType(array_ty); | 4047 | return sema.addType(array_ty); |
| 4048 | } | 4048 | } |
| ... | @@ -4051,14 +4051,17 @@ fn zirArrayTypeSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compil | ... | @@ -4051,14 +4051,17 @@ fn zirArrayTypeSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compil |
| 4051 | const tracy = trace(@src()); | 4051 | const tracy = trace(@src()); |
| 4052 | defer tracy.end(); | 4052 | defer tracy.end(); |
| 4053 | 4053 | ||
| 4054 | const inst_data = sema.code.instructions.items(.data)[inst].array_type_sentinel; | 4054 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 4055 | const len = try sema.resolveInt(block, .unneeded, inst_data.len, Type.initTag(.usize)); | ||
| 4056 | const extra = sema.code.extraData(Zir.Inst.ArrayTypeSentinel, inst_data.payload_index).data; | 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 | const uncasted_sentinel = sema.resolveInst(extra.sentinel); | 4061 | const uncasted_sentinel = sema.resolveInst(extra.sentinel); |
| 4059 | const sentinel = try sema.coerce(block, elem_type, uncasted_sentinel, .unneeded); | 4062 | const sentinel = try sema.coerce(block, elem_type, uncasted_sentinel, sentinel_src); |
| 4060 | const sentinel_val = try sema.resolveConstValue(block, .unneeded, sentinel); | 4063 | const sentinel_val = try sema.resolveConstValue(block, sentinel_src, sentinel); |
| 4061 | const array_ty = try Module.arrayType(sema.arena, len, sentinel_val, elem_type); | 4064 | const array_ty = try Type.array(sema.arena, len, sentinel_val, elem_type); |
| 4062 | 4065 | ||
| 4063 | return sema.addType(array_ty); | 4066 | return sema.addType(array_ty); |
| 4064 | } | 4067 | } |
| ... | @@ -4658,7 +4661,7 @@ fn funcCommon( | ... | @@ -4658,7 +4661,7 @@ fn funcCommon( |
| 4658 | // the function as generic. | 4661 | // the function as generic. |
| 4659 | var is_generic = false; | 4662 | var is_generic = false; |
| 4660 | const bare_return_type: Type = ret_ty: { | 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 | const err = err: { | 4666 | const err = err: { |
| 4664 | // Make sure any nested param instructions don't clobber our work. | 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,13 +6563,14 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 6560 | if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| { | 6563 | if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| { |
| 6561 | if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| { | 6564 | if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| { |
| 6562 | const final_len = lhs_info.len + rhs_info.len; | 6565 | const final_len = lhs_info.len + rhs_info.len; |
| 6566 | const final_len_including_sent = final_len + @boolToInt(res_sent != null); | ||
| 6563 | const is_pointer = lhs_ty.zigTypeTag() == .Pointer; | 6567 | const is_pointer = lhs_ty.zigTypeTag() == .Pointer; |
| 6564 | var anon_decl = try block.startAnonDecl(); | 6568 | var anon_decl = try block.startAnonDecl(); |
| 6565 | defer anon_decl.deinit(); | 6569 | defer anon_decl.deinit(); |
| 6566 | 6570 | ||
| 6567 | const lhs_sub_val = if (is_pointer) (try lhs_val.pointerDeref(anon_decl.arena())).? else lhs_val; | 6571 | const lhs_sub_val = if (is_pointer) (try lhs_val.pointerDeref(anon_decl.arena())).? else lhs_val; |
| 6568 | const rhs_sub_val = if (is_pointer) (try rhs_val.pointerDeref(anon_decl.arena())).? else rhs_val; | 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 | var i: u64 = 0; | 6575 | var i: u64 = 0; |
| 6572 | while (i < lhs_info.len) : (i += 1) { | 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,10 +6585,17 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 6581 | buf[lhs_info.len + i] = try val.copy(anon_decl.arena()); | 6585 | buf[lhs_info.len + i] = try val.copy(anon_decl.arena()); |
| 6582 | } | 6586 | } |
| 6583 | } | 6587 | } |
| 6584 | const ty = if (res_sent) |rs| | 6588 | const ty = if (res_sent) |rs| ty: { |
| 6585 | try Type.Tag.array_sentinel.create(anon_decl.arena(), .{ .len = final_len, .elem_type = lhs_info.elem_type, .sentinel = rs }) | 6589 | buf[final_len] = try rs.copy(anon_decl.arena()); |
| 6586 | else | 6590 | break :ty try Type.Tag.array_sentinel.create(anon_decl.arena(), .{ |
| 6587 | try Type.Tag.array.create(anon_decl.arena(), .{ .len = final_len, .elem_type = lhs_info.elem_type }); | 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 | const val = try Value.Tag.array.create(anon_decl.arena(), buf); | 6599 | const val = try Value.Tag.array.create(anon_decl.arena(), buf); |
| 6589 | return if (is_pointer) | 6600 | return if (is_pointer) |
| 6590 | sema.analyzeDeclRef(try anon_decl.finish(ty, val)) | 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,20 +6634,31 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 6623 | const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; | 6634 | const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; |
| 6624 | 6635 | ||
| 6625 | // In `**` rhs has to be comptime-known, but lhs can be runtime-known | 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 | const mulinfo = getArrayCatInfo(lhs_ty) orelse | 6638 | const mulinfo = getArrayCatInfo(lhs_ty) orelse |
| 6628 | return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty}); | 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 | if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| { | 6645 | if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| { |
| 6632 | var anon_decl = try block.startAnonDecl(); | 6646 | var anon_decl = try block.startAnonDecl(); |
| 6633 | defer anon_decl.deinit(); | 6647 | defer anon_decl.deinit(); |
| 6648 | |||
| 6634 | const lhs_sub_val = if (lhs_ty.zigTypeTag() == .Pointer) (try lhs_val.pointerDeref(anon_decl.arena())).? else lhs_val; | 6649 | const lhs_sub_val = if (lhs_ty.zigTypeTag() == .Pointer) (try lhs_val.pointerDeref(anon_decl.arena())).? else lhs_val; |
| 6635 | const final_ty = if (mulinfo.sentinel) |sent| | 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 | else | 6656 | else |
| 6638 | try Type.Tag.array.create(anon_decl.arena(), .{ .len = final_len, .elem_type = mulinfo.elem_type }); | 6657 | try Type.Tag.array.create(anon_decl.arena(), .{ |
| 6639 | const buf = try anon_decl.arena().alloc(Value, final_len); | 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 | // handles the optimisation where arr.len == 0 : [_]T { X } ** N | 6663 | // handles the optimisation where arr.len == 0 : [_]T { X } ** N |
| 6642 | const val = if (mulinfo.len == 1) blk: { | 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,6 +6674,9 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 6652 | buf[mulinfo.len * i + j] = try val.copy(anon_decl.arena()); | 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 | break :blk try Value.Tag.array.create(anon_decl.arena(), buf); | 6680 | break :blk try Value.Tag.array.create(anon_decl.arena(), buf); |
| 6656 | }; | 6681 | }; |
| 6657 | if (lhs_ty.zigTypeTag() == .Pointer) { | 6682 | if (lhs_ty.zigTypeTag() == .Pointer) { |
| ... | @@ -6760,7 +6785,7 @@ fn analyzeArithmetic( | ... | @@ -6760,7 +6785,7 @@ fn analyzeArithmetic( |
| 6760 | }; | 6785 | }; |
| 6761 | // TODO if the operand is comptime-known to be negative, or is a negative int, | 6786 | // TODO if the operand is comptime-known to be negative, or is a negative int, |
| 6762 | // coerce to isize instead of usize. | 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 | const runtime_src = runtime_src: { | 6789 | const runtime_src = runtime_src: { |
| 6765 | if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| { | 6790 | if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| { |
| 6766 | if (try sema.resolveDefinedValue(block, rhs_src, casted_rhs)) |rhs_val| { | 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,9 +8546,21 @@ fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE |
| 8521 | 8546 | ||
| 8522 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | 8547 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 8523 | const src = inst_data.src(); | 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 | fn zirUnionInitPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 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,7 +9010,7 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 8973 | 9010 | ||
| 8974 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | 9011 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; |
| 8975 | const operand_res = sema.resolveInst(extra.rhs); | 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 | const type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | 9015 | const type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| 8979 | const type_res = try sema.resolveType(block, src, extra.lhs); | 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,7 +9047,7 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 9010 | .data = ptr_align - 1, | 9047 | .data = ptr_align - 1, |
| 9011 | }; | 9048 | }; |
| 9012 | const align_minus_1 = try sema.addConstant( | 9049 | const align_minus_1 = try sema.addConstant( |
| 9013 | Type.initTag(.usize), | 9050 | Type.usize, |
| 9014 | Value.initPayload(&val_payload.base), | 9051 | Value.initPayload(&val_payload.base), |
| 9015 | ); | 9052 | ); |
| 9016 | const remainder = try block.addBinOp(.bit_and, operand_coerced, align_minus_1); | 9053 | const remainder = try block.addBinOp(.bit_and, operand_coerced, align_minus_1); |
| ... | @@ -9295,6 +9332,48 @@ fn checkAtomicOperandType( | ... | @@ -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 | fn resolveExportOptions( | 9377 | fn resolveExportOptions( |
| 9299 | sema: *Sema, | 9378 | sema: *Sema, |
| 9300 | block: *Block, | 9379 | block: *Block, |
| ... | @@ -9313,8 +9392,9 @@ fn resolveExportOptions( | ... | @@ -9313,8 +9392,9 @@ fn resolveExportOptions( |
| 9313 | if (!fields[section_index].isNull()) { | 9392 | if (!fields[section_index].isNull()) { |
| 9314 | return sema.fail(block, src, "TODO: implement exporting with linksection", .{}); | 9393 | return sema.fail(block, src, "TODO: implement exporting with linksection", .{}); |
| 9315 | } | 9394 | } |
| 9395 | const name_ty = Type.initTag(.const_slice_u8); | ||
| 9316 | return std.builtin.ExportOptions{ | 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 | .linkage = fields[linkage_index].toEnum(std.builtin.GlobalLinkage), | 9398 | .linkage = fields[linkage_index].toEnum(std.builtin.GlobalLinkage), |
| 9319 | .section = null, // TODO | 9399 | .section = null, // TODO |
| 9320 | }; | 9400 | }; |
| ... | @@ -9547,7 +9627,12 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A | ... | @@ -9547,7 +9627,12 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A |
| 9547 | } | 9627 | } |
| 9548 | 9628 | ||
| 9549 | const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { | 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 | const target = sema.mod.getTarget(); | 9636 | const target = sema.mod.getTarget(); |
| 9552 | const stored_val = (try ptr_val.pointerDeref(sema.arena)) orelse break :rs ptr_src; | 9637 | const stored_val = (try ptr_val.pointerDeref(sema.arena)) orelse break :rs ptr_src; |
| 9553 | const new_val = switch (op) { | 9638 | const new_val = switch (op) { |
| ... | @@ -9565,7 +9650,7 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A | ... | @@ -9565,7 +9650,7 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A |
| 9565 | }; | 9650 | }; |
| 9566 | try sema.storePtrVal(block, src, ptr_val, new_val, operand_ty); | 9651 | try sema.storePtrVal(block, src, ptr_val, new_val, operand_ty); |
| 9567 | return sema.addConstant(operand_ty, stored_val); | 9652 | return sema.addConstant(operand_ty, stored_val); |
| 9568 | } else break :rs operand_src; | 9653 | } else break :rs ptr_src; |
| 9569 | } else ptr_src; | 9654 | } else ptr_src; |
| 9570 | 9655 | ||
| 9571 | const flags: u32 = @as(u32, @enumToInt(order)) | (@as(u32, @enumToInt(op)) << 3); | 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,7 +9767,7 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void |
| 9682 | .size = .Many, | 9767 | .size = .Many, |
| 9683 | }); | 9768 | }); |
| 9684 | const src_ptr = try sema.coerce(block, wanted_src_ptr_ty, uncasted_src_ptr, src_src); | 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 | const maybe_dest_ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr); | 9772 | const maybe_dest_ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr); |
| 9688 | const maybe_src_ptr_val = try sema.resolveDefinedValue(block, src_src, src_ptr); | 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,7 +9814,7 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void |
| 9729 | } | 9814 | } |
| 9730 | const elem_ty = dest_ptr_ty.elemType2(); | 9815 | const elem_ty = dest_ptr_ty.elemType2(); |
| 9731 | const value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.byte), value_src); | 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 | const maybe_dest_ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr); | 9819 | const maybe_dest_ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr); |
| 9735 | const maybe_len_val = try sema.resolveDefinedValue(block, len_src, len); | 9820 | const maybe_len_val = try sema.resolveDefinedValue(block, len_src, len); |
| ... | @@ -10270,7 +10355,7 @@ fn fieldVal( | ... | @@ -10270,7 +10355,7 @@ fn fieldVal( |
| 10270 | try sema.requireRuntimeBlock(block, src); | 10355 | try sema.requireRuntimeBlock(block, src); |
| 10271 | return block.addTyOp(.slice_ptr, result_ty, object); | 10356 | return block.addTyOp(.slice_ptr, result_ty, object); |
| 10272 | } else if (mem.eql(u8, field_name, "len")) { | 10357 | } else if (mem.eql(u8, field_name, "len")) { |
| 10273 | const result_ty = Type.initTag(.usize); | 10358 | const result_ty = Type.usize; |
| 10274 | if (try sema.resolveMaybeUndefVal(block, object_src, object)) |val| { | 10359 | if (try sema.resolveMaybeUndefVal(block, object_src, object)) |val| { |
| 10275 | if (val.isUndef()) return sema.addConstUndef(result_ty); | 10360 | if (val.isUndef()) return sema.addConstUndef(result_ty); |
| 10276 | return sema.addConstant( | 10361 | return sema.addConstant( |
| ... | @@ -10955,7 +11040,7 @@ fn elemVal( | ... | @@ -10955,7 +11040,7 @@ fn elemVal( |
| 10955 | return block.addBinOp(.ptr_elem_val, array_maybe_ptr, elem_index); | 11040 | return block.addBinOp(.ptr_elem_val, array_maybe_ptr, elem_index); |
| 10956 | }, | 11041 | }, |
| 10957 | .One => { | 11042 | .One => { |
| 10958 | const indexable_ty = maybe_ptr_ty.elemType(); | 11043 | const indexable_ty = maybe_ptr_ty.childType(); |
| 10959 | switch (indexable_ty.zigTypeTag()) { | 11044 | switch (indexable_ty.zigTypeTag()) { |
| 10960 | .Pointer => switch (indexable_ty.ptrSize()) { | 11045 | .Pointer => switch (indexable_ty.ptrSize()) { |
| 10961 | .Slice => { | 11046 | .Slice => { |
| ... | @@ -10986,12 +11071,22 @@ fn elemVal( | ... | @@ -10986,12 +11071,22 @@ fn elemVal( |
| 10986 | try sema.requireRuntimeBlock(block, src); | 11071 | try sema.requireRuntimeBlock(block, src); |
| 10987 | return block.addBinOp(.ptr_ptr_elem_val, array_maybe_ptr, elem_index); | 11072 | return block.addBinOp(.ptr_ptr_elem_val, array_maybe_ptr, elem_index); |
| 10988 | }, | 11073 | }, |
| 10989 | .One => return sema.fail( | 11074 | .One => { |
| 10990 | block, | 11075 | const array_ty = indexable_ty.childType(); |
| 10991 | array_ptr_src, | 11076 | if (array_ty.zigTypeTag() == .Array) { |
| 10992 | "expected pointer, found '{}'", | 11077 | // We have a double pointer to an array, and we want an element |
| 10993 | .{indexable_ty.elemType()}, | 11078 | // value. This can happen with this code for example: |
| 10994 | ), | 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 | .Array => { | 11091 | .Array => { |
| 10997 | const ptr = try sema.elemPtr(block, src, array_maybe_ptr, elem_index, elem_index_src); | 11092 | const ptr = try sema.elemPtr(block, src, array_maybe_ptr, elem_index, elem_index_src); |
| ... | @@ -11463,13 +11558,15 @@ fn storePtr2( | ... | @@ -11463,13 +11558,15 @@ fn storePtr2( |
| 11463 | return; | 11558 | return; |
| 11464 | 11559 | ||
| 11465 | const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { | 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 | 11561 | const maybe_operand_val = try sema.resolveMaybeUndefVal(block, operand_src, operand); |
| 11467 | return sema.fail(block, src, "cannot store runtime value in compile time variable", .{}); | 11562 | const operand_val = maybe_operand_val orelse { |
| 11468 | if (ptr_val.tag() == .decl_ref_mut) { | 11563 | try sema.checkPtrIsNotComptimeMutable(block, ptr_val, ptr_src, operand_src); |
| 11564 | break :rs operand_src; | ||
| 11565 | }; | ||
| 11566 | if (ptr_val.isComptimeMutablePtr()) { | ||
| 11469 | try sema.storePtrVal(block, src, ptr_val, operand_val, elem_ty); | 11567 | try sema.storePtrVal(block, src, ptr_val, operand_val, elem_ty); |
| 11470 | return; | 11568 | return; |
| 11471 | } | 11569 | } else break :rs ptr_src; |
| 11472 | break :rs operand_src; | ||
| 11473 | } else ptr_src; | 11570 | } else ptr_src; |
| 11474 | 11571 | ||
| 11475 | // TODO handle if the element type requires comptime | 11572 | // TODO handle if the element type requires comptime |
| ... | @@ -11489,40 +11586,166 @@ fn storePtrVal( | ... | @@ -11489,40 +11586,166 @@ fn storePtrVal( |
| 11489 | operand_val: Value, | 11586 | operand_val: Value, |
| 11490 | operand_ty: Type, | 11587 | operand_ty: Type, |
| 11491 | ) !void { | 11588 | ) !void { |
| 11492 | if (ptr_val.castTag(.decl_ref_mut)) |decl_ref_mut| { | 11589 | var kit = try beginComptimePtrMutation(sema, block, src, ptr_val); |
| 11493 | if (decl_ref_mut.data.runtime_index < block.runtime_index) { | 11590 | try sema.checkComptimeVarStore(block, src, kit.decl_ref_mut); |
| 11494 | if (block.runtime_cond) |cond_src| { | 11591 | |
| 11495 | const msg = msg: { | 11592 | const target = sema.mod.getTarget(); |
| 11496 | const msg = try sema.errMsg(block, src, "store to comptime variable depends on runtime condition", .{}); | 11593 | const bitcasted_val = try operand_val.bitCast(operand_ty, kit.ty, target, sema.gpa, sema.arena); |
| 11497 | errdefer msg.destroy(sema.gpa); | 11594 | |
| 11498 | try sema.errNote(block, cond_src, msg, "runtime condition here", .{}); | 11595 | const arena = kit.beginArena(sema.gpa); |
| 11499 | break :msg msg; | 11596 | defer kit.finishArena(); |
| 11500 | }; | 11597 | |
| 11501 | return sema.failWithOwnedErrorMsg(msg); | 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| { | 11712 | }, |
| 11504 | const msg = msg: { | 11713 | .field_ptr => { |
| 11505 | const msg = try sema.errMsg(block, src, "cannot store to comptime variable in non-inline loop", .{}); | 11714 | const field_ptr = ptr_val.castTag(.field_ptr).?.data; |
| 11506 | errdefer msg.destroy(sema.gpa); | 11715 | var parent = try beginComptimePtrMutation(sema, block, src, field_ptr.container_ptr); |
| 11507 | try sema.errNote(block, loop_src, msg, "non-inline loop here", .{}); | 11716 | const field_ty = parent.ty.structFieldType(field_ptr.field_index); |
| 11508 | break :msg msg; | 11717 | switch (parent.val.tag()) { |
| 11509 | }; | 11718 | .undef => { |
| 11510 | return sema.failWithOwnedErrorMsg(msg); | 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; | 11744 | }, |
| 11513 | } | 11745 | .eu_payload_ptr => return sema.fail(block, src, "TODO comptime store to eu_payload_ptr", .{}), |
| 11514 | var new_arena = std.heap.ArenaAllocator.init(sema.gpa); | 11746 | .opt_payload_ptr => return sema.fail(block, src, "TODO comptime store opt_payload_ptr", .{}), |
| 11515 | errdefer new_arena.deinit(); | 11747 | .decl_ref => unreachable, // isComptimeMutablePtr() has been checked already |
| 11516 | const new_ty = try operand_ty.copy(&new_arena.allocator); | 11748 | else => unreachable, |
| 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; | ||
| 11526 | } | 11749 | } |
| 11527 | } | 11750 | } |
| 11528 | 11751 | ||
| ... | @@ -11672,7 +11895,7 @@ fn analyzeLoad( | ... | @@ -11672,7 +11895,7 @@ fn analyzeLoad( |
| 11672 | ) CompileError!Air.Inst.Ref { | 11895 | ) CompileError!Air.Inst.Ref { |
| 11673 | const ptr_ty = sema.typeOf(ptr); | 11896 | const ptr_ty = sema.typeOf(ptr); |
| 11674 | const elem_ty = switch (ptr_ty.zigTypeTag()) { | 11897 | const elem_ty = switch (ptr_ty.zigTypeTag()) { |
| 11675 | .Pointer => ptr_ty.elemType(), | 11898 | .Pointer => ptr_ty.childType(), |
| 11676 | else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty}), | 11899 | else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty}), |
| 11677 | }; | 11900 | }; |
| 11678 | if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { | 11901 | if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { |
| ... | @@ -11693,12 +11916,12 @@ fn analyzeSliceLen( | ... | @@ -11693,12 +11916,12 @@ fn analyzeSliceLen( |
| 11693 | ) CompileError!Air.Inst.Ref { | 11916 | ) CompileError!Air.Inst.Ref { |
| 11694 | if (try sema.resolveMaybeUndefVal(block, src, slice_inst)) |slice_val| { | 11917 | if (try sema.resolveMaybeUndefVal(block, src, slice_inst)) |slice_val| { |
| 11695 | if (slice_val.isUndef()) { | 11918 | if (slice_val.isUndef()) { |
| 11696 | return sema.addConstUndef(Type.initTag(.usize)); | 11919 | return sema.addConstUndef(Type.usize); |
| 11697 | } | 11920 | } |
| 11698 | return sema.addIntUnsigned(Type.usize, slice_val.sliceLen()); | 11921 | return sema.addIntUnsigned(Type.usize, slice_val.sliceLen()); |
| 11699 | } | 11922 | } |
| 11700 | try sema.requireRuntimeBlock(block, src); | 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 | fn analyzeIsNull( | 11927 | fn analyzeIsNull( |
| ... | @@ -11806,7 +12029,7 @@ fn analyzeSlice( | ... | @@ -11806,7 +12029,7 @@ fn analyzeSlice( |
| 11806 | array_type.sentinel() | 12029 | array_type.sentinel() |
| 11807 | else | 12030 | else |
| 11808 | slice_sentinel; | 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 | return_ptr_size = .One; | 12033 | return_ptr_size = .One; |
| 11811 | } | 12034 | } |
| 11812 | } | 12035 | } |
| ... | @@ -12396,7 +12619,7 @@ fn semaStructFields( | ... | @@ -12396,7 +12619,7 @@ fn semaStructFields( |
| 12396 | .code = zir, | 12619 | .code = zir, |
| 12397 | .owner_decl = decl, | 12620 | .owner_decl = decl, |
| 12398 | .func = null, | 12621 | .func = null, |
| 12399 | .fn_ret_ty = Type.initTag(.void), | 12622 | .fn_ret_ty = Type.void, |
| 12400 | .owner_func = null, | 12623 | .owner_func = null, |
| 12401 | }; | 12624 | }; |
| 12402 | defer sema.deinit(); | 12625 | defer sema.deinit(); |
| ... | @@ -12566,7 +12789,7 @@ fn semaUnionFields( | ... | @@ -12566,7 +12789,7 @@ fn semaUnionFields( |
| 12566 | .code = zir, | 12789 | .code = zir, |
| 12567 | .owner_decl = decl, | 12790 | .owner_decl = decl, |
| 12568 | .func = null, | 12791 | .func = null, |
| 12569 | .fn_ret_ty = Type.initTag(.void), | 12792 | .fn_ret_ty = Type.void, |
| 12570 | .owner_func = null, | 12793 | .owner_func = null, |
| 12571 | }; | 12794 | }; |
| 12572 | defer sema.deinit(); | 12795 | defer sema.deinit(); |
| ... | @@ -12677,7 +12900,7 @@ fn semaUnionFields( | ... | @@ -12677,7 +12900,7 @@ fn semaUnionFields( |
| 12677 | } | 12900 | } |
| 12678 | 12901 | ||
| 12679 | const field_ty: Type = if (!has_type) | 12902 | const field_ty: Type = if (!has_type) |
| 12680 | Type.initTag(.void) | 12903 | Type.void |
| 12681 | else if (field_type_ref == .none) | 12904 | else if (field_type_ref == .none) |
| 12682 | Type.initTag(.noreturn) | 12905 | Type.initTag(.noreturn) |
| 12683 | else | 12906 | else |
| ... | @@ -12959,7 +13182,7 @@ fn typeHasOnePossibleValue( | ... | @@ -12959,7 +13182,7 @@ fn typeHasOnePossibleValue( |
| 12959 | }, | 13182 | }, |
| 12960 | 13183 | ||
| 12961 | .empty_struct, .empty_struct_literal => return Value.initTag(.empty_struct_value), | 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 | .noreturn => return Value.initTag(.unreachable_value), | 13186 | .noreturn => return Value.initTag(.unreachable_value), |
| 12964 | .@"null" => return Value.initTag(.null_value), | 13187 | .@"null" => return Value.initTag(.null_value), |
| 12965 | .@"undefined" => return Value.initTag(.undef), | 13188 | .@"undefined" => return Value.initTag(.undef), |
src/Zir.zig+4-9| ... | @@ -210,8 +210,8 @@ pub const Inst = struct { | ... | @@ -210,8 +210,8 @@ pub const Inst = struct { |
| 210 | /// `[N]T` syntax. No source location provided. | 210 | /// `[N]T` syntax. No source location provided. |
| 211 | /// Uses the `bin` union field. lhs is length, rhs is element type. | 211 | /// Uses the `bin` union field. lhs is length, rhs is element type. |
| 212 | array_type, | 212 | array_type, |
| 213 | /// `[N:S]T` syntax. No source location provided. | 213 | /// `[N:S]T` syntax. Source location is the array type expression node. |
| 214 | /// Uses the `array_type_sentinel` field. | 214 | /// Uses the `pl_node` union field. Payload is `ArrayTypeSentinel`. |
| 215 | array_type_sentinel, | 215 | array_type_sentinel, |
| 216 | /// `@Vector` builtin. | 216 | /// `@Vector` builtin. |
| 217 | /// Uses the `pl_node` union field with `Bin` payload. | 217 | /// Uses the `pl_node` union field with `Bin` payload. |
| ... | @@ -1256,7 +1256,7 @@ pub const Inst = struct { | ... | @@ -1256,7 +1256,7 @@ pub const Inst = struct { |
| 1256 | .array_cat = .pl_node, | 1256 | .array_cat = .pl_node, |
| 1257 | .array_mul = .pl_node, | 1257 | .array_mul = .pl_node, |
| 1258 | .array_type = .bin, | 1258 | .array_type = .bin, |
| 1259 | .array_type_sentinel = .array_type_sentinel, | 1259 | .array_type_sentinel = .pl_node, |
| 1260 | .vector_type = .pl_node, | 1260 | .vector_type = .pl_node, |
| 1261 | .elem_type = .un_node, | 1261 | .elem_type = .un_node, |
| 1262 | .indexable_ptr_len = .un_node, | 1262 | .indexable_ptr_len = .un_node, |
| ... | @@ -2137,11 +2137,6 @@ pub const Inst = struct { | ... | @@ -2137,11 +2137,6 @@ pub const Inst = struct { |
| 2137 | node: i32, | 2137 | node: i32, |
| 2138 | int: u64, | 2138 | int: u64, |
| 2139 | float: f64, | 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 | ptr_type_simple: struct { | 2140 | ptr_type_simple: struct { |
| 2146 | is_allowzero: bool, | 2141 | is_allowzero: bool, |
| 2147 | is_mutable: bool, | 2142 | is_mutable: bool, |
| ... | @@ -2245,7 +2240,6 @@ pub const Inst = struct { | ... | @@ -2245,7 +2240,6 @@ pub const Inst = struct { |
| 2245 | node, | 2240 | node, |
| 2246 | int, | 2241 | int, |
| 2247 | float, | 2242 | float, |
| 2248 | array_type_sentinel, | ||
| 2249 | ptr_type_simple, | 2243 | ptr_type_simple, |
| 2250 | ptr_type, | 2244 | ptr_type, |
| 2251 | int_type, | 2245 | int_type, |
| ... | @@ -2427,6 +2421,7 @@ pub const Inst = struct { | ... | @@ -2427,6 +2421,7 @@ pub const Inst = struct { |
| 2427 | }; | 2421 | }; |
| 2428 | 2422 | ||
| 2429 | pub const ArrayTypeSentinel = struct { | 2423 | pub const ArrayTypeSentinel = struct { |
| 2424 | len: Ref, | ||
| 2430 | sentinel: Ref, | 2425 | sentinel: Ref, |
| 2431 | elem_type: Ref, | 2426 | elem_type: Ref, |
| 2432 | }; | 2427 | }; |
src/codegen/llvm.zig+33-27| ... | @@ -1031,54 +1031,60 @@ pub const DeclGen = struct { | ... | @@ -1031,54 +1031,60 @@ pub const DeclGen = struct { |
| 1031 | }, | 1031 | }, |
| 1032 | else => |tag| return self.todo("implement const of pointer type '{}' ({})", .{ tv.ty, tag }), | 1032 | else => |tag| return self.todo("implement const of pointer type '{}' ({})", .{ tv.ty, tag }), |
| 1033 | }, | 1033 | }, |
| 1034 | .Array => { | 1034 | .Array => switch (tv.val.tag()) { |
| 1035 | const gpa = self.gpa; | 1035 | .bytes => { |
| 1036 | if (tv.val.castTag(.bytes)) |payload| { | 1036 | const bytes = tv.val.castTag(.bytes).?.data; |
| 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 | |||
| 1042 | return self.context.constString( | 1037 | return self.context.constString( |
| 1043 | payload.data.ptr, | 1038 | bytes.ptr, |
| 1044 | @intCast(c_uint, payload.data.len), | 1039 | @intCast(c_uint, bytes.len), |
| 1045 | llvm.Bool.fromBool(!zero_sentinel), | 1040 | .True, // don't null terminate. bytes has the sentinel, if any. |
| 1046 | ); | 1041 | ); |
| 1047 | } | 1042 | }, |
| 1048 | if (tv.val.castTag(.array)) |payload| { | 1043 | .array => { |
| 1044 | const elem_vals = tv.val.castTag(.array).?.data; | ||
| 1049 | const elem_ty = tv.ty.elemType(); | 1045 | const elem_ty = tv.ty.elemType(); |
| 1050 | const elem_vals = payload.data; | 1046 | const gpa = self.gpa; |
| 1051 | const sento = tv.ty.sentinel(); | 1047 | const llvm_elems = try gpa.alloc(*const llvm.Value, elem_vals.len); |
| 1052 | const llvm_elems = try gpa.alloc(*const llvm.Value, elem_vals.len + @boolToInt(sento != null)); | ||
| 1053 | defer gpa.free(llvm_elems); | 1048 | defer gpa.free(llvm_elems); |
| 1054 | for (elem_vals) |elem_val, i| { | 1049 | for (elem_vals) |elem_val, i| { |
| 1055 | llvm_elems[i] = try self.genTypedValue(.{ .ty = elem_ty, .val = elem_val }); | 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 | const llvm_elem_ty = try self.llvmType(elem_ty); | 1052 | const llvm_elem_ty = try self.llvmType(elem_ty); |
| 1059 | return llvm_elem_ty.constArray( | 1053 | return llvm_elem_ty.constArray( |
| 1060 | llvm_elems.ptr, | 1054 | llvm_elems.ptr, |
| 1061 | @intCast(c_uint, llvm_elems.len), | 1055 | @intCast(c_uint, llvm_elems.len), |
| 1062 | ); | 1056 | ); |
| 1063 | } | 1057 | }, |
| 1064 | if (tv.val.castTag(.repeated)) |payload| { | 1058 | .repeated => { |
| 1065 | const val = payload.data; | 1059 | const val = tv.val.castTag(.repeated).?.data; |
| 1066 | const elem_ty = tv.ty.elemType(); | 1060 | const elem_ty = tv.ty.elemType(); |
| 1061 | const sentinel = tv.ty.sentinel(); | ||
| 1067 | const len = tv.ty.arrayLen(); | 1062 | const len = tv.ty.arrayLen(); |
| 1068 | 1063 | const len_including_sent = len + @boolToInt(sentinel != null); | |
| 1069 | const llvm_elems = try gpa.alloc(*const llvm.Value, len); | 1064 | const gpa = self.gpa; |
| 1065 | const llvm_elems = try gpa.alloc(*const llvm.Value, len_including_sent); | ||
| 1070 | defer gpa.free(llvm_elems); | 1066 | defer gpa.free(llvm_elems); |
| 1071 | var i: u64 = 0; | 1067 | for (llvm_elems[0..len]) |*elem| { |
| 1072 | while (i < len) : (i += 1) { | 1068 | elem.* = try self.genTypedValue(.{ .ty = elem_ty, .val = val }); |
| 1073 | llvm_elems[i] = 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 | const llvm_elem_ty = try self.llvmType(elem_ty); | 1073 | const llvm_elem_ty = try self.llvmType(elem_ty); |
| 1076 | return llvm_elem_ty.constArray( | 1074 | return llvm_elem_ty.constArray( |
| 1077 | llvm_elems.ptr, | 1075 | llvm_elems.ptr, |
| 1078 | @intCast(c_uint, llvm_elems.len), | 1076 | @intCast(c_uint, llvm_elems.len), |
| 1079 | ); | 1077 | ); |
| 1080 | } | 1078 | }, |
| 1081 | return self.todo("handle more array values", .{}); | 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 | .Optional => { | 1089 | .Optional => { |
| 1084 | var buf: Type.Payload.ElemType = undefined; | 1090 | var buf: Type.Payload.ElemType = undefined; |
src/codegen/llvm/bindings.zig+1-1| ... | @@ -194,7 +194,7 @@ pub const Type = opaque { | ... | @@ -194,7 +194,7 @@ pub const Type = opaque { |
| 194 | extern fn LLVMConstReal(RealTy: *const Type, N: f64) *const Value; | 194 | extern fn LLVMConstReal(RealTy: *const Type, N: f64) *const Value; |
| 195 | 195 | ||
| 196 | pub const constArray = LLVMConstArray; | 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 | pub const constNamedStruct = LLVMConstNamedStruct; | 199 | pub const constNamedStruct = LLVMConstNamedStruct; |
| 200 | extern fn LLVMConstNamedStruct( | 200 | extern fn LLVMConstNamedStruct( |
src/print_zir.zig+4-3| ... | @@ -542,14 +542,15 @@ const Writer = struct { | ... | @@ -542,14 +542,15 @@ const Writer = struct { |
| 542 | stream: anytype, | 542 | stream: anytype, |
| 543 | inst: Zir.Inst.Index, | 543 | inst: Zir.Inst.Index, |
| 544 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | 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 | const extra = self.code.extraData(Zir.Inst.ArrayTypeSentinel, inst_data.payload_index).data; | 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 | try stream.writeAll(", "); | 548 | try stream.writeAll(", "); |
| 549 | try self.writeInstRef(stream, extra.sentinel); | 549 | try self.writeInstRef(stream, extra.sentinel); |
| 550 | try stream.writeAll(", "); | 550 | try stream.writeAll(", "); |
| 551 | try self.writeInstRef(stream, extra.elem_type); | 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 | fn writePtrTypeSimple( | 556 | fn writePtrTypeSimple( |
src/type.zig+56-8| ... | @@ -1468,7 +1468,19 @@ pub const Type = extern union { | ... | @@ -1468,7 +1468,19 @@ pub const Type = extern union { |
| 1468 | // TODO lazy types | 1468 | // TODO lazy types |
| 1469 | .array, .vector => self.elemType().hasCodeGenBits() and self.arrayLen() != 0, | 1469 | .array, .vector => self.elemType().hasCodeGenBits() and self.arrayLen() != 0, |
| 1470 | .array_u8 => self.arrayLen() != 0, | 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 | .int_signed, .int_unsigned => self.cast(Payload.Bits).?.data != 0, | 1484 | .int_signed, .int_unsigned => self.cast(Payload.Bits).?.data != 0, |
| 1473 | 1485 | ||
| 1474 | .error_union => { | 1486 | .error_union => { |
| ... | @@ -2560,18 +2572,22 @@ pub const Type = extern union { | ... | @@ -2560,18 +2572,22 @@ pub const Type = extern union { |
| 2560 | } | 2572 | } |
| 2561 | 2573 | ||
| 2562 | /// Asserts the type is an array or vector. | 2574 | /// Asserts the type is an array or vector. |
| 2563 | pub fn arrayLen(self: Type) u64 { | 2575 | pub fn arrayLen(ty: Type) u64 { |
| 2564 | return switch (self.tag()) { | 2576 | return switch (ty.tag()) { |
| 2565 | .vector => self.castTag(.vector).?.data.len, | 2577 | .vector => ty.castTag(.vector).?.data.len, |
| 2566 | .array => self.castTag(.array).?.data.len, | 2578 | .array => ty.castTag(.array).?.data.len, |
| 2567 | .array_sentinel => self.castTag(.array_sentinel).?.data.len, | 2579 | .array_sentinel => ty.castTag(.array_sentinel).?.data.len, |
| 2568 | .array_u8 => self.castTag(.array_u8).?.data, | 2580 | .array_u8 => ty.castTag(.array_u8).?.data, |
| 2569 | .array_u8_sentinel_0 => self.castTag(.array_u8_sentinel_0).?.data, | 2581 | .array_u8_sentinel_0 => ty.castTag(.array_u8_sentinel_0).?.data, |
| 2570 | 2582 | ||
| 2571 | else => unreachable, | 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 | /// Asserts the type is an array, pointer or vector. | 2591 | /// Asserts the type is an array, pointer or vector. |
| 2576 | pub fn sentinel(self: Type) ?Value { | 2592 | pub fn sentinel(self: Type) ?Value { |
| 2577 | return switch (self.tag()) { | 2593 | return switch (self.tag()) { |
| ... | @@ -3882,9 +3898,11 @@ pub const Type = extern union { | ... | @@ -3882,9 +3898,11 @@ pub const Type = extern union { |
| 3882 | }; | 3898 | }; |
| 3883 | }; | 3899 | }; |
| 3884 | 3900 | ||
| 3901 | pub const @"u8" = initTag(.u8); | ||
| 3885 | pub const @"bool" = initTag(.bool); | 3902 | pub const @"bool" = initTag(.bool); |
| 3886 | pub const @"usize" = initTag(.usize); | 3903 | pub const @"usize" = initTag(.usize); |
| 3887 | pub const @"comptime_int" = initTag(.comptime_int); | 3904 | pub const @"comptime_int" = initTag(.comptime_int); |
| 3905 | pub const @"void" = initTag(.void); | ||
| 3888 | 3906 | ||
| 3889 | pub fn ptr(arena: *Allocator, d: Payload.Pointer.Data) !Type { | 3907 | pub fn ptr(arena: *Allocator, d: Payload.Pointer.Data) !Type { |
| 3890 | assert(d.host_size == 0 or d.bit_offset < d.host_size * 8); | 3908 | assert(d.host_size == 0 or d.bit_offset < d.host_size * 8); |
| ... | @@ -3917,6 +3935,36 @@ pub const Type = extern union { | ... | @@ -3917,6 +3935,36 @@ pub const Type = extern union { |
| 3917 | return Type.initPayload(&type_payload.base); | 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 | pub fn smallestUnsignedBits(max: u64) u16 { | 3968 | pub fn smallestUnsignedBits(max: u64) u16 { |
| 3921 | if (max == 0) return 0; | 3969 | if (max == 0) return 0; |
| 3922 | const base = std.math.log2(max); | 3970 | const base = std.math.log2(max); |
src/value.zig+80-51| ... | @@ -112,7 +112,9 @@ pub const Value = extern union { | ... | @@ -112,7 +112,9 @@ pub const Value = extern union { |
| 112 | /// This Tag will never be seen by machine codegen backends. It is changed into a | 112 | /// This Tag will never be seen by machine codegen backends. It is changed into a |
| 113 | /// `decl_ref` when a comptime variable goes out of scope. | 113 | /// `decl_ref` when a comptime variable goes out of scope. |
| 114 | decl_ref_mut, | 114 | decl_ref_mut, |
| 115 | /// Pointer to a specific element of an array. | ||
| 115 | elem_ptr, | 116 | elem_ptr, |
| 117 | /// Pointer to a specific field of a struct. | ||
| 116 | field_ptr, | 118 | field_ptr, |
| 117 | /// A slice of u8 whose memory is managed externally. | 119 | /// A slice of u8 whose memory is managed externally. |
| 118 | bytes, | 120 | bytes, |
| ... | @@ -120,7 +122,11 @@ pub const Value = extern union { | ... | @@ -120,7 +122,11 @@ pub const Value = extern union { |
| 120 | /// is stored externally. | 122 | /// is stored externally. |
| 121 | repeated, | 123 | repeated, |
| 122 | /// Each element stored as a `Value`. | 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 | array, | 127 | array, |
| 128 | /// An array with length 0 but it has a sentinel. | ||
| 129 | empty_array_sentinel, | ||
| 124 | /// Pointer and length as sub `Value` objects. | 130 | /// Pointer and length as sub `Value` objects. |
| 125 | slice, | 131 | slice, |
| 126 | float_16, | 132 | float_16, |
| ... | @@ -255,6 +261,7 @@ pub const Value = extern union { | ... | @@ -255,6 +261,7 @@ pub const Value = extern union { |
| 255 | .eu_payload_ptr, | 261 | .eu_payload_ptr, |
| 256 | .opt_payload, | 262 | .opt_payload, |
| 257 | .opt_payload_ptr, | 263 | .opt_payload_ptr, |
| 264 | .empty_array_sentinel, | ||
| 258 | => Payload.SubValue, | 265 | => Payload.SubValue, |
| 259 | 266 | ||
| 260 | .bytes, | 267 | .bytes, |
| ... | @@ -486,6 +493,7 @@ pub const Value = extern union { | ... | @@ -486,6 +493,7 @@ pub const Value = extern union { |
| 486 | .eu_payload_ptr, | 493 | .eu_payload_ptr, |
| 487 | .opt_payload, | 494 | .opt_payload, |
| 488 | .opt_payload_ptr, | 495 | .opt_payload_ptr, |
| 496 | .empty_array_sentinel, | ||
| 489 | => { | 497 | => { |
| 490 | const payload = self.cast(Payload.SubValue).?; | 498 | const payload = self.cast(Payload.SubValue).?; |
| 491 | const new_payload = try arena.create(Payload.SubValue); | 499 | const new_payload = try arena.create(Payload.SubValue); |
| ... | @@ -697,6 +705,7 @@ pub const Value = extern union { | ... | @@ -697,6 +705,7 @@ pub const Value = extern union { |
| 697 | val = val.castTag(.repeated).?.data; | 705 | val = val.castTag(.repeated).?.data; |
| 698 | }, | 706 | }, |
| 699 | .array => return out_stream.writeAll("(array)"), | 707 | .array => return out_stream.writeAll("(array)"), |
| 708 | .empty_array_sentinel => return out_stream.writeAll("(empty array with sentinel)"), | ||
| 700 | .slice => return out_stream.writeAll("(slice)"), | 709 | .slice => return out_stream.writeAll("(slice)"), |
| 701 | .float_16 => return out_stream.print("{}", .{val.castTag(.float_16).?.data}), | 710 | .float_16 => return out_stream.print("{}", .{val.castTag(.float_16).?.data}), |
| 702 | .float_32 => return out_stream.print("{}", .{val.castTag(.float_32).?.data}), | 711 | .float_32 => return out_stream.print("{}", .{val.castTag(.float_32).?.data}), |
| ... | @@ -731,22 +740,23 @@ pub const Value = extern union { | ... | @@ -731,22 +740,23 @@ pub const Value = extern union { |
| 731 | 740 | ||
| 732 | /// Asserts that the value is representable as an array of bytes. | 741 | /// Asserts that the value is representable as an array of bytes. |
| 733 | /// Copies the value into a freshly allocated slice of memory, which is owned by the caller. | 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 { | 743 | pub fn toAllocatedBytes(val: Value, ty: Type, allocator: *Allocator) ![]u8 { |
| 735 | if (self.castTag(.bytes)) |payload| { | 744 | switch (val.tag()) { |
| 736 | return std.mem.dupe(allocator, u8, payload.data); | 745 | .bytes => { |
| 737 | } | 746 | const bytes = val.castTag(.bytes).?.data; |
| 738 | if (self.castTag(.enum_literal)) |payload| { | 747 | const adjusted_len = bytes.len - @boolToInt(ty.sentinel() != null); |
| 739 | return std.mem.dupe(allocator, u8, payload.data); | 748 | const adjusted_bytes = bytes[0..adjusted_len]; |
| 740 | } | 749 | return std.mem.dupe(allocator, u8, adjusted_bytes); |
| 741 | if (self.castTag(.repeated)) |payload| { | 750 | }, |
| 742 | _ = payload; | 751 | .enum_literal => return std.mem.dupe(allocator, u8, val.castTag(.enum_literal).?.data), |
| 743 | @panic("TODO implement toAllocatedBytes for this Value tag"); | 752 | .repeated => @panic("TODO implement toAllocatedBytes for this Value tag"), |
| 744 | } | 753 | .decl_ref => { |
| 745 | if (self.castTag(.decl_ref)) |payload| { | 754 | const decl = val.castTag(.decl_ref).?.data; |
| 746 | const val = try payload.data.value(); | 755 | const decl_val = try decl.value(); |
| 747 | return val.toAllocatedBytes(allocator); | 756 | return decl_val.toAllocatedBytes(decl.ty, allocator); |
| 757 | }, | ||
| 758 | else => unreachable, | ||
| 748 | } | 759 | } |
| 749 | unreachable; | ||
| 750 | } | 760 | } |
| 751 | 761 | ||
| 752 | pub const ToTypeBuffer = Type.Payload.Bits; | 762 | pub const ToTypeBuffer = Type.Payload.Bits; |
| ... | @@ -965,6 +975,8 @@ pub const Value = extern union { | ... | @@ -965,6 +975,8 @@ pub const Value = extern union { |
| 965 | gpa: *Allocator, | 975 | gpa: *Allocator, |
| 966 | arena: *Allocator, | 976 | arena: *Allocator, |
| 967 | ) !Value { | 977 | ) !Value { |
| 978 | if (old_ty.eql(new_ty)) return val; | ||
| 979 | |||
| 968 | // For types with well-defined memory layouts, we serialize them a byte buffer, | 980 | // For types with well-defined memory layouts, we serialize them a byte buffer, |
| 969 | // then deserialize to the new type. | 981 | // then deserialize to the new type. |
| 970 | const buffer = try gpa.alloc(u8, old_ty.abiSize(target)); | 982 | const buffer = try gpa.alloc(u8, old_ty.abiSize(target)); |
| ... | @@ -1527,37 +1539,34 @@ pub const Value = extern union { | ... | @@ -1527,37 +1539,34 @@ pub const Value = extern union { |
| 1527 | 1539 | ||
| 1528 | /// Asserts the value is a pointer and dereferences it. | 1540 | /// Asserts the value is a pointer and dereferences it. |
| 1529 | /// Returns error.AnalysisFail if the pointer points to a Decl that failed semantic analysis. | 1541 | /// Returns error.AnalysisFail if the pointer points to a Decl that failed semantic analysis. |
| 1530 | pub fn pointerDeref( | 1542 | pub fn pointerDeref(val: Value, arena: *Allocator) error{ AnalysisFail, OutOfMemory }!?Value { |
| 1531 | self: Value, | 1543 | const sub_val: Value = switch (val.tag()) { |
| 1532 | allocator: *Allocator, | 1544 | .decl_ref_mut => sub_val: { |
| 1533 | ) error{ AnalysisFail, OutOfMemory }!?Value { | ||
| 1534 | const sub_val: Value = switch (self.tag()) { | ||
| 1535 | .decl_ref_mut => val: { | ||
| 1536 | // The decl whose value we are obtaining here may be overwritten with | 1545 | // The decl whose value we are obtaining here may be overwritten with |
| 1537 | // a different value, which would invalidate this memory. So we must | 1546 | // a different value, which would invalidate this memory. So we must |
| 1538 | // copy here. | 1547 | // copy here. |
| 1539 | const val = try self.castTag(.decl_ref_mut).?.data.decl.value(); | 1548 | const sub_val = try val.castTag(.decl_ref_mut).?.data.decl.value(); |
| 1540 | break :val try val.copy(allocator); | 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 | .elem_ptr => blk: { | 1552 | .elem_ptr => blk: { |
| 1544 | const elem_ptr = self.castTag(.elem_ptr).?.data; | 1553 | const elem_ptr = val.castTag(.elem_ptr).?.data; |
| 1545 | const array_val = (try elem_ptr.array_ptr.pointerDeref(allocator)) orelse return null; | 1554 | const array_val = (try elem_ptr.array_ptr.pointerDeref(arena)) orelse return null; |
| 1546 | break :blk try array_val.elemValue(allocator, elem_ptr.index); | 1555 | break :blk try array_val.elemValue(arena, elem_ptr.index); |
| 1547 | }, | 1556 | }, |
| 1548 | .field_ptr => blk: { | 1557 | .field_ptr => blk: { |
| 1549 | const field_ptr = self.castTag(.field_ptr).?.data; | 1558 | const field_ptr = val.castTag(.field_ptr).?.data; |
| 1550 | const container_val = (try field_ptr.container_ptr.pointerDeref(allocator)) orelse return null; | 1559 | const container_val = (try field_ptr.container_ptr.pointerDeref(arena)) orelse return null; |
| 1551 | break :blk try container_val.fieldValue(allocator, field_ptr.field_index); | 1560 | break :blk try container_val.fieldValue(arena, field_ptr.field_index); |
| 1552 | }, | 1561 | }, |
| 1553 | .eu_payload_ptr => blk: { | 1562 | .eu_payload_ptr => blk: { |
| 1554 | const err_union_ptr = self.castTag(.eu_payload_ptr).?.data; | 1563 | const err_union_ptr = val.castTag(.eu_payload_ptr).?.data; |
| 1555 | const err_union_val = (try err_union_ptr.pointerDeref(allocator)) orelse return null; | 1564 | const err_union_val = (try err_union_ptr.pointerDeref(arena)) orelse return null; |
| 1556 | break :blk err_union_val.castTag(.eu_payload).?.data; | 1565 | break :blk err_union_val.castTag(.eu_payload).?.data; |
| 1557 | }, | 1566 | }, |
| 1558 | .opt_payload_ptr => blk: { | 1567 | .opt_payload_ptr => blk: { |
| 1559 | const opt_ptr = self.castTag(.opt_payload_ptr).?.data; | 1568 | const opt_ptr = val.castTag(.opt_payload_ptr).?.data; |
| 1560 | const opt_val = (try opt_ptr.pointerDeref(allocator)) orelse return null; | 1569 | const opt_val = (try opt_ptr.pointerDeref(arena)) orelse return null; |
| 1561 | break :blk opt_val.castTag(.opt_payload).?.data; | 1570 | break :blk opt_val.castTag(.opt_payload).?.data; |
| 1562 | }, | 1571 | }, |
| 1563 | 1572 | ||
| ... | @@ -1582,24 +1591,33 @@ pub const Value = extern union { | ... | @@ -1582,24 +1591,33 @@ pub const Value = extern union { |
| 1582 | return sub_val; | 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 | /// Gets the decl referenced by this pointer. If the pointer does not point | 1606 | /// Gets the decl referenced by this pointer. If the pointer does not point |
| 1586 | /// to a decl, or if it points to some part of a decl (like field_ptr or element_ptr), | 1607 | /// to a decl, or if it points to some part of a decl (like field_ptr or element_ptr), |
| 1587 | /// this function returns null. | 1608 | /// this function returns null. |
| 1588 | pub fn pointerDecl(self: Value) ?*Module.Decl { | 1609 | pub fn pointerDecl(val: Value) ?*Module.Decl { |
| 1589 | return switch (self.tag()) { | 1610 | return switch (val.tag()) { |
| 1590 | .decl_ref_mut => self.castTag(.decl_ref_mut).?.data.decl, | 1611 | .decl_ref_mut => val.castTag(.decl_ref_mut).?.data.decl, |
| 1591 | .extern_fn, .decl_ref => self.cast(Payload.Decl).?.data, | 1612 | .extern_fn, .decl_ref => val.cast(Payload.Decl).?.data, |
| 1592 | .function => self.castTag(.function).?.data.owner_decl, | 1613 | .function => val.castTag(.function).?.data.owner_decl, |
| 1593 | .variable => self.castTag(.variable).?.data.owner_decl, | 1614 | .variable => val.castTag(.variable).?.data.owner_decl, |
| 1594 | else => null, | 1615 | else => null, |
| 1595 | }; | 1616 | }; |
| 1596 | } | 1617 | } |
| 1597 | 1618 | ||
| 1598 | pub fn sliceLen(val: Value) u64 { | 1619 | pub fn sliceLen(val: Value) u64 { |
| 1599 | return switch (val.tag()) { | 1620 | return switch (val.tag()) { |
| 1600 | .empty_array => 0, | ||
| 1601 | .bytes => val.castTag(.bytes).?.data.len, | ||
| 1602 | .array => val.castTag(.array).?.data.len, | ||
| 1603 | .slice => val.castTag(.slice).?.data.len.toUnsignedInt(), | 1621 | .slice => val.castTag(.slice).?.data.len.toUnsignedInt(), |
| 1604 | .decl_ref => { | 1622 | .decl_ref => { |
| 1605 | const decl = val.castTag(.decl_ref).?.data; | 1623 | const decl = val.castTag(.decl_ref).?.data; |
| ... | @@ -1615,17 +1633,23 @@ pub const Value = extern union { | ... | @@ -1615,17 +1633,23 @@ pub const Value = extern union { |
| 1615 | 1633 | ||
| 1616 | /// Asserts the value is a single-item pointer to an array, or an array, | 1634 | /// Asserts the value is a single-item pointer to an array, or an array, |
| 1617 | /// or an unknown-length pointer, and returns the element value at the index. | 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 { | 1636 | pub fn elemValue(val: Value, arena: *Allocator, index: usize) error{OutOfMemory}!Value { |
| 1619 | switch (self.tag()) { | 1637 | switch (val.tag()) { |
| 1620 | .empty_array => unreachable, // out of bounds array index | 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 | // No matter the index; all the elements are the same! | 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], | 1651 | .array => return val.castTag(.array).?.data[index], |
| 1628 | .slice => return self.castTag(.slice).?.data.ptr.elemValue(allocator, index), | 1652 | .slice => return val.castTag(.slice).?.data.ptr.elemValue(arena, index), |
| 1629 | 1653 | ||
| 1630 | else => unreachable, | 1654 | else => unreachable, |
| 1631 | } | 1655 | } |
| ... | @@ -2556,10 +2580,12 @@ pub const Value = extern union { | ... | @@ -2556,10 +2580,12 @@ pub const Value = extern union { |
| 2556 | pub const base_tag = Tag.decl_ref_mut; | 2580 | pub const base_tag = Tag.decl_ref_mut; |
| 2557 | 2581 | ||
| 2558 | base: Payload = Payload{ .tag = base_tag }, | 2582 | base: Payload = Payload{ .tag = base_tag }, |
| 2559 | data: struct { | 2583 | data: Data, |
| 2584 | |||
| 2585 | pub const Data = struct { | ||
| 2560 | decl: *Module.Decl, | 2586 | decl: *Module.Decl, |
| 2561 | runtime_index: u32, | 2587 | runtime_index: u32, |
| 2562 | }, | 2588 | }; |
| 2563 | }; | 2589 | }; |
| 2564 | 2590 | ||
| 2565 | pub const ElemPtr = struct { | 2591 | pub const ElemPtr = struct { |
| ... | @@ -2584,6 +2610,7 @@ pub const Value = extern union { | ... | @@ -2584,6 +2610,7 @@ pub const Value = extern union { |
| 2584 | 2610 | ||
| 2585 | pub const Bytes = struct { | 2611 | pub const Bytes = struct { |
| 2586 | base: Payload, | 2612 | base: Payload, |
| 2613 | /// Includes the sentinel, if any. | ||
| 2587 | data: []const u8, | 2614 | data: []const u8, |
| 2588 | }; | 2615 | }; |
| 2589 | 2616 | ||
| ... | @@ -2706,6 +2733,8 @@ pub const Value = extern union { | ... | @@ -2706,6 +2733,8 @@ pub const Value = extern union { |
| 2706 | pub const zero = initTag(.zero); | 2733 | pub const zero = initTag(.zero); |
| 2707 | pub const one = initTag(.one); | 2734 | pub const one = initTag(.one); |
| 2708 | pub const negative_one: Value = .{ .ptr_otherwise = &negative_one_payload.base }; | 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 | var negative_one_payload: Value.Payload.I64 = .{ | 2740 | var negative_one_payload: Value.Payload.I64 = .{ |
test/behavior/array.zig+28| ... | @@ -1,4 +1,5 @@ | ... | @@ -1,4 +1,5 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | ||
| 2 | const testing = std.testing; | 3 | const testing = std.testing; |
| 3 | const mem = std.mem; | 4 | const mem = std.mem; |
| 4 | const expect = testing.expect; | 5 | const expect = testing.expect; |
| ... | @@ -76,3 +77,30 @@ test "array len field" { | ... | @@ -76,3 +77,30 @@ test "array len field" { |
| 76 | try expect(ptr.len == 4); | 77 | try expect(ptr.len == 4); |
| 77 | comptime try expect(ptr.len == 4); | 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,33 +4,6 @@ const mem = std.mem; |
| 4 | const expect = testing.expect; | 4 | const expect = testing.expect; |
| 5 | const expectEqual = testing.expectEqual; | 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 | test "void arrays" { | 7 | test "void arrays" { |
| 35 | var array: [4]void = undefined; | 8 | var array: [4]void = undefined; |
| 36 | array[0] = void{}; | 9 | array[0] = void{}; |