authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-08-01 12:27:22-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-08-01 12:36:04-07:00
log6ae0825e7f87fc9b73a4b968964196b0e164f062
tree9c48fd4314fda457e5c54717407f0b15ea3aff8e
parent7e52a096dbace546cae89ab691741ecca45f28ce

Sema: implement comptime variables

Sema now properly handles alloc_inferred and alloc_inferred_mut ZIR instructions inside a comptime execution context. In this case it creates Decl objects and points to them with the new `decl_ref_mut` Value Tag. `storePtr` is updated to mutate such Decl types and values. In this case it destroys the old arena and makes a new one, preventing memory growth during comptime code execution. Additionally: * Fix `storePtr` to emit a compile error for a pointer comptime-known to be undefined. * Fix `storePtr` to emit runtime instructions for all the cases that a pointer is comptime-known but does not support comptime dereferencing, such as `@intToPtr` on a hard-coded address, or an extern function. * Fix `ret_coerce` not coercing inside inline function call context.

6 files changed, 226 insertions(+), 162 deletions(-)

src/Sema.zig+141-84
...@@ -154,9 +154,6 @@ pub fn analyzeBody(...@@ -154,9 +154,6 @@ pub fn analyzeBody(
154 // We use a while(true) loop here to avoid a redundant way of breaking out of154 // We use a while(true) loop here to avoid a redundant way of breaking out of
155 // the loop. The only way to break out of the loop is with a `noreturn`155 // the loop. The only way to break out of the loop is with a `noreturn`
156 // instruction.156 // instruction.
157 // TODO: As an optimization, make sure the codegen for these switch prongs
158 // directly jump to the next one, rather than detouring through the loop
159 // continue expression. Related: https://github.com/ziglang/zig/issues/8220
160 var i: usize = 0;157 var i: usize = 0;
161 while (true) {158 while (true) {
162 const inst = body[i];159 const inst = body[i];
...@@ -391,7 +388,7 @@ pub fn analyzeBody(...@@ -391,7 +388,7 @@ pub fn analyzeBody(
391 .condbr => return sema.zirCondbr(block, inst),388 .condbr => return sema.zirCondbr(block, inst),
392 .@"break" => return sema.zirBreak(block, inst),389 .@"break" => return sema.zirBreak(block, inst),
393 .compile_error => return sema.zirCompileError(block, inst),390 .compile_error => return sema.zirCompileError(block, inst),
394 .ret_coerce => return sema.zirRetCoerce(block, inst, true),391 .ret_coerce => return sema.zirRetCoerce(block, inst),
395 .ret_node => return sema.zirRetNode(block, inst),392 .ret_node => return sema.zirRetNode(block, inst),
396 .ret_err_value => return sema.zirRetErrValue(block, inst),393 .ret_err_value => return sema.zirRetErrValue(block, inst),
397 .@"unreachable" => return sema.zirUnreachable(block, inst),394 .@"unreachable" => return sema.zirUnreachable(block, inst),
...@@ -1396,14 +1393,19 @@ fn zirAllocComptime(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Comp...@@ -1396,14 +1393,19 @@ fn zirAllocComptime(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Comp
1396 const var_type = try sema.resolveType(block, ty_src, inst_data.operand);1393 const var_type = try sema.resolveType(block, ty_src, inst_data.operand);
1397 const ptr_type = try Module.simplePtrType(sema.arena, var_type, true, .One);1394 const ptr_type = try Module.simplePtrType(sema.arena, var_type, true, .One);
13981395
1399 const val_payload = try sema.arena.create(Value.Payload.ComptimeAlloc);1396 var anon_decl = try block.startAnonDecl();
1400 val_payload.* = .{1397 defer anon_decl.deinit();
1401 .data = .{1398 const decl = try anon_decl.finish(
1402 .runtime_index = block.runtime_index,1399 try var_type.copy(anon_decl.arena()),
1403 .val = undefined, // astgen guarantees there will be a store before the first load1400 // AstGen guarantees there will be a store before the first load, so we put a value
1404 },1401 // here indicating there is no valid value.
1405 };1402 Value.initTag(.unreachable_value),
1406 return sema.addConstant(ptr_type, Value.initPayload(&val_payload.base));1403 );
1404 try sema.mod.declareDeclDependency(sema.owner_decl, decl);
1405 return sema.addConstant(ptr_type, try Value.Tag.decl_ref_mut.create(sema.arena, .{
1406 .runtime_index = block.runtime_index,
1407 .decl = decl,
1408 }));
1407}1409}
14081410
1409fn zirAllocInferredComptime(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {1411fn zirAllocInferredComptime(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -1450,16 +1452,23 @@ fn zirAllocInferred(...@@ -1450,16 +1452,23 @@ fn zirAllocInferred(
14501452
1451 const src_node = sema.code.instructions.items(.data)[inst].node;1453 const src_node = sema.code.instructions.items(.data)[inst].node;
1452 const src: LazySrcLoc = .{ .node_offset = src_node };1454 const src: LazySrcLoc = .{ .node_offset = src_node };
1455 sema.src = src;
14531456
1454 const val_payload = try sema.arena.create(Value.Payload.InferredAlloc);1457 if (block.is_comptime) {
1455 val_payload.* = .{1458 return sema.addConstant(
1456 .data = .{},1459 inferred_alloc_ty,
1457 };1460 try Value.Tag.inferred_alloc_comptime.create(sema.arena, undefined),
1458 // `Module.constInst` does not add the instruction to the block because it is1461 );
1462 }
1463
1464 // `Sema.addConstant` does not add the instruction to the block because it is
1459 // not needed in the case of constant values. However here, we plan to "downgrade"1465 // not needed in the case of constant values. However here, we plan to "downgrade"
1460 // to a normal instruction when we hit `resolve_inferred_alloc`. So we append1466 // to a normal instruction when we hit `resolve_inferred_alloc`. So we append
1461 // to the block even though it is currently a `.constant`.1467 // to the block even though it is currently a `.constant`.
1462 const result = try sema.addConstant(inferred_alloc_ty, Value.initPayload(&val_payload.base));1468 const result = try sema.addConstant(
1469 inferred_alloc_ty,
1470 try Value.Tag.inferred_alloc.create(sema.arena, .{}),
1471 );
1463 try sema.requireFunctionBlock(block, src);1472 try sema.requireFunctionBlock(block, src);
1464 try block.instructions.append(sema.gpa, Air.refToIndex(result).?);1473 try block.instructions.append(sema.gpa, Air.refToIndex(result).?);
1465 return result;1474 return result;
...@@ -1475,25 +1484,47 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde...@@ -1475,25 +1484,47 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde
1475 const ptr_inst = Air.refToIndex(ptr).?;1484 const ptr_inst = Air.refToIndex(ptr).?;
1476 assert(sema.air_instructions.items(.tag)[ptr_inst] == .constant);1485 assert(sema.air_instructions.items(.tag)[ptr_inst] == .constant);
1477 const air_datas = sema.air_instructions.items(.data);1486 const air_datas = sema.air_instructions.items(.data);
1478 const ptr_val = sema.air_values.items[air_datas[ptr_inst].ty_pl.payload];1487 const value_index = air_datas[ptr_inst].ty_pl.payload;
1479 const inferred_alloc = ptr_val.castTag(.inferred_alloc).?;1488 const ptr_val = sema.air_values.items[value_index];
1480 const peer_inst_list = inferred_alloc.data.stored_inst_list.items;
1481 const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list);
1482 const var_is_mut = switch (sema.typeOf(ptr).tag()) {1489 const var_is_mut = switch (sema.typeOf(ptr).tag()) {
1483 .inferred_alloc_const => false,1490 .inferred_alloc_const => false,
1484 .inferred_alloc_mut => true,1491 .inferred_alloc_mut => true,
1485 else => unreachable,1492 else => unreachable,
1486 };1493 };
1487 if (var_is_mut) {1494
1488 try sema.validateVarType(block, ty_src, final_elem_ty);1495 if (ptr_val.castTag(.inferred_alloc_comptime)) |iac| {
1496 const decl = iac.data;
1497 try sema.mod.declareDeclDependency(sema.owner_decl, decl);
1498
1499 const final_elem_ty = try decl.ty.copy(sema.arena);
1500 const final_ptr_ty = try Module.simplePtrType(sema.arena, final_elem_ty, true, .One);
1501 air_datas[ptr_inst].ty_pl.ty = try sema.addType(final_ptr_ty);
1502
1503 if (var_is_mut) {
1504 sema.air_values.items[value_index] = try Value.Tag.decl_ref_mut.create(sema.arena, .{
1505 .decl = decl,
1506 .runtime_index = block.runtime_index,
1507 });
1508 } else {
1509 sema.air_values.items[value_index] = try Value.Tag.decl_ref.create(sema.arena, decl);
1510 }
1511 return;
1489 }1512 }
1490 const final_ptr_ty = try Module.simplePtrType(sema.arena, final_elem_ty, true, .One);
14911513
1492 // Change it to a normal alloc.1514 if (ptr_val.castTag(.inferred_alloc)) |inferred_alloc| {
1493 sema.air_instructions.set(ptr_inst, .{1515 const peer_inst_list = inferred_alloc.data.stored_inst_list.items;
1494 .tag = .alloc,1516 const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list);
1495 .data = .{ .ty = final_ptr_ty },1517 if (var_is_mut) {
1496 });1518 try sema.validateVarType(block, ty_src, final_elem_ty);
1519 }
1520 // Change it to a normal alloc.
1521 const final_ptr_ty = try Module.simplePtrType(sema.arena, final_elem_ty, true, .One);
1522 sema.air_instructions.set(ptr_inst, .{
1523 .tag = .alloc,
1524 .data = .{ .ty = final_ptr_ty },
1525 });
1526 return;
1527 }
1497}1528}
14981529
1499fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {1530fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
...@@ -1654,23 +1685,45 @@ fn zirStoreToInferredPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index)...@@ -1654,23 +1685,45 @@ fn zirStoreToInferredPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index)
1654 const tracy = trace(@src());1685 const tracy = trace(@src());
1655 defer tracy.end();1686 defer tracy.end();
16561687
1657 const src: LazySrcLoc = .unneeded;1688 const src: LazySrcLoc = sema.src;
1658 const bin_inst = sema.code.instructions.items(.data)[inst].bin;1689 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
1659 const ptr = sema.resolveInst(bin_inst.lhs);1690 const ptr = sema.resolveInst(bin_inst.lhs);
1660 const value = sema.resolveInst(bin_inst.rhs);1691 const operand = sema.resolveInst(bin_inst.rhs);
1692 const operand_ty = sema.typeOf(operand);
1661 const ptr_inst = Air.refToIndex(ptr).?;1693 const ptr_inst = Air.refToIndex(ptr).?;
1662 assert(sema.air_instructions.items(.tag)[ptr_inst] == .constant);1694 assert(sema.air_instructions.items(.tag)[ptr_inst] == .constant);
1663 const air_datas = sema.air_instructions.items(.data);1695 const air_datas = sema.air_instructions.items(.data);
1664 const ptr_val = sema.air_values.items[air_datas[ptr_inst].ty_pl.payload];1696 const ptr_val = sema.air_values.items[air_datas[ptr_inst].ty_pl.payload];
1665 const inferred_alloc = ptr_val.castTag(.inferred_alloc).?;1697
1666 // Add the stored instruction to the set we will use to resolve peer types1698 if (ptr_val.castTag(.inferred_alloc_comptime)) |iac| {
1667 // for the inferred allocation.1699 // There will be only one store_to_inferred_ptr because we are running at comptime.
1668 try inferred_alloc.data.stored_inst_list.append(sema.arena, value);1700 // The alloc will turn into a Decl.
1669 // Create a runtime bitcast instruction with exactly the type the pointer wants.1701 if (try sema.resolveMaybeUndefValAllowVariables(block, src, operand)) |operand_val| {
1670 const ptr_ty = try Module.simplePtrType(sema.arena, sema.typeOf(value), true, .One);1702 if (operand_val.tag() == .variable) {
1671 try sema.requireRuntimeBlock(block, src);1703 return sema.failWithNeededComptime(block, src);
1672 const bitcasted_ptr = try block.addTyOp(.bitcast, ptr_ty, ptr);1704 }
1673 return sema.storePtr(block, src, bitcasted_ptr, value);1705 var anon_decl = try block.startAnonDecl();
1706 defer anon_decl.deinit();
1707 iac.data = try anon_decl.finish(
1708 try operand_ty.copy(anon_decl.arena()),
1709 try operand_val.copy(anon_decl.arena()),
1710 );
1711 return;
1712 } else {
1713 return sema.failWithNeededComptime(block, src);
1714 }
1715 }
1716
1717 if (ptr_val.castTag(.inferred_alloc)) |inferred_alloc| {
1718 // Add the stored instruction to the set we will use to resolve peer types
1719 // for the inferred allocation.
1720 try inferred_alloc.data.stored_inst_list.append(sema.arena, operand);
1721 // Create a runtime bitcast instruction with exactly the type the pointer wants.
1722 const ptr_ty = try Module.simplePtrType(sema.arena, operand_ty, true, .One);
1723 const bitcasted_ptr = try block.addTyOp(.bitcast, ptr_ty, ptr);
1724 return sema.storePtr(block, src, bitcasted_ptr, operand);
1725 }
1726 unreachable;
1674}1727}
16751728
1676fn zirSetEvalBranchQuota(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {1729fn zirSetEvalBranchQuota(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
...@@ -5643,7 +5696,6 @@ fn zirRetCoerce(...@@ -5643,7 +5696,6 @@ fn zirRetCoerce(
5643 sema: *Sema,5696 sema: *Sema,
5644 block: *Scope.Block,5697 block: *Scope.Block,
5645 inst: Zir.Inst.Index,5698 inst: Zir.Inst.Index,
5646 need_coercion: bool,
5647) CompileError!Zir.Inst.Index {5699) CompileError!Zir.Inst.Index {
5648 const tracy = trace(@src());5700 const tracy = trace(@src());
5649 defer tracy.end();5701 defer tracy.end();
...@@ -5652,7 +5704,7 @@ fn zirRetCoerce(...@@ -5652,7 +5704,7 @@ fn zirRetCoerce(
5652 const operand = sema.resolveInst(inst_data.operand);5704 const operand = sema.resolveInst(inst_data.operand);
5653 const src = inst_data.src();5705 const src = inst_data.src();
56545706
5655 return sema.analyzeRet(block, operand, src, need_coercion);5707 return sema.analyzeRet(block, operand, src, true);
5656}5708}
56575709
5658fn zirRetNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {5710fn zirRetNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
...@@ -5673,23 +5725,20 @@ fn analyzeRet(...@@ -5673,23 +5725,20 @@ fn analyzeRet(
5673 src: LazySrcLoc,5725 src: LazySrcLoc,
5674 need_coercion: bool,5726 need_coercion: bool,
5675) CompileError!Zir.Inst.Index {5727) CompileError!Zir.Inst.Index {
5728 const casted_operand = if (!need_coercion) operand else op: {
5729 const func = sema.func.?;
5730 const fn_ty = func.owner_decl.ty;
5731 const fn_ret_ty = fn_ty.fnReturnType();
5732 break :op try sema.coerce(block, fn_ret_ty, operand, src);
5733 };
5676 if (block.inlining) |inlining| {5734 if (block.inlining) |inlining| {
5677 // We are inlining a function call; rewrite the `ret` as a `break`.5735 // We are inlining a function call; rewrite the `ret` as a `break`.
5678 try inlining.merges.results.append(sema.gpa, operand);5736 try inlining.merges.results.append(sema.gpa, casted_operand);
5679 _ = try block.addBr(inlining.merges.block_inst, operand);5737 _ = try block.addBr(inlining.merges.block_inst, casted_operand);
5680 return always_noreturn;5738 return always_noreturn;
5681 }5739 }
56825740
5683 if (need_coercion) {5741 _ = try block.addUnOp(.ret, casted_operand);
5684 if (sema.func) |func| {
5685 const fn_ty = func.owner_decl.ty;
5686 const fn_ret_ty = fn_ty.fnReturnType();
5687 const casted_operand = try sema.coerce(block, fn_ret_ty, operand, src);
5688 _ = try block.addUnOp(.ret, casted_operand);
5689 return always_noreturn;
5690 }
5691 }
5692 _ = try block.addUnOp(.ret, operand);
5693 return always_noreturn;5742 return always_noreturn;
5694}5743}
56955744
...@@ -7603,37 +7652,45 @@ fn storePtr(...@@ -7603,37 +7652,45 @@ fn storePtr(
7603 if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null)7652 if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null)
7604 return;7653 return;
76057654
7606 if (try sema.resolveMaybeUndefVal(block, src, ptr)) |ptr_val| blk: {7655 if (try sema.resolveDefinedValue(block, src, ptr)) |ptr_val| {
7607 const const_val = (try sema.resolveMaybeUndefVal(block, src, value)) orelse7656 if (ptr_val.castTag(.decl_ref_mut)) |decl_ref_mut| {
7608 return sema.mod.fail(&block.base, src, "cannot store runtime value in compile time variable", .{});7657 const const_val = (try sema.resolveMaybeUndefVal(block, src, value)) orelse
76097658 return sema.mod.fail(&block.base, src, "cannot store runtime value in compile time variable", .{});
7610 if (ptr_val.tag() == .int_u64)7659
7611 break :blk; // propogate it down to runtime7660 if (decl_ref_mut.data.runtime_index < block.runtime_index) {
76127661 if (block.runtime_cond) |cond_src| {
7613 const comptime_alloc = ptr_val.castTag(.comptime_alloc).?;7662 const msg = msg: {
7614 if (comptime_alloc.data.runtime_index < block.runtime_index) {7663 const msg = try sema.mod.errMsg(&block.base, src, "store to comptime variable depends on runtime condition", .{});
7615 if (block.runtime_cond) |cond_src| {7664 errdefer msg.destroy(sema.gpa);
7616 const msg = msg: {7665 try sema.mod.errNote(&block.base, cond_src, msg, "runtime condition here", .{});
7617 const msg = try sema.mod.errMsg(&block.base, src, "store to comptime variable depends on runtime condition", .{});7666 break :msg msg;
7618 errdefer msg.destroy(sema.gpa);7667 };
7619 try sema.mod.errNote(&block.base, cond_src, msg, "runtime condition here", .{});7668 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
7620 break :msg msg;7669 }
7621 };7670 if (block.runtime_loop) |loop_src| {
7622 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);7671 const msg = msg: {
7623 }7672 const msg = try sema.mod.errMsg(&block.base, src, "cannot store to comptime variable in non-inline loop", .{});
7624 if (block.runtime_loop) |loop_src| {7673 errdefer msg.destroy(sema.gpa);
7625 const msg = msg: {7674 try sema.mod.errNote(&block.base, loop_src, msg, "non-inline loop here", .{});
7626 const msg = try sema.mod.errMsg(&block.base, src, "cannot store to comptime variable in non-inline loop", .{});7675 break :msg msg;
7627 errdefer msg.destroy(sema.gpa);7676 };
7628 try sema.mod.errNote(&block.base, loop_src, msg, "non-inline loop here", .{});7677 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
7629 break :msg msg;7678 }
7630 };7679 unreachable;
7631 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
7632 }7680 }
7633 unreachable;7681 var new_arena = std.heap.ArenaAllocator.init(sema.gpa);
7682 errdefer new_arena.deinit();
7683 const new_ty = try elem_ty.copy(&new_arena.allocator);
7684 const new_val = try const_val.copy(&new_arena.allocator);
7685 const decl = decl_ref_mut.data.decl;
7686 var old_arena = decl.value_arena.?.promote(sema.gpa);
7687 decl.value_arena = null;
7688 try decl.finalizeNewArena(&new_arena);
7689 decl.ty = new_ty;
7690 decl.val = new_val;
7691 old_arena.deinit();
7692 return;
7634 }7693 }
7635 comptime_alloc.data.val = const_val;
7636 return;
7637 }7694 }
7638 // TODO handle if the element type requires comptime7695 // TODO handle if the element type requires comptime
76397696
src/value.zig+31-14
...@@ -100,11 +100,13 @@ pub const Value = extern union {...@@ -100,11 +100,13 @@ pub const Value = extern union {
100 function,100 function,
101 extern_fn,101 extern_fn,
102 variable,102 variable,
103 /// Represents a comptime variables storage.103 /// Represents a pointer to a Decl.
104 comptime_alloc,
105 /// Represents a pointer to a decl, not the value of the decl.
106 /// When machine codegen backend sees this, it must set the Decl's `alive` field to true.104 /// When machine codegen backend sees this, it must set the Decl's `alive` field to true.
107 decl_ref,105 decl_ref,
106 /// Pointer to a Decl, but allows comptime code to mutate the Decl's Value.
107 /// This Tag will never be seen by machine codegen backends. It is changed into a
108 /// `decl_ref` when a comptime variable goes out of scope.
109 decl_ref_mut,
108 elem_ptr,110 elem_ptr,
109 field_ptr,111 field_ptr,
110 /// A slice of u8 whose memory is managed externally.112 /// A slice of u8 whose memory is managed externally.
...@@ -134,6 +136,9 @@ pub const Value = extern union {...@@ -134,6 +136,9 @@ pub const Value = extern union {
134 /// This is a special value that tracks a set of types that have been stored136 /// This is a special value that tracks a set of types that have been stored
135 /// to an inferred allocation. It does not support any of the normal value queries.137 /// to an inferred allocation. It does not support any of the normal value queries.
136 inferred_alloc,138 inferred_alloc,
139 /// Used to coordinate alloc_inferred, store_to_inferred_ptr, and resolve_inferred_alloc
140 /// instructions for comptime code.
141 inferred_alloc_comptime,
137142
138 pub const last_no_payload_tag = Tag.empty_array;143 pub const last_no_payload_tag = Tag.empty_array;
139 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;144 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
...@@ -213,6 +218,7 @@ pub const Value = extern union {...@@ -213,6 +218,7 @@ pub const Value = extern union {
213218
214 .extern_fn,219 .extern_fn,
215 .decl_ref,220 .decl_ref,
221 .inferred_alloc_comptime,
216 => Payload.Decl,222 => Payload.Decl,
217223
218 .repeated,224 .repeated,
...@@ -235,7 +241,7 @@ pub const Value = extern union {...@@ -235,7 +241,7 @@ pub const Value = extern union {
235 .int_i64 => Payload.I64,241 .int_i64 => Payload.I64,
236 .function => Payload.Function,242 .function => Payload.Function,
237 .variable => Payload.Variable,243 .variable => Payload.Variable,
238 .comptime_alloc => Payload.ComptimeAlloc,244 .decl_ref_mut => Payload.DeclRefMut,
239 .elem_ptr => Payload.ElemPtr,245 .elem_ptr => Payload.ElemPtr,
240 .field_ptr => Payload.FieldPtr,246 .field_ptr => Payload.FieldPtr,
241 .float_16 => Payload.Float_16,247 .float_16 => Payload.Float_16,
...@@ -408,8 +414,8 @@ pub const Value = extern union {...@@ -408,8 +414,8 @@ pub const Value = extern union {
408 .function => return self.copyPayloadShallow(allocator, Payload.Function),414 .function => return self.copyPayloadShallow(allocator, Payload.Function),
409 .extern_fn => return self.copyPayloadShallow(allocator, Payload.Decl),415 .extern_fn => return self.copyPayloadShallow(allocator, Payload.Decl),
410 .variable => return self.copyPayloadShallow(allocator, Payload.Variable),416 .variable => return self.copyPayloadShallow(allocator, Payload.Variable),
411 .comptime_alloc => return self.copyPayloadShallow(allocator, Payload.ComptimeAlloc),
412 .decl_ref => return self.copyPayloadShallow(allocator, Payload.Decl),417 .decl_ref => return self.copyPayloadShallow(allocator, Payload.Decl),
418 .decl_ref_mut => return self.copyPayloadShallow(allocator, Payload.DeclRefMut),
413 .elem_ptr => {419 .elem_ptr => {
414 const payload = self.castTag(.elem_ptr).?;420 const payload = self.castTag(.elem_ptr).?;
415 const new_payload = try allocator.create(Payload.ElemPtr);421 const new_payload = try allocator.create(Payload.ElemPtr);
...@@ -485,6 +491,7 @@ pub const Value = extern union {...@@ -485,6 +491,7 @@ pub const Value = extern union {
485 .@"union" => @panic("TODO can't copy union value without knowing the type"),491 .@"union" => @panic("TODO can't copy union value without knowing the type"),
486492
487 .inferred_alloc => unreachable,493 .inferred_alloc => unreachable,
494 .inferred_alloc_comptime => unreachable,
488 }495 }
489 }496 }
490497
...@@ -592,10 +599,9 @@ pub const Value = extern union {...@@ -592,10 +599,9 @@ pub const Value = extern union {
592 .function => return out_stream.print("(function '{s}')", .{val.castTag(.function).?.data.owner_decl.name}),599 .function => return out_stream.print("(function '{s}')", .{val.castTag(.function).?.data.owner_decl.name}),
593 .extern_fn => return out_stream.writeAll("(extern function)"),600 .extern_fn => return out_stream.writeAll("(extern function)"),
594 .variable => return out_stream.writeAll("(variable)"),601 .variable => return out_stream.writeAll("(variable)"),
595 .comptime_alloc => {602 .decl_ref_mut => {
596 const ref_val = val.castTag(.comptime_alloc).?.data.val;603 const decl = val.castTag(.decl_ref_mut).?.data.decl;
597 try out_stream.writeAll("&");604 return out_stream.print("(decl_ref_mut '{s}')", .{decl.name});
598 val = ref_val;
599 },605 },
600 .decl_ref => return out_stream.writeAll("(decl ref)"),606 .decl_ref => return out_stream.writeAll("(decl ref)"),
601 .elem_ptr => {607 .elem_ptr => {
...@@ -626,6 +632,7 @@ pub const Value = extern union {...@@ -626,6 +632,7 @@ pub const Value = extern union {
626 // TODO to print this it should be error{ Set, Items }!T(val), but we need the type for that632 // TODO to print this it should be error{ Set, Items }!T(val), but we need the type for that
627 .error_union => return out_stream.print("error_union_val({})", .{val.castTag(.error_union).?.data}),633 .error_union => return out_stream.print("error_union_val({})", .{val.castTag(.error_union).?.data}),
628 .inferred_alloc => return out_stream.writeAll("(inferred allocation value)"),634 .inferred_alloc => return out_stream.writeAll("(inferred allocation value)"),
635 .inferred_alloc_comptime => return out_stream.writeAll("(inferred comptime allocation value)"),
629 .eu_payload_ptr => {636 .eu_payload_ptr => {
630 try out_stream.writeAll("(eu_payload_ptr)");637 try out_stream.writeAll("(eu_payload_ptr)");
631 val = val.castTag(.eu_payload_ptr).?.data;638 val = val.castTag(.eu_payload_ptr).?.data;
...@@ -741,8 +748,8 @@ pub const Value = extern union {...@@ -741,8 +748,8 @@ pub const Value = extern union {
741 .function,748 .function,
742 .extern_fn,749 .extern_fn,
743 .variable,750 .variable,
744 .comptime_alloc,
745 .decl_ref,751 .decl_ref,
752 .decl_ref_mut,
746 .elem_ptr,753 .elem_ptr,
747 .field_ptr,754 .field_ptr,
748 .bytes,755 .bytes,
...@@ -761,6 +768,7 @@ pub const Value = extern union {...@@ -761,6 +768,7 @@ pub const Value = extern union {
761 .@"struct",768 .@"struct",
762 .@"union",769 .@"union",
763 .inferred_alloc,770 .inferred_alloc,
771 .inferred_alloc_comptime,
764 .abi_align_default,772 .abi_align_default,
765 .eu_payload_ptr,773 .eu_payload_ptr,
766 => unreachable,774 => unreachable,
...@@ -1234,7 +1242,13 @@ pub const Value = extern union {...@@ -1234,7 +1242,13 @@ pub const Value = extern union {
1234 allocator: *Allocator,1242 allocator: *Allocator,
1235 ) error{ AnalysisFail, OutOfMemory }!?Value {1243 ) error{ AnalysisFail, OutOfMemory }!?Value {
1236 const sub_val: Value = switch (self.tag()) {1244 const sub_val: Value = switch (self.tag()) {
1237 .comptime_alloc => self.castTag(.comptime_alloc).?.data.val,1245 .decl_ref_mut => val: {
1246 // The decl whose value we are obtaining here may be overwritten with
1247 // a different value, which would invalidate this memory. So we must
1248 // copy here.
1249 const val = try self.castTag(.decl_ref_mut).?.data.decl.value();
1250 break :val try val.copy(allocator);
1251 },
1238 .decl_ref => try self.castTag(.decl_ref).?.data.value(),1252 .decl_ref => try self.castTag(.decl_ref).?.data.value(),
1239 .elem_ptr => blk: {1253 .elem_ptr => blk: {
1240 const elem_ptr = self.castTag(.elem_ptr).?.data;1254 const elem_ptr = self.castTag(.elem_ptr).?.data;
...@@ -1351,6 +1365,7 @@ pub const Value = extern union {...@@ -1351,6 +1365,7 @@ pub const Value = extern union {
1351 .undef => unreachable,1365 .undef => unreachable,
1352 .unreachable_value => unreachable,1366 .unreachable_value => unreachable,
1353 .inferred_alloc => unreachable,1367 .inferred_alloc => unreachable,
1368 .inferred_alloc_comptime => unreachable,
1354 .null_value => true,1369 .null_value => true,
13551370
1356 else => false,1371 else => false,
...@@ -1371,6 +1386,7 @@ pub const Value = extern union {...@@ -1371,6 +1386,7 @@ pub const Value = extern union {
1371 .undef => unreachable,1386 .undef => unreachable,
1372 .unreachable_value => unreachable,1387 .unreachable_value => unreachable,
1373 .inferred_alloc => unreachable,1388 .inferred_alloc => unreachable,
1389 .inferred_alloc_comptime => unreachable,
13741390
1375 else => null,1391 else => null,
1376 };1392 };
...@@ -1380,6 +1396,7 @@ pub const Value = extern union {...@@ -1380,6 +1396,7 @@ pub const Value = extern union {
1380 return switch (self.tag()) {1396 return switch (self.tag()) {
1381 .undef => unreachable,1397 .undef => unreachable,
1382 .inferred_alloc => unreachable,1398 .inferred_alloc => unreachable,
1399 .inferred_alloc_comptime => unreachable,
13831400
1384 .float_16,1401 .float_16,
1385 .float_32,1402 .float_32,
...@@ -1443,12 +1460,12 @@ pub const Value = extern union {...@@ -1443,12 +1460,12 @@ pub const Value = extern union {
1443 data: Value,1460 data: Value,
1444 };1461 };
14451462
1446 pub const ComptimeAlloc = struct {1463 pub const DeclRefMut = struct {
1447 pub const base_tag = Tag.comptime_alloc;1464 pub const base_tag = Tag.decl_ref_mut;
14481465
1449 base: Payload = Payload{ .tag = base_tag },1466 base: Payload = Payload{ .tag = base_tag },
1450 data: struct {1467 data: struct {
1451 val: Value,1468 decl: *Module.Decl,
1452 runtime_index: u32,1469 runtime_index: u32,
1453 },1470 },
1454 };1471 };
test/behavior.zig+1
...@@ -3,6 +3,7 @@ const builtin = @import("builtin");...@@ -3,6 +3,7 @@ const builtin = @import("builtin");
3test {3test {
4 // Tests that pass for both.4 // Tests that pass for both.
5 _ = @import("behavior/bool.zig");5 _ = @import("behavior/bool.zig");
6 _ = @import("behavior/basic.zig");
67
7 if (!builtin.zig_is_stage2) {8 if (!builtin.zig_is_stage2) {
8 // Tests that only pass for stage1.9 // Tests that only pass for stage1.
test/behavior/basic.zig created+9
...@@ -0,0 +1,9 @@
1// normal comment
2
3/// this is a documentation comment
4/// doc comment line 2
5fn emptyFunctionWithComments() void {}
6
7test "empty function with comments" {
8 emptyFunctionWithComments();
9}
test/behavior/bool.zig+44
...@@ -33,3 +33,47 @@ test "compile time bool not" {...@@ -33,3 +33,47 @@ test "compile time bool not" {
33 try expect(not_global_f);33 try expect(not_global_f);
34 try expect(!not_global_t);34 try expect(!not_global_t);
35}35}
36
37test "short circuit" {
38 try testShortCircuit(false, true);
39 comptime try testShortCircuit(false, true);
40}
41
42fn testShortCircuit(f: bool, t: bool) !void {
43 var hit_1 = f;
44 var hit_2 = f;
45 var hit_3 = f;
46 var hit_4 = f;
47
48 if (t or x: {
49 try expect(f);
50 break :x f;
51 }) {
52 hit_1 = t;
53 }
54 if (f or x: {
55 hit_2 = t;
56 break :x f;
57 }) {
58 try expect(f);
59 }
60
61 if (t and x: {
62 hit_3 = t;
63 break :x f;
64 }) {
65 try expect(f);
66 }
67 if (f and x: {
68 try expect(f);
69 break :x f;
70 }) {
71 try expect(f);
72 } else {
73 hit_4 = t;
74 }
75 try expect(hit_1);
76 try expect(hit_2);
77 try expect(hit_3);
78 try expect(hit_4);
79}
test/behavior/misc.zig-64
...@@ -5,70 +5,6 @@ const expectEqualStrings = std.testing.expectEqualStrings;...@@ -5,70 +5,6 @@ const expectEqualStrings = std.testing.expectEqualStrings;
5const mem = std.mem;5const mem = std.mem;
6const builtin = @import("builtin");6const builtin = @import("builtin");
77
8// normal comment
9
10/// this is a documentation comment
11/// doc comment line 2
12fn emptyFunctionWithComments() void {}
13
14test "empty function with comments" {
15 emptyFunctionWithComments();
16}
17
18comptime {
19 @export(disabledExternFn, .{ .name = "disabledExternFn", .linkage = .Internal });
20}
21
22fn disabledExternFn() callconv(.C) void {}
23
24test "call disabled extern fn" {
25 disabledExternFn();
26}
27
28test "short circuit" {
29 try testShortCircuit(false, true);
30 comptime try testShortCircuit(false, true);
31}
32
33fn testShortCircuit(f: bool, t: bool) !void {
34 var hit_1 = f;
35 var hit_2 = f;
36 var hit_3 = f;
37 var hit_4 = f;
38
39 if (t or x: {
40 try expect(f);
41 break :x f;
42 }) {
43 hit_1 = t;
44 }
45 if (f or x: {
46 hit_2 = t;
47 break :x f;
48 }) {
49 try expect(f);
50 }
51
52 if (t and x: {
53 hit_3 = t;
54 break :x f;
55 }) {
56 try expect(f);
57 }
58 if (f and x: {
59 try expect(f);
60 break :x f;
61 }) {
62 try expect(f);
63 } else {
64 hit_4 = t;
65 }
66 try expect(hit_1);
67 try expect(hit_2);
68 try expect(hit_3);
69 try expect(hit_4);
70}
71
72test "truncate" {8test "truncate" {
73 try expect(testTruncate(0x10fd) == 0xfd);9 try expect(testTruncate(0x10fd) == 0xfd);
74}10}