Skip to content

Interface default-method bodies are lost in Go and C and missing from JavaScript implementers (Go output fails with "missing method") #177

Open

Description

@matt-edmondson

What's wrong

A method with a body declared on an interface (a default method) is handled correctly by three targets:

  • C++ writes it as a non-pure virtual.
  • Rust writes it as a trait default.
  • Python gets it through multiple inheritance.

Three other targets lose it:

  • Go. Inside an interface, GenerateFunction writes only the signature and returns (GoGenerator.cs L895-L900). The body is silently dropped, but the method stays in the interface's method set. A struct that lists the interface gets var _ Greeter = (*En)(nil), which then fails to compile.
  • C. An interface becomes a struct of function pointers. GenerateFunctionPointer writes only the pointer, and an interface has no function list, so the body appears nowhere in the file, not even as a note.
  • JavaScript. An implemented interface is written only as // implements Greeter (JavaScriptGenerator.cs ~L376-L381). The implementing class never gets the default, so calling it throws.

Repro

var g = new ClassDeclaration("Greeter") { Kind = TypeDeclarationKind.Interface };
g.Members.Add(new FunctionDeclaration("Name") { ReturnType = "str", IsAbstract = true, IsReadOnly = true });
var hello = new FunctionDeclaration("Hello") { ReturnType = "int", IsReadOnly = true };
hello.Body.Add(new ReturnStatement(new LiteralExpression<int>(42)));
g.Members.Add(hello);
var en = new ClassDeclaration("En") { Kind = TypeDeclarationKind.Struct };
en.Interfaces.Add("Greeter");
// En implements Name only
  • Go (go vet): cannot use (*En)(nil) ... *En does not implement Greeter (missing method Hello)
  • JS (node): new En().Hello() throws TypeError: (intermediate value).Hello is not a function
  • C: the output contains int (*Hello)(const void* self);, and return 42 appears nowhere.

Suggested fix

  • C: write the body as a free function after the struct, int Greeter_Hello(const void* self) { return 42; }, so an implementation can point its slot at it.

  • Go: write the default as a function over the interface, func GreeterHello(self Greeter) int. Then pick one of:

    • (a) on each struct in the document that lists the interface without defining the method, synthesise a forwarder such as func (self En) Hello() int { return GreeterHello(self) };
    • (b) leave the method out of the interface's method set and write a note.

    Either way, the output must compile and nothing may be dropped silently.

  • JS: give an implementing class that doesn't define the method a forwarding method, or copy the default into it.

Acceptance criteria

  • Compile/run tests for Go and C.
  • A node run test for JS.

Activity

  1. matt-edmondson commented on Oct 6, 2026

    @matt-edmondson
    ContributorAuthor

    Triage

    • Category: Bug
    • Priority: High. When an interface has a default method, Go output doesn't compile and C drops the body silently. JavaScript implementers throw at runtime. Nothing in the generated output tells the user the code was lost.
    • Area: Coder/Languages: interface member lowering in GoGenerator, CGenerator and JavaScriptGenerator
    • Duplicates / in progress: Not a duplicate, and no open PR covers it.
    • Next step: Start with Go, because its output fails to build. Emit the default as a free function over the interface, plus a forwarder on each implementer that lacks the method. Then add the C free function and the JS forwarder, with compile and run tests for each target.

    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