Skip to content

Don't emit duplicate members for structs with multiple vtbl bases#748

Merged
tannergooding merged 1 commit into
dotnet:mainfrom
tannergooding:tannergooding-fix-multibase-vtbl-duplication
Jul 13, 2026
Merged

Don't emit duplicate members for structs with multiple vtbl bases#748
tannergooding merged 1 commit into
dotnet:mainfrom
tannergooding:tannergooding-fix-multibase-vtbl-duplication

Conversation

@tannergooding

Copy link
Copy Markdown
Member

Fixes #592.

A struct deriving from two or more bases that each carry a virtual table flattens every base into the single lpVtbl. Members from different bases that map to the same C# name and signature -- most notably each base's virtual destructor becoming Dispose -- were emitted more than once, producing a CS0111 compile error.

Deduplicate the flattened vtbl members by their emitted name and canonical parameter types so the output at least compiles. This is correct for the destructor-unifying case: ~Foo/~Bar are both overridden by the implicit Baz::~Baz, and the primary vtable slot destroys the complete object, so a single Dispose() via lpVtbl[0] is right. Legitimate cross-base overloads with differing signatures are preserved; only true CS0111/CS0102 collisions are dropped. The fix covers both the function-pointer helper path and the explicit-vtbls path.


Also ensure the 'multiple virtual bases' warning fires even when the derived type introduces its own virtual method. Previously HasVtbl only counted the indirect vtables inside the !hasVtbl branch, so the non-unifying case -- distinct secondary-base methods flattened onto the primary lpVtbl (e.g. Bar::BarMethod dispatched through lpVtbl[0], colliding with Foo::FooMethod) -- was emitted with incorrect dispatch and no diagnostic at all. The count is now computed unconditionally; hasBaseVtbl assignment is unchanged, so generated code is byte-identical and only the diagnostic differs.

The bindings remain incomplete for multiple virtual bases (the warning still reports this), since correctly modeling secondary-base dispatch requires a distinct vtable pointer per base subobject. That redesign is deferred to a follow-up.

Adds three golden regression tests (fnptr dedupe, explicit-vtbl dedupe, and the non-unifying warned case).

A struct deriving from two or more bases that each carry a virtual table flattens every base into the single lpVtbl. Members from different bases that map to the same C# name and signature (most notably each base's virtual destructor becoming Dispose) were emitted more than once, producing a CS0111 compile error.

Deduplicate the flattened vtbl members by their emitted name and canonical parameter types so the output at least compiles. The bindings remain incomplete for multiple virtual bases (a warning is still reported), since correctly modeling that requires a distinct vtable pointer per base subobject.

Also ensure the "multiple virtual bases" warning fires even when the derived type introduces its own virtual method. Previously HasVtbl only counted the indirect vtables when the derived type had no vtable of its own, so the non-unifying case (distinct secondary-base methods flattened onto the primary lpVtbl) was silently emitted with incorrect dispatch and no diagnostic.

Fixes dotnet#592

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@tannergooding tannergooding merged commit 0112e43 into dotnet:main Jul 13, 2026
14 checks passed
@tannergooding tannergooding deleted the tannergooding-fix-multibase-vtbl-duplication branch July 13, 2026 15:57
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.

Struct with two or more bases will have duplicate member names

1 participant