# # # The Nimrod Compiler # (c) Copyright 2012 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 # Second semantic checking pass over the AST. Necessary because the old # way had some inherent problems. Performs: # # * procvar checks # * effect+exception tracking # * closure analysis # * 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. when false: proc sem2call(c: PContext, n: PNode): PNode = assert n.kind in nkCallKinds proc sem2sym(c: PContext, n: PNode): PNode = assert n.kind == nkSym # ------------------------ 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 Effect tracking: We track the effects per proc; forward declarations and indirect calls cause problems: Forward declarations are computed lazily (we do this pass after a whole module) and indirect calls are assumed the worst, unless they have an effect annotation. """ type TEffects = object exc: PNode # stack of exceptions tags: PNode # list of tags bottom: int PEffects = var TEffects proc throws(tracked, n: PNode) = if n.typ == nil or n.typ.kind != tyError: tracked.add n proc excType(n: PNode): PType = # reraise is like raising E_Base: let t = if n.kind == nkEmpty: sysTypeFromName"E_Base" else: n.typ result = skipTypes(t, skipPtrs) proc createRaise(n: PNode): PNode = result = newNode(nkType) result.typ = sysTypeFromName"E_Base" if not n.isNil: result.info = n.info proc createTag(n: PNode): PNode = result = newNode(nkType) result.typ = sysTypeFromName"TEffect" if not n.isNil: result.info = n.info proc addEffect(a: PEffects, e: PNode, useLineInfo=true) = assert e.kind != nkRaiseStmt var aa = a.exc for i in a.bottom ..