8000 codegen: a method call on a struct returned by another call takes the address of an rvalue · Issue #276 · google/wuffs · GitHub
[go: up one dir, main page]

Skip to content

codegen: a method call on a struct returned by another call takes the address of an rvalue #276

Open

Description

@dio-gh

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:

// 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:

wuffs/internal/cgen/expr.go

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      0