# # # The Nimrod Compiler # (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # import intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, wordrecg, strutils, options, guards # Second semantic checking pass over the AST. Necessary because the old # way had some inherent problems. Performs: # # * effect+exception tracking # * "usage before definition" checking # * checks for invalid usages of compiletime magics (not implemented) # * checks for invalid usages of PNimNode (not implemented) # * later: will do an escape analysis for closures at least # Predefined effects: # io, time (time dependent), gc (performs GC'ed allocation), exceptions, # side effect (accesses global), store (stores into *type*), # store_unkown (performs some store) --> store(any)|store(x) # load (loads from *type*), recursive (recursive call), unsafe, # endless (has endless loops), --> user effects are defined over *patterns* # --> a TR macro can annotate the proc with user defined annotations # --> the effect system can access these # Load&Store analysis is performed on *paths*. A path is an access like # obj.x.y[i].z; splitting paths up causes some problems: # # var x = obj.x # var z = x.y[i].z # # Alias analysis is affected by this too! A good solution is *type splitting*: # T becomes T1 and T2 if it's known that T1 and T2 can't alias. # # An aliasing problem and a race condition are effectively the same problem. # Type based alias analysis is nice but not sufficient; especially splitting # an array and filling it in parallel should be supported but is not easily # done: It essentially requires a built-in 'indexSplit' operation and dependent # typing. # ------------------------ exception and tag tracking ------------------------- discard """ exception tracking: a() # raises 'x', 'e' try: b() # raises 'e' except e: # must not undo 'e' here; hrm c() --> we need a stack of scopes for this analysis """ const trackGlobals = false ## we don't need it for now type TEffects = object exc: PNode # stack of exceptions tags: PNode # list of tags uses: PNode # list of used global variables bottom: int owner: PSym init: seq[int] # list of initialized variables guards: TModel # nested guards locked: seq[PNode] # locked locations gcUnsafe, isRecursive: bool PEffects = var TEffects proc isLocalVar(a: PEffects, s: PSym): bool = s.kind in {skVar, skResult} and sfGlobal notin s.flags and s.owner == a.owner proc initVar(a: PEffects, n: PNode) = if n.kind != nkSym: return let s = n.sym if isLocalVar(a, s): for x in a.init: if x == s.id: return a.init.add s.id proc initVarViaNew(a: PEffects, n: PNode) = if n.kind != nkSym: return let s = n.sym if {tfNeedsInit, tfNotNil} * s.typ.flags <= {tfNotNil}: # 'x' is not nil, but that doesn't mean it's not nil children # are initialized: initVar(a, n) when trackGlobals: proc addUse(a: PEffects, e: PNode) = var aa = a.uses for i in 0 .. = toCover: tracked.init.add id # else we can't merge setLen(tracked.guards, oldFacts) proc trackIf(tracked: PEffects, n: PNode) = track(tracked, n.sons[0].sons[0]) let oldFacts = tracked.guards.len addFact(tracked.guards, n.sons[0].sons[0]) let oldState = tracked.init.len var inter: TIntersection = @[] var toCover = 0 track(tracked, n.sons[0].sons[1]) if not breaksBlock(n.sons[0].sons[1]): inc toCover for i in oldState.. 1: addFact(tracked.guards, branch.sons[0]) setLen(tracked.init, oldState) for i in 0 .. = toCover: tracked.init.add id # else we can't merge as it is not exhaustive setLen(tracked.guards, oldFacts) proc trackBlock(tracked: PEffects, n: PNode) = if n.kind in {nkStmtList, nkStmtListExpr}: var oldState = -1 for i in 0.. 'y' not defined after block! if oldState < 0: oldState = tracked.init.len track(tracked, n.sons[i]) if oldState > 0: setLen(tracked.init, oldState) 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.sons[i] proc track(tracked: PEffects, n: PNode) = case n.kind of nkSym: useVar(tracked, n) of nkRaiseStmt: n.sons[0].info = n.info throws(tracked.exc, n.sons[0]) for i in 0 ..