summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ccgcalls.nim21
-rw-r--r--compiler/ccgexprs.nim102
-rw-r--r--compiler/ccgstmts.nim2
-rw-r--r--compiler/ccgtypes.nim111
-rw-r--r--compiler/cgen.nim56
-rw-r--r--compiler/options.nim3
-rw-r--r--compiler/renderer.nim9
-rw-r--r--compiler/sem.nim10
-rw-r--r--compiler/semexprs.nim21
-rw-r--r--compiler/sempass2.nim7
-rw-r--r--compiler/semstmts.nim4
-rw-r--r--compiler/transf.nim4
-rw-r--r--compiler/trees.nim2
-rw-r--r--compiler/typeallowed.nim228
-rw-r--r--compiler/types.nim170
-rw-r--r--compiler/varpartitions.nim36
-rw-r--r--doc/manual_experimental.rst52
-rw-r--r--tests/ccgbugs/tviews1.nim27
18 files changed, 554 insertions, 311 deletions
diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim
index 5f99f357d..6d254ca6f 100644
--- a/compiler/ccgcalls.nim
+++ b/compiler/ccgcalls.nim
@@ -141,6 +141,13 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
 
 proc genBoundsCheck(p: BProc; arr, a, b: TLoc)
 
+proc reifiedOpenArray(n: PNode): bool {.inline.} =
+  let x = trees.getRoot(n)
+  if x != nil and x.kind == skParam:
+    result = false
+  else:
+    result = true
+
 proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope =
   var a: TLoc
 
@@ -174,7 +181,12 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope =
       else:
         result = "($5*)($1)+(($2)-($4)), ($3)-($2)+1" %
           [rdLoc(a), rdLoc(b), rdLoc(c), intLiteral(first), dest]
-    of tyOpenArray, tyVarargs, tyUncheckedArray, tyCString:
+    of tyOpenArray, tyVarargs:
+      if reifiedOpenArray(q[1]):
+        result = "($4*)($1.d)+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), dest]
+      else:
+        result = "($4*)($1)+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), dest]
+    of tyUncheckedArray, tyCString:
       result = "($4*)($1)+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), dest]
     of tyString, tySequence:
       let atyp = skipTypes(a.t, abstractInst)
@@ -188,10 +200,13 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope =
     else:
       internalError(p.config, "openArrayLoc: " & typeToString(a.t))
   else:
-    initLocExpr(p, n, a)
+    initLocExpr(p, if n.kind == nkHiddenStdConv: n[1] else: n, a)
     case skipTypes(a.t, abstractVar).kind
     of tyOpenArray, tyVarargs:
-      result = "$1, $1Len_0" % [rdLoc(a)]
+      if reifiedOpenArray(n):
+        result = "$1.d, $1.l" % [rdLoc(a)]
+      else:
+        result = "$1, $1Len_0" % [rdLoc(a)]
     of tyString, tySequence:
       let ntyp = skipTypes(n.typ, abstractInst)
       if formalType.skipTypes(abstractInst).kind in {tyVar} and ntyp.kind == tyString and
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index dd4774b2a..8f45452bf 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -271,6 +271,34 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
     linefmt(p, cpsStmts, "#genericAssign((void*)$1, (void*)$2, $3);$n",
             [addrLoc(p.config, dest), addrLoc(p.config, src), genTypeInfo(p.module, dest.t, dest.lode.info)])
 
+proc genOpenArrayConv(p: BProc; d: TLoc; a: TLoc) =
+  assert d.k != locNone
+  #  getTemp(p, d.t, d)
+
+  case a.t.skipTypes(abstractVar).kind
+  of tyOpenArray, tyVarargs:
+    if reifiedOpenArray(a.lode):
+      linefmt(p, cpsStmts, "$1.d = $2.d; $1.l = $2.l;$n",
+        [rdLoc(d), a.rdLoc])
+    else:
+      linefmt(p, cpsStmts, "$1.d = $2; $1.l = $2Len_0;$n",
+        [rdLoc(d), a.rdLoc])
+  of tySequence:
+    linefmt(p, cpsStmts, "$1.d = $2$3; $1.l = $4;$n",
+      [rdLoc(d), a.rdLoc, dataField(p), lenExpr(p, a)])
+  of tyArray:
+    linefmt(p, cpsStmts, "$1.d = $2; $1.l = $3;$n",
+      [rdLoc(d), rdLoc(a), rope(lengthOrd(p.config, a.t))])
+  of tyString:
+    let etyp = skipTypes(a.t, abstractInst)
+    if etyp.kind in {tyVar} and optSeqDestructors in p.config.globalOptions:
+      linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
+
+    linefmt(p, cpsStmts, "$1.d = $2$3; $1.l = $4;$n",
+      [rdLoc(d), a.rdLoc, dataField(p), lenExpr(p, a)])
+  else:
+    internalError(p.config, a.lode.info, "cannot handle " & $a.t.kind)
+
 proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
   # This function replaces all other methods for generating
   # the assignment operation in C.
@@ -349,7 +377,9 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
   of tyOpenArray, tyVarargs:
     # open arrays are always on the stack - really? What if a sequence is
     # passed to an open array?
-    if containsGarbageCollectedRef(dest.t):
+    if reifiedOpenArray(dest.lode):
+      genOpenArrayConv(p, dest, src)
+    elif containsGarbageCollectedRef(dest.t):
       linefmt(p, cpsStmts,     # XXX: is this correct for arrays?
            "#genericAssignOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n",
            [addrLoc(p.config, dest), addrLoc(p.config, src),
@@ -361,7 +391,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
            "$1 = $2;$n",
            [rdLoc(dest), rdLoc(src)])
   of tySet:
-    if mapType(p.config, ty) == ctArray:
+    if mapSetType(p.config, ty) == ctArray:
       linefmt(p, cpsStmts, "#nimCopyMem((void*)$1, (NIM_CONST void*)$2, $3);$n",
               [rdLoc(dest), rdLoc(src), getSize(p.config, dest.t)])
     else:
@@ -406,7 +436,7 @@ proc genDeepCopy(p: BProc; dest, src: TLoc) =
          [addrLoc(p.config, dest), addrLocOrTemp(src),
          genTypeInfo(p.module, dest.t, dest.lode.info)])
   of tySet:
-    if mapType(p.config, ty) == ctArray:
+    if mapSetType(p.config, ty) == ctArray:
       linefmt(p, cpsStmts, "#nimCopyMem((void*)$1, (NIM_CONST void*)$2, $3);$n",
               [rdLoc(dest), rdLoc(src), getSize(p.config, dest.t)])
     else:
@@ -679,7 +709,7 @@ proc isCppRef(p: BProc; typ: PType): bool {.inline.} =
       tfVarIsPtr notin skipTypes(typ, abstractInstOwned).flags
 
 proc genDeref(p: BProc, e: PNode, d: var TLoc) =
-  let mt = mapType(p.config, e[0].typ)
+  let mt = mapType(p.config, e[0].typ, mapTypeChooser(e[0]))
   if mt in {ctArray, ctPtrToArray} and lfEnforceDeref notin d.flags:
     # XXX the amount of hacks for C's arrays is incredible, maybe we should
     # simply wrap them in a struct? --> Losing auto vectorization then?
@@ -747,7 +777,7 @@ proc genAddr(p: BProc, e: PNode, d: var TLoc) =
     initLocExpr(p, e[0], a)
     putIntoDest(p, d, e, "&" & a.r, a.storage)
     #Message(e.info, warnUser, "HERE NEW &")
-  elif mapType(p.config, e[0].typ) == ctArray or isCppRef(p, e.typ):
+  elif mapType(p.config, e[0].typ, mapTypeChooser(e[0])) == ctArray or isCppRef(p, e.typ):
     expr(p, e[0], d)
   else:
     var a: TLoc
@@ -905,10 +935,16 @@ proc genBoundsCheck(p: BProc; arr, a, b: TLoc) =
   let ty = skipTypes(arr.t, abstractVarRange)
   case ty.kind
   of tyOpenArray, tyVarargs:
