Skip to content

An inherited CompareTo(TSelf) is not seen: EF Core orders by the column, and < overflows the stack #194

Closed

Description

@CaffeinatedCoder

Package

CodoMetis.TypeKit.Generators

Version

2.3.0, and every release that generates comparison

How does it fail?

Silently in EF Core, and with a crash (stack overflow) on the first < or CompareTo(object)

What you ran

A base record declares CompareTo(TSelf), the comparison seam, instead of the value object:

public abstract record ReversedBase<TSelf> where TSelf : ReversedBase<TSelf>
{
    public int CompareTo(TSelf? other) => other is null ? 1 : Raw(other).CompareTo(Raw((TSelf)this));
    private static int Raw(TSelf value) => ((IValueObject<Priority, int>)(object)value).Value;
}

public sealed partial record Priority : ReversedBase<Priority>, IValue<int>;

new[] { Priority.From(1), Priority.From(2) }.Order();            // [2, 1]
db.Jobs.OrderBy(j => j.Priority);                               // SQLite: [1, 2], not refused
Priority.From(1) < Priority.From(2);                            // stack overflow
((IComparable)Priority.From(1)).CompareTo(Priority.From(2));    // stack overflow

What you got

Measured on release/2.4.0 at 97c87c1, 2026-10-30:

  • ValueObjectComparableAspect.DeclaresCompareTo reads the value object's own methods, so the inherited seam is not seen: no [HandWrittenOrdering], and an EF Core query orders by the column ([1, 2]) where memory orders [2, 1].
  • The generated CompareTo(TSelf) is not introduced (OverrideStrategy.Ignore sees the inherited one), and the generated operators and CompareTo(object) call left.CompareTo(right). C# prefers the method declared on the more derived type, the generated CompareTo(object), over the base's CompareTo(Priority?), so CompareTo(object) calls itself until the stack overflows and the process ends.

What you expected

CMTK1008 naming the inherited member: the seam is declared on the value object (decision 28), so declare CompareTo(Priority) there, calling the base's if that is the order you mean. Reported from an outside review of #193.

Environment

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

Scope as fixed in #203

The class is a seam a base record declares, not only CompareTo(TSelf). Measured on #201's branch: a base's TryFrom(T) and Revalidate() were the value object's the same way (the generated ones, which run Create, were not introduced), and a base's CompareTo(object) failed the aspect (LAMA0500). All four are refused now (CMTK1008 for CompareTo, CMTK1011 for the others, which say to declare the seam on the value object). A base's FromKnownGood(T) is hidden by the generated one, whose signature takes the caller's expression too, and stays allowed. Decision 121 records refusing over taking a base's as the seam; a base's sealed ToString() stays the seam.

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/highWrong results, a crash or a failed build in a realistic 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 #203, merged into release/2.3.1 as fb11acf; 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/highWrong results, a crash or a failed build in a realistic 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