init checks and 'out' parameters (#14521)

* I don't care about observable stores
* enforce explicit initializations
* cleaner code for the stdlib
* stdlib: use explicit initializations
* make tests green
* algorithm.nim: set result explicitly
* remove out parameters and bring the PR into a mergable state
* updated the changelog
This commit is contained in:
Andreas Rumpf 2020-06-23 10:53:57 +02:00 • committed by GitHub
commit da29222f86
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
53 changed files with 355 additions and 257 deletions

View file

@ -435,7 +435,8 @@ type
# be any type.
tyOptDeprecated
# deadcode: was `tyOpt`, Builtin optional type
# 'out' parameter. Comparable to a 'var' parameter but every
# path must assign a value to it before it can be read from.
tyVoid
# now different from tyEmpty, hurray!
@ -1802,12 +1803,12 @@ proc skipStmtList*(n: PNode): PNode =
else:
result = n
proc toVar*(typ: PType): PType =
proc toVar*(typ: PType; kind: TTypeKind): PType =
## If ``typ`` is not a tyVar then it is converted into a `var <typ>` and
## returned. Otherwise ``typ`` is simply returned as-is.
result = typ
if typ.kind != tyVar:
result = newType(tyVar, typ.owner)
if typ.kind != kind:
result = newType(kind, typ.owner)
rawAddSon(result, typ)
proc toRef*(typ: PType): PType =
@ -1934,3 +1935,6 @@ proc toHumanStr*(kind: TSymKind): string =
proc toHumanStr*(kind: TTypeKind): string =
## strips leading `tk`
result = toHumanStrImpl(kind, 2)
proc skipAddr*(n: PNode): PNode {.inline.} =
(if n.kind == nkHiddenAddr: n[0] else: n)

View file

@ -178,10 +178,10 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope =
result = "($4*)($1)+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), dest]
of tyString, tySequence:
let atyp = skipTypes(a.t, abstractInst)
if formalType.skipTypes(abstractInst).kind == tyVar and atyp.kind == tyString and
if formalType.skipTypes(abstractInst).kind in {tyVar} and atyp.kind == tyString and
optSeqDestructors in p.config.globalOptions:
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
if atyp.kind == tyVar and not compileToCpp(p.module):
if atyp.kind in {tyVar} and not compileToCpp(p.module):
result = "($5*)(*$1)$4+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), dataField(p), dest]
else:
result = "($5*)$1$4+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), dataField(p), dest]
@ -194,10 +194,10 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope =
result = "$1, $1Len_0" % [rdLoc(a)]
of tyString, tySequence:
let ntyp = skipTypes(n.typ, abstractInst)
if formalType.skipTypes(abstractInst).kind == tyVar and ntyp.kind == tyString and
if formalType.skipTypes(abstractInst).kind in {tyVar} and ntyp.kind == tyString and
optSeqDestructors in p.config.globalOptions:
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
if ntyp.kind == tyVar and not compileToCpp(p.module):
if ntyp.kind in {tyVar} and not compileToCpp(p.module):
var t: TLoc
t.r = "(*$1)" % [a.rdLoc]
result = "(*$1)$3, $2" % [a.rdLoc, lenExpr(p, t), dataField(p)]
@ -232,7 +232,7 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): Rope =
elif ccgIntroducedPtr(p.config, param, call[0].typ[0]):
initLocExpr(p, n, a)
result = addrLoc(p.config, a)
elif p.module.compileToCpp and param.typ.kind == tyVar and
elif p.module.compileToCpp and param.typ.kind in {tyVar} and
n.kind == nkHiddenAddr:
initLocExprSingleUse(p, n[0], a)
# if the proc is 'importc'ed but not 'importcpp'ed then 'var T' still
@ -372,7 +372,7 @@ proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
assert(paramType.kind == nkSym)
if paramType.typ.isCompileTimeOnly:
result = nil
elif typ[i].kind == tyVar and ri[i].kind == nkHiddenAddr:
elif typ[i].kind in {tyVar} and ri[i].kind == nkHiddenAddr:
result = genArgNoParam(p, ri[i][0])
else:
result = genArgNoParam(p, ri[i]) #, typ.n[i].sym)
@ -449,7 +449,7 @@ proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
var ri = ri[i]
while ri.kind == nkObjDownConv: ri = ri[0]
let t = typ[i].skipTypes({tyGenericInst, tyAlias, tySink})
if t.kind == tyVar:
if t.kind in {tyVar}:
let x = if ri.kind == nkHiddenAddr: ri[0] else: ri
if x.typ.kind == tyPtr:
result = genArgNoParam(p, x)

View file

@ -126,9 +126,8 @@ proc genRawSetData(cs: TBitSet, size: int): Rope =
result = intLiteral(cast[BiggestInt](bitSetToWord(cs, size)))
proc genSetNode(p: BProc, n: PNode): Rope =
var cs: TBitSet
var size = int(getSize(p.config, n.typ))
toBitSet(p.config, n, cs)
let cs = toBitSet(p.config, n)
if size > 8:
let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels)
result = p.module.tmpBase & rope(id)
@ -676,7 +675,7 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
proc isCppRef(p: BProc; typ: PType): bool {.inline.} =
result = p.module.compileToCpp and
skipTypes(typ, abstractInstOwned).kind == tyVar and
skipTypes(typ, abstractInstOwned).kind in {tyVar} and
tfVarIsPtr notin skipTypes(typ, abstractInstOwned).flags
proc genDeref(p: BProc, e: PNode, d: var TLoc) =
@ -693,7 +692,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc) =
if typ.kind in {tyUserTypeClass, tyUserTypeClassInst} and typ.isResolvedUserTypeClass:
typ = typ.lastSon
typ = typ.skipTypes(abstractInstOwned)
if typ.kind == tyVar and tfVarIsPtr notin typ.flags and p.module.compileToCpp and e[0].kind == nkHiddenAddr:
if typ.kind in {tyVar} and tfVarIsPtr notin typ.flags and p.module.compileToCpp and e[0].kind == nkHiddenAddr:
initLocExprSingleUse(p, e[0][0], d)
return
else:
@ -716,7 +715,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc) =
else:
internalError(p.config, e.info, "genDeref " & $typ.kind)
elif p.module.compileToCpp:
if typ.kind == tyVar and tfVarIsPtr notin typ.flags and
if typ.kind in {tyVar} and tfVarIsPtr notin typ.flags and
e.kind == nkHiddenDeref:
putIntoDest(p, d, e, rdLoc(a), a.storage)
return
@ -2960,8 +2959,7 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool): Rope =
ty = skipTypes(n.typ, abstractInstOwned + {tyStatic}).kind
case ty
of tySet:
var cs: TBitSet
toBitSet(p.config, n, cs)
let cs = toBitSet(p.config, n)
result = genRawSetData(cs, int(getSize(p.config, n.typ)))
of tySequence:
if optSeqDestructors in p.config.globalOptions:

View file

@ -701,7 +701,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
return
case t.kind
of tyRef, tyPtr, tyVar, tyLent:
var star = if t.kind == tyVar and tfVarIsPtr notin origTyp.flags and
var star = if t.kind in {tyVar} and tfVarIsPtr notin origTyp.flags and
compileToCpp(m): "&" else: "*"
var et = origTyp.skipTypes(abstractInst).lastSon
var etB = et.skipTypes(abstractInst)

View file

@ -715,10 +715,8 @@ proc genCall(c: var Con; n: PNode) =
for i in 1..<n.len:
gen(c, n[i])
when false:
if t != nil and i < t.len and t[i].kind == tyVar:
# This is wrong! Pass by var is a 'might def', not a 'must def'
# like the other defs we emit. This is not good enough for a move
# optimizer.
if t != nil and i < t.len and t[i].kind == tyOut:
# Pass by 'out' is a 'must def'. Good enough for a move optimizer.
genDef(c, n[i])
# every call can potentially raise:
if c.inTryStmt > 0 and canRaiseConservative(n[0]):

View file

@ -46,7 +46,7 @@ proc isLet(n: PNode): bool =
if n.sym.kind in {skLet, skTemp, skForVar}:
result = true
elif n.sym.kind == skParam and skipTypes(n.sym.typ,
abstractInst).kind != tyVar:
abstractInst).kind notin {tyVar}:
result = true
proc isVar(n: PNode): bool =

View file

@ -1053,7 +1053,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
lineF(p, "$1 = nimCopy(null, $2, $3);$n",
[a.rdLoc, b.res, genTypeInfo(p, y.typ)])
of etyObject:
if x.typ.kind == tyVar or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded:
if x.typ.kind in {tyVar} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded:
lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
else:
useMagic(p, "nimCopy")

View file

@ -113,7 +113,7 @@ const
warnStaticIndexCheck: "$1",
warnGcUnsafe: "not GC-safe: '$1'",
warnGcUnsafe2: "$1",
warnUninit: "'$1' might not have been initialized",
warnUninit: "use explicit initialization of '$1' for clarity",
warnGcMem: "'$1' uses GC'ed memory",
warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future.",
warnLockLevel: "$1",

View file

@ -59,7 +59,7 @@ proc newFastAsgnStmt*(le, ri: PNode): PNode =
result[0] = le
result[1] = ri
proc newFastMoveStmt*(g: ModuleGraph, le, ri: PNode): PNode =
proc newFastMoveStmt*(g: ModuleGraph, le, ri: PNode): PNode =
result = newNodeI(nkFastAsgn, le.info, 2)
result[0] = le
result[1] = newNodeIT(nkCall, ri.info, ri.typ)

View file

@ -21,3 +21,7 @@ define:useStdoutAsStdmsg
#define:useNodeIds
#gc:markAndSweep
@if nimHasWarningObservableStores:
warning[ObservableStores]: off
@end

View file

