authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-14 00:23:27-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-01-14 00:23:27-05:00
logec58ddf46c4e1ac060333c6d0780955acae22442
tree16b1f97c8962817dccdcc90ffbd7169bb8171ef7
parent0d45c72d3e4f38029a453443ae6a34c398f5c530
parent336d0c97feabad4c93525ba6ef73a6b6163f49c7
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10582 from ziglang/stage2-arrays

stage2: detection of comptime array literals

10 files changed, 329 insertions(+), 110 deletions(-)

src/AstGen.zig+8-1
...@@ -1418,7 +1418,13 @@ fn arrayInitExprRlPtrInner(...@@ -1418,7 +1418,13 @@ fn arrayInitExprRlPtrInner(
1418 extra_index += 1;1418 extra_index += 1;
1419 _ = try expr(gz, scope, .{ .ptr = elem_ptr }, elem_init);1419 _ = try expr(gz, scope, .{ .ptr = elem_ptr }, elem_init);
1420 }1420 }
1421 _ = try gz.addPlNodePayloadIndex(.validate_array_init, node, payload_index);1421
1422 const tag: Zir.Inst.Tag = if (gz.force_comptime)
1423 .validate_array_init_comptime
1424 else
1425 .validate_array_init;
1426
1427 _ = try gz.addPlNodePayloadIndex(tag, node, payload_index);
1422 return .void_value;1428 return .void_value;
1423}1429}
14241430
...@@ -2317,6 +2323,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner...@@ -2317,6 +2323,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
2317 .validate_struct_init,2323 .validate_struct_init,
2318 .validate_struct_init_comptime,2324 .validate_struct_init_comptime,
2319 .validate_array_init,2325 .validate_array_init,
2326 .validate_array_init_comptime,
2320 .set_align_stack,2327 .set_align_stack,
2321 .set_cold,2328 .set_cold,
2322 .set_float_mode,2329 .set_float_mode,
src/Sema.zig+220-84
...@@ -836,7 +836,12 @@ pub fn analyzeBody(...@@ -836,7 +836,12 @@ pub fn analyzeBody(
836 continue;836 continue;
837 },837 },
838 .validate_array_init => {838 .validate_array_init => {
839 try sema.zirValidateArrayInit(block, inst);839 try sema.zirValidateArrayInit(block, inst, false);
840 i += 1;
841 continue;
842 },
843 .validate_array_init_comptime => {
844 try sema.zirValidateArrayInit(block, inst, true);
840 i += 1;845 i += 1;
841 continue;846 continue;
842 },847 },
...@@ -2815,13 +2820,18 @@ fn validateStructInit(...@@ -2815,13 +2820,18 @@ fn validateStructInit(
2815 }2820 }
2816}2821}
28172822
2818fn zirValidateArrayInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {2823fn zirValidateArrayInit(
2824 sema: *Sema,
2825 block: *Block,
2826 inst: Zir.Inst.Index,
2827 is_comptime: bool,
2828) CompileError!void {
2819 const validate_inst = sema.code.instructions.items(.data)[inst].pl_node;2829 const validate_inst = sema.code.instructions.items(.data)[inst].pl_node;
2820 const init_src = validate_inst.src();2830 const init_src = validate_inst.src();
2821 const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index);2831 const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index);
2822 const instrs = sema.code.extra[validate_extra.end..][0..validate_extra.data.body_len];2832 const instrs = sema.code.extra[validate_extra.end..][0..validate_extra.data.body_len];
2823 const elem_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node;2833 const first_elem_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node;
2824 const elem_ptr_extra = sema.code.extraData(Zir.Inst.ElemPtrImm, elem_ptr_data.payload_index).data;2834 const elem_ptr_extra = sema.code.extraData(Zir.Inst.ElemPtrImm, first_elem_ptr_data.payload_index).data;
2825 const array_ptr = sema.resolveInst(elem_ptr_extra.ptr);2835 const array_ptr = sema.resolveInst(elem_ptr_extra.ptr);
2826 const array_ty = sema.typeOf(array_ptr).childType();2836 const array_ty = sema.typeOf(array_ptr).childType();
2827 const array_len = array_ty.arrayLen();2837 const array_len = array_ty.arrayLen();
...@@ -2831,6 +2841,82 @@ fn zirValidateArrayInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compil...@@ -2831,6 +2841,82 @@ fn zirValidateArrayInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compil
2831 array_len, instrs.len,2841 array_len, instrs.len,
2832 });2842 });
2833 }2843 }
2844
2845 if (is_comptime or block.is_comptime) {
2846 // In this case the comptime machinery will have evaluated the store instructions
2847 // at comptime and we have nothing to do here.
2848 return;
2849 }
2850
2851 var array_is_comptime = true;
2852 var first_block_index: usize = std.math.maxInt(u32);
2853
2854 // Collect the comptime element values in case the array literal ends up
2855 // being comptime-known.
2856 const element_vals = try sema.arena.alloc(Value, instrs.len);
2857 const opt_opv = try sema.typeHasOnePossibleValue(block, init_src, array_ty);
2858 const air_tags = sema.air_instructions.items(.tag);
2859 const air_datas = sema.air_instructions.items(.data);
2860
2861 for (instrs) |elem_ptr, i| {
2862 const elem_ptr_data = sema.code.instructions.items(.data)[elem_ptr].pl_node;
2863 const elem_src: LazySrcLoc = .{ .node_offset = elem_ptr_data.src_node };
2864
2865 // Determine whether the value stored to this pointer is comptime-known.
2866
2867 if (opt_opv) |opv| {
2868 element_vals[i] = opv;
2869 continue;
2870 }
2871
2872 const elem_ptr_air_ref = sema.inst_map.get(elem_ptr).?;
2873 const elem_ptr_air_inst = Air.refToIndex(elem_ptr_air_ref).?;
2874 // Find the block index of the elem_ptr so that we can look at the next
2875 // instruction after it within the same block.
2876 // Possible performance enhancement: save the `block_index` between iterations
2877 // of the for loop.
2878 const next_air_inst = inst: {
2879 var block_index = block.instructions.items.len - 1;
2880 while (block.instructions.items[block_index] != elem_ptr_air_inst) {
2881 block_index -= 1;
2882 }
2883 first_block_index = @minimum(first_block_index, block_index);
2884 break :inst block.instructions.items[block_index + 1];
2885 };
2886
2887 // If the next instructon is a store with a comptime operand, this element
2888 // is comptime.
2889 switch (air_tags[next_air_inst]) {
2890 .store => {
2891 const bin_op = air_datas[next_air_inst].bin_op;
2892 if (bin_op.lhs != elem_ptr_air_ref) {
2893 array_is_comptime = false;
2894 continue;
2895 }
2896 if (try sema.resolveMaybeUndefValAllowVariables(block, elem_src, bin_op.rhs)) |val| {
2897 element_vals[i] = val;
2898 } else {
2899 array_is_comptime = false;
2900 }
2901 continue;
2902 },
2903 else => {
2904 array_is_comptime = false;
2905 continue;
2906 },
2907 }
2908 }
2909
2910 if (array_is_comptime) {
2911 // Our task is to delete all the `elem_ptr` and `store` instructions, and insert
2912 // instead a single `store` to the array_ptr with a comptime struct value.
2913
2914 block.instructions.shrinkRetainingCapacity(first_block_index);
2915
2916 const array_val = try Value.Tag.array.create(sema.arena, element_vals);
2917 const array_init = try sema.addConstant(array_ty, array_val);
2918 try sema.storePtr2(block, init_src, array_ptr, init_src, array_init, init_src, .store);
2919 }
2834}2920}
28352921
2836fn failWithBadMemberAccess(2922fn failWithBadMemberAccess(
...@@ -14085,88 +14171,112 @@ fn beginComptimePtrMutation(...@@ -14085,88 +14171,112 @@ fn beginComptimePtrMutation(
14085 .elem_ptr => {14171 .elem_ptr => {
14086 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;14172 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
14087 var parent = try beginComptimePtrMutation(sema, block, src, elem_ptr.array_ptr);14173 var parent = try beginComptimePtrMutation(sema, block, src, elem_ptr.array_ptr);
14088 const elem_ty = parent.ty.childType();14174 switch (parent.ty.zigTypeTag()) {
14089 switch (parent.val.tag()) {14175 .Array, .Vector => {
14090 .undef => {14176 const check_len = parent.ty.arrayLenIncludingSentinel();
14091 // An array has been initialized to undefined at comptime and now we14177 if (elem_ptr.index >= check_len) {
14092 // are for the first time setting an element. We must change the representation14178 // TODO have the parent include the decl so we can say "declared here"
14093 // of the array from `undef` to `array`.14179 return sema.fail(block, src, "comptime store of index {d} out of bounds of array length {d}", .{
14094 const arena = parent.beginArena(sema.gpa);14180 elem_ptr.index, check_len,
14095 defer parent.finishArena();14181 });
14182 }
14183 const elem_ty = parent.ty.childType();
14184 switch (parent.val.tag()) {
14185 .undef => {
14186 // An array has been initialized to undefined at comptime and now we
14187 // are for the first time setting an element. We must change the representation
14188 // of the array from `undef` to `array`.
14189 const arena = parent.beginArena(sema.gpa);
14190 defer parent.finishArena();
14191
14192 const array_len_including_sentinel =
14193 try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel());
14194 const elems = try arena.alloc(Value, array_len_including_sentinel);
14195 mem.set(Value, elems, Value.undef);
14196
14197 parent.val.* = try Value.Tag.array.create(arena, elems);
1409614198
14097 const array_len_including_sentinel =14199 return ComptimePtrMutationKit{
14098 try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel());14200 .decl_ref_mut = parent.decl_ref_mut,
14099 const elems = try arena.alloc(Value, array_len_including_sentinel);14201 .val = &elems[elem_ptr.index],
14100 mem.set(Value, elems, Value.undef);14202 .ty = elem_ty,
14203 };
14204 },
14205 .bytes => {
14206 // An array is memory-optimized to store a slice of bytes, but we are about
14207 // to modify an individual field and the representation has to change.
14208 // If we wanted to avoid this, there would need to be special detection
14209 // elsewhere to identify when writing a value to an array element that is stored
14210 // using the `bytes` tag, and handle it without making a call to this function.
14211 const arena = parent.beginArena(sema.gpa);
14212 defer parent.finishArena();
14213
14214 const bytes = parent.val.castTag(.bytes).?.data;
14215 const dest_len = parent.ty.arrayLenIncludingSentinel();
14216 // bytes.len may be one greater than dest_len because of the case when
14217 // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted.
14218 assert(bytes.len >= dest_len);
14219 const elems = try arena.alloc(Value, @intCast(usize, dest_len));
14220 for (elems) |*elem, i| {
14221 elem.* = try Value.Tag.int_u64.create(arena, bytes[i]);
14222 }
1410114223
14102 parent.val.* = try Value.Tag.array.create(arena, elems);14224 parent.val.* = try Value.Tag.array.create(arena, elems);
1410314225
14104 return ComptimePtrMutationKit{14226 return ComptimePtrMutationKit{
14105 .decl_ref_mut = parent.decl_ref_mut,14227 .decl_ref_mut = parent.decl_ref_mut,
14106 .val = &elems[elem_ptr.index],14228 .val = &elems[elem_ptr.index],
14107 .ty = elem_ty,14229 .ty = elem_ty,
14108 };14230 };
14109 },14231 },
14110 .bytes => {14232 .repeated => {
14111 // An array is memory-optimized to store a slice of bytes, but we are about14233 // An array is memory-optimized to store only a single element value, and
14112 // to modify an individual field and the representation has to change.14234 // that value is understood to be the same for the entire length of the array.
14113 // If we wanted to avoid this, there would need to be special detection14235 // However, now we want to modify an individual field and so the
14114 // elsewhere to identify when writing a value to an array element that is stored14236 // representation has to change. If we wanted to avoid this, there would
14115 // using the `bytes` tag, and handle it without making a call to this function.14237 // need to be special detection elsewhere to identify when writing a value to an
14116 const arena = parent.beginArena(sema.gpa);14238 // array element that is stored using the `repeated` tag, and handle it
14117 defer parent.finishArena();14239 // without making a call to this function.
14240 const arena = parent.beginArena(sema.gpa);
14241 defer parent.finishArena();
14242
14243 const repeated_val = try parent.val.castTag(.repeated).?.data.copy(arena);
14244 const array_len_including_sentinel =
14245 try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel());
14246 const elems = try arena.alloc(Value, array_len_including_sentinel);
14247 mem.set(Value, elems, repeated_val);
14248
14249 parent.val.* = try Value.Tag.array.create(arena, elems);
1411814250
14119 const bytes = parent.val.castTag(.bytes).?.data;14251 return ComptimePtrMutationKit{
14120 const dest_len = parent.ty.arrayLenIncludingSentinel();14252 .decl_ref_mut = parent.decl_ref_mut,
14121 // bytes.len may be one greater than dest_len because of the case when14253 .val = &elems[elem_ptr.index],
14122 // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted.14254 .ty = elem_ty,
14123 assert(bytes.len >= dest_len);14255 };
14124 const elems = try arena.alloc(Value, @intCast(usize, dest_len));14256 },
14125 for (elems) |*elem, i| {
14126 elem.* = try Value.Tag.int_u64.create(arena, bytes[i]);
14127 }
1412814257
14129 parent.val.* = try Value.Tag.array.create(arena, elems);14258 .array => return ComptimePtrMutationKit{
14259 .decl_ref_mut = parent.decl_ref_mut,
14260 .val = &parent.val.castTag(.array).?.data[elem_ptr.index],
14261 .ty = elem_ty,
14262 },
1413014263
14131 return ComptimePtrMutationKit{14264 else => unreachable,
14132 .decl_ref_mut = parent.decl_ref_mut,14265 }
14133 .val = &elems[elem_ptr.index],
14134 .ty = elem_ty,
14135 };
14136 },14266 },
14137 .repeated => {14267 else => {
14138 // An array is memory-optimized to store only a single element value, and14268 if (elem_ptr.index != 0) {
14139 // that value is understood to be the same for the entire length of the array.14269 // TODO include a "declared here" note for the decl
14140 // However, now we want to modify an individual field and so the14270 return sema.fail(block, src, "out of bounds comptime store of index {d}", .{
14141 // representation has to change. If we wanted to avoid this, there would14271 elem_ptr.index,
14142 // need to be special detection elsewhere to identify when writing a value to an14272 });
14143 // array element that is stored using the `repeated` tag, and handle it14273 }
14144 // without making a call to this function.
14145 const arena = parent.beginArena(sema.gpa);
14146 defer parent.finishArena();
14147
14148 const repeated_val = try parent.val.castTag(.repeated).?.data.copy(arena);
14149 const array_len_including_sentinel =
14150 try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel());
14151 const elems = try arena.alloc(Value, array_len_including_sentinel);
14152 mem.set(Value, elems, repeated_val);
14153
14154 parent.val.* = try Value.Tag.array.create(arena, elems);
14155
14156 return ComptimePtrMutationKit{14274 return ComptimePtrMutationKit{
14157 .decl_ref_mut = parent.decl_ref_mut,14275 .decl_ref_mut = parent.decl_ref_mut,
14158 .val = &elems[elem_ptr.index],14276 .val = parent.val,
14159 .ty = elem_ty,14277 .ty = parent.ty,
14160 };14278 };
14161 },14279 },
14162
14163 .array => return ComptimePtrMutationKit{
14164 .decl_ref_mut = parent.decl_ref_mut,
14165 .val = &parent.val.castTag(.array).?.data[elem_ptr.index],
14166 .ty = elem_ty,
14167 },
14168
14169 else => unreachable,
14170 }14280 }
14171 },14281 },
14172 .field_ptr => {14282 .field_ptr => {
...@@ -14296,15 +14406,41 @@ fn beginComptimePtrLoad(...@@ -14296,15 +14406,41 @@ fn beginComptimePtrLoad(
14296 .elem_ptr => {14406 .elem_ptr => {
14297 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;14407 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
14298 const parent = try beginComptimePtrLoad(sema, block, src, elem_ptr.array_ptr);14408 const parent = try beginComptimePtrLoad(sema, block, src, elem_ptr.array_ptr);
14299 const elem_ty = parent.ty.childType();14409 switch (parent.ty.zigTypeTag()) {
14300 const elem_size = elem_ty.abiSize(target);14410 .Array, .Vector => {
14301 return ComptimePtrLoadKit{14411 const check_len = parent.ty.arrayLenIncludingSentinel();
14302 .root_val = parent.root_val,14412 if (elem_ptr.index >= check_len) {
14303 .val = try parent.val.elemValue(sema.arena, elem_ptr.index),14413 // TODO have the parent include the decl so we can say "declared here"
14304 .ty = elem_ty,14414 return sema.fail(block, src, "comptime load of index {d} out of bounds of array length {d}", .{
14305 .byte_offset = try sema.usizeCast(block, src, parent.byte_offset + elem_size * elem_ptr.index),14415 elem_ptr.index, check_len,
14306 .is_mutable = parent.is_mutable,14416 });
14307 };14417 }
14418 const elem_ty = parent.ty.childType();
14419 const elem_size = elem_ty.abiSize(target);
14420 return ComptimePtrLoadKit{
14421 .root_val = parent.root_val,
14422 .val = try parent.val.elemValue(sema.arena, elem_ptr.index),
14423 .ty = elem_ty,
14424 .byte_offset = try sema.usizeCast(block, src, parent.byte_offset + elem_size * elem_ptr.index),
14425 .is_mutable = parent.is_mutable,
14426 };
14427 },
14428 else => {
14429 if (elem_ptr.index != 0) {
14430 // TODO have the parent include the decl so we can say "declared here"
14431 return sema.fail(block, src, "out of bounds comptime load of index {d}", .{
14432 elem_ptr.index,
14433 });
14434 }
14435 return ComptimePtrLoadKit{
14436 .root_val = parent.root_val,
14437 .val = parent.val,
14438 .ty = parent.ty,
14439 .byte_offset = parent.byte_offset,
14440 .is_mutable = parent.is_mutable,
14441 };
14442 },
14443 }
14308 },14444 },
14309 .field_ptr => {14445 .field_ptr => {
14310 const field_ptr = ptr_val.castTag(.field_ptr).?.data;14446 const field_ptr = ptr_val.castTag(.field_ptr).?.data;
src/Zir.zig+5
...@@ -663,6 +663,9 @@ pub const Inst = struct {...@@ -663,6 +663,9 @@ pub const Inst = struct {
663 /// because it must use one of them to find out the array type.663 /// because it must use one of them to find out the array type.
664 /// Uses the `pl_node` field. Payload is `Block`.664 /// Uses the `pl_node` field. Payload is `Block`.
665 validate_array_init,665 validate_array_init,
666 /// Same as `validate_array_init` but additionally communicates that the
667 /// resulting array initialization value is within a comptime scope.
668 validate_array_init_comptime,
666 /// A struct literal with a specified type, with no fields.669 /// A struct literal with a specified type, with no fields.
667 /// Uses the `un_node` field.670 /// Uses the `un_node` field.
668 struct_init_empty,671 struct_init_empty,
...@@ -1087,6 +1090,7 @@ pub const Inst = struct {...@@ -1087,6 +1090,7 @@ pub const Inst = struct {
1087 .validate_struct_init,1090 .validate_struct_init,
1088 .validate_struct_init_comptime,1091 .validate_struct_init_comptime,
1089 .validate_array_init,1092 .validate_array_init,
1093 .validate_array_init_comptime,
1090 .struct_init_empty,1094 .struct_init_empty,
1091 .struct_init,1095 .struct_init,
1092 .struct_init_ref,1096 .struct_init_ref,
...@@ -1341,6 +1345,7 @@ pub const Inst = struct {...@@ -1341,6 +1345,7 @@ pub const Inst = struct {
1341 .validate_struct_init = .pl_node,1345 .validate_struct_init = .pl_node,
1342 .validate_struct_init_comptime = .pl_node,1346 .validate_struct_init_comptime = .pl_node,
1343 .validate_array_init = .pl_node,1347 .validate_array_init = .pl_node,
1348 .validate_array_init_comptime = .pl_node,
1344 .struct_init_empty = .un_node,1349 .struct_init_empty = .un_node,
1345 .field_type = .pl_node,1350 .field_type = .pl_node,
1346 .field_type_ref = .pl_node,1351 .field_type_ref = .pl_node,
src/codegen/c.zig+64-12
...@@ -44,7 +44,7 @@ const BlockData = struct {...@@ -44,7 +44,7 @@ const BlockData = struct {
44 result: CValue,44 result: CValue,
45};45};
4646
47pub const CValueMap = std.AutoHashMap(Air.Inst.Index, CValue);47pub const CValueMap = std.AutoHashMap(Air.Inst.Ref, CValue);
48pub const TypedefMap = std.ArrayHashMap(48pub const TypedefMap = std.ArrayHashMap(
49 Type,49 Type,
50 struct { name: []const u8, rendered: []u8 },50 struct { name: []const u8, rendered: []u8 },
...@@ -110,11 +110,29 @@ pub const Function = struct {...@@ -110,11 +110,29 @@ pub const Function = struct {
110 func: *Module.Fn,110 func: *Module.Fn,
111111
112 fn resolveInst(f: *Function, inst: Air.Inst.Ref) !CValue {112 fn resolveInst(f: *Function, inst: Air.Inst.Ref) !CValue {
113 if (f.air.value(inst)) |_| {113 const gop = try f.value_map.getOrPut(inst);
114 return CValue{ .constant = inst };114 if (gop.found_existing) return gop.value_ptr.*;
115
116 const val = f.air.value(inst).?;
117 const ty = f.air.typeOf(inst);
118 switch (ty.zigTypeTag()) {
119 .Array => {
120 const writer = f.object.code_header.writer();
121 const decl_c_value = f.allocLocalValue();
122 gop.value_ptr.* = decl_c_value;
123 try writer.writeAll("static ");
124 try f.object.dg.renderTypeAndName(writer, ty, decl_c_value, .Const);
125 try writer.writeAll(" = ");
126 try f.object.dg.renderValue(writer, ty, val);
127 try writer.writeAll(";\n ");
128 return decl_c_value;
129 },
130 else => {
131 const result = CValue{ .constant = inst };
132 gop.value_ptr.* = result;
133 return result;
134 },
115 }135 }
116 const index = Air.refToIndex(inst).?;
117 return f.value_map.get(index).?; // Assertion means instruction does not dominate usage.
118 }136 }
119137
120 fn allocLocalValue(f: *Function) CValue {138 fn allocLocalValue(f: *Function) CValue {
...@@ -154,6 +172,8 @@ pub const Function = struct {...@@ -154,6 +172,8 @@ pub const Function = struct {
154pub const Object = struct {172pub const Object = struct {
155 dg: DeclGen,173 dg: DeclGen,
156 code: std.ArrayList(u8),174 code: std.ArrayList(u8),
175 /// Goes before code. Initialized and deinitialized in `genFunc`.
176 code_header: std.ArrayList(u8) = undefined,
157 indent_writer: IndentWriter(std.ArrayList(u8).Writer),177 indent_writer: IndentWriter(std.ArrayList(u8).Writer),
158178
159 fn writer(o: *Object) IndentWriter(std.ArrayList(u8).Writer).Writer {179 fn writer(o: *Object) IndentWriter(std.ArrayList(u8).Writer).Writer {
...@@ -218,12 +238,18 @@ pub const DeclGen = struct {...@@ -218,12 +238,18 @@ pub const DeclGen = struct {
218 // Determine if we must pointer cast.238 // Determine if we must pointer cast.
219 if (ty.eql(decl.ty)) {239 if (ty.eql(decl.ty)) {
220 try writer.writeByte('&');240 try writer.writeByte('&');
221 } else {241 try dg.renderDeclName(decl, writer);
222 try writer.writeAll("(");242 return;
223 try dg.renderType(writer, ty);
224 try writer.writeAll(")&");
225 }243 }
244
245 try writer.writeAll("((");
246 try dg.renderType(writer, ty);
247 try writer.writeAll(")&");
248 try dg.renderDeclName(decl, writer);
249 try writer.writeByte(')');
250 return;
226 }251 }
252
227 try dg.renderDeclName(decl, writer);253 try dg.renderDeclName(decl, writer);
228 }254 }
229255
...@@ -1010,6 +1036,10 @@ pub fn genFunc(f: *Function) !void {...@@ -1010,6 +1036,10 @@ pub fn genFunc(f: *Function) !void {
1010 defer tracy.end();1036 defer tracy.end();
10111037
1012 const o = &f.object;1038 const o = &f.object;
1039
1040 o.code_header = std.ArrayList(u8).init(f.object.dg.gpa);
1041 defer o.code_header.deinit();
1042
1013 const is_global = o.dg.module.decl_exports.contains(f.func.owner_decl);1043 const is_global = o.dg.module.decl_exports.contains(f.func.owner_decl);
1014 const fwd_decl_writer = o.dg.fwd_decl.writer();1044 const fwd_decl_writer = o.dg.fwd_decl.writer();
1015 if (is_global) {1045 if (is_global) {
...@@ -1020,12 +1050,26 @@ pub fn genFunc(f: *Function) !void {...@@ -1020,12 +1050,26 @@ pub fn genFunc(f: *Function) !void {
10201050
1021 try o.indent_writer.insertNewline();1051 try o.indent_writer.insertNewline();
1022 try o.dg.renderFunctionSignature(o.writer(), is_global);1052 try o.dg.renderFunctionSignature(o.writer(), is_global);
1023
1024 try o.writer().writeByte(' ');1053 try o.writer().writeByte(' ');
1054
1055 // In case we need to use the header, populate it with a copy of the function
1056 // signature here. We anticipate a brace, newline, and space.
1057 try o.code_header.ensureUnusedCapacity(o.code.items.len + 3);
1058 o.code_header.appendSliceAssumeCapacity(o.code.items);
1059 o.code_header.appendSliceAssumeCapacity("{\n ");
1060 const empty_header_len = o.code_header.items.len;
1061
1025 const main_body = f.air.getMainBody();1062 const main_body = f.air.getMainBody();
1026 try genBody(f, main_body);1063 try genBody(f, main_body);
10271064
1028 try o.indent_writer.insertNewline();1065 try o.indent_writer.insertNewline();
1066
1067 // If we have a header to insert, append the body to the header
1068 // and then return the result, freeing the body.
1069 if (o.code_header.items.len > empty_header_len) {
1070 try o.code_header.appendSlice(o.code.items[empty_header_len..]);
1071 mem.swap(std.ArrayList(u8), &o.code, &o.code_header);
1072 }
1029}1073}
10301074
1031pub fn genDecl(o: *Object) !void {1075pub fn genDecl(o: *Object) !void {
...@@ -1289,7 +1333,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO...@@ -1289,7 +1333,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
1289 };1333 };
1290 switch (result_value) {1334 switch (result_value) {
1291 .none => {},1335 .none => {},
1292 else => try f.value_map.putNoClobber(inst, result_value),1336 else => try f.value_map.putNoClobber(Air.indexToRef(inst), result_value),
1293 }1337 }
1294 }1338 }
12951339
...@@ -2189,7 +2233,15 @@ fn airCall(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -2189,7 +2233,15 @@ fn airCall(f: *Function, inst: Air.Inst.Index) !CValue {
2189fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue {2233fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue {
2190 const dbg_stmt = f.air.instructions.items(.data)[inst].dbg_stmt;2234 const dbg_stmt = f.air.instructions.items(.data)[inst].dbg_stmt;
2191 const writer = f.object.writer();2235 const writer = f.object.writer();
2192 try writer.print("#line {d}\n", .{dbg_stmt.line + 1});2236 // TODO re-evaluate whether to emit these or not. If we naively emit
2237 // these directives, the output file will report bogus line numbers because
2238 // every newline after the #line directive adds one to the line.
2239 // We also don't print the filename yet, so the output is strictly unhelpful.
2240 // If we wanted to go this route, we would need to go all the way and not output
2241 // newlines until the next dbg_stmt occurs.
2242 // Perhaps an additional compilation option is in order?
2243 //try writer.print("#line {d}\n", .{dbg_stmt.line + 1});
2244 try writer.print("/* file:{d}:{d} */\n", .{ dbg_stmt.line + 1, dbg_stmt.column + 1 });
2193 return CValue.none;2245 return CValue.none;
2194}2246}
21952247
src/print_zir.zig+1
...@@ -369,6 +369,7 @@ const Writer = struct {...@@ -369,6 +369,7 @@ const Writer = struct {
369 .validate_struct_init,369 .validate_struct_init,
370 .validate_struct_init_comptime,370 .validate_struct_init_comptime,
371 .validate_array_init,371 .validate_array_init,
372 .validate_array_init_comptime,
372 .c_import,373 .c_import,
373 => try self.writePlNodeBlock(stream, inst),374 => try self.writePlNodeBlock(stream, inst),
374375
src/value.zig+6-1
...@@ -1817,8 +1817,13 @@ pub const Value = extern union {...@@ -1817,8 +1817,13 @@ pub const Value = extern union {
18171817
1818 .decl_ref => return val.castTag(.decl_ref).?.data.val.elemValueAdvanced(index, arena, buffer),1818 .decl_ref => return val.castTag(.decl_ref).?.data.val.elemValueAdvanced(index, arena, buffer),
1819 .decl_ref_mut => return val.castTag(.decl_ref_mut).?.data.decl.val.elemValueAdvanced(index, arena, buffer),1819 .decl_ref_mut => return val.castTag(.decl_ref_mut).?.data.decl.val.elemValueAdvanced(index, arena, buffer),
1820 .elem_ptr => {
1821 const data = val.castTag(.elem_ptr).?.data;
1822 return data.array_ptr.elemValueAdvanced(index + data.index, arena, buffer);
1823 },
18201824
1821 // The child type of arrays which have only one possible value need to have only one possible value itself.1825 // The child type of arrays which have only one possible value need
1826 // to have only one possible value itself.
1822 .the_only_possible_value => return val,1827 .the_only_possible_value => return val,
18231828
1824 else => unreachable,1829 else => unreachable,
test/behavior/array.zig+7
...@@ -114,6 +114,13 @@ test "void arrays" {...@@ -114,6 +114,13 @@ test "void arrays" {
114}114}
115115
116test "nested arrays" {116test "nested arrays" {
117 if (builtin.zig_backend == .stage2_wasm) {
118 // TODO this is a recent stage2 test case regression due to an enhancement;
119 // now arrays are properly detected as comptime. This exercised a new code
120 // path in the wasm backend that is not yet implemented.
121 return error.SkipZigTest;
122 }
123
117 const array_of_strings = [_][]const u8{ "hello", "this", "is", "my", "thing" };124 const array_of_strings = [_][]const u8{ "hello", "this", "is", "my", "thing" };
118 for (array_of_strings) |s, i| {125 for (array_of_strings) |s, i| {
119 if (i == 0) try expect(mem.eql(u8, s, "hello"));126 if (i == 0) try expect(mem.eql(u8, s, "hello"));
test/behavior/array_llvm.zig+12
...@@ -33,3 +33,15 @@ test "read/write through global variable array of struct fields initialized via...@@ -33,3 +33,15 @@ test "read/write through global variable array of struct fields initialized via
33 };33 };
34 try S.doTheTest();34 try S.doTheTest();
35}35}
36
37test "implicit cast single-item pointer" {
38 try testImplicitCastSingleItemPtr();
39 comptime try testImplicitCastSingleItemPtr();
40}
41
42fn testImplicitCastSingleItemPtr() !void {
43 var byte: u8 = 100;
44 const slice = @as(*[1]u8, &byte)[0..];
45 slice[0] += 1;
46 try expect(byte == 101);
47}
test/behavior/array_stage1.zig-12
...@@ -4,18 +4,6 @@ const mem = std.mem;...@@ -4,18 +4,6 @@ const mem = std.mem;
4const expect = testing.expect;4const expect = testing.expect;
5const expectEqual = testing.expectEqual;5const expectEqual = testing.expectEqual;
66
7test "implicit cast single-item pointer" {
8 try testImplicitCastSingleItemPtr();
9 comptime try testImplicitCastSingleItemPtr();
10}
11
12fn testImplicitCastSingleItemPtr() !void {
13 var byte: u8 = 100;
14 const slice = @as(*[1]u8, &byte)[0..];
15 slice[0] += 1;
16 try expect(byte == 101);
17}
18
19fn testArrayByValAtComptime(b: [2]u8) u8 {7fn testArrayByValAtComptime(b: [2]u8) u8 {
20 return b[0];8 return b[0];
21}9}
test/behavior/for.zig+6
...@@ -62,6 +62,12 @@ test "ignore lval with underscore (for loop)" {...@@ -62,6 +62,12 @@ test "ignore lval with underscore (for loop)" {
62}62}
6363
64test "basic for loop" {64test "basic for loop" {
65 if (@import("builtin").zig_backend == .stage2_wasm) {
66 // TODO this is a recent stage2 test case regression due to an enhancement;
67 // now arrays are properly detected as comptime. This exercised a new code
68 // path in the wasm backend that is not yet implemented.
69 return error.SkipZigTest;
70 }
65 const expected_result = [_]u8{ 9, 8, 7, 6, 0, 1, 2, 3 } ** 3;71 const expected_result = [_]u8{ 9, 8, 7, 6, 0, 1, 2, 3 } ** 3;
6672
67 var buffer: [expected_result.len]u8 = undefined;73 var buffer: [expected_result.len]u8 = undefined;