DrNim (Nim compiler with Z3 integration) (#13743)
* code cleanups and feature additions * added basic test and koch/CI integration * make it build on Unix * DrNim: now buildable on Unix, only takes 10 minutes, enjoy * added basic documentation for DrNim which can also be seen as the RFC we're following * drnim: change the build setup so that drnim.exe ends up in bin/ * makes simple floating point ranges work * added basic float range check * drnim: teach Z3 about Nim's range types plus code refactoring * drnim: make unsigned numbers work * added and fixed index checking under setLen * first implementation of .ensures, .invariant and .assume (.requires still missing and so is proc type compatibility checking * drnim: .requires checking implemented * drnim: implemented .ensures properly * more impressive test involving min() * drnim: check for proc type compatibility and base method compatibility wrt .requires and .ensures * testament: support for 'pattern <directory> * koch: uses new <directory> feature of testament * drnim: added tiny musings about 'old' * Make testament work with old SSL versions * koch: add support for 'koch drnim -d:release' * drnim: preparations for the param.old notation
This commit is contained in:
parent
dd44701728
commit
9ffec79300
25 changed files with 1242 additions and 35 deletions
|
|
@ -335,8 +335,8 @@ const
|
|||
nkEffectList* = nkArgList
|
||||
# hacks ahead: an nkEffectList is a node with 4 children:
|
||||
exceptionEffects* = 0 # exceptions at position 0
|
||||
usesEffects* = 1 # read effects at position 1
|
||||
writeEffects* = 2 # write effects at position 2
|
||||
requiresEffects* = 1 # 'requires' annotation
|
||||
ensuresEffects* = 2 # 'ensures' annotation
|
||||
tagEffects* = 3 # user defined tag ('gc', 'time' etc.)
|
||||
pragmasEffects* = 4 # not an effect, but a slot for pragmas in proc type
|
||||
effectListLen* = 5 # list of effects list
|
||||
|
|
@ -633,6 +633,7 @@ type
|
|||
mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr,
|
||||
mStrToStr, mEnumToStr,
|
||||
mAnd, mOr,
|
||||
mImplies, mIff, mExists, mForall, mOld,
|
||||
mEqStr, mLeStr, mLtStr,
|
||||
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet,
|
||||
mConStrStr, mSlice,
|
||||
|
|
|
|||
|
|
@ -114,4 +114,5 @@ proc initDefines*(symbols: StringTableRef) =
|
|||
|
||||
defineSymbol("nimHasSinkInference")
|
||||
defineSymbol("nimNewIntegerOps")
|
||||
defineSymbol("nimHasInvariant")
|
||||
defineSymbol("nimHasStacktraceMsgs")
|
||||
|
|
|
|||
|
|
@ -249,6 +249,9 @@ proc pred(n: PNode): PNode =
|
|||
else:
|
||||
result = n
|
||||
|
||||
proc buildLe*(o: Operators; a, b: PNode): PNode =
|
||||
result = o.opLe.buildCall(a, b)
|
||||
|
||||
proc canon*(n: PNode; o: Operators): PNode =
|
||||
if n.safeLen >= 1:
|
||||
result = shallowCopy(n)
|
||||
|
|
|
|||
|
|
@ -72,6 +72,10 @@ 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.}
|
||||
compatibleProps*: proc (graph: ModuleGraph; formal, actual: PType): bool {.nimcall.}
|
||||
|
||||
TPassContext* = object of RootObj # the pass's context
|
||||
PPassContext* = ref TPassContext
|
||||
|
|
|
|||
|
|
@ -28,7 +28,8 @@ const
|
|||
wBorrow, wImportCompilerProc, wThread,
|
||||
wAsmNoStackFrame, wDiscardable, wNoInit, wCodegenDecl,
|
||||
wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe,
|
||||
wConstructor, wLiftLocals, wStackTrace, wLineTrace, wNoDestroy}
|
||||
wConstructor, wLiftLocals, wStackTrace, wLineTrace, wNoDestroy,
|
||||
wRequires, wEnsures}
|
||||
converterPragmas* = procPragmas
|
||||
methodPragmas* = procPragmas+{wBase}-{wImportCpp}
|
||||
templatePragmas* = {wDeprecated, wError, wGensym, wInject, wDirty,
|
||||
|
|
@ -39,7 +40,7 @@ const
|
|||
iteratorPragmas* = declPragmas + {FirstCallConv..LastCallConv, wNoSideEffect, wSideEffect,
|
||||
wMagic, wBorrow,
|
||||
wDiscardable, wGensym, wInject, wRaises,
|
||||
wTags, wLocks, wGcSafe}
|
||||
wTags, wLocks, wGcSafe, wRequires, wEnsures}
|
||||
exprPragmas* = {wLine, wLocks, wNoRewrite, wGcSafe, wNoSideEffect}
|
||||
stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangeChecks,
|
||||
wBoundChecks, wOverflowChecks, wNilChecks, wStaticBoundchecks,
|
||||
|
|
@ -52,11 +53,12 @@ const
|
|||
wDeprecated,
|
||||
wFloatChecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll,
|
||||
wLinearScanEnd, wPatterns, wTrMacros, wEffects, wNoForward, wReorder, wComputedGoto,
|
||||
wInjectStmt, wExperimental, wThis, wUsed}
|
||||
wInjectStmt, wExperimental, wThis, wUsed, wInvariant, wAssume}
|
||||
lambdaPragmas* = declPragmas + {FirstCallConv..LastCallConv,
|
||||
wNoSideEffect, wSideEffect, wNoreturn, wNosinks, wDynlib, wHeader,
|
||||
wThread, wAsmNoStackFrame,
|
||||
wRaises, wLocks, wTags, wGcSafe, wCodegenDecl} - {wExportNims, wError, wUsed} # why exclude these?
|
||||
wRaises, wLocks, wTags, wRequires, wEnsures,
|
||||
wGcSafe, wCodegenDecl} - {wExportNims, wError, wUsed} # why exclude these?
|
||||
typePragmas* = declPragmas + {wMagic, wAcyclic,
|
||||
wPure, wHeader, wCompilerProc, wCore, wFinal, wSize, wShallow,
|
||||
wIncompleteStruct, wByCopy, wByRef,
|
||||
|
|
@ -73,7 +75,8 @@ const
|
|||
wIntDefine, wStrDefine, wBoolDefine, wCompilerProc, wCore}
|
||||
letPragmas* = varPragmas
|
||||
procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNoSideEffect,
|
||||
wThread, wRaises, wLocks, wTags, wGcSafe}
|
||||
wThread, wRaises, wLocks, wTags, wGcSafe,
|
||||
wRequires, wEnsures}
|
||||
forVarPragmas* = {wInject, wGensym}
|
||||
allRoutinePragmas* = methodPragmas + iteratorPragmas + lambdaPragmas
|
||||
enumFieldPragmas* = {wDeprecated}
|
||||
|
|
@ -105,6 +108,26 @@ proc invalidPragma*(c: PContext; n: PNode) =
|
|||
proc illegalCustomPragma*(c: PContext, n: PNode, s: PSym) =
|
||||
localError(c.config, n.info, "cannot attach a custom pragma to '" & s.name.s & "'")
|
||||
|
||||
proc pragmaProposition(c: PContext, n: PNode) =
|
||||
if n.kind notin nkPragmaCallKinds or n.len != 2:
|
||||
localError(c.config, n.info, "proposition expected")
|
||||
else:
|
||||
n[1] = c.semExpr(c, n[1])
|
||||
|
||||
proc pragmaEnsures(c: PContext, n: PNode) =
|
||||
if n.kind notin nkPragmaCallKinds or n.len != 2:
|
||||
localError(c.config, n.info, "proposition expected")
|
||||
else:
|
||||
openScope(c)
|
||||
let o = getCurrOwner(c)
|
||||
if o.kind in routineKinds and o.typ != nil and o.typ.sons[0] != nil:
|
||||
var s = newSym(skResult, getIdent(c.cache, "result"), o, n.info)
|
||||
s.typ = o.typ.sons[0]
|
||||
incl(s.flags, sfUsed)
|
||||
addDecl(c, s)
|
||||
n[1] = c.semExpr(c, n[1])
|
||||
closeScope(c)
|
||||
|
||||
proc pragmaAsm*(c: PContext, n: PNode): char =
|
||||
result = '\0'
|
||||
if n != nil:
|
||||
|
|
@ -1146,6 +1169,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
|||
if sym == nil: invalidPragma(c, it)
|
||||
else: sym.flags.incl sfUsed
|
||||
of wLiftLocals: discard
|
||||
of wRequires, wInvariant, wAssume:
|
||||
pragmaProposition(c, it)
|
||||
of wEnsures:
|
||||
pragmaEnsures(c, it)
|
||||
else: invalidPragma(c, it)
|
||||
elif comesFromPush and whichKeyword(ident) != wInvalid:
|
||||
discard "ignore the .push pragma; it doesn't apply"
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
|
|||
else:
|
||||
break
|
||||
|
||||
proc effectProblem(f, a: PType; result: var string) =
|
||||
proc effectProblem(f, a: PType; result: var string; c: PContext) =
|
||||
if f.kind == tyProc and a.kind == tyProc:
|
||||
if tfThread in f.flags and tfThread notin a.flags:
|
||||
result.add "\n This expression is not GC-safe. Annotate the " &
|
||||
|
|
@ -152,7 +152,9 @@ proc effectProblem(f, a: PType; result: var string) =
|
|||
of efLockLevelsDiffer:
|
||||
result.add "\n The `.locks` requirements differ. Annotate the " &
|
||||
"proc with {.locks: 0.} to get extended error information."
|
||||
|
||||
when defined(drnim):
|
||||
if not c.graph.compatibleProps(c.graph, f, a):
|
||||
result.add "\n The `.requires` or `.ensures` properties are incompatible."
|
||||
|
||||
proc renderNotLValue(n: PNode): string =
|
||||
result = $n
|
||||
|
|
@ -238,7 +240,7 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
|
|||
var got = nArg.typ
|
||||
candidates.add typeToString(got)
|
||||
doAssert wanted != nil
|
||||
if got != nil: effectProblem(wanted, got, candidates)
|
||||
if got != nil: effectProblem(wanted, got, candidates, c)
|
||||
of kUnknown: discard "do not break 'nim check'"
|
||||
candidates.add "\n"
|
||||
if err.firstMismatch.arg == 1 and nArg.kind == nkTupleConstr and
|
||||
|
|
|
|||
|
|
@ -428,6 +428,27 @@ proc turnFinalizerIntoDestructor(c: PContext; orig: PSym; info: TLineInfo): PSym
|
|||
result.typ = newProcType(result.info, result)
|
||||
result.typ.addParam newParam
|
||||
|
||||
proc semQuantifier(c: PContext; n: PNode): PNode =
|
||||
checkMinSonsLen(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]))
|
||||
closeScope(c)
|
||||
|
||||
proc semOld(c: PContext; n: PNode): PNode =
|
||||
if n[1].kind == nkHiddenDeref:
|
||||
n[1] = n[1][0]
|
||||
if n[1].kind != nkSym or n[1].sym.kind != skParam:
|
||||
localError(c.config, n[1].info, "'old' takes a parameter name")
|
||||
elif n[1].sym.owner != getCurrOwner(c):
|
||||
localError(c.config, n[1].info, n[1].sym.name.s & " does not belong to " & getCurrOwner(c).name.s)
|
||||
result = n
|
||||
|
||||
proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
flags: TExprFlags): PNode =
|
||||
## This is the preferred code point to implement magics.
|
||||
|
|
@ -505,4 +526,8 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
|||
result[0] = newSymNode(t.destructor)
|
||||
of mUnown:
|
||||
result = semUnown(c, n)
|
||||
of mExists, mForall:
|
||||
result = semQuantifier(c, n)
|
||||
of mOld:
|
||||
result = semOld(c, n)
|
||||
else: result = n
|
||||
|
|
|
|||
|
|
@ -565,6 +565,7 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType; caller: PNode)
|
|||
elif tfNoSideEffect notin op.flags:
|
||||
markSideEffect(tracked, a)
|
||||
if paramType != nil and paramType.kind == tyVar:
|
||||
invalidateFacts(tracked.guards, n)
|
||||
if n.kind == nkSym and isLocalVar(tracked, n.sym):
|
||||
makeVolatile(tracked, n.sym)
|
||||
if paramType != nil and paramType.kind == tyProc and tfGcSafe in paramType.flags:
|
||||
|
|
@ -675,28 +676,75 @@ 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
|
||||
if fn != nil and fn.kind in routineKinds and fn.ast != nil and resultPos < fn.ast.len:
|
||||
n.sym = fn.ast[resultPos].sym
|
||||
else:
|
||||
localError(c.config, n.info, "routine has no return type, but .requires contains 'result'")
|
||||
else:
|
||||
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) =
|
||||
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)
|
||||
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)
|
||||
|
||||
proc checkBounds(c: PEffects; arr, idx: PNode) =
|
||||
checkLe(c, lowBound(c.config, arr), idx)
|
||||
checkLe(c, idx, highBound(c.config, arr, c.guards.o))
|
||||
|
||||
proc checkRange(c: PEffects; value: PNode; typ: PType) =
|
||||
if typ.skipTypes(abstractInst - {tyRange}).kind == tyRange:
|
||||
let lowBound = nkIntLit.newIntNode(firstOrd(c.config, typ))
|
||||
let t = typ.skipTypes(abstractInst - {tyRange})
|
||||
if t.kind == tyRange:
|
||||
let lowBound = copyTree(t.n[0])
|
||||
lowBound.info = value.info
|
||||
let highBound = nkIntLit.newIntNode(lastOrd(c.config, typ))
|
||||
let highBound = copyTree(t.n[1])
|
||||
highBound.info = value.info
|
||||
checkLe(c, lowBound, value)
|
||||
checkLe(c, value, highBound)
|
||||
|
|
@ -783,6 +831,7 @@ 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}:
|
||||
|
|
@ -976,6 +1025,13 @@ 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:
|
||||
if prove(tracked, pragmaList[i][1]):
|
||||
addFact(tracked.guards, pragmaList[i][1])
|
||||
|
||||
if enforcedGcSafety: tracked.inEnforcedGcSafe = true
|
||||
if enforceNoSideEffects: tracked.inEnforcedNoSideEffects = true
|
||||
track(tracked, n.lastSon)
|
||||
|
|
@ -1065,6 +1121,11 @@ proc checkMethodEffects*(g: ModuleGraph; disp, branch: PSym) =
|
|||
if sfThread in disp.flags and notGcSafe(branch.typ):
|
||||
localError(g.config, branch.info, "base method is GC-safe, but '$1' is not" %
|
||||
branch.name.s)
|
||||
when defined(drnim):
|
||||
if not g.compatibleProps(g, disp.typ, branch.typ):
|
||||
localError(g.config, branch.info, "for method '" & branch.name.s &
|
||||
"' the `.requires` or `.ensures` properties are incompatible.")
|
||||
|
||||
if branch.typ.lockLevel > disp.typ.lockLevel:
|
||||
when true:
|
||||
message(g.config, branch.info, warnLockLevel,
|
||||
|
|
@ -1088,14 +1149,22 @@ proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode) =
|
|||
let tagsSpec = effectSpec(n, wTags)
|
||||
if not isNil(tagsSpec):
|
||||
effects[tagEffects] = tagsSpec
|
||||
|
||||
let requiresSpec = propSpec(n, wRequires)
|
||||
if not isNil(requiresSpec):
|
||||
effects[requiresEffects] = requiresSpec
|
||||
let ensuresSpec = propSpec(n, wEnsures)
|
||||
if not isNil(ensuresSpec):
|
||||
effects[ensuresEffects] = ensuresSpec
|
||||
|
||||
effects[pragmasEffects] = n
|
||||
|
||||
proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects; c: PContext) =
|
||||
newSeq(effects.sons, effectListLen)
|
||||
effects[exceptionEffects] = newNodeI(nkArgList, s.info)
|
||||
effects[tagEffects] = newNodeI(nkArgList, s.info)
|
||||
effects[usesEffects] = g.emptyNode
|
||||
effects[writeEffects] = g.emptyNode
|
||||
effects[requiresEffects] = g.emptyNode
|
||||
effects[ensuresEffects] = g.emptyNode
|
||||
effects[pragmasEffects] = g.emptyNode
|
||||
|
||||
t.exc = effects[exceptionEffects]
|
||||
|
|
@ -1155,6 +1224,15 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
|
|||
# after the check, use the formal spec:
|
||||
effects[tagEffects] = tagsSpec
|
||||
|
||||
let requiresSpec = propSpec(p, wRequires)
|
||||
if not isNil(requiresSpec):
|
||||
effects[requiresEffects] = requiresSpec
|
||||
let ensuresSpec = propSpec(p, wEnsures)
|
||||
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:
|
||||
#localError(s.info, "'$1' is not GC-safe" % s.name.s)
|
||||
|
|
|
|||
|
|
@ -1930,7 +1930,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
|||
if n[pragmasPos].kind != nkEmpty:
|
||||
pragma(c, s, n[pragmasPos], validPragmas)
|
||||
# To ease macro generation that produce forwarded .async procs we now
|
||||
# allow a bit redudancy in the pragma declarations. The rule is
|
||||
# allow a bit redundancy in the pragma declarations. The rule is
|
||||
# a prototype's pragma list must be a superset of the current pragma
|
||||
# list.
|
||||
# XXX This needs more checks eventually, for example that external
|
||||
|
|
|
|||
|
|
@ -646,6 +646,8 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
|||
return isNone
|
||||
when useEffectSystem:
|
||||
if compatibleEffects(f, a) != efCompat: return isNone
|
||||
when defined(drnim):
|
||||
if not c.c.graph.compatibleProps(c.c.graph, f, a): return isNone
|
||||
|
||||
of tyNil:
|
||||
result = f.allowsNil
|
||||
|
|
|
|||
|
|
@ -148,6 +148,12 @@ proc effectSpec*(n: PNode, effectType: TSpecialWord): PNode =
|
|||
result.add(it[1])
|
||||
return
|
||||
|
||||
proc propSpec*(n: PNode, effectType: TSpecialWord): PNode =
|
||||
for i in 0..<n.len:
|
||||
var it = n[i]
|
||||
if it.kind == nkExprColonExpr and whichPragma(it) == effectType:
|
||||
return it[1]
|
||||
|
||||
proc unnestStmts(n, result: PNode) =
|
||||
if n.kind == nkStmtList:
|
||||
for x in items(n): unnestStmts(x, result)
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ type
|
|||
wNonReloadable, wExecuteOnReload,
|
||||
wAssertions, wPatterns, wTrMacros, wSinkInference, wWarnings,
|
||||
wHints, wOptimization, wRaises, wWrites, wReads, wSize, wEffects, wTags,
|
||||
wRequires, wEnsures, wInvariant, wAssume,
|
||||
wDeadCodeElimUnused, # deprecated, dead code elim always happens
|
||||
wSafecode, wPackage, wNoForward, wReorder, wNoRewrite, wNoDestroy,
|
||||
wPragma,
|
||||
|
|
@ -142,6 +143,7 @@ const
|
|||
|
||||
"assertions", "patterns", "trmacros", "sinkinference", "warnings", "hints",
|
||||
"optimization", "raises", "writes", "reads", "size", "effects", "tags",
|
||||
"requires", "ensures", "invariant", "assume",
|
||||
"deadcodeelim", # deprecated, dead code elim always happens
|
||||
"safecode", "package", "noforward", "reorder", "norewrite", "nodestroy",
|
||||
"pragma",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue