authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-12 21:26:59-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-12 21:38:46-07:00
loga3104a4a78089f3260c0dd3f4a96012c6d73a63b
tree63d73840d9d48d09cd27c3fef5e52f051a2dc4bd
parent7f006287ae89b97ff0ca977f64744ea344ec94fd

stage2: fix comptime stores and sentinel-terminated arrays

* 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);
29822998
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}
29863006
...@@ -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 }
1001910039
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 }
18901939
...@@ -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,
20882155
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}
41274197
4128pub 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
4158pub fn errorUnionType(4198pub 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}
10261026
1027pub fn resolveType(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type {1027pub 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();
24382438
2439 const bytes = try new_decl_arena.allocator.dupe(u8, zir_bytes);2439 const bytes = try new_decl_arena.allocator.dupeZ(u8, zir_bytes);
24402440
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]);
24432443
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();
40414041
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);
40464046
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();
40534053
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);
40624065
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;
46624665
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();
65666570
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 else6590 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 };
66246635
6625 // In `**` rhs has to be comptime-known, but lhs can be runtime-known6636 // 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) orelse6638 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});
66296640
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 else6656 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);
66406662
6641 // handles the optimisation where arr.len == 0 : [_]T { X } ** N6663 // 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
85218546
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);
85258550
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}
85288565
8529fn zirUnionInitPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {8566fn 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
89739010
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);
89779014
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}
92979334
9335fn 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
9348fn 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
9298fn resolveExportOptions(9377fn 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, // TODO9399 .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 }
95489628
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;
95709655
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);
96869771
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);
97339818
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;
1146411559
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)) orelse11561 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;
1147411571
11475 // TODO handle if the element type requires comptime11572 // 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
11601const 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
11618fn 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}
1152811751
...@@ -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}
1170311926
11704fn analyzeIsNull(11927fn analyzeIsNull(
...@@ -11806,7 +12029,7 @@ fn analyzeSlice(...@@ -11806,7 +12029,7 @@ fn analyzeSlice(
11806 array_type.sentinel()12029 array_type.sentinel()
11807 else12030 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 }
1267812901
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 else12906 else
...@@ -12959,7 +13182,7 @@ fn typeHasOnePossibleValue(...@@ -12959,7 +13182,7 @@ fn typeHasOnePossibleValue(
12959 },13182 },
1296013183
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 };
24282422
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();
10681063 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;
195195
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;
198198
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 }
554555
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 types1468 // 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,
14731485
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 }
25612573
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,
25702582
2571 else => unreachable,2583 else => unreachable,
2572 };2584 };
2573 }2585 }
25742586
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 };
38843900
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);
38883906
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 }
39193937
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 a112 /// 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,
259266
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 {
731740
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 }
751761
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 {
15271539
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 with1545 // The decl whose value we are obtaining here may be overwritten with
1537 // a different value, which would invalidate this memory. So we must1546 // 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 },
15631572
...@@ -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 }
15841593
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 point1606 /// 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 }
15971618
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 {
16151633
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 index1638 .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 },
16211645
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]),
16231647
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,
16261650
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),
16291653
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;
25572581
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 };
25642590
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 {
25842610
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 };
25892616
...@@ -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};
27102739
2711var negative_one_payload: Value.Payload.I64 = .{2740var negative_one_payload: Value.Payload.I64 = .{
test/behavior/array.zig+28
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");
2const testing = std.testing;3const testing = std.testing;
3const mem = std.mem;4const mem = std.mem;
4const expect = testing.expect;5const 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
81test "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;
4const expect = testing.expect;4const expect = testing.expect;
5const expectEqual = testing.expectEqual;5const expectEqual = testing.expectEqual;
66
7test "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
34test "void arrays" {7test "void arrays" {
35 var array: [4]void = undefined;8 var array: [4]void = undefined;
36 array[0] = void{};9 array[0] = void{};