authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2021-06-06 21:08:31+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2021-06-07 22:15:56+03:00
log7efd7bc3b8cc403814c0aa62be0a17ff25b33902
tree7b92414b05bfc0e66bdc8aff4f3a68abc9a88ee7
parentca6951ff7959b88573bfec7c7bf0465209f3263f

stage2: implement comptime variables


5 files changed, 181 insertions(+), 6 deletions(-)

src/AstGen.zig+1-1
...@@ -2357,7 +2357,7 @@ fn varDecl(...@@ -2357,7 +2357,7 @@ fn varDecl(
2357 return &sub_scope.base;2357 return &sub_scope.base;
2358 },2358 },
2359 .keyword_var => {2359 .keyword_var => {
2360 const is_comptime = var_decl.comptime_token != null;2360 const is_comptime = var_decl.comptime_token != null or gz.force_comptime;
2361 var resolve_inferred_alloc: Zir.Inst.Ref = .none;2361 var resolve_inferred_alloc: Zir.Inst.Ref = .none;
2362 const var_data: struct {2362 const var_data: struct {
2363 result_loc: ResultLoc,2363 result_loc: ResultLoc,
src/Module.zig+10
...@@ -1139,6 +1139,13 @@ pub const Scope = struct {...@@ -1139,6 +1139,13 @@ pub const Scope = struct {
1139 instructions: ArrayListUnmanaged(*ir.Inst),1139 instructions: ArrayListUnmanaged(*ir.Inst),
1140 label: ?*Label = null,1140 label: ?*Label = null,
1141 inlining: ?*Inlining,1141 inlining: ?*Inlining,
1142 /// If runtime_index is not 0 then one of these is guaranteed to be non null.
1143 runtime_cond: ?LazySrcLoc = null,
1144 runtime_loop: ?LazySrcLoc = null,
1145 /// Non zero if a non-inline loop or a runtime conditional have been encountered.
1146 /// Stores to to comptime variables are only allowed when var.runtime_index <= runtime_index.
1147 runtime_index: u32 = 0,
1148
1142 is_comptime: bool,1149 is_comptime: bool,
11431150
1144 /// This `Block` maps a block ZIR instruction to the corresponding1151 /// This `Block` maps a block ZIR instruction to the corresponding
...@@ -1182,6 +1189,9 @@ pub const Scope = struct {...@@ -1182,6 +1189,9 @@ pub const Scope = struct {
1182 .label = null,1189 .label = null,
1183 .inlining = parent.inlining,1190 .inlining = parent.inlining,
1184 .is_comptime = parent.is_comptime,1191 .is_comptime = parent.is_comptime,
1192 .runtime_cond = parent.runtime_cond,
1193 .runtime_loop = parent.runtime_loop,
1194 .runtime_index = parent.runtime_index,
1185 };1195 };
1186 }1196 }
11871197
src/Sema.zig+62-5
...@@ -509,7 +509,7 @@ pub fn analyzeBody(...@@ -509,7 +509,7 @@ pub fn analyzeBody(
509 };509 };
510 if (air_inst.ty.isNoReturn())510 if (air_inst.ty.isNoReturn())
511 return always_noreturn;511 return always_noreturn;
512 try map.putNoClobber(sema.gpa, inst, air_inst);512 try map.put(sema.gpa, inst, air_inst);
513 }513 }
514}514}
515515
...@@ -1238,9 +1238,26 @@ fn zirAllocExtended(...@@ -1238,9 +1238,26 @@ fn zirAllocExtended(
1238}1238}
12391239
1240fn zirAllocComptime(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {1240fn zirAllocComptime(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
1241 const tracy = trace(@src());
1242 defer tracy.end();
1243
1241 const inst_data = sema.code.instructions.items(.data)[inst].un_node;1244 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1242 const src = inst_data.src();1245 const src = inst_data.src();
1243 return sema.mod.fail(&block.base, src, "TODO implement Sema.zirAllocComptime", .{});1246 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
1247 const var_type = try sema.resolveType(block, ty_src, inst_data.operand);
1248 const ptr_type = try sema.mod.simplePtrType(sema.arena, var_type, true, .One);
1249
1250 const val_payload = try sema.arena.create(Value.Payload.ComptimeAlloc);
1251 val_payload.* = .{
1252 .data = .{
1253 .runtime_index = block.runtime_index,
1254 .val = undefined, // astgen guarantees there will be a store before the first load
1255 },
1256 };
1257 return sema.mod.constInst(sema.arena, src, .{
1258 .ty = ptr_type,
1259 .val = Value.initPayload(&val_payload.base),
1260 });
1244}1261}
12451262
1246fn zirAllocInferredComptime(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {1263fn zirAllocInferredComptime(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
...@@ -1742,6 +1759,9 @@ fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) InnerE...@@ -1742,6 +1759,9 @@ fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) InnerE
1742 };1759 };
1743 var child_block = parent_block.makeSubBlock();1760 var child_block = parent_block.makeSubBlock();
1744 child_block.label = &label;1761 child_block.label = &label;
1762 child_block.runtime_cond = null;
1763 child_block.runtime_loop = src;
1764 child_block.runtime_index += 1;
1745 const merges = &child_block.label.?.merges;1765 const merges = &child_block.label.?.merges;
17461766
1747 defer child_block.instructions.deinit(sema.gpa);1767 defer child_block.instructions.deinit(sema.gpa);
...@@ -4066,6 +4086,9 @@ fn analyzeSwitch(...@@ -4066,6 +4086,9 @@ fn analyzeSwitch(
4066 const cases = try sema.arena.alloc(Inst.SwitchBr.Case, scalar_cases_len);4086 const cases = try sema.arena.alloc(Inst.SwitchBr.Case, scalar_cases_len);
40674087
4068 var case_block = child_block.makeSubBlock();4088 var case_block = child_block.makeSubBlock();
4089 case_block.runtime_loop = null;
4090 case_block.runtime_cond = operand.src;
4091 case_block.runtime_index += 1;
4069 defer case_block.instructions.deinit(gpa);4092 defer case_block.instructions.deinit(gpa);
40704093
4071 var extra_index: usize = special.end;4094 var extra_index: usize = special.end;
...@@ -4584,14 +4607,14 @@ fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerEr...@@ -4584,14 +4607,14 @@ fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerEr
45844607
4585 const tag_override = block.sema.code.instructions.items(.tag)[inst];4608 const tag_override = block.sema.code.instructions.items(.tag)[inst];
4586 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;4609 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
4587 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };4610 sema.src = .{ .node_offset_bin_op = inst_data.src_node };
4588 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };4611 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
4589 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };4612 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
4590 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;4613 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
4591 const lhs = try sema.resolveInst(extra.lhs);4614 const lhs = try sema.resolveInst(extra.lhs);
4592 const rhs = try sema.resolveInst(extra.rhs);4615 const rhs = try sema.resolveInst(extra.rhs);
45934616
4594 return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src);4617 return sema.analyzeArithmetic(block, tag_override, lhs, rhs, sema.src, lhs_src, rhs_src);
4595}4618}
45964619
4597fn zirOverflowArithmetic(4620fn zirOverflowArithmetic(
...@@ -5150,6 +5173,9 @@ fn zirBoolBr(...@@ -5150,6 +5173,9 @@ fn zirBoolBr(
5150 };5173 };
51515174
5152 var child_block = parent_block.makeSubBlock();5175 var child_block = parent_block.makeSubBlock();
5176 child_block.runtime_loop = null;
5177 child_block.runtime_cond = lhs.src;
5178 child_block.runtime_index += 1;
5153 defer child_block.instructions.deinit(sema.gpa);5179 defer child_block.instructions.deinit(sema.gpa);
51545180
5155 var then_block = child_block.makeSubBlock();5181 var then_block = child_block.makeSubBlock();
...@@ -5258,6 +5284,9 @@ fn zirCondbr(...@@ -5258,6 +5284,9 @@ fn zirCondbr(
5258 }5284 }
52595285
5260 var sub_block = parent_block.makeSubBlock();5286 var sub_block = parent_block.makeSubBlock();
5287 sub_block.runtime_loop = null;
5288 sub_block.runtime_cond = cond.src;
5289 sub_block.runtime_index += 1;
5261 defer sub_block.instructions.deinit(sema.gpa);5290 defer sub_block.instructions.deinit(sema.gpa);
52625291
5263 _ = try sema.analyzeBody(&sub_block, then_body);5292 _ = try sema.analyzeBody(&sub_block, then_body);
...@@ -6753,7 +6782,35 @@ fn storePtr(...@@ -6753,7 +6782,35 @@ fn storePtr(
6753 if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null)6782 if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null)
6754 return;6783 return;
67556784
6756 // TODO handle comptime pointer writes6785 if (try sema.resolvePossiblyUndefinedValue(block, src, ptr)) |ptr_val| {
6786 const const_val = (try sema.resolvePossiblyUndefinedValue(block, src, value)) orelse
6787 return sema.mod.fail(&block.base, src, "cannot store runtime value in compile time variable", .{});
6788
6789 const comptime_alloc = ptr_val.castTag(.comptime_alloc).?;
6790 if (comptime_alloc.data.runtime_index < block.runtime_index) {
6791 if (block.runtime_cond) |cond_src| {
6792 const msg = msg: {
6793 const msg = try sema.mod.errMsg(&block.base, src, "store to comptime variable depends on runtime condition", .{});
6794 errdefer msg.destroy(sema.gpa);
6795 try sema.mod.errNote(&block.base, cond_src, msg, "runtime condition here", .{});
6796 break :msg msg;
6797 };
6798 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
6799 }
6800 if (block.runtime_loop) |loop_src| {
6801 const msg = msg: {
6802 const msg = try sema.mod.errMsg(&block.base, src, "cannot store to comptime variable in non-inline loop", .{});
6803 errdefer msg.destroy(sema.gpa);
6804 try sema.mod.errNote(&block.base, loop_src, msg, "non-inline loop here", .{});
6805 break :msg msg;
6806 };
6807 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
6808 }
6809 unreachable;
6810 }
6811 comptime_alloc.data.val = const_val;
6812 return;
6813 }
6757 // TODO handle if the element type requires comptime6814 // TODO handle if the element type requires comptime
67586815
6759 try sema.requireRuntimeBlock(block, src);6816 try sema.requireRuntimeBlock(block, src);
src/value.zig+26
...@@ -101,6 +101,8 @@ pub const Value = extern union {...@@ -101,6 +101,8 @@ pub const Value = extern union {
101 variable,101 variable,
102 /// Represents a pointer to another immutable value.102 /// Represents a pointer to another immutable value.
103 ref_val,103 ref_val,
104 /// Represents a comptime variables storage.
105 comptime_alloc,
104 /// Represents a pointer to a decl, not the value of the decl.106 /// Represents a pointer to a decl, not the value of the decl.
105 decl_ref,107 decl_ref,
106 elem_ptr,108 elem_ptr,
...@@ -223,6 +225,7 @@ pub const Value = extern union {...@@ -223,6 +225,7 @@ pub const Value = extern union {
223 .int_i64 => Payload.I64,225 .int_i64 => Payload.I64,
224 .function => Payload.Function,226 .function => Payload.Function,
225 .variable => Payload.Variable,227 .variable => Payload.Variable,
228 .comptime_alloc => Payload.ComptimeAlloc,
226 .elem_ptr => Payload.ElemPtr,229 .elem_ptr => Payload.ElemPtr,
227 .field_ptr => Payload.FieldPtr,230 .field_ptr => Payload.FieldPtr,
228 .float_16 => Payload.Float_16,231 .float_16 => Payload.Float_16,
...@@ -403,6 +406,7 @@ pub const Value = extern union {...@@ -403,6 +406,7 @@ pub const Value = extern union {
403 };406 };
404 return Value{ .ptr_otherwise = &new_payload.base };407 return Value{ .ptr_otherwise = &new_payload.base };
405 },408 },
409 .comptime_alloc => return self.copyPayloadShallow(allocator, Payload.ComptimeAlloc),
406 .decl_ref => return self.copyPayloadShallow(allocator, Payload.Decl),410 .decl_ref => return self.copyPayloadShallow(allocator, Payload.Decl),
407 .elem_ptr => {411 .elem_ptr => {
408 const payload = self.castTag(.elem_ptr).?;412 const payload = self.castTag(.elem_ptr).?;
...@@ -577,6 +581,11 @@ pub const Value = extern union {...@@ -577,6 +581,11 @@ pub const Value = extern union {
577 try out_stream.writeAll("&const ");581 try out_stream.writeAll("&const ");
578 val = ref_val;582 val = ref_val;
579 },583 },
584 .comptime_alloc => {
585 const ref_val = val.castTag(.comptime_alloc).?.data.val;
586 try out_stream.writeAll("&");
587 val = ref_val;
588 },
580 .decl_ref => return out_stream.writeAll("(decl ref)"),589 .decl_ref => return out_stream.writeAll("(decl ref)"),
581 .elem_ptr => {590 .elem_ptr => {
582 const elem_ptr = val.castTag(.elem_ptr).?.data;591 const elem_ptr = val.castTag(.elem_ptr).?.data;
...@@ -713,6 +722,7 @@ pub const Value = extern union {...@@ -713,6 +722,7 @@ pub const Value = extern union {
713 .extern_fn,722 .extern_fn,
714 .variable,723 .variable,
715 .ref_val,724 .ref_val,
725 .comptime_alloc,
716 .decl_ref,726 .decl_ref,
717 .elem_ptr,727 .elem_ptr,
718 .field_ptr,728 .field_ptr,
...@@ -1186,6 +1196,10 @@ pub const Value = extern union {...@@ -1186,6 +1196,10 @@ pub const Value = extern union {
1186 const payload = self.castTag(.ref_val).?;1196 const payload = self.castTag(.ref_val).?;
1187 std.hash.autoHash(&hasher, payload.data.hash());1197 std.hash.autoHash(&hasher, payload.data.hash());
1188 },1198 },
1199 .comptime_alloc => {
1200 const payload = self.castTag(.comptime_alloc).?;
1201 std.hash.autoHash(&hasher, payload.data.val.hash());
1202 },
1189 .int_big_positive, .int_big_negative => {1203 .int_big_positive, .int_big_negative => {
1190 var space: BigIntSpace = undefined;1204 var space: BigIntSpace = undefined;
1191 const big = self.toBigInt(&space);1205 const big = self.toBigInt(&space);
...@@ -1277,6 +1291,7 @@ pub const Value = extern union {...@@ -1277,6 +1291,7 @@ pub const Value = extern union {
1277 /// Returns error.AnalysisFail if the pointer points to a Decl that failed semantic analysis.1291 /// Returns error.AnalysisFail if the pointer points to a Decl that failed semantic analysis.
1278 pub fn pointerDeref(self: Value, allocator: *Allocator) error{ AnalysisFail, OutOfMemory }!Value {1292 pub fn pointerDeref(self: Value, allocator: *Allocator) error{ AnalysisFail, OutOfMemory }!Value {
1279 return switch (self.tag()) {1293 return switch (self.tag()) {
1294 .comptime_alloc => self.castTag(.comptime_alloc).?.data.val,
1280 .ref_val => self.castTag(.ref_val).?.data,1295 .ref_val => self.castTag(.ref_val).?.data,
1281 .decl_ref => self.castTag(.decl_ref).?.data.value(),1296 .decl_ref => self.castTag(.decl_ref).?.data.value(),
1282 .elem_ptr => {1297 .elem_ptr => {
...@@ -1462,6 +1477,7 @@ pub const Value = extern union {...@@ -1462,6 +1477,7 @@ pub const Value = extern union {
1462 .int_big_positive,1477 .int_big_positive,
1463 .int_big_negative,1478 .int_big_negative,
1464 .ref_val,1479 .ref_val,
1480 .comptime_alloc,
1465 .decl_ref,1481 .decl_ref,
1466 .elem_ptr,1482 .elem_ptr,
1467 .field_ptr,1483 .field_ptr,
...@@ -1542,6 +1558,16 @@ pub const Value = extern union {...@@ -1542,6 +1558,16 @@ pub const Value = extern union {
1542 data: Value,1558 data: Value,
1543 };1559 };
15441560
1561 pub const ComptimeAlloc = struct {
1562 pub const base_tag = Tag.comptime_alloc;
1563
1564 base: Payload = Payload{ .tag = base_tag },
1565 data: struct {
1566 val: Value,
1567 runtime_index: u32,
1568 },
1569 };
1570
1545 pub const ElemPtr = struct {1571 pub const ElemPtr = struct {
1546 pub const base_tag = Tag.elem_ptr;1572 pub const base_tag = Tag.elem_ptr;
15471573
test/stage2/test.zig+82
...@@ -1420,4 +1420,86 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1420,4 +1420,86 @@ pub fn addCases(ctx: *TestContext) !void {
1420 \\}1420 \\}
1421 , &[_][]const u8{":4:27: error: expected type, found comptime_int"});1421 , &[_][]const u8{":4:27: error: expected type, found comptime_int"});
1422 }1422 }
1423 {
1424 var case = ctx.exe("comptime var", linux_x64);
1425
1426 case.addError(
1427 \\pub fn main() void {
1428 \\ var a: u32 = 0;
1429 \\ comptime var b: u32 = 0;
1430 \\ if (a == 0) b = 3;
1431 \\}
1432 , &.{
1433 ":4:21: error: store to comptime variable depends on runtime condition",
1434 ":4:11: note: runtime condition here",
1435 });
1436
1437 case.addError(
1438 \\pub fn main() void {
1439 \\ var a: u32 = 0;
1440 \\ comptime var b: u32 = 0;
1441 \\ switch (a) {
1442 \\ 0 => {},
1443 \\ else => b = 3,
1444 \\ }
1445 \\}
1446 , &.{
1447 ":6:21: error: store to comptime variable depends on runtime condition",
1448 ":4:13: note: runtime condition here",
1449 });
1450
1451 case.addCompareOutput(
1452 \\pub fn main() void {
1453 \\ comptime var len: u32 = 5;
1454 \\ print(len);
1455 \\ len += 9;
1456 \\ print(len);
1457 \\}
1458 \\
1459 \\fn print(len: usize) void {
1460 \\ asm volatile ("syscall"
1461 \\ :
1462 \\ : [number] "{rax}" (1),
1463 \\ [arg1] "{rdi}" (1),
1464 \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")),
1465 \\ [arg3] "{rdx}" (len)
1466 \\ : "rcx", "r11", "memory"
1467 \\ );
1468 \\ return;
1469 \\}
1470 , "HelloHello, World!\n");
1471
1472 case.addError(
1473 \\comptime {
1474 \\ var x: i32 = 1;
1475 \\ x += 1;
1476 \\ if (x != 1) unreachable;
1477 \\}
1478 , &.{":4:17: error: unable to resolve comptime value"});
1479
1480 case.addError(
1481 \\pub fn main() void {
1482 \\ comptime var i: u64 = 0;
1483 \\ while (i < 5) : (i += 1) {}
1484 \\}
1485 , &.{
1486 ":3:24: error: cannot store to comptime variable in non-inline loop",
1487 ":3:5: note: non-inline loop here",
1488 });
1489
1490 case.addCompareOutput(
1491 \\pub fn main() void {
1492 \\ var a: u32 = 0;
1493 \\ if (a == 0) {
1494 \\ comptime var b: u32 = 0;
1495 \\ b = 1;
1496 \\ }
1497 \\}
1498 \\comptime {
1499 \\ var x: i32 = 1;
1500 \\ x += 1;
1501 \\ if (x != 2) unreachable;
1502 \\}
1503 , "");
1504 }
1423}1505}