Don't emit duplicate members for structs with multiple vtbl bases#748
Merged
tannergooding merged 1 commit intoJul 13, 2026
Conversation
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>
This was referenced Jul 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 becomingDispose-- were emitted more than once, producing aCS0111compile 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/~Barare both overridden by the implicitBaz::~Baz, and the primary vtable slot destroys the complete object, so a singleDispose()vialpVtbl[0]is right. Legitimate cross-base overloads with differing signatures are preserved; only trueCS0111/CS0102collisions are dropped. The fix covers both the function-pointer helper path and theexplicit-vtblspath.Also ensure the
'multiple virtual bases'warning fires even when the derived type introduces its own virtual method. PreviouslyHasVtblonly counted the indirect vtables inside the!hasVtblbranch, so the non-unifying case -- distinct secondary-base methods flattened onto the primarylpVtbl(e.g.Bar::BarMethoddispatched throughlpVtbl[0], colliding withFoo::FooMethod) -- was emitted with incorrect dispatch and no diagnostic at all. The count is now computed unconditionally;hasBaseVtblassignment 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).