Repository navigation
Description
Hi,
General context of this work is in #188, including the AI-disclaimers.
No in-tree codec is affected by this issue; std works around it with a local variable, and 18 std functions carry a TODO saying that it should be able to chain the two calls. Claude judges the prospective risk of it as "low": the checker accepts the chained call, but the generated C does not compile.
The following is Sol 6.1's writeup of the issue. Includes a repro, generated C, MSVC 19.51, clang-cl 18, gcc 13.3 and clang 18 compile errors, and a control with a local that compiles. The cause is reasoned from source.
A method call on a struct value passes the receiver's address. When the receiver is itself a call result, such as args.pb.pixel_format(), cgen takes the address of that call expression, which is not an lvalue in C.
Minimal repro:
pub struct repro?(
n : base.u32,
)
pub func repro.f!(pb: base.pixel_buffer) {
this.n = args.pb.pixel_format().bits_per_pixel()
}
Actual: at b36a81d3, wuffs-c gen -package_name repro repro.wuffs exits 0 with empty stderr and emits:
self->private_impl.f_n = wuffs_base__pixel_format__bits_per_pixel(&wuffs_base__pixel_buffer__pixel_format(&a_pb));Compiling the amalgamation with WUFFS_IMPLEMENTATION fails with gcc 13.3 and clang 18 (-std=c99 -Wall -c, exit 1), MSVC (cl /c /W3, exit 2) and clang-cl 18 (exit 1):
repro-release.c:26163:69: error: lvalue required as unary ‘&’ operand
repro-release.c:26163:69: error: cannot take the address of an rvalue of type 'wuffs_base__pixel_format' (aka 'struct wuffs_base__pixel_format__struct')
repro-release.c(26163): error C2102: '&' requires l-value
Control: pf = args.pb.pixel_format() into a local pf : base.pixel_format, then this.n = pf.bits_per_pixel(), compiles with all four compilers. std's TODO, for example:
wuffs/std/bmp/decode_bmp.wuffs
Lines 540 to 543 in b36a81d
| // TODO: the dst_pixfmt variable shouldn't be necessary. We should be able | |
| // to chain the two calls: "args.dst.pixel_format().bits_per_pixel()". | |
| dst_pixfmt = args.dst.pixel_format() | |
| dst_bits_per_pixel = dst_pixfmt.bits_per_pixel() |
Expected: cgen lowers a method call on a call result, for example through a temporary, or the checker rejects it.
Cause: writeExprUserDefinedCall writes & before any receiver that is not a pointer:
Lines 543 to 573 in b36a81d
| func (g *gen) writeExprUserDefinedCall(b *buffer, n *a.Expr, depth uint32) error { | |
| method := n.LHS().AsExpr() | |
| recv := method.LHS().AsExpr() | |
| recvTyp, addr := recv.MType(), "&" | |
| if p := recvTyp.Decorator(); p == t.IDNptr || p == t.IDPtr { | |
| recvTyp, addr = recvTyp.Inner(), "" | |
| } | |
| if recvTyp.Decorator() != 0 { | |
| return fmt.Errorf("cannot generate user-defined method call %q for receiver type %q", | |
| n.Str(g.tm), recv.MType().Str(g.tm)) | |
| } | |
| qid := recvTyp.QID() | |
| pkgStr := g.packagePrefix(qid) | |
| typStr := qid[1].Str(g.tm) | |
| methodStr := method.Ident().Str(g.tm) | |
| if (qid[0] == t.IDBase) && qid[1].IsRangeType() && strings.HasPrefix(methodStr, "get_") { | |
| pkgStr = "wuffs_private_impl__" | |
| } | |
| b.printf("%s%s__%s(", pkgStr, typStr, methodStr) | |
| if !recvTyp.IsEtcUtilityType() { | |
| b.writes(addr) | |
| if err := g.writeExpr(b, recv, false, depth); err != nil { | |
| return err | |
| } | |
| if len(n.Args()) > 0 { | |
| b.writes(", ") | |
| } | |
| } | |
| return g.writeArgs(b, n.Args(), depth) |
Activity