Skip to content

Fix #3451: qualify an explicit implementation as the type implements it - #3976

Merged
siegfriedpammer merged 1 commit into
masterfrom
explicit-iface-tuple-fixture
Aug 11, 2026
Merged

Fix #3451: qualify an explicit implementation as the type implements it#3976
siegfriedpammer merged 1 commit into
masterfrom
explicit-iface-tuple-fixture

Conversation

@siegfriedpammer

Copy link
Copy Markdown
Member

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:

private class C1 : I1<(int V1, int V2)>
{
	void I1<(int, int)>.Test()   // names lost
	{
	}
}

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 type in the NullableRefTypes fixture; 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 operators
  • CSharpAmbience.GetExplicitInterfaceType - tooltips and tree labels
  • CSharpDecompiler.AddInterfaceImplHelpers - the forwarders synthesized for MethodImpls

Matching while ignoring tuple names and nullability cannot be ambiguous: implementing two interfaces that differ only in those is itself an error (CS8140, CS8645).

Tests

  • TupleTests gains the issue's repro as a Pretty fixture, which fails without the fix.
  • NullableRefTypes T06/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), ilspycmd tests and the ILSpy tree/tooltip tests that consume the ambience green.


Written by an AI agent (Claude) on Siegfried's behalf.

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
@siegfriedpammer
siegfriedpammer merged commit 2efd871 into master Aug 11, 2026
17 checks passed
@siegfriedpammer
siegfriedpammer deleted the explicit-iface-tuple-fixture branch August 11, 2026 04:23
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.

Explicit interface method implementations missing ValueTuple names in type argument

1 participant