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(
154154 // We use a while(true) loop here to avoid a redundant way of breaking out of
155155 // the loop. The only way to break out of the loop is with a `noreturn`
156156 // 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
160157 var i: usize = 0;
161158 while (true) {
162159 const inst = body[i];
......@@ -391,7 +388,7 @@ pub fn analyzeBody(
391388 .condbr => return sema.zirCondbr(block, inst),
392389 .@"break" => return sema.zirBreak(block, inst),
393390 .compile_error => return sema.zirCompileError(block, inst),
394 .ret_coerce => return sema.zirRetCoerce(block, inst, true),
391 .ret_coerce => return sema.zirRetCoerce(block, inst),
395392 .ret_node => return sema.zirRetNode(block, inst),
396393 .ret_err_value => return sema.zirRetErrValue(block, inst),
397394 .@"unreachable" => return sema.zirUnreachable(block, inst),
......@@ -1396,14 +1393,19 @@ fn zirAllocComptime(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Comp
13961393 const var_type = try sema.resolveType(block, ty_src, inst_data.operand);
13971394 const ptr_type = try Module.simplePtrType(sema.arena, var_type, true, .One);
13981395
1399 const val_payload = try sema.arena.create(Value.Payload.ComptimeAlloc);
1400 val_payload.* = .{
1401 .data = .{
1402 .runtime_index = block.runtime_index,
1403 .val = undefined, // astgen guarantees there will be a store before the first load
1404 },
1405 };
1406 return sema.addConstant(ptr_type, Value.initPayload(&val_payload.base));
1396 var anon_decl = try block.startAnonDecl();
1397 defer anon_decl.deinit();
1398 const decl = try anon_decl.finish(
1399 try var_type.copy(anon_decl.arena()),
1400 // AstGen guarantees there will be a store before the first load, so we put a value
1401 // here indicating there is no valid value.
1402 Value.initTag(.unreachable_value),
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 }));
14071409}
14081410
14091411fn zirAllocInferredComptime(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -1450,16 +1452,23 @@ fn zirAllocInferred(
14501452
14511453 const src_node = sema.code.instructions.items(.data)[inst].node;
14521454 const src: LazySrcLoc = .{ .node_offset = src_node };
1455 sema.src = src;
14531456
1454 const val_payload = try sema.arena.create(Value.Payload.InferredAlloc);
1455 val_payload.* = .{
1456 .data = .{},
1457 };
1458 // `Module.constInst` does not add the instruction to the block because it is
1457 if (block.is_comptime) {
1458 return sema.addConstant(
1459 inferred_alloc_ty,
1460 try Value.Tag.inferred_alloc_comptime.create(sema.arena, undefined),
1461 );
1462 }
1463
1464 // `Sema.addConstant` does not add the instruction to the block because it is
14591465 // not needed in the case of constant values. However here, we plan to "downgrade"
14601466 // to a normal instruction when we hit `resolve_inferred_alloc`. So we append
14611467 // 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 );
14631472 try sema.requireFunctionBlock(block, src);
14641473 try block.instructions.append(sema.gpa, Air.refToIndex(result).?);
14651474 return result;
......@@ -1475,25 +1484,47 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde
14751484 const ptr_inst = Air.refToIndex(ptr).?;
14761485 assert(sema.air_instructions.items(.tag)[ptr_inst] == .constant);
14771486 const air_datas = sema.air_instructions.items(.data);
1478 const ptr_val = sema.air_values.items[air_datas[ptr_inst].ty_pl.payload];
1479 const inferred_alloc = ptr_val.castTag(.inferred_alloc).?;
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);
1487 const value_index = air_datas[ptr_inst].ty_pl.payload;
1488 const ptr_val = sema.air_values.items[value_index];
14821489 const var_is_mut = switch (sema.typeOf(ptr).tag()) {
14831490 .inferred_alloc_const => false,
14841491 .inferred_alloc_mut => true,
14851492 else => unreachable,
14861493 };
1487 if (var_is_mut) {
1488 try sema.validateVarType(block, ty_src, final_elem_ty);
1494
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;
14891512 }
1490 const final_ptr_ty = try Module.simplePtrType(sema.arena, final_elem_ty, true, .One);
14911513
1492 // Change it to a normal alloc.
1493 sema.air_instructions.set(ptr_inst, .{
1494 .tag = .alloc,
1495 .data = .{ .ty = final_ptr_ty },
1496 });
1514 if (ptr_val.castTag(.inferred_alloc)) |inferred_alloc| {
1515 const peer_inst_list = inferred_alloc.data.stored_inst_list.items;
1516 const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list);
1517 if (var_is_mut) {
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 }
14971528}
14981529
14991530fn 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)
16541685 const tracy = trace(@src());
16551686 defer tracy.end();
16561687
1657 const src: LazySrcLoc = .unneeded;
1688 const src: LazySrcLoc = sema.src;
16581689 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
16591690 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);
16611693 const ptr_inst = Air.refToIndex(ptr).?;
16621694 assert(sema.air_instructions.items(.tag)[ptr_inst] == .constant);
16631695 const air_datas = sema.air_instructions.items(.data);
16641696 const ptr_val = sema.air_values.items[air_datas[ptr_inst].ty_pl.payload];
1665 const inferred_alloc = ptr_val.castTag(.inferred_alloc).?;
1666 // Add the stored instruction to the set we will use to resolve peer types
1667 // for the inferred allocation.
1668 try inferred_alloc.data.stored_inst_list.append(sema.arena, value);
1669 // Create a runtime bitcast instruction with exactly the type the pointer wants.
1670 const ptr_ty = try Module.simplePtrType(sema.arena, sema.typeOf(value), true, .One);
1671 try sema.requireRuntimeBlock(block, src);
1672 const bitcasted_ptr = try block.addTyOp(.bitcast, ptr_ty, ptr);
1673 return sema.storePtr(block, src, bitcasted_ptr, value);
1697
1698 if (ptr_val.castTag(.inferred_alloc_comptime)) |iac| {
1699 // There will be only one store_to_inferred_ptr because we are running at comptime.
1700 // The alloc will turn into a Decl.
1701 if (try sema.resolveMaybeUndefValAllowVariables(block, src, operand)) |operand_val| {
1702 if (operand_val.tag() == .variable) {
1703 return sema.failWithNeededComptime(block, src);
1704 }
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;
16741727}
16751728
16761729fn zirSetEvalBranchQuota(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
......@@ -5643,7 +5696,6 @@ fn zirRetCoerce(
56435696 sema: *Sema,
56445697 block: *Scope.Block,
56455698 inst: Zir.Inst.Index,
5646 need_coercion: bool,
56475699) CompileError!Zir.Inst.Index {
56485700 const tracy = trace(@src());
56495701 defer tracy.end();
......@@ -5652,7 +5704,7 @@ fn zirRetCoerce(
56525704 const operand = sema.resolveInst(inst_data.operand);
56535705 const src = inst_data.src();
56545706
5655 return sema.analyzeRet(block, operand, src, need_coercion);
5707 return sema.analyzeRet(block, operand, src, true);
56565708}
56575709
56585710fn zirRetNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
......@@ -5673,23 +5725,20 @@ fn analyzeRet(
56735725 src: LazySrcLoc,
56745726 need_coercion: bool,
56755727) 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 };
56765734 if (block.inlining) |inlining| {
56775735 // We are inlining a function call; rewrite the `ret` as a `break`.
5678 try inlining.merges.results.append(sema.gpa, operand);
5679 _ = try block.addBr(inlining.merges.block_inst, operand);
5736 try inlining.merges.results.append(sema.gpa, casted_operand);
5737 _ = try block.addBr(inlining.merges.block_inst, casted_operand);
56805738 return always_noreturn;
56815739 }
56825740
5683 if (need_coercion) {
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);
5741 _ = try block.addUnOp(.ret, casted_operand);
56935742 return always_noreturn;
56945743}
56955744
......@@ -7603,37 +7652,45 @@ fn storePtr(
76037652 if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null)
76047653 return;
76057654
7606 if (try sema.resolveMaybeUndefVal(block, src, ptr)) |ptr_val| blk: {
7607 const const_val = (try sema.resolveMaybeUndefVal(block, src, value)) orelse
7608 return sema.mod.fail(&block.base, src, "cannot store runtime value in compile time variable", .{});
7609
7610 if (ptr_val.tag() == .int_u64)
7611 break :blk; // propogate it down to runtime
7612
7613 const comptime_alloc = ptr_val.castTag(.comptime_alloc).?;
7614 if (comptime_alloc.data.runtime_index < block.runtime_index) {
7615 if (block.runtime_cond) |cond_src| {
7616 const msg = msg: {
7617 const msg = try sema.mod.errMsg(&block.base, src, "store to comptime variable depends on runtime condition", .{});
7618 errdefer msg.destroy(sema.gpa);
7619 try sema.mod.errNote(&block.base, cond_src, msg, "runtime condition here", .{});
7620 break :msg msg;
7621 };
7622 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
7623 }
7624 if (block.runtime_loop) |loop_src| {
7625 const msg = msg: {
7626 const msg = try sema.mod.errMsg(&block.base, src, "cannot store to comptime variable in non-inline loop", .{});
7627 errdefer msg.destroy(sema.gpa);
7628 try sema.mod.errNote(&block.base, loop_src, msg, "non-inline loop here", .{});
7629 break :msg msg;
7630 };
7631 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
7655 if (try sema.resolveDefinedValue(block, src, ptr)) |ptr_val| {
7656 if (ptr_val.castTag(.decl_ref_mut)) |decl_ref_mut| {
7657 const const_val = (try sema.resolveMaybeUndefVal(block, src, value)) orelse
7658 return sema.mod.fail(&block.base, src, "cannot store runtime value in compile time variable", .{});
7659
7660 if (decl_ref_mut.data.runtime_index < block.runtime_index) {
7661 if (block.runtime_cond) |cond_src| {
7662 const msg = msg: {
7663 const msg = try sema.mod.errMsg(&block.base, src, "store to comptime variable depends on runtime condition", .{});
7664 errdefer msg.destroy(sema.gpa);
7665 try sema.mod.errNote(&block.base, cond_src, msg, "runtime condition here", .{});
7666 break :msg msg;
7667 };
7668 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
7669 }
7670 if (block.runtime_loop) |loop_src| {
7671 const msg = msg: {
7672 const msg = try sema.mod.errMsg(&block.base, src, "cannot store to comptime variable in non-inline loop", .{});
7673 errdefer msg.destroy(sema.gpa);
7674 try sema.mod.errNote(&block.base, loop_src, msg, "non-inline loop here", .{});
7675 break :msg msg;
7676 };
7677 return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
7678 }
7679 unreachable;
76327680 }
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;
76347693 }
7635 comptime_alloc.data.val = const_val;
7636 return;
76377694 }
76387695 // TODO handle if the element type requires comptime
76397696
src/value.zig+31-14
......@@ -100,11 +100,13 @@ pub const Value = extern union {
100100 function,
101101 extern_fn,
102102 variable,
103 /// Represents a comptime variables storage.
104 comptime_alloc,
105 /// Represents a pointer to a decl, not the value of the decl.
103 /// Represents a pointer to a Decl.
106104 /// When machine codegen backend sees this, it must set the Decl's `alive` field to true.
107105 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,
108110 elem_ptr,
109111 field_ptr,
110112 /// A slice of u8 whose memory is managed externally.
......@@ -134,6 +136,9 @@ pub const Value = extern union {
134136 /// This is a special value that tracks a set of types that have been stored
135137 /// to an inferred allocation. It does not support any of the normal value queries.
136138 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
138143 pub const last_no_payload_tag = Tag.empty_array;
139144 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
......@@ -213,6 +218,7 @@ pub const Value = extern union {
213218
214219 .extern_fn,
215220 .decl_ref,
221 .inferred_alloc_comptime,
216222 => Payload.Decl,
217223
218224 .repeated,
......@@ -235,7 +241,7 @@ pub const Value = extern union {
235241 .int_i64 => Payload.I64,
236242 .function => Payload.Function,
237243 .variable => Payload.Variable,
238 .comptime_alloc => Payload.ComptimeAlloc,
244 .decl_ref_mut => Payload.DeclRefMut,
239245 .elem_ptr => Payload.ElemPtr,
240246 .field_ptr => Payload.FieldPtr,
241247 .float_16 => Payload.Float_16,
......@@ -408,8 +414,8 @@ pub const Value = extern union {
408414 .function => return self.copyPayloadShallow(allocator, Payload.Function),
409415 .extern_fn => return self.copyPayloadShallow(allocator, Payload.Decl),
410416 .variable => return self.copyPayloadShallow(allocator, Payload.Variable),
411 .comptime_alloc => return self.copyPayloadShallow(allocator, Payload.ComptimeAlloc),
412417 .decl_ref => return self.copyPayloadShallow(allocator, Payload.Decl),
418 .decl_ref_mut => return self.copyPayloadShallow(allocator, Payload.DeclRefMut),
413419 .elem_ptr => {
414420 const payload = self.castTag(.elem_ptr).?;
415421 const new_payload = try allocator.create(Payload.ElemPtr);
......@@ -485,6 +491,7 @@ pub const Value = extern union {
485491 .@"union" => @panic("TODO can't copy union value without knowing the type"),
486492
487493 .inferred_alloc => unreachable,
494 .inferred_alloc_comptime => unreachable,
488495 }
489496 }
490497
......@@ -592,10 +599,9 @@ pub const Value = extern union {
592599 .function => return out_stream.print("(function '{s}')", .{val.castTag(.function).?.data.owner_decl.name}),
593600 .extern_fn => return out_stream.writeAll("(extern function)"),
594601 .variable => return out_stream.writeAll("(variable)"),
595 .comptime_alloc => {
596 const ref_val = val.castTag(.comptime_alloc).?.data.val;
597 try out_stream.writeAll("&");
598 val = ref_val;
602 .decl_ref_mut => {
603 const decl = val.castTag(.decl_ref_mut).?.data.decl;
604 return out_stream.print("(decl_ref_mut '{s}')", .{decl.name});
599605 },
600606 .decl_ref => return out_stream.writeAll("(decl ref)"),
601607 .elem_ptr => {
......@@ -626,6 +632,7 @@ pub const Value = extern union {
626632 // TODO to print this it should be error{ Set, Items }!T(val), but we need the type for that
627633 .error_union => return out_stream.print("error_union_val({})", .{val.castTag(.error_union).?.data}),
628634 .inferred_alloc => return out_stream.writeAll("(inferred allocation value)"),
635 .inferred_alloc_comptime => return out_stream.writeAll("(inferred comptime allocation value)"),
629636 .eu_payload_ptr => {
630637 try out_stream.writeAll("(eu_payload_ptr)");
631638 val = val.castTag(.eu_payload_ptr).?.data;
......@@ -741,8 +748,8 @@ pub const Value = extern union {
741748 .function,
742749 .extern_fn,
743750 .variable,
744 .comptime_alloc,
745751 .decl_ref,
752 .decl_ref_mut,
746753 .elem_ptr,
747754 .field_ptr,
748755 .bytes,
......@@ -761,6 +768,7 @@ pub const Value = extern union {
761768 .@"struct",
762769 .@"union",
763770 .inferred_alloc,
771 .inferred_alloc_comptime,
764772 .abi_align_default,
765773 .eu_payload_ptr,
766774 => unreachable,
......@@ -1234,7 +1242,13 @@ pub const Value = extern union {
12341242 allocator: *Allocator,
12351243 ) error{ AnalysisFail, OutOfMemory }!?Value {
12361244 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 },
12381252 .decl_ref => try self.castTag(.decl_ref).?.data.value(),
12391253 .elem_ptr => blk: {
12401254 const elem_ptr = self.castTag(.elem_ptr).?.data;
......@@ -1351,6 +1365,7 @@ pub const Value = extern union {
13511365 .undef => unreachable,
13521366 .unreachable_value => unreachable,
13531367 .inferred_alloc => unreachable,
1368 .inferred_alloc_comptime => unreachable,
13541369 .null_value => true,
13551370
13561371 else => false,
......@@ -1371,6 +1386,7 @@ pub const Value = extern union {
13711386 .undef => unreachable,
13721387 .unreachable_value => unreachable,
13731388 .inferred_alloc => unreachable,
1389 .inferred_alloc_comptime => unreachable,
13741390
13751391 else => null,
13761392 };
......@@ -1380,6 +1396,7 @@ pub const Value = extern union {
13801396 return switch (self.tag()) {
13811397 .undef => unreachable,
13821398 .inferred_alloc => unreachable,
1399 .inferred_alloc_comptime => unreachable,
13831400
13841401 .float_16,
13851402 .float_32,
......@@ -1443,12 +1460,12 @@ pub const Value = extern union {
14431460 data: Value,
14441461 };
14451462
1446 pub const ComptimeAlloc = struct {
1447 pub const base_tag = Tag.comptime_alloc;
1463 pub const DeclRefMut = struct {
1464 pub const base_tag = Tag.decl_ref_mut;
14481465
14491466 base: Payload = Payload{ .tag = base_tag },
14501467 data: struct {
1451 val: Value,
1468 decl: *Module.Decl,
14521469 runtime_index: u32,
14531470 },
14541471 };
test/behavior.zig+1
......@@ -3,6 +3,7 @@ const builtin = @import("builtin");
33test {
44 // Tests that pass for both.
55 _ = @import("behavior/bool.zig");
6 _ = @import("behavior/basic.zig");
67
78 if (!builtin.zig_is_stage2) {
89 // 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" {
3333 try expect(not_global_f);
3434 try expect(!not_global_t);
3535}
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;
55const mem = std.mem;
66const 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
728test "truncate" {
739 try expect(testTruncate(0x10fd) == 0xfd);
7410}