summary refs log tree commit diff stats
path: root/compiler/sempass2.nim
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/sempass2.nim')
-rw-r--r--compiler/sempass2.nim112
1 files changed, 97 insertions, 15 deletions
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index a00325277..b2b91490c 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -59,15 +59,19 @@ discard """
  --> 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: bool
   PEffects = var TEffects
 
 proc isLocalVar(a: PEffects, s: PSym): bool =
@@ -89,20 +93,29 @@ proc initVarViaNew(a: PEffects, n: PNode) =
     # are initialized:
     initVar(a, n)
 
+when trackGlobals: 
+  proc addUse(a: PEffects, e: PNode) =
+    var aa = a.uses
+    for i in 0 .. <aa.len:
+      if aa[i].sym.id == e.sym.id: return
+    a.uses.add(e)
+
 proc useVar(a: PEffects, n: PNode) =
   let s = n.sym
   if isLocalVar(a, s):
     if s.id notin a.init:
       if {tfNeedsInit, tfNotNil} * s.typ.flags != {}:
-        when true:
-          message(n.info, warnProveInit, s.name.s)
-        else:
-          Message(n.info, errGenerated,
-            "'$1' might not have been initialized" % s.name.s)
+        message(n.info, warnProveInit, s.name.s)
       else:
         message(n.info, warnUninit, s.name.s)
       # prevent superfluous warnings about the same variable:
       a.init.add s.id
+  if {sfGlobal, sfThread} * s.flags == {sfGlobal} and s.kind == skVar:
+    when trackGlobals:
+      a.addUse(copyNode(n))
+    if tfHasGCedMem in s.typ.flags: 
+      message(n.info, warnGcUnsafe, renderTree(n))
+      a.gcUnsafe = true
 
 type
   TIntersection = seq[tuple[id, count: int]] # a simple count table
@@ -132,6 +145,10 @@ proc createTag(n: PNode): PNode =
   result.typ = sysTypeFromName"TEffect"
   if not n.isNil: result.info = n.info
 
+proc createAnyGlobal(n: PNode): PNode =
+  result = newSymNode(anyGlobal)
+  result.info = n.info
+
 proc addEffect(a: PEffects, e: PNode, useLineInfo=true) =
   assert e.kind != nkRaiseStmt
   var aa = a.exc
@@ -161,9 +178,17 @@ proc mergeTags(a: PEffects, b, comesFrom: PNode) =
   else:
     for effect in items(b): addTag(a, effect, useLineInfo=comesFrom != nil)
 
+when trackGlobals:
+  proc mergeUses(a: PEffects, b, comesFrom: PNode) =
+    if b.isNil:
+      addUse(a, createAnyGlobal(comesFrom))
+    else:
+      for effect in items(b): addUse(a, effect)
+
 proc listEffects(a: PEffects) =
   for e in items(a.exc):  message(e.info, hintUser, typeToString(e.typ))
   for e in items(a.tags): message(e.info, hintUser, typeToString(e.typ))
+  for e in items(a.uses): message(e.info, hintUser, e.sym.name.s)
 
 proc catches(tracked: PEffects, e: PType) =
   let e = skipTypes(e, skipPtrs)
@@ -289,6 +314,13 @@ proc documentRaises*(n: PNode) =
   if n.sons[namePos].kind != nkSym: return
   documentEffect(n, n.sons[pragmasPos], wRaises, exceptionEffects)
   documentEffect(n, n.sons[pragmasPos], wTags, tagEffects)
+  documentEffect(n, n.sons[pragmasPos], wUses, usesEffects)
+
+template notGcSafe(t): expr = {tfGcSafe, tfNoSideEffect} * t.flags == {}
+
+proc importedFromC(n: PNode): bool =
+  # when imported from C, we assume GC-safety.
+  result = n.kind == nkSym and sfImportc in n.sym.flags
 
 proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
   let pragma = s.ast.sons[pragmasPos]
@@ -298,6 +330,14 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
   let tagSpec = effectSpec(pragma, wTags)
   mergeTags(tracked, tagSpec, n)
 
+  if notGcSafe(s.typ) and sfImportc notin s.flags:
+    message(n.info, warnGcUnsafe, renderTree(n))
+    tracked.gcUnsafe = true
+
+  when trackGlobals:
+    let usesSpec = effectSpec(pragma, wUses)
+    mergeUses(tracked, usesSpec, n)
+
 proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
   let n = n.skipConv
   if paramType != nil and tfNotNil in paramType.flags and 
@@ -330,9 +370,18 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
       else:
         addEffect(tracked, createRaise(n))
         addTag(tracked, createTag(n))
+        when trackGlobals: addUse(tracked, createAnyGlobal(n))
+      # assume GcUnsafe unless in its type:
+      if notGcSafe(op): 
+        message(n.info, warnGcUnsafe, renderTree(n))
+        tracked.gcUnsafe = true
     else:
       mergeEffects(tracked, effectList.sons[exceptionEffects], n)
       mergeTags(tracked, effectList.sons[tagEffects], n)
+      when trackGlobals: mergeUses(tracked, effectList.sons[usesEffects], n)
+      if notGcSafe(op):
+        message(n.info, warnGcUnsafe, renderTree(n))
+        tracked.gcUnsafe = true
   notNilCheck(tracked, n, paramType)
 
 proc breaksBlock(n: PNode): bool =
@@ -451,8 +500,11 @@ proc track(tracked: PEffects, n: PNode) =
     # XXX: in rare situations, templates and macros will reach here after
     # calling getAst(templateOrMacro()). Currently, templates and macros
     # are indistinguishable from normal procs (both have tyProc type) and
-    # we can detect them only by cheking for attached nkEffectList.
+    # we can detect them only by checking for attached nkEffectList.
     if op != nil and op.kind == tyProc and op.n.sons[0].kind == nkEffectList:
+      if notGcSafe(op) and not importedFromC(a):
+        message(n.info, warnGcUnsafe, renderTree(n))
+        tracked.gcUnsafe = true
       var effectList = op.n.sons[0]
       if a.kind == nkSym and a.sym.kind == skMethod:
         propagateEffects(tracked, n, a.sym)
@@ -462,9 +514,11 @@ proc track(tracked: PEffects, n: PNode) =
         elif isIndirectCall(a, tracked.owner):
           addEffect(tracked, createRaise(n))
           addTag(tracked, createTag(n))
+          when trackGlobals: addUse(tracked, createAnyGlobal(n))
       else:
         mergeEffects(tracked, effectList.sons[exceptionEffects], n)
         mergeTags(tracked, effectList.sons[tagEffects], n)
+        when trackGlobals: mergeUses(tracked, effectList.sons[usesEffects], n)
     for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i))
     if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
       # may not look like an assignment, but it is:
@@ -531,14 +585,21 @@ proc track(tracked: PEffects, n: PNode) =
   else:
     for i in 0 .. <safeLen(n): track(tracked, n.sons[i])
 
-proc checkRaisesSpec(spec, real: PNode, msg: string, hints: bool) =
+proc subtypeRelation(spec, real: PNode): bool =
+  result = safeInheritanceDiff(real.excType, spec.typ) <= 0
+
+proc symbolPredicate(spec, real: PNode): bool =
+  result = real.sym.id == spec.sym.id
+
+proc checkRaisesSpec(spec, real: PNode, msg: string, hints: bool;
+                     effectPredicate: proc (a, b: PNode): bool {.nimcall.}) =
   # check that any real exception is listed in 'spec'; mark those as used;
   # report any unused exception
   var used = initIntSet()
   for r in items(real):
     block search:
       for s in 0 .. <spec.len:
-        if safeInheritanceDiff(r.excType, spec[s].typ) <= 0:
+        if effectPredicate(spec[s], r):
           used.incl(s)
           break search
       # XXX call graph analysis would be nice here!
@@ -560,11 +621,18 @@ proc checkMethodEffects*(disp, branch: PSym) =
   let raisesSpec = effectSpec(p, wRaises)
   if not isNil(raisesSpec):
     checkRaisesSpec(raisesSpec, actual.sons[exceptionEffects],
-      "can raise an unlisted exception: ", hints=off)
+      "can raise an unlisted exception: ", hints=off, subtypeRelation)
   let tagsSpec = effectSpec(p, wTags)
   if not isNil(tagsSpec):
     checkRaisesSpec(tagsSpec, actual.sons[tagEffects],
-      "can have an unlisted effect: ", hints=off)
+      "can have an unlisted effect: ", hints=off, subtypeRelation)
+  let usesSpec = effectSpec(p, wUses)
+  if not isNil(usesSpec):
+    checkRaisesSpec(usesSpec, actual.sons[usesEffects],
+      "may use an unlisted global variable: ", hints=off, symbolPredicate)
+  if sfThread in disp.flags and notGcSafe(branch.typ):
+    localError(branch.info, "base method is GC-safe, but '$1' is not" % 
+                                branch.name.s)
 
 proc setEffectsForProcType*(t: PType, n: PNode) =
   var effects = t.n.sons[0]
@@ -573,21 +641,26 @@ proc setEffectsForProcType*(t: PType, n: PNode) =
   let
     raisesSpec = effectSpec(n, wRaises)
     tagsSpec = effectSpec(n, wTags)
-  if not isNil(raisesSpec) or not isNil(tagsSpec):
+    usesSpec = effectSpec(n, wUses)
+  if not isNil(raisesSpec) or not isNil(tagsSpec) or not isNil(usesSpec):
     internalAssert effects.len == 0
     newSeq(effects.sons, effectListLen)
     if not isNil(raisesSpec):
       effects.sons[exceptionEffects] = raisesSpec
     if not isNil(tagsSpec):
       effects.sons[tagEffects] = tagsSpec
+    if not isNil(usesSpec):
+      effects.sons[usesEffects] = usesSpec
 
 proc initEffects(effects: PNode; s: PSym; t: var TEffects) =
   newSeq(effects.sons, effectListLen)
   effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info)
   effects.sons[tagEffects] = newNodeI(nkArgList, s.info)
+  effects.sons[usesEffects] = newNodeI(nkArgList, s.info)
   
   t.exc = effects.sons[exceptionEffects]
   t.tags = effects.sons[tagEffects]
+  t.uses = effects.sons[usesEffects]
   t.owner = s
   t.init = @[]
   t.guards = @[]
@@ -602,7 +675,6 @@ proc trackProc*(s: PSym, body: PNode) =
   var t: TEffects
   initEffects(effects, s, t)
   track(t, body)
-  
   if not isEmptyType(s.typ.sons[0]) and tfNeedsInit in s.typ.sons[0].flags and
       s.kind in {skProc, skConverter, skMethod}:
     var res = s.ast.sons[resultPos].sym # get result symbol
@@ -612,17 +684,27 @@ proc trackProc*(s: PSym, body: PNode) =
   let raisesSpec = effectSpec(p, wRaises)
   if not isNil(raisesSpec):
     checkRaisesSpec(raisesSpec, t.exc, "can raise an unlisted exception: ",
-                    hints=on)
+                    hints=on, subtypeRelation)
     # after the check, use the formal spec:
     effects.sons[exceptionEffects] = raisesSpec
 
   let tagsSpec = effectSpec(p, wTags)
   if not isNil(tagsSpec):
     checkRaisesSpec(tagsSpec, t.tags, "can have an unlisted effect: ",
-                    hints=off)
+                    hints=off, subtypeRelation)
     # after the check, use the formal spec:
     effects.sons[tagEffects] = tagsSpec
-    
+
+  when trackGlobals:
+    let usesSpec = effectSpec(p, wUses)
+    if not isNil(usesSpec):
+      checkRaisesSpec(usesSpec, t.uses,
+        "uses an unlisted global variable: ", hints=on, symbolPredicate)
+      effects.sons[usesEffects] = usesSpec
+  if sfThread in s.flags and t.gcUnsafe:
+    localError(s.info, "'$1' is not GC-safe" % s.name.s)
+  if not t.gcUnsafe: s.typ.flags.incl tfGcSafe
+  
 proc trackTopLevelStmt*(module: PSym; n: PNode) =
   if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef,
                 nkTypeSection, nkConverterDef, nkMethodDef, nkIteratorDef}: