authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-06-08 14:16:57-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-06-08 14:16:57-04:00
logccfa16828445ebf3634c2a0f27ec359af1628efc
tree0d73a532885fca9b9e6db9fdceb8f1e014cf495f
parentc822a0b59fe123ff67bcb188f20290d818467be7
parentd41a5105cd6222564dfe6bad9cff2c445847c6b3
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #9030 from Vexu/stage2

Stage2: implement comptime variables

5 files changed, 223 insertions(+), 7 deletions(-)

src/AstGen.zig+1-1
......@@ -2357,7 +2357,7 @@ fn varDecl(
23572357 return &sub_scope.base;
23582358 },
23592359 .keyword_var => {
2360 const is_comptime = var_decl.comptime_token != null;
2360 const is_comptime = var_decl.comptime_token != null or gz.force_comptime;
23612361 var resolve_inferred_alloc: Zir.Inst.Ref = .none;
23622362 const var_data: struct {
23632363 result_loc: ResultLoc,
src/Module.zig+10
......@@ -1139,6 +1139,13 @@ pub const Scope = struct {
11391139 instructions: ArrayListUnmanaged(*ir.Inst),
11401140 label: ?*Label = null,
11411141 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
11421149 is_comptime: bool,
11431150
11441151 /// This `Block` maps a block ZIR instruction to the corresponding
......@@ -1182,6 +1189,9 @@ pub const Scope = struct {
11821189 .label = null,
11831190 .inlining = parent.inlining,
11841191 .is_comptime = parent.is_comptime,
1192 .runtime_cond = parent.runtime_cond,
1193 .runtime_loop = parent.runtime_loop,
1194 .runtime_index = parent.runtime_index,
11851195 };
11861196 }
11871197
src/Sema.zig+83-6
......@@ -147,7 +147,7 @@ pub fn analyzeBody(
147147 // directly jump to the next one, rather than detouring through the loop
148148 // continue expression. Related: https://github.com/ziglang/zig/issues/8220
149149 var i: usize = 0;
150 while (true) : (i += 1) {
150 while (true) {
151151 const inst = body[i];
152152 const air_inst = switch (tags[inst]) {
153153 // zig fmt: off
......@@ -394,78 +394,97 @@ pub fn analyzeBody(
394394 // putting them into the map.
395395 .breakpoint => {
396396 try sema.zirBreakpoint(block, inst);
397 i += 1;
397398 continue;
398399 },
399400 .fence => {
400401 try sema.zirFence(block, inst);
402 i += 1;
401403 continue;
402404 },
403405 .dbg_stmt => {
404406 try sema.zirDbgStmt(block, inst);
407 i += 1;
405408 continue;
406409 },
407410 .ensure_err_payload_void => {
408411 try sema.zirEnsureErrPayloadVoid(block, inst);
412 i += 1;
409413 continue;
410414 },
411415 .ensure_result_non_error => {
412416 try sema.zirEnsureResultNonError(block, inst);
417 i += 1;
413418 continue;
414419 },
415420 .ensure_result_used => {
416421 try sema.zirEnsureResultUsed(block, inst);
422 i += 1;
417423 continue;
418424 },
419425 .set_eval_branch_quota => {
420426 try sema.zirSetEvalBranchQuota(block, inst);
427 i += 1;
421428 continue;
422429 },
423430 .store => {
424431 try sema.zirStore(block, inst);
432 i += 1;
425433 continue;
426434 },
427435 .store_node => {
428436 try sema.zirStoreNode(block, inst);
437 i += 1;
429438 continue;
430439 },
431440 .store_to_block_ptr => {
432441 try sema.zirStoreToBlockPtr(block, inst);
442 i += 1;
433443 continue;
434444 },
435445 .store_to_inferred_ptr => {
436446 try sema.zirStoreToInferredPtr(block, inst);
447 i += 1;
437448 continue;
438449 },
439450 .resolve_inferred_alloc => {
440451 try sema.zirResolveInferredAlloc(block, inst);
452 i += 1;
441453 continue;
442454 },
443455 .validate_struct_init_ptr => {
444456 try sema.zirValidateStructInitPtr(block, inst);
457 i += 1;
445458 continue;
446459 },
447460 .validate_array_init_ptr => {
448461 try sema.zirValidateArrayInitPtr(block, inst);
462 i += 1;
449463 continue;
450464 },
451465 .@"export" => {
452466 try sema.zirExport(block, inst);
467 i += 1;
453468 continue;
454469 },
455470 .set_align_stack => {
456471 try sema.zirSetAlignStack(block, inst);
472 i += 1;
457473 continue;
458474 },
459475 .set_cold => {
460476 try sema.zirSetAlignStack(block, inst);
477 i += 1;
461478 continue;
462479 },
463480 .set_float_mode => {
464481 try sema.zirSetFloatMode(block, inst);
482 i += 1;
465483 continue;
466484 },
467485 .set_runtime_safety => {
468486 try sema.zirSetRuntimeSafety(block, inst);
487 i += 1;
469488 continue;
470489 },
471490
......@@ -509,7 +528,8 @@ pub fn analyzeBody(
509528 };
510529 if (air_inst.ty.isNoReturn())
511530 return always_noreturn;
512 try map.putNoClobber(sema.gpa, inst, air_inst);
531 try map.put(sema.gpa, inst, air_inst);
532 i += 1;
513533 }
514534}
515535
......@@ -1238,9 +1258,26 @@ fn zirAllocExtended(
12381258}
12391259
12401260fn zirAllocComptime(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
1261 const tracy = trace(@src());
1262 defer tracy.end();
1263
12411264 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
12421265 const src = inst_data.src();
1243 return sema.mod.fail(&block.base, src, "TODO implement Sema.zirAllocComptime", .{});
1266 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
1267 const var_type = try sema.resolveType(block, ty_src, inst_data.operand);
1268 const ptr_type = try sema.mod.simplePtrType(sema.arena, var_type, true, .One);
1269
1270 const val_payload = try sema.arena.create(Value.Payload.ComptimeAlloc);
1271 val_payload.* = .{
1272 .data = .{
1273 .runtime_index = block.runtime_index,
1274 .val = undefined, // astgen guarantees there will be a store before the first load
1275 },
1276 };
1277 return sema.mod.constInst(sema.arena, src, .{
1278 .ty = ptr_type,
1279 .val = Value.initPayload(&val_payload.base),
1280 });
12441281}
12451282
12461283fn zirAllocInferredComptime(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
......@@ -1742,6 +1779,9 @@ fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) InnerE
17421779 };
17431780 var child_block = parent_block.makeSubBlock();
17441781 child_block.label = &label;
1782 child_block.runtime_cond = null;
1783 child_block.runtime_loop = src;
1784 child_block.runtime_index += 1;
17451785 const merges = &child_block.label.?.merges;
17461786
17471787 defer child_block.instructions.deinit(sema.gpa);
......@@ -4066,6 +4106,9 @@ fn analyzeSwitch(
40664106 const cases = try sema.arena.alloc(Inst.SwitchBr.Case, scalar_cases_len);
40674107
40684108 var case_block = child_block.makeSubBlock();
4109 case_block.runtime_loop = null;
4110 case_block.runtime_cond = operand.src;
4111 case_block.runtime_index += 1;
40694112 defer case_block.instructions.deinit(gpa);
40704113
40714114 var extra_index: usize = special.end;
......@@ -4584,14 +4627,14 @@ fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerEr
45844627
45854628 const tag_override = block.sema.code.instructions.items(.tag)[inst];
45864629 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
4587 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
4630 sema.src = .{ .node_offset_bin_op = inst_data.src_node };
45884631 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
45894632 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
45904633 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
45914634 const lhs = try sema.resolveInst(extra.lhs);
45924635 const rhs = try sema.resolveInst(extra.rhs);
45934636
4594 return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src);
4637 return sema.analyzeArithmetic(block, tag_override, lhs, rhs, sema.src, lhs_src, rhs_src);
45954638}
45964639
45974640fn zirOverflowArithmetic(
......@@ -5150,6 +5193,9 @@ fn zirBoolBr(
51505193 };
51515194
51525195 var child_block = parent_block.makeSubBlock();
5196 child_block.runtime_loop = null;
5197 child_block.runtime_cond = lhs.src;
5198 child_block.runtime_index += 1;
51535199 defer child_block.instructions.deinit(sema.gpa);
51545200
51555201 var then_block = child_block.makeSubBlock();
......@@ -5258,6 +5304,9 @@ fn zirCondbr(
52585304 }
52595305
52605306 var sub_block = parent_block.makeSubBlock();
5307 sub_block.runtime_loop = null;
5308 sub_block.runtime_cond = cond.src;
5309 sub_block.runtime_index += 1;
52615310 defer sub_block.instructions.deinit(sema.gpa);
52625311
52635312 _ = try sema.analyzeBody(&sub_block, then_body);
......@@ -6753,7 +6802,35 @@ fn storePtr(
67536802 if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null)
67546803 return;
67556804
6756 // TODO handle comptime pointer writes
6805 if (try sema.resolvePossiblyUndefinedValue(block, src, ptr)) |ptr_val| {
6806 const const_val = (try sema.resolvePossiblyUndefinedValue(block, src, value)) orelse
6807 return sema.mod.fail(&block.base, src, "cannot store runtime value in compile time variable", .{});
6808
6809 const comptime_alloc = ptr_val.castTag(.comptime_alloc).?;
6810 if (comptime_alloc.data.runtime_index < block.runtime_index) {
6811 if (block.runtime_cond) |cond_src| {
6812 const msg = msg: {
6813 const msg = try sema.mod.errMsg(&block.base, src, "store to comptime variable depends on runtime condition", .{});
6814 errdefer msg.destroy(sema.gpa);
6815 try sema.mod.errNote(&block.base, cond_src, msg, "runtime condition here", .{});
6816 break :msg msg;
6817 };
6818 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
6819 }
6820 if (block.runtime_loop) |loop_src| {
6821 const msg = msg: {
6822 const msg = try sema.mod.errMsg(&block.base, src, "cannot store to comptime variable in non-inline loop", .{});
6823 errdefer msg.destroy(sema.gpa);
6824 try sema.mod.errNote(&block.base, loop_src, msg, "non-inline loop here", .{});
6825 break :msg msg;
6826 };
6827 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
6828 }
6829 unreachable;
6830 }
6831 comptime_alloc.data.val = const_val;
6832 return;
6833 }
67576834 // TODO handle if the element type requires comptime
67586835
67596836 try sema.requireRuntimeBlock(block, src);
src/value.zig+26
......@@ -101,6 +101,8 @@ pub const Value = extern union {
101101 variable,
102102 /// Represents a pointer to another immutable value.
103103 ref_val,
104 /// Represents a comptime variables storage.
105 comptime_alloc,
104106 /// Represents a pointer to a decl, not the value of the decl.
105107 decl_ref,
106108 elem_ptr,
......@@ -223,6 +225,7 @@ pub const Value = extern union {
223225 .int_i64 => Payload.I64,
224226 .function => Payload.Function,
225227 .variable => Payload.Variable,
228 .comptime_alloc => Payload.ComptimeAlloc,
226229 .elem_ptr => Payload.ElemPtr,
227230 .field_ptr => Payload.FieldPtr,
228231 .float_16 => Payload.Float_16,
......@@ -403,6 +406,7 @@ pub const Value = extern union {
403406 };
404407 return Value{ .ptr_otherwise = &new_payload.base };
405408 },
409 .comptime_alloc => return self.copyPayloadShallow(allocator, Payload.ComptimeAlloc),
406410 .decl_ref => return self.copyPayloadShallow(allocator, Payload.Decl),
407411 .elem_ptr => {
408412 const payload = self.castTag(.elem_ptr).?;
......@@ -577,6 +581,11 @@ pub const Value = extern union {
577581 try out_stream.writeAll("&const ");
578582 val = ref_val;
579583 },
584 .comptime_alloc => {
585 const ref_val = val.castTag(.comptime_alloc).?.data.val;
586 try out_stream.writeAll("&");
587 val = ref_val;
588 },
580589 .decl_ref => return out_stream.writeAll("(decl ref)"),
581590 .elem_ptr => {
582591 const elem_ptr = val.castTag(.elem_ptr).?.data;
......@@ -713,6 +722,7 @@ pub const Value = extern union {
713722 .extern_fn,
714723 .variable,
715724 .ref_val,
725 .comptime_alloc,
716726 .decl_ref,
717727 .elem_ptr,
718728 .field_ptr,
......@@ -1186,6 +1196,10 @@ pub const Value = extern union {
11861196 const payload = self.castTag(.ref_val).?;
11871197 std.hash.autoHash(&hasher, payload.data.hash());
11881198 },
1199 .comptime_alloc => {
1200 const payload = self.castTag(.comptime_alloc).?;
1201 std.hash.autoHash(&hasher, payload.data.val.hash());
1202 },
11891203 .int_big_positive, .int_big_negative => {
11901204 var space: BigIntSpace = undefined;
11911205 const big = self.toBigInt(&space);
......@@ -1277,6 +1291,7 @@ pub const Value = extern union {
12771291 /// Returns error.AnalysisFail if the pointer points to a Decl that failed semantic analysis.
12781292 pub fn pointerDeref(self: Value, allocator: *Allocator) error{ AnalysisFail, OutOfMemory }!Value {
12791293 return switch (self.tag()) {
1294 .comptime_alloc => self.castTag(.comptime_alloc).?.data.val,
12801295 .ref_val => self.castTag(.ref_val).?.data,
12811296 .decl_ref => self.castTag(.decl_ref).?.data.value(),
12821297 .elem_ptr => {
......@@ -1462,6 +1477,7 @@ pub const Value = extern union {
14621477 .int_big_positive,
14631478 .int_big_negative,
14641479 .ref_val,
1480 .comptime_alloc,
14651481 .decl_ref,
14661482 .elem_ptr,
14671483 .field_ptr,
......@@ -1542,6 +1558,16 @@ pub const Value = extern union {
15421558 data: Value,
15431559 };
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
15451571 pub const ElemPtr = struct {
15461572 pub const base_tag = Tag.elem_ptr;
15471573
test/stage2/test.zig+103
......@@ -1420,4 +1420,107 @@ pub fn addCases(ctx: *TestContext) !void {
14201420 \\}
14211421 , &[_][]const u8{":4:27: error: expected type, found comptime_int"});
14221422 }
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 \\pub fn main() void {}
1479 , &.{":4:17: error: unable to resolve comptime value"});
1480
1481 case.addError(
1482 \\pub fn main() void {
1483 \\ comptime var i: u64 = 0;
1484 \\ while (i < 5) : (i += 1) {}
1485 \\}
1486 , &.{
1487 ":3:24: error: cannot store to comptime variable in non-inline loop",
1488 ":3:5: note: non-inline loop here",
1489 });
1490
1491 case.addCompareOutput(
1492 \\pub fn main() void {
1493 \\ var a: u32 = 0;
1494 \\ if (a == 0) {
1495 \\ comptime var b: u32 = 0;
1496 \\ b = 1;
1497 \\ }
1498 \\}
1499 \\comptime {
1500 \\ var x: i32 = 1;
1501 \\ x += 1;
1502 \\ if (x != 2) unreachable;
1503 \\}
1504 , "");
1505
1506 case.addCompareOutput(
1507 \\pub fn main() void {
1508 \\ comptime var i: u64 = 2;
1509 \\ inline while (i < 6) : (i+=1) {
1510 \\ print(i);
1511 \\ }
1512 \\}
1513 \\fn print(len: usize) void {
1514 \\ asm volatile ("syscall"
1515 \\ :
1516 \\ : [number] "{rax}" (1),
1517 \\ [arg1] "{rdi}" (1),
1518 \\ [arg2] "{rsi}" (@ptrToInt("Hello")),
1519 \\ [arg3] "{rdx}" (len)
1520 \\ : "rcx", "r11", "memory"
1521 \\ );
1522 \\ return;
1523 \\}
1524 , "HeHelHellHello");
1525 }
14231526}