authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2023-03-21 00:09:00+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2023-03-21 00:34:12+02:00
log82133cd992575ab567091eaf2f12fbe5e326b5df
tree652d8e694c8a1a9ca72006a42b03908672aa90e0
parent9d9815fb9c21c91df41422ff582402fb56029328

Sema: improve error message of field access of wrapped type

Closes #15027

2 files changed, 66 insertions(+), 2 deletions(-)

src/Sema.zig+46-2
......@@ -2127,6 +2127,50 @@ fn failWithUseOfAsync(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError
21272127 return sema.failWithOwnedErrorMsg(msg);
21282128}
21292129
2130fn failWithInvalidFieldAccess(sema: *Sema, block: *Block, src: LazySrcLoc, object_ty: Type, field_name: []const u8) CompileError {
2131 const inner_ty = if (object_ty.isSinglePointer()) object_ty.childType() else object_ty;
2132
2133 if (inner_ty.zigTypeTag() == .Optional) opt: {
2134 var buf: Type.Payload.ElemType = undefined;
2135 const child_ty = inner_ty.optionalChild(&buf);
2136 if (!typeSupportsFieldAccess(child_ty, field_name)) break :opt;
2137 const msg = msg: {
2138 const msg = try sema.errMsg(block, src, "optional type '{}' does not support field access", .{object_ty.fmt(sema.mod)});
2139 errdefer msg.destroy(sema.gpa);
2140 try sema.errNote(block, src, msg, "consider using '.?', 'orelse', or 'if'", .{});
2141 break :msg msg;
2142 };
2143 return sema.failWithOwnedErrorMsg(msg);
2144 } else if (inner_ty.zigTypeTag() == .ErrorUnion) err: {
2145 const child_ty = inner_ty.errorUnionPayload();
2146 if (!typeSupportsFieldAccess(child_ty, field_name)) break :err;
2147 const msg = msg: {
2148 const msg = try sema.errMsg(block, src, "error union type '{}' does not support field access", .{object_ty.fmt(sema.mod)});
2149 errdefer msg.destroy(sema.gpa);
2150 try sema.errNote(block, src, msg, "consider using 'try', 'catch', or 'if'", .{});
2151 break :msg msg;
2152 };
2153 return sema.failWithOwnedErrorMsg(msg);
2154 }
2155 return sema.fail(block, src, "type '{}' does not support field access", .{object_ty.fmt(sema.mod)});
2156}
2157
2158fn typeSupportsFieldAccess(ty: Type, field_name: []const u8) bool {
2159 switch (ty.zigTypeTag()) {
2160 .Array => return mem.eql(u8, field_name, "len"),
2161 .Pointer => {
2162 const ptr_info = ty.ptrInfo().data;
2163 if (ptr_info.size == .Slice) {
2164 return mem.eql(u8, field_name, "ptr") or mem.eql(u8, field_name, "len");
2165 } else if (ptr_info.pointee_type.zigTypeTag() == .Array) {
2166 return mem.eql(u8, field_name, "len");
2167 } else return false;
2168 },
2169 .Type, .Struct, .Union => return true,
2170 else => return false,
2171 }
2172}
2173
21302174/// We don't return a pointer to the new error note because the pointer
21312175/// becomes invalid when you add another one.
21322176fn errNote(
......@@ -23321,7 +23365,7 @@ fn fieldVal(
2332123365 },
2332223366 else => {},
2332323367 }
23324 return sema.fail(block, src, "type '{}' does not support field access", .{object_ty.fmt(sema.mod)});
23368 return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name);
2332523369}
2332623370
2332723371fn fieldPtr(
......@@ -23535,7 +23579,7 @@ fn fieldPtr(
2353523579 },
2353623580 else => {},
2353723581 }
23538 return sema.fail(block, src, "type '{}' does not support field access", .{object_ty.fmt(sema.mod)});
23582 return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name);
2353923583}
2354023584
2354123585fn fieldCallBind(
test/cases/compile_errors/field_access_of_wrapped_type.zig created+20
......@@ -0,0 +1,20 @@
1const Foo = struct {
2 a: i32,
3};
4export fn f1() void {
5 var foo: ?Foo = undefined;
6 foo.a += 1;
7}
8export fn f2() void {
9 var foo: anyerror!Foo = undefined;
10 foo.a += 1;
11}
12
13// error
14// backend=stage2
15// target=native
16//
17// :6:8: error: optional type '?tmp.Foo' does not support field access
18// :6:8: note: consider using '.?', 'orelse', or 'if'
19// :10:8: error: error union type 'anyerror!tmp.Foo' does not support field access
20// :10:8: note: consider using 'try', 'catch', or 'if'