mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-25 00:35:26 +00:00
drnim: tiny progress (#13882)
* drnim: tiny progress * refactoring complete * drnim: prove .ensures annotations * Moved code around to avoid code duplication * drnim: first implementation of the 'old' property * drnim: be precise about the assignment statement * first implementation of --assumeUnique * progress on forall/exists handling
This commit is contained in:
@@ -218,7 +218,7 @@ type
|
||||
nkEnumFieldDef, # `ident = expr` in an enumeration
|
||||
nkArgList, # argument list
|
||||
nkPattern, # a special pattern; used for matching
|
||||
nkHiddenTryStmt, # token used for interpretation
|
||||
nkHiddenTryStmt, # a hidden try statement
|
||||
nkClosure, # (prc, env)-pair (internally used for code gen)
|
||||
nkGotoState, # used for the state machine (for iterators)
|
||||
nkState, # give a label to a code section (for iterators)
|
||||
|
||||
@@ -11,10 +11,24 @@
|
||||
|
||||
import
|
||||
options, idents, nimconf, extccomp, commands, msgs,
|
||||
lineinfos, modulegraphs, condsyms, os, pathutils
|
||||
lineinfos, modulegraphs, condsyms, os, pathutils, parseopt
|
||||
|
||||
from strutils import normalize
|
||||
|
||||
proc prependCurDir*(f: AbsoluteFile): AbsoluteFile =
|
||||
when defined(unix):
|
||||
if os.isAbsolute(f.string): result = f
|
||||
else: result = AbsoluteFile("./" & f.string)
|
||||
else:
|
||||
result = f
|
||||
|
||||
proc addCmdPrefix*(result: var string, kind: CmdLineKind) =
|
||||
# consider moving this to std/parseopt
|
||||
case kind
|
||||
of cmdLongOption: result.add "--"
|
||||
of cmdShortOption: result.add "-"
|
||||
of cmdArgument, cmdEnd: discard
|
||||
|
||||
type
|
||||
NimProg* = ref object
|
||||
suggestMode*: bool
|
||||
|
||||
@@ -84,8 +84,8 @@ proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true)
|
||||
|
||||
type
|
||||
Operators* = object
|
||||
opNot, opContains, opLe, opLt, opAnd, opOr, opIsNil, opEq: PSym
|
||||
opAdd, opSub, opMul, opDiv, opLen: PSym
|
||||
opNot*, opContains*, opLe*, opLt*, opAnd*, opOr*, opIsNil*, opEq*: PSym
|
||||
opAdd*, opSub*, opMul*, opDiv*, opLen*: PSym
|
||||
|
||||
proc initOperators*(g: ModuleGraph): Operators =
|
||||
result.opLe = createMagic(g, "<=", mLeI)
|
||||
@@ -156,12 +156,12 @@ proc neg(n: PNode; o: Operators): PNode =
|
||||
result[0] = newSymNode(o.opNot)
|
||||
result[1] = n
|
||||
|
||||
proc buildCall(op: PSym; a: PNode): PNode =
|
||||
proc buildCall*(op: PSym; a: PNode): PNode =
|
||||
result = newNodeI(nkCall, a.info, 2)
|
||||
result[0] = newSymNode(op)
|
||||
result[1] = a
|
||||
|
||||
proc buildCall(op: PSym; a, b: PNode): PNode =
|
||||
proc buildCall*(op: PSym; a, b: PNode): PNode =
|
||||
result = newNodeI(nkInfix, a.info, 3)
|
||||
result[0] = newSymNode(op)
|
||||
result[1] = a
|
||||
@@ -464,7 +464,7 @@ proc hasSubTree(n, x: PNode): bool =
|
||||
for i in 0..n.safeLen-1:
|
||||
if hasSubTree(n[i], x): return true
|
||||
|
||||
proc invalidateFacts*(m: var TModel, n: PNode) =
|
||||
proc invalidateFacts*(s: var seq[PNode], n: PNode) =
|
||||
# We are able to guard local vars (as opposed to 'let' variables)!
|
||||
# 'while p != nil: f(p); p = p.next'
|
||||
# This is actually quite easy to do:
|
||||
@@ -482,8 +482,11 @@ proc invalidateFacts*(m: var TModel, n: PNode) =
|
||||
# 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
|
||||
# could CSE these expressions then and help C's optimizer.
|
||||
for i in 0..high(m.s):
|
||||
if m.s[i] != nil and m.s[i].hasSubTree(n): m.s[i] = nil
|
||||
for i in 0..high(s):
|
||||
if s[i] != nil and s[i].hasSubTree(n): s[i] = nil
|
||||
|
||||
proc invalidateFacts*(m: var TModel, n: PNode) =
|
||||
invalidateFacts(m.s, n)
|
||||
|
||||
proc valuesUnequal(a, b: PNode): bool =
|
||||
if a.isValue and b.isValue:
|
||||
|
||||
@@ -72,9 +72,7 @@ type
|
||||
onDefinitionResolveForward*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.}
|
||||
onUsage*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.}
|
||||
globalDestructors*: seq[PNode]
|
||||
proofEngine*: proc (graph: ModuleGraph; assumptions: seq[PNode]; toProve: PNode): (bool, string) {.nimcall.}
|
||||
requirementsCheck*: proc (graph: ModuleGraph; assumptions: seq[PNode];
|
||||
call, requirement: PNode): (bool, string) {.nimcall.}
|
||||
strongSemCheck*: proc (graph: ModuleGraph; owner: PSym; body: PNode) {.nimcall.}
|
||||
compatibleProps*: proc (graph: ModuleGraph; formal, actual: PType): bool {.nimcall.}
|
||||
|
||||
TPassContext* = object of RootObj # the pass's context
|
||||
|
||||
@@ -33,20 +33,6 @@ when defined(profiler) or defined(memProfiler):
|
||||
{.hint: "Profiling support is turned on!".}
|
||||
import nimprof
|
||||
|
||||
proc prependCurDir(f: AbsoluteFile): AbsoluteFile =
|
||||
when defined(unix):
|
||||
if os.isAbsolute(f.string): result = f
|
||||
else: result = AbsoluteFile("./" & f.string)
|
||||
else:
|
||||
result = f
|
||||
|
||||
proc addCmdPrefix*(result: var string, kind: CmdLineKind) =
|
||||
# consider moving this to std/parseopt
|
||||
case kind
|
||||
of cmdLongOption: result.add "--"
|
||||
of cmdShortOption: result.add "-"
|
||||
of cmdArgument, cmdEnd: discard
|
||||
|
||||
proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) =
|
||||
var p = parseopt.initOptParser(cmd)
|
||||
var argsCount = 0
|
||||
|
||||
@@ -53,7 +53,7 @@ const
|
||||
wDeprecated,
|
||||
wFloatChecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll,
|
||||
wLinearScanEnd, wPatterns, wTrMacros, wEffects, wNoForward, wReorder, wComputedGoto,
|
||||
wInjectStmt, wExperimental, wThis, wUsed, wInvariant, wAssume}
|
||||
wInjectStmt, wExperimental, wThis, wUsed, wInvariant, wAssume, wAssert}
|
||||
lambdaPragmas* = declPragmas + {FirstCallConv..LastCallConv,
|
||||
wNoSideEffect, wSideEffect, wNoreturn, wNosinks, wDynlib, wHeader,
|
||||
wThread, wAsmNoStackFrame,
|
||||
|
||||
@@ -429,15 +429,29 @@ proc turnFinalizerIntoDestructor(c: PContext; orig: PSym; info: TLineInfo): PSym
|
||||
result.typ.addParam newParam
|
||||
|
||||
proc semQuantifier(c: PContext; n: PNode): PNode =
|
||||
checkMinSonsLen(n, 2, c.config)
|
||||
checkSonsLen(n, 2, c.config)
|
||||
openScope(c)
|
||||
for i in 0..n.len-2:
|
||||
let v = newSymS(skForVar, n[i], c)
|
||||
styleCheckDef(c.config, v)
|
||||
onDef(n.info, v)
|
||||
n[i] = newSymNode(v)
|
||||
addDecl(c, v)
|
||||
n[^1] = forceBool(c, semExprWithType(c, n[^1]))
|
||||
result = newNodeIT(n.kind, n.info, n.typ)
|
||||
result.add n[0]
|
||||
let args = n[1]
|
||||
assert args.kind == nkArgList
|
||||
for i in 0..args.len-2:
|
||||
let it = args[i]
|
||||
var valid = false
|
||||
if it.kind == nkInfix:
|
||||
let op = considerQuotedIdent(c, it[0])
|
||||
if op.id == ord(wIn):
|
||||
let v = newSymS(skForVar, it[1], c)
|
||||
styleCheckDef(c.config, v)
|
||||
onDef(it[1].info, v)
|
||||
let domain = semExprWithType(c, it[2], {efWantIterator})
|
||||
v.typ = domain.typ
|
||||
valid = true
|
||||
addDecl(c, v)
|
||||
result.add newTree(nkInfix, it[0], newSymNode(v), domain)
|
||||
if not valid:
|
||||
localError(c.config, n.info, "<quantifier> 'in' <range> expected")
|
||||
result.add forceBool(c, semExprWithType(c, args[^1]))
|
||||
closeScope(c)
|
||||
|
||||
proc semOld(c: PContext; n: PNode): PNode =
|
||||
|
||||
@@ -25,7 +25,7 @@ import
|
||||
ast, astalgo, idents, lowerings, magicsys, guards, sempass2, msgs,
|
||||
renderer, types, modulegraphs, options, spawn, lineinfos
|
||||
|
||||
from trees import getMagic
|
||||
from trees import getMagic, isTrue, getRoot
|
||||
from strutils import `%`
|
||||
|
||||
discard """
|
||||
|
||||
@@ -433,7 +433,8 @@ proc isForwardedProc(n: PNode): bool =
|
||||
proc trackPragmaStmt(tracked: PEffects, n: PNode) =
|
||||
for i in 0..<n.len:
|
||||
var it = n[i]
|
||||
if whichPragma(it) == wEffects:
|
||||
let pragma = whichPragma(it)
|
||||
if pragma == wEffects:
|
||||
# list the computed effects up to here:
|
||||
listEffects(tracked)
|
||||
|
||||
@@ -664,10 +665,6 @@ proc trackBlock(tracked: PEffects, n: PNode) =
|
||||
else:
|
||||
track(tracked, n)
|
||||
|
||||
proc isTrue*(n: PNode): bool =
|
||||
n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or
|
||||
n.kind == nkIntLit and n.intVal != 0
|
||||
|
||||
proc paramType(op: PType, i: int): PType =
|
||||
if op != nil and i < op.len: result = op[i]
|
||||
|
||||
@@ -676,14 +673,6 @@ proc cstringCheck(tracked: PEffects; n: PNode) =
|
||||
a.typ.kind == tyString and a.kind notin {nkStrLit..nkTripleStrLit}):
|
||||
message(tracked.config, n.info, warnUnsafeCode, renderTree(n))
|
||||
|
||||
proc prove(c: PEffects; prop: PNode): bool =
|
||||
if c.graph.proofEngine != nil:
|
||||
let (success, m) = c.graph.proofEngine(c.graph, c.guards.s,
|
||||
canon(prop, c.guards.o))
|
||||
if not success:
|
||||
message(c.config, prop.info, warnStaticIndexCheck, "cannot prove: " & $prop & m)
|
||||
result = success
|
||||
|
||||
proc patchResult(c: PEffects; n: PNode) =
|
||||
if n.kind == nkSym and n.sym.kind == skResult:
|
||||
let fn = c.owner
|
||||
@@ -695,45 +684,18 @@ proc patchResult(c: PEffects; n: PNode) =
|
||||
for i in 0..<safeLen(n):
|
||||
patchResult(c, n[i])
|
||||
|
||||
when defined(drnim):
|
||||
proc requiresCheck(c: PEffects; call: PNode; op: PType) =
|
||||
assert op.n[0].kind == nkEffectList
|
||||
if requiresEffects < op.n[0].len:
|
||||
let requires = op.n[0][requiresEffects]
|
||||
if requires != nil and requires.kind != nkEmpty:
|
||||
# we need to map the call arguments to the formal parameters used inside
|
||||
# 'requires':
|
||||
let (success, m) = c.graph.requirementsCheck(c.graph, c.guards.s, call, canon(requires, c.guards.o))
|
||||
if not success:
|
||||
message(c.config, call.info, warnStaticIndexCheck, "cannot prove: " & $requires & m)
|
||||
|
||||
else:
|
||||
template requiresCheck(c, n, op) = discard
|
||||
|
||||
proc checkLe(c: PEffects; a, b: PNode) =
|
||||
if c.graph.proofEngine != nil:
|
||||
var cmpOp = mLeI
|
||||
if a.typ != nil:
|
||||
case a.typ.skipTypes(abstractInst).kind
|
||||
of tyFloat..tyFloat128: cmpOp = mLeF64
|
||||
of tyChar, tyUInt..tyUInt64: cmpOp = mLeU
|
||||
else: discard
|
||||
|
||||
let cmp = newTree(nkInfix, newSymNode createMagic(c.graph, "<=", cmpOp), a, b)
|
||||
cmp.info = a.info
|
||||
discard prove(c, cmp)
|
||||
else:
|
||||
case proveLe(c.guards, a, b)
|
||||
of impUnknown:
|
||||
#for g in c.guards.s:
|
||||
# if g != nil: echo "I Know ", g
|
||||
message(c.config, a.info, warnStaticIndexCheck,
|
||||
"cannot prove: " & $a & " <= " & $b)
|
||||
of impYes:
|
||||
discard
|
||||
of impNo:
|
||||
message(c.config, a.info, warnStaticIndexCheck,
|
||||
"can prove: " & $a & " > " & $b)
|
||||
case proveLe(c.guards, a, b)
|
||||
of impUnknown:
|
||||
#for g in c.guards.s:
|
||||
# if g != nil: echo "I Know ", g
|
||||
message(c.config, a.info, warnStaticIndexCheck,
|
||||
"cannot prove: " & $a & " <= " & $b)
|
||||
of impYes:
|
||||
discard
|
||||
of impNo:
|
||||
message(c.config, a.info, warnStaticIndexCheck,
|
||||
"can prove: " & $a & " > " & $b)
|
||||
|
||||
proc checkBounds(c: PEffects; arr, idx: PNode) =
|
||||
checkLe(c, lowBound(c.config, arr), idx)
|
||||
@@ -831,7 +793,6 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
mergeEffects(tracked, effectList[exceptionEffects], n)
|
||||
mergeTags(tracked, effectList[tagEffects], n)
|
||||
gcsafeAndSideeffectCheck()
|
||||
requiresCheck(tracked, n, op)
|
||||
if a.kind != nkSym or a.sym.magic != mNBindSym:
|
||||
for i in 1..<n.len: trackOperand(tracked, n[i], paramType(op, i), a)
|
||||
if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
|
||||
@@ -929,7 +890,6 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
of nkWhen, nkIfStmt, nkIfExpr: trackIf(tracked, n)
|
||||
of nkBlockStmt, nkBlockExpr: trackBlock(tracked, n[1])
|
||||
of nkWhileStmt:
|
||||
track(tracked, n[0])
|
||||
# 'while true' loop?
|
||||
if isTrue(n[0]):
|
||||
trackBlock(tracked, n[1])
|
||||
@@ -938,6 +898,7 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
let oldState = tracked.init.len
|
||||
let oldFacts = tracked.guards.s.len
|
||||
addFact(tracked.guards, n[0])
|
||||
track(tracked, n[0])
|
||||
track(tracked, n[1])
|
||||
setLen(tracked.init, oldState)
|
||||
setLen(tracked.guards.s, oldFacts)
|
||||
@@ -1027,12 +988,6 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
enforcedGcSafety = true
|
||||
elif pragma == wNoSideEffect:
|
||||
enforceNoSideEffects = true
|
||||
when defined(drnim):
|
||||
if pragma == wAssume:
|
||||
addFact(tracked.guards, pragmaList[i][1])
|
||||
elif pragma == wInvariant or pragma == wAssert:
|
||||
if prove(tracked, pragmaList[i][1]):
|
||||
addFact(tracked.guards, pragmaList[i][1])
|
||||
|
||||
if enforcedGcSafety: tracked.inEnforcedGcSafe = true
|
||||
if enforceNoSideEffects: tracked.inEnforcedNoSideEffects = true
|
||||
@@ -1181,7 +1136,10 @@ proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects; c: PC
|
||||
t.init = @[]
|
||||
t.guards.s = @[]
|
||||
t.guards.o = initOperators(g)
|
||||
t.currOptions = g.config.options + s.options
|
||||
when defined(drnim):
|
||||
t.currOptions = g.config.options + s.options - {optStaticBoundsCheck}
|
||||
else:
|
||||
t.currOptions = g.config.options + s.options
|
||||
t.guards.beSmart = optStaticBoundsCheck in t.currOptions
|
||||
t.locked = @[]
|
||||
t.graph = g
|
||||
@@ -1237,7 +1195,6 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
|
||||
if not isNil(ensuresSpec):
|
||||
patchResult(t, ensuresSpec)
|
||||
effects[ensuresEffects] = ensuresSpec
|
||||
discard prove(t, ensuresSpec)
|
||||
|
||||
if sfThread in s.flags and t.gcUnsafe:
|
||||
if optThreads in g.config.globalOptions and optThreadAnalysis in g.config.globalOptions:
|
||||
@@ -1262,6 +1219,8 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
|
||||
message(g.config, s.info, warnLockLevel,
|
||||
"declared lock level is $1, but real lock level is $2" %
|
||||
[$s.typ.lockLevel, $t.maxLockLevel])
|
||||
when defined(drnim):
|
||||
if c.graph.strongSemCheck != nil: c.graph.strongSemCheck(c.graph, s, body)
|
||||
when defined(useDfa):
|
||||
if s.name.s == "testp":
|
||||
dataflowAnalysis(s, body)
|
||||
@@ -1277,3 +1236,5 @@ proc trackStmt*(c: PContext; module: PSym; n: PNode, isTopLevel: bool) =
|
||||
initEffects(g, effects, module, t, c)
|
||||
t.isTopLevel = isTopLevel
|
||||
track(t, n)
|
||||
when defined(drnim):
|
||||
if c.graph.strongSemCheck != nil: c.graph.strongSemCheck(c.graph, module, n)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import ast, types, idents, magicsys, msgs, options, modulegraphs,
|
||||
lowerings
|
||||
from trees import getMagic
|
||||
from trees import getMagic, getRoot
|
||||
|
||||
proc callProc(a: PNode): PNode =
|
||||
result = newNodeI(nkCall, a.info)
|
||||
@@ -74,7 +74,7 @@ proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; owner: PSym; typ: P
|
||||
varInit.add call
|
||||
else:
|
||||
varInit.add newFastAsgnStmt(newSymNode(result), v)
|
||||
else:
|
||||
else:
|
||||
if useShallowCopy and typeNeedsNoDeepCopy(typ) or optTinyRtti in g.config.globalOptions:
|
||||
varInit.add newFastAsgnStmt(newSymNode(result), v)
|
||||
else:
|
||||
@@ -196,7 +196,7 @@ proc createCastExpr(argsParam: PSym; objType: PType): PNode =
|
||||
result.typ = newType(tyPtr, objType.owner)
|
||||
result.typ.rawAddSon(objType)
|
||||
|
||||
proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType;
|
||||
proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType;
|
||||
owner: PSym; scratchObj: PSym,
|
||||
castExpr, call,
|
||||
varSection, varInit, result: PNode) =
|
||||
@@ -221,23 +221,6 @@ proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType;
|
||||
indirectAccess(castExpr, field, n.info))
|
||||
call.add(newSymNode(temp))
|
||||
|
||||
proc getRoot*(n: PNode): PSym =
|
||||
## ``getRoot`` takes a *path* ``n``. A path is an lvalue expression
|
||||
## like ``obj.x[i].y``. The *root* of a path is the symbol that can be
|
||||
## determined as the owner; ``obj`` in the example.
|
||||
case n.kind
|
||||
of nkSym:
|
||||
if n.sym.kind in {skVar, skResult, skTemp, skLet, skForVar}:
|
||||
result = n.sym
|
||||
of nkDotExpr, nkBracketExpr, nkHiddenDeref, nkDerefExpr,
|
||||
nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
|
||||
result = getRoot(n[0])
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||
result = getRoot(n[1])
|
||||
of nkCallKinds:
|
||||
if getMagic(n) == mSlice: result = getRoot(n[1])
|
||||
else: discard
|
||||
|
||||
proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType;
|
||||
owner: PSym; scratchObj: PSym;
|
||||
castExpr, call,
|
||||
@@ -344,7 +327,7 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P
|
||||
if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}:
|
||||
localError(g.config, n.info, "'spawn' takes a GC safe call expression")
|
||||
|
||||
var fn = n[0]
|
||||
var fn = n[0]
|
||||
let
|
||||
name = (if fn.kind == nkSym: fn.sym.name.s else: genPrefix) & "Wrapper"
|
||||
wrapperProc = newSym(skProc, getIdent(g.cache, name), owner, fn.info, g.config.options)
|
||||
|
||||
@@ -169,3 +169,24 @@ proc flattenStmts*(n: PNode): PNode =
|
||||
proc extractRange*(k: TNodeKind, n: PNode, a, b: int): PNode =
|
||||
result = newNodeI(k, n.info, b-a+1)
|
||||
for i in 0..b-a: result[i] = n[i+a]
|
||||
|
||||
proc isTrue*(n: PNode): bool =
|
||||
n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or
|
||||
n.kind == nkIntLit and n.intVal != 0
|
||||
|
||||
proc getRoot*(n: PNode): PSym =
|
||||
## ``getRoot`` takes a *path* ``n``. A path is an lvalue expression
|
||||
## like ``obj.x[i].y``. The *root* of a path is the symbol that can be
|
||||
## determined as the owner; ``obj`` in the example.
|
||||
case n.kind
|
||||
of nkSym:
|
||||
if n.sym.kind in {skVar, skResult, skTemp, skLet, skForVar, skParam}:
|
||||
result = n.sym
|
||||
of nkDotExpr, nkBracketExpr, nkHiddenDeref, nkDerefExpr,
|
||||
nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
|
||||
result = getRoot(n[0])
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||
result = getRoot(n[1])
|
||||
of nkCallKinds:
|
||||
if getMagic(n) == mSlice: result = getRoot(n[1])
|
||||
else: discard
|
||||
|
||||
Reference in New Issue
Block a user