Commit 7efd7bc3b8
Changed files (5)
test
stage2
src/AstGen.zig
@@ -2357,7 +2357,7 @@ fn varDecl(
return &sub_scope.base;
},
.keyword_var => {
- const is_comptime = var_decl.comptime_token != null;
+ const is_comptime = var_decl.comptime_token != null or gz.force_comptime;
var resolve_inferred_alloc: Zir.Inst.Ref = .none;
const var_data: struct {
result_loc: ResultLoc,
src/Module.zig
@@ -1139,6 +1139,13 @@ pub const Scope = struct {
instructions: ArrayListUnmanaged(*ir.Inst),
label: ?*Label = null,
inlining: ?*Inlining,
+ /// If runtime_index is not 0 then one of these is guaranteed to be non null.
+ runtime_cond: ?LazySrcLoc = null,
+ runtime_loop: ?LazySrcLoc = null,
+ /// Non zero if a non-inline loop or a runtime conditional have been encountered.
+ /// Stores to to comptime variables are only allowed when var.runtime_index <= runtime_index.
+ runtime_index: u32 = 0,
+
is_comptime: bool,
/// This `Block` maps a block ZIR instruction to the corresponding
@@ -1182,6 +1189,9 @@ pub const Scope = struct {
.label = null,
.inlining = parent.inlining,
.is_comptime = parent.is_comptime,
+ .runtime_cond = parent.runtime_cond,
+ .runtime_loop = parent.runtime_loop,
+ .runtime_index = parent.runtime_index,
};
}
src/Sema.zig
@@ -509,7 +509,7 @@ pub fn analyzeBody(
};
if (air_inst.ty.isNoReturn())
return always_noreturn;
- try map.putNoClobber(sema.gpa, inst, air_inst);
+ try map.put(sema.gpa, inst, air_inst);
}
}
@@ -1238,9 +1238,26 @@ fn zirAllocExtended(
}
fn zirAllocComptime(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
+ const tracy = trace(@src());
+ defer tracy.end();
+
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
- return sema.mod.fail(&block.base, src, "TODO implement Sema.zirAllocComptime", .{});
+ const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
+ const var_type = try sema.resolveType(block, ty_src, inst_data.operand);
+ const ptr_type = try sema.mod.simplePtrType(sema.arena, var_type, true, .One);
+
+ const val_payload = try sema.arena.create(Value.Payload.ComptimeAlloc);
+ val_payload.* = .{
+ .data = .{
+ .runtime_index = block.runtime_index,
+ .val = undefined, // astgen guarantees there will be a store before the first load
+ },
+ };
+ return sema.mod.constInst(sema.arena, src, .{
+ .ty = ptr_type,
+ .val = Value.initPayload(&val_payload.base),
+ });
}
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
};
var child_block = parent_block.makeSubBlock();
child_block.label = &label;
+ child_block.runtime_cond = null;
+ child_block.runtime_loop = src;
+ child_block.runtime_index += 1;
const merges = &child_block.label.?.merges;
defer child_block.instructions.deinit(sema.gpa);
@@ -4066,6 +4086,9 @@ fn analyzeSwitch(
const cases = try sema.arena.alloc(Inst.SwitchBr.Case, scalar_cases_len);
var case_block = child_block.makeSubBlock();
+ case_block.runtime_loop = null;
+ case_block.runtime_cond = operand.src;
+ case_block.runtime_index += 1;
defer case_block.instructions.deinit(gpa);
var extra_index: usize = special.end;
@@ -4584,14 +4607,14 @@ fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerEr
const tag_override = block.sema.code.instructions.items(.tag)[inst];
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
- const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
+ sema.src = .{ .node_offset_bin_op = inst_data.src_node };
const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
const lhs = try sema.resolveInst(extra.lhs);
const rhs = try sema.resolveInst(extra.rhs);
- return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src);
+ return sema.analyzeArithmetic(block, tag_override, lhs, rhs, sema.src, lhs_src, rhs_src);
}
fn zirOverflowArithmetic(
@@ -5150,6 +5173,9 @@ fn zirBoolBr(
};
var child_block = parent_block.makeSubBlock();
+ child_block.runtime_loop = null;
+ child_block.runtime_cond = lhs.src;
+ child_block.runtime_index += 1;
defer child_block.instructions.deinit(sema.gpa);
var then_block = child_block.makeSubBlock();
@@ -5258,6 +5284,9 @@ fn zirCondbr(
}
var sub_block = parent_block.makeSubBlock();
+ sub_block.runtime_loop = null;
+ sub_block.runtime_cond = cond.src;
+ sub_block.runtime_index += 1;
defer sub_block.instructions.deinit(sema.gpa);
_ = try sema.analyzeBody(&sub_block, then_body);
@@ -6753,7 +6782,35 @@ fn storePtr(
if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null)
return;
- // TODO handle comptime pointer writes
+ if (try sema.resolvePossiblyUndefinedValue(block, src, ptr)) |ptr_val| {
+ const const_val = (try sema.resolvePossiblyUndefinedValue(block, src, value)) orelse
+ return sema.mod.fail(&block.base, src, "cannot store runtime value in compile time variable", .{});
+
+ const comptime_alloc = ptr_val.castTag(.comptime_alloc).?;
+ if (comptime_alloc.data.runtime_index < block.runtime_index) {
+ if (block.runtime_cond) |cond_src| {
+ const msg = msg: {
+ const msg = try sema.mod.errMsg(&block.base, src, "store to comptime variable depends on runtime condition", .{});
+ errdefer msg.destroy(sema.gpa);
+ try sema.mod.errNote(&block.base, cond_src, msg, "runtime condition here", .{});
+ break :msg msg;
+ };
+ return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
+ }
+ if (block.runtime_loop) |loop_src| {
+ const msg = msg: {
+ const msg = try sema.mod.errMsg(&block.base, src, "cannot store to comptime variable in non-inline loop", .{});
+ errdefer msg.destroy(sema.gpa);
+ try sema.mod.errNote(&block.base, loop_src, msg, "non-inline loop here", .{});
+ break :msg msg;
+ };
+ return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
+ }
+ unreachable;
+ }
+ comptime_alloc.data.val = const_val;
+ return;
+ }
// TODO handle if the element type requires comptime
try sema.requireRuntimeBlock(block, src);
src/value.zig
@@ -101,6 +101,8 @@ pub const Value = extern union {
variable,
/// Represents a pointer to another immutable value.
ref_val,
+ /// Represents a comptime variables storage.
+ comptime_alloc,
/// Represents a pointer to a decl, not the value of the decl.
decl_ref,
elem_ptr,
@@ -223,6 +225,7 @@ pub const Value = extern union {
.int_i64 => Payload.I64,
.function => Payload.Function,
.variable => Payload.Variable,
+ .comptime_alloc => Payload.ComptimeAlloc,
.elem_ptr => Payload.ElemPtr,
.field_ptr => Payload.FieldPtr,
.float_16 => Payload.Float_16,
@@ -403,6 +406,7 @@ pub const Value = extern union {
};
return Value{ .ptr_otherwise = &new_payload.base };
},
+ .comptime_alloc => return self.copyPayloadShallow(allocator, Payload.ComptimeAlloc),
.decl_ref => return self.copyPayloadShallow(allocator, Payload.Decl),
.elem_ptr => {
const payload = self.castTag(.elem_ptr).?;
@@ -577,6 +581,11 @@ pub const Value = extern union {
try out_stream.writeAll("&const ");
val = ref_val;
},
+ .comptime_alloc => {
+ const ref_val = val.castTag(.comptime_alloc).?.data.val;
+ try out_stream.writeAll("&");
+ val = ref_val;
+ },
.decl_ref => return out_stream.writeAll("(decl ref)"),
.elem_ptr => {
const elem_ptr = val.castTag(.elem_ptr).?.data;
@@ -713,6 +722,7 @@ pub const Value = extern union {
.extern_fn,
.variable,
.ref_val,
+ .comptime_alloc,
.decl_ref,
.elem_ptr,
.field_ptr,
@@ -1186,6 +1196,10 @@ pub const Value = extern union {
const payload = self.castTag(.ref_val).?;
std.hash.autoHash(&hasher, payload.data.hash());
},
+ .comptime_alloc => {
+ const payload = self.castTag(.comptime_alloc).?;
+ std.hash.autoHash(&hasher, payload.data.val.hash());
+ },
.int_big_positive, .int_big_negative => {
var space: BigIntSpace = undefined;
const big = self.toBigInt(&space);
@@ -1277,6 +1291,7 @@ pub const Value = extern union {
/// Returns error.AnalysisFail if the pointer points to a Decl that failed semantic analysis.
pub fn pointerDeref(self: Value, allocator: *Allocator) error{ AnalysisFail, OutOfMemory }!Value {
return switch (self.tag()) {
+ .comptime_alloc => self.castTag(.comptime_alloc).?.data.val,
.ref_val => self.castTag(.ref_val).?.data,
.decl_ref => self.castTag(.decl_ref).?.data.value(),
.elem_ptr => {
@@ -1462,6 +1477,7 @@ pub const Value = extern union {
.int_big_positive,
.int_big_negative,
.ref_val,
+ .comptime_alloc,
.decl_ref,
.elem_ptr,
.field_ptr,
@@ -1542,6 +1558,16 @@ pub const Value = extern union {
data: Value,
};
+ pub const ComptimeAlloc = struct {
+ pub const base_tag = Tag.comptime_alloc;
+
+ base: Payload = Payload{ .tag = base_tag },
+ data: struct {
+ val: Value,
+ runtime_index: u32,
+ },
+ };
+
pub const ElemPtr = struct {
pub const base_tag = Tag.elem_ptr;
test/stage2/test.zig
@@ -1420,4 +1420,86 @@ pub fn addCases(ctx: *TestContext) !void {
\\}
, &[_][]const u8{":4:27: error: expected type, found comptime_int"});
}
+ {
+ var case = ctx.exe("comptime var", linux_x64);
+
+ case.addError(
+ \\pub fn main() void {
+ \\ var a: u32 = 0;
+ \\ comptime var b: u32 = 0;
+ \\ if (a == 0) b = 3;
+ \\}
+ , &.{
+ ":4:21: error: store to comptime variable depends on runtime condition",
+ ":4:11: note: runtime condition here",
+ });
+
+ case.addError(
+ \\pub fn main() void {
+ \\ var a: u32 = 0;
+ \\ comptime var b: u32 = 0;
+ \\ switch (a) {
+ \\ 0 => {},
+ \\ else => b = 3,
+ \\ }
+ \\}
+ , &.{
+ ":6:21: error: store to comptime variable depends on runtime condition",
+ ":4:13: note: runtime condition here",
+ });
+
+ case.addCompareOutput(
+ \\pub fn main() void {
+ \\ comptime var len: u32 = 5;
+ \\ print(len);
+ \\ len += 9;
+ \\ print(len);
+ \\}
+ \\
+ \\fn print(len: usize) void {
+ \\ asm volatile ("syscall"
+ \\ :
+ \\ : [number] "{rax}" (1),
+ \\ [arg1] "{rdi}" (1),
+ \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")),
+ \\ [arg3] "{rdx}" (len)
+ \\ : "rcx", "r11", "memory"
+ \\ );
+ \\ return;
+ \\}
+ , "HelloHello, World!\n");
+
+ case.addError(
+ \\comptime {
+ \\ var x: i32 = 1;
+ \\ x += 1;
+ \\ if (x != 1) unreachable;
+ \\}
+ , &.{":4:17: error: unable to resolve comptime value"});
+
+ case.addError(
+ \\pub fn main() void {
+ \\ comptime var i: u64 = 0;
+ \\ while (i < 5) : (i += 1) {}
+ \\}
+ , &.{
+ ":3:24: error: cannot store to comptime variable in non-inline loop",
+ ":3:5: note: non-inline loop here",
+ });
+
+ case.addCompareOutput(
+ \\pub fn main() void {
+ \\ var a: u32 = 0;
+ \\ if (a == 0) {
+ \\ comptime var b: u32 = 0;
+ \\ b = 1;
+ \\ }
+ \\}
+ \\comptime {
+ \\ var x: i32 = 1;
+ \\ x += 1;
+ \\ if (x != 2) unreachable;
+ \\}
+ , "");
+ }
}