Merge pull request #6225 from ziglang/stage2-comptime
stage2: introduce the ability for Scope.Block to be comptime
This commit is contained in:
@@ -725,6 +725,7 @@ pub const Scope = struct {
|
||||
/// Points to the arena allocator of DeclAnalysis
|
||||
arena: *Allocator,
|
||||
label: ?Label = null,
|
||||
is_comptime: bool,
|
||||
|
||||
pub const Label = struct {
|
||||
zir_block: *zir.Inst.Block,
|
||||
@@ -1307,7 +1308,6 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
|
||||
.return_type = return_type_inst,
|
||||
.param_types = param_types,
|
||||
}, .{});
|
||||
_ = try astgen.addZIRUnOp(self, &fn_type_scope.base, fn_src, .@"return", fn_type_inst);
|
||||
|
||||
// We need the memory for the Type to go into the arena for the Decl
|
||||
var decl_arena = std.heap.ArenaAllocator.init(self.gpa);
|
||||
@@ -1320,10 +1320,11 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
|
||||
.decl = decl,
|
||||
.instructions = .{},
|
||||
.arena = &decl_arena.allocator,
|
||||
.is_comptime = false,
|
||||
};
|
||||
defer block_scope.instructions.deinit(self.gpa);
|
||||
|
||||
const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, .{
|
||||
const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, fn_type_inst, .{
|
||||
.instructions = fn_type_scope.instructions.items,
|
||||
});
|
||||
const new_func = try decl_arena.allocator.create(Fn);
|
||||
@@ -1457,6 +1458,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
|
||||
.decl = decl,
|
||||
.instructions = .{},
|
||||
.arena = &decl_arena.allocator,
|
||||
.is_comptime = true,
|
||||
};
|
||||
defer block_scope.instructions.deinit(self.gpa);
|
||||
|
||||
@@ -1489,10 +1491,53 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
|
||||
return self.failNode(&block_scope.base, sect_expr, "TODO implement function section expression", .{});
|
||||
}
|
||||
|
||||
const explicit_type = blk: {
|
||||
const type_node = var_decl.getTypeNode() orelse
|
||||
break :blk null;
|
||||
const var_info: struct { ty: Type, val: ?Value } = if (var_decl.getInitNode()) |init_node| vi: {
|
||||
var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
|
||||
defer gen_scope_arena.deinit();
|
||||
var gen_scope: Scope.GenZIR = .{
|
||||
.decl = decl,
|
||||
.arena = &gen_scope_arena.allocator,
|
||||
.parent = decl.scope,
|
||||
};
|
||||
defer gen_scope.instructions.deinit(self.gpa);
|
||||
|
||||
const init_result_loc: astgen.ResultLoc = if (var_decl.getTypeNode()) |type_node| rl: {
|
||||
const src = tree.token_locs[type_node.firstToken()].start;
|
||||
const type_type = try astgen.addZIRInstConst(self, &gen_scope.base, src, .{
|
||||
.ty = Type.initTag(.type),
|
||||
.val = Value.initTag(.type_type),
|
||||
});
|
||||
const var_type = try astgen.expr(self, &gen_scope.base, .{ .ty = type_type }, type_node);
|
||||
break :rl .{ .ty = var_type };
|
||||
} else .none;
|
||||
|
||||
const src = tree.token_locs[init_node.firstToken()].start;
|
||||
const init_inst = try astgen.expr(self, &gen_scope.base, init_result_loc, init_node);
|
||||
|
||||
var inner_block: Scope.Block = .{
|
||||
.parent = null,
|
||||
.func = null,
|
||||
.decl = decl,
|
||||
.instructions = .{},
|
||||
.arena = &gen_scope_arena.allocator,
|
||||
.is_comptime = true,
|
||||
};
|
||||
defer inner_block.instructions.deinit(self.gpa);
|
||||
try zir_sema.analyzeBody(self, &inner_block.base, .{ .instructions = gen_scope.instructions.items });
|
||||
|
||||
// The result location guarantees the type coercion.
|
||||
const analyzed_init_inst = init_inst.analyzed_inst.?;
|
||||
// The is_comptime in the Scope.Block guarantees the result is comptime-known.
|
||||
const val = analyzed_init_inst.value().?;
|
||||
|
||||
const ty = try analyzed_init_inst.ty.copy(block_scope.arena);
|
||||
break :vi .{
|
||||
.ty = ty,
|
||||
.val = try val.copy(block_scope.arena),
|
||||
};
|
||||
} else if (!is_extern) {
|
||||
return self.failTok(&block_scope.base, var_decl.firstToken(), "variables must be initialized", .{});
|
||||
} else if (var_decl.getTypeNode()) |type_node| vi: {
|
||||
// Temporary arena for the zir instructions.
|
||||
var type_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
|
||||
defer type_scope_arena.deinit();
|
||||
@@ -1509,71 +1554,24 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
|
||||
.val = Value.initTag(.type_type),
|
||||
});
|
||||
const var_type = try astgen.expr(self, &type_scope.base, .{ .ty = type_type }, type_node);
|
||||
_ = try astgen.addZIRUnOp(self, &type_scope.base, src, .@"return", var_type);
|
||||
|
||||
break :blk try zir_sema.analyzeBodyValueAsType(self, &block_scope, .{
|
||||
const ty = try zir_sema.analyzeBodyValueAsType(self, &block_scope, var_type, .{
|
||||
.instructions = type_scope.instructions.items,
|
||||
});
|
||||
};
|
||||
|
||||
var var_type: Type = undefined;
|
||||
const value: ?Value = if (var_decl.getInitNode()) |init_node| blk: {
|
||||
var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
|
||||
defer gen_scope_arena.deinit();
|
||||
var gen_scope: Scope.GenZIR = .{
|
||||
.decl = decl,
|
||||
.arena = &gen_scope_arena.allocator,
|
||||
.parent = decl.scope,
|
||||
break :vi .{
|
||||
.ty = ty,
|
||||
.val = null,
|
||||
};
|
||||
defer gen_scope.instructions.deinit(self.gpa);
|
||||
const src = tree.token_locs[init_node.firstToken()].start;
|
||||
|
||||
// TODO comptime scope here
|
||||
const init_inst = try astgen.expr(self, &gen_scope.base, .none, init_node);
|
||||
_ = try astgen.addZIRUnOp(self, &gen_scope.base, src, .@"return", init_inst);
|
||||
|
||||
var inner_block: Scope.Block = .{
|
||||
.parent = null,
|
||||
.func = null,
|
||||
.decl = decl,
|
||||
.instructions = .{},
|
||||
.arena = &gen_scope_arena.allocator,
|
||||
};
|
||||
defer inner_block.instructions.deinit(self.gpa);
|
||||
try zir_sema.analyzeBody(self, &inner_block.base, .{ .instructions = gen_scope.instructions.items });
|
||||
|
||||
for (inner_block.instructions.items) |inst| {
|
||||
if (inst.castTag(.ret)) |ret| {
|
||||
const coerced = if (explicit_type) |some|
|
||||
try self.coerce(&inner_block.base, some, ret.operand)
|
||||
else
|
||||
ret.operand;
|
||||
const val = coerced.value() orelse
|
||||
return self.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{});
|
||||
|
||||
var_type = explicit_type orelse try ret.operand.ty.copy(block_scope.arena);
|
||||
break :blk try val.copy(block_scope.arena);
|
||||
} else {
|
||||
return self.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{});
|
||||
}
|
||||
}
|
||||
unreachable;
|
||||
} else if (!is_extern) {
|
||||
return self.failTok(&block_scope.base, var_decl.firstToken(), "variables must be initialized", .{});
|
||||
} else if (explicit_type) |some| blk: {
|
||||
var_type = some;
|
||||
break :blk null;
|
||||
} else {
|
||||
return self.failTok(&block_scope.base, var_decl.firstToken(), "unable to infer variable type", .{});
|
||||
};
|
||||
|
||||
if (is_mutable and !var_type.isValidVarType(is_extern)) {
|
||||
return self.failTok(&block_scope.base, var_decl.firstToken(), "variable of type '{}' must be const", .{var_type});
|
||||
if (is_mutable and !var_info.ty.isValidVarType(is_extern)) {
|
||||
return self.failTok(&block_scope.base, var_decl.firstToken(), "variable of type '{}' must be const", .{var_info.ty});
|
||||
}
|
||||
|
||||
var type_changed = true;
|
||||
if (decl.typedValueManaged()) |tvm| {
|
||||
type_changed = !tvm.typed_value.ty.eql(var_type);
|
||||
type_changed = !tvm.typed_value.ty.eql(var_info.ty);
|
||||
|
||||
tvm.deinit(self.gpa);
|
||||
}
|
||||
@@ -1582,7 +1580,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
|
||||
const var_payload = try decl_arena.allocator.create(Value.Payload.Variable);
|
||||
new_variable.* = .{
|
||||
.owner_decl = decl,
|
||||
.init = value orelse undefined,
|
||||
.init = var_info.val orelse undefined,
|
||||
.is_extern = is_extern,
|
||||
.is_mutable = is_mutable,
|
||||
.is_threadlocal = is_threadlocal,
|
||||
@@ -1593,7 +1591,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
|
||||
decl.typed_value = .{
|
||||
.most_recent = .{
|
||||
.typed_value = .{
|
||||
.ty = var_type,
|
||||
.ty = var_info.ty,
|
||||
.val = Value.initPayload(&var_payload.base),
|
||||
},
|
||||
.arena = decl_arena_state,
|
||||
@@ -1628,8 +1626,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
|
||||
};
|
||||
defer gen_scope.instructions.deinit(self.gpa);
|
||||
|
||||
// TODO comptime scope here
|
||||
_ = try astgen.expr(self, &gen_scope.base, .none, comptime_decl.expr);
|
||||
_ = try astgen.comptimeExpr(self, &gen_scope.base, .none, comptime_decl.expr);
|
||||
|
||||
var block_scope: Scope.Block = .{
|
||||
.parent = null,
|
||||
@@ -1637,6 +1634,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
|
||||
.decl = decl,
|
||||
.instructions = .{},
|
||||
.arena = &analysis_arena.allocator,
|
||||
.is_comptime = true,
|
||||
};
|
||||
defer block_scope.instructions.deinit(self.gpa);
|
||||
|
||||
@@ -2007,6 +2005,7 @@ fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
|
||||
.decl = decl,
|
||||
.instructions = .{},
|
||||
.arena = &arena.allocator,
|
||||
.is_comptime = false,
|
||||
};
|
||||
defer inner_block.instructions.deinit(self.gpa);
|
||||
|
||||
@@ -2092,12 +2091,19 @@ pub fn getErrorValue(self: *Module, name: []const u8) !std.StringHashMapUnmanage
|
||||
return gop.entry.*;
|
||||
}
|
||||
|
||||
/// TODO split this into `requireRuntimeBlock` and `requireFunctionBlock` and audit callsites.
|
||||
pub fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
|
||||
pub fn requireFunctionBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
|
||||
return scope.cast(Scope.Block) orelse
|
||||
return self.fail(scope, src, "instruction illegal outside function body", .{});
|
||||
}
|
||||
|
||||
pub fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
|
||||
const block = try self.requireFunctionBlock(scope, src);
|
||||
if (block.is_comptime) {
|
||||
return self.fail(scope, src, "unable to resolve comptime value", .{});
|
||||
}
|
||||
return block;
|
||||
}
|
||||
|
||||
pub fn resolveConstValue(self: *Module, scope: *Scope, base: *Inst) !Value {
|
||||
return (try self.resolveDefinedValue(scope, base)) orelse
|
||||
return self.fail(scope, base.src, "unable to resolve comptime value", .{});
|
||||
@@ -3432,6 +3438,7 @@ pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic
|
||||
.decl = parent_block.decl,
|
||||
.instructions = .{},
|
||||
.arena = parent_block.arena,
|
||||
.is_comptime = parent_block.is_comptime,
|
||||
};
|
||||
defer fail_block.instructions.deinit(mod.gpa);
|
||||
|
||||
|
||||
@@ -258,7 +258,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
|
||||
.OptionalType => return rlWrap(mod, scope, rl, try optionalType(mod, scope, node.castTag(.OptionalType).?)),
|
||||
.UnwrapOptional => return unwrapOptional(mod, scope, rl, node.castTag(.UnwrapOptional).?),
|
||||
.Block => return rlWrapVoid(mod, scope, rl, node, try blockExpr(mod, scope, node.castTag(.Block).?)),
|
||||
.LabeledBlock => return labeledBlockExpr(mod, scope, rl, node.castTag(.LabeledBlock).?),
|
||||
.LabeledBlock => return labeledBlockExpr(mod, scope, rl, node.castTag(.LabeledBlock).?, .block),
|
||||
.Break => return rlWrap(mod, scope, rl, try breakExpr(mod, scope, node.castTag(.Break).?)),
|
||||
.PtrType => return rlWrap(mod, scope, rl, try ptrType(mod, scope, node.castTag(.PtrType).?)),
|
||||
.GroupedExpression => return expr(mod, scope, rl, node.castTag(.GroupedExpression).?.expr),
|
||||
@@ -276,6 +276,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
|
||||
.For => return forExpr(mod, scope, rl, node.castTag(.For).?),
|
||||
.ArrayAccess => return arrayAccess(mod, scope, rl, node.castTag(.ArrayAccess).?),
|
||||
.Catch => return catchExpr(mod, scope, rl, node.castTag(.Catch).?),
|
||||
.Comptime => return comptimeKeyword(mod, scope, rl, node.castTag(.Comptime).?),
|
||||
|
||||
.Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}),
|
||||
.Range => return mod.failNode(scope, node, "TODO implement astgen.expr for .Range", .{}),
|
||||
@@ -294,11 +295,46 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
|
||||
.AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}),
|
||||
.FnProto => return mod.failNode(scope, node, "TODO implement astgen.expr for .FnProto", .{}),
|
||||
.ContainerDecl => return mod.failNode(scope, node, "TODO implement astgen.expr for .ContainerDecl", .{}),
|
||||
.Comptime => return mod.failNode(scope, node, "TODO implement astgen.expr for .Comptime", .{}),
|
||||
.Nosuspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Nosuspend", .{}),
|
||||
}
|
||||
}
|
||||
|
||||
fn comptimeKeyword(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Comptime) InnerError!*zir.Inst {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
return comptimeExpr(mod, scope, rl, node.expr);
|
||||
}
|
||||
|
||||
pub fn comptimeExpr(mod: *Module, parent_scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst {
|
||||
const tree = parent_scope.tree();
|
||||
const src = tree.token_locs[node.firstToken()].start;
|
||||
|
||||
// Optimization for labeled blocks: don't need to have 2 layers of blocks, we can reuse the existing one.
|
||||
if (node.castTag(.LabeledBlock)) |block_node| {
|
||||
return labeledBlockExpr(mod, parent_scope, rl, block_node, .block_comptime);
|
||||
}
|
||||
|
||||
// Make a scope to collect generated instructions in the sub-expression.
|
||||
var block_scope: Scope.GenZIR = .{
|
||||
.parent = parent_scope,
|
||||
.decl = parent_scope.decl().?,
|
||||
.arena = parent_scope.arena(),
|
||||
.instructions = .{},
|
||||
};
|
||||
defer block_scope.instructions.deinit(mod.gpa);
|
||||
|
||||
// No need to capture the result here because block_comptime_flat implies that the final
|
||||
// instruction is the block's result value.
|
||||
_ = try expr(mod, &block_scope.base, rl, node);
|
||||
|
||||
const block = try addZIRInstBlock(mod, parent_scope, src, .block_comptime_flat, .{
|
||||
.instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
|
||||
});
|
||||
|
||||
return &block.base;
|
||||
}
|
||||
|
||||
fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {
|
||||
const tree = parent_scope.tree();
|
||||
const src = tree.token_locs[node.ltoken].start;
|
||||
@@ -360,10 +396,13 @@ fn labeledBlockExpr(
|
||||
parent_scope: *Scope,
|
||||
rl: ResultLoc,
|
||||
block_node: *ast.Node.LabeledBlock,
|
||||
zir_tag: zir.Inst.Tag,
|
||||
) InnerError!*zir.Inst {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
assert(zir_tag == .block or zir_tag == .block_comptime);
|
||||
|
||||
const tree = parent_scope.tree();
|
||||
const src = tree.token_locs[block_node.lbrace].start;
|
||||
|
||||
@@ -373,7 +412,7 @@ fn labeledBlockExpr(
|
||||
const block_inst = try gen_zir.arena.create(zir.Inst.Block);
|
||||
block_inst.* = .{
|
||||
.base = .{
|
||||
.tag = .block,
|
||||
.tag = zir_tag,
|
||||
.src = src,
|
||||
},
|
||||
.positionals = .{
|
||||
@@ -773,7 +812,7 @@ fn catchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Catch)
|
||||
.else_body = undefined, // populated below
|
||||
}, .{});
|
||||
|
||||
const block = try addZIRInstBlock(mod, scope, src, .{
|
||||
const block = try addZIRInstBlock(mod, scope, src, .block, .{
|
||||
.instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
|
||||
});
|
||||
|
||||
@@ -946,7 +985,7 @@ fn boolBinOp(
|
||||
.else_body = undefined, // populated below
|
||||
}, .{});
|
||||
|
||||
const block = try addZIRInstBlock(mod, scope, src, .{
|
||||
const block = try addZIRInstBlock(mod, scope, src, .block, .{
|
||||
.instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
|
||||
});
|
||||
|
||||
@@ -1095,7 +1134,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
|
||||
.else_body = undefined, // populated below
|
||||
}, .{});
|
||||
|
||||
const block = try addZIRInstBlock(mod, scope, if_src, .{
|
||||
const block = try addZIRInstBlock(mod, scope, if_src, .block, .{
|
||||
.instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
|
||||
});
|
||||
|
||||
@@ -1218,7 +1257,7 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W
|
||||
.then_body = undefined, // populated below
|
||||
.else_body = undefined, // populated below
|
||||
}, .{});
|
||||
const cond_block = try addZIRInstBlock(mod, &loop_scope.base, while_src, .{
|
||||
const cond_block = try addZIRInstBlock(mod, &loop_scope.base, while_src, .block, .{
|
||||
.instructions = try loop_scope.arena.dupe(*zir.Inst, continue_scope.instructions.items),
|
||||
});
|
||||
// TODO avoid emitting the continue expr when there
|
||||
@@ -1231,7 +1270,7 @@ fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.W
|
||||
const loop = try addZIRInstLoop(mod, &expr_scope.base, while_src, .{
|
||||
.instructions = try expr_scope.arena.dupe(*zir.Inst, loop_scope.instructions.items),
|
||||
});
|
||||
const while_block = try addZIRInstBlock(mod, scope, while_src, .{
|
||||
const while_block = try addZIRInstBlock(mod, scope, while_src, .block, .{
|
||||
.instructions = try expr_scope.arena.dupe(*zir.Inst, expr_scope.instructions.items),
|
||||
});
|
||||
|
||||
@@ -1365,7 +1404,7 @@ fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For)
|
||||
.then_body = undefined, // populated below
|
||||
.else_body = undefined, // populated below
|
||||
}, .{});
|
||||
const cond_block = try addZIRInstBlock(mod, &loop_scope.base, for_src, .{
|
||||
const cond_block = try addZIRInstBlock(mod, &loop_scope.base, for_src, .block, .{
|
||||
.instructions = try loop_scope.arena.dupe(*zir.Inst, cond_scope.instructions.items),
|
||||
});
|
||||
|
||||
@@ -1382,7 +1421,7 @@ fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For)
|
||||
const loop = try addZIRInstLoop(mod, &for_scope.base, for_src, .{
|
||||
.instructions = try for_scope.arena.dupe(*zir.Inst, loop_scope.instructions.items),
|
||||
});
|
||||
const for_block = try addZIRInstBlock(mod, scope, for_src, .{
|
||||
const for_block = try addZIRInstBlock(mod, scope, for_src, .block, .{
|
||||
.instructions = try for_scope.arena.dupe(*zir.Inst, for_scope.instructions.items),
|
||||
});
|
||||
|
||||
@@ -2260,6 +2299,30 @@ pub fn addZIRBinOp(
|
||||
return &inst.base;
|
||||
}
|
||||
|
||||
pub fn addZIRInstBlock(
|
||||
mod: *Module,
|
||||
scope: *Scope,
|
||||
src: usize,
|
||||
tag: zir.Inst.Tag,
|
||||
body: zir.Module.Body,
|
||||
) !*zir.Inst.Block {
|
||||
const gen_zir = scope.getGenZIR();
|
||||
try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
|
||||
const inst = try gen_zir.arena.create(zir.Inst.Block);
|
||||
inst.* = .{
|
||||
.base = .{
|
||||
.tag = tag,
|
||||
.src = src,
|
||||
},
|
||||
.positionals = .{
|
||||
.body = body,
|
||||
},
|
||||
.kw_args = .{},
|
||||
};
|
||||
gen_zir.instructions.appendAssumeCapacity(&inst.base);
|
||||
return inst;
|
||||
}
|
||||
|
||||
pub fn addZIRInst(
|
||||
mod: *Module,
|
||||
scope: *Scope,
|
||||
@@ -2278,12 +2341,6 @@ pub fn addZIRInstConst(mod: *Module, scope: *Scope, src: usize, typed_value: Typ
|
||||
return addZIRInst(mod, scope, src, zir.Inst.Const, P{ .typed_value = typed_value }, .{});
|
||||
}
|
||||
|
||||
/// TODO The existence of this function is a workaround for a bug in stage1.
|
||||
pub fn addZIRInstBlock(mod: *Module, scope: *Scope, src: usize, body: zir.Module.Body) !*zir.Inst.Block {
|
||||
const P = std.meta.fieldInfo(zir.Inst.Block, "positionals").field_type;
|
||||
return addZIRInstSpecial(mod, scope, src, zir.Inst.Block, P{ .body = body }, .{});
|
||||
}
|
||||
|
||||
/// TODO The existence of this function is a workaround for a bug in stage1.
|
||||
pub fn addZIRInstLoop(mod: *Module, scope: *Scope, src: usize, body: zir.Module.Body) !*zir.Inst.Loop {
|
||||
const P = std.meta.fieldInfo(zir.Inst.Loop, "positionals").field_type;
|
||||
|
||||
@@ -189,7 +189,7 @@ pub const Inst = struct {
|
||||
}
|
||||
|
||||
pub fn cmpOperator(base: *Inst) ?std.math.CompareOperator {
|
||||
return switch (self.base.tag) {
|
||||
return switch (base.tag) {
|
||||
.cmp_lt => .lt,
|
||||
.cmp_lte => .lte,
|
||||
.cmp_eq => .eq,
|
||||
@@ -220,6 +220,14 @@ pub const Inst = struct {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
pub fn breakBlock(base: *Inst) ?*Block {
|
||||
return switch (base.tag) {
|
||||
.br => base.castTag(.br).?.block,
|
||||
.brvoid => base.castTag(.brvoid).?.block,
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
|
||||
pub const NoOp = struct {
|
||||
base: Inst,
|
||||
|
||||
|
||||
@@ -474,15 +474,15 @@ pub const TestContext = struct {
|
||||
var all_errors = try module.getAllErrorsAlloc();
|
||||
defer all_errors.deinit(allocator);
|
||||
if (all_errors.list.len != 0) {
|
||||
std.debug.warn("\nErrors occurred updating the module:\n================\n", .{});
|
||||
std.debug.print("\nErrors occurred updating the module:\n================\n", .{});
|
||||
for (all_errors.list) |err| {
|
||||
std.debug.warn(":{}:{}: error: {}\n================\n", .{ err.line + 1, err.column + 1, err.msg });
|
||||
std.debug.print(":{}:{}: error: {}\n================\n", .{ err.line + 1, err.column + 1, err.msg });
|
||||
}
|
||||
if (case.cbe) {
|
||||
const C = module.bin_file.cast(link.File.C).?;
|
||||
std.debug.warn("Generated C: \n===============\n{}\n\n===========\n\n", .{C.main.items});
|
||||
std.debug.print("Generated C: \n===============\n{}\n\n===========\n\n", .{C.main.items});
|
||||
}
|
||||
std.debug.warn("Test failed.\n", .{});
|
||||
std.debug.print("Test failed.\n", .{});
|
||||
std.process.exit(1);
|
||||
}
|
||||
}
|
||||
@@ -497,12 +497,12 @@ pub const TestContext = struct {
|
||||
var out = file.reader().readAllAlloc(arena, 1024 * 1024) catch @panic("Unable to read C output!");
|
||||
|
||||
if (expected_output.len != out.len) {
|
||||
std.debug.warn("\nTransformed C length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });
|
||||
std.debug.print("\nTransformed C length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });
|
||||
std.process.exit(1);
|
||||
}
|
||||
for (expected_output) |e, i| {
|
||||
if (out[i] != e) {
|
||||
std.debug.warn("\nTransformed C differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });
|
||||
std.debug.print("\nTransformed C differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });
|
||||
std.process.exit(1);
|
||||
}
|
||||
}
|
||||
@@ -526,12 +526,12 @@ pub const TestContext = struct {
|
||||
defer test_node.end();
|
||||
|
||||
if (expected_output.len != out_zir.items.len) {
|
||||
std.debug.warn("{}\nTransformed ZIR length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });
|
||||
std.debug.print("{}\nTransformed ZIR length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });
|
||||
std.process.exit(1);
|
||||
}
|
||||
for (expected_output) |e, i| {
|
||||
if (out_zir.items[i] != e) {
|
||||
std.debug.warn("{}\nTransformed ZIR differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });
|
||||
std.debug.print("{}\nTransformed ZIR differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });
|
||||
std.process.exit(1);
|
||||
}
|
||||
}
|
||||
@@ -554,7 +554,7 @@ pub const TestContext = struct {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
std.debug.warn("{}\nUnexpected error:\n================\n:{}:{}: error: {}\n================\nTest failed.\n", .{ case.name, a.line + 1, a.column + 1, a.msg });
|
||||
std.debug.print("{}\nUnexpected error:\n================\n:{}:{}: error: {}\n================\nTest failed.\n", .{ case.name, a.line + 1, a.column + 1, a.msg });
|
||||
std.process.exit(1);
|
||||
}
|
||||
}
|
||||
@@ -562,7 +562,7 @@ pub const TestContext = struct {
|
||||
for (handled_errors) |h, i| {
|
||||
if (!h) {
|
||||
const er = e[i];
|
||||
std.debug.warn("{}\nDid not receive error:\n================\n{}:{}: {}\n================\nTest failed.\n", .{ case.name, er.line, er.column, er.msg });
|
||||
std.debug.print("{}\nDid not receive error:\n================\n{}:{}: {}\n================\nTest failed.\n", .{ case.name, er.line, er.column, er.msg });
|
||||
std.process.exit(1);
|
||||
}
|
||||
}
|
||||
@@ -643,7 +643,7 @@ pub const TestContext = struct {
|
||||
switch (exec_result.term) {
|
||||
.Exited => |code| {
|
||||
if (code != 0) {
|
||||
std.debug.warn("elf file exited with code {}\n", .{code});
|
||||
std.debug.print("elf file exited with code {}\n", .{code});
|
||||
return error.BinaryBadExitCode;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -78,6 +78,13 @@ pub const Inst = struct {
|
||||
bitor,
|
||||
/// A labeled block of code, which can return a value.
|
||||
block,
|
||||
/// A block of code, which can return a value. There are no instructions that break out of
|
||||
/// this block; it is implied that the final instruction is the result.
|
||||
block_flat,
|
||||
/// Same as `block` but additionally makes the inner instructions execute at comptime.
|
||||
block_comptime,
|
||||
/// Same as `block_flat` but additionally makes the inner instructions execute at comptime.
|
||||
block_comptime_flat,
|
||||
/// Boolean NOT. See also `bitnot`.
|
||||
boolnot,
|
||||
/// Return a value from a `Block`.
|
||||
@@ -338,9 +345,14 @@ pub const Inst = struct {
|
||||
.merge_error_sets,
|
||||
=> BinOp,
|
||||
|
||||
.block,
|
||||
.block_flat,
|
||||
.block_comptime,
|
||||
.block_comptime_flat,
|
||||
=> Block,
|
||||
|
||||
.arg => Arg,
|
||||
.array_type_sentinel => ArrayTypeSentinel,
|
||||
.block => Block,
|
||||
.@"break" => Break,
|
||||
.breakvoid => BreakVoid,
|
||||
.call => Call,
|
||||
@@ -392,6 +404,9 @@ pub const Inst = struct {
|
||||
.bitcast_result_ptr,
|
||||
.bitor,
|
||||
.block,
|
||||
.block_flat,
|
||||
.block_comptime,
|
||||
.block_comptime_flat,
|
||||
.boolnot,
|
||||
.breakpoint,
|
||||
.call,
|
||||
|
||||
@@ -31,7 +31,10 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
|
||||
.arg => return analyzeInstArg(mod, scope, old_inst.castTag(.arg).?),
|
||||
.bitcast_ref => return analyzeInstBitCastRef(mod, scope, old_inst.castTag(.bitcast_ref).?),
|
||||
.bitcast_result_ptr => return analyzeInstBitCastResultPtr(mod, scope, old_inst.castTag(.bitcast_result_ptr).?),
|
||||
.block => return analyzeInstBlock(mod, scope, old_inst.castTag(.block).?),
|
||||
.block => return analyzeInstBlock(mod, scope, old_inst.castTag(.block).?, false),
|
||||
.block_comptime => return analyzeInstBlock(mod, scope, old_inst.castTag(.block_comptime).?, true),
|
||||
.block_flat => return analyzeInstBlockFlat(mod, scope, old_inst.castTag(.block_flat).?, false),
|
||||
.block_comptime_flat => return analyzeInstBlockFlat(mod, scope, old_inst.castTag(.block_comptime_flat).?, true),
|
||||
.@"break" => return analyzeInstBreak(mod, scope, old_inst.castTag(.@"break").?),
|
||||
.breakpoint => return analyzeInstBreakpoint(mod, scope, old_inst.castTag(.breakpoint).?),
|
||||
.breakvoid => return analyzeInstBreakVoid(mod, scope, old_inst.castTag(.breakvoid).?),
|
||||
@@ -147,17 +150,16 @@ pub fn analyzeBody(mod: *Module, scope: *Scope, body: zir.Module.Body) !void {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn analyzeBodyValueAsType(mod: *Module, block_scope: *Scope.Block, body: zir.Module.Body) !Type {
|
||||
pub fn analyzeBodyValueAsType(
|
||||
mod: *Module,
|
||||
block_scope: *Scope.Block,
|
||||
zir_result_inst: *zir.Inst,
|
||||
body: zir.Module.Body,
|
||||
) !Type {
|
||||
try analyzeBody(mod, &block_scope.base, body);
|
||||
for (block_scope.instructions.items) |inst| {
|
||||
if (inst.castTag(.ret)) |ret| {
|
||||
const val = try mod.resolveConstValue(&block_scope.base, ret.operand);
|
||||
return val.toType(block_scope.base.arena());
|
||||
} else {
|
||||
return mod.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{});
|
||||
}
|
||||
}
|
||||
unreachable;
|
||||
const result_inst = zir_result_inst.analyzed_inst.?;
|
||||
const val = try mod.resolveConstValue(&block_scope.base, result_inst);
|
||||
return val.toType(block_scope.base.arena());
|
||||
}
|
||||
|
||||
pub fn analyzeZirDecl(mod: *Module, decl: *Decl, src_decl: *zir.Decl) InnerError!bool {
|
||||
@@ -362,7 +364,7 @@ fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!
|
||||
}
|
||||
|
||||
fn analyzeInstRetType(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
|
||||
const b = try mod.requireRuntimeBlock(scope, inst.base.src);
|
||||
const b = try mod.requireFunctionBlock(scope, inst.base.src);
|
||||
const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
|
||||
const ret_type = fn_ty.fnReturnType();
|
||||
return mod.constType(scope, inst.base.src, ret_type);
|
||||
@@ -517,6 +519,7 @@ fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError
|
||||
.decl = parent_block.decl,
|
||||
.instructions = .{},
|
||||
.arena = parent_block.arena,
|
||||
.is_comptime = parent_block.is_comptime,
|
||||
};
|
||||
defer child_block.instructions.deinit(mod.gpa);
|
||||
|
||||
@@ -529,7 +532,29 @@ fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError
|
||||
return &loop_inst.base;
|
||||
}
|
||||
|
||||
fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerError!*Inst {
|
||||
fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_comptime: bool) InnerError!*Inst {
|
||||
const parent_block = scope.cast(Scope.Block).?;
|
||||
|
||||
var child_block: Scope.Block = .{
|
||||
.parent = parent_block,
|
||||
.func = parent_block.func,
|
||||
.decl = parent_block.decl,
|
||||
.instructions = .{},
|
||||
.arena = parent_block.arena,
|
||||
.label = null,
|
||||
.is_comptime = parent_block.is_comptime or is_comptime,
|
||||
};
|
||||
defer child_block.instructions.deinit(mod.gpa);
|
||||
|
||||
try analyzeBody(mod, &child_block.base, inst.positionals.body);
|
||||
|
||||
const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items);
|
||||
try parent_block.instructions.appendSlice(mod.gpa, copied_instructions);
|
||||
|
||||
return copied_instructions[copied_instructions.len - 1];
|
||||
}
|
||||
|
||||
fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_comptime: bool) InnerError!*Inst {
|
||||
const parent_block = scope.cast(Scope.Block).?;
|
||||
|
||||
// Reserve space for a Block instruction so that generated Break instructions can
|
||||
@@ -557,6 +582,7 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerErr
|
||||
.results = .{},
|
||||
.block_inst = block_inst,
|
||||
}),
|
||||
.is_comptime = is_comptime or parent_block.is_comptime,
|
||||
};
|
||||
const label = &child_block.label.?;
|
||||
|
||||
@@ -569,6 +595,28 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerErr
|
||||
assert(child_block.instructions.items.len != 0);
|
||||
assert(child_block.instructions.items[child_block.instructions.items.len - 1].ty.isNoReturn());
|
||||
|
||||
if (label.results.items.len == 0) {
|
||||
// No need for a block instruction. We can put the new instructions directly into the parent block.
|
||||
const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items);
|
||||
try parent_block.instructions.appendSlice(mod.gpa, copied_instructions);
|
||||
return copied_instructions[copied_instructions.len - 1];
|
||||
}
|
||||
if (label.results.items.len == 1) {
|
||||
const last_inst_index = child_block.instructions.items.len - 1;
|
||||
const last_inst = child_block.instructions.items[last_inst_index];
|
||||
if (last_inst.breakBlock()) |br_block| {
|
||||
if (br_block == block_inst) {
|
||||
// No need for a block instruction. We can put the new instructions directly into the parent block.
|
||||
// Here we omit the break instruction.
|
||||
const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items[0..last_inst_index]);
|
||||
try parent_block.instructions.appendSlice(mod.gpa, copied_instructions);
|
||||
return label.results.items[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
// It should be impossible to have the number of results be > 1 in a comptime scope.
|
||||
assert(!child_block.is_comptime); // We should have already got a compile error in the condbr condition.
|
||||
|
||||
// Need to set the type and emit the Block instruction. This allows machine code generation
|
||||
// to emit a jump instruction to after the block when it encounters the break.
|
||||
try parent_block.instructions.append(mod.gpa, &block_inst.base);
|
||||
@@ -1083,7 +1131,7 @@ fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inne
|
||||
const array_ptr = try resolveInst(mod, scope, inst.positionals.array_ptr);
|
||||
const uncasted_index = try resolveInst(mod, scope, inst.positionals.index);
|
||||
const elem_index = try mod.coerce(scope, Type.initTag(.usize), uncasted_index);
|
||||
|
||||
|
||||
const elem_ty = switch (array_ptr.ty.zigTypeTag()) {
|
||||
.Pointer => array_ptr.ty.elemType(),
|
||||
else => return mod.fail(scope, inst.positionals.array_ptr.src, "expected pointer, found '{}'", .{array_ptr.ty}),
|
||||
@@ -1376,6 +1424,7 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE
|
||||
.decl = parent_block.decl,
|
||||
.instructions = .{},
|
||||
.arena = parent_block.arena,
|
||||
.is_comptime = parent_block.is_comptime,
|
||||
};
|
||||
defer true_block.instructions.deinit(mod.gpa);
|
||||
try analyzeBody(mod, &true_block.base, inst.positionals.then_body);
|
||||
@@ -1386,6 +1435,7 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE
|
||||
.decl = parent_block.decl,
|
||||
.instructions = .{},
|
||||
.arena = parent_block.arena,
|
||||
.is_comptime = parent_block.is_comptime,
|
||||
};
|
||||
defer false_block.instructions.deinit(mod.gpa);
|
||||
try analyzeBody(mod, &false_block.base, inst.positionals.else_body);
|
||||
|
||||
@@ -274,7 +274,7 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
}
|
||||
|
||||
{
|
||||
var case = ctx.exe("substracting numbers at runtime", linux_x64);
|
||||
var case = ctx.exe("subtracting numbers at runtime", linux_x64);
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ sub(7, 4);
|
||||
@@ -967,10 +967,19 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
\\fn entry() void {}
|
||||
, &[_][]const u8{":2:4: error: redefinition of 'entry'"});
|
||||
|
||||
ctx.compileError("extern variable has no type", linux_x64,
|
||||
\\comptime {
|
||||
\\ _ = foo;
|
||||
\\}
|
||||
\\extern var foo;
|
||||
, &[_][]const u8{":4:1: error: unable to infer variable type"});
|
||||
{
|
||||
var case = ctx.obj("extern variable has no type", linux_x64);
|
||||
case.addError(
|
||||
\\comptime {
|
||||
\\ _ = foo;
|
||||
\\}
|
||||
\\extern var foo;
|
||||
, &[_][]const u8{":2:5: error: unable to resolve comptime value"});
|
||||
case.addError(
|
||||
\\export fn entry() void {
|
||||
\\ _ = foo;
|
||||
\\}
|
||||
\\extern var foo;
|
||||
, &[_][]const u8{":4:1: error: unable to infer variable type"});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user