Skip to content

Constructing a declared type with member initialisers gives wrong fields in JavaScript (new Point({x:0,y:0}) sets x to the object) and a TypeError in Python #335

Open

Description

@matt-edmondson

What's wrong

For a ConstructionExpression with MemberInitialiser arguments, the JavaScript generator emits new T({ x: …, y: … }) (JavaScriptGenerator.cs L233-L299). Its doc comment calls this "the options-object convention". The classes the same generator writes don't follow that convention:

  • Class with a constructor: the constructor takes positional parameters (constructor(x, y = 0)), so the whole options object lands in the first parameter.
  • Class with no constructor: the argument is ignored, so every field stays undefined.

Python emits T(x=…, y=…) (PythonGenerator.cs L354-L431). That works only when the class happens to have a constructor whose parameters have those names. A class with fields and no constructor has no __init__ that accepts keywords, so the call raises TypeError.

Reproduction (HEAD 667637c)

  1. Repo exemplar CompiledExemplar.Point() (constructor Point(x, y = 0)), with zero() and OriginTable(). Both results are silently wrong in JavaScript:
    • Point.zero() gives {"x":{"x":0,"y":0},"y":0}
    • ORIGINS[1] is {"x":{"x":1,"y":1},"y":0}
  2. Class with no constructor: class Vec { public int x; public int y; } and Vec make() { return Vec{ x = 1, y = 2 }; }.
    • JavaScript: make().x is undefined.
    • Python: make() raises TypeError: Vec() takes no arguments.
    • C++ (Vec{ .x = 1, .y = 2 }) and C# (new Vec { x = 1, y = 2 }) are correct.

Related: C++ rejects the Point exemplar with "designated initializers cannot be used with a non-aggregate type", because Point declares a constructor.

This is not #297, which covers positional construction in C, Go and Rust and explicitly excludes the member-initialiser form.

Suggested fix / acceptance criteria

When the type is declared in the generation root:

  • JavaScript, no constructor: emit Object.assign(new T(), { x: 1, y: 2 }).
  • JavaScript, constructor whose parameters match the member names: pass the values positionally, in parameter order.
  • Python, no __init__: emit something that sets the attributes, such as a helper or a synthesised keyword __init__.
  • Run tests (node, python3) assert the fields hold the given values, both with and without a constructor.

Activity

  1. matt-edmondson commented on Oct 20, 2026

    @matt-edmondson
    ContributorAuthor

    Triage


    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