diff options
208 files changed, 8889 insertions, 6328 deletions
diff --git a/.travis.yml b/.travis.yml index 38e1eba28..5a091d0c7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,7 +31,6 @@ before_script: - cd csources - sh build.sh - cd .. - - sed -i -e 's,cc = gcc,cc = clang,' config/nim.cfg - export PATH=$(pwd)/bin${PATH:+:$PATH} script: - nim c koch @@ -46,6 +45,8 @@ script: - nimble install niminst - nim c --taintMode:on -d:nimCoroutines tests/testament/tester - tests/testament/tester --pedantic all -d:nimCoroutines + - nim c -o:bin/nimpretty nimpretty/nimpretty.nim + - nim c -r nimpretty/tester.nim - ./koch web - ./koch csource - ./koch nimsuggest diff --git a/appveyor.yml b/appveyor.yml index a79d32e41..daa1d4e48 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -47,6 +47,8 @@ build_script: - koch boot -d:release - koch nimble - nim e tests/test_nimscript.nims + - nim c -o:bin/nimpretty.exe nimpretty/nimpretty.nim + - nim c -r nimpretty/tester.nim - nimble install zip -y - nimble install opengl - nimble install sdl1 diff --git a/changelog.md b/changelog.md index cb2a4b91d..3a905d0b9 100644 --- a/changelog.md +++ b/changelog.md @@ -48,6 +48,10 @@ - For string inputs, ``strutils.isUpperAscii`` and ``strutils.isLowerAscii`` now require a second mandatory parameter ``skipNonAlpha``. +- The procs ``parseHexInt`` and ``parseOctInt`` now fail on empty strings + and strings containing only valid prefixes, e.g. "0x" for hex integers. + + #### Breaking changes in the compiler - The undocumented ``#? braces`` parsing mode was removed. @@ -72,6 +76,10 @@ - Added the procs ``math.floorMod`` and ``math.floorDiv`` for floor based integer division. - Added the procs ``rationals.`div```, ``rationals.`mod```, ``rationals.floorDiv`` and ``rationals.floorMod`` for rationals. - Added the proc ``math.prod`` for product of elements in openArray. +- Added the proc ``parseBinInt`` to parse a binary integer from a string, which returns the value. +- ``parseOct`` and ``parseBin`` in parseutils now also support the ``maxLen`` argument similar to ``parseHexInt`` +- Added the proc ``flush`` for memory mapped files. +- Added the ``MemMapFileStream``. ### Library changes @@ -91,10 +99,17 @@ - The `terminal` module now exports additional procs for generating ANSI color codes as strings. - Added the parameter ``val`` for the ``CritBitTree[int].inc`` proc. +- An exception raised from a ``test`` block of ``unittest`` now shows its type in + error message. +- The ``compiler/nimeval`` API was rewritten to simplify the "compiler as an + API". Using the Nim compiler and its VM as a scripting engine has never been + easier. See ``tests/compilerapi/tcompilerapi.nim`` for an example of how to + use the Nim VM in a native Nim application. - Added the parameter ``val`` for the ``CritBitTree[T].incl`` proc. -- An exception raised from ``test`` block of ``unittest`` now shows its type in - the error message - The proc ``tgamma`` was renamed to ``gamma``. ``tgamma`` is deprecated. +- The ``pegs`` module now exports getters for the fields of its ``Peg`` and ``NonTerminal`` + object types. ``Peg``s with child nodes now have the standard ``items`` and ``pairs`` + iterators. ### Language additions @@ -104,6 +119,13 @@ - In order to make ``for`` loops and iterators more flexible to use Nim now supports so called "for-loop macros". See the `manual <manual.html#macros-for-loop-macros>`_ for more details. +- the `typedesc` special type has been renamed to just `type`. +- `static` and `type` are now also modifiers similar to `ref` and `ptr`. + They denote the special types `static[T]` and `type[T]`. +- Forcing compile-time evaluation with `static` now supports specifying + the desired target type (as a concrete type or as a type class) +- The `type` operator now supports checking that the supplied expression + matches an expected type constraint. ### Language changes @@ -131,6 +153,8 @@ - Nim now supports `except` clause in the export statement. +- Range float types, example `range[0.0 .. Inf]`. More details in language manual. + ### Tool changes - ``jsondoc2`` has been renamed ``jsondoc``, similar to how ``doc2`` was renamed diff --git a/compiler/ast.nim b/compiler/ast.nim index 014ffaf26..0e0ff77b8 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -10,7 +10,7 @@ # abstract syntax tree + symbol table import - msgs, hashes, nversion, options, strutils, std / sha1, ropes, idents, + lineinfos, hashes, nversion, options, strutils, std / sha1, ropes, idents, intsets, idgen type @@ -293,6 +293,10 @@ const # the compiler will avoid printing such names # in user messages. + sfHoisted* = sfForward + # an expression was hoised to an anonymous variable. + # the flag is applied to the var/let symbol + sfNoForward* = sfRegister # forward declarations are not required (per module) sfReorder* = sfForward @@ -454,6 +458,9 @@ type nfPreventCg # this node should be ignored by the codegen nfBlockArg # this a stmtlist appearing in a call (e.g. a do block) nfFromTemplate # a top-level node returned from a template + nfDefaultParam # an automatically inserter default parameter + nfDefaultRefsParam # a default param value references another parameter + # the flag is applied to proc default values and to calls TNodeFlags* = set[TNodeFlag] TTypeFlag* = enum # keep below 32 for efficiency reasons (now: beyond that) @@ -571,8 +578,8 @@ type TMagic* = enum # symbols that require compiler magic: mNone, mDefined, mDefinedInScope, mCompiles, mArrGet, mArrPut, mAsgn, - mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mTypeOf, mRoof, mPlugin, - mEcho, mShallowCopy, mSlurp, mStaticExec, + mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mType, mTypeOf, + mRoof, mPlugin, mEcho, mShallowCopy, mSlurp, mStaticExec, mStatic, mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst, mUnaryLt, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mNewSeqOfCap, @@ -634,14 +641,18 @@ type mNaN, mInf, mNegInf, mCompileOption, mCompileOptionArg, mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel, - mNKind, mNSymKind + mNKind, mNSymKind, + + mNccValue, mNccInc, mNcsAdd, mNcsIncl, mNcsLen, mNcsAt, + mNctPut, mNctLen, mNctGet, mNctHasNext, mNctNext, + mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal, mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNLineInfo, mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mNBindSym, mLocals, mNCallSite, - mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl, + mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl, mNGenSym, mNHint, mNWarning, mNError, - mInstantiationInfo, mGetTypeInfo, mNGenSym, + mInstantiationInfo, mGetTypeInfo, mNimvm, mIntDefine, mStrDefine, mRunnableExamples, mException, mBuiltinType @@ -967,7 +978,7 @@ const PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, nfDotSetter, nfDotField, nfIsRef, nfPreventCg, nfLL, - nfFromTemplate} + nfFromTemplate, nfDefaultRefsParam} namePos* = 0 patternPos* = 1 # empty except for term rewriting macros genericParamsPos* = 2 @@ -998,8 +1009,8 @@ const skMethod, skConverter} var ggDebug* {.deprecated.}: bool ## convenience switch for trying out things -var - gMainPackageId*: int +#var +# gMainPackageId*: int proc isCallExpr*(n: PNode): bool = result = n.kind in nkCallKinds @@ -1058,22 +1069,6 @@ proc newTree*(kind: TNodeKind; children: varargs[PNode]): PNode = result.info = children[0].info result.sons = @children -proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode = - result = newNode(kind) - result.intVal = intVal - -proc newIntTypeNode*(kind: TNodeKind, intVal: BiggestInt, typ: PType): PNode = - result = newIntNode(kind, intVal) - result.typ = typ - -proc newFloatNode*(kind: TNodeKind, floatVal: BiggestFloat): PNode = - result = newNode(kind) - result.floatVal = floatVal - -proc newStrNode*(kind: TNodeKind, strVal: string): PNode = - result = newNode(kind) - result.strVal = strVal - template previouslyInferred*(t: PType): PType = if t.sons.len > 1: t.lastSon else: nil @@ -1095,9 +1090,6 @@ proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym, # writeStacktrace() # MessageOut(name.s & " has id: " & toString(result.id)) -var emptyNode* = newNode(nkEmpty) # XXX global variable here! -# There is a single empty node that is shared! Do not overwrite it! - proc isMetaType*(t: PType): bool = return t.kind in tyMetaTypes or (t.kind == tyStatic and t.n == nil) or @@ -1224,18 +1216,35 @@ proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode = result.info = info result.typ = typ +proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode = + result = newNode(kind) + result.intVal = intVal + +proc newIntTypeNode*(kind: TNodeKind, intVal: BiggestInt, typ: PType): PNode = + result = newIntNode(kind, intVal) + result.typ = typ + +proc newFloatNode*(kind: TNodeKind, floatVal: BiggestFloat): PNode = + result = newNode(kind) + result.floatVal = floatVal + +proc newStrNode*(kind: TNodeKind, strVal: string): PNode = + result = newNode(kind) + result.strVal = strVal + +proc newStrNode*(strVal: string; info: TLineInfo): PNode = + result = newNodeI(nkStrLit, info) + result.strVal = strVal + proc addSon*(father, son: PNode) = assert son != nil if isNil(father.sons): father.sons = @[] add(father.sons, son) -var emptyParams = newNode(nkFormalParams) -emptyParams.addSon(emptyNode) - proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode, - params = emptyParams, + params, name, pattern, genericParams, - pragmas, exceptions = ast.emptyNode): PNode = + pragmas, exceptions: PNode): PNode = result = newNodeI(kind, info) result.sons = @[name, pattern, genericParams, params, pragmas, exceptions, body] @@ -1702,7 +1711,7 @@ proc isImportedException*(t: PType; conf: ConfigRef): bool = result = true proc isInfixAs*(n: PNode): bool = - return n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.id == getIdent("as").id + return n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "as" proc findUnresolvedStatic*(n: PNode): PNode = if n.kind == nkSym and n.typ.kind == tyStatic and n.typ.n == nil: @@ -1727,3 +1736,5 @@ template incompleteType*(t: PType): bool = template typeCompleted*(s: PSym) = incl s.flags, sfNoForward + +template getBody*(s: PSym): PNode = s.ast[bodyPos] diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 15072e175..f474ca65e 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -12,81 +12,58 @@ # the data structures here are used in various places of the compiler. import - ast, hashes, intsets, strutils, options, msgs, ropes, idents, rodutils + ast, hashes, intsets, strutils, options, lineinfos, ropes, idents, rodutils, + msgs proc hashNode*(p: RootRef): Hash -proc treeToYaml*(n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope +proc treeToYaml*(conf: ConfigRef; n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope # Convert a tree into its YAML representation; this is used by the # YAML code generator and it is invaluable for debugging purposes. # If maxRecDepht <> -1 then it won't print the whole graph. -proc typeToYaml*(n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope -proc symToYaml*(n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope -proc lineInfoToStr*(info: TLineInfo): Rope - -# ----------------------- node sets: --------------------------------------- -proc objectSetContains*(t: TObjectSet, obj: RootRef): bool - # returns true whether n is in t -proc objectSetIncl*(t: var TObjectSet, obj: RootRef) - # include an element n in the table t -proc objectSetContainsOrIncl*(t: var TObjectSet, obj: RootRef): bool - # more are not needed ... - -# ----------------------- str table ----------------------------------------- -proc strTableContains*(t: TStrTable, n: PSym): bool -proc strTableAdd*(t: var TStrTable, n: PSym) -proc strTableGet*(t: TStrTable, name: PIdent): PSym - -type - TTabIter*{.final.} = object # consider all fields here private - h*: Hash # current hash - -proc initTabIter*(ti: var TTabIter, tab: TStrTable): PSym -proc nextIter*(ti: var TTabIter, tab: TStrTable): PSym - # usage: - # var - # i: TTabIter - # s: PSym - # s = InitTabIter(i, table) - # while s != nil: - # ... - # s = NextIter(i, table) - # - -type - TIdentIter*{.final.} = object # iterator over all syms with same identifier - h*: Hash # current hash - name*: PIdent - - -proc initIdentIter*(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym -proc nextIdentIter*(ti: var TIdentIter, tab: TStrTable): PSym - -# these are for debugging only: They are not really deprecated, but I want -# the warning so that release versions do not contain debugging statements: -proc debug*(n: PSym) {.deprecated.} -proc debug*(n: PType) {.deprecated.} -proc debug*(n: PNode) {.deprecated.} - -template mdbg*: bool {.dirty.} = - when compiles(c.module): - c.module.fileIdx.int32 == c.config.projectMainIdx - elif compiles(c.c.module): - c.c.module.fileIdx.int32 == c.c.config.projectMainIdx - elif compiles(m.c.module): - m.c.module.fileIdx.int32 == m.c.config.projectMainIdx - elif compiles(cl.c.module): - cl.c.module.fileIdx.int32 == cl.c.config.projectMainIdx - elif compiles(p): - when compiles(p.lex): - p.lex.fileIdx.int32 == p.lex.config.projectMainIdx +proc typeToYaml*(conf: ConfigRef; n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope +proc symToYaml*(conf: ConfigRef; n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope +proc lineInfoToStr*(conf: ConfigRef; info: TLineInfo): Rope + +when declared(echo): + # these are for debugging only: They are not really deprecated, but I want + # the warning so that release versions do not contain debugging statements: + proc debug*(n: PSym; conf: ConfigRef = nil) {.deprecated.} + proc debug*(n: PType; conf: ConfigRef = nil) {.deprecated.} + proc debug*(n: PNode; conf: ConfigRef = nil) {.deprecated.} + + template debug*(x: PSym|PType|PNode) {.deprecated.} = + when compiles(c.config): + debug(c.config, x) + elif compiles(c.graph.config): + debug(c.graph.config, x) else: - p.module.module.fileIdx.int32 == p.config.projectMainIdx - elif compiles(m.module.fileIdx): - m.module.fileIdx.int32 == m.config.projectMainIdx - elif compiles(L.fileIdx): - L.fileIdx.int32 == L.config.projectMainIdx - else: - error() + error() + + template debug*(x: auto) {.deprecated.} = + echo x + + template mdbg*: bool {.deprecated.} = + when compiles(c.graph): + c.module.fileIdx == c.graph.config.projectMainIdx + elif compiles(c.module): + c.module.fileIdx == c.config.projectMainIdx + elif compiles(c.c.module): + c.c.module.fileIdx == c.c.config.projectMainIdx + elif compiles(m.c.module): + m.c.module.fileIdx == m.c.config.projectMainIdx + elif compiles(cl.c.module): + cl.c.module.fileIdx == cl.c.config.projectMainIdx + elif compiles(p): + when compiles(p.lex): + p.lex.fileIdx == p.lex.config.projectMainIdx + else: + p.module.module.fileIdx == p.config.projectMainIdx + elif compiles(m.module.fileIdx): + m.module.fileIdx == m.config.projectMainIdx + elif compiles(L.fileIdx): + L.fileIdx == L.config.projectMainIdx + else: + error() # --------------------------- ident tables ---------------------------------- proc idTableGet*(t: TIdTable, key: PIdObj): RootRef @@ -102,7 +79,6 @@ proc idNodeTablePut*(t: var TIdNodeTable, key: PIdObj, val: PNode) proc getSymFromList*(list: PNode, ident: PIdent, start: int = 0): PSym proc lookupInRecord*(n: PNode, field: PIdent): PSym -proc getModule*(s: PSym): PSym proc mustRehash*(length, counter: int): bool proc nextTry*(h, maxHash: Hash): Hash {.inline.} @@ -193,7 +169,7 @@ proc lookupInRecord(n: PNode, field: PIdent): PSym = if n.sym.name.id == field.id: result = n.sym else: return nil -proc getModule(s: PSym): PSym = +proc getModule*(s: PSym): PSym = result = s assert((result.kind == skModule) or (result.owner != result)) while result != nil and result.kind != skModule: result = result.owner @@ -250,16 +226,16 @@ proc flagsToStr[T](flags: set[T]): Rope = add(result, makeYamlString($x)) result = "[" & result & "]" -proc lineInfoToStr(info: TLineInfo): Rope = - result = "[$1, $2, $3]" % [makeYamlString(toFilename(info)), +proc lineInfoToStr(conf: ConfigRef; info: TLineInfo): Rope = + result = "[$1, $2, $3]" % [makeYamlString(toFilename(conf, info)), rope(toLinenumber(info)), rope(toColumn(info))] -proc treeToYamlAux(n: PNode, marker: var IntSet, +proc treeToYamlAux(conf: ConfigRef; n: PNode, marker: var IntSet, indent, maxRecDepth: int): Rope -proc symToYamlAux(n: PSym, marker: var IntSet, +proc symToYamlAux(conf: ConfigRef; n: PSym, marker: var IntSet, indent, maxRecDepth: int): Rope -proc typeToYamlAux(n: PType, marker: var IntSet, +proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet, indent, maxRecDepth: int): Rope proc ropeConstr(indent: int, c: openArray[Rope]): Rope = @@ -273,7 +249,7 @@ proc ropeConstr(indent: int, c: openArray[Rope]): Rope = inc(i, 2) addf(result, "$N$1}", [rspaces(indent)]) -proc symToYamlAux(n: PSym, marker: var IntSet, indent: int, +proc symToYamlAux(conf: ConfigRef; n: PSym, marker: var IntSet, indent: int, maxRecDepth: int): Rope = if n == nil: result = rope("null") @@ -281,20 +257,20 @@ proc symToYamlAux(n: PSym, marker: var IntSet, indent: int, result = "\"$1 @$2\"" % [rope(n.name.s), rope( strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))] else: - var ast = treeToYamlAux(n.ast, marker, indent + 2, maxRecDepth - 1) + var ast = treeToYamlAux(conf, n.ast, marker, indent + 2, maxRecDepth - 1) result = ropeConstr(indent, [rope("kind"), makeYamlString($n.kind), rope("name"), makeYamlString(n.name.s), - rope("typ"), typeToYamlAux(n.typ, marker, + rope("typ"), typeToYamlAux(conf, n.typ, marker, indent + 2, maxRecDepth - 1), - rope("info"), lineInfoToStr(n.info), + rope("info"), lineInfoToStr(conf, n.info), rope("flags"), flagsToStr(n.flags), rope("magic"), makeYamlString($n.magic), rope("ast"), ast, rope("options"), flagsToStr(n.options), rope("position"), rope(n.position)]) -proc typeToYamlAux(n: PType, marker: var IntSet, indent: int, +proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet, indent: int, maxRecDepth: int): Rope = if n == nil: result = rope("null") @@ -306,15 +282,15 @@ proc typeToYamlAux(n: PType, marker: var IntSet, indent: int, result = rope("[") for i in countup(0, sonsLen(n) - 1): if i > 0: add(result, ",") - addf(result, "$N$1$2", [rspaces(indent + 4), typeToYamlAux(n.sons[i], + addf(result, "$N$1$2", [rspaces(indent + 4), typeToYamlAux(conf, n.sons[i], marker, indent + 4, maxRecDepth - 1)]) addf(result, "$N$1]", [rspaces(indent + 2)]) else: result = rope("null") result = ropeConstr(indent, [rope("kind"), makeYamlString($n.kind), - rope("sym"), symToYamlAux(n.sym, marker, - indent + 2, maxRecDepth - 1), rope("n"), treeToYamlAux(n.n, marker, + rope("sym"), symToYamlAux(conf, n.sym, marker, + indent + 2, maxRecDepth - 1), rope("n"), treeToYamlAux(conf, n.n, marker, indent + 2, maxRecDepth - 1), rope("flags"), flagsToStr(n.flags), rope("callconv"), makeYamlString(CallingConvToStr[n.callConv]), @@ -322,7 +298,7 @@ proc typeToYamlAux(n: PType, marker: var IntSet, indent: int, rope("align"), rope(n.align), rope("sons"), result]) -proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int, +proc treeToYamlAux(conf: ConfigRef; n: PNode, marker: var IntSet, indent: int, maxRecDepth: int): Rope = if n == nil: result = rope("null") @@ -330,7 +306,7 @@ proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int, var istr = rspaces(indent + 2) result = "{$N$1\"kind\": $2" % [istr, makeYamlString($n.kind)] if maxRecDepth != 0: - addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)]) + addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(conf, n.info)]) case n.kind of nkCharLit..nkInt64Lit: addf(result, ",$N$1\"intVal\": $2", [istr, rope(n.intVal)]) @@ -344,7 +320,7 @@ proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int, addf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)]) of nkSym: addf(result, ",$N$1\"sym\": $2", - [istr, symToYamlAux(n.sym, marker, indent + 2, maxRecDepth)]) + [istr, symToYamlAux(conf, n.sym, marker, indent + 2, maxRecDepth)]) of nkIdent: if n.ident != nil: addf(result, ",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)]) @@ -355,27 +331,27 @@ proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int, addf(result, ",$N$1\"sons\": [", [istr]) for i in countup(0, sonsLen(n) - 1): if i > 0: add(result, ",") - addf(result, "$N$1$2", [rspaces(indent + 4), treeToYamlAux(n.sons[i], + addf(result, "$N$1$2", [rspaces(indent + 4), treeToYamlAux(conf, n.sons[i], marker, indent + 4, maxRecDepth - 1)]) addf(result, "$N$1]", [istr]) addf(result, ",$N$1\"typ\": $2", - [istr, typeToYamlAux(n.typ, marker, indent + 2, maxRecDepth)]) + [istr, typeToYamlAux(conf, n.typ, marker, indent + 2, maxRecDepth)]) addf(result, "$N$1}", [rspaces(indent)]) -proc treeToYaml(n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope = +proc treeToYaml(conf: ConfigRef; n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope = var marker = initIntSet() - result = treeToYamlAux(n, marker, indent, maxRecDepth) + result = treeToYamlAux(conf, n, marker, indent, maxRecDepth) -proc typeToYaml(n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope = +proc typeToYaml(conf: ConfigRef; n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope = var marker = initIntSet() - result = typeToYamlAux(n, marker, indent, maxRecDepth) + result = typeToYamlAux(conf, n, marker, indent, maxRecDepth) -proc symToYaml(n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope = +proc symToYaml(conf: ConfigRef; n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope = var marker = initIntSet() - result = symToYamlAux(n, marker, indent, maxRecDepth) + result = symToYamlAux(conf, n, marker, indent, maxRecDepth) -proc debugTree*(n: PNode, indent: int, maxRecDepth: int; renderType=false): Rope -proc debugType(n: PType, maxRecDepth=100): Rope = +proc debugTree*(conf: ConfigRef; n: PNode, indent: int, maxRecDepth: int; renderType=false): Rope +proc debugType(conf: ConfigRef; n: PType, maxRecDepth=100): Rope = if n == nil: result = rope("null") else: @@ -385,7 +361,7 @@ proc debugType(n: PType, maxRecDepth=100): Rope = add(result, n.sym.name.s) if n.kind in IntegralTypes and n.n != nil: add(result, ", node: ") - add(result, debugTree(n.n, 2, maxRecDepth-1, renderType=true)) + add(result, debugTree(conf, n.n, 2, maxRecDepth-1, renderType=true)) if (n.kind != tyString) and (sonsLen(n) > 0) and maxRecDepth != 0: add(result, "(") for i in countup(0, sonsLen(n) - 1): @@ -393,13 +369,13 @@ proc debugType(n: PType, maxRecDepth=100): Rope = if n.sons[i] == nil: add(result, "null") else: - add(result, debugType(n.sons[i], maxRecDepth-1)) + add(result, debugType(conf, n.sons[i], maxRecDepth-1)) if n.kind == tyObject and n.n != nil: add(result, ", node: ") - add(result, debugTree(n.n, 2, maxRecDepth-1, renderType=true)) + add(result, debugTree(conf, n.n, 2, maxRecDepth-1, renderType=true)) add(result, ")") -proc debugTree(n: PNode, indent: int, maxRecDepth: int; +proc debugTree(conf: ConfigRef; n: PNode, indent: int, maxRecDepth: int; renderType=false): Rope = if n == nil: result = rope("null") @@ -409,7 +385,7 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int; [istr, makeYamlString($n.kind)] when defined(useNodeIds): addf(result, ",$N$1\"id\": $2", [istr, rope(n.id)]) - addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)]) + addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(conf, n.info)]) if maxRecDepth != 0: addf(result, ",$N$1\"flags\": $2", [istr, rope($n.flags)]) case n.kind @@ -429,7 +405,7 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int; # [istr, symToYaml(n.sym, indent, maxRecDepth), # rope(n.sym.id)]) if renderType and n.sym.typ != nil: - addf(result, ",$N$1\"typ\": $2", [istr, debugType(n.sym.typ, 2)]) + addf(result, ",$N$1\"typ\": $2", [istr, debugType(conf, n.sym.typ, 2)]) of nkIdent: if n.ident != nil: addf(result, ",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)]) @@ -440,27 +416,28 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int; addf(result, ",$N$1\"sons\": [", [istr]) for i in countup(0, sonsLen(n) - 1): if i > 0: add(result, ",") - addf(result, "$N$1$2", [rspaces(indent + 4), debugTree(n.sons[i], + addf(result, "$N$1$2", [rspaces(indent + 4), debugTree(conf, n.sons[i], indent + 4, maxRecDepth - 1, renderType)]) addf(result, "$N$1]", [istr]) addf(result, "$N$1}", [rspaces(indent)]) -proc debug(n: PSym) = - if n == nil: - echo("null") - elif n.kind == skUnknown: - echo("skUnknown") - else: - #writeLine(stdout, $symToYaml(n, 0, 1)) - echo("$1_$2: $3, $4, $5, $6" % [ - n.name.s, $n.id, $flagsToStr(n.flags), $flagsToStr(n.loc.flags), - $lineInfoToStr(n.info), $n.kind]) +when declared(echo): + proc debug(n: PSym; conf: ConfigRef) = + if n == nil: + echo("null") + elif n.kind == skUnknown: + echo("skUnknown") + else: + #writeLine(stdout, $symToYaml(n, 0, 1)) + echo("$1_$2: $3, $4, $5, $6" % [ + n.name.s, $n.id, $flagsToStr(n.flags), $flagsToStr(n.loc.flags), + $lineInfoToStr(conf, n.info), $n.kind]) -proc debug(n: PType) = - echo($debugType(n)) + proc debug(n: PType; conf: ConfigRef) = + echo($debugType(conf, n)) -proc debug(n: PNode) = - echo($debugTree(n, 0, 100)) + proc debug(n: PNode; conf: ConfigRef) = + echo($debugTree(conf, n, 0, 100)) proc nextTry(h, maxHash: Hash): Hash = result = ((5 * h) + 1) and maxHash @@ -468,7 +445,7 @@ proc nextTry(h, maxHash: Hash): Hash = # generates each int in range(maxHash) exactly once (see any text on # random-number generation for proof). -proc objectSetContains(t: TObjectSet, obj: RootRef): bool = +proc objectSetContains*(t: TObjectSet, obj: RootRef): bool = # returns true whether n is in t var h: Hash = hashNode(obj) and high(t.data) # start with real hash value while t.data[h] != nil: @@ -492,12 +469,12 @@ proc objectSetEnlarge(t: var TObjectSet) = if t.data[i] != nil: objectSetRawInsert(n, t.data[i]) swap(t.data, n) -proc objectSetIncl(t: var TObjectSet, obj: RootRef) = +proc objectSetIncl*(t: var TObjectSet, obj: RootRef) = if mustRehash(len(t.data), t.counter): objectSetEnlarge(t) objectSetRawInsert(t.data, obj) inc(t.counter) -proc objectSetContainsOrIncl(t: var TObjectSet, obj: RootRef): bool = +proc objectSetContainsOrIncl*(t: var TObjectSet, obj: RootRef): bool = # returns true if obj is already in the string table: var h: Hash = hashNode(obj) and high(t.data) while true: @@ -515,7 +492,7 @@ proc objectSetContainsOrIncl(t: var TObjectSet, obj: RootRef): bool = inc(t.counter) result = false -proc strTableContains(t: TStrTable, n: PSym): bool = +proc strTableContains*(t: TStrTable, n: PSym): bool = var h: Hash = n.name.h and high(t.data) # start with real hash value while t.data[h] != nil: if (t.data[h] == n): @@ -571,7 +548,7 @@ proc strTableEnlarge(t: var TStrTable) = if t.data[i] != nil: strTableRawInsert(n, t.data[i]) swap(t.data, n) -proc strTableAdd(t: var TStrTable, n: PSym) = +proc strTableAdd*(t: var TStrTable, n: PSym) = if mustRehash(len(t.data), t.counter): strTableEnlarge(t) strTableRawInsert(t.data, n) inc(t.counter) @@ -607,7 +584,7 @@ proc strTableIncl*(t: var TStrTable, n: PSym; onConflictKeepOld=false): bool {.d inc(t.counter) result = false -proc strTableGet(t: TStrTable, name: PIdent): PSym = +proc strTableGet*(t: TStrTable, name: PIdent): PSym = var h: Hash = name.h and high(t.data) while true: result = t.data[h] @@ -615,13 +592,13 @@ proc strTableGet(t: TStrTable, name: PIdent): PSym = if result.name.id == name.id: break h = nextTry(h, high(t.data)) -proc initIdentIter(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym = - ti.h = s.h - ti.name = s - if tab.counter == 0: result = nil - else: result = nextIdentIter(ti, tab) -proc nextIdentIter(ti: var TIdentIter, tab: TStrTable): PSym = +type + TIdentIter* = object # iterator over all syms with same identifier + h*: Hash # current hash + name*: PIdent + +proc nextIdentIter*(ti: var TIdentIter, tab: TStrTable): PSym = var h = ti.h and high(tab.data) var start = h result = tab.data[h] @@ -634,6 +611,12 @@ proc nextIdentIter(ti: var TIdentIter, tab: TStrTable): PSym = result = tab.data[h] ti.h = nextTry(h, high(tab.data)) +proc initIdentIter*(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym = + ti.h = s.h + ti.name = s + if tab.counter == 0: result = nil + else: result = nextIdentIter(ti, tab) + proc nextIdentExcluding*(ti: var TIdentIter, tab: TStrTable, excluding: IntSet): PSym = var h: Hash = ti.h and high(tab.data) @@ -657,20 +640,33 @@ proc firstIdentExcluding*(ti: var TIdentIter, tab: TStrTable, s: PIdent, if tab.counter == 0: result = nil else: result = nextIdentExcluding(ti, tab, excluding) -proc initTabIter(ti: var TTabIter, tab: TStrTable): PSym = - ti.h = 0 # we start by zero ... - if tab.counter == 0: - result = nil # FIX 1: removed endless loop - else: - result = nextIter(ti, tab) +type + TTabIter* = object + h: Hash -proc nextIter(ti: var TTabIter, tab: TStrTable): PSym = +proc nextIter*(ti: var TTabIter, tab: TStrTable): PSym = + # usage: + # var + # i: TTabIter + # s: PSym + # s = InitTabIter(i, table) + # while s != nil: + # ... + # s = NextIter(i, table) + # result = nil while (ti.h <= high(tab.data)): result = tab.data[ti.h] inc(ti.h) # ... and increment by one always if result != nil: break +proc initTabIter*(ti: var TTabIter, tab: TStrTable): PSym = + ti.h = 0 + if tab.counter == 0: + result = nil + else: + result = nextIter(ti, tab) + iterator items*(tab: TStrTable): PSym = var it: TTabIter var s = initTabIter(it, tab) diff --git a/compiler/btrees.nim b/compiler/btrees.nim new file mode 100644 index 000000000..6cd6e51f4 --- /dev/null +++ b/compiler/btrees.nim @@ -0,0 +1,233 @@ +# +# +# The Nim Compiler +# (c) Copyright 2018 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## BTree implementation with few features, but good enough for the +## Nim compiler's needs. + +const + M = 512 # max children per B-tree node = M-1 + # (must be even and greater than 2) + Mhalf = M div 2 + +type + Node[Key, Val] = ref object + entries: int + keys: array[M, Key] + case isInternal: bool + of false: + vals: array[M, Val] + of true: + links: array[M, Node[Key, Val]] + BTree*[Key, Val] = object + root: Node[Key, Val] + entries: int ## number of key-value pairs + +proc initBTree*[Key, Val](): BTree[Key, Val] = + BTree[Key, Val](root: Node[Key, Val](entries: 0, isInternal: false)) + +template less(a, b): bool = cmp(a, b) < 0 +template eq(a, b): bool = cmp(a, b) == 0 + +proc getOrDefault*[Key, Val](b: BTree[Key, Val], key: Key): Val = + var x = b.root + while x.isInternal: + for j in 0 ..< x.entries: + if j+1 == x.entries or less(key, x.keys[j+1]): + x = x.links[j] + break + assert(not x.isInternal) + for j in 0 ..< x.entries: + if eq(key, x.keys[j]): return x.vals[j] + +proc contains*[Key, Val](b: BTree[Key, Val], key: Key): bool = + var x = b.root + while x.isInternal: + for j in 0 ..< x.entries: + if j+1 == x.entries or less(key, x.keys[j+1]): + x = x.links[j] + break + assert(not x.isInternal) + for j in 0 ..< x.entries: + if eq(key, x.keys[j]): return true + return false + +proc copyHalf[Key, Val](h, result: Node[Key, Val]) = + for j in 0 ..< Mhalf: + result.keys[j] = h.keys[Mhalf + j] + if h.isInternal: + for j in 0 ..< Mhalf: + result.links[j] = h.links[Mhalf + j] + else: + for j in 0 ..< Mhalf: + shallowCopy(result.vals[j], h.vals[Mhalf + j]) + +proc split[Key, Val](h: Node[Key, Val]): Node[Key, Val] = + ## split node in half + result = Node[Key, Val](entries: Mhalf, isInternal: h.isInternal) + h.entries = Mhalf + copyHalf(h, result) + +proc insert[Key, Val](h: Node[Key, Val], key: Key, val: Val): Node[Key, Val] = + #var t = Entry(key: key, val: val, next: nil) + var newKey = key + var j = 0 + if not h.isInternal: + while j < h.entries: + if less(key, h.keys[j]): break + inc j + for i in countdown(h.entries, j+1): + shallowCopy(h.vals[i], h.vals[i-1]) + h.vals[j] = val + else: + var newLink: Node[Key, Val] = nil + while j < h.entries: + if j+1 == h.entries or less(key, h.keys[j+1]): + let u = insert(h.links[j], key, val) + inc j + if u == nil: return nil + newKey = u.keys[0] + newLink = u + break + inc j + for i in countdown(h.entries, j+1): + h.links[i] = h.links[i-1] + h.links[j] = newLink + + for i in countdown(h.entries, j+1): + h.keys[i] = h.keys[i-1] + h.keys[j] = newKey + inc h.entries + return if h.entries < M: nil else: split(h) + +proc add*[Key, Val](b: var BTree[Key, Val]; key: Key; val: Val) = + let u = insert(b.root, key, val) + inc b.entries + if u == nil: return + + # need to split root + let t = Node[Key, Val](entries: 2, isInternal: true) + t.keys[0] = b.root.keys[0] + t.links[0] = b.root + t.keys[1] = u.keys[0] + t.links[1] = u + b.root = t + +proc toString[Key, Val](h: Node[Key, Val], indent: string; result: var string) = + if not h.isInternal: + for j in 0..<h.entries: + result.add(indent) + result.add($h.keys[j] & " " & $h.vals[j] & "\n") + else: + for j in 0..<h.entries: + if j > 0: result.add(indent & "(" & $h.keys[j] & ")\n") + toString(h.links[j], indent & " ", result) + +proc `$`[Key, Val](b: BTree[Key, Val]): string = + result = "" + toString(b.root, "", result) + +proc hasNext*[Key, Val](b: BTree[Key, Val]; index: int): bool = + result = index < b.entries + +proc countSubTree[Key, Val](it: Node[Key, Val]): int = + if it.isInternal: + result = 0 + for k in 0..<it.entries: + inc result, countSubTree(it.links[k]) + else: + result = it.entries + +proc next*[Key, Val](b: BTree[Key, Val]; index: int): (Key, Val, int) = + var it = b.root + var i = index + # navigate to the right leaf: + while it.isInternal: + var sum = 0 + for k in 0..<it.entries: + let c = countSubTree(it.links[k]) + inc sum, c + if sum > i: + it = it.links[k] + dec i, (sum - c) + break + result = (it.keys[i], it.vals[i], index+1) + +iterator pairs*[Key, Val](b: BTree[Key, Val]): (Key, Val) = + var i = 0 + while hasNext(b, i): + let (k, v, i2) = next(b, i) + i = i2 + yield (k, v) + +proc len*[Key, Val](b: BTree[Key, Val]): int {.inline.} = b.entries + +when isMainModule: + + import random, tables + + proc main = + var st = initBTree[string, string]() + st.add("www.cs.princeton.edu", "abc") + st.add("www.princeton.edu", "128.112.128.15") + st.add("www.yale.edu", "130.132.143.21") + st.add("www.simpsons.com", "209.052.165.60") + st.add("www.apple.com", "17.112.152.32") + st.add("www.amazon.com", "207.171.182.16") + st.add("www.ebay.com", "66.135.192.87") + st.add("www.cnn.com", "64.236.16.20") + st.add("www.google.com", "216.239.41.99") + st.add("www.nytimes.com", "199.239.136.200") + st.add("www.microsoft.com", "207.126.99.140") + st.add("www.dell.com", "143.166.224.230") + st.add("www.slashdot.org", "66.35.250.151") + st.add("www.espn.com", "199.181.135.201") + st.add("www.weather.com", "63.111.66.11") + st.add("www.yahoo.com", "216.109.118.65") + + assert st.getOrDefault("www.cs.princeton.edu") == "abc" + assert st.getOrDefault("www.harvardsucks.com") == nil + + assert st.getOrDefault("www.simpsons.com") == "209.052.165.60" + assert st.getOrDefault("www.apple.com") == "17.112.152.32" + assert st.getOrDefault("www.ebay.com") == "66.135.192.87" + assert st.getOrDefault("www.dell.com") == "143.166.224.230" + assert(st.entries == 16) + + for k, v in st: + echo k, ": ", v + + when false: + var b2 = initBTree[string, string]() + const iters = 10_000 + for i in 1..iters: + b2.add($i, $(iters - i)) + for i in 1..iters: + let x = b2.getOrDefault($i) + if x != $(iters - i): + echo "got ", x, ", but expected ", iters - i + echo b2.entries + + when true: + var b2 = initBTree[int, string]() + var t2 = initTable[int, string]() + const iters = 100_000 + for i in 1..iters: + let x = rand(high(int)) + if not t2.hasKey(x): + doAssert b2.getOrDefault(x).len == 0, " what, tree has this element " & $x + t2[x] = $x + b2.add(x, $x) + + doAssert b2.entries == t2.len + echo "unique entries ", b2.entries + for k, v in t2: + doAssert $k == v + doAssert b2.getOrDefault(k) == $k + + main() diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 22733f6ac..2f9cc822b 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -24,7 +24,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, # getUniqueType() is too expensive here: var typ = skipTypes(ri.sons[0].typ, abstractInst) if typ.sons[0] != nil: - if isInvalidReturnType(typ.sons[0]): + if isInvalidReturnType(p.config, typ.sons[0]): if params != nil: pl.add(~", ") # beware of 'result = p(result)'. We may need to allocate a temporary: if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri): @@ -33,13 +33,13 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, elif d.k notin {locTemp} and not hasNoInit(ri): # reset before pass as 'result' var: discard "resetLoc(p, d)" - add(pl, addrLoc(d)) + add(pl, addrLoc(p.config, d)) add(pl, ~");$n") line(p, cpsStmts, pl) else: var tmp: TLoc getTemp(p, typ.sons[0], tmp, needsInit=true) - add(pl, addrLoc(tmp)) + add(pl, addrLoc(p.config, tmp)) add(pl, ~");$n") line(p, cpsStmts, pl) genAssignment(p, d, tmp, {}) # no need for deep copying @@ -101,7 +101,7 @@ proc openArrayLoc(p: BProc, n: PNode): Rope = let ty = skipTypes(a.t, abstractVar+{tyPtr}) case ty.kind of tyArray: - let first = firstOrd(ty) + let first = firstOrd(p.config, ty) if first == 0: result = "($1)+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c)] else: @@ -128,13 +128,13 @@ proc openArrayLoc(p: BProc, n: PNode): Rope = else: result = "$1->data, ($1 ? $1->$2 : 0)" % [a.rdLoc, lenField(p)] of tyArray: - result = "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))] + result = "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))] of tyPtr, tyRef: case lastSon(a.t).kind of tyString, tySequence: result = "(*$1)->data, (*$1 ? (*$1)->$2 : 0)" % [a.rdLoc, lenField(p)] of tyArray: - result = "$1, $2" % [rdLoc(a), rope(lengthOrd(lastSon(a.t)))] + result = "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, lastSon(a.t)))] else: internalError(p.config, "openArrayLoc: " & typeToString(a.t)) else: internalError(p.config, "openArrayLoc: " & typeToString(a.t)) @@ -151,9 +151,9 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): Rope = elif skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs}: var n = if n.kind != nkHiddenAddr: n else: n.sons[0] result = openArrayLoc(p, n) - elif ccgIntroducedPtr(param): + elif ccgIntroducedPtr(p.config, param): initLocExpr(p, n, a) - result = addrLoc(a) + result = addrLoc(p.config, a) elif p.module.compileToCpp and param.typ.kind == tyVar and n.kind == nkHiddenAddr: initLocExprSingleUse(p, n.sons[0], a) @@ -163,7 +163,7 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): Rope = if callee.kind == nkSym and {sfImportC, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportC} and {lfHeader, lfNoDecl} * callee.sym.loc.flags != {}: - result = addrLoc(a) + result = addrLoc(p.config, a) else: result = rdLoc(a) else: @@ -230,7 +230,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = let rawProc = getRawProcType(p, typ) let callPattern = if tfIterator in typ.flags: PatIter else: PatProc if typ.sons[0] != nil: - if isInvalidReturnType(typ.sons[0]): + if isInvalidReturnType(p.config, typ.sons[0]): if sonsLen(ri) > 1: add(pl, ~", ") # beware of 'result = p(result)'. We may need to allocate a temporary: if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri): @@ -240,12 +240,12 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = elif d.k notin {locTemp} and not hasNoInit(ri): # reset before pass as 'result' var: discard "resetLoc(p, d)" - add(pl, addrLoc(d)) + add(pl, addrLoc(p.config, d)) genCallPattern() else: var tmp: TLoc getTemp(p, typ.sons[0], tmp, needsInit=true) - add(pl, addrLoc(tmp)) + add(pl, addrLoc(p.config, tmp)) genCallPattern() genAssignment(p, d, tmp, {}) # no need for deep copying else: @@ -508,20 +508,20 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) = add(pl, ~": ") add(pl, genArg(p, ri.sons[i], param, ri)) if typ.sons[0] != nil: - if isInvalidReturnType(typ.sons[0]): + if isInvalidReturnType(p.config, typ.sons[0]): if sonsLen(ri) > 1: add(pl, ~" ") # beware of 'result = p(result)'. We always allocate a temporary: if d.k in {locTemp, locNone}: # We already got a temp. Great, special case it: if d.k == locNone: getTemp(p, typ.sons[0], d, needsInit=true) add(pl, ~"Result: ") - add(pl, addrLoc(d)) + add(pl, addrLoc(p.config, d)) add(pl, ~"];$n") line(p, cpsStmts, pl) else: var tmp: TLoc getTemp(p, typ.sons[0], tmp, needsInit=true) - add(pl, addrLoc(tmp)) + add(pl, addrLoc(p.config, tmp)) add(pl, ~"];$n") line(p, cpsStmts, pl) genAssignment(p, d, tmp, {}) # no need for deep copying diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 20b68b0aa..0e8af5af5 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -59,7 +59,7 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = else: result = rope("NIM_NIL") of nkStrLit..nkTripleStrLit: - case skipTypes(ty, abstractVarRange).kind + case skipTypes(ty, abstractVarRange + {tyStatic}).kind of tyNil: result = genNilStringLiteral(p.module, n.info) of tyString: @@ -114,8 +114,8 @@ proc genRawSetData(cs: TBitSet, size: int): Rope = proc genSetNode(p: BProc, n: PNode): Rope = var cs: TBitSet - var size = int(getSize(n.typ)) - toBitSet(n, cs) + var size = int(getSize(p.config, n.typ)) + toBitSet(p.config, n, cs) if size > 8: let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels) result = p.module.tmpBase & rope(id) @@ -185,13 +185,13 @@ proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = # lineF(p, cpsStmts, '$1 = $2;$n', [rdLoc(dest), rdLoc(src)]) if canFormAcycle(dest.t): linefmt(p, cpsStmts, "#asgnRef((void**) $1, $2);$n", - addrLoc(dest), rdLoc(src)) + addrLoc(p.config, dest), rdLoc(src)) else: linefmt(p, cpsStmts, "#asgnRefNoCycle((void**) $1, $2);$n", - addrLoc(dest), rdLoc(src)) + addrLoc(p.config, dest), rdLoc(src)) else: linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n", - addrLoc(dest), rdLoc(src)) + addrLoc(p.config, dest), rdLoc(src)) proc asgnComplexity(n: PNode): int = if n != nil: @@ -260,13 +260,13 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = useStringh(p.module) linefmt(p, cpsStmts, "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n", - addrLoc(dest), addrLoc(src), rdLoc(dest)) + addrLoc(p.config, dest), addrLoc(p.config, src), rdLoc(dest)) else: linefmt(p, cpsStmts, "#genericShallowAssign((void*)$1, (void*)$2, $3);$n", - addrLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t, dest.lode.info)) + addrLoc(p.config, dest), addrLoc(p.config, src), genTypeInfo(p.module, dest.t, dest.lode.info)) else: linefmt(p, cpsStmts, "#genericAssign((void*)$1, (void*)$2, $3);$n", - addrLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t, dest.lode.info)) + addrLoc(p.config, dest), addrLoc(p.config, src), genTypeInfo(p.module, dest.t, dest.lode.info)) proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = # This function replaces all other methods for generating @@ -284,7 +284,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = genRefAssign(p, dest, src, flags) else: linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n", - addrLoc(dest), rdLoc(src), + addrLoc(p.config, dest), rdLoc(src), genTypeInfo(p.module, dest.t, dest.lode.info)) of tyString: if (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode): @@ -301,7 +301,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = linefmt(p, cpsStmts, "if ($1) #nimGCunrefNoCycle($1);$n", tmp.rdLoc) else: linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, #copyString($2));$n", - addrLoc(dest), rdLoc(src)) + addrLoc(p.config, dest), rdLoc(src)) of tyProc: if needsComplexAssignment(dest.t): # optimize closure assignment: @@ -346,7 +346,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = if needsComplexAssignment(dest.t): linefmt(p, cpsStmts, # XXX: is this correct for arrays? "#genericAssignOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n", - addrLoc(dest), addrLoc(src), + addrLoc(p.config, dest), addrLoc(p.config, src), genTypeInfo(p.module, dest.t, dest.lode.info)) else: useStringh(p.module) @@ -356,10 +356,10 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = "$1 = $2;$n", rdLoc(dest), rdLoc(src)) of tySet: - if mapType(ty) == ctArray: + if mapType(p.config, ty) == ctArray: useStringh(p.module) linefmt(p, cpsStmts, "memcpy((void*)$1, (NIM_CONST void*)$2, $3);$n", - rdLoc(dest), rdLoc(src), rope(getSize(dest.t))) + rdLoc(dest), rdLoc(src), rope(getSize(p.config, dest.t))) else: linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) of tyPtr, tyPointer, tyChar, tyBool, tyEnum, tyCString, @@ -371,8 +371,8 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = #writeStackTrace() #echo p.currLineInfo, " requesting" linefmt(p, cpsStmts, "#memTrackerWrite((void*)$1, $2, $3, $4);$n", - addrLoc(dest), rope getSize(dest.t), - makeCString(p.currLineInfo.toFullPath), + addrLoc(p.config, dest), rope getSize(p.config, dest.t), + makeCString(toFullPath(p.config, p.currLineInfo)), rope p.currLineInfo.safeLineNm) proc genDeepCopy(p: BProc; dest, src: TLoc) = @@ -381,31 +381,31 @@ proc genDeepCopy(p: BProc; dest, src: TLoc) = var tmp: TLoc getTemp(p, a.t, tmp) genAssignment(p, tmp, a, {}) - addrLoc(tmp) + addrLoc(p.config, tmp) else: - addrLoc(a) + addrLoc(p.config, a) - var ty = skipTypes(dest.t, abstractVarRange) + var ty = skipTypes(dest.t, abstractVarRange + {tyStatic}) case ty.kind of tyPtr, tyRef, tyProc, tyTuple, tyObject, tyArray: # XXX optimize this linefmt(p, cpsStmts, "#genericDeepCopy((void*)$1, (void*)$2, $3);$n", - addrLoc(dest), addrLocOrTemp(src), + addrLoc(p.config, dest), addrLocOrTemp(src), genTypeInfo(p.module, dest.t, dest.lode.info)) of tySequence, tyString: linefmt(p, cpsStmts, "#genericSeqDeepCopy($1, $2, $3);$n", - addrLoc(dest), rdLoc(src), + addrLoc(p.config, dest), rdLoc(src), genTypeInfo(p.module, dest.t, dest.lode.info)) of tyOpenArray, tyVarargs: linefmt(p, cpsStmts, "#genericDeepCopyOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n", - addrLoc(dest), addrLocOrTemp(src), + addrLoc(p.config, dest), addrLocOrTemp(src), genTypeInfo(p.module, dest.t, dest.lode.info)) of tySet: - if mapType(ty) == ctArray: + if mapType(p.config, ty) == ctArray: useStringh(p.module) linefmt(p, cpsStmts, "memcpy((void*)$1, (NIM_CONST void*)$2, $3);$n", - rdLoc(dest), rdLoc(src), rope(getSize(dest.t))) + rdLoc(dest), rdLoc(src), rope(getSize(p.config, dest.t))) else: linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) of tyPointer, tyChar, tyBool, tyEnum, tyCString, @@ -491,15 +491,15 @@ proc unaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) = proc binaryArithOverflowRaw(p: BProc, t: PType, a, b: TLoc; frmt: string): Rope = - var size = getSize(t) - let storage = if size < platform.intSize: rope("NI") + var size = getSize(p.config, t) + let storage = if size < p.config.target.intSize: rope("NI") else: getTypeDesc(p.module, t) result = getTempName(p.module) linefmt(p, cpsLocals, "$1 $2;$n", storage, result) lineCg(p, cpsStmts, frmt, result, rdCharLoc(a), rdCharLoc(b)) - if size < platform.intSize or t.kind in {tyRange, tyEnum}: + if size < p.config.target.intSize or t.kind in {tyRange, tyEnum}: linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3) #raiseOverflow();$n", - result, intLiteral(firstOrd(t)), intLiteral(lastOrd(t))) + result, intLiteral(firstOrd(p.config, t)), intLiteral(lastOrd(p.config, t))) proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = const @@ -547,8 +547,8 @@ proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = t = skipTypes(e.typ, abstractRange) if optOverflowCheck in p.options: linefmt(p, cpsStmts, "if ($1 == $2) #raiseOverflow();$n", - rdLoc(a), intLiteral(firstOrd(t))) - putIntoDest(p, d, e, opr[m] % [rdLoc(a), rope(getSize(t) * 8)]) + rdLoc(a), intLiteral(firstOrd(p.config, t))) + putIntoDest(p, d, e, opr[m] % [rdLoc(a), rope(getSize(p.config, t) * 8)]) proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = const @@ -604,8 +604,8 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) # BUGFIX: cannot use result-type here, as it may be a boolean - s = max(getSize(a.t), getSize(b.t)) * 8 - k = getSize(a.t) * 8 + s = max(getSize(p.config, a.t), getSize(p.config, b.t)) * 8 + k = getSize(p.config, a.t) * 8 putIntoDest(p, d, e, binArithTab[op] % [rdLoc(a), rdLoc(b), rope(s), getSimpleTypeDesc(p.module, e.typ), rope(k)]) @@ -658,7 +658,7 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = initLocExpr(p, e.sons[1], a) t = skipTypes(e.typ, abstractRange) putIntoDest(p, d, e, - unArithTab[op] % [rdLoc(a), rope(getSize(t) * 8), + unArithTab[op] % [rdLoc(a), rope(getSize(p.config, t) * 8), getSimpleTypeDesc(p.module, e.typ)]) proc isCppRef(p: BProc; typ: PType): bool {.inline.} = @@ -667,7 +667,7 @@ proc isCppRef(p: BProc; typ: PType): bool {.inline.} = tfVarIsPtr notin skipTypes(typ, abstractInst).flags proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) = - let mt = mapType(e.sons[0].typ) + let mt = mapType(p.config, e.sons[0].typ) if mt in {ctArray, ctPtrToArray} and not enforceDeref: # XXX the amount of hacks for C's arrays is incredible, maybe we should # simply wrap them in a struct? --> Losing auto vectorization then? @@ -726,12 +726,12 @@ proc genAddr(p: BProc, e: PNode, d: var TLoc) = initLocExpr(p, e.sons[0], a) putIntoDest(p, d, e, "&" & a.r, a.storage) #Message(e.info, warnUser, "HERE NEW &") - elif mapType(e.sons[0].typ) == ctArray or isCppRef(p, e.sons[0].typ): + elif mapType(p.config, e.sons[0].typ) == ctArray or isCppRef(p, e.sons[0].typ): expr(p, e.sons[0], d) else: var a: TLoc initLocExpr(p, e.sons[0], a) - putIntoDest(p, d, e, addrLoc(a), a.storage) + putIntoDest(p, d, e, addrLoc(p.config, a), a.storage) template inheritLocation(d: var TLoc, a: TLoc) = if d.k == locNone: d.storage = a.storage @@ -828,7 +828,7 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = if optFieldCheck in p.options: var a: TLoc genRecordFieldAux(p, e.sons[0], d, a) - let ty = skipTypes(a.t, abstractInst) + let ty = skipTypes(a.t, abstractInst + tyUserTypeClasses) var r = rdLoc(a) let f = e.sons[0].sons[1].sym let field = lookupFieldAgain(p, ty, f, r) @@ -846,21 +846,21 @@ proc genArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = initLocExpr(p, x, a) initLocExpr(p, y, b) var ty = skipTypes(a.t, abstractVarRange + abstractPtrs + tyUserTypeClasses) - var first = intLiteral(firstOrd(ty)) + var first = intLiteral(firstOrd(p.config, ty)) # emit range check: if optBoundsCheck in p.options and tfUncheckedArray notin ty.flags: if not isConstExpr(y): # semantic pass has already checked for const index expressions - if firstOrd(ty) == 0: - if (firstOrd(b.t) < firstOrd(ty)) or (lastOrd(b.t) > lastOrd(ty)): + if firstOrd(p.config, ty) == 0: + if (firstOrd(p.config, b.t) < firstOrd(p.config, ty)) or (lastOrd(p.config, b.t) > lastOrd(p.config, ty)): linefmt(p, cpsStmts, "if ((NU)($1) > (NU)($2)) #raiseIndexError();$n", - rdCharLoc(b), intLiteral(lastOrd(ty))) + rdCharLoc(b), intLiteral(lastOrd(p.config, ty))) else: linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3) #raiseIndexError();$n", - rdCharLoc(b), first, intLiteral(lastOrd(ty))) + rdCharLoc(b), first, intLiteral(lastOrd(p.config, ty))) else: let idx = getOrdValue(y) - if idx < firstOrd(ty) or idx > lastOrd(ty): + if idx < firstOrd(p.config, ty) or idx > lastOrd(p.config, ty): localError(p.config, x.info, "index out of bounds") d.inheritLocation(a) putIntoDest(p, d, n, @@ -884,12 +884,12 @@ proc genBoundsCheck(p: BProc; arr, a, b: TLoc) = "((NU)($1) >= (NU)($3Len_0) || (NU)($2) >= (NU)($3Len_0))) #raiseIndexError();$n", rdLoc(a), rdLoc(b), rdLoc(arr)) of tyArray: - let first = intLiteral(firstOrd(ty)) + let first = intLiteral(firstOrd(p.config, ty)) if tfUncheckedArray notin ty.flags: linefmt(p, cpsStmts, "if ($2-$1 != -1 && " & "($2-$1 < -1 || $1 < $3 || $1 > $4 || $2 < $3 || $2 > $4)) #raiseIndexError();$n", - rdCharLoc(a), rdCharLoc(b), first, intLiteral(lastOrd(ty))) + rdCharLoc(a), rdCharLoc(b), first, intLiteral(lastOrd(p.config, ty))) of tySequence, tyString: linefmt(p, cpsStmts, "if ($2-$1 != -1 && " & @@ -985,7 +985,7 @@ proc genEcho(p: BProc, n: PNode) = # this unusal way of implementing it ensures that e.g. ``echo("hallo", 45)`` # is threadsafe. internalAssert p.config, n.kind == nkBracket - if platform.targetOS == osGenode: + if p.config.target.targetOS == osGenode: # bypass libc and print directly to the Genode LOG session var args: Rope = nil var a: TLoc @@ -1007,7 +1007,7 @@ proc genEcho(p: BProc, n: PNode) = when false: p.module.includeHeader("<stdio.h>") linefmt(p, cpsStmts, "printf($1$2);$n", - makeCString(repeat("%s", n.len) & tnl), args) + makeCString(repeat("%s", n.len) & "\L"), args) linefmt(p, cpsStmts, "fflush(stdout);$n") proc gcUsage(conf: ConfigRef; n: PNode) = @@ -1121,7 +1121,7 @@ proc genReset(p: BProc, n: PNode) = var a: TLoc initLocExpr(p, n.sons[1], a) linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n", - addrLoc(a), + addrLoc(p.config, a), genTypeInfo(p.module, skipTypes(a.t, {tyVar}), n.info)) proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) = @@ -1306,7 +1306,7 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) = if d.k == locNone: getTemp(p, n.typ, d) # generate call to newSeq before adding the elements per hand: - let L = int(lengthOrd(n.sons[1].typ)) + let L = int(lengthOrd(p.config, n.sons[1].typ)) genNewSeqAux(p, d, intLiteral(L)) initLocExpr(p, n.sons[1], a) # bug #5007; do not produce excessive C source code: @@ -1420,7 +1420,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) = putIntoDest(p, d, e, ropecg(p.module, "#reprStr($1)", [rdLoc(a)]), a.storage) of tySet: putIntoDest(p, d, e, ropecg(p.module, "#reprSet($1, $2)", [ - addrLoc(a), genTypeInfo(p.module, t, e.info)]), a.storage) + addrLoc(p.config, a), genTypeInfo(p.module, t, e.info)]), a.storage) of tyOpenArray, tyVarargs: var b: TLoc case a.t.kind @@ -1431,7 +1431,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) = "$1->data, ($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p)], a.storage) of tyArray: putIntoDest(p, b, e, - "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))], a.storage) + "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))], a.storage) else: internalError(p.config, e.sons[0].info, "genRepr()") putIntoDest(p, d, e, ropecg(p.module, "#reprOpenArray($1, $2)", [rdLoc(b), @@ -1444,7 +1444,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) = localError(p.config, e.info, "'repr' doesn't support 'void' type") else: putIntoDest(p, d, e, ropecg(p.module, "#reprAny($1, $2)", - [addrLoc(a), genTypeInfo(p.module, t, e.info)]), + [addrLoc(p.config, a), genTypeInfo(p.module, t, e.info)]), a.storage) gcUsage(p.config, e) @@ -1498,8 +1498,8 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = putIntoDest(p, d, e, tmp.r) of tyArray: # YYY: length(sideeffect) is optimized away incorrectly? - if op == mHigh: putIntoDest(p, d, e, rope(lastOrd(typ))) - else: putIntoDest(p, d, e, rope(lengthOrd(typ))) + if op == mHigh: putIntoDest(p, d, e, rope(lastOrd(p.config, typ))) + else: putIntoDest(p, d, e, rope(lengthOrd(p.config, typ))) else: internalError(p.config, e.info, "genArrayLen()") proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = @@ -1537,19 +1537,19 @@ proc genSwap(p: BProc, e: PNode, d: var TLoc) = genAssignment(p, a, b, {}) genAssignment(p, b, tmp, {}) -proc rdSetElemLoc(a: TLoc, setType: PType): Rope = +proc rdSetElemLoc(conf: ConfigRef; a: TLoc, setType: PType): Rope = # read a location of an set element; it may need a subtraction operation # before the set operation result = rdCharLoc(a) assert(setType.kind == tySet) - if firstOrd(setType) != 0: - result = "($1- $2)" % [result, rope(firstOrd(setType))] + if firstOrd(conf, setType) != 0: + result = "($1- $2)" % [result, rope(firstOrd(conf, setType))] -proc fewCmps(s: PNode): bool = +proc fewCmps(conf: ConfigRef; s: PNode): bool = # this function estimates whether it is better to emit code # for constructing the set or generating a bunch of comparisons directly if s.kind != nkCurly: return false - if (getSize(s.typ) <= platform.intSize) and (nfAllConst in s.flags): + if (getSize(conf, s.typ) <= conf.target.intSize) and (nfAllConst in s.flags): result = false # it is better to emit the set generation code elif elemType(s.typ).kind in {tyInt, tyInt16..tyInt64}: result = true # better not emit the set if int is basetype! @@ -1557,10 +1557,10 @@ proc fewCmps(s: PNode): bool = result = sonsLen(s) <= 8 # 8 seems to be a good value proc binaryExprIn(p: BProc, e: PNode, a, b, d: var TLoc, frmt: string) = - putIntoDest(p, d, e, frmt % [rdLoc(a), rdSetElemLoc(b, a.t)]) + putIntoDest(p, d, e, frmt % [rdLoc(a), rdSetElemLoc(p.config, b, a.t)]) proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) = - case int(getSize(skipTypes(e.sons[1].typ, abstractVar))) + case int(getSize(p.config, skipTypes(e.sons[1].typ, abstractVar))) of 1: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&7U)))!=0)") of 2: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&15U)))!=0)") of 4: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&31U)))!=0)") @@ -1572,11 +1572,11 @@ proc binaryStmtInExcl(p: BProc, e: PNode, d: var TLoc, frmt: string) = assert(d.k == locNone) initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) - lineF(p, cpsStmts, frmt, [rdLoc(a), rdSetElemLoc(b, a.t)]) + lineF(p, cpsStmts, frmt, [rdLoc(a), rdSetElemLoc(p.config, b, a.t)]) proc genInOp(p: BProc, e: PNode, d: var TLoc) = var a, b, x, y: TLoc - if (e.sons[1].kind == nkCurly) and fewCmps(e.sons[1]): + if (e.sons[1].kind == nkCurly) and fewCmps(p.config, e.sons[1]): # a set constructor but not a constant set: # do not emit the set, but generate a bunch of comparisons; and if we do # so, we skip the unnecessary range check: This is a semantical extension @@ -1620,7 +1620,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = "&", "|", "& ~", "^"] var a, b, i: TLoc var setType = skipTypes(e.sons[1].typ, abstractVar) - var size = int(getSize(setType)) + var size = int(getSize(p.config, setType)) case size of 1, 2, 4, 8: case op @@ -1687,7 +1687,7 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) = let etyp = skipTypes(e.typ, abstractRange) if etyp.kind in ValueTypes and lfIndirect notin a.flags: putIntoDest(p, d, e, "(*($1*) ($2))" % - [getTypeDesc(p.module, e.typ), addrLoc(a)], a.storage) + [getTypeDesc(p.module, e.typ), addrLoc(p.config, a)], a.storage) elif etyp.kind == tyProc and etyp.callConv == ccClosure: putIntoDest(p, d, e, "(($1) ($2))" % [getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.storage) @@ -1924,7 +1924,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = putIntoDest(p, d, e, genSetNode(p, e)) else: if d.k == locNone: getTemp(p, e.typ, d) - if getSize(e.typ) > 8: + if getSize(p.config, e.typ) > 8: # big set: useStringh(p.module) lineF(p, cpsStmts, "memset($1, 0, sizeof($2));$n", @@ -1936,14 +1936,14 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = initLocExpr(p, it.sons[1], b) lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" & "$2[(NU)($1)>>3] |=(1U<<((NU)($1)&7U));$n", [rdLoc(idx), rdLoc(d), - rdSetElemLoc(a, e.typ), rdSetElemLoc(b, e.typ)]) + rdSetElemLoc(p.config, a, e.typ), rdSetElemLoc(p.config, b, e.typ)]) else: initLocExpr(p, it, a) lineF(p, cpsStmts, "$1[(NU)($2)>>3] |=(1U<<((NU)($2)&7U));$n", - [rdLoc(d), rdSetElemLoc(a, e.typ)]) + [rdLoc(d), rdSetElemLoc(p.config, a, e.typ)]) else: # small set - var ts = "NU" & $(getSize(e.typ) * 8) + var ts = "NU" & $(getSize(p.config, e.typ) * 8) lineF(p, cpsStmts, "$1 = 0;$n", [rdLoc(d)]) for it in e.sons: if it.kind == nkRange: @@ -1952,13 +1952,13 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = initLocExpr(p, it.sons[1], b) lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" & "$2 |=((" & ts & ")(1)<<(($1)%(sizeof(" & ts & ")*8)));$n", [ - rdLoc(idx), rdLoc(d), rdSetElemLoc(a, e.typ), - rdSetElemLoc(b, e.typ)]) + rdLoc(idx), rdLoc(d), rdSetElemLoc(p.config, a, e.typ), + rdSetElemLoc(p.config, b, e.typ)]) else: initLocExpr(p, it, a) lineF(p, cpsStmts, "$1 |=((" & ts & ")(1)<<(($2)%(sizeof(" & ts & ")*8)));$n", - [rdLoc(d), rdSetElemLoc(a, e.typ)]) + [rdLoc(d), rdSetElemLoc(p.config, a, e.typ)]) proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) = var rec: TLoc @@ -2076,7 +2076,7 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) = "(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage) else: putIntoDest(p, d, n, "(*($1*) ($2))" % - [getTypeDesc(p.module, dest), addrLoc(a)], a.storage) + [getTypeDesc(p.module, dest), addrLoc(p.config, a)], a.storage) proc downConv(p: BProc, n: PNode, d: var TLoc) = if p.module.compileToCpp: @@ -2297,7 +2297,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = incl a.flags, lfSingleUse genCall(p, ex, a) if lfSingleUse notin a.flags: - line(p, cpsStmts, a.r & ";" & tnl) + line(p, cpsStmts, a.r & ";\L") else: initLocExpr(p, ex, a) of nkAsmStmt: genAsmStmt(p, n) @@ -2313,7 +2313,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = genTypeSection(p.module, n) of nkCommentStmt, nkIteratorDef, nkIncludeStmt, nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, - nkFromStmt, nkTemplateDef, nkMacroDef: + nkFromStmt, nkTemplateDef, nkMacroDef, nkStaticStmt: discard of nkPragma: genPragma(p, n) of nkPragmaBlock: expr(p, n.lastSon, d) @@ -2367,7 +2367,7 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope = result.add "}" of tyArray: result = rope"{}" of tySet: - if mapType(t) == ctArray: result = rope"{}" + if mapType(p.config, t) == ctArray: result = rope"{}" else: result = rope"0" else: globalError(p.config, info, "cannot create null element for: " & $t.kind) @@ -2436,7 +2436,7 @@ proc genConstSimpleList(p: BProc, n: PNode): Rope = addf(result, "}$n", []) proc genConstSeq(p: BProc, n: PNode, t: PType): Rope = - var data = "{{$1, $1}" % [n.len.rope] + var data = "{{$1, $1 | NIM_STRLIT_FLAG}" % [n.len.rope] if n.len > 0: # array part needs extra curlies: data.add(", {") @@ -2464,8 +2464,8 @@ proc genConstExpr(p: BProc, n: PNode): Rope = result = genConstExpr(p, n.sons[1]) of nkCurly: var cs: TBitSet - toBitSet(n, cs) - result = genRawSetData(cs, int(getSize(n.typ))) + toBitSet(p.config, n, cs) + result = genRawSetData(cs, int(getSize(p.config, n.typ))) of nkBracket, nkPar, nkTupleConstr, nkClosure: var t = skipTypes(n.typ, abstractInst) if t.kind == tySequence: diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim index 4b4f9c0e6..664f89b73 100644 --- a/compiler/ccgmerge.nim +++ b/compiler/ccgmerge.nim @@ -47,34 +47,32 @@ const proc genSectionStart*(fs: TCFileSection; conf: ConfigRef): Rope = if compilationCachePresent(conf): - result = rope(tnl) - add(result, "/*\t") + result = nil + add(result, "\n/*\t") add(result, CFileSectionNames[fs]) - add(result, ":*/") - add(result, tnl) + add(result, ":*/\n") proc genSectionEnd*(fs: TCFileSection; conf: ConfigRef): Rope = if compilationCachePresent(conf): - result = rope(NimMergeEndMark & tnl) + result = rope(NimMergeEndMark & "\n") proc genSectionStart*(ps: TCProcSection; conf: ConfigRef): Rope = if compilationCachePresent(conf): - result = rope(tnl) - add(result, "/*\t") + result = rope(nil) + add(result, "\n/*\t") add(result, CProcSectionNames[ps]) - add(result, ":*/") - add(result, tnl) + add(result, ":*/\n") proc genSectionEnd*(ps: TCProcSection; conf: ConfigRef): Rope = if compilationCachePresent(conf): - result = rope(NimMergeEndMark & tnl) + result = rope(NimMergeEndMark & "\n") proc writeTypeCache(a: TypeCache, s: var string) = var i = 0 for id, value in pairs(a): if i == 10: i = 0 - s.add(tnl) + s.add('\L') else: s.add(' ') encodeStr($id, s) @@ -88,7 +86,7 @@ proc writeIntSet(a: IntSet, s: var string) = for x in items(a): if i == 10: i = 0 - s.add(tnl) + s.add('\L') else: s.add(' ') encodeVInt(x, s) @@ -97,8 +95,7 @@ proc writeIntSet(a: IntSet, s: var string) = proc genMergeInfo*(m: BModule): Rope = if not compilationCachePresent(m.config): return nil - var s = "/*\tNIM_merge_INFO:" - s.add(tnl) + var s = "/*\tNIM_merge_INFO:\n" s.add("typeCache:{") writeTypeCache(m.typeCache, s) s.add("declared:{") @@ -110,8 +107,7 @@ proc genMergeInfo*(m: BModule): Rope = encodeVInt(m.labels, s) s.add(" flags:") encodeVInt(cast[int](m.flags), s) - s.add(tnl) - s.add("*/") + s.add("\n*/") result = s.rope template `^`(pos: int): untyped = L.buf[pos] @@ -155,11 +151,11 @@ proc readVerbatimSection(L: var TBaseLexer): Rope = of CR: pos = nimlexbase.handleCR(L, pos) buf = L.buf - r.add(tnl) + r.add('\L') of LF: pos = nimlexbase.handleLF(L, pos) buf = L.buf - r.add(tnl) + r.add('\L') of '\0': doAssert(false, "ccgmerge: expected: " & NimMergeEndMark) break diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index f99ee9270..f9654bb1f 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -28,9 +28,9 @@ proc registerGcRoot(p: BProc, v: PSym) = appcg(p.module, p.module.initProc.procSec(cpsInit), "#nimRegisterGlobalMarker($1);$n", [prc]) -proc isAssignedImmediately(n: PNode): bool {.inline.} = +proc isAssignedImmediately(conf: ConfigRef; n: PNode): bool {.inline.} = if n.kind == nkEmpty: return false - if isInvalidReturnType(n.typ): + if isInvalidReturnType(conf, n.typ): # var v = f() # is transformed into: var v; f(addr v) # where 'f' **does not** initialize the result! @@ -65,7 +65,7 @@ proc genVarTuple(p: BProc, n: PNode) = registerGcRoot(p, v) else: assignLocalVar(p, vn) - initLocalVar(p, v, immediateAsgn=isAssignedImmediately(n[L-1])) + initLocalVar(p, v, immediateAsgn=isAssignedImmediately(p.config, n[L-1])) initLoc(field, locExpr, vn, tup.storage) if t.kind == tyTuple: field.r = "$1.Field$2" % [rdLoc(tup), rope(i)] @@ -205,7 +205,7 @@ proc genGotoState(p: BProc, n: PNode) = howManyTrys = p.nestedTryStmts.len, howManyExcepts = p.inExceptBlockLen) lineF(p, cpsStmts, " goto BeforeRet_;$n", []) - var statesCounter = lastOrd(n.sons[0].typ) + var statesCounter = lastOrd(p.config, n.sons[0].typ) if n.len >= 2 and n[1].kind == nkIntLit: statesCounter = n[1].intVal let prefix = if n.len == 3 and n[2].kind == nkStrLit: n[2].strVal.rope @@ -264,7 +264,7 @@ proc genSingleVar(p: BProc, a: PNode) = registerGcRoot(p, v) else: let value = a.sons[2] - let imm = isAssignedImmediately(value) + let imm = isAssignedImmediately(p.config, value) if imm and p.module.compileToCpp and p.splitDecls == 0 and not containsHiddenPointer(v.typ): # C++ really doesn't like things like 'Foo f; f = x' as that invokes a @@ -404,12 +404,12 @@ proc genComputedGoto(p: BProc; n: PNode) = localError(p.config, it.info, "case statement must be exhaustive for computed goto"); return casePos = i - let aSize = lengthOrd(it.sons[0].typ) + let aSize = lengthOrd(p.config, it.sons[0].typ) if aSize > 10_000: localError(p.config, it.info, "case statement has too many cases for computed goto"); return arraySize = aSize.int - if firstOrd(it.sons[0].typ) != 0: + if firstOrd(p.config, it.sons[0].typ) != 0: localError(p.config, it.info, "case statement has to start at 0 for computed goto"); return if casePos < 0: @@ -480,7 +480,7 @@ proc genWhileStmt(p: BProc, t: PNode) = lineF(p, cpsStmts, "if (!$1) goto $2;$n", [rdLoc(a), label]) var loopBody = t.sons[1] if loopBody.stmtsContainPragma(wComputedGoto) and - hasComputedGoto in CC[cCompiler].props: + hasComputedGoto in CC[p.config.cCompiler].props: # for closure support weird loop bodies are generated: if loopBody.len == 2 and loopBody.sons[0].kind == nkEmpty: loopBody = loopBody.sons[1] @@ -653,7 +653,7 @@ proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel, assert(b.sons[i].kind != nkRange) initLocExpr(p, b.sons[i], x) assert(b.sons[i].kind in {nkStrLit..nkTripleStrLit}) - var j = int(hashString(b.sons[i].strVal) and high(branches)) + var j = int(hashString(p.config, b.sons[i].strVal) and high(branches)) appcg(p.module, branches[j], "if (#eqStrings($1, $2)) goto $3;$n", [rdLoc(e), rdLoc(x), labl]) @@ -706,7 +706,7 @@ proc ifSwitchSplitPoint(p: BProc, n: PNode): int = var stmtBlock = lastSon(branch) if stmtBlock.stmtsContainPragma(wLinearScanEnd): result = i - elif hasSwitchRange notin CC[cCompiler].props: + elif hasSwitchRange notin CC[p.config.cCompiler].props: if branch.kind == nkOfBranch and branchHasTooBigRange(branch): result = i @@ -714,7 +714,7 @@ proc genCaseRange(p: BProc, branch: PNode) = var length = branch.len for j in 0 .. length-2: if branch[j].kind == nkRange: - if hasSwitchRange in CC[cCompiler].props: + if hasSwitchRange in CC[p.config.cCompiler].props: lineF(p, cpsStmts, "case $1 ... $2:$n", [ genLiteral(p, branch[j][0]), genLiteral(p, branch[j][1])]) @@ -754,7 +754,7 @@ proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) = hasDefault = true exprBlock(p, branch.lastSon, d) lineF(p, cpsStmts, "break;$n", []) - if (hasAssume in CC[cCompiler].props) and not hasDefault: + if (hasAssume in CC[p.config.cCompiler].props) and not hasDefault: lineF(p, cpsStmts, "default: __assume(0);$n", []) lineF(p, cpsStmts, "}$n", []) if lend != nil: fixLabel(p, lend) @@ -975,21 +975,22 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope = initLocExpr(p, it, a) res.add($a.rdLoc) - if isAsmStmt and hasGnuAsm in CC[cCompiler].props: + if isAsmStmt and hasGnuAsm in CC[p.config.cCompiler].props: for x in splitLines(res): var j = 0 - while x[j] in {' ', '\t'}: inc(j) - if x[j] in {'"', ':'}: - # don't modify the line if already in quotes or - # some clobber register list: - add(result, x); add(result, tnl) - elif x[j] != '\0': - # ignore empty lines - add(result, "\"") - add(result, x) - add(result, "\\n\"\n") + while j < x.len and x[j] in {' ', '\t'}: inc(j) + if j < x.len: + if x[j] in {'"', ':'}: + # don't modify the line if already in quotes or + # some clobber register list: + add(result, x); add(result, "\L") + else: + # ignore empty lines + add(result, "\"") + add(result, x) + add(result, "\\n\"\n") else: - res.add(tnl) + res.add("\L") result = res.rope proc genAsmStmt(p: BProc, t: PNode) = @@ -1001,9 +1002,9 @@ proc genAsmStmt(p: BProc, t: PNode) = # work: if p.prc == nil: # top level asm statement? - addf(p.module.s[cfsProcHeaders], CC[cCompiler].asmStmtFrmt, [s]) + addf(p.module.s[cfsProcHeaders], CC[p.config.cCompiler].asmStmtFrmt, [s]) else: - lineF(p, cpsStmts, CC[cCompiler].asmStmtFrmt, [s]) + lineF(p, cpsStmts, CC[p.config.cCompiler].asmStmtFrmt, [s]) proc determineSection(n: PNode): TCFileSection = result = cfsProcHeaders @@ -1036,7 +1037,7 @@ proc genBreakPoint(p: BProc, t: PNode) = genLineDir(p, t) # BUGFIX appcg(p.module, p.module.g.breakpoints, "#dbgRegisterBreakpoint($1, (NCSTRING)$2, (NCSTRING)$3);$n", [ - rope(toLinenumber(t.info)), makeCString(toFilename(t.info)), + rope(toLinenumber(t.info)), makeCString(toFilename(p.config, t.info)), makeCString(name)]) proc genWatchpoint(p: BProc, n: PNode) = @@ -1045,7 +1046,7 @@ proc genWatchpoint(p: BProc, n: PNode) = initLocExpr(p, n.sons[1], a) let typ = skipTypes(n.sons[1].typ, abstractVarRange) lineCg(p, cpsStmts, "#dbgRegisterWatchpoint($1, (NCSTRING)$2, $3);$n", - [a.addrLoc, makeCString(renderTree(n.sons[1])), + [addrLoc(p.config, a), makeCString(renderTree(n.sons[1])), genTypeInfo(p.module, typ, n.info)]) proc genPragma(p: BProc, n: PNode) = @@ -1076,7 +1077,7 @@ proc genDiscriminantCheck(p: BProc, a, tmp: TLoc, objtype: PType, var t = skipTypes(objtype, abstractVar) assert t.kind == tyObject discard genTypeInfo(p.module, t, a.lode.info) - var L = lengthOrd(field.typ) + var L = lengthOrd(p.config, field.typ) if not containsOrIncl(p.module.declaredThings, field.id): appcg(p.module, cfsVars, "extern $1", discriminatorTableDecl(p.module, t, field)) diff --git a/compiler/ccgthreadvars.nim b/compiler/ccgthreadvars.nim index da5c624b7..3e8a87041 100644 --- a/compiler/ccgthreadvars.nim +++ b/compiler/ccgthreadvars.nim @@ -23,27 +23,14 @@ proc accessThreadLocalVar(p: BProc, s: PSym) = add(p.procSec(cpsInit), ropecg(p.module, "\tNimTV_ = (NimThreadVars*) #GetThreadLocalVars();$n")) -var - nimtv: Rope # Nim thread vars; the struct body - nimtvDeps: seq[PType] = @[] # type deps: every module needs whole struct - nimtvDeclared = initIntSet() # so that every var/field exists only once - # in the struct - -# 'nimtv' is incredibly hard to modularize! Best effort is to store all thread -# vars in a ROD section and with their type deps and load them -# unconditionally... - -# nimtvDeps is VERY hard to cache because it's not a list of IDs nor can it be -# made to be one. - proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) = if emulatedThreadVars(m.config): # we gather all thread locals var into a struct; we need to allocate # storage for that somehow, can't use the thread local storage # allocator for it :-( - if not containsOrIncl(nimtvDeclared, s.id): - nimtvDeps.add(s.loc.t) - addf(nimtv, "$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r]) + if not containsOrIncl(m.g.nimtvDeclared, s.id): + m.g.nimtvDeps.add(s.loc.t) + addf(m.g.nimtv, "$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r]) else: if isExtern: add(m.s[cfsVars], "extern ") if optThreads in m.config.globalOptions: add(m.s[cfsVars], "NIM_THREADVAR ") @@ -51,12 +38,12 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) = addf(m.s[cfsVars], " $1;$n", [s.loc.r]) proc generateThreadLocalStorage(m: BModule) = - if nimtv != nil and (usesThreadVars in m.flags or sfMainModule in m.module.flags): - for t in items(nimtvDeps): discard getTypeDesc(m, t) - addf(m.s[cfsSeqTypes], "typedef struct {$1} NimThreadVars;$n", [nimtv]) + if m.g.nimtv != nil and (usesThreadVars in m.flags or sfMainModule in m.module.flags): + for t in items(m.g.nimtvDeps): discard getTypeDesc(m, t) + addf(m.s[cfsSeqTypes], "typedef struct {$1} NimThreadVars;$n", [m.g.nimtv]) proc generateThreadVarsSize(m: BModule) = - if nimtv != nil: + if m.g.nimtv != nil: let externc = if m.config.cmd == cmdCompileToCpp or sfCompileToCpp in m.module.flags: "extern \"C\" " else: "" diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim index c265064a1..4514ce7dc 100644 --- a/compiler/ccgtrav.nim +++ b/compiler/ccgtrav.nim @@ -70,7 +70,7 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) = tySink: genTraverseProc(c, accessor, lastSon(typ)) of tyArray: - let arraySize = lengthOrd(typ.sons[0]) + let arraySize = lengthOrd(c.p.config, typ.sons[0]) var i: TLoc getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo(), tyInt), i) let oldCode = p.s(cpsStmts) @@ -122,7 +122,6 @@ proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope = var p = newProc(nil, m) result = "Marker_" & getTypeName(m, origTyp, sig) var typ = origTyp.skipTypes(abstractInst) - if typ.kind == tyOpt: typ = optLowering(typ) let header = "static N_NIMCALL(void, $1)(void* p, NI op)" % [result] diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 7b44cddad..82508e37e 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -50,7 +50,7 @@ proc mangleName(m: BModule; s: PSym): Rope = result = s.name.s.mangle.rope add(result, idOrSig(s, m.module.name.s, m.sigConflicts)) s.loc.r = result - writeMangledName(m.ndi, s) + writeMangledName(m.ndi, s, m.config) proc mangleParamName(m: BModule; s: PSym): Rope = ## we cannot use 'sigConflicts' here since we have a BModule, not a BProc. @@ -63,7 +63,7 @@ proc mangleParamName(m: BModule; s: PSym): Rope = res.add "_0" result = res.rope s.loc.r = result - writeMangledName(m.ndi, s) + writeMangledName(m.ndi, s, m.config) proc mangleLocalName(p: BProc; s: PSym): Rope = assert s.kind in skLocalVars+{skTemp} @@ -81,7 +81,7 @@ proc mangleLocalName(p: BProc; s: PSym): Rope = result.add "_" & rope(counter+1) p.sigConflicts.inc(key) s.loc.r = result - if s.kind != skTemp: writeMangledName(p.module.ndi, s) + if s.kind != skTemp: writeMangledName(p.module.ndi, s, p.config) proc scopeMangledParam(p: BProc; param: PSym) = ## parameter generation only takes BModule, not a BProc, so we have to @@ -124,40 +124,40 @@ proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope = result = typ.loc.r if result == nil: internalError(m.config, "getTypeName: " & $typ.kind) -proc mapSetType(typ: PType): TCTypeKind = - case int(getSize(typ)) +proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind = + case int(getSize(conf, typ)) of 1: result = ctInt8 of 2: result = ctInt16 of 4: result = ctInt32 of 8: result = ctInt64 else: result = ctArray -proc mapType(typ: PType): TCTypeKind = +proc mapType(conf: ConfigRef; typ: PType): TCTypeKind = ## Maps a Nim type to a C type case typ.kind of tyNone, tyStmt: result = ctVoid of tyBool: result = ctBool of tyChar: result = ctChar - of tySet: result = mapSetType(typ) + of tySet: result = mapSetType(conf, typ) of tyOpenArray, tyArray, tyVarargs: result = ctArray of tyObject, tyTuple: result = ctStruct of tyUserTypeClasses: doAssert typ.isResolvedUserTypeClass - return mapType(typ.lastSon) + return mapType(conf, typ.lastSon) of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, tySink, tyInferred: - result = mapType(lastSon(typ)) + result = mapType(conf, lastSon(typ)) of tyEnum: - if firstOrd(typ) < 0: + if firstOrd(conf, typ) < 0: result = ctInt32 else: - case int(getSize(typ)) + case int(getSize(conf, typ)) of 1: result = ctUInt8 of 2: result = ctUInt16 of 4: result = ctInt32 of 8: result = ctInt64 else: result = ctInt32 - of tyRange: result = mapType(typ.sons[0]) + of tyRange: result = mapType(conf, typ.sons[0]) of tyPtr, tyVar, tyLent, tyRef, tyOptAsRef: var base = skipTypes(typ.lastSon, typedescInst) case base.kind @@ -169,27 +169,20 @@ proc mapType(typ: PType): TCTypeKind = else: result = ctPtr of tyPointer: result = ctPtr of tySequence: result = ctNimSeq - of tyOpt: - case optKind(typ) - of oBool: result = ctStruct - of oNil, oPtr: result = ctPtr - of oEnum: - # The 'nil' value is always negative, so we always use a signed integer - result = if getSize(typ.sons[0]) == 8: ctInt64 else: ctInt32 of tyProc: result = if typ.callConv != ccClosure: ctProc else: ctStruct of tyString: result = ctNimStr of tyCString: result = ctCString of tyInt..tyUInt64: result = TCTypeKind(ord(typ.kind) - ord(tyInt) + ord(ctInt)) of tyStatic: - if typ.n != nil: result = mapType(lastSon typ) + if typ.n != nil: result = mapType(conf, lastSon typ) else: doAssert(false, "mapType") else: doAssert(false, "mapType") -proc mapReturnType(typ: PType): TCTypeKind = +proc mapReturnType(conf: ConfigRef; typ: PType): TCTypeKind = #if skipTypes(typ, typedescInst).kind == tyArray: result = ctPtr #else: - result = mapType(typ) + result = mapType(conf, typ) proc isImportedType(t: PType): bool = result = t.sym != nil and sfImportc in t.sym.flags @@ -207,14 +200,14 @@ proc isObjLackingTypeField(typ: PType): bool {.inline.} = result = (typ.kind == tyObject) and ((tfFinal in typ.flags) and (typ.sons[0] == nil) or isPureObject(typ)) -proc isInvalidReturnType(rettype: PType): bool = +proc isInvalidReturnType(conf: ConfigRef; rettype: PType): bool = # Arrays and sets cannot be returned by a C procedure, because C is # such a poor programming language. # We exclude records with refs too. This enhances efficiency and # is necessary for proper code generation of assignments. if rettype == nil: result = true else: - case mapType(rettype) + case mapType(conf, rettype) of ctArray: result = not (skipTypes(rettype, typedescInst).kind in {tyVar, tyLent, tyRef, tyPtr}) @@ -240,9 +233,9 @@ proc cacheGetType(tab: TypeCache; sig: SigHash): Rope = proc addAbiCheck(m: BModule, t: PType, name: Rope) = if isDefined(m.config, "checkabi"): - addf(m.s[cfsTypeInfo], "NIM_CHECK_SIZE($1, $2);$n", [name, rope(getSize(t))]) + addf(m.s[cfsTypeInfo], "NIM_CHECK_SIZE($1, $2);$n", [name, rope(getSize(m.config, t))]) -proc ccgIntroducedPtr(s: PSym): bool = +proc ccgIntroducedPtr(conf: ConfigRef; s: PSym): bool = var pt = skipTypes(s.typ, typedescInst) assert skResult != s.kind if tfByRef in pt.flags: return true @@ -252,7 +245,7 @@ proc ccgIntroducedPtr(s: PSym): bool = if s.typ.sym != nil and sfForward in s.typ.sym.flags: # forwarded objects are *always* passed by pointers for consistency! result = true - elif (optByRef in s.options) or (getSize(pt) > platform.floatSize * 3): + elif (optByRef in s.options) or (getSize(conf, pt) > conf.target.floatSize * 3): result = true # requested anyway elif (tfFinal in pt.flags) and (pt.sons[0] == nil): result = false # no need, because no subtyping possible @@ -260,14 +253,14 @@ proc ccgIntroducedPtr(s: PSym): bool = result = true # ordinary objects are always passed by reference, # otherwise casting doesn't work of tyTuple: - result = (getSize(pt) > platform.floatSize*3) or (optByRef in s.options) + result = (getSize(conf, pt) > conf.target.floatSize*3) or (optByRef in s.options) else: result = false -proc fillResult(param: PNode) = +proc fillResult(conf: ConfigRef; param: PNode) = fillLoc(param.sym.loc, locParam, param, ~"Result", OnStack) let t = param.sym.typ - if mapReturnType(t) != ctArray and isInvalidReturnType(t): + if mapReturnType(conf, t) != ctArray and isInvalidReturnType(conf, t): incl(param.sym.loc.flags, lfIndirect) param.sym.loc.storage = OnUnknown @@ -364,12 +357,6 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope = of tySequence: result = getTypeForward(m, t, hashType(t)) & "*" pushType(m, t) - of tyOpt: - if optKind(etB) == oPtr: - result = getTypeForward(m, t, hashType(t)) & "*" - pushType(m, t) - else: - result = getTypeDescAux(m, t, check) else: result = getTypeDescAux(m, t, check) @@ -384,7 +371,7 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope, check: var IntSet, declareEnvironment=true; weakDep=false) = params = nil - if t.sons[0] == nil or isInvalidReturnType(t.sons[0]): + if t.sons[0] == nil or isInvalidReturnType(m.config, t.sons[0]): rettype = ~"void" else: rettype = getTypeDescAux(m, t.sons[0], check) @@ -395,7 +382,7 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope, if params != nil: add(params, ~", ") fillLoc(param.loc, locParam, t.n.sons[i], mangleParamName(m, param), param.paramStorageLoc) - if ccgIntroducedPtr(param): + if ccgIntroducedPtr(m.config, param): add(params, getTypeDescWeak(m, param.typ, check)) add(params, ~"*") incl(param.loc.flags, lfIndirect) @@ -417,10 +404,10 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope, addf(params, ", NI $1Len_$2", [param.loc.r, j.rope]) inc(j) arr = arr.sons[0] - if t.sons[0] != nil and isInvalidReturnType(t.sons[0]): + if t.sons[0] != nil and isInvalidReturnType(m.config, t.sons[0]): var arr = t.sons[0] if params != nil: add(params, ", ") - if mapReturnType(t.sons[0]) != ctArray: + if mapReturnType(m.config, t.sons[0]) != ctArray: add(params, getTypeDescWeak(m, arr, check)) add(params, "*") else: @@ -471,13 +458,13 @@ proc genRecordFieldsAux(m: BModule, n: PNode, if tfPacked notin rectype.flags: add(unionBody, "struct {") else: - if hasAttribute in CC[cCompiler].props: + if hasAttribute in CC[m.config.cCompiler].props: add(unionBody, "struct __attribute__((__packed__)){" ) else: addf(unionBody, "#pragma pack(push, 1)$nstruct{", []) add(unionBody, a) addf(unionBody, "} $1;$n", [sname]) - if tfPacked in rectype.flags and hasAttribute notin CC[cCompiler].props: + if tfPacked in rectype.flags and hasAttribute notin CC[m.config.cCompiler].props: addf(unionBody, "#pragma pack(pop)$n", []) else: add(unionBody, genRecordFieldsAux(m, k, ae, rectype, check)) @@ -526,10 +513,10 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope, var hasField = false if tfPacked in typ.flags: - if hasAttribute in CC[cCompiler].props: + if hasAttribute in CC[m.config.cCompiler].props: result = structOrUnion(typ) & " __attribute__((__packed__))" else: - result = "#pragma pack(push, 1)" & tnl & structOrUnion(typ) + result = "#pragma pack(push, 1)\L" & structOrUnion(typ) else: result = structOrUnion(typ) @@ -556,7 +543,7 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope, # proper request to generate popCurrentExceptionEx not possible for 2 reasons: # generated function will be below declared Exception type and circular dependency # between Exception and popCurrentExceptionEx function - result = genProcHeader(m, magicsys.getCompilerProc(m.g.graph, "popCurrentExceptionEx")) & ";" & rnl & result + result = genProcHeader(m, magicsys.getCompilerProc(m.g.graph, "popCurrentExceptionEx")) & ";" & result hasField = true else: appcg(m, result, " {$n $1 Sup;$n", @@ -570,9 +557,9 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope, addf(result, "char dummy;$n", []) else: add(result, desc) - add(result, "};" & tnl) - if tfPacked in typ.flags and hasAttribute notin CC[cCompiler].props: - result.add "#pragma pack(pop)" & tnl + add(result, "};\L") + if tfPacked in typ.flags and hasAttribute notin CC[m.config.cCompiler].props: + result.add "#pragma pack(pop)\L" proc getTupleDesc(m: BModule, typ: PType, name: Rope, check: var IntSet): Rope = @@ -581,9 +568,9 @@ proc getTupleDesc(m: BModule, typ: PType, name: Rope, for i in countup(0, sonsLen(typ) - 1): addf(desc, "$1 Field$2;$n", [getTypeDescAux(m, typ.sons[i], check), rope(i)]) - if desc == nil: add(result, "char dummy;" & tnl) + if desc == nil: add(result, "char dummy;\L") else: add(result, desc) - add(result, "};" & tnl) + add(result, "};\L") proc scanCppGenericSlot(pat: string, cursor, outIdx, outStars: var int): bool = # A helper proc for handling cppimport patterns, involving numeric @@ -657,21 +644,6 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = result = name & "*" & star m.typeCache[sig] = result pushType(m, et) - of tyOpt: - if etB.sons[0].kind in {tyObject, tyTuple}: - let name = getTypeForward(m, et, hashType et) - result = name & "*" & star - m.typeCache[sig] = result - pushType(m, et) - elif optKind(etB) == oBool: - let name = getTypeForward(m, et, hashType et) - result = name & "*" - m.typeCache[sig] = result - pushType(m, et) - else: - # else we have a strong dependency :-( - result = getTypeDescAux(m, et, check) & star - m.typeCache[sig] = result else: # else we have a strong dependency :-( result = getTypeDescAux(m, et, check) & star @@ -687,11 +659,11 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = (sfImportc in t.sym.flags and t.sym.magic == mNone)): m.typeCache[sig] = result var size: int - if firstOrd(t) < 0: + if firstOrd(m.config, t) < 0: addf(m.s[cfsTypes], "typedef NI32 $1;$n", [result]) size = 4 else: - size = int(getSize(t)) + size = int(getSize(m.config, t)) case size of 1: addf(m.s[cfsTypes], "typedef NU8 $1;$n", [result]) of 2: addf(m.s[cfsTypes], "typedef NU16 $1;$n", [result]) @@ -747,40 +719,8 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = else: result = rope("TGenericSeq") add(result, "*") - of tyOpt: - result = cacheGetType(m.typeCache, sig) - if result == nil: - case optKind(t) - of oBool: - result = cacheGetType(m.forwTypeCache, sig) - if result == nil: - result = getTypeName(m, origTyp, sig) - addf(m.s[cfsForwardTypes], getForwardStructFormat(m), - [structOrUnion(t), result]) - m.forwTypeCache[sig] = result - appcg(m, m.s[cfsSeqTypes], "struct $2 {$n" & - " NIM_BOOL Field0;$n" & - " $1 Field1;$n" & - "};$n", [getTypeDescAux(m, t.sons[0], check), result]) - of oPtr: - let et = t.sons[0] - if et.kind in {tyTuple, tyObject}: - let name = getTypeForward(m, et, hashType et) - result = name & "*" - pushType(m, et) - else: - result = getTypeDescAux(m, t.sons[0], check) & "*" - of oNil: - result = getTypeDescAux(m, t.sons[0], check) - of oEnum: - result = getTypeName(m, origTyp, sig) - if getSize(t.sons[0]) == 8: - addf(m.s[cfsTypes], "typedef NI64 $1;$n", [result]) - else: - addf(m.s[cfsTypes], "typedef NI32 $1;$n", [result]) - m.typeCache[sig] = result of tyArray: - var n: BiggestInt = lengthOrd(t) + var n: BiggestInt = lengthOrd(m.config, t) if n <= 0: n = 1 # make an array of at least one element result = getTypeName(m, origTyp, sig) m.typeCache[sig] = result @@ -791,8 +731,6 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = else: addAbiCheck(m, t, result) of tyObject, tyTuple: if isImportedCppType(t) and origTyp.kind == tyGenericInst: - # for instantiated templates we do not go through the type cache as the - # the type cache is not aware of 'tyGenericInst'. let cppName = getTypeName(m, t, sig) var i = 0 var chunkStart = 0 @@ -866,11 +804,11 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = result = $t.kind & '_' & getTypeName(m, t.lastSon, hashType t.lastSon) m.typeCache[sig] = result if not isImportedType(t): - let s = int(getSize(t)) + let s = int(getSize(m.config, t)) case s of 1, 2, 4, 8: addf(m.s[cfsTypes], "typedef NU$2 $1;$n", [result, rope(s*8)]) else: addf(m.s[cfsTypes], "typedef NU8 $1[$2];$n", - [result, rope(getSize(t))]) + [result, rope(getSize(m.config, t))]) of tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, tySink, tyUserTypeClass, tyUserTypeClassInst, tyInferred: result = getTypeDescAux(m, lastSon(t), check) @@ -972,7 +910,7 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType; var typename = typeToString(if origType.typeInst != nil: origType.typeInst else: origType, preferName) if typename == "ref object" and origType.skipTypes(skipPtrs).sym != nil: - typename = "anon ref object from " & $origType.skipTypes(skipPtrs).sym.info + typename = "anon ref object from " & m.config$origType.skipTypes(skipPtrs).sym.info addf(m.s[cfsTypeInit3], "$1.name = $2;$n", [name, makeCstring typename]) discard cgsym(m, "nimTypeRoot") @@ -1006,7 +944,7 @@ proc discriminatorTableName(m: BModule, objtype: PType, d: PSym): Rope = proc discriminatorTableDecl(m: BModule, objtype: PType, d: PSym): Rope = discard cgsym(m, "TNimNode") var tmp = discriminatorTableName(m, objtype, d) - result = "TNimNode* $1[$2];$n" % [tmp, rope(lengthOrd(d.typ)+1)] + result = "TNimNode* $1[$2];$n" % [tmp, rope(lengthOrd(m.config, d.typ)+1)] proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope; info: TLineInfo) = @@ -1030,7 +968,7 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope; assert(n.sons[0].kind == nkSym) var field = n.sons[0].sym var tmp = discriminatorTableName(m, typ, field) - var L = lengthOrd(field.typ) + var L = lengthOrd(m.config, field.typ) assert L > 0 if field.loc.r == nil: fillObjectFields(m, typ) if field.loc.t == nil: @@ -1140,7 +1078,7 @@ proc genEnumInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) = add(enumNames, makeCString(field.name.s)) else: add(enumNames, makeCString(field.ast.strVal)) - if i < length - 1: add(enumNames, ", " & tnl) + if i < length - 1: add(enumNames, ", \L") if field.position != i or tfEnumHasHoles in typ.flags: addf(specialCases, "$1.offset = $2;$n", [elemNode, rope(field.position)]) hasHoles = true @@ -1166,7 +1104,7 @@ proc genSetInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) = genTypeInfoAux(m, typ, typ, name, info) var tmp = getNimNode(m) addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 0;$n" & "$3.node = &$1;$n", - [tmp, rope(firstOrd(typ)), name]) + [tmp, rope(firstOrd(m.config, typ)), name]) proc genArrayInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) = genTypeInfoAuxBase(m, typ, typ, name, genTypeInfo(m, typ.sons[1], info), info) @@ -1190,8 +1128,6 @@ proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) = proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope = let origType = t var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses) - if t.kind == tyOpt: - return genTypeInfo(m, optLowering(t), info) let sig = hashType(origType) result = m.typeInfoMarker.getOrDefault(sig) diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index a6080a808..75cd3d35d 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -27,9 +27,9 @@ proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode = proc stmtsContainPragma*(n: PNode, w: TSpecialWord): bool = result = getPragmaStmt(n, w) != nil -proc hashString*(s: string): BiggestInt = +proc hashString*(conf: ConfigRef; s: string): BiggestInt = # has to be the same algorithm as system.hashString! - if CPU[targetCPU].bit == 64: + if CPU[conf.target.targetCPU].bit == 64: # we have to use the same bitwidth # as the target CPU var b = 0'i64 @@ -52,117 +52,12 @@ proc hashString*(s: string): BiggestInt = a = a +% `shl`(a, 15'i32) result = a -var - gTypeTable: array[TTypeKind, TIdTable] # XXX globals here - gCanonicalTypes: array[TTypeKind, PType] - -proc initTypeTables() = - for i in countup(low(TTypeKind), high(TTypeKind)): initIdTable(gTypeTable[i]) - -proc resetCaches* = - ## XXX: fix that more properly - initTypeTables() - for i in low(gCanonicalTypes)..high(gCanonicalTypes): - gCanonicalTypes[i] = nil - -when false: - proc echoStats*() = - for i in countup(low(TTypeKind), high(TTypeKind)): - echo i, " ", gTypeTable[i].counter - -proc slowSearch(key: PType; k: TTypeKind): PType = - # tuples are quite horrible as C does not support them directly and - # tuple[string, string] is a (strange) subtype of - # tuple[nameA, nameB: string]. This bites us here, so we - # use 'sameBackendType' instead of 'sameType'. - if idTableHasObjectAsKey(gTypeTable[k], key): return key - for h in countup(0, high(gTypeTable[k].data)): - var t = PType(gTypeTable[k].data[h].key) - if t != nil and sameBackendType(t, key): - return t - idTablePut(gTypeTable[k], key, key) - result = key - -proc getUniqueType*(key: PType): PType = - # this is a hotspot in the compiler! - result = key - when false: - if key == nil: return - var k = key.kind - case k - of tyBool, tyChar, tyInt..tyUInt64: - # no canonicalization for integral types, so that e.g. ``pid_t`` is - # produced instead of ``NI``. - result = key - of tyEmpty, tyNil, tyExpr, tyStmt, tyPointer, tyString, - tyCString, tyNone, tyVoid: - result = gCanonicalTypes[k] - if result == nil: - gCanonicalTypes[k] = key - result = key - of tyTypeDesc, tyTypeClasses, tyGenericParam, tyFromExpr: - if key.isResolvedUserTypeClass: - return getUniqueType(lastSon(key)) - if key.sym != nil: - internalError(key.sym.info, "metatype not eliminated") - else: - internalError("metatype not eliminated") - of tyDistinct: - if key.deepCopy != nil: result = key - else: result = getUniqueType(lastSon(key)) - of tyGenericInst, tyOrdinal, tyStatic, tyAlias, tySink, tyInferred: - result = getUniqueType(lastSon(key)) - #let obj = lastSon(key) - #if obj.sym != nil and obj.sym.name.s == "TOption": - # echo "for ", typeToString(key), " I returned " - # debug result - of tyPtr, tyRef, tyVar, tyLent: - let elemType = lastSon(key) - if elemType.kind in {tyBool, tyChar, tyInt..tyUInt64}: - # no canonicalization for integral types, so that e.g. ``ptr pid_t`` is - # produced instead of ``ptr NI``. - result = key - else: - result = slowSearch(key, k) - of tyGenericInvocation, tyGenericBody, - tyOpenArray, tyArray, tySet, tyRange, tyTuple, - tySequence, tyForward, tyVarargs, tyProxy, tyOpt: - # we have to do a slow linear search because types may need - # to be compared by their structure: - result = slowSearch(key, k) - of tyObject: - if tfFromGeneric notin key.flags: - # fast case; lookup per id suffices: - result = PType(idTableGet(gTypeTable[k], key)) - if result == nil: - idTablePut(gTypeTable[k], key, key) - result = key - else: - # ugly slow case: need to compare by structure - if idTableHasObjectAsKey(gTypeTable[k], key): return key - for h in countup(0, high(gTypeTable[k].data)): - var t = PType(gTypeTable[k].data[h].key) - if t != nil and sameBackendType(t, key): - return t - idTablePut(gTypeTable[k], key, key) - result = key - of tyEnum: - result = PType(idTableGet(gTypeTable[k], key)) - if result == nil: - idTablePut(gTypeTable[k], key, key) - result = key - of tyProc: - if key.callConv != ccClosure: - result = key - else: - # ugh, we need the canon here: - result = slowSearch(key, k) - of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("getUniqueType") +template getUniqueType*(key: PType): PType = key proc makeSingleLineCString*(s: string): string = result = "\"" for c in items(s): - result.add(c.toCChar) + c.toCChar(result) result.add('\"') proc mangle*(name: string): string = @@ -210,5 +105,3 @@ proc mangle*(name: string): string = requiresUnderscore = true if requiresUnderscore: result.add "_" - -initTypeTables() diff --git a/compiler/cgen.nim b/compiler/cgen.nim index e749c78db..cd344f096 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -12,14 +12,15 @@ import ast, astalgo, hashes, trees, platform, magicsys, extccomp, options, intsets, nversion, nimsets, msgs, std / sha1, bitsets, idents, types, - ccgutils, os, ropes, math, passes, rodread, wordrecg, treetab, cgmeth, + ccgutils, os, ropes, math, passes, wordrecg, treetab, cgmeth, condsyms, rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases, - lowerings, semparallel, tables, sets, ndi + lowerings, semparallel, tables, sets, ndi, lineinfos import strutils except `%` # collides with ropes.`%` from modulegraphs import ModuleGraph -from configuration import warnGcMem, errXMustBeCompileTime, hintDependency, errGenerated +from lineinfos import + warnGcMem, errXMustBeCompileTime, hintDependency, errGenerated, errCannotOpenFile import dynlib when not declared(dynlib.libCandidates): @@ -120,10 +121,10 @@ proc ropecg(m: BModule, frmt: FormatStr, args: varargs[Rope]): Rope = internalError(m.config, "ropes: invalid format string $" & $j) add(result, args[j-1]) of 'n': - if optLineDir notin m.config.options: add(result, rnl) + if optLineDir notin m.config.options: add(result, "\L") inc(i) of 'N': - add(result, rnl) + add(result, "\L") inc(i) else: internalError(m.config, "ropes: invalid format string $" & frmt[i]) elif frmt[i] == '#' and frmt[i+1] in IdentStartChars: @@ -193,7 +194,7 @@ proc genCLineDir(r: var Rope, filename: string, line: int; conf: ConfigRef) = [rope(makeSingleLineCString(filename)), rope(line)]) proc genCLineDir(r: var Rope, info: TLineInfo; conf: ConfigRef) = - genCLineDir(r, info.toFullPath, info.safeLineNm, conf) + genCLineDir(r, toFullPath(conf, info), info.safeLineNm, conf) proc freshLineInfo(p: BProc; info: TLineInfo): bool = if p.lastLineInfo.line != info.line or @@ -203,27 +204,22 @@ proc freshLineInfo(p: BProc; info: TLineInfo): bool = result = true proc genLineDir(p: BProc, t: PNode) = - var tt = t - #while tt.kind in {nkStmtListExpr}+nkCallKinds: - # tt = tt.lastSon - if tt.kind in nkCallKinds and tt.len > 1: - tt = tt.sons[1] - let line = tt.info.safeLineNm + let line = t.info.safeLineNm if optEmbedOrigSrc in p.config.globalOptions: - add(p.s(cpsStmts), ~"//" & sourceLine(p.config, tt.info) & rnl) - genCLineDir(p.s(cpsStmts), tt.info.toFullPath, line, p.config) + add(p.s(cpsStmts), ~"//" & sourceLine(p.config, t.info) & "\L") + genCLineDir(p.s(cpsStmts), toFullPath(p.config, t.info), line, p.config) if ({optStackTrace, optEndb} * p.options == {optStackTrace, optEndb}) and (p.prc == nil or sfPure notin p.prc.flags): - if freshLineInfo(p, tt.info): + if freshLineInfo(p, t.info): linefmt(p, cpsStmts, "#endb($1, $2);$N", - line.rope, makeCString(toFilename(tt.info))) + line.rope, makeCString(toFilename(p.config, t.info))) elif ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and - (p.prc == nil or sfPure notin p.prc.flags) and tt.info.fileIndex != InvalidFileIDX: - if freshLineInfo(p, tt.info): + (p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex != InvalidFileIDX: + if freshLineInfo(p, t.info): linefmt(p, cpsStmts, "nimln_($1, $2);$n", - line.rope, quotedFilename(p.config, tt.info)) + line.rope, quotedFilename(p.config, t.info)) proc postStmtActions(p: BProc) {.inline.} = add(p.s(cpsStmts), p.module.injectStmt) @@ -249,9 +245,9 @@ proc rdLoc(a: TLoc): Rope = result = a.r if lfIndirect in a.flags: result = "(*$1)" % [result] -proc addrLoc(a: TLoc): Rope = +proc addrLoc(conf: ConfigRef; a: TLoc): Rope = result = a.r - if lfIndirect notin a.flags and mapType(a.t) != ctArray: + if lfIndirect notin a.flags and mapType(conf, a.t) != ctArray: result = "(&" & result & ")" proc rdCharLoc(a: TLoc): Rope = @@ -281,7 +277,7 @@ proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc, linefmt(p, section, "$1.m_type = $2;$n", r, genTypeInfo(p.module, t, a.lode.info)) of frEmbedded: # worst case for performance: - var r = if takeAddr: addrLoc(a) else: rdLoc(a) + var r = if takeAddr: addrLoc(p.config, a) else: rdLoc(a) linefmt(p, section, "#objectInit($1, $2);$n", r, genTypeInfo(p.module, t, a.lode.info)) type @@ -310,10 +306,10 @@ proc resetLoc(p: BProc, loc: var TLoc) = linefmt(p, cpsStmts, "$1 = 0;$n", rdLoc(loc)) else: if optNilCheck in p.options: - linefmt(p, cpsStmts, "#chckNil((void*)$1);$n", addrLoc(loc)) + linefmt(p, cpsStmts, "#chckNil((void*)$1);$n", addrLoc(p.config, loc)) if loc.storage != OnStack: linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n", - addrLoc(loc), genTypeInfo(p.module, loc.t, loc.lode.info)) + addrLoc(p.config, loc), genTypeInfo(p.module, loc.t, loc.lode.info)) # XXX: generated reset procs should not touch the m_type # field, so disabling this should be safe: genObjectInit(p, cpsStmts, loc.t, loc, true) @@ -322,7 +318,7 @@ proc resetLoc(p: BProc, loc: var TLoc) = # array passed as argument decayed into pointer, bug #7332 # so we use getTypeDesc here rather than rdLoc(loc) linefmt(p, cpsStmts, "memset((void*)$1, 0, sizeof($2));$n", - addrLoc(loc), getTypeDesc(p.module, loc.t)) + addrLoc(p.config, loc), getTypeDesc(p.module, loc.t)) # XXX: We can be extra clever here and call memset only # on the bytes following the m_type field? genObjectInit(p, cpsStmts, loc.t, loc, true) @@ -339,7 +335,7 @@ proc constructLoc(p: BProc, loc: TLoc, isTemp = false) = if not isImportedCppType(typ): useStringh(p.module) linefmt(p, cpsStmts, "memset((void*)$1, 0, sizeof($2));$n", - addrLoc(loc), getTypeDesc(p.module, typ)) + addrLoc(p.config, loc), getTypeDesc(p.module, typ)) genObjectInit(p, cpsStmts, loc.t, loc, true) proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) = @@ -386,7 +382,7 @@ proc localDebugInfo(p: BProc, s: PSym) = # XXX work around a bug: No type information for open arrays possible: if skipTypes(s.typ, abstractVar).kind in {tyOpenArray, tyVarargs}: return var a = "&" & s.loc.r - if s.kind == skParam and ccgIntroducedPtr(s): a = s.loc.r + if s.kind == skParam and ccgIntroducedPtr(p.config, s): a = s.loc.r lineF(p, cpsInit, "FR_.s[$1].address = (void*)$3; FR_.s[$1].typ = $4; FR_.s[$1].name = $2;$n", [p.maxFrameLen.rope, makeCString(normalize(s.name.s)), a, @@ -414,7 +410,7 @@ proc assignLocalVar(p: BProc, n: PNode) = #assert(s.loc.k == locNone) # not yet assigned # this need not be fulfilled for inline procs; they are regenerated # for each module that uses them! - let nl = if optLineDir in p.config.options: "" else: tnl + let nl = if optLineDir in p.config.options: "" else: "\L" let decl = localVarDecl(p, n) & ";" & nl line(p, cpsLocals, decl) localDebugInfo(p, n.sym) @@ -651,33 +647,33 @@ proc cgsym(m: BModule, name: string): Rope = result = sym.loc.r proc generateHeaders(m: BModule) = - add(m.s[cfsHeaders], tnl & "#include \"nimbase.h\"" & tnl) + add(m.s[cfsHeaders], "\L#include \"nimbase.h\"\L") for it in m.headerFiles: if it[0] == '#': - add(m.s[cfsHeaders], rope(it.replace('`', '"') & tnl)) + add(m.s[cfsHeaders], rope(it.replace('`', '"') & "\L")) elif it[0] notin {'\"', '<'}: addf(m.s[cfsHeaders], "#include \"$1\"$N", [rope(it)]) else: addf(m.s[cfsHeaders], "#include $1$N", [rope(it)]) - add(m.s[cfsHeaders], "#undef LANGUAGE_C" & tnl) - add(m.s[cfsHeaders], "#undef MIPSEB" & tnl) - add(m.s[cfsHeaders], "#undef MIPSEL" & tnl) - add(m.s[cfsHeaders], "#undef PPC" & tnl) - add(m.s[cfsHeaders], "#undef R3000" & tnl) - add(m.s[cfsHeaders], "#undef R4000" & tnl) - add(m.s[cfsHeaders], "#undef i386" & tnl) - add(m.s[cfsHeaders], "#undef linux" & tnl) - add(m.s[cfsHeaders], "#undef mips" & tnl) - add(m.s[cfsHeaders], "#undef near" & tnl) - add(m.s[cfsHeaders], "#undef powerpc" & tnl) - add(m.s[cfsHeaders], "#undef unix" & tnl) + add(m.s[cfsHeaders], "#undef LANGUAGE_C\L") + add(m.s[cfsHeaders], "#undef MIPSEB\L") + add(m.s[cfsHeaders], "#undef MIPSEL\L") + add(m.s[cfsHeaders], "#undef PPC\L") + add(m.s[cfsHeaders], "#undef R3000\L") + add(m.s[cfsHeaders], "#undef R4000\L") + add(m.s[cfsHeaders], "#undef i386\L") + add(m.s[cfsHeaders], "#undef linux\L") + add(m.s[cfsHeaders], "#undef mips\L") + add(m.s[cfsHeaders], "#undef near\L") + add(m.s[cfsHeaders], "#undef powerpc\L") + add(m.s[cfsHeaders], "#undef unix\L") proc openNamespaceNim(): Rope = - result.add("namespace Nim {" & tnl) + result.add("namespace Nim {\L") proc closeNamespaceNim(): Rope = - result.add("}" & tnl) + result.add("}\L") proc closureSetup(p: BProc, prc: PSym) = if tfCapturesEnv notin prc.typ.flags: return @@ -727,7 +723,7 @@ proc genProcAux(m: BModule, prc: PSym) = internalError(m.config, prc.info, "proc has no result symbol") let resNode = prc.ast.sons[resultPos] let res = resNode.sym # get result symbol - if not isInvalidReturnType(prc.typ.sons[0]): + if not isInvalidReturnType(m.config, prc.typ.sons[0]): if sfNoInit in prc.flags: incl(res.flags, sfNoInit) if sfNoInit in prc.flags and p.module.compileToCpp and (let val = easyResultAsgn(prc.getBody); val != nil): var decl = localVarDecl(p, resNode) @@ -741,7 +737,7 @@ proc genProcAux(m: BModule, prc: PSym) = initLocalVar(p, res, immediateAsgn=false) returnStmt = ropecg(p.module, "\treturn $1;$n", rdLoc(res.loc)) else: - fillResult(resNode) + fillResult(p.config, resNode) assignParam(p, res) if sfNoInit notin prc.flags: resetLoc(p, res.loc) if skipTypes(res.typ, abstractInst).kind == tyArray: @@ -756,10 +752,10 @@ proc genProcAux(m: BModule, prc: PSym) = genStmts(p, prc.getBody) # modifies p.locals, p.init, etc. var generatedProc: Rope if sfNoReturn in prc.flags: - if hasDeclspec in extccomp.CC[extccomp.cCompiler].props: + if hasDeclspec in extccomp.CC[p.config.cCompiler].props: header = "__declspec(noreturn) " & header if sfPure in prc.flags: - if hasDeclspec in extccomp.CC[extccomp.cCompiler].props: + if hasDeclspec in extccomp.CC[p.config.cCompiler].props: header = "__declspec(naked) " & header generatedProc = ropecg(p.module, "$N$1 {$n$2$3$4}$N$N", header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)) @@ -803,13 +799,13 @@ proc genProcPrototype(m: BModule, sym: PSym) = getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym))) elif not containsOrIncl(m.declaredProtos, sym.id): var header = genProcHeader(m, sym) - if sfNoReturn in sym.flags and hasDeclspec in extccomp.CC[cCompiler].props: + if sfNoReturn in sym.flags and hasDeclspec in extccomp.CC[m.config.cCompiler].props: header = "__declspec(noreturn) " & header if sym.typ.callConv != ccInline and requiresExternC(m, sym): header = "extern \"C\" " & header - if sfPure in sym.flags and hasAttribute in CC[cCompiler].props: + if sfPure in sym.flags and hasAttribute in CC[m.config.cCompiler].props: header.add(" __attribute__((naked))") - if sfNoReturn in sym.flags and hasAttribute in CC[cCompiler].props: + if sfNoReturn in sym.flags and hasAttribute in CC[m.config.cCompiler].props: header.add(" __attribute__((noreturn))") add(m.s[cfsProcHeaders], ropecg(m, "$1;$n", header)) @@ -918,10 +914,10 @@ proc genVarPrototype(m: BModule, n: PNode) = addf(m.s[cfsVars], " $1;$n", [sym.loc.r]) proc addIntTypes(result: var Rope; conf: ConfigRef) {.inline.} = - addf(result, "#define NIM_NEW_MANGLING_RULES" & tnl & - "#define NIM_INTBITS $1" & tnl, [ - platform.CPU[targetCPU].intSize.rope]) - if optUseNimNamespace in conf.globalOptions: result.add("#define USE_NIM_NAMESPACE" & tnl) + addf(result, "#define NIM_NEW_MANGLING_RULES\L" & + "#define NIM_INTBITS $1\L", [ + platform.CPU[conf.target.targetCPU].intSize.rope]) + if optUseNimNamespace in conf.globalOptions: result.add("#define USE_NIM_NAMESPACE\L") proc getCopyright(conf: ConfigRef; cfile: Cfile): Rope = if optCompileOnly in conf.globalOptions: @@ -936,9 +932,9 @@ proc getCopyright(conf: ConfigRef; cfile: Cfile): Rope = "/* Compiled for: $2, $3, $4 */$N" & "/* Command for C compiler:$n $5 */$N") % [rope(VersionAsString), - rope(platform.OS[targetOS].name), - rope(platform.CPU[targetCPU].name), - rope(extccomp.CC[extccomp.cCompiler].name), + rope(platform.OS[conf.target.targetOS].name), + rope(platform.CPU[conf.target.targetCPU].name), + rope(extccomp.CC[conf.cCompiler].name), rope(getCompileCFileCmd(conf, cfile))] proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope = @@ -948,8 +944,8 @@ proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope = proc genFilenames(m: BModule): Rope = discard cgsym(m, "dbgRegisterFilename") result = nil - for i in 0..<fileInfos.len: - result.addf("dbgRegisterFilename($1);$N", [fileInfos[i].projPath.makeCString]) + for i in 0..<m.config.m.fileInfos.len: + result.addf("dbgRegisterFilename($1);$N", [m.config.m.fileInfos[i].projPath.makeCString]) proc genMainProc(m: BModule) = const @@ -1052,7 +1048,7 @@ proc genMainProc(m: BModule) = "}$N$N" var nimMain, otherMain: FormatStr - if platform.targetOS == osWindows and + if m.config.target.targetOS == osWindows and m.config.globalOptions * {optGenGuiApp, optGenDynLib} != {}: if optGenGuiApp in m.config.globalOptions: nimMain = WinNimMain @@ -1061,14 +1057,14 @@ proc genMainProc(m: BModule) = nimMain = WinNimDllMain otherMain = WinCDllMain m.includeHeader("<windows.h>") - elif platform.targetOS == osGenode: + elif m.config.target.targetOS == osGenode: nimMain = GenodeNimMain otherMain = ComponentConstruct m.includeHeader("<libc/component.h>") elif optGenDynLib in m.config.globalOptions: nimMain = PosixNimDllMain otherMain = PosixCDllMain - elif platform.targetOS == osStandalone: + elif m.config.target.targetOS == osStandalone: nimMain = PosixNimMain otherMain = StandaloneCMain else: @@ -1079,12 +1075,12 @@ proc genMainProc(m: BModule) = m.g.breakpoints.add(m.genFilenames) let initStackBottomCall = - if platform.targetOS == osStandalone or m.config.selectedGC == gcNone: "".rope + if m.config.target.targetOS == osStandalone or m.config.selectedGC == gcNone: "".rope else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N") inc(m.labels) appcg(m, m.s[cfsProcs], PreMainBody, [ m.g.mainDatInit, m.g.breakpoints, m.g.otherModsInit, - if emulatedThreadVars(m.config) and platform.targetOS != osStandalone: + if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone: ropecg(m, "\t#initThreadVarsEmulation();$N") else: "".rope, @@ -1094,7 +1090,7 @@ proc genMainProc(m: BModule) = [m.g.mainModInit, initStackBottomCall, rope(m.labels)]) if optNoMain notin m.config.globalOptions: if optUseNimNamespace in m.config.globalOptions: - m.s[cfsProcs].add closeNamespaceNim() & "using namespace Nim;" & tnl + m.s[cfsProcs].add closeNamespaceNim() & "using namespace Nim;\L" appcg(m, m.s[cfsProcs], otherMain, []) if optUseNimNamespace in m.config.globalOptions: m.s[cfsProcs].add openNamespaceNim() @@ -1255,7 +1251,7 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: string): BModule = excl(result.postInitProc.options, optStackTrace) let ndiName = if optCDebug in g.config.globalOptions: changeFileExt(completeCFilePath(g.config, filename), "ndi") else: "" - open(result.ndi, ndiName) + open(result.ndi, ndiName, g.config) proc nullify[T](arr: var T) = for i in low(arr)..high(arr): @@ -1305,13 +1301,15 @@ proc resetModule*(m: BModule) = proc resetCgenModules*(g: BModuleList) = for m in cgenModules(g): resetModule(m) -proc rawNewModule(g: BModuleList; module: PSym): BModule = - result = rawNewModule(g, module, module.position.FileIndex.toFullPath) +proc rawNewModule(g: BModuleList; module: PSym; conf: ConfigRef): BModule = + result = rawNewModule(g, module, toFullPath(conf, module.position.FileIndex)) -proc newModule(g: BModuleList; module: PSym): BModule = +proc newModule(g: BModuleList; module: PSym; conf: ConfigRef): BModule = # we should create only one cgen module for each module sym - result = rawNewModule(g, module) - growCache g.modules, module.position + result = rawNewModule(g, module, conf) + if module.position >= g.modules.len: + setLen(g.modules, module.position + 1) + #growCache g.modules, module.position g.modules[module.position] = result template injectG() {.dirty.} = @@ -1319,9 +1317,9 @@ template injectG() {.dirty.} = graph.backend = newModuleList(graph) let g = BModuleList(graph.backend) -proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = +proc myOpen(graph: ModuleGraph; module: PSym): PPassContext = injectG() - result = newModule(g, module) + result = newModule(g, module, graph.config) if optGenIndex in graph.config.globalOptions and g.generatedHeader == nil: let f = if graph.config.headerFile.len > 0: graph.config.headerFile else: graph.config.projectFull @@ -1353,7 +1351,8 @@ proc writeHeader(m: BModule) = result.addf("N_CDECL(void, NimMain)(void);$n", []) if optUseNimNamespace in m.config.globalOptions: result.add closeNamespaceNim() result.addf("#endif /* $1 */$n", [guard]) - writeRope(result, m.filename) + if not writeRope(result, m.filename): + rawMessage(m.config, errCannotOpenFile, m.filename) proc getCFile(m: BModule): string = let ext = @@ -1362,11 +1361,12 @@ proc getCFile(m: BModule): string = else: ".c" result = changeFileExt(completeCFilePath(m.config, withPackageName(m.config, m.cfilename)), ext) -proc myOpenCached(graph: ModuleGraph; module: PSym, rd: PRodReader): PPassContext = - injectG() - var m = newModule(g, module) - readMergeInfo(getCFile(m), m) - result = m +when false: + proc myOpenCached(graph: ModuleGraph; module: PSym, rd: PRodReader): PPassContext = + injectG() + var m = newModule(g, module, graph.config) + readMergeInfo(getCFile(m), m) + result = m proc myProcess(b: PPassContext, n: PNode): PNode = result = n @@ -1374,7 +1374,8 @@ proc myProcess(b: PPassContext, n: PNode): PNode = var m = BModule(b) if passes.skipCodegen(m.config, n): return m.initProc.options = initProcOptions(m) - softRnl = if optLineDir in m.config.options: noRnl else: rnl + #softRnl = if optLineDir in m.config.options: noRnl else: rnl + # XXX replicate this logic! genStmts(m.initProc, n) proc finishModule(m: BModule) = @@ -1401,12 +1402,14 @@ proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool = echo "diff ", cfile.cname, ".backup ", cfile.cname else: echo "new file ", cfile.cname - writeRope(code, cfile.cname) + if not writeRope(code, cfile.cname): + rawMessage(m.config, errCannotOpenFile, cfile.cname) return if existsFile(cfile.obj) and os.fileNewer(cfile.obj, cfile.cname): result = false else: - writeRope(code, cfile.cname) + if not writeRope(code, cfile.cname): + rawMessage(m.config, errCannotOpenFile, cfile.cname) # We need 2 different logics here: pending modules (including # 'nim__dat') may require file merging for the combination of dead code @@ -1418,7 +1421,7 @@ proc writeModule(m: BModule, pending: bool) = # generate code for the init statements of the module: let cfile = getCFile(m) - if m.rd == nil or optForceFullMake in m.config.globalOptions: + if true or optForceFullMake in m.config.globalOptions: genInitCode(m) finishTypeDescriptions(m) if sfMainModule in m.module.flags: @@ -1441,7 +1444,8 @@ proc writeModule(m: BModule, pending: bool) = genInitCode(m) finishTypeDescriptions(m) var code = genModule(m, cf) - writeRope(code, cfile) + if not writeRope(code, cfile): + rawMessage(m.config, errCannotOpenFile, cfile) addFileToCompile(m.config, cf) else: # Consider: first compilation compiles ``system.nim`` and produces @@ -1462,7 +1466,8 @@ proc updateCachedModule(m: BModule) = finishTypeDescriptions(m) var code = genModule(m, cf) - writeRope(code, cfile) + if not writeRope(code, cfile): + rawMessage(m.config, errCannotOpenFile, cfile) else: cf.flags = {CfileFlag.Cached} addFileToCompile(m.config, cf) @@ -1475,7 +1480,6 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = # if the module is cached, we don't regenerate the main proc # nor the dispatchers? But if the dispatchers changed? # XXX emit the dispatchers into its own .c file? - if b.rd != nil: return if n != nil: m.initProc.options = initProcOptions(m) genStmts(m.initProc, n) @@ -1498,14 +1502,10 @@ proc cgenWriteModules*(backend: RootRef, config: ConfigRef) = if g.generatedHeader != nil: finishModule(g.generatedHeader) while g.forwardedProcsCounter > 0: for m in cgenModules(g): - if m.rd == nil: - finishModule(m) + finishModule(m) for m in cgenModules(g): - if m.rd != nil: - m.updateCachedModule - else: - m.writeModule(pending=true) + m.writeModule(pending=true) writeMapping(config, g.mapping) if g.generatedHeader != nil: writeHeader(g.generatedHeader) -const cgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose) +const cgenPass* = makePass(myOpen, myProcess, myClose) diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 843677654..a526a0f00 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -11,9 +11,8 @@ import ast, astalgo, ropes, passes, options, intsets, platform, sighashes, - tables, ndi + tables, ndi, lineinfos -from msgs import TLineInfo from modulegraphs import ModuleGraph type @@ -122,6 +121,17 @@ type graph*: ModuleGraph strVersion*, seqVersion*: int # version of the string/seq implementation to use + nimtv*: Rope # Nim thread vars; the struct body + nimtvDeps*: seq[PType] # type deps: every module needs whole struct + nimtvDeclared*: IntSet # so that every var/field exists only once + # in the struct + # 'nimtv' is incredibly hard to modularize! Best + # effort is to store all thread vars in a ROD + # section and with their type deps and load them + # unconditionally... + # nimtvDeps is VERY hard to cache because it's + # not a list of IDs nor can it be made to be one. + TCGen = object of TPassContext # represents a C source file s*: TCFileSections # sections of the C file flags*: set[Codegenflag] @@ -180,7 +190,7 @@ proc newProc*(prc: PSym, module: BModule): BProc = proc newModuleList*(g: ModuleGraph): BModuleList = BModuleList(modules: @[], typeInfoMarker: initTable[SigHash, Rope](), config: g.config, - graph: g) + graph: g, nimtvDeps: @[], nimtvDeclared: initIntSet()) iterator cgenModules*(g: BModuleList): BModule = for i in 0..high(g.modules): diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim index 1d72952e2..5b58e6498 100644 --- a/compiler/cgmeth.nim +++ b/compiler/cgmeth.nim @@ -11,7 +11,7 @@ import intsets, options, ast, astalgo, msgs, idents, renderer, types, magicsys, - sempass2, strutils, modulegraphs, configuration + sempass2, strutils, modulegraphs, lineinfos proc genConv(n: PNode, d: PType, downcast: bool; conf: ConfigRef): PNode = var dest = skipTypes(d, abstractPtrs) @@ -115,7 +115,7 @@ proc createDispatcher(s: PSym): PSym = # we can't inline the dispatcher itself (for now): if disp.typ.callConv == ccInline: disp.typ.callConv = ccDefault disp.ast = copyTree(s.ast) - disp.ast.sons[bodyPos] = ast.emptyNode + disp.ast.sons[bodyPos] = newNodeI(nkEmpty, s.info) disp.loc.r = nil if s.typ.sons[0] != nil: if disp.ast.sonsLen > resultPos: @@ -124,7 +124,7 @@ proc createDispatcher(s: PSym): PSym = # We've encountered a method prototype without a filled-in # resultPos slot. We put a placeholder in there that will # be updated in fixupDispatcher(). - disp.ast.addSon(ast.emptyNode) + disp.ast.addSon(newNodeI(nkEmpty, s.info)) attachDispatcher(s, newSymNode(disp)) # attach to itself to prevent bugs: attachDispatcher(disp, newSymNode(disp)) @@ -137,7 +137,7 @@ proc fixupDispatcher(meth, disp: PSym; conf: ConfigRef) = # the lock level of the dispatcher needs to be updated/checked # against that of the method. if disp.ast.sonsLen > resultPos and meth.ast.sonsLen > resultPos and - disp.ast.sons[resultPos] == ast.emptyNode: + disp.ast.sons[resultPos].kind == nkEmpty: disp.ast.sons[resultPos] = copyTree(meth.ast.sons[resultPos]) # The following code works only with lock levels, so we disable @@ -184,7 +184,7 @@ proc methodDef*(g: ModuleGraph; s: PSym, fromCache: bool) = # internalError(s.info, "no method dispatcher found") if witness != nil: localError(g.config, s.info, "invalid declaration order; cannot attach '" & s.name.s & - "' to method defined here: " & $witness.info) + "' to method defined here: " & g.config$witness.info) elif sfBase notin s.flags: message(g.config, s.info, warnUseBase) diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim index 5568fd37b..6fa856b2f 100644 --- a/compiler/closureiters.nim +++ b/compiler/closureiters.nim @@ -1,7 +1,7 @@ # # # The Nim Compiler -# (c) Copyright 2018 Andreas Rumpf +# (c) Copyright 2018 Nim Contributors # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -132,7 +132,7 @@ import intsets, strutils, options, ast, astalgo, trees, treetab, msgs, idents, - renderer, types, magicsys, rodread, lowerings, lambdalifting, modulegraphs + renderer, types, magicsys, lowerings, lambdalifting, modulegraphs, lineinfos type Ctx = object @@ -177,7 +177,7 @@ proc newStateAssgn(ctx: var Ctx, stateNo: int = -2): PNode = ctx.newStateAssgn(newIntTypeNode(nkIntLit, stateNo, ctx.g.getSysType(TLineInfo(), tyInt))) proc newEnvVar(ctx: var Ctx, name: string, typ: PType): PSym = - result = newSym(skVar, getIdent(name), ctx.fn, ctx.fn.info) + result = newSym(skVar, getIdent(ctx.g.cache, name), ctx.fn, ctx.fn.info) result.typ = typ assert(not typ.isNil) @@ -190,7 +190,7 @@ proc newEnvVar(ctx: var Ctx, name: string, typ: PType): PSym = else: let envParam = getEnvParam(ctx.fn) # let obj = envParam.typ.lastSon - result = addUniqueField(envParam.typ.lastSon, result) + result = addUniqueField(envParam.typ.lastSon, result, ctx.g.cache) proc newEnvVarAccess(ctx: Ctx, s: PSym): PNode = if ctx.stateVarSym.isNil: @@ -210,7 +210,7 @@ proc newUnrollFinallyAccess(ctx: var Ctx, info: TLineInfo): PNode = proc newCurExcAccess(ctx: var Ctx): PNode = if ctx.curExcSym.isNil: - ctx.curExcSym = ctx.newEnvVar(":curExc", ctx.g.callCodegenProc("getCurrentException", emptyNode).typ) + ctx.curExcSym = ctx.newEnvVar(":curExc", ctx.g.callCodegenProc("getCurrentException", ctx.g.emptyNode).typ) ctx.newEnvVarAccess(ctx.curExcSym) proc newState(ctx: var Ctx, n, gotoOut: PNode): int = @@ -324,7 +324,7 @@ proc collectExceptState(ctx: var Ctx, n: PNode): PNode {.inline.} = assert(c[i].kind == nkType) let nextCond = newTree(nkCall, newSymNode(g.getSysMagic(c.info, "of", mOf)), - g.callCodegenProc("getCurrentException", emptyNode), + g.callCodegenProc("getCurrentException", ctx.g.emptyNode), c[i]) nextCond.typ = ctx.g.getSysType(c.info, tyBool) nextCond.info = c.info @@ -349,7 +349,7 @@ proc collectExceptState(ctx: var Ctx, n: PNode): PNode {.inline.} = if ifStmt.len != 0: result = newTree(nkStmtList, ctx.newNullifyCurExc(n.info), ifStmt) else: - result = emptyNode + result = ctx.g.emptyNode proc addElseToExcept(ctx: var Ctx, n: PNode) = if n.kind == nkStmtList and n[1].kind == nkIfStmt and n[1][^1].kind != nkElse: @@ -364,7 +364,7 @@ proc addElseToExcept(ctx: var Ctx, n: PNode) = block: # :curExc = getCurrentException() branchBody.add(newTree(nkAsgn, ctx.newCurExcAccess(), - ctx.g.callCodegenProc("getCurrentException", emptyNode))) + ctx.g.callCodegenProc("getCurrentException", ctx.g.emptyNode))) block: # goto nearestFinally branchBody.add(newTree(nkGotoState, ctx.g.newIntLit(n.info, ctx.nearestFinally))) @@ -372,12 +372,12 @@ proc addElseToExcept(ctx: var Ctx, n: PNode) = let elseBranch = newTree(nkElse, branchBody) n[1].add(elseBranch) -proc getFinallyNode(n: PNode): PNode = +proc getFinallyNode(ctx: var Ctx, n: PNode): PNode = result = n[^1] if result.kind == nkFinally: result = result[0] else: - result = emptyNode + result = ctx.g.emptyNode proc hasYieldsInExpressions(n: PNode): bool = case n.kind @@ -725,7 +725,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = if condNeedsSplit: let (st, ex) = exprToStmtList(n[0]) - let brk = newTree(nkBreakStmt, emptyNode) + let brk = newTree(nkBreakStmt, ctx.g.emptyNode) let branch = newTree(nkElifBranch, ctx.g.newNotCall(ex), brk) let check = newTree(nkIfStmt, branch) let newBody = newTree(nkStmtList, st, check, n[1]) @@ -777,14 +777,14 @@ proc newEndFinallyNode(ctx: var Ctx, info: TLineInfo): PNode = cmp.typ = ctx.g.getSysType(info, tyBool) let asgn = newTree(nkFastAsgn, - newSymNode(getClosureIterResult(ctx.fn), info), + newSymNode(getClosureIterResult(ctx.g, ctx.fn), info), ctx.newTmpResultAccess()) let retStmt = newTree(nkReturnStmt, asgn) let branch = newTree(nkElifBranch, cmp, retStmt) # The C++ backend requires `getCurrentException` here. - let raiseStmt = newTree(nkRaiseStmt, ctx.g.callCodegenProc("getCurrentException", emptyNode)) + let raiseStmt = newTree(nkRaiseStmt, ctx.g.callCodegenProc("getCurrentException", ctx.g.emptyNode)) raiseStmt.info = info let elseBranch = newTree(nkElse, raiseStmt) @@ -922,7 +922,7 @@ proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode result = newNodeI(nkGotoState, n.info) var tryBody = toStmtList(n[0]) var exceptBody = ctx.collectExceptState(n) - var finallyBody = newTree(nkStmtList, getFinallyNode(n)) + var finallyBody = newTree(nkStmtList, getFinallyNode(ctx, n)) finallyBody = ctx.transformReturnsInTry(finallyBody) finallyBody.add(ctx.newEndFinallyNode(finallyBody.info)) @@ -1023,11 +1023,11 @@ proc tranformStateAssignments(ctx: var Ctx, n: PNode): PNode = if n[0].sons[0].kind != nkEmpty: var a = newNodeI(nkAsgn, n[0].sons[0].info) var retVal = n[0].sons[0] #liftCapturedVars(n.sons[0], owner, d, c) - addSon(a, newSymNode(getClosureIterResult(ctx.fn))) + addSon(a, newSymNode(getClosureIterResult(ctx.g, ctx.fn))) addSon(a, retVal) retStmt.add(a) else: - retStmt.add(emptyNode) + retStmt.add(ctx.g.emptyNode) result.add(retStmt) else: @@ -1054,10 +1054,10 @@ proc tranformStateAssignments(ctx: var Ctx, n: PNode): PNode = for i in 0 ..< n.len: n[i] = ctx.tranformStateAssignments(n[i]) -proc skipStmtList(n: PNode): PNode = +proc skipStmtList(ctx: Ctx; n: PNode): PNode = result = n while result.kind in {nkStmtList}: - if result.len == 0: return emptyNode + if result.len == 0: return ctx.g.emptyNode result = result[0] proc skipEmptyStates(ctx: Ctx, stateIdx: int): int = @@ -1072,7 +1072,7 @@ proc skipEmptyStates(ctx: Ctx, stateIdx: int): int = if label == -1: newLabel = ctx.exitStateIdx else: - let fs = ctx.states[label][1].skipStmtList() + let fs = skipStmtList(ctx, ctx.states[label][1]) if fs.kind == nkGotoState: newLabel = fs[0].intVal.int if label == newLabel: break @@ -1148,7 +1148,7 @@ proc newCatchBody(ctx: var Ctx, info: TLineInfo): PNode {.inline.} = newIntTypeNode(nkIntLit, 0, intTyp)) cond.typ = boolTyp - let raiseStmt = newTree(nkRaiseStmt, emptyNode) + let raiseStmt = newTree(nkRaiseStmt, ctx.g.emptyNode) let ifBranch = newTree(nkElifBranch, cond, raiseStmt) let ifStmt = newTree(nkIfStmt, ifBranch) result.add(ifStmt) @@ -1185,7 +1185,7 @@ proc newCatchBody(ctx: var Ctx, info: TLineInfo): PNode {.inline.} = block: result.add(newTree(nkAsgn, ctx.newCurExcAccess(), - ctx.g.callCodegenProc("getCurrentException", emptyNode))) + ctx.g.callCodegenProc("getCurrentException", ctx.g.emptyNode))) proc wrapIntoTryExcept(ctx: var Ctx, n: PNode): PNode {.inline.} = let setupExc = newTree(nkCall, @@ -1235,7 +1235,7 @@ proc deleteEmptyStates(ctx: var Ctx) = # Apply new state indexes and mark unused states with -1 var iValid = 0 for i, s in ctx.states: - let body = s[1].skipStmtList() + let body = skipStmtList(ctx, s[1]) if body.kind == nkGotoState and i != ctx.states.len - 1 and i != 0: # This is an empty state. Mark with -1. s[0].intVal = -1 @@ -1244,7 +1244,7 @@ proc deleteEmptyStates(ctx: var Ctx) = inc iValid for i, s in ctx.states: - let body = s[1].skipStmtList() + let body = skipStmtList(ctx, s[1]) if body.kind != nkGotoState or i == 0: discard ctx.skipThroughEmptyStates(s) let excHandlState = ctx.exceptionTable[i] @@ -1255,7 +1255,7 @@ proc deleteEmptyStates(ctx: var Ctx) = var i = 0 while i < ctx.states.len - 1: - let fs = ctx.states[i][1].skipStmtList() + let fs = skipStmtList(ctx, ctx.states[i][1]) if fs.kind == nkGotoState and i != 0: ctx.states.delete(i) ctx.exceptionTable.delete(i) @@ -1271,10 +1271,10 @@ proc transformClosureIterator*(g: ModuleGraph; fn: PSym, n: PNode): PNode = # Lambda lifting was not done yet. Use temporary :state sym, which # be handled specially by lambda lifting. Local temp vars (if needed) # should folllow the same logic. - ctx.stateVarSym = newSym(skVar, getIdent(":state"), fn, fn.info) + ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), fn, fn.info) ctx.stateVarSym.typ = g.createClosureIterStateType(fn) - ctx.stateLoopLabel = newSym(skLabel, getIdent(":stateLoop"), fn, fn.info) + ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), fn, fn.info) let n = n.toStmtList discard ctx.newState(n, nil) diff --git a/compiler/commands.nim b/compiler/commands.nim index 09f63f0f5..866405f9f 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -26,7 +26,7 @@ bootSwitch(usedNoGC, defined(nogc), "--gc:none") import os, msgs, options, nversion, condsyms, strutils, extccomp, platform, - wordrecg, parseutils, nimblecmd, idents, parseopt, sequtils, configuration + wordrecg, parseutils, nimblecmd, idents, parseopt, sequtils, lineinfos # but some have deps to imported modules. Yay. bootSwitch(usedTinyC, hasTinyCBackend, "-d:tinyc") @@ -56,21 +56,21 @@ const x AdvancedUsage = slurp"../doc/advopt.txt".replace("//", "") % FeatureDesc -proc getCommandLineDesc(): string = - result = (HelpMessage % [VersionAsString, platform.OS[platform.hostOS].name, - CPU[platform.hostCPU].name, CompileDate]) & +proc getCommandLineDesc(conf: ConfigRef): string = + result = (HelpMessage % [VersionAsString, platform.OS[conf.target.hostOS].name, + CPU[conf.target.hostCPU].name, CompileDate]) & Usage proc helpOnError(conf: ConfigRef; pass: TCmdLinePass) = if pass == passCmd1: - msgWriteln(conf, getCommandLineDesc(), {msgStdout}) + msgWriteln(conf, getCommandLineDesc(conf), {msgStdout}) msgQuit(0) proc writeAdvancedUsage(conf: ConfigRef; pass: TCmdLinePass) = if pass == passCmd1: msgWriteln(conf, (HelpMessage % [VersionAsString, - platform.OS[platform.hostOS].name, - CPU[platform.hostCPU].name, CompileDate]) & + platform.OS[conf.target.hostOS].name, + CPU[conf.target.hostCPU].name, CompileDate]) & AdvancedUsage, {msgStdout}) msgQuit(0) @@ -78,8 +78,8 @@ proc writeAdvancedUsage(conf: ConfigRef; pass: TCmdLinePass) = proc writeFullhelp(conf: ConfigRef; pass: TCmdLinePass) = if pass == passCmd1: msgWriteln(conf, `%`(HelpMessage, [VersionAsString, - platform.OS[platform.hostOS].name, - CPU[platform.hostCPU].name, CompileDate]) & + platform.OS[conf.target.hostOS].name, + CPU[conf.target.hostCPU].name, CompileDate]) & Usage & AdvancedUsage, {msgStdout}) msgQuit(0) @@ -87,8 +87,8 @@ proc writeFullhelp(conf: ConfigRef; pass: TCmdLinePass) = proc writeVersionInfo(conf: ConfigRef; pass: TCmdLinePass) = if pass == passCmd1: msgWriteln(conf, `%`(HelpMessage, [VersionAsString, - platform.OS[platform.hostOS].name, - CPU[platform.hostCPU].name, CompileDate]), + platform.OS[conf.target.hostOS].name, + CPU[conf.target.hostCPU].name, CompileDate]), {msgStdout}) const gitHash = gorge("git log -n 1 --format=%H").strip @@ -103,7 +103,7 @@ proc writeVersionInfo(conf: ConfigRef; pass: TCmdLinePass) = proc writeCommandLineUsage*(conf: ConfigRef; helpWritten: var bool) = if not helpWritten: - msgWriteln(conf, getCommandLineDesc(), {msgStdout}) + msgWriteln(conf, getCommandLineDesc(conf), {msgStdout}) helpWritten = true proc addPrefix(switch: string): string = @@ -178,11 +178,11 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass, if i < len(arg) and (arg[i] in {':', '='}): inc(i) else: invalidCmdLineOption(conf, pass, orig, info) if state == wHint: - let x = findStr(configuration.HintsToStr, id) + let x = findStr(lineinfos.HintsToStr, id) if x >= 0: n = TNoteKind(x + ord(hintMin)) else: localError(conf, info, "unknown hint: " & id) else: - let x = findStr(configuration.WarningsToStr, id) + let x = findStr(lineinfos.WarningsToStr, id) if x >= 0: n = TNoteKind(x + ord(warnMin)) else: localError(conf, info, "unknown warning: " & id) case substr(arg, i).normalize @@ -289,14 +289,14 @@ proc processPath(conf: ConfigRef; path: string, info: TLineInfo, else: conf.projectPath / path try: - result = pathSubs(conf, p, info.toFullPath().splitFile().dir) + result = pathSubs(conf, p, toFullPath(conf, info).splitFile().dir) except ValueError: localError(conf, info, "invalid path: " & p) result = p proc processCfgPath(conf: ConfigRef; path: string, info: TLineInfo): string = let path = if path[0] == '"': strutils.unescape(path) else: path - let basedir = info.toFullPath().splitFile().dir + let basedir = toFullPath(conf, info).splitFile().dir let p = if os.isAbsolute(path) or '$' in path: path else: @@ -322,9 +322,9 @@ proc trackDirty(conf: ConfigRef; arg: string, info: TLineInfo) = let dirtyOriginalIdx = fileInfoIdx(conf, a[1]) if dirtyOriginalIdx.int32 >= 0: - msgs.setDirtyFile(dirtyOriginalIdx, a[0]) + msgs.setDirtyFile(conf, dirtyOriginalIdx, a[0]) - gTrackPos = newLineInfo(dirtyOriginalIdx, line, column) + conf.m.trackPos = newLineInfo(dirtyOriginalIdx, line, column) proc track(conf: ConfigRef; arg: string, info: TLineInfo) = var a = arg.split(',') @@ -334,7 +334,7 @@ proc track(conf: ConfigRef; arg: string, info: TLineInfo) = localError(conf, info, errInvalidNumber % a[1]) if parseUtils.parseInt(a[2], column) <= 0: localError(conf, info, errInvalidNumber % a[2]) - gTrackPos = newLineInfo(conf, a[0], line, column) + conf.m.trackPos = newLineInfo(conf, a[0], line, column) proc dynlibOverride(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = if pass in {passCmd2, passPP}: @@ -344,8 +344,6 @@ proc dynlibOverride(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, in proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; conf: ConfigRef) = var - theOS: TSystemOS - cpu: TSystemCPU key, val: string case switch.normalize of "path", "p": @@ -565,13 +563,13 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; if pass in {passCmd2, passPP}: extccomp.addLinkOptionCmd(conf, arg) of "cincludes": expectArg(conf, switch, arg, pass, info) - if pass in {passCmd2, passPP}: cIncludes.add processPath(conf, arg, info) + if pass in {passCmd2, passPP}: conf.cIncludes.add processPath(conf, arg, info) of "clibdir": expectArg(conf, switch, arg, pass, info) - if pass in {passCmd2, passPP}: cLibs.add processPath(conf, arg, info) + if pass in {passCmd2, passPP}: conf.cLibs.add processPath(conf, arg, info) of "clib": expectArg(conf, switch, arg, pass, info) - if pass in {passCmd2, passPP}: cLinkedLibs.add processPath(conf, arg, info) + if pass in {passCmd2, passPP}: conf.cLinkedLibs.add processPath(conf, arg, info) of "header": if conf != nil: conf.headerFile = arg incl(conf.globalOptions, optGenIndex) @@ -592,23 +590,26 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "os": expectArg(conf, switch, arg, pass, info) if pass in {passCmd1, passPP}: - theOS = platform.nameToOS(arg) + let theOS = platform.nameToOS(arg) if theOS == osNone: localError(conf, info, "unknown OS: '$1'" % arg) - elif theOS != platform.hostOS: - setTarget(theOS, targetCPU) + elif theOS != conf.target.hostOS: + setTarget(conf.target, theOS, conf.target.targetCPU) of "cpu": expectArg(conf, switch, arg, pass, info) if pass in {passCmd1, passPP}: - cpu = platform.nameToCPU(arg) + let cpu = platform.nameToCPU(arg) if cpu == cpuNone: localError(conf, info, "unknown CPU: '$1'" % arg) - elif cpu != platform.hostCPU: - setTarget(targetOS, cpu) + elif cpu != conf.target.hostCPU: + setTarget(conf.target, conf.target.targetOS, cpu) of "run", "r": expectNoArg(conf, switch, arg, pass, info) incl(conf.globalOptions, optRun) of "verbosity": expectArg(conf, switch, arg, pass, info) - conf.verbosity = parseInt(arg) + let verbosity = parseInt(arg) + if verbosity notin {0..3}: + localError(conf, info, "invalid verbosity level: '$1'" % arg) + conf.verbosity = verbosity conf.notes = NotesVerbosity[conf.verbosity] incl(conf.notes, conf.enableNotes) excl(conf.notes, conf.disableNotes) @@ -628,9 +629,9 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "help", "h": expectNoArg(conf, switch, arg, pass, info) helpOnError(conf, pass) - of "symbolfiles": + of "symbolfiles", "incremental": case arg.normalize - of "on": conf.symbolFiles = enabledSf + of "on": conf.symbolFiles = v2Sf of "off": conf.symbolFiles = disabledSf of "writeonly": conf.symbolFiles = writeOnlySf of "readonly": conf.symbolFiles = readOnlySf @@ -719,6 +720,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; doAssert(conf != nil) incl(conf.features, destructor) defineSymbol(conf.symbols, "nimNewRuntime") + of "nep1": + processOnOffSwitchG(conf, {optCheckNep1}, arg, pass, info) of "cppcompiletonamespace": expectNoArg(conf, switch, arg, pass, info) incl conf.globalOptions, optUseNimNamespace diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 773c0faf9..8b2d36722 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -44,6 +44,7 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimcomputedgoto") defineSymbol("nimunion") defineSymbol("nimnewshared") + defineSymbol("nimNewTypedesc") defineSymbol("nimrequiresnimframe") defineSymbol("nimparsebiggestfloatmagic") defineSymbol("nimalias") @@ -70,3 +71,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimNoNil") defineSymbol("nimNoZeroTerminator") defineSymbol("nimNotNil") + defineSymbol("nimVmExportFixed") diff --git a/compiler/configuration.nim b/compiler/configuration.nim index f9f0e623c..22e0b834e 100644 --- a/compiler/configuration.nim +++ b/compiler/configuration.nim @@ -1,360 +1,6 @@ -# -# -# The Nim Compiler -# (c) Copyright 2018 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# +## Use the module 'lineinfos' instead! -## This module contains the rather excessive configuration object that -## needs to be passed around to everything so that the compiler becomes -## more useful as a library. +{.deprecated.} -import tables - -const - explanationsBaseUrl* = "https://nim-lang.org/docs/manual" - -type - TMsgKind* = enum - errUnknown, errInternal, errIllFormedAstX, errCannotOpenFile, - errXExpected, - errGridTableNotImplemented, - errGeneralParseError, - errNewSectionExpected, - errInvalidDirectiveX, - errGenerated, - errUser, - warnCannotOpenFile, - warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, - warnDeprecated, warnConfigDeprecated, - warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel, - warnUnknownSubstitutionX, warnLanguageXNotSupported, - warnFieldXNotSupported, warnCommentXIgnored, - warnTypelessParam, - warnUseBase, warnWriteToForeignHeap, warnUnsafeCode, - warnEachIdentIsTuple, warnShadowIdent, - warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, - warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed, - warnInconsistentSpacing, warnUser, - hintSuccess, hintSuccessX, - hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, - hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, - hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath, - hintConditionAlwaysTrue, hintName, hintPattern, - hintExecuting, hintLinking, hintDependency, - hintSource, hintPerformance, hintStackTrace, hintGCStats, - hintUser, hintUserRaw - -const - MsgKindToStr*: array[TMsgKind, string] = [ - errUnknown: "unknown error", - errInternal: "internal error: $1", - errIllFormedAstX: "illformed AST: $1", - errCannotOpenFile: "cannot open '$1'", - errXExpected: "'$1' expected", - errGridTableNotImplemented: "grid table is not implemented", - errGeneralParseError: "general parse error", - errNewSectionExpected: "new section expected", - errInvalidDirectiveX: "invalid directive: '$1'", - errGenerated: "$1", - errUser: "$1", - warnCannotOpenFile: "cannot open '$1'", - warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored", - warnXIsNeverRead: "'$1' is never read", - warnXmightNotBeenInit: "'$1' might not have been initialized", - warnDeprecated: "$1 is deprecated", - warnConfigDeprecated: "config file '$1' is deprecated", - warnSmallLshouldNotBeUsed: "'l' should not be used as an identifier; may look like '1' (one)", - warnUnknownMagic: "unknown magic '$1' might crash the compiler", - warnRedefinitionOfLabel: "redefinition of label '$1'", - warnUnknownSubstitutionX: "unknown substitution '$1'", - warnLanguageXNotSupported: "language '$1' not supported", - warnFieldXNotSupported: "field '$1' not supported", - warnCommentXIgnored: "comment '$1' ignored", - warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template'", - warnUseBase: "use {.base.} for base methods; baseless methods are deprecated", - warnWriteToForeignHeap: "write to foreign heap", - warnUnsafeCode: "unsafe code: '$1'", - warnEachIdentIsTuple: "each identifier is a tuple", - warnShadowIdent: "shadowed identifier: '$1'", - warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future.", - warnProveField: "cannot prove that field '$1' is accessible", - warnProveIndex: "cannot prove index '$1' is valid", - warnGcUnsafe: "not GC-safe: '$1'", - warnGcUnsafe2: "$1", - warnUninit: "'$1' might not have been initialized", - warnGcMem: "'$1' uses GC'ed memory", - warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future.", - warnLockLevel: "$1", - warnResultShadowed: "Special variable 'result' is shadowed.", - warnInconsistentSpacing: "Number of spaces around '$#' is not consistent", - warnUser: "$1", - hintSuccess: "operation successful", - hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)", - hintLineTooLong: "line too long", - hintXDeclaredButNotUsed: "'$1' is declared but not used", - hintConvToBaseNotNeeded: "conversion to base object is not needed", - hintConvFromXtoItselfNotNeeded: "conversion from $1 to itself is pointless", - hintExprAlwaysX: "expression evaluates always to '$1'", - hintQuitCalled: "quit() called", - hintProcessing: "$1", - hintCodeBegin: "generated code listing:", - hintCodeEnd: "end of listing", - hintConf: "used config file '$1'", - hintPath: "added path: '$1'", - hintConditionAlwaysTrue: "condition is always true: '$1'", - hintName: "name should be: '$1'", - hintPattern: "$1", - hintExecuting: "$1", - hintLinking: "", - hintDependency: "$1", - hintSource: "$1", - hintPerformance: "$1", - hintStackTrace: "$1", - hintGCStats: "$1", - hintUser: "$1", - hintUserRaw: "$1"] - -const - WarningsToStr* = ["CannotOpenFile", "OctalEscape", - "XIsNeverRead", "XmightNotBeenInit", - "Deprecated", "ConfigDeprecated", - "SmallLshouldNotBeUsed", "UnknownMagic", - "RedefinitionOfLabel", "UnknownSubstitutionX", - "LanguageXNotSupported", "FieldXNotSupported", - "CommentXIgnored", - "TypelessParam", "UseBase", "WriteToForeignHeap", - "UnsafeCode", "EachIdentIsTuple", "ShadowIdent", - "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit", - "GcMem", "Destructor", "LockLevel", "ResultShadowed", - "Spacing", "User"] - - HintsToStr* = ["Success", "SuccessX", "LineTooLong", - "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", - "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf", - "Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency", - "Source", "Performance", "StackTrace", "GCStats", - "User", "UserRaw"] - -const - fatalMin* = errUnknown - fatalMax* = errInternal - errMin* = errUnknown - errMax* = errUser - warnMin* = warnCannotOpenFile - warnMax* = pred(hintSuccess) - hintMin* = hintSuccess - hintMax* = high(TMsgKind) - -static: - doAssert HintsToStr.len == ord(hintMax) - ord(hintMin) + 1 - doAssert WarningsToStr.len == ord(warnMax) - ord(warnMin) + 1 - -type - TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints - TNoteKinds* = set[TNoteKind] - -const - NotesVerbosity*: array[0..3, TNoteKinds] = [ - {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit, - warnProveField, warnProveIndex, - warnGcUnsafe, - hintSuccessX, hintPath, hintConf, - hintProcessing, hintPattern, - hintDependency, - hintExecuting, hintLinking, - hintCodeBegin, hintCodeEnd, - hintSource, hintStackTrace, - hintGCStats}, - {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit, - warnProveField, warnProveIndex, - warnGcUnsafe, - hintPath, - hintDependency, - hintCodeBegin, hintCodeEnd, - hintSource, hintStackTrace, - hintGCStats}, - {low(TNoteKind)..high(TNoteKind)} - {hintStackTrace, warnUninit}, - {low(TNoteKind)..high(TNoteKind)}] - -const - errXMustBeCompileTime* = "'$1' can only be used in compile-time context" - errArgsNeedRunOption* = "arguments can only be given if the '--run' option is selected" - -#[ -errStringLiteralExpected: "string literal expected", -errIntLiteralExpected: "integer literal expected", -errIdentifierExpected: "identifier expected, but found '$1'", -errNewlineExpected: "newline expected, but found '$1'", -errInvalidModuleName: "invalid module name: '$1'", -errOnOrOffExpected: "'on' or 'off' expected", -errNoneSpeedOrSizeExpected: "'none', 'speed' or 'size' expected", -errInvalidPragma: "invalid pragma", -errUnknownPragma: "unknown pragma: '$1'", -errAtPopWithoutPush: "'pop' without a 'push' pragma", -errEmptyAsm: "empty asm statement", -errInvalidIndentation: "invalid indentation", - -errNoReturnWithReturnTypeNotAllowed: "routines with NoReturn pragma are not allowed to have return type", -errAttemptToRedefine: , -errStmtInvalidAfterReturn: "statement not allowed after 'return', 'break', 'raise', 'continue' or proc call with noreturn pragma", -errStmtExpected: "statement expected", -errInvalidLabel: "'$1' is no label", -errInvalidCmdLineOption: "invalid command line option: '$1'", -errCmdLineArgExpected: "argument for command line option expected: '$1'", -errCmdLineNoArgExpected: "invalid argument for command line option: '$1'", -errInvalidVarSubstitution: "invalid variable substitution in '$1'", -errUnknownVar: "unknown variable: '$1'", -errUnknownCcompiler: "unknown C compiler: '$1'", -errOnOrOffExpectedButXFound: "'on' or 'off' expected, but '$1' found", -errOnOffOrListExpectedButXFound: "'on', 'off' or 'list' expected, but '$1' found", -errGenOutExpectedButXFound: "'c', 'c++' or 'yaml' expected, but '$1' found", -, -errInvalidMultipleAsgn: "multiple assignment is not allowed", -errColonOrEqualsExpected: "':' or '=' expected, but found '$1'", -errUndeclaredField: "undeclared field: '$1'", -errUndeclaredRoutine: "attempting to call undeclared routine: '$1'", -errUseQualifier: "ambiguous identifier: '$1' -- use a qualifier", -errTypeExpected: "type expected", -errSystemNeeds: "system module needs '$1'", -errExecutionOfProgramFailed: "execution of an external program failed: '$1'", -errNotOverloadable: , -errInvalidArgForX: "invalid argument for '$1'", -errStmtHasNoEffect: "statement has no effect", -, -errXExpectsArrayType: "'$1' expects an array type", -errIteratorCannotBeInstantiated: "'$1' cannot be instantiated because its body has not been compiled yet", -errExprXAmbiguous: "expression '$1' ambiguous in this context", -errConstantDivisionByZero: , -errOrdinalOrFloatTypeExpected: "ordinal or float type expected", -errOverOrUnderflow: , -errCannotEvalXBecauseIncompletelyDefined: , -errChrExpectsRange0_255: "'chr' expects an int in the range 0..255", -errDynlibRequiresExportc: "'dynlib' requires 'exportc'", -errNilAccess: "attempt to access a nil address", -errIndexOutOfBounds: "index out of bounds", -errIndexTypesDoNotMatch: "index types do not match", -errBracketsInvalidForType: "'[]' operator invalid for this type", -errValueOutOfSetBounds: "value out of set bounds", -errFieldNotInit: "field '$1' not initialized", -errExprXCannotBeCalled: "expression '$1' cannot be called", -errExprHasNoType: "expression has no type", -errExprXHasNoType:, -errCastNotInSafeMode: "'cast' not allowed in safe mode", -errExprCannotBeCastToX: , -errCommaOrParRiExpected: "',' or ')' expected", -errCurlyLeOrParLeExpected: "'{' or '(' expected", -errSectionExpected: "section ('type', 'proc', etc.) expected", -errRangeExpected: "range expected", -errMagicOnlyInSystem: "'magic' only allowed in system module", -errPowerOfTwoExpected: "power of two expected", -errStringMayNotBeEmpty: "string literal may not be empty", -errCallConvExpected: "calling convention expected", -errProcOnlyOneCallConv: "a proc can only have one calling convention", -errSymbolMustBeImported: "symbol must be imported if 'lib' pragma is used", -errExprMustBeBool: "expression must be of type 'bool'", -errConstExprExpected: "constant expression expected", -errDuplicateCaseLabel: "duplicate case label", -errRangeIsEmpty: "range is empty", -, -errSelectorMustBeOrdinal: "selector must be of an ordinal type", -errOrdXMustNotBeNegative: "ord($1) must not be negative", -errLenXinvalid: "len($1) must be less than 32768", -errTypeXhasUnknownSize: "type '$1' has unknown size", -errConstNeedsConstExpr: "a constant can only be initialized with a constant expression", -errConstNeedsValue: "a constant needs a value", -errResultCannotBeOpenArray: "the result type cannot be on open array", -errSizeTooBig: "computing the type's size produced an overflow", -errInheritanceOnlyWithEnums: "inheritance only works with an enum", -errIllegalRecursionInTypeX:, -errCannotInstantiateX: "cannot instantiate: '$1'", -errTypeMismatch: "type mismatch: got <", -errButExpected: "but expected one of: ", -errButExpectedX: "but expected '$1'", -errAmbiguousCallXYZ: "ambiguous call; both $1 and $2 match for: $3", -errWrongNumberOfArguments: "wrong number of arguments", -errWrongNumberOfArgumentsInCall: "wrong number of arguments in call to '$1'", -errMissingGenericParamsForTemplate: "'$1' has unspecified generic parameters", -errXCannotBePassedToProcVar: , -, -errImplOfXexpected: , - -errIllegalConvFromXtoY: , -errCannotBindXTwice: "cannot bind parameter '$1' twice", -errInvalidOrderInArrayConstructor: , -errInvalidOrderInEnumX: "invalid order in enum '$1'", -errEnumXHasHoles: "enum '$1' has holes", -errExceptExpected: "'except' or 'finally' expected", -errInvalidTry: "after catch all 'except' or 'finally' no section may follow", -errOptionExpected: , -errXisNoLabel: "'$1' is not a label", -errNotAllCasesCovered: "not all cases are covered", -errUnknownSubstitionVar: "unknown substitution variable: '$1'", -errComplexStmtRequiresInd: "complex statement requires indentation", -errXisNotCallable: "'$1' is not callable", -errNoPragmasAllowedForX: "no pragmas allowed for $1", -, -errInvalidParamKindX: "invalid param kind: '$1'", -errDefaultArgumentInvalid: "default argument invalid", -errNamedParamHasToBeIdent: "named parameter has to be an identifier", -errNoReturnTypeForX: "no return type allowed for $1", -errConvNeedsOneArg: "a type conversion needs exactly one argument", -errInvalidPragmaX: , -errXNotAllowedHere: "$1 not allowed here", -errXisNoType: "invalid type: '$1'", -errCircumNeedsPointer: "'[]' needs a pointer or reference type", -errInvalidExpression: "invalid expression", -errInvalidExpressionX: "invalid expression: '$1'", -errEnumHasNoValueX: "enum has no value '$1'", -, -errNoCommand: "no command given", -errInvalidCommandX: "invalid command: '$1'", -errXNeedsParamObjectType: , -errTemplateInstantiationTooNested: "template instantiation too nested, try --evalTemplateLimit:N", -errMacroInstantiationTooNested: "macro instantiation too nested, try --evalMacroLimit:N", -errInstantiationFrom: "template/generic instantiation from here", -errInvalidIndexValueForTuple: "invalid index value for tuple subscript", -errCommandExpectsFilename: "command expects a filename argument", -errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file", -errXExpected: "'$1' expected", -, -errCastToANonConcreteType: "cannot cast to a non concrete type: '$1'", -errInvalidSectionStart: "invalid section start", -errGridTableNotImplemented: "grid table is not implemented", -errGeneralParseError: "general parse error", -errNewSectionExpected: "new section expected", -errWhitespaceExpected: "whitespace expected, got '$1'", -errXisNoValidIndexFile: "'$1' is no valid index file", -errCannotRenderX: "cannot render reStructuredText element '$1'", -errVarVarTypeNotAllowed: , -errInstantiateXExplicitly: "instantiate '$1' explicitly", -errOnlyACallOpCanBeDelegator: , -errUsingNoSymbol: "'$1' is not a variable, constant or a proc name", -errMacroBodyDependsOnGenericTypes: "the macro body cannot be compiled, " & - "because the parameter '$1' has a generic type", -errDestructorNotGenericEnough: "Destructor signature is too specific. " & - "A destructor must be associated will all instantiations of a generic type", -errInlineIteratorsAsProcParams: "inline iterators can be used as parameters only for " & - "templates, macros and other inline iterators", -errXExpectsTwoArguments: "'$1' expects two arguments", -errXExpectsObjectTypes: "'$1' expects object types", -errXcanNeverBeOfThisSubtype: "'$1' can never be of this subtype", -errTooManyIterations: "interpretation requires too many iterations; " & - "if you are sure this is not a bug in your code edit " & - "compiler/vmdef.MaxLoopIterations and rebuild the compiler", -errFieldXNotFound: "field '$1' cannot be found", -errInvalidConversionFromTypeX: "invalid conversion from type '$1'", -errAssertionFailed: "assertion failed", -errCannotGenerateCodeForX: "cannot generate code for '$1'", -errXRequiresOneArgument: "$1 requires one parameter", -errUnhandledExceptionX: "unhandled exception: $1", -errCyclicTree: "macro returned a cyclic abstract syntax tree", -errXisNoMacroOrTemplate: "'$1' is no macro or template", -errXhasSideEffects: "'$1' can have side effects", -errWrongSymbolX:, -errIllegalCaptureX: "illegal capture '$1'", -errXCannotBeClosure: "'$1' cannot have 'closure' calling convention", -, -]# +import lineinfos +export lineinfos diff --git a/compiler/depends.nim b/compiler/depends.nim index 732404232..d0a1139ef 100644 --- a/compiler/depends.nim +++ b/compiler/depends.nim @@ -14,46 +14,51 @@ import from modulegraphs import ModuleGraph -proc generateDot*(project: string) - type TGen = object of TPassContext - module*: PSym + module: PSym config: ConfigRef + graph: ModuleGraph PGen = ref TGen -var gDotGraph: Rope # the generated DOT file; we need a global variable + Backend = ref object of RootRef + dotGraph: Rope -proc addDependencyAux(importing, imported: string) = - addf(gDotGraph, "$1 -> \"$2\";$n", [rope(importing), rope(imported)]) +proc addDependencyAux(b: Backend; importing, imported: string) = + addf(b.dotGraph, "$1 -> \"$2\";$n", [rope(importing), rope(imported)]) # s1 -> s2_4[label="[0-9]"]; proc addDotDependency(c: PPassContext, n: PNode): PNode = result = n - var g = PGen(c) + let g = PGen(c) + let b = Backend(g.graph.backend) case n.kind of nkImportStmt: for i in countup(0, sonsLen(n) - 1): var imported = getModuleName(g.config, n.sons[i]) - addDependencyAux(g.module.name.s, imported) + addDependencyAux(b, g.module.name.s, imported) of nkFromStmt, nkImportExceptStmt: var imported = getModuleName(g.config, n.sons[0]) - addDependencyAux(g.module.name.s, imported) + addDependencyAux(b, g.module.name.s, imported) of nkStmtList, nkBlockStmt, nkStmtListExpr, nkBlockExpr: for i in countup(0, sonsLen(n) - 1): discard addDotDependency(c, n.sons[i]) else: discard -proc generateDot(project: string) = - writeRope("digraph $1 {$n$2}$n" % [ - rope(changeFileExt(extractFilename(project), "")), gDotGraph], +proc generateDot*(graph: ModuleGraph; project: string) = + let b = Backend(graph.backend) + discard writeRope("digraph $1 {$n$2}$n" % [ + rope(changeFileExt(extractFilename(project), "")), b.dotGraph], changeFileExt(project, "dot")) -proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = +proc myOpen(graph: ModuleGraph; module: PSym): PPassContext = var g: PGen new(g) g.module = module g.config = graph.config + g.graph = graph + if graph.backend == nil: + graph.backend = Backend(dotGraph: nil) result = g const gendependPass* = makePass(open = myOpen, process = addDotDependency) diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim index 31c735794..0395728c2 100644 --- a/compiler/destroyer.nim +++ b/compiler/destroyer.nim @@ -116,8 +116,8 @@ Remarks: Rule 1.2 is not yet implemented because ``sink`` is currently import intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, - strutils, options, dfa, lowerings, rodread, tables, modulegraphs, - configuration + strutils, options, dfa, lowerings, tables, modulegraphs, + lineinfos const InterestingSyms = {skVar, skResult, skLet} @@ -132,10 +132,11 @@ type destroys, topLevelVars: PNode toDropBit: Table[int, PSym] graph: ModuleGraph + emptyNode: PNode proc getTemp(c: var Con; typ: PType; info: TLineInfo): PNode = # XXX why are temps fields in an object here? - let f = newSym(skField, getIdent(":d" & $c.tmpObj.n.len), c.owner, info) + let f = newSym(skField, getIdent(c.graph.cache, ":d" & $c.tmpObj.n.len), c.owner, info) f.typ = typ rawAddField c.tmpObj, f result = rawDirectAccess(c.tmp, f) @@ -243,17 +244,17 @@ proc genDestroy(c: Con; t: PType; dest: PNode): PNode = genOp(t.destructor, "=destroy") proc addTopVar(c: var Con; v: PNode) = - c.topLevelVars.add newTree(nkIdentDefs, v, emptyNode, emptyNode) + c.topLevelVars.add newTree(nkIdentDefs, v, c.emptyNode, c.emptyNode) proc dropBit(c: var Con; s: PSym): PSym = result = c.toDropBit.getOrDefault(s.id) assert result != nil proc registerDropBit(c: var Con; s: PSym) = - let result = newSym(skTemp, getIdent(s.name.s & "_AliveBit"), c.owner, s.info) + let result = newSym(skTemp, getIdent(c.graph.cache, s.name.s & "_AliveBit"), c.owner, s.info) result.typ = getSysType(c.graph, s.info, tyBool) let trueVal = newIntTypeNode(nkIntLit, 1, result.typ) - c.topLevelVars.add newTree(nkIdentDefs, newSymNode result, emptyNode, trueVal) + c.topLevelVars.add newTree(nkIdentDefs, newSymNode result, c.emptyNode, trueVal) c.toDropBit[s.id] = result # generate: # if not sinkParam_AliveBit: `=destroy`(sinkParam) @@ -322,13 +323,13 @@ proc destructiveMoveVar(n: PNode; c: var Con): PNode = # generate: (let tmp = v; reset(v); tmp) result = newNodeIT(nkStmtListExpr, n.info, n.typ) - var temp = newSym(skLet, getIdent("blitTmp"), c.owner, n.info) + var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), c.owner, n.info) var v = newNodeI(nkLetSection, n.info) let tempAsNode = newSymNode(temp) var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3) vpart.sons[0] = tempAsNode - vpart.sons[1] = ast.emptyNode + vpart.sons[1] = c.emptyNode vpart.sons[2] = n add(v, vpart) @@ -427,13 +428,14 @@ proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode = echo "injecting into ", n var c: Con c.owner = owner - c.tmp = newSym(skTemp, getIdent":d", owner, n.info) + c.tmp = newSym(skTemp, getIdent(g.cache, ":d"), owner, n.info) c.tmpObj = createObj(g, owner, n.info) c.tmp.typ = c.tmpObj c.destroys = newNodeI(nkStmtList, n.info) c.topLevelVars = newNodeI(nkVarSection, n.info) c.toDropBit = initTable[int, PSym]() c.graph = g + c.emptyNode = newNodeI(nkEmpty, n.info) let cfg = constructCfg(owner, n) shallowCopy(c.g, cfg) c.jumpTargets = initIntSet() diff --git a/compiler/dfa.nim b/compiler/dfa.nim index 0fd706178..013242f62 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -23,7 +23,7 @@ ## "A Graph–Free Approach to Data–Flow Analysis" by Markus Mohnen. ## https://link.springer.com/content/pdf/10.1007/3-540-45937-5_6.pdf -import ast, astalgo, types, intsets, tables, msgs, options +import ast, astalgo, types, intsets, tables, msgs, options, lineinfos type InstrKind* = enum @@ -54,7 +54,7 @@ type blocks: seq[TBlock] proc debugInfo(info: TLineInfo): string = - result = info.toFilename & ":" & $info.line + result = $info.line #info.toFilename & ":" & $info.line proc codeListing(c: ControlFlowGraph, result: var string, start=0; last = -1) = # for debugging purposes @@ -87,7 +87,8 @@ proc echoCfg*(c: ControlFlowGraph; start=0; last = -1) {.deprecated.} = ## echos the ControlFlowGraph for debugging purposes. var buf = "" codeListing(c, buf, start, last) - echo buf + when declared(echo): + echo buf proc forkI(c: var Con; n: PNode): TPosition = result = TPosition(c.code.len) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 7ab2f0eee..db4e301d4 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -16,7 +16,7 @@ import wordrecg, syntaxes, renderer, lexer, packages/docutils/rstast, packages/docutils/rst, packages/docutils/rstgen, times, packages/docutils/highlite, sempass2, json, xmltree, cgi, - typesrenderer, astalgo, modulepaths, configuration + typesrenderer, astalgo, modulepaths, lineinfos type TSections = array[TSymKind, Rope] @@ -30,6 +30,7 @@ type types: TStrTable isPureRst: bool conf*: ConfigRef + cache*: IdentCache PDoc* = ref TDocumentor ## Alias to type less. @@ -86,10 +87,11 @@ proc parseRst(text, filename: string, result = rstParse(text, filename, line, column, hasToc, rstOptions, docgenFindFile, compilerMsgHandler) -proc newDocumentor*(filename: string, conf: ConfigRef): PDoc = +proc newDocumentor*(filename: string; cache: IdentCache; conf: ConfigRef): PDoc = declareClosures() new(result) result.conf = conf + result.cache = cache initRstGenerator(result[], (if conf.cmd != cmdRst2tex: outHtml else: outLatex), conf.configVars, filename, {roSupportRawDirective}, docgenFindFile, compilerMsgHandler) @@ -190,7 +192,7 @@ proc genComment(d: PDoc, n: PNode): string = result = "" var dummyHasToc: bool if n.comment != nil: - renderRstToOut(d[], parseRst(n.comment, toFilename(n.info), + renderRstToOut(d[], parseRst(n.comment, toFilename(d.conf, n.info), toLinenumber(n.info), toColumn(n.info), dummyHasToc, d.options, d.conf), result) @@ -307,13 +309,13 @@ when false: result = findDocComment(n.sons[i]) if result != nil: return - proc extractDocComment*(s: PSym, d: PDoc = nil): string = + proc extractDocComment*(s: PSym, d: PDoc): string = let n = findDocComment(s.ast) result = "" if not n.isNil: if not d.isNil: var dummyHasToc: bool - renderRstToOut(d[], parseRst(n.comment, toFilename(n.info), + renderRstToOut(d[], parseRst(n.comment, toFilename(d.conf, n.info), toLinenumber(n.info), toColumn(n.info), dummyHasToc, d.options + {roSkipPounds}), result) @@ -349,18 +351,18 @@ proc getName(d: PDoc, n: PNode, splitAfter = -1): string = else: result = "" -proc getNameIdent(n: PNode): PIdent = +proc getNameIdent(cache: IdentCache; n: PNode): PIdent = case n.kind - of nkPostfix: result = getNameIdent(n.sons[1]) - of nkPragmaExpr: result = getNameIdent(n.sons[0]) + of nkPostfix: result = getNameIdent(cache, n.sons[1]) + of nkPragmaExpr: result = getNameIdent(cache, n.sons[0]) of nkSym: result = n.sym.name of nkIdent: result = n.ident of nkAccQuoted: var r = "" - for i in 0..<n.len: r.add(getNameIdent(n[i]).s) - result = getIdent(r) + for i in 0..<n.len: r.add(getNameIdent(cache, n[i]).s) + result = getIdent(cache, r) of nkOpenSymChoice, nkClosedSymChoice: - result = getNameIdent(n[0]) + result = getNameIdent(cache, n[0]) else: result = nil @@ -502,7 +504,7 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) = let docItemSeeSrc = getConfigVar(d.conf, "doc.item.seesrc") if docItemSeeSrc.len > 0: let cwd = canonicalizePath(d.conf, getCurrentDir()) - var path = n.info.toFullPath + var path = toFullPath(d.conf, n.info) if path.startsWith(cwd): path = path[cwd.len+1 .. ^1].replace('\\', '/') let gitUrl = getConfigVar(d.conf, "git.url") @@ -580,27 +582,27 @@ proc traceDeps(d: PDoc, it: PNode) = if d.section[k] != nil: add(d.section[k], ", ") dispA(d.conf, d.section[k], "<a class=\"reference external\" href=\"$1.html\">$1</a>", - "$1", [rope(getModuleName(d.conf, it))]) + "$1", [rope(splitFile(getModuleName(d.conf, it)).name)]) proc generateDoc*(d: PDoc, n: PNode) = case n.kind of nkCommentStmt: add(d.modDesc, genComment(d, n)) of nkProcDef: - when useEffectSystem: documentRaises(n) + when useEffectSystem: documentRaises(d.cache, n) genItem(d, n, n.sons[namePos], skProc) of nkFuncDef: - when useEffectSystem: documentRaises(n) + when useEffectSystem: documentRaises(d.cache, n) genItem(d, n, n.sons[namePos], skFunc) of nkMethodDef: - when useEffectSystem: documentRaises(n) + when useEffectSystem: documentRaises(d.cache, n) genItem(d, n, n.sons[namePos], skMethod) of nkIteratorDef: - when useEffectSystem: documentRaises(n) + when useEffectSystem: documentRaises(d.cache, n) genItem(d, n, n.sons[namePos], skIterator) of nkMacroDef: genItem(d, n, n.sons[namePos], skMacro) of nkTemplateDef: genItem(d, n, n.sons[namePos], skTemplate) of nkConverterDef: - when useEffectSystem: documentRaises(n) + when useEffectSystem: documentRaises(d.cache, n) genItem(d, n, n.sons[namePos], skConverter) of nkTypeSection, nkVarSection, nkLetSection, nkConstSection: for i in countup(0, sonsLen(n) - 1): @@ -630,23 +632,23 @@ proc generateJson*(d: PDoc, n: PNode) = d.add %{ "comment": %stripped, "line": %n.info.line.int, "col": %n.info.col } of nkProcDef: - when useEffectSystem: documentRaises(n) + when useEffectSystem: documentRaises(d.cache, n) d.add genJsonItem(d, n, n.sons[namePos], skProc) of nkFuncDef: - when useEffectSystem: documentRaises(n) + when useEffectSystem: documentRaises(d.cache, n) d.add genJsonItem(d, n, n.sons[namePos], skFunc) of nkMethodDef: - when useEffectSystem: documentRaises(n) + when useEffectSystem: documentRaises(d.cache, n) d.add genJsonItem(d, n, n.sons[namePos], skMethod) of nkIteratorDef: - when useEffectSystem: documentRaises(n) + when useEffectSystem: documentRaises(d.cache, n) d.add genJsonItem(d, n, n.sons[namePos], skIterator) of nkMacroDef: d.add genJsonItem(d, n, n.sons[namePos], skMacro) of nkTemplateDef: d.add genJsonItem(d, n, n.sons[namePos], skTemplate) of nkConverterDef: - when useEffectSystem: documentRaises(n) + when useEffectSystem: documentRaises(d.cache, n) d.add genJsonItem(d, n, n.sons[namePos], skConverter) of nkTypeSection, nkVarSection, nkLetSection, nkConstSection: for i in countup(0, sonsLen(n) - 1): @@ -673,23 +675,23 @@ proc generateTags*(d: PDoc, n: PNode, r: var Rope) = let stripped = n.comment.substr(2).strip r.add stripped of nkProcDef: - when useEffectSystem: documentRaises(n) + when useEffectSystem: documentRaises(d.cache, n) r.add genTagsItem(d, n, n.sons[namePos], skProc) of nkFuncDef: - when useEffectSystem: documentRaises(n) + when useEffectSystem: documentRaises(d.cache, n) r.add genTagsItem(d, n, n.sons[namePos], skFunc) of nkMethodDef: - when useEffectSystem: documentRaises(n) + when useEffectSystem: documentRaises(d.cache, n) r.add genTagsItem(d, n, n.sons[namePos], skMethod) of nkIteratorDef: - when useEffectSystem: documentRaises(n) + when useEffectSystem: documentRaises(d.cache, n) r.add genTagsItem(d, n, n.sons[namePos], skIterator) of nkMacroDef: r.add genTagsItem(d, n, n.sons[namePos], skMacro) of nkTemplateDef: r.add genTagsItem(d, n, n.sons[namePos], skTemplate) of nkConverterDef: - when useEffectSystem: documentRaises(n) + when useEffectSystem: documentRaises(d.cache, n) r.add genTagsItem(d, n, n.sons[namePos], skConverter) of nkTypeSection, nkVarSection, nkLetSection, nkConstSection: for i in countup(0, sonsLen(n) - 1): @@ -777,10 +779,14 @@ proc getOutFile2(conf: ConfigRef; filename, ext, dir: string): string = proc writeOutput*(d: PDoc, filename, outExt: string, useWarning = false) = var content = genOutFile(d) + var success = true if optStdout in d.conf.globalOptions: writeRope(stdout, content) else: - writeRope(content, getOutFile2(d.conf, filename, outExt, "htmldocs"), useWarning) + let outfile = getOutFile2(d.conf, filename, outExt, "htmldocs") + success = writeRope(content, outfile) + if not success: + rawMessage(d.conf, if useWarning: warnCannotOpenFile else: errCannotOpenFile, filename) proc writeOutputJson*(d: PDoc, filename, outExt: string, useWarning = false) = @@ -798,18 +804,18 @@ proc writeOutputJson*(d: PDoc, filename, outExt: string, else: discard "fixme: error report" -proc commandDoc*(conf: ConfigRef) = - var ast = parseFile(conf.projectMainIdx.FileIndex, newIdentCache(), conf) +proc commandDoc*(cache: IdentCache, conf: ConfigRef) = + var ast = parseFile(conf.projectMainIdx, cache, conf) if ast == nil: return - var d = newDocumentor(conf.projectFull, conf) + var d = newDocumentor(conf.projectFull, cache, conf) d.hasToc = true generateDoc(d, ast) writeOutput(d, conf.projectFull, HtmlExt) generateIndex(d) -proc commandRstAux(conf: ConfigRef; filename, outExt: string) = +proc commandRstAux(cache: IdentCache, conf: ConfigRef; filename, outExt: string) = var filen = addFileExt(filename, "txt") - var d = newDocumentor(filen, conf) + var d = newDocumentor(filen, cache, conf) d.onTestSnippet = proc (d: var RstGenerator; filename, cmd: string; status: int; content: string) = var outp: string @@ -841,17 +847,16 @@ proc commandRstAux(conf: ConfigRef; filename, outExt: string) = writeOutput(d, filename, outExt) generateIndex(d) -proc commandRst2Html*(conf: ConfigRef) = - commandRstAux(conf, conf.projectFull, HtmlExt) +proc commandRst2Html*(cache: IdentCache, conf: ConfigRef) = + commandRstAux(cache, conf, conf.projectFull, HtmlExt) -proc commandRst2TeX*(conf: ConfigRef) = - splitter = "\\-" - commandRstAux(conf, conf.projectFull, TexExt) +proc commandRst2TeX*(cache: IdentCache, conf: ConfigRef) = + commandRstAux(cache, conf, conf.projectFull, TexExt) -proc commandJson*(conf: ConfigRef) = - var ast = parseFile(conf.projectMainIdx.FileIndex, newIdentCache(), conf) +proc commandJson*(cache: IdentCache, conf: ConfigRef) = + var ast = parseFile(conf.projectMainIdx, cache, conf) if ast == nil: return - var d = newDocumentor(conf.projectFull, conf) + var d = newDocumentor(conf.projectFull, cache, conf) d.hasToc = true generateJson(d, ast) let json = d.jArray @@ -861,12 +866,14 @@ proc commandJson*(conf: ConfigRef) = writeRope(stdout, content) else: #echo getOutFile(gProjectFull, JsonExt) - writeRope(content, getOutFile(conf, conf.projectFull, JsonExt), useWarning = false) + let filename = getOutFile(conf, conf.projectFull, JsonExt) + if not writeRope(content, filename): + rawMessage(conf, errCannotOpenFile, filename) -proc commandTags*(conf: ConfigRef) = - var ast = parseFile(conf.projectMainIdx.FileIndex, newIdentCache(), conf) +proc commandTags*(cache: IdentCache, conf: ConfigRef) = + var ast = parseFile(conf.projectMainIdx, cache, conf) if ast == nil: return - var d = newDocumentor(conf.projectFull, conf) + var d = newDocumentor(conf.projectFull, cache, conf) d.hasToc = true var content: Rope @@ -876,9 +883,11 @@ proc commandTags*(conf: ConfigRef) = writeRope(stdout, content) else: #echo getOutFile(gProjectFull, TagsExt) - writeRope(content, getOutFile(conf, conf.projectFull, TagsExt), useWarning = false) + let filename = getOutFile(conf, conf.projectFull, TagsExt) + if not writeRope(content, filename): + rawMessage(conf, errCannotOpenFile, filename) -proc commandBuildIndex*(conf: ConfigRef) = +proc commandBuildIndex*(cache: IdentCache, conf: ConfigRef) = var content = mergeIndexes(conf.projectFull).rope let code = ropeFormatNamedVars(conf, getConfigVar(conf, "doc.file"), ["title", @@ -887,4 +896,6 @@ proc commandBuildIndex*(conf: ConfigRef) = ["Index".rope, nil, nil, rope(getDateStr()), rope(getClockStr()), content, nil, nil, nil]) # no analytics because context is not available - writeRope(code, getOutFile(conf, "theindex", HtmlExt)) + let filename = getOutFile(conf, "theindex", HtmlExt) + if not writeRope(code, filename): + rawMessage(conf, errCannotOpenFile, filename) diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim index d9a73e1cd..068c47bb3 100644 --- a/compiler/docgen2.nim +++ b/compiler/docgen2.nim @@ -11,7 +11,7 @@ # semantic checking. import - os, options, ast, astalgo, msgs, ropes, idents, passes, docgen + os, options, ast, astalgo, msgs, ropes, idents, passes, docgen, lineinfos from modulegraphs import ModuleGraph @@ -25,7 +25,7 @@ template closeImpl(body: untyped) {.dirty.} = var g = PGen(p) let useWarning = sfMainModule notin g.module.flags #echo g.module.name.s, " ", g.module.owner.id, " ", gMainPackageId - if (g.module.owner.id == gMainPackageId and optWholeProject in g.doc.conf.globalOptions) or + if (g.module.owner.id == g.doc.conf.mainPackageId and optWholeProject in g.doc.conf.globalOptions) or sfMainModule in g.module.flags: body try: @@ -35,11 +35,11 @@ template closeImpl(body: untyped) {.dirty.} = proc close(graph: ModuleGraph; p: PPassContext, n: PNode): PNode = closeImpl: - writeOutput(g.doc, g.module.filename, HtmlExt, useWarning) + writeOutput(g.doc, toFilename(graph.config, FileIndex g.module.position), HtmlExt, useWarning) proc closeJson(graph: ModuleGraph; p: PPassContext, n: PNode): PNode = closeImpl: - writeOutputJson(g.doc, g.module.filename, ".json", useWarning) + writeOutputJson(g.doc, toFilename(graph.config, FileIndex g.module.position), ".json", useWarning) proc processNode(c: PPassContext, n: PNode): PNode = result = n @@ -51,11 +51,11 @@ proc processNodeJson(c: PPassContext, n: PNode): PNode = var g = PGen(c) generateJson(g.doc, n) -proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = +proc myOpen(graph: ModuleGraph; module: PSym): PPassContext = var g: PGen new(g) g.module = module - var d = newDocumentor(module.filename, graph.config) + var d = newDocumentor(toFilename(graph.config, FileIndex module.position), graph.cache, graph.config) d.hasToc = true g.doc = d result = g diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim index 0e3d0609d..e863c8995 100644 --- a/compiler/evalffi.nim +++ b/compiler/evalffi.nim @@ -442,7 +442,7 @@ proc callForeignFunction*(call: PNode): PNode = libffi.call(cif, fn, retVal, args) if retVal.isNil: - result = emptyNode + result = newNode(nkEmpty) else: result = unpack(retVal, typ.sons[0], nil) result.info = call.info @@ -484,7 +484,7 @@ proc callForeignFunction*(fn: PNode, fntyp: PType, libffi.call(cif, fn, retVal, cargs) if retVal.isNil: - result = emptyNode + result = newNode(nkEmpty) else: result = unpack(retVal, fntyp.sons[0], nil) result.info = info diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index 01c56ec9c..d6c630e79 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -11,7 +11,7 @@ import strutils, options, ast, astalgo, msgs, os, idents, wordrecg, renderer, - rodread + lineinfos type TemplCtx = object @@ -104,7 +104,7 @@ proc evalTemplateArgs(n: PNode, s: PSym; conf: ConfigRef; fromHlo: bool): PNode let default = s.typ.n.sons[i].sym.ast if default.isNil or default.kind == nkEmpty: localError(conf, n.info, errWrongNumberOfArguments) - addSon(result, ast.emptyNode) + addSon(result, newNodeI(nkEmpty, n.info)) else: addSon(result, default.copyTree) @@ -114,7 +114,6 @@ proc evalTemplateArgs(n: PNode, s: PSym; conf: ConfigRef; fromHlo: bool): PNode # to prevent endless recursion in template instantiation const evalTemplateLimit* = 1000 -var evalTemplateCounter* = 0 # XXX remove this global proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode = when true: @@ -140,8 +139,8 @@ proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode = proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; conf: ConfigRef; fromHlo=false): PNode = - inc(evalTemplateCounter) - if evalTemplateCounter > evalTemplateLimit: + inc(conf.evalTemplateCounter) + if conf.evalTemplateCounter > evalTemplateLimit: globalError(conf, n.info, errTemplateInstantiationTooNested) result = n @@ -170,5 +169,5 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; evalTemplateAux(body.sons[i], args, ctx, result) result.flags.incl nfFromTemplate result = wrapInComesFrom(n.info, tmpl, result) - dec(evalTemplateCounter) + dec(conf.evalTemplateCounter) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 3f0e6f611..17133624b 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -9,19 +9,14 @@ # Module providing functions for calling the different external C compilers # Uses some hard-wired facts about each C/C++ compiler, plus options read -# from a configuration file, to provide generalized procedures to compile +# from a lineinfos file, to provide generalized procedures to compile # nim files. import ropes, os, strutils, osproc, platform, condsyms, options, msgs, - configuration, std / sha1, streams - -#from debuginfo import writeDebugInfo + lineinfos, std / sha1, streams type - TSystemCC* = enum - ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc, - ccTcc, ccPcc, ccUcc, ccIcl, ccIcc TInfoCCProp* = enum # properties of the C compiler: hasSwitchRange, # CC allows ranges in switch statements (GNU C) hasComputedGoto, # CC has computed goto (GNU C extension) @@ -336,37 +331,8 @@ const hExt* = ".h" -var - cCompiler* = ccGcc # the used compiler - gMixedMode*: bool # true if some module triggered C++ codegen - cIncludes*: seq[string] = @[] # directories to search for included files - cLibs*: seq[string] = @[] # directories to search for lib files - cLinkedLibs*: seq[string] = @[] # libraries to link - -# implementation - -proc libNameTmpl(): string {.inline.} = - result = if targetOS == osWindows: "$1.lib" else: "lib$1.a" - -type - CfileFlag* {.pure.} = enum - Cached, ## no need to recompile this time - External ## file was introduced via .compile pragma - - Cfile* = object - cname*, obj*: string - flags*: set[CFileFlag] - CfileList = seq[Cfile] - -var - externalToLink: seq[string] = @[] # files to link in addition to the file - # we compiled - linkOptionsCmd: string = "" - compileOptionsCmd: seq[string] = @[] - linkOptions: string = "" - compileOptions: string = "" - ccompilerpath: string = "" - toCompile: CfileList = @[] +proc libNameTmpl(conf: ConfigRef): string {.inline.} = + result = if conf.target.targetOS == osWindows: "$1.lib" else: "lib$1.a" proc nameToCC*(name: string): TSystemCC = ## Returns the kind of compiler referred to by `name`, or ccNone @@ -389,10 +355,10 @@ proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string = else: suffix - if (platform.hostOS != targetOS or platform.hostCPU != targetCPU) and + if (conf.target.hostOS != conf.target.targetOS or conf.target.hostCPU != conf.target.targetCPU) and optCompileOnly notin conf.globalOptions: - let fullCCname = platform.CPU[targetCPU].name & '.' & - platform.OS[targetOS].name & '.' & + let fullCCname = platform.CPU[conf.target.targetCPU].name & '.' & + platform.OS[conf.target.targetOS].name & '.' & CC[c].name & fullSuffix result = getConfigVar(conf, fullCCname) if result.len == 0: @@ -402,40 +368,40 @@ proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string = result = getConfigVar(conf, CC[c].name & fullSuffix) proc setCC*(conf: ConfigRef; ccname: string; info: TLineInfo) = - cCompiler = nameToCC(ccname) - if cCompiler == ccNone: + conf.cCompiler = nameToCC(ccname) + if conf.cCompiler == ccNone: localError(conf, info, "unknown C compiler: '$1'" % ccname) - compileOptions = getConfigVar(conf, cCompiler, ".options.always") - linkOptions = "" - ccompilerpath = getConfigVar(conf, cCompiler, ".path") + conf.compileOptions = getConfigVar(conf, conf.cCompiler, ".options.always") + conf.linkOptions = "" + conf.ccompilerpath = getConfigVar(conf, conf.cCompiler, ".path") for i in countup(low(CC), high(CC)): undefSymbol(conf.symbols, CC[i].name) - defineSymbol(conf.symbols, CC[cCompiler].name) + defineSymbol(conf.symbols, CC[conf.cCompiler].name) proc addOpt(dest: var string, src: string) = if len(dest) == 0 or dest[len(dest)-1] != ' ': add(dest, " ") add(dest, src) proc addLinkOption*(conf: ConfigRef; option: string) = - addOpt(linkOptions, option) + addOpt(conf.linkOptions, option) proc addCompileOption*(conf: ConfigRef; option: string) = - if strutils.find(compileOptions, option, 0) < 0: - addOpt(compileOptions, option) + if strutils.find(conf.compileOptions, option, 0) < 0: + addOpt(conf.compileOptions, option) proc addLinkOptionCmd*(conf: ConfigRef; option: string) = - addOpt(linkOptionsCmd, option) + addOpt(conf.linkOptionsCmd, option) proc addCompileOptionCmd*(conf: ConfigRef; option: string) = - compileOptionsCmd.add(option) + conf.compileOptionsCmd.add(option) proc initVars*(conf: ConfigRef) = # we need to define the symbol here, because ``CC`` may have never been set! for i in countup(low(CC), high(CC)): undefSymbol(conf.symbols, CC[i].name) - defineSymbol(conf.symbols, CC[cCompiler].name) - addCompileOption(conf, getConfigVar(conf, cCompiler, ".options.always")) + defineSymbol(conf.symbols, CC[conf.cCompiler].name) + addCompileOption(conf, getConfigVar(conf, conf.cCompiler, ".options.always")) #addLinkOption(getConfigVar(cCompiler, ".options.linker")) - if len(ccompilerpath) == 0: - ccompilerpath = getConfigVar(conf, cCompiler, ".path") + if len(conf.ccompilerpath) == 0: + conf.ccompilerpath = getConfigVar(conf, conf.cCompiler, ".path") proc completeCFilePath*(conf: ConfigRef; cfile: string, createSubDir: bool = true): string = result = completeGeneratedFilePath(conf, cfile, createSubDir) @@ -445,21 +411,21 @@ proc toObjFile*(conf: ConfigRef; filename: string): string = #if filename.endsWith(".cpp"): # result = changeFileExt(filename, "cpp." & CC[cCompiler].objExt) #else: - result = changeFileExt(filename, CC[cCompiler].objExt) + result = changeFileExt(filename, CC[conf.cCompiler].objExt) proc addFileToCompile*(conf: ConfigRef; cf: Cfile) = - toCompile.add(cf) + conf.toCompile.add(cf) proc resetCompilationLists*(conf: ConfigRef) = - toCompile.setLen 0 + conf.toCompile.setLen 0 ## XXX: we must associate these with their originating module # when the module is loaded/unloaded it adds/removes its items # That's because we still need to hash check the external files # Maybe we can do that in checkDep on the other hand? - externalToLink.setLen 0 + conf.externalToLink.setLen 0 proc addExternalFileToLink*(conf: ConfigRef; filename: string) = - externalToLink.insert(filename, 0) + conf.externalToLink.insert(filename, 0) proc execWithEcho(conf: ConfigRef; cmd: string, msg = hintExecuting): int = rawMessage(conf, msg, cmd) @@ -472,9 +438,12 @@ proc execExternalProgram*(conf: ConfigRef; cmd: string, msg = hintExecuting) = proc generateScript(conf: ConfigRef; projectFile: string, script: Rope) = let (dir, name, ext) = splitFile(projectFile) - writeRope(script, getNimcacheDir(conf) / addFileExt("compile_" & name, - platform.OS[targetOS].scriptExt)) - copyFile(conf.libpath / "nimbase.h", getNimcacheDir(conf) / "nimbase.h") + let filename = getNimcacheDir(conf) / addFileExt("compile_" & name, + platform.OS[conf.target.targetOS].scriptExt) + if writeRope(script, filename): + copyFile(conf.libpath / "nimbase.h", getNimcacheDir(conf) / "nimbase.h") + else: + rawMessage(conf, errGenerated, "could not write to file: " & filename) proc getOptSpeed(conf: ConfigRef; c: TSystemCC): string = result = getConfigVar(conf, c, ".options.speed") @@ -499,8 +468,8 @@ proc noAbsolutePaths(conf: ConfigRef): bool {.inline.} = result = conf.globalOptions * {optGenScript, optGenMapping} != {} proc cFileSpecificOptions(conf: ConfigRef; cfilename: string): string = - result = compileOptions - for option in compileOptionsCmd: + result = conf.compileOptions + for option in conf.compileOptionsCmd: if strutils.find(result, option, 0) < 0: addOpt(result, option) @@ -508,15 +477,15 @@ proc cFileSpecificOptions(conf: ConfigRef; cfilename: string): string = if optCDebug in conf.globalOptions: let key = trunk & ".debug" if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key)) - else: addOpt(result, getDebug(conf, cCompiler)) + else: addOpt(result, getDebug(conf, conf.cCompiler)) if optOptimizeSpeed in conf.options: let key = trunk & ".speed" if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key)) - else: addOpt(result, getOptSpeed(conf, cCompiler)) + else: addOpt(result, getOptSpeed(conf, conf.cCompiler)) elif optOptimizeSize in conf.options: let key = trunk & ".size" if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key)) - else: addOpt(result, getOptSize(conf, cCompiler)) + else: addOpt(result, getOptSize(conf, conf.cCompiler)) let key = trunk & ".always" if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key)) @@ -524,15 +493,15 @@ proc getCompileOptions(conf: ConfigRef): string = result = cFileSpecificOptions(conf, "__dummy__") proc getLinkOptions(conf: ConfigRef): string = - result = linkOptions & " " & linkOptionsCmd & " " - for linkedLib in items(cLinkedLibs): - result.add(CC[cCompiler].linkLibCmd % linkedLib.quoteShell) - for libDir in items(cLibs): - result.add(join([CC[cCompiler].linkDirCmd, libDir.quoteShell])) + result = conf.linkOptions & " " & conf.linkOptionsCmd & " " + for linkedLib in items(conf.cLinkedLibs): + result.add(CC[conf.cCompiler].linkLibCmd % linkedLib.quoteShell) + for libDir in items(conf.cLibs): + result.add(join([CC[conf.cCompiler].linkDirCmd, libDir.quoteShell])) proc needsExeExt(conf: ConfigRef): bool {.inline.} = - result = (optGenScript in conf.globalOptions and targetOS == osWindows) or - (platform.hostOS == osWindows) + result = (optGenScript in conf.globalOptions and conf.target.targetOS == osWindows) or + (conf.target.hostOS == osWindows) proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; cfile: string): string = result = if conf.cmd == cmdCompileToCpp and not cfile.endsWith(".c"): @@ -546,18 +515,18 @@ proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; cfile: string): string proc getLinkerExe(conf: ConfigRef; compiler: TSystemCC): string = result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe - elif gMixedMode and conf.cmd != cmdCompileToCpp: CC[compiler].cppCompiler + elif optMixedMode in conf.globalOptions and conf.cmd != cmdCompileToCpp: CC[compiler].cppCompiler else: getCompilerExe(conf, compiler, "") proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile): string = - var c = cCompiler + var c = conf.cCompiler var options = cFileSpecificOptions(conf, cfile.cname) var exe = getConfigVar(conf, c, ".exe") if exe.len == 0: exe = getCompilerExe(conf, c, cfile.cname) if needsExeExt(conf): exe = addFileExt(exe, "exe") if optGenDynLib in conf.globalOptions and - ospNeedsPIC in platform.OS[targetOS].props: + ospNeedsPIC in platform.OS[conf.target.targetOS].props: add(options, ' ' & CC[c].pic) var includeCmd, compilePattern: string @@ -565,10 +534,10 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile): string = # compute include paths: includeCmd = CC[c].includeCmd & quoteShell(conf.libpath) - for includeDir in items(cIncludes): + for includeDir in items(conf.cIncludes): includeCmd.add(join([CC[c].includeCmd, includeDir.quoteShell])) - compilePattern = joinPath(ccompilerpath, exe) + compilePattern = joinPath(conf.ccompilerpath, exe) else: includeCmd = "" compilePattern = getCompilerExe(conf, c, cfile.cname) @@ -604,9 +573,9 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile): string = proc footprint(conf: ConfigRef; cfile: Cfile): SecureHash = result = secureHash( $secureHashFile(cfile.cname) & - platform.OS[targetOS].name & - platform.CPU[targetCPU].name & - extccomp.CC[extccomp.cCompiler].name & + platform.OS[conf.target.targetOS].name & + platform.CPU[conf.target.targetCPU].name & + extccomp.CC[conf.cCompiler].name & getCompileCFileCmd(conf, cfile)) proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool = @@ -630,11 +599,11 @@ proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool = proc addExternalFileToCompile*(conf: ConfigRef; c: var Cfile) = if optForceFullMake notin conf.globalOptions and not externalFileChanged(conf, c): c.flags.incl CfileFlag.Cached - toCompile.add(c) + conf.toCompile.add(c) proc addExternalFileToCompile*(conf: ConfigRef; filename: string) = var c = Cfile(cname: filename, - obj: toObjFile(conf, completeCFilePath(conf, changeFileExt(filename, ""), false)), + obj: toObjFile(conf, completeCFilePath(conf, filename, false)), flags: {CfileFlag.External}) addExternalFileToCompile(conf, c) @@ -650,7 +619,7 @@ proc compileCFile(conf: ConfigRef; list: CFileList, script: var Rope, cmds: var add(prettyCmds, "CC: " & name) if optGenScript in conf.globalOptions: add(script, compileCmd) - add(script, tnl) + add(script, "\n") proc getLinkCmd(conf: ConfigRef; projectfile, objfiles: string): string = if optGenStaticLib in conf.globalOptions: @@ -660,24 +629,24 @@ proc getLinkCmd(conf: ConfigRef; projectfile, objfiles: string): string = if not libname.isAbsolute(): libname = getCurrentDir() / libname else: - libname = (libNameTmpl() % splitFile(conf.projectName).name) - result = CC[cCompiler].buildLib % ["libfile", libname, + libname = (libNameTmpl(conf) % splitFile(conf.projectName).name) + result = CC[conf.cCompiler].buildLib % ["libfile", libname, "objfiles", objfiles] else: - var linkerExe = getConfigVar(conf, cCompiler, ".linkerexe") - if len(linkerExe) == 0: linkerExe = getLinkerExe(conf, cCompiler) + var linkerExe = getConfigVar(conf, conf.cCompiler, ".linkerexe") + if len(linkerExe) == 0: linkerExe = getLinkerExe(conf, conf.cCompiler) # bug #6452: We must not use ``quoteShell`` here for ``linkerExe`` if needsExeExt(conf): linkerExe = addFileExt(linkerExe, "exe") if noAbsolutePaths(conf): result = linkerExe - else: result = joinPath(ccompilerpath, linkerExe) - let buildgui = if optGenGuiApp in conf.globalOptions: CC[cCompiler].buildGui + else: result = joinPath(conf.cCompilerpath, linkerExe) + let buildgui = if optGenGuiApp in conf.globalOptions: CC[conf.cCompiler].buildGui else: "" var exefile, builddll: string if optGenDynLib in conf.globalOptions: - exefile = platform.OS[targetOS].dllFrmt % splitFile(projectfile).name - builddll = CC[cCompiler].buildDll + exefile = platform.OS[conf.target.targetOS].dllFrmt % splitFile(projectfile).name + builddll = CC[conf.cCompiler].buildDll else: - exefile = splitFile(projectfile).name & platform.OS[targetOS].exeExt + exefile = splitFile(projectfile).name & platform.OS[conf.target.targetOS].exeExt builddll = "" if conf.outFile.len > 0: exefile = conf.outFile.expandTilde @@ -691,10 +660,10 @@ proc getLinkCmd(conf: ConfigRef; projectfile, objfiles: string): string = writeDebugInfo(exefile.changeFileExt("ndb")) exefile = quoteShell(exefile) let linkOptions = getLinkOptions(conf) & " " & - getConfigVar(conf, cCompiler, ".options.linker") - var linkTmpl = getConfigVar(conf, cCompiler, ".linkTmpl") + getConfigVar(conf, conf.cCompiler, ".options.linker") + var linkTmpl = getConfigVar(conf, conf.cCompiler, ".linkTmpl") if linkTmpl.len == 0: - linkTmpl = CC[cCompiler].linkTmpl + linkTmpl = CC[conf.cCompiler].linkTmpl result = quoteShell(result % ["builddll", builddll, "buildgui", buildgui, "options", linkOptions, "objfiles", objfiles, "exefile", exefile, "nim", getPrefixDir(conf), "lib", conf.libpath]) @@ -765,19 +734,20 @@ proc callCCompiler*(conf: ConfigRef; projectfile: string) = var cmds: TStringSeq = @[] var prettyCmds: TStringSeq = @[] let prettyCb = proc (idx: int) = - echo prettyCmds[idx] - compileCFile(conf, toCompile, script, cmds, prettyCmds) + when declared(echo): + echo prettyCmds[idx] + compileCFile(conf, conf.toCompile, script, cmds, prettyCmds) if optCompileOnly notin conf.globalOptions: execCmdsInParallel(conf, cmds, prettyCb) if optNoLinking notin conf.globalOptions: # call the linker: var objfiles = "" - for it in externalToLink: + for it in conf.externalToLink: let objFile = if noAbsolutePaths(conf): it.extractFilename else: it add(objfiles, ' ') add(objfiles, quoteShell( - addFileExt(objFile, CC[cCompiler].objExt))) - for x in toCompile: + addFileExt(objFile, CC[conf.cCompiler].objExt))) + for x in conf.toCompile: let objFile = if noAbsolutePaths(conf): x.obj.extractFilename else: x.obj add(objfiles, ' ') add(objfiles, quoteShell(objFile)) @@ -789,7 +759,7 @@ proc callCCompiler*(conf: ConfigRef; projectfile: string) = linkCmd = "" if optGenScript in conf.globalOptions: add(script, linkCmd) - add(script, tnl) + add(script, "\n") generateScript(conf, projectfile, script) #from json import escapeJson @@ -824,7 +794,7 @@ proc writeJsonBuildInstructions*(conf: ConfigRef; projectfile: string) = for it in llist: let objfile = if noAbsolutePaths(conf): it.extractFilename else: it - let objstr = addFileExt(objfile, CC[cCompiler].objExt) + let objstr = addFileExt(objfile, CC[conf.cCompiler].objExt) add(objfiles, ' ') add(objfiles, objstr) if pastStart: lit ",\L" @@ -848,11 +818,11 @@ proc writeJsonBuildInstructions*(conf: ConfigRef; projectfile: string) = var f: File if open(f, jsonFile, fmWrite): lit "{\"compile\":[\L" - cfiles(conf, f, buf, toCompile, false) + cfiles(conf, f, buf, conf.toCompile, false) lit "],\L\"link\":[\L" var objfiles = "" # XXX add every file here that is to link - linkfiles(conf, f, buf, objfiles, toCompile, externalToLink) + linkfiles(conf, f, buf, objfiles, conf.toCompile, conf.externalToLink) lit "],\L\"linkcmd\": " str getLinkCmd(conf, projectfile, objfiles) @@ -877,14 +847,16 @@ proc runJsonBuildInstructions*(conf: ConfigRef; projectfile: string) = add(prettyCmds, "CC: " & name) let prettyCb = proc (idx: int) = - echo prettyCmds[idx] + when declared(echo): + echo prettyCmds[idx] execCmdsInParallel(conf, cmds, prettyCb) let linkCmd = data["linkcmd"] doAssert linkCmd.kind == JString execLinkCmd(conf, linkCmd.getStr) except: - echo getCurrentException().getStackTrace() + when declared(echo): + echo getCurrentException().getStackTrace() quit "error evaluating JSON file: " & jsonFile proc genMappingFiles(conf: ConfigRef; list: CFileList): Rope = @@ -894,16 +866,18 @@ proc genMappingFiles(conf: ConfigRef; list: CFileList): Rope = proc writeMapping*(conf: ConfigRef; symbolMapping: Rope) = if optGenMapping notin conf.globalOptions: return var code = rope("[C_Files]\n") - add(code, genMappingFiles(conf, toCompile)) + add(code, genMappingFiles(conf, conf.toCompile)) add(code, "\n[C_Compiler]\nFlags=") add(code, strutils.escape(getCompileOptions(conf))) add(code, "\n[Linker]\nFlags=") add(code, strutils.escape(getLinkOptions(conf) & " " & - getConfigVar(conf, cCompiler, ".options.linker"))) + getConfigVar(conf, conf.cCompiler, ".options.linker"))) add(code, "\n[Environment]\nlibpath=") add(code, strutils.escape(conf.libpath)) addf(code, "\n[Symbols]$n$1", [symbolMapping]) - writeRope(code, joinPath(conf.projectPath, "mapping.txt")) + let filename = joinPath(conf.projectPath, "mapping.txt") + if not writeRope(code, filename): + rawMessage(conf, errGenerated, "could not write to file: " & filename) diff --git a/compiler/filter_tmpl.nim b/compiler/filter_tmpl.nim index 6c16a0b4e..09455ced7 100644 --- a/compiler/filter_tmpl.nim +++ b/compiler/filter_tmpl.nim @@ -11,7 +11,7 @@ import llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options, - renderer, filters + renderer, filters, lineinfos type TParseState = enum diff --git a/compiler/gorgeimpl.nim b/compiler/gorgeimpl.nim index 44c7651bc..44ad46136 100644 --- a/compiler/gorgeimpl.nim +++ b/compiler/gorgeimpl.nim @@ -9,7 +9,8 @@ ## Module that implements ``gorge`` for the compiler. -import msgs, std / sha1, os, osproc, streams, strutils, options +import msgs, std / sha1, os, osproc, streams, strutils, options, + lineinfos proc readOutput(p: Process): (string, int) = result[0] = "" @@ -22,7 +23,7 @@ proc readOutput(p: Process): (string, int) = result[1] = p.waitForExit proc opGorge*(cmd, input, cache: string, info: TLineInfo; conf: ConfigRef): (string, int) = - let workingDir = parentDir(info.toFullPath) + let workingDir = parentDir(toFullPath(conf, info)) if cache.len > 0:# and optForceFullMake notin gGlobalOptions: let h = secureHash(cmd & "\t" & input & "\t" & cache) let filename = options.toGeneratedFile(conf, "gorge_" & $h, "txt") diff --git a/compiler/guards.nim b/compiler/guards.nim index d39ea799b..99bb51fce 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -10,7 +10,7 @@ ## This module implements the 'implies' relation for guards. import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents, - saturate, modulegraphs, options, configuration + saturate, modulegraphs, options, lineinfos const someEq = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc, @@ -131,8 +131,8 @@ proc neg(n: PNode; o: Operators): PNode = let eAsNode = newIntNode(nkIntLit, e.sym.position) if not inSet(n.sons[1], eAsNode): s.add eAsNode result.sons[1] = s - elif t.kind notin {tyString, tySequence} and lengthOrd(t) < 1000: - result.sons[1] = complement(n.sons[1]) + #elif t.kind notin {tyString, tySequence} and lengthOrd(t) < 1000: + # result.sons[1] = complement(n.sons[1]) else: # not ({2, 3, 4}.contains(x)) x != 2 and x != 3 and x != 4 # XXX todo @@ -208,14 +208,14 @@ proc zero(): PNode = nkIntLit.newIntNode(0) proc one(): PNode = nkIntLit.newIntNode(1) proc minusOne(): PNode = nkIntLit.newIntNode(-1) -proc lowBound*(x: PNode): PNode = - result = nkIntLit.newIntNode(firstOrd(x.typ)) +proc lowBound*(conf: ConfigRef; x: PNode): PNode = + result = nkIntLit.newIntNode(firstOrd(conf, x.typ)) result.info = x.info -proc highBound*(x: PNode; o: Operators): PNode = +proc highBound*(conf: ConfigRef; x: PNode; o: Operators): PNode = let typ = x.typ.skipTypes(abstractInst) result = if typ.kind == tyArray: - nkIntLit.newIntNode(lastOrd(typ)) + nkIntLit.newIntNode(lastOrd(conf, typ)) elif typ.kind == tySequence and x.kind == nkSym and x.sym.kind == skConst: nkIntLit.newIntNode(x.sym.ast.len-1) @@ -503,7 +503,7 @@ proc leImpliesIn(x, c, aSet: PNode): TImplication = # fact: x <= 4; question x in {56}? # --> true if every value <= 4 is in the set {56} # - var value = newIntNode(c.kind, firstOrd(x.typ)) + var value = newIntNode(c.kind, firstOrd(nil, x.typ)) # don't iterate too often: if c.intVal - value.intVal < 1000: var i, pos, neg: int @@ -520,7 +520,7 @@ proc geImpliesIn(x, c, aSet: PNode): TImplication = # --> true iff every value >= 4 is in the set {56} # var value = newIntNode(c.kind, c.intVal) - let max = lastOrd(x.typ) + let max = lastOrd(nil, x.typ) # don't iterate too often: if max - value.intVal < 1000: var i, pos, neg: int @@ -532,8 +532,8 @@ proc geImpliesIn(x, c, aSet: PNode): TImplication = elif neg == i: result = impNo proc compareSets(a, b: PNode): TImplication = - if equalSets(a, b): result = impYes - elif intersectSets(a, b).len == 0: result = impNo + if equalSets(nil, a, b): result = impYes + elif intersectSets(nil, a, b).len == 0: result = impNo proc impliesIn(fact, loc, aSet: PNode): TImplication = case fact.sons[0].sym.magic @@ -799,10 +799,10 @@ proc ple(m: TModel; a, b: PNode): TImplication = # use type information too: x <= 4 iff high(x) <= 4 if b.isValue and a.typ != nil and a.typ.isOrdinalType: - if lastOrd(a.typ) <= b.intVal: return impYes + if lastOrd(nil, a.typ) <= b.intVal: return impYes # 3 <= x iff low(x) <= 3 if a.isValue and b.typ != nil and b.typ.isOrdinalType: - if firstOrd(b.typ) <= a.intVal: return impYes + if firstOrd(nil, b.typ) <= a.intVal: return impYes # x <= x if sameTree(a, b): return impYes diff --git a/compiler/hlo.nim b/compiler/hlo.nim index 8251e3179..bbbcb4e56 100644 --- a/compiler/hlo.nim +++ b/compiler/hlo.nim @@ -43,8 +43,8 @@ proc applyPatterns(c: PContext, n: PNode): PNode = if not isNil(x): assert x.kind in {nkStmtList, nkCall} # better be safe than sorry, so check evalTemplateCounter too: - inc(evalTemplateCounter) - if evalTemplateCounter > evalTemplateLimit: + inc(c.config.evalTemplateCounter) + if c.config.evalTemplateCounter > evalTemplateLimit: globalError(c.config, n.info, "template instantiation too nested") # deactivate this pattern: c.patterns[i] = nil @@ -54,7 +54,7 @@ proc applyPatterns(c: PContext, n: PNode): PNode = result = flattenStmts(x) else: result = evalPattern(c, x, result) - dec(evalTemplateCounter) + dec(c.config.evalTemplateCounter) # activate this pattern again: c.patterns[i] = pattern diff --git a/compiler/idents.nim b/compiler/idents.nim index 34cf5bf1e..0a2f2d5cf 100644 --- a/compiler/idents.nim +++ b/compiler/idents.nim @@ -30,12 +30,14 @@ type wordCounter: int idAnon*, idDelegator*, emptyIdent*: PIdent -var - legacy: IdentCache +when false: + var + legacy: IdentCache proc resetIdentCache*() = - for i in low(legacy.buckets)..high(legacy.buckets): - legacy.buckets[i] = nil + when false: + for i in low(legacy.buckets)..high(legacy.buckets): + legacy.buckets[i] = nil proc cmpIgnoreStyle*(a, b: cstring, blen: int): int = if a[0] != b[0]: return 1 @@ -111,25 +113,15 @@ proc getIdent*(self: IdentCache; identifier: string, h: Hash): PIdent = result = getIdent(cstring(identifier), len(identifier), h) proc newIdentCache*(): IdentCache = - if legacy.isNil: - result = IdentCache() - result.idAnon = result.getIdent":anonymous" - result.wordCounter = 1 - result.idDelegator = result.getIdent":delegator" - result.emptyIdent = result.getIdent("") - # initialize the keywords: - for s in countup(succ(low(specialWords)), high(specialWords)): - result.getIdent(specialWords[s], hashIgnoreStyle(specialWords[s])).id = ord(s) - legacy = result - else: - result = legacy + result = IdentCache() + result.idAnon = result.getIdent":anonymous" + result.wordCounter = 1 + result.idDelegator = result.getIdent":delegator" + result.emptyIdent = result.getIdent("") + # initialize the keywords: + for s in countup(succ(low(specialWords)), high(specialWords)): + result.getIdent(specialWords[s], hashIgnoreStyle(specialWords[s])).id = ord(s) proc whichKeyword*(id: PIdent): TSpecialWord = if id.id < 0: result = wInvalid else: result = TSpecialWord(id.id) - -proc getIdent*(identifier: string): PIdent = - ## for backwards compatibility. - if legacy.isNil: - discard newIdentCache() - legacy.getIdent identifier diff --git a/compiler/importer.nim b/compiler/importer.nim index 90e774a50..c013b93ab 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -10,8 +10,8 @@ # This module implements the symbol importing mechanism. import - intsets, strutils, os, ast, astalgo, msgs, options, idents, rodread, lookups, - semdata, passes, renderer, modulepaths, sigmatch, configuration + intsets, strutils, os, ast, astalgo, msgs, options, idents, lookups, + semdata, passes, renderer, modulepaths, sigmatch, lineinfos proc evalImport*(c: PContext, n: PNode): PNode proc evalFrom*(c: PContext, n: PNode): PNode @@ -20,7 +20,7 @@ proc readExceptSet*(c: PContext, n: PNode): IntSet = assert n.kind in {nkImportExceptStmt, nkExportExceptStmt} result = initIntSet() for i in 1 ..< n.len: - let ident = lookups.considerQuotedIdent(c.config, n[i]) + let ident = lookups.considerQuotedIdent(c, n[i]) result.incl(ident.id) proc importPureEnumField*(c: PContext; s: PSym) = @@ -69,12 +69,13 @@ proc rawImportSymbol(c: PContext, s: PSym) = if hasPattern(s): addPattern(c, s) proc importSymbol(c: PContext, n: PNode, fromMod: PSym) = - let ident = lookups.considerQuotedIdent(c.config, n) + let ident = lookups.considerQuotedIdent(c, n) let s = strTableGet(fromMod.tab, ident) if s == nil: errorUndeclaredIdentifier(c, n.info, ident.s) else: - if s.kind == skStub: loadStub(s) + when false: + if s.kind == skStub: loadStub(s) if s.kind notin ExportableSymKinds: internalError(c.config, n.info, "importSymbol: 2") # for an enumeration we have to add all identifiers @@ -132,7 +133,7 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym): PSym = result = createModuleAlias(realModule, n.sons[1].ident, realModule.info, c.config.options) -proc myImportModule(c: PContext, n: PNode): PSym = +proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym = var f = checkModuleName(c.config, n) if f != InvalidFileIDX: let L = c.graph.importStack.len @@ -143,10 +144,10 @@ proc myImportModule(c: PContext, n: PNode): PSym = var err = "" for i in countup(recursion, L-1): if i > recursion: err.add "\n" - err.add toFullPath(c.graph.importStack[i]) & " imports " & - toFullPath(c.graph.importStack[i+1]) + err.add toFullPath(c.config, c.graph.importStack[i]) & " imports " & + toFullPath(c.config, c.graph.importStack[i+1]) c.recursiveDep = err - result = importModuleAs(c, n, gImportModule(c.graph, c.module, f, c.cache)) + result = importModuleAs(c, n, c.graph.importModuleCallback(c.graph, c.module, f)) #echo "set back to ", L c.graph.importStack.setLen(L) # we cannot perform this check reliably because of @@ -161,9 +162,10 @@ proc myImportModule(c: PContext, n: PNode): PSym = else: message(c.config, n.info, warnDeprecated, result.name.s) suggestSym(c.config, n.info, result, c.graph.usageSym, false) + importStmtResult.add newStrNode(toFullPath(c.config, f), n.info) -proc impMod(c: PContext; it: PNode) = - let m = myImportModule(c, it) +proc impMod(c: PContext; it: PNode; importStmtResult: PNode) = + let m = myImportModule(c, it, importStmtResult) if m != nil: var emptySet: IntSet # ``addDecl`` needs to be done before ``importAllSymbols``! @@ -172,7 +174,8 @@ proc impMod(c: PContext; it: PNode) = #importForwarded(c, m.ast, emptySet) proc evalImport(c: PContext, n: PNode): PNode = - result = n + #result = n + result = newNodeI(nkImportStmt, n.info) for i in countup(0, sonsLen(n) - 1): let it = n.sons[i] if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket: @@ -184,14 +187,14 @@ proc evalImport(c: PContext, n: PNode): PNode = a.add sep # dummy entry, replaced in the loop for x in it[2]: a.sons[2] = x - impMod(c, a) + impMod(c, a, result) else: - impMod(c, it) + impMod(c, it, result) proc evalFrom(c: PContext, n: PNode): PNode = - result = n + result = newNodeI(nkImportStmt, n.info) checkMinSonsLen(n, 2, c.config) - var m = myImportModule(c, n.sons[0]) + var m = myImportModule(c, n.sons[0], result) if m != nil: n.sons[0] = newSymNode(m) addDecl(c, m, n.info) # add symbol to symbol table of module @@ -200,9 +203,9 @@ proc evalFrom(c: PContext, n: PNode): PNode = importSymbol(c, n.sons[i], m) proc evalImportExcept*(c: PContext, n: PNode): PNode = - result = n + result = newNodeI(nkImportStmt, n.info) checkMinSonsLen(n, 2, c.config) - var m = myImportModule(c, n.sons[0]) + var m = myImportModule(c, n.sons[0], result) if m != nil: n.sons[0] = newSymNode(m) addDecl(c, m, n.info) # add symbol to symbol table of module diff --git a/compiler/incremental.nim b/compiler/incremental.nim new file mode 100644 index 000000000..2008d35de --- /dev/null +++ b/compiler/incremental.nim @@ -0,0 +1,196 @@ +# +# +# The Nim Compiler +# (c) Copyright 2018 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Basic type definitions the module graph needs in order to support +## incremental compilations. + +const nimIncremental* = defined(nimIncremental) + +import options, lineinfos + +when nimIncremental: + import ast, msgs, intsets, btrees, db_sqlite, std / sha1 + from strutils import parseInt + + type + Writer* = object + sstack*: seq[PSym] # a stack of symbols to process + tstack*: seq[PType] # a stack of types to process + tmarks*, smarks*: IntSet + forwardedSyms*: seq[PSym] + + Reader* = object + syms*: BTree[int, PSym] + types*: BTree[int, PType] + + IncrementalCtx* = object + db*: DbConn + w*: Writer + r*: Reader + configChanged*: bool + + proc init*(incr: var IncrementalCtx) = + incr.w.sstack = @[] + incr.w.tstack = @[] + incr.w.tmarks = initIntSet() + incr.w.smarks = initIntSet() + incr.w.forwardedSyms = @[] + incr.r.syms = initBTree[int, PSym]() + incr.r.types = initBTree[int, PType]() + + + proc hashFileCached*(conf: ConfigRef; fileIdx: FileIndex; fullpath: string): string = + result = msgs.getHash(conf, fileIdx) + if result.len == 0: + result = $secureHashFile(fullpath) + msgs.setHash(conf, fileIdx, result) + + proc toDbFileId*(incr: var IncrementalCtx; conf: ConfigRef; fileIdx: FileIndex): int = + if fileIdx == FileIndex(-1): return -1 + let fullpath = toFullPath(conf, fileIdx) + let row = incr.db.getRow(sql"select id, fullhash from filenames where fullpath = ?", + fullpath) + let id = row[0] + let fullhash = hashFileCached(conf, fileIdx, fullpath) + if id.len == 0: + result = int incr.db.insertID(sql"insert into filenames(fullpath, fullhash) values (?, ?)", + fullpath, fullhash) + else: + if row[1] != fullhash: + incr.db.exec(sql"update filenames set fullhash = ? where fullpath = ?", fullhash, fullpath) + result = parseInt(id) + + proc fromDbFileId*(incr: var IncrementalCtx; conf: ConfigRef; dbId: int): FileIndex = + if dbId == -1: return FileIndex(-1) + let fullpath = incr.db.getValue(sql"select fullpath from filenames where id = ?", dbId) + doAssert fullpath.len > 0, "cannot find file name for DB ID " & $dbId + result = fileInfoIdx(conf, fullpath) + + + proc addModuleDep*(incr: var IncrementalCtx; conf: ConfigRef; + module, fileIdx: FileIndex; + isIncludeFile: bool) = + if conf.symbolFiles != v2Sf: return + + let a = toDbFileId(incr, conf, module) + let b = toDbFileId(incr, conf, fileIdx) + + incr.db.exec(sql"insert into deps(module, dependency, isIncludeFile) values (?, ?, ?)", + a, b, ord(isIncludeFile)) + + # --------------- Database model --------------------------------------------- + + proc createDb*(db: DbConn) = + db.exec(sql""" + create table if not exists controlblock( + idgen integer not null + ); + """) + + db.exec(sql""" + create table if not exists config( + config varchar(8000) not null + ); + """) + + db.exec(sql""" + create table if not exists filenames( + id integer primary key, + fullpath varchar(8000) not null, + fullHash varchar(256) not null + ); + """) + db.exec sql"create index if not exists FilenameIx on filenames(fullpath);" + + db.exec(sql""" + create table if not exists modules( + id integer primary key, + nimid integer not null, + fullpath varchar(8000) not null, + interfHash varchar(256) not null, + fullHash varchar(256) not null, + + created timestamp not null default (DATETIME('now')) + );""") + db.exec(sql"""create unique index if not exists SymNameIx on modules(fullpath);""") + + db.exec(sql""" + create table if not exists deps( + id integer primary key, + module integer not null, + dependency integer not null, + isIncludeFile integer not null, + foreign key (module) references filenames(id), + foreign key (dependency) references filenames(id) + );""") + db.exec(sql"""create index if not exists DepsIx on deps(module);""") + + db.exec(sql""" + create table if not exists types( + id integer primary key, + nimid integer not null, + module integer not null, + data blob not null, + foreign key (module) references module(id) + ); + """) + db.exec sql"create index TypeByModuleIdx on types(module);" + db.exec sql"create index TypeByNimIdIdx on types(nimid);" + + db.exec(sql""" + create table if not exists syms( + id integer primary key, + nimid integer not null, + module integer not null, + name varchar(256) not null, + data blob not null, + exported int not null, + foreign key (module) references module(id) + ); + """) + db.exec sql"create index if not exists SymNameIx on syms(name);" + db.exec sql"create index SymByNameAndModuleIdx on syms(name, module);" + db.exec sql"create index SymByModuleIdx on syms(module);" + db.exec sql"create index SymByNimIdIdx on syms(nimid);" + + + db.exec(sql""" + create table if not exists toplevelstmts( + id integer primary key, + position integer not null, + module integer not null, + data blob not null, + foreign key (module) references module(id) + ); + """) + db.exec sql"create index TopLevelStmtByModuleIdx on toplevelstmts(module);" + db.exec sql"create index TopLevelStmtByPositionIdx on toplevelstmts(position);" + + db.exec(sql""" + create table if not exists statics( + id integer primary key, + module integer not null, + data blob not null, + foreign key (module) references module(id) + ); + """) + db.exec sql"create index StaticsByModuleIdx on toplevelstmts(module);" + db.exec sql"insert into controlblock(idgen) values (0)" + + +else: + type + IncrementalCtx* = object + + template init*(incr: IncrementalCtx) = discard + + template addModuleDep*(incr: var IncrementalCtx; conf: ConfigRef; + module, fileIdx: FileIndex; + isIncludeFile: bool) = + discard diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 25b554f7b..ef54841ae 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -31,8 +31,8 @@ implements the required case distinction. import ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options, nversion, nimsets, msgs, std / sha1, bitsets, idents, types, os, tables, - times, ropes, math, passes, ccgutils, wordrecg, renderer, rodread, rodutils, - intsets, cgmeth, lowerings, sighashes, configuration + times, ropes, math, passes, ccgutils, wordrecg, renderer, + intsets, cgmeth, lowerings, sighashes, lineinfos, rodutils from modulegraphs import ModuleGraph @@ -72,7 +72,7 @@ type # has been used (i.e. the label should be emitted) isLoop: bool # whether it's a 'block' or 'while' - TGlobals = object + PGlobals = ref object of RootObj typeInfo, constants, code: Rope forwarded: seq[PSym] generatedSyms: IntSet @@ -80,7 +80,6 @@ type classes: seq[(PType, Rope)] unique: int # for temp identifier generation - PGlobals = ref TGlobals PProc = ref TProc TProc = object procDef: PNode @@ -537,7 +536,7 @@ proc genLineDir(p: PProc, n: PNode) = let line = toLinenumber(n.info) if optLineDir in p.options: lineF(p, "// line $2 \"$1\"$n", - [rope(toFilename(n.info)), rope(line)]) + [rope(toFilename(p.config, n.info)), rope(line)]) if {optStackTrace, optEndb} * p.options == {optStackTrace, optEndb} and ((p.prc == nil) or sfPure notin p.prc.flags): useMagic(p, "endb") @@ -606,11 +605,11 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = var length = sonsLen(n) var catchBranchesExist = length > 1 and n.sons[i].kind == nkExceptBranch if catchBranchesExist: - add(p.body, "++excHandler;" & tnl) + add(p.body, "++excHandler;\L") var tmpFramePtr = rope"F" if optStackTrace notin p.options: tmpFramePtr = p.getTemp(true) - line(p, tmpFramePtr & " = framePtr;" & tnl) + line(p, tmpFramePtr & " = framePtr;\L") lineF(p, "try {$n", []) var a: TCompRes gen(p, n.sons[0], a) @@ -648,15 +647,15 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = if catchBranchesExist: if not generalCatchBranchExists: useMagic(p, "reraiseException") - line(p, "else {" & tnl) - line(p, "\treraiseException();" & tnl) - line(p, "}" & tnl) + line(p, "else {\L") + line(p, "\treraiseException();\L") + line(p, "}\L") addf(p.body, "$1lastJSError = $1prevJSError;$n", [dollar]) - line(p, "} finally {" & tnl) + line(p, "} finally {\L") line(p, "framePtr = $1;$n" % [tmpFramePtr]) if i < length and n.sons[i].kind == nkFinally: genStmt(p, n.sons[i].sons[0]) - line(p, "}" & tnl) + line(p, "}\L") proc genRaiseStmt(p: PProc, n: PNode) = genLineDir(p, n) @@ -669,7 +668,7 @@ proc genRaiseStmt(p: PProc, n: PNode) = [a.rdLoc, makeJSString(typ.sym.name.s)]) else: useMagic(p, "reraiseException") - line(p, "reraiseException();" & tnl) + line(p, "reraiseException();\L") proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = var @@ -775,7 +774,7 @@ proc genAsmOrEmitStmt(p: PProc, n: PNode) = var r: TCompRes gen(p, it, r) p.body.add(r.rdLoc) - p.body.add tnl + p.body.add "\L" proc genIf(p: PProc, n: PNode, r: var TCompRes) = var cond, stmt: TCompRes @@ -798,7 +797,7 @@ proc genIf(p: PProc, n: PNode, r: var TCompRes) = p.nested: gen(p, it.sons[0], stmt) moveInto(p, stmt, r) lineF(p, "}$n", []) - line(p, repeat('}', toClose) & tnl) + line(p, repeat('}', toClose) & "\L") proc generateHeader(p: PProc, typ: PType): Rope = result = nil @@ -969,7 +968,7 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = internalAssert p.config, a.typ != etyBaseIndex and b.typ != etyBaseIndex r.address = a.res var typ = skipTypes(m.sons[0].typ, abstractPtrs) - if typ.kind == tyArray: first = firstOrd(typ.sons[0]) + if typ.kind == tyArray: first = firstOrd(p.config, typ.sons[0]) else: first = 0 if optBoundsCheck in p.options: useMagic(p, "chckIndx") @@ -1366,7 +1365,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = of tyBool: result = putToSeq("false", indirect) of tyArray: - let length = int(lengthOrd(t)) + let length = int(lengthOrd(p.config, t)) let e = elemType(t) let jsTyp = arrayTypeForElemType(e) if not jsTyp.isNil: @@ -1680,7 +1679,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = of mIsNil: unaryExpr(p, n, r, "", "($1 === null)") of mEnumToStr: genRepr(p, n, r) of mNew, mNewFinalize: genNew(p, n) - of mSizeOf: r.res = rope(getSize(n.sons[1].typ)) + of mSizeOf: r.res = rope(getSize(p.config, n.sons[1].typ)) of mChr, mArrToSeq: gen(p, n.sons[1], r) # nothing to do of mOrd: genOrd(p, n, r) of mLengthStr: @@ -1901,13 +1900,13 @@ proc frameCreate(p: PProc; procname, filename: Rope): Rope = result.add p.indentLine(ropes.`%`("framePtr = F;$n", [])) proc frameDestroy(p: PProc): Rope = - result = p.indentLine rope(("framePtr = F.prev;") & tnl) + result = p.indentLine rope(("framePtr = F.prev;") & "\L") proc genProcBody(p: PProc, prc: PSym): Rope = if hasFrameInfo(p): result = frameCreate(p, makeJSString(prc.owner.name.s & '.' & prc.name.s), - makeJSString(toFilename(prc.info))) + makeJSString(toFilename(p.config, prc.info))) else: result = nil if p.beforeRetNeeded: @@ -1926,7 +1925,7 @@ proc optionaLine(p: Rope): Rope = if p == nil: return nil else: - return p & tnl + return p & "\L" proc genProc(oldProc: PProc, prc: PSym): Rope = var @@ -1968,7 +1967,7 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = optionaLine(genProcBody(p, prc)), optionaLine(p.indentLine(returnStmt))] else: - result = ~tnl + result = ~"\L" if optHotCodeReloading in p.config.options: # Here, we introduce thunks that create the equivalent of a jump table @@ -2156,7 +2155,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = of nkRaiseStmt: genRaiseStmt(p, n) of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt, nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, - nkFromStmt, nkTemplateDef, nkMacroDef: discard + nkFromStmt, nkTemplateDef, nkMacroDef, nkStaticStmt: discard of nkPragma: genPragma(p, n) of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef: var s = n.sons[namePos].sym @@ -2170,14 +2169,14 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = discard "XXX to implement for better stack traces" else: internalError(p.config, n.info, "gen: unknown node type: " & $n.kind) -var globals: PGlobals # XXX global variable here - -proc newModule(module: PSym): BModule = +proc newModule(g: ModuleGraph; module: PSym): BModule = new(result) result.module = module result.sigConflicts = initCountTable[SigHash]() - if globals == nil: - globals = newGlobals() + if g.backend == nil: + g.backend = newGlobals() + result.graph = g + result.config = g.config proc genHeader(): Rope = result = ( @@ -2200,7 +2199,7 @@ proc genModule(p: PProc, n: PNode) = if optStackTrace in p.options: add(p.body, frameCreate(p, makeJSString("module " & p.module.module.name.s), - makeJSString(toFilename(p.module.module.info)))) + makeJSString(toFilename(p.config, p.module.module.info)))) genStmt(p, n) if optStackTrace in p.options: add(p.body, frameDestroy(p)) @@ -2210,6 +2209,7 @@ proc myProcess(b: PPassContext, n: PNode): PNode = let m = BModule(b) if passes.skipCodegen(m.config, n): return n if m.module == nil: internalError(m.config, n.info, "myProcess") + let globals = PGlobals(m.graph.backend) var p = newProc(globals, m, nil, m.module.options) p.unique = globals.unique genModule(p, n) @@ -2217,6 +2217,7 @@ proc myProcess(b: PPassContext, n: PNode): PNode = add(p.g.code, p.body) proc wholeCode(graph: ModuleGraph; m: BModule): Rope = + let globals = PGlobals(graph.backend) for prc in globals.forwarded: if not globals.generatedSyms.containsOrIncl(prc.id): var p = newProc(globals, m, nil, m.module.options) @@ -2261,8 +2262,9 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = var m = BModule(b) if passes.skipCodegen(m.config, n): return n if sfMainModule in m.module.flags: + let globals = PGlobals(graph.backend) let ext = "js" - let f = if globals.classes.len == 0: toFilename(FileIndex m.module.position) + let f = if globals.classes.len == 0: toFilename(m.config, FileIndex m.module.position) else: "nimsystem" let code = wholeCode(graph, m) let outfile = @@ -2275,15 +2277,8 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = for obj, content in items(globals.classes): genClass(m.config, obj, content, ext) -proc myOpenCached(graph: ModuleGraph; s: PSym, rd: PRodReader): PPassContext = - internalError(graph.config, "symbol files are not possible with the JS code generator") - result = nil - -proc myOpen(graph: ModuleGraph; s: PSym; cache: IdentCache): PPassContext = - var r = newModule(s) - r.graph = graph - r.config = graph.config - result = r +proc myOpen(graph: ModuleGraph; s: PSym): PPassContext = + result = newModule(graph, s) -const JSgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose) +const JSgenPass* = makePass(myOpen, myProcess, myClose) diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim index f9e4246eb..d86b09a03 100644 --- a/compiler/jstypes.nim +++ b/compiler/jstypes.nim @@ -25,7 +25,7 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope = else: s = nil for i in countup(0, length - 1): - if i > 0: add(s, ", " & tnl) + if i > 0: add(s, ", \L") add(s, genObjectFields(p, typ, n.sons[i])) result = ("{kind: 2, len: $1, offset: 0, " & "typ: null, name: null, sons: [$2]}") % [rope(length), s] @@ -56,15 +56,15 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope = else: add(u, rope(getOrdValue(b.sons[j]))) of nkElse: - u = rope(lengthOrd(field.typ)) + u = rope(lengthOrd(p.config, field.typ)) else: internalError(p.config, n.info, "genObjectFields(nkRecCase)") - if result != nil: add(result, ", " & tnl) + if result != nil: add(result, ", \L") addf(result, "[setConstr($1), $2]", [u, genObjectFields(p, typ, lastSon(b))]) result = ("{kind: 3, offset: \"$1\", len: $3, " & "typ: $2, name: $4, sons: [$5]}") % [ mangleName(p.module, field), s, - rope(lengthOrd(field.typ)), makeJSString(field.name.s), result] + rope(lengthOrd(p.config, field.typ)), makeJSString(field.name.s), result] else: internalError(p.config, n.info, "genObjectFields") proc objHasTypeField(t: PType): bool {.inline.} = @@ -85,7 +85,7 @@ proc genObjectInfo(p: PProc, typ: PType, name: Rope) = proc genTupleFields(p: PProc, typ: PType): Rope = var s: Rope = nil for i in 0 ..< typ.len: - if i > 0: add(s, ", " & tnl) + if i > 0: add(s, ", \L") s.addf("{kind: 1, offset: \"Field$1\", len: 0, " & "typ: $2, name: \"Field$1\", sons: null}", [i.rope, genTypeInfo(p, typ.sons[i])]) @@ -106,7 +106,7 @@ proc genEnumInfo(p: PProc, typ: PType, name: Rope) = for i in countup(0, length - 1): if (typ.n.sons[i].kind != nkSym): internalError(p.config, typ.n.info, "genEnumInfo") let field = typ.n.sons[i].sym - if i > 0: add(s, ", " & tnl) + if i > 0: add(s, ", \L") let extName = if field.ast == nil: field.name.s else: field.ast.strVal addf(s, "\"$1\": {kind: 1, offset: $1, typ: $2, name: $3, len: 0, sons: null}", [rope(field.position), name, makeJSString(extName)]) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 40e5bb6b0..0a4801150 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -11,7 +11,7 @@ import intsets, strutils, options, ast, astalgo, trees, treetab, msgs, - idents, renderer, types, magicsys, rodread, lowerings, tables, modulegraphs + idents, renderer, types, magicsys, lowerings, tables, modulegraphs, lineinfos discard """ The basic approach is that captured vars need to be put on the heap and @@ -136,7 +136,7 @@ proc createClosureIterStateType*(g: ModuleGraph; iter: PSym): PType = rawAddSon(result, intType) proc createStateField(g: ModuleGraph; iter: PSym): PSym = - result = newSym(skField, getIdent(":state"), iter, iter.info) + result = newSym(skField, getIdent(g.cache, ":state"), iter, iter.info) result.typ = createClosureIterStateType(g, iter) proc createEnvObj(g: ModuleGraph; owner: PSym; info: TLineInfo): PType = @@ -145,12 +145,12 @@ proc createEnvObj(g: ModuleGraph; owner: PSym; info: TLineInfo): PType = result = createObj(g, owner, info, final=false) rawAddField(result, createStateField(g, owner)) -proc getClosureIterResult*(iter: PSym): PSym = +proc getClosureIterResult*(g: ModuleGraph; iter: PSym): PSym = if resultPos < iter.ast.len: result = iter.ast.sons[resultPos].sym else: # XXX a bit hacky: - result = newSym(skResult, getIdent":result", iter, iter.info, {}) + result = newSym(skResult, getIdent(g.cache, ":result"), iter, iter.info, {}) result.typ = iter.typ.sons[0] incl(result.flags, sfUsed) iter.ast.add newSymNode(result) @@ -244,7 +244,7 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode = var env: PNode if owner.isIterator: let it = getHiddenParam(g, owner) - addUniqueField(it.typ.sons[0], hp) + addUniqueField(it.typ.sons[0], hp, g.cache) env = indirectAccess(newSymNode(it), hp, hp.info) else: let e = newSym(skLet, iter.name, owner, n.info) @@ -261,7 +261,7 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode = proc freshVarForClosureIter*(g: ModuleGraph; s, owner: PSym): PNode = let envParam = getHiddenParam(g, owner) let obj = envParam.typ.lastSon - addField(obj, s) + addField(obj, s, g.cache) var access = newSymNode(envParam) assert obj.kind == tyObject @@ -278,7 +278,7 @@ proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) = let s = n.sym if illegalCapture(s): localError(g.config, n.info, "illegal capture '$1' of type <$2> which is declared here: $3" % - [s.name.s, typeToString(s.typ), $s.info]) + [s.name.s, typeToString(s.typ), g.config$s.info]) elif owner.typ.callConv notin {ccClosure, ccDefault}: localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" % [s.name.s, owner.name.s, CallingConvToStr[owner.typ.callConv]]) @@ -325,7 +325,7 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) = if refObj == fieldType: localError(c.graph.config, dep.info, "internal error: invalid up reference computed") - let upIdent = getIdent(upName) + let upIdent = getIdent(c.graph.cache, upName) let upField = lookupInRecord(obj.n, upIdent) if upField != nil: if upField.typ != fieldType: @@ -366,7 +366,7 @@ proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) = let owner = if fn.kind == skIterator: fn else: fn.skipGenericOwner let t = c.getEnvTypeForOwner(owner, info) if cp == nil: - cp = newSym(skParam, getIdent(paramName), fn, fn.info) + cp = newSym(skParam, getIdent(c.graph.cache, paramName), fn, fn.info) incl(cp.flags, sfFromGeneric) cp.typ = t addHiddenParam(fn, cp) @@ -400,10 +400,10 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = let obj = getHiddenParam(c.graph, owner).typ.lastSon #let obj = c.getEnvTypeForOwner(s.owner).lastSon - if s.name == getIdent(":state"): + if s.name.id == getIdent(c.graph.cache, ":state").id: obj.n[0].sym.id = -s.id else: - addField(obj, s) + addField(obj, s, c.graph.cache) # but always return because the rest of the proc is only relevant when # ow != owner: return @@ -428,7 +428,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = if interestingVar(s) and not c.capturedVars.containsOrIncl(s.id): let obj = c.getEnvTypeForOwner(ow, n.info).lastSon #getHiddenParam(owner).typ.lastSon - addField(obj, s) + addField(obj, s, c.graph.cache) # create required upFields: var w = owner.skipGenericOwner if isInnerProc(w) or owner.isIterator: @@ -485,14 +485,14 @@ proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode = let field = getFieldFromObj(obj, s) if field != nil: return rawIndirectAccess(access, field, n.info) - let upField = lookupInRecord(obj.n, getIdent(upName)) + let upField = lookupInRecord(obj.n, getIdent(g.cache, upName)) if upField == nil: break access = rawIndirectAccess(access, upField, n.info) localError(g.config, n.info, "internal error: environment misses: " & s.name.s) result = n -proc newEnvVar(owner: PSym; typ: PType): PNode = - var v = newSym(skVar, getIdent(envName), owner, owner.info) +proc newEnvVar(cache: IdentCache; owner: PSym; typ: PType): PNode = + var v = newSym(skVar, getIdent(cache, envName), owner, owner.info) incl(v.flags, sfShadowed) v.typ = typ result = newSymNode(v) @@ -513,14 +513,14 @@ proc setupEnvVar(owner: PSym; d: DetectionPass; let envVarType = d.ownerToType.getOrDefault(owner.id) if envVarType.isNil: localError d.graph.config, owner.info, "internal error: could not determine closure type" - result = newEnvVar(owner, envVarType) + result = newEnvVar(d.graph.cache, owner, envVarType) c.envVars[owner.id] = result proc getUpViaParam(g: ModuleGraph; owner: PSym): PNode = let p = getHiddenParam(g, owner) result = p.newSymNode if owner.isIterator: - let upField = lookupInRecord(p.typ.lastSon.n, getIdent(upName)) + let upField = lookupInRecord(p.typ.lastSon.n, getIdent(g.cache, upName)) if upField == nil: localError(g.config, owner.info, "could not find up reference for closure iter") else: @@ -549,7 +549,7 @@ proc rawClosureCreation(owner: PSym; # add ``env.param = param`` result.add(newAsgnStmt(fieldAccess, newSymNode(local), env.info)) - let upField = lookupInRecord(env.typ.lastSon.n, getIdent(upName)) + let upField = lookupInRecord(env.typ.lastSon.n, getIdent(d.graph.cache, upName)) if upField != nil: let up = getUpViaParam(d.graph, owner) if up != nil and upField.typ == up.typ: @@ -565,13 +565,13 @@ proc closureCreationForIter(iter: PNode; d: DetectionPass; c: var LiftingPass): PNode = result = newNodeIT(nkStmtListExpr, iter.info, iter.sym.typ) let owner = iter.sym.skipGenericOwner - var v = newSym(skVar, getIdent(envName), owner, iter.info) + var v = newSym(skVar, getIdent(d.graph.cache, envName), owner, iter.info) incl(v.flags, sfShadowed) v.typ = getHiddenParam(d.graph, iter.sym).typ var vnode: PNode if owner.isIterator: let it = getHiddenParam(d.graph, owner) - addUniqueField(it.typ.sons[0], v) + addUniqueField(it.typ.sons[0], v, d.graph.cache) vnode = indirectAccess(newSymNode(it), v, v.info) else: vnode = v.newSymNode @@ -580,7 +580,7 @@ proc closureCreationForIter(iter: PNode; result.add(vs) result.add(newCall(getSysSym(d.graph, iter.info, "internalNew"), vnode)) - let upField = lookupInRecord(v.typ.lastSon.n, getIdent(upName)) + let upField = lookupInRecord(v.typ.lastSon.n, getIdent(d.graph.cache, upName)) if upField != nil: let u = setupEnvVar(owner, d, c) if u.typ == upField.typ: @@ -607,81 +607,6 @@ proc getStateField*(g: ModuleGraph; owner: PSym): PSym = proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass; c: var LiftingPass): PNode -proc transformYield(n: PNode; owner: PSym; d: DetectionPass; - c: var LiftingPass): PNode = - if c.inContainer > 0: - localError(d.graph.config, n.info, "invalid control flow: 'yield' within a constructor") - let state = getStateField(d.graph, owner) - assert state != nil - assert state.typ != nil - assert state.typ.n != nil - inc state.typ.n.sons[1].intVal - let stateNo = state.typ.n.sons[1].intVal - - var stateAsgnStmt = newNodeI(nkAsgn, n.info) - stateAsgnStmt.add(rawIndirectAccess(newSymNode(getEnvParam(owner)), - state, n.info)) - stateAsgnStmt.add(newIntTypeNode(nkIntLit, stateNo, - getSysType(d.graph, n.info, tyInt))) - - var retStmt = newNodeI(nkReturnStmt, n.info) - if n.sons[0].kind != nkEmpty: - var a = newNodeI(nkAsgn, n.sons[0].info) - var retVal = liftCapturedVars(n.sons[0], owner, d, c) - addSon(a, newSymNode(getClosureIterResult(owner))) - addSon(a, retVal) - retStmt.add(a) - else: - retStmt.add(emptyNode) - - var stateLabelStmt = newNodeI(nkState, n.info) - stateLabelStmt.add(newIntTypeNode(nkIntLit, stateNo, - getSysType(d.graph, n.info, tyInt))) - - result = newNodeI(nkStmtList, n.info) - result.add(stateAsgnStmt) - result.add(retStmt) - result.add(stateLabelStmt) - -proc transformReturn(n: PNode; owner: PSym; d: DetectionPass; - c: var LiftingPass): PNode = - let state = getStateField(d.graph, owner) - result = newNodeI(nkStmtList, n.info) - var stateAsgnStmt = newNodeI(nkAsgn, n.info) - stateAsgnStmt.add(rawIndirectAccess(newSymNode(getEnvParam(owner)), - state, n.info)) - stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(d.graph, n.info, tyInt))) - result.add(stateAsgnStmt) - result.add(n) - -proc wrapIterBody(g: ModuleGraph; n: PNode; owner: PSym): PNode = - if not owner.isIterator: return n - when false: - # unfortunately control flow is still convoluted and we can end up - # multiple times here for the very same iterator. We shield against this - # with some rather primitive check for now: - if n.kind == nkStmtList and n.len > 0: - if n.sons[0].kind == nkGotoState: return n - if n.len > 1 and n[1].kind == nkStmtList and n[1].len > 0 and - n[1][0].kind == nkGotoState: - return n - let info = n.info - result = newNodeI(nkStmtList, info) - var gs = newNodeI(nkGotoState, info) - gs.add(rawIndirectAccess(newSymNode(getHiddenParam(g, owner)), getStateField(g, owner), info)) - result.add(gs) - var state0 = newNodeI(nkState, info) - state0.add(newIntNode(nkIntLit, 0)) - result.add(state0) - - result.add(n) - - var stateAsgnStmt = newNodeI(nkAsgn, info) - stateAsgnStmt.add(rawIndirectAccess(newSymNode(getHiddenParam(g, owner)), - getStateField(g, owner), info)) - stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(g, info, tyInt))) - result.add(stateAsgnStmt) - proc symToClosure(n: PNode; owner: PSym; d: DetectionPass; c: var LiftingPass): PNode = let s = n.sym @@ -703,7 +628,7 @@ proc symToClosure(n: PNode; owner: PSym; d: DetectionPass; if access.typ == wanted: return makeClosure(d.graph, s, access, n.info) let obj = access.typ.sons[0] - let upField = lookupInRecord(obj.n, getIdent(upName)) + let upField = lookupInRecord(obj.n, getIdent(d.graph.cache, upName)) if upField == nil: localError(d.graph.config, n.info, "internal error: no environment found") return n @@ -722,8 +647,6 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass; let oldInContainer = c.inContainer c.inContainer = 0 var body = liftCapturedVars(s.getBody, s, d, c) - if oldIterTransf in d.graph.config.features: - body = wrapIterBody(d.graph, body, s) if c.envvars.getOrDefault(s.id).isNil: s.ast.sons[bodyPos] = body else: @@ -766,11 +689,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass; if n[1].kind == nkClosure: result = n[1] else: if owner.isIterator: - if oldIterTransf in d.graph.config.features and n.kind == nkYieldStmt: - return transformYield(n, owner, d, c) - elif oldIterTransf in d.graph.config.features and n.kind == nkReturnStmt: - return transformReturn(n, owner, d, c) - elif nfLL in n.flags: + if nfLL in n.flags: # special case 'when nimVm' due to bug #3636: n.sons[1] = liftCapturedVars(n[1], owner, d, c) return @@ -811,7 +730,7 @@ proc liftIterToProc*(g: ModuleGraph; fn: PSym; body: PNode; ptrType: PType): PNo fn.typ.callConv = ccClosure d.ownerToType[fn.id] = ptrType detectCapturedVars(body, fn, d) - result = wrapIterBody(g, liftCapturedVars(body, fn, d, c), fn) + result = liftCapturedVars(body, fn, d, c) fn.kind = oldKind fn.typ.callConv = oldCC @@ -840,9 +759,6 @@ proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool): PN result = liftCapturedVars(body, fn, d, c) if c.envvars.getOrDefault(fn.id) != nil: result = newTree(nkStmtList, rawClosureCreation(fn, d, c), result) - - if oldIterTransf in g.config.features: - result = wrapIterBody(g, result, fn) else: result = body #if fn.name.s == "get2": @@ -932,7 +848,7 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode = body[i].sym.kind = skLet addSon(vpart, body[i]) - addSon(vpart, ast.emptyNode) # no explicit type + addSon(vpart, newNodeI(nkEmpty, body.info)) # no explicit type if not env.isNil: call.sons[0] = makeClosure(g, call.sons[0].sym, env.newSymNode, body.info) addSon(vpart, call) @@ -942,12 +858,12 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode = var bs = newNodeI(nkBreakState, body.info) bs.addSon(call.sons[0]) - let ibs = newNode(nkIfStmt) - let elifBranch = newNode(nkElifBranch) + let ibs = newNodeI(nkIfStmt, body.info) + let elifBranch = newNodeI(nkElifBranch, body.info) elifBranch.add(bs) - let br = newNode(nkBreakStmt) - br.add(emptyNode) + let br = newNodeI(nkBreakStmt, body.info) + br.add(g.emptyNode) elifBranch.add(br) ibs.add(elifBranch) diff --git a/compiler/layouter.nim b/compiler/layouter.nim new file mode 100644 index 000000000..36ad08696 --- /dev/null +++ b/compiler/layouter.nim @@ -0,0 +1,274 @@ +# +# +# The Nim Compiler +# (c) Copyright 2018 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Layouter for nimpretty. + +import idents, lexer, lineinfos, llstream, options, msgs, strutils +from os import changeFileExt + +const + MaxLineLen = 80 + LineCommentColumn = 30 + +type + SplitKind = enum + splitComma, splitParLe, splitAnd, splitOr, splitIn, splitBinary + + SemicolonKind = enum + detectSemicolonKind, useSemicolon, dontTouch + + Emitter* = object + config: ConfigRef + fid: FileIndex + lastTok: TTokType + inquote: bool + semicolons: SemicolonKind + col, lastLineNumber, lineSpan, indentLevel, indWidth: int + nested: int + doIndentMore*: int + content: string + indentStack: seq[int] + fixedUntil: int # marks where we must not go in the content + altSplitPos: array[SplitKind, int] # alternative split positions + +proc openEmitter*(em: var Emitter, cache: IdentCache; + config: ConfigRef, fileIdx: FileIndex) = + let fullPath = config.toFullPath(fileIdx) + em.indWidth = getIndentWidth(fileIdx, llStreamOpen(fullPath, fmRead), + cache, config) + if em.indWidth == 0: em.indWidth = 2 + em.config = config + em.fid = fileIdx + em.lastTok = tkInvalid + em.inquote = false + em.col = 0 + em.content = newStringOfCap(16_000) + em.indentStack = newSeqOfCap[int](30) + em.indentStack.add 0 + +proc closeEmitter*(em: var Emitter) = + var f = llStreamOpen(em.config.outFile, fmWrite) + if f == nil: + rawMessage(em.config, errGenerated, "cannot open file: " & em.config.outFile) + f.llStreamWrite em.content + llStreamClose(f) + +proc countNewlines(s: string): int = + result = 0 + for i in 0..<s.len: + if s[i] == '\L': inc result + +proc calcCol(em: var Emitter; s: string) = + var i = s.len-1 + em.col = 0 + while i >= 0 and s[i] != '\L': + dec i + inc em.col + +template wr(x) = + em.content.add x + inc em.col, x.len + +template goodCol(col): bool = col in 40..MaxLineLen + +const + openPars = {tkParLe, tkParDotLe, + tkBracketLe, tkBracketLeColon, tkCurlyDotLe, + tkCurlyLe} + splitters = openPars + {tkComma, tkSemicolon} + oprSet = {tkOpr, tkDiv, tkMod, tkShl, tkShr, tkIn, tkNotin, tkIs, + tkIsnot, tkNot, tkOf, tkAs, tkDotDot, tkAnd, tkOr, tkXor} + +template rememberSplit(kind) = + if goodCol(em.col): + em.altSplitPos[kind] = em.content.len + +template moreIndent(em): int = + (if em.doIndentMore > 0: em.indWidth*2 else: em.indWidth) + +proc softLinebreak(em: var Emitter, lit: string) = + # XXX Use an algorithm that is outlined here: + # https://llvm.org/devmtg/2013-04/jasper-slides.pdf + # +2 because we blindly assume a comma or ' &' might follow + if not em.inquote and em.col+lit.len+2 >= MaxLineLen: + if em.lastTok in splitters: + while em.content.len > 0 and em.content[em.content.high] == ' ': + setLen(em.content, em.content.len-1) + wr("\L") + em.col = 0 + for i in 1..em.indentLevel+moreIndent(em): wr(" ") + else: + # search backwards for a good split position: + for a in em.altSplitPos: + if a > em.fixedUntil: + var spaces = 0 + while a+spaces < em.content.len and em.content[a+spaces] == ' ': + inc spaces + if spaces > 0: delete(em.content, a, a+spaces-1) + let ws = "\L" & repeat(' ',em.indentLevel+moreIndent(em)) + em.col = em.content.len - a + em.content.insert(ws, a) + break + +proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = + + template endsInWhite(em): bool = + em.content.len == 0 or em.content[em.content.high] in {' ', '\L'} + template endsInAlpha(em): bool = + em.content.len > 0 and em.content[em.content.high] in SymChars+{'_'} + + proc emitComment(em: var Emitter; tok: TToken) = + let lit = strip fileSection(em.config, em.fid, tok.commentOffsetA, tok.commentOffsetB) + em.lineSpan = countNewlines(lit) + if em.lineSpan > 0: calcCol(em, lit) + if not endsInWhite(em): + wr(" ") + if em.lineSpan == 0 and max(em.col, LineCommentColumn) + lit.len <= MaxLineLen: + for i in 1 .. LineCommentColumn - em.col: wr(" ") + wr lit + + var preventComment = false + if tok.tokType == tkComment and tok.line == em.lastLineNumber and tok.indent >= 0: + # we have an inline comment so handle it before the indentation token: + emitComment(em, tok) + preventComment = true + em.fixedUntil = em.content.high + + elif tok.indent >= 0: + if em.lastTok in (splitters + oprSet): + em.indentLevel = tok.indent + else: + if tok.indent > em.indentStack[^1]: + em.indentStack.add tok.indent + else: + # dedent? + while em.indentStack.len > 1 and em.indentStack[^1] > tok.indent: + discard em.indentStack.pop() + em.indentLevel = em.indentStack.high * em.indWidth + #[ we only correct the indentation if it is not in an expression context, + so that code like + + const splitters = {tkComma, tkSemicolon, tkParLe, tkParDotLe, + tkBracketLe, tkBracketLeColon, tkCurlyDotLe, + tkCurlyLe} + + is not touched. + ]# + # remove trailing whitespace: + while em.content.len > 0 and em.content[em.content.high] == ' ': + setLen(em.content, em.content.len-1) + wr("\L") + for i in 2..tok.line - em.lastLineNumber: wr("\L") + em.col = 0 + for i in 1..em.indentLevel: + wr(" ") + em.fixedUntil = em.content.high + + case tok.tokType + of tokKeywordLow..tokKeywordHigh: + if endsInAlpha(em): + wr(" ") + elif not em.inquote and not endsInWhite(em) and + em.lastTok notin openPars: + #and tok.tokType in oprSet + wr(" ") + + if not em.inquote: + wr(TokTypeToStr[tok.tokType]) + + case tok.tokType + of tkAnd: rememberSplit(splitAnd) + of tkOr: rememberSplit(splitOr) + of tkIn, tkNotin: + rememberSplit(splitIn) + wr(" ") + else: discard + else: + # keywords in backticks are not normalized: + wr(tok.ident.s) + + of tkColon: + wr(TokTypeToStr[tok.tokType]) + wr(" ") + of tkSemicolon, tkComma: + wr(TokTypeToStr[tok.tokType]) + rememberSplit(splitComma) + wr(" ") + of tkParDotLe, tkParLe, tkBracketDotLe, tkBracketLe, + tkCurlyLe, tkCurlyDotLe, tkBracketLeColon: + if tok.strongSpaceA > 0 and not em.endsInWhite: + wr(" ") + wr(TokTypeToStr[tok.tokType]) + rememberSplit(splitParLe) + of tkParRi, + tkBracketRi, tkCurlyRi, + tkBracketDotRi, + tkCurlyDotRi, + tkParDotRi, + tkColonColon, tkDot: + wr(TokTypeToStr[tok.tokType]) + of tkEquals: + if not em.inquote and not em.endsInWhite: wr(" ") + wr(TokTypeToStr[tok.tokType]) + if not em.inquote: wr(" ") + of tkOpr, tkDotDot: + if tok.strongSpaceA == 0 and tok.strongSpaceB == 0: + # if not surrounded by whitespace, don't produce any whitespace either: + wr(tok.ident.s) + else: + if not em.endsInWhite: wr(" ") + wr(tok.ident.s) + template isUnary(tok): bool = + tok.strongSpaceB == 0 and tok.strongSpaceA > 0 + + if not isUnary(tok): + wr(" ") + rememberSplit(splitBinary) + of tkAccent: + if not em.inquote and endsInAlpha(em): wr(" ") + wr(TokTypeToStr[tok.tokType]) + em.inquote = not em.inquote + of tkComment: + if not preventComment: + emitComment(em, tok) + of tkIntLit..tkStrLit, tkRStrLit, tkTripleStrLit, tkGStrLit, tkGTripleStrLit, tkCharLit: + let lit = fileSection(em.config, em.fid, tok.offsetA, tok.offsetB) + softLinebreak(em, lit) + if endsInAlpha(em) and tok.tokType notin {tkGStrLit, tkGTripleStrLit}: wr(" ") + em.lineSpan = countNewlines(lit) + if em.lineSpan > 0: calcCol(em, lit) + wr lit + of tkEof: discard + else: + let lit = if tok.ident != nil: tok.ident.s else: tok.literal + softLinebreak(em, lit) + if endsInAlpha(em): wr(" ") + wr lit + + em.lastTok = tok.tokType + em.lastLineNumber = tok.line + em.lineSpan + em.lineSpan = 0 + +proc starWasExportMarker*(em: var Emitter) = + if em.content.endsWith(" * "): + setLen(em.content, em.content.len-3) + em.content.add("*") + dec em.col, 2 + +proc commaWasSemicolon*(em: var Emitter) = + if em.semicolons == detectSemicolonKind: + em.semicolons = if em.content.endsWith(", "): dontTouch else: useSemicolon + if em.semicolons == useSemicolon and em.content.endsWith(", "): + setLen(em.content, em.content.len-2) + em.content.add("; ") + +proc curlyRiWasPragma*(em: var Emitter) = + if em.content.endsWith("}"): + setLen(em.content, em.content.len-1) + em.content.add(".}") diff --git a/compiler/lexer.nim b/compiler/lexer.nim index d498cf4af..c5afa6e97 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -17,7 +17,7 @@ import hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream, - wordrecg, configuration + wordrecg, lineinfos const MaxLineLength* = 80 # lines longer than this lead to a warning @@ -273,10 +273,10 @@ template tokenBegin(tok, pos) {.dirty.} = template tokenEnd(tok, pos) {.dirty.} = when defined(nimsuggest): let colB = getColNumber(L, pos)+1 - if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and - L.lineNumber == gTrackPos.line.int and L.config.ideCmd in {ideSug, ideCon}: + if L.fileIdx == L.config.m.trackPos.fileIndex and L.config.m.trackPos.col in colA..colB and + L.lineNumber == L.config.m.trackPos.line.int and L.config.ideCmd in {ideSug, ideCon}: L.cursor = CursorPosition.InToken - gTrackPos.col = colA.int16 + L.config.m.trackPos.col = colA.int16 colA = 0 when defined(nimpretty): tok.offsetB = L.offsetBase + pos @@ -284,10 +284,10 @@ template tokenEnd(tok, pos) {.dirty.} = template tokenEndIgnore(tok, pos) = when defined(nimsuggest): let colB = getColNumber(L, pos) - if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and - L.lineNumber == gTrackPos.line.int and L.config.ideCmd in {ideSug, ideCon}: - gTrackPos.fileIndex = trackPosInvalidFileIdx - gTrackPos.line = 0'u16 + if L.fileIdx == L.config.m.trackPos.fileIndex and L.config.m.trackPos.col in colA..colB and + L.lineNumber == L.config.m.trackPos.line.int and L.config.ideCmd in {ideSug, ideCon}: + L.config.m.trackPos.fileIndex = trackPosInvalidFileIdx + L.config.m.trackPos.line = 0'u16 colA = 0 when defined(nimpretty): tok.offsetB = L.offsetBase + pos @@ -298,11 +298,11 @@ template tokenEndPrevious(tok, pos) = # to the token that came before that, but only if we haven't detected # the cursor in a string literal or comment: let colB = getColNumber(L, pos) - if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and - L.lineNumber == gTrackPos.line.int and L.config.ideCmd in {ideSug, ideCon}: + if L.fileIdx == L.config.m.trackPos.fileIndex and L.config.m.trackPos.col in colA..colB and + L.lineNumber == L.config.m.trackPos.line.int and L.config.ideCmd in {ideSug, ideCon}: L.cursor = CursorPosition.BeforeToken - gTrackPos = L.previousToken - gTrackPosAttached = true + L.config.m.trackPos = L.previousToken + L.config.m.trackPosAttached = true colA = 0 when defined(nimpretty): tok.offsetB = L.offsetBase + pos @@ -630,14 +630,14 @@ proc getEscapedChar(L: var TLexer, tok: var TToken) = if L.config.oldNewlines: if tok.tokType == tkCharLit: lexMessage(L, errGenerated, "\\n not allowed in character literal") - add(tok.literal, tnl) + add(tok.literal, L.config.target.tnl) else: add(tok.literal, '\L') inc(L.bufpos) of 'p', 'P': if tok.tokType == tkCharLit: lexMessage(L, errGenerated, "\\p not allowed in character literal") - add(tok.literal, tnl) + add(tok.literal, L.config.target.tnl) inc(L.bufpos) of 'r', 'R', 'c', 'C': add(tok.literal, CR) @@ -714,11 +714,6 @@ proc handleCRLF(L: var TLexer, pos: int): int = if col > MaxLineLength: lexMessagePos(L, hintLineTooLong, pos) - if optEmbedOrigSrc in L.config.globalOptions: - let lineStart = cast[ByteAddress](L.buf) + L.lineStart - let line = newString(cast[cstring](lineStart), col) - addSourceLine(L.fileIdx, line) - case L.buf[pos] of CR: registerLine() @@ -872,7 +867,7 @@ proc getOperator(L: var TLexer, tok: var TToken) = if buf[pos] in {CR, LF, nimlexbase.EndOfFile}: tok.strongSpaceB = -1 -proc newlineFollows*(L: var TLexer): bool = +proc newlineFollows*(L: TLexer): bool = var pos = L.bufpos var buf = L.buf while true: @@ -954,6 +949,8 @@ proc skipMultiLineComment(L: var TLexer; tok: var TToken; start: int; if isDoc or defined(nimpretty): tok.literal.add buf[pos] inc(pos) L.bufpos = pos + when defined(nimpretty): + tok.commentOffsetB = L.offsetBase + pos - 1 proc scanComment(L: var TLexer, tok: var TToken) = var pos = L.bufpos @@ -962,6 +959,9 @@ proc scanComment(L: var TLexer, tok: var TToken) = # iNumber contains the number of '\n' in the token tok.iNumber = 0 assert buf[pos+1] == '#' + when defined(nimpretty): + tok.commentOffsetA = L.offsetBase + pos - 1 + if buf[pos+2] == '[': skipMultiLineComment(L, tok, pos+3, true) return @@ -1001,6 +1001,8 @@ proc scanComment(L: var TLexer, tok: var TToken) = tokenEndIgnore(tok, pos) break L.bufpos = pos + when defined(nimpretty): + tok.commentOffsetB = L.offsetBase + pos - 1 proc skip(L: var TLexer, tok: var TToken) = var pos = L.bufpos @@ -1021,6 +1023,9 @@ proc skip(L: var TLexer, tok: var TToken) = inc(pos) of CR, LF: tokenEndPrevious(tok, pos) + when defined(nimpretty): + # we are not yet in a comment, so update the comment token's line information: + if not hasComment: inc tok.line pos = handleCRLF(L, pos) buf = L.buf var indent = 0 @@ -1060,7 +1065,7 @@ proc skip(L: var TLexer, tok: var TToken) = L.bufpos = pos when defined(nimpretty): if hasComment: - tok.commentOffsetB = L.offsetBase + pos + tok.commentOffsetB = L.offsetBase + pos - 1 tok.tokType = tkComment if gIndentationWidth <= 0: gIndentationWidth = tok.indent @@ -1121,9 +1126,9 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = else: tok.tokType = tkParLe when defined(nimsuggest): - if L.fileIdx == gTrackPos.fileIndex and tok.col < gTrackPos.col and - tok.line == gTrackPos.line.int and L.config.ideCmd == ideCon: - gTrackPos.col = tok.col.int16 + if L.fileIdx == L.config.m.trackPos.fileIndex and tok.col < L.config.m.trackPos.col and + tok.line == L.config.m.trackPos.line.int and L.config.ideCmd == ideCon: + L.config.m.trackPos.col = tok.col.int16 of ')': tok.tokType = tkParRi inc(L.bufpos) @@ -1142,11 +1147,11 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = inc(L.bufpos) of '.': when defined(nimsuggest): - if L.fileIdx == gTrackPos.fileIndex and tok.col+1 == gTrackPos.col and - tok.line == gTrackPos.line.int and L.config.ideCmd == ideSug: + if L.fileIdx == L.config.m.trackPos.fileIndex and tok.col+1 == L.config.m.trackPos.col and + tok.line == L.config.m.trackPos.line.int and L.config.ideCmd == ideSug: tok.tokType = tkDot L.cursor = CursorPosition.InToken - gTrackPos.col = tok.col.int16 + L.config.m.trackPos.col = tok.col.int16 inc(L.bufpos) atTokenEnd() return @@ -1215,3 +1220,15 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = lexMessage(L, errGenerated, "invalid token: " & c & " (\\" & $(ord(c)) & ')') inc(L.bufpos) atTokenEnd() + +proc getIndentWidth*(fileIdx: FileIndex, inputstream: PLLStream; + cache: IdentCache; config: ConfigRef): int = + var lex: TLexer + var tok: TToken + initToken(tok) + openLexer(lex, fileIdx, inputstream, cache, config) + while true: + rawGetTok(lex, tok) + result = tok.indent + if result > 0 or tok.tokType == tkEof: break + closeLexer(lex) diff --git a/compiler/liftlocals.nim b/compiler/liftlocals.nim index 4603d357b..ae789cd88 100644 --- a/compiler/liftlocals.nim +++ b/compiler/liftlocals.nim @@ -11,7 +11,7 @@ import intsets, strutils, options, ast, astalgo, msgs, - idents, renderer, types, lowerings + idents, renderer, types, lowerings, lineinfos from pragmas import getPragmaVal from wordrecg import wLiftLocals @@ -20,13 +20,14 @@ type Ctx = object partialParam: PSym objType: PType + cache: IdentCache proc interestingVar(s: PSym): bool {.inline.} = result = s.kind in {skVar, skLet, skTemp, skForVar, skResult} and sfGlobal notin s.flags proc lookupOrAdd(c: var Ctx; s: PSym; info: TLineInfo): PNode = - let field = addUniqueField(c.objType, s) + let field = addUniqueField(c.objType, s, c.cache) var deref = newNodeI(nkHiddenDeref, info) deref.typ = c.objType add(deref, newSymNode(c.partialParam, info)) @@ -52,7 +53,7 @@ proc lookupParam(params, dest: PNode): PSym = if params[i].kind == nkSym and params[i].sym.name.id == dest.ident.id: return params[i].sym -proc liftLocalsIfRequested*(prc: PSym; n: PNode; conf: ConfigRef): PNode = +proc liftLocalsIfRequested*(prc: PSym; n: PNode; cache: IdentCache; conf: ConfigRef): PNode = let liftDest = getPragmaVal(prc.ast, wLiftLocals) if liftDest == nil: return n let partialParam = lookupParam(prc.typ.n, liftDest) @@ -64,7 +65,7 @@ proc liftLocalsIfRequested*(prc: PSym; n: PNode; conf: ConfigRef): PNode = if objType.kind != tyObject or tfPartial notin objType.flags: localError(conf, liftDest.info, "parameter '$1' is not a pointer to a partial object" % $liftDest) return n - var c = Ctx(partialParam: partialParam, objType: objType) + var c = Ctx(partialParam: partialParam, objType: objType, cache: cache) let w = newTree(nkStmtList, n) liftLocals(w, 0, c) result = w[0] diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim new file mode 100644 index 000000000..cad1fe6aa --- /dev/null +++ b/compiler/lineinfos.nim @@ -0,0 +1,267 @@ +# +# +# The Nim Compiler +# (c) Copyright 2018 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module contains the ``TMsgKind`` enum as well as the +## ``TLineInfo`` object. + +import ropes, tables + +const + explanationsBaseUrl* = "https://nim-lang.org/docs/manual" + +type + TMsgKind* = enum + errUnknown, errInternal, errIllFormedAstX, errCannotOpenFile, + errXExpected, + errGridTableNotImplemented, + errGeneralParseError, + errNewSectionExpected, + errInvalidDirectiveX, + errGenerated, + errUser, + warnCannotOpenFile, + warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, + warnDeprecated, warnConfigDeprecated, + warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel, + warnUnknownSubstitutionX, warnLanguageXNotSupported, + warnFieldXNotSupported, warnCommentXIgnored, + warnTypelessParam, + warnUseBase, warnWriteToForeignHeap, warnUnsafeCode, + warnEachIdentIsTuple, warnShadowIdent, + warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, + warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed, + warnInconsistentSpacing, warnUser, + hintSuccess, hintSuccessX, + hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, + hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, + hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath, + hintConditionAlwaysTrue, hintName, hintPattern, + hintExecuting, hintLinking, hintDependency, + hintSource, hintPerformance, hintStackTrace, hintGCStats, + hintGlobalVar, + hintUser, hintUserRaw + +const + MsgKindToStr*: array[TMsgKind, string] = [ + errUnknown: "unknown error", + errInternal: "internal error: $1", + errIllFormedAstX: "illformed AST: $1", + errCannotOpenFile: "cannot open '$1'", + errXExpected: "'$1' expected", + errGridTableNotImplemented: "grid table is not implemented", + errGeneralParseError: "general parse error", + errNewSectionExpected: "new section expected", + errInvalidDirectiveX: "invalid directive: '$1'", + errGenerated: "$1", + errUser: "$1", + warnCannotOpenFile: "cannot open '$1'", + warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored", + warnXIsNeverRead: "'$1' is never read", + warnXmightNotBeenInit: "'$1' might not have been initialized", + warnDeprecated: "$1 is deprecated", + warnConfigDeprecated: "config file '$1' is deprecated", + warnSmallLshouldNotBeUsed: "'l' should not be used as an identifier; may look like '1' (one)", + warnUnknownMagic: "unknown magic '$1' might crash the compiler", + warnRedefinitionOfLabel: "redefinition of label '$1'", + warnUnknownSubstitutionX: "unknown substitution '$1'", + warnLanguageXNotSupported: "language '$1' not supported", + warnFieldXNotSupported: "field '$1' not supported", + warnCommentXIgnored: "comment '$1' ignored", + warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template'", + warnUseBase: "use {.base.} for base methods; baseless methods are deprecated", + warnWriteToForeignHeap: "write to foreign heap", + warnUnsafeCode: "unsafe code: '$1'", + warnEachIdentIsTuple: "each identifier is a tuple", + warnShadowIdent: "shadowed identifier: '$1'", + warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future.", + warnProveField: "cannot prove that field '$1' is accessible", + warnProveIndex: "cannot prove index '$1' is valid", + warnGcUnsafe: "not GC-safe: '$1'", + warnGcUnsafe2: "$1", + warnUninit: "'$1' might not have been initialized", + warnGcMem: "'$1' uses GC'ed memory", + warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future.", + warnLockLevel: "$1", + warnResultShadowed: "Special variable 'result' is shadowed.", + warnInconsistentSpacing: "Number of spaces around '$#' is not consistent", + warnUser: "$1", + hintSuccess: "operation successful", + hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)", + hintLineTooLong: "line too long", + hintXDeclaredButNotUsed: "'$1' is declared but not used", + hintConvToBaseNotNeeded: "conversion to base object is not needed", + hintConvFromXtoItselfNotNeeded: "conversion from $1 to itself is pointless", + hintExprAlwaysX: "expression evaluates always to '$1'", + hintQuitCalled: "quit() called", + hintProcessing: "$1", + hintCodeBegin: "generated code listing:", + hintCodeEnd: "end of listing", + hintConf: "used config file '$1'", + hintPath: "added path: '$1'", + hintConditionAlwaysTrue: "condition is always true: '$1'", + hintName: "name should be: '$1'", + hintPattern: "$1", + hintExecuting: "$1", + hintLinking: "", + hintDependency: "$1", + hintSource: "$1", + hintPerformance: "$1", + hintStackTrace: "$1", + hintGCStats: "$1", + hintGlobalVar: "global variable declared here", + hintUser: "$1", + hintUserRaw: "$1"] + +const + WarningsToStr* = ["CannotOpenFile", "OctalEscape", + "XIsNeverRead", "XmightNotBeenInit", + "Deprecated", "ConfigDeprecated", + "SmallLshouldNotBeUsed", "UnknownMagic", + "RedefinitionOfLabel", "UnknownSubstitutionX", + "LanguageXNotSupported", "FieldXNotSupported", + "CommentXIgnored", + "TypelessParam", "UseBase", "WriteToForeignHeap", + "UnsafeCode", "EachIdentIsTuple", "ShadowIdent", + "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit", + "GcMem", "Destructor", "LockLevel", "ResultShadowed", + "Spacing", "User"] + + HintsToStr* = ["Success", "SuccessX", "LineTooLong", + "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", + "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf", + "Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency", + "Source", "Performance", "StackTrace", "GCStats", "GlobalVar", + "User", "UserRaw"] + +const + fatalMin* = errUnknown + fatalMax* = errInternal + errMin* = errUnknown + errMax* = errUser + warnMin* = warnCannotOpenFile + warnMax* = pred(hintSuccess) + hintMin* = hintSuccess + hintMax* = high(TMsgKind) + +static: + doAssert HintsToStr.len == ord(hintMax) - ord(hintMin) + 1 + doAssert WarningsToStr.len == ord(warnMax) - ord(warnMin) + 1 + +type + TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints + TNoteKinds* = set[TNoteKind] + +const + NotesVerbosity*: array[0..3, TNoteKinds] = [ + {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit, + warnProveField, warnProveIndex, + warnGcUnsafe, + hintSuccessX, hintPath, hintConf, + hintProcessing, hintPattern, + hintDependency, + hintExecuting, hintLinking, + hintCodeBegin, hintCodeEnd, + hintSource, hintStackTrace, + hintGlobalVar, hintGCStats}, + {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit, + warnProveField, warnProveIndex, + warnGcUnsafe, + hintPath, + hintDependency, + hintCodeBegin, hintCodeEnd, + hintSource, hintStackTrace, + hintGlobalVar, hintGCStats}, + {low(TNoteKind)..high(TNoteKind)} - {hintStackTrace, warnUninit}, + {low(TNoteKind)..high(TNoteKind)}] + +const + errXMustBeCompileTime* = "'$1' can only be used in compile-time context" + errArgsNeedRunOption* = "arguments can only be given if the '--run' option is selected" + +type + TFileInfo* = object + fullPath*: string # This is a canonical full filesystem path + projPath*: string # This is relative to the project's root + shortName*: string # short name of the module + quotedName*: Rope # cached quoted short name for codegen + # purposes + quotedFullName*: Rope # cached quoted full name for codegen + # purposes + + lines*: seq[string] # the source code of the module + # used for better error messages and + # embedding the original source in the + # generated code + dirtyfile*: string # the file that is actually read into memory + # and parsed; usually "" but is used + # for 'nimsuggest' + hash*: string # the checksum of the file + dirty*: bool # for 'nimfix' / 'nimpretty' like tooling + when defined(nimpretty): + fullContent*: string + FileIndex* = distinct int32 + TLineInfo* = object # This is designed to be as small as possible, + # because it is used + # in syntax nodes. We save space here by using + # two int16 and an int32. + # On 64 bit and on 32 bit systems this is + # only 8 bytes. + line*: uint16 + col*: int16 + fileIndex*: FileIndex + when defined(nimpretty): + offsetA*, offsetB*: int + commentOffsetA*, commentOffsetB*: int + + TErrorOutput* = enum + eStdOut + eStdErr + + TErrorOutputs* = set[TErrorOutput] + + ERecoverableError* = object of ValueError + ESuggestDone* = object of Exception + +proc `==`*(a, b: FileIndex): bool {.borrow.} + +const + InvalidFileIDX* = FileIndex(-1) + +proc unknownLineInfo*(): TLineInfo = + result.line = uint16(0) + result.col = int16(-1) + result.fileIndex = InvalidFileIDX + +type + Severity* {.pure.} = enum ## VS Code only supports these three + Hint, Warning, Error + +const trackPosInvalidFileIdx* = FileIndex(-2) # special marker so that no suggestions + # are produced within comments and string literals + +type + MsgConfig* = object ## does not need to be stored in the incremental cache + trackPos*: TLineInfo + trackPosAttached*: bool ## whether the tracking position was attached to + ## some close token. + + errorOutputs*: TErrorOutputs + msgContext*: seq[TLineInfo] + lastError*: TLineInfo + filenameToIndexTbl*: Table[string, FileIndex] + fileInfos*: seq[TFileInfo] + systemFileIdx*: FileIndex + + +proc initMsgConfig*(): MsgConfig = + result.msgContext = @[] + result.lastError = unknownLineInfo() + result.filenameToIndexTbl = initTable[string, FileIndex]() + result.fileInfos = @[] + result.errorOutputs = {eStdOut, eStdErr} diff --git a/compiler/nimfix/pretty.nim b/compiler/linter.nim index 96429ad53..7c9cdec83 100644 --- a/compiler/nimfix/pretty.nim +++ b/compiler/linter.nim @@ -13,9 +13,18 @@ import strutils, os, intsets, strtabs -import ".." / [options, ast, astalgo, msgs, semdata, ropes, idents, - configuration] -import prettybase +import options, ast, astalgo, msgs, semdata, ropes, idents, + lineinfos + +const + Letters* = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '_'} + +proc identLen*(line: string, start: int): int = + while start+result < line.len and line[start+result] in Letters: + inc result + +when false: + import prettybase type StyleCheck* {.pure.} = enum None, Warn, Auto @@ -27,19 +36,19 @@ var proc overwriteFiles*(conf: ConfigRef) = let doStrip = options.getConfigVar(conf, "pretty.strip").normalize == "on" - for i in 0 .. high(gSourceFiles): - if gSourceFiles[i].dirty and not gSourceFiles[i].isNimfixFile and - (not gOnlyMainfile or gSourceFiles[i].fileIdx == conf.projectMainIdx.FileIndex): - let newFile = if gOverWrite: gSourceFiles[i].fullpath - else: gSourceFiles[i].fullpath.changeFileExt(".pretty.nim") + for i in 0 .. high(conf.m.fileInfos): + if conf.m.fileInfos[i].dirty and + (not gOnlyMainfile or FileIndex(i) == conf.projectMainIdx): + let newFile = if gOverWrite: conf.m.fileInfos[i].fullpath + else: conf.m.fileInfos[i].fullpath.changeFileExt(".pretty.nim") try: var f = open(newFile, fmWrite) - for line in gSourceFiles[i].lines: + for line in conf.m.fileInfos[i].lines: if doStrip: f.write line.strip(leading = false, trailing = true) else: f.write line - f.write(gSourceFiles[i].newline) + f.write(conf.m.fileInfos[i], "\L") f.close except IOError: rawMessage(conf, errGenerated, "cannot open file: " & newFile) @@ -93,14 +102,16 @@ proc beautifyName(s: string, k: TSymKind): string = result.add s[i] inc i -proc replaceInFile(info: TLineInfo; newName: string) = - loadFile(info) +proc differ*(line: string, a, b: int, x: string): bool = + let y = line[a..b] + result = cmpIgnoreStyle(y, x) == 0 and y != x - let line = gSourceFiles[info.fileIndex.int].lines[info.line.int-1] +proc replaceInFile(conf: ConfigRef; info: TLineInfo; newName: string) = + let line = conf.m.fileInfos[info.fileIndex.int].lines[info.line.int-1] var first = min(info.col.int, line.len) if first < 0: return #inc first, skipIgnoreCase(line, "proc ", first) - while first > 0 and line[first-1] in prettybase.Letters: dec first + while first > 0 and line[first-1] in Letters: dec first if first < 0: return if line[first] == '`': inc first @@ -108,46 +119,56 @@ proc replaceInFile(info: TLineInfo; newName: string) = if differ(line, first, last, newName): # last-first+1 != newName.len or var x = line.substr(0, first-1) & newName & line.substr(last+1) - system.shallowCopy(gSourceFiles[info.fileIndex.int].lines[info.line.int-1], x) - gSourceFiles[info.fileIndex.int].dirty = true + system.shallowCopy(conf.m.fileInfos[info.fileIndex.int].lines[info.line.int-1], x) + conf.m.fileInfos[info.fileIndex.int].dirty = true -proc checkStyle(conf: ConfigRef; info: TLineInfo, s: string, k: TSymKind; sym: PSym) = +proc checkStyle(conf: ConfigRef; cache: IdentCache; info: TLineInfo, s: string, k: TSymKind; sym: PSym) = let beau = beautifyName(s, k) if s != beau: if gStyleCheck == StyleCheck.Auto: - sym.name = getIdent(beau) - replaceInFile(info, beau) + sym.name = getIdent(cache, beau) + replaceInFile(conf, info, beau) else: message(conf, info, hintName, beau) -proc styleCheckDefImpl(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) = +proc styleCheckDefImpl(conf: ConfigRef; cache: IdentCache; info: TLineInfo; s: PSym; k: TSymKind) = # operators stay as they are: - if k in {skResult, skTemp} or s.name.s[0] notin prettybase.Letters: return + if k in {skResult, skTemp} or s.name.s[0] notin Letters: return if k in {skType, skGenericParam} and sfAnon in s.flags: return if {sfImportc, sfExportc} * s.flags == {} or gCheckExtern: - checkStyle(conf, info, s.name.s, k, s) + checkStyle(conf, cache, info, s.name.s, k, s) + +proc nep1CheckDefImpl(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) = + # operators stay as they are: + if k in {skResult, skTemp} or s.name.s[0] notin Letters: return + if k in {skType, skGenericParam} and sfAnon in s.flags: return + let beau = beautifyName(s.name.s, k) + if s.name.s != beau: + message(conf, info, hintName, beau) -template styleCheckDef*(info: TLineInfo; s: PSym; k: TSymKind) = +template styleCheckDef*(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) = + if optCheckNep1 in conf.globalOptions: + nep1CheckDefImpl(conf, info, s, k) when defined(nimfix): - if gStyleCheck != StyleCheck.None: styleCheckDefImpl(conf, info, s, k) + if gStyleCheck != StyleCheck.None: styleCheckDefImpl(conf, cache, info, s, k) -template styleCheckDef*(info: TLineInfo; s: PSym) = - styleCheckDef(info, s, s.kind) -template styleCheckDef*(s: PSym) = - styleCheckDef(s.info, s, s.kind) +template styleCheckDef*(conf: ConfigRef; info: TLineInfo; s: PSym) = + styleCheckDef(conf, info, s, s.kind) +template styleCheckDef*(conf: ConfigRef; s: PSym) = + styleCheckDef(conf, s.info, s, s.kind) -proc styleCheckUseImpl(info: TLineInfo; s: PSym) = +proc styleCheckUseImpl(conf: ConfigRef; info: TLineInfo; s: PSym) = if info.fileIndex.int < 0: return # we simply convert it to what it looks like in the definition # for consistency # operators stay as they are: - if s.kind in {skResult, skTemp} or s.name.s[0] notin prettybase.Letters: + if s.kind in {skResult, skTemp} or s.name.s[0] notin Letters: return if s.kind in {skType, skGenericParam} and sfAnon in s.flags: return let newName = s.name.s - replaceInFile(info, newName) + replaceInFile(conf, info, newName) #if newName == "File": writeStackTrace() template styleCheckUse*(info: TLineInfo; s: PSym) = diff --git a/compiler/lookups.nim b/compiler/lookups.nim index b6d63d0bd..87694988a 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -10,8 +10,8 @@ # This module implements lookup helpers. import - intsets, ast, astalgo, idents, semdata, types, msgs, options, rodread, - renderer, wordrecg, idgen, nimfix.prettybase, configuration, strutils + intsets, ast, astalgo, idents, semdata, types, msgs, options, + renderer, wordrecg, idgen, nimfix.prettybase, lineinfos, strutils proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) @@ -22,13 +22,13 @@ proc noidentError(conf: ConfigRef; n, origin: PNode) = m.add "identifier expected, but found '" & n.renderTree & "'" localError(conf, n.info, m) -proc considerQuotedIdent*(conf: ConfigRef; n: PNode, origin: PNode = nil): PIdent = +proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent = ## Retrieve a PIdent from a PNode, taking into account accent nodes. ## ``origin`` can be nil. If it is not nil, it is used for a better ## error message. template handleError(n, origin: PNode) = - noidentError(conf, n, origin) - result = getIdent"<Error>" + noidentError(c.config, n, origin) + result = getIdent(c.cache, "<Error>") case n.kind of nkIdent: result = n.ident @@ -36,7 +36,7 @@ proc considerQuotedIdent*(conf: ConfigRef; n: PNode, origin: PNode = nil): PIden of nkAccQuoted: case n.len of 0: handleError(n, origin) - of 1: result = considerQuotedIdent(conf, n.sons[0], origin) + of 1: result = considerQuotedIdent(c, n.sons[0], origin) else: var id = "" for i in 0..<n.len: @@ -46,7 +46,7 @@ proc considerQuotedIdent*(conf: ConfigRef; n: PNode, origin: PNode = nil): PIden of nkSym: id.add(x.sym.name.s) of nkLiterals - nkFloatLiterals: id.add(x.renderTree) else: handleError(n, origin) - result = getIdent(id) + result = getIdent(c.cache, id) of nkOpenSymChoice, nkClosedSymChoice: if n[0].kind == nkSym: result = n.sons[0].sym.name @@ -86,7 +86,7 @@ proc skipAlias*(s: PSym; n: PNode; conf: ConfigRef): PSym = else: result = s.owner if conf.cmd == cmdPretty: - prettybase.replaceDeprecated(n.info, s, result) + prettybase.replaceDeprecated(conf, n.info, s, result) else: message(conf, n.info, warnDeprecated, "use " & result.name.s & " instead; " & s.name.s) @@ -100,15 +100,16 @@ proc searchInScopes*(c: PContext, s: PIdent): PSym = if result != nil: return result = nil -proc debugScopes*(c: PContext; limit=0) {.deprecated.} = - var i = 0 - for scope in walkScopes(c.currentScope): - echo "scope ", i - for h in 0 .. high(scope.symbols.data): - if scope.symbols.data[h] != nil: - echo scope.symbols.data[h].name.s - if i == limit: break - inc i +when declared(echo): + proc debugScopes*(c: PContext; limit=0) {.deprecated.} = + var i = 0 + for scope in walkScopes(c.currentScope): + echo "scope ", i + for h in 0 .. high(scope.symbols.data): + if scope.symbols.data[h] != nil: + echo scope.symbols.data[h].name.s + if i == limit: break + inc i proc searchInScopes*(c: PContext, s: PIdent, filter: TSymKinds): PSym = for scope in walkScopes(c.currentScope): @@ -125,9 +126,9 @@ proc errorSym*(c: PContext, n: PNode): PSym = # ensure that 'considerQuotedIdent' can't fail: if m.kind == nkDotExpr: m = m.sons[1] let ident = if m.kind in {nkIdent, nkSym, nkAccQuoted}: - considerQuotedIdent(c.config, m) + considerQuotedIdent(c, m) else: - getIdent("err:" & renderTree(m)) + getIdent(c.cache, "err:" & renderTree(m)) result = newSym(skError, ident, getCurrOwner(c), n.info, {}) result.typ = errorType(c) incl(result.flags, sfDiscardable) @@ -139,7 +140,7 @@ type TOverloadIterMode* = enum oimDone, oimNoQualifier, oimSelfModule, oimOtherModule, oimSymChoice, oimSymChoiceLocalLookup - TOverloadIter*{.final.} = object + TOverloadIter* = object it*: TIdentIter m*: PSym mode*: TOverloadIterMode @@ -147,10 +148,10 @@ type scope*: PScope inSymChoice: IntSet -proc getSymRepr*(s: PSym): string = +proc getSymRepr*(conf: ConfigRef; s: PSym): string = case s.kind of skProc, skFunc, skMethod, skConverter, skIterator: - result = getProcHeader(s) + result = getProcHeader(conf, s) else: result = s.name.s @@ -164,7 +165,8 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) = # too many 'implementation of X' errors are annoying # and slow 'suggest' down: if missingImpls == 0: - localError(c.config, s.info, "implementation of '$1' expected" % getSymRepr(s)) + localError(c.config, s.info, "implementation of '$1' expected" % + getSymRepr(c.config, s)) inc missingImpls elif {sfUsed, sfExported} * s.flags == {} and optHints in s.options: if s.kind notin {skForVar, skParam, skMethod, skUnknown, skGenericParam}: @@ -172,7 +174,7 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) = # maybe they can be made skGenericParam as well. if s.typ != nil and tfImplicitTypeParam notin s.typ.flags and s.typ.kind != tyGenericParam: - message(c.config, s.info, hintXDeclaredButNotUsed, getSymRepr(s)) + message(c.config, s.info, hintXDeclaredButNotUsed, getSymRepr(c.config, s)) s = nextIter(it, scope.symbols) proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string) = @@ -275,7 +277,7 @@ proc lookUp*(c: PContext, n: PNode): PSym = of nkSym: result = n.sym of nkAccQuoted: - var ident = considerQuotedIdent(c.config, n) + var ident = considerQuotedIdent(c, n) result = searchInScopes(c, ident).skipAlias(n, c.config) if result == nil: fixSpelling(n, ident, searchInScopes) @@ -286,7 +288,8 @@ proc lookUp*(c: PContext, n: PNode): PSym = return if contains(c.ambiguousSymbols, result.id): errorUseQualifier(c, n.info, result) - if result.kind == skStub: loadStub(result) + when false: + if result.kind == skStub: loadStub(result) type TLookupFlag* = enum @@ -296,7 +299,7 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = const allExceptModule = {low(TSymKind)..high(TSymKind)}-{skModule,skPackage} case n.kind of nkIdent, nkAccQuoted: - var ident = considerQuotedIdent(c.config, n) + var ident = considerQuotedIdent(c, n) if checkModule in flags: result = searchInScopes(c, ident).skipAlias(n, c.config) else: @@ -322,7 +325,7 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = if n.sons[1].kind == nkIdent: ident = n.sons[1].ident elif n.sons[1].kind == nkAccQuoted: - ident = considerQuotedIdent(c.config, n.sons[1]) + ident = considerQuotedIdent(c, n.sons[1]) if ident != nil: if m == c.module: result = strTableGet(c.topLevelScope.symbols, ident).skipAlias(n, c.config) @@ -341,12 +344,13 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = result = errorSym(c, n.sons[1]) else: result = nil - if result != nil and result.kind == skStub: loadStub(result) + when false: + if result != nil and result.kind == skStub: loadStub(result) proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = case n.kind of nkIdent, nkAccQuoted: - var ident = considerQuotedIdent(c.config, n) + var ident = considerQuotedIdent(c, n) o.scope = c.currentScope o.mode = oimNoQualifier while true: @@ -367,7 +371,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = if n.sons[1].kind == nkIdent: ident = n.sons[1].ident elif n.sons[1].kind == nkAccQuoted: - ident = considerQuotedIdent(c.config, n.sons[1], n) + ident = considerQuotedIdent(c, n.sons[1], n) if ident != nil: if o.m == c.module: # a module may access its private members: @@ -390,7 +394,8 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = o.inSymChoice = initIntSet() incl(o.inSymChoice, result.id) else: discard - if result != nil and result.kind == skStub: loadStub(result) + when false: + if result != nil and result.kind == skStub: loadStub(result) proc lastOverloadScope*(o: TOverloadIter): int = case o.mode @@ -441,7 +446,8 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = result = firstIdentExcluding(o.it, o.scope.symbols, n.sons[0].sym.name, o.inSymChoice).skipAlias(n, c.config) - if result != nil and result.kind == skStub: loadStub(result) + when false: + if result != nil and result.kind == skStub: loadStub(result) proc pickSym*(c: PContext, n: PNode; kinds: set[TSymKind]; flags: TSymFlags = {}): PSym = diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index 13336f00e..1b17f620c 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -12,7 +12,8 @@ const genPrefix* = ":tmp" # prefix for generated names -import ast, astalgo, types, idents, magicsys, msgs, options, modulegraphs +import ast, astalgo, types, idents, magicsys, msgs, options, modulegraphs, + lineinfos from trees import getMagic proc newDeref*(n: PNode): PNode {.inline.} = @@ -30,8 +31,8 @@ proc newTupleAccess*(g: ModuleGraph; tup: PNode, i: int): PNode = proc addVar*(father, v: PNode) = var vpart = newNodeI(nkIdentDefs, v.info, 3) vpart.sons[0] = v - vpart.sons[1] = ast.emptyNode - vpart.sons[2] = ast.emptyNode + vpart.sons[1] = newNodeI(nkEmpty, v.info) + vpart.sons[2] = vpart[1] addSon(father, vpart) proc newAsgnStmt(le, ri: PNode): PNode = @@ -49,7 +50,7 @@ proc lowerTupleUnpacking*(g: ModuleGraph; n: PNode; owner: PSym): PNode = let value = n.lastSon result = newNodeI(nkStmtList, n.info) - var temp = newSym(skTemp, getIdent(genPrefix), owner, value.info, g.config.options) + var temp = newSym(skTemp, getIdent(g.cache, genPrefix), owner, value.info, g.config.options) temp.typ = skipTypes(value.typ, abstractInst) incl(temp.flags, sfFromGeneric) @@ -73,17 +74,17 @@ proc newTupleAccessRaw*(tup: PNode, i: int): PNode = proc newTryFinally*(body, final: PNode): PNode = result = newTree(nkTryStmt, body, newTree(nkFinally, final)) -proc lowerTupleUnpackingForAsgn*(n: PNode; owner: PSym): PNode = +proc lowerTupleUnpackingForAsgn*(g: ModuleGraph; n: PNode; owner: PSym): PNode = let value = n.lastSon result = newNodeI(nkStmtList, n.info) - var temp = newSym(skLet, getIdent("_"), owner, value.info, owner.options) + var temp = newSym(skLet, getIdent(g.cache, "_"), owner, value.info, owner.options) var v = newNodeI(nkLetSection, value.info) let tempAsNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info) var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3) vpart.sons[0] = tempAsNode - vpart.sons[1] = ast.emptyNode + vpart.sons[1] = newNodeI(nkEmpty, value.info) vpart.sons[2] = value addSon(v, vpart) result.add(v) @@ -92,10 +93,10 @@ proc lowerTupleUnpackingForAsgn*(n: PNode; owner: PSym): PNode = for i in 0 .. lhs.len-1: result.add newAsgnStmt(lhs.sons[i], newTupleAccessRaw(tempAsNode, i)) -proc lowerSwap*(n: PNode; owner: PSym): PNode = +proc lowerSwap*(g: ModuleGraph; n: PNode; owner: PSym): PNode = result = newNodeI(nkStmtList, n.info) # note: cannot use 'skTemp' here cause we really need the copy for the VM :-( - var temp = newSym(skVar, getIdent(genPrefix), owner, n.info, owner.options) + var temp = newSym(skVar, getIdent(g.cache, genPrefix), owner, n.info, owner.options) temp.typ = n.sons[1].typ incl(temp.flags, sfFromGeneric) @@ -104,7 +105,7 @@ proc lowerSwap*(n: PNode; owner: PSym): PNode = var vpart = newNodeI(nkIdentDefs, v.info, 3) vpart.sons[0] = tempAsNode - vpart.sons[1] = ast.emptyNode + vpart.sons[1] = newNodeI(nkEmpty, v.info) vpart.sons[2] = n[1] addSon(v, vpart) @@ -120,7 +121,7 @@ proc createObj*(g: ModuleGraph; owner: PSym, info: TLineInfo; final=true): PType else: rawAddSon(result, getCompilerProc(g, "RootObj").typ) result.n = newNodeI(nkRecList, info) - let s = newSym(skType, getIdent("Env_" & info.toFilename), + let s = newSym(skType, getIdent(g.cache, "Env_" & toFilename(g.config, info)), owner, info, owner.options) incl s.flags, sfAnon s.typ = result @@ -171,10 +172,10 @@ proc lookupInRecord(n: PNode, id: int): PSym = if n.sym.id == -abs(id): result = n.sym else: discard -proc addField*(obj: PType; s: PSym) = +proc addField*(obj: PType; s: PSym; cache: IdentCache) = # because of 'gensym' support, we have to mangle the name with its ID. # This is hacky but the clean solution is much more complex than it looks. - var field = newSym(skField, getIdent(s.name.s & $obj.n.len), s.owner, s.info, + var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), s.owner, s.info, s.options) field.id = -s.id let t = skipIntLit(s.typ) @@ -183,10 +184,10 @@ proc addField*(obj: PType; s: PSym) = field.position = sonsLen(obj.n) addSon(obj.n, newSymNode(field)) -proc addUniqueField*(obj: PType; s: PSym): PSym {.discardable.} = +proc addUniqueField*(obj: PType; s: PSym; cache: IdentCache): PSym {.discardable.} = result = lookupInRecord(obj.n, s.id) if result == nil: - var field = newSym(skField, getIdent(s.name.s & $obj.n.len), s.owner, s.info, + var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), s.owner, s.info, s.options) field.id = -s.id let t = skipIntLit(s.typ) @@ -227,13 +228,13 @@ proc indirectAccess*(a: PNode, b: int, info: TLineInfo): PNode = addSon(result, newSymNode(field)) result.typ = field.typ -proc indirectAccess(a: PNode, b: string, info: TLineInfo): PNode = +proc indirectAccess(a: PNode, b: string, info: TLineInfo; cache: IdentCache): PNode = # returns a[].b as a node var deref = newNodeI(nkHiddenDeref, info) deref.typ = a.typ.skipTypes(abstractInst).sons[0] var t = deref.typ.skipTypes(abstractInst) var field: PSym - let bb = getIdent(b) + let bb = getIdent(cache, b) while true: assert t.kind == tyObject field = getSymFromList(t.n, bb) @@ -335,17 +336,29 @@ proc typeNeedsNoDeepCopy(t: PType): bool = if t.kind in {tyVar, tyLent, tySequence}: t = t.lastSon result = not containsGarbageCollectedRef(t) +proc hoistExpr*(varSection, expr: PNode, name: PIdent, owner: PSym): PSym = + result = newSym(skLet, name, owner, varSection.info, owner.options) + result.flags.incl sfHoisted + result.typ = expr.typ + + var varDef = newNodeI(nkIdentDefs, varSection.info, 3) + varDef.sons[0] = newSymNode(result) + varDef.sons[1] = newNodeI(nkEmpty, varSection.info) + varDef.sons[2] = expr + + varSection.add varDef + proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; owner: PSym; typ: PType; v: PNode; useShallowCopy=false): PSym = - result = newSym(skTemp, getIdent(genPrefix), owner, varSection.info, + result = newSym(skTemp, getIdent(g.cache, genPrefix), owner, varSection.info, owner.options) result.typ = typ incl(result.flags, sfFromGeneric) var vpart = newNodeI(nkIdentDefs, varSection.info, 3) vpart.sons[0] = newSymNode(result) - vpart.sons[1] = ast.emptyNode - vpart.sons[2] = if varInit.isNil: v else: ast.emptyNode + vpart.sons[1] = newNodeI(nkEmpty, varSection.info) + vpart.sons[2] = if varInit.isNil: v else: vpart[1] varSection.add vpart if varInit != nil: if useShallowCopy and typeNeedsNoDeepCopy(typ): @@ -410,7 +423,7 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym; # generate: # fv.owner = threadParam body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode, - "owner", fv.info), threadParam.newSymNode) + "owner", fv.info, g.cache), threadParam.newSymNode) body.add callCodegenProc(g, "nimArgsPassingDone", threadParam.newSymNode) if spawnKind == srByVar: @@ -421,12 +434,12 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym; localError(g.config, f.info, "cannot create a flowVar of type: " & typeToString(fv.typ.sons[1])) body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode, - if fk == fvGC: "data" else: "blob", fv.info), call) + if fk == fvGC: "data" else: "blob", fv.info, g.cache), call) if fk == fvGC: let incRefCall = newNodeI(nkCall, fv.info, 2) incRefCall.sons[0] = newSymNode(getSysMagic(g, fv.info, "GCref", mGCref)) incRefCall.sons[1] = indirectAccess(threadLocalProm.newSymNode, - "data", fv.info) + "data", fv.info, g.cache) body.add incRefCall if barrier == nil: # by now 'fv' is shared and thus might have beeen overwritten! we need @@ -438,7 +451,7 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym; body.add callCodegenProc(g, "barrierLeave", threadLocalBarrier.newSymNode) var params = newNodeI(nkFormalParams, f.info) - params.add emptyNode + params.add newNodeI(nkEmpty, f.info) params.add threadParam.newSymNode params.add argsParam.newSymNode @@ -452,14 +465,18 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym; t.n.add argsParam.newSymNode let name = (if f.kind == nkSym: f.sym.name.s else: genPrefix) & "Wrapper" - result = newSym(skProc, getIdent(name), argsParam.owner, f.info, + result = newSym(skProc, getIdent(g.cache, name), argsParam.owner, f.info, argsParam.options) - result.ast = newProcNode(nkProcDef, f.info, body, params, newSymNode(result)) + let emptyNode = newNodeI(nkEmpty, f.info) + result.ast = newProcNode(nkProcDef, f.info, body = body, + params = params, name = newSymNode(result), pattern = emptyNode, + genericParams = emptyNode, pragmas = emptyNode, + exceptions = emptyNode) result.typ = t proc createCastExpr(argsParam: PSym; objType: PType): PNode = result = newNodeI(nkCast, argsParam.info) - result.add emptyNode + result.add newNodeI(nkEmpty, argsParam.info) result.add newSymNode(argsParam) result.typ = newType(tyPtr, objType.owner) result.typ.rawAddSon(objType) @@ -468,7 +485,7 @@ proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType; scratchOb castExpr, call, varSection, varInit, result: PNode) = let formals = n[0].typ.n - let tmpName = getIdent(genPrefix) + let tmpName = getIdent(g.cache, genPrefix) for i in 1 ..< n.len: # we pick n's type here, which hopefully is 'tyArray' and not # 'tyOpenArray': @@ -481,7 +498,7 @@ proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType; scratchOb let fieldname = if i < formals.len: formals[i].sym.name else: tmpName var field = newSym(skField, fieldname, objType.owner, n.info, g.config.options) field.typ = argType - objType.addField(field) + objType.addField(field, g.cache) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i]) let temp = addLocalVar(g, varSection, varInit, objType.owner, argType, @@ -511,7 +528,7 @@ proc newIntLit*(g: ModuleGraph; info: TLineInfo; value: BiggestInt): PNode = proc genHigh*(g: ModuleGraph; n: PNode): PNode = if skipTypes(n.typ, abstractVar).kind == tyArray: - result = newIntLit(g, n.info, lastOrd(skipTypes(n.typ, abstractVar))) + result = newIntLit(g, n.info, lastOrd(g.config, skipTypes(n.typ, abstractVar))) else: result = newNodeI(nkCall, n.info, 2) result.typ = getSysType(g, n.info, tyInt) @@ -522,7 +539,7 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; scratchOb castExpr, call, varSection, varInit, result: PNode) = let formals = n[0].typ.n - let tmpName = getIdent(genPrefix) + let tmpName = getIdent(g.cache, genPrefix) # we need to copy the foreign scratch object fields into local variables # for correctness: These are called 'threadLocal' here. for i in 1 ..< n.len: @@ -543,17 +560,17 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; scratchOb slice.sons[0].typ = getSysType(g, n.info, tyInt) # fake type var fieldB = newSym(skField, tmpName, objType.owner, n.info, g.config.options) fieldB.typ = getSysType(g, n.info, tyInt) - objType.addField(fieldB) + objType.addField(fieldB, g.cache) if getMagic(n) == mSlice: let a = genAddrOf(n[1]) field.typ = a.typ - objType.addField(field) + objType.addField(field, g.cache) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a) var fieldA = newSym(skField, tmpName, objType.owner, n.info, g.config.options) fieldA.typ = getSysType(g, n.info, tyInt) - objType.addField(fieldA) + objType.addField(fieldA, g.cache) result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldA), n[2]) result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), n[3]) @@ -564,7 +581,7 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; scratchOb else: let a = genAddrOf(n) field.typ = a.typ - objType.addField(field) + objType.addField(field, g.cache) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a) result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), genHigh(g, n)) @@ -577,12 +594,12 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; scratchOb useShallowCopy=true) slice.sons[3] = threadLocal.newSymNode call.add slice - elif (let size = computeSize(argType); size < 0 or size > 16) and + elif (let size = computeSize(g.config, argType); size < 0 or size > 16) and n.getRoot != nil: # it is more efficient to pass a pointer instead: let a = genAddrOf(n) field.typ = a.typ - objType.addField(field) + objType.addField(field, g.cache) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a) let threadLocal = addLocalVar(g, varSection,nil, objType.owner, field.typ, indirectAccess(castExpr, field, n.info), @@ -591,7 +608,7 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; scratchOb else: # boring case field.typ = argType - objType.addField(field) + objType.addField(field, g.cache) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n) let threadLocal = addLocalVar(g, varSection, varInit, objType.owner, field.typ, @@ -623,8 +640,8 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}: localError(g.config, n.info, "'spawn' takes a GC safe call expression") var - threadParam = newSym(skParam, getIdent"thread", owner, n.info, g.config.options) - argsParam = newSym(skParam, getIdent"args", owner, n.info, g.config.options) + threadParam = newSym(skParam, getIdent(g.cache, "thread"), owner, n.info, g.config.options) + argsParam = newSym(skParam, getIdent(g.cache, "args"), owner, n.info, g.config.options) block: let ptrType = getSysType(g, n.info, tyPointer) threadParam.typ = ptrType @@ -635,7 +652,7 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P incl(objType.flags, tfFinal) let castExpr = createCastExpr(argsParam, objType) - var scratchObj = newSym(skVar, getIdent"scratch", owner, n.info, g.config.options) + var scratchObj = newSym(skVar, getIdent(g.cache, "scratch"), owner, n.info, g.config.options) block: scratchObj.typ = objType incl(scratchObj.flags, sfFromGeneric) @@ -653,9 +670,9 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P skFunc, skMethod, skConverter}): # for indirect calls we pass the function pointer in the scratchObj var argType = n[0].typ.skipTypes(abstractInst) - var field = newSym(skField, getIdent"fn", owner, n.info, g.config.options) + var field = newSym(skField, getIdent(g.cache, "fn"), owner, n.info, g.config.options) field.typ = argType - objType.addField(field) + objType.addField(field, g.cache) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[0]) fn = indirectAccess(castExpr, field, n.info) elif fn.kind == nkSym and fn.sym.kind == skIterator: @@ -677,17 +694,17 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P if barrier != nil: let typ = newType(tyPtr, owner) typ.rawAddSon(magicsys.getCompilerProc(g, "Barrier").typ) - var field = newSym(skField, getIdent"barrier", owner, n.info, g.config.options) + var field = newSym(skField, getIdent(g.cache, "barrier"), owner, n.info, g.config.options) field.typ = typ - objType.addField(field) + objType.addField(field, g.cache) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), barrier) barrierAsExpr = indirectAccess(castExpr, field, n.info) var fvField, fvAsExpr: PNode = nil if spawnKind == srFlowVar: - var field = newSym(skField, getIdent"fv", owner, n.info, g.config.options) + var field = newSym(skField, getIdent(g.cache, "fv"), owner, n.info, g.config.options) field.typ = retType - objType.addField(field) + objType.addField(field, g.cache) fvField = newDotExpr(scratchObj, field) fvAsExpr = indirectAccess(castExpr, field, n.info) # create flowVar: @@ -696,10 +713,10 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField) elif spawnKind == srByVar: - var field = newSym(skField, getIdent"fv", owner, n.info, g.config.options) + var field = newSym(skField, getIdent(g.cache, "fv"), owner, n.info, g.config.options) field.typ = newType(tyPtr, objType.owner) field.typ.rawAddSon(retType) - objType.addField(field) + objType.addField(field, g.cache) fvAsExpr = indirectAccess(castExpr, field, n.info) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), genAddrOf(dest)) diff --git a/compiler/macrocacheimpl.nim b/compiler/macrocacheimpl.nim new file mode 100644 index 000000000..d23040763 --- /dev/null +++ b/compiler/macrocacheimpl.nim @@ -0,0 +1,79 @@ +# +# +# The Nim Compiler +# (c) Copyright 2018 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements helpers for the macro cache. + +import lineinfos, ast, modulegraphs, vmdef, magicsys + +proc recordInc*(c: PCtx; info: TLineInfo; key: string; by: BiggestInt) = + var recorded = newNodeI(nkCommentStmt, info) + recorded.add newStrNode("inc", info) + recorded.add newStrNode(key, info) + recorded.add newIntNode(nkIntLit, by) + c.graph.recordStmt(c.graph, c.module, recorded) + +proc recordPut*(c: PCtx; info: TLineInfo; key: string; k: string; val: PNode) = + var recorded = newNodeI(nkCommentStmt, info) + recorded.add newStrNode("put", info) + recorded.add newStrNode(key, info) + recorded.add newStrNode(k, info) + recorded.add copyTree(val) + c.graph.recordStmt(c.graph, c.module, recorded) + +proc recordAdd*(c: PCtx; info: TLineInfo; key: string; val: PNode) = + var recorded = newNodeI(nkCommentStmt, info) + recorded.add newStrNode("add", info) + recorded.add newStrNode(key, info) + recorded.add copyTree(val) + c.graph.recordStmt(c.graph, c.module, recorded) + +proc recordIncl*(c: PCtx; info: TLineInfo; key: string; val: PNode) = + var recorded = newNodeI(nkCommentStmt, info) + recorded.add newStrNode("incl", info) + recorded.add newStrNode(key, info) + recorded.add copyTree(val) + c.graph.recordStmt(c.graph, c.module, recorded) + +when false: + proc genCall3(g: ModuleGraph; m: TMagic; s: string; a, b, c: PNode): PNode = + newTree(nkStaticStmt, newTree(nkCall, createMagic(g, s, m).newSymNode, a, b, c)) + + proc genCall2(g: ModuleGraph; m: TMagic; s: string; a, b: PNode): PNode = + newTree(nkStaticStmt, newTree(nkCall, createMagic(g, s, m).newSymNode, a, b)) + + template nodeFrom(s: string): PNode = + var res = newStrNode(s, info) + res.typ = getSysType(g, info, tyString) + res + + template nodeFrom(i: BiggestInt): PNode = + var res = newIntNode(i, info) + res.typ = getSysType(g, info, tyInt) + res + + template nodeFrom(n: PNode): PNode = copyTree(n) + + template record(call) = + g.recordStmt(g, c.module, call) + + proc recordInc*(c: PCtx; info: TLineInfo; key: string; by: BiggestInt) = + let g = c.graph + record genCall2(mNccInc, "inc", nodeFrom key, nodeFrom by) + + proc recordPut*(c: PCtx; info: TLineInfo; key: string; k: string; val: PNode) = + let g = c.graph + record genCall3(mNctPut, "[]=", nodeFrom key, nodeFrom k, nodeFrom val) + + proc recordAdd*(c: PCtx; info: TLineInfo; key: string; val: PNode) = + let g = c.graph + record genCall2(mNcsAdd, "add", nodeFrom key, nodeFrom val) + + proc recordIncl*(c: PCtx; info: TLineInfo; key: string; val: PNode) = + let g = c.graph + record genCall2(mNcsIncl, "incl", nodeFrom key, nodeFrom val) diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim index b5577d961..d40b9d732 100644 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -10,8 +10,8 @@ # Built-in types and compilerprocs are registered here. import - ast, astalgo, hashes, msgs, platform, nversion, times, idents, rodread, - modulegraphs + ast, astalgo, hashes, msgs, platform, nversion, times, idents, + modulegraphs, lineinfos export createMagic @@ -26,30 +26,18 @@ proc newSysType(g: ModuleGraph; kind: TTypeKind, size: int): PType = result.align = size.int16 proc getSysSym*(g: ModuleGraph; info: TLineInfo; name: string): PSym = - result = strTableGet(g.systemModule.tab, getIdent(name)) + result = strTableGet(g.systemModule.tab, getIdent(g.cache, name)) if result == nil: localError(g.config, info, "system module needs: " & name) - result = newSym(skError, getIdent(name), g.systemModule, g.systemModule.info, {}) + result = newSym(skError, getIdent(g.cache, name), g.systemModule, g.systemModule.info, {}) result.typ = newType(tyError, g.systemModule) - if result.kind == skStub: loadStub(result) if result.kind == skAlias: result = result.owner -when false: - proc createMagic*(g: ModuleGraph; name: string, m: TMagic): PSym = - result = newSym(skProc, getIdent(name), nil, unknownLineInfo()) - result.magic = m - -when false: - let - opNot* = createMagic("not", mNot) - opContains* = createMagic("contains", mInSet) - proc getSysMagic*(g: ModuleGraph; info: TLineInfo; name: string, m: TMagic): PSym = var ti: TIdentIter - let id = getIdent(name) + let id = getIdent(g.cache, name) var r = initIdentIter(ti, g.systemModule.tab, id) while r != nil: - if r.kind == skStub: loadStub(r) if r.magic == m: # prefer the tyInt variant: if r.typ.sons[0] != nil and r.typ.sons[0].kind == tyInt: return r @@ -87,7 +75,7 @@ proc getSysType*(g: ModuleGraph; info: TLineInfo; kind: TTypeKind): PType = of tyString: result = sysTypeFromName("string") of tyCString: result = sysTypeFromName("cstring") of tyPointer: result = sysTypeFromName("pointer") - of tyNil: result = newSysType(g, tyNil, ptrSize) + of tyNil: result = newSysType(g, tyNil, g.config.target.ptrSize) else: internalError(g.config, "request for typekind: " & $kind) g.sysTypes[kind] = result if result.kind != kind: @@ -139,7 +127,7 @@ proc addSonSkipIntLit*(father, son: PType) = proc setIntLitType*(g: ModuleGraph; result: PNode) = let i = result.intVal - case platform.intSize + case g.config.target.intSize of 8: result.typ = getIntLitType(g, result) of 4: if i >= low(int32) and i <= high(int32): @@ -167,15 +155,8 @@ proc setIntLitType*(g: ModuleGraph; result: PNode) = internalError(g.config, result.info, "invalid int size") proc getCompilerProc*(g: ModuleGraph; name: string): PSym = - let ident = getIdent(name) + let ident = getIdent(g.cache, name) result = strTableGet(g.compilerprocs, ident) - when false: - if result == nil: - result = strTableGet(g.rodCompilerprocs, ident) - if result != nil: - strTableAdd(g.compilerprocs, result) - if result.kind == skStub: loadStub(result) - if result.kind == skAlias: result = result.owner proc registerCompilerProc*(g: ModuleGraph; s: PSym) = strTableAdd(g.compilerprocs, s) @@ -187,9 +168,9 @@ proc registerNimScriptSymbol*(g: ModuleGraph; s: PSym) = strTableAdd(g.exposed, s) else: localError(g.config, s.info, - "symbol conflicts with other .exportNims symbol at: " & $conflict.info) + "symbol conflicts with other .exportNims symbol at: " & g.config$conflict.info) proc getNimScriptSymbol*(g: ModuleGraph; name: string): PSym = - strTableGet(g.exposed, getIdent(name)) + strTableGet(g.exposed, getIdent(g.cache, name)) proc resetNimScriptSymbols*(g: ModuleGraph) = initStrTable(g.exposed) diff --git a/compiler/main.nim b/compiler/main.nim index e3f00db9e..cd05ded62 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -9,74 +9,68 @@ # implements the command dispatcher and several commands +when not defined(nimcore): + {.error: "nimcore MUST be defined for Nim's core tooling".} + import llstream, strutils, ast, astalgo, lexer, syntaxes, renderer, options, msgs, - os, condsyms, rodread, rodwrite, times, + os, condsyms, times, wordrecg, sem, semdata, idents, passes, docgen, extccomp, cgen, jsgen, json, nversion, platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen, - docgen2, service, parser, modules, ccgutils, sigmatch, ropes, - modulegraphs, tables, rod, configuration + docgen2, parser, modules, ccgutils, sigmatch, ropes, + modulegraphs, tables, rod, lineinfos from magicsys import resetSysTypes -proc rodPass(g: ModuleGraph) = - if g.config.symbolFiles in {enabledSf, writeOnlySf}: - registerPass(rodwritePass) - -proc codegenPass = - registerPass cgenPass +proc codegenPass(g: ModuleGraph) = + registerPass g, cgenPass -proc semanticPasses = - registerPass verbosePass - registerPass semPass +proc semanticPasses(g: ModuleGraph) = + registerPass g, verbosePass + registerPass g, semPass proc writeDepsFile(g: ModuleGraph; project: string) = let f = open(changeFileExt(project, "deps"), fmWrite) for m in g.modules: if m != nil: - f.writeLine(toFullPath(m.position.FileIndex)) + f.writeLine(toFullPath(g.config, m.position.FileIndex)) for k in g.inclToMod.keys: if g.getModule(k).isNil: # don't repeat includes which are also modules - f.writeLine(k.toFullPath) + f.writeLine(toFullPath(g.config, k)) f.close() -proc commandGenDepend(graph: ModuleGraph; cache: IdentCache) = - semanticPasses() - registerPass(gendependPass) - #registerPass(cleanupPass) - compileProject(graph, cache) +proc commandGenDepend(graph: ModuleGraph) = + semanticPasses(graph) + registerPass(graph, gendependPass) + compileProject(graph) let project = graph.config.projectFull writeDepsFile(graph, project) - generateDot(project) + generateDot(graph, project) execExternalProgram(graph.config, "dot -Tpng -o" & changeFileExt(project, "png") & ' ' & changeFileExt(project, "dot")) -proc commandCheck(graph: ModuleGraph; cache: IdentCache) = +proc commandCheck(graph: ModuleGraph) = graph.config.errorMax = high(int) # do not stop after first error defineSymbol(graph.config.symbols, "nimcheck") - semanticPasses() # use an empty backend for semantic checking only - rodPass(graph) - compileProject(graph, cache) + semanticPasses(graph) # use an empty backend for semantic checking only + compileProject(graph) -proc commandDoc2(graph: ModuleGraph; cache: IdentCache; json: bool) = +proc commandDoc2(graph: ModuleGraph; json: bool) = graph.config.errorMax = high(int) # do not stop after first error - semanticPasses() - if json: registerPass(docgen2JsonPass) - else: registerPass(docgen2Pass) - #registerPass(cleanupPass()) - compileProject(graph, cache) + semanticPasses(graph) + if json: registerPass(graph, docgen2JsonPass) + else: registerPass(graph, docgen2Pass) + compileProject(graph) finishDoc2Pass(graph.config.projectName) -proc commandCompileToC(graph: ModuleGraph; cache: IdentCache) = +proc commandCompileToC(graph: ModuleGraph) = let conf = graph.config extccomp.initVars(conf) - semanticPasses() - registerPass(cgenPass) - rodPass(graph) - #registerPass(cleanupPass()) + semanticPasses(graph) + registerPass(graph, cgenPass) - compileProject(graph, cache) + compileProject(graph) cgenWriteModules(graph.backend, conf) if conf.cmd != cmdRun: let proj = changeFileExt(conf.projectFull, "") @@ -85,53 +79,51 @@ proc commandCompileToC(graph: ModuleGraph; cache: IdentCache) = if optGenScript in graph.config.globalOptions: writeDepsFile(graph, toGeneratedFile(conf, proj, "")) -proc commandJsonScript(graph: ModuleGraph; cache: IdentCache) = +proc commandJsonScript(graph: ModuleGraph) = let proj = changeFileExt(graph.config.projectFull, "") extccomp.runJsonBuildInstructions(graph.config, proj) -proc commandCompileToJS(graph: ModuleGraph; cache: IdentCache) = +proc commandCompileToJS(graph: ModuleGraph) = #incl(gGlobalOptions, optSafeCode) - setTarget(osJS, cpuJS) + setTarget(graph.config.target, osJS, cpuJS) #initDefines() defineSymbol(graph.config.symbols, "ecmascript") # For backward compatibility defineSymbol(graph.config.symbols, "js") - semanticPasses() - registerPass(JSgenPass) - compileProject(graph, cache) + semanticPasses(graph) + registerPass(graph, JSgenPass) + compileProject(graph) -proc interactivePasses(graph: ModuleGraph; cache: IdentCache) = - #incl(gGlobalOptions, optSafeCode) - #setTarget(osNimrodVM, cpuNimrodVM) +proc interactivePasses(graph: ModuleGraph) = initDefines(graph.config.symbols) defineSymbol(graph.config.symbols, "nimscript") when hasFFI: defineSymbol(graph.config.symbols, "nimffi") - registerPass(verbosePass) - registerPass(semPass) - registerPass(evalPass) + registerPass(graph, verbosePass) + registerPass(graph, semPass) + registerPass(graph, evalPass) -proc commandInteractive(graph: ModuleGraph; cache: IdentCache) = +proc commandInteractive(graph: ModuleGraph) = graph.config.errorMax = high(int) # do not stop after first error - interactivePasses(graph, cache) - compileSystemModule(graph, cache) + interactivePasses(graph) + compileSystemModule(graph) if graph.config.commandArgs.len > 0: - discard graph.compileModule(fileInfoIdx(graph.config, graph.config.projectFull), cache, {}) + discard graph.compileModule(fileInfoIdx(graph.config, graph.config.projectFull), {}) else: var m = graph.makeStdinModule() incl(m.flags, sfMainModule) - processModule(graph, m, llStreamOpenStdIn(), nil, cache) + processModule(graph, m, llStreamOpenStdIn()) const evalPasses = [verbosePass, semPass, evalPass] -proc evalNim(graph: ModuleGraph; nodes: PNode, module: PSym; cache: IdentCache) = - carryPasses(graph, nodes, module, cache, evalPasses) +proc evalNim(graph: ModuleGraph; nodes: PNode, module: PSym) = + carryPasses(graph, nodes, module, evalPasses) -proc commandEval(graph: ModuleGraph; cache: IdentCache; exp: string) = +proc commandEval(graph: ModuleGraph; exp: string) = if graph.systemModule == nil: - interactivePasses(graph, cache) - compileSystemModule(graph, cache) + interactivePasses(graph) + compileSystemModule(graph) let echoExp = "echo \"eval\\t\", " & "repr(" & exp & ")" - evalNim(graph, echoExp.parseString(cache, graph.config), - makeStdinModule(graph), cache) + evalNim(graph, echoExp.parseString(graph.cache, graph.config), + makeStdinModule(graph)) proc commandScan(cache: IdentCache, config: ConfigRef) = var f = addFileExt(mainCommandArg(config), NimExt) @@ -153,12 +145,13 @@ proc commandScan(cache: IdentCache, config: ConfigRef) = const PrintRopeCacheStats = false -proc mainCommand*(graph: ModuleGraph; cache: IdentCache) = +proc mainCommand*(graph: ModuleGraph) = let conf = graph.config + let cache = graph.cache - setupModuleCache() + setupModuleCache(graph) # In "nim serve" scenario, each command must reset the registered passes - clearPasses() + clearPasses(graph) conf.lastCmdTime = epochTime() conf.searchPaths.add(conf.libpath) setId(100) @@ -166,69 +159,69 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) = of "c", "cc", "compile", "compiletoc": # compile means compileToC currently conf.cmd = cmdCompileToC - commandCompileToC(graph, cache) + commandCompileToC(graph) of "cpp", "compiletocpp": conf.cmd = cmdCompileToCpp defineSymbol(graph.config.symbols, "cpp") - commandCompileToC(graph, cache) + commandCompileToC(graph) of "objc", "compiletooc": conf.cmd = cmdCompileToOC defineSymbol(graph.config.symbols, "objc") - commandCompileToC(graph, cache) + commandCompileToC(graph) of "run": conf.cmd = cmdRun when hasTinyCBackend: extccomp.setCC("tcc") - commandCompileToC(graph, cache) + commandCompileToC(graph) else: rawMessage(conf, errGenerated, "'run' command not available; rebuild with -d:tinyc") of "js", "compiletojs": conf.cmd = cmdCompileToJS - commandCompileToJS(graph, cache) + commandCompileToJS(graph) of "doc0": wantMainModule(conf) conf.cmd = cmdDoc loadConfigs(DocConfig, cache, conf) - commandDoc(conf) + commandDoc(cache, conf) of "doc2", "doc": conf.cmd = cmdDoc loadConfigs(DocConfig, cache, conf) defineSymbol(conf.symbols, "nimdoc") - commandDoc2(graph, cache, false) + commandDoc2(graph, false) of "rst2html": conf.cmd = cmdRst2html loadConfigs(DocConfig, cache, conf) - commandRst2Html(conf) + commandRst2Html(cache, conf) of "rst2tex": conf.cmd = cmdRst2tex loadConfigs(DocTexConfig, cache, conf) - commandRst2TeX(conf) + commandRst2TeX(cache, conf) of "jsondoc0": wantMainModule(conf) conf.cmd = cmdDoc loadConfigs(DocConfig, cache, conf) wantMainModule(conf) defineSymbol(conf.symbols, "nimdoc") - commandJson(conf) + commandJson(cache, conf) of "jsondoc2", "jsondoc": conf.cmd = cmdDoc loadConfigs(DocConfig, cache, conf) wantMainModule(conf) defineSymbol(conf.symbols, "nimdoc") - commandDoc2(graph, cache, true) + commandDoc2(graph, true) of "ctags": wantMainModule(conf) conf.cmd = cmdDoc loadConfigs(DocConfig, cache, conf) defineSymbol(conf.symbols, "nimdoc") - commandTags(conf) + commandTags(cache, conf) of "buildindex": conf.cmd = cmdDoc loadConfigs(DocConfig, cache, conf) - commandBuildIndex(conf) + commandBuildIndex(cache, conf) of "gendepend": conf.cmd = cmdGenDepend - commandGenDepend(graph, cache) + commandGenDepend(graph) of "dump": conf.cmd = cmdDump if getConfigVar(conf, "dump.format") == "json": @@ -257,11 +250,11 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) = for it in conf.searchPaths: msgWriteln(conf, it) of "check": conf.cmd = cmdCheck - commandCheck(graph, cache) + commandCheck(graph) of "parse": conf.cmd = cmdParse wantMainModule(conf) - discard parseFile(FileIndex conf.projectMainIdx, cache, conf) + discard parseFile(conf.projectMainIdx, cache, conf) of "scan": conf.cmd = cmdScan wantMainModule(conf) @@ -269,15 +262,15 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) = msgWriteln(conf, "Beware: Indentation tokens depend on the parser's state!") of "secret": conf.cmd = cmdInteractive - commandInteractive(graph, cache) + commandInteractive(graph) of "e": - commandEval(graph, cache, mainCommandArg(conf)) + commandEval(graph, mainCommandArg(conf)) of "nop", "help": # prevent the "success" message: conf.cmd = cmdDump of "jsonscript": conf.cmd = cmdJsonScript - commandJsonScript(graph, cache) + commandJsonScript(graph) else: rawMessage(conf, errGenerated, "invalid command: " & conf.command) @@ -302,5 +295,3 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) = ffDecimal, 3) resetAttributes(conf) - -#proc mainCommand*() = mainCommand(newModuleGraph(newConfigRef()), newIdentCache()) diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 460d0b4a5..334cd1ae6 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -9,7 +9,7 @@ ## This module implements the module graph data structure. The module graph ## represents a complete Nim project. Single modules can either be kept in RAM -## or stored in a ROD file. The ROD file mechanism is not yet integrated here. +## or stored in a Sqlite database. ## ## The caching of modules is critical for 'nimsuggest' and is tricky to get ## right. If module E is being edited, we need autocompletion (and type @@ -25,7 +25,8 @@ ## - Its dependent module stays the same. ## -import ast, intsets, tables, options, rod, msgs, hashes, idents +import ast, intsets, tables, options, lineinfos, hashes, idents, + incremental, btrees type ModuleGraph* = ref object @@ -40,16 +41,27 @@ type # module dependencies. backend*: RootRef # minor hack so that a backend can extend this easily config*: ConfigRef + cache*: IdentCache + vm*: RootRef # unfortunately the 'vm' state is shared project-wise, this will + # be clarified in later compiler implementations. doStopCompile*: proc(): bool {.closure.} usageSym*: PSym # for nimsuggest owners*: seq[PSym] - methods*: seq[tuple[methods: TSymSeq, dispatcher: PSym]] + methods*: seq[tuple[methods: TSymSeq, dispatcher: PSym]] # needs serialization! systemModule*: PSym sysTypes*: array[TTypeKind, PType] compilerprocs*: TStrTable exposed*: TStrTable intTypeCache*: array[-5..64, PType] opContains*, opNot*: PSym + emptyNode*: PNode + incr*: IncrementalCtx + importModuleCallback*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex): PSym {.nimcall.} + includeFileCallback*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex): PNode {.nimcall.} + recordStmt*: proc (graph: ModuleGraph; m: PSym; n: PNode) {.nimcall.} + cacheSeqs*: Table[string, PNode] # state that is shared to suppor the 'macrocache' API + cacheCounters*: Table[string, BiggestInt] + cacheTables*: Table[string, BTree[string, PNode]] proc hash*(x: FileIndex): Hash {.borrow.} @@ -59,26 +71,31 @@ proc stopCompile*(g: ModuleGraph): bool {.inline.} = result = doStopCompile != nil and doStopCompile() proc createMagic*(g: ModuleGraph; name: string, m: TMagic): PSym = - result = newSym(skProc, getIdent(name), nil, unknownLineInfo(), {}) + result = newSym(skProc, getIdent(g.cache, name), nil, unknownLineInfo(), {}) result.magic = m -proc newModuleGraph*(config: ConfigRef = nil): ModuleGraph = +proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph = result = ModuleGraph() initStrTable(result.packageSyms) result.deps = initIntSet() result.modules = @[] result.importStack = @[] result.inclToMod = initTable[FileIndex, FileIndex]() - if config.isNil: - result.config = newConfigRef() - else: - result.config = config + result.config = config + result.cache = cache result.owners = @[] result.methods = @[] initStrTable(result.compilerprocs) initStrTable(result.exposed) result.opNot = createMagic(result, "not", mNot) result.opContains = createMagic(result, "contains", mInSet) + result.emptyNode = newNode(nkEmpty) + init(result.incr) + result.recordStmt = proc (graph: ModuleGraph; m: PSym; n: PNode) {.nimcall.} = + discard + result.cacheSeqs = initTable[string, PNode]() + result.cacheCounters = initTable[string, BiggestInt]() + result.cacheTables = initTable[string, BTree[string, PNode]]() proc resetAllModules*(g: ModuleGraph) = initStrTable(packageSyms) @@ -100,15 +117,15 @@ proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b proc addDep*(g: ModuleGraph; m: PSym, dep: FileIndex) = assert m.position == m.info.fileIndex.int32 - addModuleDep(m.info.fileIndex, dep, isIncludeFile = false) + addModuleDep(g.incr, g.config, m.info.fileIndex, dep, isIncludeFile = false) if suggestMode: deps.incl m.position.dependsOn(dep.int) # we compute the transitive closure later when quering the graph lazily. - # this improve efficiency quite a lot: + # this improves efficiency quite a lot: #invalidTransitiveClosure = true proc addIncludeDep*(g: ModuleGraph; module, includeFile: FileIndex) = - addModuleDep(module, includeFile, isIncludeFile = true) + addModuleDep(g.incr, g.config, module, includeFile, isIncludeFile = true) discard hasKeyOrPut(inclToMod, includeFile, module) proc parentModule*(g: ModuleGraph; fileIdx: FileIndex): FileIndex = diff --git a/compiler/modulepaths.nim b/compiler/modulepaths.nim index daa55c2ba..87c7f3541 100644 --- a/compiler/modulepaths.nim +++ b/compiler/modulepaths.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -import ast, renderer, strutils, msgs, options, idents, os +import ast, renderer, strutils, msgs, options, idents, os, lineinfos import nimblecmd @@ -120,7 +120,7 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string = case n.kind of nkStrLit, nkRStrLit, nkTripleStrLit: try: - result = pathSubs(conf, n.strVal, n.info.toFullPath().splitFile().dir) + result = pathSubs(conf, n.strVal, toFullPath(conf, n.info).splitFile().dir) except ValueError: localError(conf, n.info, "invalid path: " & n.strVal) result = n.strVal @@ -131,7 +131,7 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string = of nkInfix: let n0 = n[0] let n1 = n[1] - if n0.kind == nkIdent and n0.ident.id == getIdent("as").id: + if n0.kind == nkIdent and n0.ident.s == "as": # XXX hack ahead: n.kind = nkImportAs n.sons[0] = n.sons[1] @@ -177,7 +177,7 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string = proc checkModuleName*(conf: ConfigRef; n: PNode; doLocalError=true): FileIndex = # This returns the full canonical path for a given module import let modulename = getModuleName(conf, n) - let fullPath = findModule(conf, modulename, n.info.toFullPath) + let fullPath = findModule(conf, modulename, toFullPath(conf, n.info)) if fullPath.len == 0: if doLocalError: let m = if modulename.len > 0: modulename else: $n diff --git a/compiler/modules.nim b/compiler/modules.nim index 5d1eba1f2..b3a1e90d6 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -10,9 +10,9 @@ ## Implements the module handling, including the caching of modules. import - ast, astalgo, magicsys, std / sha1, rodread, msgs, cgendata, sigmatch, options, + ast, astalgo, magicsys, std / sha1, msgs, cgendata, sigmatch, options, idents, os, lexer, idgen, passes, syntaxes, llstream, modulegraphs, rod, - configuration + lineinfos proc resetSystemArtifacts*(g: ModuleGraph) = magicsys.resetSysTypes(g) @@ -23,8 +23,8 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = new(result) result.id = -1 # for better error checking result.kind = skModule - let filename = fileIdx.toFullPath - result.name = getIdent(splitFile(filename).name) + let filename = toFullPath(graph.config, fileIdx) + result.name = getIdent(graph.cache, splitFile(filename).name) if not isNimIdentifier(result.name.s): rawMessage(graph.config, errGenerated, "invalid module name: " & result.name.s) @@ -32,17 +32,19 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = let pck = getPackageName(graph.config, filename) pck2 = if pck.len > 0: pck else: "unknown" - pack = getIdent(pck2) + pack = getIdent(graph.cache, pck2) var packSym = graph.packageSyms.strTableGet(pack) if packSym == nil: - packSym = newSym(skPackage, getIdent(pck2), nil, result.info) + packSym = newSym(skPackage, getIdent(graph.cache, pck2), nil, result.info) initStrTable(packSym.tab) graph.packageSyms.strTableAdd(packSym) result.owner = packSym result.position = int fileIdx - growCache graph.modules, int fileIdx + if int(fileIdx) >= graph.modules.len: + setLen(graph.modules, int(fileIdx) + 1) + #growCache graph.modules, int fileIdx graph.modules[result.position] = result incl(result.flags, sfUsed) @@ -50,95 +52,76 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = strTableAdd(result.tab, result) # a module knows itself let existing = strTableGet(packSym.tab, result.name) if existing != nil and existing.info.fileIndex != result.info.fileIndex: - localError(graph.config, result.info, "module names need to be unique per Nimble package; module clashes with " & existing.info.fileIndex.toFullPath) + localError(graph.config, result.info, + "module names need to be unique per Nimble package; module clashes with " & + toFullPath(graph.config, existing.info.fileIndex)) # strTableIncl() for error corrections: discard strTableIncl(packSym.tab, result) -proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; cache: IdentCache, flags: TSymFlags): PSym = +proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): PSym = result = graph.getModule(fileIdx) if result == nil: - #growCache gMemCacheData, fileIdx - #gMemCacheData[fileIdx].needsRecompile = Probing result = newModule(graph, fileIdx) - var rd: PRodReader result.flags = result.flags + flags if sfMainModule in result.flags: - gMainPackageId = result.owner.id - - when false: - if conf.cmd in {cmdCompileToC, cmdCompileToCpp, cmdCheck, cmdIdeTools}: - rd = handleSymbolFile(result, cache) - if result.id < 0: - internalError("handleSymbolFile should have set the module's ID") - return - else: - discard - result.id = getModuleId(fileIdx, toFullPath(fileIdx)) + graph.config.mainPackageId = result.owner.id + + result.id = getModuleId(graph, fileIdx, toFullPath(graph.config, fileIdx)) discard processModule(graph, result, - if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil, - rd, cache) - #if optCaasEnabled in gGlobalOptions: - # gMemCacheData[fileIdx].needsRecompile = Recompiled - # if validFile: doHash fileIdx + if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil) elif graph.isDirty(result): result.flags.excl sfDirty # reset module fields: initStrTable(result.tab) result.ast = nil discard processModule(graph, result, - if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil, - nil, cache) + if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil) graph.markClientsDirty(fileIdx) - when false: - if checkDepMem(fileIdx) == Yes: - result = compileModule(fileIdx, cache, flags) - else: - result = gCompiledModules[fileIdx] - -proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex; - cache: IdentCache): PSym {.procvar.} = + +proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym {.procvar.} = # this is called by the semantic checking phase assert graph.config != nil - result = compileModule(graph, fileIdx, cache, {}) + result = compileModule(graph, fileIdx, {}) graph.addDep(s, fileIdx) #if sfSystemModule in result.flags: # localError(result.info, errAttemptToRedefine, result.name.s) # restore the notes for outer module: graph.config.notes = - if s.owner.id == gMainPackageId: graph.config.mainPackageNotes - else: graph.config.foreignPackageNotes + if s.owner.id == graph.config.mainPackageId: graph.config.mainPackageNotes + else: graph.config.foreignPackageNotes -proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex; - cache: IdentCache): PNode {.procvar.} = - result = syntaxes.parseFile(fileIdx, cache, graph.config) +proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PNode {.procvar.} = + result = syntaxes.parseFile(fileIdx, graph.cache, graph.config) graph.addDep(s, fileIdx) graph.addIncludeDep(s.position.FileIndex, fileIdx) -proc compileSystemModule*(graph: ModuleGraph; cache: IdentCache) = +proc connectCallbacks*(graph: ModuleGraph) = + graph.includeFileCallback = includeModule + graph.importModuleCallback = importModule + +proc compileSystemModule*(graph: ModuleGraph) = if graph.systemModule == nil: - systemFileIdx = fileInfoIdx(graph.config, graph.config.libpath / "system.nim") - discard graph.compileModule(systemFileIdx, cache, {sfSystemModule}) + connectCallbacks(graph) + graph.config.m.systemFileIdx = fileInfoIdx(graph.config, graph.config.libpath / "system.nim") + discard graph.compileModule(graph.config.m.systemFileIdx, {sfSystemModule}) proc wantMainModule*(conf: ConfigRef) = if conf.projectFull.len == 0: fatal(conf, newLineInfo(conf, "command line", 1, 1), errGenerated, "command expects a filename") - conf.projectMainIdx = int32 fileInfoIdx(conf, addFileExt(conf.projectFull, NimExt)) - -passes.gIncludeFile = includeModule -passes.gImportModule = importModule + conf.projectMainIdx = fileInfoIdx(conf, addFileExt(conf.projectFull, NimExt)) -proc compileProject*(graph: ModuleGraph; cache: IdentCache; - projectFileIdx = InvalidFileIDX) = +proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIDX) = + connectCallbacks(graph) let conf = graph.config wantMainModule(conf) let systemFileIdx = fileInfoIdx(conf, conf.libpath / "system.nim") - let projectFile = if projectFileIdx == InvalidFileIDX: FileIndex(conf.projectMainIdx) else: projectFileIdx + let projectFile = if projectFileIdx == InvalidFileIDX: conf.projectMainIdx else: projectFileIdx graph.importStack.add projectFile if projectFile == systemFileIdx: - discard graph.compileModule(projectFile, cache, {sfMainModule, sfSystemModule}) + discard graph.compileModule(projectFile, {sfMainModule, sfSystemModule}) else: - graph.compileSystemModule(cache) - discard graph.compileModule(projectFile, cache, {sfMainModule}) + graph.compileSystemModule() + discard graph.compileModule(projectFile, {sfMainModule}) proc makeModule*(graph: ModuleGraph; filename: string): PSym = result = graph.newModule(fileInfoIdx(graph.config, filename)) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 533d3a57f..be2ece911 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -9,83 +9,28 @@ import options, strutils, os, tables, ropes, platform, terminal, macros, - configuration + lineinfos -#type -# MsgConfig* = ref object of RootObj - -type - TFileInfo* = object - fullPath: string # This is a canonical full filesystem path - projPath*: string # This is relative to the project's root - shortName*: string # short name of the module - quotedName*: Rope # cached quoted short name for codegen - # purposes - quotedFullName*: Rope # cached quoted full name for codegen - # purposes - - lines*: seq[Rope] # the source code of the module - # used for better error messages and - # embedding the original source in the - # generated code - dirtyfile: string # the file that is actually read into memory - # and parsed; usually 'nil' but is used - # for 'nimsuggest' - hash*: string # the checksum of the file - when defined(nimpretty): - fullContent*: string - FileIndex* = distinct int32 - TLineInfo* = object # This is designed to be as small as possible, - # because it is used - # in syntax nodes. We save space here by using - # two int16 and an int32. - # On 64 bit and on 32 bit systems this is - # only 8 bytes. - line*: uint16 - col*: int16 - fileIndex*: FileIndex - when defined(nimpretty): - offsetA*, offsetB*: int - commentOffsetA*, commentOffsetB*: int - - TErrorOutput* = enum - eStdOut - eStdErr - - TErrorOutputs* = set[TErrorOutput] - - ERecoverableError* = object of ValueError - ESuggestDone* = object of Exception - -proc `==`*(a, b: FileIndex): bool {.borrow.} - - -const - InvalidFileIDX* = FileIndex(-1) - -var - filenameToIndexTbl = initTable[string, FileIndex]() - fileInfos*: seq[TFileInfo] = @[] - systemFileIdx*: FileIndex - -proc toCChar*(c: char): string = +proc toCChar*(c: char; result: var string) = case c - of '\0'..'\x1F', '\x7F'..'\xFF': result = '\\' & toOctal(c) - of '\'', '\"', '\\', '?': result = '\\' & c - else: result = $(c) + of '\0'..'\x1F', '\x7F'..'\xFF': + result.add '\\' + result.add toOctal(c) + of '\'', '\"', '\\', '?': + result.add '\\' + result.add c + else: + result.add c proc makeCString*(s: string): Rope = - const - MaxLineLength = 64 + const MaxLineLength = 64 result = nil var res = newStringOfCap(int(s.len.toFloat * 1.1) + 1) add(res, "\"") for i in countup(0, len(s) - 1): if (i + 1) mod MaxLineLength == 0: - add(res, '\"') - add(res, tnl) - add(res, '\"') - add(res, toCChar(s[i])) + add(res, "\"\L\"") + toCChar(s[i], res) add(res, '\"') add(result, rope(res)) @@ -110,8 +55,8 @@ proc newFileInfo(fullPath, projPath: string): TFileInfo = result.fullContent = "" when defined(nimpretty): - proc fileSection*(fid: FileIndex; a, b: int): string = - substr(fileInfos[fid.int].fullContent, a, b) + proc fileSection*(conf: ConfigRef; fid: FileIndex; a, b: int): string = + substr(conf.m.fileInfos[fid.int].fullContent, a, b) proc fileInfoKnown*(conf: ConfigRef; filename: string): bool = var @@ -120,7 +65,7 @@ proc fileInfoKnown*(conf: ConfigRef; filename: string): bool = canon = canonicalizePath(conf, filename) except: canon = filename - result = filenameToIndexTbl.hasKey(canon) + result = conf.m.filenameToIndexTbl.hasKey(canon) proc fileInfoIdx*(conf: ConfigRef; filename: string; isKnownFile: var bool): FileIndex = var @@ -136,14 +81,14 @@ proc fileInfoIdx*(conf: ConfigRef; filename: string; isKnownFile: var bool): Fil # This flag indicates that we are working with such a path here pseudoPath = true - if filenameToIndexTbl.hasKey(canon): - result = filenameToIndexTbl[canon] + if conf.m.filenameToIndexTbl.hasKey(canon): + result = conf.m.filenameToIndexTbl[canon] else: isKnownFile = false - result = fileInfos.len.FileIndex - fileInfos.add(newFileInfo(canon, if pseudoPath: filename - else: shortenDir(conf, canon))) - filenameToIndexTbl[canon] = result + result = conf.m.fileInfos.len.FileIndex + conf.m.fileInfos.add(newFileInfo(canon, if pseudoPath: filename + else: shortenDir(conf, canon))) + conf.m.filenameToIndexTbl[canon] = result proc fileInfoIdx*(conf: ConfigRef; filename: string): FileIndex = var dummy: bool @@ -157,34 +102,9 @@ proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo = proc newLineInfo*(conf: ConfigRef; filename: string, line, col: int): TLineInfo {.inline.} = result = newLineInfo(fileInfoIdx(conf, filename), line, col) -when false: - fileInfos.add(newFileInfo("", "command line")) - var gCmdLineInfo* = newLineInfo(FileIndex(0), 1, 1) - - fileInfos.add(newFileInfo("", "compilation artifact")) - var gCodegenLineInfo* = newLineInfo(FileIndex(1), 1, 1) - proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} = raise newException(ERecoverableError, msg) -proc sourceLine*(conf: ConfigRef; i: TLineInfo): Rope - -proc unknownLineInfo*(): TLineInfo = - result.line = uint16(0) - result.col = int16(-1) - result.fileIndex = InvalidFileIDX - -type - Severity* {.pure.} = enum ## VS Code only supports these three - Hint, Warning, Error - -var - msgContext: seq[TLineInfo] = @[] - lastError = unknownLineInfo() - - errorOutputs* = {eStdOut, eStdErr} - writelnHook*: proc (output: string) {.closure.} - structuredErrorHook*: proc (info: TLineInfo; msg: string; severity: Severity) {.closure.} proc concat(strings: openarray[string]): string = var totalLen = 0 @@ -192,13 +112,13 @@ proc concat(strings: openarray[string]): string = result = newStringOfCap totalLen for s in strings: result.add s -proc suggestWriteln*(s: string) = - if eStdOut in errorOutputs: - if isNil(writelnHook): +proc suggestWriteln*(conf: ConfigRef; s: string) = + if eStdOut in conf.m.errorOutputs: + if isNil(conf.writelnHook): writeLine(stdout, s) flushFile(stdout) else: - writelnHook(s) + conf.writelnHook(s) proc msgQuit*(x: int8) = quit x proc msgQuit*(x: string) = quit x @@ -219,61 +139,61 @@ const HintTitle = "Hint: " HintColor = fgGreen -proc getInfoContextLen*(): int = return msgContext.len -proc setInfoContextLen*(L: int) = setLen(msgContext, L) +proc getInfoContextLen*(conf: ConfigRef): int = return conf.m.msgContext.len +proc setInfoContextLen*(conf: ConfigRef; L: int) = setLen(conf.m.msgContext, L) -proc pushInfoContext*(info: TLineInfo) = - msgContext.add(info) +proc pushInfoContext*(conf: ConfigRef; info: TLineInfo) = + conf.m.msgContext.add(info) -proc popInfoContext*() = - setLen(msgContext, len(msgContext) - 1) +proc popInfoContext*(conf: ConfigRef) = + setLen(conf.m.msgContext, len(conf.m.msgContext) - 1) -proc getInfoContext*(index: int): TLineInfo = - let L = msgContext.len +proc getInfoContext*(conf: ConfigRef; index: int): TLineInfo = + let L = conf.m.msgContext.len let i = if index < 0: L + index else: index if i >=% L: result = unknownLineInfo() - else: result = msgContext[i] + else: result = conf.m.msgContext[i] -template toFilename*(fileIdx: FileIndex): string = - (if fileIdx.int32 < 0: "???" else: fileInfos[fileIdx.int32].projPath) +template toFilename*(conf: ConfigRef; fileIdx: FileIndex): string = + (if fileIdx.int32 < 0 or conf == nil: "???" else: conf.m.fileInfos[fileIdx.int32].projPath) -proc toFullPath*(fileIdx: FileIndex): string = - if fileIdx.int32 < 0: result = "???" - else: result = fileInfos[fileIdx.int32].fullPath +proc toFullPath*(conf: ConfigRef; fileIdx: FileIndex): string = + if fileIdx.int32 < 0 or conf == nil: result = "???" + else: result = conf.m.fileInfos[fileIdx.int32].fullPath -proc setDirtyFile*(fileIdx: FileIndex; filename: string) = +proc setDirtyFile*(conf: ConfigRef; fileIdx: FileIndex; filename: string) = assert fileIdx.int32 >= 0 - fileInfos[fileIdx.int32].dirtyFile = filename + conf.m.fileInfos[fileIdx.int32].dirtyFile = filename -proc setHash*(fileIdx: FileIndex; hash: string) = +proc setHash*(conf: ConfigRef; fileIdx: FileIndex; hash: string) = assert fileIdx.int32 >= 0 - shallowCopy(fileInfos[fileIdx.int32].hash, hash) + shallowCopy(conf.m.fileInfos[fileIdx.int32].hash, hash) -proc getHash*(fileIdx: FileIndex): string = +proc getHash*(conf: ConfigRef; fileIdx: FileIndex): string = assert fileIdx.int32 >= 0 - shallowCopy(result, fileInfos[fileIdx.int32].hash) + shallowCopy(result, conf.m.fileInfos[fileIdx.int32].hash) -proc toFullPathConsiderDirty*(fileIdx: FileIndex): string = +proc toFullPathConsiderDirty*(conf: ConfigRef; fileIdx: FileIndex): string = if fileIdx.int32 < 0: result = "???" - elif not fileInfos[fileIdx.int32].dirtyFile.isNil: - result = fileInfos[fileIdx.int32].dirtyFile + elif not conf.m.fileInfos[fileIdx.int32].dirtyFile.isNil: + result = conf.m.fileInfos[fileIdx.int32].dirtyFile else: - result = fileInfos[fileIdx.int32].fullPath + result = conf.m.fileInfos[fileIdx.int32].fullPath -template toFilename*(info: TLineInfo): string = - info.fileIndex.toFilename +template toFilename*(conf: ConfigRef; info: TLineInfo): string = + toFilename(conf, info.fileIndex) -template toFullPath*(info: TLineInfo): string = - info.fileIndex.toFullPath +template toFullPath*(conf: ConfigRef; info: TLineInfo): string = + toFullPath(conf, info.fileIndex) proc toMsgFilename*(conf: ConfigRef; info: TLineInfo): string = if info.fileIndex.int32 < 0: result = "???" elif optListFullPaths in conf.globalOptions: - result = fileInfos[info.fileIndex.int32].fullPath + result = conf.m.fileInfos[info.fileIndex.int32].fullPath else: - result = fileInfos[info.fileIndex.int32].projPath + result = conf.m.fileInfos[info.fileIndex.int32].projPath proc toLinenumber*(info: TLineInfo): int {.inline.} = result = int info.line @@ -281,23 +201,19 @@ proc toLinenumber*(info: TLineInfo): int {.inline.} = proc toColumn*(info: TLineInfo): int {.inline.} = result = info.col -proc toFileLine*(info: TLineInfo): string {.inline.} = - result = info.toFilename & ":" & $info.line +proc toFileLine*(conf: ConfigRef; info: TLineInfo): string {.inline.} = + result = toFilename(conf, info) & ":" & $info.line -proc toFileLineCol*(info: TLineInfo): string {.inline.} = - result = info.toFilename & "(" & $info.line & ", " & $info.col & ")" +proc toFileLineCol*(conf: ConfigRef; info: TLineInfo): string {.inline.} = + result = toFilename(conf, info) & "(" & $info.line & ", " & $info.col & ")" -proc `$`*(info: TLineInfo): string = toFileLineCol(info) +proc `$`*(conf: ConfigRef; info: TLineInfo): string = toFileLineCol(conf, info) -proc `??`* (info: TLineInfo, filename: string): bool = - # only for debugging purposes - result = filename in info.toFilename +proc `$`*(info: TLineInfo): string {.error.} = discard -const trackPosInvalidFileIdx* = FileIndex(-2) # special marker so that no suggestions - # are produced within comments and string literals -var gTrackPos*: TLineInfo -var gTrackPosAttached*: bool ## whether the tracking position was attached to some - ## close token. +proc `??`* (conf: ConfigRef; info: TLineInfo, filename: string): bool = + # only for debugging purposes + result = filename in toFilename(conf, info) type MsgFlag* = enum ## flags altering msgWriteln behavior @@ -315,14 +231,14 @@ proc msgWriteln*(conf: ConfigRef; s: string, flags: MsgFlags = {}) = ## support. #if conf.cmd == cmdIdeTools and optCDebug notin gGlobalOptions: return - if not isNil(writelnHook) and msgSkipHook notin flags: - writelnHook(s) + if not isNil(conf.writelnHook) and msgSkipHook notin flags: + conf.writelnHook(s) elif optStdout in conf.globalOptions or msgStdout in flags: - if eStdOut in errorOutputs: + if eStdOut in conf.m.errorOutputs: writeLine(stdout, s) flushFile(stdout) else: - if eStdErr in errorOutputs: + if eStdErr in conf.m.errorOutputs: writeLine(stderr, s) # On Windows stderr is fully-buffered when piped, regardless of C std. when defined(windows): @@ -353,17 +269,17 @@ macro callStyledWriteLineStderr(args: varargs[typed]): untyped = result.add(arg) template callWritelnHook(args: varargs[string, `$`]) = - writelnHook concat(args) + conf.writelnHook concat(args) template styledMsgWriteln*(args: varargs[typed]) = - if not isNil(writelnHook): + if not isNil(conf.writelnHook): callIgnoringStyle(callWritelnHook, nil, args) elif optStdout in conf.globalOptions: - if eStdOut in errorOutputs: + if eStdOut in conf.m.errorOutputs: callIgnoringStyle(writeLine, stdout, args) flushFile(stdout) else: - if eStdErr in errorOutputs: + if eStdErr in conf.m.errorOutputs: if optUseColors in conf.globalOptions: callStyledWriteLineStderr(args) else: @@ -394,7 +310,7 @@ proc log*(s: string) {.procvar.} = proc quit(conf: ConfigRef; msg: TMsgKind) = if defined(debug) or msg == errInternal or hintStackTrace in conf.notes: - if stackTraceAvailable() and isNil(writelnHook): + if stackTraceAvailable() and isNil(conf.writelnHook): writeStackTrace() else: styledMsgWriteln(fgRed, "No stack traceback available\n" & @@ -425,19 +341,19 @@ proc exactEquals*(a, b: TLineInfo): bool = proc writeContext(conf: ConfigRef; lastinfo: TLineInfo) = const instantiationFrom = "template/generic instantiation from here" var info = lastinfo - for i in countup(0, len(msgContext) - 1): - if msgContext[i] != lastinfo and msgContext[i] != info: - if structuredErrorHook != nil: - structuredErrorHook(msgContext[i], instantiationFrom, - Severity.Error) + for i in 0 ..< len(conf.m.msgContext): + if conf.m.msgContext[i] != lastinfo and conf.m.msgContext[i] != info: + if conf.structuredErrorHook != nil: + conf.structuredErrorHook(conf, conf.m.msgContext[i], instantiationFrom, + Severity.Error) else: styledMsgWriteln(styleBright, - PosFormat % [toMsgFilename(conf, msgContext[i]), - coordToStr(msgContext[i].line.int), - coordToStr(msgContext[i].col+1)], + PosFormat % [toMsgFilename(conf, conf.m.msgContext[i]), + coordToStr(conf.m.msgContext[i].line.int), + coordToStr(conf.m.msgContext[i].col+1)], resetStyle, instantiationFrom) - info = msgContext[i] + info = conf.m.msgContext[i] proc ignoreMsgBecauseOfIdeTools(conf: ConfigRef; msg: TMsgKind): bool = msg >= errGenerated and conf.cmd == cmdIdeTools and optIdeDebug notin conf.globalOptions @@ -473,8 +389,9 @@ proc rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) = inc(conf.hintCounter) let s = msgKindToString(msg) % args - if structuredErrorHook != nil: - structuredErrorHook(unknownLineInfo(), s & (if kind != nil: KindFormat % kind else: ""), sev) + if conf.structuredErrorHook != nil: + conf.structuredErrorHook(conf, unknownLineInfo(), + s & (if kind != nil: KindFormat % kind else: ""), sev) if not ignoreMsgBecauseOfIdeTools(conf, msg): if kind != nil: @@ -491,6 +408,24 @@ proc resetAttributes*(conf: ConfigRef) = if {optUseColors, optStdout} * conf.globalOptions == {optUseColors}: terminal.resetAttributes(stderr) +proc addSourceLine(conf: ConfigRef; fileIdx: FileIndex, line: string) = + conf.m.fileInfos[fileIdx.int32].lines.add line + +proc sourceLine*(conf: ConfigRef; i: TLineInfo): string = + if i.fileIndex.int32 < 0: return "" + + if not optPreserveOrigSource(conf) and conf.m.fileInfos[i.fileIndex.int32].lines.len == 0: + try: + for line in lines(toFullPath(conf, i)): + addSourceLine conf, i.fileIndex, line.string + except IOError: + discard + assert i.fileIndex.int32 < conf.m.fileInfos.len + # can happen if the error points to EOF: + if i.line.int > conf.m.fileInfos[i.fileIndex.int32].lines.len: return "" + + result = conf.m.fileInfos[i.fileIndex.int32].lines[i.line.int-1] + proc writeSurroundingSrc(conf: ConfigRef; info: TLineInfo) = const indent = " " msgWriteln(conf, indent & $sourceLine(conf, info)) @@ -523,7 +458,7 @@ proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, # we try to filter error messages so that not two error message # in the same file and line are produced: #ignoreMsg = lastError == info and eh != doAbort - lastError = info + conf.m.lastError = info of warnMin..warnMax: sev = Severity.Warning ignoreMsg = optWarns notin conf.options or msg notin conf.notes @@ -547,8 +482,8 @@ proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, let s = getMessageStr(msg, arg) if not ignoreMsg: - if structuredErrorHook != nil: - structuredErrorHook(info, s & (if kind != nil: KindFormat % kind else: ""), sev) + if conf.structuredErrorHook != nil: + conf.structuredErrorHook(conf, info, s & (if kind != nil: KindFormat % kind else: ""), sev) if not ignoreMsgBecauseOfIdeTools(conf, msg): if kind != nil: styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s, @@ -562,7 +497,7 @@ proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, proc fatal*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = # this fixes bug #7080 so that it is at least obvious 'fatal' # was executed. - errorOutputs = {eStdOut, eStdErr} + conf.m.errorOutputs = {eStdOut, eStdErr} liMessage(conf, info, msg, arg, doAbort) proc globalError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = @@ -584,12 +519,12 @@ proc message*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = liMessage(conf, info, msg, arg, doNothing) proc internalError*(conf: ConfigRef; info: TLineInfo, errMsg: string) = - if conf.cmd == cmdIdeTools and structuredErrorHook.isNil: return + if conf.cmd == cmdIdeTools and conf.structuredErrorHook.isNil: return writeContext(conf, info) liMessage(conf, info, errInternal, errMsg, doAbort) proc internalError*(conf: ConfigRef; errMsg: string) = - if conf.cmd == cmdIdeTools and structuredErrorHook.isNil: return + if conf.cmd == cmdIdeTools and conf.structuredErrorHook.isNil: return writeContext(conf, unknownLineInfo()) rawMessage(conf, errInternal, errMsg) @@ -600,44 +535,19 @@ template assertNotNil*(conf: ConfigRef; e): untyped = template internalAssert*(conf: ConfigRef, e: bool) = if not e: internalError(conf, $instantiationInfo()) -proc addSourceLine*(fileIdx: FileIndex, line: string) = - fileInfos[fileIdx.int32].lines.add line.rope - -proc sourceLine*(conf: ConfigRef; i: TLineInfo): Rope = - if i.fileIndex.int32 < 0: return nil - - if not optPreserveOrigSource(conf) and fileInfos[i.fileIndex.int32].lines.len == 0: - try: - for line in lines(i.toFullPath): - addSourceLine i.fileIndex, line.string - except IOError: - discard - assert i.fileIndex.int32 < fileInfos.len - # can happen if the error points to EOF: - if i.line.int > fileInfos[i.fileIndex.int32].lines.len: return nil - - result = fileInfos[i.fileIndex.int32].lines[i.line.int-1] - proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope = assert i.fileIndex.int32 >= 0 if optExcessiveStackTrace in conf.globalOptions: - result = fileInfos[i.fileIndex.int32].quotedFullName + result = conf.m.fileInfos[i.fileIndex.int32].quotedFullName else: - result = fileInfos[i.fileIndex.int32].quotedName - -ropes.errorHandler = proc (err: RopesError, msg: string, useWarning: bool) = - case err - of rInvalidFormatStr: - internalError(newPartialConfigRef(), "ropes: invalid format string: " & msg) - of rCannotOpenFile: - rawMessage(newPartialConfigRef(), if useWarning: warnCannotOpenFile else: errCannotOpenFile, msg) + result = conf.m.fileInfos[i.fileIndex.int32].quotedName proc listWarnings*(conf: ConfigRef) = msgWriteln(conf, "Warnings:") for warn in warnMin..warnMax: msgWriteln(conf, " [$1] $2" % [ if warn in conf.notes: "x" else: " ", - configuration.WarningsToStr[ord(warn) - ord(warnMin)] + lineinfos.WarningsToStr[ord(warn) - ord(warnMin)] ]) proc listHints*(conf: ConfigRef) = @@ -645,5 +555,5 @@ proc listHints*(conf: ConfigRef) = for hint in hintMin..hintMax: msgWriteln(conf, " [$1] $2" % [ if hint in conf.notes: "x" else: " ", - configuration.HintsToStr[ord(hint) - ord(hintMin)] + lineinfos.HintsToStr[ord(hint) - ord(hintMin)] ]) diff --git a/compiler/ndi.nim b/compiler/ndi.nim index a7ca02193..9708c388d 100644 --- a/compiler/ndi.nim +++ b/compiler/ndi.nim @@ -10,7 +10,7 @@ ## This module implements the generation of ``.ndi`` files for better debugging ## support of Nim code. "ndi" stands for "Nim debug info". -import ast, msgs, ropes +import ast, msgs, ropes, options type NdiFile* = object @@ -18,19 +18,19 @@ type f: File buf: string -proc doWrite(f: var NdiFile; s: PSym) = +proc doWrite(f: var NdiFile; s: PSym; conf: ConfigRef) = f.buf.setLen 0 f.buf.add s.info.line.int f.buf.add "\t" f.buf.add s.info.col.int f.f.write(s.name.s, "\t") f.f.writeRope(s.loc.r) - f.f.writeLine("\t", s.info.toFullPath, "\t", f.buf) + f.f.writeLine("\t", toFullPath(conf, s.info), "\t", f.buf) -template writeMangledName*(f: NdiFile; s: PSym) = - if f.enabled: doWrite(f, s) +template writeMangledName*(f: NdiFile; s: PSym; conf: ConfigRef) = + if f.enabled: doWrite(f, s, conf) -proc open*(f: var NdiFile; filename: string) = +proc open*(f: var NdiFile; filename: string; conf: ConfigRef) = f.enabled = filename.len > 0 if f.enabled: f.f = open(filename, fmWrite, 8000) diff --git a/compiler/nim.cfg b/compiler/nim.cfg index 853ae7e00..1bd3fbfd6 100644 --- a/compiler/nim.cfg +++ b/compiler/nim.cfg @@ -5,6 +5,8 @@ path:"llvm" path:"$projectPath/.." define:booting +define:nimcore +#define:nimIncremental #import:"$projectpath/testability" @if windows: @@ -13,6 +15,5 @@ define:booting define:useStdoutAsStdmsg -cs:partial #define:useNodeIds #gc:markAndSweep diff --git a/compiler/nim.nim b/compiler/nim.nim index d3e00017f..90049bdfb 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -20,8 +20,8 @@ when defined(i386) and defined(windows) and defined(vcc): import commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes, - extccomp, strutils, os, osproc, platform, main, parseopt, service, - nodejs, scriptconfig, idents, modulegraphs, configuration + extccomp, strutils, os, osproc, platform, main, parseopt, + nodejs, scriptconfig, idents, modulegraphs, lineinfos when hasTinyCBackend: import tccgen @@ -37,6 +37,25 @@ proc prependCurDir(f: string): string = else: result = f +proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) = + var p = parseopt.initOptParser(cmd) + var argsCount = 0 + while true: + parseopt.next(p) + case p.kind + of cmdEnd: break + of cmdLongoption, cmdShortOption: + if p.key == " ": + p.key = "-" + if processArgument(pass, p, argsCount, config): break + else: + processSwitch(pass, p, config) + of cmdArgument: + if processArgument(pass, p, argsCount, config): break + if pass == passCmd2: + if optRun notin config.globalOptions and config.arguments.len > 0 and config.command.normalize != "run": + rawMessage(config, errGenerated, errArgsNeedRunOption) + proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = condsyms.initDefines(conf.symbols) if paramCount() == 0: @@ -60,7 +79,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = conf.projectName = p.name else: conf.projectPath = canonicalizePath(conf, getCurrentDir()) - loadConfigs(DefaultConfig, conf) # load all config files + loadConfigs(DefaultConfig, cache, conf) # load all config files let scriptFile = conf.projectFull.changeFileExt("nims") if fileExists(scriptFile): runNimScript(cache, scriptFile, freshDefines=false, conf) @@ -75,7 +94,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = processCmdLine(passCmd2, "", conf) if conf.command == "": rawMessage(conf, errGenerated, "command missing") - mainCommand(newModuleGraph(conf), cache) + mainCommand(newModuleGraph(cache, conf)) if optHints in conf.options and hintGCStats in conf.notes: echo(GC_getStatistics()) #echo(GC_getStatistics()) if conf.errorCounter == 0: diff --git a/compiler/nimblecmd.nim b/compiler/nimblecmd.nim index 8305b01f5..9a23535bf 100644 --- a/compiler/nimblecmd.nim +++ b/compiler/nimblecmd.nim @@ -10,7 +10,7 @@ ## Implements some helper procs for Nimble (Nim's package manager) support. import parseutils, strutils, strtabs, os, options, msgs, sequtils, - configuration + lineinfos proc addPath*(conf: ConfigRef; path: string, info: TLineInfo) = if not conf.searchPaths.contains(path): diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index a455b4a44..d3b4645dc 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -11,7 +11,7 @@ import llstream, nversion, commands, os, strutils, msgs, platform, condsyms, lexer, - options, idents, wordrecg, strtabs, configuration + options, idents, wordrecg, strtabs, lineinfos # ---------------- configuration file parser ----------------------------- # we use Nim's scanner here to save space and work @@ -233,7 +233,7 @@ proc getSystemConfigPath(conf: ConfigRef; filename: string): string = proc loadConfigs*(cfg: string; cache: IdentCache; conf: ConfigRef) = setDefaultLibpath(conf) - + var configFiles = newSeq[string]() template readConfigFile(path: string) = @@ -264,7 +264,3 @@ proc loadConfigs*(cfg: string; cache: IdentCache; conf: ConfigRef) = for filename in configFiles: rawMessage(conf, hintConf, filename) - -proc loadConfigs*(cfg: string; conf: ConfigRef) = - # for backwards compatibility only. - loadConfigs(cfg, newIdentCache(), conf) diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim index ff91861d0..f20b5642c 100644 --- a/compiler/nimeval.nim +++ b/compiler/nimeval.nim @@ -1,7 +1,7 @@ # # # The Nim Compiler -# (c) Copyright 2013 Andreas Rumpf +# (c) Copyright 2018 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -9,27 +9,112 @@ ## exposes the Nim VM to clients. import - ast, modules, passes, passaux, condsyms, - options, nimconf, sem, semdata, llstream, vm, modulegraphs, idents - -proc execute*(program: string) = - passes.gIncludeFile = includeModule - passes.gImportModule = importModule - initDefines() - loadConfigs(DefaultConfig) - - initDefines() - defineSymbol("nimrodvm") - defineSymbol("nimscript") - when hasFFI: defineSymbol("nimffi") - registerPass(verbosePass) - registerPass(semPass) - registerPass(evalPass) - - searchPaths.add options.libpath - var graph = newModuleGraph() + ast, astalgo, modules, passes, condsyms, + options, sem, semdata, llstream, vm, vmdef, + modulegraphs, idents, os + +type + Interpreter* = ref object ## Use Nim as an interpreter with this object + mainModule: PSym + graph: ModuleGraph + scriptName: string + +iterator exportedSymbols*(i: Interpreter): PSym = + assert i != nil + assert i.mainModule != nil, "no main module selected" + var it: TTabIter + var s = initTabIter(it, i.mainModule.tab) + while s != nil: + yield s + s = nextIter(it, i.mainModule.tab) + +proc selectUniqueSymbol*(i: Interpreter; name: string; + symKinds: set[TSymKind] = {skLet, skVar}): PSym = + ## Can be used to access a unique symbol of ``name`` and + ## the given ``symKinds`` filter. + assert i != nil + assert i.mainModule != nil, "no main module selected" + let n = getIdent(i.graph.cache, name) + var it: TIdentIter + var s = initIdentIter(it, i.mainModule.tab, n) + result = nil + while s != nil: + if s.kind in symKinds: + if result == nil: result = s + else: return nil # ambiguous + s = nextIdentIter(it, i.mainModule.tab) + +proc selectRoutine*(i: Interpreter; name: string): PSym = + ## Selects a declared rountine (proc/func/etc) from the main module. + ## The routine needs to have the export marker ``*``. The only matching + ## routine is returned and ``nil`` if it is overloaded. + result = selectUniqueSymbol(i, name, {skTemplate, skMacro, skFunc, + skMethod, skProc, skConverter}) + +proc callRoutine*(i: Interpreter; routine: PSym; args: openArray[PNode]): PNode = + assert i != nil + result = vm.execProc(PCtx i.graph.vm, routine, args) + +proc getGlobalValue*(i: Interpreter; letOrVar: PSym): PNode = + result = vm.getGlobalValue(PCtx i.graph.vm, letOrVar) + +proc implementRoutine*(i: Interpreter; pkg, module, name: string; + impl: proc (a: VmArgs) {.closure, gcsafe.}) = + assert i != nil + let vm = PCtx(i.graph.vm) + vm.registerCallback(pkg & "." & module & "." & name, impl) + +proc evalScript*(i: Interpreter; scriptStream: PLLStream = nil) = + ## This can also be used to *reload* the script. + assert i != nil + assert i.mainModule != nil, "no main module selected" + initStrTable(i.mainModule.tab) + i.mainModule.ast = nil + + let s = if scriptStream != nil: scriptStream + else: llStreamOpen(findFile(i.graph.config, i.scriptName), fmRead) + processModule(i.graph, i.mainModule, s) + +proc findNimStdLib*(): string = + ## Tries to find a path to a valid "system.nim" file. + ## Returns "" on failure. + try: + let nimexe = os.findExe("nim") + if nimexe.len == 0: return "" + result = nimexe.splitPath()[0] /../ "lib" + if not fileExists(result / "system.nim"): + when defined(unix): + result = nimexe.expandSymlink.splitPath()[0] /../ "lib" + if not fileExists(result / "system.nim"): return "" + except OSError, ValueError: + return "" + +proc createInterpreter*(scriptName: string; + searchPaths: openArray[string]; + flags: TSandboxFlags = {}): Interpreter = + var conf = newConfigRef() var cache = newIdentCache() - var m = makeStdinModule(graph) + var graph = newModuleGraph(cache, conf) + connectCallbacks(graph) + initDefines(conf.symbols) + defineSymbol(conf.symbols, "nimscript") + defineSymbol(conf.symbols, "nimconfig") + registerPass(graph, semPass) + registerPass(graph, evalPass) + + for p in searchPaths: + conf.searchPaths.add(p) + if conf.libpath.len == 0: conf.libpath = p + + var m = graph.makeModule(scriptName) incl(m.flags, sfMainModule) - compileSystemModule(graph,cache) - processModule(graph,m, llStreamOpen(program), nil, cache) + var vm = newCtx(m, cache, graph) + vm.mode = emRepl + vm.features = flags + graph.vm = vm + graph.compileSystemModule() + result = Interpreter(mainModule: m, graph: graph, scriptName: scriptName) + +proc destroyInterpreter*(i: Interpreter) = + ## destructor. + discard "currently nothing to do." diff --git a/compiler/nimfix/nimfix.nim b/compiler/nimfix/nimfix.nim index c3e29f9a1..58b019cd3 100644 --- a/compiler/nimfix/nimfix.nim +++ b/compiler/nimfix/nimfix.nim @@ -11,7 +11,7 @@ import strutils, os, parseopt import compiler/[options, commands, modules, sem, - passes, passaux, nimfix/pretty, + passes, passaux, linter, msgs, nimconf, extccomp, condsyms, modulegraphs, idents] diff --git a/compiler/nimfix/prettybase.nim b/compiler/nimfix/prettybase.nim index c32dbe623..c3e16e5ba 100644 --- a/compiler/nimfix/prettybase.nim +++ b/compiler/nimfix/prettybase.nim @@ -7,64 +7,13 @@ # distribution, for details about the copyright. # -import strutils, lexbase, streams -import ".." / [ast, msgs, idents] +import strutils except Letters +import lexbase, streams +import ".." / [ast, msgs, lineinfos, idents, options, linter] from os import splitFile -type - TSourceFile* = object - lines*: seq[string] - dirty*, isNimfixFile*: bool - fullpath*, newline*: string - fileIdx*: FileIndex - -var - gSourceFiles*: seq[TSourceFile] = @[] - -proc loadFile*(info: TLineInfo) = - let i = info.fileIndex.int - if i >= gSourceFiles.len: - gSourceFiles.setLen(i+1) - if gSourceFiles[i].lines.isNil: - gSourceFiles[i].fileIdx = info.fileIndex - gSourceFiles[i].lines = @[] - let path = info.toFullPath - gSourceFiles[i].fullpath = path - gSourceFiles[i].isNimfixFile = path.splitFile.ext == ".nimfix" - # we want to die here for IOError: - for line in lines(path): - gSourceFiles[i].lines.add(line) - # extract line ending of the file: - var lex: BaseLexer - open(lex, newFileStream(path, fmRead)) - var pos = lex.bufpos - while true: - case lex.buf[pos] - of '\c': - gSourceFiles[i].newline = "\c\L" - break - of '\L', '\0': - gSourceFiles[i].newline = "\L" - break - else: discard - inc pos - close(lex) - -const - Letters* = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '_'} - -proc identLen*(line: string, start: int): int = - while start+result < line.len and line[start+result] in Letters: - inc result - -proc differ*(line: string, a, b: int, x: string): bool = - let y = line[a..b] - result = cmpIgnoreStyle(y, x) == 0 and y != x - -proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PIdent) = - loadFile(info) - - let line = gSourceFiles[info.fileIndex.int32].lines[info.line.int-1] +proc replaceDeprecated*(conf: ConfigRef; info: TLineInfo; oldSym, newSym: PIdent) = + let line = sourceLine(conf, info) var first = min(info.col.int, line.len) if first < 0: return #inc first, skipIgnoreCase(line, "proc ", first) @@ -75,20 +24,18 @@ proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PIdent) = let last = first+identLen(line, first)-1 if cmpIgnoreStyle(line[first..last], oldSym.s) == 0: var x = line.substr(0, first-1) & newSym.s & line.substr(last+1) - system.shallowCopy(gSourceFiles[info.fileIndex.int32].lines[info.line.int-1], x) - gSourceFiles[info.fileIndex.int32].dirty = true + system.shallowCopy(conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1], x) + conf.m.fileInfos[info.fileIndex.int32].dirty = true #if newSym.s == "File": writeStackTrace() -proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PSym) = - replaceDeprecated(info, oldSym.name, newSym.name) - -proc replaceComment*(info: TLineInfo) = - loadFile(info) +proc replaceDeprecated*(conf: ConfigRef; info: TLineInfo; oldSym, newSym: PSym) = + replaceDeprecated(conf, info, oldSym.name, newSym.name) - let line = gSourceFiles[info.fileIndex.int32].lines[info.line.int-1] +proc replaceComment*(conf: ConfigRef; info: TLineInfo) = + let line = sourceLine(conf, info) var first = info.col.int if line[first] != '#': inc first var x = line.substr(0, first-1) & "discard " & line.substr(first+1).escape - system.shallowCopy(gSourceFiles[info.fileIndex.int32].lines[info.line.int-1], x) - gSourceFiles[info.fileIndex.int32].dirty = true + system.shallowCopy(conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1], x) + conf.m.fileInfos[info.fileIndex.int32].dirty = true diff --git a/compiler/nimsets.nim b/compiler/nimsets.nim index 6cb675ed8..b00353e20 100644 --- a/compiler/nimsets.nim +++ b/compiler/nimsets.nim @@ -10,7 +10,8 @@ # this unit handles Nim sets; it implements symbolic sets import - ast, astalgo, trees, nversion, msgs, platform, bitsets, types, renderer + ast, astalgo, trees, nversion, lineinfos, platform, bitsets, types, renderer, + options proc inSet*(s: PNode, elem: PNode): bool = assert s.kind == nkCurly @@ -58,10 +59,10 @@ proc someInSet*(s: PNode, a, b: PNode): bool = return true result = false -proc toBitSet*(s: PNode, b: var TBitSet) = +proc toBitSet*(conf: ConfigRef; s: PNode, b: var TBitSet) = var first, j: BiggestInt - first = firstOrd(s.typ.sons[0]) - bitSetInit(b, int(getSize(s.typ))) + first = firstOrd(conf, s.typ.sons[0]) + bitSetInit(b, int(getSize(conf, s.typ))) for i in countup(0, sonsLen(s) - 1): if s.sons[i].kind == nkRange: j = getOrdValue(s.sons[i].sons[0]) @@ -71,13 +72,13 @@ proc toBitSet*(s: PNode, b: var TBitSet) = else: bitSetIncl(b, getOrdValue(s.sons[i]) - first) -proc toTreeSet*(s: TBitSet, settype: PType, info: TLineInfo): PNode = +proc toTreeSet*(conf: ConfigRef; s: TBitSet, settype: PType, info: TLineInfo): PNode = var a, b, e, first: BiggestInt # a, b are interval borders elemType: PType n: PNode elemType = settype.sons[0] - first = firstOrd(elemType) + first = firstOrd(conf, elemType) result = newNodeI(nkCurly, info) result.typ = settype result.info = info @@ -107,42 +108,42 @@ proc toTreeSet*(s: TBitSet, settype: PType, info: TLineInfo): PNode = template nodeSetOp(a, b: PNode, op: untyped) {.dirty.} = var x, y: TBitSet - toBitSet(a, x) - toBitSet(b, y) + toBitSet(conf, a, x) + toBitSet(conf, b, y) op(x, y) - result = toTreeSet(x, a.typ, a.info) + result = toTreeSet(conf, x, a.typ, a.info) -proc unionSets*(a, b: PNode): PNode = nodeSetOp(a, b, bitSetUnion) -proc diffSets*(a, b: PNode): PNode = nodeSetOp(a, b, bitSetDiff) -proc intersectSets*(a, b: PNode): PNode = nodeSetOp(a, b, bitSetIntersect) -proc symdiffSets*(a, b: PNode): PNode = nodeSetOp(a, b, bitSetSymDiff) +proc unionSets*(conf: ConfigRef; a, b: PNode): PNode = nodeSetOp(a, b, bitSetUnion) +proc diffSets*(conf: ConfigRef; a, b: PNode): PNode = nodeSetOp(a, b, bitSetDiff) +proc intersectSets*(conf: ConfigRef; a, b: PNode): PNode = nodeSetOp(a, b, bitSetIntersect) +proc symdiffSets*(conf: ConfigRef; a, b: PNode): PNode = nodeSetOp(a, b, bitSetSymDiff) -proc containsSets*(a, b: PNode): bool = +proc containsSets*(conf: ConfigRef; a, b: PNode): bool = var x, y: TBitSet - toBitSet(a, x) - toBitSet(b, y) + toBitSet(conf, a, x) + toBitSet(conf, b, y) result = bitSetContains(x, y) -proc equalSets*(a, b: PNode): bool = +proc equalSets*(conf: ConfigRef; a, b: PNode): bool = var x, y: TBitSet - toBitSet(a, x) - toBitSet(b, y) + toBitSet(conf, a, x) + toBitSet(conf, b, y) result = bitSetEquals(x, y) -proc complement*(a: PNode): PNode = +proc complement*(conf: ConfigRef; a: PNode): PNode = var x: TBitSet - toBitSet(a, x) + toBitSet(conf, a, x) for i in countup(0, high(x)): x[i] = not x[i] - result = toTreeSet(x, a.typ, a.info) + result = toTreeSet(conf, x, a.typ, a.info) -proc deduplicate*(a: PNode): PNode = +proc deduplicate*(conf: ConfigRef; a: PNode): PNode = var x: TBitSet - toBitSet(a, x) - result = toTreeSet(x, a.typ, a.info) + toBitSet(conf, a, x) + result = toTreeSet(conf, x, a.typ, a.info) -proc cardSet*(a: PNode): BiggestInt = +proc cardSet*(conf: ConfigRef; a: PNode): BiggestInt = var x: TBitSet - toBitSet(a, x) + toBitSet(conf, a, x) result = bitSetCard(x) proc setHasRange*(s: PNode): bool = diff --git a/compiler/nversion.nim b/compiler/nversion.nim index caa818d79..4b8cf7100 100644 --- a/compiler/nversion.nim +++ b/compiler/nversion.nim @@ -15,6 +15,6 @@ const VersionAsString* = system.NimVersion RodFileVersion* = "1223" # modify this if the rod-format changes! - NimCompilerApiVersion* = 1 ## Check for the existance of this before accessing it + NimCompilerApiVersion* = 2 ## Check for the existance of this before accessing it ## as older versions of the compiler API do not ## declare this. diff --git a/compiler/options.nim b/compiler/options.nim index 150e67a18..1d6ddb09f 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -8,7 +8,8 @@ # import - os, strutils, strtabs, osproc, sets, configuration, platform + os, strutils, strtabs, osproc, sets, lineinfos, platform, + prefixmatches from terminal import isatty @@ -53,7 +54,7 @@ type # please make sure we have under 32 options optGenScript, # generate a script file to compile the *.c files optGenMapping, # generate a mapping file optRun, # run the compiled project - optCaasEnabled # compiler-as-a-service is running + optCheckNep1, # check that the names adhere to NEP-1 optSkipConfigFile, # skip the general config file optSkipProjConfigFile, # skip the project's config file optSkipUserConfigFile, # skip the users's config file @@ -73,6 +74,7 @@ type # please make sure we have under 32 options optNoCppExceptions # use C exception handling even with CPP optExcessiveStackTrace # fully qualified module filenames optWholeProject # for 'doc2': output any dependency + optMixedMode # true if some module triggered C++ codegen optListFullPaths optNoNimblePath optDynlibOverrideAll @@ -116,26 +118,65 @@ type callOperator, parallel, destructor, - notnil, - oldIterTransf + notnil SymbolFilesOption* = enum - disabledSf, enabledSf, writeOnlySf, readOnlySf, v2Sf - - ConfigRef* = ref object ## eventually all global configuration should be moved here + disabledSf, writeOnlySf, readOnlySf, v2Sf + + TSystemCC* = enum + ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc, + ccTcc, ccPcc, ccUcc, ccIcl, ccIcc + + CfileFlag* {.pure.} = enum + Cached, ## no need to recompile this time + External ## file was introduced via .compile pragma + + Cfile* = object + cname*, obj*: string + flags*: set[CFileFlag] + CfileList* = seq[Cfile] + + Suggest* = ref object + section*: IdeCmd + qualifiedPath*: seq[string] + name*: ptr string # not used beyond sorting purposes; name is also + # part of 'qualifiedPath' + filePath*: string + line*: int # Starts at 1 + column*: int # Starts at 0 + doc*: string # Not escaped (yet) + forth*: string # type + quality*: range[0..100] # matching quality + isGlobal*: bool # is a global variable + contextFits*: bool # type/non-type context matches + prefix*: PrefixMatch + symkind*: byte + scope*, localUsages*, globalUsages*: int # more usages is better + tokenLen*: int + version*: int + Suggestions* = seq[Suggest] + + ConfigRef* = ref object ## every global configuration + ## fields marked with '*' are subject to + ## the incremental compilation mechanisms + ## (+) means "part of the dependency" + target*: Target # (+) linesCompiled*: int # all lines that have been compiled - options*: TOptions - globalOptions*: TGlobalOptions + options*: TOptions # (+) + globalOptions*: TGlobalOptions # (+) + m*: MsgConfig + evalTemplateCounter*: int + evalMacroCounter*: int exitcode*: int8 cmd*: TCommands # the command - selectedGC*: TGCMode # the selected GC + selectedGC*: TGCMode # the selected GC (+) verbosity*: int # how verbose the compiler is numberOfProcessors*: int # number of processors evalExpr*: string # expression for idetools --eval lastCmdTime*: float # when caas is enabled, we measure each command symbolFiles*: SymbolFilesOption - cppDefines*: HashSet[string] + cppDefines*: HashSet[string] # (*) headerFile*: string features*: set[Feature] arguments*: string ## the arguments to be passed to the program that @@ -143,11 +184,13 @@ type helpWritten*: bool ideCmd*: IdeCmd oldNewlines*: bool + cCompiler*: TSystemCC enableNotes*: TNoteKinds disableNotes*: TNoteKinds foreignPackageNotes*: TNoteKinds notes*: TNoteKinds mainPackageNotes*: TNoteKinds + mainPackageId*: int errorCounter*: int hintCounter*: int warnCounter*: int @@ -165,7 +208,7 @@ type projectPath*: string # holds a path like /home/alice/projects/nim/compiler/ projectFull*: string # projectPath/projectName projectIsStdin*: bool # whether we're compiling from stdin - projectMainIdx*: int32 # the canonical path id of the main module + projectMainIdx*: FileIndex # the canonical path id of the main module command*: string # the main command (e.g. cc, check, scan, etc) commandArgs*: seq[string] # any arguments after the main command keepComments*: bool # whether the parser needs to keep comments @@ -174,6 +217,33 @@ type docSeeSrcUrl*: string # if empty, no seeSrc will be generated. \ # The string uses the formatting variables `path` and `line`. + # the used compiler + cIncludes*: seq[string] # directories to search for included files + cLibs*: seq[string] # directories to search for lib files + cLinkedLibs*: seq[string] # libraries to link + + externalToLink*: seq[string] # files to link in addition to the file + # we compiled (*) + linkOptionsCmd*: string + compileOptionsCmd*: seq[string] + linkOptions*: string # (*) + compileOptions*: string # (*) + ccompilerpath*: string + toCompile*: CfileList # (*) + suggestionResultHook*: proc (result: Suggest) {.closure.} + suggestVersion*: int + suggestMaxResults*: int + lastLineInfo*: TLineInfo + writelnHook*: proc (output: string) {.closure.} + structuredErrorHook*: proc (config: ConfigRef; info: TLineInfo; msg: string; + severity: Severity) {.closure.} + +template depConfigFields*(fn) {.dirty.} = + fn(target) + fn(options) + fn(globalOptions) + fn(selectedGC) + const oldExperimentalFeatures* = {implicitDeref, dotOperators, callOperator, parallel} const @@ -196,9 +266,11 @@ template newPackageCache*(): untyped = proc newConfigRef*(): ConfigRef = result = ConfigRef( selectedGC: gcRefc, + cCompiler: ccGcc, verbosity: 1, options: DefaultOptions, globalOptions: DefaultGlobalOptions, + m: initMsgConfig(), evalExpr: "", cppDefines: initSet[string](), headerFile: "", features: {}, foreignPackageNotes: {hintProcessing, warnUnknownMagic, @@ -216,15 +288,28 @@ proc newConfigRef*(): ConfigRef = projectPath: "", # holds a path like /home/alice/projects/nim/compiler/ projectFull: "", # projectPath/projectName projectIsStdin: false, # whether we're compiling from stdin - projectMainIdx: 0'i32, # the canonical path id of the main module + projectMainIdx: FileIndex(0'i32), # the canonical path id of the main module command: "", # the main command (e.g. cc, check, scan, etc) commandArgs: @[], # any arguments after the main command keepComments: true, # whether the parser needs to keep comments implicitImports: @[], # modules that are to be implicitly imported implicitIncludes: @[], # modules that are to be implicitly included docSeeSrcUrl: "", - arguments: "" + cIncludes: @[], # directories to search for included files + cLibs: @[], # directories to search for lib files + cLinkedLibs: @[], # libraries to link + + externalToLink: @[], + linkOptionsCmd: "", + compileOptionsCmd: @[], + linkOptions: "", + compileOptions: "", + ccompilerpath: "", + toCompile: @[], + arguments: "", + suggestMaxResults: 10_000 ) + setTargetFromSystem(result.target) # enable colors by default on terminals if terminal.isatty(stderr): incl(result.globalOptions, optUseColors) @@ -246,39 +331,39 @@ proc cppDefine*(c: ConfigRef; define: string) = proc isDefined*(conf: ConfigRef; symbol: string): bool = if conf.symbols.hasKey(symbol): result = conf.symbols[symbol] != "false" - elif cmpIgnoreStyle(symbol, CPU[targetCPU].name) == 0: + elif cmpIgnoreStyle(symbol, CPU[conf.target.targetCPU].name) == 0: result = true - elif cmpIgnoreStyle(symbol, platform.OS[targetOS].name) == 0: + elif cmpIgnoreStyle(symbol, platform.OS[conf.target.targetOS].name) == 0: result = true else: case symbol.normalize - of "x86": result = targetCPU == cpuI386 - of "itanium": result = targetCPU == cpuIa64 - of "x8664": result = targetCPU == cpuAmd64 + of "x86": result = conf.target.targetCPU == cpuI386 + of "itanium": result = conf.target.targetCPU == cpuIa64 + of "x8664": result = conf.target.targetCPU == cpuAmd64 of "posix", "unix": - result = targetOS in {osLinux, osMorphos, osSkyos, osIrix, osPalmos, + result = conf.target.targetOS in {osLinux, osMorphos, osSkyos, osIrix, osPalmos, osQnx, osAtari, osAix, osHaiku, osVxWorks, osSolaris, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osMacosx, osAndroid} of "linux": - result = targetOS in {osLinux, osAndroid} + result = conf.target.targetOS in {osLinux, osAndroid} of "bsd": - result = targetOS in {osNetbsd, osFreebsd, osOpenbsd, osDragonfly} + result = conf.target.targetOS in {osNetbsd, osFreebsd, osOpenbsd, osDragonfly} of "emulatedthreadvars": - result = platform.OS[targetOS].props.contains(ospLacksThreadVars) - of "msdos": result = targetOS == osDos - of "mswindows", "win32": result = targetOS == osWindows - of "macintosh": result = targetOS in {osMacos, osMacosx} - of "sunos": result = targetOS == osSolaris - of "littleendian": result = CPU[targetCPU].endian == platform.littleEndian - of "bigendian": result = CPU[targetCPU].endian == platform.bigEndian - of "cpu8": result = CPU[targetCPU].bit == 8 - of "cpu16": result = CPU[targetCPU].bit == 16 - of "cpu32": result = CPU[targetCPU].bit == 32 - of "cpu64": result = CPU[targetCPU].bit == 64 + result = platform.OS[conf.target.targetOS].props.contains(ospLacksThreadVars) + of "msdos": result = conf.target.targetOS == osDos + of "mswindows", "win32": result = conf.target.targetOS == osWindows + of "macintosh": result = conf.target.targetOS in {osMacos, osMacosx} + of "sunos": result = conf.target.targetOS == osSolaris + of "littleendian": result = CPU[conf.target.targetCPU].endian == platform.littleEndian + of "bigendian": result = CPU[conf.target.targetCPU].endian == platform.bigEndian + of "cpu8": result = CPU[conf.target.targetCPU].bit == 8 + of "cpu16": result = CPU[conf.target.targetCPU].bit == 16 + of "cpu32": result = CPU[conf.target.targetCPU].bit == 32 + of "cpu64": result = CPU[conf.target.targetCPU].bit == 64 of "nimrawsetjmp": - result = targetOS in {osSolaris, osNetbsd, osFreebsd, osOpenbsd, + result = conf.target.targetOS in {osSolaris, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osMacosx} else: discard @@ -286,8 +371,7 @@ proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd in {cmdDoc, proc usesNativeGC*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc template compilationCachePresent*(conf: ConfigRef): untyped = - conf.symbolFiles in {enabledSf, writeOnlySf} -# {optCaasEnabled, optSymbolFiles} * gGlobalOptions != {} + conf.symbolFiles in {v2Sf, writeOnlySf} template optPreserveOrigSource*(conf: ConfigRef): untyped = optEmbedOrigSrc in conf.globalOptions @@ -432,8 +516,10 @@ proc completeGeneratedFilePath*(conf: ConfigRef; f: string, createSubDir: bool = result = joinPath(subdir, tail) #echo "completeGeneratedFilePath(", f, ") = ", result -proc rawFindFile(conf: ConfigRef; f: string): string = +proc rawFindFile(conf: ConfigRef; f: string; suppressStdlib: bool): string = for it in conf.searchPaths: + if suppressStdlib and it.startsWith(conf.libpath): + continue result = joinPath(it, f) if existsFile(result): return canonicalizePath(conf, result) @@ -457,13 +543,13 @@ template patchModule(conf: ConfigRef) {.dirty.} = let ov = conf.moduleOverrides[key] if ov.len > 0: result = ov -proc findFile*(conf: ConfigRef; f: string): string {.procvar.} = +proc findFile*(conf: ConfigRef; f: string; suppressStdlib = false): string {.procvar.} = if f.isAbsolute: result = if f.existsFile: f else: "" else: - result = rawFindFile(conf, f) + result = rawFindFile(conf, f, suppressStdlib) if result.len == 0: - result = rawFindFile(conf, f.toLowerAscii) + result = rawFindFile(conf, f.toLowerAscii, suppressStdlib) if result.len == 0: result = rawFindFile2(conf, f) if result.len == 0: @@ -472,21 +558,15 @@ proc findFile*(conf: ConfigRef; f: string): string {.procvar.} = proc findModule*(conf: ConfigRef; modulename, currentModule: string): string = # returns path to module - when defined(nimfix): - # '.nimfix' modules are preferred over '.nim' modules so that specialized - # versions can be kept for 'nimfix'. - block: - let m = addFileExt(modulename, "nimfix") - let currentPath = currentModule.splitFile.dir - result = currentPath / m - if not existsFile(result): - result = findFile(conf, m) - if existsFile(result): return result + const pkgPrefix = "pkg/" let m = addFileExt(modulename, NimExt) - let currentPath = currentModule.splitFile.dir - result = currentPath / m - if not existsFile(result): - result = findFile(conf, m) + if m.startsWith(pkgPrefix): + result = findFile(conf, m.substr(pkgPrefix.len), suppressStdlib = true) + else: + let currentPath = currentModule.splitFile.dir + result = currentPath / m + if not existsFile(result): + result = findFile(conf, m) patchModule(conf) proc findProjectNimFile*(conf: ConfigRef; pkg: string): string = diff --git a/compiler/parser.nim b/compiler/parser.nim index fbc57ebb6..c513fac68 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -27,7 +27,10 @@ when isMainModule: outp.close import - llstream, lexer, idents, strutils, ast, astalgo, msgs, options, configuration + llstream, lexer, idents, strutils, ast, astalgo, msgs, options, lineinfos + +when defined(nimpretty2): + import layouter type TParser* = object # A TParser object represents a file that @@ -40,10 +43,16 @@ type tok*: TToken # The current token inPragma*: int # Pragma level inSemiStmtList*: int + emptyNode: PNode + when defined(nimpretty2): + em: Emitter SymbolMode = enum smNormal, smAllowNil, smAfterDot + TPrimaryMode = enum + pmNormal, pmTypeDesc, pmTypeDef, pmSkipSuffix + proc parseAll*(p: var TParser): PNode proc closeParser*(p: var TParser) proc parseTopLevelStmt*(p: var TParser): PNode @@ -75,6 +84,9 @@ proc parsePragma(p: var TParser): PNode proc postExprBlocks(p: var TParser, x: PNode): PNode proc parseExprStmt(p: var TParser): PNode proc parseBlock(p: var TParser): PNode +proc primary(p: var TParser, mode: TPrimaryMode): PNode +proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode + # implementation proc getTok(p: var TParser) = @@ -82,6 +94,11 @@ proc getTok(p: var TParser) = ## `tok` member. rawGetTok(p.lex, p.tok) p.hasProgress = true + when defined(nimpretty2): + emitTok(p.em, p.lex, p.tok) + while p.tok.tokType == tkComment: + rawGetTok(p.lex, p.tok) + emitTok(p.em, p.lex, p.tok) proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream, cache: IdentCache; config: ConfigRef; @@ -90,9 +107,12 @@ proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream, ## initToken(p.tok) openLexer(p.lex, fileIdx, inputStream, cache, config) + when defined(nimpretty2): + openEmitter(p.em, cache, config, fileIdx) getTok(p) # read the first token p.firstTok = true p.strongSpaces = strongSpaces + p.emptyNode = newNode(nkEmpty) proc openParser*(p: var TParser, filename: string, inputStream: PLLStream, cache: IdentCache; config: ConfigRef; @@ -102,6 +122,8 @@ proc openParser*(p: var TParser, filename: string, inputStream: PLLStream, proc closeParser(p: var TParser) = ## Close a parser, freeing up its resources. closeLexer(p.lex) + when defined(nimpretty2): + closeEmitter(p.em) proc parMessage(p: TParser, msg: TMsgKind, arg = "") = ## Produce and emit the parser message `arg` to output. @@ -131,7 +153,7 @@ proc rawSkipComment(p: var TParser, node: PNode) = if node.comment == nil: node.comment = "" when defined(nimpretty): if p.tok.commentOffsetB > p.tok.commentOffsetA: - add node.comment, fileSection(p.lex.fileIdx, p.tok.commentOffsetA, p.tok.commentOffsetB) + add node.comment, fileSection(p.lex.config, p.lex.fileIdx, p.tok.commentOffsetA, p.tok.commentOffsetB) else: add node.comment, p.tok.literal else: @@ -316,6 +338,8 @@ proc colcom(p: var TParser, n: PNode) = eat(p, tkColon) skipComment(p, n) +const tkBuiltInMagics = {tkType, tkStatic, tkAddr} + proc parseSymbol(p: var TParser, mode = smNormal): PNode = #| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`' #| | IDENT | KEYW @@ -324,7 +348,7 @@ proc parseSymbol(p: var TParser, mode = smNormal): PNode = result = newIdentNodeP(p.tok.ident, p) getTok(p) of tokKeywordLow..tokKeywordHigh: - if p.tok.tokType == tkAddr or p.tok.tokType == tkType or mode == smAfterDot: + if p.tok.tokType in tkBuiltInMagics or mode == smAfterDot: # for backwards compatibility these 2 are always valid: result = newIdentNodeP(p.tok.ident, p) getTok(p) @@ -333,7 +357,7 @@ proc parseSymbol(p: var TParser, mode = smNormal): PNode = getTok(p) else: parMessage(p, errIdentifierExpected, p.tok) - result = ast.emptyNode + result = p.emptyNode of tkAccent: result = newNodeP(nkAccQuoted, p) getTok(p) @@ -364,7 +388,7 @@ proc parseSymbol(p: var TParser, mode = smNormal): PNode = # But: this really sucks for idetools and keywords, so we don't do it # if it is a keyword: #if not isKeyword(p.tok.tokType): getTok(p) - result = ast.emptyNode + result = p.emptyNode proc colonOrEquals(p: var TParser, a: PNode): PNode = if p.tok.tokType == tkColon: @@ -392,6 +416,8 @@ proc exprColonEqExpr(p: var TParser): PNode = proc exprList(p: var TParser, endTok: TTokType, result: PNode) = #| exprList = expr ^+ comma + when defined(nimpretty2): + inc p.em.doIndentMore getTok(p) optInd(p, result) # progress guaranteed @@ -401,6 +427,8 @@ proc exprList(p: var TParser, endTok: TTokType, result: PNode) = if p.tok.tokType != tkComma: break getTok(p) optInd(p, a) + when defined(nimpretty2): + dec p.em.doIndentMore proc exprColonEqExprListAux(p: var TParser, endTok: TTokType, result: PNode) = assert(endTok in {tkCurlyRi, tkCurlyDotRi, tkBracketRi, tkParRi}) @@ -509,9 +537,6 @@ proc parseGStrLit(p: var TParser, a: PNode): PNode = else: result = a -type - TPrimaryMode = enum pmNormal, pmTypeDesc, pmTypeDef, pmSkipSuffix - proc complexOrSimpleStmt(p: var TParser): PNode proc simpleExpr(p: var TParser, mode = pmNormal): PNode @@ -609,7 +634,7 @@ proc identOrLiteral(p: var TParser, mode: TPrimaryMode): PNode = #| tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')' #| arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']' case p.tok.tokType - of tkSymbol, tkType, tkAddr: + of tkSymbol, tkBuiltInMagics: result = newIdentNodeP(p.tok.ident, p) getTok(p) result = parseGStrLit(p, result) @@ -703,7 +728,7 @@ proc identOrLiteral(p: var TParser, mode: TPrimaryMode): PNode = else: parMessage(p, errExprExpected, p.tok) getTok(p) # we must consume a token here to prevend endless loops! - result = ast.emptyNode + result = p.emptyNode proc namedParams(p: var TParser, callee: PNode, kind: TNodeKind, endTok: TTokType): PNode = @@ -725,7 +750,12 @@ proc commandParam(p: var TParser, isFirstParam: var bool): PNode = addSon(result, parseExpr(p)) isFirstParam = false -proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode = +const + tkTypeClasses = {tkRef, tkPtr, tkVar, tkStatic, tkType, + tkEnum, tkTuple, tkObject, tkProc} + +proc primarySuffix(p: var TParser, r: PNode, + baseIndent: int, mode: TPrimaryMode): PNode = #| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks? #| | doBlocks #| | '.' optInd symbol generalizedLit? @@ -742,7 +772,14 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode = case p.tok.tokType of tkParLe: # progress guaranteed - somePar() + if p.tok.strongSpaceA > 0: + # inside type sections, expressions such as `ref (int, bar)` + # are parsed as a nkCommand with a single tuple argument (nkPar) + if mode == pmTypeDef: + result = newNodeP(nkCommand, p) + result.addSon r + result.addSon primary(p, pmNormal) + break result = namedParams(p, result, nkCall, tkParRi) if result.len > 1 and result.sons[1].kind == nkExprColonExpr: result.kind = nkObjConstr @@ -758,8 +795,13 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode = # progress guaranteed somePar() result = namedParams(p, result, nkCurlyExpr, tkCurlyRi) - of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, tkAddr, tkType, - tkOpr, tkDotDot: + of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, + tkOpr, tkDotDot, tkTypeClasses - {tkRef, tkPtr}: + # XXX: In type sections we allow the free application of the + # command syntax, with the exception of expressions such as + # `foo ref` or `foo ptr`. Unfortunately, these two are also + # used as infix operators for the memory regions feature and + # the current parsing rules don't play well here. if p.inPragma == 0 and (isUnary(p) or p.tok.tokType notin {tkOpr, tkDotDot}): # actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet # solution, but pragmas.nim can't handle that @@ -784,9 +826,6 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode = else: break -proc primary(p: var TParser, mode: TPrimaryMode): PNode -proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode - proc parseOperators(p: var TParser, headNode: PNode, limit: int, mode: TPrimaryMode): PNode = result = headNode @@ -821,7 +860,11 @@ proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode = result = parseOperators(p, result, limit, mode) proc simpleExpr(p: var TParser, mode = pmNormal): PNode = + when defined(nimpretty2): + inc p.em.doIndentMore result = simpleExprAux(p, -1, mode) + when defined(nimpretty2): + dec p.em.doIndentMore proc parseIfExpr(p: var TParser, kind: TNodeKind): PNode = #| condExpr = expr colcom expr optInd @@ -896,8 +939,12 @@ proc parsePragma(p: var TParser): PNode = getTok(p) skipComment(p, a) optPar(p) - if p.tok.tokType in {tkCurlyDotRi, tkCurlyRi}: getTok(p) - else: parMessage(p, "expected '.}'") + if p.tok.tokType in {tkCurlyDotRi, tkCurlyRi}: + when defined(nimpretty2): + if p.tok.tokType == tkCurlyRi: curlyRiWasPragma(p.em) + getTok(p) + else: + parMessage(p, "expected '.}'") dec p.inPragma proc identVis(p: var TParser; allowDot=false): PNode = @@ -905,6 +952,8 @@ proc identVis(p: var TParser; allowDot=false): PNode = #| identVisDot = symbol '.' optInd symbol opr? var a = parseSymbol(p) if p.tok.tokType == tkOpr: + when defined(nimpretty2): + starWasExportMarker(p.em) result = newNodeP(nkPostfix, p) addSon(result, newIdentNodeP(p.tok.ident, p)) addSon(result, a) @@ -982,6 +1031,8 @@ proc parseTuple(p: var TParser, indentAllowed = false): PNode = var a = parseIdentColonEquals(p, {}) addSon(result, a) if p.tok.tokType notin {tkComma, tkSemiColon}: break + when defined(nimpretty2): + commaWasSemicolon(p.em) getTok(p) skipComment(p, a) optPar(p) @@ -1015,7 +1066,9 @@ proc parseParamList(p: var TParser, retColon = true): PNode = #| paramListColon = paramList? (':' optInd typeDesc)? var a: PNode result = newNodeP(nkFormalParams, p) - addSon(result, ast.emptyNode) # return type + addSon(result, p.emptyNode) # return type + when defined(nimpretty2): + inc p.em.doIndentMore let hasParLe = p.tok.tokType == tkParLe and p.tok.indent < 0 if hasParLe: getTok(p) @@ -1035,6 +1088,8 @@ proc parseParamList(p: var TParser, retColon = true): PNode = break addSon(result, a) if p.tok.tokType notin {tkComma, tkSemiColon}: break + when defined(nimpretty2): + commaWasSemicolon(p.em) getTok(p) skipComment(p, a) optPar(p) @@ -1047,13 +1102,15 @@ proc parseParamList(p: var TParser, retColon = true): PNode = result.sons[0] = parseTypeDesc(p) elif not retColon and not hasParle: # Mark as "not there" in order to mark for deprecation in the semantic pass: - result = ast.emptyNode + result = p.emptyNode + when defined(nimpretty2): + dec p.em.doIndentMore proc optPragmas(p: var TParser): PNode = if p.tok.tokType == tkCurlyDotLe and (p.tok.indent < 0 or realInd(p)): result = parsePragma(p) else: - result = ast.emptyNode + result = p.emptyNode proc parseDoBlock(p: var TParser; info: TLineInfo): PNode = #| doBlock = 'do' paramListArrow pragmas? colcom stmt @@ -1062,7 +1119,9 @@ proc parseDoBlock(p: var TParser; info: TLineInfo): PNode = colcom(p, result) result = parseStmt(p) if params.kind != nkEmpty: - result = newProcNode(nkDo, info, result, params = params, pragmas = pragmas) + result = newProcNode(nkDo, info, + body = result, params = params, name = p.emptyNode, pattern = p.emptyNode, + genericParams = p.emptyNode, pragmas = pragmas, exceptions = p.emptyNode) proc parseProcExpr(p: var TParser; isExpr: bool; kind: TNodeKind): PNode = #| procExpr = 'proc' paramListColon pragmas? ('=' COMMENT? stmt)? @@ -1075,9 +1134,9 @@ proc parseProcExpr(p: var TParser; isExpr: bool; kind: TNodeKind): PNode = if p.tok.tokType == tkEquals and isExpr: getTok(p) skipComment(p, result) - result = newProcNode(kind, info, parseStmt(p), - params = params, - pragmas = pragmas) + result = newProcNode(kind, info, body = parseStmt(p), + params = params, name = p.emptyNode, pattern = p.emptyNode, + genericParams = p.emptyNode, pragmas = pragmas, exceptions = p.emptyNode) else: result = newNodeI(nkProcTy, info) if hasSignature: @@ -1087,9 +1146,9 @@ proc parseProcExpr(p: var TParser; isExpr: bool; kind: TNodeKind): PNode = proc isExprStart(p: TParser): bool = case p.tok.tokType of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, - tkProc, tkFunc, tkIterator, tkBind, tkAddr, + tkProc, tkFunc, tkIterator, tkBind, tkBuiltInMagics, tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr, - tkTuple, tkObject, tkType, tkWhen, tkCase, tkOut: + tkTuple, tkObject, tkWhen, tkCase, tkOut: result = true else: result = false @@ -1150,7 +1209,6 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = #| | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum' #| primary = typeKeyw typeDescK #| / prefixOperator* identOrLiteral primarySuffix* - #| / 'static' primary #| / 'bind' primary if isOperator(p.tok): let isSigil = isSigilLike(p.tok) @@ -1163,7 +1221,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = #XXX prefix operators let baseInd = p.lex.currLineIndent addSon(result, primary(p, pmSkipSuffix)) - result = primarySuffix(p, result, baseInd) + result = primarySuffix(p, result, baseInd, mode) else: addSon(result, primary(p, pmNormal)) return @@ -1193,14 +1251,6 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = result = parseTypeClass(p) else: parMessage(p, "the 'concept' keyword is only valid in 'type' sections") - of tkStatic: - let info = parLineInfo(p) - getTokNoInd(p) - let next = primary(p, pmNormal) - if next.kind == nkBracket and next.sonsLen == 1: - result = newNode(nkStaticTy, info, @[next.sons[0]]) - else: - result = newNode(nkStaticExpr, info, @[next]) of tkBind: result = newNodeP(nkBind, p) getTok(p) @@ -1215,7 +1265,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = let baseInd = p.lex.currLineIndent result = identOrLiteral(p, mode) if mode != pmSkipSuffix: - result = primarySuffix(p, result, baseInd) + result = primarySuffix(p, result, baseInd, mode) proc parseTypeDesc(p: var TParser): PNode = #| typeDesc = simpleExpr @@ -1244,8 +1294,8 @@ proc postExprBlocks(p: var TParser, x: PNode): PNode = if p.tok.indent >= 0: return var - openingParams = emptyNode - openingPragmas = emptyNode + openingParams = p.emptyNode + openingPragmas = p.emptyNode if p.tok.tokType == tkDo: getTok(p) @@ -1264,8 +1314,12 @@ proc postExprBlocks(p: var TParser, x: PNode): PNode = stmtList.flags.incl nfBlockArg if openingParams.kind != nkEmpty: - result.add newProcNode(nkDo, stmtList.info, stmtList, - params = openingParams, pragmas = openingPragmas) + result.add newProcNode(nkDo, stmtList.info, body = stmtList, + params = openingParams, + name = p.emptyNode, pattern = p.emptyNode, + genericParams = p.emptyNode, + pragmas = openingPragmas, + exceptions = p.emptyNode) else: result.add stmtList @@ -1424,10 +1478,10 @@ proc parseReturnOrRaise(p: var TParser, kind: TNodeKind): PNode = getTok(p) if p.tok.tokType == tkComment: skipComment(p, result) - addSon(result, ast.emptyNode) + addSon(result, p.emptyNode) elif p.tok.indent >= 0 and p.tok.indent <= p.currInd or not isExprStart(p): # NL terminates: - addSon(result, ast.emptyNode) + addSon(result, p.emptyNode) else: var e = parseExpr(p) e = postExprBlocks(p, e) @@ -1568,7 +1622,7 @@ proc parseBlock(p: var TParser): PNode = #| blockExpr = 'block' symbol? colcom stmt result = newNodeP(nkBlockStmt, p) getTokNoInd(p) - if p.tok.tokType == tkColon: addSon(result, ast.emptyNode) + if p.tok.tokType == tkColon: addSon(result, p.emptyNode) else: addSon(result, parseSymbol(p)) colcom(p, result) addSon(result, parseStmt(p)) @@ -1586,7 +1640,7 @@ proc parseAsm(p: var TParser): PNode = result = newNodeP(nkAsmStmt, p) getTokNoInd(p) if p.tok.tokType == tkCurlyDotLe: addSon(result, parsePragma(p)) - else: addSon(result, ast.emptyNode) + else: addSon(result, p.emptyNode) case p.tok.tokType of tkStrLit: addSon(result, newStrNodeP(nkStrLit, p.tok.literal, p)) of tkRStrLit: addSon(result, newStrNodeP(nkRStrLit, p.tok.literal, p)) @@ -1594,7 +1648,7 @@ proc parseAsm(p: var TParser): PNode = newStrNodeP(nkTripleStrLit, p.tok.literal, p)) else: parMessage(p, "the 'asm' statement takes a string literal") - addSon(result, ast.emptyNode) + addSon(result, p.emptyNode) return getTok(p) @@ -1625,13 +1679,13 @@ proc parseGenericParam(p: var TParser): PNode = optInd(p, result) addSon(result, parseExpr(p)) else: - addSon(result, ast.emptyNode) + addSon(result, p.emptyNode) if p.tok.tokType == tkEquals: getTok(p) optInd(p, result) addSon(result, parseExpr(p)) else: - addSon(result, ast.emptyNode) + addSon(result, p.emptyNode) proc parseGenericParamList(p: var TParser): PNode = #| genericParamList = '[' optInd @@ -1644,6 +1698,8 @@ proc parseGenericParamList(p: var TParser): PNode = var a = parseGenericParam(p) addSon(result, a) if p.tok.tokType notin {tkComma, tkSemiColon}: break + when defined(nimpretty2): + commaWasSemicolon(p.em) getTok(p) skipComment(p, a) optPar(p) @@ -1667,22 +1723,22 @@ proc parseRoutine(p: var TParser, kind: TNodeKind): PNode = optInd(p, result) addSon(result, identVis(p)) if p.tok.tokType == tkCurlyLe and p.validInd: addSon(result, p.parsePattern) - else: addSon(result, ast.emptyNode) + else: addSon(result, p.emptyNode) if p.tok.tokType == tkBracketLe and p.validInd: result.add(p.parseGenericParamList) else: - addSon(result, ast.emptyNode) + addSon(result, p.emptyNode) addSon(result, p.parseParamList) if p.tok.tokType == tkCurlyDotLe and p.validInd: addSon(result, p.parsePragma) - else: addSon(result, ast.emptyNode) + else: addSon(result, p.emptyNode) # empty exception tracking: - addSon(result, ast.emptyNode) + addSon(result, p.emptyNode) if p.tok.tokType == tkEquals and p.validInd: getTok(p) skipComment(p, result) addSon(result, parseStmt(p)) else: - addSon(result, ast.emptyNode) + addSon(result, p.emptyNode) indAndComment(p, result) proc newCommentStmt(p: var TParser): PNode = @@ -1732,7 +1788,7 @@ proc parseConstant(p: var TParser): PNode = optInd(p, result) addSon(result, parseTypeDesc(p)) else: - addSon(result, ast.emptyNode) + addSon(result, p.emptyNode) eat(p, tkEquals) optInd(p, result) addSon(result, parseExpr(p)) @@ -1742,7 +1798,7 @@ proc parseEnum(p: var TParser): PNode = #| enum = 'enum' optInd (symbol optInd ('=' optInd expr COMMENT?)? comma?)+ result = newNodeP(nkEnumTy, p) getTok(p) - addSon(result, ast.emptyNode) + addSon(result, p.emptyNode) optInd(p, result) flexComment(p, result) # progress guaranteed @@ -1813,7 +1869,7 @@ proc parseObjectCase(p: var TParser): PNode = addSon(a, identWithPragma(p)) eat(p, tkColon) addSon(a, parseTypeDesc(p)) - addSon(a, ast.emptyNode) + addSon(a, p.emptyNode) addSon(result, a) if p.tok.tokType == tkColon: getTok(p) flexComment(p, result) @@ -1872,7 +1928,7 @@ proc parseObjectPart(p: var TParser): PNode = result = newNodeP(nkNilLit, p) getTok(p) else: - result = ast.emptyNode + result = p.emptyNode proc parseObject(p: var TParser): PNode = #| object = 'object' pragma? ('of' typeDesc)? COMMENT? objectPart @@ -1881,19 +1937,19 @@ proc parseObject(p: var TParser): PNode = if p.tok.tokType == tkCurlyDotLe and p.validInd: addSon(result, parsePragma(p)) else: - addSon(result, ast.emptyNode) + addSon(result, p.emptyNode) if p.tok.tokType == tkOf and p.tok.indent < 0: var a = newNodeP(nkOfInherit, p) getTok(p) addSon(a, parseTypeDesc(p)) addSon(result, a) else: - addSon(result, ast.emptyNode) + addSon(result, p.emptyNode) if p.tok.tokType == tkComment: skipComment(p, result) # an initial IND{>} HAS to follow: if not realInd(p): - addSon(result, emptyNode) + addSon(result, p.emptyNode) return addSon(result, parseObjectPart(p)) @@ -1928,7 +1984,7 @@ proc parseTypeClass(p: var TParser): PNode = if p.tok.tokType == tkCurlyDotLe and p.validInd: addSon(result, parsePragma(p)) else: - addSon(result, ast.emptyNode) + addSon(result, p.emptyNode) if p.tok.tokType == tkOf and p.tok.indent < 0: var a = newNodeP(nkOfInherit, p) getTok(p) @@ -1939,12 +1995,12 @@ proc parseTypeClass(p: var TParser): PNode = getTok(p) addSon(result, a) else: - addSon(result, ast.emptyNode) + addSon(result, p.emptyNode) if p.tok.tokType == tkComment: skipComment(p, result) # an initial IND{>} HAS to follow: if not realInd(p): - addSon(result, emptyNode) + addSon(result, p.emptyNode) else: addSon(result, parseStmt(p)) @@ -1957,14 +2013,14 @@ proc parseTypeDef(p: var TParser): PNode = if p.tok.tokType == tkBracketLe and p.validInd: addSon(result, parseGenericParamList(p)) else: - addSon(result, ast.emptyNode) + addSon(result, p.emptyNode) if p.tok.tokType == tkEquals: result.info = parLineInfo(p) getTok(p) optInd(p, result) addSon(result, parseTypeDefAux(p)) else: - addSon(result, ast.emptyNode) + addSon(result, p.emptyNode) indAndComment(p, result) # special extension! proc parseVarTuple(p: var TParser): PNode = @@ -1979,7 +2035,7 @@ proc parseVarTuple(p: var TParser): PNode = if p.tok.tokType != tkComma: break getTok(p) skipComment(p, a) - addSon(result, ast.emptyNode) # no type desc + addSon(result, p.emptyNode) # no type desc optPar(p) eat(p, tkParRi) eat(p, tkEquals) @@ -2040,7 +2096,7 @@ proc simpleStmt(p: var TParser): PNode = of tkComment: result = newCommentStmt(p) else: if isExprStart(p): result = parseExprStmt(p) - else: result = ast.emptyNode + else: result = p.emptyNode if result.kind notin {nkEmpty, nkCommentStmt}: skipComment(p, result) proc complexOrSimpleStmt(p: var TParser): PNode = @@ -2136,7 +2192,7 @@ proc parseStmt(p: var TParser): PNode = of tkIf, tkWhile, tkCase, tkTry, tkFor, tkBlock, tkAsm, tkProc, tkFunc, tkIterator, tkMacro, tkType, tkConst, tkWhen, tkVar: parMessage(p, "complex statement requires indentation") - result = ast.emptyNode + result = p.emptyNode else: if p.inSemiStmtList > 0: result = simpleStmt(p) @@ -2173,7 +2229,7 @@ proc parseAll(p: var TParser): PNode = proc parseTopLevelStmt(p: var TParser): PNode = ## Implements an iterator which, when called repeatedly, returns the next ## top-level statement or emptyNode if end of stream. - result = ast.emptyNode + result = p.emptyNode # progress guaranteed while true: if p.tok.indent != 0: diff --git a/compiler/passaux.nim b/compiler/passaux.nim index 568fb4c23..eabce8822 100644 --- a/compiler/passaux.nim +++ b/compiler/passaux.nim @@ -10,7 +10,7 @@ ## implements some little helper passes import - strutils, ast, astalgo, passes, idents, msgs, options, idgen, configuration + strutils, ast, astalgo, passes, idents, msgs, options, idgen, lineinfos from modulegraphs import ModuleGraph @@ -18,7 +18,7 @@ type VerboseRef = ref object of TPassContext config: ConfigRef -proc verboseOpen(graph: ModuleGraph; s: PSym; cache: IdentCache): PPassContext = +proc verboseOpen(graph: ModuleGraph; s: PSym): PPassContext = #MessageOut('compiling ' + s.name.s); result = VerboseRef(config: graph.config) rawMessage(graph.config, hintProcessing, s.name.s) diff --git a/compiler/passes.nim b/compiler/passes.nim index 8f9f57f3d..45c726f2a 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -13,24 +13,20 @@ import strutils, options, ast, astalgo, llstream, msgs, platform, os, condsyms, idents, renderer, types, extccomp, math, magicsys, nversion, - nimsets, syntaxes, times, rodread, idgen, modulegraphs, reorder, rod, - configuration + nimsets, syntaxes, times, idgen, modulegraphs, reorder, rod, + lineinfos type TPassContext* = object of RootObj # the pass's context - rd*: PRodReader # != nil if created by "openCached" PPassContext* = ref TPassContext - TPassOpen* = proc (graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext {.nimcall.} - TPassOpenCached* = - proc (graph: ModuleGraph; module: PSym, rd: PRodReader): PPassContext {.nimcall.} + TPassOpen* = proc (graph: ModuleGraph; module: PSym): PPassContext {.nimcall.} TPassClose* = proc (graph: ModuleGraph; p: PPassContext, n: PNode): PNode {.nimcall.} TPassProcess* = proc (p: PPassContext, topLevelStmt: PNode): PNode {.nimcall.} - TPass* = tuple[open: TPassOpen, openCached: TPassOpenCached, - process: TPassProcess, close: TPassClose, + TPass* = tuple[open: TPassOpen, process: TPassProcess, close: TPassClose, isFrontend: bool] TPassData* = tuple[input: PNode, closeOutput: PNode] @@ -41,23 +37,14 @@ type # This mechanism used to be used for the instantiation of generics. proc makePass*(open: TPassOpen = nil, - openCached: TPassOpenCached = nil, process: TPassProcess = nil, close: TPassClose = nil, isFrontend = false): TPass = result.open = open - result.openCached = openCached result.close = close result.process = process result.isFrontend = isFrontend -# the semantic checker needs these: -var - gImportModule*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex; cache: IdentCache): PSym {.nimcall.} - gIncludeFile*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex; cache: IdentCache): PNode {.nimcall.} - -# implementation - proc skipCodegen*(config: ConfigRef; n: PNode): bool {.inline.} = # can be used by codegen passes to determine whether they should do # something with `n`. Currently, this ignores `n` and uses the global @@ -74,44 +61,34 @@ var gPasses: array[0..maxPasses - 1, TPass] gPassesLen*: int -proc clearPasses* = +proc clearPasses*(g: ModuleGraph) = gPassesLen = 0 -proc registerPass*(p: TPass) = +proc registerPass*(g: ModuleGraph; p: TPass) = gPasses[gPassesLen] = p inc(gPassesLen) -proc carryPass*(g: ModuleGraph; p: TPass, module: PSym; cache: IdentCache; +proc carryPass*(g: ModuleGraph; p: TPass, module: PSym; m: TPassData): TPassData = - var c = p.open(g, module, cache) + var c = p.open(g, module) result.input = p.process(c, m.input) result.closeOutput = if p.close != nil: p.close(g, c, m.closeOutput) else: m.closeOutput proc carryPasses*(g: ModuleGraph; nodes: PNode, module: PSym; - cache: IdentCache; passes: TPasses) = + passes: TPasses) = var passdata: TPassData passdata.input = nodes for pass in passes: - passdata = carryPass(g, pass, module, cache, passdata) + passdata = carryPass(g, pass, module, passdata) proc openPasses(g: ModuleGraph; a: var TPassContextArray; - module: PSym; cache: IdentCache) = + module: PSym) = for i in countup(0, gPassesLen - 1): if not isNil(gPasses[i].open): - a[i] = gPasses[i].open(g, module, cache) + a[i] = gPasses[i].open(g, module) else: a[i] = nil -proc openPassesCached(g: ModuleGraph; a: var TPassContextArray, module: PSym, - rd: PRodReader) = - for i in countup(0, gPassesLen - 1): - if not isNil(gPasses[i].openCached): - a[i] = gPasses[i].openCached(g, module, rd) - if a[i] != nil: - a[i].rd = rd - else: - a[i] = nil - proc closePasses(graph: ModuleGraph; a: var TPassContextArray) = var m: PNode = nil for i in countup(0, gPassesLen - 1): @@ -127,19 +104,6 @@ proc processTopLevelStmt(n: PNode, a: var TPassContextArray): bool = if isNil(m): return false result = true -proc processTopLevelStmtCached(n: PNode, a: var TPassContextArray) = - # this implements the code transformation pipeline - var m = n - for i in countup(0, gPassesLen - 1): - if not isNil(gPasses[i].openCached): m = gPasses[i].process(a[i], m) - -proc closePassesCached(graph: ModuleGraph; a: var TPassContextArray) = - var m: PNode = nil - for i in countup(0, gPassesLen - 1): - if not isNil(gPasses[i].openCached) and not isNil(gPasses[i].close): - m = gPasses[i].close(graph, a[i], m) - a[i] = nil # free the memory here - proc resolveMod(conf: ConfigRef; module, relativeTo: string): FileIndex = let fullPath = findModule(conf, module, relativeTo) if fullPath.len == 0: @@ -151,7 +115,7 @@ proc processImplicits(conf: ConfigRef; implicits: seq[string], nodeKind: TNodeKi a: var TPassContextArray; m: PSym) = # XXX fixme this should actually be relative to the config file! let gCmdLineInfo = newLineInfo(FileIndex(0), 1, 1) - let relativeTo = m.info.toFullPath + let relativeTo = toFullPath(conf, m.info) for module in items(implicits): # implicit imports should not lead to a module importing itself if m.position != resolveMod(conf, module, relativeTo).int32: @@ -161,8 +125,7 @@ proc processImplicits(conf: ConfigRef; implicits: seq[string], nodeKind: TNodeKi importStmt.addSon str if not processTopLevelStmt(importStmt, a): break -proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream, - rd: PRodReader; cache: IdentCache): bool {.discardable.} = +proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool {.discardable.} = if graph.stopCompile(): return true var p: TParsers @@ -173,24 +136,17 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream, # new module caching mechanism: for i in 0..<gPassesLen: if not isNil(gPasses[i].open) and not gPasses[i].isFrontend: - a[i] = gPasses[i].open(graph, module, cache) + a[i] = gPasses[i].open(graph, module) else: a[i] = nil - var stmtIndex = 0 - var doContinue = true - while doContinue: - let n = loadNode(module, stmtIndex) - if n == nil or graph.stopCompile(): break - #if n.kind == nkImportStmt: - # echo "yes and it's ", n - inc stmtIndex + if not graph.stopCompile(): + let n = loadNode(graph, module) var m = n for i in 0..<gPassesLen: if not isNil(gPasses[i].process) and not gPasses[i].isFrontend: m = gPasses[i].process(a[i], m) if isNil(m): - doContinue = false break var m: PNode = nil @@ -198,10 +154,10 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream, if not isNil(gPasses[i].close) and not gPasses[i].isFrontend: m = gPasses[i].close(graph, a[i], m) a[i] = nil - elif rd == nil: - openPasses(graph, a, module, cache) + else: + openPasses(graph, a, module) if stream == nil: - let filename = fileIdx.toFullPathConsiderDirty + let filename = toFullPathConsiderDirty(graph.config, fileIdx) s = llStreamOpen(filename, fmRead) if s == nil: rawMessage(graph.config, errCannotOpenFile, filename) @@ -209,7 +165,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream, else: s = stream while true: - openParsers(p, fileIdx, s, cache, graph.config) + openParsers(p, fileIdx, s, graph.cache, graph.config) if sfSystemModule notin module.flags: # XXX what about caching? no processing then? what if I change the @@ -232,7 +188,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream, if n.kind == nkEmpty: break sl.add n if sfReorder in module.flags: - sl = reorder(graph, sl, module, cache) + sl = reorder(graph, sl, module) discard processTopLevelStmt(sl, a) break elif not processTopLevelStmt(n, a): break @@ -241,11 +197,4 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream, closePasses(graph, a) # id synchronization point for more consistent code generation: idSynchronizationPoint(1000) - else: - openPassesCached(graph, a, module, rd) - var n = loadInitSection(rd) - for i in countup(0, sonsLen(n) - 1): - if graph.stopCompile(): break - processTopLevelStmtCached(n.sons[i], a) - closePassesCached(graph, a) result = true diff --git a/compiler/patterns.nim b/compiler/patterns.nim index 5409a4811..2d2aeba76 100644 --- a/compiler/patterns.nim +++ b/compiler/patterns.nim @@ -77,7 +77,7 @@ proc checkTypes(c: PPatternContext, p: PSym, n: PNode): bool = if isNil(n.typ): result = p.typ.kind in {tyVoid, tyStmt} else: - result = sigmatch.argtypeMatches(c.c, p.typ, n.typ) + result = sigmatch.argtypeMatches(c.c, p.typ, n.typ, fromHlo = true) proc isPatternParam(c: PPatternContext, p: PNode): bool {.inline.} = result = p.kind == nkSym and p.sym.kind == skParam and p.sym.owner == c.owner diff --git a/compiler/platform.nim b/compiler/platform.nim index 8b3bf6b74..0db16f26c 100644 --- a/compiler/platform.nim +++ b/compiler/platform.nim @@ -22,7 +22,7 @@ type osNone, osDos, osWindows, osOs2, osLinux, osMorphos, osSkyos, osSolaris, osIrix, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osAix, osPalmos, osQnx, osAmiga, osAtari, osNetware, osMacos, osMacosx, osHaiku, osAndroid, osVxworks - osGenode, osJS, osNimrodVM, osStandalone + osGenode, osJS, osNimVM, osStandalone type TInfoOSProp* = enum @@ -162,7 +162,7 @@ const pathSep: ":", dirSep: "/", scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", props: {}), - (name: "NimrodVM", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/", + (name: "NimVM", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/", objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/", scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", props: {}), (name: "Standalone", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/", @@ -175,7 +175,7 @@ type # alias conditionals to condsyms (end of module). cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuPowerpc64, cpuPowerpc64el, cpuSparc, cpuVm, cpuIa64, cpuAmd64, cpuMips, cpuMipsel, - cpuArm, cpuArm64, cpuJS, cpuNimrodVM, cpuAVR, cpuMSP430, cpuSparc64, + cpuArm, cpuArm64, cpuJS, cpuNimVM, cpuAVR, cpuMSP430, cpuSparc64, cpuMips64, cpuMips64el, cpuRiscV64 type @@ -202,7 +202,7 @@ const (name: "arm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32), (name: "arm64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), (name: "js", intSize: 32, endian: bigEndian,floatSize: 64,bit: 32), - (name: "nimrodvm", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32), + (name: "nimvm", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32), (name: "avr", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16), (name: "msp430", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16), (name: "sparc64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64), @@ -210,44 +210,40 @@ const (name: "mips64el", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), (name: "riscv64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64)] -var - targetCPU*, hostCPU*: TSystemCPU - targetOS*, hostOS*: TSystemOS - -proc nameToOS*(name: string): TSystemOS -proc nameToCPU*(name: string): TSystemCPU - -var - intSize*: int - floatSize*: int - ptrSize*: int - tnl*: string # target newline - -proc setTarget*(o: TSystemOS, c: TSystemCPU) = +type + Target* = object + targetCPU*, hostCPU*: TSystemCPU + targetOS*, hostOS*: TSystemOS + intSize*: int + floatSize*: int + ptrSize*: int + tnl*: string # target newline + +proc setTarget*(t: var Target; o: TSystemOS, c: TSystemCPU) = assert(c != cpuNone) assert(o != osNone) #echo "new Target: OS: ", o, " CPU: ", c - targetCPU = c - targetOS = o - intSize = CPU[c].intSize div 8 - floatSize = CPU[c].floatSize div 8 - ptrSize = CPU[c].bit div 8 - tnl = OS[o].newLine - -proc nameToOS(name: string): TSystemOS = + t.targetCPU = c + t.targetOS = o + # assume no cross-compiling + t.hostCPU = c + t.hostOS = o + t.intSize = CPU[c].intSize div 8 + t.floatSize = CPU[c].floatSize div 8 + t.ptrSize = CPU[c].bit div 8 + t.tnl = OS[o].newLine + +proc nameToOS*(name: string): TSystemOS = for i in countup(succ(osNone), high(TSystemOS)): if cmpIgnoreStyle(name, OS[i].name) == 0: return i result = osNone -proc nameToCPU(name: string): TSystemCPU = +proc nameToCPU*(name: string): TSystemCPU = for i in countup(succ(cpuNone), high(TSystemCPU)): if cmpIgnoreStyle(name, CPU[i].name) == 0: return i result = cpuNone -hostCPU = nameToCPU(system.hostCPU) -hostOS = nameToOS(system.hostOS) - -setTarget(hostOS, hostCPU) # assume no cross-compiling - +proc setTargetFromSystem*(t: var Target) = + t.setTarget(nameToOS(system.hostOS), nameToCPU(system.hostCPU)) diff --git a/compiler/plugins/active.nim b/compiler/plugins/active.nim index 5da623e49..7b5306f9c 100644 --- a/compiler/plugins/active.nim +++ b/compiler/plugins/active.nim @@ -10,4 +10,15 @@ ## Include file that imports all plugins that are active. import - locals / locals, itersgen + "../compiler" / [pluginsupport, idents, ast], locals, itersgen + +const + plugins: array[2, Plugin] = [ + ("stdlib", "system", "iterToProc", iterToProcImpl), + ("stdlib", "system", "locals", semLocals) + ] + +proc getPlugin*(ic: IdentCache; fn: PSym): Transformation = + for p in plugins: + if pluginMatches(ic, p, fn): return p.t + return nil diff --git a/compiler/plugins/itersgen.nim b/compiler/plugins/itersgen.nim index ebb65dd4a..440d2e081 100644 --- a/compiler/plugins/itersgen.nim +++ b/compiler/plugins/itersgen.nim @@ -9,11 +9,11 @@ ## Plugin to transform an inline iterator into a data structure. -import ".." / [pluginsupport, ast, astalgo, +import ".." / [ast, astalgo, magicsys, lookups, semdata, - lambdalifting, rodread, msgs] + lambdalifting, msgs] -proc iterToProcImpl(c: PContext, n: PNode): PNode = +proc iterToProcImpl*(c: PContext, n: PNode): PNode = result = newNodeI(nkStmtList, n.info) let iter = n[1] if iter.kind != nkSym or iter.sym.kind != skIterator: @@ -40,11 +40,9 @@ proc iterToProcImpl(c: PContext, n: PNode): PNode = prc.typ.rawAddSon t let orig = iter.sym.ast prc.ast = newProcNode(nkProcDef, n.info, - name = newSymNode(prc), - params = orig[paramsPos], - pragmas = orig[pragmasPos], - body = body) + body = body, params = orig[paramsPos], name = newSymNode(prc), + pattern = c.graph.emptyNode, genericParams = c.graph.emptyNode, + pragmas = orig[pragmasPos], exceptions = c.graph.emptyNode) + prc.ast.add iter.sym.ast.sons[resultPos] addInterfaceDecl(c, prc) - -registerPlugin("stdlib", "system", "iterToProc", iterToProcImpl) diff --git a/compiler/plugins/locals/locals.nim b/compiler/plugins/locals.nim index ff7f3be58..0048ff985 100644 --- a/compiler/plugins/locals/locals.nim +++ b/compiler/plugins/locals.nim @@ -9,10 +9,10 @@ ## The builtin 'system.locals' implemented as a plugin. -import "../../" / [pluginsupport, ast, astalgo, +import ".." / [pluginsupport, ast, astalgo, magicsys, lookups, semdata, lowerings] -proc semLocals(c: PContext, n: PNode): PNode = +proc semLocals*(c: PContext, n: PNode): PNode = var counter = 0 var tupleType = newTypeS(tyTuple, c) result = newNodeIT(nkPar, n.info, tupleType) @@ -39,5 +39,3 @@ proc semLocals(c: PContext, n: PNode): PNode = var a = newSymNode(it, result.info) if it.typ.skipTypes({tyGenericInst}).kind == tyVar: a = newDeref(a) result.add(a) - -registerPlugin("stdlib", "system", "locals", semLocals) diff --git a/compiler/pluginsupport.nim b/compiler/pluginsupport.nim index f67942c97..a44436f11 100644 --- a/compiler/pluginsupport.nim +++ b/compiler/pluginsupport.nim @@ -8,40 +8,26 @@ # ## Plugin support for the Nim compiler. Right now plugins -## need to be built with the compiler only: plugins using +## need to be built with the compiler only: plugins using ## DLLs or the FFI will not work. import ast, semdata, idents type Transformation* = proc (c: PContext; n: PNode): PNode {.nimcall.} - Plugin = ref object - fn, module, package: PIdent + Plugin* = tuple + package, module, fn: string t: Transformation - next: Plugin -proc pluginMatches(p: Plugin; s: PSym): bool = - if s.name.id != p.fn.id: +proc pluginMatches*(ic: IdentCache; p: Plugin; s: PSym): bool = + if s.name.id != ic.getIdent(p.fn).id: return false let module = s.skipGenericOwner if module == nil or module.kind != skModule or - module.name.id != p.module.id: + module.name.id != ic.getIdent(p.module).id: return false let package = module.owner if package == nil or package.kind != skPackage or - package.name.id != p.package.id: + package.name.id != ic.getIdent(p.package).id: return false return true - -var head: Plugin - -proc getPlugin*(fn: PSym): Transformation = - var it = head - while it != nil: - if pluginMatches(it, fn): return it.t - it = it.next - -proc registerPlugin*(package, module, fn: string; t: Transformation) = - let oldHead = head - head = Plugin(fn: getIdent(fn), module: getIdent(module), - package: getIdent(package), t: t, next: oldHead) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index d3fa506cb..afe60e9dd 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -12,7 +12,7 @@ import os, platform, condsyms, ast, astalgo, idents, semdata, msgs, renderer, wordrecg, ropes, options, strutils, extccomp, math, magicsys, trees, - rodread, types, lookups, configuration + types, lookups, lineinfos const FirstCallConv* = wNimcall @@ -24,8 +24,8 @@ const wCompilerProc, wCore, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge, wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame, wError, wDiscardable, wNoInit, wCodegenDecl, - wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe, - wOverride, wConstructor, wExportNims, wUsed, wLiftLocals} + wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe, wOverride, + wConstructor, wExportNims, wUsed, wLiftLocals, wStacktrace, wLinetrace} converterPragmas* = procPragmas methodPragmas* = procPragmas+{wBase}-{wImportCpp} templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty, @@ -82,7 +82,13 @@ proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode = return it[1] proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) -# implementation + +proc recordPragma(c: PContext; n: PNode; key, val: string; val2 = "") = + var recorded = newNodeI(nkCommentStmt, n.info) + recorded.add newStrNode(key, n.info) + recorded.add newStrNode(val, n.info) + if val2.len > 0: recorded.add newStrNode(val2, n.info) + c.graph.recordStmt(c.graph, c.module, recorded) const errStringLiteralExpected = "string literal expected" @@ -143,7 +149,7 @@ proc processImportCpp(c: PContext; s: PSym, extname: string, info: TLineInfo) = if c.config.cmd == cmdCompileToC: let m = s.getModule() incl(m.flags, sfCompileToCpp) - extccomp.gMixedMode = true + incl c.config.globalOptions, optMixedMode proc processImportObjC(c: PContext; s: PSym, extname: string, info: TLineInfo) = setExternName(c, s, extname, info) @@ -217,9 +223,9 @@ proc isTurnedOn(c: PContext, n: PNode): bool = if x.kind == nkIntLit: return x.intVal != 0 localError(c.config, n.info, "'on' or 'off' expected") -proc onOff(c: PContext, n: PNode, op: TOptions) = - if isTurnedOn(c, n): c.config.options = c.config.options + op - else: c.config.options = c.config.options - op +proc onOff(c: PContext, n: PNode, op: TOptions, resOptions: var TOptions) = + if isTurnedOn(c, n): resOptions = resOptions + op + else: resOptions = resOptions - op proc pragmaNoForward(c: PContext, n: PNode; flag=sfNoForward) = if isTurnedOn(c, n): incl(c.module.flags, flag) @@ -227,7 +233,7 @@ proc pragmaNoForward(c: PContext, n: PNode; flag=sfNoForward) = proc processCallConv(c: PContext, n: PNode) = if n.kind in nkPragmaCallKinds and n.len == 2 and n.sons[1].kind == nkIdent: - var sw = whichKeyword(n.sons[1].ident) + let sw = whichKeyword(n.sons[1].ident) case sw of FirstCallConv..LastCallConv: c.optionStack[^1].defaultCC = wordToCallConv(sw) @@ -307,54 +313,68 @@ proc processNote(c: PContext, n: PNode) = else: invalidPragma(c, n) -proc processOption(c: PContext, n: PNode): bool = - if n.kind notin nkPragmaCallKinds or n.len != 2: result = true +proc pragmaToOptions(w: TSpecialWord): TOptions {.inline.} = + case w + of wChecks: ChecksOptions + of wObjChecks: {optObjCheck} + of wFieldChecks: {optFieldCheck} + of wRangechecks: {optRangeCheck} + of wBoundchecks: {optBoundsCheck} + of wOverflowchecks: {optOverflowCheck} + of wNilchecks: {optNilCheck} + of wFloatchecks: {optNaNCheck, optInfCheck} + of wNanChecks: {optNaNCheck} + of wInfChecks: {optInfCheck} + of wMovechecks: {optMoveCheck} + of wAssertions: {optAssert} + of wWarnings: {optWarns} + of wHints: {optHints} + of wLinedir: {optLineDir} + of wStacktrace: {optStackTrace} + of wLinetrace: {optLineTrace} + of wDebugger: {optEndb} + of wProfiler: {optProfiler, optMemTracker} + of wMemTracker: {optMemTracker} + of wByRef: {optByRef} + of wImplicitStatic: {optImplicitStatic} + of wPatterns: {optPatterns} + else: {} + +proc tryProcessOption(c: PContext, n: PNode, resOptions: var TOptions): bool = + result = true + if n.kind notin nkPragmaCallKinds or n.len != 2: result = false elif n.sons[0].kind == nkBracketExpr: processNote(c, n) - elif n.sons[0].kind != nkIdent: result = true + elif n.sons[0].kind != nkIdent: result = false else: let sw = whichKeyword(n.sons[0].ident) - case sw - of wChecks: onOff(c, n, ChecksOptions) - of wObjChecks: onOff(c, n, {optObjCheck}) - of wFieldChecks: onOff(c, n, {optFieldCheck}) - of wRangechecks: onOff(c, n, {optRangeCheck}) - of wBoundchecks: onOff(c, n, {optBoundsCheck}) - of wOverflowchecks: onOff(c, n, {optOverflowCheck}) - of wNilchecks: onOff(c, n, {optNilCheck}) - of wFloatchecks: onOff(c, n, {optNaNCheck, optInfCheck}) - of wNanChecks: onOff(c, n, {optNaNCheck}) - of wInfChecks: onOff(c, n, {optInfCheck}) - of wMovechecks: onOff(c, n, {optMoveCheck}) - of wAssertions: onOff(c, n, {optAssert}) - of wWarnings: onOff(c, n, {optWarns}) - of wHints: onOff(c, n, {optHints}) - of wCallconv: processCallConv(c, n) - of wLinedir: onOff(c, n, {optLineDir}) - of wStacktrace: onOff(c, n, {optStackTrace}) - of wLinetrace: onOff(c, n, {optLineTrace}) - of wDebugger: onOff(c, n, {optEndb}) - of wProfiler: onOff(c, n, {optProfiler, optMemTracker}) - of wMemTracker: onOff(c, n, {optMemTracker}) - of wByRef: onOff(c, n, {optByRef}) - of wDynlib: processDynLib(c, n, nil) - of wOptimization: - if n.sons[1].kind != nkIdent: - invalidPragma(c, n) - else: - case n.sons[1].ident.s.normalize - of "speed": - incl(c.config.options, optOptimizeSpeed) - excl(c.config.options, optOptimizeSize) - of "size": - excl(c.config.options, optOptimizeSpeed) - incl(c.config.options, optOptimizeSize) - of "none": - excl(c.config.options, optOptimizeSpeed) - excl(c.config.options, optOptimizeSize) - else: localError(c.config, n.info, "'none', 'speed' or 'size' expected") - of wImplicitStatic: onOff(c, n, {optImplicitStatic}) - of wPatterns: onOff(c, n, {optPatterns}) - else: result = true + let opts = pragmaToOptions(sw) + if opts != {}: + onOff(c, n, opts, resOptions) + else: + case sw + of wCallconv: processCallConv(c, n) + of wDynlib: processDynLib(c, n, nil) + of wOptimization: + if n.sons[1].kind != nkIdent: + invalidPragma(c, n) + else: + case n.sons[1].ident.s.normalize + of "speed": + incl(resOptions, optOptimizeSpeed) + excl(resOptions, optOptimizeSize) + of "size": + excl(resOptions, optOptimizeSpeed) + incl(resOptions, optOptimizeSize) + of "none": + excl(resOptions, optOptimizeSpeed) + excl(resOptions, optOptimizeSize) + else: localError(c.config, n.info, "'none', 'speed' or 'size' expected") + else: result = false + +proc processOption(c: PContext, n: PNode, resOptions: var TOptions) = + if not tryProcessOption(c, n, resOptions): + # calling conventions (boring...): + localError(c.config, n.info, "option expected") proc processPush(c: PContext, n: PNode, start: int) = if n.sons[start-1].kind in nkPragmaCallKinds: @@ -367,7 +387,7 @@ proc processPush(c: PContext, n: PNode, start: int) = x.notes = c.config.notes c.optionStack.add(x) for i in countup(start, sonsLen(n) - 1): - if processOption(c, n.sons[i]): + if not tryProcessOption(c, n.sons[i], c.config.options): # simply store it somewhere: if x.otherPragmas.isNil: x.otherPragmas = newNodeI(nkPragma, n.info) @@ -408,7 +428,7 @@ proc relativeFile(c: PContext; n: PNode; ext=""): string = var s = expectStrLit(c, n) if ext.len > 0 and splitFile(s).ext == "": s = addFileExt(s, ext) - result = parentDir(n.info.toFullPath) / s + result = parentDir(toFullPath(c.config, n.info)) / s if not fileExists(result): if isAbsolute(s): result = s else: @@ -416,6 +436,10 @@ proc relativeFile(c: PContext; n: PNode; ext=""): string = if result.len == 0: result = s proc processCompile(c: PContext, n: PNode) = + proc docompile(c: PContext; it: PNode; src, dest: string) = + var cf = Cfile(cname: src, obj: dest, flags: {CfileFlag.External}) + extccomp.addExternalFileToCompile(c.config, cf) + recordPragma(c, it, "compile", src, dest) proc getStrLit(c: PContext, n: PNode; i: int): string = n.sons[i] = c.semConstExpr(c, n[i]) @@ -430,30 +454,31 @@ proc processCompile(c: PContext, n: PNode) = if it.kind in {nkPar, nkTupleConstr} and it.len == 2: let s = getStrLit(c, it, 0) let dest = getStrLit(c, it, 1) - var found = parentDir(n.info.toFullPath) / s + var found = parentDir(toFullPath(c.config, n.info)) / s for f in os.walkFiles(found): - let nameOnly = extractFilename(f) - var cf = Cfile(cname: f, - obj: completeCFilePath(c.config, dest % nameOnly), - flags: {CfileFlag.External}) - extccomp.addExternalFileToCompile(c.config, cf) + let obj = completeCFilePath(c.config, dest % extractFilename(f)) + docompile(c, it, f, obj) else: let s = expectStrLit(c, n) - var found = parentDir(n.info.toFullPath) / s + var found = parentDir(toFullPath(c.config, n.info)) / s if not fileExists(found): if isAbsolute(s): found = s else: found = findFile(c.config, s) if found.len == 0: found = s - extccomp.addExternalFileToCompile(c.config, found) + let obj = toObjFile(c.config, completeCFilePath(c.config, found, false)) + docompile(c, it, found, obj) proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) = - let found = relativeFile(c, n, CC[cCompiler].objExt) + let found = relativeFile(c, n, CC[c.config.cCompiler].objExt) case feature - of linkNormal: extccomp.addExternalFileToLink(c.config, found) + of linkNormal: + extccomp.addExternalFileToLink(c.config, found) + recordPragma(c, n, "link", found) of linkSys: - extccomp.addExternalFileToLink(c.config, - c.config.libpath / completeCFilePath(c.config, found, false)) + let dest = c.config.libpath / completeCFilePath(c.config, found, false) + extccomp.addExternalFileToLink(c.config, dest) + recordPragma(c, n, "link", dest) else: internalError(c.config, n.info, "processCommonLink") proc pragmaBreakpoint(c: PContext, n: PNode) = @@ -484,9 +509,10 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode = if c < 0: sub = substr(str, b + 1) else: sub = substr(str, b + 1, c - 1) if sub != "": - var e = searchInScopes(con, getIdent(sub)) + var e = searchInScopes(con, getIdent(con.cache, sub)) if e != nil: - if e.kind == skStub: loadStub(e) + when false: + if e.kind == skStub: loadStub(e) incl(e.flags, sfUsed) addSon(result, newSymNode(e)) else: @@ -553,7 +579,7 @@ proc pragmaLine(c: PContext, n: PNode) = localError(c.config, n.info, "tuple expected") else: # sensible default: - n.info = getInfoContext(-1) + n.info = getInfoContext(c.config, -1) proc processPragma(c: PContext, n: PNode, i: int) = let it = n[i] @@ -638,7 +664,7 @@ proc deprecatedStmt(c: PContext; outerPragma: PNode) = let dest = qualifiedLookUp(c, n[1], {checkUndeclared}) if dest == nil or dest.kind in routineKinds: localError(c.config, n.info, warnUser, "the .deprecated pragma is unreliable for routines") - let src = considerQuotedIdent(c.config, n[0]) + let src = considerQuotedIdent(c, n[0]) let alias = newSym(skAlias, src, dest, n[0].info, c.config.options) incl(alias.flags, sfExported) if sfCompilerProc in dest.flags: markCompilerProc(c, alias) @@ -661,7 +687,7 @@ proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym = # We return a dummy symbol; later passes over the type will repair it. # Generic instantiation needs to know about this too. But we're lazy # and perform the lookup on demand instead. - result = newSym(skUnknown, considerQuotedIdent(c.config, n), nil, n.info, + result = newSym(skUnknown, considerQuotedIdent(c, n), nil, n.info, c.config.options) else: result = qualifiedLookUp(c, n, {checkUndeclared}) @@ -714,7 +740,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, elif key.kind notin nkIdentKinds: n.sons[i] = semCustomPragma(c, it) return - let ident = considerQuotedIdent(c.config, key) + let ident = considerQuotedIdent(c, key) var userPragma = strTableGet(c.userPragmas, ident) if userPragma != nil: # number of pragmas increase/decrease with user pragma expansion @@ -727,7 +753,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, i.inc(userPragma.ast.len - 1) # inc by -1 is ok, user pragmas was empty dec c.instCounter else: - var k = whichKeyword(ident) + let k = whichKeyword(ident) if k in validPragmas: case k of wExportc: @@ -736,10 +762,12 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wImportc: let name = getOptionalStr(c, it, "$1") cppDefine(c.config, name) + recordPragma(c, it, "cppdefine", name) makeExternImport(c, sym, name, it.info) of wImportCompilerProc: let name = getOptionalStr(c, it, "$1") cppDefine(c.config, name) + recordPragma(c, it, "cppdefine", name) processImportCompilerProc(c, sym, name, it.info) of wExtern: setExternName(c, sym, expectStrLit(c, it), it.info) of wImmediate: @@ -832,6 +860,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wCompilerProc, wCore: noVal(c, it) # compilerproc may not get a string! cppDefine(c.graph.config, sym.name.s) + recordPragma(c, it, "cppdefine", sym.name.s) if sfFromGeneric notin sym.flags: markCompilerProc(c, sym) of wProcVar: noVal(c, it) @@ -894,8 +923,14 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, noVal(c, it) if sym.typ == nil: invalidPragma(c, it) else: incl(sym.typ.flags, tfPacked) - of wHint: message(c.config, it.info, hintUser, expectStrLit(c, it)) - of wWarning: message(c.config, it.info, warnUser, expectStrLit(c, it)) + of wHint: + let s = expectStrLit(c, it) + recordPragma(c, it, "hint", s) + message(c.config, it.info, hintUser, s) + of wWarning: + let s = expectStrLit(c, it) + recordPragma(c, it, "warning", s) + message(c.config, it.info, warnUser, s) of wError: if sym != nil and sym.isRoutine: # This is subtle but correct: the error *statement* is only @@ -905,15 +940,23 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, noVal(c, it) incl(sym.flags, sfError) else: - localError(c.config, it.info, errUser, expectStrLit(c, it)) + let s = expectStrLit(c, it) + recordPragma(c, it, "error", s) + localError(c.config, it.info, errUser, s) of wFatal: fatal(c.config, it.info, errUser, expectStrLit(c, it)) of wDefine: processDefine(c, it) of wUndef: processUndef(c, it) of wCompile: processCompile(c, it) of wLink: processCommonLink(c, it, linkNormal) of wLinksys: processCommonLink(c, it, linkSys) - of wPassl: extccomp.addLinkOption(c.config, expectStrLit(c, it)) - of wPassc: extccomp.addCompileOption(c.config, expectStrLit(c, it)) + of wPassl: + let s = expectStrLit(c, it) + extccomp.addLinkOption(c.config, s) + recordPragma(c, it, "passl", s) + of wPassc: + let s = expectStrLit(c, it) + extccomp.addCompileOption(c.config, s) + recordPragma(c, it, "passc", s) of wBreakpoint: pragmaBreakpoint(c, it) of wWatchPoint: pragmaWatchpoint(c, it) of wPush: @@ -935,13 +978,14 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wCodegenDecl: processCodegenDecl(c, it, sym) of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, - wLinedir, wStacktrace, wLinetrace, wOptimization, wMovechecks, - wCallconv, - wDebugger, wProfiler, wFloatchecks, wNanChecks, wInfChecks, - wPatterns: - if processOption(c, it): - # calling conventions (boring...): - localError(c.config, it.info, "option expected") + wLinedir, wOptimization, wMovechecks, wCallconv, wDebugger, wProfiler, + wFloatchecks, wNanChecks, wInfChecks, wPatterns: + processOption(c, it, c.config.options) + of wStacktrace, wLinetrace: + if sym.kind in {skProc, skMethod, skConverter}: + processOption(c, it, sym.options) + else: + processOption(c, it, c.config.options) of FirstCallConv..LastCallConv: assert(sym != nil) if sym.typ == nil: invalidPragma(c, it) @@ -971,7 +1015,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wByRef: noVal(c, it) if sym == nil or sym.typ == nil: - if processOption(c, it): localError(c.config, it.info, "option expected") + processOption(c, it, c.config.options) else: incl(sym.typ.flags, tfByRef) of wByCopy: @@ -1021,9 +1065,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, processExperimental(c, it, sym) of wThis: if it.kind in nkPragmaCallKinds and it.len == 2: - c.selfName = considerQuotedIdent(c.config, it[1]) + c.selfName = considerQuotedIdent(c, it[1]) elif it.kind == nkIdent or it.len == 1: - c.selfName = getIdent("self") + c.selfName = getIdent(c.cache, "self") else: localError(c.config, it.info, "'this' pragma is allowed to have zero or one arguments") of wNoRewrite: @@ -1051,13 +1095,13 @@ proc implicitPragmas*(c: PContext, sym: PSym, n: PNode, for it in c.optionStack: let o = it.otherPragmas if not o.isNil: - pushInfoContext(n.info) + pushInfoContext(c.config, n.info) var i = 0 while i < o.len(): if singlePragma(c, sym, o, i, validPragmas): internalError(c.config, n.info, "implicitPragmas") inc i - popInfoContext() + popInfoContext(c.config) if lfExportLib in sym.loc.flags and sfExportc notin sym.flags: localError(c.config, n.info, ".dynlib requires .exportc") @@ -1069,8 +1113,7 @@ proc implicitPragmas*(c: PContext, sym: PSym, n: PNode, if sym.loc.r == nil: sym.loc.r = rope(sym.name.s) proc hasPragma*(n: PNode, pragma: TSpecialWord): bool = - if n == nil or n.sons == nil: - return false + if n == nil: return false for p in n: var key = if p.kind in nkPragmaCallKinds and p.len > 1: p[0] else: p @@ -1082,7 +1125,7 @@ proc hasPragma*(n: PNode, pragma: TSpecialWord): bool = proc pragmaRec(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) = if n == nil: return var i = 0 - while i < n.len(): + while i < n.len: if singlePragma(c, sym, n, i, validPragmas): break inc i diff --git a/compiler/prefixmatches.nim b/compiler/prefixmatches.nim index 00e2c537d..246d1ae5e 100644 --- a/compiler/prefixmatches.nim +++ b/compiler/prefixmatches.nim @@ -24,7 +24,7 @@ proc prefixMatch*(p, s: string): PrefixMatch = # check for prefix/contains: while i < L: if s[i] == '_': inc i - if eq(s[i], p[0]): + if i < L and eq(s[i], p[0]): var ii = i+1 var jj = 1 while ii < L and jj < p.len: @@ -43,10 +43,10 @@ proc prefixMatch*(p, s: string): PrefixMatch = i = 1 var j = 1 while i < s.len: - if s[i] == '_' and i < s.len-1: + if i < s.len-1 and s[i] == '_': if j < p.len and eq(p[j], s[i+1]): inc j else: return PrefixMatch.None - if s[i] in {'A'..'Z'} and s[i-1] notin {'A'..'Z'}: + if i < s.len and s[i] in {'A'..'Z'} and s[i-1] notin {'A'..'Z'}: if j < p.len and eq(p[j], s[i]): inc j else: return PrefixMatch.None inc i diff --git a/compiler/procfind.nim b/compiler/procfind.nim index 042947e72..3f47e7e8a 100644 --- a/compiler/procfind.nim +++ b/compiler/procfind.nim @@ -73,7 +73,7 @@ proc searchForProcNew(c: PContext, scope: PScope, fn: PSym): PSym = if (sfExported notin result.flags) and (sfExported in fn.flags): let message = ("public implementation '$1' has non-public " & "forward declaration in $2") % - [getProcHeader(result), $result.info] + [getProcHeader(c.config, result), c.config$result.info] localError(c.config, fn.info, message) return of paramsIncompatible: diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 6ac6e797e..3ce2e157d 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -10,7 +10,7 @@ # This module implements the renderer of the standard Nim representation. import - lexer, options, idents, strutils, ast, msgs, configuration + lexer, options, idents, strutils, ast, msgs, lineinfos type TRenderFlag* = enum @@ -330,14 +330,15 @@ proc ulitAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string = proc atom(g: TSrcGen; n: PNode): string = when defined(nimpretty): + doAssert g.config != nil, "g.config not initialized!" let comment = if n.info.commentOffsetA < n.info.commentOffsetB: - " " & fileSection(g.fid, n.info.commentOffsetA, n.info.commentOffsetB) + " " & fileSection(g.config, g.fid, n.info.commentOffsetA, n.info.commentOffsetB) else: "" if n.info.offsetA <= n.info.offsetB: # for some constructed tokens this can not be the case and we're better # off to not mess with the offset then. - return fileSection(g.fid, n.info.offsetA, n.info.offsetB) & comment + return fileSection(g.config, g.fid, n.info.offsetA, n.info.offsetB) & comment var f: float32 case n.kind of nkEmpty: result = "" @@ -386,8 +387,10 @@ proc lcomma(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int = assert(theEnd < 0) result = 0 for i in countup(start, sonsLen(n) + theEnd): - inc(result, lsub(g, n.sons[i])) - inc(result, 2) # for ``, `` + let param = n.sons[i] + if nfDefaultParam notin param.flags: + inc(result, lsub(g, param)) + inc(result, 2) # for ``, `` if result > 0: dec(result, 2) # last does not get a comma! @@ -811,8 +814,8 @@ proc gident(g: var TSrcGen, n: PNode) = var t: TTokType var s = atom(g, n) - if (s[0] in lexer.SymChars): - if (n.kind == nkIdent): + if s.len > 0 and s[0] in lexer.SymChars: + if n.kind == nkIdent: if (n.ident.id < ord(tokKeywordLow) - ord(tkSymbol)) or (n.ident.id > ord(tokKeywordHigh) - ord(tkSymbol)): t = tkSymbol @@ -1452,11 +1455,12 @@ proc `$`*(n: PNode): string = n.renderTree proc renderModule*(n: PNode, infile, outfile: string, renderFlags: TRenderFlags = {}; - fid = FileIndex(-1)) = + fid = FileIndex(-1); + conf: ConfigRef = nil) = var f: File g: TSrcGen - initSrcGen(g, renderFlags, newPartialConfigRef()) + initSrcGen(g, renderFlags, conf) g.fid = fid for i in countup(0, sonsLen(n) - 1): gsub(g, n.sons[i]) diff --git a/compiler/reorder.nim b/compiler/reorder.nim index d50be1d99..27b19a373 100644 --- a/compiler/reorder.nim +++ b/compiler/reorder.nim @@ -2,7 +2,7 @@ import intsets, ast, idents, algorithm, renderer, parser, ospaths, strutils, sequtils, msgs, modulegraphs, syntaxes, options, modulepaths, tables, - configuration + lineinfos type DepN = ref object @@ -11,11 +11,11 @@ type onStack: bool kids: seq[DepN] hAQ, hIS, hB, hCmd: int - when not defined(release): + when defined(debugReorder): expls: seq[string] DepG = seq[DepN] -when not defined(release): +when defined(debugReorder): var idNames = newTable[int, string]() proc newDepN(id: int, pnode: PNode): DepN = @@ -30,10 +30,10 @@ proc newDepN(id: int, pnode: PNode): DepN = result.hIS = -1 result.hB = -1 result.hCmd = -1 - when not defined(release): + when defined(debugReorder): result.expls = @[] -proc accQuoted(n: PNode): PIdent = +proc accQuoted(cache: IdentCache; n: PNode): PIdent = var id = "" for i in 0 ..< n.len: let x = n[i] @@ -41,33 +41,33 @@ proc accQuoted(n: PNode): PIdent = of nkIdent: id.add(x.ident.s) of nkSym: id.add(x.sym.name.s) else: discard - result = getIdent(id) + result = getIdent(cache, id) -proc addDecl(n: PNode; declares: var IntSet) = +proc addDecl(cache: IdentCache; n: PNode; declares: var IntSet) = case n.kind - of nkPostfix: addDecl(n[1], declares) - of nkPragmaExpr: addDecl(n[0], declares) + of nkPostfix: addDecl(cache, n[1], declares) + of nkPragmaExpr: addDecl(cache, n[0], declares) of nkIdent: declares.incl n.ident.id - when not defined(release): + when defined(debugReorder): idNames[n.ident.id] = n.ident.s of nkSym: declares.incl n.sym.name.id - when not defined(release): + when defined(debugReorder): idNames[n.sym.name.id] = n.sym.name.s of nkAccQuoted: - let a = accQuoted(n) + let a = accQuoted(cache, n) declares.incl a.id - when not defined(release): + when defined(debugReorder): idNames[a.id] = a.s of nkEnumFieldDef: - addDecl(n[0], declares) + addDecl(cache, n[0], declares) else: discard -proc computeDeps(n: PNode, declares, uses: var IntSet; topLevel: bool) = - template deps(n) = computeDeps(n, declares, uses, false) +proc computeDeps(cache: IdentCache; n: PNode, declares, uses: var IntSet; topLevel: bool) = + template deps(n) = computeDeps(cache, n, declares, uses, false) template decl(n) = - if topLevel: addDecl(n, declares) + if topLevel: addDecl(cache, n, declares) case n.kind of procDefs, nkMacroDef, nkTemplateDef: decl(n[0]) @@ -93,11 +93,11 @@ proc computeDeps(n: PNode, declares, uses: var IntSet; topLevel: bool) = deps(n[i]) of nkIdent: uses.incl n.ident.id of nkSym: uses.incl n.sym.name.id - of nkAccQuoted: uses.incl accQuoted(n).id + of nkAccQuoted: uses.incl accQuoted(cache, n).id of nkOpenSymChoice, nkClosedSymChoice: uses.incl n.sons[0].sym.name.id of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse, nkStaticStmt: - for i in 0..<len(n): computeDeps(n[i], declares, uses, topLevel) + for i in 0..<len(n): computeDeps(cache, n[i], declares, uses, topLevel) of nkPragma: let a = n.sons[0] if a.kind == nkExprColonExpr and a.sons[0].kind == nkIdent and @@ -136,15 +136,13 @@ proc hasIncludes(n:PNode): bool = if a.kind == nkIncludeStmt: return true -proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex; - cache: IdentCache): PNode {.procvar.} = - result = syntaxes.parseFile(fileIdx, cache, graph.config) +proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PNode {.procvar.} = + result = syntaxes.parseFile(fileIdx, graph.cache, graph.config) graph.addDep(s, fileIdx) graph.addIncludeDep(FileIndex s.position, fileIdx) proc expandIncludes(graph: ModuleGraph, module: PSym, n: PNode, - modulePath: string, includedFiles: var IntSet, - cache: IdentCache): PNode = + modulePath: string, includedFiles: var IntSet): PNode = # Parses includes and injects them in the current tree if not n.hasIncludes: return n @@ -155,11 +153,12 @@ proc expandIncludes(graph: ModuleGraph, module: PSym, n: PNode, var f = checkModuleName(graph.config, a.sons[i]) if f != InvalidFileIDX: if containsOrIncl(includedFiles, f.int): - localError(graph.config, a.info, "recursive dependency: '$1'" % f.toFilename) + localError(graph.config, a.info, "recursive dependency: '$1'" % + toFilename(graph.config, f)) else: - let nn = includeModule(graph, module, f, cache) + let nn = includeModule(graph, module, f) let nnn = expandIncludes(graph, module, nn, modulePath, - includedFiles, cache) + includedFiles) excl(includedFiles, f.int) for b in nnn: result.add b @@ -215,7 +214,7 @@ proc mergeSections(conf: ConfigRef; comps: seq[seq[DepN]], res: PNode) = # consecutive type and const sections var wmsg = "Circular dependency detected. reorder pragma may not be able to" & " reorder some nodes properely" - when not defined(release): + when defined(debugReorder): wmsg &= ":\n" for i in 0..<cs.len-1: for j in i..<cs.len: @@ -354,13 +353,13 @@ proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG = if j < i and nj.hasCommand and niHasCmd: # Preserve order for commands and calls ni.kids.add nj - when not defined(release): + when defined(debugReorder): ni.expls.add "both have commands and one comes after the other" elif j < i and nj.hasImportStmt: # Every node that comes after an import statement must # depend on that import ni.kids.add nj - when not defined(release): + when defined(debugReorder): ni.expls.add "parent is, or contains, an import statement and child comes after it" elif j < i and niHasBody and nj.hasAccQuotedDef: # Every function, macro, template... with a body depends @@ -368,13 +367,13 @@ proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG = # That's because it is hard to detect the use of functions # like "[]=", "[]", "or" ... in their bodies. ni.kids.add nj - when not defined(release): + when defined(debugReorder): ni.expls.add "one declares a quoted identifier and the other has a body and comes after it" elif j < i and niHasBody and not nj.hasBody and intersects(deps[i][0], declares): # Keep function declaration before function definition ni.kids.add nj - when not defined(release): + when defined(debugReorder): for dep in deps[i][0]: if dep in declares: ni.expls.add "one declares \"" & idNames[dep] & "\" and the other defines it" @@ -382,7 +381,7 @@ proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG = for d in declares: if uses.contains(d): ni.kids.add nj - when not defined(release): + when defined(debugReorder): ni.expls.add "one declares \"" & idNames[d] & "\" and the other uses it" proc strongConnect(v: var DepN, idx: var int, s: var seq[DepN], @@ -426,19 +425,19 @@ proc hasForbiddenPragma(n: PNode): bool = a[0].ident.s == "push": return true -proc reorder*(graph: ModuleGraph, n: PNode, module: PSym, cache: IdentCache): PNode = +proc reorder*(graph: ModuleGraph, n: PNode, module: PSym): PNode = if n.hasForbiddenPragma: return n var includedFiles = initIntSet() - let mpath = module.fileIdx.toFullPath + let mpath = toFullPath(graph.config, module.fileIdx) let n = expandIncludes(graph, module, n, mpath, - includedFiles, cache).splitSections + includedFiles).splitSections result = newNodeI(nkStmtList, n.info) var deps = newSeq[(IntSet, IntSet)](n.len) for i in 0..<n.len: deps[i][0] = initIntSet() deps[i][1] = initIntSet() - computeDeps(n[i], deps[i][0], deps[i][1], true) + computeDeps(graph.cache, n[i], deps[i][0], deps[i][1], true) var g = buildGraph(n, deps) let comps = getStrongComponents(g) diff --git a/compiler/rod.nim b/compiler/rod.nim index c144f15ef..f9208f5dc 100644 --- a/compiler/rod.nim +++ b/compiler/rod.nim @@ -9,18 +9,21 @@ ## This module implements the canonalization for the various caching mechanisms. -import ast, idgen, msgs +import ast, idgen, lineinfos, msgs, incremental, modulegraphs -when not defined(nimSymbolfiles): - template setupModuleCache* = discard - template storeNode*(module: PSym; n: PNode) = discard - template loadNode*(module: PSym; index: var int): PNode = PNode(nil) +when not nimIncremental: + template setupModuleCache*(g: ModuleGraph) = discard + template storeNode*(g: ModuleGraph; module: PSym; n: PNode) = discard + template loadNode*(g: ModuleGraph; module: PSym): PNode = newNode(nkStmtList) - template getModuleId*(fileIdx: FileIndex; fullpath: string): int = getID() + template getModuleId*(g: ModuleGraph; fileIdx: FileIndex; fullpath: string): int = getID() - template addModuleDep*(module, fileIdx: FileIndex; isIncludeFile: bool) = discard + template addModuleDep*(g: ModuleGraph; module, fileIdx: FileIndex; isIncludeFile: bool) = discard - template storeRemaining*(module: PSym) = discard + template storeRemaining*(g: ModuleGraph; module: PSym) = discard else: include rodimpl + + # idea for testing all this logic: *Always* load the AST from the DB, whether + # we already have it in RAM or not! diff --git a/compiler/rodimpl.nim b/compiler/rodimpl.nim index aff4f6909..7d24e4e67 100644 --- a/compiler/rodimpl.nim +++ b/compiler/rodimpl.nim @@ -10,59 +10,67 @@ ## This module implements the new compilation cache. import strutils, os, intsets, tables, ropes, db_sqlite, msgs, options, types, - renderer, rodutils, std / sha1, idents, astalgo, magicsys + renderer, rodutils, idents, astalgo, btrees, magicsys, cgmeth, extccomp, + btrees, trees, condsyms, nversion ## Todo: -## - Implement the 'import' replay logic so that the codegen runs over -## dependent modules. -## - Make conditional symbols and the configuration part of a module's -## dependencies. -## - Test multi methods. -## - Implement the limited VM support based on sets. -## - Depencency computation should use signature hashes in order to +## - Dependency computation should use *signature* hashes in order to ## avoid recompiling dependent modules. +## - Patch the rest of the compiler to do lazy loading of proc bodies. +## - Patch the C codegen to cache proc bodies and maybe types. -var db: DbConn +template db(): DbConn = g.incr.db -proc hashFileCached(fileIdx: int32; fullpath: string): string = - result = msgs.getHash(fileIdx) - if result.len == 0: - result = $secureHashFile(fullpath) - msgs.setHash(fileIdx, result) +proc encodeConfig(g: ModuleGraph): string = + result = newStringOfCap(100) + result.add RodFileVersion + for d in definedSymbolNames(g.config.symbols): + result.add ' ' + result.add d -proc needsRecompile(fileIdx: int32; fullpath: string; cycleCheck: var IntSet): bool = + template serialize(field) = + result.add ' ' + result.add($g.config.field) + + depConfigFields(serialize) + +proc needsRecompile(g: ModuleGraph; fileIdx: FileIndex; fullpath: string; + cycleCheck: var IntSet): bool = let root = db.getRow(sql"select id, fullhash from filenames where fullpath = ?", fullpath) if root[0].len == 0: return true - if root[1] != hashFileCached(fileIdx, fullpath): + if root[1] != hashFileCached(g.config, fileIdx, fullpath): return true # cycle detection: assume "not changed" is correct. - if cycleCheck.containsOrIncl(fileIdx): + if cycleCheck.containsOrIncl(int fileIdx): return false # check dependencies (recursively): for row in db.fastRows(sql"select fullpath from filenames where id in (select dependency from deps where module = ?)", root[0]): let dep = row[0] - if needsRecompile(dep.fileInfoIdx, dep, cycleCheck): + if needsRecompile(g, g.config.fileInfoIdx(dep), dep, cycleCheck): return true return false -proc getModuleId*(fileIdx: int32; fullpath: string): int = - if gSymbolFiles != v2Sf: return getID() - let module = db.getRow( - sql"select id, fullHash from modules where fullpath = ?", fullpath) - let currentFullhash = hashFileCached(fileIdx, fullpath) +proc getModuleId*(g: ModuleGraph; fileIdx: FileIndex; fullpath: string): int = + if g.config.symbolFiles in {disabledSf, writeOnlySf} or + g.incr.configChanged: + return getID() + let module = g.incr.db.getRow( + sql"select id, fullHash, nimid from modules where fullpath = ?", fullpath) + let currentFullhash = hashFileCached(g.config, fileIdx, fullpath) if module[0].len == 0: - result = int db.insertID(sql"insert into modules(fullpath, interfHash, fullHash) values (?, ?, ?)", - fullpath, "", currentFullhash) + result = getID() + db.exec(sql"insert into modules(fullpath, interfHash, fullHash, nimid) values (?, ?, ?, ?)", + fullpath, "", currentFullhash, result) else: - result = parseInt(module[0]) + result = parseInt(module[2]) if currentFullhash == module[1]: - # not changed, so use the cached AST (even if it might be wrong - # due to its dependencies): + # not changed, so use the cached AST: doAssert(result != 0) var cycleCheck = initIntSet() - if not needsRecompile(fileIdx, fullpath, cycleCheck): + if not needsRecompile(g, fileIdx, fullpath, cycleCheck): + echo "cached successfully! ", fullpath return -result db.exec(sql"update modules set fullHash = ? where id = ?", currentFullhash, module[0]) db.exec(sql"delete from deps where module = ?", module[0]) @@ -71,60 +79,17 @@ proc getModuleId*(fileIdx: int32; fullpath: string): int = db.exec(sql"delete from toplevelstmts where module = ?", module[0]) db.exec(sql"delete from statics where module = ?", module[0]) -type - TRodWriter = object - module: PSym - sstack: seq[PSym] # a stack of symbols to process - tstack: seq[PType] # a stack of types to process - tmarks, smarks: IntSet - forwardedSyms: seq[PSym] - - PRodWriter = var TRodWriter - -proc initRodWriter(module: PSym): TRodWriter = - result = TRodWriter(module: module, sstack: @[], tstack: @[], - tmarks: initIntSet(), smarks: initIntSet(), forwardedSyms: @[]) - -when false: - proc getDefines(): string = - result = "" - for d in definedSymbolNames(): - if result.len != 0: add(result, " ") - add(result, d) - -const - rodNL = "\L" - -proc pushType(w: PRodWriter, t: PType) = +proc pushType(w: var Writer, t: PType) = if not containsOrIncl(w.tmarks, t.id): w.tstack.add(t) -proc pushSym(w: PRodWriter, s: PSym) = +proc pushSym(w: var Writer, s: PSym) = if not containsOrIncl(w.smarks, s.id): w.sstack.add(s) -proc toDbFileId(fileIdx: int32): int = - if fileIdx == -1: return -1 - let fullpath = fileIdx.toFullPath - let row = db.getRow(sql"select id, fullhash from filenames where fullpath = ?", - fullpath) - let id = row[0] - let fullhash = hashFileCached(fileIdx, fullpath) - if id.len == 0: - result = int db.insertID(sql"insert into filenames(fullpath, fullhash) values (?, ?)", - fullpath, fullhash) - else: - if row[1] != fullhash: - db.exec(sql"update filenames set fullhash = ? where fullpath = ?", fullhash, fullpath) - result = parseInt(id) - -proc fromDbFileId(dbId: int): int32 = - if dbId == -1: return -1 - let fullpath = db.getValue(sql"select fullpath from filenames where id = ?", dbId) - doAssert fullpath.len > 0, "cannot find file name for DB ID " & $dbId - result = fileInfoIdx(fullpath) +template w: untyped = g.incr.w -proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode, +proc encodeNode(g: ModuleGraph; fInfo: TLineInfo, n: PNode, result: var string) = if n == nil: # nil nodes have to be stored too: @@ -139,14 +104,14 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode, result.add('?') encodeVInt(n.info.col, result) result.add(',') - encodeVInt(n.info.line, result) + encodeVInt(int n.info.line, result) result.add(',') - encodeVInt(toDbFileId(n.info.fileIndex), result) + encodeVInt(toDbFileId(g.incr, g.config, n.info.fileIndex), result) elif fInfo.line != n.info.line: result.add('?') encodeVInt(n.info.col, result) result.add(',') - encodeVInt(n.info.line, result) + encodeVInt(int n.info.line, result) elif fInfo.col != n.info.col: result.add('?') encodeVInt(n.info.col, result) @@ -182,10 +147,10 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode, pushSym(w, n.sym) else: for i in countup(0, sonsLen(n) - 1): - encodeNode(w, n.info, n.sons[i], result) + encodeNode(g, n.info, n.sons[i], result) add(result, ')') -proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) = +proc encodeLoc(g: ModuleGraph; loc: TLoc, result: var string) = var oldLen = result.len result.add('<') if loc.k != low(loc.k): encodeVInt(ord(loc.k), result) @@ -197,9 +162,7 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) = encodeVInt(cast[int32](loc.flags), result) if loc.lode != nil: add(result, '^') - encodeNode(w, unknownLineInfo(), loc.lode, result) - #encodeVInt(cast[int32](loc.t.id), result) - #pushType(w, loc.t) + encodeNode(g, unknownLineInfo(), loc.lode, result) if loc.r != nil: add(result, '!') encodeStr($loc.r, result) @@ -209,13 +172,13 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) = else: add(result, '>') -proc encodeType(w: PRodWriter, t: PType, result: var string) = +proc encodeType(g: ModuleGraph, t: PType, result: var string) = if t == nil: # nil nodes have to be stored too: result.add("[]") return # we need no surrounding [] here because the type is in a line of its own - if t.kind == tyForward: internalError("encodeType: tyForward") + if t.kind == tyForward: internalError(g.config, "encodeType: tyForward") # for the new rodfile viewer we use a preceding [ so that the data section # can easily be disambiguated: add(result, '[') @@ -223,7 +186,7 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) = add(result, '+') encodeVInt(t.id, result) if t.n != nil: - encodeNode(w, w.module.info, t.n, result) + encodeNode(g, unknownLineInfo(), t.n, result) if t.flags != {}: add(result, '$') encodeVInt(cast[int32](t.flags), result) @@ -269,7 +232,7 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) = add(result, '\20') encodeVInt(s.id, result) pushSym(w, s) - encodeLoc(w, t.loc, result) + encodeLoc(g, t.loc, result) for i in countup(0, sonsLen(t) - 1): if t.sons[i] == nil: add(result, "^()") @@ -278,15 +241,15 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) = encodeVInt(t.sons[i].id, result) pushType(w, t.sons[i]) -proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) = +proc encodeLib(g: ModuleGraph, lib: PLib, info: TLineInfo, result: var string) = add(result, '|') encodeVInt(ord(lib.kind), result) add(result, '|') encodeStr($lib.name, result) add(result, '|') - encodeNode(w, info, lib.path, result) + encodeNode(g, info, lib.path, result) -proc encodeInstantiations(w: PRodWriter; s: seq[PInstantiation]; +proc encodeInstantiations(g: ModuleGraph; s: seq[PInstantiation]; result: var string) = for t in s: result.add('\15') @@ -299,7 +262,7 @@ proc encodeInstantiations(w: PRodWriter; s: seq[PInstantiation]; result.add('\20') encodeVInt(t.compilesId, result) -proc encodeSym(w: PRodWriter, s: PSym, result: var string) = +proc encodeSym(g: ModuleGraph, s: PSym, result: var string) = if s == nil: # nil nodes have to be stored too: result.add("{}") @@ -317,9 +280,9 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) = result.add('?') if s.info.col != -1'i16: encodeVInt(s.info.col, result) result.add(',') - if s.info.line != -1'i16: encodeVInt(s.info.line, result) + encodeVInt(int s.info.line, result) result.add(',') - encodeVInt(toDbFileId(s.info.fileIndex), result) + encodeVInt(toDbFileId(g.incr, g.config, s.info.fileIndex), result) if s.owner != nil: result.add('*') encodeVInt(s.owner.id, result) @@ -338,11 +301,11 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) = if s.offset != - 1: result.add('`') encodeVInt(s.offset, result) - encodeLoc(w, s.loc, result) - if s.annex != nil: encodeLib(w, s.annex, s.info, result) + encodeLoc(g, s.loc, result) + if s.annex != nil: encodeLib(g, s.annex, s.info, result) if s.constraint != nil: add(result, '#') - encodeNode(w, unknownLineInfo(), s.constraint, result) + encodeNode(g, unknownLineInfo(), s.constraint, result) case s.kind of skType, skGenericParam: for t in s.typeInstCache: @@ -350,13 +313,13 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) = encodeVInt(t.id, result) pushType(w, t) of routineKinds: - encodeInstantiations(w, s.procInstCache, result) + encodeInstantiations(g, s.procInstCache, result) if s.gcUnsafetyReason != nil: result.add('\16') encodeVInt(s.gcUnsafetyReason.id, result) pushSym(w, s.gcUnsafetyReason) of skModule, skPackage: - encodeInstantiations(w, s.usedGenerics, result) + encodeInstantiations(g, s.usedGenerics, result) # we don't serialize: #tab*: TStrTable # interface table for modules of skLet, skVar, skField, skForVar: @@ -374,105 +337,93 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) = # we used to attempt to save space here by only storing a dummy AST if # it is not necessary, but Nim's heavy compile-time evaluation features # make that unfeasible nowadays: - encodeNode(w, s.info, s.ast, result) + encodeNode(g, s.info, s.ast, result) -proc storeSym(w: PRodWriter; s: PSym) = +proc storeSym(g: ModuleGraph; s: PSym) = if sfForward in s.flags and s.kind != skModule: w.forwardedSyms.add s return var buf = newStringOfCap(160) - encodeSym(w, s, buf) + encodeSym(g, s, buf) # XXX only store the name for exported symbols in order to speed up lookup # times once we enable the skStub logic. + let m = getModule(s) + let mid = if m == nil: 0 else: abs(m.id) db.exec(sql"insert into syms(nimid, module, name, data, exported) values (?, ?, ?, ?, ?)", - s.id, abs(w.module.id), s.name.s, buf, ord(sfExported in s.flags)) + s.id, mid, s.name.s, buf, ord(sfExported in s.flags)) -proc storeType(w: PRodWriter; t: PType) = +proc storeType(g: ModuleGraph; t: PType) = var buf = newStringOfCap(160) - encodeType(w, t, buf) + encodeType(g, t, buf) + let m = if t.owner != nil: getModule(t.owner) else: nil + let mid = if m == nil: 0 else: abs(m.id) db.exec(sql"insert into types(nimid, module, data) values (?, ?, ?)", - t.id, abs(w.module.id), buf) - -var w = initRodWriter(nil) + t.id, mid, buf) -proc storeNode*(module: PSym; n: PNode) = - if gSymbolFiles != v2Sf: return - w.module = module +proc storeNode*(g: ModuleGraph; module: PSym; n: PNode) = + if g.config.symbolFiles == disabledSf: return var buf = newStringOfCap(160) - encodeNode(w, module.info, n, buf) + encodeNode(g, module.info, n, buf) db.exec(sql"insert into toplevelstmts(module, position, data) values (?, ?, ?)", abs(module.id), module.offset, buf) inc module.offset var i = 0 while true: if i > 10_000: - quit "loop never ends!" + doAssert false, "loop never ends!" if w.sstack.len > 0: let s = w.sstack.pop() when false: echo "popped ", s.name.s, " ", s.id - storeSym(w, s) + storeSym(g, s) elif w.tstack.len > 0: let t = w.tstack.pop() - storeType(w, t) + storeType(g, t) when false: echo "popped type ", typeToString(t), " ", t.id else: break inc i -proc storeRemaining*(module: PSym) = - if gSymbolFiles != v2Sf: return - w.module = module +proc recordStmt*(g: ModuleGraph; module: PSym; n: PNode) = + storeNode(g, module, n) + +proc storeRemaining*(g: ModuleGraph; module: PSym) = + if g.config.symbolFiles == disabledSf: return + var stillForwarded: seq[PSym] = @[] for s in w.forwardedSyms: - assert sfForward notin s.flags - storeSym(w, s) - w.forwardedSyms.setLen 0 + if sfForward notin s.flags: + storeSym(g, s) + else: + stillForwarded.add s + swap w.forwardedSyms, stillForwarded # ---------------- decoder ----------------------------------- -type - TRodReader = object - module: PSym - #sstack: seq[(PSym, ptr PSym)] # a stack of symbols to process - #tstack: seq[(PType, ptr PType)] # a stack of types to process - - #tmarks, smarks: IntSet - syms: Table[int, PSym] ## XXX make this more efficients - types: Table[int, PType] - cache: IdentCache +type BlobReader = object s: string pos: int - PRodReader = var TRodReader - -proc initRodReader(cache: IdentCache): TRodReader = - TRodReader(module: nil, - syms: initTable[int, PSym](), types: initTable[int, PType](), - cache: cache) - -var gr = initRodReader(newIdentCache()) - using - r: PRodReader b: var BlobReader + g: ModuleGraph -proc loadSym(r; id: int, info: TLineInfo): PSym -proc loadType(r; id: int, info: TLineInfo): PType +proc loadSym(g; id: int, info: TLineInfo): PSym +proc loadType(g; id: int, info: TLineInfo): PType -proc decodeLineInfo(r; b; info: var TLineInfo) = +proc decodeLineInfo(g; b; info: var TLineInfo) = if b.s[b.pos] == '?': inc(b.pos) if b.s[b.pos] == ',': info.col = -1'i16 else: info.col = int16(decodeVInt(b.s, b.pos)) if b.s[b.pos] == ',': inc(b.pos) - if b.s[b.pos] == ',': info.line = -1'i16 - else: info.line = int16(decodeVInt(b.s, b.pos)) + if b.s[b.pos] == ',': info.line = 0'u16 + else: info.line = uint16(decodeVInt(b.s, b.pos)) if b.s[b.pos] == ',': inc(b.pos) - info.fileIndex = fromDbFileId(decodeVInt(b.s, b.pos)) + info.fileIndex = fromDbFileId(g.incr, g.config, decodeVInt(b.s, b.pos)) proc skipNode(b) = assert b.s[b.pos] == '(' @@ -488,7 +439,7 @@ proc skipNode(b) = inc pos b.pos = pos+1 # skip ')' -proc decodeNodeLazyBody(r; b; fInfo: TLineInfo, +proc decodeNodeLazyBody(g; b; fInfo: TLineInfo, belongsTo: PSym): PNode = result = nil if b.s[b.pos] == '(': @@ -497,14 +448,14 @@ proc decodeNodeLazyBody(r; b; fInfo: TLineInfo, inc(b.pos) return # nil node result = newNodeI(TNodeKind(decodeVInt(b.s, b.pos)), fInfo) - decodeLineInfo(r, b, result.info) + decodeLineInfo(g, b, result.info) if b.s[b.pos] == '$': inc(b.pos) result.flags = cast[TNodeFlags](int32(decodeVInt(b.s, b.pos))) if b.s[b.pos] == '^': inc(b.pos) var id = decodeVInt(b.s, b.pos) - result.typ = loadType(r, id, result.info) + result.typ = loadType(g, id, result.info) case result.kind of nkCharLit..nkUInt64Lit: if b.s[b.pos] == '!': @@ -525,16 +476,16 @@ proc decodeNodeLazyBody(r; b; fInfo: TLineInfo, if b.s[b.pos] == '!': inc(b.pos) var fl = decodeStr(b.s, b.pos) - result.ident = r.cache.getIdent(fl) + result.ident = g.cache.getIdent(fl) else: - internalError(result.info, "decodeNode: nkIdent") + internalError(g.config, result.info, "decodeNode: nkIdent") of nkSym: if b.s[b.pos] == '!': inc(b.pos) var id = decodeVInt(b.s, b.pos) - result.sym = loadSym(r, id, result.info) + result.sym = loadSym(g, id, result.info) else: - internalError(result.info, "decodeNode: nkSym") + internalError(g.config, result.info, "decodeNode: nkSym") else: var i = 0 while b.s[b.pos] != ')': @@ -545,17 +496,17 @@ proc decodeNodeLazyBody(r; b; fInfo: TLineInfo, skipNode(b) else: discard - addSonNilAllowed(result, decodeNodeLazyBody(r, b, result.info, nil)) + addSonNilAllowed(result, decodeNodeLazyBody(g, b, result.info, nil)) inc i if b.s[b.pos] == ')': inc(b.pos) - else: internalError(result.info, "decodeNode: ')' missing") + else: internalError(g.config, result.info, "decodeNode: ')' missing") else: - internalError(fInfo, "decodeNode: '(' missing " & $b.pos) + internalError(g.config, fInfo, "decodeNode: '(' missing " & $b.pos) -proc decodeNode(r; b; fInfo: TLineInfo): PNode = - result = decodeNodeLazyBody(r, b, fInfo, nil) +proc decodeNode(g; b; fInfo: TLineInfo): PNode = + result = decodeNodeLazyBody(g, b, fInfo, nil) -proc decodeLoc(r; b; loc: var TLoc, info: TLineInfo) = +proc decodeLoc(g; b; loc: var TLoc, info: TLineInfo) = if b.s[b.pos] == '<': inc(b.pos) if b.s[b.pos] in {'0'..'9', 'a'..'z', 'A'..'Z'}: @@ -574,7 +525,7 @@ proc decodeLoc(r; b; loc: var TLoc, info: TLineInfo) = loc.flags = {} if b.s[b.pos] == '^': inc(b.pos) - loc.lode = decodeNode(r, b, info) + loc.lode = decodeNode(g, b, info) # rrGetType(b, decodeVInt(b.s, b.pos), info) else: loc.lode = nil @@ -584,19 +535,21 @@ proc decodeLoc(r; b; loc: var TLoc, info: TLineInfo) = else: loc.r = nil if b.s[b.pos] == '>': inc(b.pos) - else: internalError(info, "decodeLoc " & b.s[b.pos]) + else: internalError(g.config, info, "decodeLoc " & b.s[b.pos]) -proc loadBlob(query: SqlQuery; id: int): BlobReader = +proc loadBlob(g; query: SqlQuery; id: int): BlobReader = let blob = db.getValue(query, id) if blob.len == 0: - internalError("symbolfiles: cannot find ID " & $ id) + internalError(g.config, "symbolfiles: cannot find ID " & $ id) result = BlobReader(pos: 0) shallowCopy(result.s, blob) + # ensure we can read without index checks: + result.s.add '\0' -proc loadType(r; id: int; info: TLineInfo): PType = - result = r.types.getOrDefault(id) +proc loadType(g; id: int; info: TLineInfo): PType = + result = g.incr.r.types.getOrDefault(id) if result != nil: return result - var b = loadBlob(sql"select data from types where nimid = ?", id) + var b = loadBlob(g, sql"select data from types where nimid = ?", id) if b.s[b.pos] == '[': inc(b.pos) @@ -611,10 +564,10 @@ proc loadType(r; id: int; info: TLineInfo): PType = setId(result.id) #if debugIds: registerID(result) else: - internalError(info, "decodeType: no id") + internalError(g.config, info, "decodeType: no id") # here this also avoids endless recursion for recursive type - r.types[result.id] = result - if b.s[b.pos] == '(': result.n = decodeNode(r, b, unknownLineInfo()) + g.incr.r.types.add(result.id, result) + if b.s[b.pos] == '(': result.n = decodeNode(g, b, unknownLineInfo()) if b.s[b.pos] == '$': inc(b.pos) result.flags = cast[TTypeFlags](int32(decodeVInt(b.s, b.pos))) @@ -623,10 +576,10 @@ proc loadType(r; id: int; info: TLineInfo): PType = result.callConv = TCallingConvention(decodeVInt(b.s, b.pos)) if b.s[b.pos] == '*': inc(b.pos) - result.owner = loadSym(r, decodeVInt(b.s, b.pos), info) + result.owner = loadSym(g, decodeVInt(b.s, b.pos), info) if b.s[b.pos] == '&': inc(b.pos) - result.sym = loadSym(r, decodeVInt(b.s, b.pos), info) + result.sym = loadSym(g, decodeVInt(b.s, b.pos), info) if b.s[b.pos] == '/': inc(b.pos) result.size = decodeVInt(b.s, b.pos) @@ -646,65 +599,65 @@ proc loadType(r; id: int; info: TLineInfo): PType = if b.s[b.pos] == '\15': inc(b.pos) - result.destructor = loadSym(r, decodeVInt(b.s, b.pos), info) + result.destructor = loadSym(g, decodeVInt(b.s, b.pos), info) if b.s[b.pos] == '\16': inc(b.pos) - result.deepCopy = loadSym(r, decodeVInt(b.s, b.pos), info) + result.deepCopy = loadSym(g, decodeVInt(b.s, b.pos), info) if b.s[b.pos] == '\17': inc(b.pos) - result.assignment = loadSym(r, decodeVInt(b.s, b.pos), info) + result.assignment = loadSym(g, decodeVInt(b.s, b.pos), info) if b.s[b.pos] == '\18': inc(b.pos) - result.sink = loadSym(r, decodeVInt(b.s, b.pos), info) + result.sink = loadSym(g, decodeVInt(b.s, b.pos), info) while b.s[b.pos] == '\19': inc(b.pos) let x = decodeVInt(b.s, b.pos) doAssert b.s[b.pos] == '\20' inc(b.pos) - let y = loadSym(r, decodeVInt(b.s, b.pos), info) + let y = loadSym(g, decodeVInt(b.s, b.pos), info) result.methods.safeAdd((x, y)) - decodeLoc(r, b, result.loc, info) + decodeLoc(g, b, result.loc, info) while b.s[b.pos] == '^': inc(b.pos) if b.s[b.pos] == '(': inc(b.pos) if b.s[b.pos] == ')': inc(b.pos) - else: internalError(info, "decodeType ^(" & b.s[b.pos]) + else: internalError(g.config, info, "decodeType ^(" & b.s[b.pos]) rawAddSon(result, nil) else: - var d = decodeVInt(b.s, b.pos) - rawAddSon(result, loadType(r, d, info)) + let d = decodeVInt(b.s, b.pos) + rawAddSon(result, loadType(g, d, info)) -proc decodeLib(r; b; info: TLineInfo): PLib = +proc decodeLib(g; b; info: TLineInfo): PLib = result = nil if b.s[b.pos] == '|': new(result) inc(b.pos) result.kind = TLibKind(decodeVInt(b.s, b.pos)) - if b.s[b.pos] != '|': internalError("decodeLib: 1") + if b.s[b.pos] != '|': internalError(g.config, "decodeLib: 1") inc(b.pos) result.name = rope(decodeStr(b.s, b.pos)) - if b.s[b.pos] != '|': internalError("decodeLib: 2") + if b.s[b.pos] != '|': internalError(g.config, "decodeLib: 2") inc(b.pos) - result.path = decodeNode(r, b, info) + result.path = decodeNode(g, b, info) -proc decodeInstantiations(r; b; info: TLineInfo; +proc decodeInstantiations(g; b; info: TLineInfo; s: var seq[PInstantiation]) = while b.s[b.pos] == '\15': inc(b.pos) var ii: PInstantiation new ii - ii.sym = loadSym(r, decodeVInt(b.s, b.pos), info) + ii.sym = loadSym(g, decodeVInt(b.s, b.pos), info) ii.concreteTypes = @[] while b.s[b.pos] == '\17': inc(b.pos) - ii.concreteTypes.add loadType(r, decodeVInt(b.s, b.pos), info) + ii.concreteTypes.add loadType(g, decodeVInt(b.s, b.pos), info) if b.s[b.pos] == '\20': inc(b.pos) ii.compilesId = decodeVInt(b.s, b.pos) s.safeAdd ii -proc loadSymFromBlob(r; b; info: TLineInfo): PSym = +proc loadSymFromBlob(g; b; info: TLineInfo): PSym = if b.s[b.pos] == '{': inc(b.pos) if b.s[b.pos] == '}': @@ -717,26 +670,26 @@ proc loadSymFromBlob(r; b; info: TLineInfo): PSym = id = decodeVInt(b.s, b.pos) setId(id) else: - internalError(info, "decodeSym: no id") + internalError(g.config, info, "decodeSym: no id") var ident: PIdent if b.s[b.pos] == '&': inc(b.pos) - ident = r.cache.getIdent(decodeStr(b.s, b.pos)) + ident = g.cache.getIdent(decodeStr(b.s, b.pos)) else: - internalError(info, "decodeSym: no ident") + internalError(g.config, info, "decodeSym: no ident") #echo "decoding: {", ident.s new(result) result.id = id result.kind = k result.name = ident # read the rest of the symbol description: - r.syms[result.id] = result + g.incr.r.syms.add(result.id, result) if b.s[b.pos] == '^': inc(b.pos) - result.typ = loadType(r, decodeVInt(b.s, b.pos), info) - decodeLineInfo(r, b, result.info) + result.typ = loadType(g, decodeVInt(b.s, b.pos), info) + decodeLineInfo(g, b, result.info) if b.s[b.pos] == '*': inc(b.pos) - result.owner = loadSym(r, decodeVInt(b.s, b.pos), result.info) + result.owner = loadSym(g, decodeVInt(b.s, b.pos), result.info) if b.s[b.pos] == '$': inc(b.pos) result.flags = cast[TSymFlags](int32(decodeVInt(b.s, b.pos))) @@ -746,8 +699,6 @@ proc loadSymFromBlob(r; b; info: TLineInfo): PSym = if b.s[b.pos] == '!': inc(b.pos) result.options = cast[TOptions](int32(decodeVInt(b.s, b.pos))) - else: - result.options = r.module.options if b.s[b.pos] == '%': inc(b.pos) result.position = decodeVInt(b.s, b.pos) @@ -755,28 +706,28 @@ proc loadSymFromBlob(r; b; info: TLineInfo): PSym = inc(b.pos) result.offset = decodeVInt(b.s, b.pos) else: - result.offset = - 1 - decodeLoc(r, b, result.loc, result.info) - result.annex = decodeLib(r, b, info) + result.offset = -1 + decodeLoc(g, b, result.loc, result.info) + result.annex = decodeLib(g, b, info) if b.s[b.pos] == '#': inc(b.pos) - result.constraint = decodeNode(r, b, unknownLineInfo()) + result.constraint = decodeNode(g, b, unknownLineInfo()) case result.kind of skType, skGenericParam: while b.s[b.pos] == '\14': inc(b.pos) - result.typeInstCache.safeAdd loadType(r, decodeVInt(b.s, b.pos), result.info) + result.typeInstCache.safeAdd loadType(g, decodeVInt(b.s, b.pos), result.info) of routineKinds: - decodeInstantiations(r, b, result.info, result.procInstCache) + decodeInstantiations(g, b, result.info, result.procInstCache) if b.s[b.pos] == '\16': inc(b.pos) - result.gcUnsafetyReason = loadSym(r, decodeVInt(b.s, b.pos), result.info) + result.gcUnsafetyReason = loadSym(g, decodeVInt(b.s, b.pos), result.info) of skModule, skPackage: - decodeInstantiations(r, b, result.info, result.usedGenerics) + decodeInstantiations(g, b, result.info, result.usedGenerics) of skLet, skVar, skField, skForVar: if b.s[b.pos] == '\18': inc(b.pos) - result.guard = loadSym(r, decodeVInt(b.s, b.pos), result.info) + result.guard = loadSym(g, decodeVInt(b.s, b.pos), result.info) if b.s[b.pos] == '\19': inc(b.pos) result.bitsize = decodeVInt(b.s, b.pos).int16 @@ -786,159 +737,146 @@ proc loadSymFromBlob(r; b; info: TLineInfo): PSym = #if result.kind in routineKinds: # result.ast = decodeNodeLazyBody(b, result.info, result) #else: - result.ast = decodeNode(r, b, result.info) + result.ast = decodeNode(g, b, result.info) if sfCompilerProc in result.flags: - registerCompilerProc(result) + registerCompilerProc(g, result) #echo "loading ", result.name.s -proc loadSym(r; id: int; info: TLineInfo): PSym = - result = r.syms.getOrDefault(id) +proc loadSym(g; id: int; info: TLineInfo): PSym = + result = g.incr.r.syms.getOrDefault(id) if result != nil: return result - var b = loadBlob(sql"select data from syms where nimid = ?", id) - result = loadSymFromBlob(r, b, info) + var b = loadBlob(g, sql"select data from syms where nimid = ?", id) + result = loadSymFromBlob(g, b, info) doAssert id == result.id, "symbol ID is not consistent!" -proc loadModuleSymTab(r; module: PSym) = +proc loadModuleSymTab(g; module: PSym) = ## goal: fill module.tab - gr.syms[module.id] = module + g.incr.r.syms.add(module.id, module) for row in db.fastRows(sql"select nimid, data from syms where module = ? and exported = 1", abs(module.id)): let id = parseInt(row[0]) - var s = r.syms.getOrDefault(id) + var s = g.incr.r.syms.getOrDefault(id) if s == nil: var b = BlobReader(pos: 0) shallowCopy(b.s, row[1]) - s = loadSymFromBlob(r, b, module.info) + # ensure we can read without index checks: + b.s.add '\0' + s = loadSymFromBlob(g, b, module.info) assert s != nil strTableAdd(module.tab, s) if sfSystemModule in module.flags: - magicsys.systemModule = module - -proc loadNode*(module: PSym; index: int): PNode = - assert gSymbolFiles == v2Sf - if index == 0: - loadModuleSymTab(gr, module) - #index = parseInt db.getValue( - # sql"select min(id) from toplevelstmts where module = ?", abs module.id) - var b = BlobReader(pos: 0) - b.s = db.getValue(sql"select data from toplevelstmts where position = ? and module = ?", - index, abs module.id) - if b.s.len == 0: - db.exec(sql"insert into controlblock(idgen) values (?)", gFrontEndId) - return nil # end marker - gr.module = module - result = decodeNode(gr, b, module.info) - -proc addModuleDep*(module, fileIdx: int32; isIncludeFile: bool) = - if gSymbolFiles != v2Sf: return - - let a = toDbFileId(module) - let b = toDbFileId(fileIdx) - - db.exec(sql"insert into deps(module, dependency, isIncludeFile) values (?, ?, ?)", - a, b, ord(isIncludeFile)) - -# --------------- Database model --------------------------------------------- - -proc createDb() = - db.exec(sql""" - create table if not exists controlblock( - idgen integer not null - ); - """) - - db.exec(sql""" - create table if not exists filenames( - id integer primary key, - fullpath varchar(8000) not null, - fullHash varchar(256) not null - ); - """) - db.exec sql"create index if not exists FilenameIx on filenames(fullpath);" - - db.exec(sql""" - create table if not exists modules( - id integer primary key, - fullpath varchar(8000) not null, - interfHash varchar(256) not null, - fullHash varchar(256) not null, - - created timestamp not null default (DATETIME('now')) - );""") - db.exec(sql"""create unique index if not exists SymNameIx on modules(fullpath);""") - - db.exec(sql""" - create table if not exists deps( - id integer primary key, - module integer not null, - dependency integer not null, - isIncludeFile integer not null, - foreign key (module) references filenames(id), - foreign key (dependency) references filenames(id) - );""") - db.exec(sql"""create index if not exists DepsIx on deps(module);""") - - db.exec(sql""" - create table if not exists types( - id integer primary key, - nimid integer not null, - module integer not null, - data blob not null, - foreign key (module) references module(id) - ); - """) - db.exec sql"create index TypeByModuleIdx on types(module);" - db.exec sql"create index TypeByNimIdIdx on types(nimid);" - - db.exec(sql""" - create table if not exists syms( - id integer primary key, - nimid integer not null, - module integer not null, - name varchar(256) not null, - data blob not null, - exported int not null, - foreign key (module) references module(id) - ); - """) - db.exec sql"create index if not exists SymNameIx on syms(name);" - db.exec sql"create index SymByNameAndModuleIdx on syms(name, module);" - db.exec sql"create index SymByModuleIdx on syms(module);" - db.exec sql"create index SymByNimIdIdx on syms(nimid);" - - - db.exec(sql""" - create table if not exists toplevelstmts( - id integer primary key, - position integer not null, - module integer not null, - data blob not null, - foreign key (module) references module(id) - ); - """) - db.exec sql"create index TopLevelStmtByModuleIdx on toplevelstmts(module);" - db.exec sql"create index TopLevelStmtByPositionIdx on toplevelstmts(position);" - - db.exec(sql""" - create table if not exists statics( - id integer primary key, - module integer not null, - data blob not null, - foreign key (module) references module(id) - ); - """) - db.exec sql"create index StaticsByModuleIdx on toplevelstmts(module);" - db.exec sql"insert into controlblock(idgen) values (0)" - -proc setupModuleCache* = - if gSymbolFiles != v2Sf: return - let dbfile = getNimcacheDir() / "rodfiles.db" + g.systemModule = module + +proc replay(g: ModuleGraph; module: PSym; n: PNode) = + # XXX check if we need to replay nkStaticStmt here. + case n.kind + #of nkStaticStmt: + #evalStaticStmt(module, g, n[0], module) + #of nkVarSection, nkLetSection: + # nkVarSections are already covered by the vmgen which produces nkStaticStmt + of nkMethodDef: + methodDef(g, n[namePos].sym, fromCache=true) + of nkCommentStmt: + # pragmas are complex and can be user-overriden via templates. So + # instead of using the original ``nkPragma`` nodes, we rely on the + # fact that pragmas.nim was patched to produce specialized recorded + # statements for us in the form of ``nkCommentStmt`` with (key, value) + # pairs. Ordinary nkCommentStmt nodes never have children so this is + # not ambiguous. + # Fortunately only a tiny subset of the available pragmas need to + # be replayed here. This is always a subset of ``pragmas.stmtPragmas``. + if n.len >= 2: + internalAssert g.config, n[0].kind == nkStrLit and n[1].kind == nkStrLit + case n[0].strVal + of "hint": message(g.config, n.info, hintUser, n[1].strVal) + of "warning": message(g.config, n.info, warnUser, n[1].strVal) + of "error": localError(g.config, n.info, errUser, n[1].strVal) + of "compile": + internalAssert g.config, n.len == 3 and n[2].kind == nkStrLit + var cf = Cfile(cname: n[1].strVal, obj: n[2].strVal, + flags: {CfileFlag.External}) + extccomp.addExternalFileToCompile(g.config, cf) + of "link": + extccomp.addExternalFileToLink(g.config, n[1].strVal) + of "passl": + extccomp.addLinkOption(g.config, n[1].strVal) + of "passc": + extccomp.addCompileOption(g.config, n[1].strVal) + of "cppdefine": + options.cppDefine(g.config, n[1].strVal) + of "inc": + let destKey = n[1].strVal + let by = n[2].intVal + let v = getOrDefault(g.cacheCounters, destKey) + g.cacheCounters[destKey] = v+by + of "put": + let destKey = n[1].strVal + let key = n[2].strVal + let val = n[3] + if not contains(g.cacheTables, destKey): + g.cacheTables[destKey] = initBTree[string, PNode]() + if not contains(g.cacheTables[destKey], key): + g.cacheTables[destKey].add(key, val) + else: + internalError(g.config, n.info, "key already exists: " & key) + of "incl": + let destKey = n[1].strVal + let val = n[2] + if not contains(g.cacheSeqs, destKey): + g.cacheSeqs[destKey] = newTree(nkStmtList, val) + else: + block search: + for existing in g.cacheSeqs[destKey]: + if exprStructuralEquivalent(existing, val, strictSymEquality=true): + break search + g.cacheSeqs[destKey].add val + of "add": + let destKey = n[1].strVal + let val = n[2] + if not contains(g.cacheSeqs, destKey): + g.cacheSeqs[destKey] = newTree(nkStmtList, val) + else: + g.cacheSeqs[destKey].add val + else: + internalAssert g.config, false + of nkImportStmt: + for x in n: + internalAssert g.config, x.kind == nkStrLit + let imported = g.importModuleCallback(g, module, fileInfoIdx(g.config, n[0].strVal)) + internalAssert g.config, imported.id < 0 + of nkStmtList, nkStmtListExpr: + for x in n: replay(g, module, x) + else: discard "nothing to do for this node" + +proc loadNode*(g: ModuleGraph; module: PSym): PNode = + loadModuleSymTab(g, module) + result = newNodeI(nkStmtList, module.info) + for row in db.rows(sql"select data from toplevelstmts where module = ? order by position asc", + abs module.id): + + var b = BlobReader(pos: 0) + # ensure we can read without index checks: + b.s = row[0] & '\0' + result.add decodeNode(g, b, module.info) + + db.exec(sql"insert into controlblock(idgen) values (?)", gFrontEndId) + replay(g, module, result) + +proc setupModuleCache*(g: ModuleGraph) = + if g.config.symbolFiles == disabledSf: return + g.recordStmt = recordStmt + let dbfile = getNimcacheDir(g.config) / "rodfiles.db" + if g.config.symbolFiles == writeOnlySf: + removeFile(dbfile) if not fileExists(dbfile): db = open(connection=dbfile, user="nim", password="", database="nim") - createDb() + createDb(db) + db.exec(sql"insert into config(config) values (?)", encodeConfig(g)) else: db = open(connection=dbfile, user="nim", password="", database="nim") + let oldConfig = db.getValue(sql"select config from config") + g.incr.configChanged = oldConfig != encodeConfig(g) db.exec(sql"pragma journal_mode=off") db.exec(sql"pragma SYNCHRONOUS=off") db.exec(sql"pragma LOCKING_MODE=exclusive") diff --git a/compiler/rodread.nim b/compiler/rodread.nim deleted file mode 100644 index 52e7a924c..000000000 --- a/compiler/rodread.nim +++ /dev/null @@ -1,1244 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2013 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -# This module is responsible for loading of rod files. -# -# Reading and writing binary files are really hard to debug. Therefore we use -# a "creative" text/binary hybrid format. ROD-files are more efficient -# to process because symbols can be loaded on demand. -# -# A ROD file consists of: -# -# - a header: -# NIM:$fileversion\n -# - the module's id (even if the module changed, its ID will not!): -# ID:Ax3\n -# - HASH value of this module: -# HASH:HASH-val\n -# - a section containing the compiler options and defines this -# module has been compiled with: -# OPTIONS:options\n -# GOPTIONS:options\n # global options -# CMD:command\n -# DEFINES:defines\n -# - FILES( -# myfile.inc -# lib/mymodA -# ) -# - an include file dependency section: -# INCLUDES( -# <fileidx> <Hash of myfile.inc>\n # fileidx is the LINE in the file section! -# ) -# - a module dependency section: -# DEPS: <fileidx> <fileidx>\n -# - an interface section: -# INTERF( -# identifier1 id\n # id is the symbol's id -# identifier2 id\n -# ) -# - a compiler proc section: -# COMPILERPROCS( -# identifier1 id\n # id is the symbol's id -# ) -# - an index consisting of (ID, linenumber)-pairs: -# INDEX( -# id-diff idx-diff\n -# id-diff idx-diff\n -# ) -# -# Since the whole index has to be read in advance, we compress it by -# storing the integer differences to the last entry instead of using the -# real numbers. -# -# - an import index consisting of (ID, moduleID)-pairs: -# IMPORTS( -# id-diff moduleID-diff\n -# id-diff moduleID-diff\n -# ) -# - a list of all exported type converters because they are needed for correct -# semantic checking: -# CONVERTERS:id id\n # symbol ID -# -# This is a misnomer now; it's really a "load unconditionally" section as -# it is also used for pattern templates. -# -# - a list of all (private or exported) methods because they are needed for -# correct dispatcher generation: -# METHODS: id id\n # symbol ID -# - an AST section that contains the module's AST: -# INIT( -# idx\n # position of the node in the DATA section -# idx\n -# ) -# - a data section, where each type, symbol or AST is stored. -# DATA( -# type -# (node) -# sym -# ) -# -# The data section MUST be the last section of the file, because processing -# stops immediately after ``DATA(`` and the rest is only loaded on demand -# by using a mem'mapped file. -# - -import - os, options, strutils, nversion, ast, astalgo, msgs, platform, condsyms, - ropes, idents, std / sha1, idgen, types, rodutils, memfiles, tables, - configuration - -type - TReasonForRecompile* = enum ## all the reasons that can trigger recompilation - rrEmpty, # dependencies not yet computed - rrNone, # no need to recompile - rrRodDoesNotExist, # rod file does not exist - rrRodInvalid, # rod file is invalid - rrHashChange, # file has been edited since last recompilation - rrDefines, # defines have changed - rrOptions, # options have changed - rrInclDeps, # an include has changed - rrModDeps # a module this module depends on has been changed - -const - reasonToFrmt*: array[TReasonForRecompile, string] = ["", - "no need to recompile: $1", "symbol file for $1 does not exist", - "symbol file for $1 has the wrong version", - "file edited since last compilation: $1", - "list of conditional symbols changed for: $1", - "list of options changed for: $1", - "an include file edited: $1", - "a module $1 depends on has changed"] - -type - TIndex*{.final.} = object # an index with compression - lastIdxKey*, lastIdxVal*: int - tab*: TIITable - r*: string # writers use this - offset*: int # readers use this - - TRodReader* = object of RootObj - pos: int # position; used for parsing - s: cstring # mmap'ed file contents - options: TOptions - reason: TReasonForRecompile - modDeps: seq[FileIndex] - files: seq[FileIndex] - dataIdx: int # offset of start of data section - convertersIdx: int # offset of start of converters section - initIdx, interfIdx, compilerProcsIdx, methodsIdx: int - filename: string - index, imports: TIndex - readerIndex: int - line: int # only used for debugging, but is always in the code - moduleID: int - syms: Table[int, PSym] # already processed symbols - memfile: MemFile # unfortunately there is no point in time where we - # can close this! XXX - methods*: TSymSeq - origFile: string - inViewMode: bool - cache*: IdentCache - config: ConfigRef - - PRodReader* = ref TRodReader - -var rodCompilerprocs*: TStrTable # global because this is needed by magicsys - -proc rawLoadStub(s: PSym) - -var gTypeTable: TIdTable - -proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym - # `info` is only used for debugging purposes -proc rrGetType(r: PRodReader, id: int, info: TLineInfo): PType - -proc decodeLineInfo(r: PRodReader, info: var TLineInfo) = - if r.s[r.pos] == '?': - inc(r.pos) - if r.s[r.pos] == ',': info.col = -1'i16 - else: info.col = int16(decodeVInt(r.s, r.pos)) - if r.s[r.pos] == ',': - inc(r.pos) - if r.s[r.pos] == ',': info.line = 0'u16 - else: info.line = uint16(decodeVInt(r.s, r.pos)) - if r.s[r.pos] == ',': - inc(r.pos) - info = newLineInfo(r.files[decodeVInt(r.s, r.pos)], int info.line, info.col) - -proc skipNode(r: PRodReader) = - assert r.s[r.pos] == '(' - var par = 0 - var pos = r.pos+1 - while true: - case r.s[pos] - of ')': - if par == 0: break - dec par - of '(': inc par - else: discard - inc pos - r.pos = pos+1 # skip ')' - -proc decodeNodeLazyBody(r: PRodReader, fInfo: TLineInfo, - belongsTo: PSym): PNode = - result = nil - if r.s[r.pos] == '(': - inc(r.pos) - if r.s[r.pos] == ')': - inc(r.pos) - return # nil node - result = newNodeI(TNodeKind(decodeVInt(r.s, r.pos)), fInfo) - decodeLineInfo(r, result.info) - if r.s[r.pos] == '$': - inc(r.pos) - result.flags = cast[TNodeFlags](int32(decodeVInt(r.s, r.pos))) - if r.s[r.pos] == '^': - inc(r.pos) - var id = decodeVInt(r.s, r.pos) - result.typ = rrGetType(r, id, result.info) - case result.kind - of nkCharLit..nkUInt64Lit: - if r.s[r.pos] == '!': - inc(r.pos) - result.intVal = decodeVBiggestInt(r.s, r.pos) - of nkFloatLit..nkFloat64Lit: - if r.s[r.pos] == '!': - inc(r.pos) - var fl = decodeStr(r.s, r.pos) - result.floatVal = parseFloat(fl) - of nkStrLit..nkTripleStrLit: - if r.s[r.pos] == '!': - inc(r.pos) - result.strVal = decodeStr(r.s, r.pos) - else: - result.strVal = "" # BUGFIX - of nkIdent: - if r.s[r.pos] == '!': - inc(r.pos) - var fl = decodeStr(r.s, r.pos) - result.ident = r.cache.getIdent(fl) - else: - internalError(r.config, result.info, "decodeNode: nkIdent") - of nkSym: - if r.s[r.pos] == '!': - inc(r.pos) - var id = decodeVInt(r.s, r.pos) - result.sym = rrGetSym(r, id, result.info) - else: - internalError(r.config, result.info, "decodeNode: nkSym") - else: - var i = 0 - while r.s[r.pos] != ')': - if belongsTo != nil and i == bodyPos: - addSonNilAllowed(result, nil) - belongsTo.offset = r.pos - skipNode(r) - else: - addSonNilAllowed(result, decodeNodeLazyBody(r, result.info, nil)) - inc i - if r.s[r.pos] == ')': inc(r.pos) - else: internalError(r.config, result.info, "decodeNode: ')' missing") - else: - internalError(r.config, fInfo, "decodeNode: '(' missing " & $r.pos) - -proc decodeNode(r: PRodReader, fInfo: TLineInfo): PNode = - result = decodeNodeLazyBody(r, fInfo, nil) - -proc decodeLoc(r: PRodReader, loc: var TLoc, info: TLineInfo) = - if r.s[r.pos] == '<': - inc(r.pos) - if r.s[r.pos] in {'0'..'9', 'a'..'z', 'A'..'Z'}: - loc.k = TLocKind(decodeVInt(r.s, r.pos)) - else: - loc.k = low(loc.k) - if r.s[r.pos] == '*': - inc(r.pos) - loc.storage = TStorageLoc(decodeVInt(r.s, r.pos)) - else: - loc.storage = low(loc.storage) - if r.s[r.pos] == '$': - inc(r.pos) - loc.flags = cast[TLocFlags](int32(decodeVInt(r.s, r.pos))) - else: - loc.flags = {} - if r.s[r.pos] == '^': - inc(r.pos) - loc.lode = decodeNode(r, info) - # rrGetType(r, decodeVInt(r.s, r.pos), info) - else: - loc.lode = nil - if r.s[r.pos] == '!': - inc(r.pos) - loc.r = rope(decodeStr(r.s, r.pos)) - else: - loc.r = nil - if r.s[r.pos] == '>': inc(r.pos) - else: internalError(r.config, info, "decodeLoc " & r.s[r.pos]) - -proc decodeType(r: PRodReader, info: TLineInfo): PType = - result = nil - if r.s[r.pos] == '[': - inc(r.pos) - if r.s[r.pos] == ']': - inc(r.pos) - return # nil type - new(result) - result.kind = TTypeKind(decodeVInt(r.s, r.pos)) - if r.s[r.pos] == '+': - inc(r.pos) - result.id = decodeVInt(r.s, r.pos) - setId(result.id) - if debugIds: registerID(result) - else: - internalError(r.config, info, "decodeType: no id") - # here this also avoids endless recursion for recursive type - idTablePut(gTypeTable, result, result) - if r.s[r.pos] == '(': result.n = decodeNode(r, unknownLineInfo()) - if r.s[r.pos] == '$': - inc(r.pos) - result.flags = cast[TTypeFlags](int32(decodeVInt(r.s, r.pos))) - if r.s[r.pos] == '?': - inc(r.pos) - result.callConv = TCallingConvention(decodeVInt(r.s, r.pos)) - if r.s[r.pos] == '*': - inc(r.pos) - result.owner = rrGetSym(r, decodeVInt(r.s, r.pos), info) - if r.s[r.pos] == '&': - inc(r.pos) - result.sym = rrGetSym(r, decodeVInt(r.s, r.pos), info) - if r.s[r.pos] == '/': - inc(r.pos) - result.size = decodeVInt(r.s, r.pos) - else: - result.size = - 1 - if r.s[r.pos] == '=': - inc(r.pos) - result.align = decodeVInt(r.s, r.pos).int16 - else: - result.align = 2 - - if r.s[r.pos] == '\14': - inc(r.pos) - result.lockLevel = decodeVInt(r.s, r.pos).TLockLevel - else: - result.lockLevel = UnspecifiedLockLevel - - if r.s[r.pos] == '\15': - inc(r.pos) - result.destructor = rrGetSym(r, decodeVInt(r.s, r.pos), info) - if r.s[r.pos] == '\16': - inc(r.pos) - result.deepCopy = rrGetSym(r, decodeVInt(r.s, r.pos), info) - if r.s[r.pos] == '\17': - inc(r.pos) - result.assignment = rrGetSym(r, decodeVInt(r.s, r.pos), info) - if r.s[r.pos] == '\18': - inc(r.pos) - result.sink = rrGetSym(r, decodeVInt(r.s, r.pos), info) - while r.s[r.pos] == '\19': - inc(r.pos) - let x = decodeVInt(r.s, r.pos) - doAssert r.s[r.pos] == '\20' - inc(r.pos) - let y = rrGetSym(r, decodeVInt(r.s, r.pos), info) - result.methods.add((x, y)) - decodeLoc(r, result.loc, info) - while r.s[r.pos] == '^': - inc(r.pos) - if r.s[r.pos] == '(': - inc(r.pos) - if r.s[r.pos] == ')': inc(r.pos) - else: internalError(r.config, info, "decodeType ^(" & r.s[r.pos]) - rawAddSon(result, nil) - else: - var d = decodeVInt(r.s, r.pos) - rawAddSon(result, rrGetType(r, d, info)) - -proc decodeLib(r: PRodReader, info: TLineInfo): PLib = - result = nil - if r.s[r.pos] == '|': - new(result) - inc(r.pos) - result.kind = TLibKind(decodeVInt(r.s, r.pos)) - if r.s[r.pos] != '|': internalError(r.config, "decodeLib: 1") - inc(r.pos) - result.name = rope(decodeStr(r.s, r.pos)) - if r.s[r.pos] != '|': internalError(r.config, "decodeLib: 2") - inc(r.pos) - result.path = decodeNode(r, info) - -proc decodeInstantiations(r: PRodReader; info: TLineInfo; - s: var seq[PInstantiation]) = - while r.s[r.pos] == '\15': - inc(r.pos) - var ii: PInstantiation - new ii - ii.sym = rrGetSym(r, decodeVInt(r.s, r.pos), info) - ii.concreteTypes = @[] - while r.s[r.pos] == '\17': - inc(r.pos) - ii.concreteTypes.add rrGetType(r, decodeVInt(r.s, r.pos), info) - if r.s[r.pos] == '\20': - inc(r.pos) - ii.compilesId = decodeVInt(r.s, r.pos) - s.add ii - -proc decodeSym(r: PRodReader, info: TLineInfo): PSym = - var - id: int - ident: PIdent - result = nil - if r.s[r.pos] == '{': - inc(r.pos) - if r.s[r.pos] == '}': - inc(r.pos) - return # nil sym - var k = TSymKind(decodeVInt(r.s, r.pos)) - if r.s[r.pos] == '+': - inc(r.pos) - id = decodeVInt(r.s, r.pos) - setId(id) - else: - internalError(r.config, info, "decodeSym: no id") - if r.s[r.pos] == '&': - inc(r.pos) - ident = r.cache.getIdent(decodeStr(r.s, r.pos)) - else: - internalError(r.config, info, "decodeSym: no ident") - #echo "decoding: {", ident.s - result = r.syms.getOrDefault(id) - if result == nil: - new(result) - result.id = id - r.syms[result.id] = result - if debugIds: registerID(result) - elif result.id != id: - internalError(r.config, info, "decodeSym: wrong id") - elif result.kind != skStub and not r.inViewMode: - # we already loaded the symbol - return - else: - reset(result[]) - result.id = id - result.kind = k - result.name = ident # read the rest of the symbol description: - if r.s[r.pos] == '^': - inc(r.pos) - result.typ = rrGetType(r, decodeVInt(r.s, r.pos), info) - decodeLineInfo(r, result.info) - if r.s[r.pos] == '*': - inc(r.pos) - result.owner = rrGetSym(r, decodeVInt(r.s, r.pos), result.info) - if r.s[r.pos] == '$': - inc(r.pos) - result.flags = cast[TSymFlags](int32(decodeVInt(r.s, r.pos))) - if r.s[r.pos] == '@': - inc(r.pos) - result.magic = TMagic(decodeVInt(r.s, r.pos)) - if r.s[r.pos] == '!': - inc(r.pos) - result.options = cast[TOptions](int32(decodeVInt(r.s, r.pos))) - else: - result.options = r.options - if r.s[r.pos] == '%': - inc(r.pos) - result.position = decodeVInt(r.s, r.pos) - elif result.kind notin routineKinds + {skModule}: - result.position = 0 - # this may have been misused as reader index! But we still - # need it for routines as the body is loaded lazily. - if r.s[r.pos] == '`': - inc(r.pos) - result.offset = decodeVInt(r.s, r.pos) - else: - result.offset = - 1 - decodeLoc(r, result.loc, result.info) - result.annex = decodeLib(r, info) - if r.s[r.pos] == '#': - inc(r.pos) - result.constraint = decodeNode(r, unknownLineInfo()) - case result.kind - of skType, skGenericParam: - while r.s[r.pos] == '\14': - inc(r.pos) - result.typeInstCache.add rrGetType(r, decodeVInt(r.s, r.pos), result.info) - of routineKinds: - decodeInstantiations(r, result.info, result.procInstCache) - if r.s[r.pos] == '\16': - inc(r.pos) - result.gcUnsafetyReason = rrGetSym(r, decodeVInt(r.s, r.pos), result.info) - of skModule, skPackage: - decodeInstantiations(r, result.info, result.usedGenerics) - of skLet, skVar, skField, skForVar: - if r.s[r.pos] == '\18': - inc(r.pos) - result.guard = rrGetSym(r, decodeVInt(r.s, r.pos), result.info) - if r.s[r.pos] == '\19': - inc(r.pos) - result.bitsize = decodeVInt(r.s, r.pos).int16 - else: discard - - if r.s[r.pos] == '(': - if result.kind in routineKinds: - result.ast = decodeNodeLazyBody(r, result.info, result) - # since we load the body lazily, we need to set the reader to - # be able to reload: - result.position = r.readerIndex - else: - result.ast = decodeNode(r, result.info) - #echo "decoded: ", ident.s, "}" - -proc skipSection(r: PRodReader) = - if r.s[r.pos] == ':': - while r.s[r.pos] > '\x0A': inc(r.pos) - elif r.s[r.pos] == '(': - var c = 0 # count () pairs - inc(r.pos) - while true: - case r.s[r.pos] - of '\x0A': inc(r.line) - of '(': inc(c) - of ')': - if c == 0: - inc(r.pos) - break - elif c > 0: - dec(c) - of '\0': break # end of file - else: discard - inc(r.pos) - else: - internalError(r.config, "skipSection " & $r.line) - -proc rdWord(r: PRodReader): string = - result = "" - while r.s[r.pos] in {'A'..'Z', '_', 'a'..'z', '0'..'9'}: - add(result, r.s[r.pos]) - inc(r.pos) - -proc newStub(r: PRodReader, name: string, id: int): PSym = - new(result) - result.kind = skStub - result.id = id - result.name = r.cache.getIdent(name) - result.position = r.readerIndex - setId(id) #MessageOut(result.name.s); - if debugIds: registerID(result) - -proc processInterf(r: PRodReader, module: PSym) = - if r.interfIdx == 0: internalError(r.config, "processInterf") - r.pos = r.interfIdx - while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'): - var w = decodeStr(r.s, r.pos) - inc(r.pos) - var key = decodeVInt(r.s, r.pos) - inc(r.pos) # #10 - var s = newStub(r, w, key) - s.owner = module - strTableAdd(module.tab, s) - r.syms[s.id] = s - -proc processCompilerProcs(r: PRodReader, module: PSym) = - if r.compilerProcsIdx == 0: internalError(r.config, "processCompilerProcs") - r.pos = r.compilerProcsIdx - while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'): - var w = decodeStr(r.s, r.pos) - inc(r.pos) - var key = decodeVInt(r.s, r.pos) - inc(r.pos) # #10 - var s = r.syms.getOrDefault(key) - if s == nil: - s = newStub(r, w, key) - s.owner = module - r.syms[s.id] = s - strTableAdd(rodCompilerprocs, s) - -proc processIndex(r: PRodReader; idx: var TIndex; outf: File = nil) = - var key, val, tmp: int - inc(r.pos, 2) # skip "(\10" - inc(r.line) - while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'): - tmp = decodeVInt(r.s, r.pos) - if r.s[r.pos] == ' ': - inc(r.pos) - key = idx.lastIdxKey + tmp - val = decodeVInt(r.s, r.pos) + idx.lastIdxVal - else: - key = idx.lastIdxKey + 1 - val = tmp + idx.lastIdxVal - iiTablePut(idx.tab, key, val) - if not outf.isNil: outf.write(key, " ", val, "\n") - idx.lastIdxKey = key - idx.lastIdxVal = val - setId(key) # ensure that this id will not be used - if r.s[r.pos] == '\x0A': - inc(r.pos) - inc(r.line) - if r.s[r.pos] == ')': inc(r.pos) - -proc cmdChangeTriggersRecompilation(old, new: TCommands): bool = - if old == new: return false - # we use a 'case' statement without 'else' so that addition of a - # new command forces us to consider it here :-) - case old - of cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, - cmdCompileToJS, cmdCompileToLLVM: - if new in {cmdDoc, cmdCheck, cmdIdeTools, cmdPretty, cmdDef, - cmdInteractive}: - return false - of cmdNone, cmdDoc, cmdInterpret, cmdPretty, cmdGenDepend, cmdDump, - cmdCheck, cmdParse, cmdScan, cmdIdeTools, cmdDef, - cmdRst2html, cmdRst2tex, cmdInteractive, cmdRun, cmdJsonScript: - discard - # else: trigger recompilation: - result = true - -proc processRodFile(r: PRodReader, hash: SecureHash) = - var - w: string - d: int - var inclHash: SecureHash - while r.s[r.pos] != '\0': - var section = rdWord(r) - if r.reason != rrNone: - break # no need to process this file further - case section - of "HASH": - inc(r.pos) # skip ':' - if hash != parseSecureHash(decodeStr(r.s, r.pos)): - r.reason = rrHashChange - of "ID": - inc(r.pos) # skip ':' - r.moduleID = decodeVInt(r.s, r.pos) - setId(r.moduleID) - of "ORIGFILE": - inc(r.pos) - r.origFile = decodeStr(r.s, r.pos) - of "OPTIONS": - inc(r.pos) # skip ':' - r.options = cast[TOptions](int32(decodeVInt(r.s, r.pos))) - if r.config.options != r.options: r.reason = rrOptions - of "GOPTIONS": - inc(r.pos) # skip ':' - var dep = cast[TGlobalOptions](int32(decodeVInt(r.s, r.pos))) - if r.config.globalOptions-harmlessOptions != dep-harmlessOptions: - r.reason = rrOptions - of "CMD": - inc(r.pos) # skip ':' - var dep = cast[TCommands](int32(decodeVInt(r.s, r.pos))) - if cmdChangeTriggersRecompilation(dep, r.config.cmd): r.reason = rrOptions - of "DEFINES": - inc(r.pos) # skip ':' - d = 0 - while r.s[r.pos] > '\x0A': - w = decodeStr(r.s, r.pos) - inc(d) - if not isDefined(r.config, w): - r.reason = rrDefines #MessageOut('not defined, but should: ' + w); - if r.s[r.pos] == ' ': inc(r.pos) - if d != countDefinedSymbols(r.config.symbols): r.reason = rrDefines - of "FILES": - inc(r.pos, 2) # skip "(\10" - inc(r.line) - while r.s[r.pos] != ')': - let finalPath = decodeStr(r.s, r.pos) - #let resolvedPath = relativePath.findModule(r.origFile) - #let finalPath = if resolvedPath.len > 0: resolvedPath else: relativePath - r.files.add(fileInfoIdx(r.config, finalPath)) - inc(r.pos) # skip #10 - inc(r.line) - if r.s[r.pos] == ')': inc(r.pos) - of "INCLUDES": - inc(r.pos, 2) # skip "(\10" - inc(r.line) - while r.s[r.pos] != ')': - w = r.files[decodeVInt(r.s, r.pos)].toFullPath - inc(r.pos) # skip ' ' - inclHash = parseSecureHash(decodeStr(r.s, r.pos)) - if r.reason == rrNone: - if not existsFile(w) or (inclHash != secureHashFile(w)): - r.reason = rrInclDeps - if r.s[r.pos] == '\x0A': - inc(r.pos) - inc(r.line) - if r.s[r.pos] == ')': inc(r.pos) - of "DEPS": - inc(r.pos) # skip ':' - while r.s[r.pos] > '\x0A': - r.modDeps.add(r.files[int32(decodeVInt(r.s, r.pos))]) - if r.s[r.pos] == ' ': inc(r.pos) - of "INTERF": - r.interfIdx = r.pos + 2 - skipSection(r) - of "COMPILERPROCS": - r.compilerProcsIdx = r.pos + 2 - skipSection(r) - of "INDEX": - processIndex(r, r.index) - of "IMPORTS": - processIndex(r, r.imports) - of "CONVERTERS": - r.convertersIdx = r.pos + 1 - skipSection(r) - of "METHODS": - r.methodsIdx = r.pos + 1 - skipSection(r) - of "DATA": - r.dataIdx = r.pos + 2 # "(\10" - # We do not read the DATA section here! We read the needed objects on - # demand. And the DATA section comes last in the file, so we stop here: - break - of "INIT": - r.initIdx = r.pos + 2 # "(\10" - skipSection(r) - else: - internalError(r.config, "invalid section: '" & section & - "' at " & $r.line & " in " & r.filename) - #MsgWriteln("skipping section: " & section & - # " at " & $r.line & " in " & r.filename) - skipSection(r) - if r.s[r.pos] == '\x0A': - inc(r.pos) - inc(r.line) - - -proc startsWith(buf: cstring, token: string, pos = 0): bool = - var s = 0 - while s < token.len and buf[pos+s] == token[s]: inc s - result = s == token.len - -proc newRodReader(modfilename: string, hash: SecureHash, - readerIndex: int; cache: IdentCache; - config: ConfigRef): PRodReader = - new(result) - result.cache = cache - result.config = config - try: - result.memfile = memfiles.open(modfilename) - except OSError: - return nil - result.files = @[] - result.modDeps = @[] - result.methods = @[] - var r = result - r.reason = rrNone - r.pos = 0 - r.line = 1 - r.readerIndex = readerIndex - r.filename = modfilename - r.syms = initTable[int, PSym]() - # we terminate the file explicitly with ``\0``, so the cast to `cstring` - # is safe: - r.s = cast[cstring](r.memfile.mem) - if startsWith(r.s, "NIM:"): - initIiTable(r.index.tab) - initIiTable(r.imports.tab) # looks like a ROD file - inc(r.pos, 4) - var version = "" - while r.s[r.pos] notin {'\0', '\x0A'}: - add(version, r.s[r.pos]) - inc(r.pos) - if r.s[r.pos] == '\x0A': inc(r.pos) - if version != RodFileVersion: - # since ROD files are only for caching, no backwards compatibility is - # needed - #echo "expected version ", version, " ", RodFileVersion - result.memfile.close - result = nil - else: - result.memfile.close - result = nil - -proc rrGetType(r: PRodReader, id: int, info: TLineInfo): PType = - result = PType(idTableGet(gTypeTable, id)) - if result == nil: - # load the type: - var oldPos = r.pos - var d = iiTableGet(r.index.tab, id) - if d == InvalidKey: internalError(r.config, info, "rrGetType") - r.pos = d + r.dataIdx - result = decodeType(r, info) - r.pos = oldPos - -type - TFileModuleRec = object - filename*: string - reason*: TReasonForRecompile - rd*: PRodReader - hash*: SecureHash - hashDone*: bool - - TFileModuleMap = seq[TFileModuleRec] - -var gMods*: TFileModuleMap = @[] - -proc decodeSymSafePos(rd: PRodReader, offset: int, info: TLineInfo): PSym = - # all compiled modules - if rd.dataIdx == 0: internalError(rd.config, info, "dataIdx == 0") - var oldPos = rd.pos - rd.pos = offset + rd.dataIdx - result = decodeSym(rd, info) - rd.pos = oldPos - -proc findSomeWhere(id: int) = - for i in countup(0, high(gMods)): - var rd = gMods[i].rd - if rd != nil: - var d = iiTableGet(rd.index.tab, id) - if d != InvalidKey: - echo "found id ", id, " in ", gMods[i].filename - -proc getReader(moduleId: int): PRodReader = - # we can't index 'gMods' here as it's indexed by a *file index* which is not - # the module ID! We could introduce a mapping ID->PRodReader but I'll leave - # this for later versions if benchmarking shows the linear search causes - # problems: - for i in 0 ..< gMods.len: - result = gMods[i].rd - if result != nil and result.moduleID == moduleId: return result - return nil - -proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym = - result = r.syms.getOrDefault(id) - if result == nil: - # load the symbol: - var d = iiTableGet(r.index.tab, id) - if d == InvalidKey: - # import from other module: - var moduleID = iiTableGet(r.imports.tab, id) - if moduleID < 0: - var x = "" - encodeVInt(id, x) - internalError(r.config, info, "missing from both indexes: +" & x) - var rd = getReader(moduleID) - doAssert rd != nil - d = iiTableGet(rd.index.tab, id) - if d != InvalidKey: - result = decodeSymSafePos(rd, d, info) - else: - var x = "" - encodeVInt(id, x) - when false: findSomeWhere(id) - internalError(r.config, info, "rrGetSym: no reader found: +" & x) - else: - # own symbol: - result = decodeSymSafePos(r, d, info) - if result != nil and result.kind == skStub: rawLoadStub(result) - -proc loadInitSection*(r: PRodReader): PNode = - if r.initIdx == 0 or r.dataIdx == 0: internalError(r.config, "loadInitSection") - var oldPos = r.pos - r.pos = r.initIdx - result = newNode(nkStmtList) - while r.s[r.pos] > '\x0A' and r.s[r.pos] != ')': - var d = decodeVInt(r.s, r.pos) - inc(r.pos) # #10 - var p = r.pos - r.pos = d + r.dataIdx - addSon(result, decodeNode(r, unknownLineInfo())) - r.pos = p - r.pos = oldPos - -proc loadConverters(r: PRodReader) = - # We have to ensure that no exported converter is a stub anymore, and the - # import mechanism takes care of the rest. - if r.convertersIdx == 0 or r.dataIdx == 0: - internalError(r.config, "importConverters") - r.pos = r.convertersIdx - while r.s[r.pos] > '\x0A': - var d = decodeVInt(r.s, r.pos) - discard rrGetSym(r, d, unknownLineInfo()) - if r.s[r.pos] == ' ': inc(r.pos) - -proc loadMethods(r: PRodReader) = - if r.methodsIdx == 0 or r.dataIdx == 0: - internalError(r.config, "loadMethods") - r.pos = r.methodsIdx - while r.s[r.pos] > '\x0A': - var d = decodeVInt(r.s, r.pos) - r.methods.add(rrGetSym(r, d, unknownLineInfo())) - if r.s[r.pos] == ' ': inc(r.pos) - -proc getHash*(fileIdx: FileIndex): SecureHash = - if fileIdx.int32 <% gMods.len and gMods[fileIdx.int32].hashDone: - return gMods[fileIdx.int32].hash - - result = secureHashFile(fileIdx.toFullPath) - if fileIdx.int32 >= gMods.len: setLen(gMods, fileIdx.int32+1) - gMods[fileIdx.int32].hash = result - -template growCache*(cache, pos) = - if cache.len <= pos: cache.setLen(pos+1) - -proc checkDep(fileIdx: FileIndex; cache: IdentCache; conf: ConfigRef): TReasonForRecompile = - assert fileIdx != InvalidFileIDX - growCache gMods, fileIdx.int32 - if gMods[fileIdx.int32].reason != rrEmpty: - # reason has already been computed for this module: - return gMods[fileIdx.int32].reason - let filename = fileIdx.toFilename - var hash = getHash(fileIdx) - gMods[fileIdx.int32].reason = rrNone # we need to set it here to avoid cycles - result = rrNone - var rodfile = toGeneratedFile(conf, conf.withPackageName(filename), RodExt) - var r = newRodReader(rodfile, hash, fileIdx.int32, cache, conf) - if r == nil: - result = (if existsFile(rodfile): rrRodInvalid else: rrRodDoesNotExist) - else: - processRodFile(r, hash) - result = r.reason - if result == rrNone: - # check modules it depends on - # NOTE: we need to process the entire module graph so that no ID will - # be used twice! However, compilation speed does not suffer much from - # this, since results are cached. - var res = checkDep(systemFileIdx, cache, conf) - if res != rrNone: result = rrModDeps - for i in countup(0, high(r.modDeps)): - res = checkDep(r.modDeps[i], cache, conf) - if res != rrNone: - result = rrModDeps - # we cannot break here, because of side-effects of `checkDep` - if result != rrNone: - rawMessage(conf, hintProcessing, reasonToFrmt[result] % filename) - if result != rrNone or optForceFullMake in conf.globalOptions: - # recompilation is necessary: - if r != nil: memfiles.close(r.memfile) - r = nil - gMods[fileIdx.int32].rd = r - gMods[fileIdx.int32].reason = result # now we know better - -proc handleSymbolFile*(module: PSym; cache: IdentCache; conf: ConfigRef): PRodReader = - if conf.symbolFiles in {disabledSf, writeOnlySf, v2Sf}: - module.id = getID() - return nil - idgen.loadMaxIds(conf, conf.projectPath / conf.projectName) - let fileIdx = module.fileIdx - discard checkDep(fileIdx, cache, conf) - #if gMods[fileIdx.int32].reason == rrEmpty: internalError("handleSymbolFile") - result = gMods[fileIdx.int32].rd - if result != nil: - module.id = result.moduleID - result.syms[module.id] = module - processInterf(result, module) - processCompilerProcs(result, module) - loadConverters(result) - loadMethods(result) - else: - module.id = getID() - -proc rawLoadStub(s: PSym) = - assert s.kind == skStub - #if s.kind != skStub: internalError("loadStub") - var rd = gMods[s.position].rd - var theId = s.id # used for later check - var d = iiTableGet(rd.index.tab, s.id) - #if d == InvalidKey: internalError("loadStub: invalid key") - var rs = decodeSymSafePos(rd, d, unknownLineInfo()) - when false: - if rs != s: - #echo "rs: ", toHex(cast[int](rs.position), int.sizeof * 2), - # "\ns: ", toHex(cast[int](s.position), int.sizeof * 2) - internalError(rs.info, "loadStub: wrong symbol") - elif rs.id != theId: - internalError(rs.info, "loadStub: wrong ID") - -proc loadStub*(s: PSym) = - ## loads the stub symbol `s`. - - # deactivate the GC here because we do a deep recursion and generate no - # garbage when restoring parts of the object graph anyway. - # Since we die with internal errors if this fails, no try-finally is - # necessary. - GC_disable() - rawLoadStub(s) - GC_enable() - -proc getBody*(s: PSym): PNode = - ## retrieves the AST's body of `s`. If `s` has been loaded from a rod-file - ## it may perform an expensive reload operation. Otherwise it's a simple - ## accessor. - assert s.kind in routineKinds - # prevent crashes due to incorrect macro transformations (bug #2377) - if s.ast.isNil or bodyPos >= s.ast.len: return ast.emptyNode - result = s.ast.sons[bodyPos] - if result == nil: - assert s.offset != 0 - var r = gMods[s.position].rd - var oldPos = r.pos - r.pos = s.offset - result = decodeNode(r, s.info) - r.pos = oldPos - s.ast.sons[bodyPos] = result - s.offset = 0 - -initIdTable(gTypeTable) -initStrTable(rodCompilerprocs) - -# viewer: -proc writeNode(f: File; n: PNode) = - f.write("(") - if n != nil: - f.write($n.kind) - if n.typ != nil: - f.write('^') - f.write(n.typ.id) - case n.kind - of nkCharLit..nkUInt64Lit: - if n.intVal != 0: - f.write('!') - f.write(n.intVal) - of nkFloatLit..nkFloat64Lit: - if n.floatVal != 0.0: - f.write('!') - f.write($n.floatVal) - of nkStrLit..nkTripleStrLit: - if n.strVal != "": - f.write('!') - f.write(n.strVal.escape) - of nkIdent: - f.write('!') - f.write(n.ident.s) - of nkSym: - f.write('!') - f.write(n.sym.id) - else: - for i in countup(0, sonsLen(n) - 1): - writeNode(f, n.sons[i]) - f.write(")") - -proc writeSym(f: File; s: PSym) = - if s == nil: - f.write("{}\n") - return - f.write("{") - f.write($s.kind) - f.write('+') - f.write(s.id) - f.write('&') - f.write(s.name.s) - if s.typ != nil: - f.write('^') - f.write(s.typ.id) - if s.owner != nil: - f.write('*') - f.write(s.owner.id) - if s.flags != {}: - f.write('$') - f.write($s.flags) - if s.magic != mNone: - f.write('@') - f.write($s.magic) - f.write('!') - f.write($s.options) - if s.position != 0: - f.write('%') - f.write($s.position) - if s.offset != -1: - f.write('`') - f.write($s.offset) - if s.constraint != nil: - f.write('#') - f.writeNode(s.constraint) - if s.ast != nil: - f.writeNode(s.ast) - f.write("}\n") - -proc writeType(f: File; t: PType) = - if t == nil: - f.write("[]\n") - return - f.write('[') - f.write($t.kind) - f.write('+') - f.write($t.id) - if t.n != nil: - f.writeNode(t.n) - if t.flags != {}: - f.write('$') - f.write($t.flags) - if t.callConv != low(t.callConv): - f.write('?') - f.write($t.callConv) - if t.owner != nil: - f.write('*') - f.write($t.owner.id) - if t.sym != nil: - f.write('&') - f.write(t.sym.id) - if t.size != -1: - f.write('/') - f.write($t.size) - if t.align != 2: - f.write('=') - f.write($t.align) - for i in countup(0, sonsLen(t) - 1): - if t.sons[i] == nil: - f.write("^()") - else: - f.write('^') - f.write($t.sons[i].id) - f.write("]\n") - -proc viewFile(rodfile: string) = - let conf = newConfigRef() - var r = newRodReader(rodfile, secureHash(""), 0, newIdentCache(), conf) - if r == nil: - rawMessage(conf, errGenerated, "cannot open file (or maybe wrong version):" & - rodfile) - return - r.inViewMode = true - var outf = system.open(rodfile.changeFileExt(".rod.txt"), fmWrite) - while r.s[r.pos] != '\0': - let section = rdWord(r) - case section - of "HASH": - inc(r.pos) # skip ':' - outf.writeLine("HASH:", $decodeVInt(r.s, r.pos)) - of "ID": - inc(r.pos) # skip ':' - r.moduleID = decodeVInt(r.s, r.pos) - setId(r.moduleID) - outf.writeLine("ID:", $r.moduleID) - of "ORIGFILE": - inc(r.pos) - r.origFile = decodeStr(r.s, r.pos) - outf.writeLine("ORIGFILE:", r.origFile) - of "OPTIONS": - inc(r.pos) # skip ':' - r.options = cast[TOptions](int32(decodeVInt(r.s, r.pos))) - outf.writeLine("OPTIONS:", $r.options) - of "GOPTIONS": - inc(r.pos) # skip ':' - let dep = cast[TGlobalOptions](int32(decodeVInt(r.s, r.pos))) - outf.writeLine("GOPTIONS:", $dep) - of "CMD": - inc(r.pos) # skip ':' - let dep = cast[TCommands](int32(decodeVInt(r.s, r.pos))) - outf.writeLine("CMD:", $dep) - of "DEFINES": - inc(r.pos) # skip ':' - var d = 0 - outf.write("DEFINES:") - while r.s[r.pos] > '\x0A': - let w = decodeStr(r.s, r.pos) - inc(d) - outf.write(" ", w) - if r.s[r.pos] == ' ': inc(r.pos) - outf.write("\n") - of "FILES": - inc(r.pos, 2) # skip "(\10" - inc(r.line) - outf.write("FILES(\n") - while r.s[r.pos] != ')': - let relativePath = decodeStr(r.s, r.pos) - let resolvedPath = findModule(conf, relativePath, r.origFile) - let finalPath = if resolvedPath.len > 0: resolvedPath else: relativePath - r.files.add(fileInfoIdx(conf, finalPath)) - inc(r.pos) # skip #10 - inc(r.line) - outf.writeLine finalPath - if r.s[r.pos] == ')': inc(r.pos) - outf.write(")\n") - of "INCLUDES": - inc(r.pos, 2) # skip "(\10" - inc(r.line) - outf.write("INCLUDES(\n") - while r.s[r.pos] != ')': - let w = r.files[decodeVInt(r.s, r.pos)] - inc(r.pos) # skip ' ' - let inclHash = decodeVInt(r.s, r.pos) - if r.s[r.pos] == '\x0A': - inc(r.pos) - inc(r.line) - outf.write(w.int32, " ", inclHash, "\n") - if r.s[r.pos] == ')': inc(r.pos) - outf.write(")\n") - of "DEPS": - inc(r.pos) # skip ':' - outf.write("DEPS:") - while r.s[r.pos] > '\x0A': - let v = int32(decodeVInt(r.s, r.pos)) - r.modDeps.add(r.files[v]) - if r.s[r.pos] == ' ': inc(r.pos) - outf.write(" ", r.files[v].int32) - outf.write("\n") - of "INTERF", "COMPILERPROCS": - inc r.pos, 2 - if section == "INTERF": r.interfIdx = r.pos - else: r.compilerProcsIdx = r.pos - outf.write(section, "(\n") - while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'): - let w = decodeStr(r.s, r.pos) - inc(r.pos) - let key = decodeVInt(r.s, r.pos) - inc(r.pos) # #10 - outf.write(w, " ", key, "\n") - if r.s[r.pos] == ')': inc r.pos - outf.write(")\n") - of "INDEX": - outf.write(section, "(\n") - processIndex(r, r.index, outf) - outf.write(")\n") - of "IMPORTS": - outf.write(section, "(\n") - processIndex(r, r.imports, outf) - outf.write(")\n") - of "CONVERTERS", "METHODS": - inc r.pos - if section == "METHODS": r.methodsIdx = r.pos - else: r.convertersIdx = r.pos - outf.write(section, ":") - while r.s[r.pos] > '\x0A': - let d = decodeVInt(r.s, r.pos) - outf.write(" ", $d) - if r.s[r.pos] == ' ': inc(r.pos) - outf.write("\n") - of "DATA": - inc(r.pos, 2) - r.dataIdx = r.pos - outf.write("DATA(\n") - while r.s[r.pos] != ')': - if r.s[r.pos] == '(': - outf.writeNode decodeNode(r, unknownLineInfo()) - outf.write("\n") - elif r.s[r.pos] == '[': - outf.writeType decodeType(r, unknownLineInfo()) - else: - outf.writeSym decodeSym(r, unknownLineInfo()) - if r.s[r.pos] == '\x0A': - inc(r.pos) - inc(r.line) - if r.s[r.pos] == ')': inc r.pos - outf.write(")\n") - of "INIT": - outf.write("INIT(\n") - inc r.pos, 2 - r.initIdx = r.pos - while r.s[r.pos] > '\x0A' and r.s[r.pos] != ')': - let d = decodeVInt(r.s, r.pos) - inc(r.pos) # #10 - #let p = r.pos - #r.pos = d + r.dataIdx - #outf.writeNode decodeNode(r, UnknownLineInfo()) - #outf.write("\n") - #r.pos = p - if r.s[r.pos] == ')': inc r.pos - outf.write("<not supported by viewer>)\n") - else: - internalError(r.config, "invalid section: '" & section & - "' at " & $r.line & " in " & r.filename) - skipSection(r) - if r.s[r.pos] == '\x0A': - inc(r.pos) - inc(r.line) - outf.close - -when isMainModule: - viewFile(paramStr(1).addFileExt(RodExt)) diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim deleted file mode 100644 index 4686baf2b..000000000 --- a/compiler/rodwrite.nim +++ /dev/null @@ -1,659 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2012 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -# This module is responsible for writing of rod files. Note that writing of -# rod files is a pass, reading of rod files is not! This is why reading and -# writing of rod files is split into two different modules. - -import - intsets, os, options, strutils, nversion, ast, astalgo, msgs, platform, - condsyms, ropes, idents, std / sha1, rodread, passes, idgen, - rodutils, modulepaths - -from modulegraphs import ModuleGraph - -type - TRodWriter = object of TPassContext - module: PSym - hash: SecureHash - options: TOptions - defines: string - inclDeps: string - modDeps: string - interf: string - compilerProcs: string - index, imports: TIndex - converters, methods: string - init: string - data: string - sstack: TSymSeq # a stack of symbols to process - tstack: TTypeSeq # a stack of types to process - files: TStringSeq - origFile: string - cache: IdentCache - config: ConfigRef - - PRodWriter = ref TRodWriter - -proc getDefines(conf: ConfigRef): string = - result = "" - for d in definedSymbolNames(conf.symbols): - if result.len != 0: add(result, " ") - add(result, d) - -proc fileIdx(w: PRodWriter, filename: string): int = - for i in countup(0, high(w.files)): - if w.files[i] == filename: - return i - result = len(w.files) - setLen(w.files, result + 1) - w.files[result] = filename - -template filename*(w: PRodWriter): string = - toFilename(FileIndex w.module.position) - -proc newRodWriter(hash: SecureHash, module: PSym; cache: IdentCache; - config: ConfigRef): PRodWriter = - new(result) - result.config = config - result.sstack = @[] - result.tstack = @[] - initIiTable(result.index.tab) - initIiTable(result.imports.tab) - result.index.r = "" - result.imports.r = "" - result.hash = hash - result.module = module - result.defines = getDefines(config) - result.options = config.options - result.files = @[] - result.inclDeps = "" - result.modDeps = "" - result.interf = newStringOfCap(2_000) - result.compilerProcs = "" - result.converters = "" - result.methods = "" - result.init = "" - result.origFile = module.info.toFullPath - result.data = newStringOfCap(12_000) - result.cache = cache - -proc addModDep(w: PRodWriter, dep: string; info: TLineInfo) = - if w.modDeps.len != 0: add(w.modDeps, ' ') - let resolved = findModule(w.config, dep, info.toFullPath) - encodeVInt(fileIdx(w, resolved), w.modDeps) - -const - rodNL = "\x0A" - -proc addInclDep(w: PRodWriter, dep: string; info: TLineInfo) = - let resolved = findModule(w.config, dep, info.toFullPath) - encodeVInt(fileIdx(w, resolved), w.inclDeps) - add(w.inclDeps, " ") - encodeStr($secureHashFile(resolved), w.inclDeps) - add(w.inclDeps, rodNL) - -proc pushType(w: PRodWriter, t: PType) = - # check so that the stack does not grow too large: - if iiTableGet(w.index.tab, t.id) == InvalidKey: - w.tstack.add(t) - -proc pushSym(w: PRodWriter, s: PSym) = - # check so that the stack does not grow too large: - if iiTableGet(w.index.tab, s.id) == InvalidKey: - when false: - if s.kind == skMethod: - echo "encoding ", s.id, " ", s.name.s - writeStackTrace() - w.sstack.add(s) - -proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode, - result: var string) = - if n == nil: - # nil nodes have to be stored too: - result.add("()") - return - result.add('(') - encodeVInt(ord(n.kind), result) - # we do not write comments for now - # Line information takes easily 20% or more of the filesize! Therefore we - # omit line information if it is the same as the father's line information: - if fInfo.fileIndex != n.info.fileIndex: - result.add('?') - encodeVInt(n.info.col, result) - result.add(',') - encodeVInt(int n.info.line, result) - result.add(',') - encodeVInt(fileIdx(w, toFullPath(n.info)), result) - elif fInfo.line != n.info.line: - result.add('?') - encodeVInt(n.info.col, result) - result.add(',') - encodeVInt(int n.info.line, result) - elif fInfo.col != n.info.col: - result.add('?') - encodeVInt(n.info.col, result) - # No need to output the file index, as this is the serialization of one - # file. - var f = n.flags * PersistentNodeFlags - if f != {}: - result.add('$') - encodeVInt(cast[int32](f), result) - if n.typ != nil: - result.add('^') - encodeVInt(n.typ.id, result) - pushType(w, n.typ) - case n.kind - of nkCharLit..nkUInt64Lit: - if n.intVal != 0: - result.add('!') - encodeVBiggestInt(n.intVal, result) - of nkFloatLit..nkFloat64Lit: - if n.floatVal != 0.0: - result.add('!') - encodeStr($n.floatVal, result) - of nkStrLit..nkTripleStrLit: - if n.strVal != "": - result.add('!') - encodeStr(n.strVal, result) - of nkIdent: - result.add('!') - encodeStr(n.ident.s, result) - of nkSym: - result.add('!') - encodeVInt(n.sym.id, result) - pushSym(w, n.sym) - else: - for i in countup(0, sonsLen(n) - 1): - encodeNode(w, n.info, n.sons[i], result) - add(result, ')') - -proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) = - var oldLen = result.len - result.add('<') - if loc.k != low(loc.k): encodeVInt(ord(loc.k), result) - if loc.storage != low(loc.storage): - add(result, '*') - encodeVInt(ord(loc.storage), result) - if loc.flags != {}: - add(result, '$') - encodeVInt(cast[int32](loc.flags), result) - if loc.lode != nil: - add(result, '^') - encodeNode(w, unknownLineInfo(), loc.lode, result) - #encodeVInt(cast[int32](loc.t.id), result) - #pushType(w, loc.t) - if loc.r != nil: - add(result, '!') - encodeStr($loc.r, result) - if oldLen + 1 == result.len: - # no data was necessary, so remove the '<' again: - setLen(result, oldLen) - else: - add(result, '>') - -proc encodeType(w: PRodWriter, t: PType, result: var string) = - if t == nil: - # nil nodes have to be stored too: - result.add("[]") - return - # we need no surrounding [] here because the type is in a line of its own - if t.kind == tyForward: internalError(w.config, "encodeType: tyForward") - # for the new rodfile viewer we use a preceding [ so that the data section - # can easily be disambiguated: - add(result, '[') - encodeVInt(ord(t.kind), result) - add(result, '+') - encodeVInt(t.id, result) - if t.n != nil: - encodeNode(w, unknownLineInfo(), t.n, result) - if t.flags != {}: - add(result, '$') - encodeVInt(cast[int32](t.flags), result) - if t.callConv != low(t.callConv): - add(result, '?') - encodeVInt(ord(t.callConv), result) - if t.owner != nil: - add(result, '*') - encodeVInt(t.owner.id, result) - pushSym(w, t.owner) - if t.sym != nil: - add(result, '&') - encodeVInt(t.sym.id, result) - pushSym(w, t.sym) - if t.size != - 1: - add(result, '/') - encodeVBiggestInt(t.size, result) - if t.align != 2: - add(result, '=') - encodeVInt(t.align, result) - if t.lockLevel.ord != UnspecifiedLockLevel.ord: - add(result, '\14') - encodeVInt(t.lockLevel.int16, result) - if t.destructor != nil and t.destructor.id != 0: - add(result, '\15') - encodeVInt(t.destructor.id, result) - pushSym(w, t.destructor) - if t.deepCopy != nil: - add(result, '\16') - encodeVInt(t.deepcopy.id, result) - pushSym(w, t.deepcopy) - if t.assignment != nil: - add(result, '\17') - encodeVInt(t.assignment.id, result) - pushSym(w, t.assignment) - if t.sink != nil: - add(result, '\18') - encodeVInt(t.sink.id, result) - pushSym(w, t.sink) - for i, s in items(t.methods): - add(result, '\19') - encodeVInt(i, result) - add(result, '\20') - encodeVInt(s.id, result) - pushSym(w, s) - encodeLoc(w, t.loc, result) - for i in countup(0, sonsLen(t) - 1): - if t.sons[i] == nil: - add(result, "^()") - else: - add(result, '^') - encodeVInt(t.sons[i].id, result) - pushType(w, t.sons[i]) - -proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) = - add(result, '|') - encodeVInt(ord(lib.kind), result) - add(result, '|') - encodeStr($lib.name, result) - add(result, '|') - encodeNode(w, info, lib.path, result) - -proc encodeInstantiations(w: PRodWriter; s: seq[PInstantiation]; - result: var string) = - for t in s: - result.add('\15') - encodeVInt(t.sym.id, result) - pushSym(w, t.sym) - for tt in t.concreteTypes: - result.add('\17') - encodeVInt(tt.id, result) - pushType(w, tt) - result.add('\20') - encodeVInt(t.compilesId, result) - -proc encodeSym(w: PRodWriter, s: PSym, result: var string) = - if s == nil: - # nil nodes have to be stored too: - result.add("{}") - return - # we need no surrounding {} here because the symbol is in a line of its own - encodeVInt(ord(s.kind), result) - result.add('+') - encodeVInt(s.id, result) - result.add('&') - encodeStr(s.name.s, result) - if s.typ != nil: - result.add('^') - encodeVInt(s.typ.id, result) - pushType(w, s.typ) - result.add('?') - if s.info.col != -1'i16: encodeVInt(s.info.col, result) - result.add(',') - if s.info.line != 0'u16: encodeVInt(int s.info.line, result) - result.add(',') - encodeVInt(fileIdx(w, toFullPath(s.info)), result) - if s.owner != nil: - result.add('*') - encodeVInt(s.owner.id, result) - pushSym(w, s.owner) - if s.flags != {}: - result.add('$') - encodeVInt(cast[int32](s.flags), result) - if s.magic != mNone: - result.add('@') - encodeVInt(ord(s.magic), result) - if s.options != w.options: - result.add('!') - encodeVInt(cast[int32](s.options), result) - if s.position != 0: - result.add('%') - encodeVInt(s.position, result) - if s.offset != - 1: - result.add('`') - encodeVInt(s.offset, result) - encodeLoc(w, s.loc, result) - if s.annex != nil: encodeLib(w, s.annex, s.info, result) - if s.constraint != nil: - add(result, '#') - encodeNode(w, unknownLineInfo(), s.constraint, result) - case s.kind - of skType, skGenericParam: - for t in s.typeInstCache: - result.add('\14') - encodeVInt(t.id, result) - pushType(w, t) - of routineKinds: - encodeInstantiations(w, s.procInstCache, result) - if s.gcUnsafetyReason != nil: - result.add('\16') - encodeVInt(s.gcUnsafetyReason.id, result) - pushSym(w, s.gcUnsafetyReason) - of skModule, skPackage: - encodeInstantiations(w, s.usedGenerics, result) - # we don't serialize: - #tab*: TStrTable # interface table for modules - of skLet, skVar, skField, skForVar: - if s.guard != nil: - result.add('\18') - encodeVInt(s.guard.id, result) - pushSym(w, s.guard) - if s.bitsize != 0: - result.add('\19') - encodeVInt(s.bitsize, result) - else: discard - # lazy loading will soon reload the ast lazily, so the ast needs to be - # the last entry of a symbol: - if s.ast != nil: - # we used to attempt to save space here by only storing a dummy AST if - # it is not necessary, but Nim's heavy compile-time evaluation features - # make that unfeasible nowadays: - encodeNode(w, s.info, s.ast, result) - -proc addToIndex(w: var TIndex, key, val: int) = - if key - w.lastIdxKey == 1: - # we do not store a key-diff of 1 to safe space - encodeVInt(val - w.lastIdxVal, w.r) - else: - encodeVInt(key - w.lastIdxKey, w.r) - add(w.r, ' ') - encodeVInt(val - w.lastIdxVal, w.r) - add(w.r, rodNL) - w.lastIdxKey = key - w.lastIdxVal = val - iiTablePut(w.tab, key, val) - -const debugWrittenIds = false - -when debugWrittenIds: - var debugWritten = initIntSet() - -proc symStack(w: PRodWriter): int = - var i = 0 - while i < len(w.sstack): - var s = w.sstack[i] - if sfForward in s.flags: - w.sstack[result] = s - inc result - elif iiTableGet(w.index.tab, s.id) == InvalidKey: - var m = getModule(s) - #if m == nil and s.kind != skPackage and sfGenSym notin s.flags: - # internalError("symStack: module nil: " & s.name.s & " " & $s.kind & " ID " & $s.id) - if m == nil or s.kind == skPackage or {sfFromGeneric, sfGenSym} * s.flags != {} or m.id == w.module.id: - # put definition in here - var L = w.data.len - addToIndex(w.index, s.id, L) - when debugWrittenIds: incl(debugWritten, s.id) - encodeSym(w, s, w.data) - add(w.data, rodNL) - # put into interface section if appropriate: - if {sfExported, sfFromGeneric} * s.flags == {sfExported} and - s.kind in ExportableSymKinds: - encodeStr(s.name.s, w.interf) - add(w.interf, ' ') - encodeVInt(s.id, w.interf) - add(w.interf, rodNL) - if sfCompilerProc in s.flags: - encodeStr(s.name.s, w.compilerProcs) - add(w.compilerProcs, ' ') - encodeVInt(s.id, w.compilerProcs) - add(w.compilerProcs, rodNL) - if s.kind == skConverter or (s.ast != nil and hasPattern(s)): - if w.converters.len != 0: add(w.converters, ' ') - encodeVInt(s.id, w.converters) - if s.kind == skMethod and sfDispatcher notin s.flags: - if w.methods.len != 0: add(w.methods, ' ') - encodeVInt(s.id, w.methods) - elif iiTableGet(w.imports.tab, s.id) == InvalidKey: - addToIndex(w.imports, s.id, m.id) - when debugWrittenIds: - if not contains(debugWritten, s.id): - echo(w.filename) - debug(s) - debug(s.owner) - debug(m) - internalError("Symbol referred to but never written") - inc(i) - setLen(w.sstack, result) - -proc typeStack(w: PRodWriter): int = - var i = 0 - while i < len(w.tstack): - var t = w.tstack[i] - if t.kind == tyForward: - w.tstack[result] = t - inc result - elif iiTableGet(w.index.tab, t.id) == InvalidKey: - var L = w.data.len - addToIndex(w.index, t.id, L) - encodeType(w, t, w.data) - add(w.data, rodNL) - inc(i) - setLen(w.tstack, result) - -proc processStacks(w: PRodWriter, finalPass: bool) = - var oldS = 0 - var oldT = 0 - while true: - var slen = symStack(w) - var tlen = typeStack(w) - if slen == oldS and tlen == oldT: break - oldS = slen - oldT = tlen - if finalPass and (oldS != 0 or oldT != 0): - internalError(w.config, "could not serialize some forwarded symbols/types") - -proc rawAddInterfaceSym(w: PRodWriter, s: PSym) = - pushSym(w, s) - processStacks(w, false) - -proc addInterfaceSym(w: PRodWriter, s: PSym) = - if w == nil: return - if s.kind in ExportableSymKinds and - {sfExported, sfCompilerProc} * s.flags != {}: - rawAddInterfaceSym(w, s) - -proc addStmt(w: PRodWriter, n: PNode) = - encodeVInt(w.data.len, w.init) - add(w.init, rodNL) - encodeNode(w, unknownLineInfo(), n, w.data) - add(w.data, rodNL) - processStacks(w, false) - -proc writeRod(w: PRodWriter) = - processStacks(w, true) - var f: File - if not open(f, completeGeneratedFilePath(w.config, changeFileExt( - withPackageName(w.config, w.filename), RodExt)), - fmWrite): - #echo "couldn't write rod file for: ", w.filename - return - # write header: - f.write("NIM:") - f.write(RodFileVersion) - f.write(rodNL) - var id = "ID:" - encodeVInt(w.module.id, id) - f.write(id) - f.write(rodNL) - - var orig = "ORIGFILE:" - encodeStr(w.origFile, orig) - f.write(orig) - f.write(rodNL) - - var hash = "HASH:" - encodeStr($w.hash, hash) - f.write(hash) - f.write(rodNL) - - var options = "OPTIONS:" - encodeVInt(cast[int32](w.options), options) - f.write(options) - f.write(rodNL) - - var goptions = "GOPTIONS:" - encodeVInt(cast[int32](w.config.globalOptions), goptions) - f.write(goptions) - f.write(rodNL) - - var cmd = "CMD:" - encodeVInt(cast[int32](w.config.cmd), cmd) - f.write(cmd) - f.write(rodNL) - - f.write("DEFINES:") - f.write(w.defines) - f.write(rodNL) - - var files = "FILES(" & rodNL - for i in countup(0, high(w.files)): - encodeStr(w.files[i], files) - files.add(rodNL) - f.write(files) - f.write(')' & rodNL) - - f.write("INCLUDES(" & rodNL) - f.write(w.inclDeps) - f.write(')' & rodNL) - - f.write("DEPS:") - f.write(w.modDeps) - f.write(rodNL) - - f.write("INTERF(" & rodNL) - f.write(w.interf) - f.write(')' & rodNL) - - f.write("COMPILERPROCS(" & rodNL) - f.write(w.compilerProcs) - f.write(')' & rodNL) - - f.write("INDEX(" & rodNL) - f.write(w.index.r) - f.write(')' & rodNL) - - f.write("IMPORTS(" & rodNL) - f.write(w.imports.r) - f.write(')' & rodNL) - - f.write("CONVERTERS:") - f.write(w.converters) - f.write(rodNL) - - f.write("METHODS:") - f.write(w.methods) - f.write(rodNL) - - f.write("INIT(" & rodNL) - f.write(w.init) - f.write(')' & rodNL) - - f.write("DATA(" & rodNL) - f.write(w.data) - f.write(')' & rodNL) - # write trailing zero which is necessary because we use memory mapped files - # for reading: - f.write("\0") - f.close() - - #echo "interf: ", w.interf.len - #echo "index: ", w.index.r.len - #echo "init: ", w.init.len - #echo "data: ", w.data.len - -proc process(c: PPassContext, n: PNode): PNode = - result = n - if c == nil: return - var w = PRodWriter(c) - case n.kind - of nkStmtList: - for i in countup(0, sonsLen(n) - 1): discard process(c, n.sons[i]) - #var s = n.sons[namePos].sym - #addInterfaceSym(w, s) - of nkProcDef, nkFuncDef, nkIteratorDef, nkConverterDef, - nkTemplateDef, nkMacroDef: - let s = n.sons[namePos].sym - if s == nil: internalError(w.config, n.info, "rodwrite.process") - if n.sons[bodyPos] == nil: - internalError(w.config, n.info, "rodwrite.process: body is nil") - if n.sons[bodyPos].kind != nkEmpty or s.magic != mNone or - sfForward notin s.flags: - addInterfaceSym(w, s) - of nkMethodDef: - let s = n.sons[namePos].sym - if s == nil: internalError(w.config, n.info, "rodwrite.process") - if n.sons[bodyPos] == nil: - internalError(w.config, n.info, "rodwrite.process: body is nil") - if n.sons[bodyPos].kind != nkEmpty or s.magic != mNone or - sfForward notin s.flags: - pushSym(w, s) - processStacks(w, false) - - of nkVarSection, nkLetSection, nkConstSection: - for i in countup(0, sonsLen(n) - 1): - var a = n.sons[i] - if a.kind == nkCommentStmt: continue - addInterfaceSym(w, a.sons[0].sym) - of nkTypeSection: - for i in countup(0, sonsLen(n) - 1): - var a = n.sons[i] - if a.kind == nkCommentStmt: continue - if a.sons[0].kind != nkSym: internalError(w.config, a.info, "rodwrite.process") - var s = a.sons[0].sym - addInterfaceSym(w, s) - # this takes care of enum fields too - # Note: The check for ``s.typ.kind = tyEnum`` is wrong for enum - # type aliasing! Otherwise the same enum symbol would be included - # several times! - # - # if (a.sons[2] <> nil) and (a.sons[2].kind = nkEnumTy) then begin - # a := s.typ.n; - # for j := 0 to sonsLen(a)-1 do - # addInterfaceSym(w, a.sons[j].sym); - # end - of nkImportStmt: - for i in countup(0, sonsLen(n) - 1): - addModDep(w, getModuleName(w.config, n.sons[i]), n.info) - addStmt(w, n) - of nkFromStmt, nkImportExceptStmt: - addModDep(w, getModuleName(w.config, n.sons[0]), n.info) - addStmt(w, n) - of nkIncludeStmt: - for i in countup(0, sonsLen(n) - 1): - addInclDep(w, getModuleName(w.config, n.sons[i]), n.info) - of nkPragma: - addStmt(w, n) - else: - discard - -proc myOpen(g: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = - if module.id < 0: internalError(g.config, "rodwrite: module ID not set") - var w = newRodWriter(rodread.getHash FileIndex module.position, module, cache, g.config) - rawAddInterfaceSym(w, module) - result = w - -proc myClose(graph: ModuleGraph; c: PPassContext, n: PNode): PNode = - result = process(c, n) - var w = PRodWriter(c) - writeRod(w) - idgen.saveMaxIds(graph.config, graph.config.projectPath / graph.config.projectName) - -const rodwritePass* = makePass(open = myOpen, close = myClose, process = process) - diff --git a/compiler/ropes.nim b/compiler/ropes.nim index 05d5e840c..973f16916 100644 --- a/compiler/ropes.nim +++ b/compiler/ropes.nim @@ -56,7 +56,7 @@ # To cache them they are inserted in a `cache` array. import - platform, hashes + hashes type FormatStr* = string # later we may change it to CString for better @@ -70,17 +70,6 @@ type length*: int data*: string # != nil if a leaf - RopeSeq* = seq[Rope] - - RopesError* = enum - rCannotOpenFile - rInvalidFormatStr - -# implementation - -var errorHandler*: proc(err: RopesError, msg: string, useWarning = false) - # avoid dependency on msgs.nim - proc len*(a: Rope): int = ## the rope's length if a == nil: result = 0 @@ -102,7 +91,7 @@ proc freezeMutableRope*(r: Rope) {.inline.} = r.length = r.data.len var - cache: array[0..2048*2 - 1, Rope] + cache: array[0..2048*2 - 1, Rope] # XXX Global here! proc resetRopeCache* = for i in low(cache)..high(cache): @@ -204,13 +193,14 @@ proc writeRope*(f: File, r: Rope) = ## writes a rope to a file. for s in leaves(r): write(f, s) -proc writeRope*(head: Rope, filename: string, useWarning = false) = +proc writeRope*(head: Rope, filename: string): bool = var f: File if open(f, filename, fmWrite): if head != nil: writeRope(f, head) close(f) + result = true else: - errorHandler(rCannotOpenFile, filename, useWarning) + result = false proc `$`*(r: Rope): string = ## converts a rope back to a string. @@ -225,11 +215,6 @@ proc ropeConcat*(a: varargs[Rope]): Rope = proc prepend*(a: var Rope, b: Rope) = a = b & a proc prepend*(a: var Rope, b: string) = a = b & a -var - rnl* = tnl.newRope - softRnl* = tnl.newRope - noRnl* = "".newRope - proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope = var i = 0 var length = len(frmt) @@ -254,7 +239,7 @@ proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope = if i >= frmt.len or frmt[i] notin {'0'..'9'}: break num = j if j > high(args) + 1: - errorHandler(rInvalidFormatStr, $(j)) + doAssert false, "invalid format string: " & frmt else: add(result, args[j-1]) of '{': @@ -265,20 +250,21 @@ proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope = inc(i) num = j if frmt[i] == '}': inc(i) - else: errorHandler(rInvalidFormatStr, $(frmt[i])) + else: + doAssert false, "invalid format string: " & frmt if j > high(args) + 1: - errorHandler(rInvalidFormatStr, $(j)) + doAssert false, "invalid format string: " & frmt else: add(result, args[j-1]) of 'n': - add(result, softRnl) + add(result, "\n") inc(i) of 'N': - add(result, rnl) + add(result, "\n") inc(i) else: - errorHandler(rInvalidFormatStr, $(frmt[i])) + doAssert false, "invalid format string: " & frmt var start = i while i < length: if frmt[i] != '$': inc(i) @@ -350,7 +336,6 @@ proc equalsFile*(r: Rope, filename: string): bool = proc writeRopeIfNotEqual*(r: Rope, filename: string): bool = # returns true if overwritten if not equalsFile(r, filename): - writeRope(r, filename) - result = true + result = writeRope(r, filename) else: result = false diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim index 30e5e4803..ae7e030b8 100644 --- a/compiler/scriptconfig.nim +++ b/compiler/scriptconfig.nim @@ -13,7 +13,7 @@ import ast, modules, idents, passes, passaux, condsyms, options, nimconf, sem, semdata, llstream, vm, vmdef, commands, msgs, - os, times, osproc, wordrecg, strtabs, modulegraphs, configuration + os, times, osproc, wordrecg, strtabs, modulegraphs, lineinfos # we support 'cmpIgnoreStyle' natively for efficiency: from strutils import cmpIgnoreStyle, contains @@ -152,28 +152,27 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string; proc runNimScript*(cache: IdentCache; scriptName: string; freshDefines=true; conf: ConfigRef) = rawMessage(conf, hintConf, scriptName) - passes.gIncludeFile = includeModule - passes.gImportModule = importModule - let graph = newModuleGraph(conf) + + let graph = newModuleGraph(cache, conf) + connectCallbacks(graph) if freshDefines: initDefines(conf.symbols) defineSymbol(conf.symbols, "nimscript") defineSymbol(conf.symbols, "nimconfig") - registerPass(semPass) - registerPass(evalPass) + registerPass(graph, semPass) + registerPass(graph, evalPass) conf.searchPaths.add(conf.libpath) var m = graph.makeModule(scriptName) incl(m.flags, sfMainModule) - vm.globalCtx = setupVM(m, cache, scriptName, graph) + graph.vm = setupVM(m, cache, scriptName, graph) - graph.compileSystemModule(cache) - discard graph.processModule(m, llStreamOpen(scriptName, fmRead), nil, cache) + graph.compileSystemModule() + discard graph.processModule(m, llStreamOpen(scriptName, fmRead)) # ensure we load 'system.nim' again for the real non-config stuff! resetSystemArtifacts(graph) - vm.globalCtx = nil # do not remove the defined symbols #initDefines() undefSymbol(conf.symbols, "nimscript") diff --git a/compiler/sem.nim b/compiler/sem.nim index d56355f14..299286545 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -13,10 +13,10 @@ import ast, strutils, hashes, options, lexer, astalgo, trees, treetab, wordrecg, ropes, msgs, os, condsyms, idents, renderer, types, platform, math, magicsys, parser, nversion, nimsets, semfold, modulepaths, importer, - procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch, + procfind, lookups, pragmas, passes, semdata, semtypinst, sigmatch, intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting, - evaltempl, patterns, parampatterns, sempass2, nimfix.pretty, semmacrosanity, - semparallel, lowerings, pluginsupport, plugins.active, rod, configuration + evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity, + semparallel, lowerings, pluginsupport, plugins.active, rod, lineinfos from modulegraphs import ModuleGraph @@ -48,15 +48,18 @@ proc semQuoteAst(c: PContext, n: PNode): PNode proc finishMethod(c: PContext, s: PSym) proc evalAtCompileTime(c: PContext, n: PNode): PNode proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode - +proc semStaticExpr(c: PContext, n: PNode): PNode +proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType +proc semTypeOf(c: PContext; n: PNode): PNode +proc hasUnresolvedArgs(c: PContext, n: PNode): bool proc isArrayConstr(n: PNode): bool {.inline.} = result = n.kind == nkBracket and n.typ.skipTypes(abstractInst).kind == tyArray -template semIdeForTemplateOrGenericCheck(n, requiresCheck) = +template semIdeForTemplateOrGenericCheck(conf, n, requiresCheck) = # we check quickly if the node is where the cursor is when defined(nimsuggest): - if n.info.fileIndex == gTrackPos.fileIndex and n.info.line == gTrackPos.line: + if n.info.fileIndex == conf.m.trackPos.fileIndex and n.info.line == conf.m.trackPos.line: requiresCheck = true template semIdeForTemplateOrGeneric(c: PContext; n: PNode; @@ -70,6 +73,16 @@ template semIdeForTemplateOrGeneric(c: PContext; n: PNode; # echo "passing to safeSemExpr: ", renderTree(n) discard safeSemExpr(c, n) +proc fitNodePostMatch(c: PContext, formal: PType, arg: PNode): PNode = + result = arg + let x = result.skipConv + if x.kind in {nkPar, nkTupleConstr} and formal.kind != tyExpr: + changeType(c, x, formal, check=true) + else: + result = skipHiddenSubConv(result) + #result.typ = takeType(formal, arg.typ) + #echo arg.info, " picked ", result.typ.typeToString + proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode = if arg.typ.isNil: localError(c.config, arg.info, "expression has no type: " & @@ -85,18 +98,12 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode = result = copyTree(arg) result.typ = formal else: - let x = result.skipConv - if x.kind in {nkPar, nkTupleConstr} and formal.kind != tyExpr: - changeType(c, x, formal, check=true) - else: - result = skipHiddenSubConv(result) - #result.typ = takeType(formal, arg.typ) - #echo arg.info, " picked ", result.typ.typeToString + result = fitNodePostMatch(c, formal, result) proc inferWithMetatype(c: PContext, formal: PType, arg: PNode, coerceDistincts = false): PNode -var commonTypeBegin = PType(kind: tyExpr) +template commonTypeBegin*(): PType = PType(kind: tyExpr) proc commonType*(x, y: PType): PType = # new type relation that is used for array constructors, @@ -183,7 +190,7 @@ proc commonType*(x: PType, y: PNode): PType = commonType(x, y.typ) proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym = - result = newSym(kind, considerQuotedIdent(c.config, n), getCurrOwner(c), n.info) + result = newSym(kind, considerQuotedIdent(c, n), getCurrOwner(c), n.info) when defined(nimsuggest): suggestDecl(c, n, result) @@ -207,7 +214,7 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym = # template; we must fix it here: see #909 result.owner = getCurrOwner(c) else: - result = newSym(kind, considerQuotedIdent(c.config, n), getCurrOwner(c), n.info) + result = newSym(kind, considerQuotedIdent(c, n), getCurrOwner(c), n.info) #if kind in {skForVar, skLet, skVar} and result.owner.kind == skModule: # incl(result.flags, sfGlobal) when defined(nimsuggest): @@ -242,14 +249,14 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym, proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, flags: TExprFlags = {}): PNode -proc symFromType(t: PType, info: TLineInfo): PSym = +proc symFromType(c: PContext; t: PType, info: TLineInfo): PSym = if t.sym != nil: return t.sym - result = newSym(skType, getIdent"AnonType", t.owner, info) + result = newSym(skType, getIdent(c.cache, "AnonType"), t.owner, info) result.flags.incl sfAnon result.typ = t proc symNodeFromType(c: PContext, t: PType, info: TLineInfo): PNode = - result = newSymNode(symFromType(t, info), info) + result = newSymNode(symFromType(c, t, info), info) result.typ = makeTypeDesc(c, t) when false: @@ -310,13 +317,13 @@ proc tryConstExpr(c: PContext, n: PNode): PNode = let oldErrorCount = c.config.errorCounter let oldErrorMax = c.config.errorMax - let oldErrorOutputs = errorOutputs + let oldErrorOutputs = c.config.m.errorOutputs - errorOutputs = {} + c.config.m.errorOutputs = {} c.config.errorMax = high(int) try: - result = evalConstExpr(c.module, c.cache, c.graph, e) + result = evalConstExpr(c.module, c.graph, e) if result == nil or result.kind == nkEmpty: result = nil else: @@ -327,7 +334,7 @@ proc tryConstExpr(c: PContext, n: PNode): PNode = c.config.errorCounter = oldErrorCount c.config.errorMax = oldErrorMax - errorOutputs = oldErrorOutputs + c.config.m.errorOutputs = oldErrorOutputs const errConstExprExpected = "constant expression expected" @@ -340,12 +347,12 @@ proc semConstExpr(c: PContext, n: PNode): PNode = result = getConstExpr(c.module, e, c.graph) if result == nil: #if e.kind == nkEmpty: globalError(n.info, errConstExprExpected) - result = evalConstExpr(c.module, c.cache, c.graph, e) + result = evalConstExpr(c.module, c.graph, e) if result == nil or result.kind == nkEmpty: if e.info != n.info: - pushInfoContext(n.info) + pushInfoContext(c.config, n.info) localError(c.config, e.info, errConstExprExpected) - popInfoContext() + popInfoContext(c.config) else: localError(c.config, e.info, errConstExprExpected) # error correction: @@ -383,8 +390,8 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode, ## coherence, making sure that variables declared with 'let' aren't ## reassigned, and binding the unbound identifiers that the macro output ## contains. - inc(evalTemplateCounter) - if evalTemplateCounter > evalTemplateLimit: + inc(c.config.evalTemplateCounter) + if c.config.evalTemplateCounter > evalTemplateLimit: globalError(c.config, s.info, "template instantiation too nested") c.friendModules.add(s.owner.getModule) @@ -423,8 +430,8 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode, result = semExpr(c, result, flags) result = fitNode(c, retType, result, result.info) - #GlobalError(s.info, errInvalidParamKindX, typeToString(s.typ.sons[0])) - dec(evalTemplateCounter) + #globalError(s.info, errInvalidParamKindX, typeToString(s.typ.sons[0])) + dec(c.config.evalTemplateCounter) discard c.friendModules.pop() const @@ -432,7 +439,7 @@ const proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, flags: TExprFlags = {}): PNode = - pushInfoContext(nOrig.info) + pushInfoContext(c.config, nOrig.info) markUsed(c.config, n.info, sym, c.graph.usageSym) styleCheckUse(n.info, sym) @@ -448,11 +455,11 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, #if c.evalContext == nil: # c.evalContext = c.createEvalContext(emStatic) - result = evalMacroCall(c.module, c.cache, c.graph, n, nOrig, sym) + result = evalMacroCall(c.module, c.graph, n, nOrig, sym) if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, sym, flags) result = wrapInComesFrom(nOrig.info, sym, result) - popInfoContext() + popInfoContext(c.config) proc forceBool(c: PContext, n: PNode): PNode = result = fitNode(c, getSysType(c.graph, n.info, tyBool), n, n.info) @@ -484,8 +491,8 @@ proc addCodeForGenerics(c: PContext, n: PNode) = addSon(n, prc.ast) c.lastGenericIdx = c.generics.len -proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = - var c = newContext(graph, module, cache) +proc myOpen(graph: ModuleGraph; module: PSym): PPassContext = + var c = newContext(graph, module) if c.p != nil: internalError(graph.config, module.info, "sem.myOpen") c.semConstExpr = semConstExpr c.semExpr = semExpr @@ -507,19 +514,13 @@ proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = graph.systemModule = module c.topLevelScope = openScope(c) # don't be verbose unless the module belongs to the main package: - if module.owner.id == gMainPackageId: + if module.owner.id == graph.config.mainPackageId: graph.config.notes = graph.config.mainPackageNotes else: if graph.config.mainPackageNotes == {}: graph.config.mainPackageNotes = graph.config.notes graph.config.notes = graph.config.foreignPackageNotes result = c -proc myOpenCached(graph: ModuleGraph; module: PSym; rd: PRodReader): PPassContext = - result = myOpen(graph, module, rd.cache) - -proc replayMethodDefs(graph: ModuleGraph; rd: PRodReader) = - for m in items(rd.methods): methodDef(graph, m, true) - proc isImportSystemStmt(g: ModuleGraph; n: PNode): bool = if g.systemModule == nil: return false case n.kind @@ -585,31 +586,31 @@ proc myProcess(context: PPassContext, n: PNode): PNode = if c.config.errorMax <= 1: result = semStmtAndGenerateGenerics(c, n) else: - let oldContextLen = msgs.getInfoContextLen() + let oldContextLen = msgs.getInfoContextLen(c.config) let oldInGenericInst = c.inGenericInst try: result = semStmtAndGenerateGenerics(c, n) except ERecoverableError, ESuggestDone: recoverContext(c) c.inGenericInst = oldInGenericInst - msgs.setInfoContextLen(oldContextLen) + msgs.setInfoContextLen(c.config, oldContextLen) if getCurrentException() of ESuggestDone: c.suggestionsMade = true result = nil else: - result = ast.emptyNode + result = newNodeI(nkEmpty, n.info) #if c.config.cmd == cmdIdeTools: findSuggest(c, n) - rod.storeNode(c.module, result) + rod.storeNode(c.graph, c.module, result) proc testExamples(c: PContext) = - let inp = toFullPath(c.module.info) + let inp = toFullPath(c.config, c.module.info) let outp = inp.changeFileExt"" & "_examples.nim" renderModule(c.runnableExamples, inp, outp) let backend = if isDefined(c.config, "js"): "js" elif isDefined(c.config, "cpp"): "cpp" elif isDefined(c.config, "objc"): "objc" else: "c" - if os.execShellCmd("nim " & backend & " -r " & outp) != 0: + if os.execShellCmd(os.getAppFilename() & " " & backend & " -r " & outp) != 0: quit "[Examples] failed" removeFile(outp) @@ -625,12 +626,10 @@ proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode = addCodeForGenerics(c, result) if c.module.ast != nil: result.add(c.module.ast) - if c.rd != nil: - replayMethodDefs(graph, c.rd) popOwner(c) popProcCon(c) - storeRemaining(c.module) + storeRemaining(c.graph, c.module) if c.runnableExamples != nil: testExamples(c) -const semPass* = makePass(myOpen, myOpenCached, myProcess, myClose, +const semPass* = makePass(myOpen, myProcess, myClose, isFrontend = true) diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim index f0fde195c..97ff4a7fc 100644 --- a/compiler/semasgn.nim +++ b/compiler/semasgn.nim @@ -157,12 +157,12 @@ proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = proc addVar(father, v, value: PNode) = var vpart = newNodeI(nkIdentDefs, v.info, 3) vpart.sons[0] = v - vpart.sons[1] = ast.emptyNode + vpart.sons[1] = newNodeI(nkEmpty, v.info) vpart.sons[2] = value addSon(father, vpart) proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode = - var temp = newSym(skTemp, getIdent(lowerings.genPrefix), c.fn, c.info) + var temp = newSym(skTemp, getIdent(c.c.cache, lowerings.genPrefix), c.fn, c.info) temp.typ = getSysType(c.c.graph, body.info, tyInt) incl(temp.flags, sfFromGeneric) @@ -207,7 +207,7 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) = if t.kind == tySequence: # XXX add 'nil' handling here body.add newSeqCall(c.c, x, y) - let i = declareCounter(c, body, firstOrd(t)) + let i = declareCounter(c, body, firstOrd(c.c.config, t)) let whileLoop = genWhileLoop(c, i, x) let elemType = t.lastSon liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType), @@ -268,17 +268,17 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp; a.kind = kind let body = newNodeI(nkStmtList, info) let procname = case kind - of attachedAsgn: getIdent"=" - of attachedSink: getIdent"=sink" - of attachedDeepCopy: getIdent"=deepcopy" - of attachedDestructor: getIdent"=destroy" + of attachedAsgn: getIdent(c.cache, "=") + of attachedSink: getIdent(c.cache, "=sink") + of attachedDeepCopy: getIdent(c.cache, "=deepcopy") + of attachedDestructor: getIdent(c.cache, "=destroy") result = newSym(skProc, procname, typ.owner, info) a.fn = result a.asgnForType = typ - let dest = newSym(skParam, getIdent"dest", result, info) - let src = newSym(skParam, getIdent"src", result, info) + let dest = newSym(skParam, getIdent(c.cache, "dest"), result, info) + let src = newSym(skParam, getIdent(c.cache, "src"), result, info) dest.typ = makeVarType(c, typ) src.typ = typ @@ -297,7 +297,7 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp; of attachedDestructor: typ.destructor = result var n = newNodeI(nkProcDef, info, bodyPos+1) - for i in 0 ..< n.len: n.sons[i] = emptyNode + for i in 0 ..< n.len: n.sons[i] = newNodeI(nkEmpty, info) n.sons[namePos] = newSymNode(result) n.sons[paramsPos] = result.typ.n n.sons[bodyPos] = body diff --git a/compiler/semcall.nim b/compiler/semcall.nim index df99d6c24..67fe99232 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -170,7 +170,7 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): add(candidates, renderTree(err.sym.ast, {renderNoBody, renderNoComments, renderNoPragmas})) else: - add(candidates, err.sym.getProcHeader(prefer)) + add(candidates, getProcHeader(c.config, err.sym, prefer)) add(candidates, "\n") if err.firstMismatch != 0 and n.len > 1: let cond = n.len > 2 @@ -213,7 +213,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) = # Gives a detailed error message; this is separated from semOverloadedCall, # as semOverlodedCall is already pretty slow (and we need this information # only in case of an error). - if errorOutputs == {}: + if c.config.m.errorOutputs == {}: # fail fast: globalError(c.config, n.info, "type mismatch") if errors.len == 0: @@ -293,7 +293,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode, orig.sons[0..1] = [nil, orig[1], f] template tryOp(x) = - let op = newIdentNode(getIdent(x), n.info) + let op = newIdentNode(getIdent(c.cache, x), n.info) n.sons[0] = op orig.sons[0] = op pickBest(op) @@ -306,17 +306,17 @@ proc resolveOverloads(c: PContext, n, orig: PNode, elif nfDotSetter in n.flags and f.kind == nkIdent and n.len == 3: # we need to strip away the trailing '=' here: - let calleeName = newIdentNode(getIdent(f.ident.s[0..f.ident.s.len-2]), n.info) - let callOp = newIdentNode(getIdent".=", n.info) + let calleeName = newIdentNode(getIdent(c.cache, f.ident.s[0..f.ident.s.len-2]), n.info) + let callOp = newIdentNode(getIdent(c.cache, ".="), n.info) n.sons[0..1] = [callOp, n[1], calleeName] orig.sons[0..1] = [callOp, orig[1], calleeName] pickBest(callOp) if overloadsState == csEmpty and result.state == csEmpty: if nfDotField in n.flags and nfExplicitCall notin n.flags: - localError(c.config, n.info, errUndeclaredField % considerQuotedIdent(c.config, f, n).s) + localError(c.config, n.info, errUndeclaredField % considerQuotedIdent(c, f, n).s) else: - localError(c.config, n.info, errUndeclaredRoutine % considerQuotedIdent(c.config, f, n).s) + localError(c.config, n.info, errUndeclaredRoutine % considerQuotedIdent(c, f, n).s) return elif result.state != csMatch: if nfExprCall in n.flags: @@ -333,7 +333,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode, internalAssert c.config, result.state == csMatch #writeMatches(result) #writeMatches(alt) - if errorOutputs == {}: + if c.config.m.errorOutputs == {}: # quick error message for performance of 'compiles' built-in: globalError(c.config, n.info, errGenerated, "ambiguous call") elif c.config.errorCounter == 0: @@ -345,7 +345,8 @@ proc resolveOverloads(c: PContext, n, orig: PNode, add(args, ")") localError(c.config, n.info, errAmbiguousCallXYZ % [ - getProcHeader(result.calleeSym), getProcHeader(alt.calleeSym), + getProcHeader(c.config, result.calleeSym), + getProcHeader(c.config, alt.calleeSym), args]) proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) = @@ -390,7 +391,23 @@ proc inferWithMetatype(c: PContext, formal: PType, result = copyTree(arg) result.typ = formal -proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode = +proc updateDefaultParams(call: PNode) = + # In generic procs, the default parameter may be unique for each + # instantiation (see tlateboundgenericparams). + # After a call is resolved, we need to re-assign any default value + # that was used during sigmatch. sigmatch is responsible for marking + # the default params with `nfDefaultParam` and `instantiateProcType` + # computes correctly the default values for each instantiation. + let calleeParams = call[0].sym.typ.n + for i in countdown(call.len - 1, 1): + if nfDefaultParam notin call[i].flags: + return + let def = calleeParams[i].sym.ast + if nfDefaultRefsParam in def.flags: call.flags.incl nfDefaultRefsParam + call[i] = def + +proc semResolvedCall(c: PContext, x: TCandidate, + n: PNode, flags: TExprFlags): PNode = assert x.state == csMatch var finalCallee = x.calleeSym markUsed(c.config, n.sons[0].info, finalCallee, c.graph.usageSym) @@ -423,8 +440,9 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode = result = x.call instGenericConvertersSons(c, result, x) - result.sons[0] = newSymNode(finalCallee, result.sons[0].info) + result[0] = newSymNode(finalCallee, result[0].info) result.typ = finalCallee.typ.sons[0] + updateDefaultParams(result) proc canDeref(n: PNode): bool {.inline.} = result = n.len >= 2 and (let t = n[1].typ; @@ -446,7 +464,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, message(c.config, n.info, hintUserRaw, "Non-matching candidates for " & renderTree(n) & "\n" & candidates) - result = semResolvedCall(c, n, r) + result = semResolvedCall(c, r, n, flags) elif implicitDeref in c.features and canDeref(n): # try to deref the first argument and then try overloading resolution again: # @@ -457,7 +475,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, # n.sons[1] = n.sons[1].tryDeref var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags) - if r.state == csMatch: result = semResolvedCall(c, n, r) + if r.state == csMatch: result = semResolvedCall(c, r, n, flags) else: # get rid of the deref again for a better error message: n.sons[1] = n.sons[1].sons[0] diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 12ac19ca3..aa0cb6e8e 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -13,8 +13,8 @@ import strutils, intsets, options, lexer, ast, astalgo, trees, treetab, wordrecg, ropes, msgs, platform, os, condsyms, idents, renderer, types, extccomp, math, - magicsys, nversion, nimsets, parser, times, passes, rodread, vmdef, - modulegraphs, configuration + magicsys, nversion, nimsets, parser, times, passes, vmdef, + modulegraphs, lineinfos type TOptionEntry* = object # entries to put on a stack for pragma parsing @@ -75,6 +75,7 @@ type PContext* = ref TContext TContext* = object of TPassContext # a context represents a module + enforceVoidContext*: PType module*: PSym # the module sym belonging to the context currentScope*: PScope # current scope importTable*: PScope # scope for all imported symbols @@ -148,7 +149,7 @@ proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair = proc filename*(c: PContext): string = # the module's filename - return toFilename(FileIndex c.module.position) + return toFilename(c.config, FileIndex c.module.position) proc scopeDepth*(c: PContext): int {.inline.} = result = if c.currentScope != nil: c.currentScope.depthLevel @@ -210,8 +211,9 @@ proc newOptionEntry*(conf: ConfigRef): POptionEntry = result.dynlib = nil result.notes = conf.notes -proc newContext*(graph: ModuleGraph; module: PSym; cache: IdentCache): PContext = +proc newContext*(graph: ModuleGraph; module: PSym): PContext = new(result) + result.enforceVoidContext = PType(kind: tyStmt) result.ambiguousSymbols = initIntSet() result.optionStack = @[] result.libs = @[] @@ -225,7 +227,7 @@ proc newContext*(graph: ModuleGraph; module: PSym; cache: IdentCache): PContext initStrTable(result.userPragmas) result.generics = @[] result.unknownIdents = initIntSet() - result.cache = cache + result.cache = graph.cache result.graph = graph initStrTable(result.signatures) result.typesWithOps = @[] @@ -286,7 +288,8 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType = result.addSonSkipIntLit(typ) proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode = - let typedesc = makeTypeDesc(c, typ) + let typedesc = newTypeS(tyTypeDesc, c) + typedesc.addSonSkipIntLit(assertNotNil(c.config, typ)) let sym = newSym(skType, c.cache.idAnon, getCurrOwner(c), info, c.config.options).linkTo(typedesc) return newSymNode(sym, info) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 92b9c365a..5d6eaf652 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -26,10 +26,13 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym, flags: TExprFlags = {}): PNode = markUsed(c.config, n.info, s, c.graph.usageSym) styleCheckUse(n.info, s) - pushInfoContext(n.info) + pushInfoContext(c.config, n.info) result = evalTemplate(n, s, getCurrOwner(c), c.config, efFromHlo in flags) if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, s, flags) - popInfoContext() + popInfoContext(c.config) + + # XXX: A more elaborate line info rewrite might be needed + result.info = n.info proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode @@ -58,7 +61,7 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # do not produce another redundant error message: #raiseRecoverableError("") result = errorNode(c, n) - if result.typ == nil or result.typ == enforceVoidContext: + if result.typ == nil or result.typ == c.enforceVoidContext: localError(c.config, n.info, errExprXHasNoType % renderTree(result, {renderNoComments})) result.typ = errorType(c) @@ -137,7 +140,7 @@ proc checkConvertible(c: PContext, castDest, src: PType): TConvStatus = else: discard -proc isCastable(dst, src: PType): bool = +proc isCastable(conf: ConfigRef; dst, src: PType): bool = ## Checks whether the source type can be cast to the destination type. ## Casting is very unrestrictive; casts are allowed as long as ## castDest.size >= src.size, and typeAllowed(dst, skParam) @@ -152,8 +155,8 @@ proc isCastable(dst, src: PType): bool = return false var dstSize, srcSize: BiggestInt - dstSize = computeSize(dst) - srcSize = computeSize(src) + dstSize = computeSize(conf, dst) + srcSize = computeSize(conf, src) if dstSize < 0: result = false elif srcSize < 0: @@ -167,7 +170,7 @@ proc isCastable(dst, src: PType): bool = (skipTypes(dst, abstractInst).kind in IntegralTypes) or (skipTypes(src, abstractInst-{tyTypeDesc}).kind in IntegralTypes) if result and src.kind == tyNil: - result = dst.size <= platform.ptrSize + result = dst.size <= conf.target.ptrSize proc isSymChoice(n: PNode): bool {.inline.} = result = n.kind in nkSymChoices @@ -188,7 +191,25 @@ proc semConv(c: PContext, n: PNode): PNode = return n result = newNodeI(nkConv, n.info) - var targetType = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc}) + + var targetType = semTypeNode(c, n.sons[0], nil) + if targetType.kind == tyTypeDesc: + internalAssert c.config, targetType.len > 0 + if targetType.base.kind == tyNone: + return semTypeOf(c, n[1]) + else: + targetType = targetType.base + elif targetType.kind == tyStatic: + var evaluated = semStaticExpr(c, n[1]) + if evaluated.kind == nkType or evaluated.typ.kind == tyTypeDesc: + result = n + result.typ = c.makeTypeDesc semStaticType(c, evaluated, nil) + return + elif targetType.base.kind == tyNone: + return evaluated + else: + targetType = targetType.base + maybeLiftType(targetType, c, n[0].info) if targetType.kind in {tySink, tyLent}: @@ -251,7 +272,7 @@ proc semCast(c: PContext, n: PNode): PNode = let castedExpr = semExprWithType(c, n.sons[1]) if tfHasMeta in targetType.flags: localError(c.config, n.sons[0].info, "cannot cast to a non concrete type: '$1'" % $targetType) - if not isCastable(targetType, castedExpr.typ): + if not isCastable(c.config, targetType, castedExpr.typ): let tar = $targetType let alt = typeToString(targetType, preferDesc) let msg = if tar != alt: tar & "=" & alt else: tar @@ -268,7 +289,7 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode = localError(c.config, n.info, errXExpectsTypeOrValue % opToStr[m]) else: n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType}) - var typ = skipTypes(n.sons[1].typ, abstractVarRange + {tyTypeDesc}) + var typ = skipTypes(n.sons[1].typ, abstractVarRange + {tyTypeDesc, tyUserTypeClassInst}) case typ.kind of tySequence, tyString, tyCString, tyOpenArray, tyVarargs: n.typ = getSysType(c.graph, n.info, tyInt) @@ -295,57 +316,98 @@ proc semSizeof(c: PContext, n: PNode): PNode = n.typ = getSysType(c.graph, n.info, tyInt) result = n +proc fixupStaticType(c: PContext, n: PNode) = + # This proc can be applied to evaluated expressions to assign + # them a static type. + # + # XXX: with implicit static, this should not be necessary, + # because the output type of operations such as `semConstExpr` + # should be a static type (as well as the type of any other + # expression that can be implicitly evaluated). For now, we + # apply this measure only in code that is enlightened to work + # with static types. + if n.typ.kind != tyStatic: + n.typ = newTypeWithSons(getCurrOwner(c), tyStatic, @[n.typ]) + n.typ.n = n # XXX: cycles like the one here look dangerous. + # Consider using `n.copyTree` + proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode = - internalAssert c.config, n.sonsLen == 3 and - n[1].typ != nil and n[1].typ.kind == tyTypeDesc and + internalAssert c.config, + n.sonsLen == 3 and + n[1].typ != nil and n[2].kind in {nkStrLit..nkTripleStrLit, nkType} - let t1 = n[1].typ.skipTypes({tyTypeDesc}) + var + res = false + t1 = n[1].typ + t2 = n[2].typ + + if t1.kind == tyTypeDesc and t2.kind != tyTypeDesc: + t1 = t1.base if n[2].kind in {nkStrLit..nkTripleStrLit}: case n[2].strVal.normalize of "closure": let t = skipTypes(t1, abstractRange) - result = newIntNode(nkIntLit, ord(t.kind == tyProc and - t.callConv == ccClosure and - tfIterator notin t.flags)) + res = t.kind == tyProc and + t.callConv == ccClosure and + tfIterator notin t.flags else: - result = newIntNode(nkIntLit, 0) + res = false else: - var rhsOrigType = n[2].typ - var t2 = rhsOrigType.skipTypes({tyTypeDesc}) maybeLiftType(t2, c, n.info) var m: TCandidate initCandidate(c, m, t2) if efExplain in flags: m.diagnostics = @[] m.diagnosticsEnabled = true - let match = typeRel(m, t2, t1) >= isSubtype # isNone - result = newIntNode(nkIntLit, ord(match)) + res = typeRel(m, t2, t1) >= isSubtype # isNone + result = newIntNode(nkIntLit, ord(res)) result.typ = n.typ proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode = if sonsLen(n) != 3: localError(c.config, n.info, "'is' operator takes 2 arguments") + let boolType = getSysType(c.graph, n.info, tyBool) result = n - n.typ = getSysType(c.graph, n.info, tyBool) + n.typ = boolType + var liftLhs = true n.sons[1] = semExprWithType(c, n[1], {efDetermineType, efWantIterator}) if n[2].kind notin {nkStrLit..nkTripleStrLit}: let t2 = semTypeNode(c, n[2], nil) n.sons[2] = newNodeIT(nkType, n[2].info, t2) + if t2.kind == tyStatic: + let evaluated = tryConstExpr(c, n[1]) + if evaluated != nil: + c.fixupStaticType(evaluated) + n[1] = evaluated + else: + result = newIntNode(nkIntLit, 0) + result.typ = boolType + return + elif t2.kind == tyTypeDesc and + (t2.base.kind == tyNone or tfExplicit in t2.flags): + # When the right-hand side is an explicit type, we must + # not allow regular values to be matched against the type: + liftLhs = false - let lhsType = n[1].typ + var lhsType = n[1].typ if lhsType.kind != tyTypeDesc: - n.sons[1] = makeTypeSymNode(c, lhsType, n[1].info) - elif lhsType.base.kind == tyNone: - # this is a typedesc variable, leave for evals - return + if liftLhs: + n[1] = makeTypeSymNode(c, lhsType, n[1].info) + lhsType = n[1].typ + else: + if lhsType.base.kind == tyNone: + # this is a typedesc variable, leave for evals + return + if lhsType.base.containsGenericType: + # BUGFIX: don't evaluate this too early: ``T is void`` + return - # BUGFIX: don't evaluate this too early: ``T is void`` - if not n[1].typ.base.containsGenericType: result = isOpImpl(c, n, flags) + result = isOpImpl(c, n, flags) proc semOpAux(c: PContext, n: PNode) = const flags = {efDetermineType} @@ -353,7 +415,7 @@ proc semOpAux(c: PContext, n: PNode) = var a = n.sons[i] if a.kind == nkExprEqExpr and sonsLen(a) == 2: let info = a.sons[0].info - a.sons[0] = newIdentNode(considerQuotedIdent(c.config, a.sons[0], a), info) + a.sons[0] = newIdentNode(considerQuotedIdent(c, a.sons[0], a), info) a.sons[1] = semExprWithType(c, a.sons[1], flags) a.typ = a.sons[1].typ else: @@ -361,7 +423,7 @@ proc semOpAux(c: PContext, n: PNode) = proc overloadedCallOpr(c: PContext, n: PNode): PNode = # quick check if there is *any* () operator overloaded: - var par = getIdent("()") + var par = getIdent(c.cache, "()") if searchInScopes(c, par) == nil: result = nil else: @@ -407,7 +469,7 @@ proc changeType(c: PContext; n: PNode, newType: PType, check: bool) = of nkCharLit..nkUInt64Lit: if check and n.kind != nkUInt64Lit: let value = n.intVal - if value < firstOrd(newType) or value > lastOrd(newType): + if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType): localError(c.config, n.info, "cannot convert " & $value & " to " & typeToString(newType)) else: discard @@ -480,6 +542,36 @@ proc fixAbstractType(c: PContext, n: PNode) = proc isAssignable(c: PContext, n: PNode; isUnsafeAddr=false): TAssignableResult = result = parampatterns.isAssignable(c.p.owner, n, isUnsafeAddr) +proc isUnresolvedSym(s: PSym): bool = + return s.kind == skGenericParam or + tfInferrableStatic in s.typ.flags or + (s.kind == skParam and s.typ.isMetaType) or + (s.kind == skType and + s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {}) + +proc hasUnresolvedArgs(c: PContext, n: PNode): bool = + # Checks whether an expression depends on generic parameters that + # don't have bound values yet. E.g. this could happen in situations + # such as: + # type Slot[T] = array[T.size, byte] + # proc foo[T](x: default(T)) + # + # Both static parameter and type parameters can be unresolved. + case n.kind + of nkSym: + return isUnresolvedSym(n.sym) + of nkIdent, nkAccQuoted: + let ident = considerQuotedIdent(c, n) + let sym = searchInScopes(c, ident) + if sym != nil: + return isUnresolvedSym(sym) + else: + return false + else: + for i in 0..<n.safeLen: + if hasUnresolvedArgs(c, n.sons[i]): return true + return false + proc newHiddenAddrTaken(c: PContext, n: PNode): PNode = if n.kind == nkHiddenDeref and not (c.config.cmd == cmdCompileToCpp or sfCompileToCpp in c.module.flags): @@ -617,24 +709,24 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = call.add(a) #echo "NOW evaluating at compile time: ", call.renderTree if sfCompileTime in callee.flags: - result = evalStaticExpr(c.module, c.cache, c.graph, call, c.p.owner) + result = evalStaticExpr(c.module, c.graph, call, c.p.owner) if result.isNil: localError(c.config, n.info, errCannotInterpretNodeX % renderTree(call)) else: result = fixupTypeAfterEval(c, result, n) else: - result = evalConstExpr(c.module, c.cache, c.graph, call) + result = evalConstExpr(c.module, c.graph, call) if result.isNil: result = n else: result = fixupTypeAfterEval(c, result, n) #if result != n: # echo "SUCCESS evaluated at compile time: ", call.renderTree proc semStaticExpr(c: PContext, n: PNode): PNode = - let a = semExpr(c, n.sons[0]) + let a = semExpr(c, n) if a.findUnresolvedStatic != nil: return a - result = evalStaticExpr(c.module, c.cache, c.graph, a, c.p.owner) + result = evalStaticExpr(c.module, c.graph, a, c.p.owner) if result.isNil: localError(c.config, n.info, errCannotInterpretNodeX % renderTree(n)) - result = emptyNode + result = c.graph.emptyNode else: result = fixupTypeAfterEval(c, result, a) @@ -742,10 +834,10 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = # This is a proc variable, apply normal overload resolution let m = resolveIndirectCall(c, n, nOrig, t) if m.state != csMatch: - if errorOutputs == {}: + if c.config.m.errorOutputs == {}: # speed up error generation: globalError(c.config, n.info, "type mismatch") - return emptyNode + return c.graph.emptyNode else: var hasErrorType = false var msg = "type mismatch: got <" @@ -765,6 +857,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = else: result = m.call instGenericConvertersSons(c, result, m) + elif t != nil and t.kind == tyTypeDesc: if n.len == 1: return semObjConstr(c, n, flags) return semConv(c, n) @@ -802,7 +895,7 @@ proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = proc buildEchoStmt(c: PContext, n: PNode): PNode = # we MUST not check 'n' for semantics again here! But for now we give up: result = newNodeI(nkCall, n.info) - var e = strTableGet(c.graph.systemModule.tab, getIdent"echo") + var e = strTableGet(c.graph.systemModule.tab, getIdent(c.cache, "echo")) if e != nil: add(result, newSymNode(e)) else: @@ -866,7 +959,7 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent, else: if check == nil: check = newNodeI(nkCheckedFieldExpr, n.info) - addSon(check, ast.emptyNode) # make space for access node + addSon(check, c.graph.emptyNode) # make space for access node s = newNodeIT(nkCurly, n.info, setType) for j in countup(0, sonsLen(it) - 2): addSon(s, copyTree(it.sons[j])) var inExpr = newNodeIT(nkCall, n.info, getSysType(c.graph, n.info, tyBool)) @@ -881,7 +974,7 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent, if result != nil: if check == nil: check = newNodeI(nkCheckedFieldExpr, n.info) - addSon(check, ast.emptyNode) # make space for access node + addSon(check, c.graph.emptyNode) # make space for access node var inExpr = newNodeIT(nkCall, n.info, getSysType(c.graph, n.info, tyBool)) addSon(inExpr, newSymNode(c.graph.opContains, n.info)) addSon(inExpr, s) @@ -938,7 +1031,7 @@ proc readTypeParameter(c: PContext, typ: PType, if rawTyp.n != nil: return rawTyp.n else: - return emptyNode + return c.graph.emptyNode else: let foundTyp = makeTypeDesc(c, rawTyp) return newSymNode(copySym(tParam.sym).linkTo(foundTyp), info) @@ -1031,7 +1124,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = of skType: markUsed(c.config, n.info, s, c.graph.usageSym) styleCheckUse(n.info, s) - if s.typ.kind == tyStatic and s.typ.n != nil: + if s.typ.kind == tyStatic and s.typ.base.kind != tyNone and s.typ.n != nil: return s.typ.n result = newSymNode(s, n.info) result.typ = makeTypeDesc(c, s.typ) @@ -1082,7 +1175,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = when defined(nimsuggest): if c.config.cmd == cmdIdeTools: suggestExpr(c, n) - if exactEquals(gTrackPos, n[1].info): suggestExprNoCheck(c, n) + if exactEquals(c.config.m.trackPos, n[1].info): suggestExprNoCheck(c, n) var s = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared, checkModule}) if s != nil: @@ -1097,7 +1190,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = n.sons[0] = semExprWithType(c, n.sons[0], flags+{efDetermineType}) #restoreOldStyleType(n.sons[0]) - var i = considerQuotedIdent(c.config, n.sons[1], n) + var i = considerQuotedIdent(c, n.sons[1], n) var ty = n.sons[0].typ var f: PSym = nil result = nil @@ -1106,7 +1199,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = case t.kind of tyTypeParamsHolders: result = readTypeParameter(c, t, i, n.info) - if result == emptyNode: + if result == c.graph.emptyNode: result = n n.typ = makeTypeFromExpr(c, n.copyTree) return @@ -1217,7 +1310,7 @@ proc dotTransformation(c: PContext, n: PNode): PNode = addSon(result, n.sons[1]) addSon(result, copyTree(n[0])) else: - var i = considerQuotedIdent(c.config, n.sons[1], n) + var i = considerQuotedIdent(c, n.sons[1], n) result = newNodeI(nkDotCall, n.info) result.flags.incl nfDotField addSon(result, newIdentNode(i, n[1].info)) @@ -1258,8 +1351,18 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = # make sure we don't evaluate generic macros/templates n.sons[0] = semExprWithType(c, n.sons[0], {efNoEvaluateGeneric}) - let arr = skipTypes(n.sons[0].typ, {tyGenericInst, + var arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyUserTypeClassInst, tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink}) + if arr.kind == tyStatic: + if arr.base.kind == tyNone: + result = n + result.typ = semStaticType(c, n[1], nil) + return + elif arr.n != nil: + return semSubscript(c, arr.n, flags) + else: + arr = arr.base + case arr.kind of tyArray, tyOpenArray, tyVarargs, tySequence, tyString, tyCString: @@ -1328,11 +1431,11 @@ proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = result = semSubscript(c, n, flags) if result == nil: # overloaded [] operator: - result = semExpr(c, buildOverloadedSubscripts(n, getIdent"[]")) + result = semExpr(c, buildOverloadedSubscripts(n, getIdent(c.cache, "[]"))) proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode = - var id = considerQuotedIdent(c.config, a[1], a) - var setterId = newIdentNode(getIdent(id.s & '='), n.info) + var id = considerQuotedIdent(c, a[1], a) + var setterId = newIdentNode(getIdent(c.cache, id.s & '='), n.info) # a[0] is already checked for semantics, that does ``builtinFieldAccess`` # this is ugly. XXX Semantic checking should use the ``nfSem`` flag for # nodes? @@ -1409,7 +1512,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = # --> `[]=`(a, i, x) a = semSubscript(c, a, {efLValue}) if a == nil: - result = buildOverloadedSubscripts(n.sons[0], getIdent"[]=") + result = buildOverloadedSubscripts(n.sons[0], getIdent(c.cache, "[]=")) add(result, n[1]) if mode == noOverloadedSubscript: bracketNotFoundError(c, result) @@ -1419,7 +1522,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = return result of nkCurlyExpr: # a{i} = x --> `{}=`(a, i, x) - result = buildOverloadedSubscripts(n.sons[0], getIdent"{}=") + result = buildOverloadedSubscripts(n.sons[0], getIdent(c.cache, "{}=")) add(result, n[1]) return semExprNoType(c, result) of nkPar, nkTupleConstr: @@ -1427,7 +1530,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = # unfortunately we need to rewrite ``(x, y) = foo()`` already here so # that overloading of the assignment operator still works. Usually we # prefer to do these rewritings in transf.nim: - return semStmt(c, lowerTupleUnpackingForAsgn(n, c.p.owner)) + return semStmt(c, lowerTupleUnpackingForAsgn(c.graph, n, c.p.owner)) else: a = semExprWithType(c, a, {efLValue}) else: @@ -1450,7 +1553,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = rhs = semExprWithType(c, n.sons[1], if lhsIsResult: {efAllowDestructor} else: {}) if lhsIsResult: - n.typ = enforceVoidContext + n.typ = c.enforceVoidContext if c.p.owner.kind != skMacro and resultTypeIsInferrable(lhs.sym.typ): var rhsTyp = rhs.typ if rhsTyp.kind in tyUserTypeClasses and rhsTyp.isResolvedUserTypeClass: @@ -1489,7 +1592,7 @@ proc semReturn(c: PContext, n: PNode): PNode = n.sons[0] = semAsgn(c, a) # optimize away ``result = result``: if n[0][1].kind == nkSym and n[0][1].sym == c.p.resultSym: - n.sons[0] = ast.emptyNode + n.sons[0] = c.graph.emptyNode else: localError(c.config, n.info, errNoReturnTypeDeclared) else: @@ -1556,8 +1659,6 @@ proc semYield(c: PContext, n: PNode): PNode = checkSonsLen(n, 1, c.config) if c.p.owner == nil or c.p.owner.kind != skIterator: localError(c.config, n.info, errYieldNotAllowedHere) - elif oldIterTransf in c.features and c.p.inTryStmt > 0 and c.p.owner.typ.callConv != ccInline: - localError(c.config, n.info, errYieldNotAllowedInTryStmt) elif n.sons[0].kind != nkEmpty: n.sons[0] = semExprWithType(c, n.sons[0]) # check for type compatibility: var iterType = c.p.owner.typ @@ -1593,13 +1694,13 @@ proc lookUpForDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PSym = checkSonsLen(n, 2, c.config) var m = lookUpForDefined(c, n.sons[0], onlyCurrentScope) if m != nil and m.kind == skModule: - let ident = considerQuotedIdent(c.config, n[1], n) + let ident = considerQuotedIdent(c, n[1], n) if m == c.module: result = strTableGet(c.topLevelScope.symbols, ident) else: result = strTableGet(m.tab, ident) of nkAccQuoted: - result = lookUpForDefined(c, considerQuotedIdent(c.config, n), onlyCurrentScope) + result = lookUpForDefined(c, considerQuotedIdent(c, n), onlyCurrentScope) of nkSym: result = n.sym of nkOpenSymChoice, nkClosedSymChoice: @@ -1612,11 +1713,9 @@ proc semDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PNode = checkSonsLen(n, 2, c.config) # we replace this node by a 'true' or 'false' node: result = newIntNode(nkIntLit, 0) - if not onlyCurrentScope and considerQuotedIdent(c.config, n[0], n).s == "defined": - if n.sons[1].kind != nkIdent: - localError(c.config, n.info, "obsolete usage of 'defined', use 'declared' instead") - elif isDefined(c.config, n.sons[1].ident.s): - result.intVal = 1 + if not onlyCurrentScope and considerQuotedIdent(c, n[0], n).s == "defined": + let d = considerQuotedIdent(c, n[1], n) + result.intVal = ord isDefined(c.config, d.s) elif lookUpForDefined(c, n.sons[1], onlyCurrentScope) != nil: result.intVal = 1 result.info = n.info @@ -1711,7 +1810,7 @@ proc processQuotations(c: PContext; n: var PNode, op: string, ids: var seq[PNode]) = template returnQuote(q) = quotes.add q - n = newIdentNode(getIdent($quotes.len), n.info) + n = newIdentNode(getIdent(c.cache, $quotes.len), n.info) ids.add n return @@ -1722,7 +1821,7 @@ proc processQuotations(c: PContext; n: var PNode, op: string, if examinedOp == op: returnQuote n[1] elif examinedOp.startsWith(op): - n.sons[0] = newIdentNode(getIdent(examinedOp.substr(op.len)), n.info) + n.sons[0] = newIdentNode(getIdent(c.cache, examinedOp.substr(op.len)), n.info) elif n.kind == nkAccQuoted and op == "``": returnQuote n[0] @@ -1748,14 +1847,17 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = processQuotations(c, quotedBlock, op, quotes, ids) var dummyTemplate = newProcNode( - nkTemplateDef, quotedBlock.info, quotedBlock, - name = newAnonSym(c, skTemplate, n.info).newSymNode) + nkTemplateDef, quotedBlock.info, body = quotedBlock, + params = c.graph.emptyNode, + name = newAnonSym(c, skTemplate, n.info).newSymNode, + pattern = c.graph.emptyNode, genericParams = c.graph.emptyNode, + pragmas = c.graph.emptyNode, exceptions = c.graph.emptyNode) if ids.len > 0: dummyTemplate.sons[paramsPos] = newNodeI(nkFormalParams, n.info) dummyTemplate[paramsPos].add getSysSym(c.graph, n.info, "typed").newSymNode # return type ids.add getSysSym(c.graph, n.info, "untyped").newSymNode # params type - ids.add emptyNode # no default value + ids.add c.graph.emptyNode # no default value dummyTemplate[paramsPos].add newNode(nkIdentDefs, n.info, ids) var tmpl = semTemplateDef(c, dummyTemplate) @@ -1780,9 +1882,9 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = openScope(c) let oldOwnerLen = len(c.graph.owners) let oldGenerics = c.generics - let oldErrorOutputs = errorOutputs - if efExplain notin flags: errorOutputs = {} - let oldContextLen = msgs.getInfoContextLen() + let oldErrorOutputs = c.config.m.errorOutputs + if efExplain notin flags: c.config.m.errorOutputs = {} + let oldContextLen = msgs.getInfoContextLen(c.config) let oldInGenericContext = c.inGenericContext let oldInUnrolledContext = c.inUnrolledContext @@ -1804,10 +1906,10 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = c.inGenericInst = oldInGenericInst c.inStaticContext = oldInStaticContext c.p = oldProcCon - msgs.setInfoContextLen(oldContextLen) + msgs.setInfoContextLen(c.config, oldContextLen) setLen(c.graph.owners, oldOwnerLen) c.currentScope = oldScope - errorOutputs = oldErrorOutputs + c.config.m.errorOutputs = oldErrorOutputs c.config.errorCounter = oldErrorCount c.config.errorMax = oldErrorMax @@ -1913,7 +2015,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = result.typ = typ result.add instantiateCreateFlowVarCall(c, typ, n.info).newSymNode else: - result.add emptyNode + result.add c.graph.emptyNode of mProcCall: result = setMs(n, s) result.sons[1] = semExpr(c, n.sons[1]) @@ -1937,17 +2039,17 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = of mRunnableExamples: if c.config.cmd == cmdDoc and n.len >= 2 and n.lastSon.kind == nkStmtList: if sfMainModule in c.module.flags: - let inp = toFullPath(c.module.info) + let inp = toFullPath(c.config, c.module.info) if c.runnableExamples == nil: c.runnableExamples = newTree(nkStmtList, newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp)))) let imports = newTree(nkStmtList) extractImports(n.lastSon, imports) for imp in imports: c.runnableExamples.add imp - c.runnableExamples.add newTree(nkBlockStmt, emptyNode, copyTree n.lastSon) + c.runnableExamples.add newTree(nkBlockStmt, c.graph.emptyNode, copyTree n.lastSon) result = setMs(n, s) else: - result = emptyNode + result = c.graph.emptyNode else: result = semDirectOp(c, n, flags) @@ -2040,7 +2142,7 @@ proc semSetConstr(c: PContext, n: PNode): PNode = if not isOrdinalType(typ): localError(c.config, n.info, errOrdinalTypeExpected) typ = makeRangeType(c, 0, MaxSetElements-1, n.info) - elif lengthOrd(typ) > MaxSetElements: + elif lengthOrd(c.config, typ) > MaxSetElements: typ = makeRangeType(c, 0, MaxSetElements-1, n.info) addSonSkipIntLit(result.typ, typ) for i in countup(0, sonsLen(n) - 1): @@ -2159,7 +2261,7 @@ proc semBlock(c: PContext, n: PNode): PNode = addDecl(c, labl) n.sons[0] = newSymNode(labl, n.sons[0].info) suggestSym(c.config, n.sons[0].info, labl, c.graph.usageSym) - styleCheckDef(labl) + styleCheckDef(c.config, labl) n.sons[1] = semExpr(c, n.sons[1]) n.typ = n.sons[1].typ if isEmptyType(n.typ): n.kind = nkBlockStmt @@ -2306,7 +2408,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # check if it is an expression macro: checkMinSonsLen(n, 1, c.config) #when defined(nimsuggest): - # if gIdeCmd == ideCon and gTrackPos == n.info: suggestExprNoCheck(c, n) + # if gIdeCmd == ideCon and c.config.m.trackPos == n.info: suggestExprNoCheck(c, n) let mode = if nfDotField in n.flags: {} else: {checkUndeclared} var s = qualifiedLookUp(c, n.sons[0], mode) if s != nil: @@ -2363,12 +2465,12 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = checkMinSonsLen(n, 1, c.config) result = semArrayAccess(c, n, flags) of nkCurlyExpr: - result = semExpr(c, buildOverloadedSubscripts(n, getIdent"{}"), flags) + result = semExpr(c, buildOverloadedSubscripts(n, getIdent(c.cache, "{}")), flags) of nkPragmaExpr: var expr = n[0] pragma = n[1] - pragmaName = considerQuotedIdent(c.config, pragma[0]) + pragmaName = considerQuotedIdent(c, pragma[0]) flags = flags case whichKeyword(pragmaName) @@ -2424,8 +2526,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # handling of sym choices is context dependent # the node is left intact for now discard - of nkStaticExpr: - result = semStaticExpr(c, n) + of nkStaticExpr: result = semStaticExpr(c, n[0]) of nkAsgn: result = semAsgn(c, n) of nkBlockStmt, nkBlockExpr: result = semBlock(c, n) of nkStmtList, nkStmtListExpr: result = semStmtList(c, n, flags) diff --git a/compiler/semfields.nim b/compiler/semfields.nim index 16d79c559..869f5ae74 100644 --- a/compiler/semfields.nim +++ b/compiler/semfields.nim @@ -23,10 +23,10 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode = of nkEmpty..pred(nkIdent), succ(nkSym)..nkNilLit: result = n of nkIdent, nkSym: result = n - let ident = considerQuotedIdent(c.c.config, n) + let ident = considerQuotedIdent(c.c, n) var L = sonsLen(forLoop) if c.replaceByFieldName: - if ident.id == considerQuotedIdent(c.c.config, forLoop[0]).id: + if ident.id == considerQuotedIdent(c.c, forLoop[0]).id: let fieldName = if c.tupleType.isNil: c.field.name.s elif c.tupleType.n.isNil: "Field" & $c.tupleIndex else: c.tupleType.n.sons[c.tupleIndex].sym.name.s @@ -34,7 +34,7 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode = return # other fields: for i in ord(c.replaceByFieldName)..L-3: - if ident.id == considerQuotedIdent(c.c.config, forLoop[i]).id: + if ident.id == considerQuotedIdent(c.c, forLoop[i]).id: var call = forLoop.sons[L-2] var tupl = call.sons[i+1-ord(c.replaceByFieldName)] if c.field.isNil: @@ -107,10 +107,10 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode = # so that 'break' etc. work as expected, we produce # a 'while true: stmt; break' loop ... result = newNodeI(nkWhileStmt, n.info, 2) - var trueSymbol = strTableGet(c.graph.systemModule.tab, getIdent"true") + var trueSymbol = strTableGet(c.graph.systemModule.tab, getIdent(c.cache, "true")) if trueSymbol == nil: localError(c.config, n.info, "system needs: 'true'") - trueSymbol = newSym(skUnknown, getIdent"true", getCurrOwner(c), n.info) + trueSymbol = newSym(skUnknown, getIdent(c.cache, "true"), getCurrOwner(c), n.info) trueSymbol.typ = getSysType(c.graph, n.info, tyBool) result.sons[0] = newSymNode(trueSymbol, n.info) @@ -162,7 +162,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode = # we avoid it now if we can: if containsNode(stmts, {nkBreakStmt}): var b = newNodeI(nkBreakStmt, n.info) - b.add(ast.emptyNode) + b.add(newNodeI(nkEmpty, n.info)) stmts.add(b) else: result = stmts diff --git a/compiler/semfold.nim b/compiler/semfold.nim index daf9ce983..2f495bc7f 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -13,7 +13,7 @@ import strutils, options, ast, astalgo, trees, treetab, nimsets, times, nversion, platform, math, msgs, os, condsyms, idents, renderer, types, - commands, magicsys, modulegraphs, strtabs + commands, magicsys, modulegraphs, strtabs, lineinfos proc newIntNodeT*(intVal: BiggestInt, n: PNode; g: ModuleGraph): PNode = case skipTypes(n.typ, abstractVarRange).kind @@ -53,24 +53,24 @@ proc getConstExpr*(m: PSym, n: PNode; g: ModuleGraph): PNode # expression proc evalOp*(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode -proc checkInRange(n: PNode, res: BiggestInt): bool = - if res in firstOrd(n.typ)..lastOrd(n.typ): +proc checkInRange(conf: ConfigRef; n: PNode, res: BiggestInt): bool = + if res in firstOrd(conf, n.typ)..lastOrd(conf, n.typ): result = true proc foldAdd(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode = let res = a +% b if ((res xor a) >= 0'i64 or (res xor b) >= 0'i64) and - checkInRange(n, res): + checkInRange(g.config, n, res): result = newIntNodeT(res, n, g) proc foldSub*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode = let res = a -% b if ((res xor a) >= 0'i64 or (res xor not b) >= 0'i64) and - checkInRange(n, res): + checkInRange(g.config, n, res): result = newIntNodeT(res, n, g) proc foldAbs*(a: BiggestInt, n: PNode; g: ModuleGraph): PNode = - if a != firstOrd(n.typ): + if a != firstOrd(g.config, n.typ): result = newIntNodeT(a, n, g) proc foldMod*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode = @@ -82,7 +82,7 @@ proc foldModU*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode = result = newIntNodeT(a %% b, n, g) proc foldDiv*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode = - if b != 0'i64 and (a != firstOrd(n.typ) or b != -1'i64): + if b != 0'i64 and (a != firstOrd(g.config, n.typ) or b != -1'i64): result = newIntNodeT(a div b, n, g) proc foldDivU*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode = @@ -96,7 +96,7 @@ proc foldMul*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode = # Fast path for normal case: small multiplicands, and no info # is lost in either method. - if resAsFloat == floatProd and checkInRange(n, res): + if resAsFloat == floatProd and checkInRange(g.config, n, res): return newIntNodeT(res, n, g) # Somebody somewhere lost info. Close enough, or way off? Note @@ -107,7 +107,7 @@ proc foldMul*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode = # abs(diff)/abs(prod) <= 1/32 iff # 32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough" if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd) and - checkInRange(n, res): + checkInRange(g.config, n, res): return newIntNodeT(res, n, g) proc ordinalValToString*(a: PNode; g: ModuleGraph): string = @@ -210,9 +210,14 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode = of mUnaryMinusI, mUnaryMinusI64: result = newIntNodeT(- getInt(a), n, g) of mUnaryMinusF64: result = newFloatNodeT(- getFloat(a), n, g) of mNot: result = newIntNodeT(1 - getInt(a), n, g) - of mCard: result = newIntNodeT(nimsets.cardSet(a), n, g) - of mBitnotI: result = newIntNodeT(not getInt(a), n, g) - of mLengthArray: result = newIntNodeT(lengthOrd(a.typ), n, g) + of mCard: result = newIntNodeT(nimsets.cardSet(g.config, a), n, g) + of mBitnotI: + case skipTypes(n.typ, abstractRange).kind + of tyUInt..tyUInt64: + result = newIntNodeT((not getInt(a)) and lastOrd(g.config, a.typ, fixedUnsigned=true), n, g) + else: + result = newIntNodeT(not getInt(a), n, g) + of mLengthArray: result = newIntNodeT(lengthOrd(g.config, a.typ), n, g) of mLengthSeq, mLengthOpenArray, mXLenSeq, mLengthStr, mXLenStr: if a.kind == nkNilLit: result = newIntNodeT(0, n, g) @@ -229,7 +234,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode = of mAbsI: result = foldAbs(getInt(a), n, g) of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64: # byte(-128) = 1...1..1000_0000'64 --> 0...0..1000_0000'64 - result = newIntNodeT(getInt(a) and (`shl`(1, getSize(a.typ) * 8) - 1), n, g) + result = newIntNodeT(getInt(a) and (`shl`(1, getSize(g.config, a.typ) * 8) - 1), n, g) of mToU8: result = newIntNodeT(getInt(a) and 0x000000FF, n, g) of mToU16: result = newIntNodeT(getInt(a) and 0x0000FFFF, n, g) of mToU32: result = newIntNodeT(getInt(a) and 0x00000000FFFFFFFF'i64, n, g) @@ -250,8 +255,10 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode = of tyInt8: result = newIntNodeT(int8(getInt(a)) shl int8(getInt(b)), n, g) of tyInt16: result = newIntNodeT(int16(getInt(a)) shl int16(getInt(b)), n, g) of tyInt32: result = newIntNodeT(int32(getInt(a)) shl int32(getInt(b)), n, g) - of tyInt64, tyInt, tyUInt..tyUInt64: + of tyInt64, tyInt: result = newIntNodeT(`shl`(getInt(a), getInt(b)), n, g) + of tyUInt..tyUInt64: + result = newIntNodeT(`shl`(getInt(a), getInt(b)) and lastOrd(g.config, a.typ, fixedUnsigned=true), n, g) else: internalError(g.config, n.info, "constant folding for shl") of mShrI: case skipTypes(n.typ, abstractRange).kind @@ -304,21 +311,21 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode = of mMulU: result = newIntNodeT(`*%`(getInt(a), getInt(b)), n, g) of mModU: result = foldModU(getInt(a), getInt(b), n, g) of mDivU: result = foldDivU(getInt(a), getInt(b), n, g) - of mLeSet: result = newIntNodeT(ord(containsSets(a, b)), n, g) - of mEqSet: result = newIntNodeT(ord(equalSets(a, b)), n, g) + of mLeSet: result = newIntNodeT(ord(containsSets(g.config, a, b)), n, g) + of mEqSet: result = newIntNodeT(ord(equalSets(g.config, a, b)), n, g) of mLtSet: - result = newIntNodeT(ord(containsSets(a, b) and not equalSets(a, b)), n, g) + result = newIntNodeT(ord(containsSets(g.config, a, b) and not equalSets(g.config, a, b)), n, g) of mMulSet: - result = nimsets.intersectSets(a, b) + result = nimsets.intersectSets(g.config, a, b) result.info = n.info of mPlusSet: - result = nimsets.unionSets(a, b) + result = nimsets.unionSets(g.config, a, b) result.info = n.info of mMinusSet: - result = nimsets.diffSets(a, b) + result = nimsets.diffSets(g.config, a, b) result.info = n.info of mSymDiffSet: - result = nimsets.symdiffSets(a, b) + result = nimsets.symdiffSets(g.config, a, b) result.info = n.info of mConStrStr: result = newStrNodeT(getStrOrChar(a) & getStrOrChar(b), n, g) of mInSet: result = newIntNodeT(ord(inSet(a, b)), n, g) @@ -415,9 +422,9 @@ proc getAppType(n: PNode; g: ModuleGraph): PNode = proc rangeCheck(n: PNode, value: BiggestInt; g: ModuleGraph) = var err = false if n.typ.skipTypes({tyRange}).kind in {tyUInt..tyUInt64}: - err = value <% firstOrd(n.typ) or value >% lastOrd(n.typ, fixedUnsigned=true) + err = value <% firstOrd(g.config, n.typ) or value >% lastOrd(g.config, n.typ, fixedUnsigned=true) else: - err = value < firstOrd(n.typ) or value > lastOrd(n.typ) + err = value < firstOrd(g.config, n.typ) or value > lastOrd(g.config, n.typ) if err: localError(g.config, n.info, "cannot convert " & $value & " to " & typeToString(n.typ)) @@ -472,7 +479,7 @@ proc foldArrayAccess(m: PSym, n: PNode; g: ModuleGraph): PNode = else: localError(g.config, n.info, "index out of bounds: " & $n) of nkBracket: - idx = idx - x.typ.firstOrd + idx = idx - firstOrd(g.config, x.typ) if idx >= 0 and idx < x.len: result = x.sons[int(idx)] else: localError(g.config, n.info, "index out of bounds: " & $n) of nkStrLit..nkTripleStrLit: @@ -547,11 +554,11 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode = "yyyy-MM-dd"), n, g) of mCompileTime: result = newStrNodeT(format(getSrcTimestamp(), "HH:mm:ss"), n, g) - of mCpuEndian: result = newIntNodeT(ord(CPU[targetCPU].endian), n, g) - of mHostOS: result = newStrNodeT(toLowerAscii(platform.OS[targetOS].name), n, g) - of mHostCPU: result = newStrNodeT(platform.CPU[targetCPU].name.toLowerAscii, n, g) - of mBuildOS: result = newStrNodeT(toLowerAscii(platform.OS[platform.hostOS].name), n, g) - of mBuildCPU: result = newStrNodeT(platform.CPU[platform.hostCPU].name.toLowerAscii, n, g) + of mCpuEndian: result = newIntNodeT(ord(CPU[g.config.target.targetCPU].endian), n, g) + of mHostOS: result = newStrNodeT(toLowerAscii(platform.OS[g.config.target.targetOS].name), n, g) + of mHostCPU: result = newStrNodeT(platform.CPU[g.config.target.targetCPU].name.toLowerAscii, n, g) + of mBuildOS: result = newStrNodeT(toLowerAscii(platform.OS[g.config.target.hostOS].name), n, g) + of mBuildCPU: result = newStrNodeT(platform.CPU[g.config.target.hostCPU].name.toLowerAscii, n, g) of mAppType: result = getAppType(n, g) of mNaN: result = newFloatNodeT(NaN, n, g) of mInf: result = newFloatNodeT(Inf, n, g) @@ -599,22 +606,22 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode = return of mSizeOf: var a = n.sons[1] - if computeSize(a.typ) < 0: + if computeSize(g.config, a.typ) < 0: localError(g.config, a.info, "cannot evaluate 'sizeof' because its type is not defined completely") result = nil elif skipTypes(a.typ, typedescInst+{tyRange}).kind in IntegralTypes+NilableTypes+{tySet}: #{tyArray,tyObject,tyTuple}: - result = newIntNodeT(getSize(a.typ), n, g) + result = newIntNodeT(getSize(g.config, a.typ), n, g) else: result = nil # XXX: size computation for complex types is still wrong of mLow: - result = newIntNodeT(firstOrd(n.sons[1].typ), n, g) + result = newIntNodeT(firstOrd(g.config, n.sons[1].typ), n, g) of mHigh: - if skipTypes(n.sons[1].typ, abstractVar).kind notin + if skipTypes(n.sons[1].typ, abstractVar+{tyUserTypeClassInst}).kind notin {tySequence, tyString, tyCString, tyOpenArray, tyVarargs}: - result = newIntNodeT(lastOrd(skipTypes(n[1].typ, abstractVar)), n, g) + result = newIntNodeT(lastOrd(g.config, skipTypes(n[1].typ, abstractVar)), n, g) else: var a = getArrayConstr(m, n.sons[1], g) if a.kind == nkBracket: @@ -630,7 +637,7 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode = of mLengthArray: # It doesn't matter if the argument is const or not for mLengthArray. # This fixes bug #544. - result = newIntNodeT(lengthOrd(n.sons[1].typ), n, g) + result = newIntNodeT(lengthOrd(g.config, n.sons[1].typ), n, g) of mAstToStr: result = newStrNodeT(renderTree(n[1], {renderNoComments}), n, g) of mConStrStr: diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 8f06e748e..0218096d6 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -32,9 +32,12 @@ type cursorInBody: bool # only for nimsuggest bracketExpr: PNode -type TSemGenericFlag = enum - withinBind, withinTypeDesc, withinMixin, withinConcept + withinBind, + withinTypeDesc, + withinMixin, + withinConcept + TSemGenericFlags = set[TSemGenericFlag] proc semGenericStmt(c: PContext, n: PNode, @@ -53,9 +56,16 @@ template macroToExpand(s): untyped = template macroToExpandSym(s): untyped = s.kind in {skMacro, skTemplate} and (s.typ.len == 1) and not fromDotExpr +template isMixedIn(sym): bool = + let s = sym + s.name.id in ctx.toMixin or (withinConcept in flags and + s.magic == mNone and + s.kind in OverloadableSyms) + proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, - ctx: var GenericCtx; fromDotExpr=false): PNode = - semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody) + ctx: var GenericCtx; flags: TSemGenericFlags, + fromDotExpr=false): PNode = + semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody) incl(s.flags, sfUsed) case s.kind of skUnknown: @@ -103,7 +113,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags, ctx: var GenericCtx): PNode = result = n - let ident = considerQuotedIdent(c.config, n) + let ident = considerQuotedIdent(c, n) var s = searchInScopes(c, ident).skipAlias(n, c.config) if s == nil: s = strTableGet(c.pureEnumFields, ident) @@ -115,10 +125,10 @@ proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags, else: if withinBind in flags: result = symChoice(c, n, s, scClosed) - elif s.name.id in ctx.toMixin: + elif s.isMixedIn: result = symChoice(c, n, s, scForceOpen) else: - result = semGenericStmtSymbol(c, n, s, ctx) + result = semGenericStmtSymbol(c, n, s, ctx, flags) # else: leave as nkIdent proc newDot(n, b: PNode): PNode = @@ -129,27 +139,27 @@ proc newDot(n, b: PNode): PNode = proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, ctx: var GenericCtx; isMacro: var bool): PNode = assert n.kind == nkDotExpr - semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody) + semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody) let luf = if withinMixin notin flags: {checkUndeclared, checkModule} else: {checkModule} var s = qualifiedLookUp(c, n, luf) if s != nil: - result = semGenericStmtSymbol(c, n, s, ctx) + result = semGenericStmtSymbol(c, n, s, ctx, flags) else: n.sons[0] = semGenericStmt(c, n.sons[0], flags, ctx) result = n let n = n[1] - let ident = considerQuotedIdent(c.config, n) + let ident = considerQuotedIdent(c, n) var s = searchInScopes(c, ident).skipAlias(n, c.config) if s != nil and s.kind in routineKinds: isMacro = s.kind in {skTemplate, skMacro} if withinBind in flags: result = newDot(result, symChoice(c, n, s, scClosed)) - elif s.name.id in ctx.toMixin: + elif s.isMixedIn: result = newDot(result, symChoice(c, n, s, scForceOpen)) else: - let syms = semGenericStmtSymbol(c, n, s, ctx, fromDotExpr=true) + let syms = semGenericStmtSymbol(c, n, s, ctx, flags, fromDotExpr=true) if syms.kind == nkSym: let choice = symChoice(c, n, s, scForceOpen) choice.kind = nkClosedSymChoice @@ -160,7 +170,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) = let s = newSymS(skUnknown, getIdentNode(c, n), c) addPrelimDecl(c, s) - styleCheckDef(n.info, s, kind) + styleCheckDef(c.config, n.info, s, kind) proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags, ctx: var GenericCtx): PNode = @@ -170,7 +180,7 @@ proc semGenericStmt(c: PContext, n: PNode, if withinTypeDesc in flags: inc c.inTypeContext #if conf.cmd == cmdIdeTools: suggestStmt(c, n) - semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody) + semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody) case n.kind of nkIdent, nkAccQuoted: @@ -207,7 +217,7 @@ proc semGenericStmt(c: PContext, n: PNode, if s == nil and {withinMixin, withinConcept}*flags == {} and fn.kind in {nkIdent, nkAccQuoted} and - considerQuotedIdent(c.config, fn).id notin ctx.toMixin: + considerQuotedIdent(c, fn).id notin ctx.toMixin: errorUndeclaredIdentifier(c, n.info, fn.renderTree) var first = int ord(withinConcept in flags) @@ -215,8 +225,7 @@ proc semGenericStmt(c: PContext, n: PNode, if s != nil: incl(s.flags, sfUsed) mixinContext = s.magic in {mDefined, mDefinedInScope, mCompiles} - let sc = symChoice(c, fn, s, - if s.name.id in ctx.toMixin: scForceOpen else: scOpen) + let sc = symChoice(c, fn, s, if s.isMixedIn: scForceOpen else: scOpen) case s.kind of skMacro: if macroToExpand(s) and sc.safeLen <= 1: @@ -275,12 +284,12 @@ proc semGenericStmt(c: PContext, n: PNode, result.sons[i] = semGenericStmt(c, result.sons[i], flags, ctx) of nkCurlyExpr: result = newNodeI(nkCall, n.info) - result.add newIdentNode(getIdent("{}"), n.info) + result.add newIdentNode(getIdent(c.cache, "{}"), n.info) for i in 0 ..< n.len: result.add(n[i]) result = semGenericStmt(c, result, flags, ctx) of nkBracketExpr: result = newNodeI(nkCall, n.info) - result.add newIdentNode(getIdent("[]"), n.info) + result.add newIdentNode(getIdent(c.cache, "[]"), n.info) for i in 0 ..< n.len: result.add(n[i]) withBracketExpr ctx, n.sons[0]: result = semGenericStmt(c, result, flags, ctx) @@ -293,13 +302,13 @@ proc semGenericStmt(c: PContext, n: PNode, case k of nkCurlyExpr: result = newNodeI(nkCall, n.info) - result.add newIdentNode(getIdent("{}="), n.info) + result.add newIdentNode(getIdent(c.cache, "{}="), n.info) for i in 0 ..< a.len: result.add(a[i]) result.add(b) result = semGenericStmt(c, result, flags, ctx) of nkBracketExpr: result = newNodeI(nkCall, n.info) - result.add newIdentNode(getIdent("[]="), n.info) + result.add newIdentNode(getIdent(c.cache, "[]="), n.info) for i in 0 ..< a.len: result.add(a[i]) result.add(b) withBracketExpr ctx, a.sons[0]: @@ -448,7 +457,7 @@ proc semGenericStmt(c: PContext, n: PNode, flags, ctx) if n.sons[paramsPos].kind != nkEmpty: if n.sons[paramsPos].sons[0].kind != nkEmpty: - addPrelimDecl(c, newSym(skUnknown, getIdent("result"), nil, n.info)) + addPrelimDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nil, n.info)) n.sons[paramsPos] = semGenericStmt(c, n.sons[paramsPos], flags, ctx) n.sons[pragmasPos] = semGenericStmt(c, n.sons[pragmasPos], flags, ctx) var body: PNode @@ -472,7 +481,6 @@ proc semGenericStmt(c: PContext, n: PNode, when defined(nimsuggest): if withinTypeDesc in flags: dec c.inTypeContext - proc semGenericStmt(c: PContext, n: PNode): PNode = var ctx: GenericCtx ctx.toMixin = initIntset() @@ -484,3 +492,4 @@ proc semConceptBody(c: PContext, n: PNode): PNode = ctx.toMixin = initIntset() result = semGenericStmt(c, n, {withinConcept}, ctx) semIdeForTemplateOrGeneric(c, result, ctx.cursorInBody) + diff --git a/compiler/seminst.nim b/compiler/seminst.nim index a5f0ca7d8..fac04e3a0 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -159,13 +159,13 @@ proc fixupInstantiatedSymbols(c: PContext, s: PSym) = var oldPrc = c.generics[i].inst.sym pushProcCon(c, oldPrc) pushOwner(c, oldPrc) - pushInfoContext(oldPrc.info) + pushInfoContext(c.config, oldPrc.info) openScope(c) var n = oldPrc.ast n.sons[bodyPos] = copyTree(s.getBody) instantiateBody(c, n, oldPrc.typ.n, oldPrc, s) closeScope(c) - popInfoContext() + popInfoContext(c.config) popOwner(c) popProcCon(c) @@ -220,6 +220,14 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType, result = replaceTypeVarsT(cl, header) closeScope(c) +proc referencesAnotherParam(n: PNode, p: PSym): bool = + if n.kind == nkSym: + return n.sym.kind == skParam and n.sym.owner == p + else: + for i in 0..<n.safeLen: + if referencesAnotherParam(n[i], p): return true + return false + proc instantiateProcType(c: PContext, pt: TIdTable, prc: PSym, info: TLineInfo) = # XXX: Instantiates a generic proc signature, while at the same @@ -236,7 +244,7 @@ proc instantiateProcType(c: PContext, pt: TIdTable, # at this point semtypinst have to become part of sem, because it # will need to use openScope, addDecl, etc. #addDecl(c, prc) - pushInfoContext(info) + pushInfoContext(c.config, info) var typeMap = initLayeredTypeMap(pt) var cl = initTypeVars(c, addr(typeMap), info, nil) var result = instCopyType(cl, prc.typ) @@ -247,25 +255,56 @@ proc instantiateProcType(c: PContext, pt: TIdTable, if i > 1: resetIdTable(cl.symMap) resetIdTable(cl.localCache) - result.sons[i] = replaceTypeVarsT(cl, result.sons[i]) - propagateToOwner(result, result.sons[i]) + + # take a note of the original type. If't a free type or static parameter + # we'll need to keep it unbound for the `fitNode` operation below... + var typeToFit = result[i] + + let needsStaticSkipping = result[i].kind == tyFromExpr + result[i] = replaceTypeVarsT(cl, result[i]) + if needsStaticSkipping: + result[i] = result[i].skipTypes({tyStatic}) + + # ...otherwise, we use the instantiated type in `fitNode` + if (typeToFit.kind != tyTypeDesc or typeToFit.base.kind != tyNone) and + (typeToFit.kind != tyStatic): + typeToFit = result[i] + internalAssert c.config, originalParams[i].kind == nkSym - when true: - let oldParam = originalParams[i].sym - let param = copySym(oldParam) - param.owner = prc - param.typ = result.sons[i] - if oldParam.ast != nil: - param.ast = fitNode(c, param.typ, oldParam.ast, oldParam.ast.info) - - # don't be lazy here and call replaceTypeVarsN(cl, originalParams[i])! - result.n.sons[i] = newSymNode(param) - addDecl(c, param) - else: - let param = replaceTypeVarsN(cl, originalParams[i]) - result.n.sons[i] = param - param.sym.owner = prc - addDecl(c, result.n.sons[i].sym) + let oldParam = originalParams[i].sym + let param = copySym(oldParam) + param.owner = prc + param.typ = result[i] + + # The default value is instantiated and fitted against the final + # concrete param type. We avoid calling `replaceTypeVarsN` on the + # call head symbol, because this leads to infinite recursion. + if oldParam.ast != nil: + var def = oldParam.ast.copyTree + if def.kind == nkCall: + for i in 1 ..< def.len: + def[i] = replaceTypeVarsN(cl, def[i]) + + def = semExprWithType(c, def) + if def.referencesAnotherParam(getCurrOwner(c)): + def.flags.incl nfDefaultRefsParam + + var converted = indexTypesMatch(c, typeToFit, def.typ, def) + if converted == nil: + # The default value doesn't match the final instantiated type. + # As an example of this, see: + # https://github.com/nim-lang/Nim/issues/1201 + # We are replacing the default value with an error node in case + # the user calls an explicit instantiation of the proc (this is + # the only way the default value might be inserted). + param.ast = errorNode(c, def) + else: + param.ast = fitNodePostMatch(c, typeToFit, converted) + param.typ = result[i] + + result.n[i] = newSymNode(param) + propagateToOwner(result, result[i]) + addDecl(c, param) resetIdTable(cl.symMap) resetIdTable(cl.localCache) @@ -278,7 +317,7 @@ proc instantiateProcType(c: PContext, pt: TIdTable, skipIntLiteralParams(result) prc.typ = result - popInfoContext() + popInfoContext(c.config) proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, info: TLineInfo): PSym = @@ -309,7 +348,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, let gp = n.sons[genericParamsPos] internalAssert c.config, gp.kind != nkEmpty n.sons[namePos] = newSymNode(result) - pushInfoContext(info) + pushInfoContext(c.config, info) var entry = TInstantiation.new entry.sym = result # we need to compare both the generic types and the concrete types: @@ -328,7 +367,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, inc i if tfTriggersCompileTime in result.typ.flags: incl(result.flags, sfCompileTime) - n.sons[genericParamsPos] = ast.emptyNode + n.sons[genericParamsPos] = c.graph.emptyNode var oldPrc = genericCacheGet(fn, entry[], c.compilesContextId) if oldPrc == nil: # we MUST not add potentially wrong instantiations to the caching mechanism. @@ -353,7 +392,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, else: result = oldPrc popProcCon(c) - popInfoContext() + popInfoContext(c.config) closeScope(c) # close scope for parameters popOwner(c) c.currentScope = oldScope diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 8515e971d..1975fb77b 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -41,7 +41,7 @@ proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode = result = semSubscript(c, result, flags) if result.isNil: let x = copyTree(n) - x.sons[0] = newIdentNode(getIdent"[]", n.info) + x.sons[0] = newIdentNode(getIdent(c.cache, "[]"), n.info) bracketNotFoundError(c, x) #localError(c.config, n.info, "could not resolve: " & $n) result = n @@ -76,9 +76,9 @@ proc semInstantiationInfo(c: PContext, n: PNode): PNode = result = newNodeIT(nkTupleConstr, n.info, n.typ) let idx = expectIntLit(c, n.sons[1]) let useFullPaths = expectIntLit(c, n.sons[2]) - let info = getInfoContext(idx) + let info = getInfoContext(c.config, idx) var filename = newNodeIT(nkStrLit, n.info, getSysType(c.graph, n.info, tyString)) - filename.strVal = if useFullPaths != 0: info.toFullPath else: info.toFilename + filename.strVal = if useFullPaths != 0: toFullPath(c.config, info) else: toFilename(c.config, info) var line = newNodeIT(nkIntLit, n.info, getSysType(c.graph, n.info, tyInt)) line.intVal = toLinenumber(info) var column = newNodeIT(nkIntLit, n.info, getSysType(c.graph, n.info, tyInt)) @@ -154,7 +154,7 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym) result = newIntNodeT(ord(not complexObj), traitCall, c.graph) else: localError(c.config, traitCall.info, "unknown trait") - result = emptyNode + result = newNodeI(nkEmpty, traitCall.info) proc semTypeTraits(c: PContext, n: PNode): PNode = checkMinSonsLen(n, 2, c.config) @@ -174,7 +174,7 @@ proc semOrd(c: PContext, n: PNode): PNode = if isOrdinalType(parType): discard elif parType.kind == tySet: - result.typ = makeRangeType(c, firstOrd(parType), lastOrd(parType), n.info) + result.typ = makeRangeType(c, firstOrd(c.config, parType), lastOrd(c.config, parType), n.info) else: localError(c.config, n.info, errOrdinalTypeExpected) result.typ = errorType(c) @@ -194,7 +194,7 @@ proc semBindSym(c: PContext, n: PNode): PNode = localError(c.config, n.sons[2].info, errConstExprExpected) return errorNode(c, n) - let id = newIdentNode(getIdent(sl.strVal), n.info) + let id = newIdentNode(getIdent(c.cache, sl.strVal), n.info) let s = qualifiedLookUp(c, id, {checkUndeclared}) if s != nil: # we need to mark all symbols: @@ -209,10 +209,6 @@ proc semBindSym(c: PContext, n: PNode): PNode = proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode -proc isStrangeArray(t: PType): bool = - let t = t.skipTypes(abstractInst) - result = t.kind == tyArray and t.firstOrd != 0 - proc semOf(c: PContext, n: PNode): PNode = if sonsLen(n) == 3: n.sons[1] = semExprWithType(c, n.sons[1]) @@ -283,7 +279,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, of mRoof: localError(c.config, n.info, "builtin roof operator is not supported anymore") of mPlugin: - let plugin = getPlugin(n[0].sym) + let plugin = getPlugin(c.cache, n[0].sym) if plugin.isNil: localError(c.config, n.info, "cannot find plugin " & n[0].sym.name.s) result = n diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index 1e0802953..8b639806d 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -54,7 +54,7 @@ proc locateFieldInInitExpr(c: PContext, field: PSym, initExpr: PNode): PNode = invalidObjConstr(c, assignment) continue - if fieldId == considerQuotedIdent(c.config, assignment[0]).id: + if fieldId == considerQuotedIdent(c, assignment[0]).id: return assignment proc semConstrField(c: PContext, flags: TExprFlags, @@ -295,11 +295,11 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = if field.kind != nkExprColonExpr: invalidObjConstr(c, field) continue - let id = considerQuotedIdent(c.config, field[0]) + let id = considerQuotedIdent(c, field[0]) # This node was not processed. There are two possible reasons: # 1) It was shadowed by a field with the same name on the left for j in 1 ..< i: - let prevId = considerQuotedIdent(c.config, result[j][0]) + let prevId = considerQuotedIdent(c, result[j][0]) if prevId.id == id.id: localError(c.config, field.info, errFieldInitTwice % id.s) return diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim index ea5aab628..0d780bdee 100644 --- a/compiler/semparallel.nim +++ b/compiler/semparallel.nim @@ -136,8 +136,8 @@ proc checkLe(c: AnalysisCtx; a, b: PNode) = localError(c.graph.config, a.info, "can prove: " & ?a & " > " & ?b & " (bounds check)") proc checkBounds(c: AnalysisCtx; arr, idx: PNode) = - checkLe(c, arr.lowBound, idx) - checkLe(c, idx, arr.highBound(c.guards.o)) + checkLe(c, lowBound(c.graph.config, arr), idx) + checkLe(c, idx, highBound(c.graph.config, arr, c.guards.o)) proc addLowerBoundAsFacts(c: var AnalysisCtx) = for v in c.locals: @@ -438,7 +438,7 @@ proc transformSpawn(g: ModuleGraph; owner: PSym; n, barrier: PNode): PNode = let t = b[1][0].typ.sons[0] if spawnResult(t, true) == srByVar: result.add wrapProcForSpawn(g, owner, m, b.typ, barrier, it[0]) - it.sons[it.len-1] = emptyNode + it.sons[it.len-1] = newNodeI(nkEmpty, it.info) else: it.sons[it.len-1] = wrapProcForSpawn(g, owner, m, b.typ, barrier, nil) if result.isNil: result = n @@ -483,7 +483,7 @@ proc liftParallel*(g: ModuleGraph; owner: PSym; n: PNode): PNode = checkArgs(a, body) var varSection = newNodeI(nkVarSection, n.info) - var temp = newSym(skTemp, getIdent"barrier", owner, n.info) + var temp = newSym(skTemp, getIdent(g.cache, "barrier"), owner, n.info) temp.typ = magicsys.getCompilerProc(g, "Barrier").typ incl(temp.flags, sfFromGeneric) let tempNode = newSymNode(temp) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index b66d7d9f2..4d3ee0408 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -9,7 +9,7 @@ import intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, - wordrecg, strutils, options, guards, writetracking, configuration, + wordrecg, strutils, options, guards, writetracking, lineinfos, modulegraphs when defined(useDfa): @@ -185,7 +185,7 @@ proc markGcUnsafe(a: PEffects; reason: PNode) = if reason.kind == nkSym: a.owner.gcUnsafetyReason = reason.sym else: - a.owner.gcUnsafetyReason = newSym(skUnknown, getIdent("<unknown>"), + a.owner.gcUnsafetyReason = newSym(skUnknown, a.owner.name, a.owner, reason.info, {}) when true: @@ -410,7 +410,7 @@ proc effectSpec(n: PNode, effectType: TSpecialWord): PNode = result.add(it.sons[1]) return -proc documentEffect(n, x: PNode, effectType: TSpecialWord, idx: int): PNode = +proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, idx: int): PNode = let spec = effectSpec(x, effectType) if isNil(spec): let s = n.sons[namePos].sym @@ -424,14 +424,14 @@ proc documentEffect(n, x: PNode, effectType: TSpecialWord, idx: int): PNode = for i in 0 ..< real.len: var t = typeToString(real[i].typ) if t.startsWith("ref "): t = substr(t, 4) - effects.sons[i] = newIdentNode(getIdent(t), n.info) + effects.sons[i] = newIdentNode(getIdent(cache, t), n.info) # set the type so that the following analysis doesn't screw up: effects.sons[i].typ = real[i].typ result = newNode(nkExprColonExpr, n.info, @[ - newIdentNode(getIdent(specialWords[effectType]), n.info), effects]) + newIdentNode(getIdent(cache, specialWords[effectType]), n.info), effects]) -proc documentWriteEffect(n: PNode; flag: TSymFlag; pragmaName: string): PNode = +proc documentWriteEffect(cache: IdentCache; n: PNode; flag: TSymFlag; pragmaName: string): PNode = let s = n.sons[namePos].sym let params = s.typ.n @@ -442,21 +442,21 @@ proc documentWriteEffect(n: PNode; flag: TSymFlag; pragmaName: string): PNode = if effects.len > 0: result = newNode(nkExprColonExpr, n.info, @[ - newIdentNode(getIdent(pragmaName), n.info), effects]) + newIdentNode(getIdent(cache, pragmaName), n.info), effects]) -proc documentNewEffect(n: PNode): PNode = +proc documentNewEffect(cache: IdentCache; n: PNode): PNode = let s = n.sons[namePos].sym if tfReturnsNew in s.typ.flags: - result = newIdentNode(getIdent("new"), n.info) + result = newIdentNode(getIdent(cache, "new"), n.info) -proc documentRaises*(n: PNode) = +proc documentRaises*(cache: IdentCache; n: PNode) = if n.sons[namePos].kind != nkSym: return let pragmas = n.sons[pragmasPos] - let p1 = documentEffect(n, pragmas, wRaises, exceptionEffects) - let p2 = documentEffect(n, pragmas, wTags, tagEffects) - let p3 = documentWriteEffect(n, sfWrittenTo, "writes") - let p4 = documentNewEffect(n) - let p5 = documentWriteEffect(n, sfEscapes, "escapes") + let p1 = documentEffect(cache, n, pragmas, wRaises, exceptionEffects) + let p2 = documentEffect(cache, n, pragmas, wTags, tagEffects) + let p3 = documentWriteEffect(cache, n, sfWrittenTo, "writes") + let p4 = documentNewEffect(cache, n) + let p5 = documentWriteEffect(cache, n, sfEscapes, "escapes") if p1 != nil or p2 != nil or p3 != nil or p4 != nil or p5 != nil: if pragmas.kind == nkEmpty: @@ -859,9 +859,9 @@ proc checkRaisesSpec(g: ModuleGraph; spec, real: PNode, msg: string, hints: bool used.incl(s) break search # XXX call graph analysis would be nice here! - pushInfoContext(spec.info) + pushInfoContext(g.config, spec.info) localError(g.config, r.info, errGenerated, msg & typeToString(r.typ)) - popInfoContext() + popInfoContext(g.config) # hint about unnecessarily listed exception types: if hints: for s in 0 ..< spec.len: @@ -915,8 +915,8 @@ proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects) = newSeq(effects.sons, effectListLen) effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info) effects.sons[tagEffects] = newNodeI(nkArgList, s.info) - effects.sons[usesEffects] = ast.emptyNode - effects.sons[writeEffects] = ast.emptyNode + effects.sons[usesEffects] = g.emptyNode + effects.sons[writeEffects] = g.emptyNode t.exc = effects.sons[exceptionEffects] t.tags = effects.sons[tagEffects] diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 12337262a..2a959b8a8 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -36,8 +36,6 @@ const errRecursiveDependencyX = "recursive dependency: '$1'" errPragmaOnlyInHeaderOfProcX = "pragmas are only allowed in the header of a proc; redefinition of $1" -var enforceVoidContext = PType(kind: tyStmt) # XXX global variable here - proc semDiscard(c: PContext, n: PNode): PNode = result = n checkSonsLen(n, 1, c.config) @@ -87,15 +85,15 @@ proc semWhile(c: PContext, n: PNode): PNode = n.sons[1] = semStmt(c, n.sons[1]) dec(c.p.nestedLoopCounter) closeScope(c) - if n.sons[1].typ == enforceVoidContext: - result.typ = enforceVoidContext + if n.sons[1].typ == c.enforceVoidContext: + result.typ = c.enforceVoidContext -proc toCover(t: PType): BiggestInt = - var t2 = skipTypes(t, abstractVarRange-{tyTypeDesc}) +proc toCover(c: PContext, t: PType): BiggestInt = + let t2 = skipTypes(t, abstractVarRange-{tyTypeDesc}) if t2.kind == tyEnum and enumHasHoles(t2): result = sonsLen(t2.n) else: - result = lengthOrd(skipTypes(t, abstractVar-{tyTypeDesc})) + result = lengthOrd(c.config, skipTypes(t, abstractVar-{tyTypeDesc})) proc semProc(c: PContext, n: PNode): PNode @@ -146,7 +144,7 @@ proc discardCheck(c: PContext, result: PNode) = result.typ.typeToString & "' and has to be discarded" if result.info.line != n.info.line or result.info.fileIndex != n.info.fileIndex: - s.add "; start of expression here: " & $result.info + s.add "; start of expression here: " & c.config$result.info if result.typ.kind == tyProc: s.add "; for a function call use ()" localError(c.config, n.info, s) @@ -173,7 +171,7 @@ proc semIf(c: PContext, n: PNode): PNode = for it in n: discardCheck(c, it.lastSon) result.kind = nkIfStmt # propagate any enforced VoidContext: - if typ == enforceVoidContext: result.typ = enforceVoidContext + if typ == c.enforceVoidContext: result.typ = c.enforceVoidContext else: for it in n: let j = it.len-1 @@ -203,7 +201,7 @@ proc semCase(c: PContext, n: PNode): PNode = for i in countup(1, sonsLen(n) - 1): var x = n.sons[i] when defined(nimsuggest): - if c.config.ideCmd == ideSug and exactEquals(gTrackPos, x.info) and caseTyp.kind == tyEnum: + if c.config.ideCmd == ideSug and exactEquals(c.config.m.trackPos, x.info) and caseTyp.kind == tyEnum: suggestEnum(c, x, caseTyp) case x.kind of nkOfBranch: @@ -230,7 +228,7 @@ proc semCase(c: PContext, n: PNode): PNode = else: illFormedAst(x, c.config) if chckCovered: - if covered == toCover(n.sons[0].typ): + if covered == toCover(c, n.sons[0].typ): hasElse = true else: localError(c.config, n.info, "not all cases are covered") @@ -238,8 +236,8 @@ proc semCase(c: PContext, n: PNode): PNode = if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or not hasElse: for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon) # propagate any enforced VoidContext: - if typ == enforceVoidContext: - result.typ = enforceVoidContext + if typ == c.enforceVoidContext: + result.typ = c.enforceVoidContext else: for i in 1..n.len-1: var it = n.sons[i] @@ -318,8 +316,8 @@ proc semTry(c: PContext, n: PNode): PNode = if isEmptyType(typ) or typ.kind in {tyNil, tyExpr}: discardCheck(c, n.sons[0]) for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon) - if typ == enforceVoidContext: - result.typ = enforceVoidContext + if typ == c.enforceVoidContext: + result.typ = c.enforceVoidContext else: if n.lastSon.kind == nkFinally: discardCheck(c, n.lastSon.lastSon) n.sons[0] = fitNode(c, typ, n.sons[0], n.sons[0].info) @@ -366,7 +364,7 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym = if result.owner.kind == skModule: incl(result.flags, sfGlobal) suggestSym(c.config, n.info, result, c.graph.usageSym) - styleCheckDef(result) + styleCheckDef(c.config, result) proc checkNilable(c: PContext; v: PSym) = if {sfGlobal, sfImportC} * v.flags == {sfGlobal} and @@ -394,7 +392,7 @@ proc isDiscardUnderscore(v: PSym): bool = result = true proc semUsing(c: PContext; n: PNode): PNode = - result = ast.emptyNode + result = c.graph.emptyNode if not isTopLevel(c): localError(c.config, n.info, errXOnlyAtModuleScope % "using") for i in countup(0, sonsLen(n)-1): var a = n.sons[i] @@ -442,10 +440,10 @@ proc makeDeref(n: PNode): PNode = proc fillPartialObject(c: PContext; n: PNode; typ: PType) = if n.len == 2: let x = semExprWithType(c, n[0]) - let y = considerQuotedIdent(c.config, n[1]) + let y = considerQuotedIdent(c, n[1]) let obj = x.typ.skipTypes(abstractPtrs) if obj.kind == tyObject and tfPartial in obj.flags: - let field = newSym(skField, getIdent(y.s), obj.sym, n[1].info) + let field = newSym(skField, getIdent(c.cache, y.s), obj.sym, n[1].info) field.typ = skipIntLit(typ) field.position = sonsLen(obj.n) addSon(obj.n, newSymNode(field)) @@ -481,7 +479,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = typ = semTypeNode(c, a.sons[length-2], nil) else: typ = nil - var def: PNode = ast.emptyNode + var def: PNode = c.graph.emptyNode if a.sons[length-1].kind != nkEmpty: def = semExprWithType(c, a.sons[length-1], {efAllowDestructor}) if def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro: @@ -521,7 +519,9 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = localError(c.config, a.info, errWrongNumberOfVariables) b = newNodeI(nkVarTuple, a.info) newSons(b, length) - b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator + # keep type desc for doc generator + # NOTE: at the moment this is always ast.emptyNode, see parser.nim + b.sons[length-2] = a.sons[length-2] b.sons[length-1] = def addToVarSection(c, result, n, b) elif tup.kind == tyTuple and def.kind in {nkPar, nkTupleConstr} and @@ -560,7 +560,12 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = # keep documentation information: b.comment = a.comment addSon(b, newSymNode(v)) - addSon(b, a.sons[length-2]) # keep type desc for doc generator + # keep type desc for doc generator, but only if the user explicitly + # added it + if a.sons[length-2].kind != nkEmpty: + addSon(b, newNodeIT(nkType, a.info, typ)) + else: + addSon(b, a.sons[length-2]) addSon(b, copyTree(def)) addToVarSection(c, result, n, b) else: @@ -571,8 +576,12 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = b.sons[j] = newSymNode(v) checkNilable(c, v) if sfCompileTime in v.flags: hasCompileTime = true + if v.flags * {sfGlobal, sfThread} == {sfGlobal}: + message(c.config, v.info, hintGlobalVar) if hasCompileTime: - vm.setupCompileTimeVar(c.module, c.cache, c.graph, result) + vm.setupCompileTimeVar(c.module, c.graph, result) + # handled by the VM codegen: + #c.graph.recordStmt(c.graph, c.module, result) proc semConst(c: PContext, n: PNode): PNode = result = copyNode(n) @@ -625,7 +634,7 @@ proc addForVarDecl(c: PContext, v: PSym) = proc symForVar(c: PContext, n: PNode): PSym = let m = if n.kind == nkPragmaExpr: n.sons[0] else: n result = newSymG(skForVar, m, c) - styleCheckDef(result) + styleCheckDef(c.config, result) proc semForVars(c: PContext, n: PNode): PNode = result = n @@ -664,7 +673,7 @@ proc semForVars(c: PContext, n: PNode): PNode = proc implicitIterator(c: PContext, it: string, arg: PNode): PNode = result = newNodeI(nkCall, arg.info) - result.add(newIdentNode(it.getIdent, arg.info)) + result.add(newIdentNode(getIdent(c.cache, it), arg.info)) if arg.typ != nil and arg.typ.kind in {tyVar, tyLent}: result.add newDeref(arg) else: @@ -698,7 +707,8 @@ proc handleForLoopMacro(c: PContext; n: PNode): PNode = match = symx else: localError(c.config, n.info, errAmbiguousCallXYZ % [ - getProcHeader(match), getProcHeader(symx), $iterExpr]) + getProcHeader(c.config, match), + getProcHeader(c.config, symx), $iterExpr]) symx = nextOverloadIter(o, c, headSymbol) if match == nil: return @@ -746,8 +756,8 @@ proc semFor(c: PContext, n: PNode): PNode = else: result = semForVars(c, n) # propagate any enforced VoidContext: - if n.sons[length-1].typ == enforceVoidContext: - result.typ = enforceVoidContext + if n.sons[length-1].typ == c.enforceVoidContext: + result.typ = c.enforceVoidContext closeScope(c) proc semRaise(c: PContext, n: PNode): PNode = @@ -796,8 +806,8 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = let name = a.sons[0] var s: PSym if name.kind == nkDotExpr and a[2].kind == nkObjectTy: - let pkgName = considerQuotedIdent(c.config, name[0]) - let typName = considerQuotedIdent(c.config, name[1]) + let pkgName = considerQuotedIdent(c, name[0]) + let typName = considerQuotedIdent(c, name[1]) let pkg = c.graph.packageSyms.strTableGet(pkgName) if pkg.isNil or pkg.kind != skPackage: localError(c.config, name.info, "unknown package name: " & pkgName.s) @@ -835,7 +845,7 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = typsym.info = s.info else: localError(c.config, name.info, "cannot complete type '" & s.name.s & "' twice; " & - "previous type completion was here: " & $typsym.info) + "previous type completion was here: " & c.config$typsym.info) s = typsym # add it here, so that recursive types are possible: if sfGenSym notin s.flags: addInterfaceDecl(c, s) @@ -989,7 +999,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = internalAssert c.config, st.kind in {tyPtr, tyRef} internalAssert c.config, st.lastSon.sym == nil incl st.flags, tfRefsAnonObj - let obj = newSym(skType, getIdent(s.name.s & ":ObjectType"), + let obj = newSym(skType, getIdent(c.cache, s.name.s & ":ObjectType"), getCurrOwner(c), s.info) obj.typ = st.lastSon st.lastSon.sym = obj @@ -1057,9 +1067,9 @@ proc semAllTypeSections(c: PContext; n: PNode): PNode = var f = checkModuleName(c.config, n.sons[i]) if f != InvalidFileIDX: if containsOrIncl(c.includedFiles, f.int): - localError(c.config, n.info, errRecursiveDependencyX % f.toFilename) + localError(c.config, n.info, errRecursiveDependencyX % toFilename(c.config, f)) else: - let code = gIncludeFile(c.graph, c.module, f, c.cache) + let code = c.graph.includeFileCallback(c.graph, c.module, f) gatherStmts c, code, result excl(c.includedFiles, f.int) of nkStmtList: @@ -1131,7 +1141,7 @@ proc semBorrow(c: PContext, n: PNode, s: PSym) = proc addResult(c: PContext, t: PType, info: TLineInfo, owner: TSymKind) = if t != nil: - var s = newSym(skResult, getIdent"result", getCurrOwner(c), info) + var s = newSym(skResult, getIdent(c.cache, "result"), getCurrOwner(c), info) s.typ = t incl(s.flags, sfUsed) addParamOrResult(c, s, owner) @@ -1150,7 +1160,7 @@ proc lookupMacro(c: PContext, n: PNode): PSym = result = n.sym if result.kind notin {skMacro, skTemplate}: result = nil else: - result = searchInScopes(c, considerQuotedIdent(c.config, n), {skMacro, skTemplate}) + result = searchInScopes(c, considerQuotedIdent(c, n), {skMacro, skTemplate}) proc semProcAnnotation(c: PContext, prc: PNode; validPragmas: TSpecialWords): PNode = @@ -1162,7 +1172,7 @@ proc semProcAnnotation(c: PContext, prc: PNode; let m = lookupMacro(c, key) if m == nil: if key.kind == nkIdent and key.ident.id == ord(wDelegator): - if considerQuotedIdent(c.config, prc.sons[namePos]).s == "()": + if considerQuotedIdent(c, prc.sons[namePos]).s == "()": prc.sons[namePos] = newIdentNode(c.cache.idDelegator, prc.info) prc.sons[pragmasPos] = copyExcept(n, i) else: @@ -1177,7 +1187,7 @@ proc semProcAnnotation(c: PContext, prc: PNode; x.add(newSymNode(m)) prc.sons[pragmasPos] = copyExcept(n, i) if prc[pragmasPos].kind != nkEmpty and prc[pragmasPos].len == 0: - prc.sons[pragmasPos] = emptyNode + prc.sons[pragmasPos] = c.graph.emptyNode if it.kind in nkPragmaCallKinds and it.len > 1: # pass pragma arguments to the macro too: @@ -1200,7 +1210,7 @@ proc setGenericParamsMisc(c: PContext; n: PNode): PNode = # issue https://github.com/nim-lang/Nim/issues/1713 result = semGenericParamList(c, orig) if n.sons[miscPos].kind == nkEmpty: - n.sons[miscPos] = newTree(nkBracket, ast.emptyNode, orig) + n.sons[miscPos] = newTree(nkBracket, c.graph.emptyNode, orig) else: n.sons[miscPos].sons[1] = orig n.sons[genericParamsPos] = result @@ -1271,7 +1281,7 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode = result = n s.ast = result n.sons[namePos].sym = s - n.sons[genericParamsPos] = emptyNode + n.sons[genericParamsPos] = c.graph.emptyNode # for LL we need to avoid wrong aliasing let params = copyTree n.typ.n n.sons[paramsPos] = params @@ -1398,14 +1408,14 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = localError(c.config, n.info, errGenerated, "'destroy' or 'deepCopy' expected for 'override'") -proc cursorInProcAux(n: PNode): bool = - if inCheckpoint(n.info) != cpNone: return true +proc cursorInProcAux(conf: ConfigRef; n: PNode): bool = + if inCheckpoint(n.info, conf.m.trackPos) != cpNone: return true for i in 0..<n.safeLen: - if cursorInProcAux(n[i]): return true + if cursorInProcAux(conf, n[i]): return true -proc cursorInProc(n: PNode): bool = - if n.info.fileIndex == gTrackPos.fileIndex: - result = cursorInProcAux(n) +proc cursorInProc(conf: ConfigRef; n: PNode): bool = + if n.info.fileIndex == conf.m.trackPos.fileIndex: + result = cursorInProcAux(conf, n) type TProcCompilationSteps = enum @@ -1484,6 +1494,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, s.ast = n #s.scope = c.currentScope + s.options = c.config.options + # before compiling the proc body, set as current the scope # where the proc was declared let oldScope = c.currentScope @@ -1544,7 +1556,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, # linking names do agree: if proto.typ.callConv != s.typ.callConv or proto.typ.flags < s.typ.flags: localError(c.config, n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProcX % - ("'" & proto.name.s & "' from " & $proto.info)) + ("'" & proto.name.s & "' from " & c.config$proto.info)) if sfForward notin proto.flags: wrongRedefinition(c, n.info, proto.name.s) excl(proto.flags, sfForward) @@ -1555,6 +1567,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, addParams(c, proto.typ.n, proto.kind) proto.info = s.info # more accurate line information s.typ = proto.typ + proto.options = s.options s = proto n.sons[genericParamsPos] = proto.ast.sons[genericParamsPos] n.sons[paramsPos] = proto.ast.sons[paramsPos] @@ -1566,7 +1579,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, proto.ast = n # needed for code generation popOwner(c) pushOwner(c, s) - s.options = c.config.options + if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n) if s.name.s[0] in {'.', '('}: if s.name.s in [".", ".()", ".="] and {destructor, dotOperators} * c.features == {}: @@ -1585,7 +1598,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, # only used for overload resolution (there is no instantiation of # the symbol, so we must process the body now) if not usePseudoGenerics and c.config.ideCmd in {ideSug, ideCon} and not - cursorInProc(n.sons[bodyPos]): + cursorInProc(c.config, n.sons[bodyPos]): discard "speed up nimsuggest" if s.kind == skMethod: semMethodPrototype(c, s, n) else: @@ -1605,7 +1618,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, n.sons[bodyPos] = transformBody(c.graph, c.module, semBody, s) else: if s.typ.sons[0] != nil and kind != skIterator: - addDecl(c, newSym(skUnknown, getIdent"result", nil, n.info)) + addDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nil, n.info)) openScope(c) n.sons[bodyPos] = semGenericStmt(c, n.sons[bodyPos]) @@ -1614,7 +1627,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if s.kind == skMethod: semMethodPrototype(c, s, n) if sfImportc in s.flags: # so we just ignore the body after semantic checking for importc: - n.sons[bodyPos] = ast.emptyNode + n.sons[bodyPos] = c.graph.emptyNode popProcCon(c) else: if s.kind == skMethod: semMethodPrototype(c, s, n) @@ -1693,7 +1706,7 @@ proc semMethod(c: PContext, n: PNode): PNode = let ret = s.typ.sons[0] disp.typ.sons[0] = ret if disp.ast[resultPos].kind == nkSym: - if isEmptyType(ret): disp.ast.sons[resultPos] = emptyNode + if isEmptyType(ret): disp.ast.sons[resultPos] = c.graph.emptyNode else: disp.ast[resultPos].sym.typ = ret proc semConverterDef(c: PContext, n: PNode): PNode = @@ -1739,9 +1752,9 @@ proc evalInclude(c: PContext, n: PNode): PNode = var f = checkModuleName(c.config, n.sons[i]) if f != InvalidFileIDX: if containsOrIncl(c.includedFiles, f.int): - localError(c.config, n.info, errRecursiveDependencyX % f.toFilename) + localError(c.config, n.info, errRecursiveDependencyX % toFilename(c.config, f)) else: - addSon(result, semStmt(c, gIncludeFile(c.graph, c.module, f, c.cache))) + addSon(result, semStmt(c, c.graph.includeFileCallback(c.graph, c.module, f))) excl(c.includedFiles, f.int) proc setLine(n: PNode, info: TLineInfo) = @@ -1767,12 +1780,18 @@ proc semStaticStmt(c: PContext, n: PNode): PNode = #echo "semStaticStmt" #writeStackTrace() inc c.inStaticContext + openScope(c) let a = semStmt(c, n.sons[0]) + closeScope(c) dec c.inStaticContext n.sons[0] = a - evalStaticStmt(c.module, c.cache, c.graph, a, c.p.owner) - result = newNodeI(nkDiscardStmt, n.info, 1) - result.sons[0] = emptyNode + evalStaticStmt(c.module, c.graph, a, c.p.owner) + when false: + # for incremental replays, keep the AST as required for replays: + result = n + else: + result = newNodeI(nkDiscardStmt, n.info, 1) + result.sons[0] = c.graph.emptyNode proc usesResult(n: PNode): bool = # nkStmtList(expr) properly propagates the void context, @@ -1835,9 +1854,9 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = localError(c.config, result.info, "concept predicate failed") of tyUnknown: continue else: discard - if n.sons[i].typ == enforceVoidContext: #or usesResult(n.sons[i]): + if n.sons[i].typ == c.enforceVoidContext: #or usesResult(n.sons[i]): voidContext = true - n.typ = enforceVoidContext + n.typ = c.enforceVoidContext if i == last and (length == 1 or efWantValue in flags): n.typ = n.sons[i].typ if not isEmptyType(n.typ): n.kind = nkStmtListExpr diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 352bc5c6b..75c6bc4bb 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -99,7 +99,7 @@ proc semBindStmt(c: PContext, n: PNode, toBind: var IntSet): PNode = proc semMixinStmt(c: PContext, n: PNode, toMixin: var IntSet): PNode = for i in 0 ..< n.len: - toMixin.incl(considerQuotedIdent(c.config, n.sons[i]).id) + toMixin.incl(considerQuotedIdent(c, n.sons[i]).id) result = newNodeI(nkEmpty, n.info) proc replaceIdentBySym(c: PContext; n: var PNode, s: PNode) = @@ -166,7 +166,7 @@ proc onlyReplaceParams(c: var TemplCtx, n: PNode): PNode = result.sons[i] = onlyReplaceParams(c, n.sons[i]) proc newGenSym(kind: TSymKind, n: PNode, c: var TemplCtx): PSym = - result = newSym(kind, considerQuotedIdent(c.c.config, n), c.owner, n.info) + result = newSym(kind, considerQuotedIdent(c.c, n), c.owner, n.info) incl(result.flags, sfGenSym) incl(result.flags, sfShadowed) @@ -206,14 +206,14 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) = # # We need to ensure that both 'a' produce the same gensym'ed symbol. # So we need only check the *current* scope. - let s = localSearchInScope(c.c, considerQuotedIdent(c.c.config, ident)) + let s = localSearchInScope(c.c, considerQuotedIdent(c.c, ident)) if s != nil and s.owner == c.owner and sfGenSym in s.flags: styleCheckUse(n.info, s) replaceIdentBySym(c.c, n, newSymNode(s, n.info)) else: let local = newGenSym(k, ident, c) addPrelimDecl(c.c, local) - styleCheckDef(n.info, local) + styleCheckDef(c.c.config, n.info, local) replaceIdentBySym(c.c, n, newSymNode(local, n.info)) else: replaceIdentBySym(c.c, n, ident) @@ -260,7 +260,7 @@ proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode = var s = newGenSym(k, ident, c) s.ast = n addPrelimDecl(c.c, s) - styleCheckDef(n.info, s) + styleCheckDef(c.c.config, n.info, s) n.sons[namePos] = newSymNode(s, n.sons[namePos].info) else: n.sons[namePos] = ident @@ -305,7 +305,7 @@ proc semTemplBodySons(c: var TemplCtx, n: PNode): PNode = proc semTemplBody(c: var TemplCtx, n: PNode): PNode = result = n - semIdeForTemplateOrGenericCheck(n, c.cursorInBody) + semIdeForTemplateOrGenericCheck(c.c.config, n, c.cursorInBody) case n.kind of nkIdent: if n.ident.id in c.toInject: return n @@ -382,7 +382,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = # labels are always 'gensym'ed: let s = newGenSym(skLabel, n.sons[0], c) addPrelimDecl(c.c, s) - styleCheckDef(s) + styleCheckDef(c.c.config, s) n.sons[0] = newSymNode(s, n.sons[0].info) n.sons[1] = semTemplBody(c, n.sons[1]) closeScope(c) @@ -460,14 +460,14 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = x.sons[1] = semTemplBody(c, x.sons[1]) of nkBracketExpr: result = newNodeI(nkCall, n.info) - result.add newIdentNode(getIdent("[]"), n.info) + result.add newIdentNode(getIdent(c.c.cache, "[]"), n.info) for i in 0 ..< n.len: result.add(n[i]) let n0 = semTemplBody(c, n.sons[0]) withBracketExpr c, n0: result = semTemplBodySons(c, result) of nkCurlyExpr: result = newNodeI(nkCall, n.info) - result.add newIdentNode(getIdent("{}"), n.info) + result.add newIdentNode(getIdent(c.c.cache, "{}"), n.info) for i in 0 ..< n.len: result.add(n[i]) result = semTemplBodySons(c, result) of nkAsgn, nkFastAsgn: @@ -479,7 +479,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = case k of nkBracketExpr: result = newNodeI(nkCall, n.info) - result.add newIdentNode(getIdent("[]="), n.info) + result.add newIdentNode(getIdent(c.c.cache, "[]="), n.info) for i in 0 ..< a.len: result.add(a[i]) result.add(b) let a0 = semTemplBody(c, a.sons[0]) @@ -487,7 +487,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = result = semTemplBodySons(c, result) of nkCurlyExpr: result = newNodeI(nkCall, n.info) - result.add newIdentNode(getIdent("{}="), n.info) + result.add newIdentNode(getIdent(c.c.cache, "{}="), n.info) for i in 0 ..< a.len: result.add(a[i]) result.add(b) result = semTemplBodySons(c, result) @@ -518,7 +518,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode = result = n - semIdeForTemplateOrGenericCheck(n, c.cursorInBody) + semIdeForTemplateOrGenericCheck(c.c.config, n, c.cursorInBody) case n.kind of nkIdent: let s = qualifiedLookUp(c.c, n, {}) @@ -551,7 +551,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = incl(s.flags, sfGlobal) else: s = semIdentVis(c, skTemplate, n.sons[0], {}) - styleCheckDef(s) + styleCheckDef(c.config, s) # check parameter list: #s.scope = c.currentScope pushOwner(c, s) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 8b5c26f99..d1ebb950c 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -37,6 +37,12 @@ const errNoGenericParamsAllowedForX = "no generic parameters allowed for $1" errInOutFlagNotExtern = "the '$1' modifier can be used only with imported types" +const + mStaticTy = {mStatic} + mTypeTy = {mType, mTypeOf} + # XXX: This should be needed only temporarily until the C + # sources are rebuilt + proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType = if prev == nil: result = newTypeS(kind, c) @@ -66,7 +72,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = base = semTypeNode(c, n.sons[0].sons[0], nil) if base.kind != tyEnum: localError(c.config, n.sons[0].info, "inheritance only works with an enum") - counter = lastOrd(base) + 1 + counter = lastOrd(c.config, base) + 1 rawAddSon(result, base) let isPure = result.sym != nil and sfPure in result.sym.flags var symbols: TStrTable @@ -114,8 +120,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = incl(e.flags, sfExported) if not isPure: strTableAdd(c.module.tab, e) addSon(result.n, newSymNode(e)) - let conf = c.config - styleCheckDef(e) + styleCheckDef(c.config, e) if sfGenSym notin e.flags: if not isPure: addDecl(c, e) else: importPureEnumField(c, e) @@ -133,7 +138,7 @@ proc semSet(c: PContext, n: PNode, prev: PType): PType = if base.kind != tyGenericParam: if not isOrdinalType(base): localError(c.config, n.info, errOrdinalTypeExpected) - elif lengthOrd(base) > MaxSetElements: + elif lengthOrd(c.config, base) > MaxSetElements: localError(c.config, n.info, errSetTooBig) else: localError(c.config, n.info, errXExpectsOneTypeParam % "set") @@ -157,7 +162,7 @@ proc semVarargs(c: PContext, n: PNode, prev: PType): PType = var base = semTypeNode(c, n.sons[1], nil) addSonSkipIntLit(result, base) if sonsLen(n) == 3: - result.n = newIdentNode(considerQuotedIdent(c.config, n.sons[2]), n.sons[2].info) + result.n = newIdentNode(considerQuotedIdent(c, n.sons[2]), n.sons[2].info) else: localError(c.config, n.info, errXExpectsOneTypeParam % "varargs") addSonSkipIntLit(result, errorType(c)) @@ -233,13 +238,13 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = if not hasUnknownTypes: if not sameType(rangeT[0].skipTypes({tyRange}), rangeT[1].skipTypes({tyRange})): localError(c.config, n.info, "type mismatch") - elif not rangeT[0].isOrdinalType: - localError(c.config, n.info, "ordinal type expected") + elif not rangeT[0].isOrdinalType and rangeT[0].kind notin tyFloat..tyFloat128: + localError(c.config, n.info, "ordinal or float type expected") elif enumHasHoles(rangeT[0]): localError(c.config, n.info, "enum '$1' has holes" % typeToString(rangeT[0])) for i in 0..1: - if hasGenericArguments(range[i]): + if hasUnresolvedArgs(c, range[i]): result.n.addSon makeStaticExpr(c, range[i]) result.flags.incl tfUnresolved else: @@ -267,7 +272,7 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType = n.sons[1].floatVal < 0.0: incl(result.flags, tfNeedsInit) else: - if n[1].kind == nkInfix and considerQuotedIdent(c.config, n[1][0]).s == "..<": + if n[1].kind == nkInfix and considerQuotedIdent(c, n[1][0]).s == "..<": localError(c.config, n[0].info, "range types need to be constructed with '..', '..<' is not supported") else: localError(c.config, n.sons[0].info, "expected range") @@ -296,7 +301,7 @@ proc semArrayIndex(c: PContext, n: PNode): PType = localError(c.config, info, errOrdinalTypeExpected) result = makeRangeWithStaticExpr(c, e) if c.inGenericContext > 0: result.flags.incl tfUnresolved - elif e.kind in nkCallKinds and hasGenericArguments(e): + elif e.kind in (nkCallKinds + {nkBracketExpr}) and hasUnresolvedArgs(c, e): if not isOrdinalType(e.typ): localError(c.config, n[1].info, errOrdinalTypeExpected) # This is an int returning call, depending on an @@ -364,6 +369,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = if result != nil: markUsed(c.config, n.info, result, c.graph.usageSym) styleCheckUse(n.info, result) + if result.kind == skParam and result.typ.kind == tyTypeDesc: # This is a typedesc param. is it already bound? # it's not bound when it's used multiple times in the @@ -389,8 +395,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = else: localError(c.config, n.info, errTypeExpected) return errorSym(c, n) - - if result.kind != skType: + if result.kind != skType and result.magic notin (mStaticTy + mTypeTy): # this implements the wanted ``var v: V, x: V`` feature ... var ov: TOverloadIter var amb = initOverloadIter(ov, c, n) @@ -451,7 +456,7 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType = else: addSon(result.n, newSymNode(field)) addSonSkipIntLit(result, typ) - if c.config.cmd == cmdPretty: styleCheckDef(a.sons[j].info, field) + styleCheckDef(c.config, a.sons[j].info, field) if result.n.len == 0: result.n = nil proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, @@ -462,7 +467,7 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, # for gensym'ed identifiers the identifier may already have been # transformed to a symbol and we need to use that here: result = newSymG(kind, n.sons[1], c) - var v = considerQuotedIdent(c.config, n.sons[0]) + var v = considerQuotedIdent(c, n.sons[0]) if sfExported in allowed and v.id == ord(wStar): incl(result.flags, sfExported) else: @@ -491,7 +496,7 @@ proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, else: discard else: result = semIdentVis(c, kind, n, allowed) - if c.config.cmd == cmdPretty: styleCheckDef(n.info, result) + styleCheckDef(c.config, n.info, result) proc checkForOverlap(c: PContext, t: PNode, currentEx, branchIndex: int) = let ex = t[branchIndex][currentEx].skipConv @@ -553,7 +558,7 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int, inc(covered) else: if r.kind == nkCurly: - r = r.deduplicate + r = deduplicate(c.config, r) # first element is special and will overwrite: branch.sons[i]: branch.sons[i] = semCaseBranchSetElem(c, t, r[0], covered) @@ -584,10 +589,10 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int, var typ = skipTypes(a.sons[0].typ, abstractVar-{tyTypeDesc}) if not isOrdinalType(typ): localError(c.config, n.info, "selector must be of an ordinal type") - elif firstOrd(typ) != 0: + elif firstOrd(c.config, typ) != 0: localError(c.config, n.info, "low(" & $a.sons[0].sym.name.s & ") must be 0 for discriminant") - elif lengthOrd(typ) > 0x00007FFF: + elif lengthOrd(c.config, typ) > 0x00007FFF: localError(c.config, n.info, "len($1) must be less than 32768" % a.sons[0].sym.name.s) var chckCovered = true for i in countup(1, sonsLen(n) - 1): @@ -603,7 +608,7 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int, else: illFormedAst(n, c.config) delSon(b, sonsLen(b) - 1) semRecordNodeAux(c, lastSon(n.sons[i]), check, pos, b, rectype) - if chckCovered and covered != lengthOrd(a.sons[0].typ): + if chckCovered and covered != lengthOrd(c.config, a.sons[0].typ): localError(c.config, a.info, "not all cases are covered") addSon(father, a) @@ -658,7 +663,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, var length = sonsLen(n) var a: PNode if father.kind != nkRecList and length>=4: a = newNodeI(nkRecList, n.info) - else: a = ast.emptyNode + else: a = newNodeI(nkEmpty, n.info) if n.sons[length-1].kind != nkEmpty: localError(c.config, n.sons[length-1].info, errInitHereNotAllowed) var typ: PType @@ -685,7 +690,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, localError(c.config, n.sons[i].info, "attempt to redefine: '" & f.name.s & "'") if a.kind == nkEmpty: addSon(father, newSymNode(f)) else: addSon(a, newSymNode(f)) - styleCheckDef(f) + styleCheckDef(c.config, f) if a.kind != nkEmpty: addSon(father, a) of nkSym: # This branch only valid during generic object @@ -770,7 +775,7 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType = semRecordNodeAux(c, n.sons[2], check, pos, result.n, result) if n.sons[0].kind != nkEmpty: # dummy symbol for `pragma`: - var s = newSymS(skType, newIdentNode(getIdent("dummy"), n.info), c) + var s = newSymS(skType, newIdentNode(getIdent(c.cache, "dummy"), n.info), c) s.typ = result pragma(c, s, n.sons[0], typePragmas) if base == nil and tfInheritable notin result.flags: @@ -804,8 +809,6 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) = else: if sfGenSym notin param.flags: addDecl(c, param) -let typedescId = getIdent"typedesc" - template shouldHaveMeta(t) = internalAssert c.config, tfHasMeta in t.flags # result.lastSon.flags.incl tfHasMeta @@ -815,13 +818,13 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, info: TLineInfo, anon = false): PType = if paramType == nil: return # (e.g. proc return type) - proc addImplicitGenericImpl(typeClass: PType, typId: PIdent): PType = + proc addImplicitGenericImpl(c: PContext; typeClass: PType, typId: PIdent): PType = if genericParams == nil: # This happens with anonymous proc types appearing in signatures # XXX: we need to lift these earlier return let finalTypId = if typId != nil: typId - else: getIdent(paramName & ":type") + else: getIdent(c.cache, paramName & ":type") # is this a bindOnce type class already present in the param list? for i in countup(0, genericParams.len - 1): if genericParams.sons[i].sym.name.id == finalTypId.id: @@ -852,16 +855,16 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, (if lifted != nil: lifted else: typ) template addImplicitGeneric(e): untyped = - addImplicitGenericImpl(e, paramTypId) + addImplicitGenericImpl(c, e, paramTypId) case paramType.kind: of tyAnything: - result = addImplicitGenericImpl(newTypeS(tyGenericParam, c), nil) + result = addImplicitGenericImpl(c, newTypeS(tyGenericParam, c), nil) of tyStatic: - # proc(a: expr{string}, b: expr{nkLambda}) - # overload on compile time values and AST trees - if paramType.n != nil: return # this is a concrete type + if paramType.base.kind != tyNone and paramType.n != nil: + # this is a concrete static value + return if tfUnresolved in paramType.flags: return # already lifted let base = paramType.base.maybeLift if base.isMetaType and procKind == skMacro: @@ -873,14 +876,19 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, if tfUnresolved notin paramType.flags: # naked typedescs are not bindOnce types if paramType.base.kind == tyNone and paramTypId != nil and - paramTypId.id == typedescId.id: paramTypId = nil + paramTypId.id == getIdent(c.cache, "typedesc").id: + # XXX Why doesn't this check for tyTypeDesc instead? + paramTypId = nil result = addImplicitGeneric( c.newTypeWithSons(tyTypeDesc, @[paramType.base])) of tyDistinct: if paramType.sonsLen == 1: # disable the bindOnce behavior for the type class - result = liftingWalk(paramType.sons[0], true) + result = liftingWalk(paramType.base, true) + + of tyAlias: + result = liftingWalk(paramType.base) of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyLent, tyPtr, tyRef, tyProc: @@ -997,13 +1005,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, prev: PType, kind: TSymKind; isType=false): PType = # for historical reasons (code grows) this is invoked for parameter # lists too and then 'isType' is false. - var cl: IntSet checkMinSonsLen(n, 1, c.config) result = newProcType(c, n.info, prev) - if genericParams != nil and sonsLen(genericParams) == 0: - cl = initIntSet() var check = initIntSet() var counter = 0 + for i in countup(1, n.len - 1): var a = n.sons[i] if a.kind != nkIdentDefs: @@ -1013,6 +1019,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, # pass over this instantiation: if a.kind == nkSym and sfFromGeneric in a.sym.flags: continue illFormedAst(a, c.config) + checkMinSonsLen(a, 3, c.config) var typ: PType = nil @@ -1021,26 +1028,52 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, length = sonsLen(a) hasType = a.sons[length-2].kind != nkEmpty hasDefault = a.sons[length-1].kind != nkEmpty + if hasType: typ = semParamType(c, a.sons[length-2], constraint) if hasDefault: - def = semExprWithType(c, a.sons[length-1]) - # check type compatibility between def.typ and typ: + def = a[^1] + block determineType: + if genericParams != nil and genericParams.len > 0: + def = semGenericStmt(c, def) + if hasUnresolvedArgs(c, def): + def.typ = makeTypeFromExpr(c, def.copyTree) + break determineType + + def = semExprWithType(c, def, {efDetermineType}) + if def.referencesAnotherParam(getCurrOwner(c)): + def.flags.incl nfDefaultRefsParam + if typ == nil: typ = def.typ - elif def != nil: - # and def.typ != nil and def.typ.kind != tyNone: + if typ.kind == tyTypeDesc: + # consider a proc such as: + # proc takesType(T = int) + # a naive analysis may conclude that the proc type is type[int] + # which will prevent other types from matching - clearly a very + # surprising behavior. We must instead fix the expected type of + # the proc to be the unbound typedesc type: + typ = newTypeWithSons(c, tyTypeDesc, @[newTypeS(tyNone, c)]) + + else: + # if def.typ != nil and def.typ.kind != tyNone: # example code that triggers it: # proc sort[T](cmp: proc(a, b: T): int = cmp) if not containsGenericType(typ): + # check type compatibility between def.typ and typ: + def = fitNode(c, typ, def, def.info) + elif typ.kind == tyStatic: + def = semConstExpr(c, def) def = fitNode(c, typ, def, def.info) + if not hasType and not hasDefault: if isType: localError(c.config, a.info, "':' expected") if kind in {skTemplate, skMacro}: typ = newTypeS(tyExpr, c) elif skipTypes(typ, {tyGenericInst, tyAlias, tySink}).kind == tyVoid: continue + for j in countup(0, length-3): var arg = newSymG(skParam, a.sons[j], c) if not hasType and not hasDefault and kind notin {skTemplate, skMacro}: @@ -1056,13 +1089,14 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, arg.position = counter arg.constraint = constraint inc(counter) - if def != nil and def.kind != nkEmpty: arg.ast = copyTree(def) + if def != nil and def.kind != nkEmpty: + arg.ast = copyTree(def) if containsOrIncl(check, arg.name.id): localError(c.config, a.sons[j].info, "attempt to redefine: '" & arg.name.s & "'") addSon(result.n, newSymNode(arg)) rawAddSon(result, finalType) addParamOrResult(c, arg, kind) - if c.config.cmd == cmdPretty: styleCheckDef(a.sons[j].info, arg) + styleCheckDef(c.config, a.sons[j].info, arg) var r: PType if n.sons[0].kind != nkEmpty: @@ -1311,7 +1345,7 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType = incl dummyParam.flags, sfUsed addDecl(c, dummyParam) - result.n.sons[3] = semConceptBody(c, n[3]) + result.n[3] = semConceptBody(c, n[3]) closeScope(c) proc semProcTypeWithScope(c: PContext, n: PNode, @@ -1322,7 +1356,7 @@ proc semProcTypeWithScope(c: PContext, n: PNode, # start with 'ccClosure', but of course pragmas can overwrite this: result.callConv = ccClosure # dummy symbol for `pragma`: - var s = newSymS(kind, newIdentNode(getIdent("dummy"), n.info), c) + var s = newSymS(kind, newIdentNode(getIdent(c.cache, "dummy"), n.info), c) s.typ = result if n.sons[1].kind != nkEmpty and n.sons[1].len > 0: pragma(c, s, n.sons[1], procTypePragmas) @@ -1345,11 +1379,17 @@ proc fixupTypeOf(c: PContext, prev: PType, typExpr: PNode) = proc symFromExpectedTypeNode(c: PContext, n: PNode): PSym = if n.kind == nkType: - result = symFromType(n.typ, n.info) + result = symFromType(c, n.typ, n.info) else: localError(c.config, n.info, errTypeExpected) result = errorSym(c, n) +proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType = + result = newOrPrevType(tyStatic, prev, c) + var base = semTypeNode(c, childNode, nil).skipTypes({tyTypeDesc, tyAlias}) + result.rawAddSon(base) + result.flags.incl tfHasStatic + proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = result = nil inc c.inTypeContext @@ -1393,7 +1433,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = elif n[0].kind notin nkIdentKinds: result = semTypeExpr(c, n, prev) else: - let op = considerQuotedIdent(c.config, n.sons[0]) + let op = considerQuotedIdent(c, n.sons[0]) if op.id in {ord(wAnd), ord(wOr)} or op.s == "|": checkSonsLen(n, 3, c.config) var @@ -1457,7 +1497,11 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of mSeq: result = semContainer(c, n, tySequence, "seq", prev) of mOpt: result = semContainer(c, n, tyOpt, "opt", prev) of mVarargs: result = semVarargs(c, n, prev) - of mTypeDesc: result = makeTypeDesc(c, semTypeNode(c, n[1], nil)) + of mTypeDesc, mTypeTy: + result = makeTypeDesc(c, semTypeNode(c, n[1], nil)) + result.flags.incl tfExplicit + of mStaticTy: + result = semStaticType(c, n[1], prev) of mExpr: result = semTypeNode(c, n.sons[0], nil) if result != nil: @@ -1546,11 +1590,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of nkPtrTy: result = semAnyRef(c, n, tyPtr, prev) of nkVarTy: result = semVarType(c, n, prev) of nkDistinctTy: result = semDistinct(c, n, prev) - of nkStaticTy: - result = newOrPrevType(tyStatic, prev, c) - var base = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc}) - result.rawAddSon(base) - result.flags.incl tfHasStatic + of nkStaticTy: result = semStaticType(c, n[0], prev) of nkIteratorTy: if n.sonsLen == 0: result = newTypeS(tyBuiltInTypeClass, c) @@ -1585,7 +1625,7 @@ when false: result = semTypeNodeInner(c, n, prev) instAllTypeBoundOp(c, n.info) -proc setMagicType(m: PSym, kind: TTypeKind, size: int) = +proc setMagicType(conf: ConfigRef; m: PSym, kind: TTypeKind, size: int) = # source : https://en.wikipedia.org/wiki/Data_structure_alignment#x86 m.typ.kind = kind m.typ.size = size @@ -1596,10 +1636,10 @@ proc setMagicType(m: PSym, kind: TTypeKind, size: int) = # FIXME: proper support for clongdouble should be added. # long double size can be 8, 10, 12, 16 bytes depending on platform & compiler - if targetCPU == cpuI386 and size == 8: + if conf.target.targetCPU == cpuI386 and size == 8: #on Linux/BSD i386, double are aligned to 4bytes (except with -malign-double) if kind in {tyFloat64, tyFloat} and - targetOS in {osLinux, osAndroid, osNetbsd, osFreebsd, osOpenbsd, osDragonfly}: + conf.target.targetOS in {osLinux, osAndroid, osNetbsd, osFreebsd, osOpenbsd, osDragonfly}: m.typ.align = 4 # on i386, all known compiler, 64bits ints are aligned to 4bytes (except with -malign-double) elif kind in {tyInt, tyUInt, tyInt64, tyUInt64}: @@ -1609,73 +1649,76 @@ proc setMagicType(m: PSym, kind: TTypeKind, size: int) = proc processMagicType(c: PContext, m: PSym) = case m.magic - of mInt: setMagicType(m, tyInt, intSize) - of mInt8: setMagicType(m, tyInt8, 1) - of mInt16: setMagicType(m, tyInt16, 2) - of mInt32: setMagicType(m, tyInt32, 4) - of mInt64: setMagicType(m, tyInt64, 8) - of mUInt: setMagicType(m, tyUInt, intSize) - of mUInt8: setMagicType(m, tyUInt8, 1) - of mUInt16: setMagicType(m, tyUInt16, 2) - of mUInt32: setMagicType(m, tyUInt32, 4) - of mUInt64: setMagicType(m, tyUInt64, 8) - of mFloat: setMagicType(m, tyFloat, floatSize) - of mFloat32: setMagicType(m, tyFloat32, 4) - of mFloat64: setMagicType(m, tyFloat64, 8) - of mFloat128: setMagicType(m, tyFloat128, 16) - of mBool: setMagicType(m, tyBool, 1) - of mChar: setMagicType(m, tyChar, 1) + of mInt: setMagicType(c.config, m, tyInt, c.config.target.intSize) + of mInt8: setMagicType(c.config, m, tyInt8, 1) + of mInt16: setMagicType(c.config, m, tyInt16, 2) + of mInt32: setMagicType(c.config, m, tyInt32, 4) + of mInt64: setMagicType(c.config, m, tyInt64, 8) + of mUInt: setMagicType(c.config, m, tyUInt, c.config.target.intSize) + of mUInt8: setMagicType(c.config, m, tyUInt8, 1) + of mUInt16: setMagicType(c.config, m, tyUInt16, 2) + of mUInt32: setMagicType(c.config, m, tyUInt32, 4) + of mUInt64: setMagicType(c.config, m, tyUInt64, 8) + of mFloat: setMagicType(c.config, m, tyFloat, c.config.target.floatSize) + of mFloat32: setMagicType(c.config, m, tyFloat32, 4) + of mFloat64: setMagicType(c.config, m, tyFloat64, 8) + of mFloat128: setMagicType(c.config, m, tyFloat128, 16) + of mBool: setMagicType(c.config, m, tyBool, 1) + of mChar: setMagicType(c.config, m, tyChar, 1) of mString: - setMagicType(m, tyString, ptrSize) + setMagicType(c.config, m, tyString, c.config.target.ptrSize) rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar)) of mCstring: - setMagicType(m, tyCString, ptrSize) + setMagicType(c.config, m, tyCString, c.config.target.ptrSize) rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar)) - of mPointer: setMagicType(m, tyPointer, ptrSize) + of mPointer: setMagicType(c.config, m, tyPointer, c.config.target.ptrSize) of mEmptySet: - setMagicType(m, tySet, 1) + setMagicType(c.config, m, tySet, 1) rawAddSon(m.typ, newTypeS(tyEmpty, c)) - of mIntSetBaseType: setMagicType(m, tyRange, intSize) - of mNil: setMagicType(m, tyNil, ptrSize) + of mIntSetBaseType: setMagicType(c.config, m, tyRange, c.config.target.intSize) + of mNil: setMagicType(c.config, m, tyNil, c.config.target.ptrSize) of mExpr: if m.name.s == "auto": - setMagicType(m, tyAnything, 0) + setMagicType(c.config, m, tyAnything, 0) else: - setMagicType(m, tyExpr, 0) + setMagicType(c.config, m, tyExpr, 0) if m.name.s == "expr": m.typ.flags.incl tfOldSchoolExprStmt of mStmt: - setMagicType(m, tyStmt, 0) + setMagicType(c.config, m, tyStmt, 0) if m.name.s == "stmt": m.typ.flags.incl tfOldSchoolExprStmt - of mTypeDesc: - setMagicType(m, tyTypeDesc, 0) + of mTypeDesc, mType: + setMagicType(c.config, m, tyTypeDesc, 0) + rawAddSon(m.typ, newTypeS(tyNone, c)) + of mStatic: + setMagicType(c.config, m, tyStatic, 0) rawAddSon(m.typ, newTypeS(tyNone, c)) of mVoidType: - setMagicType(m, tyVoid, 0) + setMagicType(c.config, m, tyVoid, 0) of mArray: - setMagicType(m, tyArray, 0) + setMagicType(c.config, m, tyArray, 0) of mOpenArray: - setMagicType(m, tyOpenArray, 0) + setMagicType(c.config, m, tyOpenArray, 0) of mVarargs: - setMagicType(m, tyVarargs, 0) + setMagicType(c.config, m, tyVarargs, 0) of mRange: - setMagicType(m, tyRange, 0) + setMagicType(c.config, m, tyRange, 0) rawAddSon(m.typ, newTypeS(tyNone, c)) of mSet: - setMagicType(m, tySet, 0) + setMagicType(c.config, m, tySet, 0) of mSeq: - setMagicType(m, tySequence, 0) + setMagicType(c.config, m, tySequence, 0) of mOpt: - setMagicType(m, tyOpt, 0) + setMagicType(c.config, m, tyOpt, 0) of mOrdinal: - setMagicType(m, tyOrdinal, 0) + setMagicType(c.config, m, tyOrdinal, 0) rawAddSon(m.typ, newTypeS(tyNone, c)) of mPNimrodNode: incl m.typ.flags, tfTriggersCompileTime of mException: discard of mBuiltinType: case m.name.s - of "lent": setMagicType(m, tyLent, ptrSize) - of "sink": setMagicType(m, tySink, 0) + of "lent": setMagicType(c.config, m, tyLent, c.config.target.ptrSize) + of "sink": setMagicType(c.config, m, tySink, 0) else: localError(c.config, m.info, errTypeExpected) else: localError(c.config, m.info, errTypeExpected) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 61d92bb19..22ea09af1 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -9,7 +9,8 @@ # This module does the instantiation of generic types. -import ast, astalgo, msgs, types, magicsys, semdata, renderer, options +import ast, astalgo, msgs, types, magicsys, semdata, renderer, options, + lineinfos const tfInstClearedFlags = {tfHasMeta, tfUnresolved} @@ -27,7 +28,7 @@ proc checkConstructedType*(conf: ConfigRef; info: TLineInfo, typ: PType) = localError(conf, info, "invalid pragma: acyclic") elif t.kind in {tyVar, tyLent} and t.sons[0].kind in {tyVar, tyLent}: localError(conf, info, "type 'var var' is not allowed") - elif computeSize(t) == szIllegalRecursion: + elif computeSize(conf, t) == szIllegalRecursion: localError(conf, info, "illegal recursion in type '" & typeToString(t) & "'") when false: if t.kind == tyObject and t.sons[0] != nil: @@ -143,17 +144,6 @@ proc isTypeParam(n: PNode): bool = (n.sym.kind == skGenericParam or (n.sym.kind == skType and sfFromGeneric in n.sym.flags)) -proc hasGenericArguments*(n: PNode): bool = - if n.kind == nkSym: - return n.sym.kind == skGenericParam or - tfInferrableStatic in n.sym.typ.flags or - (n.sym.kind == skType and - n.sym.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {}) - else: - for i in 0..<n.safeLen: - if hasGenericArguments(n.sons[i]): return true - return false - proc reResolveCallsWithTypedescParams(cl: var TReplTypeVars, n: PNode): PNode = # This is needed for tgenericshardcases # It's possible that a generic param will be used in a proc call to a @@ -230,6 +220,17 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym = # symbol is not our business: if cl.owner != nil and s.owner != cl.owner: return s + + # XXX: Bound symbols in default parameter expressions may reach here. + # We cannot process them, becase `sym.n` may point to a proc body with + # cyclic references that will lead to an infinite recursion. + # Perhaps we should not use a black-list here, but a whitelist instead + # (e.g. skGenericParam and skType). + # Note: `s.magic` may be `mType` in an example such as: + # proc foo[T](a: T, b = myDefault(type(a))) + if s.kind == skProc or s.magic != mNone: + return s + #result = PSym(idTableGet(cl.symMap, s)) #if result == nil: result = copySym(s, false) @@ -277,7 +278,8 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = # is difficult to handle: const eqFlags = eqTypeFlags + {tfGcSafe} var body = t.sons[0] - if body.kind != tyGenericBody: internalError(cl.c.config, cl.info, "no generic body") + if body.kind != tyGenericBody: + internalError(cl.c.config, cl.info, "no generic body") var header: PType = t # search for some instantiation here: if cl.allowMetaTypes: @@ -568,35 +570,35 @@ proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode; var typeMap = initLayeredTypeMap(pt) var cl = initTypeVars(p, addr(typeMap), n.info, owner) cl.allowMetaTypes = allowMetaTypes - pushInfoContext(n.info) + pushInfoContext(p.config, n.info) result = replaceTypeVarsN(cl, n) - popInfoContext() + popInfoContext(p.config) proc replaceTypesForLambda*(p: PContext, pt: TIdTable, n: PNode; original, new: PSym): PNode = var typeMap = initLayeredTypeMap(pt) var cl = initTypeVars(p, addr(typeMap), n.info, original) idTablePut(cl.symMap, original, new) - pushInfoContext(n.info) + pushInfoContext(p.config, n.info) result = replaceTypeVarsN(cl, n) - popInfoContext() + popInfoContext(p.config) proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo, t: PType): PType = var typeMap = initLayeredTypeMap(pt) var cl = initTypeVars(p, addr(typeMap), info, nil) - pushInfoContext(info) + pushInfoContext(p.config, info) result = replaceTypeVarsT(cl, t) - popInfoContext() + popInfoContext(p.config) proc prepareMetatypeForSigmatch*(p: PContext, pt: TIdTable, info: TLineInfo, t: PType): PType = var typeMap = initLayeredTypeMap(pt) var cl = initTypeVars(p, addr(typeMap), info, nil) cl.allowMetaTypes = true - pushInfoContext(info) + pushInfoContext(p.config, info) result = replaceTypeVarsT(cl, t) - popInfoContext() + popInfoContext(p.config) template generateTypeInstance*(p: PContext, pt: TIdTable, arg: PNode, t: PType): untyped = diff --git a/compiler/service.nim b/compiler/service.nim index f1a988ae5..0e82c03f8 100644 --- a/compiler/service.nim +++ b/compiler/service.nim @@ -11,7 +11,7 @@ import times, commands, options, msgs, nimconf, - extccomp, strutils, os, platform, parseopt, idents, configuration + extccomp, strutils, os, platform, parseopt, idents, lineinfos when useCaas: import net @@ -26,25 +26,6 @@ var # in caas mode, the list of defines and options will be given at start-up? # it's enough to check that the previous compilation command is the same? -proc processCmdLine*(pass: TCmdLinePass, cmd: string; config: ConfigRef) = - var p = parseopt.initOptParser(cmd) - var argsCount = 0 - while true: - parseopt.next(p) - case p.kind - of cmdEnd: break - of cmdLongoption, cmdShortOption: - if p.key == " ": - p.key = "-" - if processArgument(pass, p, argsCount, config): break - else: - processSwitch(pass, p, config) - of cmdArgument: - if processArgument(pass, p, argsCount, config): break - if pass == passCmd2: - if optRun notin config.globalOptions and config.arguments.len > 0 and config.command.normalize != "run": - rawMessage(config, errGenerated, errArgsNeedRunOption) - proc serve*(cache: IdentCache; action: proc (cache: IdentCache){.nimcall.}; config: ConfigRef) = template execute(cmd) = curCaasCmd = cmd @@ -71,7 +52,7 @@ proc serve*(cache: IdentCache; action: proc (cache: IdentCache){.nimcall.}; conf var inp = "".TaintedString server.listen() var stdoutSocket = newSocket() - msgs.writelnHook = proc (line: string) = + config.writelnHook = proc (line: string) = stdoutSocket.send(line & "\c\L") while true: diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 46b83c386..0bf2b8459 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -12,7 +12,7 @@ import ast, md5, tables, ropes from hashes import Hash from astalgo import debug -from types import typeToString, preferDesc +import types from strutils import startsWith, contains when false: @@ -148,19 +148,23 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) = of tyGenericInvocation: for i in countup(0, sonsLen(t) - 1): c.hashType t.sons[i], flags - return of tyDistinct: if CoType in flags: c.hashType t.lastSon, flags else: c.hashSym(t.sym) - return - of tyAlias, tySink, tyGenericInst, tyUserTypeClasses: + of tyGenericInst: + if sfInfixCall in t.base.sym.flags: + # This is an imported C++ generic type. + # We cannot trust the `lastSon` to hold a properly populated and unique + # value for each instantiation, so we hash the generic parameters here: + let normalizedType = t.skipGenericAlias + for i in 0 .. normalizedType.len - 2: + c.hashType t.sons[i], flags + else: + c.hashType t.lastSon, flags + of tyAlias, tySink, tyUserTypeClasses: c.hashType t.lastSon, flags - return - else: - discard - case t.kind of tyBool, tyChar, tyInt..tyUInt64: # no canonicalization for integral types, so that e.g. ``pid_t`` is # produced instead of ``NI``: @@ -168,11 +172,12 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) = if t.sym != nil and {sfImportc, sfExportc} * t.sym.flags != {}: c.hashSym(t.sym) of tyObject, tyEnum: - c &= char(t.kind) if t.typeInst != nil: assert t.typeInst.kind == tyGenericInst - for i in countup(1, sonsLen(t.typeInst) - 2): + for i in countup(0, sonsLen(t.typeInst) - 2): c.hashType t.typeInst.sons[i], flags + return + c &= char(t.kind) # Every cyclic type in Nim need to be constructed via some 't.sym', so this # is actually safe without an infinite recursion check: if t.sym != nil: @@ -184,18 +189,19 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) = c.hashTypeSym(t.sym) else: c.hashSym(t.sym) - if sfAnon in t.sym.flags: + if {sfAnon, sfGenSym} * t.sym.flags != {}: # generated object names can be identical, so we need to # disambiguate furthermore by hashing the field types and names: # mild hack to prevent endless recursions (makes nimforum compile again): - excl t.sym.flags, sfAnon + let oldFlags = t.sym.flags + t.sym.flags = t.sym.flags - {sfAnon, sfGenSym} let n = t.n for i in 0 ..< n.len: assert n[i].kind == nkSym let s = n[i].sym c.hashSym s c.hashType s.typ, flags - incl t.sym.flags, sfAnon + t.sym.flags = oldFlags else: c &= t.id if t.len > 0 and t.sons[0] != nil: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index fcfdda8bb..84e59349e 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -13,9 +13,9 @@ import intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst, magicsys, condsyms, idents, lexer, options, parampatterns, strutils, trees, - nimfix.pretty + linter, lineinfos -when not defined(noDocgen): +when defined(booting) or defined(nimsuggest): import docgen type @@ -253,7 +253,7 @@ proc complexDisambiguation(a, b: PType): int = result = x - y proc writeMatches*(c: TCandidate) = - echo "Candidate '", c.calleeSym.name.s, "' at ", c.calleeSym.info + echo "Candidate '", c.calleeSym.name.s, "' at ", c.c.config $ c.calleeSym.info echo " exact matches: ", c.exactMatches echo " generic matches: ", c.genericMatches echo " subtype matches: ", c.subtypeMatches @@ -376,8 +376,10 @@ proc handleRange(f, a: PType, min, max: TTypeKind): TTypeRelation = if k == f.kind: result = isSubrange elif k == tyInt and f.kind in {tyRange, tyInt8..tyInt64, tyUInt..tyUInt64} and - isIntLit(ab) and ab.n.intVal >= firstOrd(f) and - ab.n.intVal <= lastOrd(f): + isIntLit(ab) and ab.n.intVal >= firstOrd(nil, f) and + ab.n.intVal <= lastOrd(nil, f): + # passing 'nil' to firstOrd/lastOrd here as type checking rules should + # not depent on the target integer size configurations! # integer literal in the proper range; we want ``i16 + 4`` to stay an # ``int16`` operation so we declare the ``4`` pseudo-equal to int16 result = isFromIntLit @@ -387,8 +389,10 @@ proc handleRange(f, a: PType, min, max: TTypeKind): TTypeRelation = result = isConvertible elif a.kind == tyRange and a.sons[0].kind in {tyInt..tyInt64, tyUInt8..tyUInt32} and - a.n[0].intVal >= firstOrd(f) and - a.n[1].intVal <= lastOrd(f): + a.n[0].intVal >= firstOrd(nil, f) and + a.n[1].intVal <= lastOrd(nil, f): + # passing 'nil' to firstOrd/lastOrd here as type checking rules should + # not depent on the target integer size configurations! result = isConvertible else: result = isNone #elif f.kind == tyInt and k in {tyInt..tyInt32}: result = isIntConv @@ -633,20 +637,22 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = else: discard proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} = - let - a0 = firstOrd(a) - a1 = lastOrd(a) - f0 = firstOrd(f) - f1 = lastOrd(f) - if a0 == f0 and a1 == f1: - result = isEqual - elif a0 >= f0 and a1 <= f1: - result = isConvertible - elif a0 <= f1 and f0 <= a1: - # X..Y and C..D overlap iff (X <= D and C <= Y) - result = isConvertible - else: - result = isNone + template checkRange[T](a0, a1, f0, f1: T): TTypeRelation = + if a0 == f0 and a1 == f1: + isEqual + elif a0 >= f0 and a1 <= f1: + isConvertible + elif a0 <= f1 and f0 <= a1: + # X..Y and C..D overlap iff (X <= D and C <= Y) + isConvertible + else: + isNone + + if f.isOrdinalType: + checkRange(firstOrd(nil, a), lastOrd(nil, a), firstOrd(nil, f), lastOrd(nil, f)) + else: + checkRange(firstFloat(a), lastFloat(a), firstFloat(f), lastFloat(f)) + proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType = var @@ -718,7 +724,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType = addDecl(c, param) var - oldWriteHook: type(writelnHook) + oldWriteHook: type(m.c.config.writelnHook) diagnostics: seq[string] errorPrefix: string flags: TExprFlags = {} @@ -726,12 +732,12 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType = sfExplain in typeClass.sym.flags if collectDiagnostics: - oldWriteHook = writelnHook + oldWriteHook = m.c.config.writelnHook # XXX: we can't write to m.diagnostics directly, because # Nim doesn't support capturing var params in closures diagnostics = @[] flags = {efExplain} - writelnHook = proc (s: string) = + m.c.config.writelnHook = proc (s: string) = if errorPrefix == nil: errorPrefix = typeClass.sym.name.s & ":" let msg = s.replace("Error:", errorPrefix) if oldWriteHook != nil: oldWriteHook msg @@ -740,7 +746,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType = var checkedBody = c.semTryExpr(c, body.copyTree, flags) if collectDiagnostics: - writelnHook = oldWriteHook + m.c.config.writelnHook = oldWriteHook for msg in diagnostics: m.diagnostics.safeAdd msg m.diagnosticsEnabled = true @@ -764,11 +770,11 @@ proc shouldSkipDistinct(m: TCandidate; rules: PNode, callIdent: PIdent): bool = # XXX This is bad as 'considerQuotedIdent' can produce an error! if rules.kind == nkWith: for r in rules: - if considerQuotedIdent(m.c.graph.config, r) == callIdent: return true + if considerQuotedIdent(m.c, r) == callIdent: return true return false else: for r in rules: - if considerQuotedIdent(m.c.graph.config, r) == callIdent: return false + if considerQuotedIdent(m.c, r) == callIdent: return false return true proc maybeSkipDistinct(m: TCandidate; t: PType, callee: PSym): PType = @@ -887,17 +893,17 @@ proc inferStaticsInRange(c: var TCandidate, if inferStaticParam(c, exp, rhs): return isGeneric else: - failureToInferStaticParam(c.c.graph.config, exp) + failureToInferStaticParam(c.c.config, exp) if lowerBound.kind == nkIntLit: if upperBound.kind == nkIntLit: - if lengthOrd(concrete) == upperBound.intVal - lowerBound.intVal + 1: + if lengthOrd(c.c.config, concrete) == upperBound.intVal - lowerBound.intVal + 1: return isGeneric else: return isNone - doInferStatic(upperBound, lengthOrd(concrete) + lowerBound.intVal - 1) + doInferStatic(upperBound, lengthOrd(c.c.config, concrete) + lowerBound.intVal - 1) elif upperBound.kind == nkIntLit: - doInferStatic(lowerBound, upperBound.intVal + 1 - lengthOrd(concrete)) + doInferStatic(lowerBound, upperBound.intVal + 1 - lengthOrd(c.c.config, concrete)) template subtypeCheck() = if result <= isSubrange and f.lastSon.skipTypes(abstractInst).kind in {tyRef, tyPtr, tyVar, tyLent}: @@ -1085,8 +1091,8 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, else: isNone of tyAnything: - return if f.kind == tyAnything: isGeneric - else: isNone + if f.kind == tyAnything: return isGeneric + else: return isNone of tyUserTypeClass, tyUserTypeClassInst: if c.c.matchedConcept != nil and c.c.matchedConcept.depth <= 4: @@ -1176,7 +1182,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, elif c.c.matchedConcept != nil and aRange.rangeHasUnresolvedStatic: return inferStaticsInRange(c, aRange, f) else: - if lengthOrd(fRange) != lengthOrd(aRange): + if lengthOrd(c.c.config, fRange) != lengthOrd(c.c.config, aRange): result = isNone else: discard of tyOpenArray, tyVarargs: @@ -1342,7 +1348,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, if a.len == 1: let pointsTo = a.sons[0].skipTypes(abstractInst) if pointsTo.kind == tyChar: result = isConvertible - elif pointsTo.kind == tyArray and firstOrd(pointsTo.sons[0]) == 0 and + elif pointsTo.kind == tyArray and firstOrd(nil, pointsTo.sons[0]) == 0 and skipTypes(pointsTo.sons[0], {tyRange}).kind in {tyInt..tyInt64} and pointsTo.sons[1].kind == tyChar: result = isConvertible @@ -1662,13 +1668,17 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, let prev = PType(idTableGet(c.bindings, f)) if prev == nil: if aOrig.kind == tyStatic: - result = typeRel(c, f.lastSon, a) - if result != isNone and f.n != nil: - if not exprStructuralEquivalent(f.n, aOrig.n): - result = isNone + if f.base.kind != tyNone: + result = typeRel(c, f.base, a) + if result != isNone and f.n != nil: + if not exprStructuralEquivalent(f.n, aOrig.n): + result = isNone + else: + result = isGeneric if result != isNone: put(c, f, aOrig) elif aOrig.n != nil and aOrig.n.typ != nil: - result = typeRel(c, f.lastSon, aOrig.n.typ) + result = if f.base.kind != tyNone: typeRel(c, f.lastSon, aOrig.n.typ) + else: isGeneric if result != isNone: var boundType = newTypeWithSons(c.c, tyStatic, @[aOrig.n.typ]) boundType.n = aOrig.n @@ -1703,9 +1713,13 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, # proc foo(T: typedesc, x: T) # when `f` is an unresolved typedesc, `a` could be any # type, so we should not perform this check earlier - if a.kind != tyTypeDesc: return isNone - - if f.base.kind == tyNone: + if a.kind != tyTypeDesc: + if a.kind == tyGenericParam and tfWildcard in a.flags: + # TODO: prevent `a` from matching as a wildcard again + result = isGeneric + else: + result = isNone + elif f.base.kind == tyNone: result = isGeneric else: result = typeRel(c, f.base, a.base) @@ -1774,7 +1788,7 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate, else: result.typ = f if result.typ == nil: internalError(c.graph.config, arg.info, "implicitConv") - addSon(result, ast.emptyNode) + addSon(result, c.graph.emptyNode) addSon(result, arg) proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType, @@ -1993,7 +2007,10 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, return arg elif a.kind == tyVoid and f.matchesVoidProc and argOrig.kind == nkStmtList: # lift do blocks without params to lambdas - let lifted = c.semExpr(c, newProcNode(nkDo, argOrig.info, argOrig), {}) + let p = c.graph + let lifted = c.semExpr(c, newProcNode(nkDo, argOrig.info, body = argOrig, + params = p.emptyNode, name = p.emptyNode, pattern = p.emptyNode, + genericParams = p.emptyNode, pragmas = p.emptyNode, exceptions = p.emptyNode), {}) if f.kind == tyBuiltInTypeClass: inc m.genericMatches put(m, f, lifted.typ) @@ -2127,10 +2144,10 @@ proc prepareOperand(c: PContext; a: PNode): PNode = result = a considerGenSyms(c, result) -proc prepareNamedParam(a: PNode; conf: ConfigRef) = +proc prepareNamedParam(a: PNode; c: PContext) = if a.sons[0].kind != nkIdent: var info = a.sons[0].info - a.sons[0] = newIdentNode(considerQuotedIdent(conf, a.sons[0]), info) + a.sons[0] = newIdentNode(considerQuotedIdent(c, a.sons[0]), info) proc arrayConstr(c: PContext, n: PNode): PType = result = newTypeS(tyArray, c) @@ -2195,7 +2212,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, elif n.sons[a].kind == nkExprEqExpr: # named param # check if m.callee has such a param: - prepareNamedParam(n.sons[a], c.config) + prepareNamedParam(n.sons[a], c) if n.sons[a].sons[0].kind != nkIdent: localError(c.config, n.sons[a].info, "named parameter has to be an identifier") m.state = csNoMatch @@ -2346,12 +2363,22 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = m.firstMismatch = f break else: - # use default value: + if formal.ast.kind == nkEmpty: + # The default param value is set to empty in `instantiateProcType` + # when the type of the default expression doesn't match the type + # of the instantiated proc param: + localError(c.config, m.call.info, + ("The default parameter '$1' has incompatible type " & + "with the explicitly requested proc instantiation") % + formal.name.s) + if nfDefaultRefsParam in formal.ast.flags: + m.call.flags.incl nfDefaultRefsParam var def = copyTree(formal.ast) if def.kind == nkNilLit: def = implicitConv(nkHiddenStdConv, formal.typ, def, m, c) if {tfImplicitTypeParam, tfGenericTypeParam} * formal.typ.flags != {}: put(m, formal.typ, def.typ) + def.flags.incl nfDefaultParam setSon(m.call, formal.position + 1, def) inc(f) # forget all inferred types if the overload matching failed @@ -2359,14 +2386,18 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = for t in m.inferredTypes: if t.sonsLen > 1: t.sons.setLen 1 -proc argtypeMatches*(c: PContext, f, a: PType): bool = +proc argtypeMatches*(c: PContext, f, a: PType, fromHlo = false): bool = var m: TCandidate initCandidate(c, m, f) - let res = paramTypesMatch(m, f, a, ast.emptyNode, nil) + let res = paramTypesMatch(m, f, a, c.graph.emptyNode, nil) #instantiateGenericConverters(c, res, m) # XXX this is used by patterns.nim too; I think it's better to not # instantiate generic converters for that - result = res != nil + if not fromHlo: + res != nil + else: + # pattern templates do not allow for conversions except from int literal + res != nil and m.convMatches == 0 and m.intConvMatches in [0, 256] proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo; op: TTypeAttachedOp; col: int): PSym {.procvar.} = diff --git a/compiler/suggest.nim b/compiler/suggest.nim index 23aecfa71..a21d64338 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -32,7 +32,7 @@ # included from sigmatch.nim -import algorithm, prefixmatches, configuration +import algorithm, prefixmatches, lineinfos from wordrecg import wDeprecated when defined(nimsuggest): @@ -41,31 +41,6 @@ when defined(nimsuggest): const sep = '\t' -type - Suggest* = ref object - section*: IdeCmd - qualifiedPath*: seq[string] - name*: PIdent # not used beyond sorting purposes; name is also - # part of 'qualifiedPath' - filePath*: string - line*: int # Starts at 1 - column*: int # Starts at 0 - doc*: string # Not escaped (yet) - symkind*: TSymKind - forth*: string # type - quality*: range[0..100] # matching quality - isGlobal*: bool # is a global variable - contextFits*: bool # type/non-type context matches - prefix*: PrefixMatch - scope*, localUsages*, globalUsages*: int # more usages is better - tokenLen*: int - Suggestions* = seq[Suggest] - -var - suggestionResultHook*: proc (result: Suggest) {.closure.} - suggestVersion*: int - suggestMaxResults* = 10_000 - #template sectionSuggest(): expr = "##begin\n" & getStackTrace() & "##end\n" template origModuleName(m: PSym): string = m.name.s @@ -104,7 +79,7 @@ proc cmpSuggestions(a, b: Suggest): int = cf globalUsages # if all is equal, sort alphabetically for deterministic output, # independent of hashing order: - result = cmp(a.name.s, b.name.s) + result = cmp(a.name[], b.name[]) proc symToSuggest(conf: ConfigRef; s: PSym, isLocal: bool, section: IdeCmd, info: TLineInfo; quality: range[0..100]; prefix: PrefixMatch; @@ -117,14 +92,14 @@ proc symToSuggest(conf: ConfigRef; s: PSym, isLocal: bool, section: IdeCmd, info result.prefix = prefix result.contextFits = inTypeContext == (s.kind in {skType, skGenericParam}) result.scope = scope - result.name = s.name + result.name = addr s.name.s when defined(nimsuggest): result.globalUsages = s.allUsages.len var c = 0 for u in s.allUsages: if u.fileIndex == info.fileIndex: inc c result.localUsages = c - result.symkind = s.kind + result.symkind = byte s.kind if optIdeTerse notin conf.globalOptions: result.qualifiedPath = @[] if not isLocal and s.kind != skModule: @@ -140,23 +115,24 @@ proc symToSuggest(conf: ConfigRef; s: PSym, isLocal: bool, section: IdeCmd, info result.forth = typeToString(s.typ) else: result.forth = "" - when not defined(noDocgen): + when defined(nimsuggest) and not defined(noDocgen): result.doc = s.extractDocComment let infox = if section in {ideUse, ideHighlight, ideOutline}: info else: s.info - result.filePath = toFullPath(infox) + result.filePath = toFullPath(conf, infox) result.line = toLinenumber(infox) result.column = toColumn(infox) + result.version = conf.suggestVersion proc `$`*(suggest: Suggest): string = result = $suggest.section result.add(sep) if suggest.section == ideHighlight: - if suggest.symkind == skVar and suggest.isGlobal: + if suggest.symkind.TSymKind == skVar and suggest.isGlobal: result.add("skGlobalVar") - elif suggest.symkind == skLet and suggest.isGlobal: + elif suggest.symkind.TSymKind == skLet and suggest.isGlobal: result.add("skGlobalLet") else: - result.add($suggest.symkind) + result.add($suggest.symkind.TSymKind) result.add(sep) result.add($suggest.line) result.add(sep) @@ -164,9 +140,9 @@ proc `$`*(suggest: Suggest): string = result.add(sep) result.add($suggest.tokenLen) else: - result.add($suggest.symkind) + result.add($suggest.symkind.TSymKind) result.add(sep) - if suggest.qualifiedPath != nil: + if suggest.qualifiedPath.len != 0: result.add(suggest.qualifiedPath.join(".")) result.add(sep) result.add(suggest.forth) @@ -177,20 +153,20 @@ proc `$`*(suggest: Suggest): string = result.add(sep) result.add($suggest.column) result.add(sep) - when not defined(noDocgen): + when defined(nimsuggest) and not defined(noDocgen): result.add(suggest.doc.escape) - if suggestVersion == 0: + if suggest.version == 0: result.add(sep) result.add($suggest.quality) if suggest.section == ideSug: result.add(sep) result.add($suggest.prefix) -proc suggestResult(s: Suggest) = - if not isNil(suggestionResultHook): - suggestionResultHook(s) +proc suggestResult(conf: ConfigRef; s: Suggest) = + if not isNil(conf.suggestionResultHook): + conf.suggestionResultHook(s) else: - suggestWriteln($s) + conf.suggestWriteln($s) proc produceOutput(a: var Suggestions; conf: ConfigRef) = if conf.ideCmd in {ideSug, ideCon}: @@ -198,13 +174,13 @@ proc produceOutput(a: var Suggestions; conf: ConfigRef) = when defined(debug): # debug code writeStackTrace() - if a.len > suggestMaxResults: a.setLen(suggestMaxResults) - if not isNil(suggestionResultHook): + if a.len > conf.suggestMaxResults: a.setLen(conf.suggestMaxResults) + if not isNil(conf.suggestionResultHook): for s in a: - suggestionResultHook(s) + conf.suggestionResultHook(s) else: for s in a: - suggestWriteln($s) + conf.suggestWriteln($s) proc filterSym(s: PSym; prefix: PNode; res: var PrefixMatch): bool {.inline.} = proc prefixMatch(s: PSym; n: PNode): PrefixMatch = @@ -340,14 +316,14 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) var typ = n.typ var pm: PrefixMatch when defined(nimsuggest): - if n.kind == nkSym and n.sym.kind == skError and suggestVersion == 0: + if n.kind == nkSym and n.sym.kind == skError and c.config.suggestVersion == 0: # consider 'foo.|' where 'foo' is some not imported module. - let fullPath = findModule(c.config, n.sym.name.s, n.info.toFullPath) + let fullPath = findModule(c.config, n.sym.name.s, toFullPath(c.config, n.info)) if fullPath.len == 0: # error: no known module name: typ = nil else: - let m = gImportModule(c.graph, c.module, fileInfoIdx(c.config, fullpath), c.cache) + let m = c.graph.importModuleCallback(c.graph, c.module, fileInfoIdx(c.config, fullpath)) if m == nil: typ = nil else: for it in items(n.sym.tab): @@ -397,17 +373,17 @@ type TCheckPointResult* = enum cpNone, cpFuzzy, cpExact -proc inCheckpoint*(current: TLineInfo): TCheckPointResult = - if current.fileIndex == gTrackPos.fileIndex: - if current.line == gTrackPos.line and - abs(current.col-gTrackPos.col) < 4: +proc inCheckpoint*(current, trackPos: TLineInfo): TCheckPointResult = + if current.fileIndex == trackPos.fileIndex: + if current.line == trackPos.line and + abs(current.col-trackPos.col) < 4: return cpExact - if current.line >= gTrackPos.line: + if current.line >= trackPos.line: return cpFuzzy -proc isTracked*(current: TLineInfo, tokenLen: int): bool = - if current.fileIndex==gTrackPos.fileIndex and current.line==gTrackPos.line: - let col = gTrackPos.col +proc isTracked*(current, trackPos: TLineInfo, tokenLen: int): bool = + if current.fileIndex==trackPos.fileIndex and current.line==trackPos.line: + let col = trackPos.col if col >= current.col and col <= current.col+tokenLen-1: return true @@ -425,30 +401,27 @@ when defined(nimsuggest): if infoB.infoToInt == infoAsInt: return s.allUsages.add(info) -var - lastLineInfo*: TLineInfo # XXX global here - proc findUsages(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym) = - if suggestVersion == 1: - if usageSym == nil and isTracked(info, s.name.s.len): + if conf.suggestVersion == 1: + if usageSym == nil and isTracked(info, conf.m.trackPos, s.name.s.len): usageSym = s - suggestResult(symToSuggest(conf, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0)) + suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0)) elif s == usageSym: - if lastLineInfo != info: - suggestResult(symToSuggest(conf, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0)) - lastLineInfo = info + if conf.lastLineInfo != info: + suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0)) + conf.lastLineInfo = info when defined(nimsuggest): proc listUsages*(conf: ConfigRef; s: PSym) = #echo "usages ", len(s.allUsages) for info in s.allUsages: let x = if info == s.info and info.col == s.info.col: ideDef else: ideUse - suggestResult(symToSuggest(conf, s, isLocal=false, x, info, 100, PrefixMatch.None, false, 0)) + suggestResult(conf, symToSuggest(conf, s, isLocal=false, x, info, 100, PrefixMatch.None, false, 0)) proc findDefinition(conf: ConfigRef; info: TLineInfo; s: PSym) = if s.isNil: return - if isTracked(info, s.name.s.len): - suggestResult(symToSuggest(conf, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0)) + if isTracked(info, conf.m.trackPos, s.name.s.len): + suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0)) suggestQuit() proc ensureIdx[T](x: var T, y: int) = @@ -460,7 +433,7 @@ proc ensureSeq[T](x: var seq[T]) = proc suggestSym*(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true) {.inline.} = ## misnamed: should be 'symDeclared' when defined(nimsuggest): - if suggestVersion == 0: + if conf.suggestVersion == 0: if s.allUsages.isNil: s.allUsages = @[info] else: @@ -471,14 +444,14 @@ proc suggestSym*(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym; elif conf.ideCmd == ideDef: findDefinition(conf, info, s) elif conf.ideCmd == ideDus and s != nil: - if isTracked(info, s.name.s.len): - suggestResult(symToSuggest(conf, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0)) + if isTracked(info, conf.m.trackPos, s.name.s.len): + suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0)) findUsages(conf, info, s, usageSym) - elif conf.ideCmd == ideHighlight and info.fileIndex == gTrackPos.fileIndex: - suggestResult(symToSuggest(conf, s, isLocal=false, ideHighlight, info, 100, PrefixMatch.None, false, 0)) - elif conf.ideCmd == ideOutline and info.fileIndex == gTrackPos.fileIndex and + elif conf.ideCmd == ideHighlight and info.fileIndex == conf.m.trackPos.fileIndex: + suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideHighlight, info, 100, PrefixMatch.None, false, 0)) + elif conf.ideCmd == ideOutline and info.fileIndex == conf.m.trackPos.fileIndex and isDecl: - suggestResult(symToSuggest(conf, s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0)) + suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0)) proc warnAboutDeprecated(conf: ConfigRef; info: TLineInfo; s: PSym) = if s.kind in routineKinds: @@ -510,7 +483,7 @@ proc safeSemExpr*(c: PContext, n: PNode): PNode = try: result = c.semExpr(c, n) except ERecoverableError: - result = ast.emptyNode + result = c.graph.emptyNode proc sugExpr(c: PContext, n: PNode, outputs: var Suggestions) = if n.kind == nkDotExpr: @@ -519,14 +492,14 @@ proc sugExpr(c: PContext, n: PNode, outputs: var Suggestions) = # of the next line, so we check the 'field' is actually on the same # line as the object to prevent this from happening: let prefix = if n.len == 2 and n[1].info.line == n[0].info.line and - not gTrackPosAttached: n[1] else: nil + not c.config.m.trackPosAttached: n[1] else: nil suggestFieldAccess(c, obj, prefix, outputs) #if optIdeDebug in gGlobalOptions: # echo "expression ", renderTree(obj), " has type ", typeToString(obj.typ) #writeStackTrace() else: - let prefix = if gTrackPosAttached: nil else: n + let prefix = if c.config.m.trackPosAttached: nil else: n suggestEverything(c, n, prefix, outputs) proc suggestExprNoCheck*(c: PContext, n: PNode) = @@ -555,10 +528,10 @@ proc suggestExprNoCheck*(c: PContext, n: PNode) = suggestQuit() proc suggestExpr*(c: PContext, n: PNode) = - if exactEquals(gTrackPos, n.info): suggestExprNoCheck(c, n) + if exactEquals(c.config.m.trackPos, n.info): suggestExprNoCheck(c, n) proc suggestDecl*(c: PContext, n: PNode; s: PSym) = - let attached = gTrackPosAttached + let attached = c.config.m.trackPosAttached if attached: inc(c.inTypeContext) defer: if attached: dec(c.inTypeContext) @@ -574,7 +547,7 @@ proc suggestEnum*(c: PContext; n: PNode; t: PType) = if outputs.len > 0: suggestQuit() proc suggestSentinel*(c: PContext) = - if c.config.ideCmd != ideSug or c.module.position != gTrackPos.fileIndex.int32: return + if c.config.ideCmd != ideSug or c.module.position != c.config.m.trackPos.fileIndex.int32: return if c.compilesContextId > 0: return inc(c.compilesContextId) var outputs: Suggestions = @[] @@ -587,7 +560,9 @@ proc suggestSentinel*(c: PContext) = for it in items(scope.symbols): var pm: PrefixMatch if filterSymNoOpr(it, nil, pm): - outputs.add(symToSuggest(c.config, it, isLocal = isLocal, ideSug, newLineInfo(gTrackPos.fileIndex, -1, -1), 0, PrefixMatch.None, false, scopeN)) + outputs.add(symToSuggest(c.config, it, isLocal = isLocal, ideSug, + newLineInfo(c.config.m.trackPos.fileIndex, -1, -1), 0, + PrefixMatch.None, false, scopeN)) dec(c.compilesContextId) produceOutput(outputs, c.config) diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim index 4bc153e46..069f65eee 100644 --- a/compiler/syntaxes.nim +++ b/compiler/syntaxes.nim @@ -11,7 +11,7 @@ import strutils, llstream, ast, astalgo, idents, lexer, options, msgs, parser, - filters, filter_tmpl, renderer, configuration + filters, filter_tmpl, renderer, lineinfos type TFilterKind* = enum @@ -38,7 +38,6 @@ proc parseAll*(p: var TParsers): PNode = result = parser.parseAll(p.parser) of skinEndX: internalError(p.config, "parser to implement") - result = ast.emptyNode proc parseTopLevelStmt*(p: var TParsers): PNode = case p.skin @@ -46,7 +45,6 @@ proc parseTopLevelStmt*(p: var TParsers): PNode = result = parser.parseTopLevelStmt(p.parser) of skinEndX: internalError(p.config, "parser to implement") - result = ast.emptyNode proc utf8Bom(s: string): int = if s.len >= 3 and s[0] == '\xEF' and s[1] == '\xBB' and s[2] == '\xBF': @@ -62,7 +60,7 @@ proc containsShebang(s: string, i: int): bool = proc parsePipe(filename: string, inputStream: PLLStream; cache: IdentCache; config: ConfigRef): PNode = - result = ast.emptyNode + result = newNode(nkEmpty) var s = llStreamOpen(filename, fmRead) if s != nil: var line = newStringOfCap(80) @@ -144,7 +142,7 @@ proc openParsers*(p: var TParsers, fileIdx: FileIndex, inputstream: PLLStream; assert config != nil var s: PLLStream p.skin = skinStandard - let filename = fileIdx.toFullPathConsiderDirty + let filename = toFullPathConsiderDirty(config, fileIdx) var pipe = parsePipe(filename, inputstream, cache, config) p.config() = config if pipe != nil: s = evalPipe(p, pipe, filename, inputstream) @@ -162,7 +160,7 @@ proc parseFile*(fileIdx: FileIndex; cache: IdentCache; config: ConfigRef): PNode var p: TParsers f: File - let filename = fileIdx.toFullPathConsiderDirty + let filename = toFullPathConsiderDirty(config, fileIdx) if not open(f, filename): rawMessage(config, errGenerated, "cannot open file: " & filename) return diff --git a/compiler/transf.nim b/compiler/transf.nim index c2add13ff..abe713eb8 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -20,9 +20,9 @@ import intsets, strutils, options, ast, astalgo, trees, treetab, msgs, lookups, - idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread, + idents, renderer, types, passes, semfold, magicsys, cgmeth, lambdalifting, sempass2, lowerings, destroyer, liftlocals, closureiters, - modulegraphs + modulegraphs, lineinfos type PTransNode* = distinct PNode @@ -94,7 +94,7 @@ proc getCurrOwner(c: PTransf): PSym = else: result = c.module proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PNode = - let r = newSym(skTemp, getIdent(genPrefix), getCurrOwner(c), info) + let r = newSym(skTemp, getIdent(c.graph.cache, genPrefix), getCurrOwner(c), info) r.typ = typ #skipTypes(typ, {tyGenericInst, tyAlias, tySink}) incl(r.flags, sfFromGeneric) let owner = getCurrOwner(c) @@ -194,7 +194,7 @@ proc transformVarSection(c: PTransf, v: PNode): PTransNode = idNodeTablePut(c.transCon.mapping, it.sons[j].sym, x) defs[j] = x.PTransNode assert(it.sons[L-2].kind == nkEmpty) - defs[L-2] = ast.emptyNode.PTransNode + defs[L-2] = newNodeI(nkEmpty, it.info).PTransNode defs[L-1] = transform(c, it.sons[L-1]) result[i] = defs @@ -221,7 +221,7 @@ proc hasContinue(n: PNode): bool = proc newLabel(c: PTransf, n: PNode): PSym = result = newSym(skLabel, nil, getCurrOwner(c), n.info) - result.name = getIdent(genPrefix & $result.id) + result.name = getIdent(c.graph.cache, genPrefix & $result.id) proc freshLabels(c: PTransf, n: PNode; symMap: var TIdTable) = if n.kind in {nkBlockStmt, nkBlockExpr}: @@ -393,7 +393,7 @@ proc generateThunk(c: PTransf; prc: PNode, dest: PType): PNode = if c.graph.config.cmd == cmdCompileToJS: return prc result = newNodeIT(nkClosure, prc.info, dest) var conv = newNodeIT(nkHiddenSubConv, prc.info, dest) - conv.add(emptyNode) + conv.add(newNodeI(nkEmpty, prc.info)) conv.add(prc) if prc.kind == nkClosure: internalError(c.graph.config, prc.info, "closure to closure created") @@ -410,8 +410,8 @@ proc transformConv(c: PTransf, n: PNode): PTransNode = if not isOrdinalType(source): # float -> int conversions. ugh. result = transformSons(c, n) - elif firstOrd(n.typ) <= firstOrd(n.sons[1].typ) and - lastOrd(n.sons[1].typ) <= lastOrd(n.typ): + elif firstOrd(c.graph.config, n.typ) <= firstOrd(c.graph.config, n.sons[1].typ) and + lastOrd(c.graph.config, n.sons[1].typ) <= lastOrd(c.graph.config, n.typ): # BUGFIX: simply leave n as it is; we need a nkConv node, # but no range check: result = transformSons(c, n) @@ -423,8 +423,8 @@ proc transformConv(c: PTransf, n: PNode): PTransNode = result = newTransNode(nkChckRange, n, 3) dest = skipTypes(n.typ, abstractVar) result[0] = transform(c, n.sons[1]) - result[1] = newIntTypeNode(nkIntLit, firstOrd(dest), dest).PTransNode - result[2] = newIntTypeNode(nkIntLit, lastOrd(dest), dest).PTransNode + result[1] = newIntTypeNode(nkIntLit, firstOrd(c.graph.config, dest), dest).PTransNode + result[2] = newIntTypeNode(nkIntLit, lastOrd(c.graph.config, dest), dest).PTransNode of tyFloat..tyFloat128: # XXX int64 -> float conversion? if skipTypes(n.typ, abstractVar).kind == tyRange: @@ -598,7 +598,7 @@ proc transformFor(c: PTransf, n: PNode): PTransNode = idNodeTablePut(newC.mapping, formal, temp) var body = iter.getBody.copyTree - pushInfoContext(n.info) + pushInfoContext(c.graph.config, n.info) # XXX optimize this somehow. But the check "c.inlining" is not correct: var symMap: TIdTable initIdTable symMap @@ -608,7 +608,7 @@ proc transformFor(c: PTransf, n: PNode): PTransNode = add(stmtList, transform(c, body)) #findWrongOwners(c, stmtList.pnode) dec(c.inlining) - popInfoContext() + popInfoContext(c.graph.config) popTransCon(c) # echo "transformed: ", stmtList.PNode.renderTree @@ -721,16 +721,16 @@ proc transformExceptBranch(c: PTransf, n: PNode): PTransNode = let actions = newTransNode(nkStmtListExpr, n[1], 2) # Generating `let exc = (excType)(getCurrentException())` # -> getCurrentException() - let excCall = PTransNode(callCodegenProc(c.graph, "getCurrentException", ast.emptyNode)) + let excCall = PTransNode(callCodegenProc(c.graph, "getCurrentException", newNodeI(nkEmpty, n.info))) # -> (excType) let convNode = newTransNode(nkHiddenSubConv, n[1].info, 2) - convNode[0] = PTransNode(ast.emptyNode) + convNode[0] = PTransNode(newNodeI(nkEmpty, n.info)) convNode[1] = excCall PNode(convNode).typ = excTypeNode.typ.toRef() # -> let exc = ... let identDefs = newTransNode(nkIdentDefs, n[1].info, 3) identDefs[0] = PTransNode(n[0][2]) - identDefs[1] = PTransNode(ast.emptyNode) + identDefs[1] = PTransNode(newNodeI(nkEmpty, n.info)) identDefs[2] = convNode let letSection = newTransNode(nkLetSection, n[1].info, 1) @@ -780,6 +780,43 @@ proc commonOptimizations*(g: ModuleGraph; c: PSym, n: PNode): PNode = else: result = n +proc hoistParamsUsedInDefault(c: PTransf, call, letSection, defExpr: PNode): PNode = + # This takes care of complicated signatures such as: + # proc foo(a: int, b = a) + # proc bar(a: int, b: int, c = a + b) + # + # The recursion may confuse you. It performs two duties: + # + # 1) extracting all referenced params from default expressions + # into a let section preceeding the call + # + # 2) replacing the "references" within the default expression + # with these extracted skLet symbols. + # + # The first duty is carried out directly in the code here, while the second + # duty is activated by returning a non-nil value. The caller is responsible + # for replacing the input to the function with the returned non-nil value. + # (which is the hoisted symbol) + if defExpr.kind == nkSym: + if defExpr.sym.kind == skParam and defExpr.sym.owner == call[0].sym: + let paramPos = defExpr.sym.position + 1 + + if call[paramPos].kind == nkSym and sfHoisted in call[paramPos].sym.flags: + # Already hoisted, we still need to return it in order to replace the + # placeholder expression in the default value. + return call[paramPos] + + let hoistedVarSym = hoistExpr(letSection, + call[paramPos], + getIdent(c.graph.cache, genPrefix), + c.transCon.owner).newSymNode + call[paramPos] = hoistedVarSym + return hoistedVarSym + else: + for i in 0..<defExpr.safeLen: + let hoisted = hoistParamsUsedInDefault(c, call, letSection, defExpr[i]) + if hoisted != nil: defExpr[i] = hoisted + proc transform(c: PTransf, n: PNode): PTransNode = when false: var oldDeferAnchor: PNode @@ -849,6 +886,15 @@ proc transform(c: PTransf, n: PNode): PTransNode = of nkBreakStmt: result = transformBreak(c, n) of nkCallKinds: result = transformCall(c, n) + var call = result.PNode + if nfDefaultRefsParam in call.flags: + # We've found a default value that references another param. + # See the notes in `hoistParamsUsedInDefault` for more details. + var hoistedParams = newNodeI(nkLetSection, call.info, 0) + for i in 1 ..< call.len: + let hoisted = hoistParamsUsedInDefault(c, call, hoistedParams, call[i]) + if hoisted != nil: call[i] = hoisted + result = newTree(nkStmtListExpr, hoistedParams, call).PTransNode of nkAddr, nkHiddenAddr: result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref) of nkDerefExpr, nkHiddenDeref: @@ -863,7 +909,7 @@ proc transform(c: PTransf, n: PNode): PTransNode = # ensure that e.g. discard "some comment" gets optimized away # completely: result = PTransNode(newNode(nkCommentStmt)) - of nkCommentStmt, nkTemplateDef: + of nkCommentStmt, nkTemplateDef, nkImportStmt, nkStaticStmt: return n.PTransNode of nkConstSection: # do not replace ``const c = 3`` with ``const 3 = 3`` @@ -916,7 +962,7 @@ proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode = # Note: For interactive mode we cannot call 'passes.skipCodegen' and skip # this step! We have to rely that the semantic pass transforms too errornous # nodes into an empty node. - if c.rd != nil or nfTransf in n.flags: return n + if nfTransf in n.flags: return n pushTransCon(c, newTransCon(owner)) result = PNode(transform(c, n)) popTransCon(c) @@ -981,11 +1027,11 @@ proc transformBody*(g: ModuleGraph; module: PSym, n: PNode, prc: PSym): PNode = liftDefer(c, result) #result = liftLambdas(prc, result) when useEffectSystem: trackProc(g, prc, result) - result = liftLocalsIfRequested(prc, result, g.config) + result = liftLocalsIfRequested(prc, result, g.cache, g.config) if c.needsDestroyPass: #and newDestructors: result = injectDestructorCalls(g, prc, result) - if prc.isIterator and oldIterTransf notin g.config.features: + if prc.isIterator: result = g.transformClosureIterator(prc, result) incl(result.flags, nfTransf) diff --git a/compiler/types.nim b/compiler/types.nim index 1fab842cc..c5af855cc 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -10,7 +10,8 @@ # this module contains routines for accessing and iterating over types import - intsets, ast, astalgo, trees, msgs, strutils, platform, renderer, options + intsets, ast, astalgo, trees, msgs, strutils, platform, renderer, options, + lineinfos type TPreferedDesc* = enum @@ -88,13 +89,16 @@ proc isPureObject*(typ: PType): bool = proc getOrdValue*(n: PNode): BiggestInt = case n.kind - of nkCharLit..nkUInt64Lit: result = n.intVal - of nkNilLit: result = 0 - of nkHiddenStdConv: result = getOrdValue(n.sons[1]) - else: - #localError(n.info, errOrdinalTypeExpected) - # XXX check usages of getOrdValue - result = high(BiggestInt) + of nkCharLit..nkUInt64Lit: n.intVal + of nkNilLit: 0 + of nkHiddenStdConv: getOrdValue(n.sons[1]) + else: high(BiggestInt) + +proc getFloatValue*(n: PNode): BiggestFloat = + case n.kind + of nkFloatLiterals: n.floatVal + of nkHiddenStdConv: getFloatValue(n.sons[1]) + else: NaN proc isIntLit*(t: PType): bool {.inline.} = result = t.kind == tyInt and t.n != nil and t.n.kind == nkIntLit @@ -102,7 +106,7 @@ proc isIntLit*(t: PType): bool {.inline.} = proc isFloatLit*(t: PType): bool {.inline.} = result = t.kind == tyFloat and t.n != nil and t.n.kind == nkFloatLit -proc getProcHeader*(sym: PSym; prefer: TPreferedDesc = preferName): string = +proc getProcHeader*(conf: ConfigRef; sym: PSym; prefer: TPreferedDesc = preferName): string = result = sym.owner.name.s & '.' & sym.name.s & '(' var n = sym.typ.n for i in countup(1, sonsLen(n) - 1): @@ -118,13 +122,13 @@ proc getProcHeader*(sym: PSym; prefer: TPreferedDesc = preferName): string = if n.sons[0].typ != nil: result.add(": " & typeToString(n.sons[0].typ, prefer)) result.add "[declared in " - result.add($sym.info) + result.add(conf$sym.info) result.add "]" proc elemType*(t: PType): PType = assert(t != nil) case t.kind - of tyGenericInst, tyDistinct, tyAlias: result = elemType(lastSon(t)) + of tyGenericInst, tyDistinct, tyAlias, tySink: result = elemType(lastSon(t)) of tyArray: result = t.sons[1] else: result = t.lastSon assert(result != nil) @@ -134,12 +138,12 @@ proc isOrdinalType*(t: PType): bool = const # caution: uint, uint64 are no ordinal types! baseKinds = {tyChar,tyInt..tyInt64,tyUInt8..tyUInt32,tyBool,tyEnum} - parentKinds = {tyRange, tyOrdinal, tyGenericInst, tyAlias, tyDistinct} + parentKinds = {tyRange, tyOrdinal, tyGenericInst, tyAlias, tySink, tyDistinct} t.kind in baseKinds or (t.kind in parentKinds and isOrdinalType(t.sons[0])) proc enumHasHoles*(t: PType): bool = var b = t - while b.kind in {tyRange, tyGenericInst, tyAlias}: b = b.sons[0] + while b.kind in {tyRange, tyGenericInst, tyAlias, tySink}: b = b.sons[0] result = b.kind == tyEnum and tfEnumHasHoles in b.flags proc iterOverTypeAux(marker: var IntSet, t: PType, iter: TTypeIter, @@ -164,7 +168,7 @@ proc iterOverTypeAux(marker: var IntSet, t: PType, iter: TTypeIter, if result: return if not containsOrIncl(marker, t.id): case t.kind - of tyGenericInst, tyGenericBody, tyAlias, tyInferred: + of tyGenericInst, tyGenericBody, tyAlias, tySink, tyInferred: result = iterOverTypeAux(marker, lastSon(t), iter, closure) else: for i in countup(0, sonsLen(t) - 1): @@ -214,7 +218,7 @@ proc searchTypeForAux(t: PType, predicate: TTypePredicate, if t.sons[0] != nil: result = searchTypeForAux(t.sons[0].skipTypes(skipPtrs), predicate, marker) if not result: result = searchTypeNodeForAux(t.n, predicate, marker) - of tyGenericInst, tyDistinct, tyAlias: + of tyGenericInst, tyDistinct, tyAlias, tySink: result = searchTypeForAux(lastSon(t), predicate, marker) of tyArray, tySet, tyTuple: for i in countup(0, sonsLen(t) - 1): @@ -257,7 +261,7 @@ proc analyseObjectWithTypeFieldAux(t: PType, if res == frHeader: result = frHeader if result == frNone: if isObjectWithTypeFieldPredicate(t): result = frHeader - of tyGenericInst, tyDistinct, tyAlias: + of tyGenericInst, tyDistinct, tyAlias, tySink: result = analyseObjectWithTypeFieldAux(lastSon(t), marker) of tyArray, tyTuple: for i in countup(0, sonsLen(t) - 1): @@ -501,7 +505,10 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = #internalAssert t.len == 0 result = "untyped" of tyFromExpr: - result = renderTree(t.n) + if t.n == nil: + result = "unknown" + else: + result = "type(" & renderTree(t.n) & ")" of tyArray: if t.sons[0].kind == tyRange: result = "array[" & rangeToStr(t.sons[0].n) & ", " & @@ -589,18 +596,19 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = result = typeToStr[t.kind] result.addTypeFlags(t) -proc firstOrd*(t: PType): BiggestInt = + +proc firstOrd*(conf: ConfigRef; t: PType): BiggestInt = case t.kind of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyProxy: result = 0 - of tySet, tyVar: result = firstOrd(t.sons[0]) - of tyArray: result = firstOrd(t.sons[0]) + of tySet, tyVar: result = firstOrd(conf, t.sons[0]) + of tyArray: result = firstOrd(conf, t.sons[0]) of tyRange: assert(t.n != nil) # range directly given: assert(t.n.kind == nkRange) result = getOrdValue(t.n.sons[0]) of tyInt: - if platform.intSize == 4: result = - (2147483646) - 2 + if conf != nil and conf.target.intSize == 4: result = - (2147483646) - 2 else: result = 0x8000000000000000'i64 of tyInt8: result = - 128 of tyInt16: result = - 32768 @@ -610,38 +618,55 @@ proc firstOrd*(t: PType): BiggestInt = of tyEnum: # if basetype <> nil then return firstOrd of basetype if sonsLen(t) > 0 and t.sons[0] != nil: - result = firstOrd(t.sons[0]) + result = firstOrd(conf, t.sons[0]) else: assert(t.n.sons[0].kind == nkSym) result = t.n.sons[0].sym.position - of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic, tyInferred: - result = firstOrd(lastSon(t)) + of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink, + tyStatic, tyInferred, tyUserTypeClassInst: + result = firstOrd(conf, lastSon(t)) of tyOrdinal: - if t.len > 0: result = firstOrd(lastSon(t)) - else: internalError(newPartialConfigRef(), "invalid kind for firstOrd(" & $t.kind & ')') + if t.len > 0: result = firstOrd(conf, lastSon(t)) + else: internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')') else: - internalError(newPartialConfigRef(), "invalid kind for firstOrd(" & $t.kind & ')') + internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')') result = 0 -proc lastOrd*(t: PType; fixedUnsigned = false): BiggestInt = + +proc firstFloat*(t: PType): BiggestFloat = + case t.kind + of tyFloat..tyFloat128: -Inf + of tyRange: + assert(t.n != nil) # range directly given: + assert(t.n.kind == nkRange) + getFloatValue(t.n.sons[0]) + of tyVar: firstFloat(t.sons[0]) + of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink, + tyStatic, tyInferred: + firstFloat(lastSon(t)) + else: + internalError(newPartialConfigRef(), "invalid kind for firstFloat(" & $t.kind & ')') + NaN + +proc lastOrd*(conf: ConfigRef; t: PType; fixedUnsigned = false): BiggestInt = case t.kind of tyBool: result = 1 of tyChar: result = 255 - of tySet, tyVar: result = lastOrd(t.sons[0]) - of tyArray: result = lastOrd(t.sons[0]) + of tySet, tyVar: result = lastOrd(conf, t.sons[0]) + of tyArray: result = lastOrd(conf, t.sons[0]) of tyRange: assert(t.n != nil) # range directly given: assert(t.n.kind == nkRange) result = getOrdValue(t.n.sons[1]) of tyInt: - if platform.intSize == 4: result = 0x7FFFFFFF + if conf != nil and conf.target.intSize == 4: result = 0x7FFFFFFF else: result = 0x7FFFFFFFFFFFFFFF'i64 of tyInt8: result = 0x0000007F of tyInt16: result = 0x00007FFF of tyInt32: result = 0x7FFFFFFF of tyInt64: result = 0x7FFFFFFFFFFFFFFF'i64 of tyUInt: - if platform.intSize == 4: result = 0xFFFFFFFF + if conf != nil and conf.target.intSize == 4: result = 0xFFFFFFFF elif fixedUnsigned: result = 0xFFFFFFFFFFFFFFFF'i64 else: result = 0x7FFFFFFFFFFFFFFF'i64 of tyUInt8: result = 0xFF @@ -653,28 +678,46 @@ proc lastOrd*(t: PType; fixedUnsigned = false): 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, tyTypeDesc, tyAlias, tyStatic, tyInferred: - result = lastOrd(lastSon(t)) + of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink, + tyStatic, tyInferred: + result = lastOrd(conf, lastSon(t)) of tyProxy: result = 0 of tyOrdinal: - if t.len > 0: result = lastOrd(lastSon(t)) - else: internalError(newPartialConfigRef(), "invalid kind for lastOrd(" & $t.kind & ')') + if t.len > 0: result = lastOrd(conf, lastSon(t)) + else: internalError(conf, "invalid kind for lastOrd(" & $t.kind & ')') else: - internalError(newPartialConfigRef(), "invalid kind for lastOrd(" & $t.kind & ')') + internalError(conf, "invalid kind for lastOrd(" & $t.kind & ')') result = 0 -proc lengthOrd*(t: PType): BiggestInt = + +proc lastFloat*(t: PType): BiggestFloat = + case t.kind + of tyFloat..tyFloat128: Inf + of tyVar: lastFloat(t.sons[0]) + of tyRange: + assert(t.n != nil) # range directly given: + assert(t.n.kind == nkRange) + getFloatValue(t.n.sons[1]) + of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink, + tyStatic, tyInferred: + lastFloat(lastSon(t)) + else: + internalError(newPartialConfigRef(), "invalid kind for lastFloat(" & $t.kind & ')') + NaN + + +proc lengthOrd*(conf: ConfigRef; t: PType): BiggestInt = case t.kind - of tyInt64, tyInt32, tyInt: result = lastOrd(t) - of tyDistinct: result = lengthOrd(t.sons[0]) + of tyInt64, tyInt32, tyInt: result = lastOrd(conf, t) + of tyDistinct: result = lengthOrd(conf, t.sons[0]) else: - let last = lastOrd t - let first = firstOrd t + let last = lastOrd(conf, t) + let first = firstOrd(conf, t) # XXX use a better overflow check here: if last == high(BiggestInt) and first <= 0: result = last else: - result = lastOrd(t) - firstOrd(t) + 1 + result = lastOrd(conf, t) - firstOrd(conf, t) + 1 # -------------- type equality ----------------------------------------------- @@ -1209,81 +1252,24 @@ proc typeAllowed*(t: PType, kind: TSymKind; flags: TTypeAllowedFlags = {}): PTyp proc align(address, alignment: BiggestInt): BiggestInt = result = (address + (alignment - 1)) and not (alignment - 1) -type - OptKind* = enum ## What to map 'opt T' to internally. - oBool ## opt[T] requires an additional 'bool' field - oNil ## opt[T] has no overhead since 'nil' - ## is available - oEnum ## We can use some enum value that is not yet - ## used for opt[T] - oPtr ## opt[T] actually introduces a hidden pointer - ## in order for the type recursion to work - -proc optKind*(typ: PType): OptKind = - ## return true iff 'opt[T]' can be mapped to 'T' internally - ## because we have a 'nil' value available: - assert typ.kind == tyOpt - case typ.sons[0].skipTypes(abstractInst).kind - of tyRef, tyPtr, tyProc: - result = oNil - of tyArray, tyObject, tyTuple: - result = oPtr - of tyBool: result = oEnum - of tyEnum: - assert(typ.n.sons[0].kind == nkSym) - if typ.n.sons[0].sym.position != low(int): - result = oEnum - else: - result = oBool - else: - result = oBool - -proc optLowering*(typ: PType): PType = - case optKind(typ) - of oNil: result = typ.sons[0] - of oPtr: - result = newType(tyOptAsRef, typ.owner) - result.rawAddSon typ.sons[0] - of oBool: - result = newType(tyTuple, typ.owner) - result.rawAddSon newType(tyBool, typ.owner) - result.rawAddSon typ.sons[0] - of oEnum: - if lastOrd(typ) + 1 < `shl`(BiggestInt(1), 32): - result = newType(tyInt32, typ.owner) - else: - result = newType(tyInt64, typ.owner) - -proc optEnumValue*(typ: PType): BiggestInt = - assert typ.kind == tyOpt - assert optKind(typ) == oEnum - let elem = typ.sons[0].skipTypes(abstractInst).kind - if elem == tyBool: - result = 2 - else: - assert elem == tyEnum - assert typ.n.sons[0].sym.position != low(int) - result = typ.n.sons[0].sym.position - 1 - - const szNonConcreteType* = -3 szIllegalRecursion* = -2 szUnknownSize* = -1 -proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt -proc computeRecSizeAux(n: PNode, a, currOffset: var BiggestInt): BiggestInt = +proc computeSizeAux(conf: ConfigRef; typ: PType, a: var BiggestInt): BiggestInt +proc computeRecSizeAux(conf: ConfigRef; n: PNode, a, currOffset: var BiggestInt): BiggestInt = var maxAlign, maxSize, b, res: BiggestInt case n.kind of nkRecCase: assert(n.sons[0].kind == nkSym) - result = computeRecSizeAux(n.sons[0], a, currOffset) + result = computeRecSizeAux(conf, n.sons[0], a, currOffset) maxSize = 0 maxAlign = 1 for i in countup(1, sonsLen(n) - 1): case n.sons[i].kind of nkOfBranch, nkElse: - res = computeRecSizeAux(lastSon(n.sons[i]), b, currOffset) + res = computeRecSizeAux(conf, lastSon(n.sons[i]), b, currOffset) if res < 0: return res maxSize = max(maxSize, res) maxAlign = max(maxAlign, b) @@ -1296,20 +1282,20 @@ proc computeRecSizeAux(n: PNode, a, currOffset: var BiggestInt): BiggestInt = result = 0 maxAlign = 1 for i in countup(0, sonsLen(n) - 1): - res = computeRecSizeAux(n.sons[i], b, currOffset) + res = computeRecSizeAux(conf, n.sons[i], b, currOffset) if res < 0: return res currOffset = align(currOffset, b) + res result = align(result, b) + res if b > maxAlign: maxAlign = b a = maxAlign of nkSym: - result = computeSizeAux(n.sym.typ, a) + result = computeSizeAux(conf, n.sym.typ, a) n.sym.offset = int(currOffset) else: a = 1 result = szNonConcreteType -proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt = +proc computeSizeAux(conf: ConfigRef; typ: PType, a: var BiggestInt): BiggestInt = var res, maxAlign, length, currOffset: BiggestInt if typ.size == szIllegalRecursion: # we are already computing the size of the type @@ -1323,7 +1309,7 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt = typ.size = szIllegalRecursion # mark as being computed case typ.kind of tyInt, tyUInt: - result = intSize + result = conf.target.intSize a = result of tyInt8, tyUInt8, tyBool, tyChar: result = 1 @@ -1341,30 +1327,30 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt = result = 16 a = result of tyFloat: - result = floatSize + result = conf.target.floatSize a = result of tyProc: - if typ.callConv == ccClosure: result = 2 * ptrSize - else: result = ptrSize - a = ptrSize + if typ.callConv == ccClosure: result = 2 * conf.target.ptrSize + else: result = conf.target.ptrSize + a = conf.target.ptrSize of tyString, tyNil: - result = ptrSize + result = conf.target.ptrSize a = result of tyCString, tySequence, tyPtr, tyRef, tyVar, tyLent, tyOpenArray: let base = typ.lastSon if base == typ or (base.kind == tyTuple and base.size==szIllegalRecursion): result = szIllegalRecursion - else: result = ptrSize + else: result = conf.target.ptrSize a = result of tyArray: - let elemSize = computeSizeAux(typ.sons[1], a) + let elemSize = computeSizeAux(conf, typ.sons[1], a) if elemSize < 0: return elemSize - result = lengthOrd(typ.sons[0]) * elemSize + result = lengthOrd(conf, typ.sons[0]) * elemSize of tyEnum: - if firstOrd(typ) < 0: + if firstOrd(conf, typ) < 0: result = 4 # use signed int32 else: - length = lastOrd(typ) # BUGFIX: use lastOrd! + length = lastOrd(conf, typ) # BUGFIX: use lastOrd! if length + 1 < `shl`(1, 8): result = 1 elif length + 1 < `shl`(1, 16): result = 2 elif length + 1 < `shl`(BiggestInt(1), 32): result = 4 @@ -1374,7 +1360,7 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt = if typ.sons[0].kind == tyGenericParam: result = szUnknownSize else: - length = lengthOrd(typ.sons[0]) + length = lengthOrd(conf, typ.sons[0]) if length <= 8: result = 1 elif length <= 16: result = 2 elif length <= 32: result = 4 @@ -1383,12 +1369,12 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt = else: result = align(length, 8) div 8 + 1 a = result of tyRange: - result = computeSizeAux(typ.sons[0], a) + result = computeSizeAux(conf, typ.sons[0], a) of tyTuple: result = 0 maxAlign = 1 for i in countup(0, sonsLen(typ) - 1): - res = computeSizeAux(typ.sons[i], a) + res = computeSizeAux(conf, typ.sons[i], a) if res < 0: return res maxAlign = max(maxAlign, a) result = align(result, a) + res @@ -1396,61 +1382,52 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt = a = maxAlign of tyObject: if typ.sons[0] != nil: - result = computeSizeAux(typ.sons[0].skipTypes(skipPtrs), a) + result = computeSizeAux(conf, typ.sons[0].skipTypes(skipPtrs), a) if result < 0: return maxAlign = a elif isObjectWithTypeFieldPredicate(typ): - result = intSize + result = conf.target.intSize maxAlign = result else: result = 0 maxAlign = 1 currOffset = result - result = computeRecSizeAux(typ.n, a, currOffset) + result = computeRecSizeAux(conf, typ.n, a, currOffset) if result < 0: return if a < maxAlign: a = maxAlign result = align(result, a) of tyInferred: if typ.len > 1: - result = computeSizeAux(typ.lastSon, a) - of tyGenericInst, tyDistinct, tyGenericBody, tyAlias: - result = computeSizeAux(lastSon(typ), a) + result = computeSizeAux(conf, typ.lastSon, a) + of tyGenericInst, tyDistinct, tyGenericBody, tyAlias, tySink: + result = computeSizeAux(conf, lastSon(typ), a) of tyTypeClasses: - result = if typ.isResolvedUserTypeClass: computeSizeAux(typ.lastSon, a) + result = if typ.isResolvedUserTypeClass: computeSizeAux(conf, typ.lastSon, a) else: szUnknownSize of tyTypeDesc: - result = computeSizeAux(typ.base, a) + result = computeSizeAux(conf, typ.base, a) of tyForward: return szIllegalRecursion of tyStatic: - result = if typ.n != nil: computeSizeAux(typ.lastSon, a) + result = if typ.n != nil: computeSizeAux(conf, typ.lastSon, a) else: szUnknownSize - of tyOpt: - case optKind(typ) - of oBool: result = computeSizeAux(lastSon(typ), a) + 1 - of oEnum: - if lastOrd(typ) + 1 < `shl`(BiggestInt(1), 32): result = 4 - else: result = 8 - of oNil: result = computeSizeAux(lastSon(typ), a) - of oPtr: result = ptrSize else: #internalError("computeSizeAux()") result = szUnknownSize typ.size = result typ.align = int16(a) -proc computeSize*(typ: PType): BiggestInt = +proc computeSize*(conf: ConfigRef; typ: PType): BiggestInt = var a: BiggestInt = 1 - result = computeSizeAux(typ, a) + result = computeSizeAux(conf, typ, a) proc getReturnType*(s: PSym): PType = # Obtains the return type of a iterator/proc/macro/template assert s.kind in skProcKinds result = s.typ.sons[0] -proc getSize*(typ: PType): BiggestInt = - result = computeSize(typ) - #if result < 0: internalError("getSize: " & $typ.kind) - # XXX review all usages of 'getSize' +proc getSize*(conf: ConfigRef; typ: PType): BiggestInt = + result = computeSize(conf, typ) + if result < 0: internalError(conf, "getSize: " & $typ.kind) proc containsGenericTypeIter(t: PType, closure: RootRef): bool = case t.kind @@ -1601,7 +1578,7 @@ proc isEmptyContainer*(t: PType): bool = of tyArray: result = t.sons[1].kind == tyEmpty of tySet, tySequence, tyOpenArray, tyVarargs: result = t.sons[0].kind == tyEmpty - of tyGenericInst, tyAlias: result = isEmptyContainer(t.lastSon) + of tyGenericInst, tyAlias, tySink: result = isEmptyContainer(t.lastSon) else: result = false proc takeType*(formal, arg: PType): PType = diff --git a/compiler/vm.nim b/compiler/vm.nim index cbd304caa..b16eb0fd4 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -19,7 +19,7 @@ import ast except getstr import strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, parser, vmdeps, idents, trees, renderer, options, transf, parseutils, - vmmarshal, gorgeimpl, configuration + vmmarshal, gorgeimpl, lineinfos, tables, btrees, macrocacheimpl from semfold import leValueConv, ordinalValToString from evaltempl import evalTemplate @@ -66,7 +66,7 @@ proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int; recursionLimit=100) = stackTraceAux(c, x.next, x.comesFrom, recursionLimit-1) var info = c.debug[pc] # we now use the same format as in system/except.nim - var s = substr(toFilename(info), 0) + var s = substr(toFilename(c.config, info), 0) # this 'substr' prevents a strange corruption. XXX This needs to be # investigated eventually but first attempts to fix it broke everything # see the araq-wip-fixed-writebarrier branch. @@ -204,22 +204,14 @@ proc asgnComplex(x: var TFullReg, y: TFullReg) = of rkRegisterAddr: x.regAddr = y.regAddr of rkNodeAddr: x.nodeAddr = y.nodeAddr -proc putIntoNode(n: var PNode; x: TFullReg) = +proc writeField(n: var PNode, x: TFullReg) = case x.kind of rkNone: discard of rkInt: n.intVal = x.intVal of rkFloat: n.floatVal = x.floatVal - of rkNode: - if nfIsRef in x.node.flags: - n = x.node - else: - let destIsRef = nfIsRef in n.flags - n[] = x.node[] - # Ref-ness must be kept for the destination - if destIsRef: - n.flags.incl nfIsRef - of rkRegisterAddr: putIntoNode(n, x.regAddr[]) - of rkNodeAddr: n[] = x.nodeAddr[][] + of rkNode: n = x.node + of rkRegisterAddr: writeField(n, x.regAddr[]) + of rkNodeAddr: n = x.nodeAddr[] proc putIntoReg(dest: var TFullReg; n: PNode) = case n.kind @@ -370,7 +362,7 @@ proc opConv(c: PCtx; dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): dest.intVal = int(src.floatVal) else: dest.intVal = src.intVal - if dest.intVal < firstOrd(desttyp) or dest.intVal > lastOrd(desttyp): + if dest.intVal < firstOrd(c.config, desttyp) or dest.intVal > lastOrd(c.config, desttyp): return true of tyUInt..tyUInt64: if dest.kind != rkInt: @@ -498,9 +490,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = asgnComplex(regs[ra], regs[instr.regB]) of opcAsgnRef: asgnRef(regs[ra], regs[instr.regB]) - of opcRegToNode: - decodeB(rkNode) - putIntoNode(regs[ra].node, regs[rb]) of opcNodeToReg: let ra = instr.regA let rb = instr.regB @@ -559,7 +548,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = else: stackTrace(c, tos, pc, errIndexOutOfBounds) elif idx <% arr.len: - putIntoNode(arr.sons[idx], regs[rc]) + writeField(arr.sons[idx], regs[rc]) else: stackTrace(c, tos, pc, errIndexOutOfBounds) of opcLdObj: @@ -579,9 +568,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if dest.kind == nkNilLit: stackTrace(c, tos, pc, errNilAccess) elif dest.sons[shiftedRb].kind == nkExprColonExpr: - putIntoNode(dest.sons[shiftedRb].sons[1], regs[rc]) + writeField(dest.sons[shiftedRb].sons[1], regs[rc]) else: - putIntoNode(dest.sons[shiftedRb], regs[rc]) + writeField(dest.sons[shiftedRb], regs[rc]) of opcWrStrIdx: decodeBC(rkNode) let idx = regs[rb].intVal.int @@ -624,9 +613,24 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let ra = instr.regA let rc = instr.regC case regs[ra].kind - of rkNodeAddr: putIntoNode(regs[ra].nodeAddr[], regs[rc]) + of rkNodeAddr: + # XXX: Workaround for vmgen bug: + let n = regs[rc].regToNode + if (nfIsRef in regs[ra].nodeAddr[].flags or + regs[ra].nodeAddr[].kind == nkNilLit) and nfIsRef notin n.flags: + if regs[ra].nodeAddr[].kind == nkNilLit: + stackTrace(c, tos, pc, errNilAccess) + regs[ra].nodeAddr[][] = n[] + regs[ra].nodeAddr[].flags.incl nfIsRef + else: + regs[ra].nodeAddr[] = n of rkRegisterAddr: regs[ra].regAddr[] = regs[rc] - of rkNode: putIntoNode(regs[ra].node, regs[rc]) + of rkNode: + if regs[ra].node.kind == nkNilLit: + stackTrace(c, tos, pc, errNilAccess) + assert nfIsRef in regs[ra].node.flags + regs[ra].node[] = regs[rc].regToNode[] + regs[ra].node.flags.incl nfIsRef else: stackTrace(c, tos, pc, errNilAccess) of opcAddInt: decodeBC(rkInt) @@ -700,12 +704,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = decodeB(rkNode) var b = newNodeIT(nkCurly, regs[ra].node.info, regs[ra].node.typ) addSon(b, regs[rb].regToNode) - var r = diffSets(regs[ra].node, b) + var r = diffSets(c.config, regs[ra].node, b) discardSons(regs[ra].node) for i in countup(0, sonsLen(r) - 1): addSon(regs[ra].node, r.sons[i]) of opcCard: decodeB(rkInt) - regs[ra].intVal = nimsets.cardSet(regs[rb].node) + regs[ra].intVal = nimsets.cardSet(c.config, regs[rb].node) of opcMulInt: decodeBC(rkInt) let @@ -853,35 +857,35 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].intVal = ord(regs[rb].node.strVal < regs[rc].node.strVal) of opcLeSet: decodeBC(rkInt) - regs[ra].intVal = ord(containsSets(regs[rb].node, regs[rc].node)) + regs[ra].intVal = ord(containsSets(c.config, regs[rb].node, regs[rc].node)) of opcEqSet: decodeBC(rkInt) - regs[ra].intVal = ord(equalSets(regs[rb].node, regs[rc].node)) + regs[ra].intVal = ord(equalSets(c.config, regs[rb].node, regs[rc].node)) of opcLtSet: decodeBC(rkInt) let a = regs[rb].node let b = regs[rc].node - regs[ra].intVal = ord(containsSets(a, b) and not equalSets(a, b)) + regs[ra].intVal = ord(containsSets(c.config, a, b) and not equalSets(c.config, a, b)) of opcMulSet: decodeBC(rkNode) createSet(regs[ra]) move(regs[ra].node.sons, - nimsets.intersectSets(regs[rb].node, regs[rc].node).sons) + nimsets.intersectSets(c.config, regs[rb].node, regs[rc].node).sons) of opcPlusSet: decodeBC(rkNode) createSet(regs[ra]) move(regs[ra].node.sons, - nimsets.unionSets(regs[rb].node, regs[rc].node).sons) + nimsets.unionSets(c.config, regs[rb].node, regs[rc].node).sons) of opcMinusSet: decodeBC(rkNode) createSet(regs[ra]) move(regs[ra].node.sons, - nimsets.diffSets(regs[rb].node, regs[rc].node).sons) + nimsets.diffSets(c.config, regs[rb].node, regs[rc].node).sons) of opcSymdiffSet: decodeBC(rkNode) createSet(regs[ra]) move(regs[ra].node.sons, - nimsets.symdiffSets(regs[rb].node, regs[rc].node).sons) + nimsets.symdiffSets(c.config, regs[rb].node, regs[rc].node).sons) of opcConcatStr: decodeBC(rkNode) createStr regs[ra] @@ -972,7 +976,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = when hasFFI: let prcValue = c.globals.sons[prc.position-1] if prcValue.kind == nkEmpty: - globalError(c.config, c.debug[pc], "canot run " & prc.name.s) + globalError(c.config, c.debug[pc], "cannot run " & prc.name.s) let newValue = callForeignFunction(prcValue, prc.typ, tos.slots, rb+1, rc-1, c.debug[pc]) if newValue.kind != nkEmpty: @@ -1211,6 +1215,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcNBindSym: decodeBx(rkNode) regs[ra].node = copyTree(c.constants.sons[rbx]) + regs[ra].node.flags.incl nfIsRef of opcNChild: decodeBC(rkNode) let idx = regs[rc].intVal.int @@ -1291,7 +1296,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = # getType opcode: ensureKind(rkNode) if regs[rb].kind == rkNode and regs[rb].node.typ != nil: - regs[ra].node = opMapTypeToAst(regs[rb].node.typ, c.debug[pc]) + regs[ra].node = opMapTypeToAst(c.cache, regs[rb].node.typ, c.debug[pc]) else: stackTrace(c, tos, pc, "node has no type") of 1: @@ -1305,14 +1310,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = # getTypeInst opcode: ensureKind(rkNode) if regs[rb].kind == rkNode and regs[rb].node.typ != nil: - regs[ra].node = opMapTypeInstToAst(regs[rb].node.typ, c.debug[pc]) + regs[ra].node = opMapTypeInstToAst(c.cache, regs[rb].node.typ, c.debug[pc]) else: stackTrace(c, tos, pc, "node has no type") else: # getTypeImpl opcode: ensureKind(rkNode) if regs[rb].kind == rkNode and regs[rb].node.typ != nil: - regs[ra].node = opMapTypeImplToAst(regs[rb].node.typ, c.debug[pc]) + regs[ra].node = opMapTypeImplToAst(c.cache, regs[rb].node.typ, c.debug[pc]) else: stackTrace(c, tos, pc, "node has no type") of opcNStrVal: @@ -1336,14 +1341,17 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].node.strVal = opSlurp(regs[rb].node.strVal, c.debug[pc], c.module, c.config) of opcGorge: - decodeBC(rkNode) - inc pc - let rd = c.code[pc].regA - - createStr regs[ra] - regs[ra].node.strVal = opGorge(regs[rb].node.strVal, - regs[rc].node.strVal, regs[rd].node.strVal, - c.debug[pc], c.config)[0] + when defined(nimcore): + decodeBC(rkNode) + inc pc + let rd = c.code[pc].regA + + createStr regs[ra] + regs[ra].node.strVal = opGorge(regs[rb].node.strVal, + regs[rc].node.strVal, regs[rd].node.strVal, + c.debug[pc], c.config)[0] + else: + globalError(c.config, c.debug[pc], "VM is not built with 'gorge' support") of opcNError: decodeB(rkNode) let a = regs[ra].node @@ -1358,7 +1366,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = # c.debug[pc].line.int - countLines(regs[rb].strVal) ? var error: string let ast = parseString(regs[rb].node.strVal, c.cache, c.config, - c.debug[pc].toFullPath, c.debug[pc].line.int, + toFullPath(c.config, c.debug[pc]), c.debug[pc].line.int, proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) = if error.isNil and msg <= errMax: error = formatMsg(conf, info, msg, arg)) @@ -1373,7 +1381,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = decodeB(rkNode) var error: string let ast = parseString(regs[rb].node.strVal, c.cache, c.config, - c.debug[pc].toFullPath, c.debug[pc].line.int, + toFullPath(c.config, c.debug[pc]), c.debug[pc].line.int, proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) = if error.isNil and msg <= errMax: error = formatMsg(conf, info, msg, arg)) @@ -1392,7 +1400,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcNGetFile: decodeB(rkNode) let n = regs[rb].node - regs[ra].node = newStrNode(nkStrLit, n.info.toFilename) + regs[ra].node = newStrNode(nkStrLit, toFilename(c.config, n.info)) regs[ra].node.info = n.info regs[ra].node.typ = n.typ of opcNGetLine: @@ -1453,7 +1461,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = stackTrace(c, tos, pc, errFieldXNotFound & "strVal") else: regs[ra].node = newNodeI(nkIdent, c.debug[pc]) - regs[ra].node.ident = getIdent(regs[rb].node.strVal) + regs[ra].node.ident = getIdent(c.cache, regs[rb].node.strVal) of opcSetType: if regs[ra].kind != rkNode: internalError(c.config, c.debug[pc], "cannot set type") @@ -1566,9 +1574,110 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = else: regs[rc].node.strVal if k < 0 or k > ord(high(TSymKind)): internalError(c.config, c.debug[pc], "request to create symbol of invalid kind") - var sym = newSym(k.TSymKind, name.getIdent, c.module.owner, c.debug[pc]) + var sym = newSym(k.TSymKind, getIdent(c.cache, name), c.module.owner, c.debug[pc]) incl(sym.flags, sfGenSym) regs[ra].node = newSymNode(sym) + regs[ra].node.flags.incl nfIsRef + of opcNccValue: + decodeB(rkInt) + let destKey = regs[rb].node.strVal + regs[ra].intVal = getOrDefault(c.graph.cacheCounters, destKey) + of opcNccInc: + let g = c.graph + let destKey = regs[ra].node.strVal + let by = regs[instr.regB].intVal + let v = getOrDefault(g.cacheCounters, destKey) + g.cacheCounters[destKey] = v+by + recordInc(c, c.debug[pc], destKey, by) + of opcNcsAdd: + let g = c.graph + let destKey = regs[ra].node.strVal + let val = regs[instr.regB].node + if not contains(g.cacheSeqs, destKey): + g.cacheSeqs[destKey] = newTree(nkStmtList, val) + # newNodeI(nkStmtList, c.debug[pc]) + else: + g.cacheSeqs[destKey].add val + recordAdd(c, c.debug[pc], destKey, val) + of opcNcsIncl: + let g = c.graph + let destKey = regs[ra].node.strVal + let val = regs[instr.regB].node + if not contains(g.cacheSeqs, destKey): + g.cacheSeqs[destKey] = newTree(nkStmtList, val) + else: + block search: + for existing in g.cacheSeqs[destKey]: + if exprStructuralEquivalent(existing, val, strictSymEquality=true): + break search + g.cacheSeqs[destKey].add val + recordIncl(c, c.debug[pc], destKey, val) + of opcNcsLen: + let g = c.graph + decodeB(rkInt) + let destKey = regs[rb].node.strVal + regs[ra].intVal = + if contains(g.cacheSeqs, destKey): g.cacheSeqs[destKey].len else: 0 + of opcNcsAt: + let g = c.graph + decodeBC(rkNode) + let idx = regs[rc].intVal + let destKey = regs[rb].node.strVal + if contains(g.cacheSeqs, destKey) and idx <% g.cacheSeqs[destKey].len: + regs[ra].node = g.cacheSeqs[destKey][idx.int] + else: + stackTrace(c, tos, pc, errIndexOutOfBounds) + of opcNctPut: + let g = c.graph + let destKey = regs[ra].node.strVal + let key = regs[instr.regB].node.strVal + let val = regs[instr.regC].node + if not contains(g.cacheTables, destKey): + g.cacheTables[destKey] = initBTree[string, PNode]() + if not contains(g.cacheTables[destKey], key): + g.cacheTables[destKey].add(key, val) + recordPut(c, c.debug[pc], destKey, key, val) + else: + stackTrace(c, tos, pc, "key already exists: " & key) + of opcNctLen: + let g = c.graph + decodeB(rkInt) + let destKey = regs[rb].node.strVal + regs[ra].intVal = + if contains(g.cacheTables, destKey): g.cacheTables[destKey].len else: 0 + of opcNctGet: + let g = c.graph + decodeBC(rkNode) + let destKey = regs[rb].node.strVal + let key = regs[rc].node.strVal + if contains(g.cacheTables, destKey): + if contains(g.cacheTables[destKey], key): + regs[ra].node = getOrDefault(g.cacheTables[destKey], key) + else: + stackTrace(c, tos, pc, "key does not exist: " & key) + else: + stackTrace(c, tos, pc, "key does not exist: " & destKey) + of opcNctHasNext: + let g = c.graph + decodeBC(rkInt) + let destKey = regs[rb].node.strVal + regs[ra].intVal = + if g.cacheTables.contains(destKey): + ord(btrees.hasNext(g.cacheTables[destKey], regs[rc].intVal.int)) + else: + 0 + of opcNctNext: + let g = c.graph + decodeBC(rkNode) + let destKey = regs[rb].node.strVal + let index = regs[rc].intVal + if contains(g.cacheTables, destKey): + let (k, v, nextIndex) = btrees.next(g.cacheTables[destKey], index.int) + regs[ra].node = newTree(nkTupleConstr, newStrNode(k, c.debug[pc]), v, + newIntNode(nkIntLit, nextIndex)) + else: + stackTrace(c, tos, pc, "key does not exist: " & destKey) + of opcTypeTrait: # XXX only supports 'name' for now; we can use regC to encode the # type trait operation @@ -1583,7 +1692,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let rb = instr.regB inc pc let typ = c.types[c.code[pc].regBx - wordExcess] - putIntoReg(regs[ra], loadAny(regs[rb].node.strVal, typ, c.config)) + putIntoReg(regs[ra], loadAny(regs[rb].node.strVal, typ, c.cache, c.config)) of opcMarshalStore: decodeB(rkNode) inc pc @@ -1654,36 +1763,30 @@ proc getGlobalValue*(c: PCtx; s: PSym): PNode = include vmops -# for now we share the 'globals' environment. XXX Coming soon: An API for -# storing&loading the 'globals' environment to get what a component system -# requires. -var - globalCtx*: PCtx - -proc setupGlobalCtx(module: PSym; cache: IdentCache; graph: ModuleGraph) = - if globalCtx.isNil: - globalCtx = newCtx(module, cache, graph) - registerAdditionalOps(globalCtx) +proc setupGlobalCtx(module: PSym; graph: ModuleGraph) = + if graph.vm.isNil: + graph.vm = newCtx(module, graph.cache, graph) + registerAdditionalOps(PCtx graph.vm) else: - refresh(globalCtx, module) + refresh(PCtx graph.vm, module) -proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = +proc myOpen(graph: ModuleGraph; module: PSym): PPassContext = #var c = newEvalContext(module, emRepl) #c.features = {allowCast, allowFFI, allowInfiniteLoops} #pushStackFrame(c, newStackFrame()) # XXX produce a new 'globals' environment here: - setupGlobalCtx(module, cache, graph) - result = globalCtx + setupGlobalCtx(module, graph) + result = PCtx graph.vm when hasFFI: - globalCtx.features = {allowFFI, allowCast} + PCtx(graph.vm).features = {allowFFI, allowCast} proc myProcess(c: PPassContext, n: PNode): PNode = let c = PCtx(c) # don't eval errornous code: if c.oldErrorCount == c.config.errorCounter: evalStmt(c, n) - result = emptyNode + result = newNodeI(nkEmpty, n.info) else: result = n c.oldErrorCount = c.config.errorCounter @@ -1691,19 +1794,19 @@ proc myProcess(c: PPassContext, n: PNode): PNode = proc myClose(graph: ModuleGraph; c: PPassContext, n: PNode): PNode = myProcess(c, n) -const evalPass* = makePass(myOpen, nil, myProcess, myClose) +const evalPass* = makePass(myOpen, myProcess, myClose) -proc evalConstExprAux(module: PSym; cache: IdentCache; +proc evalConstExprAux(module: PSym; g: ModuleGraph; prc: PSym, n: PNode, mode: TEvalMode): PNode = let n = transformExpr(g, module, n) - setupGlobalCtx(module, cache, g) - var c = globalCtx + setupGlobalCtx(module, g) + var c = PCtx g.vm let oldMode = c.mode defer: c.mode = oldMode c.mode = mode let start = genExpr(c, n, requiresValue = mode!=emStaticStmt) - if c.code[start].opcode == opcEof: return emptyNode + if c.code[start].opcode == opcEof: return newNodeI(nkEmpty, n.info) assert c.code[start].opcode != opcEof when debugEchoCode: c.echoCode start var tos = PStackFrame(prc: prc, comesFrom: 0, next: nil) @@ -1712,17 +1815,17 @@ proc evalConstExprAux(module: PSym; cache: IdentCache; result = rawExecute(c, start, tos).regToNode if result.info.col < 0: result.info = n.info -proc evalConstExpr*(module: PSym; cache: IdentCache, g: ModuleGraph; e: PNode): PNode = - result = evalConstExprAux(module, cache, g, nil, e, emConst) +proc evalConstExpr*(module: PSym; g: ModuleGraph; e: PNode): PNode = + result = evalConstExprAux(module, g, nil, e, emConst) -proc evalStaticExpr*(module: PSym; cache: IdentCache, g: ModuleGraph; e: PNode, prc: PSym): PNode = - result = evalConstExprAux(module, cache, g, prc, e, emStaticExpr) +proc evalStaticExpr*(module: PSym; g: ModuleGraph; e: PNode, prc: PSym): PNode = + result = evalConstExprAux(module, g, prc, e, emStaticExpr) -proc evalStaticStmt*(module: PSym; cache: IdentCache, g: ModuleGraph; e: PNode, prc: PSym) = - discard evalConstExprAux(module, cache, g, prc, e, emStaticStmt) +proc evalStaticStmt*(module: PSym; g: ModuleGraph; e: PNode, prc: PSym) = + discard evalConstExprAux(module, g, prc, e, emStaticStmt) -proc setupCompileTimeVar*(module: PSym; cache: IdentCache, g: ModuleGraph; n: PNode) = - discard evalConstExprAux(module, cache, g, nil, n, emStaticStmt) +proc setupCompileTimeVar*(module: PSym; g: ModuleGraph; n: PNode) = + discard evalConstExprAux(module, g, nil, n, emStaticStmt) proc setupMacroParam(x: PNode, typ: PType): TFullReg = case typ.kind @@ -1748,13 +1851,12 @@ iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) = # to prevent endless recursion in macro instantiation const evalMacroLimit = 1000 -var evalMacroCounter: int -proc evalMacroCall*(module: PSym; cache: IdentCache; g: ModuleGraph; +proc evalMacroCall*(module: PSym; g: ModuleGraph; n, nOrig: PNode, sym: PSym): PNode = # XXX globalError() is ugly here, but I don't know a better solution for now - inc(evalMacroCounter) - if evalMacroCounter > evalMacroLimit: + inc(g.config.evalMacroCounter) + if g.config.evalMacroCounter > evalMacroLimit: globalError(g.config, n.info, "macro instantiation too nested") # immediate macros can bypass any type and arity checking so we check the @@ -1763,8 +1865,8 @@ proc evalMacroCall*(module: PSym; cache: IdentCache; g: ModuleGraph; globalError(g.config, n.info, "in call '$#' got $#, but expected $# argument(s)" % [ n.renderTree, $(n.safeLen-1), $(sym.typ.len-1)]) - setupGlobalCtx(module, cache, g) - var c = globalCtx + setupGlobalCtx(module, g) + var c = PCtx g.vm c.comesFromHeuristic.line = 0'u16 c.callsite = nOrig @@ -1795,12 +1897,12 @@ proc evalMacroCall*(module: PSym; cache: IdentCache; g: ModuleGraph; if idx < n.len: tos.slots[idx] = setupMacroParam(n.sons[idx], gp[i].sym.typ) else: - dec(evalMacroCounter) + dec(g.config.evalMacroCounter) c.callsite = nil localError(c.config, n.info, "expected " & $gp.len & " generic parameter(s)") elif gp[i].sym.typ.kind in {tyStatic, tyTypeDesc}: - dec(evalMacroCounter) + dec(g.config.evalMacroCounter) c.callsite = nil globalError(c.config, n.info, "static[T] or typedesc nor supported for .immediate macros") # temporary storage: @@ -1808,5 +1910,5 @@ proc evalMacroCall*(module: PSym; cache: IdentCache; g: ModuleGraph; result = rawExecute(c, start, tos).regToNode if result.info.line < 0: result.info = n.info if cyclicTree(result): globalError(c.config, n.info, "macro produced a cyclic tree") - dec(evalMacroCounter) + dec(g.config.evalMacroCounter) c.callsite = nil diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index e10a62006..f7466b392 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -10,7 +10,8 @@ ## This module contains the type definitions for the new evaluation engine. ## An instruction is 1-3 int32s in memory, it is a register based VM. -import ast, passes, msgs, idents, intsets, options, modulegraphs +import ast, passes, msgs, idents, intsets, options, modulegraphs, lineinfos, + tables, btrees const byteExcess* = 128 # we use excess-K for immediates @@ -35,7 +36,6 @@ type opcAsgnFloat, opcAsgnRef, opcAsgnComplex, - opcRegToNode, opcNodeToReg, opcLdArr, # a = b[c] @@ -91,6 +91,9 @@ type opcNSetFloatVal, opcNSetSymbol, opcNSetIdent, opcNSetType, opcNSetStrVal, opcNNewNimNode, opcNCopyNimNode, opcNCopyNimTree, opcNDel, opcGenSym, + opcNccValue, opcNccInc, opcNcsAdd, opcNcsIncl, opcNcsLen, opcNcsAt, + opcNctPut, opcNctLen, opcNctGet, opcNctHasNext, opcNctNext, + opcSlurp, opcGorge, opcParseExprToAst, diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index 2c92348a6..865ecd36e 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -7,11 +7,11 @@ # distribution, for details about the copyright. # -import ast, types, msgs, os, streams, options, idents +import ast, types, msgs, os, streams, options, idents, lineinfos proc opSlurp*(file: string, info: TLineInfo, module: PSym; conf: ConfigRef): string = try: - var filename = parentDir(info.toFullPath) / file + var filename = parentDir(toFullPath(conf, info)) / file if not fileExists(filename): filename = findFile(conf, file) result = readFile(filename) @@ -23,8 +23,8 @@ proc opSlurp*(file: string, info: TLineInfo, module: PSym; conf: ConfigRef): str localError(conf, info, "cannot open file: " & file) result = "" -proc atomicTypeX(name: string; m: TMagic; t: PType; info: TLineInfo): PNode = - let sym = newSym(skType, getIdent(name), t.owner, info) +proc atomicTypeX(cache: IdentCache; name: string; m: TMagic; t: PType; info: TLineInfo): PNode = + let sym = newSym(skType, getIdent(cache, name), t.owner, info) sym.magic = m sym.typ = t result = newSymNode(sym) @@ -34,51 +34,51 @@ proc atomicTypeX(s: PSym; info: TLineInfo): PNode = result = newSymNode(s) result.info = info -proc mapTypeToAstX(t: PType; info: TLineInfo; +proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; inst=false; allowRecursionX=false): PNode -proc mapTypeToBracketX(name: string; m: TMagic; t: PType; info: TLineInfo; +proc mapTypeToBracketX(cache: IdentCache; name: string; m: TMagic; t: PType; info: TLineInfo; inst=false): PNode = result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t) - result.add atomicTypeX(name, m, t, info) + result.add atomicTypeX(cache, name, m, t, info) for i in 0 ..< t.len: if t.sons[i] == nil: - let void = atomicTypeX("void", mVoid, t, info) + let void = atomicTypeX(cache, "void", mVoid, t, info) void.typ = newType(tyVoid, t.owner) result.add void else: - result.add mapTypeToAstX(t.sons[i], info, inst) + result.add mapTypeToAstX(cache, t.sons[i], info, inst) -proc objectNode(n: PNode): PNode = +proc objectNode(cache: IdentCache; n: PNode): PNode = if n.kind == nkSym: result = newNodeI(nkIdentDefs, n.info) result.add n # name - result.add mapTypeToAstX(n.sym.typ, n.info, true, false) # type - result.add ast.emptyNode # no assigned value + result.add mapTypeToAstX(cache, n.sym.typ, n.info, true, false) # type + result.add newNodeI(nkEmpty, n.info) # no assigned value else: result = copyNode(n) for i in 0 ..< n.safeLen: - result.add objectNode(n[i]) + result.add objectNode(cache, n[i]) -proc mapTypeToAstX(t: PType; info: TLineInfo; +proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; inst=false; allowRecursionX=false): PNode = var allowRecursion = allowRecursionX - template atomicType(name, m): untyped = atomicTypeX(name, m, t, info) + template atomicType(name, m): untyped = atomicTypeX(cache, name, m, t, info) template atomicType(s): untyped = atomicTypeX(s, info) - template mapTypeToAst(t,info): untyped = mapTypeToAstX(t, info, inst) - template mapTypeToAstR(t,info): untyped = mapTypeToAstX(t, info, inst, true) + template mapTypeToAst(t,info): untyped = mapTypeToAstX(cache, t, info, inst) + template mapTypeToAstR(t,info): untyped = mapTypeToAstX(cache, t, info, inst, true) template mapTypeToAst(t,i,info): untyped = - if i<t.len and t.sons[i]!=nil: mapTypeToAstX(t.sons[i], info, inst) - else: ast.emptyNode + if i<t.len and t.sons[i]!=nil: mapTypeToAstX(cache, t.sons[i], info, inst) + else: newNodeI(nkEmpty, info) template mapTypeToBracket(name, m, t, info): untyped = - mapTypeToBracketX(name, m, t, info, inst) + mapTypeToBracketX(cache, name, m, t, info, inst) template newNodeX(kind): untyped = newNodeIT(kind, if t.n.isNil: info else: t.n.info, t) template newIdentDefs(n,t): untyped = var id = newNodeX(nkIdentDefs) id.add n # name id.add mapTypeToAst(t, info) # type - id.add ast.emptyNode # no assigned value + id.add newNodeI(nkEmpty, info) # no assigned value id template newIdentDefs(s): untyped = newIdentDefs(s, s.typ) @@ -103,7 +103,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo; result.add atomicType("array", mArray) if inst and t.sons[0].kind == tyRange: var rng = newNodeX(nkInfix) - rng.add newIdentNode(getIdent(".."), info) + rng.add newIdentNode(getIdent(cache, ".."), info) rng.add t.sons[0].n.sons[0].copyTree rng.add t.sons[0].n.sons[1].copyTree result.add rng @@ -132,14 +132,14 @@ proc mapTypeToAstX(t: PType; info: TLineInfo; for i in 1 ..< t.len-1: result.add mapTypeToAst(t.sons[i], info) else: - result = mapTypeToAstX(t.lastSon, info, inst, allowRecursion) + result = mapTypeToAstX(cache, t.lastSon, info, inst, allowRecursion) of tyGenericBody: if inst: result = mapTypeToAstR(t.lastSon, info) else: result = mapTypeToAst(t.lastSon, info) of tyAlias: - result = mapTypeToAstX(t.lastSon, info, inst, allowRecursion) + result = mapTypeToAstX(cache, t.lastSon, info, inst, allowRecursion) of tyOrdinal: result = mapTypeToAst(t.lastSon, info) of tyDistinct: @@ -156,22 +156,22 @@ proc mapTypeToAstX(t: PType; info: TLineInfo; of tyObject: if inst: result = newNodeX(nkObjectTy) - result.add ast.emptyNode # pragmas not reconstructed yet - if t.sons[0] == nil: result.add ast.emptyNode # handle parent object + result.add newNodeI(nkEmpty, info) # pragmas not reconstructed yet + if t.sons[0] == nil: result.add newNodeI(nkEmpty, info) # handle parent object else: var nn = newNodeX(nkOfInherit) nn.add mapTypeToAst(t.sons[0], info) result.add nn if t.n.len > 0: - result.add objectNode(t.n) + result.add objectNode(cache, t.n) else: - result.add ast.emptyNode + result.add newNodeI(nkEmpty, info) else: if allowRecursion or t.sym == nil: result = newNodeIT(nkObjectTy, if t.n.isNil: info else: t.n.info, t) - result.add ast.emptyNode + result.add newNodeI(nkEmpty, info) if t.sons[0] == nil: - result.add ast.emptyNode + result.add newNodeI(nkEmpty, info) else: result.add mapTypeToAst(t.sons[0], info) result.add copyTree(t.n) @@ -179,7 +179,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo; result = atomicType(t.sym) of tyEnum: result = newNodeIT(nkEnumTy, if t.n.isNil: info else: t.n.info, t) - result.add ast.emptyNode # pragma node, currently always empty for enum + result.add newNodeI(nkEmpty, info) # pragma node, currently always empty for enum for c in t.n.sons: result.add copyTree(c) of tyTuple: @@ -223,13 +223,13 @@ proc mapTypeToAstX(t: PType; info: TLineInfo; result = newNodeX(nkProcTy) var fp = newNodeX(nkFormalParams) if t.sons[0] == nil: - fp.add ast.emptyNode + fp.add newNodeI(nkEmpty, info) else: fp.add mapTypeToAst(t.sons[0], t.n[0].info) for i in 1..<t.sons.len: fp.add newIdentDefs(t.n[i], t.sons[i]) result.add fp - result.add ast.emptyNode # pragmas aren't reconstructed yet + result.add newNodeI(nkEmpty, info) # pragmas aren't reconstructed yet else: result = mapTypeToBracket("proc", mNone, t, info) of tyOpenArray: result = mapTypeToBracket("openArray", mOpenArray, t, info) @@ -283,15 +283,15 @@ proc mapTypeToAstX(t: PType; info: TLineInfo; result.add t.n.copyTree of tyUnused, tyOptAsRef: assert(false, "mapTypeToAstX") -proc opMapTypeToAst*(t: PType; info: TLineInfo): PNode = - result = mapTypeToAstX(t, info, false, true) +proc opMapTypeToAst*(cache: IdentCache; t: PType; info: TLineInfo): PNode = + result = mapTypeToAstX(cache, t, info, false, true) # the "Inst" version includes generic parameters in the resulting type tree # and also tries to look like the corresponding Nim type declaration -proc opMapTypeInstToAst*(t: PType; info: TLineInfo): PNode = - result = mapTypeToAstX(t, info, true, false) +proc opMapTypeInstToAst*(cache: IdentCache; t: PType; info: TLineInfo): PNode = + result = mapTypeToAstX(cache, t, info, true, false) # the "Impl" version includes generic parameters in the resulting type tree # and also tries to look like the corresponding Nim type implementation -proc opMapTypeImplToAst*(t: PType; info: TLineInfo): PNode = - result = mapTypeToAstX(t, info, true, true) +proc opMapTypeImplToAst*(cache: IdentCache; t: PType; info: TLineInfo): PNode = + result = mapTypeToAstX(cache, t, info, true, true) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 8a3c7e2e6..d2243376c 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -29,7 +29,7 @@ import strutils, ast, astalgo, types, msgs, renderer, vmdef, - trees, intsets, rodread, magicsys, options, lowerings + trees, intsets, magicsys, options, lowerings, lineinfos import platform from os import splitFile @@ -40,8 +40,8 @@ type TGenFlag = enum gfAddrOf, gfFieldAccess TGenFlags = set[TGenFlag] -proc debugInfo(info: TLineInfo): string = - result = info.toFilename.splitFile.name & ":" & $info.line +proc debugInfo(c: PCtx; info: TLineInfo): string = + result = toFilename(c.config, info).splitFile.name & ":" & $info.line proc codeListing(c: PCtx, result: var string, start=0; last = -1) = # first iteration: compute all necessary labels: @@ -85,7 +85,7 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) = else: result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA, x.regBx-wordExcess) result.add("\t#") - result.add(debugInfo(c.debug[i])) + result.add(debugInfo(c, c.debug[i])) result.add("\n") inc i @@ -550,7 +550,7 @@ proc genField(c: PCtx; n: PNode): TRegister = result = s.position proc genIndex(c: PCtx; n: PNode; arr: PType): TRegister = - if arr.skipTypes(abstractInst).kind == tyArray and (let x = firstOrd(arr); + if arr.skipTypes(abstractInst).kind == tyArray and (let x = firstOrd(c.config, arr); x != 0): let tmp = c.genx(n) # freeing the temporary here means we can produce: regA = regA - Imm @@ -767,12 +767,12 @@ proc genIntCast(c: PCtx; n: PNode; dest: var TDest) = var unsignedIntegers = {tyUInt8..tyUInt32, tyChar} let src = n.sons[1].typ.skipTypes(abstractRange)#.kind let dst = n.sons[0].typ.skipTypes(abstractRange)#.kind - let src_size = src.getSize + let src_size = getSize(c.config, src) - if platform.intSize < 8: + if c.config.target.intSize < 8: signedIntegers.incl(tyInt) unsignedIntegers.incl(tyUInt) - if src_size == dst.getSize and src.kind in allowedIntegers and + if src_size == getSize(c.config, dst) and src.kind in allowedIntegers and dst.kind in allowedIntegers: let tmp = c.genx(n.sons[1]) var tmp2 = c.getTemp(n.sons[1].typ) @@ -804,6 +804,17 @@ proc genIntCast(c: PCtx; n: PNode; dest: var TDest) = else: globalError(c.config, n.info, "VM is only allowed to 'cast' between integers of same size") +proc genVoidABC(c: PCtx, n: PNode, dest: TDest, opcode: TOpcode) = + unused(c, n, dest) + var + tmp1 = c.genx(n[1]) + tmp2 = c.genx(n[2]) + tmp3 = c.genx(n[3]) + c.gABC(n, opcode, tmp1, tmp2, tmp3) + c.freeTemp(tmp1) + c.freeTemp(tmp2) + c.freeTemp(tmp3) + proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = case m of mAnd: c.genAndOr(n, opcFJmp, dest) @@ -966,7 +977,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = c.freeTemp(tmp) of mSwap: unused(c, n, dest) - c.gen(lowerSwap(n, if c.prc == nil: c.module else: c.prc.sym)) + c.gen(lowerSwap(c.graph, n, if c.prc == nil: c.module else: c.prc.sym)) of mIsNil: genUnaryABC(c, n, dest, opcIsNil) of mCopyStr: if dest < 0: dest = c.getTemp(n.typ) @@ -1068,20 +1079,25 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mNLen: genUnaryABI(c, n, dest, opcLenSeq, nimNodeFlag) of mGetImpl: genUnaryABC(c, n, dest, opcGetImpl) of mNChild: genBinaryABC(c, n, dest, opcNChild) - of mNSetChild, mNDel: - unused(c, n, dest) - var - tmp1 = c.genx(n.sons[1]) - tmp2 = c.genx(n.sons[2]) - tmp3 = c.genx(n.sons[3]) - c.gABC(n, if m == mNSetChild: opcNSetChild else: opcNDel, tmp1, tmp2, tmp3) - c.freeTemp(tmp1) - c.freeTemp(tmp2) - c.freeTemp(tmp3) + of mNSetChild: genVoidABC(c, n, dest, opcNSetChild) + of mNDel: genVoidABC(c, n, dest, opcNDel) of mNAdd: genBinaryABC(c, n, dest, opcNAdd) of mNAddMultiple: genBinaryABC(c, n, dest, opcNAddMultiple) of mNKind: genUnaryABC(c, n, dest, opcNKind) of mNSymKind: genUnaryABC(c, n, dest, opcNSymKind) + + of mNccValue: genUnaryABC(c, n, dest, opcNccValue) + of mNccInc: genBinaryABC(c, n, dest, opcNccInc) + of mNcsAdd: genBinaryABC(c, n, dest, opcNcsAdd) + of mNcsIncl: genBinaryABC(c, n, dest, opcNcsIncl) + of mNcsLen: genUnaryABC(c, n, dest, opcNcsLen) + of mNcsAt: genBinaryABC(c, n, dest, opcNcsAt) + of mNctPut: genVoidABC(c, n, dest, opcNctPut) + of mNctLen: genUnaryABC(c, n, dest, opcNctLen) + of mNctGet: genBinaryABC(c, n, dest, opcNctGet) + of mNctHasNext: genBinaryABC(c, n, dest, opcNctHasNext) + of mNctNext: genBinaryABC(c, n, dest, opcNctNext) + of mNIntVal: genUnaryABC(c, n, dest, opcNIntVal) of mNFloatVal: genUnaryABC(c, n, dest, opcNFloatVal) of mNSymbol: genUnaryABC(c, n, dest, opcNSymbol) @@ -1544,7 +1560,6 @@ proc getNullValueAux(obj: PNode, result: PNode; conf: ConfigRef) = proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode = var t = skipTypes(typ, abstractRange-{tyTypeDesc}) - result = emptyNode case t.kind of tyBool, tyEnum, tyChar, tyInt..tyInt64: result = newNodeIT(nkIntLit, info, t) @@ -1576,7 +1591,7 @@ proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode = getNullValueAux(t.n, result, conf) of tyArray: result = newNodeIT(nkBracket, info, t) - for i in countup(0, int(lengthOrd(t)) - 1): + for i in countup(0, int(lengthOrd(conf, t)) - 1): addSon(result, getNullValue(elemType(t), info, conf)) of tyTuple: result = newNodeIT(nkTupleConstr, info, t) @@ -1590,6 +1605,7 @@ proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode = result = newNodeIT(nkBracket, info, t) else: globalError(conf, info, "cannot create null element for: " & $t.kind) + result = newNodeI(nkEmpty, info) proc ldNullOpcode(t: PType): TOpcode = assert t != nil @@ -1796,6 +1812,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = if s.magic != mNone: genMagic(c, n, dest, s.magic) elif matches(s, "stdlib", "marshal", "to"): + # XXX marshal load&store should not be opcodes, but use the + # general callback mechanisms. genMarshalLoad(c, n, dest) elif matches(s, "stdlib", "marshal", "$$"): genMarshalStore(c, n, dest) @@ -1881,7 +1899,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = else: dest = tmp0 of nkEmpty, nkCommentStmt, nkTypeSection, nkConstSection, nkPragma, - nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt: + nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt, nkExportStmt: unused(c, n, dest) of nkStringToCString, nkCStringToString: gen(c, n.sons[0], dest) @@ -2009,7 +2027,7 @@ proc genProc(c: PCtx; s: PSym): int = #c.removeLastEof result = c.code.len+1 # skip the jump instruction if x.kind == nkEmpty: - x = newTree(nkBracket, newIntNode(nkIntLit, result), ast.emptyNode) + x = newTree(nkBracket, newIntNode(nkIntLit, result), x) else: x.sons[0] = newIntNode(nkIntLit, result) s.ast.sons[miscPos] = x diff --git a/compiler/vmmarshal.nim b/compiler/vmmarshal.nim index f38be7c29..eb01b3514 100644 --- a/compiler/vmmarshal.nim +++ b/compiler/vmmarshal.nim @@ -10,7 +10,7 @@ ## Implements marshaling for the VM. import streams, json, intsets, tables, ast, astalgo, idents, types, msgs, - options + options, lineinfos proc ptrToInt(x: PNode): int {.inline.} = result = cast[int](x) # don't skip alignment @@ -140,6 +140,7 @@ proc storeAny*(s: var string; t: PType; a: PNode; conf: ConfigRef) = proc loadAny(p: var JsonParser, t: PType, tab: var Table[BiggestInt, PNode]; + cache: IdentCache; conf: ConfigRef): PNode = case t.kind of tyNone: assert false @@ -174,7 +175,7 @@ proc loadAny(p: var JsonParser, t: PType, next(p) result = newNode(nkBracket) while p.kind != jsonArrayEnd and p.kind != jsonEof: - result.add loadAny(p, t.elemType, tab, conf) + result.add loadAny(p, t.elemType, tab, cache, conf) if p.kind == jsonArrayEnd: next(p) else: raiseParseErr(p, "']' end of array expected") of tySequence: @@ -186,7 +187,7 @@ proc loadAny(p: var JsonParser, t: PType, next(p) result = newNode(nkBracket) while p.kind != jsonArrayEnd and p.kind != jsonEof: - result.add loadAny(p, t.elemType, tab, conf) + result.add loadAny(p, t.elemType, tab, cache, conf) if p.kind == jsonArrayEnd: next(p) else: raiseParseErr(p, "") else: @@ -202,7 +203,7 @@ proc loadAny(p: var JsonParser, t: PType, next(p) if i >= t.len: raiseParseErr(p, "too many fields to tuple type " & typeToString(t)) - result.add loadAny(p, t.sons[i], tab, conf) + result.add loadAny(p, t.sons[i], tab, cache, conf) inc i if p.kind == jsonObjectEnd: next(p) else: raiseParseErr(p, "'}' end of object expected") @@ -214,7 +215,7 @@ proc loadAny(p: var JsonParser, t: PType, while p.kind != jsonObjectEnd and p.kind != jsonEof: if p.kind != jsonString: raiseParseErr(p, "string expected for a field name") - let ident = getIdent(p.str) + let ident = getIdent(cache, p.str) let field = lookupInRecord(t.n, ident) if field.isNil: raiseParseErr(p, "unknown field for object of type " & typeToString(t)) @@ -224,7 +225,7 @@ proc loadAny(p: var JsonParser, t: PType, setLen(result.sons, pos + 1) let fieldNode = newNode(nkExprColonExpr) fieldNode.addSon(newSymNode(newSym(skField, ident, nil, unknownLineInfo()))) - fieldNode.addSon(loadAny(p, field.typ, tab, conf)) + fieldNode.addSon(loadAny(p, field.typ, tab, cache, conf)) result.sons[pos] = fieldNode if p.kind == jsonObjectEnd: next(p) else: raiseParseErr(p, "'}' end of object expected") @@ -233,7 +234,7 @@ proc loadAny(p: var JsonParser, t: PType, next(p) result = newNode(nkCurly) while p.kind != jsonArrayEnd and p.kind != jsonEof: - result.add loadAny(p, t.lastSon, tab, conf) + result.add loadAny(p, t.lastSon, tab, cache, conf) next(p) if p.kind == jsonArrayEnd: next(p) else: raiseParseErr(p, "']' end of array expected") @@ -252,7 +253,7 @@ proc loadAny(p: var JsonParser, t: PType, if p.kind == jsonInt: let idx = p.getInt next(p) - result = loadAny(p, t.lastSon, tab, conf) + result = loadAny(p, t.lastSon, tab, cache, conf) tab[idx] = result else: raiseParseErr(p, "index for ref type expected") if p.kind == jsonArrayEnd: next(p) @@ -280,14 +281,14 @@ proc loadAny(p: var JsonParser, t: PType, return raiseParseErr(p, "float expected") of tyRange, tyGenericInst, tyAlias, tySink: - result = loadAny(p, t.lastSon, tab, conf) + result = loadAny(p, t.lastSon, tab, cache, conf) else: internalError conf, "cannot marshal at compile-time " & t.typeToString -proc loadAny*(s: string; t: PType; conf: ConfigRef): PNode = +proc loadAny*(s: string; t: PType; cache: IdentCache; conf: ConfigRef): PNode = var tab = initTable[BiggestInt, PNode]() var p: JsonParser open(p, newStringStream(s), "unknown file") next(p) - result = loadAny(p, t, tab, conf) + result = loadAny(p, t, tab, cache, conf) close(p) diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 617295b0d..a7d47d7a3 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -107,15 +107,16 @@ proc registerAdditionalOps*(c: PCtx) = wrap1f_math(ceil) wrap2f_math(fmod) - wrap2s(getEnv, ospathsop) - wrap1s(existsEnv, ospathsop) - wrap2svoid(putEnv, ospathsop) - wrap1s(dirExists, osop) - wrap1s(fileExists, osop) - wrap2svoid(writeFile, systemop) - wrap1s(readFile, systemop) - systemop getCurrentExceptionMsg - registerCallback c, "stdlib.*.staticWalkDir", proc (a: VmArgs) {.nimcall.} = - setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1))) - systemop gorgeEx + when defined(nimcore): + wrap2s(getEnv, ospathsop) + wrap1s(existsEnv, ospathsop) + wrap2svoid(putEnv, ospathsop) + wrap1s(dirExists, osop) + wrap1s(fileExists, osop) + wrap2svoid(writeFile, systemop) + wrap1s(readFile, systemop) + systemop getCurrentExceptionMsg + registerCallback c, "stdlib.*.staticWalkDir", proc (a: VmArgs) {.nimcall.} = + setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1))) + systemop gorgeEx macrosop getProjectPath diff --git a/compiler/writetracking.nim b/compiler/writetracking.nim index c0f7b7b20..1ea1deb2d 100644 --- a/compiler/writetracking.nim +++ b/compiler/writetracking.nim @@ -15,7 +15,8 @@ ## * Computing an aliasing relation based on the assignments. This relation ## is then used to compute the 'writes' and 'escapes' effects. -import intsets, idents, ast, astalgo, trees, renderer, msgs, types, options +import intsets, idents, ast, astalgo, trees, renderer, msgs, types, options, + lineinfos const debug = false diff --git a/doc/docgen.rst b/doc/docgen.rst index e6693e153..d196b3a18 100644 --- a/doc/docgen.rst +++ b/doc/docgen.rst @@ -303,9 +303,9 @@ symbols in the `system module <system.html>`_. `#len,seq[T] <system.html#len,seq[T]>`_ * ``iterator pairs[T](a: seq[T]): tuple[key: int, val: T] {.inline.}`` **=>** `#pairs.i,seq[T] <system.html#pairs.i,seq[T]>`_ -* ``template newException[](exceptn: typedesc; message: string): expr`` **=>** - `#newException.t,typedesc,string - <system.html#newException.t,typedesc,string>`_ +* ``template newException[](exceptn: type; message: string): expr`` **=>** + `#newException.t,type,string + <system.html#newException.t,type,string>`_ Index (idx) file format diff --git a/doc/intern.txt b/doc/intern.txt index dadb0eb05..b253cac42 100644 --- a/doc/intern.txt +++ b/doc/intern.txt @@ -38,10 +38,6 @@ Path Purpose Bootstrapping the compiler ========================== -As of version 0.8.5 the compiler is maintained in Nim. (The first versions -have been implemented in Object Pascal.) The Python-based build system has -been rewritten in Nim too. - Compiling the compiler is a simple matter of running:: nim c koch.nim @@ -202,16 +198,131 @@ Compilation cache ================= The implementation of the compilation cache is tricky: There are lots -of issues to be solved for the front- and backend. In the following -sections *global* means *shared between modules* or *property of the whole -program*. +of issues to be solved for the front- and backend. + + +General approach: AST replay +---------------------------- + +We store a module's AST of a successful semantic check in a SQLite +database. There are plenty of features that require a sub sequence +to be re-applied, for example: + +.. code-block:: nim + {.compile: "foo.c".} # even if the module is loaded from the DB, + # "foo.c" needs to be compiled/linked. + +The solution is to **re-play** the module's top level statements. +This solves the problem without having to special case the logic +that fills the internal seqs which are affected by the pragmas. + +In fact, this decribes how the AST should be stored in the database, +as a "shallow" tree. Let's assume we compile module ``m`` with the +following contents: + +.. code-block:: nim + import strutils + + var x*: int = 90 + {.compile: "foo.c".} + proc p = echo "p" + proc q = echo "q" + static: + echo "static" + +Conceptually this is the AST we store for the module: + +.. code-block:: nim + import strutils + + var x* + {.compile: "foo.c".} + proc p + proc q + static: + echo "static" + +The symbol's ``ast`` field is loaded lazily, on demand. This is where most +savings come from, only the shallow outer AST is reconstructed immediately. + +It is also important that the replay involves the ``import`` statement so +that the dependencies are resolved properly. + + +Shared global compiletime state +------------------------------- + +Nim allows ``.global, compiletime`` variables that can be filled by macro +invokations across different modules. This feature breaks modularity in a +severe way. Plenty of different solutions have been proposed: + +- Restrict the types of global compiletime variables to ``Set[T]`` or + similar unordered, only-growable collections so that we can track + the module's write effects to these variables and reapply the changes + in a different order. +- In every module compilation, reset the variable to its default value. +- Provide a restrictive API that can load/save the compiletime state to + a file. + +(These solutions are not mutually exclusive.) + +Since we adopt the "replay the top level statements" idea, the natural +solution to this problem is to emit pseudo top level statements that +reflect the mutations done to the global variable. However, this is +MUCH harder than it sounds, for example ``squeaknim`` uses this +snippet: + +.. code-block:: nim + apicall.add(") module: '" & dllName & "'>\C" & + "\t^self externalCallFailed\C!\C\C") + stCode.add(st & "\C\t\"Generated by NimSqueak\"\C\t" & apicall) + +We can "replay" ``stCode.add`` only if the values of ``st`` +and ``apicall`` are known. And even then a hash table's ``add`` with its +hashing mechanism is too hard to replay. + +In practice, things are worse still, consider ``someGlobal[i][j].add arg``. +We only know the root is ``someGlobal`` but the concrete path to the data +is unknown as is the value that is added. We could compute a "diff" between +the global states and use that to compute a symbol patchset, but this is +quite some work, expensive to do at runtime (it would need to run after +every module has been compiled) and also would break for hash tables. + +We need an API that hides the complex aliasing problems by not relying +on Nim's global variables. The obvious solution is to use string keys +instead of global variables: + +.. code-block:: nim + + proc cachePut*(key: string; value: string) + proc cacheGet*(key: string): string + +However, the values being strings/json is quite problematic: Many +lookup tables that are built at compiletime embed *proc vars* and +types which have no obvious string representation... Seems like +AST diffing is still the best idea as it will not require to use +an alien API and works with some existing Nimble packages, at least. + +On the other hand, in Nim's future I would like to replace the VM +by native code. A diff algorithm wouldn't work for that. +Instead the native code would work with an API like ``put``, ``get``: + +.. code-block:: nim + + proc cachePut*(key: string; value: NimNode) + proc cacheGet*(key: string): NimNode + +The API should embrace the AST diffing notion: See the +module ``macrocache`` for the final details. -Frontend issues ---------------- Methods and type converters -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +--------------------------- + +In the following +sections *global* means *shared between modules* or *property of the whole +program*. Nim contains language features that are *global*. The best example for that are multi methods: Introducing a new method with the same name and some @@ -238,20 +349,17 @@ If in the above example module ``B`` is re-compiled, but ``A`` is not then ``B`` needs to be aware of ``toBool`` even though ``toBool`` is not referenced in ``B`` *explicitly*. -Both the multi method and the type converter problems are solved by storing -them in special sections in the ROD file that are loaded *unconditionally* -when the ROD file is read. +Both the multi method and the type converter problems are solved by the +AST replay implementation. + Generics ~~~~~~~~ -If we generate an instance of a generic, we'd like to re-use that -instance if possible across module boundaries. However, this is not -possible if the compilation cache is enabled. So we give up then and use -the caching of generics only per module, not per project. This means that -``--symbolFiles:on`` hurts a bit for efficiency. A better solution would -be to persist the instantiations in a global cache per project. This might be -implemented in later versions. +We cache generic instantiations and need to ensure this caching works +well with the incremental compilation feature. Since the cache is +attached to the ``PSym`` datastructure, it should work without any +special logic. Backend issues @@ -259,13 +367,10 @@ Backend issues - Init procs must not be "forgotten" to be called. - Files must not be "forgotten" to be linked. -- Anything that is contained in ``nim__dat.c`` is shared between modules - implicitly. - Method dispatchers are global. - DLL loading via ``dlsym`` is global. - Emulated thread vars are global. - However the biggest problem is that dead code elimination breaks modularity! To see why, consider this scenario: The module ``G`` (for example the huge Gtk2 module...) is compiled with dead code elimination turned on. So none @@ -274,25 +379,21 @@ of ``G``'s procs is generated at all. Then module ``B`` is compiled that requires ``G.P1``. Ok, no problem, ``G.P1`` is loaded from the symbol file and ``G.c`` now contains ``G.P1``. -Then module ``A`` (that depends onto ``B`` and ``G``) is compiled and ``B`` +Then module ``A`` (that depends on ``B`` and ``G``) is compiled and ``B`` and ``G`` are left unchanged. ``A`` requires ``G.P2``. So now ``G.c`` MUST contain both ``P1`` and ``P2``, but we haven't even loaded ``P1`` from the symbol file, nor do we want to because we then quickly -would restore large parts of the whole program. But we also don't want to -store ``P1`` in ``B.c`` because that would mean to store every symbol where -it is referred from which ultimately means the main module and putting -everything in a single C file. +would restore large parts of the whole program. -There is however another solution: The old file ``G.c`` containing ``P1`` is -**merged** with the new file ``G.c`` containing ``P2``. This is the solution -that is implemented in the C code generator (have a look at the ``ccgmerge`` -module). The merging may lead to *cruft* (aka dead code) in generated C code -which can only be removed by recompiling a project with the compilation cache -turned off. Nevertheless the merge solution is way superior to the -cheap solution "turn off dead code elimination if the compilation cache is -turned on". +Solution +~~~~~~~~ +The backend must have some logic so that if the currently processed module +is from the compilation cache, the ``ast`` field is not accessed. Instead +the generated C(++) for the symbol's body needs to be cached too and +inserted back into the produced C file. This approach seems to deal with +all the outlined problems above. Debugging Nim's memory management @@ -317,7 +418,7 @@ Introduction I use the term *cell* here to refer to everything that is traced (sequences, refs, strings). -This section describes how the new GC works. +This section describes how the GC works. The basic algorithm is *Deferrent Reference Counting* with cycle detection. References on the stack are not counted for better performance and easier C diff --git a/doc/manual.rst b/doc/manual.rst index d0a778bdd..ddee7b4b9 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -741,22 +741,26 @@ For further details, see `Convertible relation Subrange types -------------- -A subrange type is a range of values from an ordinal type (the base +A subrange type is a range of values from an ordinal or floating point type (the base type). To define a subrange type, one must specify it's limiting values: the lowest and highest value of the type: .. code-block:: nim type Subrange = range[0..5] + PositiveFloat = range[0.0..Inf] ``Subrange`` is a subrange of an integer which can only hold the values 0 -to 5. Assigning any other value to a variable of type ``Subrange`` is a +to 5. ``PositiveFloat`` defines a subrange of all positive floating point values. +NaN does not belong to any subrange of floating point types. +Assigning any other value to a variable of type ``Subrange`` is a checked runtime error (or static error if it can be statically determined). Assignments from the base type to one of its subrange types (and vice versa) are allowed. -A subrange type has the same size as its base type (``int`` in the example). +A subrange type has the same size as its base type (``int`` in the +Subrange example). Pre-defined floating point types @@ -1732,7 +1736,7 @@ But it seems all this boilerplate code needs to be repeated for the ``Euro`` currency. This can be solved with templates_. .. code-block:: nim - template additive(typ: typedesc) = + template additive(typ: type) = proc `+` *(x, y: typ): typ {.borrow.} proc `-` *(x, y: typ): typ {.borrow.} @@ -1740,13 +1744,13 @@ currency. This can be solved with templates_. proc `+` *(x: typ): typ {.borrow.} proc `-` *(x: typ): typ {.borrow.} - template multiplicative(typ, base: typedesc) = + template multiplicative(typ, base: type) = proc `*` *(x: typ, y: base): typ {.borrow.} proc `*` *(x: base, y: typ): typ {.borrow.} proc `div` *(x: typ, y: base): typ {.borrow.} proc `mod` *(x: typ, y: base): typ {.borrow.} - template comparable(typ: typedesc) = + template comparable(typ: type) = proc `<` * (x, y: typ): bool {.borrow.} proc `<=` * (x, y: typ): bool {.borrow.} proc `==` * (x, y: typ): bool {.borrow.} @@ -2396,7 +2400,7 @@ argument's resolution: rem unresolvedExpression(undeclaredIdentifier) ``untyped`` and ``varargs[untyped]`` are the only metatype that are lazy in this sense, the other -metatypes ``typed`` and ``typedesc`` are not lazy. +metatypes ``typed`` and ``type`` are not lazy. Varargs matching @@ -4274,29 +4278,6 @@ therefore very useful for type specialization within generic code: deletedKeys: seq[bool] -Type operator -------------- - -The ``type`` (in many other languages called `typeof`:idx:) operator can -be used to get the type of an expression: - -.. code-block:: nim - var x = 0 - var y: type(x) # y has type int - -If ``type`` is used to determine the result type of a proc/iterator/converter -call ``c(X)`` (where ``X`` stands for a possibly empty list of arguments), the -interpretation where ``c`` is an iterator is preferred over the -other interpretations: - -.. code-block:: nim - import strutils - - # strutils contains both a ``split`` proc and iterator, but since an - # an iterator is the preferred interpretation, `y` has the type ``string``: - var y: type("a b c".split) - - Type Classes ------------ @@ -4450,10 +4431,10 @@ the presence of callable symbols with specific signatures: OutputStream = concept var s s.write(string) -In order to check for symbols accepting ``typedesc`` params, you must prefix -the type with an explicit ``type`` modifier. The named instance of the type, -following the ``concept`` keyword is also considered an explicit ``typedesc`` -value that will be matched only as a type. +In order to check for symbols accepting ``type`` params, you must prefix +the type with the explicit ``type`` modifier. The named instance of the +type, following the ``concept`` keyword is also considered to have the +explicit modifier and will be matched only as a type. .. code-block:: nim type @@ -4513,7 +4494,7 @@ The concept types can be parametric just like the regular generic types: import typetraits type - AnyMatrix*[R, C: static[int]; T] = concept m, var mvar, type M + AnyMatrix*[R, C: static int; T] = concept m, var mvar, type M M.ValueType is T M.Rows == R M.Cols == C @@ -4523,7 +4504,7 @@ The concept types can be parametric just like the regular generic types: type TransposedType = stripGenericParams(M)[C, R, T] - AnySquareMatrix*[N: static[int], T] = AnyMatrix[N, N, T] + AnySquareMatrix*[N: static int, T] = AnyMatrix[N, N, T] AnyTransform3D* = AnyMatrix[4, 4, float] @@ -4542,7 +4523,7 @@ The concept types can be parametric just like the regular generic types: ### matrix.nim type - Matrix*[M, N: static[int]; T] = object + Matrix*[M, N: static int; T] = object data: array[M*N, T] proc `[]`*(M: Matrix; m, n: int): M.T = @@ -4554,7 +4535,7 @@ The concept types can be parametric just like the regular generic types: # Adapt the Matrix type to the concept's requirements template Rows*(M: type Matrix): expr = M.M template Cols*(M: type Matrix): expr = M.N - template ValueType*(M: type Matrix): typedesc = M.T + template ValueType*(M: type Matrix): type = M.T ------------- ### usage.nim @@ -4582,7 +4563,7 @@ operator and also when types dependent on them are being matched: .. code-block:: nim type - MatrixReducer[M, N: static[int]; T] = concept x + MatrixReducer[M, N: static int; T] = concept x x.reduce(SquareMatrix[N, T]) is array[M, int] The Nim compiler includes a simple linear equation solver, allowing it to @@ -4771,12 +4752,12 @@ object inheritance syntax involving the ``of`` keyword: # the varargs param will here be converted to an array of StringRefValues # the proc will have only two instantiations for the two character types - proc log(format: static[string], varargs[StringRef]) + proc log(format: static string, varargs[StringRef]) # this proc will allow char and wchar values to be mixed in # the same call at the cost of additional instantiations # the varargs param will be converted to a tuple - proc log(format: static[string], varargs[distinct StringRef]) + proc log(format: static string, varargs[distinct StringRef]) .. @@ -4940,9 +4921,8 @@ templates: | ``notin`` and ``isnot`` have the obvious meanings. The "types" of templates can be the symbols ``untyped``, -``typed`` or ``typedesc`` (stands for *type -description*). These are "meta types", they can only be used in certain -contexts. Real types can be used too; this implies that ``typed`` expressions +``typed`` or ``type``. These are "meta types", they can only be used in certain +contexts. Regular types can be used too; this implies that ``typed`` expressions are expected. @@ -5109,7 +5089,7 @@ In templates identifiers can be constructed with the backticks notation: .. code-block:: nim :test: "nim c $1" - template typedef(name: untyped, typ: typedesc) = + template typedef(name: untyped, typ: type) = type `T name`* {.inject.} = typ `P name`* {.inject.} = ref `T name` @@ -5171,7 +5151,7 @@ template cannot be accessed in the instantiation context: .. code-block:: nim :test: "nim c $1" - template newException*(exceptn: typedesc, message: string): untyped = + template newException*(exceptn: type, message: string): untyped = var e: ref exceptn # e is implicitly gensym'ed here new(e) @@ -5493,7 +5473,7 @@ As their name suggests, static parameters must be known at compile-time: .. code-block:: nim - proc precompiledRegex(pattern: static[string]): RegEx = + proc precompiledRegex(pattern: static string): RegEx = var res {.global.} = re(pattern) return res @@ -5513,9 +5493,9 @@ Static params can also appear in the signatures of generic types: .. code-block:: nim type - Matrix[M,N: static[int]; T: Number] = array[0..(M*N - 1), T] + Matrix[M,N: static int; T: Number] = array[0..(M*N - 1), T] # Note how `Number` is just a type constraint here, while - # `static[int]` requires us to supply a compile-time int value + # `static int` requires us to supply a compile-time int value AffineTransform2D[T] = Matrix[3, 3, T] AffineTransform3D[T] = Matrix[4, 4, T] @@ -5523,53 +5503,75 @@ Static params can also appear in the signatures of generic types: var m1: AffineTransform3D[float] # OK var m2: AffineTransform2D[string] # Error, `string` is not a `Number` +Please note that ``static T`` is just a syntactic convenience for the +underlying generic type ``static[T]``. The type param can be omitted +to obtain the type class of all values known at compile-time. A more +specific type class can be created by instantiating ``static`` with +another type class. -typedesc --------- +You can force the evaluation of a certain expression at compile-time by +coercing it to a corresponding ``static`` type: + +.. code-block:: nim + import math + + echo static(fac(5)), " ", static[bool](16.isPowerOfTwo) + +The complier will report any failure to evaluate the expression or a +possible type mismatch error. -`typedesc` is a special type allowing one to treat types as compile-time values -(i.e. if types are compile-time values and all values have a type, then -typedesc must be their type). +type[T] +------- + +In many contexts, Nim allows you to treat the names of types as regular +values. These values exists only during the compilation phase, but since +all values must have a type, ``type`` is considered their special type. -When used as a regular proc param, typedesc acts as a type class. The proc -will be instantiated for each unique type parameter and one can refer to the -instantiation type using the param name: +``type`` acts like a generic type. For instance, the type of the symbol +``int`` is ``type[int]``. Just like with regular generic types, when the +generic param is ommited, ``type`` denotes the type class of all types. +As a syntactic convenience, you can also use ``type`` as a modifier. +``type int`` is considered the same as ``type[int]``. + +Procs featuring ``type`` params are considered implicitly generic. +They will be instantiated for each unique combination of supplied types +and within the body of the proc, the name of each param will refer to +the bound concrete type: .. code-block:: nim - proc new(T: typedesc): ref T = + proc new(T: type): ref T = echo "allocating ", T.name new(result) var n = Node.new var tree = new(BinaryTree[int]) -When multiple typedesc params are present, they will bind freely to different -types. To force a bind-once behavior -one can use an explicit ``typedesc[T]`` generic param: +When multiple type params are present, they will bind freely to different +types. To force a bind-once behavior one can use an explicit generic param: .. code-block:: nim - proc acceptOnlyTypePairs[T, U](A, B: typedesc[T]; C, D: typedesc[U]) + proc acceptOnlyTypePairs[T, U](A, B: type[T]; C, D: type[U]) -Once bound, typedesc params can appear in the rest of the proc signature: +Once bound, type params can appear in the rest of the proc signature: .. code-block:: nim :test: "nim c $1" - template declareVariableWithType(T: typedesc, value: T) = + template declareVariableWithType(T: type, value: T) = var x: T = value declareVariableWithType int, 42 Overload resolution can be further influenced by constraining the set of -types that will match the typedesc param: +types that will match the type param: .. code-block:: nim :test: "nim c $1" - template maxval(T: typedesc[int]): int = high(int) - template maxval(T: typedesc[float]): float = Inf + template maxval(T: type int): int = high(int) + template maxval(T: type float): float = Inf var i = int.maxval var f = float.maxval @@ -5578,7 +5580,35 @@ types that will match the typedesc param: The constraint can be a concrete type or a type class. +type operator +------------- + +You can obtain the type of a given expression by constructing a ``type`` +value from it (in many other languages this is known as the `typeof`:idx: +operator): +.. code-block:: nim + var x = 0 + var y: type(x) # y has type int + +You may add a constraint to the resulting type to trigger a compile-time error +if the expression doesn't have the expected type: + +.. code-block:: nim + var x = 0 + var y: type[object](x) # Error: type mismatch: got <int> but expected 'object' + +If ``type`` is used to determine the result type of a proc/iterator/converter +call ``c(X)`` (where ``X`` stands for a possibly empty list of arguments), the +interpretation where ``c`` is an iterator is preferred over the +other interpretations: + +.. code-block:: nim + import strutils + + # strutils contains both a ``split`` proc and iterator, but since an + # an iterator is the preferred interpretation, `y` has the type ``string``: + var y: type("a b c".split) Special Operators @@ -6251,6 +6281,35 @@ Likewise the following does not make sense as the name is ``strutils`` already: import lib.pure.strutils as strutils +Collective imports from a directory +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The syntax ``import dir / [moduleA, moduleB]`` can be used to import multiple modules +from the same directory. + +Path names are syntactically either Nim identifiers or string literals. If the path +name is not a valid Nim identifier it needs to be a string literal: + +.. code-block:: nim + import "gfx/3d/somemodule" # in quotes because '3d' is not a valid Nim identifier + + +Pseudo import/include paths +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A directory can also be a so called "pseudo directory". + +There are two pseudo directories: + +1. ``std``: The ``std`` pseudo directory is the abstract location of Nim's standard + library. For example, the syntax ``import std / strutils`` is used to unambiguously + refer to the standard library's ``strutils`` module. +2. ``pkg``: The ``pkg`` pseudo directory is used to unambiguously refer to a Nimble + package. However, for technical details that lie outside of the scope of this document + its semantics are: *Use the search path to look for module name but ignore the standard + library locations*. In other words, it is the opposite of ``std``. + + From import statement ~~~~~~~~~~~~~~~~~~~~~ @@ -6301,16 +6360,6 @@ modules don't need to import a module's dependencies: When the exported symbol is another module, all of its definitions will be forwarded. You can use an ``except`` list to exclude some of the symbols. -Note on paths ------------ -In module related statements, if any part of the module name / -path begins with a number, you may have to quote it in double quotes. -In the following example, it would be seen as a literal number '3.0' of type -'float64' if not quoted, if uncertain - quote it: - -.. code-block:: nim - import "gfx/3d/somemodule" - Scope rules ----------- @@ -7439,7 +7488,7 @@ Custom pragmas are defined using templates annotated with pragma ``pragma``: .. code-block:: nim template dbTable(name: string, table_space: string = "") {.pragma.} template dbKey(name: string = "", primary_key: bool = false) {.pragma.} - template dbForeignKey(t: typedesc) {.pragma.} + template dbForeignKey(t: type) {.pragma.} template dbIgnore {.pragma.} diff --git a/doc/tut1.rst b/doc/tut1.rst index f2251c463..aa6114cf7 100644 --- a/doc/tut1.rst +++ b/doc/tut1.rst @@ -208,7 +208,8 @@ Note that declaring multiple variables with a single assignment which calls a procedure can have unexpected results: the compiler will *unroll* the assignments and end up calling the procedure several times. If the result of the procedure depends on side effects, your variables may end up having -different values! For safety use only constant values. +different values! For safety use side-effect free procedures if making multiple +assignments. Constants @@ -642,7 +643,7 @@ initialisation. Parameters ---------- -Parameters are constant in the procedure body. By default, their value cannot be +Parameters are immutable in the procedure body. By default, their value cannot be changed because this allows the compiler to implement parameter passing in the most efficient way. If a mutable variable is needed inside the procedure, it has to be declared with ``var`` in the procedure body. Shadowing the parameter name diff --git a/doc/tut2.rst b/doc/tut2.rst index 91cb52834..39e3bd89a 100644 --- a/doc/tut2.rst +++ b/doc/tut2.rst @@ -618,9 +618,9 @@ Turning the ``log`` proc into a template solves this problem: log("x has the value: " & $x) The parameters' types can be ordinary types or the meta types ``untyped``, -``typed``, or ``typedesc``. -``typedesc`` stands for *type description*, and ``untyped`` means symbol lookups and -type resolution is not performed before the expression is passed to the template. +``typed``, or ``type``. ``type`` suggests that only a type symbol may be given +as an argument, and ``untyped`` means symbol lookups and type resolution is not +performed before the expression is passed to the template. If the template has no explicit return type, ``void`` is used for consistency with procs and methods. diff --git a/examples/allany.nim b/examples/allany.nim index 6ff055aa6..8a5ab81f0 100644 --- a/examples/allany.nim +++ b/examples/allany.nim @@ -1,22 +1,20 @@ # All and any template all(container, cond: untyped): bool = - block: - var result = true - for it in items(container): - if not cond(it): - result = false - break - result + var result = true + for it in items(container): + if not cond(it): + result = false + break + result template any(container, cond: untyped): bool = - block: - var result = false - for it in items(container): - if cond(it): - result = true - break - result + var result = false + for it in items(container): + if cond(it): + result = true + break + result if all("mystring", {'a'..'z'}.contains) and any("myohmy", 'y'.`==`): echo "works" diff --git a/examples/readme.txt b/examples/readme.txt index 176bc8239..686271660 100644 --- a/examples/readme.txt +++ b/examples/readme.txt @@ -1,5 +1,2 @@ In this directory you will find several examples for how to use the Nim library. - -Copyright (c) 2004-2012 Andreas Rumpf. -All rights reserved. diff --git a/koch.nim b/koch.nim index 4f85c6583..9411a95de 100644 --- a/koch.nim +++ b/koch.nim @@ -254,15 +254,13 @@ proc buildTool(toolname, args: string) = copyFile(dest="bin" / splitFile(toolname).name.exe, source=toolname.exe) proc buildTools(latest: bool) = - let nimsugExe = "bin/nimsuggest".exe - nimexec "c --noNimblePath -p:compiler -d:release -o:" & nimsugExe & + nimexec "c --noNimblePath -p:compiler -d:release -o:" & ("bin/nimsuggest".exe) & " nimsuggest/nimsuggest.nim" - let nimgrepExe = "bin/nimgrep".exe - nimexec "c -d:release -o:" & nimgrepExe & " tools/nimgrep.nim" + nimexec "c -d:release -o:" & ("bin/nimgrep".exe) & " tools/nimgrep.nim" when defined(windows): buildVccTool() - #nimexec "c -o:" & ("bin/nimresolve".exe) & " tools/nimresolve.nim" + nimexec "c -o:" & ("bin/nimpretty".exe) & " nimpretty/nimpretty.nim" buildNimble(latest) @@ -472,7 +470,7 @@ proc temp(args: string) = # commit. let (bootArgs, programArgs) = splitArgs(args) let nimexec = findNim() - exec(nimexec & " c -d:debug " & bootArgs & " compiler" / "nim", 125) + exec(nimexec & " c -d:debug --debugger:native " & bootArgs & " compiler" / "nim", 125) copyExe(output, finalDest) if programArgs.len > 0: exec(finalDest & " " & programArgs) diff --git a/lib/core/macrocache.nim b/lib/core/macrocache.nim new file mode 100644 index 000000000..bd48b5bd4 --- /dev/null +++ b/lib/core/macrocache.nim @@ -0,0 +1,47 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2018 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module provides an API for macros that need to collect compile +## time information across module boundaries in global variables. +## Starting with version 0.19 of Nim this is not directly supported anymore +## as it breaks incremental compilations. +## Instead the API here needs to be used. See XXX (wikipedia page) for a +## theoretical foundation behind this. + +type + CacheSeq* = distinct string + CacheTable* = distinct string + CacheCounter* = distinct string + +proc value*(c: CacheCounter): int {.magic: "NccValue".} +proc inc*(c: CacheCounter; by = 1) {.magic: "NccInc".} + +proc add*(s: CacheSeq; value: NimNode) {.magic: "NcsAdd".} +proc incl*(s: CacheSeq; value: NimNode) {.magic: "NcsIncl".} +proc len*(s: CacheSeq): int {.magic: "NcsLen".} +proc `[]`*(s: CacheSeq; i: int): NimNode {.magic: "NcsAt".} + +iterator items*(s: CacheSeq): NimNode = + for i in 0 ..< len(s): yield s[i] + +proc `[]=`*(t: CacheTable; key: string, value: NimNode) {.magic: "NctPut".} + ## 'key' has to be unique! + +proc len*(t: CacheTable): int {.magic: "NctLen".} +proc `[]`*(t: CacheTable; key: string): NimNode {.magic: "NctGet".} + +proc hasNext(t: CacheTable; iter: int): bool {.magic: "NctHasNext".} +proc next(t: CacheTable; iter: int): (string, NimNode, int) {.magic: "NctNext".} + +iterator pairs*(t: CacheTable): (string, NimNode) = + var h = 0 + while hasNext(t, h): + let (a, b, h2) = next(t, h) + yield (a, b) + h = h2 diff --git a/lib/core/macros.nim b/lib/core/macros.nim index fa5cd67df..1f251b73e 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -126,9 +126,6 @@ type ## represents a Nim *symbol* in the compiler; a *symbol* is a looked-up ## *ident*. -{.deprecated: [TNimrodNodeKind: NimNodeKind, TNimNodeKinds: NimNodeKinds, - TNimrodTypeKind: NimTypeKind, TNimrodSymKind: NimSymKind, - TNimrodIdent: NimIdent, PNimrodSymbol: NimSym].} const nnkLiterals* = {nnkCharLit..nnkNilLit} diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index adac16777..7ee071c79 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -363,6 +363,7 @@ proc addNodes(n: PRstNode): string = addNodesAux(n, result) proc rstnodeToRefnameAux(n: PRstNode, r: var string, b: var bool) = + if n == nil: return if n.kind == rnLeaf: for i in countup(0, len(n.text) - 1): case n.text[i] diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 03a27017a..ef456f093 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -188,13 +188,16 @@ proc addTexChar(dest: var string, c: char) = of '`': add(dest, "\\symbol{96}") else: add(dest, c) -var splitter*: string = "<wbr />" - proc escChar*(target: OutputTarget, dest: var string, c: char) {.inline.} = case target of outHtml: addXmlChar(dest, c) of outLatex: addTexChar(dest, c) +proc addSplitter(target: OutputTarget; dest: var string) {.inline.} = + case target + of outHtml: add(dest, "<wbr />") + of outLatex: add(dest, "\\-") + proc nextSplitPoint*(s: string, start: int): int = result = start while result < len(s) + 0: @@ -215,9 +218,9 @@ proc esc*(target: OutputTarget, s: string, splitAfter = -1): string = var j = 0 while j < len(s): var k = nextSplitPoint(s, j) - if (splitter != " ") or (partLen + k - j + 1 > splitAfter): - partLen = 0 - add(result, splitter) + #if (splitter != " ") or (partLen + k - j + 1 > splitAfter): + partLen = 0 + addSplitter(target, result) for i in countup(j, k): escChar(target, result, s[i]) inc(partLen, k - j + 1) j = k + 1 diff --git a/lib/pure/asyncfile.nim b/lib/pure/asyncfile.nim index 1df7c3fc0..37339d3d1 100644 --- a/lib/pure/asyncfile.nim +++ b/lib/pure/asyncfile.nim @@ -91,7 +91,7 @@ proc newAsyncFile*(fd: AsyncFd): AsyncFile = proc openAsync*(filename: string, mode = fmRead): AsyncFile = ## Opens a file specified by the path in ``filename`` using - ## the specified ``mode`` asynchronously. + ## the specified FileMode ``mode`` asynchronously. when defined(windows) or defined(nimdoc): let flags = FILE_FLAG_OVERLAPPED or FILE_ATTRIBUTE_NORMAL let desiredAccess = getDesiredAccess(mode) diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim index 328308a9b..409240b37 100644 --- a/lib/pure/collections/deques.nim +++ b/lib/pure/collections/deques.nim @@ -38,7 +38,7 @@ ## Note: For inter thread communication use ## a `Channel <channels.html>`_ instead. -import math +import math, typetraits type Deque*[T] = object @@ -160,16 +160,18 @@ proc peekLast*[T](deq: Deque[T]): T {.inline.} = emptyCheck(deq) result = deq.data[(deq.tail - 1) and deq.mask] -template default[T](t: typedesc[T]): T = - var v: T - v +template destroy(x: untyped) = + when defined(nimNewRuntime) and not supportsCopyMem(type(x)): + `=destroy`(x) + else: + reset(x) proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} = ## Remove and returns the first element of the `deq`. emptyCheck(deq) dec deq.count result = deq.data[deq.head] - deq.data[deq.head] = default(type(result)) + destroy(deq.data[deq.head]) deq.head = (deq.head + 1) and deq.mask proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} = @@ -178,7 +180,34 @@ proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} = dec deq.count deq.tail = (deq.tail - 1) and deq.mask result = deq.data[deq.tail] - deq.data[deq.tail] = default(type(result)) + destroy(deq.data[deq.tail]) + +proc clear*[T](deq: var Deque[T]) {.inline.} = + ## Resets the deque so that it is empty. + for el in mitems(deq): destroy(el) + deq.count = 0 + deq.tail = deq.head + +proc shrink*[T](deq: var Deque[T], fromFirst = 0, fromLast = 0) = + ## Remove `fromFirst` elements from the front of the deque and + ## `fromLast` elements from the back. If the supplied number of + ## elements exceeds the total number of elements in the deque, + ## the deque will remain empty. + ## + ## Any user defined destructors + if fromFirst + fromLast > deq.count: + clear(deq) + return + + for i in 0 ..< fromFirst: + destroy(deq.data[deq.head]) + deq.head = (deq.head + 1) and deq.mask + + for i in 0 ..< fromLast: + destroy(deq.data[deq.tail]) + deq.tail = (deq.tail - 1) and deq.mask + + dec deq.count, fromFirst + fromLast proc `$`*[T](deq: Deque[T]): string = ## Turn a deque into its string representation. @@ -215,6 +244,22 @@ when isMainModule: assert deq.find(6) >= 0 assert deq.find(789) < 0 + block: + var d = initDeque[int](1) + d.addLast 7 + d.addLast 8 + d.addLast 10 + d.addFirst 5 + d.addFirst 2 + d.addFirst 1 + d.addLast 20 + d.shrink(fromLast = 2) + doAssert($d == "[1, 2, 5, 7, 8]") + d.shrink(2, 1) + doAssert($d == "[5, 7]") + d.shrink(2, 2) + doAssert d.len == 0 + for i in -2 .. 10: if i in deq: assert deq.contains(i) and deq.find(i) >= 0 diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 8530e4c42..8b4fb0f8c 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -1184,7 +1184,7 @@ proc request*(client: HttpClient | AsyncHttpClient, url: string, ## Connects to the hostname specified by the URL and performs a request ## using the custom method string specified by ``httpMethod``. ## - ## Connection will kept alive. Further requests on the same ``client`` to + ## Connection will be kept alive. Further requests on the same ``client`` to ## the same hostname will not require a new connection to be made. The ## connection can be closed by using the ``close`` procedure. ## diff --git a/lib/pure/marshal.nim b/lib/pure/marshal.nim index b88c9dd85..b90d2899c 100644 --- a/lib/pure/marshal.nim +++ b/lib/pure/marshal.nim @@ -270,6 +270,8 @@ proc store*[T](s: Stream, data: T) = proc `$$`*[T](x: T): string = ## returns a string representation of `x`. + ## + ## Note: to serialize `x` to JSON use $(%x) from the ``json`` module var stored = initIntSet() var d: T shallowCopy(d, x) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 8ea8ee203..b921b1841 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -147,6 +147,18 @@ when not defined(JS): # C proc ln*(x: float32): float32 {.importc: "logf", header: "<math.h>".} proc ln*(x: float64): float64 {.importc: "log", header: "<math.h>".} ## Computes the natural log of `x` +else: # JS + proc sqrt*(x: float32): float32 {.importc: "Math.sqrt", nodecl.} + proc sqrt*(x: float64): float64 {.importc: "Math.sqrt", nodecl.} + + proc ln*(x: float32): float32 {.importc: "Math.log", nodecl.} + proc ln*(x: float64): float64 {.importc: "Math.log", nodecl.} + +proc log*[T: SomeFloat](x, base: T): T = + ## Computes the logarithm ``base`` of ``x`` + ln(x) / ln(base) + +when not defined(JS): # C proc log10*(x: float32): float32 {.importc: "log10f", header: "<math.h>".} proc log10*(x: float64): float64 {.importc: "log10", header: "<math.h>".} ## Computes the common logarithm (base 10) of `x` @@ -204,11 +216,6 @@ when not defined(JS): # C ## Computes the inverse hyperbolic tangent of `x` else: # JS - proc sqrt*(x: float32): float32 {.importc: "Math.sqrt", nodecl.} - proc sqrt*(x: float64): float64 {.importc: "Math.sqrt", nodecl.} - - proc ln*(x: float32): float32 {.importc: "Math.log", nodecl.} - proc ln*(x: float64): float64 {.importc: "Math.log", nodecl.} proc log10*(x: float32): float32 {.importc: "Math.log10", nodecl.} proc log10*(x: float64): float64 {.importc: "Math.log10", nodecl.} proc log2*(x: float32): float32 {.importc: "Math.log2", nodecl.} @@ -682,3 +689,6 @@ when isMainModule: doAssert floorMod(8.0, -3.0) ==~ -1.0 doAssert floorMod(-8.5, 3.0) ==~ 0.5 + + block: # log + doAssert log(4.0, 3.0) == ln(4.0) / ln(3.0) diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index bf6795b0c..c7b8ebbd8 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -22,7 +22,11 @@ elif defined(posix): else: {.error: "the memfiles module is not supported on your operating system!".} -import os +import os, streams + +proc newEIO(msg: string): ref IOError = + new(result) + result.msg = msg type MemFile* = object ## represents a memory mapped file @@ -44,11 +48,14 @@ proc mapMem*(m: var MemFile, mode: FileMode = fmRead, ## ## ``mappedSize`` of ``-1`` maps to the whole file, and ## ``offset`` must be multiples of the PAGE SIZE of your OS + if mode == fmAppend: + raise newEIO("The append mode is not supported.") + var readonly = mode == fmRead when defined(windows): result = mapViewOfFileEx( m.mapHandle, - if readonly: FILE_MAP_READ else: FILE_MAP_WRITE, + if readonly: FILE_MAP_READ else: FILE_MAP_READ or FILE_MAP_WRITE, int32(offset shr 32), int32(offset and 0xffffffff), if mappedSize == -1: 0 else: mappedSize, @@ -113,6 +120,9 @@ proc open*(filename: string, mode: FileMode = fmRead, ## mm_half = memfiles.open("/tmp/test.mmap", mode = fmReadWrite, mappedSize = 512) # The file can be resized only when write mode is used: + if mode == fmAppend: + raise newEIO("The append mode is not supported.") + assert newFileSize == -1 or mode != fmRead var readonly = mode == fmRead @@ -121,6 +131,10 @@ proc open*(filename: string, mode: FileMode = fmRead, result.size = 0 when defined(windows): + let desiredAccess = GENERIC_READ + let shareMode = FILE_SHARE_READ + let flags = FILE_FLAG_RANDOM_ACCESS + template fail(errCode: OSErrorCode, msg: untyped) = rollback() if result.fHandle != 0: discard closeHandle(result.fHandle) @@ -133,11 +147,11 @@ proc open*(filename: string, mode: FileMode = fmRead, winApiProc( filename, # GENERIC_ALL != (GENERIC_READ or GENERIC_WRITE) - if readonly: GENERIC_READ else: GENERIC_READ or GENERIC_WRITE, - FILE_SHARE_READ, + if readonly: desiredAccess else: desiredAccess or GENERIC_WRITE, + if readonly: shareMode else: shareMode or FILE_SHARE_WRITE, nil, if newFileSize != -1: CREATE_ALWAYS else: OPEN_EXISTING, - if readonly: FILE_ATTRIBUTE_READONLY else: FILE_ATTRIBUTE_TEMPORARY, + if readonly: FILE_ATTRIBUTE_READONLY or flags else: FILE_ATTRIBUTE_NORMAL or flags, 0) when useWinUnicode: @@ -172,7 +186,7 @@ proc open*(filename: string, mode: FileMode = fmRead, result.mem = mapViewOfFileEx( result.mapHandle, - if readonly: FILE_MAP_READ else: FILE_MAP_WRITE, + if readonly: FILE_MAP_READ else: FILE_MAP_READ or FILE_MAP_WRITE, int32(offset shr 32), int32(offset and 0xffffffff), if mappedSize == -1: 0 else: mappedSize, @@ -245,6 +259,28 @@ proc open*(filename: string, mode: FileMode = fmRead, if close(result.handle) == 0: result.handle = -1 +proc flush*(f: var MemFile; attempts: Natural = 3) = + ## Flushes `f`'s buffer for the number of attempts equal to `attempts`. + ## If were errors an exception `OSError` will be raised. + var res = false + var lastErr: OSErrorCode + when defined(windows): + for i in 1..attempts: + res = flushViewOfFile(f.mem, 0) != 0 + if res: + break + lastErr = osLastError() + if lastErr != ERROR_LOCK_VIOLATION.OSErrorCode: + raiseOSError(lastErr) + else: + for i in 1..attempts: + res = msync(f.mem, f.size, MS_SYNC or MS_INVALIDATE) == 0 + if res: + break + lastErr = osLastError() + if lastErr != EBUSY.OSErrorCode: + raiseOSError(lastErr, "error flushing mapping") + proc close*(f: var MemFile) = ## closes the memory mapped file `f`. All changes are written back to the ## file system, if `f` was opened with write access. @@ -362,9 +398,8 @@ iterator lines*(mfile: MemFile, buf: var TaintedString, delim='\l', eat='\r'): T ## echo line for ms in memSlices(mfile, delim, eat): - buf.setLen(ms.size) - copyMem(addr(buf[0]), ms.data, ms.size) - buf[ms.size] = '\0' + setLen(buf.string, ms.size) + copyMem(buf.cstring, ms.data, ms.size) yield buf iterator lines*(mfile: MemFile, delim='\l', eat='\r'): TaintedString {.inline.} = @@ -382,3 +417,68 @@ iterator lines*(mfile: MemFile, delim='\l', eat='\r'): TaintedString {.inline.} var buf = TaintedString(newStringOfCap(80)) for line in lines(mfile, buf, delim, eat): yield buf + +type + MemMapFileStream* = ref MemMapFileStreamObj ## a stream that encapsulates a `MemFile` + MemMapFileStreamObj* = object of Stream + mf: MemFile + mode: FileMode + pos: ByteAddress + +proc mmsClose(s: Stream) = + MemMapFileStream(s).pos = -1 + close(MemMapFileStream(s).mf) + +proc mmsFlush(s: Stream) = flush(MemMapFileStream(s).mf) + +proc mmsAtEnd(s: Stream): bool = (MemMapFileStream(s).pos >= MemMapFileStream(s).mf.size) or + (MemMapFileStream(s).pos < 0) + +proc mmsSetPosition(s: Stream, pos: int) = + if pos > MemMapFileStream(s).mf.size or pos < 0: + raise newEIO("cannot set pos in stream") + MemMapFileStream(s).pos = pos + +proc mmsGetPosition(s: Stream): int = MemMapFileStream(s).pos + +proc mmsPeekData(s: Stream, buffer: pointer, bufLen: int): int = + let startAddress = cast[ByteAddress](MemMapFileStream(s).mf.mem) + let p = cast[ByteAddress](MemMapFileStream(s).pos) + let l = min(bufLen, MemMapFileStream(s).mf.size - p) + moveMem(buffer, cast[pointer](startAddress + p), l) + result = l + +proc mmsReadData(s: Stream, buffer: pointer, bufLen: int): int = + result = mmsPeekData(s, buffer, bufLen) + inc(MemMapFileStream(s).pos, result) + +proc mmsWriteData(s: Stream, buffer: pointer, bufLen: int) = + if MemMapFileStream(s).mode == fmRead: + raise newEIO("cannot write to read-only stream") + let size = MemMapFileStream(s).mf.size + if MemMapFileStream(s).pos + bufLen > size: + raise newEIO("cannot write to stream") + let p = cast[ByteAddress](MemMapFileStream(s).mf.mem) + + cast[ByteAddress](MemMapFileStream(s).pos) + moveMem(cast[pointer](p), buffer, bufLen) + inc(MemMapFileStream(s).pos, bufLen) + +proc newMemMapFileStream*(filename: string, mode: FileMode = fmRead, fileSize: int = -1): + MemMapFileStream = + ## creates a new stream from the file named `filename` with the mode `mode`. + ## Raises ## `EOS` if the file cannot be opened. See the `system + ## <system.html>`_ module for a list of available FileMode enums. + ## ``fileSize`` can only be set if the file does not exist and is opened + ## with write access (e.g., with fmReadWrite). + var mf: MemFile = open(filename, mode, newFileSize = fileSize) + new(result) + result.mode = mode + result.mf = mf + result.closeImpl = mmsClose + result.atEndImpl = mmsAtEnd + result.setPositionImpl = mmsSetPosition + result.getPositionImpl = mmsGetPosition + result.readDataImpl = mmsReadData + result.peekDataImpl = mmsPeekData + result.writeDataImpl = mmsWriteData + result.flushImpl = mmsFlush diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 5008b904c..3bc87728b 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -917,7 +917,7 @@ proc rawCreateDir(dir: string): bool = elif errno == EEXIST: result = false else: - echo res + #echo res raiseOSError(osLastError()) else: when useWinUnicode: diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 664446d54..e8bca4bdd 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -832,7 +832,7 @@ elif not defined(useNimRtl): # Parent process. Copy process information. if poEchoCmd in options: - echo(command, " ", join(args, " ")) + when declared(echo): echo(command, " ", join(args, " ")) result.id = pid result.exitFlag = false diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim index d54f1454b..e633d8cf7 100644 --- a/lib/pure/parseutils.nim +++ b/lib/pure/parseutils.nim @@ -47,12 +47,14 @@ proc parseHex*(s: string, number: var int, start = 0; maxLen = 0): int {. ## discard parseHex("0x38", value) ## assert value == -200 ## - ## If 'maxLen==0' the length of the hexadecimal number has no - ## upper bound. Not more than ```maxLen`` characters are parsed. + ## If ``maxLen == 0`` the length of the hexadecimal number has no upper bound. + ## Else no more than ``start + maxLen`` characters are parsed, up to the + ## length of the string. var i = start var foundDigit = false - let last = if maxLen == 0: s.len else: i+maxLen - if i+1 < last and s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2) + # get last index based on minimum `start + maxLen` or `s.len` + let last = min(s.len, if maxLen == 0: s.len else: i+maxLen) + if i+1 < last and s[i] == '0' and (s[i+1] in {'x', 'X'}): inc(i, 2) elif i < last and s[i] == '#': inc(i) while i < last: case s[i] @@ -70,14 +72,20 @@ proc parseHex*(s: string, number: var int, start = 0; maxLen = 0): int {. inc(i) if foundDigit: result = i-start -proc parseOct*(s: string, number: var int, start = 0): int {. +proc parseOct*(s: string, number: var int, start = 0, maxLen = 0): int {. rtl, extern: "npuParseOct", noSideEffect.} = - ## parses an octal number and stores its value in ``number``. Returns + ## Parses an octal number and stores its value in ``number``. Returns ## the number of the parsed characters or 0 in case of an error. + ## + ## If ``maxLen == 0`` the length of the octal number has no upper bound. + ## Else no more than ``start + maxLen`` characters are parsed, up to the + ## length of the string. var i = start var foundDigit = false - if i+1 < s.len and s[i] == '0' and (s[i+1] == 'o' or s[i+1] == 'O'): inc(i, 2) - while i < s.len: + # get last index based on minimum `start + maxLen` or `s.len` + let last = min(s.len, if maxLen == 0: s.len else: i+maxLen) + if i+1 < last and s[i] == '0' and (s[i+1] in {'o', 'O'}): inc(i, 2) + while i < last: case s[i] of '_': discard of '0'..'7': @@ -87,14 +95,20 @@ proc parseOct*(s: string, number: var int, start = 0): int {. inc(i) if foundDigit: result = i-start -proc parseBin*(s: string, number: var int, start = 0): int {. +proc parseBin*(s: string, number: var int, start = 0, maxLen = 0): int {. rtl, extern: "npuParseBin", noSideEffect.} = - ## parses an binary number and stores its value in ``number``. Returns + ## Parses an binary number and stores its value in ``number``. Returns ## the number of the parsed characters or 0 in case of an error. + ## + ## If ``maxLen == 0`` the length of the binary number has no upper bound. + ## Else no more than ``start + maxLen`` characters are parsed, up to the + ## length of the string. var i = start var foundDigit = false - if i+1 < s.len and s[i] == '0' and (s[i+1] == 'b' or s[i+1] == 'B'): inc(i, 2) - while i < s.len: + # get last index based on minimum `start + maxLen` or `s.len` + let last = min(s.len, if maxLen == 0: s.len else: i+maxLen) + if i+1 < last and s[i] == '0' and (s[i+1] in {'b', 'B'}): inc(i, 2) + while i < last: case s[i] of '_': discard of '0'..'1': diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index 39c5790ed..d16527a56 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -32,7 +32,7 @@ const ## can be captured. More subpatterns cannot be captured! type - PegKind = enum + PegKind* = enum pkEmpty, pkAny, ## any character (.) pkAnyRune, ## any Unicode character (_) @@ -67,7 +67,7 @@ type pkRule, ## a <- b pkList, ## a, b pkStartAnchor ## ^ --> Internal DSL: startAnchor() - NonTerminalFlag = enum + NonTerminalFlag* = enum ntDeclared, ntUsed NonTerminalObj = object ## represents a non terminal symbol name: string ## the name of the symbol @@ -86,6 +86,25 @@ type else: sons: seq[Peg] NonTerminal* = ref NonTerminalObj +proc name*(nt: NonTerminal): string = nt.name +proc line*(nt: NonTerminal): int = nt.line +proc col*(nt: NonTerminal): int = nt.col +proc flags*(nt: NonTerminal): set[NonTerminalFlag] = nt.flags +proc rule*(nt: NonTerminal): Peg = nt.rule + +proc kind*(p: Peg): PegKind = p.kind +proc term*(p: Peg): string = p.term +proc ch*(p: Peg): char = p.ch +proc charChoice*(p: Peg): ref set[char] = p.charChoice +proc nt*(p: Peg): NonTerminal = p.nt +proc index*(p: Peg): range[0..MaxSubpatterns] = p.index +iterator items*(p: Peg): Peg {.inline.} = + for s in p.sons: + yield s +iterator pairs*(p: Peg): (int, Peg) {.inline.} = + for i in 0 ..< p.sons.len: + yield (i, p.sons[i]) + proc term*(t: string): Peg {.nosideEffect, rtl, extern: "npegs$1Str".} = ## constructs a PEG from a terminal string if t.len != 1: diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index a0bba05a4..1ab73faea 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -135,6 +135,11 @@ proc write*(s: Stream, x: string) = else: if x.len > 0: writeData(s, cstring(x), x.len) +proc write*(s: Stream, args: varargs[string, `$`]) = + ## writes one or more strings to the the stream. No length fields or + ## terminating zeros are written. + for str in args: write(s, str) + proc writeLine*(s: Stream, args: varargs[string, `$`]) = ## writes one or more strings to the the stream `s` followed ## by a new line. No length field or terminating zero is written. @@ -266,8 +271,8 @@ proc peekStr*(s: Stream, length: int): TaintedString = proc readLine*(s: Stream, line: var TaintedString): bool = ## reads a line of text from the stream `s` into `line`. `line` must not be ## ``nil``! 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. + ## A line of text may be delimited by ```LF`` or ``CRLF``. + ## The newline character(s) are not part of the returned string. ## Returns ``false`` if the end of the file has been reached, ``true`` ## otherwise. If ``false`` is returned `line` contains no new data. line.string.setLen(0) @@ -317,6 +322,13 @@ proc peekLine*(s: Stream): TaintedString = defer: setPosition(s, pos) result = readLine(s) +iterator lines*(s: Stream): TaintedString = + ## Iterates over every line in the stream. + ## The iteration is based on ``readLine``. + var line: TaintedString + while s.readLine(line): + yield line + when not defined(js): type diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index bea0a0243..ab34a0b2d 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -17,6 +17,10 @@ import parseutils from math import pow, round, floor, log10 from algorithm import reverse +when defined(nimVmExportFixed): + from unicode import toLower, toUpper + export toLower, toUpper + {.deadCodeElim: on.} # dce option deprecated {.push debugger:off .} # the user does not want to trace a part @@ -844,7 +848,7 @@ proc parseInt*(s: string): int {.noSideEffect, procvar, ## Parses a decimal integer value contained in `s`. ## ## If `s` is not a valid integer, `ValueError` is raised. - var L = parseutils.parseInt(s, result, 0) + let L = parseutils.parseInt(s, result, 0) if L != s.len or L == 0: raise newException(ValueError, "invalid integer: " & s) @@ -853,7 +857,7 @@ proc parseBiggestInt*(s: string): BiggestInt {.noSideEffect, procvar, ## Parses a decimal integer value contained in `s`. ## ## If `s` is not a valid integer, `ValueError` is raised. - var L = parseutils.parseBiggestInt(s, result, 0) + let L = parseutils.parseBiggestInt(s, result, 0) if L != s.len or L == 0: raise newException(ValueError, "invalid integer: " & s) @@ -862,7 +866,7 @@ proc parseUInt*(s: string): uint {.noSideEffect, procvar, ## Parses a decimal unsigned integer value contained in `s`. ## ## If `s` is not a valid integer, `ValueError` is raised. - var L = parseutils.parseUInt(s, result, 0) + let L = parseutils.parseUInt(s, result, 0) if L != s.len or L == 0: raise newException(ValueError, "invalid unsigned integer: " & s) @@ -871,7 +875,7 @@ proc parseBiggestUInt*(s: string): BiggestUInt {.noSideEffect, procvar, ## Parses a decimal unsigned integer value contained in `s`. ## ## If `s` is not a valid integer, `ValueError` is raised. - var L = parseutils.parseBiggestUInt(s, result, 0) + let L = parseutils.parseBiggestUInt(s, result, 0) if L != s.len or L == 0: raise newException(ValueError, "invalid unsigned integer: " & s) @@ -880,33 +884,42 @@ proc parseFloat*(s: string): float {.noSideEffect, procvar, ## Parses a decimal floating point value contained in `s`. If `s` is not ## a valid floating point number, `ValueError` is raised. ``NAN``, ## ``INF``, ``-INF`` are also supported (case insensitive comparison). - var L = parseutils.parseFloat(s, result, 0) + let L = parseutils.parseFloat(s, result, 0) if L != s.len or L == 0: raise newException(ValueError, "invalid float: " & s) +proc parseBinInt*(s: string): int {.noSideEffect, procvar, + rtl, extern: "nsuParseBinInt".} = + ## Parses a binary integer value contained in `s`. + ## + ## If `s` is not a valid binary integer, `ValueError` is raised. `s` can have + ## one of the following optional prefixes: ``0b``, ``0B``. Underscores within + ## `s` are ignored. + let L = parseutils.parseBin(s, result, 0) + if L != s.len or L == 0: + raise newException(ValueError, "invalid binary integer: " & s) + +proc parseOctInt*(s: string): int {.noSideEffect, + rtl, extern: "nsuParseOctInt".} = + ## Parses an octal integer value contained in `s`. + ## + ## If `s` is not a valid oct integer, `ValueError` is raised. `s` can have one + ## of the following optional prefixes: ``0o``, ``0O``. Underscores within + ## `s` are ignored. + let L = parseutils.parseOct(s, result, 0) + if L != s.len or L == 0: + raise newException(ValueError, "invalid oct integer: " & s) + proc parseHexInt*(s: string): int {.noSideEffect, procvar, rtl, extern: "nsuParseHexInt".} = ## Parses a hexadecimal integer value contained in `s`. ## - ## If `s` is not a valid integer, `ValueError` is raised. `s` can have one + ## If `s` is not a valid hex integer, `ValueError` is raised. `s` can have one ## of the following optional prefixes: ``0x``, ``0X``, ``#``. Underscores ## within `s` are ignored. - var i = 0 - if i+1 < s.len and s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2) - elif i < s.len and s[i] == '#': inc(i) - while i < s.len: - case s[i] - of '_': inc(i) - of '0'..'9': - result = result shl 4 or (ord(s[i]) - ord('0')) - inc(i) - of 'a'..'f': - result = result shl 4 or (ord(s[i]) - ord('a') + 10) - inc(i) - of 'A'..'F': - result = result shl 4 or (ord(s[i]) - ord('A') + 10) - inc(i) - else: raise newException(ValueError, "invalid integer: " & s) + let L = parseutils.parseHex(s, result, 0) + if L != s.len or L == 0: + raise newException(ValueError, "invalid hex integer: " & s) proc generateHexCharToValueMap(): string = ## Generate a string to map a hex digit to uint value @@ -1616,23 +1629,6 @@ proc delete*(s: var string, first, last: int) {.noSideEffect, inc(j) setLen(s, newLen) -proc parseOctInt*(s: string): int {.noSideEffect, - rtl, extern: "nsuParseOctInt".} = - ## Parses an octal integer value contained in `s`. - ## - ## If `s` is not a valid integer, `ValueError` is raised. `s` can have one - ## of the following optional prefixes: ``0o``, ``0O``. Underscores within - ## `s` are ignored. - var i = 0 - if i+1 < s.len and s[i] == '0' and (s[i+1] == 'o' or s[i+1] == 'O'): inc(i, 2) - while i < s.len: - case s[i] - of '_': inc(i) - of '0'..'7': - result = result shl 3 or (ord(s[i]) - ord('0')) - inc(i) - else: raise newException(ValueError, "invalid integer: " & s) - proc toOct*(x: BiggestInt, len: Positive): string {.noSideEffect, rtl, extern: "nsuToOct".} = ## Converts `x` into its octal representation. diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index fcca4d5d7..ac41a0aad 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -467,16 +467,18 @@ proc resetAttributes*(f: File) = f.write(ansiResetCode) type - Style* = enum ## different styles for text output + Style* = enum ## different styles for text output styleBright = 1, ## bright text styleDim, ## dim text - styleUnknown, ## unknown + styleItalic, ## italic (or reverse on terminals not supporting) styleUnderscore = 4, ## underscored text styleBlink, ## blinking/bold text - styleReverse = 7, ## unknown + styleReverse = 7, ## reverse styleHidden ## hidden text + styleStrikethrough, ## strikethrough {.deprecated: [TStyle: Style].} +{.deprecated: [styleUnknown: styleItalic].} when not defined(windows): var @@ -540,7 +542,9 @@ type fgBlue, ## blue fgMagenta, ## magenta fgCyan, ## cyan - fgWhite ## white + fgWhite, ## white + fg8Bit, ## 256-color (not supported, see ``enableTrueColors`` instead.) + fgDefault ## default terminal foreground color BackgroundColor* = enum ## terminal's background colors bgBlack = 40, ## black @@ -550,28 +554,40 @@ type bgBlue, ## blue bgMagenta, ## magenta bgCyan, ## cyan - bgWhite ## white + bgWhite, ## white + bg8Bit, ## 256-color (not supported, see ``enableTrueColors`` instead.) + bgDefault ## default terminal background color {.deprecated: [TForegroundColor: ForegroundColor, TBackgroundColor: BackgroundColor].} +when defined(windows): + var defaultForegroundColor, defaultBackgroundColor: int16 = 0xFFFF'i16 # Default to an invalid value 0xFFFF + proc setForegroundColor*(f: File, fg: ForegroundColor, bright=false) = ## Sets the terminal's foreground color. when defined(windows): let h = conHandle(f) var old = getAttributes(h) and not FOREGROUND_RGB + if defaultForegroundColor == 0xFFFF'i16: + defaultForegroundColor = old old = if bright: old or FOREGROUND_INTENSITY else: old and not(FOREGROUND_INTENSITY) const lookup: array[ForegroundColor, int] = [ - 0, + 0, # ForegroundColor enum with ordinal 30 (FOREGROUND_RED), (FOREGROUND_GREEN), (FOREGROUND_RED or FOREGROUND_GREEN), (FOREGROUND_BLUE), (FOREGROUND_RED or FOREGROUND_BLUE), (FOREGROUND_BLUE or FOREGROUND_GREEN), - (FOREGROUND_BLUE or FOREGROUND_GREEN or FOREGROUND_RED)] - discard setConsoleTextAttribute(h, toU16(old or lookup[fg])) + (FOREGROUND_BLUE or FOREGROUND_GREEN or FOREGROUND_RED), + 0, # fg8Bit not supported, see ``enableTrueColors`` instead. + 0] # unused + if fg == fgDefault: + discard setConsoleTextAttribute(h, toU16(old or defaultForegroundColor)) + else: + discard setConsoleTextAttribute(h, toU16(old or lookup[fg])) else: gFG = ord(fg) if bright: inc(gFG, 60) @@ -582,18 +598,25 @@ proc setBackgroundColor*(f: File, bg: BackgroundColor, bright=false) = when defined(windows): let h = conHandle(f) var old = getAttributes(h) and not BACKGROUND_RGB + if defaultBackgroundColor == 0xFFFF'i16: + defaultBackgroundColor = old old = if bright: old or BACKGROUND_INTENSITY else: old and not(BACKGROUND_INTENSITY) const lookup: array[BackgroundColor, int] = [ - 0, + 0, # BackgroundColor enum with ordinal 40 (BACKGROUND_RED), (BACKGROUND_GREEN), (BACKGROUND_RED or BACKGROUND_GREEN), (BACKGROUND_BLUE), (BACKGROUND_RED or BACKGROUND_BLUE), (BACKGROUND_BLUE or BACKGROUND_GREEN), - (BACKGROUND_BLUE or BACKGROUND_GREEN or BACKGROUND_RED)] - discard setConsoleTextAttribute(h, toU16(old or lookup[bg])) + (BACKGROUND_BLUE or BACKGROUND_GREEN or BACKGROUND_RED), + 0, # bg8Bit not supported, see ``enableTrueColors`` instead. + 0] # unused + if bg == bgDefault: + discard setConsoleTextAttribute(h, toU16(old or defaultBackgroundColor)) + else: + discard setConsoleTextAttribute(h, toU16(old or lookup[bg])) else: gBG = ord(bg) if bright: inc(gBG, 60) @@ -690,8 +713,8 @@ template styledEchoProcessArg(f: File, cmd: TerminalCmd) = when cmd == bgColor: fgSetColor = false -macro styledWriteLine*(f: File, m: varargs[typed]): untyped = - ## Similar to ``writeLine``, but treating terminal style arguments specially. +macro styledWrite*(f: File, m: varargs[typed]): untyped = + ## Similar to ``write``, but treating terminal style arguments specially. ## When some argument is ``Style``, ``set[Style]``, ``ForegroundColor``, ## ``BackgroundColor`` or ``TerminalCmd`` then it is not sent directly to ## ``f``, but instead corresponding terminal style proc is called. @@ -700,8 +723,8 @@ macro styledWriteLine*(f: File, m: varargs[typed]): untyped = ## ## .. code-block:: nim ## - ## proc error(msg: string) = - ## styledWriteLine(stderr, fgRed, "Error: ", resetStyle, msg) + ## stdout.styledWrite(fgRed, "red text ") + ## stdout.styledWrite(fgGreen, "green text") ## let m = callsite() var reset = false @@ -712,8 +735,8 @@ macro styledWriteLine*(f: File, m: varargs[typed]): untyped = case item.kind of nnkStrLit..nnkTripleStrLit: if i == m.len - 1: - # optimize if string literal is last, just call writeLine - result.add(newCall(bindSym"writeLine", f, item)) + # optimize if string literal is last, just call write + result.add(newCall(bindSym"write", f, item)) if reset: result.add(newCall(bindSym"resetAttributes", f)) return else: @@ -722,16 +745,24 @@ macro styledWriteLine*(f: File, m: varargs[typed]): untyped = else: result.add(newCall(bindSym"styledEchoProcessArg", f, item)) reset = true - - result.add(newCall(bindSym"write", f, newStrLitNode("\n"))) if reset: result.add(newCall(bindSym"resetAttributes", f)) -macro styledEcho*(args: varargs[untyped]): untyped = +template styledWriteLine*(f: File, args: varargs[untyped]) = + ## Calls ``styledWrite`` and appends a newline at the end. + ## + ## Example: + ## + ## .. code-block:: nim + ## + ## proc error(msg: string) = + ## styledWriteLine(stderr, fgRed, "Error: ", resetStyle, msg) + ## + styledWrite(f, args) + write(f, "\n") + +template styledEcho*(args: varargs[untyped]) = ## Echoes styles arguments to stdout using ``styledWriteLine``. - result = newCall(bindSym"styledWriteLine") - result.add(bindSym"stdout") - for arg in children(args): - result.add(arg) + stdout.styledWriteLine(args) proc getch*(): char = ## Read a single character from the terminal, blocking until it is entered. @@ -779,7 +810,7 @@ when defined(windows): inc i, x password.string.setLen(max(password.len - x, 0)) of chr(0x0): - # modifier key - ignore - for details see + # modifier key - ignore - for details see # https://github.com/nim-lang/Nim/issues/7764 continue else: @@ -838,16 +869,6 @@ proc resetAttributes*() {.noconv.} = ## ``system.addQuitProc(resetAttributes)``. resetAttributes(stdout) -when not defined(testing) and isMainModule: - #system.addQuitProc(resetAttributes) - write(stdout, "never mind") - stdout.eraseLine() - stdout.styledWriteLine("styled text ", {styleBright, styleBlink, styleUnderscore}) - stdout.setBackGroundColor(bgCyan, true) - stdout.setForeGroundColor(fgBlue) - stdout.writeLine("ordinary text") - stdout.resetAttributes() - proc isTrueColorSupported*(): bool = ## Returns true if a terminal supports true color. return trueColorIsSupported @@ -898,3 +919,43 @@ proc disableTrueColors*() = trueColorIsEnabled = false else: trueColorIsEnabled = false + +when not defined(testing) and isMainModule: + #system.addQuitProc(resetAttributes) + write(stdout, "never mind") + stdout.eraseLine() + stdout.styledWriteLine({styleBright, styleBlink, styleUnderscore}, "styled text ") + stdout.styledWriteLine("italic text ", {styleItalic}) + stdout.setBackGroundColor(bgCyan, true) + stdout.setForeGroundColor(fgBlue) + stdout.write("blue text in cyan background") + stdout.resetAttributes() + echo "" + stdout.writeLine("ordinary text") + echo "more ordinary text" + styledEcho styleBright, fgGreen, "[PASS]", resetStyle, fgGreen, " Yay!" + echo "ordinary text again" + styledEcho styleBright, fgRed, "[FAIL]", resetStyle, fgRed, " Nay :(" + echo "ordinary text again" + setForeGroundColor(fgGreen) + echo "green text" + echo "more green text" + setForeGroundColor(fgBlue) + echo "blue text" + resetAttributes() + echo "ordinary text" + + stdout.styledWriteLine(fgRed, "red text ") + stdout.styledWriteLine(fgWhite, bgRed, "white text in red background") + stdout.styledWriteLine(" ordinary text ") + stdout.styledWriteLine(fgGreen, "green text") + + stdout.styledWrite(fgRed, "red text ") + stdout.styledWrite(fgWhite, bgRed, "white text in red background") + stdout.styledWrite(" ordinary text ") + stdout.styledWrite(fgGreen, "green text") + echo "" + echo "ordinary text" + stdout.styledWriteLine(fgRed, "red text ", styleBright, "bold red", fgDefault, " bold text") + stdout.styledWriteLine(bgYellow, "text in yellow bg", styleBright, " bold text in yellow bg", bgDefault, " bold text") + echo "ordinary text" diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 7cecc31ab..7fd60b818 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -238,11 +238,11 @@ proc utcZoneInfoFromUtc(time: Time): ZonedTime {.tags: [], raises: [], benign .} proc utcZoneInfoFromTz(adjTime: Time): ZonedTime {.tags: [], raises: [], benign .} proc localZoneInfoFromUtc(time: Time): ZonedTime {.tags: [], raises: [], benign .} proc localZoneInfoFromTz(adjTime: Time): ZonedTime {.tags: [], raises: [], benign .} -proc initTime*(unix: int64, nanosecond: NanosecondRange): Time +proc initTime*(unix: int64, nanosecond: NanosecondRange): Time {.tags: [], raises: [], benign noSideEffect.} proc initDuration*(nanoseconds, microseconds, milliseconds, - seconds, minutes, hours, days, weeks: int64 = 0): Duration + seconds, minutes, hours, days, weeks: int64 = 0): Duration {.tags: [], raises: [], benign noSideEffect.} proc nanosecond*(time: Time): NanosecondRange = @@ -531,7 +531,7 @@ proc `$`*(dur: Duration): string = let quantity = numParts[unit] if quantity != 0.int64: parts.add(stringifyUnit(quantity, $unit)) - + result = humanizeParts(parts) proc `+`*(a, b: Duration): Duration {.operator.} = @@ -1243,23 +1243,23 @@ proc evaluateStaticInterval(interval: TimeInterval): Duration = minutes = interval.minutes, hours = interval.hours) -proc between*(startDt, endDt:DateTime): TimeInterval = +proc between*(startDt, endDt: DateTime): TimeInterval = ## Evaluate difference between two dates in ``TimeInterval`` format, so, it ## will be relative. ## - ## **Warning:** It's not recommended to use ``between`` for ``DateTime's`` in - ## different ``TimeZone's``. + ## **Warning:** It's not recommended to use ``between`` for ``DateTime's`` in + ## different ``TimeZone's``. ## ``a + between(a, b) == b`` is only guaranteed when ``a`` and ``b`` are in UTC. runnableExamples: - var a = initDateTime(year = 2018, month = Month(3), monthday = 25, + var a = initDateTime(year = 2018, month = Month(3), monthday = 25, hour = 0, minute = 59, second = 59, nanosecond = 1, zone = utc()).local - var b = initDateTime(year = 2018, month = Month(3), monthday = 25, + var b = initDateTime(year = 2018, month = Month(3), monthday = 25, hour = 1, minute = 1, second = 1, nanosecond = 0, zone = utc()).local doAssert between(a, b) == initTimeInterval( nanoseconds=999, milliseconds=999, microseconds=999, seconds=1, minutes=1) - + a = parse("2018-01-09T00:00:00+00:00", "yyyy-MM-dd'T'HH:mm:sszzz", utc()) b = parse("2018-01-10T23:00:00-02:00", "yyyy-MM-dd'T'HH:mm:sszzz") doAssert between(a, b) == initTimeInterval(hours=1, days=2) @@ -1526,7 +1526,6 @@ proc formatToken(dt: DateTime, token: string, buf: var string) = else: raise newException(ValueError, "Invalid format string: " & token) - proc format*(dt: DateTime, f: string): string {.tags: [].}= ## This procedure formats `dt` as specified by `f`. The following format ## specifiers are available: @@ -1601,18 +1600,14 @@ proc format*(dt: DateTime, f: string): string {.tags: [].}= inc(i) formatToken(dt, currentF, result) -proc format*(time: Time, f: string, zone_info: proc(t: Time): DateTime): string {.tags: [].} = - ## converts a `Time` value to a string representation. It will use format from +proc format*(time: Time, f: string, zone: Timezone = local()): string {.tags: [].} = + ## Converts a `Time` value to a string representation. It will use format from ## ``format(dt: DateTime, f: string)``. runnableExamples: - var dt = initDateTime(01, mJan, 1970, 00, 00, 00, local()) + var dt = initDateTime(01, mJan, 1970, 00, 00, 00, utc()) var tm = dt.toTime() - doAssert format(tm, "yyyy-MM-dd'T'HH:mm:ss", local) == "1970-01-01T00:00:00" - dt = initDateTime(01, mJan, 1970, 00, 00, 00, utc()) - tm = dt.toTime() - doAssert format(tm, "yyyy-MM-dd'T'HH:mm:ss", utc) == "1970-01-01T00:00:00" - - zone_info(time).format(f) + doAssert format(tm, "yyyy-MM-dd'T'HH:mm:ss", utc()) == "1970-01-01T00:00:00" + time.inZone(zone).format(f) proc `$`*(dt: DateTime): string {.tags: [], raises: [], benign.} = ## Converts a `DateTime` object to a string representation. @@ -1984,16 +1979,12 @@ proc countYearsAndDays*(daySpan: int): tuple[years: int, days: int] = proc toTimeInterval*(time: Time): TimeInterval = ## Converts a Time to a TimeInterval. ## - ## To be used when diffing times. + ## To be used when diffing times. Consider using `between` instead. runnableExamples: let a = fromUnix(10) - let dt = initDateTime(01, mJan, 1970, 00, 00, 00, local()) - doAssert a.toTimeInterval() == initTimeInterval( - years=1970, days=1, seconds=10, hours=convert( - Seconds, Hours, -dt.utcOffset - ) - ) - + let b = fromUnix(1_500_000_000) + let ti = b.toTimeInterval() - a.toTimeInterval() + doAssert a + ti == b var dt = time.local initTimeInterval(dt.nanosecond, 0, 0, dt.second, dt.minute, dt.hour, dt.monthday, 0, dt.month.ord - 1, dt.year) @@ -2150,7 +2141,7 @@ proc timeToTimeInterval*(t: Time): TimeInterval {.deprecated.} = t.toTimeInterval() proc getDayOfWeek*(day, month, year: int): WeekDay {.tags: [], raises: [], benign, deprecated.} = - ## **Deprecated since v0.18.0:** use + ## **Deprecated since v0.18.0:** use ## ``getDayOfWeek(monthday: MonthdayRange; month: Month; year: int)`` instead. getDayOfWeek(day, month.Month, year) diff --git a/lib/std/varints.nim b/lib/std/varints.nim index bfc1945fe..483d5c96c 100644 --- a/lib/std/varints.nim +++ b/lib/std/varints.nim @@ -19,7 +19,7 @@ proc readVu64*(z: openArray[byte]; pResult: var uint64): int = return 1 if z[0] <= 248: if z.len < 2: return 0 - pResult = (uint64 z[0] - 241) * 256 + uint64 z[1] + 240 + pResult = (uint64 z[0] - 241) * 256 + z[1].uint64 + 240 return 2 if z.len < int(z[0]-246): return 0 if z[0] == 249: @@ -135,6 +135,13 @@ when isMainModule: if encodeZigzag(decodeZigzag(test)) != test: echo "Failure for ", test, " ", encodeZigzag(decodeZigzag(test)), " ", decodeZigzag(test) + for test in 0u64..300u64: + let wrLen = writeVu64(dest, test) + let rdLen = readVu64(dest, got) + assert wrLen == rdLen + if got != test: + echo "BUG! expected: ", test, " got: ", got, " z0: ", dest[0] + # check this also works for floats: for test in [0.0, 0.1, 2.0, +Inf, Nan, NegInf]: let t = cast[uint64](test) diff --git a/lib/system.nim b/lib/system.nim index fb02fde23..15f952feb 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -179,10 +179,24 @@ proc unsafeAddr*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} = ## Cannot be overloaded. discard -proc `type`*(x: untyped): typeDesc {.magic: "TypeOf", noSideEffect, compileTime.} = - ## Builtin 'type' operator for accessing the type of an expression. - ## Cannot be overloaded. - discard +when defined(nimNewTypedesc): + type + `static`* {.magic: "Static".}[T] + ## meta type representing all values that can be evaluated at compile-time. + ## + ## The type coercion ``static(x)`` can be used to force the compile-time + ## evaluation of the given expression ``x``. + + `type`* {.magic: "Type".}[T] + ## meta type representing the type of all type values. + ## + ## The coercion ``type(x)`` can be used to obtain the type of the given + ## expression ``x``. +else: + proc `type`*(x: untyped): typeDesc {.magic: "TypeOf", noSideEffect, compileTime.} = + ## Builtin 'type' operator for accessing the type of an expression. + ## Cannot be overloaded. + discard proc `not` *(x: bool): bool {.magic: "Not", noSideEffect.} ## Boolean not; returns true iff ``x == false``. diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index 2c9b1e502..b33ca93f2 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -562,7 +562,8 @@ else: when not declared(nimNewSeqOfCap): proc nimNewSeqOfCap(typ: PNimType, cap: int): pointer {.compilerproc.} = when defined(gcRegions): - result = newStr(typ, cap, ntfNoRefs notin typ.base.flags) + let s = mulInt(cap, typ.base.size) # newStr already adds GenericSeqSize + result = newStr(typ, s, ntfNoRefs notin typ.base.flags) else: let s = addInt(mulInt(cap, typ.base.size), GenericSeqSize) when declared(newObjNoInit): diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim index 7671e5962..5bf69dd94 100644 --- a/lib/system/nimscript.nim +++ b/lib/system/nimscript.nim @@ -182,7 +182,7 @@ template checkOsError = if err.len > 0: raise newException(OSError, err) template log(msg: string, body: untyped) = - if mode == ScriptMode.Verbose or mode == ScriptMode.Whatif: + if mode in {ScriptMode.Verbose, ScriptMode.Whatif}: echo "[NimScript] ", msg if mode != ScriptMode.WhatIf: body diff --git a/lib/system/platforms.nim b/lib/system/platforms.nim index 97f97e8ae..b561cd3ba 100644 --- a/lib/system/platforms.nim +++ b/lib/system/platforms.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -## Platform detection for Nim. This module is included by the system module! +## Platform detection for NimScript. This module is included by the system module! ## Do not import it directly! type @@ -62,7 +62,7 @@ const elif defined(haiku): OsPlatform.haiku elif defined(android): OsPlatform.android elif defined(js): OsPlatform.js - elif defined(nimrodVM): OsPlatform.nimVM + elif defined(nimVM): OsPlatform.nimVM elif defined(standalone): OsPlatform.standalone else: OsPlatform.none ## the OS this program will run on. diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 7e22f98c7..3263f1d37 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -129,7 +129,6 @@ const PIPE_ACCESS_OUTBOUND* = 2'i32 PIPE_NOWAIT* = 0x00000001'i32 SYNCHRONIZE* = 0x00100000'i32 - FILE_FLAG_WRITE_THROUGH* = 0x80000000'i32 CREATE_NO_WINDOW* = 0x08000000'i32 @@ -281,15 +280,31 @@ else: importc:"CreateHardLinkA", dynlib: "kernel32", stdcall.} const - FILE_ATTRIBUTE_ARCHIVE* = 32'i32 - FILE_ATTRIBUTE_COMPRESSED* = 2048'i32 - FILE_ATTRIBUTE_NORMAL* = 128'i32 - FILE_ATTRIBUTE_DIRECTORY* = 16'i32 - FILE_ATTRIBUTE_HIDDEN* = 2'i32 - FILE_ATTRIBUTE_READONLY* = 1'i32 - FILE_ATTRIBUTE_REPARSE_POINT* = 1024'i32 - FILE_ATTRIBUTE_SYSTEM* = 4'i32 - FILE_ATTRIBUTE_TEMPORARY* = 256'i32 + FILE_ATTRIBUTE_READONLY* = 0x00000001'i32 + FILE_ATTRIBUTE_HIDDEN* = 0x00000002'i32 + FILE_ATTRIBUTE_SYSTEM* = 0x00000004'i32 + FILE_ATTRIBUTE_DIRECTORY* = 0x00000010'i32 + FILE_ATTRIBUTE_ARCHIVE* = 0x00000020'i32 + FILE_ATTRIBUTE_DEVICE* = 0x00000040'i32 + FILE_ATTRIBUTE_NORMAL* = 0x00000080'i32 + FILE_ATTRIBUTE_TEMPORARY* = 0x00000100'i32 + FILE_ATTRIBUTE_SPARSE_FILE* = 0x00000200'i32 + FILE_ATTRIBUTE_REPARSE_POINT* = 0x00000400'i32 + FILE_ATTRIBUTE_COMPRESSED* = 0x00000800'i32 + FILE_ATTRIBUTE_OFFLINE* = 0x00001000'i32 + FILE_ATTRIBUTE_NOT_CONTENT_INDEXED* = 0x00002000'i32 + + FILE_FLAG_FIRST_PIPE_INSTANCE* = 0x00080000'i32 + FILE_FLAG_OPEN_NO_RECALL* = 0x00100000'i32 + FILE_FLAG_OPEN_REPARSE_POINT* = 0x00200000'i32 + FILE_FLAG_POSIX_SEMANTICS* = 0x01000000'i32 + FILE_FLAG_BACKUP_SEMANTICS* = 0x02000000'i32 + FILE_FLAG_DELETE_ON_CLOSE* = 0x04000000'i32 + FILE_FLAG_SEQUENTIAL_SCAN* = 0x08000000'i32 + FILE_FLAG_RANDOM_ACCESS* = 0x10000000'i32 + FILE_FLAG_NO_BUFFERING* = 0x20000000'i32 + FILE_FLAG_OVERLAPPED* = 0x40000000'i32 + FILE_FLAG_WRITE_THROUGH* = 0x80000000'i32 MAX_PATH* = 260 @@ -683,8 +698,6 @@ const FILE_MAP_WRITE* = 2'i32 INVALID_FILE_SIZE* = -1'i32 - FILE_FLAG_BACKUP_SEMANTICS* = 33554432'i32 - FILE_FLAG_OPEN_REPARSE_POINT* = 0x00200000'i32 DUPLICATE_SAME_ACCESS* = 2 FILE_READ_DATA* = 0x00000001 # file & pipe FILE_WRITE_DATA* = 0x00000002 # file & pipe @@ -695,6 +708,7 @@ const ERROR_PATH_NOT_FOUND* = 3 ERROR_ACCESS_DENIED* = 5 ERROR_NO_MORE_FILES* = 18 + ERROR_LOCK_VIOLATION* = 33 ERROR_HANDLE_EOF* = 38 ERROR_BAD_ARGUMENTS* = 165 @@ -763,6 +777,9 @@ when not useWinUnicode: proc unmapViewOfFile*(lpBaseAddress: pointer): WINBOOL {.stdcall, dynlib: "kernel32", importc: "UnmapViewOfFile".} +proc flushViewOfFile*(lpBaseAddress: pointer, dwNumberOfBytesToFlush: DWORD): WINBOOL {. + stdcall, dynlib: "kernel32", importc: "FlushViewOfFile".} + type OVERLAPPED* {.pure, inheritable.} = object internal*: PULONG @@ -785,7 +802,6 @@ type const ERROR_IO_PENDING* = 997 # a.k.a WSA_IO_PENDING - FILE_FLAG_OVERLAPPED* = 1073741824 WSAECONNABORTED* = 10053 WSAEADDRINUSE* = 10048 WSAECONNRESET* = 10054 diff --git a/tools/nimpretty.nim b/nimpretty/nimpretty.nim index 396f17b0b..aa9756c45 100644 --- a/tools/nimpretty.nim +++ b/nimpretty/nimpretty.nim @@ -12,7 +12,7 @@ when not defined(nimpretty): {.error: "This needs to be compiled with --define:nimPretty".} -import ../compiler / [idents, msgs, ast, syntaxes, renderer] +import ../compiler / [idents, msgs, ast, syntaxes, renderer, options] import parseopt, strutils, os @@ -25,6 +25,7 @@ Usage: nimpretty [options] file.nim Options: --backup:on|off create a backup file before overwritting (default: ON) + --output:file set the output file (default: overwrite the .nim file) --version show the version --help show this help """ @@ -39,14 +40,18 @@ proc writeVersion() = stdout.flushFile() quit(0) -proc prettyPrint(infile: string) = - let fileIdx = fileInfoIdx(infile) - let tree = parseFile(fileIdx, newIdentCache()) - let outfile = changeFileExt(infile, ".pretty.nim") - renderModule(tree, infile, outfile, {}, fileIdx) +proc prettyPrint(infile, outfile: string) = + var conf = newConfigRef() + let fileIdx = fileInfoIdx(conf, infile) + conf.outFile = outfile + when defined(nimpretty2): + discard parseFile(fileIdx, newIdentCache(), conf) + else: + let tree = parseFile(fileIdx, newIdentCache(), conf) + renderModule(tree, infile, outfile, {}, fileIdx, conf) proc main = - var infile: string + var infile, outfile: string var backup = true for kind, key, val in getopt(): case kind @@ -57,12 +62,14 @@ proc main = of "help", "h": writeHelp() of "version", "v": writeVersion() of "backup": backup = parseBool(val) + of "output", "o": outfile = val else: writeHelp() of cmdEnd: assert(false) # cannot happen if infile.len == 0: quit "[Error] no input file." if backup: os.copyFile(source=infile, dest=changeFileExt(infile, ".nim.backup")) - prettyPrint(infile) + if outfile.len == 0: outfile = infile + prettyPrint(infile, outfile) main() diff --git a/nimpretty/nimpretty.nim.cfg b/nimpretty/nimpretty.nim.cfg new file mode 100644 index 000000000..5fafa6d2a --- /dev/null +++ b/nimpretty/nimpretty.nim.cfg @@ -0,0 +1,2 @@ +--define: nimpretty +--define: nimpretty2 diff --git a/nimpretty/tester.nim b/nimpretty/tester.nim new file mode 100644 index 000000000..8798ce06a --- /dev/null +++ b/nimpretty/tester.nim @@ -0,0 +1,29 @@ +# Small program that runs the test cases + +import strutils, os + +const + dir = "nimpretty/tests/" + +var + failures = 0 + +proc test(infile, outfile: string) = + if execShellCmd("nimpretty -o:$2 --backup:off $1" % [infile, outfile]) != 0: + quit("FAILURE") + let nimFile = splitFile(infile).name + let expected = dir / "expected" / nimFile & ".nim" + let produced = dir / nimFile & ".pretty" + if strip(readFile(expected)) != strip(readFile(produced)): + echo "FAILURE: files differ: ", nimFile + discard execShellCmd("diff -uNdr " & expected & " " & produced) + failures += 1 + else: + echo "SUCCESS: files identical: ", nimFile + +for t in walkFiles(dir / "*.nim"): + let res = t.changeFileExt("pretty") + test(t, res) + removeFile(res) + +if failures > 0: quit($failures & " failures occurred.") diff --git a/nimpretty/tests/exhaustive.nim b/nimpretty/tests/exhaustive.nim new file mode 100644 index 000000000..a2501a193 --- /dev/null +++ b/nimpretty/tests/exhaustive.nim @@ -0,0 +1,316 @@ +discard """ + outputsub: '''ObjectAssignmentError''' + exitcode: "1" +""" + +import verylongnamehere,verylongnamehere,verylongnamehereverylongnamehereverylong,namehere,verylongnamehere + +proc `[]=`() = discard "index setter" +proc `putter=`() = discard cast[pointer](cast[int](buffer) + size) + +(not false) + +let expr = if true: "true" else: "false" + +var body = newNimNode(nnkIfExpr).add( + newNimNode(nnkElifBranch).add( + infix(newDotExpr(ident("a"), ident("kind")), "==", newDotExpr(ident("b"), ident("kind"))), + condition + ), + newNimNode(nnkElse).add(newStmtList(newNimNode(nnkReturnStmt).add(ident("false")))) +) + +# comment + +var x = 1 + +type + GeneralTokenizer* = object of RootObj ## comment here + kind*: TokenClass ## and here + start*, length*: int ## you know how it goes... + buf: cstring + pos: int # other comment here. + state: TokenClass + +var x*: string +var y: seq[string] #[ yay inline comments. So nice I have to care bout these. ]# + +echo "#", x, "##", y, "#" & "string" & $test + +echo (tup, here) +echo(argA, argB) + +import macros + +## A documentation comment here. +## That spans multiple lines. +## And is not to be touched. + +const numbers = [4u8, 5'u16, 89898_00] + +macro m(n): untyped = + result = foo"string literal" + +{.push m.} +proc p() = echo "p", 1+4 * 5, if true: 5 else: 6 +proc q(param: var ref ptr string) = + p() + if true: + echo a and b or not c and not -d +{.pop.} + +q() + +when false: + # bug #4766 + type + Plain = ref object + discard + + Wrapped[T] = object + value: T + + converter toWrapped[T](value: T): Wrapped[T] = + Wrapped[T](value: value) + + let result = Plain() + discard $result + +when false: + # bug #3670 + template someTempl(someConst: bool) = + when someConst: + var a: int + if true: + when not someConst: + var a: int + a = 5 + + someTempl(true) + + +# +# +# The Nim Compiler +# (c) Copyright 2018 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Layouter for nimpretty. Still primitive but useful. + +import idents, lexer, lineinfos, llstream, options, msgs, strutils +from os import changeFileExt + +const + MaxLineLen = 80 + LineCommentColumn = 30 + +type + SplitKind = enum + splitComma, splitParLe, splitAnd, splitOr, splitIn, splitBinary + + Emitter* = object + f: PLLStream + config: ConfigRef + fid: FileIndex + lastTok: TTokType + inquote {.pragmaHereWrongCurlyEnd}: bool + col, lastLineNumber, lineSpan, indentLevel: int + content: string + fixedUntil: int # marks where we must not go in the content + altSplitPos: array[SplitKind, int] # alternative split positions + +proc openEmitter*[T, S](em: var Emitter; config: ConfigRef, fileIdx: FileIndex) {.pragmaHereWrongCurlyEnd} = + let outfile = changeFileExt(config.toFullPath(fileIdx), ".pretty.nim") + em.f = llStreamOpen(outfile, fmWrite) + em.config = config + em.fid = fileIdx + em.lastTok = tkInvalid + em.inquote = false + em.col = 0 + em.content = newStringOfCap(16_000) + if em.f == nil: + rawMessage(config, errGenerated, "cannot open file: " & outfile) + +proc closeEmitter*(em: var Emitter) {.inline.} = + em.f.llStreamWrite em.content + llStreamClose(em.f) + +proc countNewlines(s: string): int = + result = 0 + for i in 0..<s.len: + if s[i+1] == '\L': inc result + +proc calcCol(em: var Emitter; s: string) = + var i = s.len-1 + em.col = 0 + while i >= 0 and s[i] != '\L': + dec i + inc em.col + +template wr(x) = + em.content.add x + inc em.col, x.len + +template goodCol(col): bool = col in 40..MaxLineLen + +const splitters = {tkComma, tkSemicolon, tkParLe, tkParDotLe, + tkBracketLe, tkBracketLeColon, tkCurlyDotLe, + tkCurlyLe} + +template rememberSplit(kind) = + if goodCol(em.col): + em.altSplitPos[kind] = em.content.len + +proc softLinebreak(em: var Emitter, lit: string) = + # XXX Use an algorithm that is outlined here: + # https://llvm.org/devmtg/2013-04/jasper-slides.pdf + # +2 because we blindly assume a comma or ' &' might follow + if not em.inquote and em.col+lit.len+2 >= MaxLineLen: + if em.lastTok in splitters: + wr("\L") + em.col = 0 + for i in 1..em.indentLevel+2: wr(" ") + else: + # search backwards for a good split position: + for a in em.altSplitPos: + if a > em.fixedUntil: + let ws = "\L" & repeat(' ',em.indentLevel+2) + em.col = em.content.len - a + em.content.insert(ws, a) + break + +proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = + + template endsInWhite(em): bool = + em.content.len > 0 and em.content[em.content.high] in {' ', '\L'} + template endsInAlpha(em): bool = + em.content.len > 0 and em.content[em.content.high] in SymChars+{'_'} + + proc emitComment(em: var Emitter; tok: TToken) = + let lit = strip fileSection(em.config, em.fid, tok.commentOffsetA, tok.commentOffsetB) + em.lineSpan = countNewlines(lit) + if em.lineSpan > 0: calcCol(em, lit) + if not endsInWhite(em): + wr(" ") + if em.lineSpan == 0 and max(em.col, LineCommentColumn) + lit.len <= MaxLineLen: + for i in 1 .. LineCommentColumn - em.col: wr(" ") + wr lit + + var preventComment = case tok.tokType + of tokKeywordLow..tokKeywordHigh: + if endsInAlpha(em): wr(" ") + wr(TokTypeToStr[tok.tokType]) + + case tok.tokType + of tkAnd: rememberSplit(splitAnd) + of tkOr: rememberSplit(splitOr) + of tkIn: rememberSplit(splitIn) + else: 90 + else: + "case returns value" + + + if tok.tokType == tkComment and tok.line == em.lastLineNumber and tok.indent >= 0: + # we have an inline comment so handle it before the indentation token: + emitComment(em, tok) + preventComment = true + em.fixedUntil = em.content.high + + elif tok.indent >= 0: + em.indentLevel = tok.indent + # remove trailing whitespace: + while em.content.len > 0 and em.content[em.content.high] == ' ': + setLen(em.content, em.content.len-1) + wr("\L") + for i in 2..tok.line - em.lastLineNumber: wr("\L") + em.col = 0 + for i in 1..tok.indent: + wr(" ") + em.fixedUntil = em.content.high + + case tok.tokType + of tokKeywordLow..tokKeywordHigh: + if endsInAlpha(em): wr(" ") + wr(TokTypeToStr[tok.tokType]) + + case tok.tokType + of tkAnd: rememberSplit(splitAnd) + of tkOr: rememberSplit(splitOr) + of tkIn: rememberSplit(splitIn) + else: discard + + of tkColon: + wr(TokTypeToStr[tok.tokType]) + wr(" ") + of tkSemicolon, + tkComma: + wr(TokTypeToStr[tok.tokType]) + wr(" ") + rememberSplit(splitComma) + of tkParLe, tkParRi, tkBracketLe, + tkBracketRi, tkCurlyLe, tkCurlyRi, + tkBracketDotLe, tkBracketDotRi, + tkCurlyDotLe, tkCurlyDotRi, + tkParDotLe, tkParDotRi, + tkColonColon, tkDot, tkBracketLeColon: + wr(TokTypeToStr[tok.tokType]) + if tok.tokType in splitters: + rememberSplit(splitParLe) + of tkEquals: + if not em.endsInWhite: wr(" ") + wr(TokTypeToStr[tok.tokType]) + wr(" ") + of tkOpr, tkDotDot: + if not em.endsInWhite: wr(" ") + wr(tok.ident.s) + template isUnary(tok): bool = + tok.strongSpaceB == 0 and tok.strongSpaceA > 0 + + if not isUnary(tok) or em.lastTok in {tkOpr, tkDotDot}: + wr(" ") + rememberSplit(splitBinary) + of tkAccent: + wr(TokTypeToStr[tok.tokType]) + em.inquote = not em.inquote + of tkComment: + if not preventComment: + emitComment(em, tok) + of tkIntLit..tkStrLit, tkRStrLit, tkTripleStrLit, tkGStrLit, tkGTripleStrLit, tkCharLit: + let lit = fileSection(em.config, em.fid, tok.offsetA, tok.offsetB) + softLinebreak(em, lit) + if endsInAlpha(em) and tok.tokType notin {tkGStrLit, tkGTripleStrLit}: wr(" ") + em.lineSpan = countNewlines(lit) + if em.lineSpan > 0: calcCol(em, lit) + wr lit + of tkEof: discard + else: + let lit = if tok.ident != nil: tok.ident.s else: tok.literal + softLinebreak(em, lit) + if endsInAlpha(em): wr(" ") + wr lit + + em.lastTok = tok.tokType + em.lastLineNumber = tok.line + em.lineSpan + em.lineSpan = 0 + +proc starWasExportMarker*(em: var Emitter) = + if em.content.endsWith(" * "): + setLen(em.content, em.content.len-3) + em.content.add("*") + dec em.col, 2 + +type + Thing = ref object + grade: string + # this name is great + name: string + +proc f() = + var c: char + var str: string + if c == '\\': + # escape char + str &= c diff --git a/nimpretty/tests/expected/exhaustive.nim b/nimpretty/tests/expected/exhaustive.nim new file mode 100644 index 000000000..95071fce3 --- /dev/null +++ b/nimpretty/tests/expected/exhaustive.nim @@ -0,0 +1,325 @@ +discard """ + outputsub: '''ObjectAssignmentError''' + exitcode: "1" +""" + +import verylongnamehere, verylongnamehere, + verylongnamehereverylongnamehereverylong, namehere, verylongnamehere + +proc `[]=`() = discard "index setter" +proc `putter=`() = discard cast[pointer](cast[int](buffer) + size) + +(not false) + +let expr = if true: "true" else: "false" + +var body = newNimNode(nnkIfExpr).add( + newNimNode(nnkElifBranch).add( + infix(newDotExpr(ident("a"), ident("kind")), "==", newDotExpr(ident("b"), + ident("kind"))), + condition + ), + newNimNode(nnkElse).add(newStmtList(newNimNode(nnkReturnStmt).add(ident( + "false")))) +) + +# comment + +var x = 1 + +type + GeneralTokenizer* = object of RootObj ## comment here + kind*: TokenClass ## and here + start*, length*: int ## you know how it goes... + buf: cstring + pos: int # other comment here. + state: TokenClass + +var x*: string +var y: seq[string] #[ yay inline comments. So nice I have to care bout these. ]# + +echo "#", x, "##", y, "#" & "string" & $test + +echo (tup, here) +echo(argA, argB) + +import macros + +## A documentation comment here. +## That spans multiple lines. +## And is not to be touched. + +const numbers = [4u8, 5'u16, 89898_00] + +macro m(n): untyped = + result = foo"string literal" + +{.push m.} +proc p() = echo "p", 1+4 * 5, if true: 5 else: 6 +proc q(param: var ref ptr string) = + p() + if true: + echo a and b or not c and not -d +{.pop.} + +q() + +when false: + # bug #4766 + type + Plain = ref object + discard + + Wrapped[T] = object + value: T + + converter toWrapped[T](value: T): Wrapped[T] = + Wrapped[T](value: value) + + let result = Plain() + discard $result + +when false: + # bug #3670 + template someTempl(someConst: bool) = + when someConst: + var a: int + if true: + when not someConst: + var a: int + a = 5 + + someTempl(true) + + +# +# +# The Nim Compiler +# (c) Copyright 2018 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Layouter for nimpretty. Still primitive but useful. + +import idents, lexer, lineinfos, llstream, options, msgs, strutils +from os import changeFileExt + +const + MaxLineLen = 80 + LineCommentColumn = 30 + +type + SplitKind = enum + splitComma, splitParLe, splitAnd, splitOr, splitIn, splitBinary + + Emitter* = object + f: PLLStream + config: ConfigRef + fid: FileIndex + lastTok: TTokType + inquote {.pragmaHereWrongCurlyEnd.}: bool + col, lastLineNumber, lineSpan, indentLevel: int + content: string + fixedUntil: int # marks where we must not go in the content + altSplitPos: array[SplitKind, int] # alternative split positions + +proc openEmitter*[T, S](em: var Emitter; config: ConfigRef; + fileIdx: FileIndex) {.pragmaHereWrongCurlyEnd.} = + let outfile = changeFileExt(config.toFullPath(fileIdx), ".pretty.nim") + em.f = llStreamOpen(outfile, fmWrite) + em.config = config + em.fid = fileIdx + em.lastTok = tkInvalid + em.inquote = false + em.col = 0 + em.content = newStringOfCap(16_000) + if em.f == nil: + rawMessage(config, errGenerated, "cannot open file: " & outfile) + +proc closeEmitter*(em: var Emitter) {.inline.} = + em.f.llStreamWrite em.content + llStreamClose(em.f) + +proc countNewlines(s: string): int = + result = 0 + for i in 0..<s.len: + if s[i+1] == '\L': inc result + +proc calcCol(em: var Emitter; s: string) = + var i = s.len-1 + em.col = 0 + while i >= 0 and s[i] != '\L': + dec i + inc em.col + +template wr(x) = + em.content.add x + inc em.col, x.len + +template goodCol(col): bool = col in 40..MaxLineLen + +const splitters = {tkComma, tkSemicolon, tkParLe, tkParDotLe, + tkBracketLe, tkBracketLeColon, tkCurlyDotLe, + tkCurlyLe} + +template rememberSplit(kind) = + if goodCol(em.col): + em.altSplitPos[kind] = em.content.len + +proc softLinebreak(em: var Emitter; lit: string) = + # XXX Use an algorithm that is outlined here: + # https://llvm.org/devmtg/2013-04/jasper-slides.pdf + # +2 because we blindly assume a comma or ' &' might follow + if not em.inquote and em.col+lit.len+2 >= MaxLineLen: + if em.lastTok in splitters: + wr("\L") + em.col = 0 + for i in 1..em.indentLevel+2: wr(" ") + else: + # search backwards for a good split position: + for a in em.altSplitPos: + if a > em.fixedUntil: + let ws = "\L" & repeat(' ', em.indentLevel+2) + em.col = em.content.len - a + em.content.insert(ws, a) + break + +proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = + + template endsInWhite(em): bool = + em.content.len > 0 and em.content[em.content.high] in {' ', '\L'} + template endsInAlpha(em): bool = + em.content.len > 0 and em.content[em.content.high] in SymChars+{'_'} + + proc emitComment(em: var Emitter; tok: TToken) = + let lit = strip fileSection(em.config, em.fid, tok.commentOffsetA, + tok.commentOffsetB) + em.lineSpan = countNewlines(lit) + if em.lineSpan > 0: calcCol(em, lit) + if not endsInWhite(em): + wr(" ") + if em.lineSpan == 0 and max(em.col, + LineCommentColumn) + lit.len <= MaxLineLen: + for i in 1 .. LineCommentColumn - em.col: wr(" ") + wr lit + + var preventComment = case tok.tokType + of tokKeywordLow..tokKeywordHigh: + if endsInAlpha(em): wr(" ") + wr(TokTypeToStr[tok.tokType]) + + case tok.tokType + of tkAnd: rememberSplit(splitAnd) + of tkOr: rememberSplit(splitOr) + of tkIn: rememberSplit(splitIn) + else: 90 + else: + "case returns value" + + + if tok.tokType == tkComment and tok.line == em.lastLineNumber and + tok.indent >= 0: + # we have an inline comment so handle it before the indentation token: + emitComment(em, tok) + preventComment = true + em.fixedUntil = em.content.high + + elif tok.indent >= 0: + em.indentLevel = tok.indent + # remove trailing whitespace: + while em.content.len > 0 and em.content[em.content.high] == ' ': + setLen(em.content, em.content.len-1) + wr("\L") + for i in 2..tok.line - em.lastLineNumber: wr("\L") + em.col = 0 + for i in 1..tok.indent: + wr(" ") + em.fixedUntil = em.content.high + + case tok.tokType + of tokKeywordLow..tokKeywordHigh: + if endsInAlpha(em): wr(" ") + wr(TokTypeToStr[tok.tokType]) + + case tok.tokType + of tkAnd: rememberSplit(splitAnd) + of tkOr: rememberSplit(splitOr) + of tkIn: rememberSplit(splitIn) + else: discard + + of tkColon: + wr(TokTypeToStr[tok.tokType]) + wr(" ") + of tkSemicolon, + tkComma: + wr(TokTypeToStr[tok.tokType]) + wr(" ") + rememberSplit(splitComma) + of tkParLe, tkParRi, tkBracketLe, + tkBracketRi, tkCurlyLe, tkCurlyRi, + tkBracketDotLe, tkBracketDotRi, + tkCurlyDotLe, tkCurlyDotRi, + tkParDotLe, tkParDotRi, + tkColonColon, tkDot, tkBracketLeColon: + wr(TokTypeToStr[tok.tokType]) + if tok.tokType in splitters: + rememberSplit(splitParLe) + of tkEquals: + if not em.endsInWhite: wr(" ") + wr(TokTypeToStr[tok.tokType]) + wr(" ") + of tkOpr, tkDotDot: + if not em.endsInWhite: wr(" ") + wr(tok.ident.s) + template isUnary(tok): bool = + tok.strongSpaceB == 0 and tok.strongSpaceA > 0 + + if not isUnary(tok) or em.lastTok in {tkOpr, tkDotDot}: + wr(" ") + rememberSplit(splitBinary) + of tkAccent: + wr(TokTypeToStr[tok.tokType]) + em.inquote = not em.inquote + of tkComment: + if not preventComment: + emitComment(em, tok) + of tkIntLit..tkStrLit, tkRStrLit, tkTripleStrLit, tkGStrLit, + tkGTripleStrLit, tkCharLit: + let lit = fileSection(em.config, em.fid, tok.offsetA, tok.offsetB) + softLinebreak(em, lit) + if endsInAlpha(em) and tok.tokType notin {tkGStrLit, tkGTripleStrLit}: wr( + " ") + em.lineSpan = countNewlines(lit) + if em.lineSpan > 0: calcCol(em, lit) + wr lit + of tkEof: discard + else: + let lit = if tok.ident != nil: tok.ident.s else: tok.literal + softLinebreak(em, lit) + if endsInAlpha(em): wr(" ") + wr lit + + em.lastTok = tok.tokType + em.lastLineNumber = tok.line + em.lineSpan + em.lineSpan = 0 + +proc starWasExportMarker*(em: var Emitter) = + if em.content.endsWith(" * "): + setLen(em.content, em.content.len-3) + em.content.add("*") + dec em.col, 2 + +type + Thing = ref object + grade: string + # this name is great + name: string + +proc f() = + var c: char + var str: string + if c == '\\': + # escape char + str &= c diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim index 5c505ddf7..3ba1120b2 100644 --- a/nimsuggest/nimsuggest.nim +++ b/nimsuggest/nimsuggest.nim @@ -9,6 +9,9 @@ ## Nimsuggest is a tool that helps to give editors IDE like capabilities. +when not defined(nimcore): + {.error: "nimcore MUST be defined for Nim's core tooling".} + import strutils, os, parseopt, parseutils, sequtils, net, rdstdin, sexp # Do NOT import suggest. It will lead to wierd bugs with # suggestionResultHook, because suggest.nim is included by sigmatch. @@ -17,7 +20,7 @@ import compiler / [options, commands, modules, sem, passes, passaux, msgs, nimconf, extccomp, condsyms, sigmatch, ast, scriptconfig, - idents, modulegraphs, vm, prefixmatches] + idents, modulegraphs, vm, prefixmatches, lineinfos] when defined(windows): import winlean @@ -74,8 +77,8 @@ proc writelnToChannel(line: string) = proc sugResultHook(s: Suggest) = results.send(s) -proc errorHook(info: TLineInfo; msg: string; sev: Severity) = - results.send(Suggest(section: ideChk, filePath: toFullPath(info), +proc errorHook(conf: ConfigRef; info: TLineInfo; msg: string; sev: Severity) = + results.send(Suggest(section: ideChk, filePath: toFullPath(conf, info), line: toLinenumber(info), column: toColumn(info), doc: msg, forth: $sev)) @@ -109,7 +112,7 @@ proc sexp(s: Suggest): SexpNode = let qp = if s.qualifiedPath.isNil: @[] else: s.qualifiedPath result = convertSexp([ s.section, - s.symkind, + TSymKind s.symkind, qp.map(newSString), s.filePath, s.forth, @@ -141,49 +144,48 @@ proc listEpc(): SexpNode = methodDesc.add(docstring) result.add(methodDesc) -proc findNode(n: PNode): PSym = +proc findNode(n: PNode; trackPos: TLineInfo): PSym = #echo "checking node ", n.info if n.kind == nkSym: - if isTracked(n.info, n.sym.name.s.len): return n.sym + if isTracked(n.info, trackPos, n.sym.name.s.len): return n.sym else: for i in 0 ..< safeLen(n): - let res = n.sons[i].findNode + let res = findNode(n[i], trackPos) if res != nil: return res -proc symFromInfo(graph: ModuleGraph; gTrackPos: TLineInfo): PSym = - let m = graph.getModule(gTrackPos.fileIndex) - #echo m.isNil, " I knew it ", gTrackPos.fileIndex +proc symFromInfo(graph: ModuleGraph; trackPos: TLineInfo): PSym = + let m = graph.getModule(trackPos.fileIndex) if m != nil and m.ast != nil: - result = m.ast.findNode + result = findNode(m.ast, trackPos) proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int; - graph: ModuleGraph; cache: IdentCache) = + graph: ModuleGraph) = let conf = graph.config myLog("cmd: " & $cmd & ", file: " & file & ", dirtyFile: " & dirtyfile & "[" & $line & ":" & $col & "]") conf.ideCmd = cmd if cmd == ideChk: - msgs.structuredErrorHook = errorHook - msgs.writelnHook = myLog + conf.structuredErrorHook = errorHook + conf.writelnHook = myLog else: - msgs.structuredErrorHook = nil - msgs.writelnHook = myLog - if cmd == ideUse and suggestVersion != 0: + conf.structuredErrorHook = nil + conf.writelnHook = myLog + if cmd == ideUse and conf.suggestVersion != 0: graph.resetAllModules() var isKnownFile = true let dirtyIdx = fileInfoIdx(conf, file, isKnownFile) - if dirtyfile.len != 0: msgs.setDirtyFile(dirtyIdx, dirtyfile) - else: msgs.setDirtyFile(dirtyIdx, nil) + if dirtyfile.len != 0: msgs.setDirtyFile(conf, dirtyIdx, dirtyfile) + else: msgs.setDirtyFile(conf, dirtyIdx, nil) - gTrackPos = newLineInfo(dirtyIdx, line, col) - gTrackPosAttached = false + conf.m.trackPos = newLineInfo(dirtyIdx, line, col) + conf.m.trackPosAttached = false conf.errorCounter = 0 - if suggestVersion == 1: + if conf.suggestVersion == 1: graph.usageSym = nil if not isKnownFile: - graph.compileProject(cache) - if suggestVersion == 0 and conf.ideCmd in {ideUse, ideDus} and + graph.compileProject() + if conf.suggestVersion == 0 and conf.ideCmd in {ideUse, ideDus} and dirtyfile.len == 0: discard "no need to recompile anything" else: @@ -191,16 +193,16 @@ proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int; graph.markDirty dirtyIdx graph.markClientsDirty dirtyIdx if conf.ideCmd != ideMod: - graph.compileProject(cache, modIdx) + graph.compileProject(modIdx) if conf.ideCmd in {ideUse, ideDus}: - let u = if suggestVersion != 1: graph.symFromInfo(gTrackPos) else: graph.usageSym + let u = if conf.suggestVersion != 1: graph.symFromInfo(conf.m.trackPos) else: graph.usageSym if u != nil: listUsages(conf, u) else: - localError(conf, gTrackPos, "found no symbol at this position " & $gTrackPos) + localError(conf, conf.m.trackPos, "found no symbol at this position " & (conf $ conf.m.trackPos)) proc executeEpc(cmd: IdeCmd, args: SexpNode; - graph: ModuleGraph; cache: IdentCache) = + graph: ModuleGraph) = let file = args[0].getStr line = args[1].getNum @@ -208,7 +210,7 @@ proc executeEpc(cmd: IdeCmd, args: SexpNode; var dirtyfile = "" if len(args) > 3: dirtyfile = args[3].getStr(nil) - execute(cmd, file, dirtyfile, int(line), int(column), graph, cache) + execute(cmd, file, dirtyfile, int(line), int(column), graph) proc returnEpc(socket: Socket, uid: BiggestInt, s: SexpNode|string, return_symbol = "return") = @@ -377,7 +379,7 @@ proc replEpc(x: ThreadParams) {.thread.} = "unexpected call: " & epcAPI quit errMessage -proc execCmd(cmd: string; graph: ModuleGraph; cache: IdentCache; cachedMsgs: CachedMsgs) = +proc execCmd(cmd: string; graph: ModuleGraph; cachedMsgs: CachedMsgs) = let conf = graph.config template sentinel() = @@ -432,20 +434,20 @@ proc execCmd(cmd: string; graph: ModuleGraph; cache: IdentCache; cachedMsgs: Cac results.send(Suggest(section: ideKnown, quality: ord(fileInfoKnown(conf, orig)))) else: if conf.ideCmd == ideChk: - for cm in cachedMsgs: errorHook(cm.info, cm.msg, cm.sev) - execute(conf.ideCmd, orig, dirtyfile, line, col, graph, cache) + for cm in cachedMsgs: errorHook(conf, cm.info, cm.msg, cm.sev) + execute(conf.ideCmd, orig, dirtyfile, line, col, graph) sentinel() -proc recompileFullProject(graph: ModuleGraph; cache: IdentCache) = +proc recompileFullProject(graph: ModuleGraph) = #echo "recompiling full project" resetSystemArtifacts(graph) - vm.globalCtx = nil + graph.vm = nil graph.resetAllModules() GC_fullcollect() - compileProject(graph, cache) + compileProject(graph) #echo GC_getStatistics() -proc mainThread(graph: ModuleGraph; cache: IdentCache) = +proc mainThread(graph: ModuleGraph) = let conf = graph.config if gLogging: for it in conf.searchPaths: @@ -457,17 +459,17 @@ proc mainThread(graph: ModuleGraph; cache: IdentCache) = else: writelnToChannel(line) - msgs.writelnHook = wrHook - suggestionResultHook = sugResultHook + conf.writelnHook = wrHook + conf.suggestionResultHook = sugResultHook graph.doStopCompile = proc (): bool = requests.peek() > 0 var idle = 0 var cachedMsgs: CachedMsgs = @[] while true: let (hasData, req) = requests.tryRecv() if hasData: - msgs.writelnHook = wrHook - suggestionResultHook = sugResultHook - execCmd(req, graph, cache, cachedMsgs) + conf.writelnHook = wrHook + conf.suggestionResultHook = sugResultHook + execCmd(req, graph, cachedMsgs) idle = 0 else: os.sleep 250 @@ -475,23 +477,22 @@ proc mainThread(graph: ModuleGraph; cache: IdentCache) = if idle == 20 and gRefresh: # we use some nimsuggest activity to enable a lazy recompile: conf.ideCmd = ideChk - msgs.writelnHook = proc (s: string) = discard + conf.writelnHook = proc (s: string) = discard cachedMsgs.setLen 0 - msgs.structuredErrorHook = proc (info: TLineInfo; msg: string; sev: Severity) = + conf.structuredErrorHook = proc (conf: ConfigRef; info: TLineInfo; msg: string; sev: Severity) = cachedMsgs.add(CachedMsg(info: info, msg: msg, sev: sev)) - suggestionResultHook = proc (s: Suggest) = discard - recompileFullProject(graph, cache) + conf.suggestionResultHook = proc (s: Suggest) = discard + recompileFullProject(graph) var inputThread: Thread[ThreadParams] -proc mainCommand(graph: ModuleGraph; cache: IdentCache) = +proc mainCommand(graph: ModuleGraph) = let conf = graph.config - clearPasses() - registerPass verbosePass - registerPass semPass + clearPasses(graph) + registerPass graph, verbosePass + registerPass graph, semPass conf.cmd = cmdIdeTools - incl conf.globalOptions, optCaasEnabled wantMainModule(conf) if not fileExists(conf.projectFull): @@ -502,12 +503,12 @@ proc mainCommand(graph: ModuleGraph; cache: IdentCache) = # do not stop after the first error: conf.errorMax = high(int) # do not print errors, but log them - msgs.writelnHook = proc (s: string) = log(s) - msgs.structuredErrorHook = nil + conf.writelnHook = proc (s: string) = log(s) + conf.structuredErrorHook = nil # compile the project before showing any input so that we already # can answer questions right away: - compileProject(graph, cache) + compileProject(graph) open(requests) open(results) @@ -520,7 +521,7 @@ proc mainCommand(graph: ModuleGraph; cache: IdentCache) = (gPort, "sug \"" & conf.projectFull & "\":" & gAddress)) of mcmdcon: createThread(inputThread, replCmdline, (gPort, "con \"" & conf.projectFull & "\":" & gAddress)) - mainThread(graph, cache) + mainThread(graph) joinThread(inputThread) close(requests) close(results) @@ -555,8 +556,8 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) = gMode = mepc conf.verbosity = 0 # Port number gotta be first. of "debug": incl(conf.globalOptions, optIdeDebug) - of "v2": suggestVersion = 0 - of "v1": suggestVersion = 1 + of "v2": conf.suggestVersion = 0 + of "v1": conf.suggestVersion = 1 of "tester": gMode = mstdin gEmitEof = true @@ -568,7 +569,7 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) = else: gRefresh = true of "maxresults": - suggestMaxResults = parseInt(p.val) + conf.suggestMaxResults = parseInt(p.val) else: processSwitch(pass, p, conf) of cmdArgument: let a = unixToNativePath(p.key) @@ -589,7 +590,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = else: processCmdLine(passCmd1, "", conf) if gMode != mstdin: - msgs.writelnHook = proc (msg: string) = discard + conf.writelnHook = proc (msg: string) = discard if conf.projectName != "": try: conf.projectFull = canonicalizePath(conf, conf.projectName) @@ -628,8 +629,8 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = extccomp.initVars(conf) processCmdLine(passCmd2, "", conf) - let graph = newModuleGraph(conf) + let graph = newModuleGraph(cache, conf) graph.suggestMode = true - mainCommand(graph, cache) + mainCommand(graph) handleCmdline(newIdentCache(), newConfigRef()) diff --git a/nimsuggest/nimsuggest.nim.cfg b/nimsuggest/nimsuggest.nim.cfg index 38e74b3c7..820db0dba 100644 --- a/nimsuggest/nimsuggest.nim.cfg +++ b/nimsuggest/nimsuggest.nim.cfg @@ -8,6 +8,8 @@ path:"$lib/packages/docutils" define:useStdoutAsStdmsg define:nimsuggest +define:nimcore + # die when nimsuggest uses more than 4GB: @if cpu32: define:"nimMaxHeap=2000" @@ -15,7 +17,6 @@ define:nimsuggest define:"nimMaxHeap=4000" @end -#cs:partial #define:useNodeIds #define:booting #define:noDocgen diff --git a/tests/arithm/tnot.nim b/tests/arithm/tnot.nim new file mode 100644 index 000000000..6a4877b2c --- /dev/null +++ b/tests/arithm/tnot.nim @@ -0,0 +1,58 @@ +discard """ + output: ''' +-5 +-5 +-5 +-5 +4 +4 +4 +4 +251 +65531 +4294967291 +18446744073709551611 +4 +4 +4 +4 +''' +""" + +# Signed types +block: + const t0: int8 = not 4 + const t1: int16 = not 4 + const t2: int32 = not 4 + const t3: int64 = not 4 + const t4: int8 = not -5 + const t5: int16 = not -5 + const t6: int32 = not -5 + const t7: int64 = not -5 + echo t0 + echo t1 + echo t2 + echo t3 + echo t4 + echo t5 + echo t6 + echo t7 + +# Unsigned types +block: + const t0: uint8 = not 4'u8 + const t1: uint16 = not 4'u16 + const t2: uint32 = not 4'u32 + const t3: uint64 = not 4'u64 + const t4: uint8 = not 251'u8 + const t5: uint16 = not 65531'u16 + const t6: uint32 = not 4294967291'u32 + const t7: uint64 = not 18446744073709551611'u64 + echo t0 + echo t1 + echo t2 + echo t3 + echo t4 + echo t5 + echo t6 + echo t7 diff --git a/tests/arithm/tshl.nim b/tests/arithm/tshl.nim new file mode 100644 index 000000000..0aa46d021 --- /dev/null +++ b/tests/arithm/tshl.nim @@ -0,0 +1,34 @@ +discard """ + output: ''' +0 +0 +1 +1 +0 +0 +0 +1 +''' +""" + +# Signed types +block: + const t0: int8 = 1'i8 shl 8 + const t1: int16 = 1'i16 shl 16 + const t2: int32 = 1'i32 shl 32 + const t3: int64 = 1'i64 shl 64 + echo t0 + echo t1 + echo t2 + echo t3 + +# Unsigned types +block: + const t0: uint8 = 1'u8 shl 8 + const t1: uint16 = 1'u16 shl 16 + const t2: uint32 = 1'u32 shl 32 + const t3: uint64 = 1'u64 shl 64 + echo t0 + echo t1 + echo t2 + echo t3 diff --git a/tests/async/tasynctry2.nim b/tests/async/tasynctry2.nim deleted file mode 100644 index 4b3f17cc5..000000000 --- a/tests/async/tasynctry2.nim +++ /dev/null @@ -1,18 +0,0 @@ -discard """ - file: "tasynctry2.nim" - errormsg: "\'yield\' cannot be used within \'try\' in a non-inlined iterator" - line: 14 -""" -import asyncdispatch - -{.experimental: "oldIterTransf".} - -proc foo(): Future[bool] {.async.} = discard - -proc test5(): Future[int] {.async.} = - try: - discard await foo() - raise newException(ValueError, "Test5") - except: - discard await foo() - result = 0 diff --git a/tests/compilerapi/exposed.nim b/tests/compilerapi/exposed.nim new file mode 100644 index 000000000..73becd93d --- /dev/null +++ b/tests/compilerapi/exposed.nim @@ -0,0 +1,3 @@ + +proc addFloats*(x, y, z: float): float = + discard "implementation overriden by tcompilerapi.nim" diff --git a/tests/compilerapi/myscript.nim b/tests/compilerapi/myscript.nim new file mode 100644 index 000000000..539b07de1 --- /dev/null +++ b/tests/compilerapi/myscript.nim @@ -0,0 +1,9 @@ + +import exposed + +echo "top level statements are executed!" + +proc hostProgramRunsThis*(a, b: float): float = + result = addFloats(a, b, 1.0) + +let hostProgramWantsThis* = "my secret" diff --git a/tests/compilerapi/tcompilerapi.nim b/tests/compilerapi/tcompilerapi.nim new file mode 100644 index 000000000..90d343264 --- /dev/null +++ b/tests/compilerapi/tcompilerapi.nim @@ -0,0 +1,47 @@ +discard """ + output: '''top level statements are executed! +2.0 +my secret +''' +""" + +## Example program that demonstrates how to use the +## compiler as an API to embed into your own projects. + +import "../../compiler" / [ast, vmdef, vm, nimeval] +import std / [os] + +proc main() = + let std = findNimStdLib() + if std.len == 0: + quit "cannot find Nim's standard library" + + var intr = createInterpreter("myscript.nim", [std, getAppDir()]) + intr.implementRoutine("*", "exposed", "addFloats", proc (a: VmArgs) = + setResult(a, getFloat(a, 0) + getFloat(a, 1) + getFloat(a, 2)) + ) + + intr.evalScript() + + let foreignProc = selectRoutine(intr, "hostProgramRunsThis") + if foreignProc == nil: + quit "script does not export a proc of the name: 'hostProgramRunsThis'" + let res = intr.callRoutine(foreignProc, [newFloatNode(nkFloatLit, 0.9), + newFloatNode(nkFloatLit, 0.1)]) + if res.kind == nkFloatLit: + echo res.floatVal + else: + echo "bug!" + + let foreignValue = selectUniqueSymbol(intr, "hostProgramWantsThis") + if foreignValue == nil: + quit "script does not export a global of the name: hostProgramWantsThis" + let val = intr.getGlobalValue(foreignValue) + if val.kind in {nkStrLit..nkTripleStrLit}: + echo val.strVal + else: + echo "bug!" + + destroyInterpreter(intr) + +main() \ No newline at end of file diff --git a/tests/concepts/libs/trie.nim b/tests/concepts/libs/trie.nim new file mode 100644 index 000000000..bdd318492 --- /dev/null +++ b/tests/concepts/libs/trie.nim @@ -0,0 +1,26 @@ +import + hashes, tables, trie_database + +type + MemDBTable = Table[KeccakHash, string] + + MemDB* = object + tbl: MemDBTable + +proc hash*(key: KeccakHash): int = + hashes.hash(key.data) + +proc get*(db: MemDB, key: KeccakHash): string = + db.tbl[key] + +proc del*(db: var MemDB, key: KeccakHash): bool = + if db.tbl.hasKey(key): + db.tbl.del(key) + return true + else: + return false + +proc put*(db: var MemDB, key: KeccakHash, value: string): bool = + db.tbl[key] = value + return true + diff --git a/tests/concepts/libs/trie_database.nim b/tests/concepts/libs/trie_database.nim new file mode 100644 index 000000000..a45c64842 --- /dev/null +++ b/tests/concepts/libs/trie_database.nim @@ -0,0 +1,12 @@ +type + KeccakHash* = object + data*: string + + BytesRange* = object + bytes*: string + + TrieDatabase* = concept db + put(var db, KeccakHash, string) is bool + del(var db, KeccakHash) is bool + get(db, KeccakHash) is string + diff --git a/tests/concepts/t6770.nim b/tests/concepts/t6770.nim new file mode 100644 index 000000000..1787ee670 --- /dev/null +++ b/tests/concepts/t6770.nim @@ -0,0 +1,27 @@ +discard """ +output: ''' +10 +10 +''' +""" + +type GA = concept c + c.a is int + +type A = object + a: int + +type AA = object + case exists: bool + of true: + a: int + else: + discard + +proc print(inp: GA) = + echo inp.a + +let failing = AA(exists: true, a: 10) +let working = A(a:10) +print(working) +print(failing) diff --git a/tests/concepts/treversable.nim b/tests/concepts/treversable.nim new file mode 100644 index 000000000..6ebc077d9 --- /dev/null +++ b/tests/concepts/treversable.nim @@ -0,0 +1,31 @@ +# issue 7705, 7703, 7702 +discard """ + output: ''' +z +e + ''' +""" + +type + Reversable*[T] = concept a + a[int] is T + a.high is int + a.len is int + a.low is int + +proc get[T](s: Reversable[T], n: int): T = + s[n] + +proc hi[T](s: Reversable[T]): int = + s.high + +proc lo[T](s: Reversable[T]): int = + s.low + +iterator reverse*[T](s: Reversable[T]): T = + assert hi(s) - lo(s) == len(s) - 1 + for z in hi(s).countdown(lo(s)): + yield s.get(z) + +for s in @["e", "z"].reverse: + echo s diff --git a/tests/concepts/ttrieconcept.nim b/tests/concepts/ttrieconcept.nim new file mode 100644 index 000000000..a26e6b146 --- /dev/null +++ b/tests/concepts/ttrieconcept.nim @@ -0,0 +1,7 @@ +import libs/[trie_database, trie] + +proc takeDb(d: TrieDatabase) = discard +var mdb: MemDB + +takeDb(mdb) + diff --git a/tests/cpp/tempty_generic_obj.nim b/tests/cpp/tempty_generic_obj.nim index b4c746a30..b61c699f6 100644 --- a/tests/cpp/tempty_generic_obj.nim +++ b/tests/cpp/tempty_generic_obj.nim @@ -20,3 +20,19 @@ v.doSomething() var vf = initVector[float]() vf.doSomething() # Nim uses doSomething[int] here in C++ + +# Alternative definition: +# https://github.com/nim-lang/Nim/issues/7653 + +type VectorAlt* {.importcpp: "std::vector", header: "<vector>", nodecl.} [T] = object +proc mkVector*[T]: VectorAlt[T] {.importcpp: "std::vector<'*0>()", header: "<vector>", constructor, nodecl.} + +proc foo(): VectorAlt[cint] = + mkVector[cint]() + +proc bar(): VectorAlt[cstring] = + mkVector[cstring]() + +var x = foo() +var y = bar() + diff --git a/tests/errmsgs/tproper_stacktrace.nim b/tests/errmsgs/tproper_stacktrace.nim index 57e65fa6f..134946651 100644 --- a/tests/errmsgs/tproper_stacktrace.nim +++ b/tests/errmsgs/tproper_stacktrace.nim @@ -1,11 +1,141 @@ discard """ - outputsub: '''tproper_stacktrace.nim(7) tproper_stacktrace''' - exitcode: 1 + output: '''ok''' """ +import strscans, strutils -template fuzzy(x) = - echo x[] != 9 +proc raiseTestException*() = + raise newException(Exception, "test") -var p: ptr int -fuzzy p +proc matchStackTrace(actualEntries: openarray[StackTraceEntry], expected: string) = + var expectedEntries = newSeq[StackTraceEntry]() + var i = 0 + template checkEqual(actual, expected: typed, subject: string) = + if actual != expected: + echo "Unexpected ", subject, " on line ", i + echo "Actual: ", actual + echo "Expected: ", expected + doAssert(false) + + for l in splitLines(expected.strip): + var procname, filename: string + var line: int + if not scanf(l, "$s$w.nim($i) $w", filename, line, procname): + doAssert(false, "Wrong expected stack trace") + checkEqual($actualEntries[i].filename, filename & ".nim", "file name") + if line != 0: + checkEqual(actualEntries[i].line, line, "line number") + checkEqual($actualEntries[i].procname, procname, "proc name") + inc i + + doAssert(i == actualEntries.len, "Unexpected number of lines in stack trace") + +template verifyStackTrace*(expectedStackTrace: string, body: untyped) = + var verified = false + try: + body + except Exception as e: + verified = true + # echo "Stack trace:" + # echo e.getStackTrace + matchStackTrace(e.getStackTraceEntries(), expectedStackTrace) + + doAssert(verified, "No exception was raised") + + + + + + + + + + + + + + + + + + + + + + + + + +when isMainModule: +# <-- Align with line 70 in the text editor + block: + proc bar() = + raiseTestException() + + proc foo() = + bar() + + const expectedStackTrace = """ + tproper_stacktrace.nim(86) tproper_stacktrace + tproper_stacktrace.nim(76) foo + tproper_stacktrace.nim(73) bar + tproper_stacktrace.nim(7) raiseTestException + """ + + verifyStackTrace expectedStackTrace: + foo() + + block: + proc bar(x: int) = + raiseTestException() + + template foo(x: int) = + bar(x) + + const expectedStackTrace = """ + tproper_stacktrace.nim(103) tproper_stacktrace + tproper_stacktrace.nim(90) bar + tproper_stacktrace.nim(7) raiseTestException + """ + + verifyStackTrace expectedStackTrace: + var x: int + foo(x) + + block: #6803 + proc bar(x = 500) = + raiseTestException() + + proc foo() = + bar() + + const expectedStackTrace = """ + tproper_stacktrace.nim(120) tproper_stacktrace + tproper_stacktrace.nim(110) foo + tproper_stacktrace.nim(107) bar + tproper_stacktrace.nim(7) raiseTestException + """ + + verifyStackTrace expectedStackTrace: + foo() + + block: + proc bar() {.stackTrace: off.} = + proc baz() = # Stack trace should be enabled + raiseTestException() + baz() + + proc foo() = + bar() + + const expectedStackTrace = """ + tproper_stacktrace.nim(139) tproper_stacktrace + tproper_stacktrace.nim(129) foo + tproper_stacktrace.nim(125) baz + tproper_stacktrace.nim(7) raiseTestException + """ + + verifyStackTrace expectedStackTrace: + foo() + + echo "ok" diff --git a/tests/float/tfloatrange.nim b/tests/float/tfloatrange.nim new file mode 100644 index 000000000..e8ea1912e --- /dev/null +++ b/tests/float/tfloatrange.nim @@ -0,0 +1,49 @@ +discard """ + cmd: "nim c -d:release --rangeChecks:on $file" + output: '''StrictPositiveRange +float +range fail expected +range fail expected +''' +""" +import math, fenv + +type + Positive = range[0.0..Inf] + StrictPositive = range[minimumPositiveValue(float)..Inf] + Negative32 = range[-maximumPositiveValue(float32) .. -1.0'f32] + +proc myoverload(x: float) = + echo "float" + +proc myoverload(x: Positive) = + echo "PositiveRange" + +proc myoverload(x: StrictPositive) = + echo "StrictPositiveRange" + +let x = 9.0.StrictPositive +myoverload(x) +myoverload(9.0) + +doAssert(sqrt(x) == 3.0) + +var z = -10.0 +try: + myoverload(StrictPositive(z)) +except: + echo "range fail expected" + + +proc strictOnlyProc(x: StrictPositive): bool = + if x > 1.0: true else: false + +let x2 = 5.0.Positive +doAssert(strictOnlyProc(x2)) + +try: + let x4 = 0.0.Positive + discard strictOnlyProc(x4) +except: + echo "range fail expected" + \ No newline at end of file diff --git a/tests/generics/t3977.nim b/tests/generics/t3977.nim index 314017744..eed1a7d63 100644 --- a/tests/generics/t3977.nim +++ b/tests/generics/t3977.nim @@ -1,12 +1,14 @@ discard """ - output: '''42''' + output: "42\n42" """ type Foo[N: static[int]] = object proc foo[N](x: Foo[N]) = + let n = N echo N + echo n var f1: Foo[42] f1.foo diff --git a/tests/generics/tlateboundgenericparams.nim b/tests/generics/tlateboundgenericparams.nim new file mode 100644 index 000000000..9f0580fd2 --- /dev/null +++ b/tests/generics/tlateboundgenericparams.nim @@ -0,0 +1,145 @@ +discard """ + output: "1\n10\n1\n10" + nimout: ''' +bar instantiated with 1 +bar instantiated with 10 +''' +""" + +import typetraits + +type + Foo = object + +proc defaultFoo: Foo = discard +proc defaultInt: int = 1 +proc defaultTInt(T: type): int = 2 +proc defaultTFoo[T](x: typedesc[T]): Foo = discard +proc defaultTOldSchool[T](x: typedesc[T]): T = discard +proc defaultTModern(T: type): T = discard + +proc specializedDefault(T: type int): int = 10 +proc specializedDefault(T: type string): string = "default" + +converter intFromFoo(x: Foo): int = 3 + +proc consumeInt(x: int) = + discard + +const activeTests = {1..100} + +when true: + template test(n, body) = + when n in activeTests: + block: + body + + template reject(x) = + static: assert(not compiles(x)) + + test 1: + proc t[T](val: T = defaultInt()) = + consumeInt val + + t[int]() + reject t[string]() + + test 2: + proc t1[T](val: T = defaultFoo()) = + static: + assert type(val).name == "int" + assert T.name == "int" + + consumeInt val + + # here, the converter should kick in, but notice + # how `val` is still typed `int` inside the proc. + t1[int]() + + proc t2[T](val: T = defaultFoo()) = + discard + + reject t2[string]() + + test 3: + proc tInt[T](val = defaultInt()): string = + return type(val).name + + doAssert tInt[int]() == "int" + doAssert tInt[string]() == "int" + + proc tInt2[T](val = defaultTInt(T)): string = + return type(val).name + + doAssert tInt2[int]() == "int" + doAssert tInt2[string]() == "int" + + proc tDefTModern[T](val = defaultTModern(T)): string = + return type(val).name + + doAssert tDefTModern[int]() == "int" + doAssert tDefTModern[string]() == "string" + doAssert tDefTModern[Foo]() == "Foo" + + proc tDefTOld[T](val = defaultTOldSchool(T)): string = + return type(val).name + + doAssert tDefTOld[int]() == "int" + doAssert tDefTOld[string]() == "string" + doAssert tDefTOld[Foo]() == "Foo" + + test 4: + proc t[T](val: T = defaultTFoo(T)): string = + return type(val).name + + doAssert t[int]() == "int" + doAssert t[Foo]() == "Foo" + reject t[string]() + + test 5: + proc t1[T](a: T = specializedDefault(T)): T = + return a + + doAssert t1[int]() == 10 + doAssert t1[string]() == "default" + + proc t2[T](a: T, b = specializedDefault(T)): auto = + return $a & $b + + doAssert t2(5) == "510" + doAssert t2("string ") == "string default" + + proc t3[T](a: T, b = specializedDefault(type(a))): auto = + return $a & $b + + doAssert t3(100) == "10010" + doAssert t3("another ") == "another default" + + test 6: + # https://github.com/nim-lang/Nim/issues/5595 + type + Point[T] = object + x, y: T + + proc getOrigin[T](): Point[T] = Point[T](x: 0, y: 0) + + proc rotate[T](point: Point[T], radians: float, + origin = getOrigin[T]()): Point[T] = + discard + + var p = getOrigin[float]() + var rotated = p.rotate(2.1) + + test 7: + proc bar(x: static[int]) = + static: echo "bar instantiated with ", x + echo x + + proc foo(x: static[int] = 1) = + bar(x) + + foo() + foo(10) + foo(1) + foo(10) + diff --git a/tests/global/t5958.nim b/tests/global/t5958.nim new file mode 100644 index 000000000..5abcad4a9 --- /dev/null +++ b/tests/global/t5958.nim @@ -0,0 +1,9 @@ +discard """ + line: 9 + errormsg: "undeclared identifier: 'a'" +""" + +static: + var a = 1 + +echo a diff --git a/tests/lexer/tmissingnl.nim b/tests/lexer/tmissingnl.nim index 33b7debf1..095d9570e 100644 --- a/tests/lexer/tmissingnl.nim +++ b/tests/lexer/tmissingnl.nim @@ -4,7 +4,7 @@ discard """ errormsg: "invalid indentation" """ -import strutils var s: seq[int] = @[0, 1, 2, 3, 4, 5, 6] +import strutils let s: seq[int] = @[0, 1, 2, 3, 4, 5, 6] #s[1..3] = @[] diff --git a/tests/macros/tmacros1.nim b/tests/macros/tmacros1.nim index 9e3ab028b..80afb6641 100644 --- a/tests/macros/tmacros1.nim +++ b/tests/macros/tmacros1.nim @@ -12,12 +12,12 @@ macro outterMacro*(n, blck: untyped): untyped = echo "Using arg ! " & n.repr result = "Got: '" & $n.kind & "' " & $j var callNode = n[0] - expectKind(n, TNimrodNodeKind.nnkCall) - if n.len != 3 or n[1].kind != TNimrodNodeKind.nnkIdent: + expectKind(n, NimNodeKind.nnkCall) + if n.len != 3 or n[1].kind != NimNodeKind.nnkIdent: error("Macro " & callNode.repr & " requires the ident passed as parameter (eg: " & callNode.repr & "(the_name_you_want)): statements.") - result = newNimNode(TNimrodNodeKind.nnkStmtList) + result = newNimNode(NimNodeKind.nnkStmtList) var ass : NimNode = newNimNode(nnkAsgn) ass.add(newIdentNode(n[1].ident)) ass.add(newStrLitNode(innerProc(4))) diff --git a/tests/macros/tmacrostmt.nim b/tests/macros/tmacrostmt.nim index 6f648958f..849a32ea3 100644 --- a/tests/macros/tmacrostmt.nim +++ b/tests/macros/tmacrostmt.nim @@ -1,5 +1,5 @@ import macros -macro case_token(n: untyped): untyped {.immediate.} = +macro case_token(n: varargs[untyped]): untyped = # creates a lexical analyzer from regular expressions # ... (implementation is an exercise for the reader :-) nil @@ -21,6 +21,6 @@ case_token: inc i macro foo: typed = var exp = newCall("whatwhat", newIntLitNode(1)) if compiles(getAst(exp)): return exp - else: echo "Does not compute!" + else: echo "Does not compute! (test OK)" foo() diff --git a/tests/metatype/ttypedesc1.nim b/tests/metatype/ttypedesc1.nim index e9eee581f..837c8eccc 100644 --- a/tests/metatype/ttypedesc1.nim +++ b/tests/metatype/ttypedesc1.nim @@ -5,15 +5,16 @@ type x: T y: U -proc getTypeName(t: typedesc): string = t.name +proc getTypeName1(t: typedesc): string = t.name +proc getTypeName2(t: type): string = t.name -proc foo(T: typedesc[float], a: auto): string = +proc foo(T: type float, a: auto): string = result = "float " & $(a.len > 5) proc foo(T: typedesc[TFoo], a: int): string = result = "TFoo " & $(a) -proc foo(T: typedesc[int or bool]): string = +proc foo(T: type[int or bool]): string = var a: T a = 10 result = "int or bool " & ($a) @@ -23,8 +24,8 @@ template foo(T: typedesc[seq]): string = "seq" test "types can be used as proc params": # XXX: `check` needs to know that TFoo[int, float] is a type and # cannot be assigned for a local variable for later inspection - check ((string.getTypeName == "string")) - check ((getTypeName(int) == "int")) + check ((string.getTypeName1 == "string")) + check ((getTypeName2(int) == "int")) check ((foo(TFoo[int, float], 1000) == "TFoo 1000")) @@ -37,6 +38,25 @@ test "types can be used as proc params": check ((foo(seq[int]) == "seq")) check ((foo(seq[TFoo[bool, string]]) == "seq")) -when false: - proc foo(T: typedesc[seq], s: T) = nil +template accept(x) = + static: assert(compiles(x)) + +template reject(x) = + static: assert(not compiles(x)) + +var + si: seq[int] + ss: seq[string] + +proc foo(T: typedesc[seq], s: T) = + discard + +accept: + foo seq[int], si + +reject: + foo seq[string], si + +reject: + foo seq[int], ss diff --git a/tests/metatype/ttypedesc2.nim b/tests/metatype/ttypedesc2.nim index 4b6cfe6bc..94a7367e7 100644 --- a/tests/metatype/ttypedesc2.nim +++ b/tests/metatype/ttypedesc2.nim @@ -34,3 +34,17 @@ when true: type Point[T] = tuple[x, y: T] proc origin(T: typedesc): Point[T] = discard discard origin(int) + +# https://github.com/nim-lang/Nim/issues/7516 +import typetraits + +proc hasDefault1(T: type = int): auto = return T.name +doAssert hasDefault1(int) == "int" +doAssert hasDefault1(string) == "string" +doAssert hasDefault1() == "int" + +proc hasDefault2(T = string): auto = return T.name +doAssert hasDefault2(int) == "int" +doAssert hasDefault2(string) == "string" +doAssert hasDefault2() == "string" + diff --git a/tests/metatype/ttypedesc3.nim b/tests/metatype/ttypedesc3.nim index 9f19bd6e3..3d1cf2ec9 100644 --- a/tests/metatype/ttypedesc3.nim +++ b/tests/metatype/ttypedesc3.nim @@ -4,9 +4,9 @@ type Base = object of RootObj Child = object of Base -proc pr(T: typedesc[Base]) = echo "proc " & T.name -method me(T: typedesc[Base]) = echo "method " & T.name -iterator it(T: typedesc[Base]): auto = yield "yield " & T.name +proc pr(T: type[Base]) = echo "proc " & T.name +method me(T: type[Base]) = echo "method " & T.name +iterator it(T: type[Base]): auto = yield "yield " & T.name Base.pr Child.pr diff --git a/tests/metatype/ttypeselectors.nim b/tests/metatype/ttypeselectors.nim index 1209fe78f..2a2455adb 100644 --- a/tests/metatype/ttypeselectors.nim +++ b/tests/metatype/ttypeselectors.nim @@ -5,16 +5,16 @@ output: "8\n8\n4" import macros, typetraits -template selectType(x: int): typeDesc = +template selectType(x: int): type = when x < 10: int else: string -template simpleTypeTempl: typeDesc = +template simpleTypeTempl: type = string -macro typeFromMacro: typedesc = string +macro typeFromMacro: type = string # The tests below check that the result variable of the # selected type matches the literal types in the code: diff --git a/tests/misc/tparamsindefault.nim b/tests/misc/tparamsindefault.nim new file mode 100644 index 000000000..c678dcc60 --- /dev/null +++ b/tests/misc/tparamsindefault.nim @@ -0,0 +1,114 @@ +discard """ +output: ''' +@[1, 2, 3]@[1, 2, 3] +a +a +1 +3 is an int +2 is an int +miau is a string +f1 1 1 1 +f1 2 3 3 +f1 10 20 30 +f2 100 100 100 +f2 200 300 300 +f2 300 400 400 +f3 10 10 20 +f3 10 15 25 +true true +false true +world +''' +""" + +template reject(x) = + assert(not compiles(x)) + +block: + # https://github.com/nim-lang/Nim/issues/7756 + proc foo[T](x: seq[T], y: seq[T] = x) = + echo x, y + + let a = @[1, 2, 3] + foo(a) + +block: + # https://github.com/nim-lang/Nim/issues/1201 + proc issue1201(x: char|int = 'a') = echo x + + issue1201() + issue1201('a') + issue1201(1) + + # https://github.com/nim-lang/Nim/issues/7000 + proc test(a: int|string = 2) = + when a is int: + echo a, " is an int" + elif a is string: + echo a, " is a string" + + test(3) # works + test() # works + test("miau") + +block: + # https://github.com/nim-lang/Nim/issues/3002 and similar + proc f1(a: int, b = a, c = b) = + echo "f1 ", a, " ", b, " ", c + + proc f2(a: int, b = a, c: int = b) = + echo "f2 ", a, " ", b, " ", c + + proc f3(a: int, b = a, c = a + b) = + echo "f3 ", a, " ", b, " ", c + + f1 1 + f1(2, 3) + f1 10, 20, 30 + 100.f2 + 200.f2 300 + 300.f2(400) + + 10.f3() + 10.f3(15) + + reject: + # This is a type mismatch error: + proc f4(a: int, b = a, c: float = b) = discard + + reject: + # undeclared identifier + proc f5(a: int, b = c, c = 10) = discard + + reject: + # undeclared identifier + proc f6(a: int, b = b) = discard + + reject: + # undeclared identifier + proc f7(a = a) = discard + +block: + proc f(a: var int, b: ptr int, c = addr(a)) = + echo addr(a) == b, " ", b == c + + var x = 10 + f(x, addr(x)) + f(x, nil, nil) + +block: + # https://github.com/nim-lang/Nim/issues/1046 + proc pySubstr(s: string, start: int, endd = s.len()): string = + var + revStart = start + revEnd = endd + + if start < 0: + revStart = s.len() + start + if endd < 0: + revEnd = s.len() + endd + + return s[revStart .. revEnd-1] + + echo pySubstr("Hello world", -5) + diff --git a/tests/niminaction/Chapter1/various1.nim b/tests/niminaction/Chapter1/various1.nim new file mode 100644 index 000000000..688180fd2 --- /dev/null +++ b/tests/niminaction/Chapter1/various1.nim @@ -0,0 +1,45 @@ +discard """ + exitCode: 0 + outputsub: "Woof!" +""" + +import strutils +echo("hello".to_upper()) +echo("world".toUpper()) + +type + Dog = object #<1> + age: int #<2> + +let dog = Dog(age: 3) #<3> + +proc showNumber(num: int | float) = + echo(num) + +showNumber(3.14) +showNumber(42) + +for i in 0 .. <10: + echo(i) + +block: # Block added due to clash. + type + Dog = object + + proc bark(self: Dog) = #<1> + echo("Woof!") + + let dog = Dog() + dog.bark() #<2> + +import sequtils, future, strutils +let list = @["Dominik Picheta", "Andreas Rumpf", "Desmond Hume"] +list.map( + (x: string) -> (string, string) => (x.split[0], x.split[1]) +).echo + +import strutils +let list1 = @["Dominik Picheta", "Andreas Rumpf", "Desmond Hume"] +for name in list1: + echo((name.split[0], name.split[1])) + diff --git a/tests/niminaction/Chapter2/explicit_discard.nim b/tests/niminaction/Chapter2/explicit_discard.nim new file mode 100644 index 000000000..3e94c335b --- /dev/null +++ b/tests/niminaction/Chapter2/explicit_discard.nim @@ -0,0 +1,7 @@ +discard """ + line: 7 + errormsg: "has to be discarded" +""" + +proc myProc(name: string): string = "Hello " & name +myProc("Dominik") \ No newline at end of file diff --git a/tests/niminaction/Chapter2/no_def_eq.nim b/tests/niminaction/Chapter2/no_def_eq.nim new file mode 100644 index 000000000..77f0a7dd8 --- /dev/null +++ b/tests/niminaction/Chapter2/no_def_eq.nim @@ -0,0 +1,16 @@ +discard """ + line: 16 + errormsg: "type mismatch" +""" + +type + Dog = object + name: string + + Cat = object + name: string + +let dog: Dog = Dog(name: "Fluffy") +let cat: Cat = Cat(name: "Fluffy") + +echo(dog == cat) \ No newline at end of file diff --git a/tests/niminaction/Chapter2/no_iterator.nim b/tests/niminaction/Chapter2/no_iterator.nim new file mode 100644 index 000000000..331d69480 --- /dev/null +++ b/tests/niminaction/Chapter2/no_iterator.nim @@ -0,0 +1,7 @@ +discard """ + line: 6 + errormsg: "type mismatch" +""" + +for i in 5: + echo i \ No newline at end of file diff --git a/tests/niminaction/Chapter2/no_seq_type.nim b/tests/niminaction/Chapter2/no_seq_type.nim new file mode 100644 index 000000000..493be270a --- /dev/null +++ b/tests/niminaction/Chapter2/no_seq_type.nim @@ -0,0 +1,6 @@ +discard """ + line: 6 + errormsg: "cannot infer the type of the sequence" +""" + +var list = @[] \ No newline at end of file diff --git a/tests/niminaction/Chapter2/resultaccept.nim b/tests/niminaction/Chapter2/resultaccept.nim new file mode 100644 index 000000000..7dd976b40 --- /dev/null +++ b/tests/niminaction/Chapter2/resultaccept.nim @@ -0,0 +1,28 @@ +discard """ + output: "" +""" + +# Page 35. + +proc implicit: string = + "I will be returned" + +proc discarded: string = + discard "I will not be returned" + +proc explicit: string = + return "I will be returned" + +proc resultVar: string = + result = "I will be returned" + +proc resultVar2: string = + result = "" + result.add("I will be ") + result.add("returned") + +doAssert implicit() == "I will be returned" +doAssert discarded() == nil +doAssert explicit() == "I will be returned" +doAssert resultVar() == "I will be returned" +doAssert resultVar2() == "I will be returned" \ No newline at end of file diff --git a/tests/niminaction/Chapter2/resultreject.nim b/tests/niminaction/Chapter2/resultreject.nim new file mode 100644 index 000000000..de59af7d9 --- /dev/null +++ b/tests/niminaction/Chapter2/resultreject.nim @@ -0,0 +1,33 @@ +discard """ + line: 27 + errormsg: "has to be discarded" +""" + +# Page 35. + +proc implicit: string = + "I will be returned" + +proc discarded: string = + discard "I will not be returned" + +proc explicit: string = + return "I will be returned" + +proc resultVar: string = + result = "I will be returned" + +proc resultVar2: string = + result = "" + result.add("I will be ") + result.add("returned") + +proc resultVar3: string = + result = "I am the result" + "I will cause an error" + +doAssert implicit() == "I will be returned" +doAssert discarded() == nil +doAssert explicit() == "I will be returned" +doAssert resultVar() == "I will be returned" +doAssert resultVar2() == "I will be returned" \ No newline at end of file diff --git a/tests/niminaction/Chapter2/various2.nim b/tests/niminaction/Chapter2/various2.nim new file mode 100644 index 000000000..3f6a3f453 --- /dev/null +++ b/tests/niminaction/Chapter2/various2.nim @@ -0,0 +1,369 @@ +discard """ + exitCode: 0 + outputsub: '''42 is greater than 0''' +""" + +if 42 >= 0: + echo "42 is greater than 0" + + +echo("Output: ", + 5) +echo(5 + + 5) +# --- Removed code that is supposed to fail here. Not going to test those. --- + +# Single-line comment +#[ +Multiline comment +]# +when false: + echo("Commented-out code") + +let decimal = 42 +let hex = 0x42 +let octal = 0o42 +let binary = 0b101010 + +let a: int16 = 42 +let b = 42'i8 + +let c = 1'f32 # --- Changed names here to avoid clashes --- +let d = 1.0e19 + +let e = false +let f = true + +let g = 'A' +let h = '\109' +let i = '\x79' + +let text = "The book title is \"Nim in Action\"" + +let filepath = "C:\\Program Files\\Nim" + +# --- Changed name here to avoid clashes --- +let filepath1 = r"C:\Program Files\Nim" + +let multiLine = """foo + bar + baz +""" +echo multiLine + +import strutils +# --- Changed name here to avoid clashes --- +let multiLine1 = """foo + bar + baz +""" +echo multiLine1.unindent +doAssert multiLine1.unindent == "foo\nbar\nbaz\n" + +proc fillString(): string = + result = "" + echo("Generating string") + for i in 0 .. 4: + result.add($i) #<1> + +const count = fillString() + +var + text1 = "hello" + number: int = 10 + isTrue = false + +var 火 = "Fire" +let ogień = true + +var `var` = "Hello" +echo(`var`) + +proc myProc(name: string): string = "Hello " & name +discard myProc("Dominik") + +proc bar(): int #<1> + +proc foo(): float = bar().float +proc bar(): int = foo().int + +proc noReturn() = echo("Hello") +proc noReturn2(): void = echo("Hello") + +proc noReturn3 = echo("Hello") + +proc message(recipient: string): auto = + "Hello " & recipient + +doAssert message("Dom") == "Hello Dom" + +proc max(a: int, b: int): int = + if a > b: a else: b + +doAssert max(5, 10) == 10 + +proc max2(a, b: int): int = + if a > b: a else: b + +proc genHello(name: string, surname = "Doe"): string = + "Hello " & name & " " & surname + +# -- Leaving these as asserts as that is in the original code, just in case +# -- somehow in the future `assert` is removed :) +assert genHello("Peter") == "Hello Peter Doe" +assert genHello("Peter", "Smith") == "Hello Peter Smith" + +proc genHello2(names: varargs[string]): string = + result = "" + for name in names: + result.add("Hello " & name & "\n") + +doAssert genHello2("John", "Bob") == "Hello John\nHello Bob\n" + +proc getUserCity(firstName, lastName: string): string = + case firstName + of "Damien": return "Tokyo" + of "Alex": return "New York" + else: return "Unknown" + +proc getUserCity(userID: int): string = + case userID + of 1: return "Tokyo" + of 2: return "New York" + else: return "Unknown" + +doAssert getUserCity("Damien", "Lundi") == "Tokyo" +doAssert getUserCity(2) == "New York" # -- Errata here: missing closing " + +import sequtils +let numbers = @[1, 2, 3, 4, 5, 6] +let odd = filter(numbers, proc (x: int): bool = x mod 2 != 0) +doAssert odd == @[1, 3, 5] + +import sequtils, future +let numbers1 = @[1, 2, 3, 4, 5, 6] +let odd1 = filter(numbers1, (x: int) -> bool => x mod 2 != 0) +assert odd1 == @[1, 3, 5] + +proc isValid(x: int, validator: proc (x: int): bool) = + if validator(x): echo(x, " is valid") + else: echo(x, " is NOT valid") + +import future +proc isValid2(x: int, validator: (x: int) -> bool) = + if validator(x): echo(x, " is valid") + else: echo(x, " is NOT valid") + +var list: array[3, int] +list[0] = 1 +list[1] = 42 +assert list[0] == 1 +assert list[1] == 42 +assert list[2] == 0 #<1> + +echo list.repr #<2> + +# echo list[500] + +var list2: array[-10 .. -9, int] +list2[-10] = 1 +list2[-9] = 2 + +var list3 = ["Hi", "There"] + +var list4 = ["My", "name", "is", "Dominik"] +for item in list4: + echo(item) + +for i in list4.low .. list4.high: + echo(list4[i]) + +var list5: seq[int] = @[] +doAssertRaises(IndexError): + list5[0] = 1 + +list5.add(1) + +assert list5[0] == 1 +doAssertRaises(IndexError): + echo list5[42] + +# -- Errata: var list: seq[int]; echo(list[0]). This now creates an exception, +# -- not a SIGSEGV. + +block: + var list = newSeq[string](3) + assert list[0] == nil + list[0] = "Foo" + list[1] = "Bar" + list[2] = "Baz" + + list.add("Lorem") + +block: + let list = @[4, 8, 15, 16, 23, 42] + for i in 0 .. <list.len: + stdout.write($list[i] & " ") + +var collection: set[int16] +doAssert collection == {} + +block: + let collection = {'a', 'x', 'r'} + doAssert 'a' in collection + +block: + let collection = {'a', 'T', 'z'} + let isAllLowerCase = {'A' .. 'Z'} * collection == {} + doAssert(not isAllLowerCase) + +let age = 10 +if age > 0 and age <= 10: + echo("You're still a child") +elif age > 10 and age < 18: + echo("You're a teenager") +else: + echo("You're an adult") + +let variable = "Arthur" +case variable +of "Arthur", "Zaphod", "Ford": + echo("Male") +of "Marvin": + echo("Robot") +of "Trillian": + echo("Female") +else: + echo("Unknown") + +let ageDesc = if age < 18: "Non-Adult" else: "Adult" + +block: + var i = 0 + while i < 3: + echo(i) + i.inc + +block label: + var i = 0 + while true: + while i < 5: + if i > 3: break label + i.inc + +iterator values(): int = + var i = 0 + while i < 5: + yield i + i.inc + +for value in values(): + echo(value) + +import os +for filename in walkFiles("*.nim"): + echo(filename) + +for item in @[1, 2, 3]: + echo(item) + +for i, value in @[1, 2, 3]: echo("Value at ", i, ": ", value) + +doAssertRaises(IOError): + proc second() = + raise newException(IOError, "Somebody set us up the bomb") + + proc first() = + second() + + first() + +block: + proc second() = + raise newException(IOError, "Somebody set us up the bomb") + + proc first() = + try: + second() + except: + echo("Cannot perform second action because: " & + getCurrentExceptionMsg()) + + first() + +block: + type + Person = object + name: string + age: int + + var person: Person + var person1 = Person(name: "Neo", age: 28) + +block: + type + PersonObj = object + name: string + age: int + PersonRef = ref PersonObj + + # proc setName(person: PersonObj) = + # person.name = "George" + + proc setName(person: PersonRef) = + person.name = "George" + +block: + type + Dog = object + name: string + + Cat = object + name: string + + let dog: Dog = Dog(name: "Fluffy") + let cat: Cat = Cat(name: "Fluffy") + +block: + type + Dog = tuple + name: string + + Cat = tuple + name: string + + let dog: Dog = (name: "Fluffy") + let cat: Cat = (name: "Fluffy") + + echo(dog == cat) + +block: + type + Point = tuple[x, y: int] + Point2 = (int, int) + + let pos: Point = (x: 100, y: 50) + doAssert pos == (100, 50) + + let pos1: Point = (x: 100, y: 50) + let (x, y) = pos1 #<1> + let (left, _) = pos1 + doAssert x == pos1[0] + doAssert y == pos1[1] + doAssert left == x + +block: + type + Color = enum + colRed, + colGreen, + colBlue + + let color: Color = colRed + +block: + type + Color {.pure.} = enum + red, green, blue + + let color = Color.red \ No newline at end of file diff --git a/tests/niminaction/Chapter3/various3.nim b/tests/niminaction/Chapter3/various3.nim new file mode 100644 index 000000000..478229b00 --- /dev/null +++ b/tests/niminaction/Chapter3/various3.nim @@ -0,0 +1,93 @@ +import threadpool +proc foo: string = "Dog" +var x: FlowVar[string] = spawn foo() +assert(^x == "Dog") + +block: + type + Box = object + case empty: bool + of false: + contents: string + else: + discard + + var obj = Box(empty: false, contents: "Hello") + assert obj.contents == "Hello" + + var obj2 = Box(empty: true) + doAssertRaises(FieldError): + echo(obj2.contents) + +import json +assert parseJson("null").kind == JNull +assert parseJson("true").kind == JBool +assert parseJson("42").kind == JInt +assert parseJson("3.14").kind == JFloat +assert parseJson("\"Hi\"").kind == JString +assert parseJson("""{ "key": "value" }""").kind == JObject +assert parseJson("[1, 2, 3, 4]").kind == JArray + +import json +let data = """ + {"username": "Dominik"} +""" + +let obj = parseJson(data) +assert obj.kind == JObject +assert obj["username"].kind == JString +assert obj["username"].str == "Dominik" + +block: + proc count10(): int = + for i in 0 .. <10: + result.inc + assert count10() == 10 + +type + Point = tuple[x, y: int] + +var point = (5, 10) +var point2 = (x: 5, y: 10) + +type + Human = object + name: string + age: int + +var jeff = Human(name: "Jeff", age: 23) +var amy = Human(name: "Amy", age: 20) + +import asyncdispatch + +var future = newFuture[int]() +doAssert(not future.finished) + +future.callback = + proc (future: Future[int]) = + echo("Future is no longer empty, ", future.read) + +future.complete(42) + +import asyncdispatch, asyncfile + +when false: + var file = openAsync("") + let dataFut = file.readAll() + dataFut.callback = + proc (future: Future[string]) = + echo(future.read()) + + asyncdispatch.runForever() + +import asyncdispatch, asyncfile, os + +proc readFiles() {.async.} = + # --- Changed to getTempDir here. + var file = openAsync(getTempDir() / "test.txt", fmReadWrite) + let data = await file.readAll() + echo(data) + await file.write("Hello!\n") + +waitFor readFiles() + diff --git a/tests/niminaction/Chapter3/various3.nim.cfg b/tests/niminaction/Chapter3/various3.nim.cfg new file mode 100644 index 000000000..6c1ded992 --- /dev/null +++ b/tests/niminaction/Chapter3/various3.nim.cfg @@ -0,0 +1 @@ +threads:on \ No newline at end of file diff --git a/tests/overflw/toverflw.nim b/tests/overflw/toverflw.nim index 771a43303..20bc56a53 100644 --- a/tests/overflw/toverflw.nim +++ b/tests/overflw/toverflw.nim @@ -1,21 +1,84 @@ discard """ file: "toverflw.nim" - output: "the computation overflowed" + output: "ok" + cmd: "nim $target -d:release $options $file" + """ # Tests nim's ability to detect overflows {.push overflowChecks: on.} var - a, b: int -a = high(int) -b = -2 + a = high(int) + b = -2 + overflowDetected = false + try: writeLine(stdout, b - a) except OverflowError: - writeLine(stdout, "the computation overflowed") + overflowDetected = true {.pop.} # overflow check -#OUT the computation overflowed + +doAssert(overflowDetected) + +block: # Overflow checks in a proc + var + a = high(int) + b = -2 + overflowDetected = false + + {.push overflowChecks: on.} + proc foo() = + let c = b - a + {.pop.} + + try: + foo() + except OverflowError: + overflowDetected = true + + doAssert(overflowDetected) + +block: # Overflow checks in a forward declared proc + var + a = high(int) + b = -2 + overflowDetected = false + + proc foo() + + {.push overflowChecks: on.} + proc foo() = + let c = b - a + {.pop.} + + try: + foo() + except OverflowError: + overflowDetected = true + + doAssert(overflowDetected) + +block: # Overflow checks doesn't affect fwd declaration + var + a = high(int) + b = -2 + overflowDetected = false + + {.push overflowChecks: on.} + proc foo() + {.pop.} + + proc foo() = + let c = b - a + + try: + foo() + except OverflowError: + overflowDetected = true + + doAssert(not overflowDetected) +echo "ok" diff --git a/tests/parser/ttypeclasses.nim b/tests/parser/ttypeclasses.nim index 9f487c7a8..46fd20686 100644 --- a/tests/parser/ttypeclasses.nim +++ b/tests/parser/ttypeclasses.nim @@ -1,17 +1,42 @@ -discard """ - action: run -""" - type R = ref V = var D = distinct P = ptr + T = type + S = static + OBJ = object + TPL = tuple + SEQ = seq +var i: int var x: ref int var y: distinct int var z: ptr int +const C = @[1, 2, 3] + +static: + assert x is ref + assert y is distinct + assert z is ptr + assert C is static + assert C[1] is static[int] + assert C[0] is static[SomeInteger] + assert C isnot static[string] + assert C is SEQ|OBJ + assert C isnot OBJ|TPL + assert int is int + assert int is T + assert int is SomeInteger + assert seq[int] is type + assert seq[int] is type[seq] + assert seq[int] isnot type[seq[float]] + assert i isnot type[int] + assert type(i) is type[int] + assert x isnot T + assert y isnot S + assert z isnot enum + assert x isnot object + assert y isnot tuple + assert z isnot seq -doAssert x is ref -doAssert y is distinct -doAssert z is ptr \ No newline at end of file diff --git a/tests/parser/ttypemodifiers.nim b/tests/parser/ttypemodifiers.nim new file mode 100644 index 000000000..2c322b44b --- /dev/null +++ b/tests/parser/ttypemodifiers.nim @@ -0,0 +1,527 @@ +discard """ +nimout: ''' +StmtList + TypeSection + TypeDef + Ident "BarePtr" + Empty + PtrTy + TypeDef + Ident "GenericPtr" + Empty + PtrTy + Bracket + Ident "int" + TypeDef + Ident "PrefixPtr" + Empty + PtrTy + Ident "int" + TypeDef + Ident "PtrTuple" + Empty + PtrTy + Par + Ident "int" + Ident "string" + TypeDef + Ident "BareRef" + Empty + RefTy + TypeDef + Ident "GenericRef" + Empty + RefTy + Bracket + Ident "int" + TypeDef + Ident "RefTupleCl" + Empty + RefTy + TupleTy + TypeDef + Ident "RefTupleType" + Empty + RefTy + Par + Ident "int" + Ident "string" + TypeDef + Ident "RefTupleVars" + Empty + RefTy + Par + Ident "a" + Ident "b" + TypeDef + Ident "BareStatic" + Empty + Ident "static" + TypeDef + Ident "GenericStatic" + Empty + BracketExpr + Ident "static" + Ident "int" + TypeDef + Ident "PrefixStatic" + Empty + Command + Ident "static" + Ident "int" + TypeDef + Ident "StaticTupleCl" + Empty + Command + Ident "static" + TupleClassTy + TypeDef + Ident "StaticTuple" + Empty + Command + Ident "static" + Par + Ident "int" + Ident "string" + TypeDef + Ident "BareType" + Empty + Ident "type" + TypeDef + Ident "GenericType" + Empty + BracketExpr + Ident "type" + Ident "float" + TypeDef + Ident "TypeTupleGen" + Empty + BracketExpr + Ident "type" + TupleClassTy + TypeDef + Ident "TypeTupleCl" + Empty + Command + Ident "type" + TupleClassTy + TypeDef + Ident "TypeInstance" + Empty + Command + Ident "type" + BracketExpr + Ident "Foo" + RefTy + TypeDef + Ident "bareTypeDesc" + Empty + Ident "typedesc" + TypeDef + Ident "TypeOfVar" + Empty + Call + Ident "type" + Ident "a" + TypeDef + Ident "TypeOfVarAlt" + Empty + Command + Ident "type" + Par + Ident "a" + TypeDef + Ident "TypeOfTuple1" + Empty + Call + Ident "type" + Ident "a" + TypeDef + Ident "TypeOfTuple2" + Empty + Call + Ident "type" + Ident "a" + Ident "b" + TypeDef + Ident "TypeOfTuple1A" + Empty + Command + Ident "type" + TupleConstr + Ident "a" + TypeDef + Ident "TypeOfTuple2A" + Empty + Command + Ident "type" + Par + Ident "a" + Ident "b" + TypeDef + Ident "TypeTuple" + Empty + Command + Ident "type" + Par + Ident "int" + Ident "string" + TypeDef + Ident "GenericTypedesc" + Empty + BracketExpr + Ident "typedesc" + Ident "int" + TypeDef + Ident "T" + Empty + Ident "type" + ProcDef + Ident "foo" + Empty + Empty + FormalParams + Ident "type" + IdentDefs + Ident "bareType" + Ident "type" + Empty + IdentDefs + Ident "genType" + BracketExpr + Ident "type" + Ident "int" + Empty + IdentDefs + Ident "typeInt" + Command + Ident "type" + Ident "int" + Empty + IdentDefs + Ident "typeIntAlt" + Call + Ident "type" + Ident "int" + Empty + IdentDefs + Ident "typeOfVar" + Call + Ident "type" + Ident "a" + Empty + IdentDefs + Ident "typeDotType" + DotExpr + Ident "foo" + Ident "type" + Empty + IdentDefs + Ident "typeTupleCl" + Command + Ident "type" + TupleClassTy + Empty + IdentDefs + Ident "bareStatic" + Ident "static" + Empty + IdentDefs + Ident "genStatic" + BracketExpr + Ident "static" + Ident "int" + Empty + IdentDefs + Ident "staticInt" + Command + Ident "static" + Ident "int" + Empty + IdentDefs + Ident "staticVal1" + Command + Ident "static" + IntLit 10 + Empty + IdentDefs + Ident "staticVal2" + Call + Ident "static" + StrLit "str" + Empty + IdentDefs + Ident "staticVal3" + Command + Ident "static" + StrLit "str" + Empty + IdentDefs + Ident "staticVal4" + CallStrLit + Ident "static" + RStrLit "str" + Empty + IdentDefs + Ident "staticDotVal" + DotExpr + IntLit 10 + Ident "static" + Empty + IdentDefs + Ident "bareRef" + RefTy + Empty + IdentDefs + Ident "refTuple1" + RefTy + Par + Ident "int" + Empty + IdentDefs + Ident "refTuple1A" + RefTy + TupleConstr + Ident "int" + Empty + IdentDefs + Ident "refTuple2" + RefTy + Par + Ident "int" + Ident "string" + Empty + IdentDefs + Ident "genRef" + RefTy + Bracket + Ident "int" + Empty + IdentDefs + Ident "refInt" + RefTy + Ident "int" + Empty + IdentDefs + Ident "refCall" + RefTy + Par + Ident "a" + Empty + IdentDefs + Ident "macroCall1" + Command + Ident "foo" + Ident "bar" + Empty + IdentDefs + Ident "macroCall2" + Call + Ident "foo" + Ident "bar" + Empty + IdentDefs + Ident "macroCall3" + Call + DotExpr + Ident "foo" + Ident "bar" + Ident "baz" + Empty + IdentDefs + Ident "macroCall4" + Call + BracketExpr + Ident "foo" + Ident "bar" + Ident "baz" + Empty + IdentDefs + Ident "macroCall5" + Command + Ident "foo" + Command + Ident "bar" + Ident "baz" + IntLit 10 + Empty + Empty + StmtList + Asgn + Ident "staticTen" + Command + Ident "static" + IntLit 10 + Asgn + Ident "staticA" + Call + Ident "static" + Ident "a" + Asgn + Ident "staticCall" + Command + Ident "static" + Call + Ident "foo" + IntLit 1 + Asgn + Ident "staticStrCall" + Command + Ident "static" + CallStrLit + Ident "foo" + RStrLit "x" + Asgn + Ident "staticChainCall" + Command + Ident "static" + Command + Ident "foo" + Ident "bar" + Asgn + Ident "typeTen" + Command + Ident "type" + IntLit 10 + Asgn + Ident "typeA" + Call + Ident "type" + Ident "a" + Asgn + Ident "typeCall" + Command + Ident "type" + Call + Ident "foo" + IntLit 1 + Asgn + Ident "typeStrCall" + Command + Ident "type" + CallStrLit + Ident "foo" + RStrLit "x" + Asgn + Ident "typeChainCall" + Command + Ident "type" + Command + Ident "foo" + Ident "bar" + Asgn + Ident "normalChainCall" + Command + Ident "foo" + Command + Ident "bar" + Ident "baz" + Asgn + Ident "normalTupleCall2" + Call + Ident "foo" + Ident "a" + Ident "b" + StaticStmt + StmtList + Ident "singleStaticStmt" + StaticStmt + StmtList + Ident "staticStmtList1" + Ident "staticStmtList2" +''' +""" + +import macros + +dumpTree: + type + BarePtr = ptr + GenericPtr = ptr[int] + PrefixPtr = ptr int + PtrTuple = ptr (int, string) + BareRef = ref + GenericRef = ref[int] + RefTupleCl = ref tuple + RefTupleType = ref (int, string) + RefTupleVars = ref (a, b) + BareStatic = static # Used to be Error: invalid indentation + GenericStatic = static[int] + PrefixStatic = static int + StaticTupleCl = static tuple + StaticTuple = static (int, string) + BareType = type + GenericType = type[float] + TypeTupleGen = type[tuple] + TypeTupleCl = type tuple # Used to be Error: invalid indentation + TypeInstance = type Foo[ref] + bareTypeDesc = typedesc + TypeOfVar = type(a) + TypeOfVarAlt = type (a) # Used to be Error: invalid indentation + TypeOfTuple1 = type(a,) + TypeOfTuple2 = type(a,b) + TypeOfTuple1A = type (a,) # Used to be Error: invalid indentation + TypeOfTuple2A = type (a,b) # Used to be Error: invalid indentation + TypeTuple = type (int, string) # Used to be Error: invalid indentation + GenericTypedesc = typedesc[int] + T = type + + proc foo( + bareType : type, + genType : type[int], + typeInt : type int, + typeIntAlt : type(int), + typeOfVar : type(a), + typeDotType : foo.type, + typeTupleCl : type tuple, # Used to be Error: ')' expected + bareStatic : static, # Used to be Error: expression expected, but found ',' + genStatic : static[int], + staticInt : static int, + staticVal1 : static 10, + staticVal2 : static("str"), + staticVal3 : static "str", + staticVal4 : static"str", # Used to be Error: expression expected, but found 'str' + staticDotVal : 10.static, + bareRef : ref, + refTuple1 : ref (int), + refTuple1A : ref (int,), + refTuple2 : ref (int,string), + genRef : ref[int], + refInt : ref int, + refCall : ref(a), + macroCall1 : foo bar, + macroCall2 : foo(bar), + macroCall3 : foo.bar(baz), + macroCall4 : foo[bar](baz), + macroCall5 : foo bar baz = 10 + ): type = + staticTen = static 10 + staticA = static(a) + # staticAspace = static (a) # With newTypedesc: Error: invalid indentation + # staticAtuple = static (a,) # With newTypedesc: Error: invalid indentation + # staticTuple = static (a,b) # With newTypedesc: Error: invalid indentation + # staticTypeTuple = static (int,string) # With newTypedesc: Error: invalid indentation + staticCall = static foo(1) + staticStrCall = static foo"x" + staticChainCall = static foo bar + + typeTen = type 10 + typeA = type(a) + # typeAspace = type (a) # Error: invalid indentation + # typeAtuple = type (a,) # Error: invalid indentation + # typeTuple = type (a,b) # Error: invalid indentation + # typeTypeTuple = type (int,string) # Error: invalid indentation + typeCall = type foo(1) + typeStrCall = type foo"x" + typeChainCall = type foo bar + + normalChainCall = foo bar baz + # normalTupleCall1 = foo(a,) # Error: invalid indentation + normalTupleCall2 = foo(a,b) + # normalTupleCall3 = foo (a,b) # Error: invalid indentation + + static: singleStaticStmt + static: + staticStmtList1 + staticStmtList2 + diff --git a/tests/pragmas/treorder.nim b/tests/pragmas/treorder.nim index 6a6bbff4d..1006af527 100644 --- a/tests/pragmas/treorder.nim +++ b/tests/pragmas/treorder.nim @@ -71,5 +71,4 @@ macro make(arg: untyped): untyped = proc first(i: int): void = make(second) -static: - var ss: string = "" \ No newline at end of file +var ss {.compileTime.}: string = "" \ No newline at end of file diff --git a/tests/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim new file mode 100644 index 000000000..789bd7588 --- /dev/null +++ b/tests/statictypes/tstatictypes.nim @@ -0,0 +1,109 @@ +discard """ +nimout: ''' +staticAlialProc instantiated with 358 +staticAlialProc instantiated with 368 +''' +output: ''' +16 +16 +b is 2 times a +17 +''' +""" + +import macros + +template ok(x) = assert(x) +template no(x) = assert(not x) + +template accept(x) = + static: assert(compiles(x)) + +template reject(x) = + static: assert(not compiles(x)) + +proc plus(a, b: int): int = a + b + +template isStatic(x: static): bool = true +template isStatic(x: auto): bool = false + +var v = 1 + +when true: + # test that `isStatic` works as expected + const C = 2 + + static: + ok C.isStatic + ok isStatic(plus(1, 2)) + ok plus(C, 2).isStatic + + no isStatic(v) + no plus(1, v).isStatic + +when true: + # test that proc instantiation works as expected + type + StaticTypeAlias = static[int] + + proc staticAliasProc(a: StaticTypeAlias, + b: static[int], + c: static int) = + static: + assert a.isStatic and b.isStatic and c.isStatic + assert isStatic(a + plus(b, c)) + echo "staticAlialProc instantiated with ", a, b, c + + when b mod a == 0: + echo "b is ", b div a, " times a" + + echo a + b + c + + staticAliasProc 1+2, 5, 8 + staticAliasProc 3, 2+3, 9-1 + staticAliasProc 3, 3+3, 4+4 + +when true: + # test static coercions. normal cases that should work: + accept: + var s1 = static[int] plus(1, 2) + var s2 = static(plus(1,2)) + var s3 = static plus(1,2) + var s4 = static[SomeInteger](1 + 2) + + # the sub-script operator can be used only with types: + reject: + var just_static3 = static[plus(1,2)] + + # static coercion takes into account the type: + reject: + var x = static[string](plus(1, 2)) + reject: + var x = static[string] plus(1, 2) + reject: + var x = static[SomeFloat] plus(3, 4) + + # you cannot coerce a run-time variable + reject: + var x = static(v) + +when true: + type + ArrayWrapper1[S: static int] = object + data: array[S + 1, int] + + ArrayWrapper2[S: static[int]] = object + data: array[S.plus(2), int] + + ArrayWrapper3[S: static[(int, string)]] = object + data: array[S[0], int] + + var aw1: ArrayWrapper1[5] + var aw2: ArrayWrapper2[5] + var aw3: ArrayWrapper3[(10, "str")] + + static: + assert aw1.data.high == 5 + assert aw2.data.high == 6 + assert aw3.data.high == 9 + diff --git a/tests/stdlib/tmemfiles2.nim b/tests/stdlib/tmemfiles2.nim index 7ea94cffc..d6cfa533a 100644 --- a/tests/stdlib/tmemfiles2.nim +++ b/tests/stdlib/tmemfiles2.nim @@ -4,9 +4,10 @@ discard """ Half read size: 10 Data: Hello''' """ import memfiles, os +const + fn = "test.mmap" var mm, mm_full, mm_half: MemFile - fn = "test.mmap" p: pointer if fileExists(fn): removeFile(fn) diff --git a/tests/stdlib/tmemmapstreams.nim b/tests/stdlib/tmemmapstreams.nim new file mode 100644 index 000000000..243574f1a --- /dev/null +++ b/tests/stdlib/tmemmapstreams.nim @@ -0,0 +1,53 @@ +discard """ + file: "tmemmapstreams.nim" + output: '''Created size: 10 +Position after writing: 5 +Position after writing one char: 6 +Peeked data: Hello +Position after peeking: 0 +Readed data: Hello! +Position after reading line: 7 +Position after setting position: 6 +Readed line: Hello! +Position after reading line: 7''' +""" +import os, streams, memfiles +const + fn = "test.mmapstream" +var + mms: MemMapFileStream + +if fileExists(fn): removeFile(fn) + +# Create a new memory mapped file, data all zeros +mms = newMemMapFileStream(fn, mode = fmReadWrite, fileSize = 10) +mms.close() +if fileExists(fn): echo "Created size: ", getFileSize(fn) + +# write, flush, peek, read +mms = newMemMapFileStream(fn, mode = fmReadWrite) +let s = "Hello" + +mms.write(s) +mms.flush +echo "Position after writing: ", mms.getPosition() +mms.write('!') +mms.flush +echo "Position after writing one char: ", mms.getPosition() +mms.close() + +mms = newMemMapFileStream(fn, mode = fmRead) +echo "Peeked data: ", mms.peekStr(s.len) +echo "Position after peeking: ", mms.getPosition() +echo "Readed data: ", mms.readLine +echo "Position after reading line: ", mms.getPosition() +mms.setPosition(mms.getPosition() - 1) +echo "Position after setting position: ", mms.getPosition() + +mms.setPosition(0) +echo "Readed line: ", mms.readLine +echo "Position after reading line: ", mms.getPosition() + +mms.close() + +if fileExists(fn): removeFile(fn) diff --git a/tests/stdlib/tpegs.nim b/tests/stdlib/tpegs.nim new file mode 100644 index 000000000..e5b709a66 --- /dev/null +++ b/tests/stdlib/tpegs.nim @@ -0,0 +1,78 @@ +discard """ + output: ''' +pkNonTerminal: Sum @(2, 3) + pkSequence: (Product (('+' / '-') Product)*) + pkNonTerminal: Product @(3, 7) + pkSequence: (Value (('*' / '/') Value)*) + pkNonTerminal: Value @(4, 5) + pkOrderedChoice: (([0-9] [0-9]*) / ('(' Expr ')')) + pkSequence: ([0-9] [0-9]*) + pkCharChoice: [0-9] + pkGreedyRepSet: [0-9]* + pkSequence: ('(' Expr ')') + pkChar: '(' + pkNonTerminal: Expr @(1, 4) + pkNonTerminal: Sum @(2, 3) + pkChar: ')' + pkGreedyRep: (('*' / '/') Value)* + pkSequence: (('*' / '/') Value) + pkOrderedChoice: ('*' / '/') + pkChar: '*' + pkChar: '/' + pkNonTerminal: Value @(4, 5) + pkGreedyRep: (('+' / '-') Product)* + pkSequence: (('+' / '-') Product) + pkOrderedChoice: ('+' / '-') + pkChar: '+' + pkChar: '-' + pkNonTerminal: Product @(3, 7) +''' +""" + +import strutils, streams +import pegs + +const + indent = " " + +let + pegSrc = """ +Expr <- Sum +Sum <- Product (('+' / '-') Product)* +Product <- Value (('*' / '/') Value)* +Value <- [0-9]+ / '(' Expr ')' + """ + pegAst: Peg = pegSrc.peg + +var + outp = newStringStream() + processed: seq[string] = @[] + +proc prt(outp: Stream, kind: PegKind, s: string; level: int = 0) = + outp.writeLine indent.repeat(level) & "$1: $2" % [$kind, s] + +proc recLoop(p: Peg, level: int = 0) = + case p.kind + of pkEmpty..pkWhitespace: + discard + of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: + outp.prt(p.kind, $p, level) + of pkChar, pkGreedyRepChar: + outp.prt(p.kind, $p, level) + of pkCharChoice, pkGreedyRepSet: + outp.prt(p.kind, $p, level) + of pkNonTerminal: + outp.prt(p.kind, + "$1 @($3, $4)" % [p.nt.name, $p.nt.rule.kind, $p.nt.line, $p.nt.col], level) + if not(p.nt.name in processed): + processed.add p.nt.name + p.nt.rule.recLoop level+1 + of pkBackRef..pkBackRefIgnoreStyle: + outp.prt(p.kind, $p, level) + else: + outp.prt(p.kind, $p, level) + for s in items(p): + s.recLoop level+1 + +pegAst.recLoop +echo outp.data \ No newline at end of file diff --git a/tests/stdlib/tstrutil.nim b/tests/stdlib/tstrutil.nim index 6f78a91ac..4d4081d39 100644 --- a/tests/stdlib/tstrutil.nim +++ b/tests/stdlib/tstrutil.nim @@ -7,6 +7,14 @@ discard """ import strutils +import macros + +template rejectParse(e) = + try: + discard e + raise newException(AssertionError, "This was supposed to fail: $#!" % astToStr(e)) + except ValueError: discard + proc testStrip() = write(stdout, strip(" ha ")) @@ -148,7 +156,6 @@ proc testDelete = delete(s, 0, 0) assert s == "1236789ABCDEFG" - proc testIsAlphaNumeric = assert isAlphaNumeric("abcdABC1234") == true assert isAlphaNumeric("a") == true @@ -203,10 +210,50 @@ proc testCountLines = assertCountLines("\nabc\n123") assertCountLines("\nabc\n123\n") +proc testParseInts = + # binary + assert "0b1111".parseBinInt == 15 + assert "0B1111".parseBinInt == 15 + assert "1111".parseBinInt == 15 + assert "1110".parseBinInt == 14 + assert "1_1_1_1".parseBinInt == 15 + assert "0b1_1_1_1".parseBinInt == 15 + rejectParse "".parseBinInt + rejectParse "_".parseBinInt + rejectParse "0b".parseBinInt + rejectParse "0b1234".parseBinInt + # hex + assert "0x72".parseHexInt == 114 + assert "0X72".parseHexInt == 114 + assert "#72".parseHexInt == 114 + assert "72".parseHexInt == 114 + assert "FF".parseHexInt == 255 + assert "ff".parseHexInt == 255 + assert "fF".parseHexInt == 255 + assert "0x7_2".parseHexInt == 114 + rejectParse "".parseHexInt + rejectParse "_".parseHexInt + rejectParse "0x".parseHexInt + rejectParse "0xFFG".parseHexInt + rejectParse "reject".parseHexInt + # octal + assert "0o17".parseOctInt == 15 + assert "0O17".parseOctInt == 15 + assert "17".parseOctInt == 15 + assert "10".parseOctInt == 8 + assert "0o1_0_0".parseOctInt == 64 + rejectParse "".parseOctInt + rejectParse "_".parseOctInt + rejectParse "0o".parseOctInt + rejectParse "9".parseOctInt + rejectParse "0o9".parseOctInt + rejectParse "reject".parseOctInt + testDelete() testFind() testRFind() testCountLines() +testParseInts() assert(insertSep($1000_000) == "1_000_000") assert(insertSep($232) == "232") diff --git a/tests/template/tdefined_overload.nim b/tests/template/tdefined_overload.nim new file mode 100644 index 000000000..bcc83e414 --- /dev/null +++ b/tests/template/tdefined_overload.nim @@ -0,0 +1,46 @@ +discard """ + output: "Valid and not defined" +""" +# test for issue #7997 +# checking for `when not defined` in a template for some compile time symbol +# results in a compilation error of: +# Error: obsolete usage of 'defined', use 'declared' instead +# if the symbol is 'overloaded' by some variable or procedure, because in +# that case the argument of `defined` is of kind `nkSym` instead of `nkIdent` +# (for which was checked in `semexprs.semDefined`). + +block: + # check whether a proc with the same name as the argument to `defined` + # compiles + proc overloaded() = + discard + + template definedCheck(): untyped = + when not defined(overloaded): true + else: false + doAssert definedCheck == true + +block: + # check whether a variable with the same name as the argument to `defined` + # compiles + var overloaded: int + + template definedCheck(): untyped = + when not defined(overloaded): true + else: false + doAssert definedCheck == true + +block: + # check whether a non overloaded when check still works properly + when not defined(validIdentifier): + echo "Valid and not defined" + +block: + # now check that invalid identifiers cause a compilation error + # by using reject template. + template reject(b) = + static: doAssert(not compiles(b)) + + reject: + when defined(123): + echo "Invalid identifier! Will not be echoed" diff --git a/tests/template/tpattern_with_converter.nim b/tests/template/tpattern_with_converter.nim new file mode 100644 index 000000000..e0632552b --- /dev/null +++ b/tests/template/tpattern_with_converter.nim @@ -0,0 +1,27 @@ +discard """ + output: 10.0 +""" + +type + MyFloat = object + val: float + +converter to_myfloat*(x: float): MyFloat {.inline.} = + MyFloat(val: x) + +proc `+`(x1, x2: MyFloat): MyFloat = + MyFloat(val: x1.val + x2.val) + +proc `*`(x1, x2: MyFloat): MyFloat = + MyFloat(val: x1.val * x2.val) + +template optMul{`*`(a, 2.0)}(a: MyFloat): MyFloat = + a + a + +func floatMyFloat(x: MyFloat): MyFloat = + result = x * 2.0 + +func floatDouble(x: float): float = + result = x * 2.0 + +echo floatDouble(5) \ No newline at end of file diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index 84e536636..9affbc159 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -266,8 +266,17 @@ proc testNimInAction(r: var TResults, cat: Category, options: string) = testSpec r, makeTest(filename, options, cat, actionCompile), targetCPP let tests = [ + "niminaction/Chapter1/various1", + "niminaction/Chapter2/various2", + "niminaction/Chapter2/resultaccept", + "niminaction/Chapter2/resultreject", + "niminaction/Chapter2/explicit_discard", + "niminaction/Chapter2/no_def_eq", + "niminaction/Chapter2/no_iterator", + "niminaction/Chapter2/no_seq_type", "niminaction/Chapter3/ChatApp/src/server", "niminaction/Chapter3/ChatApp/src/client", + "niminaction/Chapter3/various3", "niminaction/Chapter6/WikipediaStats/concurrency_regex", "niminaction/Chapter6/WikipediaStats/concurrency", "niminaction/Chapter6/WikipediaStats/naive", @@ -278,8 +287,34 @@ proc testNimInAction(r: var TResults, cat: Category, options: string) = "niminaction/Chapter7/Tweeter/src/tweeter", "niminaction/Chapter7/Tweeter/src/createDatabase", "niminaction/Chapter7/Tweeter/tests/database_test", - "niminaction/Chapter8/sdl/sdl_test", + "niminaction/Chapter8/sdl/sdl_test" ] + + # Verify that the files have not been modified. Death shall fall upon + # whoever edits these hashes without dom96's permission, j/k. But please only + # edit when making a conscious breaking change, also please try to make your + # commit message clear and notify me so I can easily compile an errata later. + var testHashes: seq[string] = @[] + + for test in tests: + testHashes.add(getMD5(readFile("tests" / test.addFileExt("nim")).string)) + + const refHashes = @[ + "51afdfa84b3ca3d810809d6c4e5037ba", "30f07e4cd5eaec981f67868d4e91cfcf", + "d14e7c032de36d219c9548066a97e846", "2e40bfd5daadb268268727da91bb4e81", + "c5d3853ed0aba04bf6d35ba28a98dca0", "058603145ff92d46c009006b06e5b228", + "7b94a029b94ddb7efafddd546c965ff6", "586d74514394e49f2370dfc01dd9e830", + "e1901837b757c9357dc8d259fd0ef0f6", "097670c7ae12e825debaf8ec3995227b", + "a8cb7b78cc78d28535ab467361db5d6e", "bfaec2816a1848991b530c1ad17a0184", + "47cb71bb4c1198d6d29cdbee05aa10b9", "87e4436809f9d73324cfc4f57f116770", + "7b7db5cddc8cf8fa9b6776eef1d0a31d", "e6e40219f0f2b877869b738737b7685e", + "6532ee87d819f2605a443d5e94f9422a", "9a8fe78c588d08018843b64b57409a02", + "03a801275b8b76b4170c870cd0da079d", "20bb7d3e2d38d43b0cb5fcff4909a4a8", + "af6844598f534fab6942abfa4dfe9ab2", "2a7a17f84f6503d9bc89a5ab8feea127" + ] + doAssert testHashes == refHashes, "Nim in Action tests were changed." + + # Run the tests. for testfile in tests: test "tests/" & testfile & ".nim", actionCompile @@ -291,6 +326,7 @@ proc testNimInAction(r: var TResults, cat: Category, options: string) = + # ------------------------- manyloc ------------------------------------------- #proc runSpecialTests(r: var TResults, options: string) = # for t in ["lib/packages/docutils/highlite"]: diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim index 0185156ec..0764b6363 100644 --- a/tests/testament/tester.nim +++ b/tests/testament/tester.nim @@ -129,7 +129,7 @@ proc callCCompiler(cmdTemplate, filename, options: string, let c = parseCmdLine(cmdTemplate % ["target", targetToCmd[target], "options", options, "file", filename.quoteShell, "filedir", filename.getFileDir()]) - var p = startProcess(command="gcc", args=c[5.. ^1], + var p = startProcess(command="gcc", args=c[5 .. ^1], options={poStdErrToStdOut, poUsePath}) let outp = p.outputStream var x = newStringOfCap(120) diff --git a/tests/types/t7905.nim b/tests/types/t7905.nim new file mode 100644 index 000000000..ef75bb86c --- /dev/null +++ b/tests/types/t7905.nim @@ -0,0 +1,33 @@ +discard """ + output: ''' +(member: "hello world") +(member: 123.456) +(member: "hello world", x: ...) +(member: 123.456, x: ...) +''' +""" + +template foobar(arg: typed): untyped = + type + MyType = object + member: type(arg) + + var myVar: MyType + myVar.member = arg + echo myVar + +foobar("hello world") +foobar(123.456'f64) + +template foobarRec(arg: typed): untyped = + type + MyType = object + member: type(arg) + x: ref MyType + + var myVar: MyType + myVar.member = arg + echo myVar + +foobarRec("hello world") +foobarRec(123.456'f64) diff --git a/tests/types/tisopr.nim b/tests/types/tisopr.nim index 2f9dbf245..f05f443de 100644 --- a/tests/types/tisopr.nim +++ b/tests/types/tisopr.nim @@ -5,7 +5,7 @@ false false true true -no''' +yes''' """ proc IsVoid[T](): string = diff --git a/tests/vm/tnilref.nim b/tests/vm/tnilref.nim new file mode 100644 index 000000000..5e27cf0cb --- /dev/null +++ b/tests/vm/tnilref.nim @@ -0,0 +1,7 @@ +discard """ + errormsg: "attempt to access a nil address" +""" + +static: + var s: ref int + s[] = 1 \ No newline at end of file diff --git a/tests/vm/tnimnode.nim b/tests/vm/tnimnode.nim index 188bac9bf..4210ab41d 100644 --- a/tests/vm/tnimnode.nim +++ b/tests/vm/tnimnode.nim @@ -4,14 +4,12 @@ proc assertEq(arg0,arg1: string): void = if arg0 != arg1: raiseAssert("strings not equal:\n" & arg0 & "\n" & arg1) -static: - # a simple assignment of stmtList to another variable - var node: NimNode - # an assignment of stmtList into an array - var nodeArray: array[1, NimNode] - # an assignment of stmtList into a seq - var nodeSeq = newSeq[NimNode](2) - +# a simple assignment of stmtList to another variable +var node {.compileTime.}: NimNode +# an assignment of stmtList into an array +var nodeArray {.compileTime.}: array[1, NimNode] +# an assignment of stmtList into a seq +var nodeSeq {.compileTime.} = newSeq[NimNode](2) proc checkNode(arg: NimNode; name: string): void {. compileTime .} = echo "checking ", name @@ -35,10 +33,10 @@ proc checkNode(arg: NimNode; name: string): void {. compileTime .} = echo "OK" -static: - # the root node that is used to generate the Ast - var stmtList: NimNode +# the root node that is used to generate the Ast +var stmtList {.compileTime.}: NimNode +static: stmtList = newStmtList(nnkDiscardStmt.newTree(newEmptyNode())) checkNode(stmtList, "direct construction") diff --git a/tests/vm/tref.nim b/tests/vm/tref.nim index 517a67fb0..27b7bf313 100644 --- a/tests/vm/tref.nim +++ b/tests/vm/tref.nim @@ -9,4 +9,49 @@ static: b[5] = 'c' doAssert a[] == "Hellocworld" - doAssert b[] == "Hellocworld" \ No newline at end of file + doAssert b[] == "Hellocworld" + + proc notGlobal() = + var + a: ref string + b: ref string + new a + + a[] = "Hello world" + b = a + + b[5] = 'c' + doAssert a[] == "Hellocworld" + doAssert b[] == "Hellocworld" + notGlobal() + +static: # bug 6081 + block: + type Obj = object + field: ref int + var i: ref int + new(i) + var r = Obj(field: i) + var rr = r + r.field = nil + doAssert rr.field != nil + + proc foo() = # Proc to avoid special global logic + var s: seq[ref int] + var i: ref int + new(i) + s.add(i) + var head = s[0] + s[0] = nil + doAssert head != nil + + foo() + +static: + + block: # global alias + var s: ref int + new(s) + var ss = s + s[] = 1 + doAssert ss[] == 1 \ No newline at end of file diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim index 4af824cf4..80f5aeee0 100644 --- a/tests/vm/tvmmisc.nim +++ b/tests/vm/tvmmisc.nim @@ -19,9 +19,9 @@ block: var x = default(type(0)) # #6379 -static: - import algorithm +import algorithm +static: var numArray = [1, 2, 3, 4, -1] numArray.sort(cmp) assert numArray == [-1, 1, 2, 3, 4] |