mirror of
https://github.com/ziglang/zig.git
synced 2025-12-30 10:03:21 +00:00
Merge pull request #9030 from Vexu/stage2
Stage2: implement comptime variables
This commit is contained in:
commit
ccfa168284
@ -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,
|
||||
|
||||
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
89
src/Sema.zig
89
src/Sema.zig
@ -147,7 +147,7 @@ pub fn analyzeBody(
|
||||
// directly jump to the next one, rather than detouring through the loop
|
||||
// continue expression. Related: https://github.com/ziglang/zig/issues/8220
|
||||
var i: usize = 0;
|
||||
while (true) : (i += 1) {
|
||||
while (true) {
|
||||
const inst = body[i];
|
||||
const air_inst = switch (tags[inst]) {
|
||||
// zig fmt: off
|
||||
@ -394,78 +394,97 @@ pub fn analyzeBody(
|
||||
// putting them into the map.
|
||||
.breakpoint => {
|
||||
try sema.zirBreakpoint(block, inst);
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
.fence => {
|
||||
try sema.zirFence(block, inst);
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
.dbg_stmt => {
|
||||
try sema.zirDbgStmt(block, inst);
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
.ensure_err_payload_void => {
|
||||
try sema.zirEnsureErrPayloadVoid(block, inst);
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
.ensure_result_non_error => {
|
||||
try sema.zirEnsureResultNonError(block, inst);
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
.ensure_result_used => {
|
||||
try sema.zirEnsureResultUsed(block, inst);
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
.set_eval_branch_quota => {
|
||||
try sema.zirSetEvalBranchQuota(block, inst);
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
.store => {
|
||||
try sema.zirStore(block, inst);
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
.store_node => {
|
||||
try sema.zirStoreNode(block, inst);
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
.store_to_block_ptr => {
|
||||
try sema.zirStoreToBlockPtr(block, inst);
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
.store_to_inferred_ptr => {
|
||||
try sema.zirStoreToInferredPtr(block, inst);
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
.resolve_inferred_alloc => {
|
||||
try sema.zirResolveInferredAlloc(block, inst);
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
.validate_struct_init_ptr => {
|
||||
try sema.zirValidateStructInitPtr(block, inst);
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
.validate_array_init_ptr => {
|
||||
try sema.zirValidateArrayInitPtr(block, inst);
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
.@"export" => {
|
||||
try sema.zirExport(block, inst);
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
.set_align_stack => {
|
||||
try sema.zirSetAlignStack(block, inst);
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
.set_cold => {
|
||||
try sema.zirSetAlignStack(block, inst);
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
.set_float_mode => {
|
||||
try sema.zirSetFloatMode(block, inst);
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
.set_runtime_safety => {
|
||||
try sema.zirSetRuntimeSafety(block, inst);
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
|
||||
@ -509,7 +528,8 @@ 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);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1238,9 +1258,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 +1779,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 +4106,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 +4627,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 +5193,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 +5304,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 +6802,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);
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -1420,4 +1420,107 @@ 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;
|
||||
\\}
|
||||
\\pub fn main() void {}
|
||||
, &.{":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;
|
||||
\\}
|
||||
, "");
|
||||
|
||||
case.addCompareOutput(
|
||||
\\pub fn main() void {
|
||||
\\ comptime var i: u64 = 2;
|
||||
\\ inline while (i < 6) : (i+=1) {
|
||||
\\ print(i);
|
||||
\\ }
|
||||
\\}
|
||||
\\fn print(len: usize) void {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (1),
|
||||
\\ [arg1] "{rdi}" (1),
|
||||
\\ [arg2] "{rsi}" (@ptrToInt("Hello")),
|
||||
\\ [arg3] "{rdx}" (len)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ return;
|
||||
\\}
|
||||
, "HeHelHellHello");
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user