Skip to content

Remove generic-parameter-count overload tie-breaker in language version 202c #12829

Description

@tangent-vector

Problem

compareOverloadCandidateSpecificity currently breaks a tie between otherwise-equivalent applicable generic candidates by comparing their required generic-parameter counts. In practice, the candidate with fewer required generic parameters wins.

That count is not a sound proxy for semantic specificity. For example:

int choose<T : IFloat>(T value)
{
    return 1;
}

__generic<T : __BuiltinFloatingPointType, let N : int>
int choose(vector<T, N> value)
{
    return 2;
}

int result = choose(float3(0.0));

Both candidates are applicable, and their argument conversion costs tie. Slang currently chooses the broad T : IFloat overload because it has one required generic parameter while the structurally narrower vector<T, N> overload has two. The parameter count has caused resolution to select the candidate that is less specific for this call by the criterion the source comment says we ultimately want: applicability of one candidate implying applicability of the other.

There is also an implementation/comment discrepancy worth resolving. The comment says that a candidate with more generic parameters is preferred, but return leftSpecCount - rightSpecCount (where a negative result means the left candidate is better) produces the observed preference for fewer parameters.

Proposed change

For language version 202c, remove the generic-parameter-count comparison from compareOverloadCandidateSpecificity. If no other principled rule distinguishes two applicable candidates, diagnose ambiguity instead of silently selecting one based on this unrelated scalar count.

Preserve the existing behavior for older language versions because changing which overload is selected, or turning a previously resolved call into an ambiguity, is source-breaking.

This is deliberately narrower than the larger work Slang needs for semantic partial ordering of overload candidates. A future implementation should compare applicability/constraint implication and actual specialized parameter shapes. Removing the count heuristic does not solve that larger problem, but in 202c it can stop this heuristic from making actively misleading choices in the meantime.

Why changing the direction is insufficient

Either direction can produce the desired answer only by coincidence. The source already gives this example:

int doThing<T>(vector<T, 3> value);
int doThing<T, let N : int>(vector<T, N> value);

For a float3 argument, today's preference for fewer generic parameters selects vector<T, 3>, which is the desirable result. But that result follows from the fixed-size overload accepting a strict subset of the values accepted by the variable-size overload; parameter count merely happens to correlate with the real relationship. Reversing the count rule would break this case while helping the IFloat/vector example. Retaining today's direction does the opposite.

I have not identified a category where generic-parameter count itself establishes a valid specificity relationship. When the rule gets the desired answer, a structural or constraint-based rule appears to be the actual justification.

Spike results

As a narrow experiment, removing only this comparison had the expected local behavior:

  • The IFloat/vector<T, N> example became ambiguous rather than selecting the broad overload.
  • Adding [OverloadRank(1)] to the shaped overload then resolved the tie in its favor, because the count heuristic no longer returned first.
  • The core module rebuilt successfully.
  • 141 executed generic-related diagnostic subtests passed; 59 unavailable subtests were ignored by the local configuration.
  • A before/after sample of 138 generic language-feature files had identical results: 107 passed and 31 failed for unrelated pre-existing reasons.

These results are not evidence that removal has no compatibility impact; the change should still be gated to 202c and tested explicitly in both legacy and 202c modes.

Suggested acceptance criteria

  • Legacy language versions preserve the existing tie-break behavior.
  • Language version 202c does not use required generic-parameter count to choose between otherwise-tied candidates.
  • Tests cover both the harmful IFloat/vector<T, N> case and a case such as vector<T, 3>/vector<T, N> that will remain ambiguous until a principled structural-specificity rule is implemented.
  • The implementation comment no longer describes parameter count as a valid stand-in for specificity.

Metadata

Metadata

Labels

Dev OpenedreproducedProvided instructions confirmed to reproduce the issue

Type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions