diff options
-rwxr-xr-x | compiler/ast.nim | 13 | ||||
-rwxr-xr-x | compiler/commands.nim | 92 | ||||
-rwxr-xr-x | compiler/msgs.nim | 3 | ||||
-rwxr-xr-x | compiler/options.nim | 3 | ||||
-rwxr-xr-x | compiler/pragmas.nim | 21 | ||||
-rwxr-xr-x | compiler/semdata.nim | 4 | ||||
-rwxr-xr-x | compiler/semexprs.nim | 2 | ||||
-rwxr-xr-x | compiler/types.nim | 143 | ||||
-rwxr-xr-x | doc/advopt.txt | 1 | ||||
-rwxr-xr-x | doc/manual.txt | 84 | ||||
-rwxr-xr-x | lib/core/macros.nim | 28 | ||||
-rwxr-xr-x | lib/pure/cgi.nim | 70 | ||||
-rwxr-xr-x | lib/pure/os.nim | 42 | ||||
-rwxr-xr-x | lib/pure/parseopt.nim | 2 | ||||
-rwxr-xr-x | lib/system.nim | 30 | ||||
-rwxr-xr-x | lib/system/sysio.nim | 6 | ||||
-rwxr-xr-x | todo.txt | 4 | ||||
-rwxr-xr-x | web/news.txt | 2 |
18 files changed, 266 insertions, 284 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 85ec2c9b3..200f32ed2 100755 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -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] diff --git a/compiler/commands.nim b/compiler/commands.nim index 9e58b0deb..3a2c0d6cb 100755 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -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 diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 7c0f11181..d34c6b410 100755 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -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]", diff --git a/compiler/options.nim b/compiler/options.nim index 0e4960a7e..1af06953d 100755 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -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 diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 039474266..9bc5adf65 100755 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -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) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 306638d6c..e9c0567d1 100755 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -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})) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 3a8b827a0..da64db9a9 100755 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -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)) diff --git a/compiler/types.nim b/compiler/types.nim index d8d0caf35..144f4052e 100755 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -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} - - skipPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst} + 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, 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()") diff --git a/doc/advopt.txt b/doc/advopt.txt index db8e44a8b..fd569da6d 100755 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -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 diff --git a/doc/manual.txt b/doc/manual.txt index 56c1fa95c..122668dfb 100755 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -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. diff --git a/lib/core/macros.nim b/lib/core/macros.nim index bebcab677..e362922c8 100755 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -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, diff --git a/lib/pure/cgi.nim b/lib/pure/cgi.nim index 47517ae98..c8536236d 100755 --- a/lib/pure/cgi.nim +++ b/lib/pure/cgi.nim @@ -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. diff --git a/lib/pure/os.nim b/lib/pure/os.nim index ccbf98828..50c6ffeba 100755 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -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 diff --git a/lib/pure/parseopt.nim b/lib/pure/parseopt.nim index 1981c9242..347871bac 100755 --- a/lib/pure/parseopt.nim +++ b/lib/pure/parseopt.nim @@ -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 = "" diff --git a/lib/system.nim b/lib/system.nim index ea004b925..8e4205189 100755 --- a/lib/system.nim +++ b/lib/system.nim @@ -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" diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim index d48e87ae2..56815c0c5 100755 --- a/lib/system/sysio.nim +++ b/lib/system/sysio.nim @@ -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) diff --git a/todo.txt b/todo.txt index 6fff06b95..7a2c6dbf7 100755 --- a/todo.txt +++ b/todo.txt @@ -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 diff --git a/web/news.txt b/web/news.txt index bd2d970d2..462133d24 100755 --- a/web/news.txt +++ b/web/news.txt @@ -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 |