Skip to content

An interface's explicit implementation of a generated interface member is replaced without a diagnostic #191

Closed

Description

@CaffeinatedCoder

Package

CodoMetis.TypeKit.Generators

Version

2.3.0, and every release that generates these members

How does it fail?

Silently. It compiles or answers, and does less than it appears to

What you ran

An interface can implement a member of an interface the generators implement, explicitly, and every type that declares it reaches that implementation until it declares a public member of its own:

public interface IBounded<TSelf> : IMinMaxValue<TSelf> where TSelf : IBounded<TSelf>
{
    static TSelf IMinMaxValue<TSelf>.MaxValue => throw new NotSupportedException();
    static TSelf IMinMaxValue<TSelf>.MinValue => throw new NotSupportedException();
}

public interface IFormats : IFormattable
{
    string IFormattable.ToString(string? format, IFormatProvider? provider) => "interface";
}

public readonly partial record struct Bounded : IValue<int>, IBounded<Bounded>;
public readonly partial record struct Formats : IValue<int>, IFormats;

static T Max<T>() where T : IMinMaxValue<T> => T.MaxValue;
Max<Bounded>();                                        // the interface's, which throws
((IFormattable)Formats.From(1)).ToString(null, null);  // "interface"

What you got

Measured on feat/value-object-arithmetic (2.4.0 in progress), the same since these members are generated: Max<Bounded>() returned the generated MaxValue, and the IFormattable call printed 1. The generated public member replaced the interface's explicit implementation, with no diagnostic.

ValueObjectDeclaration.ExplicitImplementationsOf, which reports an explicit implementation of a generated interface's member as CMTK1011, reads the value object and its base records, never the interfaces it implements.

What you expected

The same answer as an explicit implementation on the value object itself: CMTK1011, naming the member and the interface that implements it. That reports more under an Error in a minor, so the fix records the exception to decision 34, as #159 did.

The input class is every interface the generators implement whose member an interface can implement explicitly: IFormattable, ISpanFormattable, IUtf8SpanFormattable, IParsable, ISpanParsable, IUtf8SpanParsable, IMinMaxValue, IComparable, IComparable<T>, IComparisonOperators, IConvertible. Only IMinMaxValue and IFormattable are measured so far.

Found while fixing the same gap in the arithmetic operators of #184, where an interface's explicit implementation is kept (decision 121), from an outside review of #188.

Environment

.NET SDK 10.0.4xx, Metalama 2026.1; any consumer.

Scope as fixed (#193)

Measured on release/2.4.0 at 97c87c1, 2026-10-30: all eleven interfaces of the input class were replaced as described, two interfaces down and through a base record's interface too. The fix found more of the same kind:

  • Another assembly's types. Metalama lists no private member of a type in another assembly, and an explicit implementation is private. An interface from a referenced project was missed, and so was a base record from one, which the rule has missed since phase 11 (its IFormattable replaced, its IMinMaxValue kept answering). The check now uses TryFindImplementationForInterfaceMember, which sees them.
  • IConvertible declared through an interface crashed the aspect (LAMA0041). It is CMTK1011 now.
  • Closed sets. An interface's explicit IClosedSet<TSelf>.Name or TryParse was replaced without a word, and IParsable<TSelf> through an interface crashed the aspect (LAMA0512). Both are CMTK1011.

Not refused, each with a test: an interface's ==, which the record's own replaces in any record; .NET's INumberBase<TSelf>, whose UTF-8 bridges are the only such members in .NET's interfaces; and interfaces the generators don't implement (a ToString() seam, a Uri's ordering). The comparison interfaces answer CMTK1008, as on the value object itself. ExplicitlyImplementsEquatableBetween has no gap. The exception to decision 34 is recorded as decision 121 on release/2.3.1 (124 in #193 on release/2.4.0, renumbered so 2.3.1's records run without a gap).

IValueObject<,> declared through a user interface still fails with LAMA0512: #192.

A closed set over a base class of another project that implements IClosedSet<TSelf> explicitly, reported by a second outside review (All 2 vs T.All 0, names and parsing apart through the interface, EF Core storing the base's name), is the same case: refused since 466c541, and held by SetOverLibraryBase in 38b316f. The same review's base-record findings are #195 and #197, fixed in #193 as well; #194 and #196 are separate.

Moved to 2.3.1 (2026-10-30)

The defect shipped in 2.3.0, so its fix ships in the patch 2.3.1 (epic #198). The fix is cherry-picked onto fix/interface-explicit-implementation-refused-2-3-1 (from build/open-2-3-1, #200); #193's later commits for #195 and #197 follow it there, and #193 closes in favour of that branch's PR.

Activity

  1. added this to the 2.4.0 milestone on Oct 30, 2026
  2. added
    bugSomething isn't working
    silentCompiles or answers and does less than it appears to
    severity/mediumInconsistency or failure in a narrow scenario
    package/generatorsAffects CodoMetis.TypeKit.Generators
    area/value-objectsValue-object contracts and what the aspects generate for them
    on Oct 30, 2026
  3. modified the milestones: 2.4.0, 2.3.1 on Oct 30, 2026
  4. CaffeinatedCoder commented on Oct 30, 2026

    @CaffeinatedCoder
    OwnerAuthor

    Fixed by #201, merged into release/2.3.1 as 8fac39b; ships in 2.3.1.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/value-objectsValue-object contracts and what the aspects generate for thembugSomething isn't workingpackage/generatorsAffects CodoMetis.TypeKit.Generatorsseverity/mediumInconsistency or failure in a narrow scenariosilentCompiles or answers and does less than it appears to

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions