Fix #3451: qualify an explicit implementation as the type implements it - #3976
Merged
Conversation
Tuple element names and nullability are not part of a type's identity, so an interface resolved through one of its members carries neither. Naming an explicit implementation from that type produced `void I<(int, int)>.M()` on a type declared as `I<(int A, int B)>`, which the C# compiler rejects outright with CS0540 - the decompiled source did not build. The nullable case was already recorded as a TODO in the NullableRefTypes fixture, where the mismatch costs a CS8643 warning rather than an error. The implementing type's base-type list is the only place those annotations are recorded, so the qualifier is looked up there. Three call sites derived it independently - the AST builder for all five member kinds, the ambience used for tooltips and tree labels, and the forwarders synthesized for MethodImpls - so they now share one helper rather than repeating the rule twice more. Matching while ignoring tuple names and nullability cannot be ambiguous: implementing two interfaces that differ only in those is itself an error (CS8140, CS8645). Assisted-by: Claude:claude-opus-5[1m]:Claude Code
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 #3451.
Problem
Tuple element names and nullability are not part of a type's identity, so an interface resolved through one of its members carries neither. Naming an explicit interface implementation from that type therefore dropped them:
That output does not build -
error CS0540: containing type does not implement interface 'I1<(int, int)>', exactly as reported.The same rule applies to nullability, where the mismatch costs a CS8643 warning rather than an error. That half was already recorded as a
// TODO: declaring type is not yet rendered with nullability annotations from the base typein theNullableRefTypesfixture; this change resolves it, so the TODO and the two expectations it guarded are updated.Solution
The implementing type's base-type list is the only place those annotations are recorded, so the qualifier is looked up there via the new
TypeSystemExtensions.GetInterfaceAsImplementedBy(internal - no public API change).Three call sites derived the qualifier independently and all three had the bug; they now share the helper:
TypeSystemAstBuilder.GetExplicitInterfaceType- methods, properties, indexers, events and operatorsCSharpAmbience.GetExplicitInterfaceType- tooltips and tree labelsCSharpDecompiler.AddInterfaceImplHelpers- the forwarders synthesized for MethodImplsMatching while ignoring tuple names and nullability cannot be ambiguous: implementing two interfaces that differ only in those is itself an error (CS8140, CS8645).
Tests
TupleTestsgains the issue's repro as a Pretty fixture, which fails without the fix.NullableRefTypesT06/T07 now expect the annotated qualifier.The forwarder path has no new test: reaching it needs a MethodImpl that is not expressible as a C# explicit implementation, so it would take an IL fixture, and all five member kinds route through the one helper the tuple fixture already covers.
Decompiler suite green (3369),
ilspycmdtests and the ILSpy tree/tooltip tests that consume the ambience green.Written by an AI agent (Claude) on Siegfried's behalf.