Skip to content

ast, cgen: fix receiver methods on embedded interfaces (fix #19550)#27476

Open
Macho0x wants to merge 1 commit into
vlang:masterfrom
Macho0x:fix-interface-receiver-method-embed-19550
Open

ast, cgen: fix receiver methods on embedded interfaces (fix #19550)#27476
Macho0x wants to merge 1 commit into
vlang:masterfrom
Macho0x:fix-interface-receiver-method-embed-19550

Conversation

@Macho0x

@Macho0x Macho0x commented Jun 16, 2026

Copy link
Copy Markdown

This PR fixes the compiler error and incorrect runtime behavior when calling a receiver method (defined outside an interface) on an interface that embeds another interface.

Fixes #19550.

Problem

Given:

interface Node {
    name string
mut:
    children []&Node
}

fn (mut node Node) append_child(child &Node) {
    node.children << child
}

interface Element {
    Node
    attributes map[string]string
}

// HTMLBodyElement implements Element (and therefore Node)

Calling element.append_child(&Node(&Text{...})) on a value of type &Element failed with:

error: cannot implement interface Element with a different interface &Node

Even when the checker error was bypassed, the generated code reinterpreted the Element* interface struct as a Node*, leading to incorrect field offsets and broken runtime behavior.

Root cause

  1. When an interface embeds another interface, new_method_with_receiver_type was rewriting self-referential parameters of concrete receiver methods to match the outer interface type. That made signatures such as fn (mut n Node) add(child &Node) appear to require &Element, which is wrong.

  2. In cgen, calling a method inherited from an embedded interface used a raw pointer cast from the outer interface struct to the embedded interface struct. The two structs have different C layouts, so the cast read/wrote the wrong fields.

Fix

  • vlib/v/ast/ast.v: new_method_with_receiver_type now only transforms self-referential parameters for interface method declarations (no_body == true). Concrete receiver methods keep their original parameter types.
  • vlib/v/gen/c/fn.v: when the receiver is an interface and the method comes from an embedded interface, generate an interface-to-interface conversion (I_as_I) instead of a pointer cast. For mut receivers, the converted value is stored in a temporary whose address is passed to the method; the field pointers inside the temporary still refer to the original object, so mutations are visible.

Test
Added vlib/v/tests/interfaces/interface_receiver_method_on_embedded_interface_test.v, which exercises:

  • a mut receiver method inherited from an embedded interface,
  • passing a concrete value cast to the embedded interface as an argument,
  • correct field access on both the outer and embedded interface after the call.

Verification

- Only rewrite self-referential parameters for interface method declarations in new_method_with_receiver_type; keep concrete receiver method parameters unchanged.

- Generate an interface-to-interface conversion for the receiver when calling a method inherited from an embedded interface, instead of reinterpreting the interface struct pointer.

- Add regression test.
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

V error with receiver method on interface

1 participant