You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Calling a method through a pointer or reference, or on another call's result, generates C, C++ and Rust that doesn't compile #170
C maps both TypeIndirection.Reference and TypeIndirection.Pointer to * (L1109-1122). Rust maps Pointer to a raw *const T / *mut T (RustGenerator.cs L1616-1630), and raw pointers don't auto-deref for method calls.
The C output was compiled with gcc 13.3 (-std=c11 -Wall -Wextra -pedantic) and the C++ output with g++ -std=c++20:
C, dist:return Vec_length(&other); passes a const Vec** where a const Vec* is expected. gcc 13 warns with -Wincompatible-pointer-types; GCC 14+ makes this an error by default.
C, chain:return Vec_length(&Vec_twice(&v)); fails with error: lvalue required as unary '&' operand. No method chain, and no call on a function result, can compile in C.
C++, dist with a Pointer parameter:return other.length(); fails with error: request for member 'length' in 'other', which is of pointer type 'const Vec*' (maybe you meant to use '->' ?).
Rust, same Pointer parameter:other.length() on a *const Vec has no such method. This follows from the type mapping; it was not compiled.
Why it matters
CLAUDE.md says the receiver is modelled because languages disagree about it (a.b(c) versus b(&a, c)), but the lowering only handles a by-value lvalue receiver. Taking const T& parameters is the normal C++ idiom, and the C generator's own receiver is a T* self. Any method call on a reference or pointer parameter, and any method chain, produces C that doesn't compile.
This is distinct from #141, which is about having no node for the current instance. This issue is about ordinary parameters and call results.
Suggested fix
Have the C, C++ and Rust generators push the static-type scope that already exists in StandardLanguageGenerator, so the receiver's indirection is known. Today only GoGenerator pushes it.
C:
Omit the & when the receiver's static type is already a pointer or reference.
For a non-lvalue receiver (CallExpression, BinaryExpression, ConditionalExpression, a literal), hoist it into a temporary or emit a WriteInexpressible note.
C++: emit -> when the receiver's static type is a Pointer.
Rust: emit unsafe { (*p).m() }, or an agreed alternative, when the receiver is a Pointer.
Acceptance criteria
CGeneratedSourceCompilesTests, CppGeneratedSourceCompilesTests and RustGeneratedSourceCompilesTests each gain three cases, and all of them compile:
Priority: High. Any method chain, and any call on a reference parameter, produces C that doesn't compile. Taking a const T& parameter is the normal C++ idiom, so this is hit routinely. In C++ and Rust the same happens on pointer receivers.
Area:Coder/Languages: CGenerator, CppGenerator and RustGenerator call-receiver lowering, plus the static-type scope in StandardLanguageGenerator.
Next step: Wire the static-type scope into the C, C++ and Rust generators the way GoGenerator does. Then add the three receiver cases (reference, pointer, chained) to each *GeneratedSourceCompilesTests.
Activity
matt-edmondson commentedon Oct 6, 2026
Triage
const T¶meter is the normal C++ idiom, so this is hit routinely. In C++ and Rust the same happens on pointer receivers.Coder/Languages:CGenerator,CppGeneratorandRustGeneratorcall-receiver lowering, plus the static-type scope inStandardLanguageGenerator.GoGeneratordoes. Then add the three receiver cases (reference, pointer, chained) to each*GeneratedSourceCompilesTests.Generated by Claude Code