Skip to content

Commit

Permalink
fix #6407 SomeFloat and SomeInteger types fails to differentiate in a…
Browse files Browse the repository at this point in the history
… generic call from other generic
  • Loading branch information
bung87 committed Apr 26, 2023
1 parent f0ae1ed commit 124a58a
Show file tree
Hide file tree
Showing 10 changed files with 129 additions and 15 deletions.
2 changes: 1 addition & 1 deletion compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1874,7 +1874,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
rhsTyp = rhsTyp.lastSon
if lhs.sym.typ.kind == tyAnything:
rhsTyp = rhsTyp.skipIntLit(c.idgen)
if cmpTypes(c, lhs.typ, rhsTyp) in {isGeneric, isEqual}:
if cmpTypes(c, lhs.typ, rhsTyp) in {isGeneric, isEqual, isTypeClass}:
internalAssert c.config, c.p.resultSym != nil
# Make sure the type is valid for the result variable
typeAllowedCheck(c, n.info, rhsTyp, skResult)
Expand Down
70 changes: 62 additions & 8 deletions compiler/sigmatch.nim
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ type
TCandidate* = object
c*: PContext
exactMatches*: int # also misused to prefer iters over procs
typeClassMatches: int
genericMatches: int # also misused to prefer constraints
genericConstrainMatches: int
subtypeMatches: int
intConvMatches: int # conversions to int are not as expensive
convMatches: int
Expand Down Expand Up @@ -182,6 +184,7 @@ proc copyCandidate(a: var TCandidate, b: TCandidate) =
a.convMatches = b.convMatches
a.intConvMatches = b.intConvMatches
a.genericMatches = b.genericMatches
a.typeClassMatches = b.typeClassMatches
a.state = b.state
a.callee = b.callee
a.calleeSym = b.calleeSym
Expand Down Expand Up @@ -282,7 +285,9 @@ proc complexDisambiguation(a, b: PType): int =
proc writeMatches*(c: TCandidate) =
echo "Candidate '", c.calleeSym.name.s, "' at ", c.c.config $ c.calleeSym.info
echo " exact matches: ", c.exactMatches
echo " type class matches: ", c.typeClassMatches
echo " generic matches: ", c.genericMatches
echo " generic constrain matches: ", c.genericConstrainMatches
echo " subtype matches: ", c.subtypeMatches
echo " intconv matches: ", c.intConvMatches
echo " conv matches: ", c.convMatches
Expand All @@ -291,7 +296,19 @@ proc writeMatches*(c: TCandidate) =
proc cmpCandidates*(a, b: TCandidate): int =
result = a.exactMatches - b.exactMatches
if result != 0: return
result = a.genericMatches - b.genericMatches
result = a.typeClassMatches - b.typeClassMatches
if result != 0: return
let ag = a.genericMatches + a.genericConstrainMatches
let bg = b.genericMatches + b.genericConstrainMatches
result = ag - bg
if result != 0: return
result = a.subtypeMatches - b.subtypeMatches
if result != 0: return
result = a.typeClassMatches - b.typeClassMatches
if result != 0: return
let ag = a.genericMatches + a.genericConstrainMatches
let bg = b.genericMatches + b.genericConstrainMatches
result = ag - bg
if result != 0: return
result = a.subtypeMatches - b.subtypeMatches
if result != 0: return
Expand Down Expand Up @@ -1653,7 +1670,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
target.callConv != effectiveArgType.callConv:
return isNone
put(c, f, a)
return isGeneric
return isTypeClass
else:
return isNone

Expand Down Expand Up @@ -2033,7 +2050,8 @@ proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) =
case r
of isConvertible, isIntConv: inc(m.convMatches, convMatch)
of isSubtype, isSubrange: inc(m.subtypeMatches)
of isGeneric, isInferred, isBothMetaConvertible: inc(m.genericMatches)
of isTypeClass: inc(m.typeClassMatches, 2)
of isGeneric, isInferred, isBothMetaConvertible: inc(m.genericMatches, 2)
of isFromIntLit: inc(m.intConvMatches, 256)
of isInferredConvertible:
inc(m.convMatches)
Expand Down Expand Up @@ -2173,9 +2191,12 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
inc(m.convMatches)
result = implicitConv(nkHiddenStdConv, f, result, m, c)
else:
inc(m.genericMatches)
of isGeneric:
inc(m.genericMatches)
inc(m.genericMatches, 2)
of isGeneric, isTypeClass:
if r == isGeneric:
inc(m.genericMatches, 2)
else:
inc(m.typeClassMatches, 2)
if arg.typ == nil:
result = arg
elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple or
Expand Down Expand Up @@ -2204,7 +2225,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
of isNone:
# do not do this in ``typeRel`` as it then can't infer T in ``ref T``:
if a.kind in {tyProxy, tyUnknown}:
inc(m.genericMatches)
inc(m.genericMatches, 2)
m.fauxMatch = a.kind
return arg
elif a.kind == tyVoid and f.matchesVoidProc and argOrig.kind == nkStmtList:
Expand All @@ -2216,7 +2237,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
params = nkFormalParams.newTree(p.emptyNode), name = p.emptyNode, pattern = p.emptyNode,
genericParams = p.emptyNode, pragmas = p.emptyNode, exceptions = p.emptyNode), {})
if f.kind == tyBuiltInTypeClass:
inc m.genericMatches
inc m.typeClassMatches, 2
put(m, f, lifted.typ)
inc m.convMatches
return implicitConv(nkHiddenStdConv, f, lifted, m, c)
Expand Down Expand Up @@ -2395,6 +2416,36 @@ proc findFirstArgBlock(m: var TCandidate, n: PNode): int =
result = a2
else: break

proc checkGenericConstraint(m: var TCandidate; t: PType; typ: PType): bool =
if typ == nil: return
case t.kind
of tyOr:
for s in t.sons:
if m.checkGenericConstraint(s, typ):
return true
of tyAnd:
for s in t.sons:
if m.checkGenericConstraint(s, typ):
result = true
else:
result = false
break
of tyNot:
if t[0].kind == tyGenericParam:
let tt = PType(idTableGet(m.bindings, t[0]))
if tt != nil:
return not m.checkGenericConstraint(tt, typ)
return not m.checkGenericConstraint(t[0], typ)
of tyBuiltInTypeClass:
return m.checkGenericConstraint(t[0], typ)
of tyTypeDesc:
return m.checkGenericConstraint(t[0], typ[0])
of tyGenericParam:
if t.len == 0: return true
result = m.checkGenericConstraint(t[0], typ)
else:
return t.kind == typ.kind

proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var IntSet) =

template noMatch() =
Expand All @@ -2421,6 +2472,9 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int
elif not (isLValue(c, n, isOutParam(formal.typ))):
m.firstMismatch.kind = kVarNeeded
noMatch()
if formal.typ.kind == tyGenericParam and formal.typ.len > 0:
if m.checkGenericConstraint(formal.typ, n.typ):
inc(m.genericConstrainMatches, 1)

m.state = csMatch # until proven otherwise
m.firstMismatch = MismatchInfo()
Expand Down
1 change: 1 addition & 0 deletions compiler/types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type
isInferred, # generic proc was matched against a concrete type
isInferredConvertible, # same as above, but requiring proc CC conversion
isGeneric,
isTypeClass,
isFromIntLit, # conversion *from* int literal; proven safe
isEqual

Expand Down
10 changes: 5 additions & 5 deletions lib/pure/json.nim
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,11 @@ proc `[]=`*(obj: JsonNode, key: string, val: JsonNode) {.inline.} =
assert(obj.kind == JObject)
obj.fields[key] = val

proc `%`*(o: enum): JsonNode =
## Construct a JsonNode that represents the specified enum value as a
## string. Creates a new `JString JsonNode`.
result = %($o)

proc `%`*[T: object](o: T): JsonNode =
## Construct JsonNode from tuples and objects.
result = newJObject()
Expand All @@ -402,11 +407,6 @@ proc `%`*(o: ref object): JsonNode =
else:
result = %(o[])

proc `%`*(o: enum): JsonNode =
## Construct a JsonNode that represents the specified enum value as a
## string. Creates a new `JString JsonNode`.
result = %($o)

proc toJsonImpl(x: NimNode): NimNode =
case x.kind
of nnkBracket: # array
Expand Down
2 changes: 1 addition & 1 deletion lib/pure/strformat.nim
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@ proc formatValue*(result: var string; value: string; specifier: string) =
setLen(value, runeOffset(value, spec.precision))
result.add alignString(value, spec.minimumWidth, spec.align, spec.fill)

proc formatValue[T: not SomeInteger](result: var string; value: T; specifier: string) =
proc formatValue[T: not SomeNumber](result: var string; value: T; specifier: string) =
mixin `$`
formatValue(result, $value, specifier)

Expand Down
10 changes: 10 additions & 0 deletions tests/generics/t13549.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

proc foo[T](x: T):int = 1

proc foo[T: tuple](x: T):int = 2

doAssert foo((1, 2, 3)) == 2

type Obj = object
proc foo(x: object):int = 3
doAssert foo(Obj()) == 3
14 changes: 14 additions & 0 deletions tests/generics/t18314.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
discard """
disabled: true
"""

type
A = ref object of RootObj
B = ref object of A
C = ref object of B

proc foo[T: A](a: T):int = 1
proc foo[T: B](b: T):int = 2

var c = C()
doAssert foo(c) == 2
13 changes: 13 additions & 0 deletions tests/generics/t4084.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
discard """
disabled: true
"""

type
Image[T: int32|int64] = object
data: seq[T]

proc newImage[T](w, h: int): ref Image[T] =
new(result)
result.data = newSeq[T](w * h)

var i = newImage[string](320, 200)
12 changes: 12 additions & 0 deletions tests/generics/t6407.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
discard """
action: compile
"""

proc foo[T6407: SomeFloat](y: T6407):int = 0

proc foo[T6407: SomeInteger](y: T6407):int = 1

proc boo[T6407](x: T6407):int =
foo[T6407](x)

doAssert boo(1) == 1
10 changes: 10 additions & 0 deletions tests/generics/t6407_2.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
discard """
action: compile
"""

proc sort[T: uint8|char|byte](c: T) = discard
proc sort[T: bool](c: T) = discard

proc sorted[T](c: T): T = sort[T](c)

doAssert sorted(true) == false

0 comments on commit 124a58a

Please sign in to comment.