Improved support for nkSymChoices in type coercions

For example, this allows you to pick up a proc with a specific signature from
an overload set.

bugfix: nimrod generated invalid code when a RVO function had a single
compile-time param

bugfix: nkHiddenDerefs were not neutralised properly for indirect proc calls
This commit is contained in:
Zahary Karadjov 2012-10-15 16:36:00 +03:00
commit 3c9e3a6a71
5 changed files with 147 additions and 51 deletions

View file

@ -715,6 +715,7 @@ const
nkCommand, nkCallStrLit} nkCommand, nkCallStrLit}
nkLambdaKinds* = {nkLambda, nkDo} nkLambdaKinds* = {nkLambda, nkDo}
nkSymChoices* = {nkClosedSymChoice, nkOpenSymChoice}
skLocalVars* = {skVar, skLet, skForVar, skParam} skLocalVars* = {skVar, skLet, skForVar, skParam}

View file

@ -18,13 +18,14 @@ proc leftAppearsOnRightSide(le, ri: PNode): bool =
proc hasNoInit(call: PNode): bool {.inline.} = proc hasNoInit(call: PNode): bool {.inline.} =
result = call.sons[0].kind == nkSym and sfNoInit in call.sons[0].sym.flags result = call.sons[0].kind == nkSym and sfNoInit in call.sons[0].sym.flags
proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, pl: PRope) = proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
var pl = pl callee, params: PRope) =
var pl = con(callee, "(".toRope, params)
# getUniqueType() is too expensive here: # getUniqueType() is too expensive here:
var typ = skipTypes(ri.sons[0].typ, abstractInst) var typ = skipTypes(ri.sons[0].typ, abstractInst)
if typ.sons[0] != nil: if typ.sons[0] != nil:
if isInvalidReturnType(typ.sons[0]): if isInvalidReturnType(typ.sons[0]):
if sonsLen(ri) > 1: app(pl, ", ") if params != nil: pl.app(", ")
# beware of 'result = p(result)'. We may need to allocate a temporary: # beware of 'result = p(result)'. We may need to allocate a temporary:
if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri): if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri):
# Great, we can use 'd': # Great, we can use 'd':
@ -125,9 +126,9 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
# getUniqueType() is too expensive here: # getUniqueType() is too expensive here:
var typ = skipTypes(ri.sons[0].typ, abstractInst) var typ = skipTypes(ri.sons[0].typ, abstractInst)
assert(typ.kind == tyProc) assert(typ.kind == tyProc)
assert(sonsLen(typ) == sonsLen(typ.n))
var length = sonsLen(ri) var length = sonsLen(ri)
for i in countup(1, length - 1): for i in countup(1, length - 1):
assert(sonsLen(typ) == sonsLen(typ.n))
if ri.sons[i].typ.isCompileTimeOnly: continue if ri.sons[i].typ.isCompileTimeOnly: continue
if params != nil: app(params, ", ") if params != nil: app(params, ", ")
if i < sonsLen(typ): if i < sonsLen(typ):
@ -135,7 +136,7 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
app(params, genArg(p, ri.sons[i], typ.n.sons[i].sym)) app(params, genArg(p, ri.sons[i], typ.n.sons[i].sym))
else: else:
app(params, genArgNoParam(p, ri.sons[i])) app(params, genArgNoParam(p, ri.sons[i]))
fixupCall(p, le, ri, d, con(op.r, "(".toRope, params)) fixupCall(p, le, ri, d, op.r, params)
proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
@ -210,16 +211,16 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
if skipTypes(param.typ, {tyGenericInst}).kind == tyPtr: app(pl, "->") if skipTypes(param.typ, {tyGenericInst}).kind == tyPtr: app(pl, "->")
else: app(pl, ".") else: app(pl, ".")
app(pl, op.r) app(pl, op.r)
app(pl, "(") var params: PRope
for i in countup(2, length - 1): for i in countup(2, length - 1):
if params != nil: params.app(", ")
assert(sonsLen(typ) == sonsLen(typ.n)) assert(sonsLen(typ) == sonsLen(typ.n))
if i < sonsLen(typ): if i < sonsLen(typ):
assert(typ.n.sons[i].kind == nkSym) assert(typ.n.sons[i].kind == nkSym)
app(pl, genArg(p, ri.sons[i], typ.n.sons[i].sym)) app(params, genArg(p, ri.sons[i], typ.n.sons[i].sym))
else: else:
app(pl, genArgNoParam(p, ri.sons[i])) app(params, genArgNoParam(p, ri.sons[i]))
if i < length - 1: app(pl, ", ") fixupCall(p, le, ri, d, pl, params)
fixupCall(p, le, ri, d, pl)
proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) = proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
# generates a crappy ObjC call # generates a crappy ObjC call

View file

@ -125,44 +125,48 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
else: else:
markUsed(n, s) markUsed(n, s)
result = newSymNode(s, n.info) result = newSymNode(s, n.info)
proc checkConversionBetweenObjects(info: TLineInfo, castDest, src: PType) = type
var diff = inheritanceDiff(castDest, src) TConvStatus = enum
if diff == high(int): convOK,
LocalError(info, errGenerated, MsgKindToString(errIllegalConvFromXtoY) % [ convNotNeedeed,
src.typeToString, castDest.typeToString]) convNotLegal
proc checkConversionBetweenObjects(castDest, src: PType): TConvStatus =
return if inheritanceDiff(castDest, src) == high(int):
convNotLegal
else:
convOK
const const
IntegralTypes = {tyBool, tyEnum, tyChar, tyInt..tyUInt64} IntegralTypes = {tyBool, tyEnum, tyChar, tyInt..tyUInt64}
proc checkConvertible(info: TLineInfo, castDest, src: PType) = proc checkConvertible(castDest, src: PType): TConvStatus =
if sameType(castDest, src) and castDest.sym == src.sym: result = convOK
if sameType(castDest, src) and castDest.sym == src.sym:
# don't annoy conversions that may be needed on another processor: # don't annoy conversions that may be needed on another processor:
if castDest.kind notin IntegralTypes+{tyRange}: if castDest.kind notin IntegralTypes+{tyRange}:
Message(info, hintConvFromXtoItselfNotNeeded, typeToString(castDest)) result = convNotNeedeed
return return
var d = skipTypes(castDest, abstractVar) var d = skipTypes(castDest, abstractVar)
var s = skipTypes(src, abstractVar) var s = skipTypes(src, abstractVar)
while (d != nil) and (d.Kind in {tyPtr, tyRef}) and (d.Kind == s.Kind): while (d != nil) and (d.Kind in {tyPtr, tyRef}) and (d.Kind == s.Kind):
d = base(d) d = base(d)
s = base(s) s = base(s)
if d == nil: if d == nil:
LocalError(info, errGenerated, msgKindToString(errIllegalConvFromXtoY) % [ result = convNotLegal
src.typeToString, castDest.typeToString]) elif d.Kind == tyObject and s.Kind == tyObject:
elif d.Kind == tyObject and s.Kind == tyObject: result = checkConversionBetweenObjects(d, s)
checkConversionBetweenObjects(info, d, s)
elif (skipTypes(castDest, abstractVarRange).Kind in IntegralTypes) and elif (skipTypes(castDest, abstractVarRange).Kind in IntegralTypes) and
(skipTypes(src, abstractVarRange).Kind in IntegralTypes): (skipTypes(src, abstractVarRange).Kind in IntegralTypes):
# accept conversion between integral types # accept conversion between integral types
else: else:
# we use d, s here to speed up that operation a bit: # we use d, s here to speed up that operation a bit:
case cmpTypes(d, s) case cmpTypes(d, s)
of isNone, isGeneric: of isNone, isGeneric:
if not compareTypes(castDest, src, dcEqIgnoreDistinct): if not compareTypes(castDest, src, dcEqIgnoreDistinct):
LocalError(info, errGenerated, `%`( result = convNotLegal
MsgKindToString(errIllegalConvFromXtoY), else:
[typeToString(src), typeToString(castDest)]))
else:
nil nil
proc isCastable(dst, src: PType): bool = proc isCastable(dst, src: PType): bool =
@ -184,23 +188,32 @@ proc isCastable(dst, src: PType): bool =
(skipTypes(src, abstractInst).kind in IntegralTypes) (skipTypes(src, abstractInst).kind in IntegralTypes)
proc isSymChoice(n: PNode): bool {.inline.} = proc isSymChoice(n: PNode): bool {.inline.} =
result = n.kind in {nkClosedSymChoice, nkOpenSymChoice} result = n.kind in nkSymChoices
proc semConv(c: PContext, n: PNode, s: PSym): PNode = proc semConv(c: PContext, n: PNode, s: PSym): PNode =
if sonsLen(n) != 2: if sonsLen(n) != 2:
LocalError(n.info, errConvNeedsOneArg) LocalError(n.info, errConvNeedsOneArg)
return n return n
result = newNodeI(nkConv, n.info) result = newNodeI(nkConv, n.info)
result.typ = semTypeNode(c, n.sons[0], nil) result.typ = semTypeNode(c, n.sons[0], nil).skipTypes({tyGenericInst})
addSon(result, copyTree(n.sons[0])) addSon(result, copyTree(n.sons[0]))
addSon(result, semExprWithType(c, n.sons[1])) addSon(result, semExprWithType(c, n.sons[1]))
var op = result.sons[1] var op = result.sons[1]
if not isSymChoice(op): if not isSymChoice(op):
checkConvertible(result.info, result.typ, op.typ) let status = checkConvertible(result.typ, op.typ)
else: case status
of convOK: nil
of convNotNeedeed:
Message(n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString)
of convNotLegal:
LocalError(n.info, errGenerated, MsgKindToString(errIllegalConvFromXtoY)%
[op.typ.typeToString, result.typ.typeToString])
else:
for i in countup(0, sonsLen(op) - 1): for i in countup(0, sonsLen(op) - 1):
let it = op.sons[i] let it = op.sons[i]
if sameType(result.typ, it.typ): let status = checkConvertible(result.typ, it.typ)
if status == convOK:
markUsed(n, it.sym) markUsed(n, it.sym)
markIndirect(c, it.sym) markIndirect(c, it.sym)
return it return it
@ -492,12 +505,16 @@ proc analyseIfAddressTaken(c: PContext, n: PNode): PNode =
result = newHiddenAddrTaken(c, n) # BUGFIX! result = newHiddenAddrTaken(c, n) # BUGFIX!
proc analyseIfAddressTakenInCall(c: PContext, n: PNode) = proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
checkMinSonsLen(n, 1)
const const
FakeVarParams = {mNew, mNewFinalize, mInc, ast.mDec, mIncl, mExcl, FakeVarParams = {mNew, mNewFinalize, mInc, ast.mDec, mIncl, mExcl,
mSetLengthStr, mSetLengthSeq, mAppendStrCh, mAppendStrStr, mSwap, mSetLengthStr, mSetLengthSeq, mAppendStrCh, mAppendStrStr, mSwap,
mAppendSeqElem, mNewSeq, mReset, mShallowCopy} mAppendSeqElem, mNewSeq, mReset, mShallowCopy}
checkMinSonsLen(n, 1)
var t = n.sons[0].typ # get the real type of the callee
# it may be a proc var with a generic alias type, so we skip over them
var t = n.sons[0].typ.skipTypes({tyGenericInst})
if n.sons[0].kind == nkSym and n.sons[0].sym.magic in FakeVarParams: if n.sons[0].kind == nkSym and n.sons[0].sym.magic in FakeVarParams:
# BUGFIX: check for L-Value still needs to be done for the arguments! # BUGFIX: check for L-Value still needs to be done for the arguments!
for i in countup(1, sonsLen(n) - 1): for i in countup(1, sonsLen(n) - 1):
@ -618,7 +635,8 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
semOpAux(c, n) semOpAux(c, n)
var t: PType = nil var t: PType = nil
if (n.sons[0].typ != nil): t = skipTypes(n.sons[0].typ, abstractInst) if (n.sons[0].typ != nil): t = skipTypes(n.sons[0].typ, abstractInst)
if (t != nil) and (t.kind == tyProc): if (t != nil) and (t.kind == tyProc):
# This is a proc variable, apply normal overload resolution
var m: TCandidate var m: TCandidate
initCandidate(m, t) initCandidate(m, t)
matches(c, n, nOrig, m) matches(c, n, nOrig, m)
@ -648,6 +666,10 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
# we assume that a procedure that calls something indirectly # we assume that a procedure that calls something indirectly
# has side-effects: # has side-effects:
if tfNoSideEffect notin t.flags: incl(c.p.owner.flags, sfSideEffect) if tfNoSideEffect notin t.flags: incl(c.p.owner.flags, sfSideEffect)
elif (t != nil) and t.kind == tyTypeDesc:
let destType = t.skipTypes({tyTypeDesc, tyGenericInst})
result = semConv(c, n, symFromType(destType, n.info))
return
else: else:
result = overloadedCallOpr(c, n) result = overloadedCallOpr(c, n)
# Now that nkSym does not imply an iteration over the proc/iterator space, # Now that nkSym does not imply an iteration over the proc/iterator space,
@ -956,10 +978,10 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
result.typ = elemType(arr) result.typ = elemType(arr)
#GlobalError(n.info, errIndexTypesDoNotMatch) #GlobalError(n.info, errIndexTypesDoNotMatch)
of tyTypeDesc: of tyTypeDesc:
result = n.sons[0] # The result so far is a tyTypeDesc bound to # The result so far is a tyTypeDesc bound
# a tyGenericBody. The line below will substitute # a tyGenericBody. The line below will substitute
# it with the instantiated type. # it with the instantiated type.
result.typ.sons[0] = semTypeNode(c, n, nil).linkTo(result.sym) result = symNodeFromType(c, semTypeNode(c, n, nil), n.info)
of tyTuple: of tyTuple:
checkSonsLen(n, 2) checkSonsLen(n, 2)
n.sons[0] = makeDeref(n.sons[0]) n.sons[0] = makeDeref(n.sons[0])
@ -1715,12 +1737,10 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkTableConstr: of nkTableConstr:
result = semTableConstr(c, n) result = semTableConstr(c, n)
of nkClosedSymChoice, nkOpenSymChoice: of nkClosedSymChoice, nkOpenSymChoice:
LocalError(n.info, errExprXAmbiguous, renderTree(n, {renderNoComments})) # handling of sym choices is context dependent
# error correction: Pick first element: # the node is left intact for now
result = n.sons[0]
of nkStaticExpr: of nkStaticExpr:
result = semStaticExpr(c, n) result = semStaticExpr(c, n)
of nkAsgn: result = semAsgn(c, n) of nkAsgn: result = semAsgn(c, n)
of nkBlockStmt: result = semBlock(c, n) of nkBlockStmt: result = semBlock(c, n)
of nkStmtList: result = semStmtList(c, n) of nkStmtList: result = semStmtList(c, n)

View file

@ -685,8 +685,8 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType,
result = userConvMatch(c, m, base(f), a, arg) result = userConvMatch(c, m, base(f), a, arg)
proc ParamTypesMatch(c: PContext, m: var TCandidate, f, a: PType, proc ParamTypesMatch(c: PContext, m: var TCandidate, f, a: PType,
arg, argOrig: PNode): PNode = arg, argOrig: PNode): PNode =
if arg == nil or arg.kind != nkClosedSymChoice: if arg == nil or arg.kind notin nkSymChoices:
result = ParamTypesMatchAux(c, m, f, a, arg, argOrig) result = ParamTypesMatchAux(c, m, f, a, arg, argOrig)
else: else:
# CAUTION: The order depends on the used hashing scheme. Thus it is # CAUTION: The order depends on the used hashing scheme. Thus it is

74
tests/run/tvtable.nim Executable file
View file

@ -0,0 +1,74 @@
discard """
output: '''
OBJ 1 foo
10
OBJ 1 bar
OBJ 2 foo
5
OBJ 2 bar
'''
"""
type
# these are the signatures of the virtual procs for each type
fooProc[T] = proc (o: var T): int
barProc[T] = proc (o: var T)
# an untyped table to store the proc pointers
# it's also possible to use a strontly typed tuple here
VTable = array[0..1, pointer]
TBase = object {.inheritable.}
vtbl: ptr VTable
TUserObject1 = object of TBase
x: int
TUserObject2 = object of TBase
y: int
proc foo(o: var TUserObject1): int =
echo "OBJ 1 foo"
return 10
proc bar(o: var TUserObject1) =
echo "OBJ 1 bar"
proc foo(o: var TUserObject2): int =
echo "OBJ 2 foo"
return 5
proc bar(o: var TUserObject2) =
echo "OBJ 2 bar"
proc getVTable(T: typedesc): ptr VTable =
# pay attention to what's going on here
# this will initialize the vtable for each type at program start-up
#
# fooProc[T](foo) is a type coercion - it looks for a proc named foo
# matching the signature fooProc[T] (e.g. proc (o: var TUserObject1): int)
var vtbl {.global.} = [
cast[pointer](fooProc[T](foo)),
cast[pointer](barProc[T](bar))
]
return vtbl.addr
proc create(T: typedesc): T =
result.vtbl = getVTable(T)
proc baseFoo(o: var TBase): int =
return cast[fooProc[TBase]](o.vtbl[0]) (o)
proc baseBar(o: var TBase) =
cast[barProc[TBase]](o.vtbl[1]) (o)
var a = TUserObject1.create
var b = TUserObject2.create
echo a.baseFoo
a.baseBar
echo b.baseFoo
b.baseBar