| author | |
| committer | |
| log | 7efd7bc3b8cc403814c0aa62be0a17ff25b33902 |
| tree | 7b92414b05bfc0e66bdc8aff4f3a68abc9a88ee7 |
| parent | ca6951ff7959b88573bfec7c7bf0465209f3263f |
5 files changed, 181 insertions(+), 6 deletions(-)
src/AstGen.zig+1-1| ... | ... | @@ -2357,7 +2357,7 @@ fn varDecl( |
| 2357 | 2357 | return &sub_scope.base; |
| 2358 | 2358 | }, |
| 2359 | 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 | 2361 | var resolve_inferred_alloc: Zir.Inst.Ref = .none; |
| 2362 | 2362 | const var_data: struct { |
| 2363 | 2363 | result_loc: ResultLoc, |
src/Module.zig+10| ... | ... | @@ -1139,6 +1139,13 @@ pub const Scope = struct { |
| 1139 | 1139 | instructions: ArrayListUnmanaged(*ir.Inst), |
| 1140 | 1140 | label: ?*Label = null, |
| 1141 | 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 | 1149 | is_comptime: bool, |
| 1143 | 1150 | |
| 1144 | 1151 | /// This `Block` maps a block ZIR instruction to the corresponding |
| ... | ... | @@ -1182,6 +1189,9 @@ pub const Scope = struct { |
| 1182 | 1189 | .label = null, |
| 1183 | 1190 | .inlining = parent.inlining, |
| 1184 | 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 | } |
| 1187 | 1197 |
src/Sema.zig+62-5| ... | ... | @@ -509,7 +509,7 @@ pub fn analyzeBody( |
| 509 | 509 | }; |
| 510 | 510 | if (air_inst.ty.isNoReturn()) |
| 511 | 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 | } |
| 515 | 515 | |
| ... | ... | @@ -1238,9 +1238,26 @@ fn zirAllocExtended( |
| 1238 | 1238 | } |
| 1239 | 1239 | |
| 1240 | 1240 | fn zirAllocComptime(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { |
| 1241 | const tracy = trace(@src()); | |
| 1242 | defer tracy.end(); | |
| 1243 | ||
| 1241 | 1244 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 1242 | 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 | } |
| 1245 | 1262 | |
| 1246 | 1263 | fn 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 | 1759 | }; |
| 1743 | 1760 | var child_block = parent_block.makeSubBlock(); |
| 1744 | 1761 | child_block.label = &label; |
| 1762 | child_block.runtime_cond = null; | |
| 1763 | child_block.runtime_loop = src; | |
| 1764 | child_block.runtime_index += 1; | |
| 1745 | 1765 | const merges = &child_block.label.?.merges; |
| 1746 | 1766 | |
| 1747 | 1767 | defer child_block.instructions.deinit(sema.gpa); |
| ... | ... | @@ -4066,6 +4086,9 @@ fn analyzeSwitch( |
| 4066 | 4086 | const cases = try sema.arena.alloc(Inst.SwitchBr.Case, scalar_cases_len); |
| 4067 | 4087 | |
| 4068 | 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 | 4092 | defer case_block.instructions.deinit(gpa); |
| 4070 | 4093 | |
| 4071 | 4094 | var extra_index: usize = special.end; |
| ... | ... | @@ -4584,14 +4607,14 @@ fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerEr |
| 4584 | 4607 | |
| 4585 | 4608 | const tag_override = block.sema.code.instructions.items(.tag)[inst]; |
| 4586 | 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 | 4611 | const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; |
| 4589 | 4612 | const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; |
| 4590 | 4613 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 4591 | 4614 | const lhs = try sema.resolveInst(extra.lhs); |
| 4592 | 4615 | const rhs = try sema.resolveInst(extra.rhs); |
| 4593 | 4616 | |
| 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 | } |
| 4596 | 4619 | |
| 4597 | 4620 | fn zirOverflowArithmetic( |
| ... | ... | @@ -5150,6 +5173,9 @@ fn zirBoolBr( |
| 5150 | 5173 | }; |
| 5151 | 5174 | |
| 5152 | 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 | 5179 | defer child_block.instructions.deinit(sema.gpa); |
| 5154 | 5180 | |
| 5155 | 5181 | var then_block = child_block.makeSubBlock(); |
| ... | ... | @@ -5258,6 +5284,9 @@ fn zirCondbr( |
| 5258 | 5284 | } |
| 5259 | 5285 | |
| 5260 | 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 | 5290 | defer sub_block.instructions.deinit(sema.gpa); |
| 5262 | 5291 | |
| 5263 | 5292 | _ = try sema.analyzeBody(&sub_block, then_body); |
| ... | ... | @@ -6753,7 +6782,35 @@ fn storePtr( |
| 6753 | 6782 | if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null) |
| 6754 | 6783 | return; |
| 6755 | 6784 | |
| 6756 | // TODO handle comptime pointer writes | |
| 6785 | 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 | 6814 | // TODO handle if the element type requires comptime |
| 6758 | 6815 | |
| 6759 | 6816 | try sema.requireRuntimeBlock(block, src); |
src/value.zig+26| ... | ... | @@ -101,6 +101,8 @@ pub const Value = extern union { |
| 101 | 101 | variable, |
| 102 | 102 | /// Represents a pointer to another immutable value. |
| 103 | 103 | ref_val, |
| 104 | /// Represents a comptime variables storage. | |
| 105 | comptime_alloc, | |
| 104 | 106 | /// Represents a pointer to a decl, not the value of the decl. |
| 105 | 107 | decl_ref, |
| 106 | 108 | elem_ptr, |
| ... | ... | @@ -223,6 +225,7 @@ pub const Value = extern union { |
| 223 | 225 | .int_i64 => Payload.I64, |
| 224 | 226 | .function => Payload.Function, |
| 225 | 227 | .variable => Payload.Variable, |
| 228 | .comptime_alloc => Payload.ComptimeAlloc, | |
| 226 | 229 | .elem_ptr => Payload.ElemPtr, |
| 227 | 230 | .field_ptr => Payload.FieldPtr, |
| 228 | 231 | .float_16 => Payload.Float_16, |
| ... | ... | @@ -403,6 +406,7 @@ pub const Value = extern union { |
| 403 | 406 | }; |
| 404 | 407 | return Value{ .ptr_otherwise = &new_payload.base }; |
| 405 | 408 | }, |
| 409 | .comptime_alloc => return self.copyPayloadShallow(allocator, Payload.ComptimeAlloc), | |
| 406 | 410 | .decl_ref => return self.copyPayloadShallow(allocator, Payload.Decl), |
| 407 | 411 | .elem_ptr => { |
| 408 | 412 | const payload = self.castTag(.elem_ptr).?; |
| ... | ... | @@ -577,6 +581,11 @@ pub const Value = extern union { |
| 577 | 581 | try out_stream.writeAll("&const "); |
| 578 | 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 | 589 | .decl_ref => return out_stream.writeAll("(decl ref)"), |
| 581 | 590 | .elem_ptr => { |
| 582 | 591 | const elem_ptr = val.castTag(.elem_ptr).?.data; |
| ... | ... | @@ -713,6 +722,7 @@ pub const Value = extern union { |
| 713 | 722 | .extern_fn, |
| 714 | 723 | .variable, |
| 715 | 724 | .ref_val, |
| 725 | .comptime_alloc, | |
| 716 | 726 | .decl_ref, |
| 717 | 727 | .elem_ptr, |
| 718 | 728 | .field_ptr, |
| ... | ... | @@ -1186,6 +1196,10 @@ pub const Value = extern union { |
| 1186 | 1196 | const payload = self.castTag(.ref_val).?; |
| 1187 | 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 | 1203 | .int_big_positive, .int_big_negative => { |
| 1190 | 1204 | var space: BigIntSpace = undefined; |
| 1191 | 1205 | const big = self.toBigInt(&space); |
| ... | ... | @@ -1277,6 +1291,7 @@ pub const Value = extern union { |
| 1277 | 1291 | /// Returns error.AnalysisFail if the pointer points to a Decl that failed semantic analysis. |
| 1278 | 1292 | pub fn pointerDeref(self: Value, allocator: *Allocator) error{ AnalysisFail, OutOfMemory }!Value { |
| 1279 | 1293 | return switch (self.tag()) { |
| 1294 | .comptime_alloc => self.castTag(.comptime_alloc).?.data.val, | |
| 1280 | 1295 | .ref_val => self.castTag(.ref_val).?.data, |
| 1281 | 1296 | .decl_ref => self.castTag(.decl_ref).?.data.value(), |
| 1282 | 1297 | .elem_ptr => { |
| ... | ... | @@ -1462,6 +1477,7 @@ pub const Value = extern union { |
| 1462 | 1477 | .int_big_positive, |
| 1463 | 1478 | .int_big_negative, |
| 1464 | 1479 | .ref_val, |
| 1480 | .comptime_alloc, | |
| 1465 | 1481 | .decl_ref, |
| 1466 | 1482 | .elem_ptr, |
| 1467 | 1483 | .field_ptr, |
| ... | ... | @@ -1542,6 +1558,16 @@ pub const Value = extern union { |
| 1542 | 1558 | data: Value, |
| 1543 | 1559 | }; |
| 1544 | 1560 | |
| 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 | 1571 | pub const ElemPtr = struct { |
| 1546 | 1572 | pub const base_tag = Tag.elem_ptr; |
| 1547 | 1573 |
test/stage2/test.zig+82| ... | ... | @@ -1420,4 +1420,86 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1420 | 1420 | \\} |
| 1421 | 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 | } |