mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-17 02:32:11 +00:00
concepts: fix generic concept devaluation skipped in typeRel (#26164)
Fix and tests for issues #26147 case A. See individual commit messages. Checked on the full test suite: no new failure with respect to devel as baseline.
This commit is contained in:
@@ -1805,7 +1805,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
let inst = prepareMetatypeForSigmatch(c.c, c.bindings, c.call.info, f)
|
||||
return typeRel(c, inst, a, flags)
|
||||
|
||||
if x.kind == tyGenericInvocation:
|
||||
if concpt.kind == tyConcept:
|
||||
# Concept dispatch devaluates generic concepts,
|
||||
# so it must precede the generic invocation case
|
||||
result = enterConceptMatch(c, f, x, flags)
|
||||
elif x.kind == tyGenericInvocation:
|
||||
if f[0] == x[0]:
|
||||
for i in 1..<f.len:
|
||||
# Handle when checking against a generic that isn't fully instantiated
|
||||
@@ -1826,8 +1830,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
# Workaround for regression #4589
|
||||
if f[i].kind != tyTypeDesc: return
|
||||
result = isGeneric
|
||||
elif concpt.kind == tyConcept:
|
||||
result = enterConceptMatch(c, f, x, flags)
|
||||
else:
|
||||
let genericBody = f[0]
|
||||
var askip = skippedNone
|
||||
|
||||
@@ -73,3 +73,21 @@ proc unpackBoth[K1, V1, K2, V2;
|
||||
|
||||
let otherPair = Pair[string, float](k: "eight", v: 8.0)
|
||||
doAssert unpackBoth(pair, otherPair) == ((7, "seven"), ("eight", 8.0))
|
||||
|
||||
# A concrete `items` overload must win over one declared over a concept that
|
||||
# the type happens to satisfy, and not generate an "ambiguous call".
|
||||
type OverloadDummy[T] = ref object
|
||||
data: seq[T]
|
||||
|
||||
proc `[]`[T](d: OverloadDummy[T], i: int): T = d.data[i]
|
||||
proc len[T](d: OverloadDummy[T]): int = d.data.len
|
||||
|
||||
iterator items[T](d: OverloadDummy[T]): T =
|
||||
# deliberately distinguishable from the concept-provided iterator
|
||||
for i in 0 ..< d.len:
|
||||
yield d.data[i] * 10
|
||||
|
||||
var overloadAcc: seq[int] = @[]
|
||||
for x in OverloadDummy[int](data: @[1, 2, 3]):
|
||||
overloadAcc.add x
|
||||
doAssert overloadAcc == @[10, 20, 30]
|
||||
|
||||
46
tests/concepts/tconcept_overload_priority.nim
Normal file
46
tests/concepts/tconcept_overload_priority.nim
Normal file
@@ -0,0 +1,46 @@
|
||||
discard """
|
||||
action: run
|
||||
"""
|
||||
|
||||
# The choice between a routine declared over a concept and a concrete overload
|
||||
# must not depend on how many requirements the concept happens to declare.
|
||||
#
|
||||
# `sumGeneric` ranks concepts by requirement count, and concrete types by
|
||||
# structural nesting depth, which makes sense within each category but not
|
||||
# between one another. So we must make sure we don't fall through that case
|
||||
# accidentally picking ambiguous calls arbitrarily (see GitHub issue #26147).
|
||||
|
||||
type
|
||||
Small[T] = concept
|
||||
proc `[]`(a: Self; index: int): T
|
||||
proc len(a: Self): int
|
||||
|
||||
Large[T] = concept
|
||||
proc `[]`(a: Self; index: int): T
|
||||
proc len(a: Self): int
|
||||
proc extra1(a: Self): int
|
||||
proc extra2(a: Self): int
|
||||
proc extra3(a: Self): int
|
||||
|
||||
Box[T] = ref object
|
||||
data: seq[T]
|
||||
|
||||
proc `[]`[T](b: Box[T], i: int): T = b.data[i]
|
||||
proc len[T](b: Box[T]): int = b.data.len
|
||||
proc extra1[T](b: Box[T]): int = 0
|
||||
proc extra2[T](b: Box[T]): int = 0
|
||||
proc extra3[T](b: Box[T]): int = 0
|
||||
|
||||
proc pickSmall[T](x: Small[T]): string = "concept"
|
||||
proc pickSmall[T](x: Box[T]): string = "concrete"
|
||||
|
||||
proc pickLarge[T](x: Large[T]): string = "concept"
|
||||
proc pickLarge[T](x: Box[T]): string = "concrete"
|
||||
|
||||
let b = Box[int](data: @[1, 2, 3])
|
||||
|
||||
# `Box` satisfies both concepts, so both calls must select the concrete
|
||||
# overload, and must agree with each other.
|
||||
doAssert pickSmall(b) == "concrete"
|
||||
doAssert pickLarge(b) == "concrete"
|
||||
doAssert pickSmall(b) == pickLarge(b)
|
||||
Reference in New Issue
Block a user