mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
Merge pull request #15028 from Vexu/compile-errors
Sema: improve error message of field access of wrapped type
This commit is contained in:
commit
f7204c7f37
48
src/Sema.zig
48
src/Sema.zig
@ -2127,6 +2127,50 @@ fn failWithUseOfAsync(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError
|
||||
return sema.failWithOwnedErrorMsg(msg);
|
||||
}
|
||||
|
||||
fn failWithInvalidFieldAccess(sema: *Sema, block: *Block, src: LazySrcLoc, object_ty: Type, field_name: []const u8) CompileError {
|
||||
const inner_ty = if (object_ty.isSinglePointer()) object_ty.childType() else object_ty;
|
||||
|
||||
if (inner_ty.zigTypeTag() == .Optional) opt: {
|
||||
var buf: Type.Payload.ElemType = undefined;
|
||||
const child_ty = inner_ty.optionalChild(&buf);
|
||||
if (!typeSupportsFieldAccess(child_ty, field_name)) break :opt;
|
||||
const msg = msg: {
|
||||
const msg = try sema.errMsg(block, src, "optional type '{}' does not support field access", .{object_ty.fmt(sema.mod)});
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
try sema.errNote(block, src, msg, "consider using '.?', 'orelse', or 'if'", .{});
|
||||
break :msg msg;
|
||||
};
|
||||
return sema.failWithOwnedErrorMsg(msg);
|
||||
} else if (inner_ty.zigTypeTag() == .ErrorUnion) err: {
|
||||
const child_ty = inner_ty.errorUnionPayload();
|
||||
if (!typeSupportsFieldAccess(child_ty, field_name)) break :err;
|
||||
const msg = msg: {
|
||||
const msg = try sema.errMsg(block, src, "error union type '{}' does not support field access", .{object_ty.fmt(sema.mod)});
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
try sema.errNote(block, src, msg, "consider using 'try', 'catch', or 'if'", .{});
|
||||
break :msg msg;
|
||||
};
|
||||
return sema.failWithOwnedErrorMsg(msg);
|
||||
}
|
||||
return sema.fail(block, src, "type '{}' does not support field access", .{object_ty.fmt(sema.mod)});
|
||||
}
|
||||
|
||||
fn typeSupportsFieldAccess(ty: Type, field_name: []const u8) bool {
|
||||
switch (ty.zigTypeTag()) {
|
||||
.Array => return mem.eql(u8, field_name, "len"),
|
||||
.Pointer => {
|
||||
const ptr_info = ty.ptrInfo().data;
|
||||
if (ptr_info.size == .Slice) {
|
||||
return mem.eql(u8, field_name, "ptr") or mem.eql(u8, field_name, "len");
|
||||
} else if (ptr_info.pointee_type.zigTypeTag() == .Array) {
|
||||
return mem.eql(u8, field_name, "len");
|
||||
} else return false;
|
||||
},
|
||||
.Type, .Struct, .Union => return true,
|
||||
else => return false,
|
||||
}
|
||||
}
|
||||
|
||||
/// We don't return a pointer to the new error note because the pointer
|
||||
/// becomes invalid when you add another one.
|
||||
fn errNote(
|
||||
@ -23321,7 +23365,7 @@ fn fieldVal(
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
return sema.fail(block, src, "type '{}' does not support field access", .{object_ty.fmt(sema.mod)});
|
||||
return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name);
|
||||
}
|
||||
|
||||
fn fieldPtr(
|
||||
@ -23535,7 +23579,7 @@ fn fieldPtr(
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
return sema.fail(block, src, "type '{}' does not support field access", .{object_ty.fmt(sema.mod)});
|
||||
return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name);
|
||||
}
|
||||
|
||||
fn fieldCallBind(
|
||||
|
||||
@ -3815,6 +3815,8 @@ pub const DeclGen = struct {
|
||||
|
||||
const field_ty = union_obj.fields.values()[field_index].ty;
|
||||
if (union_obj.layout == .Packed) {
|
||||
if (!field_ty.hasRuntimeBits())
|
||||
return llvm_union_ty.constNull();
|
||||
const non_int_val = try lowerValue(dg, .{ .ty = field_ty, .val = tag_and_val.val });
|
||||
const ty_bit_size = @intCast(u16, field_ty.bitSize(target));
|
||||
const small_int_ty = dg.context.intType(ty_bit_size);
|
||||
|
||||
@ -1113,6 +1113,14 @@ pub const Value = extern union {
|
||||
.bool_true,
|
||||
=> return BigIntMutable.init(&space.limbs, 1).toConst(),
|
||||
|
||||
.enum_field_index => {
|
||||
const index = val.castTag(.enum_field_index).?.data;
|
||||
return BigIntMutable.init(&space.limbs, index).toConst();
|
||||
},
|
||||
.runtime_value => {
|
||||
const sub_val = val.castTag(.runtime_value).?.data;
|
||||
return sub_val.toBigIntAdvanced(space, target, opt_sema);
|
||||
},
|
||||
.int_u64 => return BigIntMutable.init(&space.limbs, val.castTag(.int_u64).?.data).toConst(),
|
||||
.int_i64 => return BigIntMutable.init(&space.limbs, val.castTag(.int_i64).?.data).toConst(),
|
||||
.int_big_positive => return val.castTag(.int_big_positive).?.asBigInt(),
|
||||
@ -1979,6 +1987,13 @@ pub const Value = extern union {
|
||||
.variable,
|
||||
=> .gt,
|
||||
|
||||
.enum_field_index => return std.math.order(lhs.castTag(.enum_field_index).?.data, 0),
|
||||
.runtime_value => {
|
||||
// This is needed to correctly handle hashing the value.
|
||||
// Checks in Sema should prevent direct comparisons from reaching here.
|
||||
const val = lhs.castTag(.runtime_value).?.data;
|
||||
return val.orderAgainstZeroAdvanced(opt_sema);
|
||||
},
|
||||
.int_u64 => std.math.order(lhs.castTag(.int_u64).?.data, 0),
|
||||
.int_i64 => std.math.order(lhs.castTag(.int_i64).?.data, 0),
|
||||
.int_big_positive => lhs.castTag(.int_big_positive).?.asBigInt().orderAgainstScalar(0),
|
||||
|
||||
@ -32,3 +32,14 @@ test "@src used as a comptime parameter" {
|
||||
const T2 = S.Foo(@src());
|
||||
try expect(T1 != T2);
|
||||
}
|
||||
|
||||
test "@src in tuple passed to anytype function" {
|
||||
const S = struct {
|
||||
fn Foo(a: anytype) u32 {
|
||||
return a[0].line;
|
||||
}
|
||||
};
|
||||
const l1 = S.Foo(.{@src()});
|
||||
const l2 = S.Foo(.{@src()});
|
||||
try expect(l1 != l2);
|
||||
}
|
||||
|
||||
@ -1492,3 +1492,43 @@ test "union reassignment can use previous value" {
|
||||
a = U{ .b = a.a };
|
||||
try expect(a.b == 32);
|
||||
}
|
||||
|
||||
test "packed union with zero-bit field" {
|
||||
if (builtin.zig_backend == .stage2_x86_64) 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_wasm) return error.SkipZigTest; // TODO
|
||||
|
||||
const S = packed struct {
|
||||
nested: packed union {
|
||||
zero: void,
|
||||
sized: u32,
|
||||
},
|
||||
bar: u32,
|
||||
|
||||
fn doTest(self: @This()) !void {
|
||||
try expect(self.bar == 42);
|
||||
}
|
||||
};
|
||||
try S.doTest(.{ .nested = .{ .zero = {} }, .bar = 42 });
|
||||
}
|
||||
|
||||
test "reinterpreting enum value inside packed union" {
|
||||
if (builtin.zig_backend == .stage2_x86_64) 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_wasm) return error.SkipZigTest; // TODO
|
||||
|
||||
const U = packed union {
|
||||
tag: enum { a, b },
|
||||
val: u8,
|
||||
|
||||
fn doTest() !void {
|
||||
var u: @This() = .{ .tag = .a };
|
||||
u.val += 1;
|
||||
try expect(u.tag == .b);
|
||||
}
|
||||
};
|
||||
try U.doTest();
|
||||
comptime try U.doTest();
|
||||
}
|
||||
|
||||
20
test/cases/compile_errors/field_access_of_wrapped_type.zig
Normal file
20
test/cases/compile_errors/field_access_of_wrapped_type.zig
Normal file
@ -0,0 +1,20 @@
|
||||
const Foo = struct {
|
||||
a: i32,
|
||||
};
|
||||
export fn f1() void {
|
||||
var foo: ?Foo = undefined;
|
||||
foo.a += 1;
|
||||
}
|
||||
export fn f2() void {
|
||||
var foo: anyerror!Foo = undefined;
|
||||
foo.a += 1;
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :6:8: error: optional type '?tmp.Foo' does not support field access
|
||||
// :6:8: note: consider using '.?', 'orelse', or 'if'
|
||||
// :10:8: error: error union type 'anyerror!tmp.Foo' does not support field access
|
||||
// :10:8: note: consider using 'try', 'catch', or 'if'
|
||||
Loading…
x
Reference in New Issue
Block a user