-    linefmt(p, cpsStmts,
-      "if ($2-$1 != -1 && " &
-      "((NU)($1) >= (NU)($3Len_0) || (NU)($2) >= (NU)($3Len_0))){ #raiseIndexError(); $4}$n",
-      [rdLoc(a), rdLoc(b), rdLoc(arr), raiseInstr(p)])
+    if reifiedOpenArray(arr.lode):
+      linefmt(p, cpsStmts,
+        "if ($2-$1 != -1 && " &
+        "((NU)($1) >= (NU)($3.l) || (NU)($2) >= (NU)($3.l))){ #raiseIndexError(); $4}$n",
+        [rdLoc(a), rdLoc(b), rdLoc(arr), raiseInstr(p)])
+    else:
+      linefmt(p, cpsStmts,
+        "if ($2-$1 != -1 && " &
+        "((NU)($1) >= (NU)($3Len_0) || (NU)($2) >= (NU)($3Len_0))){ #raiseIndexError(); $4}$n",
+        [rdLoc(a), rdLoc(b), rdLoc(arr), raiseInstr(p)])
   of tyArray:
     let first = intLiteral(firstOrd(p.config, ty))
     linefmt(p, cpsStmts,
@@ -925,13 +961,22 @@ proc genBoundsCheck(p: BProc; arr, a, b: TLoc) =
 proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
   var a, b: TLoc
   initLocExpr(p, x, a)
-  initLocExpr(p, y, b) # emit range check:
-  if optBoundsCheck in p.options:
-    linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)){ #raiseIndexError2($1,$2Len_0-1); $3}$n",
-            [rdLoc(b), rdLoc(a), raiseInstr(p)]) # BUGFIX: ``>=`` and not ``>``!
-  inheritLocation(d, a)
-  putIntoDest(p, d, n,
-              ropecg(p.module, "$1[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage)
+  initLocExpr(p, y, b)
+  if not reifiedOpenArray(x):
+    # emit range check:
+    if optBoundsCheck in p.options:
+      linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)){ #raiseIndexError2($1,$2Len_0-1); $3}$n",
+              [rdLoc(b), rdLoc(a), raiseInstr(p)]) # BUGFIX: ``>=`` and not ``>``!
+    inheritLocation(d, a)
+    putIntoDest(p, d, n,
+                ropecg(p.module, "$1[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage)
+  else:
+    if optBoundsCheck in p.options:
+      linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2.l)){ #raiseIndexError2($1,$2.l-1); $3}$n",
+              [rdLoc(b), rdLoc(a), raiseInstr(p)]) # BUGFIX: ``>=`` and not ``>``!
+    inheritLocation(d, a)
+    putIntoDest(p, d, n,
+                ropecg(p.module, "$1.d[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage)
 
 proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
   var a, b: TLoc
@@ -1675,8 +1720,12 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
       else:
         putIntoDest(p, d, e, ropecg(p.module, "($2)-($1)+1", [rdLoc(b), rdLoc(c)]))
     else:
-      if op == mHigh: unaryExpr(p, e, d, "($1Len_0-1)")
-      else: unaryExpr(p, e, d, "$1Len_0")
+      if not reifiedOpenArray(a):
+        if op == mHigh: unaryExpr(p, e, d, "($1Len_0-1)")
+        else: unaryExpr(p, e, d, "$1Len_0")
+      else:
+        if op == mHigh: unaryExpr(p, e, d, "($1.l-1)")
+        else: unaryExpr(p, e, d, "$1.l")
   of tyCString:
     if op == mHigh: unaryExpr(p, e, d, "($1 ? (#nimCStrLen($1)-1) : -1)")
     else: unaryExpr(p, e, d, "($1 ? #nimCStrLen($1) : 0)")
@@ -2253,13 +2302,12 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     putIntoDest(p, d, e, "((NI)NIM_ALIGNOF($1))" % [getTypeDesc(p.module, t)])
   of mOffsetOf:
     var dotExpr: PNode
-    block findDotExpr:
-      if e[1].kind == nkDotExpr:
-        dotExpr = e[1]
-      elif e[1].kind == nkCheckedFieldExpr:
-        dotExpr = e[1][0]
-      else:
-        internalError(p.config, e.info, "unknown ast")
+    if e[1].kind == nkDotExpr:
+      dotExpr = e[1]
+    elif e[1].kind == nkCheckedFieldExpr:
+      dotExpr = e[1][0]
+    else:
+      internalError(p.config, e.info, "unknown ast")
     let t = dotExpr[0].typ.skipTypes({tyTypeDesc})
     let tname = getTypeDesc(p.module, t)
     let member =
@@ -2813,8 +2861,10 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope =
       result.add getDefaultValue(p, t.sons[1], info)
     result.add "}"
     #result = rope"{}"
+  of tyOpenArray, tyVarargs:
+    result = rope"{NIM_NIL, 0}"
   of tySet:
-    if mapType(p.config, t) == ctArray: result = rope"{}"
+    if mapSetType(p.config, t) == ctArray: result = rope"{}"
     else: result = rope"0"
   else:
     globalError(p.config, info, "cannot create null element for: " & $t.kind)
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index e40e94d01..48c024b1f 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -1556,7 +1556,7 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
     let le = e[0]
     let ri = e[1]
     var a: TLoc
-    discard getTypeDesc(p.module, le.typ.skipTypes(skipPtrs))
+    discard getTypeDesc(p.module, le.typ.skipTypes(skipPtrs), skVar)
     initLoc(a, locNone, le, OnUnknown)
     a.flags.incl(lfEnforceDeref)
     a.flags.incl(lfPrepareForMutation)
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index fad6093ed..5b5720934 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -149,7 +149,7 @@ proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind =
   of 8: result = ctInt64
   else: result = ctArray
 
-proc mapType(conf: ConfigRef; typ: PType): TCTypeKind =
+proc mapType(conf: ConfigRef; typ: PType; kind: TSymKind): TCTypeKind =
   ## Maps a Nim type to a C type
   case typ.kind
   of tyNone, tyTyped: result = ctVoid
@@ -157,14 +157,17 @@ proc mapType(conf: ConfigRef; typ: PType): TCTypeKind =
   of tyChar: result = ctChar
   of tyNil: result = ctPtr
   of tySet: result = mapSetType(conf, typ)
-  of tyOpenArray, tyArray, tyVarargs, tyUncheckedArray: result = ctArray
+  of tyOpenArray, tyVarargs:
+    if kind == skParam: result = ctArray
+    else: result = ctStruct
+  of tyArray, tyUncheckedArray: result = ctArray
   of tyObject, tyTuple: result = ctStruct
   of tyUserTypeClasses:
     doAssert typ.isResolvedUserTypeClass
-    return mapType(conf, typ.lastSon)
+    return mapType(conf, typ.lastSon, kind)
   of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal,
      tyTypeDesc, tyAlias, tySink, tyInferred, tyOwned:
-    result = mapType(conf, lastSon(typ))
+    result = mapType(conf, lastSon(typ), kind)
   of tyEnum:
     if firstOrd(conf, typ) < 0:
       result = ctInt32
@@ -175,7 +178,7 @@ proc mapType(conf: ConfigRef; typ: PType): TCTypeKind =
       of 4: result = ctInt32
       of 8: result = ctInt64
       else: result = ctInt32
-  of tyRange: result = mapType(conf, typ[0])
+  of tyRange: result = mapType(conf, typ[0], kind)
   of tyPtr, tyVar, tyLent, tyRef:
     var base = skipTypes(typ.lastSon, typedescInst)
     case base.kind
@@ -192,14 +195,14 @@ proc mapType(conf: ConfigRef; typ: PType): TCTypeKind =
   of tyInt..tyUInt64:
     result = TCTypeKind(ord(typ.kind) - ord(tyInt) + ord(ctInt))
   of tyStatic:
-    if typ.n != nil: result = mapType(conf, lastSon typ)
+    if typ.n != nil: result = mapType(conf, lastSon typ, kind)
     else: doAssert(false, "mapType")
   else: doAssert(false, "mapType")
 
 proc mapReturnType(conf: ConfigRef; typ: PType): TCTypeKind =
   #if skipTypes(typ, typedescInst).kind == tyArray: result = ctPtr
   #else:
-  result = mapType(conf, typ)
+  result = mapType(conf, typ, skResult)
 
 proc isImportedType(t: PType): bool =
   result = t.sym != nil and sfImportc in t.sym.flags
@@ -209,7 +212,7 @@ proc isImportedCppType(t: PType): bool =
   result = (t.sym != nil and sfInfixCall in t.sym.flags) or
            (x.sym != nil and sfInfixCall in x.sym.flags)
 
-proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope
+proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKind): Rope
 
 proc isObjLackingTypeField(typ: PType): bool {.inline.} =
   result = (typ.kind == tyObject) and ((tfFinal in typ.flags) and
@@ -222,7 +225,7 @@ proc isInvalidReturnType(conf: ConfigRef; rettype: PType): bool =
   # is necessary for proper code generation of assignments.
   if rettype == nil: result = true
   else:
-    case mapType(conf, rettype)
+    case mapType(conf, rettype, skResult)
     of ctArray:
       result = not (skipTypes(rettype, typedescInst).kind in
           {tyVar, tyLent, tyRef, tyPtr})
@@ -346,8 +349,8 @@ proc getTypePre(m: BModule, typ: PType; sig: SigHash): Rope =
     if result == nil: result = cacheGetType(m.typeCache, sig)
 
 proc structOrUnion(t: PType): Rope =
-  let cachedUnion {.global.} = rope("union")
-  let cachedStruct {.global.} = rope("struct")
+  let cachedUnion = rope("union")
+  let cachedStruct = rope("struct")
   let t = t.skipTypes({tyAlias, tySink})
   if tfUnion in t.flags: cachedUnion
   else: cachedStruct
@@ -379,7 +382,7 @@ proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope =
     doAssert m.forwTypeCache[sig] == result
   else: internalError(m.config, "getTypeForward(" & $typ.kind & ')')
 
-proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope =
+proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet; kind: TSymKind): Rope =
   ## like getTypeDescAux but creates only a *weak* dependency. In other words
   ## we know we only need a pointer to it so we only generate a struct forward
   ## declaration:
@@ -387,7 +390,7 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope =
   case etB.kind
   of tyObject, tyTuple:
     if isImportedCppType(etB) and t.kind == tyGenericInst:
-      result = getTypeDescAux(m, t, check)
+      result = getTypeDescAux(m, t, check, kind)
     else:
       result = getTypeForward(m, t, hashType(t))
       pushType(m, t)
@@ -417,18 +420,18 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope =
       result = getTypeForward(m, t, sig) & seqStar(m)
     pushType(m, t)
   else:
-    result = getTypeDescAux(m, t, check)
+    result = getTypeDescAux(m, t, check, kind)
 
 proc getSeqPayloadType(m: BModule; t: PType): Rope =
   var check = initIntSet()
-  result = getTypeDescWeak(m, t, check) & "_Content"
+  result = getTypeDescWeak(m, t, check, skParam) & "_Content"
   #result = getTypeForward(m, t, hashType(t)) & "_Content"
 
 proc seqV2ContentType(m: BModule; t: PType; check: var IntSet) =
   let sig = hashType(t)
   let result = cacheGetType(m.typeCache, sig)
   if result == nil:
-    discard getTypeDescAux(m, t, check)
+    discard getTypeDescAux(m, t, check, skVar)
   else:
     # little hack for now to prevent multiple definitions of the same
     # Seq_Content:
@@ -437,7 +440,7 @@ $3ifndef $2_Content_PP
 $3define $2_Content_PP
 struct $2_Content { NI cap; $1 data[SEQ_DECL_SIZE];};
 $3endif$N
-      """, [getTypeDescAux(m, t.skipTypes(abstractInst)[0], check), result, rope"#"])
+      """, [getTypeDescAux(m, t.skipTypes(abstractInst)[0], check, skVar), result, rope"#"])
 
 proc paramStorageLoc(param: PSym): TStorageLoc =
   if param.typ.skipTypes({tyVar, tyLent, tyTypeDesc}).kind notin {
@@ -453,7 +456,7 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
   if t[0] == nil or isInvalidReturnType(m.config, t[0]):
     rettype = ~"void"
   else:
-    rettype = getTypeDescAux(m, t[0], check)
+    rettype = getTypeDescAux(m, t[0], check, skResult)
   for i in 1..<t.n.len:
     if t.n[i].kind != nkSym: internalError(m.config, t.n.info, "genProcParams")
     var param = t.n[i].sym
@@ -462,14 +465,14 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
     fillLoc(param.loc, locParam, t.n[i], mangleParamName(m, param),
             param.paramStorageLoc)
     if ccgIntroducedPtr(m.config, param, t[0]):
-      params.add(getTypeDescWeak(m, param.typ, check))
+      params.add(getTypeDescWeak(m, param.typ, check, skParam))
       params.add(~"*")
       incl(param.loc.flags, lfIndirect)
       param.loc.storage = OnUnknown
     elif weakDep:
-      params.add(getTypeDescWeak(m, param.typ, check))
+      params.add(getTypeDescWeak(m, param.typ, check, skParam))
     else:
-      params.add(getTypeDescAux(m, param.typ, check))
+      params.add(getTypeDescAux(m, param.typ, check, skParam))
     params.add(~" ")
     params.add(param.loc.r)
     # declare the len field for open arrays:
@@ -487,10 +490,10 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
     var arr = t[0]
     if params != nil: params.add(", ")
     if mapReturnType(m.config, t[0]) != ctArray:
-      params.add(getTypeDescWeak(m, arr, check))
+      params.add(getTypeDescWeak(m, arr, check, skResult))
       params.add("*")
     else:
-      params.add(getTypeDescAux(m, arr, check))
+      params.add(getTypeDescAux(m, arr, check, skResult))
     params.addf(" Result", [])
   if t.callConv == ccClosure and declareEnvironment:
     if params != nil: params.add(", ")
@@ -562,16 +565,16 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
       let fieldType = field.loc.lode.typ.skipTypes(abstractInst)
       if fieldType.kind == tyUncheckedArray:
         result.addf("$1 $2[SEQ_DECL_SIZE];$n",
-            [getTypeDescAux(m, fieldType.elemType, check), sname])
+            [getTypeDescAux(m, fieldType.elemType, check, skField), sname])
       elif fieldType.kind == tySequence:
         # we need to use a weak dependency here for trecursive_table.
-        result.addf("$1 $2;$n", [getTypeDescWeak(m, field.loc.t, check), sname])
+        result.addf("$1 $2;$n", [getTypeDescWeak(m, field.loc.t, check, skField), sname])
       elif field.bitsize != 0:
-        result.addf("$1 $2:$3;$n", [getTypeDescAux(m, field.loc.t, check), sname, rope($field.bitsize)])
+        result.addf("$1 $2:$3;$n", [getTypeDescAux(m, field.loc.t, check, skField), sname, rope($field.bitsize)])
       else:
         # don't use fieldType here because we need the
         # tyGenericInst for C++ template support
-        result.addf("$1 $2;$n", [getTypeDescAux(m, field.loc.t, check), sname])
+        result.addf("$1 $2;$n", [getTypeDescAux(m, field.loc.t, check, skField), sname])
   else: internalError(m.config, n.info, "genRecordFieldsAux()")
 
 proc getRecordFields(m: BModule, typ: PType, check: var IntSet): Rope =
@@ -610,7 +613,7 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope,
         hasField = true
     elif m.compileToCpp:
       appcg(m, result, " : public $1 {$n",
-                      [getTypeDescAux(m, typ[0].skipTypes(skipPtrs), check)])
+                      [getTypeDescAux(m, typ[0].skipTypes(skipPtrs), check, skField)])
       if typ.isException and m.config.exc == excCpp:
         when false:
           appcg(m, result, "virtual void raise() { throw *this; }$n", []) # required for polymorphic exceptions
@@ -623,7 +626,7 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope,
       hasField = true
     else:
       appcg(m, result, " {$n  $1 Sup;$n",
-                      [getTypeDescAux(m, typ[0].skipTypes(skipPtrs), check)])
+                      [getTypeDescAux(m, typ[0].skipTypes(skipPtrs), check, skField)])
       hasField = true
   else:
     result.addf(" {$n", [name])
@@ -643,7 +646,7 @@ proc getTupleDesc(m: BModule, typ: PType, name: Rope,
   var desc: Rope = nil
   for i in 0..<typ.len:
     desc.addf("$1 Field$2;$n",
-         [getTypeDescAux(m, typ[i], check), rope(i)])
+         [getTypeDescAux(m, typ[i], check, skField), rope(i)])
   if desc == nil: result.add("char dummy;\L")
   else: result.add(desc)
   result.add("};\L")
@@ -677,7 +680,20 @@ proc resolveStarsInCppType(typ: PType, idx, stars: int): PType =
       result = if result.kind == tyGenericInst: result[1]
                else: result.elemType
 
-proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
+proc getOpenArrayDesc(m: BModule, t: PType, check: var IntSet; kind: TSymKind): Rope =
+  let sig = hashType(t)
+  if kind == skParam:
+    result = getTypeDescWeak(m, t[0], check, kind) & "*"
+  else:
+    result = cacheGetType(m.typeCache, sig)
+    if result == nil:
+      result = getTypeName(m, t, sig)
+      m.typeCache[sig] = result
+      let elemType = getTypeDescWeak(m, t[0], check, kind)
+      m.s[cfsTypes].addf("typedef struct {$n$2* d;$nNI l;$n} $1;$n",
+                         [result, elemType])
+
+proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKind): Rope =
   # returns only the type's name
 
   var t = origTyp.skipTypes(irrelevantForBackend-{tyOwned})
@@ -696,7 +712,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
       addAbiCheck(m, t, result)
 
   result = getTypePre(m, t, sig)
-  if result != nil:
+  if result != nil and t.kind != tyOpenArray:
     excl(check, t.id)
     return
   case t.kind
@@ -705,7 +721,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
                     compileToCpp(m): "&" else: "*"
     var et = origTyp.skipTypes(abstractInst).lastSon
     var etB = et.skipTypes(abstractInst)
-    if mapType(m.config, t) == ctPtrToArray:
+    if mapType(m.config, t, kind) == ctPtrToArray:
       if etB.kind == tySet:
         et = getSysType(m.g.graph, unknownLineInfo, tyUInt8)
       else:
@@ -715,7 +731,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
     case etB.kind
     of tyObject, tyTuple:
       if isImportedCppType(etB) and et.kind == tyGenericInst:
-        result = getTypeDescAux(m, et, check) & star
+        result = getTypeDescAux(m, et, check, kind) & star
       else:
         # no restriction! We have a forward declaration for structs
         let name = getTypeForward(m, et, hashType et)
@@ -723,7 +739,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
         m.typeCache[sig] = result
     of tySequence:
       if optSeqDestructors in m.config.globalOptions:
-        result = getTypeDescWeak(m, et, check) & star
+        result = getTypeDescWeak(m, et, check, kind) & star
         m.typeCache[sig] = result
       else:
         # no restriction! We have a forward declaration for structs
@@ -733,11 +749,10 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
         pushType(m, et)
     else:
       # else we have a strong dependency  :-(
-      result = getTypeDescAux(m, et, check) & star
+      result = getTypeDescAux(m, et, check, kind) & star
       m.typeCache[sig] = result
   of tyOpenArray, tyVarargs:
-    result = getTypeDescWeak(m, t[0], check) & "*"
-    m.typeCache[sig] = result
+    result = getOpenArrayDesc(m, t, check, kind)
   of tyEnum:
     result = cacheGetType(m.typeCache, sig)
     if result == nil:
@@ -783,7 +798,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
              [result, rettype, desc])
   of tySequence:
     if optSeqDestructors in m.config.globalOptions:
-      result = getTypeDescWeak(m, t, check)
+      result = getTypeDescWeak(m, t, check, kind)
     else:
       # we cannot use getTypeForward here because then t would be associated
       # with the name of the struct, not with the pointer to the struct:
@@ -804,11 +819,11 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
           if m.compileToCpp:
             appcg(m, m.s[cfsSeqTypes],
                 cppSeq & "  $1 data[SEQ_DECL_SIZE];$n" &
-                "};$n", [getTypeDescAux(m, t[0], check), result])
+                "};$n", [getTypeDescAux(m, t[0], check, kind), result])
           else:
             appcg(m, m.s[cfsSeqTypes],
                 cSeq & "  $1 data[SEQ_DECL_SIZE];$n" &
-                "};$n", [getTypeDescAux(m, t[0], check), result])
+                "};$n", [getTypeDescAux(m, t[0], check, kind), result])
         else:
           result = rope("TGenericSeq")
       result.add(seqStar(m))
@@ -816,7 +831,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
     result = getTypeName(m, origTyp, sig)
     m.typeCache[sig] = result
     if not isImportedType(t):
-      let foo = getTypeDescAux(m, t[0], check)
+      let foo = getTypeDescAux(m, t[0], check, kind)
       m.s[cfsTypes].addf("typedef $1 $2[1];$n", [foo, result])
   of tyArray:
     var n: BiggestInt = toInt64(lengthOrd(m.config, t))
@@ -824,7 +839,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
     result = getTypeName(m, origTyp, sig)
     m.typeCache[sig] = result
     if not isImportedType(t):
-      let foo = getTypeDescAux(m, t[1], check)
+      let foo = getTypeDescAux(m, t[1], check, kind)
       m.s[cfsTypes].addf("typedef $1 $2[$3];$n",
            [foo, result, rope(n)])
   of tyObject, tyTuple:
@@ -840,7 +855,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
           internalAssert m.config, ty.n != nil
           result.add ty.n.renderTree
         else:
-          result.add getTypeDescAux(m, ty, check)
+          result.add getTypeDescAux(m, ty, check, kind)
 
       while i < cppName.data.len:
         if cppName.data[i] == '\'':
@@ -901,16 +916,16 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
              [result, rope(getSize(m.config, t))])
   of tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, tySink, tyOwned,
      tyUserTypeClass, tyUserTypeClassInst, tyInferred:
-    result = getTypeDescAux(m, lastSon(t), check)
+    result = getTypeDescAux(m, lastSon(t), check, kind)
   else:
     internalError(m.config, "getTypeDescAux(" & $t.kind & ')')
     result = nil
   # fixes bug #145:
   excl(check, t.id)
 
-proc getTypeDesc(m: BModule, typ: PType): Rope =
+proc getTypeDesc(m: BModule, typ: PType; kind = skParam): Rope =
   var check = initIntSet()
-  result = getTypeDescAux(m, typ, check)
+  result = getTypeDescAux(m, typ, check, kind)
 
 type
   TClosureTypeKind = enum ## In C closures are mapped to 3 different things.
@@ -942,7 +957,7 @@ proc finishTypeDescriptions(m: BModule) =
     if optSeqDestructors in m.config.globalOptions and t.skipTypes(abstractInst).kind == tySequence:
       seqV2ContentType(m, t, check)
     else:
-      discard getTypeDescAux(m, t, check)
+      discard getTypeDescAux(m, t, check, skParam)
     inc(i)
   m.typeStack.setLen 0
 
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index d40aa32d7..d5e1e020c 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -310,14 +310,19 @@ include ccgtypes
 
 # ------------------------------ Manager of temporaries ------------------
 
+template mapTypeChooser(n: PNode): TSymKind =
+  (if n.kind == nkSym: n.sym.kind else: skVar)
+
+template mapTypeChooser(a: TLoc): TSymKind = mapTypeChooser(a.lode)
+
 proc addrLoc(conf: ConfigRef; a: TLoc): Rope =
   result = a.r
-  if lfIndirect notin a.flags and mapType(conf, a.t) != ctArray:
+  if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a)) != ctArray:
     result = "(&" & result & ")"
 
 proc byRefLoc(p: BProc; a: TLoc): Rope =
   result = a.r
-  if lfIndirect notin a.flags and mapType(p.config, a.t) != ctArray and not
+  if lfIndirect notin a.flags and mapType(p.config, a.t, mapTypeChooser(a)) != ctArray and not
       p.module.compileToCpp:
     result = "(&" & result & ")"
 
@@ -364,7 +369,7 @@ proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: var TLoc,
         rawConstExpr(p, newNodeIT(nkType, a.lode.info, objType), tmp)
         linefmt(p, cpsStmts,
             "#nimCopyMem((void*)$1, (NIM_CONST void*)&$2, sizeof($3));$n",
-            [rdLoc(a), rdLoc(tmp), getTypeDesc(p.module, objType)])
+            [rdLoc(a), rdLoc(tmp), getTypeDesc(p.module, objType, mapTypeChooser(a))])
       else:
         rawConstExpr(p, newNodeIT(nkType, a.lode.info, t), tmp)
         genAssignment(p, a, tmp, {})
@@ -387,7 +392,7 @@ proc genRefAssign(p: BProc, dest, src: TLoc)
 
 proc isComplexValueType(t: PType): bool {.inline.} =
   let t = t.skipTypes(abstractInst + tyUserTypeClasses)
-  result = t.kind in {tyArray, tySet, tyTuple, tyObject} or
+  result = t.kind in {tyArray, tySet, tyTuple, tyObject, tyOpenArray} or
     (t.kind == tyProc and t.callConv == ccClosure)
 
 include ccgreset
@@ -420,7 +425,8 @@ proc resetLoc(p: BProc, loc: var TLoc) =
       # array passed as argument decayed into pointer, bug #7332
       # so we use getTypeDesc here rather than rdLoc(loc)
       linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n",
-              [addrLoc(p.config, loc), getTypeDesc(p.module, loc.t)])
+              [addrLoc(p.config, loc),
+              getTypeDesc(p.module, loc.t, mapTypeChooser(loc))])
       # XXX: We can be extra clever here and call memset only
       # on the bytes following the m_type field?
       genObjectInit(p, cpsStmts, loc.t, loc, constructObj)
@@ -431,14 +437,14 @@ proc constructLoc(p: BProc, loc: var TLoc, isTemp = false) =
     linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
   elif not isComplexValueType(typ):
     linefmt(p, cpsStmts, "$1 = ($2)0;$n", [rdLoc(loc),
-      getTypeDesc(p.module, typ)])
+      getTypeDesc(p.module, typ, mapTypeChooser(loc))])
   else:
     if not isTemp or containsGarbageCollectedRef(loc.t):
       # don't use nimZeroMem for temporary values for performance if we can
       # avoid it:
       if not isImportedCppType(typ):
         linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n",
-                [addrLoc(p.config, loc), getTypeDesc(p.module, typ)])
+                [addrLoc(p.config, loc), getTypeDesc(p.module, typ, mapTypeChooser(loc))])
     genObjectInit(p, cpsStmts, loc.t, loc, constructObj)
 
 proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) =
@@ -456,7 +462,7 @@ proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) =
 proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) =
   inc(p.labels)
   result.r = "T" & rope(p.labels) & "_"
-  linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t), result.r])
+  linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, skVar), result.r])
   result.k = locTemp
   result.lode = lodeTyp t
   result.storage = OnStack
@@ -466,7 +472,7 @@ proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) =
 proc getTempCpp(p: BProc, t: PType, result: var TLoc; value: Rope) =
   inc(p.labels)
   result.r = "T" & rope(p.labels) & "_"
-  linefmt(p, cpsStmts, "$1 $2 = $3;$n", [getTypeDesc(p.module, t), result.r, value])
+  linefmt(p, cpsStmts, "$1 $2 = $3;$n", [getTypeDesc(p.module, t, skVar), result.r, value])
   result.k = locTemp
   result.lode = lodeTyp t
   result.storage = OnStack
@@ -488,7 +494,7 @@ proc localVarDecl(p: BProc; n: PNode): Rope =
     if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy)
   if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0:
     result.addf("NIM_ALIGN($1) ", [rope(s.alignment)])
-  result.add getTypeDesc(p.module, s.typ)
+  result.add getTypeDesc(p.module, s.typ, skVar)
   if s.constraint.isNil:
     if sfRegister in s.flags: result.add(" register")
     #elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds:
@@ -541,7 +547,7 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) =
         internalError(p.config, n.info, ".threadvar variables cannot have a value")
     else:
       var decl: Rope = nil
-      var td = getTypeDesc(p.module, s.loc.t)
+      var td = getTypeDesc(p.module, s.loc.t, skVar)
       if s.constraint.isNil:
         if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0:
           decl.addf "NIM_ALIGN($1) ", [rope(s.alignment)]
@@ -687,7 +693,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) =
       initLoc(dest, locTemp, lib.path, OnStack)
       dest.r = getTempName(m)
       appcg(m, m.s[cfsDynLibInit],"$1 $2;$n",
-           [getTypeDesc(m, lib.path.typ), rdLoc(dest)])
+           [getTypeDesc(m, lib.path.typ, skVar), rdLoc(dest)])
       expr(p, lib.path, dest)
 
       m.s[cfsVars].add(p.s(cpsLocals))
@@ -727,7 +733,7 @@ proc symInDynamicLib(m: BModule, sym: PSym) =
       params.add(rdLoc(a))
       params.add(", ")
     let load = "\t$1 = ($2) ($3$4));$n" %
-        [tmp, getTypeDesc(m, sym.typ), params, makeCString($extname)]
+        [tmp, getTypeDesc(m, sym.typ, skVar), params, makeCString($extname)]
     var last = lastSon(n)
     if last.kind == nkHiddenStdConv: last = last[1]
     internalAssert(m.config, last.kind == nkStrLit)
@@ -741,8 +747,8 @@ proc symInDynamicLib(m: BModule, sym: PSym) =
   else:
     appcg(m, m.s[cfsDynLibInit],
         "\t$1 = ($2) #nimGetProcAddr($3, $4);$n",
-        [tmp, getTypeDesc(m, sym.typ), lib.name, makeCString($extname)])
-  m.s[cfsVars].addf("$2 $1;$n", [sym.loc.r, getTypeDesc(m, sym.loc.t)])
+        [tmp, getTypeDesc(m, sym.typ, skVar), lib.name, makeCString($extname)])
+  m.s[cfsVars].addf("$2 $1;$n", [sym.loc.r, getTypeDesc(m, sym.loc.t, skVar)])
 
 proc varInDynamicLib(m: BModule, sym: PSym) =
   var lib = sym.annex
@@ -754,9 +760,9 @@ proc varInDynamicLib(m: BModule, sym: PSym) =
   inc(m.labels, 2)
   appcg(m, m.s[cfsDynLibInit],
       "$1 = ($2*) #nimGetProcAddr($3, $4);$n",
-      [tmp, getTypeDesc(m, sym.typ), lib.name, makeCString($extname)])
+      [tmp, getTypeDesc(m, sym.typ, skVar), lib.name, makeCString($extname)])
   m.s[cfsVars].addf("$2* $1;$n",
-      [sym.loc.r, getTypeDesc(m, sym.loc.t)])
+      [sym.loc.r, getTypeDesc(m, sym.loc.t, skVar)])
 
 proc symInDynamicLibPartial(m: BModule, sym: PSym) =
   sym.loc.r = mangleDynLibProc(sym)
@@ -1187,7 +1193,7 @@ proc requestConstImpl(p: BProc, sym: PSym) =
         [getTypeDesc(q, sym.typ), actualConstName, genBracedInit(q.initProc, sym.ast, isConst = true)])
     if m.hcrOn:
       # generate the global pointer with the real name
-      q.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t), sym.loc.r])
+      q.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, skVar), sym.loc.r])
       # register it (but ignore the boolean result of hcrRegisterGlobal)
       q.initProc.procSec(cpsLocals).addf(
         "\thcrRegisterGlobal($1, \"$2\", sizeof($3), NULL, (void**)&$2);$n",
@@ -1201,13 +1207,13 @@ proc requestConstImpl(p: BProc, sym: PSym) =
   if q != m and not containsOrIncl(m.declaredThings, sym.id):
     assert(sym.loc.r != nil)
     if m.hcrOn:
-      m.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t), sym.loc.r]);
+      m.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, skVar), sym.loc.r]);
       m.initProc.procSec(cpsLocals).addf(
         "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r,
-        getTypeDesc(m, sym.loc.t), getModuleDllPath(q, sym)])
+        getTypeDesc(m, sym.loc.t, skVar), getModuleDllPath(q, sym)])
     else:
       let headerDecl = "extern NIM_CONST $1 $2;$n" %
-          [getTypeDesc(m, sym.loc.t), sym.loc.r]
+          [getTypeDesc(m, sym.loc.t, skVar), sym.loc.r]
       m.s[cfsData].add(headerDecl)
       if sfExportc in sym.flags and p.module.g.generatedHeader != nil:
         p.module.g.generatedHeader.s[cfsData].add(headerDecl)
@@ -1247,7 +1253,7 @@ proc genVarPrototype(m: BModule, n: PNode) =
       if sym.kind in {skLet, skVar, skField, skForVar} and sym.alignment > 0:
         m.s[cfsVars].addf "NIM_ALIGN($1) ", [rope(sym.alignment)]
       m.s[cfsVars].add(if m.hcrOn: "static " else: "extern ")
-      m.s[cfsVars].add(getTypeDesc(m, sym.loc.t))
+      m.s[cfsVars].add(getTypeDesc(m, sym.loc.t, skVar))
       if m.hcrOn: m.s[cfsVars].add("*")
       if lfDynamicLib in sym.loc.flags: m.s[cfsVars].add("*")
       if sfRegister in sym.flags: m.s[cfsVars].add(" register")
@@ -1255,7 +1261,7 @@ proc genVarPrototype(m: BModule, n: PNode) =
       m.s[cfsVars].addf(" $1;$n", [sym.loc.r])
       if m.hcrOn: m.initProc.procSec(cpsLocals).addf(
         "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r,
-        getTypeDesc(m, sym.loc.t), getModuleDllPath(m, sym)])
+        getTypeDesc(m, sym.loc.t, skVar), getModuleDllPath(m, sym)])
 
 proc addNimDefines(result: var Rope; conf: ConfigRef) {.inline.} =
   result.addf("#define NIM_INTBITS $1\L", [
@@ -1613,10 +1619,10 @@ proc hcrGetProcLoadCode(m: BModule, sym, prefix, handle, getProcFunc: string): R
   prc.typ.sym = nil
 
   if not containsOrIncl(m.declaredThings, prc.id):
-    m.s[cfsVars].addf("static $2 $1;$n", [prc.loc.r, getTypeDesc(m, prc.loc.t)])
+    m.s[cfsVars].addf("static $2 $1;$n", [prc.loc.r, getTypeDesc(m, prc.loc.t, skVar)])
 
   result = "\t$1 = ($2) $3($4, $5);$n" %
-      [tmp, getTypeDesc(m, prc.typ), getProcFunc.rope, handle.rope, makeCString(prefix & sym)]
+      [tmp, getTypeDesc(m, prc.typ, skVar), getProcFunc.rope, handle.rope, makeCString(prefix & sym)]
 
 proc genInitCode(m: BModule) =
   ## this function is called in cgenWriteModules after all modules are closed,
diff --git a/compiler/options.nim b/compiler/options.nim
index 14016495f..5b23ac9af 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -162,7 +162,8 @@ type
       ## which itself requires `nimble install libffi`, see #10150
       ## Note: this feature can't be localized with {.push.}
     vmopsDanger,
-    strictFuncs
+    strictFuncs,
+    views
 
   LegacyFeature* = enum
     allowSemcheckedAstModification,
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index 9601bf082..9c7609f9a 100644
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -1035,7 +1035,14 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
       gsub(g, n, 1)
   of nkHiddenStdConv, nkHiddenSubConv:
     if n.len >= 2:
-      gsub(g, n[1])
+      when false:
+        # if {renderIds, renderIr} * g.flags != {}:
+        put(g, tkSymbol, "(conv)")
+        put(g, tkParLe, "(")
+        gsub(g, n[1])
+        put(g, tkParRi, ")")
+      else:
+        gsub(g, n[1])
     else:
       put(g, tkSymbol, "(wrong conv)")
   of nkHiddenCallConv:
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 07c76eed4..705e1b72c 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -17,7 +17,7 @@ import
   intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting,
   evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity,
   lowerings, plugins/active, rod, lineinfos, strtabs, int128,
-  isolation_check
+  isolation_check, typeallowed
 
 from modulegraphs import ModuleGraph, PPassContext, onUse, onDef, onDefResolveForward
 
@@ -228,9 +228,9 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
 proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
                         allowed: TSymFlags): PSym
 
-proc typeAllowedCheck(conf: ConfigRef; info: TLineInfo; typ: PType; kind: TSymKind;
+proc typeAllowedCheck(c: PContext; info: TLineInfo; typ: PType; kind: TSymKind;
                       flags: TTypeAllowedFlags = {}) =
-  let t = typeAllowed(typ, kind, flags)
+  let t = typeAllowed(typ, kind, c, flags)
   if t != nil:
     var err: string
     if t == typ:
@@ -240,10 +240,10 @@ proc typeAllowedCheck(conf: ConfigRef; info: TLineInfo; typ: PType; kind: TSymKi
     else:
       err = "invalid type: '$1' in this context: '$2' for $3" % [typeToString(t),
               typeToString(typ), toHumanStr(kind)]
-    localError(conf, info, err)
+    localError(c.config, info, err)
 
 proc paramsTypeCheck(c: PContext, typ: PType) {.inline.} =
-  typeAllowedCheck(c.config, typ.n.info, typ, skProc)
+  typeAllowedCheck(c, typ.n.info, typ, skProc)
 
 proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym
 proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 0e1c5e9d3..80e04ead4 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -178,7 +178,7 @@ proc checkConvertible(c: PContext, targetTyp: PType, src: PNode): TConvStatus =
     else:
       discard
 
-proc isCastable(conf: ConfigRef; dst, src: PType): bool =
+proc isCastable(c: PContext; dst, src: PType): bool =
   ## Checks whether the source type can be cast to the destination type.
   ## Casting is very unrestrictive; casts are allowed as long as
   ## castDest.size >= src.size, and typeAllowed(dst, skParam)
@@ -193,6 +193,7 @@ proc isCastable(conf: ConfigRef; dst, src: PType): bool =
     return false
   if skipTypes(dst, abstractInst).kind == tyBuiltInTypeClass:
     return false
+  let conf = c.config
   if conf.selectedGC in {gcArc, gcOrc}:
     let d = skipTypes(dst, abstractInst)
     let s = skipTypes(src, abstractInst)
@@ -210,7 +211,7 @@ proc isCastable(conf: ConfigRef; dst, src: PType): bool =
     result = false
   elif srcSize < 0:
     result = false
-  elif typeAllowed(dst, skParam) != nil:
+  elif typeAllowed(dst, skParam, c) != nil:
     result = false
   elif dst.kind == tyProc and dst.callConv == ccClosure:
     result = src.kind == tyProc and src.callConv == ccClosure
@@ -338,7 +339,7 @@ proc semCast(c: PContext, n: PNode): PNode =
   let castedExpr = semExprWithType(c, n[1])
   if tfHasMeta in targetType.flags:
     localError(c.config, n[0].info, "cannot cast to a non concrete type: '$1'" % $targetType)
-  if not isCastable(c.config, targetType, castedExpr.typ):
+  if not isCastable(c, targetType, castedExpr.typ):
     let tar = $targetType
     let alt = typeToString(targetType, preferDesc)
     let msg = if tar != alt: tar & "=" & alt else: tar
@@ -794,7 +795,7 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
     if callee.kind notin {skProc, skFunc, skConverter, skConst} or callee.isGenericRoutine:
       return
 
-    if n.typ != nil and typeAllowed(n.typ, skConst) != nil: return
+    if n.typ != nil and typeAllowed(n.typ, skConst, c) != nil: return
 
     var call = newNodeIT(nkCall, n.info, n.typ)
     call.add(n[0])
@@ -962,9 +963,9 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
           # t.sym != nil
           # sfAnon notin t.sym.flags
           # t.kind != tySequence(It is tyProc)
-          if typ.sym != nil and sfAnon notin typ.sym.flags and 
+          if typ.sym != nil and sfAnon notin typ.sym.flags and
                                 typ.kind == tyProc:
-            msg.add(" = " & 
+            msg.add(" = " &
                 typeToString(typ, preferDesc))
           localError(c.config, n.info, msg)
         return errorNode(c, n)
@@ -1600,7 +1601,7 @@ proc takeImplicitAddr(c: PContext, n: PNode; isLent: bool): PNode =
 proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} =
   if le.kind == nkHiddenDeref:
     var x = le[0]
-    if x.typ.kind in {tyVar, tyLent} and x.kind == nkSym and x.sym.kind == skResult:
+    if (x.typ.kind in {tyVar, tyLent} or isViewType(x.typ)) and x.kind == nkSym and x.sym.kind == skResult:
       n[0] = x # 'result[]' --> 'result'
       n[1] = takeImplicitAddr(c, ri, x.typ.kind == tyLent)
       x.typ.flags.incl tfVarIsPtr
@@ -1732,7 +1733,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
         if cmpTypes(c, lhs.typ, rhsTyp) in {isGeneric, isEqual}:
           internalAssert c.config, c.p.resultSym != nil
           # Make sure the type is valid for the result variable
-          typeAllowedCheck(c.config, n.info, rhsTyp, skResult)
+          typeAllowedCheck(c, n.info, rhsTyp, skResult)
           lhs.typ = rhsTyp
           c.p.resultSym.typ = rhsTyp
           c.p.owner.typ[0] = rhsTyp
@@ -1825,7 +1826,9 @@ proc semYieldVarResult(c: PContext, n: PNode, restype: PType) =
             tupleConstr[i] = takeImplicitAddr(c, tupleConstr[i], e.kind == tyLent)
         else:
           localError(c.config, n[0].info, errXExpected, "tuple constructor")
-  else: discard
+  else:
+    if isViewType(t):
+      n[0] = takeImplicitAddr(c, n[0], false)
 
 proc semYield(c: PContext, n: PNode): PNode =
   result = n
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index f745f9075..b48d7e29e 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -1243,8 +1243,11 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
     effects[ensuresEffects] = ensuresSpec
 
   var mutationInfo = MutationInfo()
-  if strictFuncs in c.features and not t.hasSideEffect and t.hasDangerousAssign:
-    t.hasSideEffect = mutatesNonVarParameters(s, body, mutationInfo)
+  if {strictFuncs, views} * c.features != {}:
+    var partitions = computeGraphPartitions(s, body)
+    if not t.hasSideEffect and t.hasDangerousAssign:
+      t.hasSideEffect = varpartitions.hasSideEffect(partitions, mutationInfo)
+    checkBorrowedLocations(partitions, g.config)
 
   if sfThread in s.flags and t.gcUnsafe:
     if optThreads in g.config.globalOptions and optThreadAnalysis in g.config.globalOptions:
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 2c3ff2bea..14f08b4b3 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -534,7 +534,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
 
     if c.matchedConcept != nil:
       typFlags.incl taConcept
-    typeAllowedCheck(c.config, a.info, typ, symkind, typFlags)
+    typeAllowedCheck(c, a.info, typ, symkind, typFlags)
 
     when false: liftTypeBoundOps(c, typ, a.info)
     instAllTypeBoundOp(c, a.info)
@@ -667,7 +667,7 @@ proc semConst(c: PContext, n: PNode): PNode =
     if def.kind != nkNilLit:
       if c.matchedConcept != nil:
         typFlags.incl taConcept
-      typeAllowedCheck(c.config, a.info, typ, skConst, typFlags)
+      typeAllowedCheck(c, a.info, typ, skConst, typFlags)
 
     var b: PNode
     if a.kind == nkVarTuple:
diff --git a/compiler/transf.nim b/compiler/transf.nim
index 8ff0664da..5dd275414 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -494,6 +494,7 @@ proc transformConv(c: PTransf, n: PNode): PNode =
       result = transformSons(c, n)
   of tyOpenArray, tyVarargs:
     result = transform(c, n[1])
+    #result = transformSons(c, n)
     result.typ = takeType(n.typ, n[1].typ)
     #echo n.info, " came here and produced ", typeToString(result.typ),
     #   " from ", typeToString(n.typ), " and ", typeToString(n[1].typ)
@@ -1107,6 +1108,9 @@ proc transformBody*(g: ModuleGraph, prc: PSym, cache: bool): PNode =
     else:
       prc.transformedBody = nil
 
+  #if prc.name.s == "main":
+  #  echo "transformed into ", renderTree(result, {renderIds})
+
 proc transformStmt*(g: ModuleGraph; module: PSym, n: PNode): PNode =
   if nfTransf in n.flags:
     result = n
diff --git a/compiler/trees.nim b/compiler/trees.nim
index bfb429f13..c5c1a0d75 100644
--- a/compiler/trees.nim
+++ b/compiler/trees.nim
@@ -183,7 +183,7 @@ proc getRoot*(n: PNode): PSym =
     if n.sym.kind in {skVar, skResult, skTemp, skLet, skForVar, skParam}:
       result = n.sym
   of nkDotExpr, nkBracketExpr, nkHiddenDeref, nkDerefExpr,
-      nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
+      nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr, nkHiddenAddr, nkAddr:
     result = getRoot(n[0])
   of nkHiddenStdConv, nkHiddenSubConv, nkConv:
     result = getRoot(n[1])
diff --git a/compiler/typeallowed.nim b/compiler/typeallowed.nim
new file mode 100644
index 000000000..5a192783e
--- /dev/null
+++ b/compiler/typeallowed.nim
@@ -0,0 +1,228 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2020 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module contains 'typeAllowed' and friends which check
+## for invalid types like 'openArray[var int]'.
+
+import
+  intsets, ast, renderer, options, semdata, types
+
+type
+  TTypeAllowedFlag* = enum
+    taField,
+    taHeap,
+    taConcept,
+    taIsOpenArray,
+    taNoUntyped
+    taIsTemplateOrMacro
+    taProcContextIsNotMacro
+
+  TTypeAllowedFlags* = set[TTypeAllowedFlag]
+
+proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind;
+                    c: PContext; flags: TTypeAllowedFlags = {}): PType
+
+proc typeAllowedNode(marker: var IntSet, n: PNode, kind: TSymKind,
+                     c: PContext; flags: TTypeAllowedFlags = {}): PType =
+  if n != nil:
+    result = typeAllowedAux(marker, n.typ, kind, c, flags)
+    if result == nil:
+      case n.kind
+      of nkNone..nkNilLit:
+        discard
+      else:
+        #if n.kind == nkRecCase and kind in {skProc, skFunc, skConst}:
+        #  return n[0].typ
+        for i in 0..<n.len:
+          let it = n[i]
+          result = typeAllowedNode(marker, it, kind, c, flags)
+          if result != nil: break
+
+proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
+                    c: PContext; flags: TTypeAllowedFlags = {}): PType =
+  assert(kind in {skVar, skLet, skConst, skProc, skFunc, skParam, skResult})
+  # if we have already checked the type, return true, because we stop the
+  # evaluation if something is wrong:
+  result = nil
+  if typ == nil: return nil
+  if containsOrIncl(marker, typ.id): return nil
+  var t = skipTypes(typ, abstractInst-{tyTypeDesc})
+  case t.kind
+  of tyVar, tyLent:
+    if kind in {skProc, skFunc, skConst} and (views notin c.features):
+      result = t
+    elif t.kind == tyLent and kind != skResult:
+      result = t
+    else:
+      var t2 = skipTypes(t[0], abstractInst-{tyTypeDesc})
+      case t2.kind
+      of tyVar, tyLent:
+        if taHeap notin flags: result = t2 # ``var var`` is illegal on the heap
+      of tyOpenArray:
+        if (kind != skParam and views notin c.features) or taIsOpenArray in flags: result = t
+        else: result = typeAllowedAux(marker, t2[0], kind, c, flags+{taIsOpenArray})
+      of tyUncheckedArray:
+        if kind != skParam: result = t
+        else: result = typeAllowedAux(marker, t2[0], kind, c, flags)
+      else:
+        if kind notin {skParam, skResult}: result = t
+        else: result = typeAllowedAux(marker, t2, kind, c, flags)
+  of tyProc:
+    if kind in {skVar, skLet, skConst} and taIsTemplateOrMacro in flags:
+      result = t
+    else:
+      if isInlineIterator(typ) and kind in {skVar, skLet, skConst, skParam, skResult}:
+        # only closure iterators may be assigned to anything.
+        result = t
+      let f = if kind in {skProc, skFunc}: flags+{taNoUntyped} else: flags
+      for i in 1..<t.len:
+        if result != nil: break
+        result = typeAllowedAux(marker, t[i], skParam, c, f-{taIsOpenArray})
+      if result.isNil and t[0] != nil:
+        result = typeAllowedAux(marker, t[0], skResult, c, flags)
+  of tyTypeDesc:
+    if kind in {skVar, skLet, skConst} and taProcContextIsNotMacro in flags:
+      result = t
+    else:
+      # XXX: This is still a horrible idea...
+      result = nil
+  of tyUntyped, tyTyped:
+    if kind notin {skParam, skResult} or taNoUntyped in flags: result = t
+  of tyStatic:
+    if kind notin {skParam}: result = t
+  of tyVoid:
+    if taField notin flags: result = t
+  of tyTypeClasses:
+    if tfGenericTypeParam in t.flags or taConcept in flags: #or taField notin flags:
+      discard
+    elif t.isResolvedUserTypeClass:
+      result = typeAllowedAux(marker, t.lastSon, kind, c, flags)
+    elif kind notin {skParam, skResult}:
+      result = t
+  of tyGenericBody, tyGenericParam, tyGenericInvocation,
+     tyNone, tyForward, tyFromExpr:
+    result = t
+  of tyNil:
+    if kind != skConst and kind != skParam: result = t
+  of tyString, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString, tyPointer:
+    result = nil
+  of tyOrdinal:
+    if kind != skParam: result = t
+  of tyGenericInst, tyDistinct, tyAlias, tyInferred:
+    result = typeAllowedAux(marker, lastSon(t), kind, c, flags)
+  of tyRange:
+    if skipTypes(t[0], abstractInst-{tyTypeDesc}).kind notin
+      {tyChar, tyEnum, tyInt..tyFloat128, tyInt..tyUInt64}: result = t
+  of tyOpenArray:
+    # you cannot nest openArrays/sinks/etc.
+    if (kind != skParam and views notin c.features) or taIsOpenArray in flags:
+      result = t
+    else:
+      result = typeAllowedAux(marker, t[0], kind, c, flags+{taIsOpenArray})
+  of tyVarargs, tySink:
+    # you cannot nest openArrays/sinks/etc.
+    if kind != skParam or taIsOpenArray in flags:
+      result = t
+    else:
+      result = typeAllowedAux(marker, t[0], kind, c, flags+{taIsOpenArray})
+  of tyUncheckedArray:
+    if kind != skParam and taHeap notin flags:
+      result = t
+    else:
+      result = typeAllowedAux(marker, lastSon(t), kind, c, flags-{taHeap})
+  of tySequence:
+    if t[0].kind != tyEmpty:
+      result = typeAllowedAux(marker, t[0], kind, c, flags+{taHeap})
+    elif kind in {skVar, skLet}:
+      result = t[0]
+  of tyArray:
+    if t[1].kind == tyTypeDesc:
+      result = t[1]
+    elif t[1].kind != tyEmpty:
+      result = typeAllowedAux(marker, t[1], kind, c, flags)
+    elif kind in {skVar, skLet}:
+      result = t[1]
+  of tyRef:
+    if kind == skConst: result = t
+    else: result = typeAllowedAux(marker, t.lastSon, kind, c, flags+{taHeap})
+  of tyPtr:
+    result = typeAllowedAux(marker, t.lastSon, kind, c, flags+{taHeap})
+  of tySet:
+    for i in 0..<t.len:
+      result = typeAllowedAux(marker, t[i], kind, c, flags)
+      if result != nil: break
+  of tyObject, tyTuple:
+    if kind in {skProc, skFunc, skConst} and
+        t.kind == tyObject and t[0] != nil:
+      result = t
+    else:
+      let flags = flags+{taField}
+      for i in 0..<t.len:
+        result = typeAllowedAux(marker, t[i], kind, c, flags)
+        if result != nil: break
+      if result.isNil and t.n != nil:
+        result = typeAllowedNode(marker, t.n, kind, c, flags)
+  of tyEmpty:
+    if kind in {skVar, skLet}: result = t
+  of tyProxy:
+    # for now same as error node; we say it's a valid type as it should
+    # prevent cascading errors:
+    result = nil
+  of tyOwned:
+    if t.len == 1 and t[0].skipTypes(abstractInst).kind in {tyRef, tyPtr, tyProc}:
+      result = typeAllowedAux(marker, t.lastSon, kind, c, flags+{taHeap})
+    else:
+      result = t
+  of tyOptDeprecated: doAssert false
+
+proc typeAllowed*(t: PType, kind: TSymKind; c: PContext; flags: TTypeAllowedFlags = {}): PType =
+  # returns 'nil' on success and otherwise the part of the type that is
+  # wrong!
+  var marker = initIntSet()
+  result = typeAllowedAux(marker, t, kind, c, flags)
+
+proc isViewTypeAux(marker: var IntSet, t: PType): bool
+
+proc isViewTypeNode(marker: var IntSet, n: PNode): bool =
+  case n.kind
+  of nkSym:
+    result = isViewTypeAux(marker, n.typ)
+  of nkOfBranch:
+    result = isViewTypeNode(marker, n.lastSon)
+  else:
+    for child in n:
+      result = isViewTypeNode(marker, child)
+      if result: break
+
+proc isViewTypeAux(marker: var IntSet, t: PType): bool =
+  if containsOrIncl(marker, t.id): return false
+  case t.kind
+  of tyVar, tyLent, tyVarargs, tyOpenArray:
+    result = true
+  of tyGenericInst, tyDistinct, tyAlias, tyInferred, tySink, tyOwned,
+     tyUncheckedArray, tySequence, tyArray, tyRef, tyStatic, tyFromExpr:
+    result = isViewTypeAux(marker, lastSon(t))
+  of tyTuple:
+    for i in 0..<t.len:
+      result = isViewTypeAux(marker, t[i])
+      if result: break
+  of tyObject:
+    result = false
+    if t.n != nil:
+      result = isViewTypeNode(marker, t.n)
+    if t[0] != nil:
+      result = result or isViewTypeAux(marker, t[0])
+  else:
+    # it doesn't matter what these types contain, 'ptr openArray' is not a
+    # view type!
+    result = false
+
+proc isViewType*(t: PType): bool =
+  var marker = initIntSet()
+  result = isViewTypeAux(marker, t)
diff --git a/compiler/types.nim b/compiler/types.nim
index 44827aa33..b9fc4d4a2 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -329,7 +329,7 @@ proc containsTyRef*(typ: PType): bool =
   result = searchTypeFor(typ, isTyRef)
 
 proc isHiddenPointer(t: PType): bool =
-  result = t.kind in {tyString, tySequence}
+  result = t.kind in {tyString, tySequence, tyOpenArray, tyVarargs}
 
 proc containsHiddenPointer*(typ: PType): bool =
   # returns true if typ contains a string, table or sequence (all the things
@@ -1240,37 +1240,6 @@ proc commonSuperclass*(a, b: PType): PType =
       return t
     y = y[0]
 
-type
-  TTypeAllowedFlag* = enum
-    taField,
-    taHeap,
-    taConcept,
-    taIsOpenArray,
-    taNoUntyped
-    taIsTemplateOrMacro
-    taProcContextIsNotMacro
-
-  TTypeAllowedFlags* = set[TTypeAllowedFlag]
-
-proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
-                    flags: TTypeAllowedFlags = {}): PType
-
-proc typeAllowedNode(marker: var IntSet, n: PNode, kind: TSymKind,
-                     flags: TTypeAllowedFlags = {}): PType =
-  if n != nil:
-    result = typeAllowedAux(marker, n.typ, kind, flags)
-    if result == nil:
-      case n.kind
-      of nkNone..nkNilLit:
-        discard
-      else:
-        #if n.kind == nkRecCase and kind in {skProc, skFunc, skConst}:
-        #  return n[0].typ
-        for i in 0..<n.len:
-          let it = n[i]
-          result = typeAllowedNode(marker, it, kind, flags)
-          if result != nil: break
-
 proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]],
                 last: TTypeKind): bool =
   var a = a
