diff options
author | Araq <rumpf_a@web.de> | 2013-06-09 23:29:43 +0200 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2013-06-09 23:29:43 +0200 |
commit | 23ef565a3c2c1f83816fefbeadb0fc59e754997d (patch) | |
tree | 7a2adeeb6f3215c35ff0b7fdeeee6f9907308931 | |
parent | 2aaa8f7909e51eb3d971e197f152e247c64362e9 (diff) | |
download | Nim-23ef565a3c2c1f83816fefbeadb0fc59e754997d.tar.gz |
implemented large parts of the 'not nil' checking
-rw-r--r-- | compiler/ast.nim | 29 | ||||
-rw-r--r-- | compiler/magicsys.nim | 16 | ||||
-rw-r--r-- | compiler/msgs.nim | 9 | ||||
-rw-r--r-- | compiler/parampatterns.nim | 2 | ||||
-rw-r--r-- | compiler/pragmas.nim | 10 | ||||
-rw-r--r-- | compiler/semexprs.nim | 25 | ||||
-rw-r--r-- | compiler/sempass2.nim | 68 | ||||
-rw-r--r-- | compiler/semstmts.nim | 2 | ||||
-rw-r--r-- | compiler/semtypes.nim | 67 | ||||
-rw-r--r-- | compiler/semtypinst.nim | 2 | ||||
-rw-r--r-- | compiler/transf.nim | 7 | ||||
-rw-r--r-- | compiler/trees.nim | 3 | ||||
-rw-r--r-- | compiler/wordrecg.nim | 4 | ||||
-rw-r--r-- | doc/manual.txt | 76 | ||||
-rw-r--r-- | doc/nimrodc.txt | 2 | ||||
-rw-r--r-- | lib/pure/times.nim | 36 | ||||
-rw-r--r-- | tests/reject/tuninit1.nim | 36 | ||||
-rw-r--r-- | todo.txt | 3 | ||||
-rw-r--r-- | tools/niminst/debcreation.nim | 4 | ||||
-rw-r--r-- | web/index.txt | 2 | ||||
-rw-r--r-- | web/news.txt | 2 |
21 files changed, 293 insertions, 112 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 6e5a17d99..f4b1b84f5 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -353,7 +353,7 @@ type nfSem # node has been checked for semantics TNodeFlags* = set[TNodeFlag] - TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 19) + TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 23) tfVarargs, # procedure has C styled varargs tfNoSideEffect, # procedure type does not allow side effects tfFinal, # is the object final? @@ -380,7 +380,13 @@ type tfByRef, # pass object/tuple by reference (C backend) tfIterator, # type is really an iterator, not a tyProc tfShared, # type is 'shared' - tfNotNil # type cannot be 'nil' + tfNotNil, # type cannot be 'nil' + + tfNeedsInit, # type constains a "not nil" constraint somewhere or some + # other type so that it requires inititalization + tfHasShared, # type constains a "shared" constraint modifier somewhere + tfHasMeta, # type has "typedesc" or "expr" somewhere + tfHasGCedMem, # type contains GC'ed memory TTypeFlags* = set[TTypeFlag] @@ -1168,14 +1174,25 @@ proc newSons(father: PNode, length: int) = else: setlen(father.sons, length) -proc addSon*(father, son: PType) {.deprecated.} = - if isNil(father.sons): father.sons = @[] - add(father.sons, son) - #assert((father.kind != tyGenericInvokation) or (son.kind != tyGenericInst)) +proc propagateToOwner*(owner, elem: PType) = + owner.flags = owner.flags + (elem.flags * {tfNeedsInit, tfHasShared, + tfHasMeta, tfHasGCedMem}) + if tfNotNil in elem.flags: + owner.flags.incl tfNeedsInit + + if tfShared in elem.flags: + owner.flags.incl tfHasShared + + if elem.kind in {tyExpr, tyTypeDesc}: + owner.flags.incl tfHasMeta + elif elem.kind in {tyString, tyRef, tySequence} or + elem.kind == tyProc and elem.callConv == ccClosure: + owner.flags.incl tfHasGCedMem proc rawAddSon*(father, son: PType) = if isNil(father.sons): father.sons = @[] add(father.sons, son) + if not son.isNil: propagateToOwner(father, son) proc addSon(father, son: PNode) = assert son != nil diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim index 352b6ca04..1972dec98 100644 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -43,6 +43,18 @@ proc getSysSym(name: string): PSym = result.typ = newType(tyError, systemModule) if result.kind == skStub: loadStub(result) +proc getSysMagic*(name: string, m: TMagic): PSym = + var ti: TIdentIter + let id = getIdent(name) + result = InitIdentIter(ti, systemModule.tab, id) + while result != nil: + if result.kind == skStub: loadStub(result) + if result.magic == m: return result + result = NextIdentIter(ti, systemModule.tab) + rawMessage(errSystemNeeds, name) + result = newSym(skError, id, systemModule, systemModule.info) + result.typ = newType(tyError, systemModule) + proc sysTypeFromName*(name: string): PType = result = getSysSym(name).typ @@ -111,7 +123,9 @@ proc skipIntLit*(t: PType): PType {.inline.} = proc AddSonSkipIntLit*(father, son: PType) = if isNil(father.sons): father.sons = @[] - add(father.sons, son.skipIntLit) + let s = son.skipIntLit + add(father.sons, s) + propagateToOwner(father, s) proc setIntLitType*(result: PNode) = let i = result.intVal diff --git a/compiler/msgs.nim b/compiler/msgs.nim index f75aec0d5..302a7cfd8 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -106,7 +106,7 @@ type warnUnknownSubstitutionX, warnLanguageXNotSupported, warnCommentXIgnored, warnNilStatement, warnAnalysisLoophole, warnDifferentHeaps, warnWriteToForeignHeap, warnImplicitClosure, - warnEachIdentIsTuple, warnShadowIdent, warnUninit, warnUser, + warnEachIdentIsTuple, warnShadowIdent, warnProveInit, warnUninit, warnUser, hintSuccess, hintSuccessX, hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, @@ -355,7 +355,8 @@ const warnImplicitClosure: "implicit closure convention: '$1' [ImplicitClosure]", warnEachIdentIsTuple: "each identifier is a tuple [EachIdentIsTuple]", warnShadowIdent: "shadowed identifier: '$1' [ShadowIdent]", - warnUninit: "read from potentially uninitialized variable: '$1' [Uninit]", + warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future. [ProveInit]", + warnUninit: "'$1' might not have been initialized [Uninit]", warnUser: "$1 [User]", hintSuccess: "operation successful [Success]", hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#) [SuccessX]", @@ -375,14 +376,14 @@ const hintUser: "$1 [User]"] const - WarningsToStr*: array[0..20, string] = ["CannotOpenFile", "OctalEscape", + WarningsToStr*: array[0..21, string] = ["CannotOpenFile", "OctalEscape", "XIsNeverRead", "XmightNotBeenInit", "Deprecated", "ConfigDeprecated", "SmallLshouldNotBeUsed", "UnknownMagic", "RedefinitionOfLabel", "UnknownSubstitutionX", "LanguageXNotSupported", "CommentXIgnored", "NilStmt", "AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap", - "ImplicitClosure", "EachIdentIsTuple", "ShadowIdent", "Uninit", + "ImplicitClosure", "EachIdentIsTuple", "ShadowIdent", "ProveInit", "Uninit", "User"] HintsToStr*: array[0..15, string] = ["Success", "SuccessX", "LineTooLong", diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index de7bcaeee..283f83906 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -50,6 +50,8 @@ proc add(code: var TPatternCode, op: TOpcode) {.inline.} = proc whichAlias*(p: PSym): TAliasRequest = if p.constraint != nil: result = TAliasRequest(p.constraint.strVal[0].ord) + else: + result = aqNone proc compileConstraints(p: PNode, result: var TPatternCode) = case p.kind diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index cc432aea8..cecec8e5e 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -50,7 +50,7 @@ const typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl, wPure, wHeader, wCompilerProc, wFinal, wSize, wExtern, wShallow, wImportcpp, wImportobjc, wError, wIncompleteStruct, wByCopy, wByRef, - wInheritable, wGenSym, wInject} + wInheritable, wGenSym, wInject, wRequiresInit} fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern, wImportcpp, wImportobjc, wError} varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, @@ -253,11 +253,11 @@ proc processNote(c: PContext, n: PNode) = of wHint: var x = findStr(msgs.HintsToStr, n.sons[0].sons[1].ident.s) if x >= 0: nk = TNoteKind(x + ord(hintMin)) - else: invalidPragma(n) + else: invalidPragma(n); return of wWarning: var x = findStr(msgs.WarningsToStr, n.sons[0].sons[1].ident.s) if x >= 0: nk = TNoteKind(x + ord(warnMin)) - else: InvalidPragma(n) + else: InvalidPragma(n); return else: invalidPragma(n) return @@ -695,6 +695,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, noVal(it) if sym.typ == nil: invalidPragma(it) else: incl(sym.typ.flags, tfIncompleteStruct) + of wRequiresInit: + noVal(it) + if sym.typ == nil: invalidPragma(it) + else: incl(sym.typ.flags, tfNeedsInit) of wByRef: noVal(it) if sym == nil or sym.typ == nil: diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index fba028a46..cdab9b535 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1601,6 +1601,25 @@ proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = addSonSkipIntLit(typ, n.sons[i].typ) result.typ = typ +proc checkInitialized(n: PNode, ids: TIntSet, info: TLineInfo) = + case n.kind + of nkRecList: + for i in countup(0, sonsLen(n) - 1): + checkInitialized(n.sons[i], ids, info) + of nkRecCase: + if (n.sons[0].kind != nkSym): InternalError(info, "checkInitialized") + checkInitialized(n.sons[0], ids, info) + when false: + # XXX we cannot check here, as we don't know the branch! + for i in countup(1, sonsLen(n) - 1): + case n.sons[i].kind + of nkOfBranch, nkElse: checkInitialized(lastSon(n.sons[i]), ids, info) + else: internalError(info, "checkInitialized") + of nkSym: + if tfNeedsInit in n.sym.typ.flags and n.sym.name.id notin ids: + Message(info, errGenerated, "field not initialized: " & n.sym.name.s) + else: internalError(info, "checkInitialized") + proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = var t = semTypeNode(c, n.sons[0], nil) result = n @@ -1611,6 +1630,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = if t.kind != tyObject: localError(n.info, errGenerated, "object constructor needs an object type") return + var objType = t var ids = initIntSet() for i in 1.. <n.len: let it = n.sons[i] @@ -1642,6 +1662,11 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = localError(it.info, errUndeclaredFieldX, id.s) it.sons[1] = e # XXX object field name check for 'case objects' if the kind is static? + if tfNeedsInit in objType.flags: + while true: + checkInitialized(objType.n, ids, n.info) + if objType.sons[0] == nil: break + objType = skipTypes(objType.sons[0], {tyGenericInst}) proc semBlock(c: PContext, n: PNode): PNode = result = n diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 64aae09c1..151cb9cbc 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -9,7 +9,7 @@ import intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, - wordrecg, strutils, options + wordrecg, strutils, options, guards # Second semantic checking pass over the AST. Necessary because the old # way had some inherent problems. Performs: @@ -75,7 +75,7 @@ type owner: PSym init: seq[int] # list of initialized variables # coming soon: "guard" tracking for 'let' variables - + guards: TModel # nested guards PEffects = var TEffects proc isLocalVar(a: PEffects, s: PSym): bool = @@ -93,11 +93,14 @@ proc useVar(a: PEffects, n: PNode) = let s = n.sym if isLocalVar(a, s): if s.id notin a.init: - if true: - Message(n.info, warnUninit, s.name.s) + if tfNeedsInit in s.typ.flags: + when true: + Message(n.info, warnProveInit, s.name.s) + else: + Message(n.info, errGenerated, + "'$1' might not have been initialized" % s.name.s) else: - Message(n.info, errGenerated, - "read from potentially uninitialized variable: '$1'" % s.name.s) + Message(n.info, warnUninit, s.name.s) # prevent superfluous warnings about the same variable: a.init.add s.id @@ -295,7 +298,7 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) = let tagSpec = effectSpec(pragma, wTags) mergeTags(tracked, tagSpec, n) -proc trackOperand(tracked: PEffects, n: PNode) = +proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) = let op = n.typ if op != nil and op.kind == tyProc and n.kind != nkNilLit: InternalAssert op.n.sons[0].kind == nkEffectList @@ -312,16 +315,40 @@ proc trackOperand(tracked: PEffects, n: PNode) = else: mergeEffects(tracked, effectList.sons[exceptionEffects], n) mergeTags(tracked, effectList.sons[tagEffects], n) + if paramType != nil and tfNotNil in paramType.flags and + op != nil and tfNotNil notin op.flags: + case impliesNotNil(tracked.guards, n) + of impUnknown: + Message(n.info, errGenerated, + "cannot prove '$1' is not nil" % n.renderTree) + of impNo: + Message(n.info, errGenerated, "'$1' is provably nil" % n.renderTree) + of impYes: discard + +proc breaksBlock(n: PNode): bool = + case n.kind + of nkStmtList, nkStmtListExpr: + for c in n: + if breaksBlock(c): return true + of nkBreakStmt, nkReturnStmt, nkRaiseStmt: + return true + of nkCallKinds: + if n.sons[0].kind == nkSym and sfNoReturn in n.sons[0].sym.flags: + return true + else: + discard proc trackCase(tracked: PEffects, n: PNode) = track(tracked, n.sons[0]) let oldState = tracked.init.len var inter: TIntersection = @[] + var toCover = 0 for i in 1.. <n.len: let branch = n.sons[i] setLen(tracked.init, oldState) for i in 0 .. <branch.len: track(tracked, branch.sons[i]) + if not breaksBlock(branch.lastSon): inc toCover for i in oldState.. <tracked.init.len: addToIntersection(inter, tracked.init[i]) let exh = case skipTypes(n.sons[0].Typ, abstractVarRange-{tyTypeDesc}).Kind @@ -332,30 +359,41 @@ proc trackCase(tracked: PEffects, n: PNode) = setLen(tracked.init, oldState) if exh: for id, count in items(inter): - if count == n.len-1: tracked.init.add id + if count >= toCover: tracked.init.add id # else we can't merge proc trackIf(tracked: PEffects, n: PNode) = track(tracked, n.sons[0].sons[0]) + let oldFacts = tracked.guards.len + addFact(tracked.guards, n.sons[0].sons[0]) let oldState = tracked.init.len var inter: TIntersection = @[] + var toCover = 0 track(tracked, n.sons[0].sons[1]) + if not breaksBlock(n.sons[0].sons[1]): inc toCover for i in oldState.. <tracked.init.len: addToIntersection(inter, tracked.init[i]) for i in 1.. <n.len: let branch = n.sons[i] + setLen(tracked.guards, oldFacts) + for j in 0..i-1: + addFactNeg(tracked.guards, n.sons[j].sons[0]) + if branch.len > 1: + addFact(tracked.guards, branch.sons[0]) setLen(tracked.init, oldState) for i in 0 .. <branch.len: track(tracked, branch.sons[i]) + if not breaksBlock(branch.lastSon): inc toCover for i in oldState.. <tracked.init.len: addToIntersection(inter, tracked.init[i]) setLen(tracked.init, oldState) if lastSon(n).len == 1: for id, count in items(inter): - if count == n.len: tracked.init.add id + if count >= toCover: tracked.init.add id # else we can't merge as it is not exhaustive + setLen(tracked.guards, oldFacts) proc trackBlock(tracked: PEffects, n: PNode) = if n.kind in {nkStmtList, nkStmtListExpr}: @@ -377,6 +415,9 @@ proc isTrue(n: PNode): bool = n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or n.kind == nkIntLit and n.intVal != 0 +proc paramType(op: PType, i: int): PType = + if op != nil and i < op.len: result = op.sons[i] + proc track(tracked: PEffects, n: PNode) = case n.kind of nkSym: @@ -404,11 +445,12 @@ proc track(tracked: PEffects, n: PNode) = else: mergeEffects(tracked, effectList.sons[exceptionEffects], n) mergeTags(tracked, effectList.sons[tagEffects], n) - for i in 1 .. <len(n): trackOperand(tracked, n.sons[i]) + for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i)) if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq, mShallowCopy}: # may not look like an assignment, but it is: initVar(tracked, n.sons[1]) + # XXX new(objWithNotNil) is not initialized properly! for i in 0 .. <safeLen(n): track(tracked, n.sons[i]) of nkTryStmt: trackTryStmt(tracked, n) @@ -510,8 +552,14 @@ proc trackProc*(s: PSym, body: PNode) = t.tags = effects.sons[tagEffects] t.owner = s t.init = @[] + t.guards = @[] track(t, body) + if not isEmptyType(s.typ.sons[0]) and tfNeedsInit in s.typ.sons[0].flags and + s.kind in {skProc, skConverter, skMethod}: + var res = s.ast.sons[resultPos].sym # get result symbol + if res.id notin t.init: + Message(body.info, warnProveInit, "result") let p = s.ast.sons[pragmasPos] let raisesSpec = effectSpec(p, wRaises) if not isNil(raisesSpec): diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 6123957cd..75ab9920d 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -374,7 +374,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = if warnShadowIdent in gNotes and not identWithin(def, v.name): Message(a.info, warnShadowIdent, v.name.s) if def != nil and def.kind != nkEmpty: - # this is only needed for the evaluation pass: + # this is needed for the evaluation pass and for the guard checking: v.ast = def if sfThread in v.flags: LocalError(def.info, errThreadvarCannotInit) if a.kind != nkVarTuple: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 658b3507f..769e2ab7d 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -23,7 +23,7 @@ proc newConstraint(c: PContext, k: TTypeKind): PType = proc semEnum(c: PContext, n: PNode, prev: PType): PType = if n.sonsLen == 0: return newConstraint(c, tyEnum) - var + var counter, x: BiggestInt e: PSym base: PType @@ -39,6 +39,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = counter = lastOrd(base) + 1 rawAddSon(result, base) let isPure = result.sym != nil and sfPure in result.sym.flags + var hasNull = false for i in countup(1, sonsLen(n) - 1): case n.sons[i].kind of nkEnumFieldDef: @@ -74,6 +75,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = else: illFormedAst(n) e.typ = result e.position = int(counter) + if e.position == 0: hasNull = true if result.sym != nil and sfExported in result.sym.flags: incl(e.flags, sfUsed) incl(e.flags, sfExported) @@ -81,6 +83,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = addSon(result.n, newSymNode(e)) if sfGenSym notin e.flags and not isPure: addDecl(c, e) inc(counter) + if not hasNull: incl(result.flags, tfNeedsInit) proc semSet(c: PContext, n: PNode, prev: PType): PType = result = newOrPrevType(tySet, prev, c) @@ -168,7 +171,14 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = proc semRange(c: PContext, n: PNode, prev: PType): PType = result = nil if sonsLen(n) == 2: - if isRange(n[1]): result = semRangeAux(c, n[1], prev) + if isRange(n[1]): + result = semRangeAux(c, n[1], prev) + let n = result.n + if n.sons[0].kind in {nkCharLit..nkUInt64Lit}: + if n.sons[0].intVal > 0 or n.sons[1].intVal < 0: + incl(result.flags, tfNeedsInit) + elif n.sons[0].floatVal > 0.0 or n.sons[1].floatVal < 0.0: + incl(result.flags, tfNeedsInit) else: LocalError(n.sons[0].info, errRangeExpected) result = newOrPrevType(tyError, prev, c) @@ -386,34 +396,34 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int, swap(branch.sons[L-2], branch.sons[L-1]) checkForOverlap(c, t, i, branchIndex) -proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int, - father: PNode, rectype: PSym) -proc semRecordCase(c: PContext, n: PNode, check: var TIntSet, pos: var int, - father: PNode, rectype: PSym) = +proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int, + father: PNode, rectype: PType) +proc semRecordCase(c: PContext, n: PNode, check: var TIntSet, pos: var int, + father: PNode, rectype: PType) = var a = copyNode(n) checkMinSonsLen(n, 2) semRecordNodeAux(c, n.sons[0], check, pos, a, rectype) - if a.sons[0].kind != nkSym: + if a.sons[0].kind != nkSym: internalError("semRecordCase: discriminant is no symbol") return incl(a.sons[0].sym.flags, sfDiscriminant) var covered: biggestInt = 0 var typ = skipTypes(a.sons[0].Typ, abstractVar-{tyTypeDesc}) - if not isOrdinalType(typ): + if not isOrdinalType(typ): LocalError(n.info, errSelectorMustBeOrdinal) - elif firstOrd(typ) < 0: + elif firstOrd(typ) < 0: LocalError(n.info, errOrdXMustNotBeNegative, a.sons[0].sym.name.s) - elif lengthOrd(typ) > 0x00007FFF: + elif lengthOrd(typ) > 0x00007FFF: LocalError(n.info, errLenXinvalid, a.sons[0].sym.name.s) var chckCovered = true - for i in countup(1, sonsLen(n) - 1): + for i in countup(1, sonsLen(n) - 1): var b = copyTree(n.sons[i]) addSon(a, b) case n.sons[i].kind - of nkOfBranch: + of nkOfBranch: checkMinSonsLen(b, 2) semCaseBranch(c, a, b, i, covered) - of nkElse: + of nkElse: chckCovered = false checkSonsLen(b, 1) else: illFormedAst(n) @@ -424,7 +434,7 @@ proc semRecordCase(c: PContext, n: PNode, check: var TIntSet, pos: var int, addSon(father, a) proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int, - father: PNode, rectype: PSym) = + father: PNode, rectype: PType) = if n == nil: return case n.kind of nkRecWhen: @@ -463,7 +473,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int, semRecordCase(c, n, check, pos, father, rectype) of nkNilLit: if father.kind != nkRecList: addSon(father, newNodeI(nkRecList, n.info)) - of nkRecList: + of nkRecList: # attempt to keep the nesting at a sane level: var a = if father.kind == nkRecList: father else: copyNode(n) for i in countup(0, sonsLen(n) - 1): @@ -473,7 +483,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int, checkMinSonsLen(n, 3) var length = sonsLen(n) var a: PNode - if father.kind != nkRecList and length >= 4: a = newNodeI(nkRecList, n.info) + if father.kind != nkRecList and length>=4: a = newNodeI(nkRecList, n.info) else: a = ast.emptyNode if n.sons[length-1].kind != nkEmpty: localError(n.sons[length-1].info, errInitHereNotAllowed) @@ -483,17 +493,19 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int, typ = errorType(c) else: typ = semTypeNode(c, n.sons[length-2], nil) + propagateToOwner(rectype, typ) + let rec = rectype.sym for i in countup(0, sonsLen(n)-3): var f = semIdentWithPragma(c, skField, n.sons[i], {sfExported}) suggestSym(n.sons[i], f) f.typ = typ f.position = pos - if (rectype != nil) and ({sfImportc, sfExportc} * rectype.flags != {}) and + if (rec != nil) and ({sfImportc, sfExportc} * rec.flags != {}) and (f.loc.r == nil): f.loc.r = toRope(f.name.s) - f.flags = f.flags + ({sfImportc, sfExportc} * rectype.flags) + f.flags = f.flags + ({sfImportc, sfExportc} * rec.flags) inc(pos) - if ContainsOrIncl(check, f.name.id): + if ContainsOrIncl(check, f.name.id): localError(n.sons[i].info, errAttemptToRedefine, f.name.s) if a.kind == nkEmpty: addSon(father, newSymNode(f)) else: addSon(a, newSymNode(f)) @@ -502,20 +514,20 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int, else: illFormedAst(n) proc addInheritedFieldsAux(c: PContext, check: var TIntSet, pos: var int, - n: PNode) = + n: PNode) = case n.kind - of nkRecCase: + of nkRecCase: if (n.sons[0].kind != nkSym): InternalError(n.info, "addInheritedFieldsAux") addInheritedFieldsAux(c, check, pos, n.sons[0]) - for i in countup(1, sonsLen(n) - 1): + for i in countup(1, sonsLen(n) - 1): case n.sons[i].kind - of nkOfBranch, nkElse: + of nkOfBranch, nkElse: addInheritedFieldsAux(c, check, pos, lastSon(n.sons[i])) else: internalError(n.info, "addInheritedFieldsAux(record case branch)") - of nkRecList: - for i in countup(0, sonsLen(n) - 1): + of nkRecList: + for i in countup(0, sonsLen(n) - 1): addInheritedFieldsAux(c, check, pos, n.sons[i]) - of nkSym: + of nkSym: Incl(check, n.sym.name.id) inc(pos) else: InternalError(n.info, "addInheritedFieldsAux()") @@ -553,7 +565,7 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType = result = newOrPrevType(tyObject, prev, c) rawAddSon(result, base) result.n = newNodeI(nkRecList, n.info) - semRecordNodeAux(c, n.sons[2], check, pos, result.n, result.sym) + semRecordNodeAux(c, n.sons[2], check, pos, result.n, result) if n.sons[0].kind != nkEmpty: # dummy symbol for `pragma`: var s = newSymS(skType, newIdentNode(getIdent("dummy"), n.info), c) @@ -853,6 +865,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = if result.kind in NilableTypes and n.sons[2].kind == nkNilLit: result = freshType(result, prev) result.flags.incl(tfNotNil) + result.flags.incl(tfNeedsInit) else: LocalError(n.info, errGenerated, "invalid type") else: diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 26341525c..31fbc33e1 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -152,6 +152,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = x = lookupTypeVar(cl, x) if header == nil: header = copyType(t, t.owner, false) header.sons[i] = x + propagateToOwner(header, x) #idTablePut(cl.typeMap, body.sons[i-1], x) if header != nil: # search again after first pass: @@ -170,6 +171,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = var x = replaceTypeVarsT(cl, t.sons[i]) assert x.kind != tyGenericInvokation header.sons[i] = x + propagateToOwner(header, x) idTablePut(cl.typeMap, body.sons[i-1], x) for i in countup(1, sonsLen(t) - 1): diff --git a/compiler/transf.nim b/compiler/transf.nim index 058143cdd..a35669e1d 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -504,8 +504,8 @@ proc transformCase(c: PTransf, n: PNode): PTransNode = result.add(elseBranch) elif result.Pnode.lastSon.kind != nkElse and not ( skipTypes(n.sons[0].Typ, abstractVarRange).Kind in - {tyInt..tyInt64, tyChar, tyEnum}): - # fix a stupid code gen bug by normalizing: + {tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32}): + # fix a stupid code gen bug by normalizing: var elseBranch = newTransNode(nkElse, n.info, 1) elseBranch[0] = newTransNode(nkNilLit, n.info, 0) add(result, elseBranch) @@ -704,7 +704,7 @@ proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode = if nfTransf in n.flags or prc.kind in {skTemplate, skMacro}: result = n else: - when useEffectSystem: trackProc(prc, n) + #when useEffectSystem: trackProc(prc, n) var c = openTransf(module, "") result = processTransf(c, n) if prc.kind != skMacro: @@ -713,6 +713,7 @@ proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode = if prc.kind == skIterator and prc.typ.callConv == ccClosure: result = lambdalifting.liftIterator(prc, result) incl(result.flags, nfTransf) + when useEffectSystem: trackProc(prc, result) proc transformStmt*(module: PSym, n: PNode): PNode = if nfTransf in n.flags: diff --git a/compiler/trees.nim b/compiler/trees.nim index f371cb021..ab5c97a19 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -93,8 +93,7 @@ proc getOpSym*(op: PNode): PSym = proc getMagic*(op: PNode): TMagic = case op.kind - of nkCall, nkHiddenCallConv, nkCommand, nkCallStrLit, nkPrefix, nkPostfix, - nkInfix: + of nkCallKinds: case op.sons[0].Kind of nkSym: result = op.sons[0].sym.magic else: result = mNone diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 36d08c718..2a8a02bd6 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -41,7 +41,7 @@ type wImmediate, wDestructor, wImportCpp, wImportObjC, wImportCompilerProc, - wImportc, wExportc, wIncompleteStruct, + wImportc, wExportc, wIncompleteStruct, wRequiresInit, wAlign, wNodecl, wPure, wSideeffect, wHeader, wNosideeffect, wNoreturn, wMerge, wLib, wDynlib, wCompilerproc, wProcVar, wFatal, wError, wWarning, wHint, wLine, wPush, wPop, wDefine, wUndef, @@ -122,7 +122,7 @@ const "immediate", "destructor", "importcpp", "importobjc", "importcompilerproc", "importc", "exportc", "incompletestruct", - "align", "nodecl", "pure", "sideeffect", + "requiresinit", "align", "nodecl", "pure", "sideeffect", "header", "nosideeffect", "noreturn", "merge", "lib", "dynlib", "compilerproc", "procvar", "fatal", "error", "warning", "hint", "line", "push", "pop", "define", "undef", "linedir", "stacktrace", "linetrace", diff --git a/doc/manual.txt b/doc/manual.txt index f163e0d5f..7147eb631 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -327,36 +327,36 @@ Numerical constants `Numerical constants`:idx: are of a single type and have the form:: - hexdigit ::= digit | 'A'..'F' | 'a'..'f' - octdigit ::= '0'..'7' - bindigit ::= '0'..'1' - HEX_LIT ::= '0' ('x' | 'X' ) hexdigit ( ['_'] hexdigit )* - DEC_LIT ::= digit ( ['_'] digit )* - OCT_LIT ::= '0o' octdigit ( ['_'] octdigit )* - BIN_LIT ::= '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )* + hexdigit = digit | 'A'..'F' | 'a'..'f' + octdigit = '0'..'7' + bindigit = '0'..'1' + HEX_LIT = '0' ('x' | 'X' ) hexdigit ( ['_'] hexdigit )* + DEC_LIT = digit ( ['_'] digit )* + OCT_LIT = '0o' octdigit ( ['_'] octdigit )* + BIN_LIT = '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )* - INT_LIT ::= HEX_LIT - | DEC_LIT - | OCT_LIT - | BIN_LIT - - INT8_LIT ::= INT_LIT ['\''] ('i' | 'I') '8' - INT16_LIT ::= INT_LIT ['\''] ('i' | 'I') '16' - INT32_LIT ::= INT_LIT ['\''] ('i' | 'I') '32' - INT64_LIT ::= INT_LIT ['\''] ('i' | 'I') '64' - - UINT8_LIT ::= INT_LIT ['\''] ('u' | 'U') - UINT8_LIT ::= INT_LIT ['\''] ('u' | 'U') '8' - UINT16_LIT ::= INT_LIT ['\''] ('u' | 'U') '16' - UINT32_LIT ::= INT_LIT ['\''] ('u' | 'U') '32' - UINT64_LIT ::= INT_LIT ['\''] ('u' | 'U') '64' - - exponent ::= ('e' | 'E' ) ['+' | '-'] digit ( ['_'] digit )* - FLOAT_LIT ::= digit (['_'] digit)* ('.' (['_'] digit)* [exponent] |exponent) - FLOAT32_LIT ::= HEX_LIT '\'' ('f'|'F') '32' - | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] ('f'|'F') '32' - FLOAT64_LIT ::= HEX_LIT '\'' ('f'|'F') '64' - | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] ('f'|'F') '64' + INT_LIT = HEX_LIT + | DEC_LIT + | OCT_LIT + | BIN_LIT + + INT8_LIT = INT_LIT ['\''] ('i' | 'I') '8' + INT16_LIT = INT_LIT ['\''] ('i' | 'I') '16' + INT32_LIT = INT_LIT ['\''] ('i' | 'I') '32' + INT64_LIT = INT_LIT ['\''] ('i' | 'I') '64' + + UINT8_LIT = INT_LIT ['\''] ('u' | 'U') + UINT8_LIT = INT_LIT ['\''] ('u' | 'U') '8' + UINT16_LIT = INT_LIT ['\''] ('u' | 'U') '16' + UINT32_LIT = INT_LIT ['\''] ('u' | 'U') '32' + UINT64_LIT = INT_LIT ['\''] ('u' | 'U') '64' + + exponent = ('e' | 'E' ) ['+' | '-'] digit ( ['_'] digit )* + FLOAT_LIT = digit (['_'] digit)* ('.' (['_'] digit)* [exponent] |exponent) + FLOAT32_LIT = HEX_LIT '\'' ('f'|'F') '32' + | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] ('f'|'F') '32' + FLOAT64_LIT = HEX_LIT '\'' ('f'|'F') '64' + | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] ('f'|'F') '64' As can be seen in the productions, numerical constants can contain underscores @@ -1818,6 +1818,24 @@ If a proc is annotated with the ``noinit`` pragma this refers to its implicit proc returnUndefinedValue: int {.noinit.} = nil +The implicit initialization can be also prevented by the `requiresInit`:idx: +type pragma. The compiler requires an explicit initialization then. However +it does a `control flow analysis`:idx: to prove the variable has been +initialized and does not rely on syntactic properties: + +.. code-block:: nimrod + type + TMyObject = object {.requiresInit.} + + proc p() = + # the following is valid: + var x: TMyObject + if someCondition(): + x = a() + else: + x = a() + use x + let statement ------------- diff --git a/doc/nimrodc.txt b/doc/nimrodc.txt index 7f77a50d2..339cee382 100644 --- a/doc/nimrodc.txt +++ b/doc/nimrodc.txt @@ -20,7 +20,7 @@ on the different supported platforms. It is not a definition of the Nimrod programming language (therefore is the `manual <manual.html>`_). Nimrod is free software; it is licensed under the -`GNU General Public License <gpl.html>`_. +`MIT License <http://www.opensource.org/licenses/mit-license.php>`_. Compiler Usage diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 2eb8d692b..86609c8e3 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -338,24 +338,24 @@ when not defined(JS): const weekDays: array [0..6, TWeekDay] = [ dSun, dMon, dTue, dWed, dThu, dFri, dSat] - result.second = int(tm.second) - result.minute = int(tm.minute) - result.hour = int(tm.hour) - result.monthday = int(tm.monthday) - result.month = TMonth(tm.month) - result.year = tm.year + 1900'i32 - result.weekday = weekDays[int(tm.weekDay)] - result.yearday = int(tm.yearday) - result.isDST = tm.isDST > 0 - if local: - if result.isDST: - result.tzname = getTzname().DST - else: - result.tzname = getTzname().nonDST - else: - result.tzname = "UTC" - - result.timezone = if local: getTimezone() else: 0 + TTimeInfo(second: int(tm.second), + minute: int(tm.minute), + hour: int(tm.hour), + monthday: int(tm.monthday), + month: TMonth(tm.month), + year: tm.year + 1900'i32, + weekday: weekDays[int(tm.weekDay)], + yearday: int(tm.yearday), + isDST: tm.isDST > 0, + tzname: if local: + if tm.isDST > 0: + getTzname().DST + else: + getTzname().nonDST + else: + "UTC", + timezone: if local: getTimezone() else: 0 + ) proc timeInfoToTM(t: TTimeInfo): structTM = const diff --git a/tests/reject/tuninit1.nim b/tests/reject/tuninit1.nim new file mode 100644 index 000000000..e51f9ce7c --- /dev/null +++ b/tests/reject/tuninit1.nim @@ -0,0 +1,36 @@ +discard """ + errormsg: "'y' might not have been initialized" + line:28 +""" + +import strutils + +{.warning[Uninit]:on.} + +proc p = + var x, y, z: int + if stdin.readLine == "true": + x = 34 + + while false: + y = 999 + break + + while true: + if x == 12: break + y = 9999 + + try: + z = parseInt("1233") + except E_Base: + case x + of 34: z = 123 + of 13: z = 34 + else: z = 8 + else: + y = 3444 + x = 3111 + z = 0 + echo x, y, z + +p() diff --git a/todo.txt b/todo.txt index 2aea8660f..9a3377d1c 100644 --- a/todo.txt +++ b/todo.txt @@ -2,7 +2,8 @@ version 0.9.4 ============= - make 'bind' default for templates and introduce 'mixin'; -- implement full 'not nil' checking; range[1..3] needs the same mechanism +- test 'not nil' checking more +- prove field accesses; prove array accesses - special rule for ``[]=`` - ``=`` should be overloadable; requires specialization for ``=``; general lift mechanism in the compiler is already implemented for 'fields' diff --git a/tools/niminst/debcreation.nim b/tools/niminst/debcreation.nim index 1e08e5653..982bfaf3d 100644 --- a/tools/niminst/debcreation.nim +++ b/tools/niminst/debcreation.nim @@ -227,9 +227,9 @@ when isMainModule: #echo(createRules()) - prepDeb("nimrod", "0.8.14", "Dominik Picheta", "morfeusz8@gmail.com", + prepDeb("nimrod", "0.9.2", "Dominik Picheta", "morfeusz8@gmail.com", "The Nimrod compiler", "Compiler for the Nimrod programming language", - @[("bin/nimrod", "gpl2"), ("lib/*", "lgpl")], + @[("bin/nimrod", "MIT"), ("lib/*", "MIT")], @["bin/nimrod"], @["config/*"], @["doc/*"], @["lib/*"], "gcc (>= 4:4.3.2)", "gcc (>= 4:4.3.2)") diff --git a/web/index.txt b/web/index.txt index 260d303c8..e504b65c4 100644 --- a/web/index.txt +++ b/web/index.txt @@ -89,7 +89,7 @@ Nimrod plays nice with others * **The Nimrod Compiler can also generate C++ or Objective C for easier interfacing.** * There are lots of bindings: for example, bindings to GTK2, the Windows API, - the POSIX API, OpenGL, SDL, Cario, Python, Lua, TCL, X11, libzip, PCRE, + the POSIX API, OpenGL, SDL, Cairo, Python, Lua, TCL, X11, libzip, PCRE, libcurl, mySQL and SQLite are included in the standard distribution. * A C to Nimrod conversion utility: New bindings to C libraries are easily generated by ``c2nim``. diff --git a/web/news.txt b/web/news.txt index 4f932bc05..f5737ffa3 100644 --- a/web/news.txt +++ b/web/news.txt @@ -36,7 +36,7 @@ Language Additions - Arrays can now be declared with a single integer literal ``N`` instead of a range; the range is then ``0..N-1``. - +- Added ``requiresInit`` pragma to enforce explicit initialization. 2013-05-20 New website design! |