more destructor based changes (#10885)

* mark user defined destructors with sfOverriden to simplify the logic
* refactoring in preparation to merge liftings and generic instantiations for destructors
* ast: introduce nkHiddenTryStmt for destructor generation in order to be able to optimize better the code later on
* renamed 'patterns' switch to 'trmacros' as it was totally misleading before
* destructors: introduce tfCheckedForDestructor flag in preparation of strict =destroy checking
* test for invalid/too late destructor introductions
* liftdestructors: make code robust for nimsuggest
* --newruntime works for hello world again
* newruntime: code generation for closures
This commit is contained in:
Andreas Rumpf 2019-03-23 00:12:05 +01:00 • committed by GitHub
commit bf592c4e94
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 443 additions and 242 deletions

View file

@ -9,8 +9,8 @@
import
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
wordrecg, strutils, options, guards, lineinfos, semfold,
modulegraphs
wordrecg, strutils, options, guards, lineinfos, semfold, semdata,
modulegraphs, lowerings, sigmatch
when not defined(leanCompiler):
import writetracking
@ -18,11 +18,30 @@ when not defined(leanCompiler):
when defined(useDfa):
import dfa
# Second semantic checking pass over the AST. Necessary because the old
# way had some inherent problems. Performs:
#
# * effect+exception tracking
# * "usage before definition" checking
include liftdestructors
#[ Second semantic checking pass over the AST. Necessary because the old
way had some inherent problems. Performs:
* effect+exception tracking
* "usage before definition" checking
* also now calls the "lift destructor logic" at strategic positions, this
is about to be put into the spec:
We treat assignment and sinks and destruction as identical.
In the construct let/var x = expr() x's type is marked.
In x = y the type of x is marked.
For every sink parameter of type T T is marked. TODO!
For every call f() the return type of f() is marked.
]#
# ------------------------ exception and tag tracking -------------------------
@ -60,6 +79,7 @@ type
maxLockLevel, currLockLevel: TLockLevel
config: ConfigRef
graph: ModuleGraph
c: PContext
PEffects = var TEffects
proc `<`(a, b: TLockLevel): bool {.borrow.}
@ -416,73 +436,6 @@ proc trackPragmaStmt(tracked: PEffects, n: PNode) =
# list the computed effects up to here:
listEffects(tracked)
proc effectSpec(n: PNode, effectType: TSpecialWord): PNode =
for i in countup(0, sonsLen(n) - 1):
var it = n.sons[i]
if it.kind == nkExprColonExpr and whichPragma(it) == effectType:
result = it.sons[1]
if result.kind notin {nkCurly, nkBracket}:
result = newNodeI(nkCurly, result.info)
result.add(it.sons[1])
return
proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, idx: int): PNode =
let spec = effectSpec(x, effectType)
if isNil(spec):
let s = n.sons[namePos].sym
let actual = s.typ.n.sons[0]
if actual.len != effectListLen: return
let real = actual.sons[idx]
# warning: hack ahead:
var effects = newNodeI(nkBracket, n.info, real.len)
for i in 0 ..< real.len:
var t = typeToString(real[i].typ)
if t.startsWith("ref "): t = substr(t, 4)
effects.sons[i] = newIdentNode(getIdent(cache, t), n.info)
# set the type so that the following analysis doesn't screw up:
effects.sons[i].typ = real[i].typ
result = newNode(nkExprColonExpr, n.info, @[
newIdentNode(getIdent(cache, specialWords[effectType]), n.info), effects])
proc documentWriteEffect(cache: IdentCache; n: PNode; flag: TSymFlag; pragmaName: string): PNode =
let s = n.sons[namePos].sym
let params = s.typ.n
var effects = newNodeI(nkBracket, n.info)
for i in 1 ..< params.len:
if params[i].kind == nkSym and flag in params[i].sym.flags:
effects.add params[i]
if effects.len > 0:
result = newNode(nkExprColonExpr, n.info, @[
newIdentNode(getIdent(cache, pragmaName), n.info), effects])
proc documentNewEffect(cache: IdentCache; n: PNode): PNode =
let s = n.sons[namePos].sym
if tfReturnsNew in s.typ.flags:
result = newIdentNode(getIdent(cache, "new"), n.info)
proc documentRaises*(cache: IdentCache; n: PNode) =
if n.sons[namePos].kind != nkSym: return
let pragmas = n.sons[pragmasPos]
let p1 = documentEffect(cache, n, pragmas, wRaises, exceptionEffects)
let p2 = documentEffect(cache, n, pragmas, wTags, tagEffects)
let p3 = documentWriteEffect(cache, n, sfWrittenTo, "writes")
let p4 = documentNewEffect(cache, n)
let p5 = documentWriteEffect(cache, n, sfEscapes, "escapes")
if p1 != nil or p2 != nil or p3 != nil or p4 != nil or p5 != nil:
if pragmas.kind == nkEmpty:
n.sons[pragmasPos] = newNodeI(nkPragma, n.info)
if p1 != nil: n.sons[pragmasPos].add p1
if p2 != nil: n.sons[pragmasPos].add p2
if p3 != nil: n.sons[pragmasPos].add p3
if p4 != nil: n.sons[pragmasPos].add p4
if p5 != nil: n.sons[pragmasPos].add p5
template notGcSafe(t): untyped = {tfGcSafe, tfNoSideEffect} * t.flags == {}
proc importedFromC(n: PNode): bool =
@ -749,11 +702,13 @@ proc track(tracked: PEffects, n: PNode) =
# Here we add a `Exception` tag in order to cover both the cases.
addEffect(tracked, createRaise(tracked.graph, n))
of nkCallKinds:
if getConstExpr(tracked.owner_module, n, tracked.graph) != nil:
return
# p's effects are ours too:
var a = n.sons[0]
let op = a.typ
if getConstExpr(tracked.owner_module, n, tracked.graph) != nil:
return
if op != nil:
createTypeBoundOps(tracked.c, op, n.info)
if a.kind == nkCast and a[1].typ.kind == tyProc:
a = a[1]
# XXX: in rare situations, templates and macros will reach here after
@ -813,10 +768,12 @@ proc track(tracked: PEffects, n: PNode) =
addAsgnFact(tracked.guards, n.sons[0], n.sons[1])
notNilCheck(tracked, n.sons[1], n.sons[0].typ)
when false: cstringCheck(tracked, n)
createTypeBoundOps(tracked.c, n[0].typ, n.info)
of nkVarSection, nkLetSection:
for child in n:
let last = lastSon(child)
if last.kind != nkEmpty: track(tracked, last)
createTypeBoundOps(tracked.c, child[0].typ, child.info)
if child.kind == nkIdentDefs and last.kind != nkEmpty:
for i in 0 .. child.len-3:
initVar(tracked, child.sons[i], volatileCheck=false)
@ -965,7 +922,7 @@ proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode) =
effects[tagEffects] = tagsSpec
effects[pragmasEffects] = n
proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects) =
proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects; c: PContext) =
newSeq(effects.sons, effectListLen)
effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info)
effects.sons[tagEffects] = newNodeI(nkArgList, s.info)
@ -983,8 +940,10 @@ proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects) =
t.locked = @[]
t.graph = g
t.config = g.config
t.c = c
proc trackProc*(g: ModuleGraph; s: PSym, body: PNode) =
proc trackProc*(c: PContext; s: PSym, body: PNode) =
let g = c.graph
var effects = s.typ.n.sons[0]
if effects.kind != nkEffectList: return
# effects already computed?
@ -992,7 +951,7 @@ proc trackProc*(g: ModuleGraph; s: PSym, body: PNode) =
if effects.len == effectListLen: return
var t: TEffects
initEffects(g, effects, s, t)
initEffects(g, effects, s, t, c)
track(t, body)
if not isEmptyType(s.typ.sons[0]) and
{tfNeedsInit, tfNotNil} * s.typ.sons[0].flags != {} and
@ -1043,12 +1002,13 @@ proc trackProc*(g: ModuleGraph; s: PSym, body: PNode) =
dataflowAnalysis(s, body)
when false: trackWrites(s, body)
proc trackTopLevelStmt*(g: ModuleGraph; module: PSym; n: PNode) =
proc trackTopLevelStmt*(c: PContext; module: PSym; n: PNode) =
if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkFuncDef,
nkTypeSection, nkConverterDef, nkMethodDef, nkIteratorDef}:
return
let g = c.graph
var effects = newNode(nkEffectList, n.info)
var t: TEffects
initEffects(g, effects, module, t)
initEffects(g, effects, module, t, c)
t.isToplevel = true
track(t, n)