Skip to content

Rust and Go output doesn't compile for mixed numeric operands or implicit widening: x * 2 on an f64, a + b on i64/i32, long f(int b) { return b; } #238

Open

Description

@matt-edmondson

What's wrong

The AST follows C-family numeric semantics: an int mixed with a long or a double is widened implicitly, and an int value can be assigned to, returned as, or passed as a long or double.

StandardLanguageGenerator already computes the promoted type. Promoted() is at Coder/Languages/StandardLanguageGenerator.cs:398, and StaticType uses it at :345/:356 (since #152 / PR #220). But that type is only used for inference:

  • GenerateBinary (StandardLanguageGenerator.cs:~1184) writes both operands exactly as they are.
  • Assignments, initialisers and returns do the same.

Rust and Go do no implicit numeric conversion, so the output does not compile in either. C, C++, C#, JavaScript and Python accept the same AST.

Reproduction (HEAD 440ef35, rustc and go vet)

var scale = new FunctionDeclaration("scale") { ReturnType = "double" };
scale.Parameters.Add(new Parameter("x", "double"));
scale.Body.Add(new ReturnStatement(new BinaryExpression(new VariableReference("x"), BinaryOperator.Multiply, Literal.Number(2))));

var widen = new FunctionDeclaration("widen") { ReturnType = "long" };
widen.Parameters.Add(new Parameter("a", "long"));
widen.Parameters.Add(new Parameter("b", "int"));
widen.Body.Add(new VariableDeclaration("half", "double", new VariableReference("b")));
widen.Body.Add(new ReturnStatement(new BinaryExpression(new VariableReference("a"), BinaryOperator.Add, new VariableReference("b"))));

Rust:

pub fn scale(x: f64) -> f64 { return (x * 2); }          // E0277: cannot multiply `f64` by `{integer}`
pub fn widen(a: i64, b: i32) -> i64 {
    let mut half: f64 = b;                                // E0308: mismatched types
    return (a + b);                                       // E0277: cannot add `i32` to `i64`
}

Go:

var half float64 = b   // cannot use b (variable of type int) as float64 value
return (a + b)         // invalid operation: a + b (mismatched types int64 and int)

Go accepts x * 2 because 2 is an untyped constant; Rust has no such rule.

#152 fixed the inferred type of mixed expressions, but the emitted operands were never converted.

Suggested fix / acceptance criteria

  • When a binary expression's operands have different known numeric static types, the Rust and Go generators convert the narrower operand to the Promoted type: b as i64 / int64(b), x as f64 / float64(x).
  • An integer literal next to a floating operand is written as a floating literal (2.0) or converted.
  • The same conversion applies wherever a known narrower numeric type meets a declared wider type:
    • a typed VariableDeclaration/FieldDeclaration initialiser
    • an AssignmentStatement value
    • a ReturnStatement against the function's return type
    • a call argument against a known parameter type
  • Narrowing (e.g. double to int) is left as it is; the AST never makes it implicit.
  • RustGeneratedSourceCompilesTests and GoGeneratedSourceCompilesTests compile cases covering:
    • mixed int/long operands
    • an int literal next to a double
    • an int-typed value initialising a double
    • an int-typed value returned as a long

Activity

  1. matt-edmondson commented on Oct 17, 2026

    @matt-edmondson
    ContributorAuthor

    Triage

    Notes: Add one hook such as ConvertTo(expr, fromType, toType) that defaults to identity and is overridden for Rust (as) and Go (T(x)), then call it at each site where a known narrower type meets a wider one. That keeps the other five targets unchanged. Start with the binary-operand case, since x * 2 on an f64 is the most common failure.


    Generated by Claude Code

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

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions