implemented large parts of the 'not nil' checking
This commit is contained in:
parent
2aaa8f7909
commit
23ef565a3c
21 changed files with 290 additions and 109 deletions
|
|
@ -353,7 +353,7 @@ type
|
||||||
nfSem # node has been checked for semantics
|
nfSem # node has been checked for semantics
|
||||||
|
|
||||||
TNodeFlags* = set[TNodeFlag]
|
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
|
tfVarargs, # procedure has C styled varargs
|
||||||
tfNoSideEffect, # procedure type does not allow side effects
|
tfNoSideEffect, # procedure type does not allow side effects
|
||||||
tfFinal, # is the object final?
|
tfFinal, # is the object final?
|
||||||
|
|
@ -380,7 +380,13 @@ type
|
||||||
tfByRef, # pass object/tuple by reference (C backend)
|
tfByRef, # pass object/tuple by reference (C backend)
|
||||||
tfIterator, # type is really an iterator, not a tyProc
|
tfIterator, # type is really an iterator, not a tyProc
|
||||||
tfShared, # type is 'shared'
|
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]
|
TTypeFlags* = set[TTypeFlag]
|
||||||
|
|
||||||
|
|
@ -1168,14 +1174,25 @@ proc newSons(father: PNode, length: int) =
|
||||||
else:
|
else:
|
||||||
setlen(father.sons, length)
|
setlen(father.sons, length)
|
||||||
|
|
||||||
proc addSon*(father, son: PType) {.deprecated.} =
|
proc propagateToOwner*(owner, elem: PType) =
|
||||||
if isNil(father.sons): father.sons = @[]
|
owner.flags = owner.flags + (elem.flags * {tfNeedsInit, tfHasShared,
|
||||||
add(father.sons, son)
|
tfHasMeta, tfHasGCedMem})
|
||||||
#assert((father.kind != tyGenericInvokation) or (son.kind != tyGenericInst))
|
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) =
|
proc rawAddSon*(father, son: PType) =
|
||||||
if isNil(father.sons): father.sons = @[]
|
if isNil(father.sons): father.sons = @[]
|
||||||
add(father.sons, son)
|
add(father.sons, son)
|
||||||
|
if not son.isNil: propagateToOwner(father, son)
|
||||||
|
|
||||||
proc addSon(father, son: PNode) =
|
proc addSon(father, son: PNode) =
|
||||||
assert son != nil
|
assert son != nil
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,18 @@ proc getSysSym(name: string): PSym =
|
||||||
result.typ = newType(tyError, systemModule)
|
result.typ = newType(tyError, systemModule)
|
||||||
if result.kind == skStub: loadStub(result)
|
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 =
|
proc sysTypeFromName*(name: string): PType =
|
||||||
result = getSysSym(name).typ
|
result = getSysSym(name).typ
|
||||||
|
|
||||||
|
|
@ -111,7 +123,9 @@ proc skipIntLit*(t: PType): PType {.inline.} =
|
||||||
|
|
||||||
proc AddSonSkipIntLit*(father, son: PType) =
|
proc AddSonSkipIntLit*(father, son: PType) =
|
||||||
if isNil(father.sons): father.sons = @[]
|
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) =
|
proc setIntLitType*(result: PNode) =
|
||||||
let i = result.intVal
|
let i = result.intVal
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ type
|
||||||
warnUnknownSubstitutionX, warnLanguageXNotSupported, warnCommentXIgnored,
|
warnUnknownSubstitutionX, warnLanguageXNotSupported, warnCommentXIgnored,
|
||||||
warnNilStatement, warnAnalysisLoophole,
|
warnNilStatement, warnAnalysisLoophole,
|
||||||
warnDifferentHeaps, warnWriteToForeignHeap, warnImplicitClosure,
|
warnDifferentHeaps, warnWriteToForeignHeap, warnImplicitClosure,
|
||||||
warnEachIdentIsTuple, warnShadowIdent, warnUninit, warnUser,
|
warnEachIdentIsTuple, warnShadowIdent, warnProveInit, warnUninit, warnUser,
|
||||||
hintSuccess, hintSuccessX,
|
hintSuccess, hintSuccessX,
|
||||||
hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
|
hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
|
||||||
hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
|
hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
|
||||||
|
|
@ -355,7 +355,8 @@ const
|
||||||
warnImplicitClosure: "implicit closure convention: '$1' [ImplicitClosure]",
|
warnImplicitClosure: "implicit closure convention: '$1' [ImplicitClosure]",
|
||||||
warnEachIdentIsTuple: "each identifier is a tuple [EachIdentIsTuple]",
|
warnEachIdentIsTuple: "each identifier is a tuple [EachIdentIsTuple]",
|
||||||
warnShadowIdent: "shadowed identifier: '$1' [ShadowIdent]",
|
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]",
|
warnUser: "$1 [User]",
|
||||||
hintSuccess: "operation successful [Success]",
|
hintSuccess: "operation successful [Success]",
|
||||||
hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#) [SuccessX]",
|
hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#) [SuccessX]",
|
||||||
|
|
@ -375,14 +376,14 @@ const
|
||||||
hintUser: "$1 [User]"]
|
hintUser: "$1 [User]"]
|
||||||
|
|
||||||
const
|
const
|
||||||
WarningsToStr*: array[0..20, string] = ["CannotOpenFile", "OctalEscape",
|
WarningsToStr*: array[0..21, string] = ["CannotOpenFile", "OctalEscape",
|
||||||
"XIsNeverRead", "XmightNotBeenInit",
|
"XIsNeverRead", "XmightNotBeenInit",
|
||||||
"Deprecated", "ConfigDeprecated",
|
"Deprecated", "ConfigDeprecated",
|
||||||
"SmallLshouldNotBeUsed", "UnknownMagic",
|
"SmallLshouldNotBeUsed", "UnknownMagic",
|
||||||
"RedefinitionOfLabel", "UnknownSubstitutionX", "LanguageXNotSupported",
|
"RedefinitionOfLabel", "UnknownSubstitutionX", "LanguageXNotSupported",
|
||||||
"CommentXIgnored", "NilStmt",
|
"CommentXIgnored", "NilStmt",
|
||||||
"AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap",
|
"AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap",
|
||||||
"ImplicitClosure", "EachIdentIsTuple", "ShadowIdent", "Uninit",
|
"ImplicitClosure", "EachIdentIsTuple", "ShadowIdent", "ProveInit", "Uninit",
|
||||||
"User"]
|
"User"]
|
||||||
|
|
||||||
HintsToStr*: array[0..15, string] = ["Success", "SuccessX", "LineTooLong",
|
HintsToStr*: array[0..15, string] = ["Success", "SuccessX", "LineTooLong",
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,8 @@ proc add(code: var TPatternCode, op: TOpcode) {.inline.} =
|
||||||
proc whichAlias*(p: PSym): TAliasRequest =
|
proc whichAlias*(p: PSym): TAliasRequest =
|
||||||
if p.constraint != nil:
|
if p.constraint != nil:
|
||||||
result = TAliasRequest(p.constraint.strVal[0].ord)
|
result = TAliasRequest(p.constraint.strVal[0].ord)
|
||||||
|
else:
|
||||||
|
result = aqNone
|
||||||
|
|
||||||
proc compileConstraints(p: PNode, result: var TPatternCode) =
|
proc compileConstraints(p: PNode, result: var TPatternCode) =
|
||||||
case p.kind
|
case p.kind
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ const
|
||||||
typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
|
typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
|
||||||
wPure, wHeader, wCompilerProc, wFinal, wSize, wExtern, wShallow,
|
wPure, wHeader, wCompilerProc, wFinal, wSize, wExtern, wShallow,
|
||||||
wImportcpp, wImportobjc, wError, wIncompleteStruct, wByCopy, wByRef,
|
wImportcpp, wImportobjc, wError, wIncompleteStruct, wByCopy, wByRef,
|
||||||
wInheritable, wGenSym, wInject}
|
wInheritable, wGenSym, wInject, wRequiresInit}
|
||||||
fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
|
fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
|
||||||
wImportcpp, wImportobjc, wError}
|
wImportcpp, wImportobjc, wError}
|
||||||
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
|
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
|
||||||
|
|
@ -253,11 +253,11 @@ proc processNote(c: PContext, n: PNode) =
|
||||||
of wHint:
|
of wHint:
|
||||||
var x = findStr(msgs.HintsToStr, n.sons[0].sons[1].ident.s)
|
var x = findStr(msgs.HintsToStr, n.sons[0].sons[1].ident.s)
|
||||||
if x >= 0: nk = TNoteKind(x + ord(hintMin))
|
if x >= 0: nk = TNoteKind(x + ord(hintMin))
|
||||||
else: invalidPragma(n)
|
else: invalidPragma(n); return
|
||||||
of wWarning:
|
of wWarning:
|
||||||
var x = findStr(msgs.WarningsToStr, n.sons[0].sons[1].ident.s)
|
var x = findStr(msgs.WarningsToStr, n.sons[0].sons[1].ident.s)
|
||||||
if x >= 0: nk = TNoteKind(x + ord(warnMin))
|
if x >= 0: nk = TNoteKind(x + ord(warnMin))
|
||||||
else: InvalidPragma(n)
|
else: InvalidPragma(n); return
|
||||||
else:
|
else:
|
||||||
invalidPragma(n)
|
invalidPragma(n)
|
||||||
return
|
return
|
||||||
|
|
@ -695,6 +695,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||||
noVal(it)
|
noVal(it)
|
||||||
if sym.typ == nil: invalidPragma(it)
|
if sym.typ == nil: invalidPragma(it)
|
||||||
else: incl(sym.typ.flags, tfIncompleteStruct)
|
else: incl(sym.typ.flags, tfIncompleteStruct)
|
||||||
|
of wRequiresInit:
|
||||||
|
noVal(it)
|
||||||
|
if sym.typ == nil: invalidPragma(it)
|
||||||
|
else: incl(sym.typ.flags, tfNeedsInit)
|
||||||
of wByRef:
|
of wByRef:
|
||||||
noVal(it)
|
noVal(it)
|
||||||
if sym == nil or sym.typ == nil:
|
if sym == nil or sym.typ == nil:
|
||||||
|
|
|
||||||
|
|
@ -1601,6 +1601,25 @@ proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
addSonSkipIntLit(typ, n.sons[i].typ)
|
addSonSkipIntLit(typ, n.sons[i].typ)
|
||||||
result.typ = 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 =
|
proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
var t = semTypeNode(c, n.sons[0], nil)
|
var t = semTypeNode(c, n.sons[0], nil)
|
||||||
result = n
|
result = n
|
||||||
|
|
@ -1611,6 +1630,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
if t.kind != tyObject:
|
if t.kind != tyObject:
|
||||||
localError(n.info, errGenerated, "object constructor needs an object type")
|
localError(n.info, errGenerated, "object constructor needs an object type")
|
||||||
return
|
return
|
||||||
|
var objType = t
|
||||||
var ids = initIntSet()
|
var ids = initIntSet()
|
||||||
for i in 1.. <n.len:
|
for i in 1.. <n.len:
|
||||||
let it = n.sons[i]
|
let it = n.sons[i]
|
||||||
|
|
@ -1642,6 +1662,11 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
localError(it.info, errUndeclaredFieldX, id.s)
|
localError(it.info, errUndeclaredFieldX, id.s)
|
||||||
it.sons[1] = e
|
it.sons[1] = e
|
||||||
# XXX object field name check for 'case objects' if the kind is static?
|
# 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 =
|
proc semBlock(c: PContext, n: PNode): PNode =
|
||||||
result = n
|
result = n
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
|
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
|
# Second semantic checking pass over the AST. Necessary because the old
|
||||||
# way had some inherent problems. Performs:
|
# way had some inherent problems. Performs:
|
||||||
|
|
@ -75,7 +75,7 @@ type
|
||||||
owner: PSym
|
owner: PSym
|
||||||
init: seq[int] # list of initialized variables
|
init: seq[int] # list of initialized variables
|
||||||
# coming soon: "guard" tracking for 'let' variables
|
# coming soon: "guard" tracking for 'let' variables
|
||||||
|
guards: TModel # nested guards
|
||||||
PEffects = var TEffects
|
PEffects = var TEffects
|
||||||
|
|
||||||
proc isLocalVar(a: PEffects, s: PSym): bool =
|
proc isLocalVar(a: PEffects, s: PSym): bool =
|
||||||
|
|
@ -93,11 +93,14 @@ proc useVar(a: PEffects, n: PNode) =
|
||||||
let s = n.sym
|
let s = n.sym
|
||||||
if isLocalVar(a, s):
|
if isLocalVar(a, s):
|
||||||
if s.id notin a.init:
|
if s.id notin a.init:
|
||||||
if true:
|
if tfNeedsInit in s.typ.flags:
|
||||||
Message(n.info, warnUninit, s.name.s)
|
when true:
|
||||||
|
Message(n.info, warnProveInit, s.name.s)
|
||||||
else:
|
else:
|
||||||
Message(n.info, errGenerated,
|
Message(n.info, errGenerated,
|
||||||
"read from potentially uninitialized variable: '$1'" % s.name.s)
|
"'$1' might not have been initialized" % s.name.s)
|
||||||
|
else:
|
||||||
|
Message(n.info, warnUninit, s.name.s)
|
||||||
# prevent superfluous warnings about the same variable:
|
# prevent superfluous warnings about the same variable:
|
||||||
a.init.add s.id
|
a.init.add s.id
|
||||||
|
|
||||||
|
|
@ -295,7 +298,7 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
|
||||||
let tagSpec = effectSpec(pragma, wTags)
|
let tagSpec = effectSpec(pragma, wTags)
|
||||||
mergeTags(tracked, tagSpec, n)
|
mergeTags(tracked, tagSpec, n)
|
||||||
|
|
||||||
proc trackOperand(tracked: PEffects, n: PNode) =
|
proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
|
||||||
let op = n.typ
|
let op = n.typ
|
||||||
if op != nil and op.kind == tyProc and n.kind != nkNilLit:
|
if op != nil and op.kind == tyProc and n.kind != nkNilLit:
|
||||||
InternalAssert op.n.sons[0].kind == nkEffectList
|
InternalAssert op.n.sons[0].kind == nkEffectList
|
||||||
|
|
@ -312,16 +315,40 @@ proc trackOperand(tracked: PEffects, n: PNode) =
|
||||||
else:
|
else:
|
||||||
mergeEffects(tracked, effectList.sons[exceptionEffects], n)
|
mergeEffects(tracked, effectList.sons[exceptionEffects], n)
|
||||||
mergeTags(tracked, effectList.sons[tagEffects], 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) =
|
proc trackCase(tracked: PEffects, n: PNode) =
|
||||||
track(tracked, n.sons[0])
|
track(tracked, n.sons[0])
|
||||||
let oldState = tracked.init.len
|
let oldState = tracked.init.len
|
||||||
var inter: TIntersection = @[]
|
var inter: TIntersection = @[]
|
||||||
|
var toCover = 0
|
||||||
for i in 1.. <n.len:
|
for i in 1.. <n.len:
|
||||||
let branch = n.sons[i]
|
let branch = n.sons[i]
|
||||||
setLen(tracked.init, oldState)
|
setLen(tracked.init, oldState)
|
||||||
for i in 0 .. <branch.len:
|
for i in 0 .. <branch.len:
|
||||||
track(tracked, branch.sons[i])
|
track(tracked, branch.sons[i])
|
||||||
|
if not breaksBlock(branch.lastSon): inc toCover
|
||||||
for i in oldState.. <tracked.init.len:
|
for i in oldState.. <tracked.init.len:
|
||||||
addToIntersection(inter, tracked.init[i])
|
addToIntersection(inter, tracked.init[i])
|
||||||
let exh = case skipTypes(n.sons[0].Typ, abstractVarRange-{tyTypeDesc}).Kind
|
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)
|
setLen(tracked.init, oldState)
|
||||||
if exh:
|
if exh:
|
||||||
for id, count in items(inter):
|
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
|
# else we can't merge
|
||||||
|
|
||||||
proc trackIf(tracked: PEffects, n: PNode) =
|
proc trackIf(tracked: PEffects, n: PNode) =
|
||||||
track(tracked, n.sons[0].sons[0])
|
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
|
let oldState = tracked.init.len
|
||||||
|
|
||||||
var inter: TIntersection = @[]
|
var inter: TIntersection = @[]
|
||||||
|
var toCover = 0
|
||||||
track(tracked, n.sons[0].sons[1])
|
track(tracked, n.sons[0].sons[1])
|
||||||
|
if not breaksBlock(n.sons[0].sons[1]): inc toCover
|
||||||
for i in oldState.. <tracked.init.len:
|
for i in oldState.. <tracked.init.len:
|
||||||
addToIntersection(inter, tracked.init[i])
|
addToIntersection(inter, tracked.init[i])
|
||||||
|
|
||||||
for i in 1.. <n.len:
|
for i in 1.. <n.len:
|
||||||
let branch = n.sons[i]
|
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)
|
setLen(tracked.init, oldState)
|
||||||
for i in 0 .. <branch.len:
|
for i in 0 .. <branch.len:
|
||||||
track(tracked, branch.sons[i])
|
track(tracked, branch.sons[i])
|
||||||
|
if not breaksBlock(branch.lastSon): inc toCover
|
||||||
for i in oldState.. <tracked.init.len:
|
for i in oldState.. <tracked.init.len:
|
||||||
addToIntersection(inter, tracked.init[i])
|
addToIntersection(inter, tracked.init[i])
|
||||||
setLen(tracked.init, oldState)
|
setLen(tracked.init, oldState)
|
||||||
if lastSon(n).len == 1:
|
if lastSon(n).len == 1:
|
||||||
for id, count in items(inter):
|
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
|
# else we can't merge as it is not exhaustive
|
||||||
|
setLen(tracked.guards, oldFacts)
|
||||||
|
|
||||||
proc trackBlock(tracked: PEffects, n: PNode) =
|
proc trackBlock(tracked: PEffects, n: PNode) =
|
||||||
if n.kind in {nkStmtList, nkStmtListExpr}:
|
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 == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or
|
||||||
n.kind == nkIntLit and n.intVal != 0
|
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) =
|
proc track(tracked: PEffects, n: PNode) =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkSym:
|
of nkSym:
|
||||||
|
|
@ -404,11 +445,12 @@ proc track(tracked: PEffects, n: PNode) =
|
||||||
else:
|
else:
|
||||||
mergeEffects(tracked, effectList.sons[exceptionEffects], n)
|
mergeEffects(tracked, effectList.sons[exceptionEffects], n)
|
||||||
mergeTags(tracked, effectList.sons[tagEffects], 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,
|
if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize,
|
||||||
mNewSeq, mShallowCopy}:
|
mNewSeq, mShallowCopy}:
|
||||||
# may not look like an assignment, but it is:
|
# may not look like an assignment, but it is:
|
||||||
initVar(tracked, n.sons[1])
|
initVar(tracked, n.sons[1])
|
||||||
|
# XXX new(objWithNotNil) is not initialized properly!
|
||||||
for i in 0 .. <safeLen(n):
|
for i in 0 .. <safeLen(n):
|
||||||
track(tracked, n.sons[i])
|
track(tracked, n.sons[i])
|
||||||
of nkTryStmt: trackTryStmt(tracked, n)
|
of nkTryStmt: trackTryStmt(tracked, n)
|
||||||
|
|
@ -510,8 +552,14 @@ proc trackProc*(s: PSym, body: PNode) =
|
||||||
t.tags = effects.sons[tagEffects]
|
t.tags = effects.sons[tagEffects]
|
||||||
t.owner = s
|
t.owner = s
|
||||||
t.init = @[]
|
t.init = @[]
|
||||||
|
t.guards = @[]
|
||||||
track(t, body)
|
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 p = s.ast.sons[pragmasPos]
|
||||||
let raisesSpec = effectSpec(p, wRaises)
|
let raisesSpec = effectSpec(p, wRaises)
|
||||||
if not isNil(raisesSpec):
|
if not isNil(raisesSpec):
|
||||||
|
|
|
||||||
|
|
@ -374,7 +374,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||||
if warnShadowIdent in gNotes and not identWithin(def, v.name):
|
if warnShadowIdent in gNotes and not identWithin(def, v.name):
|
||||||
Message(a.info, warnShadowIdent, v.name.s)
|
Message(a.info, warnShadowIdent, v.name.s)
|
||||||
if def != nil and def.kind != nkEmpty:
|
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
|
v.ast = def
|
||||||
if sfThread in v.flags: LocalError(def.info, errThreadvarCannotInit)
|
if sfThread in v.flags: LocalError(def.info, errThreadvarCannotInit)
|
||||||
if a.kind != nkVarTuple:
|
if a.kind != nkVarTuple:
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
||||||
counter = lastOrd(base) + 1
|
counter = lastOrd(base) + 1
|
||||||
rawAddSon(result, base)
|
rawAddSon(result, base)
|
||||||
let isPure = result.sym != nil and sfPure in result.sym.flags
|
let isPure = result.sym != nil and sfPure in result.sym.flags
|
||||||
|
var hasNull = false
|
||||||
for i in countup(1, sonsLen(n) - 1):
|
for i in countup(1, sonsLen(n) - 1):
|
||||||
case n.sons[i].kind
|
case n.sons[i].kind
|
||||||
of nkEnumFieldDef:
|
of nkEnumFieldDef:
|
||||||
|
|
@ -74,6 +75,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
||||||
else: illFormedAst(n)
|
else: illFormedAst(n)
|
||||||
e.typ = result
|
e.typ = result
|
||||||
e.position = int(counter)
|
e.position = int(counter)
|
||||||
|
if e.position == 0: hasNull = true
|
||||||
if result.sym != nil and sfExported in result.sym.flags:
|
if result.sym != nil and sfExported in result.sym.flags:
|
||||||
incl(e.flags, sfUsed)
|
incl(e.flags, sfUsed)
|
||||||
incl(e.flags, sfExported)
|
incl(e.flags, sfExported)
|
||||||
|
|
@ -81,6 +83,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
||||||
addSon(result.n, newSymNode(e))
|
addSon(result.n, newSymNode(e))
|
||||||
if sfGenSym notin e.flags and not isPure: addDecl(c, e)
|
if sfGenSym notin e.flags and not isPure: addDecl(c, e)
|
||||||
inc(counter)
|
inc(counter)
|
||||||
|
if not hasNull: incl(result.flags, tfNeedsInit)
|
||||||
|
|
||||||
proc semSet(c: PContext, n: PNode, prev: PType): PType =
|
proc semSet(c: PContext, n: PNode, prev: PType): PType =
|
||||||
result = newOrPrevType(tySet, prev, c)
|
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 =
|
proc semRange(c: PContext, n: PNode, prev: PType): PType =
|
||||||
result = nil
|
result = nil
|
||||||
if sonsLen(n) == 2:
|
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:
|
else:
|
||||||
LocalError(n.sons[0].info, errRangeExpected)
|
LocalError(n.sons[0].info, errRangeExpected)
|
||||||
result = newOrPrevType(tyError, prev, c)
|
result = newOrPrevType(tyError, prev, c)
|
||||||
|
|
@ -387,9 +397,9 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int,
|
||||||
checkForOverlap(c, t, i, branchIndex)
|
checkForOverlap(c, t, i, branchIndex)
|
||||||
|
|
||||||
proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int,
|
proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int,
|
||||||
father: PNode, rectype: PSym)
|
father: PNode, rectype: PType)
|
||||||
proc semRecordCase(c: PContext, n: PNode, check: var TIntSet, pos: var int,
|
proc semRecordCase(c: PContext, n: PNode, check: var TIntSet, pos: var int,
|
||||||
father: PNode, rectype: PSym) =
|
father: PNode, rectype: PType) =
|
||||||
var a = copyNode(n)
|
var a = copyNode(n)
|
||||||
checkMinSonsLen(n, 2)
|
checkMinSonsLen(n, 2)
|
||||||
semRecordNodeAux(c, n.sons[0], check, pos, a, rectype)
|
semRecordNodeAux(c, n.sons[0], check, pos, a, rectype)
|
||||||
|
|
@ -424,7 +434,7 @@ proc semRecordCase(c: PContext, n: PNode, check: var TIntSet, pos: var int,
|
||||||
addSon(father, a)
|
addSon(father, a)
|
||||||
|
|
||||||
proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int,
|
proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int,
|
||||||
father: PNode, rectype: PSym) =
|
father: PNode, rectype: PType) =
|
||||||
if n == nil: return
|
if n == nil: return
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkRecWhen:
|
of nkRecWhen:
|
||||||
|
|
@ -483,15 +493,17 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int,
|
||||||
typ = errorType(c)
|
typ = errorType(c)
|
||||||
else:
|
else:
|
||||||
typ = semTypeNode(c, n.sons[length-2], nil)
|
typ = semTypeNode(c, n.sons[length-2], nil)
|
||||||
|
propagateToOwner(rectype, typ)
|
||||||
|
let rec = rectype.sym
|
||||||
for i in countup(0, sonsLen(n)-3):
|
for i in countup(0, sonsLen(n)-3):
|
||||||
var f = semIdentWithPragma(c, skField, n.sons[i], {sfExported})
|
var f = semIdentWithPragma(c, skField, n.sons[i], {sfExported})
|
||||||
suggestSym(n.sons[i], f)
|
suggestSym(n.sons[i], f)
|
||||||
f.typ = typ
|
f.typ = typ
|
||||||
f.position = pos
|
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 == nil):
|
||||||
f.loc.r = toRope(f.name.s)
|
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)
|
inc(pos)
|
||||||
if ContainsOrIncl(check, f.name.id):
|
if ContainsOrIncl(check, f.name.id):
|
||||||
localError(n.sons[i].info, errAttemptToRedefine, f.name.s)
|
localError(n.sons[i].info, errAttemptToRedefine, f.name.s)
|
||||||
|
|
@ -553,7 +565,7 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType =
|
||||||
result = newOrPrevType(tyObject, prev, c)
|
result = newOrPrevType(tyObject, prev, c)
|
||||||
rawAddSon(result, base)
|
rawAddSon(result, base)
|
||||||
result.n = newNodeI(nkRecList, n.info)
|
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:
|
if n.sons[0].kind != nkEmpty:
|
||||||
# dummy symbol for `pragma`:
|
# dummy symbol for `pragma`:
|
||||||
var s = newSymS(skType, newIdentNode(getIdent("dummy"), n.info), c)
|
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:
|
if result.kind in NilableTypes and n.sons[2].kind == nkNilLit:
|
||||||
result = freshType(result, prev)
|
result = freshType(result, prev)
|
||||||
result.flags.incl(tfNotNil)
|
result.flags.incl(tfNotNil)
|
||||||
|
result.flags.incl(tfNeedsInit)
|
||||||
else:
|
else:
|
||||||
LocalError(n.info, errGenerated, "invalid type")
|
LocalError(n.info, errGenerated, "invalid type")
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -152,6 +152,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
|
||||||
x = lookupTypeVar(cl, x)
|
x = lookupTypeVar(cl, x)
|
||||||
if header == nil: header = copyType(t, t.owner, false)
|
if header == nil: header = copyType(t, t.owner, false)
|
||||||
header.sons[i] = x
|
header.sons[i] = x
|
||||||
|
propagateToOwner(header, x)
|
||||||
#idTablePut(cl.typeMap, body.sons[i-1], x)
|
#idTablePut(cl.typeMap, body.sons[i-1], x)
|
||||||
if header != nil:
|
if header != nil:
|
||||||
# search again after first pass:
|
# search again after first pass:
|
||||||
|
|
@ -170,6 +171,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
|
||||||
var x = replaceTypeVarsT(cl, t.sons[i])
|
var x = replaceTypeVarsT(cl, t.sons[i])
|
||||||
assert x.kind != tyGenericInvokation
|
assert x.kind != tyGenericInvokation
|
||||||
header.sons[i] = x
|
header.sons[i] = x
|
||||||
|
propagateToOwner(header, x)
|
||||||
idTablePut(cl.typeMap, body.sons[i-1], x)
|
idTablePut(cl.typeMap, body.sons[i-1], x)
|
||||||
|
|
||||||
for i in countup(1, sonsLen(t) - 1):
|
for i in countup(1, sonsLen(t) - 1):
|
||||||
|
|
|
||||||
|
|
@ -504,7 +504,7 @@ proc transformCase(c: PTransf, n: PNode): PTransNode =
|
||||||
result.add(elseBranch)
|
result.add(elseBranch)
|
||||||
elif result.Pnode.lastSon.kind != nkElse and not (
|
elif result.Pnode.lastSon.kind != nkElse and not (
|
||||||
skipTypes(n.sons[0].Typ, abstractVarRange).Kind in
|
skipTypes(n.sons[0].Typ, abstractVarRange).Kind in
|
||||||
{tyInt..tyInt64, tyChar, tyEnum}):
|
{tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32}):
|
||||||
# fix a stupid code gen bug by normalizing:
|
# fix a stupid code gen bug by normalizing:
|
||||||
var elseBranch = newTransNode(nkElse, n.info, 1)
|
var elseBranch = newTransNode(nkElse, n.info, 1)
|
||||||
elseBranch[0] = newTransNode(nkNilLit, n.info, 0)
|
elseBranch[0] = newTransNode(nkNilLit, n.info, 0)
|
||||||
|
|
@ -704,7 +704,7 @@ proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode =
|
||||||
if nfTransf in n.flags or prc.kind in {skTemplate, skMacro}:
|
if nfTransf in n.flags or prc.kind in {skTemplate, skMacro}:
|
||||||
result = n
|
result = n
|
||||||
else:
|
else:
|
||||||
when useEffectSystem: trackProc(prc, n)
|
#when useEffectSystem: trackProc(prc, n)
|
||||||
var c = openTransf(module, "")
|
var c = openTransf(module, "")
|
||||||
result = processTransf(c, n)
|
result = processTransf(c, n)
|
||||||
if prc.kind != skMacro:
|
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:
|
if prc.kind == skIterator and prc.typ.callConv == ccClosure:
|
||||||
result = lambdalifting.liftIterator(prc, result)
|
result = lambdalifting.liftIterator(prc, result)
|
||||||
incl(result.flags, nfTransf)
|
incl(result.flags, nfTransf)
|
||||||
|
when useEffectSystem: trackProc(prc, result)
|
||||||
|
|
||||||
proc transformStmt*(module: PSym, n: PNode): PNode =
|
proc transformStmt*(module: PSym, n: PNode): PNode =
|
||||||
if nfTransf in n.flags:
|
if nfTransf in n.flags:
|
||||||
|
|
|
||||||
|
|
@ -93,8 +93,7 @@ proc getOpSym*(op: PNode): PSym =
|
||||||
|
|
||||||
proc getMagic*(op: PNode): TMagic =
|
proc getMagic*(op: PNode): TMagic =
|
||||||
case op.kind
|
case op.kind
|
||||||
of nkCall, nkHiddenCallConv, nkCommand, nkCallStrLit, nkPrefix, nkPostfix,
|
of nkCallKinds:
|
||||||
nkInfix:
|
|
||||||
case op.sons[0].Kind
|
case op.sons[0].Kind
|
||||||
of nkSym: result = op.sons[0].sym.magic
|
of nkSym: result = op.sons[0].sym.magic
|
||||||
else: result = mNone
|
else: result = mNone
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ type
|
||||||
|
|
||||||
wImmediate, wDestructor, wImportCpp, wImportObjC,
|
wImmediate, wDestructor, wImportCpp, wImportObjC,
|
||||||
wImportCompilerProc,
|
wImportCompilerProc,
|
||||||
wImportc, wExportc, wIncompleteStruct,
|
wImportc, wExportc, wIncompleteStruct, wRequiresInit,
|
||||||
wAlign, wNodecl, wPure, wSideeffect, wHeader,
|
wAlign, wNodecl, wPure, wSideeffect, wHeader,
|
||||||
wNosideeffect, wNoreturn, wMerge, wLib, wDynlib, wCompilerproc, wProcVar,
|
wNosideeffect, wNoreturn, wMerge, wLib, wDynlib, wCompilerproc, wProcVar,
|
||||||
wFatal, wError, wWarning, wHint, wLine, wPush, wPop, wDefine, wUndef,
|
wFatal, wError, wWarning, wHint, wLine, wPush, wPop, wDefine, wUndef,
|
||||||
|
|
@ -122,7 +122,7 @@ const
|
||||||
|
|
||||||
"immediate", "destructor", "importcpp", "importobjc",
|
"immediate", "destructor", "importcpp", "importobjc",
|
||||||
"importcompilerproc", "importc", "exportc", "incompletestruct",
|
"importcompilerproc", "importc", "exportc", "incompletestruct",
|
||||||
"align", "nodecl", "pure", "sideeffect",
|
"requiresinit", "align", "nodecl", "pure", "sideeffect",
|
||||||
"header", "nosideeffect", "noreturn", "merge", "lib", "dynlib",
|
"header", "nosideeffect", "noreturn", "merge", "lib", "dynlib",
|
||||||
"compilerproc", "procvar", "fatal", "error", "warning", "hint", "line",
|
"compilerproc", "procvar", "fatal", "error", "warning", "hint", "line",
|
||||||
"push", "pop", "define", "undef", "linedir", "stacktrace", "linetrace",
|
"push", "pop", "define", "undef", "linedir", "stacktrace", "linetrace",
|
||||||
|
|
|
||||||
|
|
@ -327,35 +327,35 @@ Numerical constants
|
||||||
|
|
||||||
`Numerical constants`:idx: are of a single type and have the form::
|
`Numerical constants`:idx: are of a single type and have the form::
|
||||||
|
|
||||||
hexdigit ::= digit | 'A'..'F' | 'a'..'f'
|
hexdigit = digit | 'A'..'F' | 'a'..'f'
|
||||||
octdigit ::= '0'..'7'
|
octdigit = '0'..'7'
|
||||||
bindigit ::= '0'..'1'
|
bindigit = '0'..'1'
|
||||||
HEX_LIT ::= '0' ('x' | 'X' ) hexdigit ( ['_'] hexdigit )*
|
HEX_LIT = '0' ('x' | 'X' ) hexdigit ( ['_'] hexdigit )*
|
||||||
DEC_LIT ::= digit ( ['_'] digit )*
|
DEC_LIT = digit ( ['_'] digit )*
|
||||||
OCT_LIT ::= '0o' octdigit ( ['_'] octdigit )*
|
OCT_LIT = '0o' octdigit ( ['_'] octdigit )*
|
||||||
BIN_LIT ::= '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )*
|
BIN_LIT = '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )*
|
||||||
|
|
||||||
INT_LIT ::= HEX_LIT
|
INT_LIT = HEX_LIT
|
||||||
| DEC_LIT
|
| DEC_LIT
|
||||||
| OCT_LIT
|
| OCT_LIT
|
||||||
| BIN_LIT
|
| BIN_LIT
|
||||||
|
|
||||||
INT8_LIT ::= INT_LIT ['\''] ('i' | 'I') '8'
|
INT8_LIT = INT_LIT ['\''] ('i' | 'I') '8'
|
||||||
INT16_LIT ::= INT_LIT ['\''] ('i' | 'I') '16'
|
INT16_LIT = INT_LIT ['\''] ('i' | 'I') '16'
|
||||||
INT32_LIT ::= INT_LIT ['\''] ('i' | 'I') '32'
|
INT32_LIT = INT_LIT ['\''] ('i' | 'I') '32'
|
||||||
INT64_LIT ::= INT_LIT ['\''] ('i' | 'I') '64'
|
INT64_LIT = INT_LIT ['\''] ('i' | 'I') '64'
|
||||||
|
|
||||||
UINT8_LIT ::= INT_LIT ['\''] ('u' | 'U')
|
UINT8_LIT = INT_LIT ['\''] ('u' | 'U')
|
||||||
UINT8_LIT ::= INT_LIT ['\''] ('u' | 'U') '8'
|
UINT8_LIT = INT_LIT ['\''] ('u' | 'U') '8'
|
||||||
UINT16_LIT ::= INT_LIT ['\''] ('u' | 'U') '16'
|
UINT16_LIT = INT_LIT ['\''] ('u' | 'U') '16'
|
||||||
UINT32_LIT ::= INT_LIT ['\''] ('u' | 'U') '32'
|
UINT32_LIT = INT_LIT ['\''] ('u' | 'U') '32'
|
||||||
UINT64_LIT ::= INT_LIT ['\''] ('u' | 'U') '64'
|
UINT64_LIT = INT_LIT ['\''] ('u' | 'U') '64'
|
||||||
|
|
||||||
exponent ::= ('e' | 'E' ) ['+' | '-'] digit ( ['_'] digit )*
|
exponent = ('e' | 'E' ) ['+' | '-'] digit ( ['_'] digit )*
|
||||||
FLOAT_LIT ::= digit (['_'] digit)* ('.' (['_'] digit)* [exponent] |exponent)
|
FLOAT_LIT = digit (['_'] digit)* ('.' (['_'] digit)* [exponent] |exponent)
|
||||||
FLOAT32_LIT ::= HEX_LIT '\'' ('f'|'F') '32'
|
FLOAT32_LIT = HEX_LIT '\'' ('f'|'F') '32'
|
||||||
| (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] ('f'|'F') '32'
|
| (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] ('f'|'F') '32'
|
||||||
FLOAT64_LIT ::= HEX_LIT '\'' ('f'|'F') '64'
|
FLOAT64_LIT = HEX_LIT '\'' ('f'|'F') '64'
|
||||||
| (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] ('f'|'F') '64'
|
| (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] ('f'|'F') '64'
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1818,6 +1818,24 @@ If a proc is annotated with the ``noinit`` pragma this refers to its implicit
|
||||||
proc returnUndefinedValue: int {.noinit.} = nil
|
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
|
let statement
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>`_).
|
programming language (therefore is the `manual <manual.html>`_).
|
||||||
|
|
||||||
Nimrod is free software; it is licensed under the
|
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
|
Compiler Usage
|
||||||
|
|
|
||||||
|
|
@ -338,24 +338,24 @@ when not defined(JS):
|
||||||
const
|
const
|
||||||
weekDays: array [0..6, TWeekDay] = [
|
weekDays: array [0..6, TWeekDay] = [
|
||||||
dSun, dMon, dTue, dWed, dThu, dFri, dSat]
|
dSun, dMon, dTue, dWed, dThu, dFri, dSat]
|
||||||
result.second = int(tm.second)
|
TTimeInfo(second: int(tm.second),
|
||||||
result.minute = int(tm.minute)
|
minute: int(tm.minute),
|
||||||
result.hour = int(tm.hour)
|
hour: int(tm.hour),
|
||||||
result.monthday = int(tm.monthday)
|
monthday: int(tm.monthday),
|
||||||
result.month = TMonth(tm.month)
|
month: TMonth(tm.month),
|
||||||
result.year = tm.year + 1900'i32
|
year: tm.year + 1900'i32,
|
||||||
result.weekday = weekDays[int(tm.weekDay)]
|
weekday: weekDays[int(tm.weekDay)],
|
||||||
result.yearday = int(tm.yearday)
|
yearday: int(tm.yearday),
|
||||||
result.isDST = tm.isDST > 0
|
isDST: tm.isDST > 0,
|
||||||
if local:
|
tzname: if local:
|
||||||
if result.isDST:
|
if tm.isDST > 0:
|
||||||
result.tzname = getTzname().DST
|
getTzname().DST
|
||||||
else:
|
else:
|
||||||
result.tzname = getTzname().nonDST
|
getTzname().nonDST
|
||||||
else:
|
else:
|
||||||
result.tzname = "UTC"
|
"UTC",
|
||||||
|
timezone: if local: getTimezone() else: 0
|
||||||
result.timezone = if local: getTimezone() else: 0
|
)
|
||||||
|
|
||||||
proc timeInfoToTM(t: TTimeInfo): structTM =
|
proc timeInfoToTM(t: TTimeInfo): structTM =
|
||||||
const
|
const
|
||||||
|
|
|
||||||
36
tests/reject/tuninit1.nim
Normal file
36
tests/reject/tuninit1.nim
Normal 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()
|
||||||
3
todo.txt
3
todo.txt
|
|
@ -2,7 +2,8 @@ version 0.9.4
|
||||||
=============
|
=============
|
||||||
|
|
||||||
- make 'bind' default for templates and introduce 'mixin';
|
- 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 ``[]=``
|
- special rule for ``[]=``
|
||||||
- ``=`` should be overloadable; requires specialization for ``=``; general
|
- ``=`` should be overloadable; requires specialization for ``=``; general
|
||||||
lift mechanism in the compiler is already implemented for 'fields'
|
lift mechanism in the compiler is already implemented for 'fields'
|
||||||
|
|
|
||||||
|
|
@ -227,9 +227,9 @@ when isMainModule:
|
||||||
|
|
||||||
#echo(createRules())
|
#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",
|
"The Nimrod compiler", "Compiler for the Nimrod programming language",
|
||||||
@[("bin/nimrod", "gpl2"), ("lib/*", "lgpl")],
|
@[("bin/nimrod", "MIT"), ("lib/*", "MIT")],
|
||||||
@["bin/nimrod"], @["config/*"], @["doc/*"], @["lib/*"],
|
@["bin/nimrod"], @["config/*"], @["doc/*"], @["lib/*"],
|
||||||
"gcc (>= 4:4.3.2)", "gcc (>= 4:4.3.2)")
|
"gcc (>= 4:4.3.2)", "gcc (>= 4:4.3.2)")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ Nimrod plays nice with others
|
||||||
* **The Nimrod Compiler can also generate C++ or Objective C for easier
|
* **The Nimrod Compiler can also generate C++ or Objective C for easier
|
||||||
interfacing.**
|
interfacing.**
|
||||||
* There are lots of bindings: for example, bindings to GTK2, the Windows API,
|
* 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.
|
libcurl, mySQL and SQLite are included in the standard distribution.
|
||||||
* A C to Nimrod conversion utility: New bindings to C libraries are easily
|
* A C to Nimrod conversion utility: New bindings to C libraries are easily
|
||||||
generated by ``c2nim``.
|
generated by ``c2nim``.
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ Language Additions
|
||||||
|
|
||||||
- Arrays can now be declared with a single integer literal ``N`` instead of a
|
- Arrays can now be declared with a single integer literal ``N`` instead of a
|
||||||
range; the range is then ``0..N-1``.
|
range; the range is then ``0..N-1``.
|
||||||
|
- Added ``requiresInit`` pragma to enforce explicit initialization.
|
||||||
|
|
||||||
|
|
||||||
2013-05-20 New website design!
|
2013-05-20 New website design!
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue