mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 04:48:20 +00:00
Merge pull request #16604 from mlugg/result-type-shenanigans
Fix RLS issues, fix crash on invalid result type for `@splat`, refactor some bits of generic instantiations
This commit is contained in:
commit
275e926cf8
@ -1528,11 +1528,13 @@ pub fn refToInterned(ref: Inst.Ref) ?InternPool.Index {
|
||||
}
|
||||
|
||||
pub fn internedToRef(ip_index: InternPool.Index) Inst.Ref {
|
||||
assert(@intFromEnum(ip_index) >> 31 == 0);
|
||||
return switch (ip_index) {
|
||||
.var_args_param_type => .var_args_param_type,
|
||||
.none => .none,
|
||||
else => @enumFromInt(@as(u31, @intCast(@intFromEnum(ip_index)))),
|
||||
else => {
|
||||
assert(@intFromEnum(ip_index) >> 31 == 0);
|
||||
return @enumFromInt(@as(u31, @intCast(@intFromEnum(ip_index))));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1509,9 +1509,11 @@ fn arrayInitExpr(
|
||||
const tag: Zir.Inst.Tag = if (types.array != .none) .array_init else .array_init_anon;
|
||||
return arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag);
|
||||
},
|
||||
.ty, .coerced_ty => {
|
||||
const tag: Zir.Inst.Tag = if (types.array != .none) .array_init else .array_init_anon;
|
||||
const result = try arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag);
|
||||
.ty, .coerced_ty => |ty_inst| {
|
||||
const arr_ty = if (types.array != .none) types.array else blk: {
|
||||
break :blk try gz.addUnNode(.opt_eu_base_ty, ty_inst, node);
|
||||
};
|
||||
const result = try arrayInitExprInner(gz, scope, node, array_init.ast.elements, arr_ty, types.elem, .array_init);
|
||||
return rvalue(gz, ri, result, node);
|
||||
},
|
||||
.ptr => |ptr_res| {
|
||||
@ -1748,7 +1750,9 @@ fn structInitExpr(
|
||||
},
|
||||
.ty, .coerced_ty => |ty_inst| {
|
||||
if (struct_init.ast.type_expr == 0) {
|
||||
const result = try structInitExprRlNone(gz, scope, node, struct_init, ty_inst, .struct_init_anon);
|
||||
const struct_ty_inst = try gz.addUnNode(.opt_eu_base_ty, ty_inst, node);
|
||||
_ = try gz.addUnNode(.validate_struct_init_ty, struct_ty_inst, node);
|
||||
const result = try structInitExprRlTy(gz, scope, node, struct_init, struct_ty_inst, .struct_init);
|
||||
return rvalue(gz, ri, result, node);
|
||||
}
|
||||
const inner_ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
|
||||
@ -2565,6 +2569,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
|
||||
.array_type_sentinel,
|
||||
.elem_type_index,
|
||||
.elem_type,
|
||||
.vector_elem_type,
|
||||
.vector_type,
|
||||
.indexable_ptr_len,
|
||||
.anyframe_type,
|
||||
@ -2743,6 +2748,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
|
||||
.for_len,
|
||||
.@"try",
|
||||
.try_ptr,
|
||||
.opt_eu_base_ty,
|
||||
=> break :b false,
|
||||
|
||||
.extended => switch (gz.astgen.instructions.items(.data)[inst].extended.opcode) {
|
||||
@ -8314,7 +8320,10 @@ fn builtinCall(
|
||||
local_val.used = ident_token;
|
||||
_ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{
|
||||
.operand = local_val.inst,
|
||||
.options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .export_options_type } }, params[1]),
|
||||
// TODO: the result location here should be `.{ .coerced_ty = .export_options_type }`, but
|
||||
// that currently hits assertions in Sema due to type resolution issues.
|
||||
// See #16603
|
||||
.options = try comptimeExpr(gz, scope, .{ .rl = .none }, params[1]),
|
||||
});
|
||||
return rvalue(gz, ri, .void_value, node);
|
||||
}
|
||||
@ -8329,7 +8338,10 @@ fn builtinCall(
|
||||
const loaded = try gz.addUnNode(.load, local_ptr.ptr, node);
|
||||
_ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{
|
||||
.operand = loaded,
|
||||
.options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .export_options_type } }, params[1]),
|
||||
// TODO: the result location here should be `.{ .coerced_ty = .export_options_type }`, but
|
||||
// that currently hits assertions in Sema due to type resolution issues.
|
||||
// See #16603
|
||||
.options = try comptimeExpr(gz, scope, .{ .rl = .none }, params[1]),
|
||||
});
|
||||
return rvalue(gz, ri, .void_value, node);
|
||||
}
|
||||
@ -8363,7 +8375,10 @@ fn builtinCall(
|
||||
},
|
||||
else => return astgen.failNode(params[0], "symbol to export must identify a declaration", .{}),
|
||||
}
|
||||
const options = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .export_options_type } }, params[1]);
|
||||
// TODO: the result location here should be `.{ .coerced_ty = .export_options_type }`, but
|
||||
// that currently hits assertions in Sema due to type resolution issues.
|
||||
// See #16603
|
||||
const options = try comptimeExpr(gz, scope, .{ .rl = .none }, params[1]);
|
||||
_ = try gz.addPlNode(.@"export", node, Zir.Inst.Export{
|
||||
.namespace = namespace,
|
||||
.decl_name = decl_name,
|
||||
@ -8373,7 +8388,10 @@ fn builtinCall(
|
||||
},
|
||||
.@"extern" => {
|
||||
const type_inst = try typeExpr(gz, scope, params[0]);
|
||||
const options = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .extern_options_type } }, params[1]);
|
||||
// TODO: the result location here should be `.{ .coerced_ty = .extern_options_type }`, but
|
||||
// that currently hits assertions in Sema due to type resolution issues.
|
||||
// See #16603
|
||||
const options = try comptimeExpr(gz, scope, .{ .rl = .none }, params[1]);
|
||||
const result = try gz.addExtendedPayload(.builtin_extern, Zir.Inst.BinNode{
|
||||
.node = gz.nodeIndexToRelative(node),
|
||||
.lhs = type_inst,
|
||||
@ -8477,7 +8495,10 @@ fn builtinCall(
|
||||
// zig fmt: on
|
||||
|
||||
.Type => {
|
||||
const operand = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .type_info_type } }, params[0]);
|
||||
// TODO: the result location here should be `.{ .coerced_ty = .type_info_type }`, but
|
||||
// that currently hits assertions in Sema due to type resolution issues.
|
||||
// See #16603
|
||||
const operand = try expr(gz, scope, .{ .rl = .none }, params[0]);
|
||||
|
||||
const gpa = gz.astgen.gpa;
|
||||
|
||||
@ -8604,13 +8625,7 @@ fn builtinCall(
|
||||
|
||||
.splat => {
|
||||
const result_type = try ri.rl.resultType(gz, node, "@splat");
|
||||
const elem_type = try gz.add(.{
|
||||
.tag = .elem_type_index,
|
||||
.data = .{ .bin = .{
|
||||
.lhs = result_type,
|
||||
.rhs = @as(Zir.Inst.Ref, @enumFromInt(0)),
|
||||
} },
|
||||
});
|
||||
const elem_type = try gz.addUnNode(.vector_elem_type, result_type, node);
|
||||
const scalar = try expr(gz, scope, .{ .rl = .{ .ty = elem_type } }, params[0]);
|
||||
const result = try gz.addPlNode(.splat, node, Zir.Inst.Bin{
|
||||
.lhs = result_type,
|
||||
@ -8755,7 +8770,10 @@ fn builtinCall(
|
||||
},
|
||||
.prefetch => {
|
||||
const ptr = try expr(gz, scope, .{ .rl = .none }, params[0]);
|
||||
const options = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .prefetch_options_type } }, params[1]);
|
||||
// TODO: the result location here should be `.{ .coerced_ty = .preftech_options_type }`, but
|
||||
// that currently hits assertions in Sema due to type resolution issues.
|
||||
// See #16603
|
||||
const options = try comptimeExpr(gz, scope, .{ .rl = .none }, params[1]);
|
||||
_ = try gz.addExtendedPayload(.prefetch, Zir.Inst.BinNode{
|
||||
.node = gz.nodeIndexToRelative(node),
|
||||
.lhs = ptr,
|
||||
|
||||
@ -5938,35 +5938,6 @@ pub fn paramSrc(
|
||||
unreachable;
|
||||
}
|
||||
|
||||
pub fn argSrc(
|
||||
mod: *Module,
|
||||
call_node_offset: i32,
|
||||
decl: *Decl,
|
||||
start_arg_i: usize,
|
||||
bound_arg_src: ?LazySrcLoc,
|
||||
) LazySrcLoc {
|
||||
@setCold(true);
|
||||
const gpa = mod.gpa;
|
||||
if (start_arg_i == 0 and bound_arg_src != null) return bound_arg_src.?;
|
||||
const arg_i = start_arg_i - @intFromBool(bound_arg_src != null);
|
||||
const tree = decl.getFileScope(mod).getTree(gpa) catch |err| {
|
||||
// In this case we emit a warning + a less precise source location.
|
||||
log.warn("unable to load {s}: {s}", .{
|
||||
decl.getFileScope(mod).sub_file_path, @errorName(err),
|
||||
});
|
||||
return LazySrcLoc.nodeOffset(0);
|
||||
};
|
||||
const node = decl.relativeToNodeIndex(call_node_offset);
|
||||
var args: [1]Ast.Node.Index = undefined;
|
||||
const call_full = tree.fullCall(&args, node) orelse {
|
||||
assert(tree.nodes.items(.tag)[node] == .builtin_call);
|
||||
const call_args_node = tree.extra_data[tree.nodes.items(.data)[node].rhs - 1];
|
||||
const call_args_offset = decl.nodeIndexToRelative(call_args_node);
|
||||
return mod.initSrc(call_args_offset, decl, arg_i);
|
||||
};
|
||||
return LazySrcLoc.nodeOffset(decl.nodeIndexToRelative(call_full.ast.params[arg_i]));
|
||||
}
|
||||
|
||||
pub fn initSrc(
|
||||
mod: *Module,
|
||||
init_node_offset: i32,
|
||||
|
||||
1134
src/Sema.zig
1134
src/Sema.zig
File diff suppressed because it is too large
Load Diff
15
src/Zir.zig
15
src/Zir.zig
@ -248,6 +248,9 @@ pub const Inst = struct {
|
||||
/// Given a pointer type, returns its element type.
|
||||
/// Uses the `un_node` field.
|
||||
elem_type,
|
||||
/// Given a vector type, returns its element type.
|
||||
/// Uses the `un_node` field.
|
||||
vector_elem_type,
|
||||
/// Given a pointer to an indexable object, returns the len property. This is
|
||||
/// used by for loops. This instruction also emits a for-loop specific compile
|
||||
/// error if the indexable object is not indexable.
|
||||
@ -700,10 +703,16 @@ pub const Inst = struct {
|
||||
/// *?S returns *S
|
||||
/// Uses the `un_node` field.
|
||||
field_base_ptr,
|
||||
/// Given a type, strips all optional and error union types wrapping it.
|
||||
/// e.g. `E!?u32` becomes `u32`, `[]u8` becomes `[]u8`.
|
||||
/// Uses the `un_node` field.
|
||||
opt_eu_base_ty,
|
||||
/// Checks that the type supports array init syntax.
|
||||
/// Returns the underlying indexable type (since the given type may be e.g. an optional).
|
||||
/// Uses the `un_node` field.
|
||||
validate_array_init_ty,
|
||||
/// Checks that the type supports struct init syntax.
|
||||
/// Returns the underlying struct type (since the given type may be e.g. an optional).
|
||||
/// Uses the `un_node` field.
|
||||
validate_struct_init_ty,
|
||||
/// Given a set of `field_ptr` instructions, assumes they are all part of a struct
|
||||
@ -1023,6 +1032,7 @@ pub const Inst = struct {
|
||||
.vector_type,
|
||||
.elem_type_index,
|
||||
.elem_type,
|
||||
.vector_elem_type,
|
||||
.indexable_ptr_len,
|
||||
.anyframe_type,
|
||||
.as,
|
||||
@ -1234,6 +1244,7 @@ pub const Inst = struct {
|
||||
.save_err_ret_index,
|
||||
.restore_err_ret_index,
|
||||
.for_len,
|
||||
.opt_eu_base_ty,
|
||||
=> false,
|
||||
|
||||
.@"break",
|
||||
@ -1327,6 +1338,7 @@ pub const Inst = struct {
|
||||
.vector_type,
|
||||
.elem_type_index,
|
||||
.elem_type,
|
||||
.vector_elem_type,
|
||||
.indexable_ptr_len,
|
||||
.anyframe_type,
|
||||
.as,
|
||||
@ -1522,6 +1534,7 @@ pub const Inst = struct {
|
||||
.for_len,
|
||||
.@"try",
|
||||
.try_ptr,
|
||||
.opt_eu_base_ty,
|
||||
=> false,
|
||||
|
||||
.extended => switch (data.extended.opcode) {
|
||||
@ -1557,6 +1570,7 @@ pub const Inst = struct {
|
||||
.vector_type = .pl_node,
|
||||
.elem_type_index = .bin,
|
||||
.elem_type = .un_node,
|
||||
.vector_elem_type = .un_node,
|
||||
.indexable_ptr_len = .un_node,
|
||||
.anyframe_type = .un_node,
|
||||
.as = .bin,
|
||||
@ -1676,6 +1690,7 @@ pub const Inst = struct {
|
||||
.switch_block_ref = .pl_node,
|
||||
.array_base_ptr = .un_node,
|
||||
.field_base_ptr = .un_node,
|
||||
.opt_eu_base_ty = .un_node,
|
||||
.validate_array_init_ty = .pl_node,
|
||||
.validate_struct_init_ty = .un_node,
|
||||
.validate_struct_init = .pl_node,
|
||||
|
||||
@ -155,6 +155,7 @@ const Writer = struct {
|
||||
.alloc_mut,
|
||||
.alloc_comptime_mut,
|
||||
.elem_type,
|
||||
.vector_elem_type,
|
||||
.indexable_ptr_len,
|
||||
.anyframe_type,
|
||||
.bit_not,
|
||||
@ -229,6 +230,7 @@ const Writer = struct {
|
||||
.make_ptr_const,
|
||||
.validate_deref,
|
||||
.check_comptime_control_flow,
|
||||
.opt_eu_base_ty,
|
||||
=> try self.writeUnNode(stream, inst),
|
||||
|
||||
.ref,
|
||||
|
||||
@ -3039,6 +3039,7 @@ pub const Type = struct {
|
||||
return switch (mod.intern_pool.indexToKey(ty.toIntern())) {
|
||||
.struct_type => |struct_type| {
|
||||
const struct_obj = mod.structPtrUnwrap(struct_type.index).?;
|
||||
assert(struct_obj.haveFieldTypes());
|
||||
return struct_obj.fields.values()[index].ty;
|
||||
},
|
||||
.union_type => |union_type| {
|
||||
|
||||
@ -761,3 +761,17 @@ test "slicing array of zero-sized values" {
|
||||
for (arr[0..]) |zero|
|
||||
try expect(zero == 0);
|
||||
}
|
||||
|
||||
test "array init with no result pointer sets field result types" {
|
||||
const S = struct {
|
||||
// A function parameter has a result type, but no result pointer.
|
||||
fn f(arr: [1]u32) u32 {
|
||||
return arr[0];
|
||||
}
|
||||
};
|
||||
|
||||
const x: u64 = 123;
|
||||
const y = S.f(.{@intCast(x)});
|
||||
|
||||
try expect(y == x);
|
||||
}
|
||||
|
||||
@ -430,3 +430,64 @@ test "method call as parameter type" {
|
||||
try expectEqual(@as(u64, 123), S.foo(S{}, 123));
|
||||
try expectEqual(@as(u64, 500), S.foo(S{}, 500));
|
||||
}
|
||||
|
||||
test "non-anytype generic parameters provide result type" {
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
|
||||
|
||||
const S = struct {
|
||||
fn f(comptime T: type, y: T) !void {
|
||||
try expectEqual(@as(T, 123), y);
|
||||
}
|
||||
|
||||
fn g(x: anytype, y: @TypeOf(x)) !void {
|
||||
try expectEqual(@as(@TypeOf(x), 0x222), y);
|
||||
}
|
||||
};
|
||||
|
||||
var rt_u16: u16 = 123;
|
||||
var rt_u32: u32 = 0x10000222;
|
||||
|
||||
try S.f(u8, @intCast(rt_u16));
|
||||
try S.f(u8, @intCast(123));
|
||||
|
||||
try S.g(rt_u16, @truncate(rt_u32));
|
||||
try S.g(rt_u16, @truncate(0x10000222));
|
||||
|
||||
try comptime S.f(u8, @intCast(123));
|
||||
try comptime S.g(@as(u16, undefined), @truncate(0x99990222));
|
||||
}
|
||||
|
||||
test "argument to generic function has correct result type" {
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
|
||||
|
||||
const S = struct {
|
||||
fn foo(_: anytype, e: enum { a, b }) bool {
|
||||
return e == .b;
|
||||
}
|
||||
|
||||
fn doTheTest() !void {
|
||||
var t = true;
|
||||
|
||||
// Since the enum literal passes through a runtime conditional here, these can only
|
||||
// compile if RLS provides the correct result type to the argument
|
||||
try expect(foo({}, if (!t) .a else .b));
|
||||
try expect(!foo("dummy", if (t) .a else .b));
|
||||
try expect(foo({}, if (t) .b else .a));
|
||||
try expect(!foo(123, if (t) .a else .a));
|
||||
try expect(foo(123, if (t) .b else .b));
|
||||
}
|
||||
};
|
||||
|
||||
try S.doTheTest();
|
||||
try comptime S.doTheTest();
|
||||
}
|
||||
|
||||
@ -1724,3 +1724,17 @@ test "packed struct field in anonymous struct" {
|
||||
fn countFields(v: anytype) usize {
|
||||
return @typeInfo(@TypeOf(v)).Struct.fields.len;
|
||||
}
|
||||
|
||||
test "struct init with no result pointer sets field result types" {
|
||||
const S = struct {
|
||||
// A function parameter has a result type, but no result pointer.
|
||||
fn f(s: struct { x: u32 }) u32 {
|
||||
return s.x;
|
||||
}
|
||||
};
|
||||
|
||||
const x: u64 = 123;
|
||||
const y = S.f(.{ .x = @intCast(x) });
|
||||
|
||||
try expect(y == x);
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ pub export fn entry() void {
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :7:14: error: runtime-known argument passed to comptime-only type parameter
|
||||
// :7:14: error: runtime-known argument passed to parameter of comptime-only type
|
||||
// :9:12: note: declared here
|
||||
// :4:16: note: struct requires comptime because of this field
|
||||
// :4:16: note: types are not available at runtime
|
||||
|
||||
@ -11,4 +11,4 @@ pub export fn entry() void {
|
||||
// target=native
|
||||
//
|
||||
// :6:31: error: unable to resolve comptime value
|
||||
// :6:31: note: argument to parameter with comptime-only type must be comptime-known
|
||||
// :6:31: note: value being casted to 'comptime_int' must be comptime-known
|
||||
|
||||
@ -25,6 +25,8 @@ const S = struct {
|
||||
// target=native
|
||||
//
|
||||
// :3:18: error: expected type 'bool', found 'void'
|
||||
// :18:43: note: parameter type declared here
|
||||
// :8:18: error: expected type 'void', found 'bool'
|
||||
// :19:43: note: parameter type declared here
|
||||
// :14:26: error: runtime-known argument passed to comptime parameter
|
||||
// :20:57: note: declared comptime here
|
||||
|
||||
@ -76,8 +76,9 @@ pub export fn entry8() void {
|
||||
// :19:38: error: value stored in comptime field does not match the default value of the field
|
||||
// :31:19: error: value stored in comptime field does not match the default value of the field
|
||||
// :25:29: note: default value set here
|
||||
// :41:16: error: value stored in comptime field does not match the default value of the field
|
||||
// :41:19: error: value stored in comptime field does not match the default value of the field
|
||||
// :35:29: note: default value set here
|
||||
// :45:12: error: value stored in comptime field does not match the default value of the field
|
||||
// :53:16: error: value stored in comptime field does not match the default value of the field
|
||||
// :53:25: error: value stored in comptime field does not match the default value of the field
|
||||
// :66:43: error: value stored in comptime field does not match the default value of the field
|
||||
// :59:35: error: value stored in comptime field does not match the default value of the field
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
export fn f() void {
|
||||
_ = @as(u32, @splat(5));
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :2:18: error: expected vector type, found 'u32'
|
||||
@ -7,5 +7,5 @@ comptime {
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :3:51: error: expected type 'builtin.GlobalLinkage', found 'u32'
|
||||
// :3:21: error: expected type 'builtin.GlobalLinkage', found 'u32'
|
||||
// :?:?: note: enum declared here
|
||||
|
||||
@ -8,3 +8,4 @@ pub export fn entry() void {
|
||||
// target=native
|
||||
//
|
||||
// :3:21: error: expected type 'u0', found '*const [4:0]u8'
|
||||
// :1:23: note: parameter type declared here
|
||||
|
||||
@ -207,7 +207,7 @@ pub fn addCases(ctx: *Cases) !void {
|
||||
":1:38: note: declared comptime here",
|
||||
":8:36: error: runtime-known argument passed to comptime parameter",
|
||||
":2:41: note: declared comptime here",
|
||||
":13:29: error: runtime-known argument passed to comptime-only type parameter",
|
||||
":13:29: error: runtime-known argument passed to parameter of comptime-only type",
|
||||
":3:24: note: declared here",
|
||||
":12:35: note: struct requires comptime because of this field",
|
||||
":12:35: note: types are not available at runtime",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user