fixes #2220; #2219; breaks #2022; for #2022 callsite needs to be used

This commit is contained in:
Araq 2015-03-09 15:02:28 +01:00
commit 1efb5174f2
12 changed files with 290 additions and 171 deletions

View file

@ -459,7 +459,7 @@ type
tfNotNil, # type cannot be 'nil' tfNotNil, # type cannot be 'nil'
tfNeedsInit, # type constains a "not nil" constraint somewhere or some tfNeedsInit, # type constains a "not nil" constraint somewhere or some
# other type so that it requires inititalization # other type so that it requires initalization
tfVarIsPtr, # 'var' type is translated like 'ptr' even in C++ mode tfVarIsPtr, # 'var' type is translated like 'ptr' even in C++ mode
tfHasMeta, # type contains "wildcard" sub-types such as generic params tfHasMeta, # type contains "wildcard" sub-types such as generic params
# or other type classes # or other type classes

View file

@ -83,7 +83,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
if cmp < 0: best = z # x is better than the best so far if cmp < 0: best = z # x is better than the best so far
elif cmp == 0: alt = z # x is as good as the best so far elif cmp == 0: alt = z # x is as good as the best so far
else: discard else: discard
#if sym.name.s == "*" and (n.info ?? "temp5.nim") and n.info.line == 140: #if sym.name.s == "cmp" and (n.info ?? "rstgen.nim") and n.info.line == 516:
# echo "Matches ", n.info, " ", typeToString(sym.typ) # echo "Matches ", n.info, " ", typeToString(sym.typ)
# debug sym # debug sym
# writeMatches(z) # writeMatches(z)

View file

@ -157,13 +157,36 @@ proc sumGeneric(t: PType): int =
result = ord(t.kind == tyGenericInvocation) result = ord(t.kind == tyGenericInvocation)
for i in 0 .. <t.len: result += t.sons[i].sumGeneric for i in 0 .. <t.len: result += t.sons[i].sumGeneric
break break
of tyProc:
# proc matches proc better than 'stmt' to disambiguate 'spawn'
return 1
of tyGenericParam, tyExpr, tyStatic, tyStmt, tyTypeDesc: break of tyGenericParam, tyExpr, tyStatic, tyStmt, tyTypeDesc: break
of tyBool, tyChar, tyEnum, tyObject, tyProc, tyPointer,
tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128,
tyUInt..tyUInt64:
return 1
else: return 0 else: return 0
#var ggDebug: bool
proc complexDisambiguation(a, b: PType): int = proc complexDisambiguation(a, b: PType): int =
# 'a' matches better if *every* argument matches better or equal than 'b'.
var winner = 0
for i in 1 .. <min(a.len, b.len):
let x = a.sons[i].sumGeneric
let y = b.sons[i].sumGeneric
#if ggDebug:
# echo "came her ", typeToString(a.sons[i]), " ", typeToString(b.sons[i])
if x != y:
if winner == 0:
if x > y: winner = 1
else: winner = -1
elif x > y:
if winner != 1:
# contradiction
return 0
else:
if winner != -1:
return 0
result = winner
when false:
var x, y: int var x, y: int
for i in 1 .. <a.len: x += a.sons[i].sumGeneric for i in 1 .. <a.len: x += a.sons[i].sumGeneric
for i in 1 .. <b.len: y += b.sons[i].sumGeneric for i in 1 .. <b.len: y += b.sons[i].sumGeneric
@ -338,7 +361,8 @@ proc minRel(a, b: TTypeRelation): TTypeRelation =
proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation = proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation =
result = isNone result = isNone
if sameType(f, a): result = isEqual if sameType(f, a):
result = isEqual
elif sonsLen(a) == sonsLen(f): elif sonsLen(a) == sonsLen(f):
result = isEqual result = isEqual
let firstField = if f.kind == tyTuple: 0 let firstField = if f.kind == tyTuple: 0
@ -380,17 +404,17 @@ proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
# no luck resolving the type, so the inference fails # no luck resolving the type, so the inference fails
return isNone return isNone
let reverseRel = typeRel(c, a, f) let reverseRel = typeRel(c, a, f)
if reverseRel == isGeneric: if reverseRel >= isGeneric:
result = isInferred result = isInferred
inc c.genericMatches #inc c.genericMatches
else: else:
result = typeRel(c, f, a) result = typeRel(c, f, a)
if result <= isSubtype or inconsistentVarTypes(f, a): if result <= isSubtype or inconsistentVarTypes(f, a):
result = isNone result = isNone
if result == isEqual: #if result == isEqual:
inc c.exactMatches # inc c.exactMatches
proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
case a.kind case a.kind
@ -433,6 +457,7 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
return isNone return isNone
when useEffectSystem: when useEffectSystem:
if not compatibleEffects(f, a): return isNone if not compatibleEffects(f, a): return isNone
of tyNil: of tyNil:
result = f.allowsNil result = f.allowsNil
of tyIter: of tyIter:
@ -590,7 +615,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
return typeRel(c, f, lastSon(a)) return typeRel(c, f, lastSon(a))
template bindingRet(res) = template bindingRet(res) =
when res == isGeneric:
if doBind: if doBind:
let bound = aOrig.skipTypes({tyRange}).skipIntLit let bound = aOrig.skipTypes({tyRange}).skipIntLit
if doBind: put(c.bindings, f, bound) if doBind: put(c.bindings, f, bound)
@ -605,20 +629,21 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyOr: of tyOr:
# seq[int|string] vs seq[number] # seq[int|string] vs seq[number]
# both int and string must match against number # both int and string must match against number
# but ensure that '[T: A|A]' matches as good as '[T: A]' (bug #2219):
result = isGeneric
for branch in a.sons: for branch in a.sons:
if typeRel(c, f, branch, false) == isNone: let x = typeRel(c, f, branch, false)
return isNone if x == isNone: return isNone
if x < result: result = x
return isGeneric
of tyAnd: of tyAnd:
# seq[Sortable and Iterable] vs seq[Sortable] # seq[Sortable and Iterable] vs seq[Sortable]
# only one match is enough # only one match is enough
for branch in a.sons: for branch in a.sons:
if typeRel(c, f, branch, false) != isNone: let x = typeRel(c, f, branch, false)
return isGeneric if x != isNone:
return if x >= isGeneric: isGeneric else: x
return isNone result = isNone
of tyNot: of tyNot:
case f.kind case f.kind
@ -781,11 +806,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
inc(c.inheritancePenalty, depth) inc(c.inheritancePenalty, depth)
result = isSubtype result = isSubtype
of tyDistinct: of tyDistinct:
if (a.kind == tyDistinct) and sameDistinctTypes(f, a): result = isEqual if a.kind == tyDistinct and sameDistinctTypes(f, a): result = isEqual
elif c.coerceDistincts: result = typeRel(c, f.base, a) elif c.coerceDistincts: result = typeRel(c, f.base, a)
of tySet: of tySet:
if a.kind == tySet: if a.kind == tySet:
if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty): if f.sons[0].kind != tyGenericParam and a.sons[0].kind == tyEmpty:
result = isSubtype result = isSubtype
else: else:
result = typeRel(c, f.sons[0], a.sons[0]) result = typeRel(c, f.sons[0], a.sons[0])
@ -865,7 +890,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
result = typeRel(c, ff, aa) result = typeRel(c, ff, aa)
if result == isNone: return if result == isNone: return
if ff.kind == tyRange and result != isEqual: return isNone if ff.kind == tyRange and result != isEqual: return isNone
result = isGeneric #result = isGeneric
# XXX See bug #2220. A[int] should match A[int] better than some generic X # XXX See bug #2220. A[int] should match A[int] better than some generic X
else: else:
result = typeRel(c, lastSon(f), a) result = typeRel(c, lastSon(f), a)
@ -904,18 +929,23 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyAnd: of tyAnd:
considerPreviousT: considerPreviousT:
for branch in f.sons: for branch in f.sons:
if typeRel(c, branch, aOrig) < isSubtype: let x = typeRel(c, branch, aOrig)
return isNone if x < isSubtype: return isNone
# 'and' implies minimum matching result:
bindingRet isGeneric if x < result: result = x
bindingRet result
of tyOr: of tyOr:
considerPreviousT: considerPreviousT:
result = isNone
for branch in f.sons: for branch in f.sons:
if typeRel(c, branch, aOrig) >= isSubtype: let x = typeRel(c, branch, aOrig)
bindingRet isGeneric # 'or' implies maximum matching result:
if x > result: result = x
return isNone if result >= isSubtype:
bindingRet result
else:
result = isNone
of tyNot: of tyNot:
considerPreviousT: considerPreviousT:
@ -975,6 +1005,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
internalAssert a.sons != nil and a.sons.len > 0 internalAssert a.sons != nil and a.sons.len > 0
c.typedescMatched = true c.typedescMatched = true
result = typeRel(c, f.base, a.skipTypes({tyGenericParam, tyTypeDesc})) result = typeRel(c, f.base, a.skipTypes({tyGenericParam, tyTypeDesc}))
if result > isGeneric: result = isGeneric
else: else:
result = isNone result = isNone
else: else:
@ -998,12 +1029,15 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
return isNone return isNone
if doBind: if doBind:
put(c.bindings, f, concrete) put(c.bindings, f, concrete)
elif result > isGeneric:
result = isGeneric
elif a.kind == tyEmpty: elif a.kind == tyEmpty:
result = isGeneric result = isGeneric
elif x.kind == tyGenericParam: elif x.kind == tyGenericParam:
result = isGeneric result = isGeneric
else: else:
result = typeRel(c, x, a) # check if it fits result = typeRel(c, x, a) # check if it fits
if result > isGeneric: result = isGeneric
of tyStatic: of tyStatic:
let prev = PType(idTableGet(c.bindings, f)) let prev = PType(idTableGet(c.bindings, f))
@ -1220,8 +1254,9 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
of isConvertible, isIntConv: inc(m.convMatches) of isConvertible, isIntConv: inc(m.convMatches)
of isSubtype, isSubrange: inc(m.subtypeMatches) of isSubtype, isSubrange: inc(m.subtypeMatches)
of isGeneric, isInferred: inc(m.genericMatches) of isGeneric, isInferred: inc(m.genericMatches)
of isInferredConvertible: inc(m.genericMatches); inc(m.convMatches)
of isFromIntLit: inc(m.intConvMatches, 256) of isFromIntLit: inc(m.intConvMatches, 256)
of isInferredConvertible:
inc(m.convMatches)
of isEqual: inc(m.exactMatches) of isEqual: inc(m.exactMatches)
of isNone: discard of isNone: discard
@ -1255,9 +1290,11 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
result = implicitConv(nkHiddenSubConv, f, arg, m, c) result = implicitConv(nkHiddenSubConv, f, arg, m, c)
of isSubrange: of isSubrange:
inc(m.subtypeMatches) inc(m.subtypeMatches)
if f.kind == tyVar:
result = arg
else:
result = implicitConv(nkHiddenStdConv, f, arg, m, c) result = implicitConv(nkHiddenStdConv, f, arg, m, c)
of isInferred, isInferredConvertible: of isInferred, isInferredConvertible:
inc(m.genericMatches)
if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds: if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds:
result = c.semInferredLambda(c, m.bindings, arg) result = c.semInferredLambda(c, m.bindings, arg)
else: else:
@ -1266,6 +1303,8 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
if r == isInferredConvertible: if r == isInferredConvertible:
inc(m.convMatches) inc(m.convMatches)
result = implicitConv(nkHiddenStdConv, f, result, m, c) result = implicitConv(nkHiddenStdConv, f, result, m, c)
else:
inc(m.genericMatches)
of isGeneric: of isGeneric:
inc(m.genericMatches) inc(m.genericMatches)
if arg.typ == nil: if arg.typ == nil:
@ -1331,7 +1370,16 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
for i in countup(0, sonsLen(arg) - 1): for i in countup(0, sonsLen(arg) - 1):
if arg.sons[i].sym.kind in {skProc, skMethod, skConverter}+skIterators: if arg.sons[i].sym.kind in {skProc, skMethod, skConverter}+skIterators:
copyCandidate(z, m) copyCandidate(z, m)
z.callee = arg.sons[i].typ
z.calleeSym = arg.sons[i].sym
#if arg.sons[i].sym.name.s == "cmp":
# ggDebug = true
# echo "CALLLEEEEEEEE ", typeToString(z.callee)
var r = typeRel(z, f, arg.sons[i].typ) var r = typeRel(z, f, arg.sons[i].typ)
#if arg.sons[i].sym.name.s == "cmp": # and arg.info.line == 606:
# echo "M ", r, " ", arg.info, " ", typeToString(arg.sons[i].sym.typ)
# debug arg.sons[i].sym
# writeMatches(z)
if r != isNone: if r != isNone:
case x.state case x.state
of csEmpty, csNoMatch: of csEmpty, csNoMatch:
@ -1644,7 +1692,7 @@ tests:
setup: setup:
var c: TCandidate var c: TCandidate
InitCandidate(nil, c, nil) initCandidate(nil, c, nil)
template yes(x, y) = template yes(x, y) =
test astToStr(x) & " is " & astToStr(y): test astToStr(x) & " is " & astToStr(y):

