summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/ccgexprs.nim2
-rw-r--r--compiler/ccgstmts.nim2
-rw-r--r--compiler/jsgen.nim19
-rw-r--r--compiler/pragmas.nim55
-rw-r--r--compiler/semcall.nim104
-rw-r--r--compiler/semexprs.nim9
-rw-r--r--compiler/semgnrc.nim13
-rw-r--r--compiler/seminst.nim7
-rw-r--r--compiler/semmagic.nim8
-rw-r--r--compiler/sempass2.nim34
-rw-r--r--compiler/semstmts.nim10
-rw-r--r--compiler/sigmatch.nim5
-rw-r--r--compiler/transf.nim12
-rw-r--r--compiler/types.nim13
14 files changed, 161 insertions, 132 deletions
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index f9fa1a0f6..6c96f209e 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -670,6 +670,8 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) =
     #if e[0].kind != nkBracketExpr:
     #  message(e.info, warnUser, "CAME HERE " & renderTree(e))
     expr(p, e.sons[0], d)
+    if e.sons[0].typ.skipTypes(abstractInst).kind == tyRef:
+      d.s = OnHeap
   else:
     var a: TLoc
     let typ = skipTypes(e.sons[0].typ, abstractInst)
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index a5ce147c3..9056a61cb 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -21,7 +21,7 @@ proc registerGcRoot(p: BProc, v: PSym) =
     # we register a specialized marked proc here; this has the advantage
     # that it works out of the box for thread local storage then :-)
     let prc = genTraverseProcForGlobal(p.module, v)
-    appcg(p.module, p.module.initProc.procSec(cpsStmts),
+    appcg(p.module, p.module.initProc.procSec(cpsInit),
       "#nimRegisterGlobalMarker($1);$n", [prc])
 
 proc isAssignedImmediately(n: PNode): bool {.inline.} =
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index a0b54812f..80bcd2b0e 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -8,7 +8,7 @@
 #
 
 # This is the JavaScript code generator.
-# Soon also a PHP code generator. ;-)
+# Also a PHP code generator. ;-)
 
 discard """
 The JS code generator contains only 2 tricks:
@@ -1280,14 +1280,15 @@ proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) =
       assert(typ.kind == tyProc)
       genPatternCall(p, n, pat, typ, r)
       return
-  gen(p, n.sons[1], r)
-  if r.typ == etyBaseIndex:
-    if r.address == nil:
-      globalError(n.info, "cannot invoke with infix syntax")
-    r.res = "$1[$2]" % [r.address, r.res]
-    r.address = nil
-    r.typ = etyNone
-  add(r.res, "." | "->")
+  if n.len != 1:
+    gen(p, n.sons[1], r)
+    if r.typ == etyBaseIndex:
+      if r.address == nil:
+        globalError(n.info, "cannot invoke with infix syntax")
+      r.res = "$1[$2]" % [r.address, r.res]
+      r.address = nil
+      r.typ = etyNone
+    add(r.res, "." | "->")
   var op: TCompRes
   if p.target == targetPHP:
     op.kind = resCallee
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index 1f93f5317..db5f8e727 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -90,43 +90,38 @@ proc pragmaAsm*(c: PContext, n: PNode): char =
       else:
         invalidPragma(it)
 
-proc setExternName(s: PSym, extname: string) =
-  s.loc.r = rope(extname % s.name.s)
+proc setExternName(s: PSym, extname: string, info: TLineInfo) =
+  # special cases to improve performance:
+  if extname == "$1":
+    s.loc.r = rope(s.name.s)
+  elif '$' notin extname:
+    s.loc.r = rope(extname)
+  else:
+    try:
+      s.loc.r = rope(extname % s.name.s)
+    except ValueError:
+      localError(info, "invalid extern name: '" & extname & "'. (Forgot to escape '$'?)")
   if gCmd == cmdPretty and '$' notin extname:
     # note that '{.importc.}' is transformed into '{.importc: "$1".}'
     s.loc.flags.incl(lfFullExternalName)
 
-proc makeExternImport(s: PSym, extname: string) =
-  setExternName(s, extname)
+proc makeExternImport(s: PSym, extname: string, info: TLineInfo) =
+  setExternName(s, extname, info)
   incl(s.flags, sfImportc)
   excl(s.flags, sfForward)
 
-proc validateExternCName(s: PSym, info: TLineInfo) =
-  ## Validates that the symbol name in s.loc.r is a valid C identifier.
-  ##
-  ## Valid identifiers are those alphanumeric including the underscore not
-  ## starting with a number. If the check fails, a generic error will be
-  ## displayed to the user.
-  let target = $s.loc.r
-  if target.len < 1 or target[0] notin IdentStartChars or
-      not target.allCharsInSet(IdentChars):
-    localError(info, errGenerated, "invalid exported symbol")
-
 proc makeExternExport(s: PSym, extname: string, info: TLineInfo) =
-  setExternName(s, extname)
-  # XXX to fix make it work with nimrtl.
-  #if gCmd in {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC}:
-  #  validateExternCName(s, info)
+  setExternName(s, extname, info)
   incl(s.flags, sfExportc)
 
-proc processImportCompilerProc(s: PSym, extname: string) =
-  setExternName(s, extname)
+proc processImportCompilerProc(s: PSym, extname: string, info: TLineInfo) =
+  setExternName(s, extname, info)
   incl(s.flags, sfImportc)
   excl(s.flags, sfForward)
   incl(s.loc.flags, lfImportCompilerProc)
 
-proc processImportCpp(s: PSym, extname: string) =
-  setExternName(s, extname)
+proc processImportCpp(s: PSym, extname: string, info: TLineInfo) =
+  setExternName(s, extname, info)
   incl(s.flags, sfImportc)
   incl(s.flags, sfInfixCall)
   excl(s.flags, sfForward)
@@ -134,8 +129,8 @@ proc processImportCpp(s: PSym, extname: string) =
   incl(m.flags, sfCompileToCpp)
   extccomp.gMixedMode = true
 
-proc processImportObjC(s: PSym, extname: string) =
-  setExternName(s, extname)
+proc processImportObjC(s: PSym, extname: string, info: TLineInfo) =
+  setExternName(s, extname, info)
   incl(s.flags, sfImportc)
   incl(s.flags, sfNamedParamCall)
   excl(s.flags, sfForward)
@@ -623,10 +618,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
       of wExportc:
         makeExternExport(sym, getOptionalStr(c, it, "$1"), it.info)
         incl(sym.flags, sfUsed) # avoid wrong hints
-      of wImportc: makeExternImport(sym, getOptionalStr(c, it, "$1"))
+      of wImportc: makeExternImport(sym, getOptionalStr(c, it, "$1"), it.info)
       of wImportCompilerProc:
-        processImportCompilerProc(sym, getOptionalStr(c, it, "$1"))
-      of wExtern: setExternName(sym, expectStrLit(c, it))
+        processImportCompilerProc(sym, getOptionalStr(c, it, "$1"), it.info)
+      of wExtern: setExternName(sym, expectStrLit(c, it), it.info)
       of wImmediate:
         if sym.kind in {skTemplate, skMacro}:
           incl(sym.flags, sfImmediate)
@@ -637,9 +632,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
         if sym.kind == skTemplate: incl(sym.flags, sfDirty)
         else: invalidPragma(it)
       of wImportCpp:
-        processImportCpp(sym, getOptionalStr(c, it, "$1"))
+        processImportCpp(sym, getOptionalStr(c, it, "$1"), it.info)
       of wImportObjC:
-        processImportObjC(sym, getOptionalStr(c, it, "$1"))
+        processImportObjC(sym, getOptionalStr(c, it, "$1"), it.info)
       of wAlign:
         if sym.typ == nil: invalidPragma(it)
         var align = expectIntLit(c, it)
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index 97209167d..b83872fe9 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -40,67 +40,49 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
                        filter: TSymKinds,
                        best, alt: var TCandidate,
                        errors: var CandidateErrors) =
-  var o: TOverloadIter
-  # thanks to the lazy semchecking for operands, we need to iterate over the
-  # symbol table *before* any call to 'initCandidate' which might invoke
-  # semExpr which might modify the symbol table in cases like
-  # 'init(a, 1, (var b = new(Type2); b))'.
-  var symx = initOverloadIter(o, c, headSymbol)
-  let symScope = o.lastOverloadScope
-
-  var syms: seq[tuple[a: PSym, b: int]] = @[]
-  while symx != nil:
-    if symx.kind in filter:
-      syms.add((symx, o.lastOverloadScope))
-    symx = nextOverloadIter(o, c, headSymbol)
-  if syms.len == 0:
-    when false:
-      if skIterator notin filter:
-        # also try iterators, but these are 2nd class:
-        symx = initOverloadIter(o, c, headSymbol)
-        while symx != nil:
-          if symx.kind == skIterator:
-            syms.add((symx, 100))
-          symx = nextOverloadIter(o, c, headSymbol)
-        if syms.len == 0: return
-    else:
-      return
-
-  var z: TCandidate
-  initCandidate(c, best, syms[0][0], initialBinding, symScope)
-  initCandidate(c, alt, syms[0][0], initialBinding, symScope)
-  best.state = csNoMatch
-
-  for i in 0 .. <syms.len:
-    let sym = syms[i][0]
-    determineType(c, sym)
-    initCandidate(c, z, sym, initialBinding, syms[i][1])
-
-    #if sym.name.s == "*" and (n.info ?? "temp5.nim") and n.info.line == 140:
-    #  gDebug = true
-    matches(c, n, orig, z)
-    if errors != nil:
-      errors.safeAdd((sym, int z.mutabilityProblem))
-      if z.errors != nil:
-        for err in z.errors:
-          errors.add(err)
-    if z.state == csMatch:
-      # little hack so that iterators are preferred over everything else:
-      if sym.kind == skIterator: inc(z.exactMatches, 200)
-      case best.state
-      of csEmpty, csNoMatch: best = z
-      of csMatch:
-        var cmp = cmpCandidates(best, z)
-        if cmp < 0: best = z   # x is better than the best so far
-        elif cmp == 0: alt = z # x is as good as the best so far
-        else: discard
-      #if sym.name.s == "cmp" and (n.info ?? "rstgen.nim") and n.info.line == 516:
-      #  echo "Matches ", n.info, " ", typeToString(sym.typ)
-      #  debug sym
-      #  writeMatches(z)
-      #  for i in 1 .. <len(z.call):
-      #    z.call[i].typ.debug
-      #  quit 1
+  while true:
+    block pickAttempt:
+      var o: TOverloadIter
+      var sym = initOverloadIter(o, c, headSymbol)
+      # Thanks to the lazy semchecking for operands, we need to check whether
+      # 'initCandidate' modifies the symbol table (via semExpr).
+      # This can occur in cases like 'init(a, 1, (var b = new(Type2); b))'
+      let counterInitial = c.currentScope.symbols.counter
+      # Initialise 'best' and 'alt' with the first available symbol
+      while sym != nil:
+        if sym.kind in filter:
+          initCandidate(c, best, sym, initialBinding, o.lastOverloadScope)
+          initCandidate(c, alt, sym, initialBinding, o.lastOverloadScope)
+          best.state = csNoMatch
+          break
+        else:
+          sym = nextOverloadIter(o, c, headSymbol)
+      var z: TCandidate
+      while sym != nil:
+        if sym.kind notin filter:
+          sym = nextOverloadIter(o, c, headSymbol)
+          continue
+        determineType(c, sym)
+        initCandidate(c, z, sym, initialBinding, o.lastOverloadScope)
+        if c.currentScope.symbols.counter != counterInitial: break pickAttempt
+        matches(c, n, orig, z)
+        if errors != nil:
+          errors.safeAdd((sym, int z.mutabilityProblem))
+          if z.errors != nil:
+            for err in z.errors:
+              errors.add(err)
+        if z.state == csMatch:
+          # little hack so that iterators are preferred over everything else:
+          if sym.kind == skIterator: inc(z.exactMatches, 200)
+          case best.state
+          of csEmpty, csNoMatch: best = z
+          of csMatch:
+            var cmp = cmpCandidates(best, z)
+            if cmp < 0: best = z   # x is better than the best so far
+            elif cmp == 0: alt = z # x is as good as the best so far
+            else: discard
+        sym = nextOverloadIter(o, c, headSymbol)
+    break # pick attempt was successful
 
 proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
   # Gives a detailed error message; this is separated from semOverloadedCall,
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index fc31829ba..e3552f94e 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -701,9 +701,6 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
         # error correction, prevents endless for loop elimination in transf.
         # See bug #2051:
         result.sons[0] = newSymNode(errorSym(c, n))
-      if sfNoSideEffect notin callee.flags:
-        if {sfImportc, sfSideEffect} * callee.flags != {}:
-          incl(c.p.owner.flags, sfSideEffect)
 
 proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode
 
@@ -804,9 +801,6 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
     else:
       result = m.call
       instGenericConvertersSons(c, result, m)
-    # we assume that a procedure that calls something indirectly
-    # has side-effects:
-    if tfNoSideEffect notin t.flags: incl(c.p.owner.flags, sfSideEffect)
   elif t != nil and t.kind == tyTypeDesc:
     if n.len == 1: return semObjConstr(c, n, flags)
     return semConv(c, n)
@@ -1040,9 +1034,6 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
 
     markUsed(n.info, s)
     styleCheckUse(n.info, s)
-    # if a proc accesses a global variable, it is not side effect free:
-    if sfGlobal in s.flags:
-      incl(c.p.owner.flags, sfSideEffect)
     result = newSymNode(s, n.info)
     # We cannot check for access to outer vars for example because it's still
     # not sure the symbol really ends up being used:
diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim
index 9ea3efd0c..b8451865e 100644
--- a/compiler/semgnrc.nim
+++ b/compiler/semgnrc.nim
@@ -202,24 +202,25 @@ proc semGenericStmt(c: PContext, n: PNode,
     if s != nil:
       incl(s.flags, sfUsed)
       mixinContext = s.magic in {mDefined, mDefinedInScope, mCompiles}
-      let scOption = if s.name.id in ctx.toMixin: scForceOpen else: scOpen
+      let sc = symChoice(c, fn, s,
+            if s.name.id in ctx.toMixin: scForceOpen else: scOpen)
       case s.kind
       of skMacro:
-        if macroToExpand(s):
+        if macroToExpand(s) and sc.safeLen <= 1:
           styleCheckUse(fn.info, s)
           result = semMacroExpr(c, n, n, s, {efNoSemCheck})
           result = semGenericStmt(c, result, flags, ctx)
         else:
-          n.sons[0] = symChoice(c, fn, s, scOption)
+          n.sons[0] = sc
           result = n
         mixinContext = true
       of skTemplate:
-        if macroToExpand(s):
+        if macroToExpand(s) and sc.safeLen <= 1:
           styleCheckUse(fn.info, s)
           result = semTemplateExpr(c, n, s, {efNoSemCheck})
           result = semGenericStmt(c, result, flags, ctx)
         else:
-          n.sons[0] = symChoice(c, fn, s, scOption)
+          n.sons[0] = sc
           result = n
         # BUGFIX: we must not return here, we need to do first phase of
         # symbol lookup. Also since templates and macros can do scope injections
@@ -230,7 +231,7 @@ proc semGenericStmt(c: PContext, n: PNode,
         # Leave it as an identifier.
         discard
       of skProc, skMethod, skIterator, skConverter, skModule:
-        result.sons[0] = symChoice(c, fn, s, scOption)
+        result.sons[0] = sc
         # do not check of 's.magic==mRoof' here because it might be some
         # other '^' but after overload resolution the proper one:
         if ctx.bracketExpr != nil and n.len == 2 and s.name.s == "^":
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index 460db4f7c..a57b8e520 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -172,9 +172,10 @@ proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
       popInfoContext()
 
 proc sideEffectsCheck(c: PContext, s: PSym) =
-  if {sfNoSideEffect, sfSideEffect} * s.flags ==
-      {sfNoSideEffect, sfSideEffect}:
-    localError(s.info, errXhasSideEffects, s.name.s)
+  when false:
+    if {sfNoSideEffect, sfSideEffect} * s.flags ==
+        {sfNoSideEffect, sfSideEffect}:
+      localError(s.info, errXhasSideEffects, s.name.s)
 
 proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
                           allowMetaTypes = false): PType =
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index d9fec6275..cbe9bc176 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -114,8 +114,12 @@ proc semTypeTraits(c: PContext, n: PNode): PNode =
 
 proc semOrd(c: PContext, n: PNode): PNode =
   result = n
-  result.typ = makeRangeType(c, firstOrd(n.sons[1].typ),
-                                lastOrd(n.sons[1].typ), n.info)
+  let parType = n.sons[1].typ
+  if isOrdinalType(parType) or parType.kind == tySet:
+    result.typ = makeRangeType(c, firstOrd(parType), lastOrd(parType), n.info)
+  else:
+    localError(n.info, errOrdinalTypeExpected)
+    result.typ = errorType(c)
 
 proc semBindSym(c: PContext, n: PNode): PNode =
   result = copyNode(n)
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index b12ab5e96..6d2e82ef0 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -59,7 +59,7 @@ type
     init: seq[int] # list of initialized variables
     guards: TModel # nested guards
     locked: seq[PNode] # locked locations
-    gcUnsafe, isRecursive, isToplevel: bool
+    gcUnsafe, isRecursive, isToplevel, hasSideEffect: bool
     maxLockLevel, currLockLevel: TLockLevel
   PEffects = var TEffects
 
@@ -192,6 +192,14 @@ proc markGcUnsafe(a: PEffects; reason: PNode) =
       a.owner.gcUnsafetyReason = newSym(skUnknown, getIdent("<unknown>"),
                                         a.owner, reason.info)
 
+when true:
+  template markSideEffect(a: PEffects; reason: typed) =
+    a.hasSideEffect = true
+else:
+  template markSideEffect(a: PEffects; reason: typed) =
+    a.hasSideEffect = true
+    markGcUnsafe(a, reason)
+
 proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet) =
   let u = s.gcUnsafetyReason
   if u != nil and not cycleCheck.containsOrIncl(u.id):
@@ -226,12 +234,16 @@ proc useVar(a: PEffects, n: PNode) =
         message(n.info, warnUninit, s.name.s)
       # prevent superfluous warnings about the same variable:
       a.init.add s.id
-  if {sfGlobal, sfThread} * s.flags != {} and s.kind in {skVar, skLet}:
+  if {sfGlobal, sfThread} * s.flags != {} and s.kind in {skVar, skLet} and
+      s.magic != mNimVm:
     if s.guard != nil: guardGlobal(a, n, s.guard)
     if {sfGlobal, sfThread} * s.flags == {sfGlobal} and
         (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem):
       #if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
       markGcUnsafe(a, s)
+    else:
+      markSideEffect(a, s)
+
 
 type
   TIntersection = seq[tuple[id, count: int]] # a simple count table
@@ -495,6 +507,8 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
   if notGcSafe(s.typ) and sfImportc notin s.flags:
     if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
     markGcUnsafe(tracked, s)
+  if tfNoSideEffect notin s.typ.flags:
+    markSideEffect(tracked, s)
   mergeLockLevels(tracked, n, s.getLockLevel)
 
 proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
@@ -550,12 +564,16 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
       if notGcSafe(op) and not isOwnedProcVar(a, tracked.owner):
         if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
         markGcUnsafe(tracked, a)
+      elif tfNoSideEffect notin op.flags and not isOwnedProcVar(a, tracked.owner):
+        markSideEffect(tracked, a)
     else:
       mergeEffects(tracked, effectList.sons[exceptionEffects], n)
       mergeTags(tracked, effectList.sons[tagEffects], n)
       if notGcSafe(op):
         if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
         markGcUnsafe(tracked, a)
+      elif tfNoSideEffect notin op.flags:
+        markSideEffect(tracked, a)
   notNilCheck(tracked, n, paramType)
 
 proc breaksBlock(n: PNode): bool =
@@ -684,6 +702,7 @@ proc track(tracked: PEffects, n: PNode) =
         if a.sym == tracked.owner: tracked.isRecursive = true
         # even for recursive calls we need to check the lock levels (!):
         mergeLockLevels(tracked, n, a.sym.getLockLevel)
+        if sfSideEffect in a.sym.flags: markSideEffect(tracked, a)
       else:
         mergeLockLevels(tracked, n, op.lockLevel)
       var effectList = op.n.sons[0]
@@ -702,6 +721,10 @@ proc track(tracked: PEffects, n: PNode) =
           if not (a.kind == nkSym and a.sym == tracked.owner):
             if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
             markGcUnsafe(tracked, a)
+        if tfNoSideEffect notin op.flags and not importedFromC(a):
+          # and it's not a recursive call:
+          if not (a.kind == nkSym and a.sym == tracked.owner):
+            markSideEffect(tracked, a)
     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:
@@ -912,8 +935,15 @@ proc trackProc*(s: PSym, body: PNode) =
     else:
       listGcUnsafety(s, onlyWarning=true)
       #localError(s.info, warnGcUnsafe2, s.name.s)
+  if sfNoSideEffect in s.flags and t.hasSideEffect:
+    when false:
+      listGcUnsafety(s, onlyWarning=false)
+    else:
+      localError(s.info, errXhasSideEffects, s.name.s)
   if not t.gcUnsafe:
     s.typ.flags.incl tfGcSafe
+  if not t.hasSideEffect:
+    s.typ.flags.incl tfNoSideEffect
   if s.typ.lockLevel == UnspecifiedLockLevel:
     s.typ.lockLevel = t.maxLockLevel
   elif t.maxLockLevel > s.typ.lockLevel:
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 5d1770a32..5f9989ea2 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -304,8 +304,14 @@ proc semTry(c: PContext, n: PNode): PNode =
 proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode =
   result = fitNode(c, typ, n)
   if result.kind in {nkHiddenStdConv, nkHiddenSubConv}:
-    changeType(result.sons[1], typ, check=true)
-    result = result.sons[1]
+    let r1 = result.sons[1]
+    if r1.kind in {nkCharLit..nkUInt64Lit} and typ.skipTypes(abstractRange).kind in {tyFloat..tyFloat128}:
+      result = newFloatNode(nkFloatLit, BiggestFloat r1.intVal)
+      result.info = n.info
+      result.typ = typ
+    else:
+      changeType(r1, typ, check=true)
+      result = r1
   elif not sameType(result.typ, typ):
     changeType(result, typ, check=false)
 
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index ce4f893ea..ec429968c 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -221,13 +221,14 @@ proc cmpCandidates*(a, b: TCandidate): int =
   if result != 0: return
   result = a.convMatches - b.convMatches
   if result != 0: return
-  result = a.calleeScope - b.calleeScope
-  if result != 0: return
   # the other way round because of other semantics:
   result = b.inheritancePenalty - a.inheritancePenalty
   if result != 0: return
   # prefer more specialized generic over more general generic:
   result = complexDisambiguation(a.callee, b.callee)
+  # only as a last resort, consider scoping:
+  if result != 0: return
+  result = a.calleeScope - b.calleeScope
 
 proc writeMatches*(c: TCandidate) =
   writeLine(stdout, "exact matches: " & $c.exactMatches)
diff --git a/compiler/transf.nim b/compiler/transf.nim
index d64276cfb..fc400c524 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -474,12 +474,14 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
 
 type
   TPutArgInto = enum
-    paDirectMapping, paFastAsgn, paVarAsgn
+    paDirectMapping, paFastAsgn, paVarAsgn, paComplexOpenarray
 
 proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
   # This analyses how to treat the mapping "formal <-> arg" in an
   # inline context.
   if skipTypes(formal, abstractInst).kind in {tyOpenArray, tyVarargs}:
+    if arg.kind == nkStmtListExpr:
+      return paComplexOpenarray
     return paDirectMapping    # XXX really correct?
                               # what if ``arg`` has side-effects?
   case arg.kind
@@ -569,6 +571,14 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
       assert(skipTypes(formal.typ, abstractInst).kind == tyVar)
       idNodeTablePut(newC.mapping, formal, arg)
       # XXX BUG still not correct if the arg has a side effect!
+    of paComplexOpenarray:
+      let typ = newType(tySequence, formal.owner)
+      addSonSkipIntLit(typ, formal.typ.sons[0])
+      var temp = newTemp(c, typ, formal.info)
+      addVar(v, temp)
+      add(stmtList, newAsgnStmt(c, temp, arg.PTransNode))
+      idNodeTablePut(newC.mapping, formal, temp)
+
   var body = iter.getBody.copyTree
   pushInfoContext(n.info)
   # XXX optimize this somehow. But the check "c.inlining" is not correct:
diff --git a/compiler/types.nim b/compiler/types.nim
index c56382b89..312e36ae5 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -48,6 +48,8 @@ proc isOrdinalType*(t: PType): bool
 proc enumHasHoles*(t: PType): bool
 
 const
+  # TODO: Remove tyTypeDesc from each abstractX and (where necessary)
+  # replace with typedescX
   abstractPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyDistinct, tyOrdinal,
                    tyConst, tyMutable, tyTypeDesc}
   abstractVar* = {tyVar, tyGenericInst, tyDistinct, tyOrdinal,
@@ -61,6 +63,7 @@ const
 
   skipPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyConst, tyMutable,
                tyTypeDesc}
+  # typedescX is used if we're sure tyTypeDesc should be included (or skipped)
   typedescPtrs* = abstractPtrs + {tyTypeDesc}
   typedescInst* = abstractInst + {tyTypeDesc}
 
@@ -148,10 +151,12 @@ proc skipGeneric(t: PType): PType =
 
 proc isOrdinalType(t: PType): bool =
   assert(t != nil)
-  # caution: uint, uint64 are no ordinal types!
-  result = t.kind in {tyChar,tyInt..tyInt64,tyUInt8..tyUInt32,tyBool,tyEnum} or
-      (t.kind in {tyRange, tyOrdinal, tyConst, tyMutable, tyGenericInst}) and
-       isOrdinalType(t.sons[0])
+  const
+    # caution: uint, uint64 are no ordinal types!
+    baseKinds = {tyChar,tyInt..tyInt64,tyUInt8..tyUInt32,tyBool,tyEnum}
+    parentKinds = {tyRange, tyOrdinal, tyConst, tyMutable, tyGenericInst,
+                   tyDistinct}
+  t.kind in baseKinds or (t.kind in parentKinds and isOrdinalType(t.sons[0]))
 
 proc enumHasHoles(t: PType): bool =
   var b = t