borrow checking (#15282)

* refactoring: move procs to typeallowed.nim
* frontend preparations for first class openArray support
* prepare the code generator for first class openArray
* code generation for first class openArray; WIP
* code generation for open arrays, progress
* added isViewType proc
* preparations for borrow checking
* added borrow checking to the front end
This commit is contained in:
Andreas Rumpf 2020-09-09 07:32:03 +02:00 • committed by GitHub
commit 10988d4840
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 554 additions and 311 deletions

View file

@ -141,6 +141,13 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
proc genBoundsCheck(p: BProc; arr, a, b: 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 = proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope =
var a: TLoc var a: TLoc
@ -174,7 +181,12 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope =
else: else:
result = "($5*)($1)+(($2)-($4)), ($3)-($2)+1" % result = "($5*)($1)+(($2)-($4)), ($3)-($2)+1" %
[rdLoc(a), rdLoc(b), rdLoc(c), intLiteral(first), dest] [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] result = "($4*)($1)+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), dest]
of tyString, tySequence: of tyString, tySequence:
let atyp = skipTypes(a.t, abstractInst) let atyp = skipTypes(a.t, abstractInst)
@ -188,9 +200,12 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope =
else: else:
internalError(p.config, "openArrayLoc: " & typeToString(a.t)) internalError(p.config, "openArrayLoc: " & typeToString(a.t))
else: else:
initLocExpr(p, n, a) initLocExpr(p, if n.kind == nkHiddenStdConv: n[1] else: n, a)
case skipTypes(a.t, abstractVar).kind case skipTypes(a.t, abstractVar).kind
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
if reifiedOpenArray(n):
result = "$1.d, $1.l" % [rdLoc(a)]
else:
result = "$1, $1Len_0" % [rdLoc(a)] result = "$1, $1Len_0" % [rdLoc(a)]
of tyString, tySequence: of tyString, tySequence:
let ntyp = skipTypes(n.typ, abstractInst) let ntyp = skipTypes(n.typ, abstractInst)

View file

@ -271,6 +271,34 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
linefmt(p, cpsStmts, "#genericAssign((void*)$1, (void*)$2, $3);$n", 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)]) [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) = proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
# This function replaces all other methods for generating # This function replaces all other methods for generating
# the assignment operation in C. # the assignment operation in C.
@ -349,7 +377,9 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
# open arrays are always on the stack - really? What if a sequence is # open arrays are always on the stack - really? What if a sequence is
# passed to an open array? # 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? linefmt(p, cpsStmts, # XXX: is this correct for arrays?
"#genericAssignOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n", "#genericAssignOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n",
[addrLoc(p.config, dest), addrLoc(p.config, src), [addrLoc(p.config, dest), addrLoc(p.config, src),
@ -361,7 +391,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
"$1 = $2;$n", "$1 = $2;$n",
[rdLoc(dest), rdLoc(src)]) [rdLoc(dest), rdLoc(src)])
of tySet: 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", linefmt(p, cpsStmts, "#nimCopyMem((void*)$1, (NIM_CONST void*)$2, $3);$n",
[rdLoc(dest), rdLoc(src), getSize(p.config, dest.t)]) [rdLoc(dest), rdLoc(src), getSize(p.config, dest.t)])
else: else:
@ -406,7 +436,7 @@ proc genDeepCopy(p: BProc; dest, src: TLoc) =
[addrLoc(p.config, dest), addrLocOrTemp(src), [addrLoc(p.config, dest), addrLocOrTemp(src),
genTypeInfo(p.module, dest.t, dest.lode.info)]) genTypeInfo(p.module, dest.t, dest.lode.info)])
of tySet: 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", linefmt(p, cpsStmts, "#nimCopyMem((void*)$1, (NIM_CONST void*)$2, $3);$n",
[rdLoc(dest), rdLoc(src), getSize(p.config, dest.t)]) [rdLoc(dest), rdLoc(src), getSize(p.config, dest.t)])
else: else:
@ -679,7 +709,7 @@ proc isCppRef(p: BProc; typ: PType): bool {.inline.} =
tfVarIsPtr notin skipTypes(typ, abstractInstOwned).flags tfVarIsPtr notin skipTypes(typ, abstractInstOwned).flags
proc genDeref(p: BProc, e: PNode, d: var TLoc) = 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: if mt in {ctArray, ctPtrToArray} and lfEnforceDeref notin d.flags:
# XXX the amount of hacks for C's arrays is incredible, maybe we should # XXX the amount of hacks for C's arrays is incredible, maybe we should
# simply wrap them in a struct? --> Losing auto vectorization then? # 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) initLocExpr(p, e[0], a)
putIntoDest(p, d, e, "&" & a.r, a.storage) putIntoDest(p, d, e, "&" & a.r, a.storage)
#Message(e.info, warnUser, "HERE NEW &") #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) expr(p, e[0], d)
else: else:
var a: TLoc var a: TLoc
@ -905,6 +935,12 @@ proc genBoundsCheck(p: BProc; arr, a, b: TLoc) =
let ty = skipTypes(arr.t, abstractVarRange) let ty = skipTypes(arr.t, abstractVarRange)
case ty.kind case ty.kind
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
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, linefmt(p, cpsStmts,
"if ($2-$1 != -1 && " & "if ($2-$1 != -1 && " &
"((NU)($1) >= (NU)($3Len_0) || (NU)($2) >= (NU)($3Len_0))){ #raiseIndexError(); $4}$n", "((NU)($1) >= (NU)($3Len_0) || (NU)($2) >= (NU)($3Len_0))){ #raiseIndexError(); $4}$n",
@ -925,13 +961,22 @@ proc genBoundsCheck(p: BProc; arr, a, b: TLoc) =
proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
var a, b: TLoc var a, b: TLoc
initLocExpr(p, x, a) initLocExpr(p, x, a)
initLocExpr(p, y, b) # emit range check: initLocExpr(p, y, b)
if not reifiedOpenArray(x):
# emit range check:
if optBoundsCheck in p.options: if optBoundsCheck in p.options:
linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)){ #raiseIndexError2($1,$2Len_0-1); $3}$n", 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 ``>``! [rdLoc(b), rdLoc(a), raiseInstr(p)]) # BUGFIX: ``>=`` and not ``>``!
inheritLocation(d, a) inheritLocation(d, a)
putIntoDest(p, d, n, putIntoDest(p, d, n,
ropecg(p.module, "$1[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage) 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) = proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
var a, b: TLoc var a, b: TLoc
@ -1675,8 +1720,12 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
else: else:
putIntoDest(p, d, e, ropecg(p.module, "($2)-($1)+1", [rdLoc(b), rdLoc(c)])) putIntoDest(p, d, e, ropecg(p.module, "($2)-($1)+1", [rdLoc(b), rdLoc(c)]))
else: else:
if not reifiedOpenArray(a):
if op == mHigh: unaryExpr(p, e, d, "($1Len_0-1)") if op == mHigh: unaryExpr(p, e, d, "($1Len_0-1)")
else: unaryExpr(p, e, d, "$1Len_0") 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: of tyCString:
if op == mHigh: unaryExpr(p, e, d, "($1 ? (#nimCStrLen($1)-1) : -1)") if op == mHigh: unaryExpr(p, e, d, "($1 ? (#nimCStrLen($1)-1) : -1)")
else: unaryExpr(p, e, d, "($1 ? #nimCStrLen($1) : 0)") else: unaryExpr(p, e, d, "($1 ? #nimCStrLen($1) : 0)")
@ -2253,7 +2302,6 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
putIntoDest(p, d, e, "((NI)NIM_ALIGNOF($1))" % [getTypeDesc(p.module, t)]) putIntoDest(p, d, e, "((NI)NIM_ALIGNOF($1))" % [getTypeDesc(p.module, t)])
of mOffsetOf: of mOffsetOf:
var dotExpr: PNode var dotExpr: PNode
block findDotExpr:
if e[1].kind == nkDotExpr: if e[1].kind == nkDotExpr:
dotExpr = e[1] dotExpr = e[1]
elif e[1].kind == nkCheckedFieldExpr: elif e[1].kind == nkCheckedFieldExpr:
@ -2813,8 +2861,10 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope =
result.add getDefaultValue(p, t.sons[1], info) result.add getDefaultValue(p, t.sons[1], info)
result.add "}" result.add "}"
#result = rope"{}" #result = rope"{}"
of tyOpenArray, tyVarargs:
result = rope"{NIM_NIL, 0}"
of tySet: of tySet:
if mapType(p.config, t) == ctArray: result = rope"{}" if mapSetType(p.config, t) == ctArray: result = rope"{}"
else: result = rope"0" else: result = rope"0"
else: else:
globalError(p.config, info, "cannot create null element for: " & $t.kind) globalError(p.config, info, "cannot create null element for: " & $t.kind)

View file

@ -1556,7 +1556,7 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
let le = e[0] let le = e[0]
let ri = e[1] let ri = e[1]
var a: TLoc 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) initLoc(a, locNone, le, OnUnknown)
a.flags.incl(lfEnforceDeref) a.flags.incl(lfEnforceDeref)
a.flags.incl(lfPrepareForMutation) a.flags.incl(lfPrepareForMutation)

View file

@ -149,7 +149,7 @@ proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind =
of 8: result = ctInt64 of 8: result = ctInt64
else: result = ctArray 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 ## Maps a Nim type to a C type
case typ.kind case typ.kind
of tyNone, tyTyped: result = ctVoid of tyNone, tyTyped: result = ctVoid
@ -157,14 +157,17 @@ proc mapType(conf: ConfigRef; typ: PType): TCTypeKind =
of tyChar: result = ctChar of tyChar: result = ctChar
of tyNil: result = ctPtr of tyNil: result = ctPtr
of tySet: result = mapSetType(conf, typ) 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 tyObject, tyTuple: result = ctStruct
of tyUserTypeClasses: of tyUserTypeClasses:
doAssert typ.isResolvedUserTypeClass doAssert typ.isResolvedUserTypeClass
return mapType(conf, typ.lastSon) return mapType(conf, typ.lastSon, kind)
of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal, of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal,
tyTypeDesc, tyAlias, tySink, tyInferred, tyOwned: tyTypeDesc, tyAlias, tySink, tyInferred, tyOwned:
result = mapType(conf, lastSon(typ)) result = mapType(conf, lastSon(typ), kind)
of tyEnum: of tyEnum:
if firstOrd(conf, typ) < 0: if firstOrd(conf, typ) < 0:
result = ctInt32 result = ctInt32
@ -175,7 +178,7 @@ proc mapType(conf: ConfigRef; typ: PType): TCTypeKind =
of 4: result = ctInt32 of 4: result = ctInt32
of 8: result = ctInt64 of 8: result = ctInt64
else: result = ctInt32 else: result = ctInt32
of tyRange: result = mapType(conf, typ[0]) of tyRange: result = mapType(conf, typ[0], kind)
of tyPtr, tyVar, tyLent, tyRef: of tyPtr, tyVar, tyLent, tyRef:
var base = skipTypes(typ.lastSon, typedescInst) var base = skipTypes(typ.lastSon, typedescInst)
case base.kind case base.kind
@ -192,14 +195,14 @@ proc mapType(conf: ConfigRef; typ: PType): TCTypeKind =
of tyInt..tyUInt64: of tyInt..tyUInt64:
result = TCTypeKind(ord(typ.kind) - ord(tyInt) + ord(ctInt)) result = TCTypeKind(ord(typ.kind) - ord(tyInt) + ord(ctInt))
of tyStatic: 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")
else: doAssert(false, "mapType") else: doAssert(false, "mapType")
proc mapReturnType(conf: ConfigRef; typ: PType): TCTypeKind = proc mapReturnType(conf: ConfigRef; typ: PType): TCTypeKind =
#if skipTypes(typ, typedescInst).kind == tyArray: result = ctPtr #if skipTypes(typ, typedescInst).kind == tyArray: result = ctPtr
#else: #else:
result = mapType(conf, typ) result = mapType(conf, typ, skResult)
proc isImportedType(t: PType): bool = proc isImportedType(t: PType): bool =
result = t.sym != nil and sfImportc in t.sym.flags 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 result = (t.sym != nil and sfInfixCall in t.sym.flags) or
(x.sym != nil and sfInfixCall in x.sym.flags) (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.} = proc isObjLackingTypeField(typ: PType): bool {.inline.} =
result = (typ.kind == tyObject) and ((tfFinal in typ.flags) and 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. # is necessary for proper code generation of assignments.
if rettype == nil: result = true if rettype == nil: result = true
else: else:
case mapType(conf, rettype) case mapType(conf, rettype, skResult)
of ctArray: of ctArray:
result = not (skipTypes(rettype, typedescInst).kind in result = not (skipTypes(rettype, typedescInst).kind in
{tyVar, tyLent, tyRef, tyPtr}) {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) if result == nil: result = cacheGetType(m.typeCache, sig)
proc structOrUnion(t: PType): Rope = proc structOrUnion(t: PType): Rope =
let cachedUnion {.global.} = rope("union") let cachedUnion = rope("union")
let cachedStruct {.global.} = rope("struct") let cachedStruct = rope("struct")
let t = t.skipTypes({tyAlias, tySink}) let t = t.skipTypes({tyAlias, tySink})
if tfUnion in t.flags: cachedUnion if tfUnion in t.flags: cachedUnion
else: cachedStruct else: cachedStruct
@ -379,7 +382,7 @@ proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope =
doAssert m.forwTypeCache[sig] == result doAssert m.forwTypeCache[sig] == result
else: internalError(m.config, "getTypeForward(" & $typ.kind & ')') 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 ## 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 ## we know we only need a pointer to it so we only generate a struct forward
## declaration: ## declaration:
@ -387,7 +390,7 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope =
case etB.kind case etB.kind
of tyObject, tyTuple: of tyObject, tyTuple:
if isImportedCppType(etB) and t.kind == tyGenericInst: if isImportedCppType(etB) and t.kind == tyGenericInst:
result = getTypeDescAux(m, t, check) result = getTypeDescAux(m, t, check, kind)
else: else:
result = getTypeForward(m, t, hashType(t)) result = getTypeForward(m, t, hashType(t))
pushType(m, 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) result = getTypeForward(m, t, sig) & seqStar(m)
pushType(m, t) pushType(m, t)
else: else:
result = getTypeDescAux(m, t, check) result = getTypeDescAux(m, t, check, kind)
proc getSeqPayloadType(m: BModule; t: PType): Rope = proc getSeqPayloadType(m: BModule; t: PType): Rope =
var check = initIntSet() var check = initIntSet()
result = getTypeDescWeak(m, t, check) & "_Content" result = getTypeDescWeak(m, t, check, skParam) & "_Content"
#result = getTypeForward(m, t, hashType(t)) & "_Content" #result = getTypeForward(m, t, hashType(t)) & "_Content"
proc seqV2ContentType(m: BModule; t: PType; check: var IntSet) = proc seqV2ContentType(m: BModule; t: PType; check: var IntSet) =
let sig = hashType(t) let sig = hashType(t)
let result = cacheGetType(m.typeCache, sig) let result = cacheGetType(m.typeCache, sig)
if result == nil: if result == nil:
discard getTypeDescAux(m, t, check) discard getTypeDescAux(m, t, check, skVar)
else: else:
# little hack for now to prevent multiple definitions of the same # little hack for now to prevent multiple definitions of the same
# Seq_Content: # Seq_Content:
@ -437,7 +440,7 @@ $3ifndef $2_Content_PP
$3define $2_Content_PP $3define $2_Content_PP
struct $2_Content { NI cap; $1 data[SEQ_DECL_SIZE];}; struct $2_Content { NI cap; $1 data[SEQ_DECL_SIZE];};
$3endif$N $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 = proc paramStorageLoc(param: PSym): TStorageLoc =
if param.typ.skipTypes({tyVar, tyLent, tyTypeDesc}).kind notin { 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]): if t[0] == nil or isInvalidReturnType(m.config, t[0]):
rettype = ~"void" rettype = ~"void"
else: else:
rettype = getTypeDescAux(m, t[0], check) rettype = getTypeDescAux(m, t[0], check, skResult)
for i in 1..<t.n.len: for i in 1..<t.n.len:
if t.n[i].kind != nkSym: internalError(m.config, t.n.info, "genProcParams") if t.n[i].kind != nkSym: internalError(m.config, t.n.info, "genProcParams")
var param = t.n[i].sym 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), fillLoc(param.loc, locParam, t.n[i], mangleParamName(m, param),
param.paramStorageLoc) param.paramStorageLoc)
if ccgIntroducedPtr(m.config, param, t[0]): if ccgIntroducedPtr(m.config, param, t[0]):
params.add(getTypeDescWeak(m, param.typ, check)) params.add(getTypeDescWeak(m, param.typ, check, skParam))
params.add(~"*") params.add(~"*")
incl(param.loc.flags, lfIndirect) incl(param.loc.flags, lfIndirect)
param.loc.storage = OnUnknown param.loc.storage = OnUnknown
elif weakDep: elif weakDep:
params.add(getTypeDescWeak(m, param.typ, check)) params.add(getTypeDescWeak(m, param.typ, check, skParam))
else: else:
params.add(getTypeDescAux(m, param.typ, check)) params.add(getTypeDescAux(m, param.typ, check, skParam))
params.add(~" ") params.add(~" ")
params.add(param.loc.r) params.add(param.loc.r)
# declare the len field for open arrays: # 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] var arr = t[0]
if params != nil: params.add(", ") if params != nil: params.add(", ")
if mapReturnType(m.config, t[0]) != ctArray: if mapReturnType(m.config, t[0]) != ctArray:
params.add(getTypeDescWeak(m, arr, check)) params.add(getTypeDescWeak(m, arr, check, skResult))
params.add("*") params.add("*")
else: else:
params.add(getTypeDescAux(m, arr, check)) params.add(getTypeDescAux(m, arr, check, skResult))
params.addf(" Result", []) params.addf(" Result", [])
if t.callConv == ccClosure and declareEnvironment: if t.callConv == ccClosure and declareEnvironment:
if params != nil: params.add(", ") if params != nil: params.add(", ")
@ -562,16 +565,16 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
let fieldType = field.loc.lode.typ.skipTypes(abstractInst) let fieldType = field.loc.lode.typ.skipTypes(abstractInst)
if fieldType.kind == tyUncheckedArray: if fieldType.kind == tyUncheckedArray:
result.addf("$1 $2[SEQ_DECL_SIZE];$n", 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: elif fieldType.kind == tySequence:
# we need to use a weak dependency here for trecursive_table. # 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: 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: else:
# don't use fieldType here because we need the # don't use fieldType here because we need the
# tyGenericInst for C++ template support # 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()") else: internalError(m.config, n.info, "genRecordFieldsAux()")
proc getRecordFields(m: BModule, typ: PType, check: var IntSet): Rope = proc getRecordFields(m: BModule, typ: PType, check: var IntSet): Rope =
@ -610,7 +613,7 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope,
hasField = true hasField = true
elif m.compileToCpp: elif m.compileToCpp:
appcg(m, result, " : public $1 {$n", 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: if typ.isException and m.config.exc == excCpp:
when false: when false:
appcg(m, result, "virtual void raise() { throw *this; }$n", []) # required for polymorphic exceptions 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 hasField = true
else: else:
appcg(m, result, " {$n $1 Sup;$n", appcg(m, result, " {$n $1 Sup;$n",
[getTypeDescAux(m, typ[0].skipTypes(skipPtrs), check)]) [getTypeDescAux(m, typ[0].skipTypes(skipPtrs), check, skField)])
hasField = true hasField = true
else: else:
result.addf(" {$n", [name]) result.addf(" {$n", [name])
@ -643,7 +646,7 @@ proc getTupleDesc(m: BModule, typ: PType, name: Rope,
var desc: Rope = nil var desc: Rope = nil
for i in 0..<typ.len: for i in 0..<typ.len:
desc.addf("$1 Field$2;$n", 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") if desc == nil: result.add("char dummy;\L")
else: result.add(desc) else: result.add(desc)
result.add("};\L") result.add("};\L")
@ -677,7 +680,20 @@ proc resolveStarsInCppType(typ: PType, idx, stars: int): PType =
result = if result.kind == tyGenericInst: result[1] result = if result.kind == tyGenericInst: result[1]
else: result.elemType 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 # returns only the type's name
var t = origTyp.skipTypes(irrelevantForBackend-{tyOwned}) var t = origTyp.skipTypes(irrelevantForBackend-{tyOwned})
@ -696,7 +712,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
addAbiCheck(m, t, result) addAbiCheck(m, t, result)
result = getTypePre(m, t, sig) result = getTypePre(m, t, sig)
if result != nil: if result != nil and t.kind != tyOpenArray:
excl(check, t.id) excl(check, t.id)
return return
case t.kind case t.kind
@ -705,7 +721,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
compileToCpp(m): "&" else: "*" compileToCpp(m): "&" else: "*"
var et = origTyp.skipTypes(abstractInst).lastSon var et = origTyp.skipTypes(abstractInst).lastSon
var etB = et.skipTypes(abstractInst) var etB = et.skipTypes(abstractInst)
if mapType(m.config, t) == ctPtrToArray: if mapType(m.config, t, kind) == ctPtrToArray:
if etB.kind == tySet: if etB.kind == tySet:
et = getSysType(m.g.graph, unknownLineInfo, tyUInt8) et = getSysType(m.g.graph, unknownLineInfo, tyUInt8)
else: else:
@ -715,7 +731,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
case etB.kind case etB.kind
of tyObject, tyTuple: of tyObject, tyTuple:
if isImportedCppType(etB) and et.kind == tyGenericInst: if isImportedCppType(etB) and et.kind == tyGenericInst:
result = getTypeDescAux(m, et, check) & star result = getTypeDescAux(m, et, check, kind) & star
else: else:
# no restriction! We have a forward declaration for structs # no restriction! We have a forward declaration for structs
let name = getTypeForward(m, et, hashType et) 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 m.typeCache[sig] = result
of tySequence: of tySequence:
if optSeqDestructors in m.config.globalOptions: if optSeqDestructors in m.config.globalOptions:
result = getTypeDescWeak(m, et, check) & star result = getTypeDescWeak(m, et, check, kind) & star
m.typeCache[sig] = result m.typeCache[sig] = result
else: else:
# no restriction! We have a forward declaration for structs # 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) pushType(m, et)
else: else:
# else we have a strong dependency :-( # else we have a strong dependency :-(
result = getTypeDescAux(m, et, check) & star result = getTypeDescAux(m, et, check, kind) & star
m.typeCache[sig] = result m.typeCache[sig] = result
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
result = getTypeDescWeak(m, t[0], check) & "*" result = getOpenArrayDesc(m, t, check, kind)
m.typeCache[sig] = result
of tyEnum: of tyEnum:
result = cacheGetType(m.typeCache, sig) result = cacheGetType(m.typeCache, sig)
if result == nil: if result == nil:
@ -783,7 +798,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
[result, rettype, desc]) [result, rettype, desc])
of tySequence: of tySequence:
if optSeqDestructors in m.config.globalOptions: if optSeqDestructors in m.config.globalOptions:
result = getTypeDescWeak(m, t, check) result = getTypeDescWeak(m, t, check, kind)
else: else:
# we cannot use getTypeForward here because then t would be associated # we cannot use getTypeForward here because then t would be associated
# with the name of the struct, not with the pointer to the struct: # 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: if m.compileToCpp:
appcg(m, m.s[cfsSeqTypes], appcg(m, m.s[cfsSeqTypes],
cppSeq & " $1 data[SEQ_DECL_SIZE];$n" & cppSeq & " $1 data[SEQ_DECL_SIZE];$n" &
"};$n", [getTypeDescAux(m, t[0], check), result]) "};$n", [getTypeDescAux(m, t[0], check, kind), result])
else: else:
appcg(m, m.s[cfsSeqTypes], appcg(m, m.s[cfsSeqTypes],
cSeq & " $1 data[SEQ_DECL_SIZE];$n" & cSeq & " $1 data[SEQ_DECL_SIZE];$n" &
"};$n", [getTypeDescAux(m, t[0], check), result]) "};$n", [getTypeDescAux(m, t[0], check, kind), result])
else: else:
result = rope("TGenericSeq") result = rope("TGenericSeq")
result.add(seqStar(m)) result.add(seqStar(m))
@ -816,7 +831,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
result = getTypeName(m, origTyp, sig) result = getTypeName(m, origTyp, sig)
m.typeCache[sig] = result m.typeCache[sig] = result
if not isImportedType(t): 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]) m.s[cfsTypes].addf("typedef $1 $2[1];$n", [foo, result])
of tyArray: of tyArray:
var n: BiggestInt = toInt64(lengthOrd(m.config, t)) 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) result = getTypeName(m, origTyp, sig)
m.typeCache[sig] = result m.typeCache[sig] = result
if not isImportedType(t): 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", m.s[cfsTypes].addf("typedef $1 $2[$3];$n",
[foo, result, rope(n)]) [foo, result, rope(n)])
of tyObject, tyTuple: of tyObject, tyTuple:
@ -840,7 +855,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
internalAssert m.config, ty.n != nil internalAssert m.config, ty.n != nil
result.add ty.n.renderTree result.add ty.n.renderTree
else: else:
result.add getTypeDescAux(m, ty, check) result.add getTypeDescAux(m, ty, check, kind)
while i < cppName.data.len: while i < cppName.data.len:
if cppName.data[i] == '\'': if cppName.data[i] == '\'':
@ -901,16 +916,16 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
[result, rope(getSize(m.config, t))]) [result, rope(getSize(m.config, t))])
of tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, tySink, tyOwned, of tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, tySink, tyOwned,
tyUserTypeClass, tyUserTypeClassInst, tyInferred: tyUserTypeClass, tyUserTypeClassInst, tyInferred:
result = getTypeDescAux(m, lastSon(t), check) result = getTypeDescAux(m, lastSon(t), check, kind)
else: else:
internalError(m.config, "getTypeDescAux(" & $t.kind & ')') internalError(m.config, "getTypeDescAux(" & $t.kind & ')')
result = nil result = nil
# fixes bug #145: # fixes bug #145:
excl(check, t.id) excl(check, t.id)
proc getTypeDesc(m: BModule, typ: PType): Rope = proc getTypeDesc(m: BModule, typ: PType; kind = skParam): Rope =
var check = initIntSet() var check = initIntSet()
result = getTypeDescAux(m, typ, check) result = getTypeDescAux(m, typ, check, kind)
type type
TClosureTypeKind = enum ## In C closures are mapped to 3 different things. 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: if optSeqDestructors in m.config.globalOptions and t.skipTypes(abstractInst).kind == tySequence:
seqV2ContentType(m, t, check) seqV2ContentType(m, t, check)
else: else:
discard getTypeDescAux(m, t, check) discard getTypeDescAux(m, t, check, skParam)
inc(i) inc(i)
m.typeStack.setLen 0 m.typeStack.setLen 0

View file

@ -310,14 +310,19 @@ include ccgtypes
# ------------------------------ Manager of temporaries ------------------ # ------------------------------ 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 = proc addrLoc(conf: ConfigRef; a: TLoc): Rope =
result = a.r 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 & ")" result = "(&" & result & ")"
proc byRefLoc(p: BProc; a: TLoc): Rope = proc byRefLoc(p: BProc; a: TLoc): Rope =
result = a.r 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: p.module.compileToCpp:
result = "(&" & result & ")" 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) rawConstExpr(p, newNodeIT(nkType, a.lode.info, objType), tmp)
linefmt(p, cpsStmts, linefmt(p, cpsStmts,
"#nimCopyMem((void*)$1, (NIM_CONST void*)&$2, sizeof($3));$n", "#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: else:
rawConstExpr(p, newNodeIT(nkType, a.lode.info, t), tmp) rawConstExpr(p, newNodeIT(nkType, a.lode.info, t), tmp)
genAssignment(p, a, tmp, {}) genAssignment(p, a, tmp, {})
@ -387,7 +392,7 @@ proc genRefAssign(p: BProc, dest, src: TLoc)
proc isComplexValueType(t: PType): bool {.inline.} = proc isComplexValueType(t: PType): bool {.inline.} =
let t = t.skipTypes(abstractInst + tyUserTypeClasses) 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) (t.kind == tyProc and t.callConv == ccClosure)
include ccgreset include ccgreset
@ -420,7 +425,8 @@ proc resetLoc(p: BProc, loc: var TLoc) =
# array passed as argument decayed into pointer, bug #7332 # array passed as argument decayed into pointer, bug #7332
# so we use getTypeDesc here rather than rdLoc(loc) # so we use getTypeDesc here rather than rdLoc(loc)
linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n", 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 # XXX: We can be extra clever here and call memset only
# on the bytes following the m_type field? # on the bytes following the m_type field?
genObjectInit(p, cpsStmts, loc.t, loc, constructObj) 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)]) linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
elif not isComplexValueType(typ): elif not isComplexValueType(typ):
linefmt(p, cpsStmts, "$1 = ($2)0;$n", [rdLoc(loc), linefmt(p, cpsStmts, "$1 = ($2)0;$n", [rdLoc(loc),
getTypeDesc(p.module, typ)]) getTypeDesc(p.module, typ, mapTypeChooser(loc))])
else: else:
if not isTemp or containsGarbageCollectedRef(loc.t): if not isTemp or containsGarbageCollectedRef(loc.t):
# don't use nimZeroMem for temporary values for performance if we can # don't use nimZeroMem for temporary values for performance if we can
# avoid it: # avoid it:
if not isImportedCppType(typ): if not isImportedCppType(typ):
linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n", 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) genObjectInit(p, cpsStmts, loc.t, loc, constructObj)
proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) = 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) = proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) =
inc(p.labels) inc(p.labels)
result.r = "T" & rope(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.k = locTemp
result.lode = lodeTyp t result.lode = lodeTyp t
result.storage = OnStack 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) = proc getTempCpp(p: BProc, t: PType, result: var TLoc; value: Rope) =
inc(p.labels) inc(p.labels)
result.r = "T" & rope(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.k = locTemp
result.lode = lodeTyp t result.lode = lodeTyp t
result.storage = OnStack 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 == skLet: incl(s.loc.flags, lfNoDeepCopy)
if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0: if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0:
result.addf("NIM_ALIGN($1) ", [rope(s.alignment)]) 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 s.constraint.isNil:
if sfRegister in s.flags: result.add(" register") if sfRegister in s.flags: result.add(" register")
#elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds: #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") internalError(p.config, n.info, ".threadvar variables cannot have a value")
else: else:
var decl: Rope = nil 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.constraint.isNil:
if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0: if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0:
decl.addf "NIM_ALIGN($1) ", [rope(s.alignment)] decl.addf "NIM_ALIGN($1) ", [rope(s.alignment)]
@ -687,7 +693,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) =
initLoc(dest, locTemp, lib.path, OnStack) initLoc(dest, locTemp, lib.path, OnStack)
dest.r = getTempName(m) dest.r = getTempName(m)
appcg(m, m.s[cfsDynLibInit],"$1 $2;$n", 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) expr(p, lib.path, dest)
m.s[cfsVars].add(p.s(cpsLocals)) m.s[cfsVars].add(p.s(cpsLocals))
@ -727,7 +733,7 @@ proc symInDynamicLib(m: BModule, sym: PSym) =
params.add(rdLoc(a)) params.add(rdLoc(a))
params.add(", ") params.add(", ")
let load = "\t$1 = ($2) ($3$4));$n" % 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) var last = lastSon(n)
if last.kind == nkHiddenStdConv: last = last[1] if last.kind == nkHiddenStdConv: last = last[1]
internalAssert(m.config, last.kind == nkStrLit) internalAssert(m.config, last.kind == nkStrLit)
@ -741,8 +747,8 @@ proc symInDynamicLib(m: BModule, sym: PSym) =
else: else:
appcg(m, m.s[cfsDynLibInit], appcg(m, m.s[cfsDynLibInit],
"\t$1 = ($2) #nimGetProcAddr($3, $4);$n", "\t$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)]) m.s[cfsVars].addf("$2 $1;$n", [sym.loc.r, getTypeDesc(m, sym.loc.t, skVar)])
proc varInDynamicLib(m: BModule, sym: PSym) = proc varInDynamicLib(m: BModule, sym: PSym) =
var lib = sym.annex var lib = sym.annex
@ -754,9 +760,9 @@ proc varInDynamicLib(m: BModule, sym: PSym) =
inc(m.labels, 2) inc(m.labels, 2)
appcg(m, m.s[cfsDynLibInit], appcg(m, m.s[cfsDynLibInit],
"$1 = ($2*) #nimGetProcAddr($3, $4);$n", "$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", 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) = proc symInDynamicLibPartial(m: BModule, sym: PSym) =
sym.loc.r = mangleDynLibProc(sym) 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)]) [getTypeDesc(q, sym.typ), actualConstName, genBracedInit(q.initProc, sym.ast, isConst = true)])
if m.hcrOn: if m.hcrOn:
# generate the global pointer with the real name # 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) # register it (but ignore the boolean result of hcrRegisterGlobal)
q.initProc.procSec(cpsLocals).addf( q.initProc.procSec(cpsLocals).addf(
"\thcrRegisterGlobal($1, \"$2\", sizeof($3), NULL, (void**)&$2);$n", "\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): if q != m and not containsOrIncl(m.declaredThings, sym.id):
assert(sym.loc.r != nil) assert(sym.loc.r != nil)
if m.hcrOn: 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( m.initProc.procSec(cpsLocals).addf(
"\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r, "\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: else:
let headerDecl = "extern NIM_CONST $1 $2;$n" % 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) m.s[cfsData].add(headerDecl)
if sfExportc in sym.flags and p.module.g.generatedHeader != nil: if sfExportc in sym.flags and p.module.g.generatedHeader != nil:
p.module.g.generatedHeader.s[cfsData].add(headerDecl) 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: 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].addf "NIM_ALIGN($1) ", [rope(sym.alignment)]
m.s[cfsVars].add(if m.hcrOn: "static " else: "extern ") 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 m.hcrOn: m.s[cfsVars].add("*")
if lfDynamicLib in sym.loc.flags: m.s[cfsVars].add("*") if lfDynamicLib in sym.loc.flags: m.s[cfsVars].add("*")
if sfRegister in sym.flags: m.s[cfsVars].add(" register") 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]) m.s[cfsVars].addf(" $1;$n", [sym.loc.r])
if m.hcrOn: m.initProc.procSec(cpsLocals).addf( if m.hcrOn: m.initProc.procSec(cpsLocals).addf(
"\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r, "\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.} = proc addNimDefines(result: var Rope; conf: ConfigRef) {.inline.} =
result.addf("#define NIM_INTBITS $1\L", [ 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 prc.typ.sym = nil
if not containsOrIncl(m.declaredThings, prc.id): 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" % 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) = proc genInitCode(m: BModule) =
## this function is called in cgenWriteModules after all modules are closed, ## this function is called in cgenWriteModules after all modules are closed,

View file

@ -162,7 +162,8 @@ type
## which itself requires `nimble install libffi`, see #10150 ## which itself requires `nimble install libffi`, see #10150
## Note: this feature can't be localized with {.push.} ## Note: this feature can't be localized with {.push.}
vmopsDanger, vmopsDanger,
strictFuncs strictFuncs,
views
LegacyFeature* = enum LegacyFeature* = enum
allowSemcheckedAstModification, allowSemcheckedAstModification,

View file

@ -1035,6 +1035,13 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
gsub(g, n, 1) gsub(g, n, 1)
of nkHiddenStdConv, nkHiddenSubConv: of nkHiddenStdConv, nkHiddenSubConv:
if n.len >= 2: if n.len >= 2:
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]) gsub(g, n[1])
else: else:
put(g, tkSymbol, "(wrong conv)") put(g, tkSymbol, "(wrong conv)")

View file

@ -17,7 +17,7 @@ import
intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting, intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting,
evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity, evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity,
lowerings, plugins/active, rod, lineinfos, strtabs, int128, lowerings, plugins/active, rod, lineinfos, strtabs, int128,
isolation_check isolation_check, typeallowed
from modulegraphs import ModuleGraph, PPassContext, onUse, onDef, onDefResolveForward 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, proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
allowed: TSymFlags): PSym 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 = {}) = flags: TTypeAllowedFlags = {}) =
let t = typeAllowed(typ, kind, flags) let t = typeAllowed(typ, kind, c, flags)
if t != nil: if t != nil:
var err: string var err: string
if t == typ: if t == typ:
@ -240,10 +240,10 @@ proc typeAllowedCheck(conf: ConfigRef; info: TLineInfo; typ: PType; kind: TSymKi
else: else:
err = "invalid type: '$1' in this context: '$2' for $3" % [typeToString(t), err = "invalid type: '$1' in this context: '$2' for $3" % [typeToString(t),
typeToString(typ), toHumanStr(kind)] typeToString(typ), toHumanStr(kind)]
localError(conf, info, err) localError(c.config, info, err)
proc paramsTypeCheck(c: PContext, typ: PType) {.inline.} = 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 expectMacroOrTemplateCall(c: PContext, n: PNode): PSym
proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode

View file

@ -178,7 +178,7 @@ proc checkConvertible(c: PContext, targetTyp: PType, src: PNode): TConvStatus =
else: else:
discard 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. ## Checks whether the source type can be cast to the destination type.
## Casting is very unrestrictive; casts are allowed as long as ## Casting is very unrestrictive; casts are allowed as long as
## castDest.size >= src.size, and typeAllowed(dst, skParam) ## castDest.size >= src.size, and typeAllowed(dst, skParam)
@ -193,6 +193,7 @@ proc isCastable(conf: ConfigRef; dst, src: PType): bool =
return false return false
if skipTypes(dst, abstractInst).kind == tyBuiltInTypeClass: if skipTypes(dst, abstractInst).kind == tyBuiltInTypeClass:
return false return false
let conf = c.config
if conf.selectedGC in {gcArc, gcOrc}: if conf.selectedGC in {gcArc, gcOrc}:
let d = skipTypes(dst, abstractInst) let d = skipTypes(dst, abstractInst)
let s = skipTypes(src, abstractInst) let s = skipTypes(src, abstractInst)
@ -210,7 +211,7 @@ proc isCastable(conf: ConfigRef; dst, src: PType): bool =
result = false result = false
elif srcSize < 0: elif srcSize < 0:
result = false result = false
elif typeAllowed(dst, skParam) != nil: elif typeAllowed(dst, skParam, c) != nil:
result = false result = false
elif dst.kind == tyProc and dst.callConv == ccClosure: elif dst.kind == tyProc and dst.callConv == ccClosure:
result = src.kind == tyProc and src.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]) let castedExpr = semExprWithType(c, n[1])
if tfHasMeta in targetType.flags: if tfHasMeta in targetType.flags:
localError(c.config, n[0].info, "cannot cast to a non concrete type: '$1'" % $targetType) 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 tar = $targetType
let alt = typeToString(targetType, preferDesc) let alt = typeToString(targetType, preferDesc)
let msg = if tar != alt: tar & "=" & alt else: tar 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: if callee.kind notin {skProc, skFunc, skConverter, skConst} or callee.isGenericRoutine:
return 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) var call = newNodeIT(nkCall, n.info, n.typ)
call.add(n[0]) call.add(n[0])
@ -1600,7 +1601,7 @@ proc takeImplicitAddr(c: PContext, n: PNode; isLent: bool): PNode =
proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} = proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} =
if le.kind == nkHiddenDeref: if le.kind == nkHiddenDeref:
var x = le[0] 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[0] = x # 'result[]' --> 'result'
n[1] = takeImplicitAddr(c, ri, x.typ.kind == tyLent) n[1] = takeImplicitAddr(c, ri, x.typ.kind == tyLent)
x.typ.flags.incl tfVarIsPtr 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}: if cmpTypes(c, lhs.typ, rhsTyp) in {isGeneric, isEqual}:
internalAssert c.config, c.p.resultSym != nil internalAssert c.config, c.p.resultSym != nil
# Make sure the type is valid for the result variable # 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 lhs.typ = rhsTyp
c.p.resultSym.typ = rhsTyp c.p.resultSym.typ = rhsTyp
c.p.owner.typ[0] = 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) tupleConstr[i] = takeImplicitAddr(c, tupleConstr[i], e.kind == tyLent)
else: else:
localError(c.config, n[0].info, errXExpected, "tuple constructor") 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 = proc semYield(c: PContext, n: PNode): PNode =
result = n result = n

View file

@ -1243,8 +1243,11 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
effects[ensuresEffects] = ensuresSpec effects[ensuresEffects] = ensuresSpec
var mutationInfo = MutationInfo() var mutationInfo = MutationInfo()
if strictFuncs in c.features and not t.hasSideEffect and t.hasDangerousAssign: if {strictFuncs, views} * c.features != {}:
t.hasSideEffect = mutatesNonVarParameters(s, body, mutationInfo) 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 sfThread in s.flags and t.gcUnsafe:
if optThreads in g.config.globalOptions and optThreadAnalysis in g.config.globalOptions: if optThreads in g.config.globalOptions and optThreadAnalysis in g.config.globalOptions:

View file

@ -534,7 +534,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
if c.matchedConcept != nil: if c.matchedConcept != nil:
typFlags.incl taConcept 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) when false: liftTypeBoundOps(c, typ, a.info)
instAllTypeBoundOp(c, a.info) instAllTypeBoundOp(c, a.info)
@ -667,7 +667,7 @@ proc semConst(c: PContext, n: PNode): PNode =
if def.kind != nkNilLit: if def.kind != nkNilLit:
if c.matchedConcept != nil: if c.matchedConcept != nil:
typFlags.incl taConcept typFlags.incl taConcept
typeAllowedCheck(c.config, a.info, typ, skConst, typFlags) typeAllowedCheck(c, a.info, typ, skConst, typFlags)
var b: PNode var b: PNode
if a.kind == nkVarTuple: if a.kind == nkVarTuple:

View file

@ -494,6 +494,7 @@ proc transformConv(c: PTransf, n: PNode): PNode =
result = transformSons(c, n) result = transformSons(c, n)
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
result = transform(c, n[1]) result = transform(c, n[1])
#result = transformSons(c, n)
result.typ = takeType(n.typ, n[1].typ) result.typ = takeType(n.typ, n[1].typ)
#echo n.info, " came here and produced ", typeToString(result.typ), #echo n.info, " came here and produced ", typeToString(result.typ),
# " from ", typeToString(n.typ), " and ", typeToString(n[1].typ) # " from ", typeToString(n.typ), " and ", typeToString(n[1].typ)
@ -1107,6 +1108,9 @@ proc transformBody*(g: ModuleGraph, prc: PSym, cache: bool): PNode =
else: else:
prc.transformedBody = nil prc.transformedBody = nil
#if prc.name.s == "main":
# echo "transformed into ", renderTree(result, {renderIds})
proc transformStmt*(g: ModuleGraph; module: PSym, n: PNode): PNode = proc transformStmt*(g: ModuleGraph; module: PSym, n: PNode): PNode =
if nfTransf in n.flags: if nfTransf in n.flags:
result = n result = n

View file

@ -183,7 +183,7 @@ proc getRoot*(n: PNode): PSym =
if n.sym.kind in {skVar, skResult, skTemp, skLet, skForVar, skParam}: if n.sym.kind in {skVar, skResult, skTemp, skLet, skForVar, skParam}:
result = n.sym result = n.sym
of nkDotExpr, nkBracketExpr, nkHiddenDeref, nkDerefExpr, of nkDotExpr, nkBracketExpr, nkHiddenDeref, nkDerefExpr,
nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr, nkHiddenAddr, nkAddr:
result = getRoot(n[0]) result = getRoot(n[0])
of nkHiddenStdConv, nkHiddenSubConv, nkConv: of nkHiddenStdConv, nkHiddenSubConv, nkConv:
result = getRoot(n[1]) result = getRoot(n[1])

228
compiler/typeallowed.nim Normal file
View file

@ -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)

View file

@ -329,7 +329,7 @@ proc containsTyRef*(typ: PType): bool =
result = searchTypeFor(typ, isTyRef) result = searchTypeFor(typ, isTyRef)
proc isHiddenPointer(t: PType): bool = proc isHiddenPointer(t: PType): bool =
result = t.kind in {tyString, tySequence} result = t.kind in {tyString, tySequence, tyOpenArray, tyVarargs}
proc containsHiddenPointer*(typ: PType): bool = proc containsHiddenPointer*(typ: PType): bool =
# returns true if typ contains a string, table or sequence (all the things # 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 return t
y = y[0] 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]], proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]],
last: TTypeKind): bool = last: TTypeKind): bool =
var a = a var a = a
@ -1280,143 +1249,6 @@ proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]],
a = a[i] a = a[i]
result = a.kind == last 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 include sizealignoffsetimpl
proc computeSize*(conf: ConfigRef; typ: PType): BiggestInt = proc computeSize*(conf: ConfigRef; typ: PType): BiggestInt =

View file

@ -17,6 +17,7 @@ import ast, types, lineinfos, options, msgs, renderer
from trees import getMagic, whichPragma from trees import getMagic, whichPragma
from wordrecg import wNoSideEffect from wordrecg import wNoSideEffect
from isolation_check import canAlias from isolation_check import canAlias
from typeallowed import isViewType
type type
SubgraphFlag = enum SubgraphFlag = enum
@ -48,7 +49,7 @@ type
maxMutation, minConnection: int maxMutation, minConnection: int
mutations: seq[int] mutations: seq[int]
Partitions = object Partitions* = object
abstractTime: int abstractTime: int
s: seq[VarIndex] s: seq[VarIndex]
graphs: seq[MutationInfo] graphs: seq[MutationInfo]
@ -74,7 +75,7 @@ proc `$`*(config: ConfigRef; g: MutationInfo): string =
result.add config $ g.connectedVia result.add config $ g.connectedVia
result.add " is the statement that connected the mutation to the parameter" 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: for g in mitems c.graphs:
if g.flags == {isMutated, connectsConstParam} and mutationAfterConnection(g): if g.flags == {isMutated, connectsConstParam} and mutationAfterConnection(g):
info = g info = g
@ -505,17 +506,16 @@ proc traverse(c: var Partitions; n: PNode) =
else: else:
for child in n: traverse(c, child) for child in n: traverse(c, child)
proc mutatesNonVarParameters*(s: PSym; n: PNode; info: var MutationInfo): bool = proc computeGraphPartitions*(s: PSym; n: PNode; cursorInference = false): Partitions =
var par = Partitions(performCursorInference: false) result = Partitions(performCursorInference: cursorInference)
if s.kind != skMacro: if s.kind notin {skModule, skMacro}:
let params = s.typ.n let params = s.typ.n
for i in 1..<params.len: for i in 1..<params.len:
registerVariable(par, params[i]) registerVariable(result, params[i])
if resultPos < s.ast.safeLen: if resultPos < s.ast.safeLen:
registerVariable(par, s.ast[resultPos]) registerVariable(result, s.ast[resultPos])
traverse(par, n) traverse(result, n)
result = hasSideEffect(par, info)
proc dangerousMutation(g: MutationInfo; v: VarIndex): bool = proc dangerousMutation(g: MutationInfo; v: VarIndex): bool =
if isMutated in g.flags: if isMutated in g.flags:
@ -524,16 +524,16 @@ proc dangerousMutation(g: MutationInfo; v: VarIndex): bool =
return true return true
return false return false
proc computeCursors*(s: PSym; n: PNode; config: ConfigRef) = proc checkBorrowedLocations*(par: var Partitions; config: ConfigRef) =
var par = Partitions(performCursorInference: true) for i in 0 ..< par.s.len:
if s.kind notin {skMacro, skModule}: let s = par.s[i].sym
let params = s.typ.n if s.kind != skParam and isViewType(s.typ):
for i in 1..<params.len: let rid = root(par, i)
registerVariable(par, params[i]) if par.s[rid].kind == isRootOf and dangerousMutation(par.graphs[par.s[rid].graphIndex], par.s[i]):
if resultPos < s.ast.safeLen: localError(config, s.info, config $ par.graphs[par.s[rid].graphIndex])
registerVariable(par, s.ast[resultPos])
traverse(par, n) proc computeCursors*(s: PSym; n: PNode; config: ConfigRef) =
var par = computeGraphPartitions(s, n, true)
for i in 0 ..< par.s.len: for i in 0 ..< par.s.len:
let v = addr(par.s[i]) let v = addr(par.s[i])
if v.flags == {} and v.sym.kind notin {skParam, skResult} and if v.flags == {} and v.sym.kind notin {skParam, skResult} and

View file

@ -1836,3 +1836,55 @@ For example:
The algorithm behind this analysis is currently not documented. 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.

27
tests/ccgbugs/tviews1.nim Normal file
View file

@ -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])