@ -58,18 +58,18 @@ proc someInSet*(s: PNode, a, b: PNode): bool =
return true
result = false
proc toBitSet*(conf: ConfigRef; s: PNode, b: var TBitSet) =
proc toBitSet*(conf: ConfigRef; s: PNode): TBitSet =
var first, j: Int128
first = firstOrd(conf, s.typ[0])
bitSetInit(b, int(getSize(conf, s.typ)))
bitSetInit(result, int(getSize(conf, s.typ)))
for i in 0..<s.len:
if s[i].kind == nkRange:
j = getOrdValue(s[i][0], first)
while j <= getOrdValue(s[i][1], first):
bitSetIncl(b, toInt64(j - first))
bitSetIncl(result, toInt64(j - first))
inc(j)
else:
bitSetIncl(b, toInt64(getOrdValue(s[i]) - first))
bitSetIncl(result, toInt64(getOrdValue(s[i]) - first))
proc toTreeSet*(conf: ConfigRef; s: TBitSet, settype: PType, info: TLineInfo): PNode =
var
@ -106,9 +106,8 @@ proc toTreeSet*(conf: ConfigRef; s: TBitSet, settype: PType, info: TLineInfo): P
inc(e)
template nodeSetOp(a, b: PNode, op: untyped) {.dirty.} =
var x, y: TBitSet
toBitSet(conf, a, x)
toBitSet(conf, b, y)
var x = toBitSet(conf, a)
let y = toBitSet(conf, b)
op(x, y)
result = toTreeSet(conf, x, a.typ, a.info)
@ -118,31 +117,26 @@ proc intersectSets*(conf: ConfigRef; a, b: PNode): PNode = nodeSetOp(a, b, bitSe
proc symdiffSets*(conf: ConfigRef; a, b: PNode): PNode = nodeSetOp(a, b, bitSetSymDiff)
proc containsSets*(conf: ConfigRef; a, b: PNode): bool =
var x, y: TBitSet
toBitSet(conf, a, x)
toBitSet(conf, b, y)
let x = toBitSet(conf, a)
let y = toBitSet(conf, b)
result = bitSetContains(x, y)
proc equalSets*(conf: ConfigRef; a, b: PNode): bool =
var x, y: TBitSet
toBitSet(conf, a, x)
toBitSet(conf, b, y)
let x = toBitSet(conf, a)
let y = toBitSet(conf, b)
result = bitSetEquals(x, y)
proc complement*(conf: ConfigRef; a: PNode): PNode =
var x: TBitSet
toBitSet(conf, a, x)
var x = toBitSet(conf, a)
for i in 0..high(x): x[i] = not x[i]
result = toTreeSet(conf, x, a.typ, a.info)
proc deduplicate*(conf: ConfigRef; a: PNode): PNode =
var x: TBitSet
toBitSet(conf, a, x)
let x = toBitSet(conf, a)
result = toTreeSet(conf, x, a.typ, a.info)
proc cardSet*(conf: ConfigRef; a: PNode): BiggestInt =
var x: TBitSet
toBitSet(conf, a, x)
let x = toBitSet(conf, a)
result = bitSetCard(x)
proc setHasRange*(s: PNode): bool =

View file

@ -214,7 +214,7 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult
result = arNone
case n.kind
of nkEmpty:
if n.typ != nil and n.typ.kind == tyVar:
if n.typ != nil and n.typ.kind in {tyVar}:
result = arLValue
of nkSym:
let kinds = if isUnsafeAddr: {skVar, skResult, skTemp, skParam, skLet, skForVar}
@ -231,7 +231,7 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult
result = arLValue
elif n.sym.kind == skType:
let t = n.sym.typ.skipTypes({tyTypeDesc})
if t.kind == tyVar: result = arStrange
if t.kind in {tyVar}: result = arStrange
of nkDotExpr:
let t = skipTypes(n[0].typ, abstractInst-{tyTypeDesc})
if t.kind in {tyVar, tySink, tyPtr, tyRef}:
@ -277,7 +277,7 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult
# builtin slice keeps lvalue-ness:
if getMagic(n) in {mArrGet, mSlice}:
result = isAssignable(owner, n[1], isUnsafeAddr)
elif n.typ != nil and n.typ.kind == tyVar:
elif n.typ != nil and n.typ.kind in {tyVar}:
result = arLValue
elif isUnsafeAddr and n.typ != nil and n.typ.kind == tyLent:
result = arLValue

View file

@ -793,7 +793,7 @@ proc primarySuffix(p: var TParser, r: PNode,
break
result = namedParams(p, result, nkCurlyExpr, tkCurlyRi)
of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast,
tkOpr, tkDotDot, tkVar, tkStatic, tkType, tkEnum, tkTuple,
tkOpr, tkDotDot, tkVar, tkOut, tkStatic, tkType, tkEnum, tkTuple,
tkObject, tkProc:
# XXX: In type sections we allow the free application of the
# command syntax, with the exception of expressions such as
@ -1300,6 +1300,10 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
optInd(p, result)
result.add(primary(p, pmNormal))
of tkVar: result = parseTypeDescKAux(p, nkVarTy, mode)
of tkOut:
# I like this parser extension to be in 1.4 as it still might turn out
# useful in the long run.
result = parseTypeDescKAux(p, nkMutableTy, mode)
of tkRef: result = parseTypeDescKAux(p, nkRefTy, mode)
of tkPtr: result = parseTypeDescKAux(p, nkPtrTy, mode)
of tkDistinct: result = parseTypeDescKAux(p, nkDistinctTy, mode)

View file

@ -309,8 +309,8 @@ proc lsub(g: TSrcGen; n: PNode): int
proc litAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string =
proc skip(t: PType): PType =
result = t
while result != nil and result.kind in {tyGenericInst, tyRange, tyVar, tyLent, tyDistinct,
tyOrdinal, tyAlias, tySink}:
while result != nil and result.kind in {tyGenericInst, tyRange, tyVar,
tyLent, tyDistinct, tyOrdinal, tyAlias, tySink}:
result = lastSon(result)
let typ = n.typ.skip
@ -883,7 +883,7 @@ proc bracketKind*(g: TSrcGen, n: PNode): BracketKind =
case n.kind
of nkClosedSymChoice, nkOpenSymChoice:
if n.len > 0: result = bracketKind(g, n[0])
of nkSym:
of nkSym:
result = case n.sym.name.s
of "[]": bkBracket
of "[]=": bkBracketAsgn
@ -974,7 +974,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
put(g, tkParRi, ")")
put(g, tkColon, ":")
gsub(g, n, n.len-1)
elif n.len >= 1:
elif n.len >= 1:
case bracketKind(g, n[0])
of bkBracket:
gsub(g, n, 1)

View file

@ -107,6 +107,7 @@ proc extractRunnableExamplesSource*(conf: ConfigRef; n: PNode): string =
var indent = info.col
let numLines = numLines(conf, info.fileIndex).uint16
var lastNonemptyPos = 0
result = ""
var ldata = LineData(lineFirst: first.line.int, conf: conf)
visitMultilineStrings(ldata, n[^1])

View file

@ -676,7 +676,7 @@ proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym =
if t.kind == tyDistinct or param.typ.kind == tyDistinct: hasDistinct = true
var x: PType
if param.typ.kind == tyVar:
x = newTypeS(tyVar, c)
x = newTypeS(param.typ.kind, c)
x.addSonSkipIntLit t.baseOfDistinct
else:
x = t.baseOfDistinct

View file

@ -712,7 +712,7 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
# note sometimes this is eval'ed twice so we check for nkHiddenAddr here:
for i in 1..<n.len:
if i < t.len and t[i] != nil and
skipTypes(t[i], abstractInst-{tyTypeDesc}).kind == tyVar:
skipTypes(t[i], abstractInst-{tyTypeDesc}).kind in {tyVar}:
let it = n[i]
if isAssignable(c, it) notin {arLValue, arLocalLValue}:
if it.kind != nkHiddenAddr:
@ -733,7 +733,7 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
# calls and then they wouldn't be analysed otherwise
analyseIfAddressTakenInCall(c, n[i])
if i < t.len and
skipTypes(t[i], abstractInst-{tyTypeDesc}).kind == tyVar:
skipTypes(t[i], abstractInst-{tyTypeDesc}).kind in {tyVar}:
if n[i].kind != nkHiddenAddr:
n[i] = analyseIfAddressTaken(c, n[i])
@ -1711,7 +1711,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
var le = a.typ
if le == nil:
localError(c.config, a.info, "expression has no type")
elif (skipTypes(le, {tyGenericInst, tyAlias, tySink}).kind != tyVar and
elif (skipTypes(le, {tyGenericInst, tyAlias, tySink}).kind notin {tyVar} and
isAssignable(c, a) in {arNone, arLentValue}) or
skipTypes(le, abstractVar).kind in {tyOpenArray, tyVarargs}:
# Direct assignment to a discriminant is allowed!

View file

@ -42,9 +42,6 @@ type
proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode
proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode
proc skipAddr(n: PNode): PNode {.inline.} =
(if n.kind == nkHiddenAddr: n[0] else: n)
proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode =
result = newNodeI(nkBracketExpr, n.info)
for i in 1..<n.len: result.add(n[i])

View file

@ -249,7 +249,7 @@ proc semConstructFields(c: PContext, recNode: PNode,
else:
badDiscriminatorError()
elif discriminatorVal.sym.kind notin {skLet, skParam} or
discriminatorVal.sym.typ.kind == tyVar:
discriminatorVal.sym.typ.kind in {tyVar}:
localError(c.config, discriminatorVal.info,
"runtime discriminator must be immutable if branch fields are " &
"initialized, a 'let' binding is required.")

View file

@ -66,7 +66,7 @@ type
TEffects = object
exc: PNode # stack of exceptions
tags: PNode # list of tags
bottom, inTryStmt, inExceptOrFinallyStmt: int
bottom, inTryStmt, inExceptOrFinallyStmt, leftPartOfAsgn: int
owner: PSym
ownerModule: PSym
init: seq[int] # list of initialized variables
@ -87,6 +87,7 @@ proc `==`(a, b: TLockLevel): bool {.borrow.}
proc max(a, b: TLockLevel): TLockLevel {.borrow.}
proc isLocalVar(a: PEffects, s: PSym): bool =
# and (s.kind != skParam or s.typ.kind == tyOut)
s.kind in {skVar, skResult} and sfGlobal notin s.flags and
s.owner == a.owner and s.typ != nil
@ -246,6 +247,18 @@ proc listGcUnsafety(s: PSym; onlyWarning: bool; conf: ConfigRef) =
var cycleCheck = initIntSet()
listGcUnsafety(s, onlyWarning, cycleCheck, conf)
proc useVarNoInitCheck(a: PEffects; n: PNode; s: PSym) =
if {sfGlobal, sfThread} * s.flags != {} and s.kind in {skVar, skLet} and
s.magic != mNimvm:
if s.guard != nil: guardGlobal(a, n, s.guard)
if {sfGlobal, sfThread} * s.flags == {sfGlobal} and
(tfHasGCedMem in s.typ.flags or s.typ.isGCedMem):
#if a.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n)
markGcUnsafe(a, s)
markSideEffect(a, s)
else:
markSideEffect(a, s)
proc useVar(a: PEffects, n: PNode) =
let s = n.sym
if a.inExceptOrFinallyStmt > 0:
@ -257,20 +270,11 @@ proc useVar(a: PEffects, n: PNode) =
elif s.id notin a.init:
if s.typ.requiresInit:
message(a.config, n.info, warnProveInit, s.name.s)
else:
elif a.leftPartOfAsgn <= 0:
message(a.config, n.info, warnUninit, s.name.s)
# prevent superfluous warnings about the same variable:
a.init.add s.id
if {sfGlobal, sfThread} * s.flags != {} and s.kind in {skVar, skLet} and
s.magic != mNimvm:
if s.guard != nil: guardGlobal(a, n, s.guard)
if {sfGlobal, sfThread} * s.flags == {sfGlobal} and
(tfHasGCedMem in s.typ.flags or s.typ.isGCedMem):
#if a.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n)
markGcUnsafe(a, s)
markSideEffect(a, s)
else:
markSideEffect(a, s)
useVarNoInitCheck(a, n, s)
type
@ -538,7 +542,7 @@ proc isNoEffectList(n: PNode): bool {.inline.} =
proc isTrival(caller: PNode): bool {.inline.} =
result = caller.kind == nkSym and caller.sym.magic in {mEqProc, mIsNil, mMove, mWasMoved}
proc trackOperand(tracked: PEffects, n: PNode, paramType: PType; caller: PNode) =
proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, paramType: PType; caller: PNode) =
let a = skipConvAndClosure(n)
let op = a.typ
# assume indirect calls are taken here:
@ -572,7 +576,7 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType; caller: PNode)
markGcUnsafe(tracked, a)
elif tfNoSideEffect notin op.flags:
markSideEffect(tracked, a)
if paramType != nil and paramType.kind == tyVar:
if paramType != nil and paramType.kind in {tyVar}:
invalidateFacts(tracked.guards, n)
if n.kind == nkSym and isLocalVar(tracked, n.sym):
makeVolatile(tracked, n.sym)
@ -732,7 +736,7 @@ proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo) =
optSeqDestructors in tracked.config.globalOptions:
tracked.owner.flags.incl sfInjectDestructors
proc track(tracked: PEffects, n: PNode) =
proc trackCall(tracked: PEffects; n: PNode) =
template gcsafeAndSideeffectCheck() =
if notGcSafe(op) and not importedFromC(a):
# and it's not a recursive call:
@ -744,11 +748,105 @@ proc track(tracked: PEffects, n: PNode) =
if not (a.kind == nkSym and a.sym == tracked.owner):
markSideEffect(tracked, a)
# p's effects are ours too:
var a = n[0]
#if canRaise(a):
# echo "this can raise ", tracked.config $ n.info
let op = a.typ
if n.typ != nil:
if tracked.owner.kind != skMacro and n.typ.skipTypes(abstractVar).kind != tyOpenArray:
createTypeBoundOps(tracked, n.typ, n.info)
if getConstExpr(tracked.ownerModule, n, tracked.graph) != nil:
return
if a.kind == nkCast and a[1].typ.kind == tyProc:
a = a[1]
# XXX: in rare situations, templates and macros will reach here after
# calling getAst(templateOrMacro()). Currently, templates and macros
# are indistinguishable from normal procs (both have tyProc type) and
# we can detect them only by checking for attached nkEffectList.
if op != nil and op.kind == tyProc and op.n[0].kind == nkEffectList:
if a.kind == nkSym:
if a.sym == tracked.owner: tracked.isRecursive = true
# even for recursive calls we need to check the lock levels (!):
mergeLockLevels(tracked, n, a.sym.getLockLevel)
if sfSideEffect in a.sym.flags: markSideEffect(tracked, a)
else:
mergeLockLevels(tracked, n, op.lockLevel)
var effectList = op.n[0]
if a.kind == nkSym and a.sym.kind == skMethod:
propagateEffects(tracked, n, a.sym)
elif isNoEffectList(effectList):
if isForwardedProc(a):
propagateEffects(tracked, n, a.sym)
elif isIndirectCall(a, tracked.owner):
assumeTheWorst(tracked, n, op)
gcsafeAndSideeffectCheck()
else:
mergeEffects(tracked, effectList[exceptionEffects], n)
mergeTags(tracked, effectList[tagEffects], n)
gcsafeAndSideeffectCheck()
if a.kind != nkSym or a.sym.magic != mNBindSym:
for i in 1..<n.len: trackOperandForIndirectCall(tracked, n[i], paramType(op, i), a)
if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
# may not look like an assignment, but it is:
let arg = n[1]
initVarViaNew(tracked, arg)
if arg.typ.len != 0 and {tfRequiresInit} * arg.typ.lastSon.flags != {}:
if a.sym.magic == mNewSeq and n[2].kind in {nkCharLit..nkUInt64Lit} and
n[2].intVal == 0:
# var s: seq[notnil]; newSeq(s, 0) is a special case!
discard
else:
message(tracked.config, arg.info, warnProveInit, $arg)
# check required for 'nim check':
if n[1].typ.len > 0:
createTypeBoundOps(tracked, n[1].typ.lastSon, n.info)
createTypeBoundOps(tracked, n[1].typ, n.info)
# new(x, finalizer): Problem: how to move finalizer into 'createTypeBoundOps'?
elif a.kind == nkSym and a.sym.magic in {mArrGet, mArrPut} and
optStaticBoundsCheck in tracked.currOptions:
checkBounds(tracked, n[1], n[2])
if a.kind == nkSym and a.sym.name.s.len > 0 and a.sym.name.s[0] == '=' and
tracked.owner.kind != skMacro:
let opKind = find(AttachedOpToStr, a.sym.name.s.normalize)
if opKind != -1:
# rebind type bounds operations after createTypeBoundOps call
let t = n[1].typ.skipTypes({tyAlias, tyVar})
if a.sym != t.attachedOps[TTypeAttachedOp(opKind)]:
createTypeBoundOps(tracked, t, n.info)
let op = t.attachedOps[TTypeAttachedOp(opKind)]
if op != nil:
n[0].sym = op
if a.kind != nkSym or a.sym.magic != mRunnableExamples:
for i in 0..<n.safeLen:
track(tracked, n[i])
if op != nil and op.kind == tyProc:
for i in 1..<min(n.safeLen, op.len):
case op[i].kind
of tySink:
checkForSink(tracked.config, tracked.owner, n[i])
#of tyOut:
# consider this case: p(out x, x); we want to remark that 'x' is not
# initialized until after the call. Since we do this after we analysed the
# call, this is fine.
# initVar(tracked, n[i].skipAddr, false)
else: discard
proc track(tracked: PEffects, n: PNode) =
case n.kind
of nkSym:
useVar(tracked, n)
if n.sym.typ != nil and tfHasAsgn in n.sym.typ.flags:
tracked.owner.flags.incl sfInjectDestructors
of nkHiddenAddr, nkAddr:
if n[0].kind == nkSym and isLocalVar(tracked, n[0].sym):
useVarNoInitCheck(tracked, n[0], n[0].sym)
else:
track(tracked, n[0])
of nkRaiseStmt:
if n[0].kind != nkEmpty:
n[0].info = n.info
@ -763,86 +861,7 @@ proc track(tracked: PEffects, n: PNode) =
# Here we add a `Exception` tag in order to cover both the cases.
addEffect(tracked, createRaise(tracked.graph, n), nil)
of nkCallKinds:
# p's effects are ours too:
var a = n[0]
#if canRaise(a):
# echo "this can raise ", tracked.config $ n.info
let op = a.typ
if n.typ != nil:
if tracked.owner.kind != skMacro and n.typ.skipTypes(abstractVar).kind != tyOpenArray:
createTypeBoundOps(tracked, n.typ, n.info)
if getConstExpr(tracked.ownerModule, n, tracked.graph) != nil:
return
if a.kind == nkCast and a[1].typ.kind == tyProc:
a = a[1]
# XXX: in rare situations, templates and macros will reach here after
# calling getAst(templateOrMacro()). Currently, templates and macros
# are indistinguishable from normal procs (both have tyProc type) and
# we can detect them only by checking for attached nkEffectList.
if op != nil and op.kind == tyProc and op.n[0].kind == nkEffectList:
if a.kind == nkSym:
if a.sym == tracked.owner: tracked.isRecursive = true
# even for recursive calls we need to check the lock levels (!):
mergeLockLevels(tracked, n, a.sym.getLockLevel)
if sfSideEffect in a.sym.flags: markSideEffect(tracked, a)
else:
mergeLockLevels(tracked, n, op.lockLevel)
var effectList = op.n[0]
if a.kind == nkSym and a.sym.kind == skMethod:
propagateEffects(tracked, n, a.sym)
elif isNoEffectList(effectList):
if isForwardedProc(a):
propagateEffects(tracked, n, a.sym)
elif isIndirectCall(a, tracked.owner):
assumeTheWorst(tracked, n, op)
gcsafeAndSideeffectCheck()
else:
mergeEffects(tracked, effectList[exceptionEffects], n)
mergeTags(tracked, effectList[tagEffects], n)
gcsafeAndSideeffectCheck()
if a.kind != nkSym or a.sym.magic != mNBindSym:
for i in 1..<n.len: trackOperand(tracked, n[i], paramType(op, i), a)
if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
# may not look like an assignment, but it is:
let arg = n[1]
initVarViaNew(tracked, arg)
if arg.typ.len != 0 and {tfRequiresInit} * arg.typ.lastSon.flags != {}:
if a.sym.magic == mNewSeq and n[2].kind in {nkCharLit..nkUInt64Lit} and
n[2].intVal == 0:
# var s: seq[notnil]; newSeq(s, 0) is a special case!
discard
else:
message(tracked.config, arg.info, warnProveInit, $arg)
# check required for 'nim check':
if n[1].typ.len > 0:
createTypeBoundOps(tracked, n[1].typ.lastSon, n.info)
createTypeBoundOps(tracked, n[1].typ, n.info)
# new(x, finalizer): Problem: how to move finalizer into 'createTypeBoundOps'?
elif a.kind == nkSym and a.sym.magic in {mArrGet, mArrPut} and
optStaticBoundsCheck in tracked.currOptions:
checkBounds(tracked, n[1], n[2])
if a.kind == nkSym and a.sym.name.s.len > 0 and a.sym.name.s[0] == '=' and
tracked.owner.kind != skMacro:
let opKind = find(AttachedOpToStr, a.sym.name.s.normalize)
if opKind != -1:
# rebind type bounds operations after createTypeBoundOps call
let t = n[1].typ.skipTypes({tyAlias, tyVar})
if a.sym != t.attachedOps[TTypeAttachedOp(opKind)]:
createTypeBoundOps(tracked, t, n.info)
let op = t.attachedOps[TTypeAttachedOp(opKind)]
if op != nil:
n[0].sym = op
if a.kind != nkSym or a.sym.magic != mRunnableExamples:
for i in 0..<n.safeLen:
track(tracked, n[i])
if op != nil and op.kind == tyProc:
for i in 1..<min(n.safeLen, op.len):
if op[i].kind == tySink:
checkForSink(tracked.config, tracked.owner, n[i])
trackCall(tracked, n)
of nkDotExpr:
guardDotAccess(tracked, n)
for i in 0..<n.len: track(tracked, n[i])
@ -856,7 +875,9 @@ proc track(tracked: PEffects, n: PNode) =
track(tracked, n[1])
initVar(tracked, n[0], volatileCheck=true)
invalidateFacts(tracked.guards, n[0])
inc tracked.leftPartOfAsgn
track(tracked, n[0])
dec tracked.leftPartOfAsgn
addAsgnFact(tracked.guards, n[0], n[1])
notNilCheck(tracked, n[1], n[0].typ)
when false: cstringCheck(tracked, n)
@ -1042,7 +1063,10 @@ proc track(tracked: PEffects, n: PNode) =
if optStaticBoundsCheck in tracked.currOptions and n.len == 2:
if n[0].typ != nil and skipTypes(n[0].typ, abstractVar).kind != tyTuple:
checkBounds(tracked, n[0], n[1])
for i in 0 ..< n.len: track(tracked, n[i])
track(tracked, n[0])
dec tracked.leftPartOfAsgn
for i in 1 ..< n.len: track(tracked, n[i])
inc tracked.leftPartOfAsgn
else:
for i in 0..<n.safeLen: track(tracked, n[i])
@ -1180,6 +1204,9 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
if isSinkTypeForParam(typ) or
(t.config.selectedGC in {gcArc, gcOrc} and isClosure(typ.skipTypes(abstractInst))):
createTypeBoundOps(t, typ, param.info)
when false:
if typ.kind == tyOut and param.id notin t.init:
message(g.config, param.info, warnProveInit, param.name.s)
if not isEmptyType(s.typ[0]) and
(s.typ[0].requiresInit or s.typ[0].skipTypes(abstractInst).kind == tyVar) and

View file

@ -731,13 +731,8 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
var v = symForVar(c, n[0][i])
if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
case iter.kind
of tyVar:
v.typ = newTypeS(tyVar, c)
v.typ.add iterAfterVarLent[i]
if tfVarIsPtr in iter.flags:
v.typ.flags.incl tfVarIsPtr
of tyLent:
v.typ = newTypeS(tyLent, c)
of tyVar, tyLent:
v.typ = newTypeS(iter.kind, c)
v.typ.add iterAfterVarLent[i]
if tfVarIsPtr in iter.flags:
v.typ.flags.incl tfVarIsPtr
@ -794,13 +789,8 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
var v = symForVar(c, n[i])
if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
case iter.kind
of tyVar:
v.typ = newTypeS(tyVar, c)
v.typ.add iterAfterVarLent[i]
if tfVarIsPtr in iter.flags:
v.typ.flags.incl tfVarIsPtr
of tyLent:
v.typ = newTypeS(tyLent, c)
of tyVar, tyLent:
v.typ = newTypeS(iter.kind, c)
v.typ.add iterAfterVarLent[i]
if tfVarIsPtr in iter.flags:
v.typ.flags.incl tfVarIsPtr

View file

@ -193,16 +193,16 @@ proc semVarargs(c: PContext, n: PNode, prev: PType): PType =
localError(c.config, n.info, errXExpectsOneTypeParam % "varargs")
addSonSkipIntLit(result, errorType(c))
proc semVarType(c: PContext, n: PNode, prev: PType): PType =
proc semVarOutType(c: PContext, n: PNode, prev: PType; kind: TTypeKind): PType =
if n.len == 1:
result = newOrPrevType(tyVar, prev, c)
result = newOrPrevType(kind, prev, c)
var base = semTypeNode(c, n[0], nil).skipTypes({tyTypeDesc})
if base.kind == tyVar:
localError(c.config, n.info, "type 'var var' is not allowed")
base = base[0]
addSonSkipIntLit(result, base)
else:
result = newConstraint(c, tyVar)
result = newConstraint(c, kind)
proc semDistinct(c: PContext, n: PNode, prev: PType): PType =
if n.len == 0: return newConstraint(c, tyDistinct)
@ -1915,7 +1915,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
of nkTypeClassTy: result = semTypeClass(c, n, prev)
of nkRefTy: result = semAnyRef(c, n, tyRef, prev)
of nkPtrTy: result = semAnyRef(c, n, tyPtr, prev)
of nkVarTy: result = semVarType(c, n, prev)
of nkVarTy: result = semVarOutType(c, n, prev, tyVar)
of nkDistinctTy: result = semDistinct(c, n, prev)
of nkStaticTy: result = semStaticType(c, n[0], prev)
of nkIteratorTy:

View file

@ -1902,7 +1902,7 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
if destIsGeneric:
dest = generateTypeInstance(c, m.bindings, arg, dest)
let fdest = typeRel(m, f, dest)
if fdest in {isEqual, isGeneric} and not (dest.kind == tyLent and f.kind == tyVar):
if fdest in {isEqual, isGeneric} and not (dest.kind == tyLent and f.kind in {tyVar}):
markUsed(c, arg.info, c.converters[i])
var s = newSymNode(c.converters[i])
s.typ = c.converters[i].typ
@ -1915,7 +1915,7 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
var param: PNode = nil
if srca == isSubtype:
param = implicitConv(nkHiddenSubConv, src, copyTree(arg), m, c)
elif src.kind == tyVar:
elif src.kind in {tyVar}:
# Analyse the converter return type
param = newNodeIT(nkHiddenAddr, arg.info, s.typ[1])
param.add copyTree(arg)
@ -2082,7 +2082,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
result = implicitConv(nkHiddenSubConv, f, arg, m, c)
of isSubrange:
inc(m.subtypeMatches)
if f.kind == tyVar:
if f.kind in {tyVar}:
result = arg
else:
result = implicitConv(nkHiddenStdConv, f, arg, m, c)
@ -2327,10 +2327,10 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
noMatch()
return
if formal.typ.kind == tyVar:
if formal.typ.kind in {tyVar}:
let argConverter = if arg.kind == nkHiddenDeref: arg[0] else: arg
if argConverter.kind == nkHiddenCallConv:
if argConverter.typ.kind != tyVar:
if argConverter.typ.kind notin {tyVar}:
m.firstMismatch.kind = kVarNeeded
noMatch()
return
@ -2604,7 +2604,7 @@ proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo;
if op == attachedDeepCopy:
if f.kind in {tyRef, tyPtr}: f = f.lastSon
else:
if f.kind == tyVar: f = f.lastSon
if f.kind in {tyVar}: f = f.lastSon
if typeRel(m, f, t) == isNone:
localError(c.config, info, "cannot instantiate: '" & dc.name.s & "'")
else:

View file

@ -37,7 +37,7 @@ proc spawnResult*(t: PType; inParallel: bool): TSpawnResult =
else: srFlowVar
proc flowVarKind(c: ConfigRef, t: PType): TFlowVarKind =
if c.selectedGC in {gcArc, gcOrc}: fvBlob
if c.selectedGC in {gcArc, gcOrc}: fvBlob
elif t.skipTypes(abstractInst).kind in {tyRef, tyString, tySequence}: fvGC
elif containsGarbageCollectedRef(t): fvInvalid
else: fvBlob
@ -201,7 +201,7 @@ proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType;
# we pick n's type here, which hopefully is 'tyArray' and not
# 'tyOpenArray':
var argType = n[i].typ.skipTypes(abstractInst)
if i < formals.len:
if i < formals.len:
if formals[i].typ.kind in {tyVar, tyLent}:
localError(g.config, n[i].info, "'spawn'ed function cannot have a 'var' parameter")
if formals[i].typ.kind in {tyTypeDesc, tyStatic}:

View file

@ -423,7 +423,7 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PNode =
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
result.typ = n.typ
elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
result.typ = toVar(result.typ)
result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind)
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
var m = n[0][1]
if m.kind == a or m.kind == b:
@ -433,7 +433,7 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PNode =
if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
result.typ = n.typ
elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
result.typ = toVar(result.typ)
result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind)
else:
if n[0].kind == a or n[0].kind == b:
# addr ( deref ( x )) --> x
@ -569,14 +569,14 @@ proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
result = putArgInto(arg[0], formal)
of nkCurly, nkBracket:
for i in 0..<arg.len:
if putArgInto(arg[i], formal) != paDirectMapping:
if putArgInto(arg[i], formal) != paDirectMapping:
return paFastAsgn
result = paDirectMapping
of nkPar, nkTupleConstr, nkObjConstr:
for i in 0..<arg.len:
let a = if arg[i].kind == nkExprColonExpr: arg[i][1]
else: arg[0]
if putArgInto(a, formal) != paDirectMapping:
if putArgInto(a, formal) != paDirectMapping:
return paFastAsgn
result = paDirectMapping
else:
@ -667,7 +667,7 @@ proc transformFor(c: PTransf, n: PNode): PNode =
stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg))
idNodeTablePut(newC.mapping, formal, temp)
of paVarAsgn:
assert(skipTypes(formal.typ, abstractInst).kind == tyVar)
assert(skipTypes(formal.typ, abstractInst).kind in {tyVar})
idNodeTablePut(newC.mapping, formal, arg)
# XXX BUG still not correct if the arg has a side effect!
of paComplexOpenarray:

View file

@ -438,7 +438,7 @@ const
"lent ", "varargs[$1]", "UncheckedArray[$1]", "Error Type",
"BuiltInTypeClass", "UserTypeClass",
"UserTypeClassInst", "CompositeTypeClass", "inferred",
"and", "or", "not", "any", "static", "TypeFromExpr", "FieldAccessor",
"and", "or", "not", "any", "static", "TypeFromExpr", "out ",
"void"]
const preferToResolveSymbols = {preferName, preferTypeName, preferModuleInfo,

View file

@ -85,7 +85,7 @@ proc allRoots(n: PNode; result: var seq[ptr TSym]; info: var set[RootInfo]) =
assert(typ.n[i].kind == nkSym)
let paramType = typ.n[i]
if paramType.typ.isCompileTimeOnly: continue
if sfEscapes in paramType.sym.flags or paramType.typ.kind == tyVar:
if sfEscapes in paramType.sym.flags or paramType.typ.kind in {tyVar}:
allRoots(it, result, info)
else:
allRoots(it, result, info)
@ -164,7 +164,7 @@ proc depsArgs(w: var W; n: PNode) =
let paramType = typ.n[i]
if paramType.typ.isCompileTimeOnly: continue
var destInfo: set[RootInfo] = {}
if sfWrittenTo in paramType.sym.flags or paramType.typ.kind == tyVar:
if sfWrittenTo in paramType.sym.flags or paramType.typ.kind in {tyVar}:
# p(f(x, y), X, g(h, z))
destInfo.incl markAsWrittenTo
if sfEscapes in paramType.sym.flags:
@ -247,7 +247,7 @@ proc markWriteOrEscape(w: var W; conf: ConfigRef) =
for p in a.dest:
if p.kind == skParam and p.owner == w.owner:
incl(p.flags, sfWrittenTo)
if w.owner.kind == skFunc and p.typ.kind != tyVar:
if w.owner.kind == skFunc and p.typ.kind notin {tyVar}:
localError(conf, a.info, "write access to non-var parameter: " & p.name.s)
if {rootIsResultOrParam, rootIsHeapAccess, markAsEscaping}*a.destInfo != {}: