lazy operand sem'checking (beware)

This commit is contained in:
Araq 2013-03-02 20:23:56 +01:00
commit eebee0eff2
6 changed files with 88 additions and 43 deletions

View file

@ -19,13 +19,9 @@ import
# implementation # implementation
type proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.procvar.}
TExprFlag = enum proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.
efLValue, efWantIterator, efInTypeof, efWantStmt, efDetermineType procvar.}
TExprFlags = set[TExprFlag]
proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
proc semExprNoType(c: PContext, n: PNode): PNode proc semExprNoType(c: PContext, n: PNode): PNode
proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
proc semProcBody(c: PContext, n: PNode): PNode proc semProcBody(c: PContext, n: PNode): PNode
@ -200,14 +196,12 @@ proc addCodeForGenerics(c: PContext, n: PNode) =
addSon(n, prc.ast) addSon(n, prc.ast)
c.lastGenericIdx = c.generics.len c.lastGenericIdx = c.generics.len
proc semExprNoFlags(c: PContext, n: PNode): PNode {.procvar.} =
result = semExpr(c, n, {})
proc myOpen(module: PSym): PPassContext = proc myOpen(module: PSym): PPassContext =
var c = newContext(module) var c = newContext(module)
if c.p != nil: InternalError(module.info, "sem.myOpen") if c.p != nil: InternalError(module.info, "sem.myOpen")
c.semConstExpr = semConstExpr c.semConstExpr = semConstExpr
c.semExpr = semExprNoFlags c.semExpr = semExpr
c.semExprWithType = semExprWithType
c.semConstBoolExpr = semConstBoolExpr c.semConstBoolExpr = semConstBoolExpr
c.semOverloadedCall = semOverloadedCall c.semOverloadedCall = semOverloadedCall
c.semTypeNode = semTypeNode c.semTypeNode = semTypeNode

View file

@ -39,6 +39,10 @@ type
TInstantiationPair* = object TInstantiationPair* = object
genericSym*: PSym genericSym*: PSym
inst*: PInstantiation inst*: PInstantiation
TExprFlag* = enum
efLValue, efWantIterator, efInTypeof, efWantStmt, efDetermineType
TExprFlags* = set[TExprFlag]
PContext* = ref TContext PContext* = ref TContext
TContext* = object of TPassContext # a context represents a module TContext* = object of TPassContext # a context represents a module
@ -64,7 +68,8 @@ type
# to some new symbol in a generic instantiation # to some new symbol in a generic instantiation
libs*: TLinkedList # all libs used by this module libs*: TLinkedList # all libs used by this module
semConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # for the pragmas semConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # for the pragmas
semExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # for the pragmas semExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
semExprWithType*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
semConstBoolExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # XXX bite the bullet semConstBoolExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # XXX bite the bullet
semOverloadedCall*: proc (c: PContext, n, nOrig: PNode, semOverloadedCall*: proc (c: PContext, n, nOrig: PNode,
filter: TSymKinds): PNode {.nimcall.} filter: TSymKinds): PNode {.nimcall.}

View file

@ -31,10 +31,6 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym, semCheck = true): PNode =
proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
proc newDeref(n: PNode): PNode {.inline.} =
result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0])
addSon(result, n)
proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = semExpr(c, n, flags) result = semExpr(c, n, flags)
if result.kind == nkEmpty: if result.kind == nkEmpty:
@ -319,7 +315,7 @@ proc semIs(c: PContext, n: PNode): PNode =
proc semOpAux(c: PContext, n: PNode) = proc semOpAux(c: PContext, n: PNode) =
const flags = {efDetermineType} const flags = {efDetermineType}
for i in countup(1, n.sonsLen- 1): for i in countup(1, n.sonsLen-1):
var a = n.sons[i] var a = n.sons[i]
if a.kind == nkExprEqExpr and sonsLen(a) == 2: if a.kind == nkExprEqExpr and sonsLen(a) == 2:
var info = a.sons[0].info var info = a.sons[0].info
@ -328,7 +324,7 @@ proc semOpAux(c: PContext, n: PNode) =
a.typ = a.sons[1].typ a.typ = a.sons[1].typ
else: else:
n.sons[i] = semExprWithType(c, a, flags) n.sons[i] = semExprWithType(c, a, flags)
proc overloadedCallOpr(c: PContext, n: PNode): PNode = proc overloadedCallOpr(c: PContext, n: PNode): PNode =
# quick check if there is *any* () operator overloaded: # quick check if there is *any* () operator overloaded:
var par = getIdent("()") var par = getIdent("()")
@ -694,7 +690,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
# this seems to be a hotspot in the compiler! # this seems to be a hotspot in the compiler!
let nOrig = n.copyTree let nOrig = n.copyTree
semOpAux(c, n) #semLazyOpAux(c, n)
result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags) result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags)
if result == nil: if result == nil:
result = overloadedCallOpr(c, n) result = overloadedCallOpr(c, n)
@ -706,6 +702,7 @@ proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
of skMacro: result = semMacroExpr(c, result, nOrig, callee) of skMacro: result = semMacroExpr(c, result, nOrig, callee)
of skTemplate: result = semTemplateExpr(c, result, callee) of skTemplate: result = semTemplateExpr(c, result, callee)
else: else:
semFinishOperands(c, n)
activate(c, n) activate(c, n)
fixAbstractType(c, result) fixAbstractType(c, result)
analyseIfAddressTakenInCall(c, result) analyseIfAddressTakenInCall(c, result)
@ -845,16 +842,17 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
return semSym(c, n, s, flags) return semSym(c, n, s, flags)
n.sons[0] = semExprWithType(c, n.sons[0], flags) n.sons[0] = semExprWithType(c, n.sons[0], flags)
restoreOldStyleType(n.sons[0]) #restoreOldStyleType(n.sons[0])
var i = considerAcc(n.sons[1]) var i = considerAcc(n.sons[1])
var ty = n.sons[0].typ var ty = n.sons[0].typ
var f: PSym = nil var f: PSym = nil
result = nil result = nil
if isTypeExpr(n.sons[0]): if isTypeExpr(n.sons[0]) or ty.kind == tyTypeDesc and ty.len == 1:
if ty.kind == tyTypeDesc: ty = ty.sons[0]
case ty.kind case ty.kind
of tyEnum: of tyEnum:
# look up if the identifier belongs to the enum: # look up if the identifier belongs to the enum:
while ty != nil: while ty != nil:
f = getSymFromList(ty.n, i) f = getSymFromList(ty.n, i)
if f != nil: break if f != nil: break
ty = ty.sons[0] # enum inheritance ty = ty.sons[0] # enum inheritance
@ -881,7 +879,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
# XXX: This is probably not relevant any more # XXX: This is probably not relevant any more
# reset to prevent 'nil' bug: see "tests/reject/tenumitems.nim": # reset to prevent 'nil' bug: see "tests/reject/tenumitems.nim":
ty = n.sons[0].Typ ty = n.sons[0].Typ
ty = skipTypes(ty, {tyGenericInst, tyVar, tyPtr, tyRef}) ty = skipTypes(ty, {tyGenericInst, tyVar, tyPtr, tyRef})
var check: PNode = nil var check: PNode = nil
if ty.kind == tyObject: if ty.kind == tyObject:

View file

@ -484,10 +484,17 @@ proc semForVars(c: PContext, n: PNode): PNode =
n.sons[length-1] = SemStmt(c, n.sons[length-1]) n.sons[length-1] = SemStmt(c, n.sons[length-1])
Dec(c.p.nestedLoopCounter) Dec(c.p.nestedLoopCounter)
proc newDeref(n: PNode): PNode {.inline.} =
result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0])
addSon(result, n)
proc implicitIterator(c: PContext, it: string, arg: PNode): PNode = proc implicitIterator(c: PContext, it: string, arg: PNode): PNode =
result = newNodeI(nkCall, arg.info) result = newNodeI(nkCall, arg.info)
result.add(newIdentNode(it.getIdent, arg.info)) result.add(newIdentNode(it.getIdent, arg.info))
result.add(arg) if arg.typ != nil and arg.typ.kind == tyVar:
result.add newDeref(arg)
else:
result.add arg
result = semExprNoDeref(c, result, {efWantIterator}) result = semExprNoDeref(c, result, {efWantIterator})
proc semFor(c: PContext, n: PNode): PNode = proc semFor(c: PContext, n: PNode): PNode =
@ -776,8 +783,7 @@ proc activate(c: PContext, n: PNode) =
# XXX: This proc is part of my plan for getting rid of # XXX: This proc is part of my plan for getting rid of
# forward declarations. stay tuned. # forward declarations. stay tuned.
when false: when false:
# well for now it breaks code ... I added the test case in main.nim of the # well for now it breaks code ...
# compiler itself to break bootstrapping :P
case n.kind case n.kind
of nkLambdaKinds: of nkLambdaKinds:
discard semLambda(c, n, {}) discard semLambda(c, n, {})
@ -1142,8 +1148,8 @@ proc instantiateDestructor*(c: PContext, typ: PType): bool =
else: else:
return false return false
proc insertDestructors(c: PContext, varSection: PNode): proc insertDestructors(c: PContext,
tuple[outer: PNode, inner: PNode] = varSection: PNode): tuple[outer, inner: PNode] =
# Accepts a var or let section. # Accepts a var or let section.
# #
# When a var section has variables with destructors # When a var section has variables with destructors

View file

@ -799,6 +799,28 @@ proc setSon(father: PNode, at: int, son: PNode) =
if sonsLen(father) <= at: setlen(father.sons, at + 1) if sonsLen(father) <= at: setlen(father.sons, at + 1)
father.sons[at] = son father.sons[at] = son
# we are allowed to modify the calling node in the 'prepare*' procs:
proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode =
if formal.kind == tyExpr and formal.len != 1:
# {tyTypeDesc, tyExpr, tyStmt, tyProxy}:
# a.typ == nil is valid
result = a
elif a.typ.isNil:
result = c.semExprWithType(c, a, {efDetermineType})
else:
result = a
proc prepareOperand(c: PContext; a: PNode): PNode =
if a.typ.isNil:
result = c.semExprWithType(c, a, {efDetermineType})
else:
result = a
proc prepareNamedParam(a: PNode) =
if a.sons[0].kind != nkIdent:
var info = a.sons[0].info
a.sons[0] = newIdentNode(considerAcc(a.sons[0]), info)
proc matchesAux(c: PContext, n, nOrig: PNode, proc matchesAux(c: PContext, n, nOrig: PNode,
m: var TCandidate, marker: var TIntSet) = m: var TCandidate, marker: var TIntSet) =
template checkConstraint(n: expr) {.immediate, dirty.} = template checkConstraint(n: expr) {.immediate, dirty.} =
@ -823,6 +845,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
if n.sons[a].kind == nkExprEqExpr: if n.sons[a].kind == nkExprEqExpr:
# named param # named param
# check if m.callee has such a param: # check if m.callee has such a param:
prepareNamedParam(n.sons[a])
if n.sons[a].sons[0].kind != nkIdent: if n.sons[a].sons[0].kind != nkIdent:
LocalError(n.sons[a].info, errNamedParamHasToBeIdent) LocalError(n.sons[a].info, errNamedParamHasToBeIdent)
m.state = csNoMatch m.state = csNoMatch
@ -838,9 +861,11 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
m.state = csNoMatch m.state = csNoMatch
return return
m.baseTypeMatch = false m.baseTypeMatch = false
n.sons[a].sons[1] = prepareOperand(c, formal.typ, n.sons[a].sons[1])
n.sons[a].typ = n.sons[a].sons[1].typ
var arg = ParamTypesMatch(c, m, formal.typ, n.sons[a].typ, var arg = ParamTypesMatch(c, m, formal.typ, n.sons[a].typ,
n.sons[a].sons[1], nOrig.sons[a].sons[1]) n.sons[a].sons[1], nOrig.sons[a].sons[1])
if arg == nil: if arg == nil:
m.state = csNoMatch m.state = csNoMatch
return return
checkConstraint(n.sons[a].sons[1]) checkConstraint(n.sons[a].sons[1])
@ -852,30 +877,33 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
if f != formalLen - 1: container = nil if f != formalLen - 1: container = nil
else: else:
setSon(m.call, formal.position + 1, arg) setSon(m.call, formal.position + 1, arg)
else: else:
# unnamed param # unnamed param
if f >= formalLen: if f >= formalLen:
# too many arguments? # too many arguments?
if tfVarArgs in m.callee.flags: if tfVarArgs in m.callee.flags:
# is ok... but don't increment any counters... # is ok... but don't increment any counters...
if skipTypes(n.sons[a].typ, abstractVar).kind == tyString: # we have no formal here to snoop at:
addSon(m.call, implicitConv(nkHiddenStdConv, getSysType(tyCString), n.sons[a] = prepareOperand(c, n.sons[a])
if skipTypes(n.sons[a].typ, abstractVar).kind == tyString:
addSon(m.call, implicitConv(nkHiddenStdConv, getSysType(tyCString),
copyTree(n.sons[a]), m, c)) copyTree(n.sons[a]), m, c))
else: else:
addSon(m.call, copyTree(n.sons[a])) addSon(m.call, copyTree(n.sons[a]))
elif formal != nil: elif formal != nil:
m.baseTypeMatch = false m.baseTypeMatch = false
n.sons[a] = prepareOperand(c, formal.typ, n.sons[a])
var arg = ParamTypesMatch(c, m, formal.typ, n.sons[a].typ, var arg = ParamTypesMatch(c, m, formal.typ, n.sons[a].typ,
n.sons[a], nOrig.sons[a]) n.sons[a], nOrig.sons[a])
if (arg != nil) and m.baseTypeMatch and (container != nil): if (arg != nil) and m.baseTypeMatch and (container != nil):
addSon(container, arg) addSon(container, arg)
else: else:
m.state = csNoMatch m.state = csNoMatch
return return
else: else:
m.state = csNoMatch m.state = csNoMatch
return return
else: else:
if m.callee.n.sons[f].kind != nkSym: if m.callee.n.sons[f].kind != nkSym:
InternalError(n.sons[a].info, "matches") InternalError(n.sons[a].info, "matches")
return return
@ -886,6 +914,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
m.state = csNoMatch m.state = csNoMatch
return return
m.baseTypeMatch = false m.baseTypeMatch = false
n.sons[a] = prepareOperand(c, formal.typ, n.sons[a])
var arg = ParamTypesMatch(c, m, formal.typ, n.sons[a].typ, var arg = ParamTypesMatch(c, m, formal.typ, n.sons[a].typ,
n.sons[a], nOrig.sons[a]) n.sons[a], nOrig.sons[a])
if arg == nil: if arg == nil:
@ -898,12 +927,18 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
setSon(m.call, formal.position + 1, setSon(m.call, formal.position + 1,
implicitConv(nkHiddenStdConv, formal.typ, container, m, c)) implicitConv(nkHiddenStdConv, formal.typ, container, m, c))
if f != formalLen - 1: container = nil if f != formalLen - 1: container = nil
else: else:
setSon(m.call, formal.position + 1, arg) setSon(m.call, formal.position + 1, arg)
checkConstraint(n.sons[a]) checkConstraint(n.sons[a])
inc(a) inc(a)
inc(f) inc(f)
proc semFinishOperands*(c: PContext, n: PNode) =
# this needs to be called to ensure that after overloading resolution every
# argument has been sem'checked:
for i in 1 .. <n.len:
n.sons[i] = prepareOperand(c, n.sons[i])
proc partialMatch*(c: PContext, n, nOrig: PNode, m: var TCandidate) = proc partialMatch*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
# for 'suggest' support: # for 'suggest' support:
var marker = initIntSet() var marker = initIntSet()

View file

@ -1,6 +1,9 @@
version 0.9.2 version 0.9.2
============= =============
- lazy overloading resolution:
* get rid of ``expr[typ]``, use perhaps ``static[typ]`` instead
* typedesc[T] needs to be documented in ast.nim
- FFI: - FFI:
* test libffi on windows * test libffi on windows
* test: times.format with the FFI * test: times.format with the FFI
@ -15,6 +18,8 @@ version 0.9.2
version 0.9.4 version 0.9.4
============= =============
- provide tool/API to track leaks/object counts
- use big blocks in the allocator
- make 'bind' default for templates and introduce 'mixin'; - make 'bind' default for templates and introduce 'mixin';
special rule for ``[]=`` special rule for ``[]=``
- implicit deref for parameter matching; overloading based on 'var T' - implicit deref for parameter matching; overloading based on 'var T'
@ -123,3 +128,5 @@ Bugs
- blocks can "export" an identifier but the CCG generates {} for them ... - blocks can "export" an identifier but the CCG generates {} for them ...
- JS gen: fix exception handling - JS gen: fix exception handling
- the better scoping for locals is the wrong default for endb - the better scoping for locals is the wrong default for endb
- osproc execProcesses can deadlock if all processes fail (as experienced
in c++ mode)