mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
stage2: evaluate TypeOf arguments in a separate scope
This commit is contained in:
parent
db82c1b982
commit
ff72b8a819
@ -2159,6 +2159,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
|
||||
.negate,
|
||||
.negate_wrap,
|
||||
.typeof,
|
||||
.typeof_builtin,
|
||||
.xor,
|
||||
.optional_type,
|
||||
.optional_payload_safe,
|
||||
@ -6875,29 +6876,54 @@ fn typeOf(
|
||||
scope: *Scope,
|
||||
rl: ResultLoc,
|
||||
node: Ast.Node.Index,
|
||||
params: []const Ast.Node.Index,
|
||||
args: []const Ast.Node.Index,
|
||||
) InnerError!Zir.Inst.Ref {
|
||||
if (params.len < 1) {
|
||||
if (args.len < 1) {
|
||||
return gz.astgen.failNode(node, "expected at least 1 argument, found 0", .{});
|
||||
}
|
||||
if (params.len == 1) {
|
||||
const expr_result = try reachableExpr(gz, scope, .none, params[0], node);
|
||||
const result = try gz.addUnNode(.typeof, expr_result, node);
|
||||
return rvalue(gz, rl, result, node);
|
||||
}
|
||||
const gpa = gz.astgen.gpa;
|
||||
if (args.len == 1) {
|
||||
const typeof_inst = try gz.makeBlockInst(.typeof_builtin, node);
|
||||
|
||||
const payload_index = try addExtra(gz.astgen, Zir.Inst.NodeMultiOp{
|
||||
var typeof_scope = gz.makeSubBlock(scope);
|
||||
typeof_scope.force_comptime = false;
|
||||
defer typeof_scope.unstack();
|
||||
|
||||
const ty_expr = try reachableExpr(&typeof_scope, &typeof_scope.base, .none, args[0], node);
|
||||
if (!gz.refIsNoReturn(ty_expr)) {
|
||||
_ = try typeof_scope.addBreak(.break_inline, typeof_inst, ty_expr);
|
||||
}
|
||||
try typeof_scope.setBlockBody(typeof_inst);
|
||||
|
||||
// typeof_scope unstacked now, can add new instructions to gz
|
||||
try gz.instructions.append(gpa, typeof_inst);
|
||||
return rvalue(gz, rl, indexToRef(typeof_inst), node);
|
||||
}
|
||||
const payload_size: u32 = std.meta.fields(Zir.Inst.TypeOfPeer).len;
|
||||
const payload_index = try reserveExtra(gz.astgen, payload_size + args.len);
|
||||
var args_index = payload_index + payload_size;
|
||||
|
||||
const typeof_inst = try gz.addExtendedMultiOpPayloadIndex(.typeof_peer, payload_index, args.len);
|
||||
|
||||
var typeof_scope = gz.makeSubBlock(scope);
|
||||
typeof_scope.force_comptime = false;
|
||||
|
||||
for (args) |arg, i| {
|
||||
const param_ref = try reachableExpr(&typeof_scope, &typeof_scope.base, .none, arg, node);
|
||||
gz.astgen.extra.items[args_index + i] = @enumToInt(param_ref);
|
||||
}
|
||||
_ = try typeof_scope.addBreak(.break_inline, refToIndex(typeof_inst).?, .void_value);
|
||||
|
||||
const body = typeof_scope.instructionsSlice();
|
||||
gz.astgen.setExtra(payload_index, Zir.Inst.TypeOfPeer{
|
||||
.body_len = @intCast(u32, body.len),
|
||||
.body_index = @intCast(u32, gz.astgen.extra.items.len),
|
||||
.src_node = gz.nodeIndexToRelative(node),
|
||||
});
|
||||
var extra_index = try reserveExtra(gz.astgen, params.len);
|
||||
for (params) |param| {
|
||||
const param_ref = try reachableExpr(gz, scope, .none, param, node);
|
||||
gz.astgen.extra.items[extra_index] = @enumToInt(param_ref);
|
||||
extra_index += 1;
|
||||
}
|
||||
try gz.astgen.extra.appendSlice(gpa, body);
|
||||
typeof_scope.unstack();
|
||||
|
||||
const result = try gz.addExtendedMultiOpPayloadIndex(.typeof_peer, payload_index, params.len);
|
||||
return rvalue(gz, rl, result, node);
|
||||
return rvalue(gz, rl, typeof_inst, node);
|
||||
}
|
||||
|
||||
fn builtinCall(
|
||||
|
||||
44
src/Sema.zig
44
src/Sema.zig
@ -124,6 +124,7 @@ pub const Block = struct {
|
||||
runtime_index: u32 = 0,
|
||||
|
||||
is_comptime: bool,
|
||||
is_typeof: bool = false,
|
||||
|
||||
/// when null, it is determined by build mode, changed by @setRuntimeSafety
|
||||
want_safety: ?bool = null,
|
||||
@ -181,6 +182,7 @@ pub const Block = struct {
|
||||
.label = null,
|
||||
.inlining = parent.inlining,
|
||||
.is_comptime = parent.is_comptime,
|
||||
.is_typeof = parent.is_typeof,
|
||||
.runtime_cond = parent.runtime_cond,
|
||||
.runtime_loop = parent.runtime_loop,
|
||||
.runtime_index = parent.runtime_index,
|
||||
@ -682,6 +684,7 @@ fn analyzeBodyInner(
|
||||
.size_of => try sema.zirSizeOf(block, inst),
|
||||
.bit_size_of => try sema.zirBitSizeOf(block, inst),
|
||||
.typeof => try sema.zirTypeof(block, inst),
|
||||
.typeof_builtin => try sema.zirTypeofBuiltin(block, inst),
|
||||
.log2_int_type => try sema.zirLog2IntType(block, inst),
|
||||
.typeof_log2_int_type => try sema.zirTypeofLog2IntType(block, inst),
|
||||
.xor => try sema.zirBitwise(block, inst, .xor),
|
||||
@ -10574,6 +10577,29 @@ fn zirTypeof(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
|
||||
return sema.addType(operand_ty);
|
||||
}
|
||||
|
||||
fn zirTypeofBuiltin(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
const pl_node = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index);
|
||||
const body = sema.code.extra[extra.end..][0..extra.data.body_len];
|
||||
|
||||
var child_block: Block = .{
|
||||
.parent = block,
|
||||
.sema = sema,
|
||||
.src_decl = block.src_decl,
|
||||
.namespace = block.namespace,
|
||||
.wip_capture_scope = block.wip_capture_scope,
|
||||
.instructions = .{},
|
||||
.inlining = block.inlining,
|
||||
.is_comptime = false,
|
||||
.is_typeof = true,
|
||||
};
|
||||
defer child_block.instructions.deinit(sema.gpa);
|
||||
|
||||
const operand = try sema.resolveBody(&child_block, body, inst);
|
||||
const operand_ty = sema.typeOf(operand);
|
||||
return sema.addType(operand_ty);
|
||||
}
|
||||
|
||||
fn zirTypeofLog2IntType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
|
||||
const src = inst_data.src();
|
||||
@ -10624,8 +10650,24 @@ fn zirTypeofPeer(
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand);
|
||||
const extra = sema.code.extraData(Zir.Inst.TypeOfPeer, extended.operand);
|
||||
const src: LazySrcLoc = .{ .node_offset = extra.data.src_node };
|
||||
const body = sema.code.extra[extra.data.body_index..][0..extra.data.body_len];
|
||||
|
||||
var child_block: Block = .{
|
||||
.parent = block,
|
||||
.sema = sema,
|
||||
.src_decl = block.src_decl,
|
||||
.namespace = block.namespace,
|
||||
.wip_capture_scope = block.wip_capture_scope,
|
||||
.instructions = .{},
|
||||
.inlining = block.inlining,
|
||||
.is_comptime = false,
|
||||
.is_typeof = true,
|
||||
};
|
||||
defer child_block.instructions.deinit(sema.gpa);
|
||||
_ = try sema.analyzeBody(&child_block, body);
|
||||
|
||||
const args = sema.code.refSlice(extra.end, extended.small);
|
||||
|
||||
const inst_list = try sema.gpa.alloc(Air.Inst.Ref, args.len);
|
||||
|
||||
11
src/Zir.zig
11
src/Zir.zig
@ -547,6 +547,9 @@ pub const Inst = struct {
|
||||
/// Returns the type of a value.
|
||||
/// Uses the `un_node` field.
|
||||
typeof,
|
||||
/// Implements `@TypeOf` for one operand.
|
||||
/// Uses the `pl_node` field.
|
||||
typeof_builtin,
|
||||
/// Given a value, look at the type of it, which must be an integer type.
|
||||
/// Returns the integer type for the RHS of a shift operation.
|
||||
/// Uses the `un_node` field.
|
||||
@ -1067,6 +1070,7 @@ pub const Inst = struct {
|
||||
.negate,
|
||||
.negate_wrap,
|
||||
.typeof,
|
||||
.typeof_builtin,
|
||||
.xor,
|
||||
.optional_type,
|
||||
.optional_payload_safe,
|
||||
@ -1429,6 +1433,7 @@ pub const Inst = struct {
|
||||
.ptr_cast = .pl_node,
|
||||
.truncate = .pl_node,
|
||||
.align_cast = .pl_node,
|
||||
.typeof_builtin = .pl_node,
|
||||
|
||||
.has_decl = .pl_node,
|
||||
.has_field = .pl_node,
|
||||
@ -2391,6 +2396,12 @@ pub const Inst = struct {
|
||||
};
|
||||
};
|
||||
|
||||
pub const TypeOfPeer = struct {
|
||||
src_node: i32,
|
||||
body_len: u32,
|
||||
body_index: u32,
|
||||
};
|
||||
|
||||
pub const BuiltinCall = struct {
|
||||
options: Ref,
|
||||
callee: Ref,
|
||||
|
||||
@ -374,6 +374,7 @@ const Writer = struct {
|
||||
.validate_array_init,
|
||||
.validate_array_init_comptime,
|
||||
.c_import,
|
||||
.typeof_builtin,
|
||||
=> try self.writePlNodeBlock(stream, inst),
|
||||
|
||||
.condbr,
|
||||
@ -458,9 +459,8 @@ const Writer = struct {
|
||||
.variable => try self.writeVarExtended(stream, extended),
|
||||
.alloc => try self.writeAllocExtended(stream, extended),
|
||||
|
||||
.compile_log,
|
||||
.typeof_peer,
|
||||
=> try self.writeNodeMultiOp(stream, extended),
|
||||
.compile_log => try self.writeNodeMultiOp(stream, extended),
|
||||
.typeof_peer => try self.writeTypeofPeer(stream, extended),
|
||||
|
||||
.add_with_overflow,
|
||||
.sub_with_overflow,
|
||||
@ -1966,6 +1966,19 @@ const Writer = struct {
|
||||
try self.writeSrc(stream, src);
|
||||
}
|
||||
|
||||
fn writeTypeofPeer(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void {
|
||||
const extra = self.code.extraData(Zir.Inst.TypeOfPeer, extended.operand);
|
||||
const body = self.code.extra[extra.data.body_index..][0..extra.data.body_len];
|
||||
try self.writeBracedBody(stream, body);
|
||||
try stream.writeAll(",[");
|
||||
const args = self.code.refSlice(extra.end, extended.small);
|
||||
for (args) |arg, i| {
|
||||
if (i != 0) try stream.writeAll(", ");
|
||||
try self.writeInstRef(stream, arg);
|
||||
}
|
||||
try stream.writeAll("])");
|
||||
}
|
||||
|
||||
fn writeBoolBr(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
|
||||
const inst_data = self.code.instructions.items(.data)[inst].bool_br;
|
||||
const extra = self.code.extraData(Zir.Inst.Block, inst_data.payload_index);
|
||||
|
||||
@ -102,6 +102,7 @@ test {
|
||||
builtin.zig_backend != .stage2_wasm)
|
||||
{
|
||||
// Tests that pass for stage1, llvm backend, C backend
|
||||
_ = @import("behavior/bugs/5474.zig");
|
||||
_ = @import("behavior/bugs/9584.zig");
|
||||
_ = @import("behavior/bugs/10970.zig");
|
||||
_ = @import("behavior/cast_int.zig");
|
||||
@ -152,7 +153,6 @@ test {
|
||||
_ = @import("behavior/bugs/4328.zig");
|
||||
_ = @import("behavior/bugs/5398.zig");
|
||||
_ = @import("behavior/bugs/5413.zig");
|
||||
_ = @import("behavior/bugs/5474.zig");
|
||||
_ = @import("behavior/bugs/5487.zig");
|
||||
_ = @import("behavior/bugs/6456.zig");
|
||||
_ = @import("behavior/bugs/6781.zig");
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
// baseline (control) struct with array of scalar
|
||||
const Box0 = struct {
|
||||
@ -25,33 +26,36 @@ const Box2 = struct {
|
||||
};
|
||||
};
|
||||
|
||||
fn doTest() !void {
|
||||
// var
|
||||
{
|
||||
var box0: Box0 = .{ .items = undefined };
|
||||
try std.testing.expect(@typeInfo(@TypeOf(box0.items[0..])).Pointer.is_const == false);
|
||||
fn mutable() !void {
|
||||
var box0: Box0 = .{ .items = undefined };
|
||||
try std.testing.expect(@typeInfo(@TypeOf(box0.items[0..])).Pointer.is_const == false);
|
||||
|
||||
var box1: Box1 = .{ .items = undefined };
|
||||
try std.testing.expect(@typeInfo(@TypeOf(box1.items[0..])).Pointer.is_const == false);
|
||||
var box1: Box1 = .{ .items = undefined };
|
||||
try std.testing.expect(@typeInfo(@TypeOf(box1.items[0..])).Pointer.is_const == false);
|
||||
|
||||
var box2: Box2 = .{ .items = undefined };
|
||||
try std.testing.expect(@typeInfo(@TypeOf(box2.items[0..])).Pointer.is_const == false);
|
||||
}
|
||||
|
||||
// const
|
||||
{
|
||||
const box0: Box0 = .{ .items = undefined };
|
||||
try std.testing.expect(@typeInfo(@TypeOf(box0.items[0..])).Pointer.is_const == true);
|
||||
|
||||
const box1: Box1 = .{ .items = undefined };
|
||||
try std.testing.expect(@typeInfo(@TypeOf(box1.items[0..])).Pointer.is_const == true);
|
||||
|
||||
const box2: Box2 = .{ .items = undefined };
|
||||
try std.testing.expect(@typeInfo(@TypeOf(box2.items[0..])).Pointer.is_const == true);
|
||||
}
|
||||
var box2: Box2 = .{ .items = undefined };
|
||||
try std.testing.expect(@typeInfo(@TypeOf(box2.items[0..])).Pointer.is_const == false);
|
||||
}
|
||||
|
||||
test "pointer-to-array constness for zero-size elements" {
|
||||
try doTest();
|
||||
comptime try doTest();
|
||||
fn constant() !void {
|
||||
const box0: Box0 = .{ .items = undefined };
|
||||
try std.testing.expect(@typeInfo(@TypeOf(box0.items[0..])).Pointer.is_const == true);
|
||||
|
||||
const box1: Box1 = .{ .items = undefined };
|
||||
try std.testing.expect(@typeInfo(@TypeOf(box1.items[0..])).Pointer.is_const == true);
|
||||
|
||||
const box2: Box2 = .{ .items = undefined };
|
||||
try std.testing.expect(@typeInfo(@TypeOf(box2.items[0..])).Pointer.is_const == true);
|
||||
}
|
||||
|
||||
test "pointer-to-array constness for zero-size elements, var" {
|
||||
try mutable();
|
||||
comptime try mutable();
|
||||
}
|
||||
|
||||
test "pointer-to-array constness for zero-size elements, const" {
|
||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
||||
|
||||
try constant();
|
||||
comptime try constant();
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user