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:
Andrew Kelley 2023-08-10 11:50:01 -07:00 committed by GitHub
commit 275e926cf8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 860 additions and 493 deletions

View File

@ -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))));
},
};
}

View File

@ -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,

View File

@ -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,

File diff suppressed because it is too large Load Diff

View File

@ -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,

View File

@ -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,

View File

@ -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| {

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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",