@@ -1280,143 +1249,6 @@ proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]],
     a = a[i]
   result = a.kind == last
 
-proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
-                    flags: TTypeAllowedFlags = {}): PType =
-  assert(kind in {skVar, skLet, skConst, skProc, skFunc, skParam, skResult})
-  # if we have already checked the type, return true, because we stop the
-  # evaluation if something is wrong:
-  result = nil
-  if typ == nil: return nil
-  if containsOrIncl(marker, typ.id): return nil
-  var t = skipTypes(typ, abstractInst-{tyTypeDesc})
-  case t.kind
-  of tyVar, tyLent:
-    if kind in {skProc, skFunc, skConst}:
-      result = t
-    elif t.kind == tyLent and kind != skResult:
-      result = t
-    else:
-      var t2 = skipTypes(t[0], abstractInst-{tyTypeDesc})
-      case t2.kind
-      of tyVar, tyLent:
-        if taHeap notin flags: result = t2 # ``var var`` is illegal on the heap
-      of tyOpenArray:
-        if kind != skParam or taIsOpenArray in flags: result = t
-        else: result = typeAllowedAux(marker, t2[0], kind, flags+{taIsOpenArray})
-      of tyUncheckedArray:
-        if kind != skParam: result = t
-        else: result = typeAllowedAux(marker, t2[0], kind, flags)
-      else:
-        if kind notin {skParam, skResult}: result = t
-        else: result = typeAllowedAux(marker, t2, kind, flags)
-  of tyProc:
-    if kind in {skVar, skLet, skConst} and taIsTemplateOrMacro in flags:
-      result = t
-    else:
-      if isInlineIterator(typ) and kind in {skVar, skLet, skConst, skParam, skResult}:
-        # only closure iterators may be assigned to anything.
-        result = t
-      let f = if kind in {skProc, skFunc}: flags+{taNoUntyped} else: flags
-      for i in 1..<t.len:
-        if result != nil: break
-        result = typeAllowedAux(marker, t[i], skParam, f-{taIsOpenArray})
-      if result.isNil and t[0] != nil:
-        result = typeAllowedAux(marker, t[0], skResult, flags)
-  of tyTypeDesc:
-    if kind in {skVar, skLet, skConst} and taProcContextIsNotMacro in flags:
-      result = t
-    else:
-      # XXX: This is still a horrible idea...
-      result = nil
-  of tyUntyped, tyTyped:
-    if kind notin {skParam, skResult} or taNoUntyped in flags: result = t
-  of tyStatic:
-    if kind notin {skParam}: result = t
-  of tyVoid:
-    if taField notin flags: result = t
-  of tyTypeClasses:
-    if tfGenericTypeParam in t.flags or taConcept in flags: #or taField notin flags:
-      discard
-    elif t.isResolvedUserTypeClass:
-      result = typeAllowedAux(marker, t.lastSon, kind, flags)
-    elif kind notin {skParam, skResult}:
-      result = t
-  of tyGenericBody, tyGenericParam, tyGenericInvocation,
-     tyNone, tyForward, tyFromExpr:
-    result = t
-  of tyNil:
-    if kind != skConst and kind != skParam: result = t
-  of tyString, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString, tyPointer:
-    result = nil
-  of tyOrdinal:
-    if kind != skParam: result = t
-  of tyGenericInst, tyDistinct, tyAlias, tyInferred:
-    result = typeAllowedAux(marker, lastSon(t), kind, flags)
-  of tyRange:
-    if skipTypes(t[0], abstractInst-{tyTypeDesc}).kind notin
-      {tyChar, tyEnum, tyInt..tyFloat128, tyInt..tyUInt64}: result = t
-  of tyOpenArray, tyVarargs, tySink:
-    # you cannot nest openArrays/sinks/etc.
-    if kind != skParam or taIsOpenArray in flags:
-      result = t
-    else:
-      result = typeAllowedAux(marker, t[0], kind, flags+{taIsOpenArray})
-  of tyUncheckedArray:
-    if kind != skParam and taHeap notin flags:
-      result = t
-    else:
-      result = typeAllowedAux(marker, lastSon(t), kind, flags-{taHeap})
-  of tySequence:
-    if t[0].kind != tyEmpty:
-      result = typeAllowedAux(marker, t[0], kind, flags+{taHeap})
-    elif kind in {skVar, skLet}:
-      result = t[0]
-  of tyArray:
-    if t[1].kind == tyTypeDesc:
-      result = t[1]
-    elif t[1].kind != tyEmpty:
-      result = typeAllowedAux(marker, t[1], kind, flags)
-    elif kind in {skVar, skLet}:
-      result = t[1]
-  of tyRef:
-    if kind == skConst: result = t
-    else: result = typeAllowedAux(marker, t.lastSon, kind, flags+{taHeap})
-  of tyPtr:
-    result = typeAllowedAux(marker, t.lastSon, kind, flags+{taHeap})
-  of tySet:
-    for i in 0..<t.len:
-      result = typeAllowedAux(marker, t[i], kind, flags)
-      if result != nil: break
-  of tyObject, tyTuple:
-    if kind in {skProc, skFunc, skConst} and
-        t.kind == tyObject and t[0] != nil:
-      result = t
-    else:
-      let flags = flags+{taField}
-      for i in 0..<t.len:
-        result = typeAllowedAux(marker, t[i], kind, flags)
-        if result != nil: break
-      if result.isNil and t.n != nil:
-        result = typeAllowedNode(marker, t.n, kind, flags)
-  of tyEmpty:
-    if kind in {skVar, skLet}: result = t
-  of tyProxy:
-    # for now same as error node; we say it's a valid type as it should
-    # prevent cascading errors:
-    result = nil
-  of tyOwned:
-    if t.len == 1 and t[0].skipTypes(abstractInst).kind in {tyRef, tyPtr, tyProc}:
-      result = typeAllowedAux(marker, t.lastSon, kind, flags+{taHeap})
-    else:
-      result = t
-  of tyOptDeprecated: doAssert false
-
-proc typeAllowed*(t: PType, kind: TSymKind; flags: TTypeAllowedFlags = {}): PType =
-  # returns 'nil' on success and otherwise the part of the type that is
-  # wrong!
-  var marker = initIntSet()
-  result = typeAllowedAux(marker, t, kind, flags)
-
 include sizealignoffsetimpl
 
 proc computeSize*(conf: ConfigRef; typ: PType): BiggestInt =
diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim
index 6cf8e1932..756249c8b 100644
--- a/compiler/varpartitions.nim
+++ b/compiler/varpartitions.nim
@@ -17,6 +17,7 @@ import ast, types, lineinfos, options, msgs, renderer
 from trees import getMagic, whichPragma
 from wordrecg import wNoSideEffect
 from isolation_check import canAlias
+from typeallowed import isViewType
 
 type
   SubgraphFlag = enum
@@ -48,7 +49,7 @@ type
     maxMutation, minConnection: int
     mutations: seq[int]
 
-  Partitions = object
+  Partitions* = object
     abstractTime: int
     s: seq[VarIndex]
     graphs: seq[MutationInfo]
@@ -74,7 +75,7 @@ proc `$`*(config: ConfigRef; g: MutationInfo): string =
       result.add config $ g.connectedVia
       result.add " is the statement that connected the mutation to the parameter"
 
-proc hasSideEffect(c: var Partitions; info: var MutationInfo): bool =
+proc hasSideEffect*(c: var Partitions; info: var MutationInfo): bool =
   for g in mitems c.graphs:
     if g.flags == {isMutated, connectsConstParam} and mutationAfterConnection(g):
       info = g
@@ -505,17 +506,16 @@ proc traverse(c: var Partitions; n: PNode) =
   else:
     for child in n: traverse(c, child)
 
-proc mutatesNonVarParameters*(s: PSym; n: PNode; info: var MutationInfo): bool =
-  var par = Partitions(performCursorInference: false)
-  if s.kind != skMacro:
+proc computeGraphPartitions*(s: PSym; n: PNode; cursorInference = false): Partitions =
+  result = Partitions(performCursorInference: cursorInference)
+  if s.kind notin {skModule, skMacro}:
     let params = s.typ.n
     for i in 1..<params.len:
-      registerVariable(par, params[i])
+      registerVariable(result, params[i])
     if resultPos < s.ast.safeLen:
-      registerVariable(par, s.ast[resultPos])
+      registerVariable(result, s.ast[resultPos])
 
-  traverse(par, n)
-  result = hasSideEffect(par, info)
+  traverse(result, n)
 
 proc dangerousMutation(g: MutationInfo; v: VarIndex): bool =
   if isMutated in g.flags:
@@ -524,16 +524,16 @@ proc dangerousMutation(g: MutationInfo; v: VarIndex): bool =
         return true
   return false
 
-proc computeCursors*(s: PSym; n: PNode; config: ConfigRef) =
-  var par = Partitions(performCursorInference: true)
-  if s.kind notin {skMacro, skModule}:
-    let params = s.typ.n
-    for i in 1..<params.len:
-      registerVariable(par, params[i])
-    if resultPos < s.ast.safeLen:
-      registerVariable(par, s.ast[resultPos])
+proc checkBorrowedLocations*(par: var Partitions; config: ConfigRef) =
+  for i in 0 ..< par.s.len:
+    let s = par.s[i].sym
+    if s.kind != skParam and isViewType(s.typ):
+      let rid = root(par, i)
+      if par.s[rid].kind == isRootOf and dangerousMutation(par.graphs[par.s[rid].graphIndex], par.s[i]):
+        localError(config, s.info, config $ par.graphs[par.s[rid].graphIndex])
 
-  traverse(par, n)
+proc computeCursors*(s: PSym; n: PNode; config: ConfigRef) =
+  var par = computeGraphPartitions(s, n, true)
   for i in 0 ..< par.s.len:
     let v = addr(par.s[i])
     if v.flags == {} and v.sym.kind notin {skParam, skResult} and
diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst
index 913b771c2..ac8dedf11 100644
--- a/doc/manual_experimental.rst
+++ b/doc/manual_experimental.rst
@@ -1836,3 +1836,55 @@ For example:
 
 
 The algorithm behind this analysis is currently not documented.
+
+
+View types
+==========
+
+A view type is a type that contains one of the following types:
+
+- ``var T`` (mutable view into ``T``)
+- ``lent T`` (immutable view into ``T``)
+- ``openArray[T]`` (pair of (pointer to array of ``T``, size))
+
+Since version 1.4 Nim allows view types to be used as local variables.
+This feature needs to be enabled via ``{.experimental: "views".}``.
+
+A local variable of a view type *borrows* from the locations and
+it is statically enforced that the view does not outlive the location
+it was borrowed from.
+
+For example:
+
+.. code-block:: nim
+
+  {.experimental: "views".}
+
+  proc take(a: openArray[int]) =
+    echo a.len
+
+  proc main(s: seq[int]) =
+    var x: openArray[int] = s # 'x' is a view into 's'
+    # it is checked that 'x' does not outlive 's' and
+    # that 's' is not mutated.
+    for i in 0 .. high(x):
+      echo x[i]
+    take(x)
+
+    take(x.toOpenArray(0, 1)) # slicing remains possible
+    let y = x  # create a view from a view
+    take y
+    # it is checked that 'y' does not outlive 'x' and
+    # that 'x' is not mutated as long as 'y' lives.
+
+
+  main(@[11, 22, 33])
+
+
+
+If a view type is used as a return type, the location must borrow from the
+first parameter that is passed to the proc.
+See https://nim-lang.org/docs/manual.html#procedures-var-return-type for
+details about how this is done for ``var T``.
+
+The algorithm behind this analysis is currently not documented.
diff --git a/tests/ccgbugs/tviews1.nim b/tests/ccgbugs/tviews1.nim
new file mode 100644
index 000000000..3ce0bb6d8
--- /dev/null
+++ b/tests/ccgbugs/tviews1.nim
@@ -0,0 +1,27 @@
+discard """
+  output: '''11
+22
+33
+3
+2
+3'''
+  targets: "c cpp"
+"""
+
+{.experimental: "views".}
+
+proc take(a: openArray[int]) =
+  echo a.len
+
+proc main(s: seq[int]) =
+  var x: openArray[int] = s
+  for i in 0 .. high(x):
+    echo x[i]
+  take(x)
+
+  take(x.toOpenArray(0, 1))
+  let y = x
+  take y
+
+
+main(@[11, 22, 33])