View file

@ -75,11 +75,12 @@ const
proc lowerBound*[T](a: openArray[T], key: T, cmp: proc(x,y: T): int {.closure.}): int = proc lowerBound*[T](a: openArray[T], key: T, cmp: proc(x,y: T): int {.closure.}): int =
## same as binarySearch except that if key is not in `a` then this ## same as binarySearch except that if key is not in `a` then this
## returns the location where `key` would be if it were. In other ## returns the location where `key` would be if it were. In other
## words if you have a sorted sequence and you call insert(thing, elm, lowerBound(thing, elm)) ## words if you have a sorted sequence and you call
## the sequence will still be sorted ## insert(thing, elm, lowerBound(thing, elm))
## the sequence will still be sorted.
## ##
## `cmp` is the comparator function to use, the expected return values are the same as ## `cmp` is the comparator function to use, the expected return values are
## that of system.cmp ## the same as that of system.cmp.
## ##
## example:: ## example::
## ##
@ -187,7 +188,8 @@ proc sort*[T](a: var openArray[T],
dec(m, s*2) dec(m, s*2)
s = s*2 s = s*2
proc sorted*[T](a: openArray[T], cmp: proc(x, y: T): int {.closure.}, order = SortOrder.Ascending): seq[T] = proc sorted*[T](a: openArray[T], cmp: proc(x, y: T): int {.closure.},
order = SortOrder.Ascending): seq[T] =
## returns `a` sorted by `cmp` in the specified `order`. ## returns `a` sorted by `cmp` in the specified `order`.
result = newSeq[T](a.len) result = newSeq[T](a.len)
for i in 0 .. a.high: for i in 0 .. a.high:

View file

@ -1,5 +1,11 @@
# Bug #2022 # Bug #2022
discard """
output: '''@[97, 45]
@[true, false]
@[false, false]'''
"""
## The goal of this snippet is to provide and test a construct for general- ## The goal of this snippet is to provide and test a construct for general-
## purpose, random-access mapping. I use an AST-manipulation-based approach ## purpose, random-access mapping. I use an AST-manipulation-based approach
## because it's more efficient than using procedure pointers and less ## because it's more efficient than using procedure pointers and less
@ -31,6 +37,7 @@ type Mapped[Input; predicate: static[string]] = object
input: Input input: Input
macro map(input, predicate: expr): expr = macro map(input, predicate: expr): expr =
let predicate = callsite()[2]
newNimNode(nnkObjConstr).add( newNimNode(nnkObjConstr).add(
newNimNode(nnkBracketExpr).add( newNimNode(nnkBracketExpr).add(
ident"Mapped", ident"Mapped",

View file

@ -1,14 +1,19 @@
discard """
output: '''another number: 123
yay'''
"""
# Test overloading of procs when used as function pointers # Test overloading of procs when used as function pointers
import strutils import strutils
proc parseInt(x: float): int {.noSideEffect.} = nil proc parseInt(x: float): int {.noSideEffect.} = discard
proc parseInt(x: bool): int {.noSideEffect.} = nil proc parseInt(x: bool): int {.noSideEffect.} = discard
proc parseInt(x: float32): int {.noSideEffect.} = nil proc parseInt(x: float32): int {.noSideEffect.} = discard
proc parseInt(x: int8): int {.noSideEffect.} = nil proc parseInt(x: int8): int {.noSideEffect.} = discard
proc parseInt(x: TFile): int {.noSideEffect.} = nil proc parseInt(x: TFile): int {.noSideEffect.} = discard
proc parseInt(x: char): int {.noSideEffect.} = nil proc parseInt(x: char): int {.noSideEffect.} = discard
proc parseInt(x: int16): int {.noSideEffect.} = nil proc parseInt(x: int16): int {.noSideEffect.} = discard
proc parseInt[T](x: T): int = echo x; 34 proc parseInt[T](x: T): int = echo x; 34
@ -22,9 +27,10 @@ var
proc takeParseInt(x: proc (y: string): int {.noSideEffect.}): int = proc takeParseInt(x: proc (y: string): int {.noSideEffect.}): int =
result = x("123") result = x("123")
echo "Give a list of numbers (separated by spaces): " if false:
var x = stdin.readline.split.map(parseInt).max echo "Give a list of numbers (separated by spaces): "
echo x, " is the maximum!" var x = stdin.readline.split.map(parseInt).max
echo x, " is the maximum!"
echo "another number: ", takeParseInt(parseInt) echo "another number: ", takeParseInt(parseInt)

View file

@ -0,0 +1,22 @@
discard """
output: '''ref ref T ptr S'''
"""
proc foo[T](x: T) =
echo "only T"
proc foo[T](x: ref T) =
echo "ref T"
proc foo[T, S](x: ref ref T; y: ptr S) =
echo "ref ref T ptr S"
proc foo[T, S](x: ref T; y: ptr S) =
echo "ref T ptr S"
proc foo[T](x: ref T; default = 0) =
echo "ref T; default"
var x: ref ref int
var y: ptr ptr int
foo(x, y)

View file

@ -1,17 +1,42 @@
discard """ discard """
output: "Version 2 was called." output: '''Version 2 was called.
disabled: true This has the highest precedence.
This has the second-highest precedence.
This has the lowest precedence.'''
""" """
# bug #2220 # bug #2220
when true:
type A[T] = object
type B = A[int]
type A[T] = object proc q[X](x: X) =
type B = A[int]
proc p[X](x: X) =
echo "Version 1 was called." echo "Version 1 was called."
proc p(x: B) = proc q(x: B) =
echo "Version 2 was called." echo "Version 2 was called."
p(B()) # This call reported as ambiguous. q(B()) # This call reported as ambiguous.
# bug #2219
template testPred(a: expr) =
block:
type A = object of RootObj
type B = object of A
type SomeA = A|A # A hack to make "A" a typeclass.
when a >= 3:
proc p[X](x: X) =
echo "This has the highest precedence."
when a >= 2:
proc p[X: A](x: X) =
echo "This has the second-highest precedence."
when a >= 1:
proc p[X: SomeA](x: X) =
echo "This has the lowest precedence."
p(B())
testPred(3)
testPred(2)
testPred(1)

View file

@ -7,3 +7,12 @@ import algorithm
# bug #1657 # bug #1657
var modules = @["hi", "ho", "ha", "huu"] var modules = @["hi", "ho", "ha", "huu"]
sort(modules, system.cmp) sort(modules, system.cmp)
type
MyType = object
x: string
proc cmp(a, b: MyType): int = cmp(a.x, b.x)
var modulesB = @[MyType(x: "ho"), MyType(x: "ha")]
sort(modulesB, cmp)

View file

@ -1,7 +1,7 @@
discard """ discard """
errormsg: "'" errormsg: "'"
file: "sequtils.nim" file: "sequtils.nim"
line: 416 line: 435
""" """
# unfortunately our tester doesn't support multiple lines of compiler # unfortunately our tester doesn't support multiple lines of compiler
# error messages yet... # error messages yet...