diff options
37 files changed, 759 insertions, 169 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/guards.nim b/compiler/guards.nim new file mode 100644 index 000000000..6d0bf6bc6 --- /dev/null +++ b/compiler/guards.nim @@ -0,0 +1,346 @@ +# +# +# The Nimrod Compiler +# (c) Copyright 2013 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements the 'implies' relation for guards. + +import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer + +const + someEq = {mEqI, mEqI64, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc, + mEqUntracedRef, mEqStr, mEqSet, mEqCString} + + # set excluded here as the semantics are vastly different: + someLe = {mLeI, mLeI64, mLeF64, mLeU, mLeU64, mLeEnum, + mLeCh, mLeB, mLePtr, mLeStr} + someLt = {mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum, + mLtCh, mLtB, mLtPtr, mLtStr} + + someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq} + + someIn = {mInRange, mInSet} + +proc isValue(n: PNode): bool = n.kind in {nkCharLit..nkNilLit} +proc isLocation(n: PNode): bool = not n.isValue +#n.kind in {nkSym, nkBracketExpr, nkDerefExpr, nkHiddenDeref, nkDotExpr} + +proc isLet(n: PNode): bool = + if n.kind == nkSym: + # XXX allow skResult, skVar here if not re-bound + if n.sym.kind in {skLet, skTemp, skForVar}: + result = true + elif n.sym.kind == skParam and skipTypes(n.sym.typ, + abstractInst).kind != tyVar: + result = true + +proc isLetLocation(m: PNode): bool = + var n = m + while true: + case n.kind + of nkDotExpr, nkCheckedFieldExpr, nkObjUpConv, nkObjDownConv: + n = n.sons[0] + of nkBracketExpr: + if isConstExpr(n.sons[1]) or isLet(n.sons[1]): + n = n.sons[0] + else: return + of nkHiddenStdConv, nkHiddenSubConv, nkConv: + n = n.sons[1] + else: + break + result = n.isLet + +proc neg(n: PNode): PNode = + if n.getMagic == mNot: + result = n.sons[1] + else: + result = newNodeI(nkCall, n.info, 2) + result.sons[0] = newSymNode(getSysMagic("not", mNot)) + result.sons[1] = n + +proc usefulFact(n: PNode): PNode = + case n.getMagic + of someEq+someLe+someLt: + if isLetLocation(n.sons[1]) or n.len == 3 and isLetLocation(n.sons[2]): + # XXX algebraic simplifications! 'i-1 < a.len' --> 'i < a.len+1' + result = n + of someIn, mIsNil: + if isLetLocation(n.sons[1]): + result = n + of mAnd: + let + a = usefulFact(n.sons[1]) + b = usefulFact(n.sons[2]) + if a != nil and b != nil: + result = newNodeI(nkCall, n.info, 3) + result.sons[0] = newSymNode(getSysMagic("and", mAnd)) + result.sons[1] = a + result.sons[2] = b + elif a != nil: + result = a + elif b != nil: + result = b + of mNot: + case n.sons[1].getMagic + of mNot: + # normalize 'not (not a)' into 'a': + result = usefulFact(n.sons[1].sons[1]) + of mOr: + # not (a or b) --> not a and not b + let n = n.sons[1] + let + a = usefulFact(n.sons[1]) + b = usefulFact(n.sons[2]) + if a != nil and b != nil: + result = newNodeI(nkCall, n.info, 3) + result.sons[0] = newSymNode(getSysMagic("and", mAnd)) + result.sons[1] = a.neg + result.sons[2] = b.neg + else: + let a = usefulFact(n.sons[1]) + if a != nil: result = n + of mOr: + # 'or' sucks! (p.isNil or q.isNil) --> hard to do anything + # with that knowledge... + # DeMorgan helps a little though: + # not a or not b --> not (a and b) + # (x == 3) or (y == 2) ---> not ( not (x==3) and not (y == 2)) + # not (x != 3 and y != 2) + let + a = usefulFact(n.sons[1]) + b = usefulFact(n.sons[2]) + if a != nil and b != nil: + result = newNodeI(nkCall, n.info, 3) + result.sons[0] = newSymNode(getSysMagic("and", mAnd)) + result.sons[1] = a.neg + result.sons[2] = b.neg + result = result.neg + elif n.kind == nkSym and n.sym.kind == skLet: + # consider: + # let a = 2 < x + # if a: + # ... + # We make can easily replace 'a' by '2 < x' here: + result = usefulFact(n.sym.ast) + elif n.kind == nkStmtListExpr: + result = usefulFact(n.lastSon) + +type + TModel* = seq[PNode] # the "knowledge base" + +proc addFact*(m: var TModel, n: PNode) = + let n = usefulFact(n) + if n != nil: m.add n + +proc addFactNeg*(m: var TModel, n: PNode) = addFact(m, n.neg) + +proc sameTree(a, b: PNode): bool = + result = false + if a == b: + result = true + elif (a != nil) and (b != nil) and (a.kind == b.kind): + case a.kind + of nkSym: result = a.sym == b.sym + of nkIdent: result = a.ident.id == b.ident.id + of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal + of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal + of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal + of nkType: result = a.typ == b.typ + of nkEmpty, nkNilLit: result = true + else: + if sonsLen(a) == sonsLen(b): + for i in countup(0, sonsLen(a) - 1): + if not sameTree(a.sons[i], b.sons[i]): return + result = true + +proc valuesUnequal(a, b: PNode): bool = + if a.isValue and b.isValue: + result = not SameValue(a, b) + +type + TImplication* = enum + impUnknown, impNo, impYes + +proc impliesEq(fact, eq: PNode): TImplication = + let (loc, val) = if isLocation(eq.sons[1]): (1, 2) else: (2, 1) + + case fact.sons[0].sym.magic + of someEq: + if sameTree(fact.sons[1], eq.sons[loc]): + # this is not correct; consider: a == b; a == 1 --> unknown! + if sameTree(fact.sons[2], eq.sons[val]): result = impYes + elif valuesUnequal(fact.sons[2], eq.sons[val]): result = impNo + elif sameTree(fact.sons[2], eq.sons[loc]): + if sameTree(fact.sons[1], eq.sons[val]): result = impYes + elif valuesUnequal(fact.sons[1], eq.sons[val]): result = impNo + of mInSet: + if sameTree(fact.sons[1], eq.sons[loc]) and isValue(eq.sons[val]): + if inSet(fact.sons[2], eq.sons[val]): result = impYes + else: result = impNo + of mIsNil: + if sameTree(fact.sons[1], eq.sons[loc]): + if eq.sons[val].kind == nkNilLit: + result = impYes + of mNot, mOr, mAnd: internalError(eq.info, "impliesEq") + else: nil + +proc impliesIsNil(fact, eq: PNode): TImplication = + case fact.sons[0].sym.magic + of someEq: + if sameTree(fact.sons[1], eq.sons[1]): + if fact.sons[2].kind == nkNilLit: result = impYes + elif sameTree(fact.sons[2], eq.sons[1]): + if fact.sons[1].kind == nkNilLit: result = impYes + of mIsNil: + if sameTree(fact.sons[1], eq.sons[1]): + result = impYes + of mNot, mOr, mAnd: internalError(eq.info, "impliesIsNil") + else: nil + +proc pred(n: PNode): PNode = + if n.kind in {nkCharLit..nkUInt64Lit} and n.intVal != low(biggestInt): + result = copyNode(n) + dec result.intVal + else: + result = n + +proc impliesGe(fact, x, c: PNode): TImplication = + InternalAssert isLocation(x) + case fact.sons[0].sym.magic + of someEq: + if sameTree(fact.sons[1], x): + if isValue(fact.sons[2]) and isValue(c): + # fact: x = 4; question x >= 56? --> true iff 4 >= 56 + if leValue(c, fact.sons[2]): result = impYes + else: result = impNo + elif sameTree(fact.sons[2], x): + if isValue(fact.sons[1]) and isValue(c): + if leValue(c, fact.sons[1]): result = impYes + else: result = impNo + of someLt: + if sameTree(fact.sons[1], x): + if isValue(fact.sons[2]) and isValue(c): + # fact: x < 4; question N <= x? --> false iff N <= 4 + if leValue(fact.sons[2], c): result = impNo + # fact: x < 4; question 2 <= x? --> we don't know + elif sameTree(fact.sons[2], x): + # fact: 3 < x; question: N-1 < x ? --> true iff N-1 <= 3 + if isValue(fact.sons[1]) and isValue(c): + if leValue(c.pred, fact.sons[1]): result = impYes + of someLe: + if sameTree(fact.sons[1], x): + if isValue(fact.sons[2]) and isValue(c): + # fact: x <= 4; question x >= 56? --> false iff 4 <= 56 + if leValue(fact.sons[2], c): result = impNo + # fact: x <= 4; question x >= 2? --> we don't know + elif sameTree(fact.sons[2], x): + # fact: 3 <= x; question: x >= 2 ? --> true iff 2 <= 3 + if isValue(fact.sons[1]) and isValue(c): + if leValue(c, fact.sons[1]): result = impYes + of mNot, mOr, mAnd: internalError(x.info, "impliesGe") + else: nil + +proc impliesLe(fact, x, c: PNode): TImplication = + if not isLocation(x): + return impliesGe(fact, c, x) + case fact.sons[0].sym.magic + of someEq: + if sameTree(fact.sons[1], x): + if isValue(fact.sons[2]) and isValue(c): + # fact: x = 4; question x <= 56? --> true iff 4 <= 56 + if leValue(fact.sons[2], c): result = impYes + else: result = impNo + elif sameTree(fact.sons[2], x): + if isValue(fact.sons[1]) and isValue(c): + if leValue(fact.sons[1], c): result = impYes + else: result = impNo + of someLt: + if sameTree(fact.sons[1], x): + if isValue(fact.sons[2]) and isValue(c): + # fact: x < 4; question x <= N? --> true iff N-1 <= 4 + if leValue(fact.sons[2], c.pred): result = impYes + # fact: x < 4; question x <= 2? --> we don't know + elif sameTree(fact.sons[2], x): + # fact: 3 < x; question: x <= 1 ? --> false iff 1 <= 3 + if isValue(fact.sons[1]) and isValue(c): + if leValue(c, fact.sons[1]): result = impNo + + of someLe: + if sameTree(fact.sons[1], x): + if isValue(fact.sons[2]) and isValue(c): + # fact: x <= 4; question x <= 56? --> true iff 4 <= 56 + if leValue(fact.sons[2], c): result = impYes + # fact: x <= 4; question x <= 2? --> we don't know + + elif sameTree(fact.sons[2], x): + # fact: 3 <= x; question: x <= 2 ? --> false iff 2 < 3 + if isValue(fact.sons[1]) and isValue(c): + if leValue(c, fact.sons[1].pred): result = impNo + + of mNot, mOr, mAnd: internalError(x.info, "impliesLe") + else: nil + +proc impliesLt(fact, x, c: PNode): TImplication = + # x < 3 same as x <= 2: + let p = c.pred + if p != c: + result = impliesLe(fact, x, p) + else: + # 4 < x same as 3 <= x + let q = x.pred + if q != x: + result = impliesLe(fact, q, c) + +proc factImplies(fact, prop: PNode, isNegation: bool): TImplication = + case fact.getMagic + of mNot: + case factImplies(fact.sons[1], prop, not isNegation) + of impUnknown: return impUnknown + of impNo: return impYes + of impYes: return impNo + of mAnd: + if not isNegation: + result = factImplies(fact.sons[1], prop, isNegation) + if result != impUnknown: return result + return factImplies(fact.sons[2], prop, isNegation) + else: + # careful! not (a and b) means not a or not b: + # a or b --> both need to imply 'prop' + let a = factImplies(fact.sons[1], prop, isNegation) + let b = factImplies(fact.sons[2], prop, isNegation) + if a == b: return a + return impUnknown + else: discard + + case prop.sons[0].sym.magic + of mNot: + case fact.factImplies(prop.sons[1], isNegation) + of impUnknown: result = impUnknown + of impNo: result = impYes + of impYes: result = impNo + of mIsNil: + result = impliesIsNil(fact, prop) + of someEq: + result = impliesEq(fact, prop) + of someLe: + result = impliesLe(fact, prop.sons[1], prop.sons[2]) + of someLt: + result = impliesLt(fact, prop.sons[1], prop.sons[2]) + else: + internalError(prop.info, "invalid proposition") + +proc doesImply*(facts: TModel, prop: PNode): TImplication = + assert prop.kind in nkCallKinds + for f in facts: + result = f.factImplies(prop, false) + if result != impUnknown: return + +proc impliesNotNil*(facts: TModel, arg: PNode): TImplication = + var x = newNodeI(nkCall, arg.info, 2) + x.sons[0] = newSymNode(getSysMagic("isNil", mIsNil)) + x.sons[1] = arg + result = doesImply(facts, x.neg) 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/nimrod.ini b/compiler/nimrod.ini index bc2f775b6..49dcd25ba 100644 --- a/compiler/nimrod.ini +++ b/compiler/nimrod.ini @@ -64,7 +64,6 @@ Files: "bin/empty.txt" [Lib] Files: "lib/nimbase.h" -Files: "lib/copying.txt" Files: "lib/*.nim" Files: "lib/*.cfg" 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/advopt.txt b/doc/advopt.txt index 38461244d..3b6fafd0f 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -12,6 +12,8 @@ Advanced commands: //check checks the project for syntax and semantic //idetools compiler support for IDEs: possible options: --track:FILE,LINE,COL track a file/cursor position + --trackDirty:DIRTY_FILE,ORIG_FILE,LINE,COL + track a file, currently not saved to disk --suggest suggest all possible symbols at position --def list all possible definitions at position --context list possible invokation context diff --git a/doc/lib.txt b/doc/lib.txt index 2f781f375..1b004aa9d 100644 --- a/doc/lib.txt +++ b/doc/lib.txt @@ -255,6 +255,22 @@ Parsers This is a low level module that implements an extremely efficient buffering scheme for lexers and parsers. This is used by the diverse parsing modules. +* `highlite <highlite.html>`_ + Source highlighter for programming or markup languages. Currently + only few languages are supported, other languages may be added. + The interface supports one language nested in another. + +* `rst <rst.html>`_ + This module implements a reStructuredText parser. A large subset + is implemented. Some features of the markdown wiki syntax are + also supported. + +* `rstast <rstast.html>`_ + This module implements an AST for the reStructuredText parser. + +* `rstgen <rstgen.html>`_ + This module implements a generator of HTML/Latex from reStructuredText. + XML Processing -------------- diff --git a/doc/manual.txt b/doc/manual.txt index f163e0d5f..2c19911b4 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 @@ -1388,7 +1388,7 @@ accesses its environment. If it does so, it has the calling convention Distinct type ------------- -A distinct type is new type derived from a `base type`:idx: that is +A `distinct type`:idx: is new type derived from a `base type`:idx: that is incompatible with its base type. In particular, it is an essential property of a distinct type that it **does not** imply a subtype relation between it and its base type. Explicit type conversions from a distinct type to its @@ -1435,7 +1435,7 @@ number without unit; and the same holds for division: This quickly gets tedious. The implementations are trivial and the compiler should not generate all this code only to optimize it away later - after all ``+`` for dollars should produce the same binary code as ``+`` for ints. -The pragma ``borrow`` has been designed to solve this problem; in principle +The pragma `borrow`:idx: has been designed to solve this problem; in principle it generates the above trivial implementations: .. code-block:: nimrod @@ -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/doc/tut1.txt b/doc/tut1.txt index 28e23b0f0..0cc9b05c1 100644 --- a/doc/tut1.txt +++ b/doc/tut1.txt @@ -1182,6 +1182,21 @@ type and instead write it embedded directly as the type of the first dimension: type TLightTower = array[1..10, array[north..west, TBlinkLights]] +It is quite frequent to have arrays start at zero, so there's a shortcut syntax +to specify a range from zero to the specified index minus one: + +.. code-block:: nimrod + type + TIntArray = array[0..5, int] # an array that is indexed with 0..5 + TQuickArray = array[6, int] # an array that is indexed with 0..5 + var + x: TIntArray + y: TQuickArray + x = [1, 2, 3, 4, 5, 6] + y = x + for i in low(x)..high(x): + echo(x[i], y[i]) + Sequences --------- diff --git a/lib/copying.txt b/lib/copying.txt deleted file mode 100644 index 0ff6b7d87..000000000 --- a/lib/copying.txt +++ /dev/null @@ -1,24 +0,0 @@ -=============================================================================== -Nimrod -- a Compiler for Nimrod. http://nimrod-code.org/ - -Copyright (C) 2004-2013 Andreas Rumpf. All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -[ MIT license: http://www.opensource.org/licenses/mit-license.php ] \ No newline at end of file diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 54c17367f..91664cd50 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -## This module implements a `reStructuredText`:idx parser. A large +## This module implements a `reStructuredText`:idx: parser. A large ## subset is implemented. Some features of the `markdown`:idx: wiki syntax are ## also supported. diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim index 23233fd39..3d191dacb 100644 --- a/lib/packages/docutils/rstast.nim +++ b/lib/packages/docutils/rstast.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -## This module implements an AST for the `reStructuredText`:idx parser. +## This module implements an AST for the `reStructuredText`:idx: parser. import strutils diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 53bd8188e..a393943fb 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -## This module implements a generator of HTML/Latex from `reStructuredText`:idx. +## This module implements a generator of HTML/Latex from `reStructuredText`:idx:. import strutils, os, hashes, strtabs, rstast, rst, highlite @@ -692,4 +692,4 @@ proc rstToHtml*(s: string, options: TRstParseOptions, var rst = rstParse(s, filen, 0, 1, dummyHasToc, options) result = "" renderRstToOut(d, rst, result) - \ No newline at end of file + diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index b6877696c..d8fed845a 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -82,7 +82,7 @@ type of pkChar, pkGreedyRepChar: ch: char of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char] of pkNonTerminal: nt: PNonTerminal - of pkBackRef..pkBackRefIgnoreStyle: index: range[1..MaxSubpatterns] + of pkBackRef..pkBackRefIgnoreStyle: index: range[0..MaxSubpatterns] else: sons: seq[TNode] PNonTerminal* = ref TNonTerminal 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/lib/wrappers/readline/readline.nim b/lib/wrappers/readline/readline.nim index d14171c46..1f0dd564f 100644 --- a/lib/wrappers/readline/readline.nim +++ b/lib/wrappers/readline/readline.nim @@ -156,33 +156,34 @@ const type Thook_func* = proc (a2: cstring): cstring{.cdecl.} -# If non-null, this contains the address of a function that the application -# wants called before trying the standard tilde expansions. The function -# is called with the text sans tilde, and returns a malloc()'ed string -# which is the expansion, or a NULL pointer if the expansion fails. +when not defined(macosx): + # If non-null, this contains the address of a function that the application + # wants called before trying the standard tilde expansions. The function + # is called with the text sans tilde, and returns a malloc()'ed string + # which is the expansion, or a NULL pointer if the expansion fails. -var expansion_preexpansion_hook*{.importc: "tilde_expansion_preexpansion_hook", - dynlib: tildeDll.}: Thook_func + var expansion_preexpansion_hook*{.importc: "tilde_expansion_preexpansion_hook", + dynlib: tildeDll.}: Thook_func -# If non-null, this contains the address of a function to call if the -# standard meaning for expanding a tilde fails. The function is called -# with the text (sans tilde, as in "foo"), and returns a malloc()'ed string -# which is the expansion, or a NULL pointer if there is no expansion. + # If non-null, this contains the address of a function to call if the + # standard meaning for expanding a tilde fails. The function is called + # with the text (sans tilde, as in "foo"), and returns a malloc()'ed string + # which is the expansion, or a NULL pointer if there is no expansion. -var expansion_failure_hook*{.importc: "tilde_expansion_failure_hook", - dynlib: tildeDll.}: Thook_func + var expansion_failure_hook*{.importc: "tilde_expansion_failure_hook", + dynlib: tildeDll.}: Thook_func -# When non-null, this is a NULL terminated array of strings which -# are duplicates for a tilde prefix. Bash uses this to expand -# `=~' and `:~'. + # When non-null, this is a NULL terminated array of strings which + # are duplicates for a tilde prefix. Bash uses this to expand + # `=~' and `:~'. -var additional_prefixes*{.importc: "tilde_additional_prefixes", dynlib: tildeDll.}: cstringArray + var additional_prefixes*{.importc: "tilde_additional_prefixes", dynlib: tildeDll.}: cstringArray -# When non-null, this is a NULL terminated array of strings which match -# the end of a username, instead of just "/". Bash sets this to -# `:' and `=~'. + # When non-null, this is a NULL terminated array of strings which match + # the end of a username, instead of just "/". Bash sets this to + # `:' and `=~'. -var additional_suffixes*{.importc: "tilde_additional_suffixes", dynlib: tildeDll.}: cstringArray + var additional_suffixes*{.importc: "tilde_additional_suffixes", dynlib: tildeDll.}: cstringArray # Return a new string which is the result of tilde expanding STRING. @@ -229,7 +230,8 @@ type # The current undo list for RL_LINE_BUFFER. -var undo_list*{.importc: "rl_undo_list", dynlib: readlineDll.}: ptr TUNDO_LIST +when not defined(macosx): + var undo_list*{.importc: "rl_undo_list", dynlib: readlineDll.}: ptr TUNDO_LIST # The data structure for mapping textual names to code addresses. @@ -239,7 +241,8 @@ type function*: TCommandFunc -var funmap*{.importc: "funmap", dynlib: readlineDll.}: ptr ptr TFUNMAP +when not defined(macosx): + var funmap*{.importc: "funmap", dynlib: readlineDll.}: ptr ptr TFUNMAP # **************************************************************** # diff --git a/tests/reject/tdisallowif.nim b/tests/reject/tdisallowif.nim index f7bb7098b..10f54288a 100644 --- a/tests/reject/tdisallowif.nim +++ b/tests/reject/tdisallowif.nim @@ -21,7 +21,7 @@ s[0] = substr(s[0], 0, 2) echo s[0] -if true: +if s[0] != "hi": echo "do it" echo "more branches" else: diff --git a/tests/reject/tnotnil1.nim b/tests/reject/tnotnil1.nim new file mode 100644 index 000000000..3535bbd63 --- /dev/null +++ b/tests/reject/tnotnil1.nim @@ -0,0 +1,24 @@ +discard """ + errormsg: "'y' is provably nil" + line:22 +""" + +import strutils + + +type + TObj = object + x, y: int + +proc q(x: pointer not nil) = + nil + +proc p() = + var x: pointer + let y = x + if not y.isNil: + q(y) + else: + q(y) + +p() diff --git a/tests/reject/tnotnil2.nim b/tests/reject/tnotnil2.nim new file mode 100644 index 000000000..bd6b8b675 --- /dev/null +++ b/tests/reject/tnotnil2.nim @@ -0,0 +1,24 @@ +discard """ + errormsg: "cannot prove 'y' is not nil" + line:20 +""" + +import strutils + + +type + TObj = object + x, y: int + +proc q(x: pointer not nil) = + nil + +proc p() = + var x: pointer + let y = x + if not y.isNil or y != x: + q(y) + else: + q(y) + +p() diff --git a/tests/reject/tuninit1.nim b/tests/reject/tuninit1.nim new file mode 100644 index 000000000..2a994b187 --- /dev/null +++ b/tests/reject/tuninit1.nim @@ -0,0 +1,36 @@ +discard """ + errormsg: "'y' might not have been initialized" + line:34 +""" + +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/tests/run/tpegs.nim b/tests/run/tpegs.nim index e64cd8fef..efc6a8fa4 100644 --- a/tests/run/tpegs.nim +++ b/tests/run/tpegs.nim @@ -77,7 +77,7 @@ type of pkChar, pkGreedyRepChar: ch: char of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char] of pkNonTerminal: nt: PNonTerminal - of pkBackRef..pkBackRefIgnoreStyle: index: range[1..MaxSubpatterns] + of pkBackRef..pkBackRefIgnoreStyle: index: range[0..MaxSubpatterns] else: sons: seq[TNode] PNonTerminal* = ref TNonTerminal diff --git a/todo.txt b/todo.txt index ad0a4afa2..5b9f36dcd 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 d844b5012..98d3298b9 100644 --- a/web/news.txt +++ b/web/news.txt @@ -36,7 +36,9 @@ Language Additions - Arrays can now be declared with a single integer literal ``N`` instead of a range; the range is then ``0..N-1``. -- macros.dumptree and macros.dumplisp have been made immediate, dumptree_imm and dumplisp_imm are now deprecated. +- ``macros.dumpTree`` and ``macros.dumpLisp`` have been made ``immediate``, + ``dumpTreeImm`` and ``dumpLispImm`` are now deprecated. +- Added ``requiresInit`` pragma to enforce explicit initialization. 2013-05-20 New website design! diff --git a/web/nimrod.ini b/web/nimrod.ini index 36252deb6..d9c6cb786 100644 --- a/web/nimrod.ini +++ b/web/nimrod.ini @@ -60,7 +60,9 @@ srcdoc2: "pure/collections/intsets;pure/collections/queues;pure/encodings" srcdoc2: "pure/events;pure/collections/sequtils;pure/irc;pure/cookies" srcdoc2: "pure/ftpclient;pure/memfiles;pure/subexes;pure/collections/critbits" srcdoc2: "pure/asyncio;pure/actors;core/locks;pure/oids;pure/endians;pure/uri" -srcdoc2: "pure/nimprof;pure/unittest" +srcdoc2: "pure/nimprof;pure/unittest;packages/docutils/highlite" +srcdoc2: "packages/docutils/rst;packages/docutils/rstast" +srcdoc2: "packages/docutils/rstgen" webdoc: "wrappers/libcurl;pure/md5;wrappers/mysql;wrappers/iup" webdoc: "wrappers/sqlite3;wrappers/postgres;wrappers/tinyc" |