implemented large parts of the 'not nil' checking

This commit is contained in:
Araq
2013-06-09 23:29:43 +02:00
parent 2aaa8f7909
commit 23ef565a3c
21 changed files with 290 additions and 109 deletions

View File

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

View File

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

View File

@@ -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",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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",

View File

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

View File

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

View File

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

36
tests/reject/tuninit1.nim Normal file
View File

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

View File

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

View File

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

View File

@@ -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``.

View File

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