guards.nim does compile
This commit is contained in:
parent
e1a921ce4f
commit
7eb39d9d26
13 changed files with 241 additions and 230 deletions
|
|
@ -267,7 +267,7 @@ errSetTooBig: "set is too large",
|
||||||
errBaseTypeMustBeOrdinal: "base type of a set must be an ordinal",
|
errBaseTypeMustBeOrdinal: "base type of a set must be an ordinal",
|
||||||
errInheritanceOnlyWithNonFinalObjects: "inheritance only works with non-final objects",
|
errInheritanceOnlyWithNonFinalObjects: "inheritance only works with non-final objects",
|
||||||
errInheritanceOnlyWithEnums: "inheritance only works with an enum",
|
errInheritanceOnlyWithEnums: "inheritance only works with an enum",
|
||||||
errIllegalRecursionInTypeX: "illegal recursion in type '$1'",
|
errIllegalRecursionInTypeX:,
|
||||||
errCannotInstantiateX: "cannot instantiate: '$1'",
|
errCannotInstantiateX: "cannot instantiate: '$1'",
|
||||||
errExprHasNoAddress: "expression has no address",
|
errExprHasNoAddress: "expression has no address",
|
||||||
errXStackEscape: "address of '$1' may not escape its stack frame",
|
errXStackEscape: "address of '$1' may not escape its stack frame",
|
||||||
|
|
@ -307,7 +307,7 @@ errDefaultArgumentInvalid: "default argument invalid",
|
||||||
errNamedParamHasToBeIdent: "named parameter has to be an identifier",
|
errNamedParamHasToBeIdent: "named parameter has to be an identifier",
|
||||||
errNoReturnTypeForX: "no return type allowed for $1",
|
errNoReturnTypeForX: "no return type allowed for $1",
|
||||||
errConvNeedsOneArg: "a type conversion needs exactly one argument",
|
errConvNeedsOneArg: "a type conversion needs exactly one argument",
|
||||||
errInvalidPragmaX: "invalid pragma: $1",
|
errInvalidPragmaX: ,
|
||||||
errXNotAllowedHere: "$1 not allowed here",
|
errXNotAllowedHere: "$1 not allowed here",
|
||||||
errInvalidControlFlowX: "invalid control flow: $1",
|
errInvalidControlFlowX: "invalid control flow: $1",
|
||||||
errXisNoType: "invalid type: '$1'",
|
errXisNoType: "invalid type: '$1'",
|
||||||
|
|
@ -345,7 +345,7 @@ errNewSectionExpected: "new section expected",
|
||||||
errWhitespaceExpected: "whitespace expected, got '$1'",
|
errWhitespaceExpected: "whitespace expected, got '$1'",
|
||||||
errXisNoValidIndexFile: "'$1' is no valid index file",
|
errXisNoValidIndexFile: "'$1' is no valid index file",
|
||||||
errCannotRenderX: "cannot render reStructuredText element '$1'",
|
errCannotRenderX: "cannot render reStructuredText element '$1'",
|
||||||
errVarVarTypeNotAllowed: "type 'var var' is not allowed",
|
errVarVarTypeNotAllowed: ,
|
||||||
errInstantiateXExplicitly: "instantiate '$1' explicitly",
|
errInstantiateXExplicitly: "instantiate '$1' explicitly",
|
||||||
errOnlyACallOpCanBeDelegator: "only a call operator can be a delegator",
|
errOnlyACallOpCanBeDelegator: "only a call operator can be a delegator",
|
||||||
errUsingNoSymbol: "'$1' is not a variable, constant or a proc name",
|
errUsingNoSymbol: "'$1' is not a variable, constant or a proc name",
|
||||||
|
|
|
||||||
|
|
@ -613,7 +613,7 @@ proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool =
|
||||||
if gCmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}:
|
if gCmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}:
|
||||||
return false
|
return false
|
||||||
|
|
||||||
var hashFile = toGeneratedFile(conf, cfile.cname.withPackageName, "sha1")
|
var hashFile = toGeneratedFile(conf, conf.withPackageName(cfile.cname), "sha1")
|
||||||
var currentHash = footprint(conf, cfile)
|
var currentHash = footprint(conf, cfile)
|
||||||
var f: File
|
var f: File
|
||||||
if open(f, hashFile, fmRead):
|
if open(f, hashFile, fmRead):
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
## This module implements the 'implies' relation for guards.
|
## This module implements the 'implies' relation for guards.
|
||||||
|
|
||||||
import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents,
|
import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents,
|
||||||
saturate
|
saturate, modulegraphs, options, configuration
|
||||||
|
|
||||||
const
|
const
|
||||||
someEq = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
|
someEq = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
|
||||||
|
|
@ -83,18 +83,25 @@ proc isLetLocation(m: PNode, isApprox: bool): bool =
|
||||||
|
|
||||||
proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true)
|
proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true)
|
||||||
|
|
||||||
let
|
type
|
||||||
opLe = createMagic("<=", mLeI)
|
Operators* = object
|
||||||
opLt = createMagic("<", mLtI)
|
opNot, opContains, opLe, opLt, opAnd, opOr, opIsNil, opEq: PSym
|
||||||
opAnd = createMagic("and", mAnd)
|
opAdd, opSub, opMul, opDiv, opLen: PSym
|
||||||
opOr = createMagic("or", mOr)
|
|
||||||
opIsNil = createMagic("isnil", mIsNil)
|
proc initOperators*(g: ModuleGraph): Operators =
|
||||||
opEq = createMagic("==", mEqI)
|
result.opLe = createMagic(g, "<=", mLeI)
|
||||||
opAdd = createMagic("+", mAddI)
|
result.opLt = createMagic(g, "<", mLtI)
|
||||||
opSub = createMagic("-", mSubI)
|
result.opAnd = createMagic(g, "and", mAnd)
|
||||||
opMul = createMagic("*", mMulI)
|
result.opOr = createMagic(g, "or", mOr)
|
||||||
opDiv = createMagic("div", mDivI)
|
result.opIsNil = createMagic(g, "isnil", mIsNil)
|
||||||
opLen = createMagic("len", mLengthSeq)
|
result.opEq = createMagic(g, "==", mEqI)
|
||||||
|
result.opAdd = createMagic(g, "+", mAddI)
|
||||||
|
result.opSub = createMagic(g, "-", mSubI)
|
||||||
|
result.opMul = createMagic(g, "*", mMulI)
|
||||||
|
result.opDiv = createMagic(g, "div", mDivI)
|
||||||
|
result.opLen = createMagic(g, "len", mLengthSeq)
|
||||||
|
result.opNot = createMagic(g, "not", mNot)
|
||||||
|
result.opContains = createMagic(g, "contains", mInSet)
|
||||||
|
|
||||||
proc swapArgs(fact: PNode, newOp: PSym): PNode =
|
proc swapArgs(fact: PNode, newOp: PSym): PNode =
|
||||||
result = newNodeI(nkCall, fact.info, 3)
|
result = newNodeI(nkCall, fact.info, 3)
|
||||||
|
|
@ -102,16 +109,16 @@ proc swapArgs(fact: PNode, newOp: PSym): PNode =
|
||||||
result.sons[1] = fact.sons[2]
|
result.sons[1] = fact.sons[2]
|
||||||
result.sons[2] = fact.sons[1]
|
result.sons[2] = fact.sons[1]
|
||||||
|
|
||||||
proc neg(n: PNode): PNode =
|
proc neg(n: PNode; o: Operators): PNode =
|
||||||
if n == nil: return nil
|
if n == nil: return nil
|
||||||
case n.getMagic
|
case n.getMagic
|
||||||
of mNot:
|
of mNot:
|
||||||
result = n.sons[1]
|
result = n.sons[1]
|
||||||
of someLt:
|
of someLt:
|
||||||
# not (a < b) == a >= b == b <= a
|
# not (a < b) == a >= b == b <= a
|
||||||
result = swapArgs(n, opLe)
|
result = swapArgs(n, o.opLe)
|
||||||
of someLe:
|
of someLe:
|
||||||
result = swapArgs(n, opLt)
|
result = swapArgs(n, o.opLt)
|
||||||
of mInSet:
|
of mInSet:
|
||||||
if n.sons[1].kind != nkCurly: return nil
|
if n.sons[1].kind != nkCurly: return nil
|
||||||
let t = n.sons[2].typ.skipTypes(abstractInst)
|
let t = n.sons[2].typ.skipTypes(abstractInst)
|
||||||
|
|
@ -133,11 +140,11 @@ proc neg(n: PNode): PNode =
|
||||||
of mOr:
|
of mOr:
|
||||||
# not (a or b) --> not a and not b
|
# not (a or b) --> not a and not b
|
||||||
let
|
let
|
||||||
a = n.sons[1].neg
|
a = n.sons[1].neg(o)
|
||||||
b = n.sons[2].neg
|
b = n.sons[2].neg(o)
|
||||||
if a != nil and b != nil:
|
if a != nil and b != nil:
|
||||||
result = newNodeI(nkCall, n.info, 3)
|
result = newNodeI(nkCall, n.info, 3)
|
||||||
result.sons[0] = newSymNode(opAnd)
|
result.sons[0] = newSymNode(o.opAnd)
|
||||||
result.sons[1] = a
|
result.sons[1] = a
|
||||||
result.sons[2] = b
|
result.sons[2] = b
|
||||||
elif a != nil:
|
elif a != nil:
|
||||||
|
|
@ -147,7 +154,7 @@ proc neg(n: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
# leave not (a == 4) as it is
|
# leave not (a == 4) as it is
|
||||||
result = newNodeI(nkCall, n.info, 2)
|
result = newNodeI(nkCall, n.info, 2)
|
||||||
result.sons[0] = newSymNode(opNot)
|
result.sons[0] = newSymNode(o.opNot)
|
||||||
result.sons[1] = n
|
result.sons[1] = n
|
||||||
|
|
||||||
proc buildCall(op: PSym; a: PNode): PNode =
|
proc buildCall(op: PSym; a: PNode): PNode =
|
||||||
|
|
@ -181,7 +188,7 @@ proc `|div|`(a, b: PNode): PNode =
|
||||||
if a.kind in {nkCharLit..nkUInt64Lit}: result.intVal = a.intVal div b.intVal
|
if a.kind in {nkCharLit..nkUInt64Lit}: result.intVal = a.intVal div b.intVal
|
||||||
else: result.floatVal = a.floatVal / b.floatVal
|
else: result.floatVal = a.floatVal / b.floatVal
|
||||||
|
|
||||||
proc negate(a, b, res: PNode): PNode =
|
proc negate(a, b, res: PNode; o: Operators): PNode =
|
||||||
if b.kind in {nkCharLit..nkUInt64Lit} and b.intVal != low(BiggestInt):
|
if b.kind in {nkCharLit..nkUInt64Lit} and b.intVal != low(BiggestInt):
|
||||||
var b = copyNode(b)
|
var b = copyNode(b)
|
||||||
b.intVal = -b.intVal
|
b.intVal = -b.intVal
|
||||||
|
|
@ -189,11 +196,11 @@ proc negate(a, b, res: PNode): PNode =
|
||||||
b.intVal = b.intVal |+| a.intVal
|
b.intVal = b.intVal |+| a.intVal
|
||||||
result = b
|
result = b
|
||||||
else:
|
else:
|
||||||
result = buildCall(opAdd, a, b)
|
result = buildCall(o.opAdd, a, b)
|
||||||
elif b.kind in {nkFloatLit..nkFloat64Lit}:
|
elif b.kind in {nkFloatLit..nkFloat64Lit}:
|
||||||
var b = copyNode(b)
|
var b = copyNode(b)
|
||||||
b.floatVal = -b.floatVal
|
b.floatVal = -b.floatVal
|
||||||
result = buildCall(opAdd, a, b)
|
result = buildCall(o.opAdd, a, b)
|
||||||
else:
|
else:
|
||||||
result = res
|
result = res
|
||||||
|
|
||||||
|
|
@ -205,7 +212,7 @@ proc lowBound*(x: PNode): PNode =
|
||||||
result = nkIntLit.newIntNode(firstOrd(x.typ))
|
result = nkIntLit.newIntNode(firstOrd(x.typ))
|
||||||
result.info = x.info
|
result.info = x.info
|
||||||
|
|
||||||
proc highBound*(x: PNode): PNode =
|
proc highBound*(x: PNode; o: Operators): PNode =
|
||||||
let typ = x.typ.skipTypes(abstractInst)
|
let typ = x.typ.skipTypes(abstractInst)
|
||||||
result = if typ.kind == tyArray:
|
result = if typ.kind == tyArray:
|
||||||
nkIntLit.newIntNode(lastOrd(typ))
|
nkIntLit.newIntNode(lastOrd(typ))
|
||||||
|
|
@ -213,23 +220,23 @@ proc highBound*(x: PNode): PNode =
|
||||||
x.sym.kind == skConst:
|
x.sym.kind == skConst:
|
||||||
nkIntLit.newIntNode(x.sym.ast.len-1)
|
nkIntLit.newIntNode(x.sym.ast.len-1)
|
||||||
else:
|
else:
|
||||||
opAdd.buildCall(opLen.buildCall(x), minusOne())
|
o.opAdd.buildCall(o.opLen.buildCall(x), minusOne())
|
||||||
result.info = x.info
|
result.info = x.info
|
||||||
|
|
||||||
proc reassociation(n: PNode): PNode =
|
proc reassociation(n: PNode; o: Operators): PNode =
|
||||||
result = n
|
result = n
|
||||||
# (foo+5)+5 --> foo+10; same for '*'
|
# (foo+5)+5 --> foo+10; same for '*'
|
||||||
case result.getMagic
|
case result.getMagic
|
||||||
of someAdd:
|
of someAdd:
|
||||||
if result[2].isValue and
|
if result[2].isValue and
|
||||||
result[1].getMagic in someAdd and result[1][2].isValue:
|
result[1].getMagic in someAdd and result[1][2].isValue:
|
||||||
result = opAdd.buildCall(result[1][1], result[1][2] |+| result[2])
|
result = o.opAdd.buildCall(result[1][1], result[1][2] |+| result[2])
|
||||||
if result[2].intVal == 0:
|
if result[2].intVal == 0:
|
||||||
result = result[1]
|
result = result[1]
|
||||||
of someMul:
|
of someMul:
|
||||||
if result[2].isValue and
|
if result[2].isValue and
|
||||||
result[1].getMagic in someMul and result[1][2].isValue:
|
result[1].getMagic in someMul and result[1][2].isValue:
|
||||||
result = opMul.buildCall(result[1][1], result[1][2] |*| result[2])
|
result = o.opMul.buildCall(result[1][1], result[1][2] |*| result[2])
|
||||||
if result[2].intVal == 1:
|
if result[2].intVal == 1:
|
||||||
result = result[1]
|
result = result[1]
|
||||||
elif result[2].intVal == 0:
|
elif result[2].intVal == 0:
|
||||||
|
|
@ -243,12 +250,12 @@ proc pred(n: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
result = n
|
result = n
|
||||||
|
|
||||||
proc canon*(n: PNode): PNode =
|
proc canon*(n: PNode; o: Operators): PNode =
|
||||||
# XXX for now only the new code in 'semparallel' uses this
|
# XXX for now only the new code in 'semparallel' uses this
|
||||||
if n.safeLen >= 1:
|
if n.safeLen >= 1:
|
||||||
result = shallowCopy(n)
|
result = shallowCopy(n)
|
||||||
for i in 0 ..< n.len:
|
for i in 0 ..< n.len:
|
||||||
result.sons[i] = canon(n.sons[i])
|
result.sons[i] = canon(n.sons[i], o)
|
||||||
elif n.kind == nkSym and n.sym.kind == skLet and
|
elif n.kind == nkSym and n.sym.kind == skLet and
|
||||||
n.sym.ast.getMagic in (someEq + someAdd + someMul + someMin +
|
n.sym.ast.getMagic in (someEq + someAdd + someMul + someMin +
|
||||||
someMax + someHigh + {mUnaryLt} + someSub + someLen + someDiv):
|
someMax + someHigh + {mUnaryLt} + someSub + someLen + someDiv):
|
||||||
|
|
@ -263,24 +270,24 @@ proc canon*(n: PNode): PNode =
|
||||||
# (4 + foo) + 2 --> (foo + 4) + 2
|
# (4 + foo) + 2 --> (foo + 4) + 2
|
||||||
of someHigh:
|
of someHigh:
|
||||||
# high == len+(-1)
|
# high == len+(-1)
|
||||||
result = opAdd.buildCall(opLen.buildCall(result[1]), minusOne())
|
result = o.opAdd.buildCall(o.opLen.buildCall(result[1]), minusOne())
|
||||||
of mUnaryLt:
|
of mUnaryLt:
|
||||||
result = buildCall(opAdd, result[1], minusOne())
|
result = buildCall(o.opAdd, result[1], minusOne())
|
||||||
of someSub:
|
of someSub:
|
||||||
# x - 4 --> x + (-4)
|
# x - 4 --> x + (-4)
|
||||||
result = negate(result[1], result[2], result)
|
result = negate(result[1], result[2], result, o)
|
||||||
of someLen:
|
of someLen:
|
||||||
result.sons[0] = opLen.newSymNode
|
result.sons[0] = o.opLen.newSymNode
|
||||||
of someLt:
|
of someLt:
|
||||||
# x < y same as x <= y-1:
|
# x < y same as x <= y-1:
|
||||||
let y = n[2].canon
|
let y = n[2].canon(o)
|
||||||
let p = pred(y)
|
let p = pred(y)
|
||||||
let minus = if p != y: p else: opAdd.buildCall(y, minusOne()).canon
|
let minus = if p != y: p else: o.opAdd.buildCall(y, minusOne()).canon(o)
|
||||||
result = opLe.buildCall(n[1].canon, minus)
|
result = o.opLe.buildCall(n[1].canon(o), minus)
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
result = skipConv(result)
|
result = skipConv(result)
|
||||||
result = reassociation(result)
|
result = reassociation(result, o)
|
||||||
# most important rule: (x-4) <= a.len --> x <= a.len+4
|
# most important rule: (x-4) <= a.len --> x <= a.len+4
|
||||||
case result.getMagic
|
case result.getMagic
|
||||||
of someLe:
|
of someLe:
|
||||||
|
|
@ -291,10 +298,10 @@ proc canon*(n: PNode): PNode =
|
||||||
case x.getMagic
|
case x.getMagic
|
||||||
of someSub:
|
of someSub:
|
||||||
result = buildCall(result[0].sym, x[1],
|
result = buildCall(result[0].sym, x[1],
|
||||||
reassociation(opAdd.buildCall(y, x[2])))
|
reassociation(o.opAdd.buildCall(y, x[2]), o))
|
||||||
of someAdd:
|
of someAdd:
|
||||||
# Rule A:
|
# Rule A:
|
||||||
let plus = negate(y, x[2], nil).reassociation
|
let plus = negate(y, x[2], nil, o).reassociation(o)
|
||||||
if plus != nil: result = buildCall(result[0].sym, x[1], plus)
|
if plus != nil: result = buildCall(result[0].sym, x[1], plus)
|
||||||
else: discard
|
else: discard
|
||||||
elif y.kind in nkCallKinds and y.len == 3 and y[2].isValue and
|
elif y.kind in nkCallKinds and y.len == 3 and y[2].isValue and
|
||||||
|
|
@ -303,9 +310,9 @@ proc canon*(n: PNode): PNode =
|
||||||
case y.getMagic
|
case y.getMagic
|
||||||
of someSub:
|
of someSub:
|
||||||
result = buildCall(result[0].sym, y[1],
|
result = buildCall(result[0].sym, y[1],
|
||||||
reassociation(opAdd.buildCall(x, y[2])))
|
reassociation(o.opAdd.buildCall(x, y[2]), o))
|
||||||
of someAdd:
|
of someAdd:
|
||||||
let plus = negate(x, y[2], nil).reassociation
|
let plus = negate(x, y[2], nil, o).reassociation(o)
|
||||||
# ensure that Rule A will not trigger afterwards with the
|
# ensure that Rule A will not trigger afterwards with the
|
||||||
# additional 'not isLetLocation' constraint:
|
# additional 'not isLetLocation' constraint:
|
||||||
if plus != nil and not isLetLocation(x, true):
|
if plus != nil and not isLetLocation(x, true):
|
||||||
|
|
@ -323,15 +330,15 @@ proc canon*(n: PNode): PNode =
|
||||||
result.sons[2] = y[1]
|
result.sons[2] = y[1]
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc `+@`*(a: PNode; b: BiggestInt): PNode =
|
#proc `+@`*(a: PNode; b: BiggestInt): PNode =
|
||||||
canon(if b != 0: opAdd.buildCall(a, nkIntLit.newIntNode(b)) else: a)
|
# canon(if b != 0: opAdd.buildCall(a, nkIntLit.newIntNode(b)) else: a)
|
||||||
|
|
||||||
proc usefulFact(n: PNode): PNode =
|
proc usefulFact(n: PNode; o: Operators): PNode =
|
||||||
case n.getMagic
|
case n.getMagic
|
||||||
of someEq:
|
of someEq:
|
||||||
if skipConv(n.sons[2]).kind == nkNilLit and (
|
if skipConv(n.sons[2]).kind == nkNilLit and (
|
||||||
isLetLocation(n.sons[1], false) or isVar(n.sons[1])):
|
isLetLocation(n.sons[1], false) or isVar(n.sons[1])):
|
||||||
result = opIsNil.buildCall(n.sons[1])
|
result = o.opIsNil.buildCall(n.sons[1])
|
||||||
else:
|
else:
|
||||||
if isLetLocation(n.sons[1], true) or isLetLocation(n.sons[2], true):
|
if isLetLocation(n.sons[1], true) or isLetLocation(n.sons[2], true):
|
||||||
# XXX algebraic simplifications! 'i-1 < a.len' --> 'i < a.len+1'
|
# XXX algebraic simplifications! 'i-1 < a.len' --> 'i < a.len+1'
|
||||||
|
|
@ -351,11 +358,11 @@ proc usefulFact(n: PNode): PNode =
|
||||||
result = n
|
result = n
|
||||||
of mAnd:
|
of mAnd:
|
||||||
let
|
let
|
||||||
a = usefulFact(n.sons[1])
|
a = usefulFact(n.sons[1], o)
|
||||||
b = usefulFact(n.sons[2])
|
b = usefulFact(n.sons[2], o)
|
||||||
if a != nil and b != nil:
|
if a != nil and b != nil:
|
||||||
result = newNodeI(nkCall, n.info, 3)
|
result = newNodeI(nkCall, n.info, 3)
|
||||||
result.sons[0] = newSymNode(opAnd)
|
result.sons[0] = newSymNode(o.opAnd)
|
||||||
result.sons[1] = a
|
result.sons[1] = a
|
||||||
result.sons[2] = b
|
result.sons[2] = b
|
||||||
elif a != nil:
|
elif a != nil:
|
||||||
|
|
@ -363,9 +370,9 @@ proc usefulFact(n: PNode): PNode =
|
||||||
elif b != nil:
|
elif b != nil:
|
||||||
result = b
|
result = b
|
||||||
of mNot:
|
of mNot:
|
||||||
let a = usefulFact(n.sons[1])
|
let a = usefulFact(n.sons[1], o)
|
||||||
if a != nil:
|
if a != nil:
|
||||||
result = a.neg
|
result = a.neg(o)
|
||||||
of mOr:
|
of mOr:
|
||||||
# 'or' sucks! (p.isNil or q.isNil) --> hard to do anything
|
# 'or' sucks! (p.isNil or q.isNil) --> hard to do anything
|
||||||
# with that knowledge...
|
# with that knowledge...
|
||||||
|
|
@ -374,14 +381,14 @@ proc usefulFact(n: PNode): PNode =
|
||||||
# (x == 3) or (y == 2) ---> not ( not (x==3) and not (y == 2))
|
# (x == 3) or (y == 2) ---> not ( not (x==3) and not (y == 2))
|
||||||
# not (x != 3 and y != 2)
|
# not (x != 3 and y != 2)
|
||||||
let
|
let
|
||||||
a = usefulFact(n.sons[1]).neg
|
a = usefulFact(n.sons[1], o).neg(o)
|
||||||
b = usefulFact(n.sons[2]).neg
|
b = usefulFact(n.sons[2], o).neg(o)
|
||||||
if a != nil and b != nil:
|
if a != nil and b != nil:
|
||||||
result = newNodeI(nkCall, n.info, 3)
|
result = newNodeI(nkCall, n.info, 3)
|
||||||
result.sons[0] = newSymNode(opAnd)
|
result.sons[0] = newSymNode(o.opAnd)
|
||||||
result.sons[1] = a
|
result.sons[1] = a
|
||||||
result.sons[2] = b
|
result.sons[2] = b
|
||||||
result = result.neg
|
result = result.neg(o)
|
||||||
elif n.kind == nkSym and n.sym.kind == skLet:
|
elif n.kind == nkSym and n.sym.kind == skLet:
|
||||||
# consider:
|
# consider:
|
||||||
# let a = 2 < x
|
# let a = 2 < x
|
||||||
|
|
@ -389,32 +396,34 @@ proc usefulFact(n: PNode): PNode =
|
||||||
# ...
|
# ...
|
||||||
# We make can easily replace 'a' by '2 < x' here:
|
# We make can easily replace 'a' by '2 < x' here:
|
||||||
if n.sym.ast != nil:
|
if n.sym.ast != nil:
|
||||||
result = usefulFact(n.sym.ast)
|
result = usefulFact(n.sym.ast, o)
|
||||||
elif n.kind == nkStmtListExpr:
|
elif n.kind == nkStmtListExpr:
|
||||||
result = usefulFact(n.lastSon)
|
result = usefulFact(n.lastSon, o)
|
||||||
|
|
||||||
type
|
type
|
||||||
TModel* = seq[PNode] # the "knowledge base"
|
TModel* = object
|
||||||
|
s: seq[PNode] # the "knowledge base"
|
||||||
|
o: Operators
|
||||||
|
|
||||||
proc addFact*(m: var TModel, nn: PNode) =
|
proc addFact*(m: var TModel, nn: PNode) =
|
||||||
let n = usefulFact(nn)
|
let n = usefulFact(nn, m.o)
|
||||||
if n != nil: m.add n
|
if n != nil: m.s.add n
|
||||||
|
|
||||||
proc addFactNeg*(m: var TModel, n: PNode) =
|
proc addFactNeg*(m: var TModel, n: PNode) =
|
||||||
let n = n.neg
|
let n = n.neg(m.o)
|
||||||
if n != nil: addFact(m, n)
|
if n != nil: addFact(m, n)
|
||||||
|
|
||||||
proc canonOpr(opr: PSym): PSym =
|
proc sameOpr(a, b: PSym): bool =
|
||||||
case opr.magic
|
case a.magic
|
||||||
of someEq: result = opEq
|
of someEq: result = b.magic in someEq
|
||||||
of someLe: result = opLe
|
of someLe: result = b.magic in someLe
|
||||||
of someLt: result = opLt
|
of someLt: result = b.magic in someLt
|
||||||
of someLen: result = opLen
|
of someLen: result = b.magic in someLen
|
||||||
of someAdd: result = opAdd
|
of someAdd: result = b.magic in someAdd
|
||||||
of someSub: result = opSub
|
of someSub: result = b.magic in someSub
|
||||||
of someMul: result = opMul
|
of someMul: result = b.magic in someMul
|
||||||
of someDiv: result = opDiv
|
of someDiv: result = b.magic in someDiv
|
||||||
else: result = opr
|
else: result = a == b
|
||||||
|
|
||||||
proc sameTree*(a, b: PNode): bool =
|
proc sameTree*(a, b: PNode): bool =
|
||||||
result = false
|
result = false
|
||||||
|
|
@ -425,7 +434,7 @@ proc sameTree*(a, b: PNode): bool =
|
||||||
of nkSym:
|
of nkSym:
|
||||||
result = a.sym == b.sym
|
result = a.sym == b.sym
|
||||||
if not result and a.sym.magic != mNone:
|
if not result and a.sym.magic != mNone:
|
||||||
result = a.sym.magic == b.sym.magic or canonOpr(a.sym) == canonOpr(b.sym)
|
result = a.sym.magic == b.sym.magic or sameOpr(a.sym, b.sym)
|
||||||
of nkIdent: result = a.ident.id == b.ident.id
|
of nkIdent: result = a.ident.id == b.ident.id
|
||||||
of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal
|
of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal
|
||||||
of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
|
of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
|
||||||
|
|
@ -462,8 +471,8 @@ proc invalidateFacts*(m: var TModel, n: PNode) =
|
||||||
# The same mechanism could be used for more complex data stored on the heap;
|
# The same mechanism could be used for more complex data stored on the heap;
|
||||||
# procs that 'write: []' cannot invalidate 'n.kind' for instance. In fact, we
|
# procs that 'write: []' cannot invalidate 'n.kind' for instance. In fact, we
|
||||||
# could CSE these expressions then and help C's optimizer.
|
# could CSE these expressions then and help C's optimizer.
|
||||||
for i in 0..high(m):
|
for i in 0..high(m.s):
|
||||||
if m[i] != nil and m[i].hasSubTree(n): m[i] = nil
|
if m.s[i] != nil and m.s[i].hasSubTree(n): m.s[i] = nil
|
||||||
|
|
||||||
proc valuesUnequal(a, b: PNode): bool =
|
proc valuesUnequal(a, b: PNode): bool =
|
||||||
if a.isValue and b.isValue:
|
if a.isValue and b.isValue:
|
||||||
|
|
@ -486,7 +495,7 @@ proc impliesEq(fact, eq: PNode): TImplication =
|
||||||
if sameTree(fact.sons[2], eq.sons[loc]) and isValue(eq.sons[val]):
|
if sameTree(fact.sons[2], eq.sons[loc]) and isValue(eq.sons[val]):
|
||||||
if inSet(fact.sons[1], eq.sons[val]): result = impYes
|
if inSet(fact.sons[1], eq.sons[val]): result = impYes
|
||||||
else: result = impNo
|
else: result = impNo
|
||||||
of mNot, mOr, mAnd: internalError(eq.info, "impliesEq")
|
of mNot, mOr, mAnd: assert(false, "impliesEq")
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc leImpliesIn(x, c, aSet: PNode): TImplication =
|
proc leImpliesIn(x, c, aSet: PNode): TImplication =
|
||||||
|
|
@ -549,7 +558,7 @@ proc impliesIn(fact, loc, aSet: PNode): TImplication =
|
||||||
elif sameTree(fact.sons[2], loc):
|
elif sameTree(fact.sons[2], loc):
|
||||||
# 4 < x --> 3 <= x
|
# 4 < x --> 3 <= x
|
||||||
result = geImpliesIn(fact.sons[2], fact.sons[1].pred, aSet)
|
result = geImpliesIn(fact.sons[2], fact.sons[1].pred, aSet)
|
||||||
of mNot, mOr, mAnd: internalError(loc.info, "impliesIn")
|
of mNot, mOr, mAnd: assert(false, "impliesIn")
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc valueIsNil(n: PNode): TImplication =
|
proc valueIsNil(n: PNode): TImplication =
|
||||||
|
|
@ -567,11 +576,11 @@ proc impliesIsNil(fact, eq: PNode): TImplication =
|
||||||
result = valueIsNil(fact.sons[2].skipConv)
|
result = valueIsNil(fact.sons[2].skipConv)
|
||||||
elif sameTree(fact.sons[2], eq.sons[1]):
|
elif sameTree(fact.sons[2], eq.sons[1]):
|
||||||
result = valueIsNil(fact.sons[1].skipConv)
|
result = valueIsNil(fact.sons[1].skipConv)
|
||||||
of mNot, mOr, mAnd: internalError(eq.info, "impliesIsNil")
|
of mNot, mOr, mAnd: assert(false, "impliesIsNil")
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc impliesGe(fact, x, c: PNode): TImplication =
|
proc impliesGe(fact, x, c: PNode): TImplication =
|
||||||
internalAssert isLocation(x)
|
assert isLocation(x)
|
||||||
case fact.sons[0].sym.magic
|
case fact.sons[0].sym.magic
|
||||||
of someEq:
|
of someEq:
|
||||||
if sameTree(fact.sons[1], x):
|
if sameTree(fact.sons[1], x):
|
||||||
|
|
@ -603,7 +612,7 @@ proc impliesGe(fact, x, c: PNode): TImplication =
|
||||||
# fact: 3 <= x; question: x >= 2 ? --> true iff 2 <= 3
|
# fact: 3 <= x; question: x >= 2 ? --> true iff 2 <= 3
|
||||||
if isValue(fact.sons[1]) and isValue(c):
|
if isValue(fact.sons[1]) and isValue(c):
|
||||||
if leValue(c, fact.sons[1]): result = impYes
|
if leValue(c, fact.sons[1]): result = impYes
|
||||||
of mNot, mOr, mAnd: internalError(x.info, "impliesGe")
|
of mNot, mOr, mAnd: assert(false, "impliesGe")
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc impliesLe(fact, x, c: PNode): TImplication =
|
proc impliesLe(fact, x, c: PNode): TImplication =
|
||||||
|
|
@ -643,7 +652,7 @@ proc impliesLe(fact, x, c: PNode): TImplication =
|
||||||
if isValue(fact.sons[1]) and isValue(c):
|
if isValue(fact.sons[1]) and isValue(c):
|
||||||
if leValue(c, fact.sons[1].pred): result = impNo
|
if leValue(c, fact.sons[1].pred): result = impNo
|
||||||
|
|
||||||
of mNot, mOr, mAnd: internalError(x.info, "impliesLe")
|
of mNot, mOr, mAnd: assert(false, "impliesLe")
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc impliesLt(fact, x, c: PNode): TImplication =
|
proc impliesLt(fact, x, c: PNode): TImplication =
|
||||||
|
|
@ -707,14 +716,14 @@ proc factImplies(fact, prop: PNode): TImplication =
|
||||||
|
|
||||||
proc doesImply*(facts: TModel, prop: PNode): TImplication =
|
proc doesImply*(facts: TModel, prop: PNode): TImplication =
|
||||||
assert prop.kind in nkCallKinds
|
assert prop.kind in nkCallKinds
|
||||||
for f in facts:
|
for f in facts.s:
|
||||||
# facts can be invalidated, in which case they are 'nil':
|
# facts can be invalidated, in which case they are 'nil':
|
||||||
if not f.isNil:
|
if not f.isNil:
|
||||||
result = f.factImplies(prop)
|
result = f.factImplies(prop)
|
||||||
if result != impUnknown: return
|
if result != impUnknown: return
|
||||||
|
|
||||||
proc impliesNotNil*(facts: TModel, arg: PNode): TImplication =
|
proc impliesNotNil*(m: TModel, arg: PNode): TImplication =
|
||||||
result = doesImply(facts, opIsNil.buildCall(arg).neg)
|
result = doesImply(m, m.o.opIsNil.buildCall(arg).neg(m.o))
|
||||||
|
|
||||||
proc simpleSlice*(a, b: PNode): BiggestInt =
|
proc simpleSlice*(a, b: PNode): BiggestInt =
|
||||||
# returns 'c' if a..b matches (i+c)..(i+c), -1 otherwise. (i)..(i) is matched
|
# returns 'c' if a..b matches (i+c)..(i+c), -1 otherwise. (i)..(i) is matched
|
||||||
|
|
@ -817,20 +826,20 @@ proc ple(m: TModel; a, b: PNode): TImplication =
|
||||||
if a.getMagic in someMul and a[2].isValue and a[1].getMagic in someDiv and
|
if a.getMagic in someMul and a[2].isValue and a[1].getMagic in someDiv and
|
||||||
a[1][2].isValue:
|
a[1][2].isValue:
|
||||||
# simplify (x div 4) * 2 <= y to x div (c div d) <= y
|
# simplify (x div 4) * 2 <= y to x div (c div d) <= y
|
||||||
if ple(m, buildCall(opDiv, a[1][1], `|div|`(a[1][2], a[2])), b) == impYes:
|
if ple(m, buildCall(m.o.opDiv, a[1][1], `|div|`(a[1][2], a[2])), b) == impYes:
|
||||||
return impYes
|
return impYes
|
||||||
|
|
||||||
# x*3 + x == x*4. It follows that:
|
# x*3 + x == x*4. It follows that:
|
||||||
# x*3 + y <= x*4 if y <= x and 3 <= 4
|
# x*3 + y <= x*4 if y <= x and 3 <= 4
|
||||||
if a =~ x*dc + y and b =~ x2*ec:
|
if a =~ x*dc + y and b =~ x2*ec:
|
||||||
if sameTree(x, x2):
|
if sameTree(x, x2):
|
||||||
let ec1 = opAdd.buildCall(ec, minusOne())
|
let ec1 = m.o.opAdd.buildCall(ec, minusOne())
|
||||||
if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? x:
|
if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? x:
|
||||||
return impYes
|
return impYes
|
||||||
elif a =~ x*dc and b =~ x2*ec + y:
|
elif a =~ x*dc and b =~ x2*ec + y:
|
||||||
#echo "BUG cam ehrer e ", a, " <=? ", b
|
#echo "BUG cam ehrer e ", a, " <=? ", b
|
||||||
if sameTree(x, x2):
|
if sameTree(x, x2):
|
||||||
let ec1 = opAdd.buildCall(ec, minusOne())
|
let ec1 = m.o.opAdd.buildCall(ec, minusOne())
|
||||||
if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? zero():
|
if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? zero():
|
||||||
return impYes
|
return impYes
|
||||||
|
|
||||||
|
|
@ -863,9 +872,9 @@ proc ple(m: TModel; a, b: PNode): TImplication =
|
||||||
|
|
||||||
# use the knowledge base:
|
# use the knowledge base:
|
||||||
return pleViaModel(m, a, b)
|
return pleViaModel(m, a, b)
|
||||||
#return doesImply(m, opLe.buildCall(a, b))
|
#return doesImply(m, o.opLe.buildCall(a, b))
|
||||||
|
|
||||||
type TReplacements = seq[tuple[a,b: PNode]]
|
type TReplacements = seq[tuple[a, b: PNode]]
|
||||||
|
|
||||||
proc replaceSubTree(n, x, by: PNode): PNode =
|
proc replaceSubTree(n, x, by: PNode): PNode =
|
||||||
if sameTree(n, x):
|
if sameTree(n, x):
|
||||||
|
|
@ -883,11 +892,11 @@ proc applyReplacements(n: PNode; rep: TReplacements): PNode =
|
||||||
|
|
||||||
proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication =
|
proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication =
|
||||||
# now check for inferrable facts: a <= b and b <= c implies a <= c
|
# now check for inferrable facts: a <= b and b <= c implies a <= c
|
||||||
for i in 0..m.high:
|
for i in 0..m.s.high:
|
||||||
let fact = m[i]
|
let fact = m.s[i]
|
||||||
if fact != nil and fact.getMagic in someLe:
|
if fact != nil and fact.getMagic in someLe:
|
||||||
# mark as used:
|
# mark as used:
|
||||||
m[i] = nil
|
m.s[i] = nil
|
||||||
# i <= len-100
|
# i <= len-100
|
||||||
# i <=? len-1
|
# i <=? len-1
|
||||||
# --> true if (len-100) <= (len-1)
|
# --> true if (len-100) <= (len-1)
|
||||||
|
|
@ -919,7 +928,7 @@ proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication =
|
||||||
proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
|
proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
|
||||||
# compute replacements:
|
# compute replacements:
|
||||||
var replacements: TReplacements = @[]
|
var replacements: TReplacements = @[]
|
||||||
for fact in model:
|
for fact in model.s:
|
||||||
if fact != nil and fact.getMagic in someEq:
|
if fact != nil and fact.getMagic in someEq:
|
||||||
let a = fact[1]
|
let a = fact[1]
|
||||||
let b = fact[2]
|
let b = fact[2]
|
||||||
|
|
@ -929,12 +938,13 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
|
||||||
var a = aa
|
var a = aa
|
||||||
var b = bb
|
var b = bb
|
||||||
if replacements.len > 0:
|
if replacements.len > 0:
|
||||||
m = @[]
|
m.s = @[]
|
||||||
|
m.o = model.o
|
||||||
# make the other facts consistent:
|
# make the other facts consistent:
|
||||||
for fact in model:
|
for fact in model.s:
|
||||||
if fact != nil and fact.getMagic notin someEq:
|
if fact != nil and fact.getMagic notin someEq:
|
||||||
# XXX 'canon' should not be necessary here, but it is
|
# XXX 'canon' should not be necessary here, but it is
|
||||||
m.add applyReplacements(fact, replacements).canon
|
m.s.add applyReplacements(fact, replacements).canon(m.o)
|
||||||
a = applyReplacements(aa, replacements)
|
a = applyReplacements(aa, replacements)
|
||||||
b = applyReplacements(bb, replacements)
|
b = applyReplacements(bb, replacements)
|
||||||
else:
|
else:
|
||||||
|
|
@ -943,31 +953,31 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
|
||||||
result = pleViaModelRec(m, a, b)
|
result = pleViaModelRec(m, a, b)
|
||||||
|
|
||||||
proc proveLe*(m: TModel; a, b: PNode): TImplication =
|
proc proveLe*(m: TModel; a, b: PNode): TImplication =
|
||||||
let x = canon(opLe.buildCall(a, b))
|
let x = canon(m.o.opLe.buildCall(a, b), m.o)
|
||||||
#echo "ROOT ", renderTree(x[1]), " <=? ", renderTree(x[2])
|
#echo "ROOT ", renderTree(x[1]), " <=? ", renderTree(x[2])
|
||||||
result = ple(m, x[1], x[2])
|
result = ple(m, x[1], x[2])
|
||||||
if result == impUnknown:
|
if result == impUnknown:
|
||||||
# try an alternative: a <= b iff not (b < a) iff not (b+1 <= a):
|
# try an alternative: a <= b iff not (b < a) iff not (b+1 <= a):
|
||||||
let y = canon(opLe.buildCall(opAdd.buildCall(b, one()), a))
|
let y = canon(m.o.opLe.buildCall(m.o.opAdd.buildCall(b, one()), a), m.o)
|
||||||
result = ~ple(m, y[1], y[2])
|
result = ~ple(m, y[1], y[2])
|
||||||
|
|
||||||
proc addFactLe*(m: var TModel; a, b: PNode) =
|
proc addFactLe*(m: var TModel; a, b: PNode) =
|
||||||
m.add canon(opLe.buildCall(a, b))
|
m.s.add canon(m.o.opLe.buildCall(a, b), m.o)
|
||||||
|
|
||||||
proc settype(n: PNode): PType =
|
proc settype(n: PNode): PType =
|
||||||
result = newType(tySet, n.typ.owner)
|
result = newType(tySet, n.typ.owner)
|
||||||
addSonSkipIntLit(result, n.typ)
|
addSonSkipIntLit(result, n.typ)
|
||||||
|
|
||||||
proc buildOf(it, loc: PNode): PNode =
|
proc buildOf(it, loc: PNode; o: Operators): PNode =
|
||||||
var s = newNodeI(nkCurly, it.info, it.len-1)
|
var s = newNodeI(nkCurly, it.info, it.len-1)
|
||||||
s.typ = settype(loc)
|
s.typ = settype(loc)
|
||||||
for i in 0..it.len-2: s.sons[i] = it.sons[i]
|
for i in 0..it.len-2: s.sons[i] = it.sons[i]
|
||||||
result = newNodeI(nkCall, it.info, 3)
|
result = newNodeI(nkCall, it.info, 3)
|
||||||
result.sons[0] = newSymNode(opContains)
|
result.sons[0] = newSymNode(o.opContains)
|
||||||
result.sons[1] = s
|
result.sons[1] = s
|
||||||
result.sons[2] = loc
|
result.sons[2] = loc
|
||||||
|
|
||||||
proc buildElse(n: PNode): PNode =
|
proc buildElse(n: PNode; o: Operators): PNode =
|
||||||
var s = newNodeIT(nkCurly, n.info, settype(n.sons[0]))
|
var s = newNodeIT(nkCurly, n.info, settype(n.sons[0]))
|
||||||
for i in 1..n.len-2:
|
for i in 1..n.len-2:
|
||||||
let branch = n.sons[i]
|
let branch = n.sons[i]
|
||||||
|
|
@ -975,23 +985,23 @@ proc buildElse(n: PNode): PNode =
|
||||||
for j in 0..branch.len-2:
|
for j in 0..branch.len-2:
|
||||||
s.add(branch.sons[j])
|
s.add(branch.sons[j])
|
||||||
result = newNodeI(nkCall, n.info, 3)
|
result = newNodeI(nkCall, n.info, 3)
|
||||||
result.sons[0] = newSymNode(opContains)
|
result.sons[0] = newSymNode(o.opContains)
|
||||||
result.sons[1] = s
|
result.sons[1] = s
|
||||||
result.sons[2] = n.sons[0]
|
result.sons[2] = n.sons[0]
|
||||||
|
|
||||||
proc addDiscriminantFact*(m: var TModel, n: PNode) =
|
proc addDiscriminantFact*(m: var TModel, n: PNode) =
|
||||||
var fact = newNodeI(nkCall, n.info, 3)
|
var fact = newNodeI(nkCall, n.info, 3)
|
||||||
fact.sons[0] = newSymNode(opEq)
|
fact.sons[0] = newSymNode(m.o.opEq)
|
||||||
fact.sons[1] = n.sons[0]
|
fact.sons[1] = n.sons[0]
|
||||||
fact.sons[2] = n.sons[1]
|
fact.sons[2] = n.sons[1]
|
||||||
m.add fact
|
m.s.add fact
|
||||||
|
|
||||||
proc addAsgnFact*(m: var TModel, key, value: PNode) =
|
proc addAsgnFact*(m: var TModel, key, value: PNode) =
|
||||||
var fact = newNodeI(nkCall, key.info, 3)
|
var fact = newNodeI(nkCall, key.info, 3)
|
||||||
fact.sons[0] = newSymNode(opEq)
|
fact.sons[0] = newSymNode(m.o.opEq)
|
||||||
fact.sons[1] = key
|
fact.sons[1] = key
|
||||||
fact.sons[2] = value
|
fact.sons[2] = value
|
||||||
m.add fact
|
m.s.add fact
|
||||||
|
|
||||||
proc sameSubexprs*(m: TModel; a, b: PNode): bool =
|
proc sameSubexprs*(m: TModel; a, b: PNode): bool =
|
||||||
# This should be used to check whether two *path expressions* refer to the
|
# This should be used to check whether two *path expressions* refer to the
|
||||||
|
|
@ -1004,7 +1014,7 @@ proc sameSubexprs*(m: TModel; a, b: PNode): bool =
|
||||||
# However, nil checking requires exactly the same mechanism! But for now
|
# However, nil checking requires exactly the same mechanism! But for now
|
||||||
# we simply use sameTree and live with the unsoundness of the analysis.
|
# we simply use sameTree and live with the unsoundness of the analysis.
|
||||||
var check = newNodeI(nkCall, a.info, 3)
|
var check = newNodeI(nkCall, a.info, 3)
|
||||||
check.sons[0] = newSymNode(opEq)
|
check.sons[0] = newSymNode(m.o.opEq)
|
||||||
check.sons[1] = a
|
check.sons[1] = a
|
||||||
check.sons[2] = b
|
check.sons[2] = b
|
||||||
result = m.doesImply(check) == impYes
|
result = m.doesImply(check) == impYes
|
||||||
|
|
@ -1012,11 +1022,11 @@ proc sameSubexprs*(m: TModel; a, b: PNode): bool =
|
||||||
proc addCaseBranchFacts*(m: var TModel, n: PNode, i: int) =
|
proc addCaseBranchFacts*(m: var TModel, n: PNode, i: int) =
|
||||||
let branch = n.sons[i]
|
let branch = n.sons[i]
|
||||||
if branch.kind == nkOfBranch:
|
if branch.kind == nkOfBranch:
|
||||||
m.add buildOf(branch, n.sons[0])
|
m.s.add buildOf(branch, n.sons[0], m.o)
|
||||||
else:
|
else:
|
||||||
m.add n.buildElse.neg
|
m.s.add n.buildElse(m.o).neg(m.o)
|
||||||
|
|
||||||
proc buildProperFieldCheck(access, check: PNode): PNode =
|
proc buildProperFieldCheck(access, check: PNode; o: Operators): PNode =
|
||||||
if check.sons[1].kind == nkCurly:
|
if check.sons[1].kind == nkCurly:
|
||||||
result = copyTree(check)
|
result = copyTree(check)
|
||||||
if access.kind == nkDotExpr:
|
if access.kind == nkDotExpr:
|
||||||
|
|
@ -1028,10 +1038,10 @@ proc buildProperFieldCheck(access, check: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
# it is some 'not'
|
# it is some 'not'
|
||||||
assert check.getMagic == mNot
|
assert check.getMagic == mNot
|
||||||
result = buildProperFieldCheck(access, check.sons[1]).neg
|
result = buildProperFieldCheck(access, check.sons[1], o).neg(o)
|
||||||
|
|
||||||
proc checkFieldAccess*(m: TModel, n: PNode) =
|
proc checkFieldAccess*(m: TModel, n: PNode; conf: ConfigRef) =
|
||||||
for i in 1..n.len-1:
|
for i in 1..n.len-1:
|
||||||
let check = buildProperFieldCheck(n.sons[0], n.sons[i])
|
let check = buildProperFieldCheck(n.sons[0], n.sons[i], m.o)
|
||||||
if check != nil and m.doesImply(check) != impYes:
|
if check != nil and m.doesImply(check) != impYes:
|
||||||
message(n.info, warnProveField, renderTree(n.sons[0])); break
|
message(conf, n.info, warnProveField, renderTree(n.sons[0])); break
|
||||||
|
|
|
||||||
|
|
@ -594,11 +594,11 @@ proc internalError*(conf: ConfigRef; errMsg: string) =
|
||||||
writeContext(conf, unknownLineInfo())
|
writeContext(conf, unknownLineInfo())
|
||||||
rawMessage(conf, errInternal, errMsg)
|
rawMessage(conf, errInternal, errMsg)
|
||||||
|
|
||||||
template assertNotNil*(conf, e): untyped =
|
template assertNotNil*(conf: ConfigRef; e): untyped =
|
||||||
if e == nil: internalError(conf, $instantiationInfo())
|
if e == nil: internalError(conf, $instantiationInfo())
|
||||||
e
|
e
|
||||||
|
|
||||||
template internalAssert*(conf, e: bool) =
|
template internalAssert*(conf: ConfigRef, e: bool) =
|
||||||
if not e: internalError(conf, $instantiationInfo())
|
if not e: internalError(conf, $instantiationInfo())
|
||||||
|
|
||||||
proc addSourceLine*(fileIdx: FileIndex, line: string) =
|
proc addSourceLine*(fileIdx: FileIndex, line: string) =
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@
|
||||||
import
|
import
|
||||||
strutils, os, intsets, strtabs
|
strutils, os, intsets, strtabs
|
||||||
|
|
||||||
import "../compiler" / [options, ast, astalgo, msgs, semdata, ropes, idents]
|
import ".." / [options, ast, astalgo, msgs, semdata, ropes, idents,
|
||||||
|
configuration]
|
||||||
import prettybase
|
import prettybase
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -24,8 +25,8 @@ var
|
||||||
gStyleCheck*: StyleCheck
|
gStyleCheck*: StyleCheck
|
||||||
gCheckExtern*, gOnlyMainfile*: bool
|
gCheckExtern*, gOnlyMainfile*: bool
|
||||||
|
|
||||||
proc overwriteFiles*() =
|
proc overwriteFiles*(conf: ConfigRef) =
|
||||||
let doStrip = options.getConfigVar("pretty.strip").normalize == "on"
|
let doStrip = options.getConfigVar(conf, "pretty.strip").normalize == "on"
|
||||||
for i in 0 .. high(gSourceFiles):
|
for i in 0 .. high(gSourceFiles):
|
||||||
if gSourceFiles[i].dirty and not gSourceFiles[i].isNimfixFile and
|
if gSourceFiles[i].dirty and not gSourceFiles[i].isNimfixFile and
|
||||||
(not gOnlyMainfile or gSourceFiles[i].fileIdx == gProjectMainIdx.FileIndex):
|
(not gOnlyMainfile or gSourceFiles[i].fileIdx == gProjectMainIdx.FileIndex):
|
||||||
|
|
@ -41,7 +42,7 @@ proc overwriteFiles*() =
|
||||||
f.write(gSourceFiles[i].newline)
|
f.write(gSourceFiles[i].newline)
|
||||||
f.close
|
f.close
|
||||||
except IOError:
|
except IOError:
|
||||||
rawMessage(errCannotOpenFile, newFile)
|
rawMessage(conf, errGenerated, "cannot open file: " & newFile)
|
||||||
|
|
||||||
proc `=~`(s: string, a: openArray[string]): bool =
|
proc `=~`(s: string, a: openArray[string]): bool =
|
||||||
for x in a:
|
for x in a:
|
||||||
|
|
@ -110,30 +111,30 @@ proc replaceInFile(info: TLineInfo; newName: string) =
|
||||||
system.shallowCopy(gSourceFiles[info.fileIndex.int].lines[info.line.int-1], x)
|
system.shallowCopy(gSourceFiles[info.fileIndex.int].lines[info.line.int-1], x)
|
||||||
gSourceFiles[info.fileIndex.int].dirty = true
|
gSourceFiles[info.fileIndex.int].dirty = true
|
||||||
|
|
||||||
proc checkStyle(info: TLineInfo, s: string, k: TSymKind; sym: PSym) =
|
proc checkStyle(conf: ConfigRef; info: TLineInfo, s: string, k: TSymKind; sym: PSym) =
|
||||||
let beau = beautifyName(s, k)
|
let beau = beautifyName(s, k)
|
||||||
if s != beau:
|
if s != beau:
|
||||||
if gStyleCheck == StyleCheck.Auto:
|
if gStyleCheck == StyleCheck.Auto:
|
||||||
sym.name = getIdent(beau)
|
sym.name = getIdent(beau)
|
||||||
replaceInFile(info, beau)
|
replaceInFile(info, beau)
|
||||||
else:
|
else:
|
||||||
message(info, hintName, beau)
|
message(conf, info, hintName, beau)
|
||||||
|
|
||||||
proc styleCheckDefImpl(info: TLineInfo; s: PSym; k: TSymKind) =
|
proc styleCheckDefImpl(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) =
|
||||||
# operators stay as they are:
|
# operators stay as they are:
|
||||||
if k in {skResult, skTemp} or s.name.s[0] notin prettybase.Letters: return
|
if k in {skResult, skTemp} or s.name.s[0] notin prettybase.Letters: return
|
||||||
if k in {skType, skGenericParam} and sfAnon in s.flags: return
|
if k in {skType, skGenericParam} and sfAnon in s.flags: return
|
||||||
if {sfImportc, sfExportc} * s.flags == {} or gCheckExtern:
|
if {sfImportc, sfExportc} * s.flags == {} or gCheckExtern:
|
||||||
checkStyle(info, s.name.s, k, s)
|
checkStyle(conf, info, s.name.s, k, s)
|
||||||
|
|
||||||
template styleCheckDef*(info: TLineInfo; s: PSym; k: TSymKind) =
|
template styleCheckDef*(info: TLineInfo; s: PSym; k: TSymKind) =
|
||||||
when defined(nimfix):
|
when defined(nimfix):
|
||||||
if gStyleCheck != StyleCheck.None: styleCheckDefImpl(info, s, k)
|
if gStyleCheck != StyleCheck.None: styleCheckDefImpl(conf, info, s, k)
|
||||||
|
|
||||||
template styleCheckDef*(info: TLineInfo; s: PSym) =
|
template styleCheckDef*(info: TLineInfo; s: PSym) =
|
||||||
styleCheckDef(info, s, s.kind)
|
styleCheckDef(conf, info, s, s.kind)
|
||||||
template styleCheckDef*(s: PSym) =
|
template styleCheckDef*(s: PSym) =
|
||||||
styleCheckDef(s.info, s, s.kind)
|
styleCheckDef(conf, s.info, s, s.kind)
|
||||||
|
|
||||||
proc styleCheckUseImpl(info: TLineInfo; s: PSym) =
|
proc styleCheckUseImpl(info: TLineInfo; s: PSym) =
|
||||||
if info.fileIndex.int < 0: return
|
if info.fileIndex.int < 0: return
|
||||||
|
|
@ -151,4 +152,4 @@ proc styleCheckUseImpl(info: TLineInfo; s: PSym) =
|
||||||
|
|
||||||
template styleCheckUse*(info: TLineInfo; s: PSym) =
|
template styleCheckUse*(info: TLineInfo; s: PSym) =
|
||||||
when defined(nimfix):
|
when defined(nimfix):
|
||||||
if gStyleCheck != StyleCheck.None: styleCheckUseImpl(info, s)
|
if gStyleCheck != StyleCheck.None: styleCheckUseImpl(conf, info, s)
|
||||||
|
|
|
||||||
|
|
@ -131,15 +131,23 @@ type
|
||||||
symbols*: StringTableRef ## We need to use a StringTableRef here as defined
|
symbols*: StringTableRef ## We need to use a StringTableRef here as defined
|
||||||
## symbols are always guaranteed to be style
|
## symbols are always guaranteed to be style
|
||||||
## insensitive. Otherwise hell would break lose.
|
## insensitive. Otherwise hell would break lose.
|
||||||
|
packageCache*: StringTableRef
|
||||||
|
|
||||||
const oldExperimentalFeatures* = {implicitDeref, dotOperators, callOperator, parallel}
|
const oldExperimentalFeatures* = {implicitDeref, dotOperators, callOperator, parallel}
|
||||||
|
|
||||||
|
template newPackageCache*(): untyped =
|
||||||
|
newStringTable(when FileSystemCaseSensitive:
|
||||||
|
modeCaseInsensitive
|
||||||
|
else:
|
||||||
|
modeCaseSensitive)
|
||||||
|
|
||||||
proc newConfigRef*(): ConfigRef =
|
proc newConfigRef*(): ConfigRef =
|
||||||
result = ConfigRef(cppDefines: initSet[string](),
|
result = ConfigRef(cppDefines: initSet[string](),
|
||||||
headerFile: "", features: {}, foreignPackageNotes: {hintProcessing, warnUnknownMagic,
|
headerFile: "", features: {}, foreignPackageNotes: {hintProcessing, warnUnknownMagic,
|
||||||
hintQuitCalled, hintExecuting},
|
hintQuitCalled, hintExecuting},
|
||||||
notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1],
|
notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1],
|
||||||
symbols: newStringTable(modeStyleInsensitive))
|
symbols: newStringTable(modeStyleInsensitive),
|
||||||
|
packageCache: newPackageCache())
|
||||||
|
|
||||||
proc newPartialConfigRef*(): ConfigRef =
|
proc newPartialConfigRef*(): ConfigRef =
|
||||||
## create a new ConfigRef that is only good enough for error reporting.
|
## create a new ConfigRef that is only good enough for error reporting.
|
||||||
|
|
@ -422,7 +430,7 @@ proc rawFindFile2(conf: ConfigRef; f: string): string =
|
||||||
|
|
||||||
template patchModule(conf: ConfigRef) {.dirty.} =
|
template patchModule(conf: ConfigRef) {.dirty.} =
|
||||||
if result.len > 0 and gModuleOverrides.len > 0:
|
if result.len > 0 and gModuleOverrides.len > 0:
|
||||||
let key = getPackageName(result) & "_" & splitFile(result).name
|
let key = getPackageName(conf, result) & "_" & splitFile(result).name
|
||||||
if gModuleOverrides.hasKey(key):
|
if gModuleOverrides.hasKey(key):
|
||||||
let ov = gModuleOverrides[key]
|
let ov = gModuleOverrides[key]
|
||||||
if ov.len > 0: result = ov
|
if ov.len > 0: result = ov
|
||||||
|
|
|
||||||
|
|
@ -15,23 +15,16 @@ iterator myParentDirs(p: string): string =
|
||||||
if current.len == 0: break
|
if current.len == 0: break
|
||||||
yield current
|
yield current
|
||||||
|
|
||||||
template newPackageCache(): untyped =
|
proc resetPackageCache*(conf: ConfigRef) =
|
||||||
newStringTable(when FileSystemCaseSensitive:
|
conf.packageCache = newPackageCache()
|
||||||
modeCaseInsensitive
|
|
||||||
else:
|
|
||||||
modeCaseSensitive)
|
|
||||||
|
|
||||||
var packageCache = newPackageCache()
|
proc getPackageName*(conf: ConfigRef; path: string): string =
|
||||||
|
|
||||||
proc resetPackageCache*() = packageCache = newPackageCache()
|
|
||||||
|
|
||||||
proc getPackageName*(path: string): string =
|
|
||||||
var parents = 0
|
var parents = 0
|
||||||
block packageSearch:
|
block packageSearch:
|
||||||
for d in myParentDirs(path):
|
for d in myParentDirs(path):
|
||||||
if packageCache.hasKey(d):
|
if conf.packageCache.hasKey(d):
|
||||||
#echo "from cache ", d, " |", packageCache[d], "|", path.splitFile.name
|
#echo "from cache ", d, " |", packageCache[d], "|", path.splitFile.name
|
||||||
return packageCache[d]
|
return conf.packageCache[d]
|
||||||
inc parents
|
inc parents
|
||||||
for file in walkFiles(d / "*.nimble"):
|
for file in walkFiles(d / "*.nimble"):
|
||||||
result = file.splitFile.name
|
result = file.splitFile.name
|
||||||
|
|
@ -43,12 +36,12 @@ proc getPackageName*(path: string): string =
|
||||||
if result.isNil: result = ""
|
if result.isNil: result = ""
|
||||||
for d in myParentDirs(path):
|
for d in myParentDirs(path):
|
||||||
#echo "set cache ", d, " |", result, "|", parents
|
#echo "set cache ", d, " |", result, "|", parents
|
||||||
packageCache[d] = result
|
conf.packageCache[d] = result
|
||||||
dec parents
|
dec parents
|
||||||
if parents <= 0: break
|
if parents <= 0: break
|
||||||
|
|
||||||
proc withPackageName*(path: string): string =
|
proc withPackageName*(conf: ConfigRef; path: string): string =
|
||||||
let x = path.getPackageName
|
let x = getPackageName(conf, path)
|
||||||
if x.len == 0:
|
if x.len == 0:
|
||||||
result = path
|
result = path
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,8 @@
|
||||||
## This module implements the pattern matching features for term rewriting
|
## This module implements the pattern matching features for term rewriting
|
||||||
## macro support.
|
## macro support.
|
||||||
|
|
||||||
import strutils, ast, astalgo, types, msgs, idents, renderer, wordrecg, trees
|
import strutils, ast, astalgo, types, msgs, idents, renderer, wordrecg, trees,
|
||||||
|
options
|
||||||
|
|
||||||
# we precompile the pattern here for efficiency into some internal
|
# we precompile the pattern here for efficiency into some internal
|
||||||
# stack based VM :-) Why? Because it's fun; I did no benchmarks to see if that
|
# stack based VM :-) Why? Because it's fun; I did no benchmarks to see if that
|
||||||
|
|
@ -41,8 +42,8 @@ type
|
||||||
const
|
const
|
||||||
MaxStackSize* = 64 ## max required stack size by the VM
|
MaxStackSize* = 64 ## max required stack size by the VM
|
||||||
|
|
||||||
proc patternError(n: PNode) =
|
proc patternError(n: PNode; conf: ConfigRef) =
|
||||||
localError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
|
localError(conf, n.info, "illformed AST: " & renderTree(n, {renderNoComments}))
|
||||||
|
|
||||||
proc add(code: var TPatternCode, op: TOpcode) {.inline.} =
|
proc add(code: var TPatternCode, op: TOpcode) {.inline.} =
|
||||||
add(code, chr(ord(op)))
|
add(code, chr(ord(op)))
|
||||||
|
|
@ -53,34 +54,34 @@ proc whichAlias*(p: PSym): TAliasRequest =
|
||||||
else:
|
else:
|
||||||
result = aqNone
|
result = aqNone
|
||||||
|
|
||||||
proc compileConstraints(p: PNode, result: var TPatternCode) =
|
proc compileConstraints(p: PNode, result: var TPatternCode; conf: ConfigRef) =
|
||||||
case p.kind
|
case p.kind
|
||||||
of nkCallKinds:
|
of nkCallKinds:
|
||||||
if p.sons[0].kind != nkIdent:
|
if p.sons[0].kind != nkIdent:
|
||||||
patternError(p.sons[0])
|
patternError(p.sons[0], conf)
|
||||||
return
|
return
|
||||||
let op = p.sons[0].ident
|
let op = p.sons[0].ident
|
||||||
if p.len == 3:
|
if p.len == 3:
|
||||||
if op.s == "|" or op.id == ord(wOr):
|
if op.s == "|" or op.id == ord(wOr):
|
||||||
compileConstraints(p.sons[1], result)
|
compileConstraints(p.sons[1], result, conf)
|
||||||
compileConstraints(p.sons[2], result)
|
compileConstraints(p.sons[2], result, conf)
|
||||||
result.add(ppOr)
|
result.add(ppOr)
|
||||||
elif op.s == "&" or op.id == ord(wAnd):
|
elif op.s == "&" or op.id == ord(wAnd):
|
||||||
compileConstraints(p.sons[1], result)
|
compileConstraints(p.sons[1], result, conf)
|
||||||
compileConstraints(p.sons[2], result)
|
compileConstraints(p.sons[2], result, conf)
|
||||||
result.add(ppAnd)
|
result.add(ppAnd)
|
||||||
else:
|
else:
|
||||||
patternError(p)
|
patternError(p, conf)
|
||||||
elif p.len == 2 and (op.s == "~" or op.id == ord(wNot)):
|
elif p.len == 2 and (op.s == "~" or op.id == ord(wNot)):
|
||||||
compileConstraints(p.sons[1], result)
|
compileConstraints(p.sons[1], result, conf)
|
||||||
result.add(ppNot)
|
result.add(ppNot)
|
||||||
else:
|
else:
|
||||||
patternError(p)
|
patternError(p, conf)
|
||||||
of nkAccQuoted, nkPar:
|
of nkAccQuoted, nkPar:
|
||||||
if p.len == 1:
|
if p.len == 1:
|
||||||
compileConstraints(p.sons[0], result)
|
compileConstraints(p.sons[0], result, conf)
|
||||||
else:
|
else:
|
||||||
patternError(p)
|
patternError(p, conf)
|
||||||
of nkIdent:
|
of nkIdent:
|
||||||
let spec = p.ident.s.normalize
|
let spec = p.ident.s.normalize
|
||||||
case spec
|
case spec
|
||||||
|
|
@ -97,24 +98,24 @@ proc compileConstraints(p: PNode, result: var TPatternCode) =
|
||||||
of "nosideeffect": result.add(ppNoSideEffect)
|
of "nosideeffect": result.add(ppNoSideEffect)
|
||||||
else:
|
else:
|
||||||
# check all symkinds:
|
# check all symkinds:
|
||||||
internalAssert int(high(TSymKind)) < 255
|
internalAssert conf, int(high(TSymKind)) < 255
|
||||||
for i in low(TSymKind)..high(TSymKind):
|
for i in low(TSymKind)..high(TSymKind):
|
||||||
if cmpIgnoreStyle(($i).substr(2), spec) == 0:
|
if cmpIgnoreStyle(($i).substr(2), spec) == 0:
|
||||||
result.add(ppSymKind)
|
result.add(ppSymKind)
|
||||||
result.add(chr(i.ord))
|
result.add(chr(i.ord))
|
||||||
return
|
return
|
||||||
# check all nodekinds:
|
# check all nodekinds:
|
||||||
internalAssert int(high(TNodeKind)) < 255
|
internalAssert conf, int(high(TNodeKind)) < 255
|
||||||
for i in low(TNodeKind)..high(TNodeKind):
|
for i in low(TNodeKind)..high(TNodeKind):
|
||||||
if cmpIgnoreStyle($i, spec) == 0:
|
if cmpIgnoreStyle($i, spec) == 0:
|
||||||
result.add(ppNodeKind)
|
result.add(ppNodeKind)
|
||||||
result.add(chr(i.ord))
|
result.add(chr(i.ord))
|
||||||
return
|
return
|
||||||
patternError(p)
|
patternError(p, conf)
|
||||||
else:
|
else:
|
||||||
patternError(p)
|
patternError(p, conf)
|
||||||
|
|
||||||
proc semNodeKindConstraints*(p: PNode): PNode =
|
proc semNodeKindConstraints*(p: PNode; conf: ConfigRef): PNode =
|
||||||
## does semantic checking for a node kind pattern and compiles it into an
|
## does semantic checking for a node kind pattern and compiles it into an
|
||||||
## efficient internal format.
|
## efficient internal format.
|
||||||
assert p.kind == nkCurlyExpr
|
assert p.kind == nkCurlyExpr
|
||||||
|
|
@ -123,11 +124,11 @@ proc semNodeKindConstraints*(p: PNode): PNode =
|
||||||
result.strVal.add(chr(aqNone.ord))
|
result.strVal.add(chr(aqNone.ord))
|
||||||
if p.len >= 2:
|
if p.len >= 2:
|
||||||
for i in 1..<p.len:
|
for i in 1..<p.len:
|
||||||
compileConstraints(p.sons[i], result.strVal)
|
compileConstraints(p.sons[i], result.strVal, conf)
|
||||||
if result.strVal.len > MaxStackSize-1:
|
if result.strVal.len > MaxStackSize-1:
|
||||||
internalError(p.info, "parameter pattern too complex")
|
internalError(conf, p.info, "parameter pattern too complex")
|
||||||
else:
|
else:
|
||||||
patternError(p)
|
patternError(p, conf)
|
||||||
result.strVal.add(ppEof)
|
result.strVal.add(ppEof)
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
|
||||||
|
|
@ -886,7 +886,7 @@ proc checkDep(fileIdx: FileIndex; cache: IdentCache; conf: ConfigRef): TReasonFo
|
||||||
var hash = getHash(fileIdx)
|
var hash = getHash(fileIdx)
|
||||||
gMods[fileIdx.int32].reason = rrNone # we need to set it here to avoid cycles
|
gMods[fileIdx.int32].reason = rrNone # we need to set it here to avoid cycles
|
||||||
result = rrNone
|
result = rrNone
|
||||||
var rodfile = toGeneratedFile(conf, filename.withPackageName, RodExt)
|
var rodfile = toGeneratedFile(conf, conf.withPackageName(filename), RodExt)
|
||||||
var r = newRodReader(rodfile, hash, fileIdx.int32, cache, conf)
|
var r = newRodReader(rodfile, hash, fileIdx.int32, cache, conf)
|
||||||
if r == nil:
|
if r == nil:
|
||||||
result = (if existsFile(rodfile): rrRodInvalid else: rrRodDoesNotExist)
|
result = (if existsFile(rodfile): rrRodInvalid else: rrRodDoesNotExist)
|
||||||
|
|
|
||||||
|
|
@ -480,7 +480,7 @@ proc writeRod(w: PRodWriter) =
|
||||||
processStacks(w, true)
|
processStacks(w, true)
|
||||||
var f: File
|
var f: File
|
||||||
if not open(f, completeGeneratedFilePath(w.config, changeFileExt(
|
if not open(f, completeGeneratedFilePath(w.config, changeFileExt(
|
||||||
w.filename.withPackageName, RodExt)),
|
withPackageName(w.config, w.filename), RodExt)),
|
||||||
fmWrite):
|
fmWrite):
|
||||||
#echo "couldn't write rod file for: ", w.filename
|
#echo "couldn't write rod file for: ", w.filename
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
|
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
|
||||||
wordrecg, strutils, options, guards, writetracking
|
wordrecg, strutils, options, guards, writetracking, configuration
|
||||||
|
|
||||||
when defined(useDfa):
|
when defined(useDfa):
|
||||||
import dfa
|
import dfa
|
||||||
|
|
@ -52,6 +52,7 @@ type
|
||||||
locked: seq[PNode] # locked locations
|
locked: seq[PNode] # locked locations
|
||||||
gcUnsafe, isRecursive, isToplevel, hasSideEffect, inEnforcedGcSafe: bool
|
gcUnsafe, isRecursive, isToplevel, hasSideEffect, inEnforcedGcSafe: bool
|
||||||
maxLockLevel, currLockLevel: TLockLevel
|
maxLockLevel, currLockLevel: TLockLevel
|
||||||
|
config: ConfigRef
|
||||||
PEffects = var TEffects
|
PEffects = var TEffects
|
||||||
|
|
||||||
proc `<`(a, b: TLockLevel): bool {.borrow.}
|
proc `<`(a, b: TLockLevel): bool {.borrow.}
|
||||||
|
|
@ -72,24 +73,23 @@ proc getLockLevel(t: PType): TLockLevel =
|
||||||
|
|
||||||
proc lockLocations(a: PEffects; pragma: PNode) =
|
proc lockLocations(a: PEffects; pragma: PNode) =
|
||||||
if pragma.kind != nkExprColonExpr:
|
if pragma.kind != nkExprColonExpr:
|
||||||
localError(pragma.info, errGenerated, "locks pragma without argument")
|
localError(a.config, pragma.info, "locks pragma without argument")
|
||||||
return
|
return
|
||||||
var firstLL = TLockLevel(-1'i16)
|
var firstLL = TLockLevel(-1'i16)
|
||||||
for x in pragma[1]:
|
for x in pragma[1]:
|
||||||
let thisLL = getLockLevel(x.typ)
|
let thisLL = getLockLevel(x.typ)
|
||||||
if thisLL != 0.TLockLevel:
|
if thisLL != 0.TLockLevel:
|
||||||
if thisLL < 0.TLockLevel or thisLL > MaxLockLevel.TLockLevel:
|
if thisLL < 0.TLockLevel or thisLL > MaxLockLevel.TLockLevel:
|
||||||
localError(x.info, "invalid lock level: " & $thisLL)
|
localError(a.config, x.info, "invalid lock level: " & $thisLL)
|
||||||
elif firstLL < 0.TLockLevel: firstLL = thisLL
|
elif firstLL < 0.TLockLevel: firstLL = thisLL
|
||||||
elif firstLL != thisLL:
|
elif firstLL != thisLL:
|
||||||
localError(x.info, errGenerated,
|
localError(a.config, x.info,
|
||||||
"multi-lock requires the same static lock level for every operand")
|
"multi-lock requires the same static lock level for every operand")
|
||||||
a.maxLockLevel = max(a.maxLockLevel, firstLL)
|
a.maxLockLevel = max(a.maxLockLevel, firstLL)
|
||||||
a.locked.add x
|
a.locked.add x
|
||||||
if firstLL >= 0.TLockLevel and firstLL != a.currLockLevel:
|
if firstLL >= 0.TLockLevel and firstLL != a.currLockLevel:
|
||||||
if a.currLockLevel > 0.TLockLevel and a.currLockLevel <= firstLL:
|
if a.currLockLevel > 0.TLockLevel and a.currLockLevel <= firstLL:
|
||||||
localError(pragma.info, errGenerated,
|
localError(a.config, pragma.info, "invalid nested locking")
|
||||||
"invalid nested locking")
|
|
||||||
a.currLockLevel = firstLL
|
a.currLockLevel = firstLL
|
||||||
|
|
||||||
proc guardGlobal(a: PEffects; n: PNode; guard: PSym) =
|
proc guardGlobal(a: PEffects; n: PNode; guard: PSym) =
|
||||||
|
|
@ -102,7 +102,7 @@ proc guardGlobal(a: PEffects; n: PNode; guard: PSym) =
|
||||||
# message(n.info, warnUnguardedAccess, renderTree(n))
|
# message(n.info, warnUnguardedAccess, renderTree(n))
|
||||||
#else:
|
#else:
|
||||||
if not a.isTopLevel:
|
if not a.isTopLevel:
|
||||||
localError(n.info, errGenerated, "unguarded access: " & renderTree(n))
|
localError(a.config, n.info, "unguarded access: " & renderTree(n))
|
||||||
|
|
||||||
# 'guard*' are checks which are concerned with 'guard' annotations
|
# 'guard*' are checks which are concerned with 'guard' annotations
|
||||||
# (var x{.guard: y.}: int)
|
# (var x{.guard: y.}: int)
|
||||||
|
|
@ -125,7 +125,7 @@ proc guardDotAccess(a: PEffects; n: PNode) =
|
||||||
if ty == nil: break
|
if ty == nil: break
|
||||||
ty = ty.skipTypes(skipPtrs)
|
ty = ty.skipTypes(skipPtrs)
|
||||||
if field == nil:
|
if field == nil:
|
||||||
localError(n.info, errGenerated, "invalid guard field: " & g.name.s)
|
localError(a.config, n.info, "invalid guard field: " & g.name.s)
|
||||||
return
|
return
|
||||||
g = field
|
g = field
|
||||||
#ri.sym.guard = field
|
#ri.sym.guard = field
|
||||||
|
|
@ -138,7 +138,7 @@ proc guardDotAccess(a: PEffects; n: PNode) =
|
||||||
for L in a.locked:
|
for L in a.locked:
|
||||||
#if a.guards.sameSubexprs(dot, L): return
|
#if a.guards.sameSubexprs(dot, L): return
|
||||||
if guards.sameTree(dot, L): return
|
if guards.sameTree(dot, L): return
|
||||||
localError(n.info, errGenerated, "unguarded access: " & renderTree(n))
|
localError(a.config, n.info, "unguarded access: " & renderTree(n))
|
||||||
else:
|
else:
|
||||||
guardGlobal(a, n, g)
|
guardGlobal(a, n, g)
|
||||||
|
|
||||||
|
|
@ -167,9 +167,9 @@ proc initVarViaNew(a: PEffects, n: PNode) =
|
||||||
elif isLocalVar(a, s):
|
elif isLocalVar(a, s):
|
||||||
makeVolatile(a, s)
|
makeVolatile(a, s)
|
||||||
|
|
||||||
proc warnAboutGcUnsafe(n: PNode) =
|
proc warnAboutGcUnsafe(n: PNode; conf: ConfigRef) =
|
||||||
#assert false
|
#assert false
|
||||||
message(n.info, warnGcUnsafe, renderTree(n))
|
message(conf, n.info, warnGcUnsafe, renderTree(n))
|
||||||
|
|
||||||
proc markGcUnsafe(a: PEffects; reason: PSym) =
|
proc markGcUnsafe(a: PEffects; reason: PSym) =
|
||||||
if not a.inEnforcedGcSafe:
|
if not a.inEnforcedGcSafe:
|
||||||
|
|
@ -194,42 +194,42 @@ else:
|
||||||
a.hasSideEffect = true
|
a.hasSideEffect = true
|
||||||
markGcUnsafe(a, reason)
|
markGcUnsafe(a, reason)
|
||||||
|
|
||||||
proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet) =
|
proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet; conf: ConfigRef) =
|
||||||
let u = s.gcUnsafetyReason
|
let u = s.gcUnsafetyReason
|
||||||
if u != nil and not cycleCheck.containsOrIncl(u.id):
|
if u != nil and not cycleCheck.containsOrIncl(u.id):
|
||||||
let msgKind = if onlyWarning: warnGcUnsafe2 else: errGenerated
|
let msgKind = if onlyWarning: warnGcUnsafe2 else: errGenerated
|
||||||
case u.kind
|
case u.kind
|
||||||
of skLet, skVar:
|
of skLet, skVar:
|
||||||
message(s.info, msgKind,
|
message(conf, s.info, msgKind,
|
||||||
("'$#' is not GC-safe as it accesses '$#'" &
|
("'$#' is not GC-safe as it accesses '$#'" &
|
||||||
" which is a global using GC'ed memory") % [s.name.s, u.name.s])
|
" which is a global using GC'ed memory") % [s.name.s, u.name.s])
|
||||||
of routineKinds:
|
of routineKinds:
|
||||||
# recursive call *always* produces only a warning so the full error
|
# recursive call *always* produces only a warning so the full error
|
||||||
# message is printed:
|
# message is printed:
|
||||||
listGcUnsafety(u, true, cycleCheck)
|
listGcUnsafety(u, true, cycleCheck, conf)
|
||||||
message(s.info, msgKind,
|
message(conf, s.info, msgKind,
|
||||||
"'$#' is not GC-safe as it calls '$#'" %
|
"'$#' is not GC-safe as it calls '$#'" %
|
||||||
[s.name.s, u.name.s])
|
[s.name.s, u.name.s])
|
||||||
of skParam, skForVar:
|
of skParam, skForVar:
|
||||||
message(s.info, msgKind,
|
message(conf, s.info, msgKind,
|
||||||
"'$#' is not GC-safe as it performs an indirect call via '$#'" %
|
"'$#' is not GC-safe as it performs an indirect call via '$#'" %
|
||||||
[s.name.s, u.name.s])
|
[s.name.s, u.name.s])
|
||||||
else:
|
else:
|
||||||
message(u.info, msgKind,
|
message(conf, u.info, msgKind,
|
||||||
"'$#' is not GC-safe as it performs an indirect call here" % s.name.s)
|
"'$#' is not GC-safe as it performs an indirect call here" % s.name.s)
|
||||||
|
|
||||||
proc listGcUnsafety(s: PSym; onlyWarning: bool) =
|
proc listGcUnsafety(s: PSym; onlyWarning: bool; conf: ConfigRef) =
|
||||||
var cycleCheck = initIntSet()
|
var cycleCheck = initIntSet()
|
||||||
listGcUnsafety(s, onlyWarning, cycleCheck)
|
listGcUnsafety(s, onlyWarning, cycleCheck, conf)
|
||||||
|
|
||||||
proc useVar(a: PEffects, n: PNode) =
|
proc useVar(a: PEffects, n: PNode) =
|
||||||
let s = n.sym
|
let s = n.sym
|
||||||
if isLocalVar(a, s):
|
if isLocalVar(a, s):
|
||||||
if s.id notin a.init:
|
if s.id notin a.init:
|
||||||
if {tfNeedsInit, tfNotNil} * s.typ.flags != {}:
|
if {tfNeedsInit, tfNotNil} * s.typ.flags != {}:
|
||||||
message(n.info, warnProveInit, s.name.s)
|
message(a.config, n.info, warnProveInit, s.name.s)
|
||||||
else:
|
else:
|
||||||
message(n.info, warnUninit, s.name.s)
|
message(a.config, n.info, warnUninit, s.name.s)
|
||||||
# prevent superfluous warnings about the same variable:
|
# prevent superfluous warnings about the same variable:
|
||||||
a.init.add s.id
|
a.init.add s.id
|
||||||
if {sfGlobal, sfThread} * s.flags != {} and s.kind in {skVar, skLet} and
|
if {sfGlobal, sfThread} * s.flags != {} and s.kind in {skVar, skLet} and
|
||||||
|
|
@ -257,9 +257,8 @@ proc addToIntersection(inter: var TIntersection, s: int) =
|
||||||
proc throws(tracked, n: PNode) =
|
proc throws(tracked, n: PNode) =
|
||||||
if n.typ == nil or n.typ.kind != tyError: tracked.add n
|
if n.typ == nil or n.typ.kind != tyError: tracked.add n
|
||||||
|
|
||||||
proc getEbase(): PType =
|
proc getEbase(g: ModuleGraph): PType =
|
||||||
result = if getCompilerProc("Exception") != nil: sysTypeFromName"Exception"
|
result = g.sysTypeFromName"Exception"
|
||||||
else: sysTypeFromName"E_Base"
|
|
||||||
|
|
||||||
proc excType(n: PNode): PType =
|
proc excType(n: PNode): PType =
|
||||||
# reraise is like raising E_Base:
|
# reraise is like raising E_Base:
|
||||||
|
|
|
||||||
|
|
@ -14,21 +14,21 @@ import ast, astalgo, msgs, types, magicsys, semdata, renderer, options
|
||||||
const
|
const
|
||||||
tfInstClearedFlags = {tfHasMeta, tfUnresolved}
|
tfInstClearedFlags = {tfHasMeta, tfUnresolved}
|
||||||
|
|
||||||
proc checkPartialConstructedType(info: TLineInfo, t: PType) =
|
proc checkPartialConstructedType(conf: ConfigRef; info: TLineInfo, t: PType) =
|
||||||
if tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject:
|
if tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject:
|
||||||
localError(info, errInvalidPragmaX, "acyclic")
|
localError(conf, info, "invalid pragma: acyclic")
|
||||||
elif t.kind in {tyVar, tyLent} and t.sons[0].kind in {tyVar, tyLent}:
|
elif t.kind in {tyVar, tyLent} and t.sons[0].kind in {tyVar, tyLent}:
|
||||||
localError(info, errVarVarTypeNotAllowed)
|
localError(conf, info, "type 'var var' is not allowed")
|
||||||
|
|
||||||
proc checkConstructedType*(info: TLineInfo, typ: PType) =
|
proc checkConstructedType*(conf: ConfigRef; info: TLineInfo, typ: PType) =
|
||||||
var t = typ.skipTypes({tyDistinct})
|
var t = typ.skipTypes({tyDistinct})
|
||||||
if t.kind in tyTypeClasses: discard
|
if t.kind in tyTypeClasses: discard
|
||||||
elif tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject:
|
elif tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject:
|
||||||
localError(info, errInvalidPragmaX, "acyclic")
|
localError(conf, info, "invalid pragma: acyclic")
|
||||||
elif t.kind in {tyVar, tyLent} and t.sons[0].kind in {tyVar, tyLent}:
|
elif t.kind in {tyVar, tyLent} and t.sons[0].kind in {tyVar, tyLent}:
|
||||||
localError(info, errVarVarTypeNotAllowed)
|
localError(conf, info, "type 'var var' is not allowed")
|
||||||
elif computeSize(t) == szIllegalRecursion:
|
elif computeSize(t) == szIllegalRecursion:
|
||||||
localError(info, errIllegalRecursionInTypeX, typeToString(t))
|
localError(conf, info, "illegal recursion in type '" & typeToString(t) & "'")
|
||||||
when false:
|
when false:
|
||||||
if t.kind == tyObject and t.sons[0] != nil:
|
if t.kind == tyObject and t.sons[0] != nil:
|
||||||
if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags:
|
if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags:
|
||||||
|
|
@ -36,9 +36,8 @@ proc checkConstructedType*(info: TLineInfo, typ: PType) =
|
||||||
|
|
||||||
proc searchInstTypes*(key: PType): PType =
|
proc searchInstTypes*(key: PType): PType =
|
||||||
let genericTyp = key.sons[0]
|
let genericTyp = key.sons[0]
|
||||||
internalAssert genericTyp.kind == tyGenericBody and
|
if not (genericTyp.kind == tyGenericBody and
|
||||||
key.sons[0] == genericTyp and
|
key.sons[0] == genericTyp and genericTyp.sym != nil): return
|
||||||
genericTyp.sym != nil
|
|
||||||
|
|
||||||
if genericTyp.sym.typeInstCache == nil:
|
if genericTyp.sym.typeInstCache == nil:
|
||||||
return
|
return
|
||||||
|
|
@ -195,19 +194,19 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0): PNode =
|
||||||
var branch: PNode = nil # the branch to take
|
var branch: PNode = nil # the branch to take
|
||||||
for i in countup(0, sonsLen(n) - 1):
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
var it = n.sons[i]
|
var it = n.sons[i]
|
||||||
if it == nil: illFormedAst(n)
|
if it == nil: illFormedAst(n, cl.c.config)
|
||||||
case it.kind
|
case it.kind
|
||||||
of nkElifBranch:
|
of nkElifBranch:
|
||||||
checkSonsLen(it, 2)
|
checkSonsLen(it, 2, cl.c.config)
|
||||||
var cond = prepareNode(cl, it.sons[0])
|
var cond = prepareNode(cl, it.sons[0])
|
||||||
var e = cl.c.semConstExpr(cl.c, cond)
|
var e = cl.c.semConstExpr(cl.c, cond)
|
||||||
if e.kind != nkIntLit:
|
if e.kind != nkIntLit:
|
||||||
internalError(e.info, "ReplaceTypeVarsN: when condition not a bool")
|
internalError(cl.c.config, e.info, "ReplaceTypeVarsN: when condition not a bool")
|
||||||
if e.intVal != 0 and branch == nil: branch = it.sons[1]
|
if e.intVal != 0 and branch == nil: branch = it.sons[1]
|
||||||
of nkElse:
|
of nkElse:
|
||||||
checkSonsLen(it, 1)
|
checkSonsLen(it, 1, cl.c.config)
|
||||||
if branch == nil: branch = it.sons[0]
|
if branch == nil: branch = it.sons[0]
|
||||||
else: illFormedAst(n)
|
else: illFormedAst(n, cl.c.config)
|
||||||
if branch != nil:
|
if branch != nil:
|
||||||
result = replaceTypeVarsN(cl, branch)
|
result = replaceTypeVarsN(cl, branch)
|
||||||
else:
|
else:
|
||||||
|
|
@ -244,14 +243,14 @@ proc lookupTypeVar(cl: var TReplTypeVars, t: PType): PType =
|
||||||
result = cl.typeMap.lookup(t)
|
result = cl.typeMap.lookup(t)
|
||||||
if result == nil:
|
if result == nil:
|
||||||
if cl.allowMetaTypes or tfRetType in t.flags: return
|
if cl.allowMetaTypes or tfRetType in t.flags: return
|
||||||
localError(t.sym.info, errCannotInstantiateX, typeToString(t))
|
localError(cl.c.config, t.sym.info, "cannot instantiate: " & typeToString(t))
|
||||||
result = errorType(cl.c)
|
result = errorType(cl.c)
|
||||||
# In order to prevent endless recursions, we must remember
|
# In order to prevent endless recursions, we must remember
|
||||||
# this bad lookup and replace it with errorType everywhere.
|
# this bad lookup and replace it with errorType everywhere.
|
||||||
# These code paths are only active in "nim check"
|
# These code paths are only active in "nim check"
|
||||||
cl.typeMap.put(t, result)
|
cl.typeMap.put(t, result)
|
||||||
elif result.kind == tyGenericParam and not cl.allowMetaTypes:
|
elif result.kind == tyGenericParam and not cl.allowMetaTypes:
|
||||||
internalError(cl.info, "substitution with generic parameter")
|
internalError(cl.c.config, cl.info, "substitution with generic parameter")
|
||||||
|
|
||||||
proc instCopyType*(cl: var TReplTypeVars, t: PType): PType =
|
proc instCopyType*(cl: var TReplTypeVars, t: PType): PType =
|
||||||
# XXX: relying on allowMetaTypes is a kludge
|
# XXX: relying on allowMetaTypes is a kludge
|
||||||
|
|
@ -278,7 +277,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
|
||||||
# is difficult to handle:
|
# is difficult to handle:
|
||||||
const eqFlags = eqTypeFlags + {tfGcSafe}
|
const eqFlags = eqTypeFlags + {tfGcSafe}
|
||||||
var body = t.sons[0]
|
var body = t.sons[0]
|
||||||
if body.kind != tyGenericBody: internalError(cl.info, "no generic body")
|
if body.kind != tyGenericBody: internalError(cl.c.config, cl.info, "no generic body")
|
||||||
var header: PType = t
|
var header: PType = t
|
||||||
# search for some instantiation here:
|
# search for some instantiation here:
|
||||||
if cl.allowMetaTypes:
|
if cl.allowMetaTypes:
|
||||||
|
|
@ -351,7 +350,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
|
||||||
# handleGenericInvocation will handle the alias-to-alias-to-alias case
|
# handleGenericInvocation will handle the alias-to-alias-to-alias case
|
||||||
if newbody.isGenericAlias: newbody = newbody.skipGenericAlias
|
if newbody.isGenericAlias: newbody = newbody.skipGenericAlias
|
||||||
rawAddSon(result, newbody)
|
rawAddSon(result, newbody)
|
||||||
checkPartialConstructedType(cl.info, newbody)
|
checkPartialConstructedType(cl.c.config, cl.info, newbody)
|
||||||
let dc = newbody.deepCopy
|
let dc = newbody.deepCopy
|
||||||
if cl.allowMetaTypes == false:
|
if cl.allowMetaTypes == false:
|
||||||
if dc != nil and sfFromGeneric notin newbody.deepCopy.flags:
|
if dc != nil and sfFromGeneric notin newbody.deepCopy.flags:
|
||||||
|
|
@ -417,7 +416,7 @@ proc propagateFieldFlags(t: PType, n: PNode) =
|
||||||
# The type must be fully instantiated!
|
# The type must be fully instantiated!
|
||||||
if n.isNil:
|
if n.isNil:
|
||||||
return
|
return
|
||||||
internalAssert n.kind != nkRecWhen
|
#internalAssert n.kind != nkRecWhen
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkSym:
|
of nkSym:
|
||||||
propagateToOwner(t, n.sym.typ)
|
propagateToOwner(t, n.sym.typ)
|
||||||
|
|
@ -454,7 +453,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
||||||
result.kind = tyUserTypeClassInst
|
result.kind = tyUserTypeClassInst
|
||||||
|
|
||||||
of tyGenericBody:
|
of tyGenericBody:
|
||||||
localError(cl.info, errCannotInstantiateX, typeToString(t))
|
localError(cl.c.config, cl.info, "cannot instantiate: " & typeToString(t))
|
||||||
result = errorType(cl.c)
|
result = errorType(cl.c)
|
||||||
#result = replaceTypeVarsT(cl, lastSon(t))
|
#result = replaceTypeVarsT(cl, lastSon(t))
|
||||||
|
|
||||||
|
|
@ -533,7 +532,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
||||||
case result.kind
|
case result.kind
|
||||||
of tyArray:
|
of tyArray:
|
||||||
let idx = result.sons[0]
|
let idx = result.sons[0]
|
||||||
internalAssert idx.kind != tyStatic
|
internalAssert cl.c.config, idx.kind != tyStatic
|
||||||
|
|
||||||
of tyObject, tyTuple:
|
of tyObject, tyTuple:
|
||||||
propagateFieldFlags(result, result.n)
|
propagateFieldFlags(result, result.n)
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
## * Computing an aliasing relation based on the assignments. This relation
|
## * Computing an aliasing relation based on the assignments. This relation
|
||||||
## is then used to compute the 'writes' and 'escapes' effects.
|
## is then used to compute the 'writes' and 'escapes' effects.
|
||||||
|
|
||||||
import intsets, idents, ast, astalgo, trees, renderer, msgs, types
|
import intsets, idents, ast, astalgo, trees, renderer, msgs, types, options
|
||||||
|
|
||||||
const
|
const
|
||||||
debug = false
|
debug = false
|
||||||
|
|
@ -180,7 +180,7 @@ proc deps(w: var W; n: PNode) =
|
||||||
let last = lastSon(child)
|
let last = lastSon(child)
|
||||||
if last.kind == nkEmpty: continue
|
if last.kind == nkEmpty: continue
|
||||||
if child.kind == nkVarTuple and last.kind in {nkPar, nkTupleConstr}:
|
if child.kind == nkVarTuple and last.kind in {nkPar, nkTupleConstr}:
|
||||||
internalAssert child.len-2 == last.len
|
if child.len-2 != last.len: return
|
||||||
for i in 0 .. child.len-3:
|
for i in 0 .. child.len-3:
|
||||||
deps(w, child.sons[i], last.sons[i], {})
|
deps(w, child.sons[i], last.sons[i], {})
|
||||||
else:
|
else:
|
||||||
|
|
@ -220,7 +220,7 @@ proc possibleAliases(w: var W; result: var seq[ptr TSym]) =
|
||||||
# x = f(..., y, ....)
|
# x = f(..., y, ....)
|
||||||
for i in 0 ..< a.srcNoTc: addNoDup a.src[i]
|
for i in 0 ..< a.srcNoTc: addNoDup a.src[i]
|
||||||
|
|
||||||
proc markWriteOrEscape(w: var W) =
|
proc markWriteOrEscape(w: var W; conf: ConfigRef) =
|
||||||
## Both 'writes' and 'escapes' effects ultimately only care
|
## Both 'writes' and 'escapes' effects ultimately only care
|
||||||
## about *parameters*.
|
## about *parameters*.
|
||||||
## However, due to aliasing, even locals that might not look as parameters
|
## However, due to aliasing, even locals that might not look as parameters
|
||||||
|
|
@ -249,7 +249,7 @@ proc markWriteOrEscape(w: var W) =
|
||||||
if p.kind == skParam and p.owner == w.owner:
|
if p.kind == skParam and p.owner == w.owner:
|
||||||
incl(p.flags, sfWrittenTo)
|
incl(p.flags, sfWrittenTo)
|
||||||
if w.owner.kind == skFunc and p.typ.kind != tyVar:
|
if w.owner.kind == skFunc and p.typ.kind != tyVar:
|
||||||
localError(a.info, "write access to non-var parameter: " & p.name.s)
|
localError(conf, a.info, "write access to non-var parameter: " & p.name.s)
|
||||||
|
|
||||||
if {rootIsResultOrParam, rootIsHeapAccess, markAsEscaping}*a.destInfo != {}:
|
if {rootIsResultOrParam, rootIsHeapAccess, markAsEscaping}*a.destInfo != {}:
|
||||||
var destIsParam = false
|
var destIsParam = false
|
||||||
|
|
@ -263,14 +263,14 @@ proc markWriteOrEscape(w: var W) =
|
||||||
if p.kind == skParam and p.owner == w.owner:
|
if p.kind == skParam and p.owner == w.owner:
|
||||||
incl(p.flags, sfEscapes)
|
incl(p.flags, sfEscapes)
|
||||||
|
|
||||||
proc trackWrites*(owner: PSym; body: PNode) =
|
proc trackWrites*(owner: PSym; body: PNode; conf: ConfigRef) =
|
||||||
var w: W
|
var w: W
|
||||||
w.owner = owner
|
w.owner = owner
|
||||||
w.assignments = @[]
|
w.assignments = @[]
|
||||||
# Phase 1: Collect and preprocess any assignments in the proc body:
|
# Phase 1: Collect and preprocess any assignments in the proc body:
|
||||||
deps(w, body)
|
deps(w, body)
|
||||||
# Phase 2: Compute the 'writes' and 'escapes' effects:
|
# Phase 2: Compute the 'writes' and 'escapes' effects:
|
||||||
markWriteOrEscape(w)
|
markWriteOrEscape(w, conf)
|
||||||
if w.returnsNew != asgnOther and not isEmptyType(owner.typ.sons[0]) and
|
if w.returnsNew != asgnOther and not isEmptyType(owner.typ.sons[0]) and
|
||||||
containsGarbageCollectedRef(owner.typ.sons[0]):
|
containsGarbageCollectedRef(owner.typ.sons[0]):
|
||||||
incl(owner.typ.flags, tfReturnsNew)
|
incl(owner.typ.flags, tfReturnsNew)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue