beginning of a taint mode; type system enhancements

This commit is contained in:
Araq
2011-09-24 00:46:41 +02:00
parent 2359b8b107
commit 6023e994fb
18 changed files with 265 additions and 283 deletions

View File

@@ -143,6 +143,7 @@ type
nkWhileStmt, # a while statement
nkCaseStmt, # a case statement
nkVarSection, # a var section
nkLetSection, # a let section
nkConstSection, # a const section
nkConstDef, # a const definition
nkTypeSection, # a type section (consists of type definitions)
@@ -178,6 +179,8 @@ type
nkRefTy,
nkPtrTy,
nkVarTy,
nkConstTy, # ``const T``
nkMutableTy, # ``mutable T``
nkDistinctTy, # distinct type
nkProcTy,
nkEnumTy,
@@ -186,7 +189,7 @@ type
TNodeKinds* = set[TNodeKind]
type
TSymFlag* = enum # already 29 flags!
TSymFlag* = enum # already 30 flags!
sfUsed, # read access of sym (for warnings) or simply used
sfExported, # symbol is exported from module
sfFromGeneric, # symbol is instantiation of a generic; this is needed
@@ -212,6 +215,7 @@ type
sfProcvar, # proc can be passed to a proc var
sfDiscriminant, # field is a discriminant in a record/object
sfDeprecated, # symbol is deprecated
sfError, # usage of symbol should trigger a compile-time error
sfInClosure, # variable is accessed by a closure
sfThread, # proc will run as a thread
sfCompileTime, # proc can be evaluated at compile time
@@ -254,7 +258,12 @@ type
tyPointer, tyOpenArray,
tyString, tyCString, tyForward,
tyInt, tyInt8, tyInt16, tyInt32, tyInt64, # signed integers
tyFloat, tyFloat32, tyFloat64, tyFloat128
tyFloat, tyFloat32, tyFloat64, tyFloat128,
tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64,
tyBigNum,
tyConst, tyMutable, tyVarargs,
tyIter, # unused
tyProxy # currently unused
TTypeKinds* = set[TTypeKind]

View File

@@ -30,94 +30,8 @@ const
"Copyright (c) 2004-2011 by Andreas Rumpf\n"
const
Usage = """
Usage:
nimrod command [options] [projectfile] [arguments]
Command:
compile, c compile project with default code generator (C)
doc generate the documentation for inputfile
i start Nimrod in interactive mode (limited)
Arguments:
arguments are passed to the program being run (if --run option is selected)
Options:
-p, --path:PATH add path to search paths
-d, --define:SYMBOL define a conditional symbol
-u, --undef:SYMBOL undefine a conditional symbol
-f, --forceBuild force rebuilding of all modules
--stackTrace:on|off turn stack tracing on|off
--lineTrace:on|off turn line tracing on|off
--threads:on|off turn support for multi-threading on|off
-x, --checks:on|off turn all runtime checks on|off
--objChecks:on|off turn obj conversion checks on|off
--fieldChecks:on|off turn case variant field checks on|off
--rangeChecks:on|off turn range checks on|off
--boundChecks:on|off turn bound checks on|off
--overflowChecks:on|off turn int over-/underflow checks on|off
-a, --assertions:on|off turn assertions on|off
--floatChecks:on|off turn all floating point (NaN/Inf) checks on|off
--nanChecks:on|off turn NaN checks on|off
--infChecks:on|off turn Inf checks on|off
--deadCodeElim:on|off whole program dead code elimination on|off
--opt:none|speed|size optimize not at all or for speed|size
--app:console|gui|lib generate a console|GUI application|dynamic library
-r, --run run the compiled program with given arguments
--advanced show advanced command line switches
-h, --help show this help
"""
AdvancedUsage = """
Advanced commands:
compileToC, cc compile project with C code generator
compileToCpp, cpp compile project to C++ code
compileToOC, objc compile project to Objective C code
rst2html convert a reStructuredText file to HTML
rst2tex convert a reStructuredText file to TeX
run run the project (with Tiny C backend; buggy!)
pretty pretty print the inputfile
genDepend generate a DOT file containing the
module dependency graph
dump dump all defined conditionals and search paths
check checks the project for syntax and semantic
idetools compiler support for IDEs: possible options:
--track:FILE,LINE,COL track a file/cursor position
--suggest suggest all possible symbols at position
--def list all possible symbols at position
--context list possible invokation context
Advanced options:
-o, --out:FILE set the output filename
--stdout output to stdout
-w, --warnings:on|off turn all warnings on|off
--warning[X]:on|off turn specific warning X on|off
--hints:on|off turn all hints on|off
--hint[X]:on|off turn specific hint X on|off
--lib:PATH set the system library path
--nimcache:PATH set the path used for generated files
-c, --compileOnly compile only; do not assemble or link
--noLinking compile but do not link
--noMain do not generate a main procedure
--genScript generate a compile script (in the 'nimcache'
subdirectory named 'compile_$project$scriptext')
--os:SYMBOL set the target operating system (cross-compilation)
--cpu:SYMBOL set the target processor (cross-compilation)
--debuginfo enables debug information
--debugger:on|off turn Embedded Nimrod Debugger on|off
-t, --passc:OPTION pass an option to the C compiler
-l, --passl:OPTION pass an option to the linker
--genMapping generate a mapping file containing
(Nimrod, mangled) identifier pairs
--lineDir:on|off generation of #line directive on|off
--threadanalysis:on|off turn thread analysis on|off
--skipCfg do not read the general configuration file
--skipProjCfg do not read the project's configuration file
--gc:refc|boehm|none use Nimrod's native GC|Boehm GC|no GC
--index:FILE use FILE to generate a documentation index file
--putenv:key=value set an environment variable
--listCmd list the commands used to execute external programs
--parallelBuild=0|1|... perform a parallel build
value = number of processors (0 for auto-detect)
--verbosity:0|1|2|3 set Nimrod's verbosity level (0 is default)
-v, --version show detailed version information
"""
Usage = slurp"doc/basicopt.txt".replace("//", "")
AdvancedUsage = slurp"doc/advopt.txt".replace("//", "")
proc getCommandLineDesc(): string =
result = `%`(HelpMessage, [VersionAsString, platform.os[platform.hostOS].name,
@@ -276,6 +190,7 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool =
of "symbolfiles": result = contains(gGlobalOptions, optSymbolFiles)
of "genscript": result = contains(gGlobalOptions, optGenScript)
of "threads": result = contains(gGlobalOptions, optThreads)
of "taintmode": result = contains(gGlobalOptions, optTaintMode)
else: InvalidCmdLineOption(passCmd1, switch, info)
proc processPath(path: string): string =
@@ -399,6 +314,7 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) =
of "assertions", "a": ProcessOnOffSwitch({optAssert}, arg, pass, info)
of "deadcodeelim": ProcessOnOffSwitchG({optDeadCodeElim}, arg, pass, info)
of "threads": ProcessOnOffSwitchG({optThreads}, arg, pass, info)
of "taintmode": ProcessOnOffSwitchG({optTaintMode}, arg, pass, info)
of "opt":
expectArg(switch, arg, pass, info)
case arg.normalize

View File

@@ -89,7 +89,7 @@ type
errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX,
errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument,
errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate,
errXhasSideEffects, errIteratorExpected,
errXhasSideEffects, errIteratorExpected, errWrongSymbolX,
errUser,
warnCannotOpenFile,
warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
@@ -315,6 +315,7 @@ const
errXisNoMacroOrTemplate: "\'$1\' is no macro or template",
errXhasSideEffects: "\'$1\' can have side effects",
errIteratorExpected: "iterator within for loop context expected",
errWrongSymbolX: "usage of \'$1\' is a user-defined error",
errUser: "$1",
warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]",
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]",

View File

@@ -46,7 +46,8 @@ type # please make sure we have under 32 options
optSuggest, # ideTools: 'suggest'
optContext, # ideTools: 'context'
optDef, # ideTools: 'def'
optThreadAnalysis # thread analysis pass
optThreadAnalysis, # thread analysis pass
optTaintMode # taint mode turned on
TGlobalOptions* = set[TGlobalOption]
TCommands* = enum # Nimrod's commands

View File

@@ -23,15 +23,15 @@ const
wMagic, wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader,
wCompilerProc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
wNoStackFrame}
wNoStackFrame, wError}
converterPragmas* = procPragmas
methodPragmas* = procPragmas
macroPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
wMagic, wNosideEffect, wCompilerProc, wDeprecated, wExtern,
wImportcpp, wImportobjc}
wImportcpp, wImportobjc, wError}
iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideEffect, wSideEffect,
wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern,
wImportcpp, wImportobjc}
wImportcpp, wImportobjc, wError}
stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, wLinedir,
wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError, wFatal,
@@ -43,14 +43,14 @@ const
wDeprecated, wExtern, wThread, wImportcpp, wImportobjc, wNoStackFrame}
typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
wPure, wHeader, wCompilerProc, wFinal, wSize, wExtern, wShallow,
wImportcpp, wImportobjc}
wImportcpp, wImportobjc, wError}
fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
wImportcpp, wImportobjc}
wImportcpp, wImportobjc, wError}
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
wMagic, wHeader, wDeprecated, wCompilerProc, wDynLib, wExtern,
wImportcpp, wImportobjc}
wImportcpp, wImportobjc, wError}
constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl,
wExtern, wImportcpp, wImportobjc}
wExtern, wImportcpp, wImportobjc, wError}
procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideEffect,
wThread}
allRoutinePragmas* = procPragmas + iteratorPragmas + lambdaPragmas
@@ -530,7 +530,12 @@ proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
if sym.typ != nil: incl(sym.typ.flags, tfThread)
of wHint: Message(it.info, hintUser, expectStrLit(c, it))
of wWarning: Message(it.info, warnUser, expectStrLit(c, it))
of wError: LocalError(it.info, errUser, expectStrLit(c, it))
of wError:
if sym != nil:
noVal(it)
incl(sym.flags, sfError)
else:
LocalError(it.info, errUser, expectStrLit(c, it))
of wFatal: Fatal(it.info, errUser, expectStrLit(c, it))
of wDefine: processDefine(c, it)
of wUndef: processUndef(c, it)

View File

@@ -178,7 +178,9 @@ proc makeRangeType(c: PContext, first, last: biggestInt,
proc markUsed*(n: PNode, s: PSym) =
incl(s.flags, sfUsed)
if sfDeprecated in s.flags: Message(n.info, warnDeprecated, s.name.s)
if {sfDeprecated, sfError} * s.flags != {}:
if sfDeprecated in s.flags: Message(n.info, warnDeprecated, s.name.s)
if sfError in s.flags: LocalError(n.info, errWrongSymbolX, s.name.s)
proc illFormedAst*(n: PNode) =
GlobalError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))

View File

@@ -103,7 +103,7 @@ proc checkConversionBetweenObjects(info: TLineInfo, castDest, src: PType) =
proc checkConvertible(info: TLineInfo, castDest, src: PType) =
const
IntegralTypes = {tyBool, tyEnum, tyChar, tyInt..tyFloat128}
if sameType(castDest, src):
if sameType(castDest, src) and castDest.sym == src.sym:
# don't annoy conversions that may be needed on another processor:
if not (castDest.kind in {tyInt..tyFloat128, tyNil}):
Message(info, hintConvFromXtoItselfNotNeeded, typeToString(castDest))

View File

@@ -50,16 +50,19 @@ proc equalParams*(a, b: PNode): TParamsEquality
proc isOrdinalType*(t: PType): bool
proc enumHasHoles*(t: PType): bool
const
abstractPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyDistinct, tyOrdinal}
abstractVar* = {tyVar, tyGenericInst, tyDistinct, tyOrdinal}
abstractRange* = {tyGenericInst, tyRange, tyDistinct, tyOrdinal}
abstractVarRange* = {tyGenericInst, tyRange, tyVar, tyDistinct, tyOrdinal}
abstractInst* = {tyGenericInst, tyDistinct, tyOrdinal}
abstractPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyDistinct, tyOrdinal,
tyConst, tyMutable}
abstractVar* = {tyVar, tyGenericInst, tyDistinct, tyOrdinal,
tyConst, tyMutable}
abstractRange* = {tyGenericInst, tyRange, tyDistinct, tyOrdinal,
tyConst, tyMutable}
abstractVarRange* = {tyGenericInst, tyRange, tyVar, tyDistinct, tyOrdinal,
tyConst, tyMutable}
abstractInst* = {tyGenericInst, tyDistinct, tyOrdinal, tyConst, tyMutable}
skipPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst}
skipPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyConst, tyMutable}
proc skipTypes*(t: PType, kinds: TTypeKinds): PType
proc elemType*(t: PType): PType
proc containsObject*(t: PType): bool
proc containsGarbageCollectedRef*(typ: PType): bool
proc containsHiddenPointer*(typ: PType): bool
@@ -122,10 +125,11 @@ proc getOrdValue(n: PNode): biggestInt =
result = 0
proc isCompatibleToCString(a: PType): bool =
result = false
if a.kind == tyArray:
if (firstOrd(a.sons[0]) == 0) and
(skipTypes(a.sons[0], {tyRange}).kind in {tyInt..tyInt64}) and
(skipTypes(a.sons[0], {tyRange, tyConst,
tyMutable, tyGenericInst}).kind in
{tyInt..tyInt64, tyUInt..tyUInt64}) and
(a.sons[1].kind == tyChar):
result = true
@@ -142,7 +146,7 @@ proc getProcHeader(sym: PSym): string =
add(result, ')')
if n.sons[0].typ != nil: result.add(": " & typeToString(n.sons[0].typ))
proc elemType(t: PType): PType =
proc elemType*(t: PType): PType =
assert(t != nil)
case t.kind
of tyGenericInst, tyDistinct: result = elemType(lastSon(t))
@@ -153,36 +157,7 @@ proc elemType(t: PType): PType =
proc skipGeneric(t: PType): PType =
result = t
while result.kind == tyGenericInst: result = lastSon(result)
proc skipRange(t: PType): PType =
result = t
while result.kind == tyRange: result = base(result)
proc skipAbstract(t: PType): PType =
result = t
while result.kind in {tyRange, tyGenericInst}: result = lastSon(result)
proc skipVar(t: PType): PType =
result = t
while result.kind == tyVar: result = result.sons[0]
proc skipVarGeneric(t: PType): PType =
result = t
while result.kind in {tyGenericInst, tyVar}: result = lastSon(result)
proc skipPtrsGeneric(t: PType): PType =
result = t
while result.kind in {tyGenericInst, tyVar, tyPtr, tyRef}:
result = lastSon(result)
proc skipVarGenericRange(t: PType): PType =
result = t
while result.kind in {tyGenericInst, tyVar, tyRange}: result = lastSon(result)
proc skipGenericRange(t: PType): PType =
result = t
while result.kind in {tyGenericInst, tyVar, tyRange}: result = lastSon(result)
proc skipTypes(t: PType, kinds: TTypeKinds): PType =
result = t
while result.kind in kinds: result = lastSon(result)
@@ -190,18 +165,18 @@ proc skipTypes(t: PType, kinds: TTypeKinds): PType =
proc isOrdinalType(t: PType): bool =
assert(t != nil)
result = (t.Kind in {tyChar, tyInt..tyInt64, tyBool, tyEnum}) or
(t.Kind in {tyRange, tyOrdinal}) and isOrdinalType(t.sons[0])
(t.Kind in {tyRange, tyOrdinal, tyConst, tyMutable, tyGenericInst}) and
isOrdinalType(t.sons[0])
proc enumHasHoles(t: PType): bool =
var b = t
while b.kind == tyRange: b = b.sons[0]
while b.kind in {tyConst, tyMutable, tyRange, tyGenericInst}: b = b.sons[0]
result = b.Kind == tyEnum and tfEnumHasHoles in b.flags
proc iterOverTypeAux(marker: var TIntSet, t: PType, iter: TTypeIter,
closure: PObject): bool
proc iterOverNode(marker: var TIntSet, n: PNode, iter: TTypeIter,
closure: PObject): bool =
result = false
if n != nil:
case n.kind
of nkNone..nkNilLit:
@@ -289,9 +264,9 @@ proc containsObject(t: PType): bool =
result = searchTypeFor(t, isObjectPredicate)
proc isObjectWithTypeFieldPredicate(t: PType): bool =
result = (t.kind == tyObject) and (t.sons[0] == nil) and
not ((t.sym != nil) and (sfPure in t.sym.flags)) and
not (tfFinal in t.flags)
result = t.kind == tyObject and t.sons[0] == nil and
not (t.sym != nil and sfPure in t.sym.flags) and
tfFinal notin t.flags
proc analyseObjectWithTypeFieldAux(t: PType,
marker: var TIntSet): TTypeFieldResult =
@@ -310,7 +285,7 @@ proc analyseObjectWithTypeFieldAux(t: PType,
if res == frHeader: result = frHeader
if result == frNone:
if isObjectWithTypeFieldPredicate(t): result = frHeader
of tyGenericInst, tyDistinct:
of tyGenericInst, tyDistinct, tyConst, tyMutable:
result = analyseObjectWithTypeFieldAux(lastSon(t), marker)
of tyArray, tyArrayConstr, tyTuple:
for i in countup(0, sonsLen(t) - 1):
@@ -406,8 +381,6 @@ proc mutateTypeAux(marker: var TIntSet, t: PType, iter: TTypeMutator,
if not ContainsOrIncl(marker, t.id):
for i in countup(0, sonsLen(t) - 1):
result.sons[i] = mutateTypeAux(marker, result.sons[i], iter, closure)
if (result.sons[i] == nil) and (result.kind == tyGenericInst):
assert(false)
if t.n != nil: result.n = mutateNode(marker, t.n, iter, closure)
assert(result != nil)
@@ -427,7 +400,10 @@ proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
"distinct $1", "enum", "ordinal[$1]", "array[$1, $2]", "object", "tuple",
"set[$1]", "range[$1]", "ptr ", "ref ", "var ", "seq[$1]", "proc",
"pointer", "OpenArray[$1]", "string", "CString", "Forward", "int", "int8",
"int16", "int32", "int64", "float", "float32", "float64", "float128"]
"int16", "int32", "int64", "float", "float32", "float64", "float128",
"uint", "uint8", "uint16", "uint32", "uint64", "bignum", "const ",
"!", "varargs[$1]", "iter[$1]", "proxy[$1]"]
var t = typ
result = ""
if t == nil: return
@@ -474,7 +450,7 @@ proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
add(result, typeToString(t.sons[i]))
if i < sonsLen(t) - 1: add(result, ", ")
add(result, ']')
of tyPtr, tyRef, tyVar:
of tyPtr, tyRef, tyVar, tyMutable, tyConst:
result = typeToStr[t.kind] & typeToString(t.sons[0])
of tyRange:
result = "range " & rangeToStr(t.n)
@@ -495,6 +471,8 @@ proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
addSep(prag)
add(prag, "thread")
if len(prag) != 0: add(result, "{." & prag & ".}")
of tyVarargs, tyIter, tyProxy:
result = typeToStr[t.kind] % typeToString(t.sons[0])
else:
result = typeToStr[t.kind]
@@ -528,7 +506,8 @@ proc firstOrd(t: PType): biggestInt =
else:
assert(t.n.sons[0].kind == nkSym)
result = t.n.sons[0].sym.position
of tyGenericInst, tyDistinct: result = firstOrd(lastSon(t))
of tyGenericInst, tyDistinct, tyConst, tyMutable:
result = firstOrd(lastSon(t))
else:
InternalError("invalid kind for first(" & $t.kind & ')')
result = 0
@@ -553,7 +532,8 @@ proc lastOrd(t: PType): biggestInt =
of tyEnum:
assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym)
result = t.n.sons[sonsLen(t.n) - 1].sym.position
of tyGenericInst, tyDistinct: result = lastOrd(lastSon(t))
of tyGenericInst, tyDistinct, tyConst, tyMutable:
result = lastOrd(lastSon(t))
else:
InternalError("invalid kind for last(" & $t.kind & ')')
result = 0
@@ -561,21 +541,21 @@ proc lastOrd(t: PType): biggestInt =
proc lengthOrd(t: PType): biggestInt =
case t.kind
of tyInt64, tyInt32, tyInt: result = lastOrd(t)
of tyDistinct: result = lengthOrd(t.sons[0])
of tyDistinct, tyConst, tyMutable: result = lengthOrd(t.sons[0])
else: result = lastOrd(t) - firstOrd(t) + 1
proc equalParam(a, b: PSym): TParamsEquality =
if SameTypeOrNil(a.typ, b.typ):
if (a.ast == b.ast):
if a.ast == b.ast:
result = paramsEqual
elif (a.ast != nil) and (b.ast != nil):
elif a.ast != nil and b.ast != nil:
if ExprStructuralEquivalent(a.ast, b.ast): result = paramsEqual
else: result = paramsIncompatible
elif (a.ast != nil):
elif a.ast != nil:
result = paramsEqual
elif (b.ast != nil):
elif b.ast != nil:
result = paramsIncompatible
else:
else:
result = paramsNotEqual
proc equalParams(a, b: PNode): TParamsEquality =
@@ -611,11 +591,10 @@ proc SameTypeOrNil(a, b: PType): bool =
if a == b:
result = true
else:
if (a == nil) or (b == nil): result = false
if a == nil or b == nil: result = false
else: result = SameType(a, b)
proc SameLiteral(x, y: PNode): bool =
result = false
if x.kind == y.kind:
case x.kind
of nkCharLit..nkInt64Lit: result = x.intVal == y.intVal
@@ -625,7 +604,7 @@ proc SameLiteral(x, y: PNode): bool =
proc SameRanges(a, b: PNode): bool =
result = SameLiteral(a.sons[0], b.sons[0]) and
SameLiteral(a.sons[1], b.sons[1])
SameLiteral(a.sons[1], b.sons[1])
proc sameTuple(a, b: PType, DistinctOf: bool): bool =
# two tuples are equivalent iff the names, types and positions are the same;
@@ -637,7 +616,7 @@ proc sameTuple(a, b: PType, DistinctOf: bool): bool =
if DistinctOf: result = equalOrDistinctOf(a.sons[i], b.sons[i])
else: result = SameType(a.sons[i], b.sons[i])
if not result: return
if (a.n != nil) and (b.n != nil):
if a.n != nil and b.n != nil:
for i in countup(0, sonsLen(a.n) - 1):
# check field names:
if a.n.sons[i].kind != nkSym: InternalError(a.n.info, "sameTuple")
@@ -659,13 +638,14 @@ proc SameType(x, y: PType): bool =
return false
case a.Kind
of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString,
tyInt..tyFloat128, tyExpr, tyStmt, tyTypeDesc:
tyInt..tyBigNum, tyExpr, tyStmt, tyTypeDesc:
result = true
of tyEnum, tyForward, tyObject, tyDistinct: result = (a.id == b.id)
of tyEnum, tyForward, tyObject, tyDistinct, tyProxy: result = (a.id == b.id)
of tyTuple: result = sameTuple(a, b, false)
of tyGenericInst: result = sameType(lastSon(a), lastSon(b))
of tyGenericParam, tyGenericInvokation, tyGenericBody, tySequence, tyOrdinal,
tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr, tyArray, tyProc:
tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr, tyArray, tyProc,
tyConst, tyMutable, tyVarargs, tyIter:
if sonsLen(a) == sonsLen(b):
result = true
for i in countup(0, sonsLen(a) - 1):
@@ -694,13 +674,14 @@ proc equalOrDistinctOf(x, y: PType): bool =
return false
case a.Kind
of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString,
tyInt..tyFloat128, tyExpr, tyStmt, tyTypeDesc:
tyInt..tyBigNum, tyExpr, tyStmt, tyTypeDesc:
result = true
of tyEnum, tyForward, tyObject, tyDistinct: result = (a.id == b.id)
of tyEnum, tyForward, tyObject, tyDistinct, tyProxy: result = (a.id == b.id)
of tyTuple: result = sameTuple(a, b, true)
of tyGenericInst: result = equalOrDistinctOf(lastSon(a), lastSon(b))
of tyGenericParam, tyGenericInvokation, tyGenericBody, tySequence, tyOrdinal,
tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr, tyArray, tyProc:
tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr, tyArray, tyProc,
tyConst, tyMutable, tyVarargs, tyIter:
if sonsLen(a) == sonsLen(b):
result = true
for i in countup(0, sonsLen(a) - 1):
@@ -769,7 +750,7 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool =
result = false #InternalError('shit found');
of tyEmpty, tyNil:
result = kind == skConst
of tyString, tyBool, tyChar, tyEnum, tyInt..tyFloat128, tyCString, tyPointer:
of tyString, tyBool, tyChar, tyEnum, tyInt..tyBigNum, tyCString, tyPointer:
result = true
of tyOrdinal:
result = kind == skParam
@@ -778,7 +759,7 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool =
of tyRange:
result = skipTypes(t.sons[0], abstractInst).kind in
{tyChar, tyEnum, tyInt..tyFloat128}
of tyOpenArray:
of tyOpenArray, tyVarargs:
result = (kind == skParam) and typeAllowedAux(marker, t.sons[0], skVar)
of tySequence:
result = (kind != skConst) and typeAllowedAux(marker, t.sons[0], skVar) or
@@ -788,7 +769,7 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool =
t.sons[1].kind == tyEmpty
of tyPtr, tyRef:
result = typeAllowedAux(marker, t.sons[0], skVar)
of tyArrayConstr, tyTuple, tySet:
of tyArrayConstr, tyTuple, tySet, tyConst, tyMutable, tyIter, tyProxy:
for i in countup(0, sonsLen(t) - 1):
result = typeAllowedAux(marker, t.sons[i], kind)
if not result: break
@@ -856,19 +837,19 @@ proc computeSizeAux(typ: PType, a: var biggestInt): biggestInt =
return
typ.size = - 2 # mark as being computed
case typ.kind
of tyInt:
of tyInt, tyUInt:
result = IntSize
a = result
of tyInt8, tyBool, tyChar:
of tyInt8, tyUInt8, tyBool, tyChar:
result = 1
a = result
of tyInt16:
of tyInt16, tyUInt16:
result = 2
a = result
of tyInt32, tyFloat32:
of tyInt32, tyUInt32, tyFloat32:
result = 4
a = result
of tyInt64, tyFloat64:
of tyInt64, tyUInt64, tyFloat64:
result = 8
a = result
of tyFloat:
@@ -878,7 +859,8 @@ proc computeSizeAux(typ: PType, a: var biggestInt): biggestInt =
if typ.callConv == ccClosure: result = 2 * ptrSize
else: result = ptrSize
a = ptrSize
of tyNil, tyCString, tyString, tySequence, tyPtr, tyRef, tyVar, tyOpenArray:
of tyNil, tyCString, tyString, tySequence, tyPtr, tyRef, tyVar, tyOpenArray,
tyBigNum:
result = ptrSize
a = result
of tyArray, tyArrayConstr:
@@ -930,7 +912,8 @@ proc computeSizeAux(typ: PType, a: var biggestInt): biggestInt =
if result < 0: return
if a < maxAlign: a = maxAlign
result = align(result, a)
of tyGenericInst, tyDistinct, tyGenericBody:
of tyGenericInst, tyDistinct, tyGenericBody, tyMutable, tyConst, tyIter,
tyProxy:
result = computeSizeAux(lastSon(typ), a)
else:
#internalError("computeSizeAux()")

View File

@@ -40,6 +40,7 @@ Advanced options:
(Nimrod, mangled) identifier pairs
--lineDir:on|off generation of #line directive on|off
--threadanalysis:on|off turn thread analysis on|off
--taintMode:on|off turn taint mode on|off
--skipCfg do not read the general configuration file
--skipProjCfg do not read the project's configuration file
--gc:refc|boehm|none use Nimrod's native GC|Boehm GC|no GC

View File

@@ -435,8 +435,9 @@ have no side-effect can be used in constant expressions too:
The rules for compile-time computability are:
1. Literals are compile-time computable.
2. Procedure calls of the form ``p(X)`` are compile-time computable if
1. Literals are compile-time computable.
2. Type conversions are compile-time computable.
3. Procedure calls of the form ``p(X)`` are compile-time computable if
``p`` is a proc without side-effects (see the `noSideEffect pragma`_
for details) and if ``X`` is a (possibly empty) list of compile-time
computable arguments.
@@ -1207,7 +1208,7 @@ Void type
~~~~~~~~~
The `void`:idx: type denotes the absense of any type. Parameters of
type ``void`` are treated as non-existent, a result ``void`` type means that
type ``void`` are treated as non-existent, ``void`` as a return type means that
the procedure does not return a value:
.. code-block:: nimrod
@@ -1293,7 +1294,49 @@ algorithm (in pseudo-code) determines type equality:
Since types are graphs which can have cycles, the above algorithm needs an
auxiliary set ``s`` to detect this case.
Type equality modulo type distinction
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The following algorithm (in pseudo-code) determines whether two types
are equal with no respect to ``distinct`` types. For brevity the cycle check
with an auxiliary set ``s`` is omitted:
.. code-block:: nimrod
proc typeEqualsOrDistinct(a, b: PType): bool =
if a.kind == b.kind:
case a.kind
of int, intXX, float, floatXX, char, string, cstring, pointer,
bool, nil, void:
# leaf type: kinds identical; nothing more to check
result = true
of ref, ptr, var, set, seq, openarray:
result = typeEqualsOrDistinct(a.baseType, b.baseType)
of range:
result = typeEqualsOrDistinct(a.baseType, b.baseType) and
(a.rangeA == b.rangeA) and (a.rangeB == b.rangeB)
of array:
result = typeEqualsOrDistinct(a.baseType, b.baseType) and
typeEqualsOrDistinct(a.indexType, b.indexType)
of tuple:
if a.tupleLen == b.tupleLen:
for i in 0..a.tupleLen-1:
if not typeEqualsOrDistinct(a[i], b[i]): return false
result = true
of distinct:
result = typeEqualsOrDistinct(a.baseType, b.baseType)
of object, enum:
result = a == b
of proc:
result = typeEqualsOrDistinct(a.parameterTuple, b.parameterTuple) and
typeEqualsOrDistinct(a.resultType, b.resultType) and
a.callingConvention == b.callingConvention
elif a.kind == distinct:
result = typeEqualsOrDistinct(a.baseType, b)
elif b.kind == distinct:
result = typeEqualsOrDistinct(a, b.baseType)
Subtype relation
~~~~~~~~~~~~~~~~
@@ -1323,14 +1366,6 @@ algorithm returns true:
# XXX range types?
proc isImplicitlyConvertible(a, b: PType): bool =
case a.kind
of proc:
if b.kind == proc:
var x = a.parameterTuple
var y = b.parameterTuple
if x.tupleLen == y.tupleLen:
for i in 0.. x.tupleLen-1:
if not isSubtype(x[i], y[i]): return false
result = isSubType(b.resultType, a.resultType)
of int8: result = b.kind in {int16, int32, int64, int}
of int16: result = b.kind in {int32, int64, int}
of int32: result = b.kind in {int64, int}
@@ -1357,10 +1392,9 @@ algorithm returns true:
proc isExplicitlyConvertible(a, b: PType): bool =
if isImplicitlyConvertible(a, b): return true
if typeEqualsOrDistinct(a, b): return true
if isIntegralType(a) and isIntegralType(b): return true
if isSubtype(a, b) or isSubtype(b, a): return true
if a.kind == distinct and typeEquals(a.baseType, b): return true
if b.kind == distinct and typeEquals(b.baseType, a): return true
return false
The convertible relation can be relaxed by a user-defined type
@@ -1379,7 +1413,10 @@ The convertible relation can be relaxed by a user-defined type
# you can use the explicit form too
x = chr.toInt
echo x # => 97
The type conversion ``T(a)`` is an L-value if ``a`` is an L-value and
``typeEqualsOrDistinct(T, type(a))`` holds.
Assignment compatibility
~~~~~~~~~~~~~~~~~~~~~~~~
@@ -2852,7 +2889,7 @@ as helpers for macros.
noReturn pragma
---------------
The `noreturn`:idx: pragma is used to mark a proc that it never returns.
The `noreturn`:idx: pragma is used to mark a proc that never returns.
Acyclic pragma
@@ -2930,8 +2967,17 @@ only consist of an assembler statement.
error pragma
------------
The `error`:idx: pragma is used to make the compiler output an error message
with the given content. Compilation currently aborts after an error, but this
may be changed in later versions.
with the given content. Compilation does not necessarily abort after an error
though.
The ``error`` pragma can also be used to
annotate a symbol (like an iterator or proc). The *usage* of the symbol then
triggers a compile-time error. This is especially useful to rule out that some
operation is valid due to overloading and type conversions:
.. code-block:: nimrod
## check that underlying int values are compared and not the pointers:
proc `==`(x, y: ptr int): bool {.error.}
fatal pragma
@@ -3238,7 +3284,7 @@ Even though Nimrod's `thread`:idx: support and semantics are preliminary,
they should be quite usable already. To enable thread support
the ``--threads:on`` command line switch needs to be used. The ``system``
module then contains several threading primitives.
See the `threads <threads.html>`_ and `inboxes <inboxes.html>`_ modules
See the `threads <threads.html>`_ and `channels <channels.html>`_ modules
for the thread API.
Nimrod's memory model for threads is quite different than that of other common
@@ -3259,7 +3305,7 @@ violations of the `no heap sharing restriction`:idx:\: This restriction implies
that it is invalid to construct a data structure that consists of memory
allocated from different (thread local) heaps.
Since the semantic checking of threads requires a whole program analysis,
Since the semantic checking of threads requires whole program analysis,
it is quite expensive and can be turned off with ``--threadanalysis:off`` to
improve compile times.

View File

@@ -36,15 +36,18 @@ type
nnkMacroDef, nnkTemplateDef, nnkIteratorDef, nnkOfBranch,
nnkElifBranch, nnkExceptBranch, nnkElse, nnkMacroStmt,
nnkAsmStmt, nnkPragma, nnkIfStmt, nnkWhenStmt,
nnkForStmt, nnkWhileStmt, nnkCaseStmt, nnkVarSection,
nnkConstSection, nnkConstDef, nnkTypeSection, nnkTypeDef,
nnkForStmt, nnkWhileStmt, nnkCaseStmt,
nnkVarSection, nnkLetSection, nnkConstSection,
nnkConstDef, nnkTypeSection, nnkTypeDef,
nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt,
nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt,
nnkDiscardStmt, nnkStmtList, nnkImportStmt, nnkFromStmt,
nnkIncludeStmt, nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr,
nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy,
nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen,
nnkRefTy, nnkPtrTy, nnkVarTy, nnkDistinctTy,
nnkRefTy, nnkPtrTy, nnkVarTy,
nnkConstTy, nnkMutableTy,
nnkDistinctTy,
nnkProcTy, nnkEnumTy, nnkEnumFieldDef, nnkReturnToken
TNimNodeKinds* = set[TNimrodNodeKind]
TNimrodTypeKind* = enum
@@ -184,7 +187,7 @@ proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} =
## in a string literal node
return newStrLitNode(repr(n))
proc prettyPrint*(n: PNimrodNode): string {.compileTime.} =
proc toLisp*(n: PNimrodNode): string {.compileTime.} =
## Convert the AST `n` to a human-readable string
##
## You can use this as a tool to explore the Nimrod's abstract syntax
@@ -204,10 +207,10 @@ proc prettyPrint*(n: PNimrodNode): string {.compileTime.} =
of nnkIdent: add(result, $n.ident)
of nnkSym, nnkNone: assert false
else:
add(result, prettyPrint(n[0]))
add(result, toLisp(n[0]))
for j in 1..n.len-1:
add(result, ", ")
add(result, prettyPrint(n[j]))
add(result, toLisp(n[j]))
add(result, ")")
@@ -215,7 +218,7 @@ proc toYaml*(n: PNimrodNode): string {.magic: "AstToYaml".}
## Converts the AST `n` to an YAML string
##
## Provides more detailed, potentially harder to digest information
## than `prettyPrint`
## than `toLisp`
proc parseExpr*(s: string) : expr {.magic: "ParseExprToAst".}
## Compiles the passed string to its AST representation
@@ -226,10 +229,13 @@ proc parseStmt*(s: string) : stmt {.magic: "ParseStmtToAst".}
## Expects one or more statements
proc getAst*(macroOrTemplate: expr): expr {.magic: "ExpandMacroToAst".}
## Obtains the AST nodes returned from a macro or template invocation
## example:
## macro FooMacro() =
## var ast = getAst(BarTemplate())
## Obtains the AST nodes returned from a macro or template invocation.
## Example:
##
## .. code-block:: nimrod
##
## macro FooMacro() =
## var ast = getAst(BarTemplate())
proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} =
## checks that `n` is of kind `k`. If this is not the case,

View File

@@ -107,7 +107,7 @@ proc cgiError*(msg: string) {.noreturn.} =
raise e
proc getEncodedData(allowedMethods: set[TRequestMethod]): string =
case getenv("REQUEST_METHOD")
case getenv("REQUEST_METHOD").string
of "POST":
if methodPost notin allowedMethods:
cgiError("'REQUEST_METHOD' 'POST' is not supported")
@@ -192,131 +192,131 @@ proc validateData*(data: PStringTable, validKeys: openarray[string]) =
proc getContentLength*(): string =
## returns contents of the ``CONTENT_LENGTH`` environment variable
return getenv("CONTENT_LENGTH")
return getenv("CONTENT_LENGTH").string
proc getContentType*(): string =
## returns contents of the ``CONTENT_TYPE`` environment variable
return getenv("CONTENT_Type")
return getenv("CONTENT_Type").string
proc getDocumentRoot*(): string =
## returns contents of the ``DOCUMENT_ROOT`` environment variable
return getenv("DOCUMENT_ROOT")
return getenv("DOCUMENT_ROOT").string
proc getGatewayInterface*(): string =
## returns contents of the ``GATEWAY_INTERFACE`` environment variable
return getenv("GATEWAY_INTERFACE")
return getenv("GATEWAY_INTERFACE").string
proc getHttpAccept*(): string =
## returns contents of the ``HTTP_ACCEPT`` environment variable
return getenv("HTTP_ACCEPT")
return getenv("HTTP_ACCEPT").string
proc getHttpAcceptCharset*(): string =
## returns contents of the ``HTTP_ACCEPT_CHARSET`` environment variable
return getenv("HTTP_ACCEPT_CHARSET")
return getenv("HTTP_ACCEPT_CHARSET").string
proc getHttpAcceptEncoding*(): string =
## returns contents of the ``HTTP_ACCEPT_ENCODING`` environment variable
return getenv("HTTP_ACCEPT_ENCODING")
return getenv("HTTP_ACCEPT_ENCODING").string
proc getHttpAcceptLanguage*(): string =
## returns contents of the ``HTTP_ACCEPT_LANGUAGE`` environment variable
return getenv("HTTP_ACCEPT_LANGUAGE")
return getenv("HTTP_ACCEPT_LANGUAGE").string
proc getHttpConnection*(): string =
## returns contents of the ``HTTP_CONNECTION`` environment variable
return getenv("HTTP_CONNECTION")
return getenv("HTTP_CONNECTION").string
proc getHttpCookie*(): string =
## returns contents of the ``HTTP_COOKIE`` environment variable
return getenv("HTTP_COOKIE")
return getenv("HTTP_COOKIE").string
proc getHttpHost*(): string =
## returns contents of the ``HTTP_HOST`` environment variable
return getenv("HTTP_HOST")
return getenv("HTTP_HOST").string
proc getHttpReferer*(): string =
## returns contents of the ``HTTP_REFERER`` environment variable
return getenv("HTTP_REFERER")
return getenv("HTTP_REFERER").string
proc getHttpUserAgent*(): string =
## returns contents of the ``HTTP_USER_AGENT`` environment variable
return getenv("HTTP_USER_AGENT")
return getenv("HTTP_USER_AGENT").string
proc getPathInfo*(): string =
## returns contents of the ``PATH_INFO`` environment variable
return getenv("PATH_INFO")
return getenv("PATH_INFO").string
proc getPathTranslated*(): string =
## returns contents of the ``PATH_TRANSLATED`` environment variable
return getenv("PATH_TRANSLATED")
return getenv("PATH_TRANSLATED").string
proc getQueryString*(): string =
## returns contents of the ``QUERY_STRING`` environment variable
return getenv("QUERY_STRING")
return getenv("QUERY_STRING").string
proc getRemoteAddr*(): string =
## returns contents of the ``REMOTE_ADDR`` environment variable
return getenv("REMOTE_ADDR")
return getenv("REMOTE_ADDR").string
proc getRemoteHost*(): string =
## returns contents of the ``REMOTE_HOST`` environment variable
return getenv("REMOTE_HOST")
return getenv("REMOTE_HOST").string
proc getRemoteIdent*(): string =
## returns contents of the ``REMOTE_IDENT`` environment variable
return getenv("REMOTE_IDENT")
return getenv("REMOTE_IDENT").string
proc getRemotePort*(): string =
## returns contents of the ``REMOTE_PORT`` environment variable
return getenv("REMOTE_PORT")
return getenv("REMOTE_PORT").string
proc getRemoteUser*(): string =
## returns contents of the ``REMOTE_USER`` environment variable
return getenv("REMOTE_USER")
return getenv("REMOTE_USER").string
proc getRequestMethod*(): string =
## returns contents of the ``REQUEST_METHOD`` environment variable
return getenv("REQUEST_METHOD")
return getenv("REQUEST_METHOD").string
proc getRequestURI*(): string =
## returns contents of the ``REQUEST_URI`` environment variable
return getenv("REQUEST_URI")
return getenv("REQUEST_URI").string
proc getScriptFilename*(): string =
## returns contents of the ``SCRIPT_FILENAME`` environment variable
return getenv("SCRIPT_FILENAME")
return getenv("SCRIPT_FILENAME").string
proc getScriptName*(): string =
## returns contents of the ``SCRIPT_NAME`` environment variable
return getenv("SCRIPT_NAME")
return getenv("SCRIPT_NAME").string
proc getServerAddr*(): string =
## returns contents of the ``SERVER_ADDR`` environment variable
return getenv("SERVER_ADDR")
return getenv("SERVER_ADDR").string
proc getServerAdmin*(): string =
## returns contents of the ``SERVER_ADMIN`` environment variable
return getenv("SERVER_ADMIN")
return getenv("SERVER_ADMIN").string
proc getServerName*(): string =
## returns contents of the ``SERVER_NAME`` environment variable
return getenv("SERVER_NAME")
return getenv("SERVER_NAME").string
proc getServerPort*(): string =
## returns contents of the ``SERVER_PORT`` environment variable
return getenv("SERVER_PORT")
return getenv("SERVER_PORT").string
proc getServerProtocol*(): string =
## returns contents of the ``SERVER_PROTOCOL`` environment variable
return getenv("SERVER_PROTOCOL")
return getenv("SERVER_PROTOCOL").string
proc getServerSignature*(): string =
## returns contents of the ``SERVER_SIGNATURE`` environment variable
return getenv("SERVER_SIGNATURE")
return getenv("SERVER_SIGNATURE").string
proc getServerSoftware*(): string =
## returns contents of the ``SERVER_SOFTWARE`` environment variable
return getenv("SERVER_SOFTWARE")
return getenv("SERVER_SOFTWARE").string
proc setTestData*(keysvalues: openarray[string]) =
## fills the appropriate environment variables to test your CGI application.
@@ -360,10 +360,10 @@ proc setCookie*(name, value: string) =
var
gcookies: PStringTable = nil
proc getCookie*(name: string): string =
proc getCookie*(name: string): TaintedString =
## Gets a cookie. If no cookie of `name` exists, "" is returned.
if gcookies == nil: gcookies = parseCookies(getHttpCookie())
result = gcookies[name]
result = TaintedString(gcookies[name])
proc existsCookie*(name: string): bool =
## Checks if a cookie of `name` exists.

View File

@@ -692,7 +692,7 @@ proc findEnvVar(key: string): int =
if startsWith(environment[i], temp): return i
return -1
proc getEnv*(key: string): string =
proc getEnv*(key: string): TaintedString =
## Returns the value of the `environment variable`:idx: named `key`.
##
## If the variable does not exist, "" is returned. To distinguish
@@ -700,11 +700,11 @@ proc getEnv*(key: string): string =
## `existsEnv(key)`.
var i = findEnvVar(key)
if i >= 0:
return substr(environment[i], find(environment[i], '=')+1)
return TaintedString(substr(environment[i], find(environment[i], '=')+1))
else:
var env = cgetenv(key)
if env == nil: return ""
result = $env
if env == nil: return TaintedString("")
result = TaintedString($env)
proc existsEnv*(key: string): bool =
## Checks whether the environment variable named `key` exists.
@@ -733,14 +733,15 @@ proc putEnv*(key, val: string) =
if SetEnvironmentVariableA(key, val) == 0'i32:
OSError()
iterator envPairs*(): tuple[key, value: string] =
iterator envPairs*(): tuple[key, value: TaintedString] =
## Iterate over all `environments variables`:idx:. In the first component
## of the tuple is the name of the current variable stored, in the second
## its value.
getEnvVarsC()
for i in 0..high(environment):
var p = find(environment[i], '=')
yield (substr(environment[i], 0, p-1), substr(environment[i], p+1))
yield (TaintedString(substr(environment[i], 0, p-1)),
TaintedString(substr(environment[i], p+1)))
iterator walkFiles*(pattern: string): string =
## Iterate over all the files that match the `pattern`. On POSIX this uses
@@ -1078,18 +1079,18 @@ proc exclFilePermissions*(filename: string,
proc getHomeDir*(): string {.rtl, extern: "nos$1".} =
## Returns the home directory of the current user.
when defined(windows): return getEnv("USERPROFILE") & "\\"
else: return getEnv("HOME") & "/"
when defined(windows): return string(getEnv("USERPROFILE")) & "\\"
else: return string(getEnv("HOME")) & "/"
proc getConfigDir*(): string {.rtl, extern: "nos$1".} =
## Returns the config directory of the current user for applications.
when defined(windows): return getEnv("APPDATA") & "\\"
else: return getEnv("HOME") & "/.config/"
when defined(windows): return string(getEnv("APPDATA")) & "\\"
else: return string(getEnv("HOME")) & "/.config/"
proc getTempDir*(): string {.rtl, extern: "nos$1".} =
## Returns the temporary directory of the current user for applications to
## save temporary files in.
when defined(windows): return getEnv("TEMP") & "\\"
when defined(windows): return string(getEnv("TEMP")) & "\\"
else: return "/tmp/"
when defined(windows):
@@ -1107,14 +1108,14 @@ when defined(windows):
if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLineA())
result = ownArgv.len-1
proc paramStr*(i: int): string {.rtl, extern: "nos$1".} =
proc paramStr*(i: int): TaintedString {.rtl, extern: "nos$1".} =
## Returns the `i`-th `command line argument`:idx: given to the
## application.
##
## `i` should be in the range `1..paramCount()`, else
## the `EOutOfIndex` exception is raised.
if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLineA())
return ownArgv[i]
return TaintedString(ownArgv[i])
elif not defined(createNimRtl):
# On Posix, there is no portable way to get the command line from a DLL.
@@ -1122,8 +1123,8 @@ elif not defined(createNimRtl):
cmdCount {.importc: "cmdCount".}: cint
cmdLine {.importc: "cmdLine".}: cstringArray
proc paramStr*(i: int): string =
if i < cmdCount and i >= 0: return $cmdLine[i]
proc paramStr*(i: int): TaintedString =
if i < cmdCount and i >= 0: return TaintedString($cmdLine[i])
raise newException(EInvalidIndex, "invalid index")
proc paramCount*(): int = return cmdCount-1
@@ -1172,13 +1173,14 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1".} =
result = "" # error!
else:
# little heuristic that may work on other POSIX-like systems:
result = getEnv("_")
result = string(getEnv("_"))
if len(result) == 0:
result = ParamStr(0) # POSIX guaranties that this contains the executable
# as it has been executed by the calling process
result = string(ParamStr(0))
# POSIX guaranties that this contains the executable
# as it has been executed by the calling process
if len(result) > 0 and result[0] != DirSep: # not an absolute path?
# iterate over any path in the $PATH environment variable
for p in split(getEnv("PATH"), {PathSep}):
for p in split(string(getEnv("PATH")), {PathSep}):
var x = joinPath(p, result)
if ExistsFile(x): return x
@@ -1230,7 +1232,7 @@ proc findExe*(exe: string): string =
## is added an ``.exe`` file extension if it has no extension.
result = addFileExt(exe, os.exeExt)
if ExistsFile(result): return
var path = os.getEnv("PATH")
var path = string(os.getEnv("PATH"))
for candidate in split(path, pathSep):
var x = candidate / result
if ExistsFile(x): return x

View File

@@ -48,7 +48,7 @@ when defined(os.ParamCount):
else:
result.cmd = ""
for i in countup(1, ParamCount()):
result.cmd = result.cmd & quoteIfContainsWhite(paramStr(i)) & ' '
result.cmd = result.cmd & quoteIfContainsWhite(paramStr(i).string) & ' '
result.kind = cmdEnd
result.key = ""
result.val = ""

View File

@@ -787,6 +787,19 @@ proc compileOption*(option, arg: string): bool {.
const
hasThreadSupport = compileOption("threads")
hasSharedHeap = defined(boehmgc) # don't share heaps; every thread has its own
# taintMode = compileOption("taintmode")
when defined(taintMode):
# XXX use a compile time option for it!
type TaintedString* = distinct string ## a distinct string type that
## is `tainted`:idx:. It is an alias for
## ``string`` if the taint mode is not
## turned on. Use the ``-d:taintMode``
## command line switch to turn the taint
## mode on.
else:
type TaintedString* = string
when hasThreadSupport:
{.pragma: rtlThreadVar, threadvar.}
@@ -896,11 +909,6 @@ type # these work for most platforms:
PInt64* = ptr Int64 ## an alias for ``ptr int64``
PInt32* = ptr Int32 ## an alias for ``ptr int32``
type TOptional*[T] = object
case hasValue* : bool
of true: value*: T
of false: nil
proc toFloat*(i: int): float {.
magic: "ToFloat", noSideEffect, importc: "toFloat".}
## converts an integer `i` into a ``float``. If the conversion
@@ -1653,7 +1661,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
proc FlushFile*(f: TFile) {.importc: "fflush", noDecl.}
## Flushes `f`'s buffer.
proc readFile*(filename: string): string
proc readFile*(filename: string): TaintedString
## Opens a file named `filename` for reading. Then reads the
## file's content completely into a string and
## closes the file afterwards. Returns the string.
@@ -1675,7 +1683,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
proc write*(f: TFile, a: openArray[string])
## Writes a value to the file `f`. May throw an IO exception.
proc readLine*(f: TFile): string
proc readLine*(f: TFile): TaintedString
## reads a line of text from the file `f`. May throw an IO exception.
## A line of text may be delimited by ``CR``, ``LF`` or
## ``CRLF``. The newline character(s) are not part of the returned string.
@@ -1814,22 +1822,22 @@ when not defined(EcmaScript) and not defined(NimrodVM):
when hasThreadSupport:
include "system/channels"
iterator lines*(filename: string): string =
iterator lines*(filename: string): TaintedString =
## Iterate over any line in the file named `filename`.
## If the file does not exist `EIO` is raised.
var f = open(filename)
var res = ""
while not endOfFile(f):
rawReadLine(f, res)
yield res
yield TaintedString(res)
Close(f)
iterator lines*(f: TFile): string =
iterator lines*(f: TFile): TaintedString =
## Iterate over any line in the file `f`.
var res = ""
while not endOfFile(f):
rawReadLine(f, res)
yield res
yield TaintedString(res)
include "system/assign"
include "system/repr"

View File

@@ -55,8 +55,8 @@ proc rawReadLine(f: TFile, result: var string) =
break
add result, chr(int(c))
proc readLine(f: TFile): string =
result = ""
proc readLine(f: TFile): TaintedString =
result = TaintedString("")
rawReadLine(f, result)
proc write(f: TFile, i: int) =
@@ -81,7 +81,7 @@ proc write(f: TFile, c: Char) = putc(c, f)
proc write(f: TFile, a: openArray[string]) =
for x in items(a): write(f, x)
proc readFile(filename: string): string =
proc readFile(filename: string): TaintedString =
var f = open(filename)
try:
var len = getFileSize(f)

View File

@@ -2,14 +2,14 @@ Version 0.8.14
==============
- 'let x = y'
- threads should not have an inbox per default
- T(x) as l-value
- fix actors.nim
- make threadvar efficient again on linux after testing
- fix the 'const' issues
- test the sort implementation again
- optional indentation for 'case' statement
- taint mode
- const ptr/ref
- {.error.} pragma for proc headers
version 0.9.0

View File

@@ -45,6 +45,8 @@ Language Additions
- The built-in type ``void`` can be used to denote the absense of any type.
This is useful in generic code.
- Return types may be of the type ``var T`` to return an l-value.
- The error pragma can now be used to mark symbols whose *usage* should trigger
a compile-time error.
Compiler Additions