From a95316f78bf3a785c441c5832b5b11c262ef70ce Mon Sep 17 00:00:00 2001 From: cooldome Date: Thu, 26 Apr 2018 23:47:51 +0100 Subject: [PATCH 001/158] Implement float range types --- compiler/msgs.nim | 5 +- compiler/semtypes.nim | 4 +- compiler/sigmatch.nim | 33 +++++--- compiler/types.nim | 190 ++++++++++++++++++++++++++---------------- 4 files changed, 144 insertions(+), 88 deletions(-) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 5ae2c49700..e16a5cea2f 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -46,7 +46,8 @@ type errInvalidArgForX, errStmtHasNoEffect, errXExpectsTypeOrValue, errXExpectsArrayType, errIteratorCannotBeInstantiated, errExprXAmbiguous, errConstantDivisionByZero, errOrdinalTypeExpected, - errOrdinalOrFloatTypeExpected, errOverOrUnderflow, + errOrdinalOrFloatTypeExpected, errFloatTypeExpected, errStringTypeExpected, + errOverOrUnderflow, errCannotEvalXBecauseIncompletelyDefined, errChrExpectsRange0_255, errDynlibRequiresExportc, errUndeclaredFieldX, errNilAccess, errIndexOutOfBounds, errIndexTypesDoNotMatch, errBracketsInvalidForType, @@ -219,6 +220,8 @@ const errConstantDivisionByZero: "division by zero", errOrdinalTypeExpected: "ordinal type expected", errOrdinalOrFloatTypeExpected: "ordinal or float type expected", + errFloatTypeExpected: "float type expected", + errStringTypeExpected: "string type expected", errOverOrUnderflow: "over- or underflow", errCannotEvalXBecauseIncompletelyDefined: "cannot evaluate '$1' because type is not defined completely", errChrExpectsRange0_255: "\'chr\' expects an int in the range 0..255", diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 1fc263617b..0d1688d33b 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -205,8 +205,8 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = if not hasUnknownTypes: if not sameType(rangeT[0].skipTypes({tyRange}), rangeT[1].skipTypes({tyRange})): localError(n.info, errPureTypeMismatch) - elif not rangeT[0].isOrdinalType: - localError(n.info, errOrdinalTypeExpected) + elif not rangeT[0].isOrdinalType and rangeT[0].kind notin tyFloat..tyFloat128: + localError(n.info, errOrdinalOrFloatTypeExpected) elif enumHasHoles(rangeT[0]): localError(n.info, errEnumXHasHoles, rangeT[0].sym.name.s) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 96d815df72..cb821c02f2 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -628,20 +628,27 @@ 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 + template check_range_in(t: typedesc): untyped = + let + a0 = firstValue[t](a) + a1 = lastValue[t](a) + f0 = firstValue[t](f) + f1 = lastValue[t](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 + + if f.isOrdinalType: + check_range_in(BiggestInt) else: - result = isNone + check_range_in(BiggestFloat) + proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType = var diff --git a/compiler/types.nim b/compiler/types.nim index 5d60fa9b4e..19e61a3329 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -86,14 +86,33 @@ proc isPureObject*(typ: PType): bool = t = t.sons[0].skipTypes(skipPtrs) result = t.sym != nil and sfPure in t.sym.flags -proc getOrdValue*(n: PNode): BiggestInt = +proc getValue*[T:BiggestInt|BiggestFloat|string](n: PNode): T = + ## get value of liternal node case n.kind - of nkCharLit..nkUInt64Lit: result = n.intVal - of nkNilLit: result = 0 - of nkHiddenStdConv: result = getOrdValue(n.sons[1]) + of nkNilLit: reset(result) + of nkHiddenStdConv: result = getValue[T](n.sons[1]) else: - localError(n.info, errOrdinalTypeExpected) - result = 0 + when T is BiggestInt: + case n.kind + of nkCharLit..nkUInt64Lit: result = n.intVal + else: + localError(n.info, errOrdinalTypeExpected) + result = 0 + elif T is BiggestFloat: + case n.kind + of nkFloatLiterals: result = n.floatVal + else: + localError(n.info, errFloatTypeExpected) + result = NaN + else: + case n.kind: + of nkStrLit..nkTripleStrLit: result = n.strVal + else: + localError(n.info, errStringTypeExpected) + result = nil + +proc getOrdValue*(n: PNode): BiggestInt {.inline.} = + getValue[BiggestInt](n) proc isIntLit*(t: PType): bool {.inline.} = result = t.kind == tyInt and t.n != nil and t.n.kind == nkIntLit @@ -587,79 +606,106 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = result = typeToStr[t.kind] result.addTypeFlags(t) -proc firstOrd*(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 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 - else: result = 0x8000000000000000'i64 - of tyInt8: result = - 128 - of tyInt16: result = - 32768 - of tyInt32: result = - 2147483646 - 2 - of tyInt64: result = 0x8000000000000000'i64 - of tyUInt..tyUInt64: result = 0 - of tyEnum: - # if basetype <> nil then return firstOrd of basetype - if sonsLen(t) > 0 and t.sons[0] != nil: - result = firstOrd(t.sons[0]) +proc firstValue*[T:BiggestInt|BiggestFloat](t: PType): T = + case t.kind: + of tyVar: result = firstValue[T](t.sons[0]) + of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias: + result = firstValue[T](lastSon(t)) + of tyRange: + assert(t.n != nil) # range directly given: + assert(t.n.kind == nkRange) + result = getValue[T](t.n.sons[0]) else: - assert(t.n.sons[0].kind == nkSym) - result = t.n.sons[0].sym.position - of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias: - result = firstOrd(lastSon(t)) - of tyOrdinal: - if t.len > 0: result = firstOrd(lastSon(t)) - else: internalError("invalid kind for first(" & $t.kind & ')') - else: - internalError("invalid kind for first(" & $t.kind & ')') - result = 0 + when T is BiggestInt: + case t.kind: + of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyProxy: + result = 0 + of tySet, tyVar: result = firstValue[T](t.sons[0]) + of tyArray: result = firstValue[T](t.sons[0]) + of tyInt: + if platform.intSize == 4: result = - (2147483646) - 2 + else: result = 0x8000000000000000'i64 + of tyInt8: result = - 128 + of tyInt16: result = - 32768 + of tyInt32: result = - 2147483646 - 2 + of tyInt64: result = 0x8000000000000000'i64 + of tyUInt..tyUInt64: result = 0 + of tyEnum: + # if basetype <> nil then return firstOrd of basetype + if sonsLen(t) > 0 and t.sons[0] != nil: + result = firstValue[T](t.sons[0]) + else: + assert(t.n.sons[0].kind == nkSym) + result = t.n.sons[0].sym.position + of tyOrdinal: + if t.len > 0: result = firstValue[T](lastSon(t)) + else: internalError("invalid kind for first(" & $t.kind & ')') + else: + internalError("invalid kind for first(" & $t.kind & ')') + result = 0 -proc lastOrd*(t: PType; fixedUnsigned = false): BiggestInt = + elif T is BiggestFloat: + case t.kind: + of tyFloat..tyFloat128: result = -Inf + else: + internalError("invalid kind for first(" & $t.kind & ')') + result = NaN + +proc firstOrd*(t: PType): BiggestInt {.inline.} = + firstValue[BiggestInt](t) + +proc lastValue*[T:BiggestInt|BiggestFloat](t: PType; fixedUnsigned = false): T = 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 tyVar: result = lastValue[T](t.sons[0]) + of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias: + result = lastValue[T](lastSon(t)) 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 - 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 - elif fixedUnsigned: result = 0xFFFFFFFFFFFFFFFF'i64 - else: result = 0x7FFFFFFFFFFFFFFF'i64 - of tyUInt8: result = 0xFF - of tyUInt16: result = 0xFFFF - of tyUInt32: result = 0xFFFFFFFF - of tyUInt64: - if fixedUnsigned: result = 0xFFFFFFFFFFFFFFFF'i64 - else: result = 0x7FFFFFFFFFFFFFFF'i64 - 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: - result = lastOrd(lastSon(t)) - of tyProxy: result = 0 - of tyOrdinal: - if t.len > 0: result = lastOrd(lastSon(t)) - else: internalError("invalid kind for last(" & $t.kind & ')') + result = getValue[T](t.n.sons[1]) else: - internalError("invalid kind for last(" & $t.kind & ')') - result = 0 + when T is BiggestInt: + case t.kind + of tyBool: result = 1 + of tyChar: result = 255 + of tySet, tyArray: result = lastValue[T](t.sons[0]) + of tyInt: + if platform.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 + elif fixedUnsigned: result = 0xFFFFFFFFFFFFFFFF'i64 + else: result = 0x7FFFFFFFFFFFFFFF'i64 + of tyUInt8: result = 0xFF + of tyUInt16: result = 0xFFFF + of tyUInt32: result = 0xFFFFFFFF + of tyUInt64: + if fixedUnsigned: result = 0xFFFFFFFFFFFFFFFF'i64 + else: result = 0x7FFFFFFFFFFFFFFF'i64 + of tyEnum: + assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym) + result = t.n.sons[sonsLen(t.n) - 1].sym.position + + of tyProxy: result = 0 + of tyOrdinal: + if t.len > 0: result = lastValue[T](lastSon(t)) + else: internalError("invalid kind for last(" & $t.kind & ')') + else: + internalError("invalid kind for last(" & $t.kind & ')') + result = 0 + elif T is BiggestFloat: + case t.kind: + of tyFloat..tyFloat128: result = Inf + else: + internalError("invalid kind for first(" & $t.kind & ')') + result = NaN + +proc lastOrd*(t: PType; fixedUnsigned = false): BiggestInt {.inline.}= + lastValue[BiggestInt](t, fixedUnsigned) proc lengthOrd*(t: PType): BiggestInt = case t.kind From 0c9c1c013e9060ccafca9ac4c22031e687b6c984 Mon Sep 17 00:00:00 2001 From: cooldome Date: Sun, 29 Apr 2018 22:57:05 +0100 Subject: [PATCH 002/158] Add a test --- tests/float/tfloatrange.nim | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 tests/float/tfloatrange.nim diff --git a/tests/float/tfloatrange.nim b/tests/float/tfloatrange.nim new file mode 100644 index 0000000000..56aaf7450f --- /dev/null +++ b/tests/float/tfloatrange.nim @@ -0,0 +1,37 @@ +discard """ + cmd: "nim c -d:release --rangeChecks:on $file" + output: '''StrictPositiveRange +float +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" + + From b3a80dd2eb775367b52c1aed3f130cbdd51d36aa Mon Sep 17 00:00:00 2001 From: cooldome Date: Sun, 29 Apr 2018 23:17:07 +0100 Subject: [PATCH 003/158] update the doc --- changelog.md | 2 ++ compiler/types.nim | 2 +- doc/manual.rst | 10 +++++++--- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/changelog.md b/changelog.md index 8ac02a388a..ddfeb4d362 100644 --- a/changelog.md +++ b/changelog.md @@ -81,6 +81,8 @@ Imported exceptions can be raised and caught just like Nim exceptions. More details in language manual. +- Range float typed, 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/types.nim b/compiler/types.nim index 19e61a3329..bb6a132bb6 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -704,7 +704,7 @@ proc lastValue*[T:BiggestInt|BiggestFloat](t: PType; fixedUnsigned = false): T = internalError("invalid kind for first(" & $t.kind & ')') result = NaN -proc lastOrd*(t: PType; fixedUnsigned = false): BiggestInt {.inline.}= +proc lastOrd*(t: PType; fixedUnsigned = false): BiggestInt {.inline.} = lastValue[BiggestInt](t, fixedUnsigned) proc lengthOrd*(t: PType): BiggestInt = diff --git a/doc/manual.rst b/doc/manual.rst index 636bf796be..e67d8c0f46 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 float 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 From 0ef93bdea4dba268e7e30e31aab7922ee55168f7 Mon Sep 17 00:00:00 2001 From: cooldome Date: Sun, 29 Apr 2018 23:26:21 +0100 Subject: [PATCH 004/158] update test --- tests/float/tfloatrange.nim | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/float/tfloatrange.nim b/tests/float/tfloatrange.nim index 56aaf7450f..e8ea1912e3 100644 --- a/tests/float/tfloatrange.nim +++ b/tests/float/tfloatrange.nim @@ -3,6 +3,7 @@ discard """ output: '''StrictPositiveRange float range fail expected +range fail expected ''' """ import math, fenv @@ -21,7 +22,6 @@ proc myoverload(x: Positive) = proc myoverload(x: StrictPositive) = echo "StrictPositiveRange" - let x = 9.0.StrictPositive myoverload(x) myoverload(9.0) @@ -35,3 +35,15 @@ 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 From 2d755a6f13059629badedb3e8130e07201acd519 Mon Sep 17 00:00:00 2001 From: cooldome Date: Mon, 30 Apr 2018 23:59:32 +0100 Subject: [PATCH 005/158] fix spacing in changelog --- changelog.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 936b7fb3fa..5437232528 100644 --- a/changelog.md +++ b/changelog.md @@ -87,8 +87,7 @@ is now invalid. Internally a Nim string still has the trailing zero for zero-copy interoperability with ``cstring``. Compile your code with the next switch ``--laxStrings:on`` if you need a transition period. - - + - Range float types, example `range[0.0 .. Inf]`. More details in language manual. ### Tool changes From 635451591d74a66cf69d3837070dadab1030f357 Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 16 May 2018 02:01:55 +0200 Subject: [PATCH 006/158] extccomp: no globals anymore --- compiler/ccgstmts.nim | 14 ++--- compiler/ccgtypes.nim | 8 +-- compiler/cgen.nim | 12 ++-- compiler/commands.nim | 6 +- compiler/extccomp.nim | 142 ++++++++++++++++-------------------------- compiler/options.nim | 43 ++++++++++++- compiler/pragmas.nim | 4 +- 7 files changed, 118 insertions(+), 111 deletions(-) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 1e0a3c818b..d45acd7e82 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -477,7 +477,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] @@ -703,7 +703,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 @@ -711,7 +711,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])]) @@ -751,7 +751,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) @@ -967,7 +967,7 @@ 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) @@ -993,9 +993,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 diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 7b44cddad3..1056683df4 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -471,13 +471,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,7 +526,7 @@ 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) @@ -571,7 +571,7 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope, else: add(result, desc) add(result, "};" & tnl) - if tfPacked in typ.flags and hasAttribute notin CC[cCompiler].props: + if tfPacked in typ.flags and hasAttribute notin CC[m.config.cCompiler].props: result.add "#pragma pack(pop)" & tnl proc getTupleDesc(m: BModule, typ: PType, name: Rope, diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 133e86cea5..6bffc137fa 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -756,10 +756,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 +803,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)) @@ -938,7 +938,7 @@ proc getCopyright(conf: ConfigRef; cfile: Cfile): Rope = [rope(VersionAsString), rope(platform.OS[targetOS].name), rope(platform.CPU[targetCPU].name), - rope(extccomp.CC[extccomp.cCompiler].name), + rope(extccomp.CC[conf.cCompiler].name), rope(getCompileCFileCmd(conf, cfile))] proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope = diff --git a/compiler/commands.nim b/compiler/commands.nim index 09f63f0f5b..d1ffda34cd 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -565,13 +565,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) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 3f0e6f611f..f14193e2a0 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -16,12 +16,7 @@ import ropes, os, strutils, osproc, platform, condsyms, options, msgs, configuration, std / sha1, streams -#from debuginfo import writeDebugInfo - 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,38 +331,9 @@ 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 nameToCC*(name: string): TSystemCC = ## Returns the kind of compiler referred to by `name`, or ccNone ## if the name doesn't refer to any known compiler. @@ -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) @@ -499,8 +465,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 +474,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,11 +490,11 @@ 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 @@ -546,11 +512,11 @@ 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) @@ -565,10 +531,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) @@ -606,7 +572,7 @@ proc footprint(conf: ConfigRef; cfile: Cfile): SecureHash = $secureHashFile(cfile.cname) & platform.OS[targetOS].name & platform.CPU[targetCPU].name & - extccomp.CC[extccomp.cCompiler].name & + extccomp.CC[conf.cCompiler].name & getCompileCFileCmd(conf, cfile)) proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool = @@ -630,7 +596,7 @@ 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, @@ -661,21 +627,21 @@ proc getLinkCmd(conf: ConfigRef; projectfile, objfiles: string): string = libname = getCurrentDir() / libname else: libname = (libNameTmpl() % splitFile(conf.projectName).name) - result = CC[cCompiler].buildLib % ["libfile", libname, + 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 + builddll = CC[conf.cCompiler].buildDll else: exefile = splitFile(projectfile).name & platform.OS[targetOS].exeExt builddll = "" @@ -691,10 +657,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]) @@ -766,18 +732,18 @@ proc callCCompiler*(conf: ConfigRef; projectfile: string) = var prettyCmds: TStringSeq = @[] let prettyCb = proc (idx: int) = echo prettyCmds[idx] - compileCFile(conf, toCompile, script, cmds, prettyCmds) + 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)) @@ -824,7 +790,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 +814,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) @@ -894,13 +860,13 @@ 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)) diff --git a/compiler/options.nim b/compiler/options.nim index 2027897fa4..3570611500 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -73,6 +73,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 @@ -121,6 +122,19 @@ type SymbolFilesOption* = enum disabledSf, enabledSf, 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] + ConfigRef* = ref object ## eventually all global configuration should be moved here linesCompiled*: int # all lines that have been compiled options*: TOptions @@ -142,6 +156,7 @@ type helpWritten*: bool ideCmd*: IdeCmd oldNewlines*: bool + cCompiler*: TSystemCC enableNotes*: TNoteKinds disableNotes*: TNoteKinds foreignPackageNotes*: TNoteKinds @@ -173,6 +188,20 @@ 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 + const oldExperimentalFeatures* = {implicitDeref, dotOperators, callOperator, parallel} const @@ -195,6 +224,7 @@ template newPackageCache*(): untyped = proc newConfigRef*(): ConfigRef = result = ConfigRef( selectedGC: gcRefc, + cCompiler: ccGcc, verbosity: 1, options: DefaultOptions, globalOptions: DefaultGlobalOptions, @@ -221,7 +251,18 @@ proc newConfigRef*(): ConfigRef = 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: "" + docSeeSrcUrl: "", + 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: @[] ) # enable colors by default on terminals if terminal.isatty(stderr): diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index de98a5e42a..27c720d095 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -143,7 +143,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) @@ -444,7 +444,7 @@ proc processCompile(c: PContext, n: PNode) = extccomp.addExternalFileToCompile(c.config, found) 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 linkSys: From 479212995a615fc0a0827cc27756bb8cf865c1b0 Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 16 May 2018 02:05:00 +0200 Subject: [PATCH 007/158] ccgutils: code cleanup, no globals --- compiler/ccgutils.nim | 109 +----------------------------------------- 1 file changed, 1 insertion(+), 108 deletions(-) diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index a6080a8081..46b5d7cfa3 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -52,112 +52,7 @@ 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 = "\"" @@ -210,5 +105,3 @@ proc mangle*(name: string): string = requiresUnderscore = true if requiresUnderscore: result.add "_" - -initTypeTables() From 2a7fc84c86c48c6ca6354c8c2f9232c3f1a0b049 Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 16 May 2018 03:06:07 +0200 Subject: [PATCH 008/158] remove ast.emptyNode global; cleanup configuration.nim --- compiler/ast.nim | 10 +- compiler/cgmeth.nim | 6 +- compiler/configuration.nim | 178 ---------------------------------- compiler/destroyer.nim | 8 +- compiler/evalffi.nim | 4 +- compiler/evaltempl.nim | 2 +- compiler/lambdalifting.nim | 4 +- compiler/lowerings.nim | 22 +++-- compiler/modulegraphs.nim | 2 + compiler/parser.nim | 92 ++++++++++-------- compiler/plugins/itersgen.nim | 8 +- compiler/rodread.nim | 2 +- compiler/sem.nim | 2 +- compiler/semasgn.nim | 4 +- compiler/semexprs.nim | 29 +++--- compiler/semfields.nim | 2 +- compiler/seminst.nim | 2 +- compiler/semmagic.nim | 2 +- compiler/semparallel.nim | 2 +- compiler/sempass2.nim | 4 +- compiler/semstmts.nim | 16 +-- compiler/semtypes.nim | 2 +- compiler/sigmatch.nim | 9 +- compiler/suggest.nim | 2 +- compiler/syntaxes.nim | 4 +- compiler/transf.nim | 10 +- compiler/vm.nim | 6 +- compiler/vmdeps.nim | 22 ++--- compiler/vmgen.nim | 4 +- 29 files changed, 148 insertions(+), 312 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 5a84b2b021..ec727544e9 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1095,9 +1095,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 @@ -1229,13 +1226,10 @@ proc addSon*(father, son: PNode) = 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] diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim index 1d72952e20..c739381bba 100644 --- a/compiler/cgmeth.nim +++ b/compiler/cgmeth.nim @@ -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 diff --git a/compiler/configuration.nim b/compiler/configuration.nim index f9f0e623cb..bd9651c089 100644 --- a/compiler/configuration.nim +++ b/compiler/configuration.nim @@ -11,8 +11,6 @@ ## needs to be passed around to everything so that the compiler becomes ## more useful as a library. -import tables - const explanationsBaseUrl* = "https://nim-lang.org/docs/manual" @@ -182,179 +180,3 @@ const 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", -, -]# diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim index 31c7357941..0dc90b5529 100644 --- a/compiler/destroyer.nim +++ b/compiler/destroyer.nim @@ -132,6 +132,7 @@ 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? @@ -243,7 +244,7 @@ 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) @@ -253,7 +254,7 @@ proc registerDropBit(c: var Con; s: PSym) = let result = newSym(skTemp, getIdent(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) @@ -328,7 +329,7 @@ proc destructiveMoveVar(n: PNode; c: var Con): PNode = 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) @@ -434,6 +435,7 @@ proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode = 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/evalffi.nim b/compiler/evalffi.nim index 0e3d0609d3..e863c89954 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 01c56ec9c6..a1b5e731c9 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -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) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 773e5e29c5..2789e4b8f7 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -629,7 +629,7 @@ proc transformYield(n: PNode; owner: PSym; d: DetectionPass; addSon(a, retVal) retStmt.add(a) else: - retStmt.add(emptyNode) + retStmt.add(newNodeI(nkEmpty, n.info)) var stateLabelStmt = newNodeI(nkState, n.info) stateLabelStmt.add(newIntTypeNode(nkIntLit, stateNo, @@ -923,7 +923,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) diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index 13336f00e1..3f855c1eba 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -30,8 +30,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 = @@ -83,7 +83,7 @@ proc lowerTupleUnpackingForAsgn*(n: PNode; owner: PSym): PNode = 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) @@ -104,7 +104,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) @@ -344,8 +344,8 @@ proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; owner: PSym; typ: P 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): @@ -438,7 +438,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 @@ -454,12 +454,16 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym; let name = (if f.kind == nkSym: f.sym.name.s else: genPrefix) & "Wrapper" result = newSym(skProc, getIdent(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) diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 460d0b4a5c..d8fa4cedd7 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -50,6 +50,7 @@ type exposed*: TStrTable intTypeCache*: array[-5..64, PType] opContains*, opNot*: PSym + emptyNode*: PNode proc hash*(x: FileIndex): Hash {.borrow.} @@ -79,6 +80,7 @@ proc newModuleGraph*(config: ConfigRef = nil): ModuleGraph = initStrTable(result.exposed) result.opNot = createMagic(result, "not", mNot) result.opContains = createMagic(result, "contains", mInSet) + result.emptyNode = newNode(nkEmpty) proc resetAllModules*(g: ModuleGraph) = initStrTable(packageSyms) diff --git a/compiler/parser.nim b/compiler/parser.nim index fbc57ebb66..82e6549ed2 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -40,6 +40,7 @@ type tok*: TToken # The current token inPragma*: int # Pragma level inSemiStmtList*: int + emptyNode: PNode SymbolMode = enum smNormal, smAllowNil, smAfterDot @@ -93,6 +94,7 @@ proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream, 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; @@ -333,7 +335,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 +366,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: @@ -703,7 +705,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 = @@ -1015,7 +1017,7 @@ 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 let hasParLe = p.tok.tokType == tkParLe and p.tok.indent < 0 if hasParLe: getTok(p) @@ -1047,13 +1049,13 @@ 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 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 +1064,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 +1079,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: @@ -1244,8 +1248,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 +1268,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 +1432,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 +1576,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 +1594,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 +1602,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 +1633,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 @@ -1667,22 +1675,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 +1740,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 +1750,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 +1821,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 +1880,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 +1889,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 +1936,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 +1947,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 +1965,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 +1987,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 +2048,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 +2144,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 +2181,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/plugins/itersgen.nim b/compiler/plugins/itersgen.nim index ebb65dd4ad..7af97904e3 100644 --- a/compiler/plugins/itersgen.nim +++ b/compiler/plugins/itersgen.nim @@ -40,10 +40,10 @@ 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) diff --git a/compiler/rodread.nim b/compiler/rodread.nim index 52e7a924c6..86cac0d234 100644 --- a/compiler/rodread.nim +++ b/compiler/rodread.nim @@ -966,7 +966,7 @@ proc getBody*(s: PSym): PNode = ## 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 + if s.ast.isNil or bodyPos >= s.ast.len: return newNodeI(nkEmpty, s.info) result = s.ast.sons[bodyPos] if result == nil: assert s.offset != 0 diff --git a/compiler/sem.nim b/compiler/sem.nim index c5c3dd99bc..f3cc6288d2 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -594,7 +594,7 @@ proc myProcess(context: PPassContext, n: PNode): PNode = 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) diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim index f0fde195c6..4a923ef2b9 100644 --- a/compiler/semasgn.nim +++ b/compiler/semasgn.nim @@ -157,7 +157,7 @@ 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) @@ -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/semexprs.nim b/compiler/semexprs.nim index 6b32fa66c0..aa98bf9307 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -634,7 +634,7 @@ proc semStaticExpr(c: PContext, n: PNode): PNode = result = evalStaticExpr(c.module, c.cache, 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) @@ -745,7 +745,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = if 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 <" @@ -866,7 +866,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 +881,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 +938,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) @@ -1106,7 +1106,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 @@ -1489,7 +1489,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: @@ -1748,14 +1748,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) @@ -1913,7 +1916,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]) @@ -1944,10 +1947,10 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = 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) diff --git a/compiler/semfields.nim b/compiler/semfields.nim index 16d79c5590..b432ee9638 100644 --- a/compiler/semfields.nim +++ b/compiler/semfields.nim @@ -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/seminst.nim b/compiler/seminst.nim index a5f0ca7d81..e95b945535 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -328,7 +328,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. diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 8515e971dd..91bfc0b1d8 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -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) diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim index ea5aab6284..9e7aead13c 100644 --- a/compiler/semparallel.nim +++ b/compiler/semparallel.nim @@ -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 diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index b66d7d9f2a..2ba2a6ace2 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -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 3687e50e9c..fe1afb709f 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -394,7 +394,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] @@ -481,7 +481,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: @@ -1176,7 +1176,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: @@ -1199,7 +1199,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 @@ -1270,7 +1270,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 @@ -1613,7 +1613,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) @@ -1692,7 +1692,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 = @@ -1771,7 +1771,7 @@ proc semStaticStmt(c: PContext, n: PNode): PNode = 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 + result.sons[0] = c.graph.emptyNode proc usesResult(n: PNode): bool = # nkStmtList(expr) properly propagates the void context, diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 8b5c26f99b..69f1be0a1d 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -658,7 +658,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 diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 41cac2a4af..0df52d0aff 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1774,7 +1774,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 +1993,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) @@ -2354,7 +2357,7 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = proc argtypeMatches*(c: PContext, f, a: PType): 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 diff --git a/compiler/suggest.nim b/compiler/suggest.nim index 23aecfa71b..7d4eae3809 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -510,7 +510,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: diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim index 4bc153e464..109f90cb17 100644 --- a/compiler/syntaxes.nim +++ b/compiler/syntaxes.nim @@ -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) diff --git a/compiler/transf.nim b/compiler/transf.nim index a10e8a1e54..81085af96b 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -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 @@ -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") @@ -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) diff --git a/compiler/vm.nim b/compiler/vm.nim index dcb4cefd71..5eb4d70f34 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -213,7 +213,7 @@ proc putIntoNode(n: var PNode; x: TFullReg) = if nfIsRef in x.node.flags: n = x.node else: - let destIsRef = nfIsRef in n.flags + let destIsRef = nfIsRef in n.flags n[] = x.node[] # Ref-ness must be kept for the destination if destIsRef: @@ -1683,7 +1683,7 @@ proc myProcess(c: PPassContext, n: PNode): PNode = # 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 @@ -1703,7 +1703,7 @@ proc evalConstExprAux(module: PSym; cache: IdentCache; 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) diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index 2c92348a6b..5f3cc2d49d 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -54,7 +54,7 @@ proc objectNode(n: PNode): PNode = 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 newNodeI(nkEmpty, n.info) # no assigned value else: result = copyNode(n) for i in 0 ..< n.safeLen: @@ -69,7 +69,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo; template mapTypeToAstR(t,info): untyped = mapTypeToAstX(t, info, inst, true) template mapTypeToAst(t,i,info): untyped = if i 0: result.add objectNode(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.. Date: Thu, 17 May 2018 15:21:22 +0200 Subject: [PATCH 009/158] preparations of making compiler/msgs.nim free of global variables --- compiler/astalgo.nim | 92 +++++++++++++++++----------------- compiler/ccgexprs.nim | 2 +- compiler/ccgstmts.nim | 2 +- compiler/ccgtypes.nim | 6 +-- compiler/cgen.nim | 20 ++++---- compiler/commands.nim | 6 +-- compiler/dfa.nim | 2 +- compiler/docgen.nim | 8 +-- compiler/docgen2.nim | 6 +-- compiler/gorgeimpl.nim | 2 +- compiler/importer.nim | 4 +- compiler/jsgen.nim | 8 +-- compiler/lexer.nim | 2 +- compiler/lookups.nim | 2 +- compiler/lowerings.nim | 2 +- compiler/main.nim | 4 +- compiler/modulepaths.nim | 4 +- compiler/modules.nim | 8 +-- compiler/msgs.nim | 55 +++++++++----------- compiler/ndi.nim | 12 ++--- compiler/nimfix/pretty.nim | 10 ++-- compiler/nimfix/prettybase.nim | 18 +++---- compiler/passes.nim | 4 +- compiler/pragmas.nim | 6 +-- compiler/reorder.nim | 5 +- compiler/rodread.nim | 10 ++-- compiler/rodwrite.nim | 14 +++--- compiler/sem.nim | 2 +- compiler/semdata.nim | 2 +- compiler/semexprs.nim | 2 +- compiler/semmagic.nim | 2 +- compiler/semstmts.nim | 4 +- compiler/suggest.nim | 4 +- compiler/syntaxes.nim | 4 +- compiler/vm.nim | 8 +-- compiler/vmdeps.nim | 2 +- compiler/vmgen.nim | 6 +-- nimsuggest/nimsuggest.nim | 12 ++--- 38 files changed, 179 insertions(+), 183 deletions(-) diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index d98a42b34f..2ad1e24cba 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -15,13 +15,13 @@ import ast, hashes, intsets, strutils, options, msgs, ropes, idents, rodutils 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 +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 # ----------------------- node sets: --------------------------------------- proc objectSetContains*(t: TObjectSet, obj: RootRef): bool @@ -63,9 +63,9 @@ 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.} +proc debug*(conf: ConfigRef; n: PSym) {.deprecated.} +proc debug*(conf: ConfigRef; n: PType) {.deprecated.} +proc debug*(conf: ConfigRef; n: PNode) {.deprecated.} template mdbg*: bool {.dirty.} = when compiles(c.module): @@ -250,16 +250,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 +273,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 +281,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 +306,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 +322,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 +330,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 +344,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 +355,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 +385,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 +393,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 +409,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 +429,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,12 +440,12 @@ 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) = +proc debug(conf: ConfigRef; n: PSym) = if n == nil: echo("null") elif n.kind == skUnknown: @@ -454,13 +454,13 @@ proc debug(n: PSym) = #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]) + $lineInfoToStr(conf, n.info), $n.kind]) -proc debug(n: PType) = - echo($debugType(n)) +proc debug(conf: ConfigRef; n: PType) = + echo($debugType(conf, n)) -proc debug(n: PNode) = - echo($debugTree(n, 0, 100)) +proc debug(conf: ConfigRef; n: PNode) = + echo($debugTree(conf, n, 0, 100)) proc nextTry(h, maxHash: Hash): Hash = result = ((5 * h) + 1) and maxHash diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 335aa2f84a..8f4eefcb94 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -370,7 +370,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = #echo p.currLineInfo, " requesting" linefmt(p, cpsStmts, "#memTrackerWrite((void*)$1, $2, $3, $4);$n", addrLoc(dest), rope getSize(dest.t), - makeCString(p.currLineInfo.toFullPath), + makeCString(toFullPath(p.config, p.currLineInfo)), rope p.currLineInfo.safeLineNm) proc genDeepCopy(p: BProc; dest, src: TLoc) = diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index d45acd7e82..adbf6c7dff 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -1028,7 +1028,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) = diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 1056683df4..fb4ba86e28 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 diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 6bffc137fa..98223e9764 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -193,7 +193,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 @@ -212,12 +212,12 @@ proc genLineDir(p: BProc, t: PNode) = 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) + genCLineDir(p.s(cpsStmts), toFullPath(p.config, tt.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): linefmt(p, cpsStmts, "#endb($1, $2);$N", - line.rope, makeCString(toFilename(tt.info))) + line.rope, makeCString(toFilename(p.config, tt.info))) elif ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and (p.prc == nil or sfPure notin p.prc.flags) and tt.info.fileIndex != InvalidFileIDX: @@ -1249,7 +1249,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): @@ -1299,12 +1299,12 @@ 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) + result = rawNewModule(g, module, conf) growCache g.modules, module.position g.modules[module.position] = result @@ -1315,7 +1315,7 @@ template injectG() {.dirty.} = proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): 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 @@ -1358,7 +1358,7 @@ proc getCFile(m: BModule): string = proc myOpenCached(graph: ModuleGraph; module: PSym, rd: PRodReader): PPassContext = injectG() - var m = newModule(g, module) + var m = newModule(g, module, graph.config) readMergeInfo(getCFile(m), m) result = m diff --git a/compiler/commands.nim b/compiler/commands.nim index d1ffda34cd..b8ca918f95 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -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,7 +322,7 @@ 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) diff --git a/compiler/dfa.nim b/compiler/dfa.nim index 0fd7061781..95fbe28726 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -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 diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 7ab2f0eee1..73bed84959 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -190,7 +190,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 +307,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) @@ -502,7 +502,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") diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim index d9a73e1cd5..5e36cd356e 100644 --- a/compiler/docgen2.nim +++ b/compiler/docgen2.nim @@ -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 @@ -55,7 +55,7 @@ proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): 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.config) d.hasToc = true g.doc = d result = g diff --git a/compiler/gorgeimpl.nim b/compiler/gorgeimpl.nim index 44c7651bca..b175d23c50 100644 --- a/compiler/gorgeimpl.nim +++ b/compiler/gorgeimpl.nim @@ -22,7 +22,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/importer.nim b/compiler/importer.nim index 90e774a502..2dec05d66b 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -143,8 +143,8 @@ 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)) #echo "set back to ", L diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 25b554f7b0..697d01d868 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -537,7 +537,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") @@ -1907,7 +1907,7 @@ 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: @@ -2200,7 +2200,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)) @@ -2262,7 +2262,7 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = if passes.skipCodegen(m.config, n): return n if sfMainModule in m.module.flags: 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 = diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 5915619877..0a31f80495 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -717,7 +717,7 @@ proc handleCRLF(L: var TLexer, pos: int): int = 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) + addSourceLine(L.config, L.fileIdx, line) case L.buf[pos] of CR: diff --git a/compiler/lookups.nim b/compiler/lookups.nim index b6d63d0bd4..46617680ee 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -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) diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index 3f855c1eba..9d4401fc4e 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -120,7 +120,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("Env_" & toFilename(g.config, info)), owner, info, owner.options) incl s.flags, sfAnon s.typ = result diff --git a/compiler/main.nim b/compiler/main.nim index e3f00db9ef..b41cdc8d63 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -35,10 +35,10 @@ 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) = diff --git a/compiler/modulepaths.nim b/compiler/modulepaths.nim index daa55c2ba5..8d21fa7551 100644 --- a/compiler/modulepaths.nim +++ b/compiler/modulepaths.nim @@ -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 @@ -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 5d1eba1f26..09d5d60b5a 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -23,7 +23,7 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = new(result) result.id = -1 # for better error checking result.kind = skModule - let filename = fileIdx.toFullPath + let filename = toFullPath(graph.config, fileIdx) result.name = getIdent(splitFile(filename).name) if not isNimIdentifier(result.name.s): rawMessage(graph.config, errGenerated, "invalid module name: " & result.name.s) @@ -50,7 +50,9 @@ 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) @@ -73,7 +75,7 @@ proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; cache: IdentCache, f return else: discard - result.id = getModuleId(fileIdx, toFullPath(fileIdx)) + result.id = getModuleId(fileIdx, toFullPath(graph.config, fileIdx)) discard processModule(graph, result, if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil, rd, cache) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 533d3a57f0..4c02a134e2 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -157,13 +157,6 @@ 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) @@ -184,7 +177,7 @@ var errorOutputs* = {eStdOut, eStdErr} writelnHook*: proc (output: string) {.closure.} - structuredErrorHook*: proc (info: TLineInfo; msg: string; severity: Severity) {.closure.} + structuredErrorHook*: proc (config: ConfigRef; info: TLineInfo; msg: string; severity: Severity) {.closure.} proc concat(strings: openarray[string]): string = var totalLen = 0 @@ -234,26 +227,26 @@ proc getInfoContext*(index: int): TLineInfo = if i >=% L: result = unknownLineInfo() else: result = msgContext[i] -template toFilename*(fileIdx: FileIndex): string = +template toFilename*(conf: ConfigRef; fileIdx: FileIndex): string = (if fileIdx.int32 < 0: "???" else: fileInfos[fileIdx.int32].projPath) -proc toFullPath*(fileIdx: FileIndex): string = +proc toFullPath*(conf: ConfigRef; fileIdx: FileIndex): string = if fileIdx.int32 < 0: result = "???" else: result = 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 -proc setHash*(fileIdx: FileIndex; hash: string) = +proc setHash*(conf: ConfigRef; fileIdx: FileIndex; hash: string) = assert fileIdx.int32 >= 0 shallowCopy(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) -proc toFullPathConsiderDirty*(fileIdx: FileIndex): string = +proc toFullPathConsiderDirty*(conf: ConfigRef; fileIdx: FileIndex): string = if fileIdx.int32 < 0: result = "???" elif not fileInfos[fileIdx.int32].dirtyFile.isNil: @@ -261,11 +254,11 @@ proc toFullPathConsiderDirty*(fileIdx: FileIndex): string = else: result = 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: @@ -281,17 +274,17 @@ 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 = +proc `??`* (conf: ConfigRef; info: TLineInfo, filename: string): bool = # only for debugging purposes - result = filename in info.toFilename + result = filename in toFilename(conf, info) const trackPosInvalidFileIdx* = FileIndex(-2) # special marker so that no suggestions # are produced within comments and string literals @@ -428,7 +421,7 @@ proc writeContext(conf: ConfigRef; lastinfo: TLineInfo) = for i in countup(0, len(msgContext) - 1): if msgContext[i] != lastinfo and msgContext[i] != info: if structuredErrorHook != nil: - structuredErrorHook(msgContext[i], instantiationFrom, + structuredErrorHook(conf, msgContext[i], instantiationFrom, Severity.Error) else: styledMsgWriteln(styleBright, @@ -474,7 +467,7 @@ proc rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) = let s = msgKindToString(msg) % args if structuredErrorHook != nil: - structuredErrorHook(unknownLineInfo(), s & (if kind != nil: KindFormat % kind else: ""), sev) + structuredErrorHook(conf, unknownLineInfo(), s & (if kind != nil: KindFormat % kind else: ""), sev) if not ignoreMsgBecauseOfIdeTools(conf, msg): if kind != nil: @@ -548,7 +541,7 @@ proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, if not ignoreMsg: if structuredErrorHook != nil: - structuredErrorHook(info, s & (if kind != nil: KindFormat % kind else: ""), sev) + 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, @@ -600,7 +593,7 @@ template assertNotNil*(conf: ConfigRef; e): untyped = template internalAssert*(conf: ConfigRef, e: bool) = if not e: internalError(conf, $instantiationInfo()) -proc addSourceLine*(fileIdx: FileIndex, line: string) = +proc addSourceLine*(conf: ConfigRef; fileIdx: FileIndex, line: string) = fileInfos[fileIdx.int32].lines.add line.rope proc sourceLine*(conf: ConfigRef; i: TLineInfo): Rope = @@ -608,8 +601,8 @@ proc sourceLine*(conf: ConfigRef; i: TLineInfo): Rope = if not optPreserveOrigSource(conf) and fileInfos[i.fileIndex.int32].lines.len == 0: try: - for line in lines(i.toFullPath): - addSourceLine i.fileIndex, line.string + for line in lines(toFullPath(conf, i)): + addSourceLine conf, i.fileIndex, line.string except IOError: discard assert i.fileIndex.int32 < fileInfos.len diff --git a/compiler/ndi.nim b/compiler/ndi.nim index a7ca02193c..9708c388dc 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/nimfix/pretty.nim b/compiler/nimfix/pretty.nim index 96429ad535..2fa2a2c3d3 100644 --- a/compiler/nimfix/pretty.nim +++ b/compiler/nimfix/pretty.nim @@ -93,8 +93,8 @@ proc beautifyName(s: string, k: TSymKind): string = result.add s[i] inc i -proc replaceInFile(info: TLineInfo; newName: string) = - loadFile(info) +proc replaceInFile(conf: ConfigRef; info: TLineInfo; newName: string) = + loadFile(conf, info) let line = gSourceFiles[info.fileIndex.int].lines[info.line.int-1] var first = min(info.col.int, line.len) @@ -116,7 +116,7 @@ proc checkStyle(conf: ConfigRef; info: TLineInfo, s: string, k: TSymKind; sym: P if s != beau: if gStyleCheck == StyleCheck.Auto: sym.name = getIdent(beau) - replaceInFile(info, beau) + replaceInFile(conf, info, beau) else: message(conf, info, hintName, beau) @@ -136,7 +136,7 @@ template styleCheckDef*(info: TLineInfo; s: PSym) = template styleCheckDef*(s: PSym) = styleCheckDef(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 @@ -147,7 +147,7 @@ proc styleCheckUseImpl(info: TLineInfo; s: PSym) = 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/nimfix/prettybase.nim b/compiler/nimfix/prettybase.nim index c32dbe623f..89c48ae6ed 100644 --- a/compiler/nimfix/prettybase.nim +++ b/compiler/nimfix/prettybase.nim @@ -8,7 +8,7 @@ # import strutils, lexbase, streams -import ".." / [ast, msgs, idents] +import ".." / [ast, msgs, idents, options] from os import splitFile type @@ -21,14 +21,14 @@ type var gSourceFiles*: seq[TSourceFile] = @[] -proc loadFile*(info: TLineInfo) = +proc loadFile*(conf: ConfigRef; 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 + let path = toFullPath(conf, info) gSourceFiles[i].fullpath = path gSourceFiles[i].isNimfixFile = path.splitFile.ext == ".nimfix" # we want to die here for IOError: @@ -61,8 +61,8 @@ 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) +proc replaceDeprecated*(conf: ConfigRef; info: TLineInfo; oldSym, newSym: PIdent) = + loadFile(conf, info) let line = gSourceFiles[info.fileIndex.int32].lines[info.line.int-1] var first = min(info.col.int, line.len) @@ -79,11 +79,11 @@ proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PIdent) = gSourceFiles[info.fileIndex.int32].dirty = true #if newSym.s == "File": writeStackTrace() -proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PSym) = - replaceDeprecated(info, oldSym.name, newSym.name) +proc replaceDeprecated*(conf: ConfigRef; info: TLineInfo; oldSym, newSym: PSym) = + replaceDeprecated(conf, info, oldSym.name, newSym.name) -proc replaceComment*(info: TLineInfo) = - loadFile(info) +proc replaceComment*(conf: ConfigRef; info: TLineInfo) = + loadFile(conf, info) let line = gSourceFiles[info.fileIndex.int32].lines[info.line.int-1] var first = info.col.int diff --git a/compiler/passes.nim b/compiler/passes.nim index 8f9f57f3de..26e33185cf 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -151,7 +151,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: @@ -201,7 +201,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream, elif rd == nil: openPasses(graph, a, module, cache) 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) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 27c720d095..19431613c9 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -404,7 +404,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: @@ -426,7 +426,7 @@ 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, @@ -435,7 +435,7 @@ proc processCompile(c: PContext, n: PNode) = extccomp.addExternalFileToCompile(c.config, cf) 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: diff --git a/compiler/reorder.nim b/compiler/reorder.nim index d50be1d998..e4ba221e3e 100644 --- a/compiler/reorder.nim +++ b/compiler/reorder.nim @@ -155,7 +155,8 @@ 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 nnn = expandIncludes(graph, module, nn, modulePath, @@ -430,7 +431,7 @@ proc reorder*(graph: ModuleGraph, n: PNode, module: PSym, cache: IdentCache): PN 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 result = newNodeI(nkStmtList, n.info) diff --git a/compiler/rodread.nim b/compiler/rodread.nim index 86cac0d234..3eeee1127b 100644 --- a/compiler/rodread.nim +++ b/compiler/rodread.nim @@ -658,7 +658,7 @@ proc processRodFile(r: PRodReader, hash: SecureHash) = inc(r.pos, 2) # skip "(\10" inc(r.line) while r.s[r.pos] != ')': - w = r.files[decodeVInt(r.s, r.pos)].toFullPath + w = toFullPath(r.config, r.files[decodeVInt(r.s, r.pos)]) inc(r.pos) # skip ' ' inclHash = parseSecureHash(decodeStr(r.s, r.pos)) if r.reason == rrNone: @@ -865,11 +865,11 @@ proc loadMethods(r: PRodReader) = r.methods.add(rrGetSym(r, d, unknownLineInfo())) if r.s[r.pos] == ' ': inc(r.pos) -proc getHash*(fileIdx: FileIndex): SecureHash = +proc getHash*(conf: ConfigRef; fileIdx: FileIndex): SecureHash = if fileIdx.int32 <% gMods.len and gMods[fileIdx.int32].hashDone: return gMods[fileIdx.int32].hash - result = secureHashFile(fileIdx.toFullPath) + result = secureHashFile(toFullPath(conf, fileIdx)) if fileIdx.int32 >= gMods.len: setLen(gMods, fileIdx.int32+1) gMods[fileIdx.int32].hash = result @@ -882,8 +882,8 @@ proc checkDep(fileIdx: FileIndex; cache: IdentCache; conf: ConfigRef): TReasonFo 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) + let filename = toFilename(conf, fileIdx) + var hash = getHash(conf, 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) diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim index 4686baf2b7..a5574fdf0a 100644 --- a/compiler/rodwrite.nim +++ b/compiler/rodwrite.nim @@ -56,7 +56,7 @@ proc fileIdx(w: PRodWriter, filename: string): int = w.files[result] = filename template filename*(w: PRodWriter): string = - toFilename(FileIndex w.module.position) + toFilename(w.config, FileIndex w.module.position) proc newRodWriter(hash: SecureHash, module: PSym; cache: IdentCache; config: ConfigRef): PRodWriter = @@ -80,20 +80,20 @@ proc newRodWriter(hash: SecureHash, module: PSym; cache: IdentCache; result.converters = "" result.methods = "" result.init = "" - result.origFile = module.info.toFullPath + result.origFile = toFullPath(config, module.info) 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) + let resolved = findModule(w.config, dep, toFullPath(w.config, info)) 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) + let resolved = findModule(w.config, dep, toFullPath(w.config, info)) encodeVInt(fileIdx(w, resolved), w.inclDeps) add(w.inclDeps, " ") encodeStr($secureHashFile(resolved), w.inclDeps) @@ -130,7 +130,7 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode, result.add(',') encodeVInt(int n.info.line, result) result.add(',') - encodeVInt(fileIdx(w, toFullPath(n.info)), result) + encodeVInt(fileIdx(w, toFullPath(w.config, n.info)), result) elif fInfo.line != n.info.line: result.add('?') encodeVInt(n.info.col, result) @@ -308,7 +308,7 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) = result.add(',') if s.info.line != 0'u16: encodeVInt(int s.info.line, result) result.add(',') - encodeVInt(fileIdx(w, toFullPath(s.info)), result) + encodeVInt(fileIdx(w, toFullPath(w.config, s.info)), result) if s.owner != nil: result.add('*') encodeVInt(s.owner.id, result) @@ -645,7 +645,7 @@ proc process(c: PPassContext, n: PNode): PNode = 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) + var w = newRodWriter(rodread.getHash(g.config, FileIndex module.position), module, cache, g.config) rawAddInterfaceSym(w, module) result = w diff --git a/compiler/sem.nim b/compiler/sem.nim index f3cc6288d2..8b616bd84f 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -599,7 +599,7 @@ proc myProcess(context: PPassContext, n: PNode): PNode = rod.storeNode(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" diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 12ac19ca3a..392f00c018 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -148,7 +148,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 diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index aa98bf9307..c4edd6252e 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1940,7 +1940,7 @@ 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)))) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 91bfc0b1d8..caf821ab5b 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -78,7 +78,7 @@ proc semInstantiationInfo(c: PContext, n: PNode): PNode = let useFullPaths = expectIntLit(c, n.sons[2]) let info = getInfoContext(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)) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index fe1afb709f..2faf6b39ba 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1056,7 +1056,7 @@ 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) gatherStmts c, code, result @@ -1738,7 +1738,7 @@ 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))) excl(c.includedFiles, f.int) diff --git a/compiler/suggest.nim b/compiler/suggest.nim index 7d4eae3809..0700b4140f 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -143,7 +143,7 @@ proc symToSuggest(conf: ConfigRef; s: PSym, isLocal: bool, section: IdeCmd, info when 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) @@ -342,7 +342,7 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) when defined(nimsuggest): if n.kind == nkSym and n.sym.kind == skError and 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 diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim index 109f90cb17..cb12629b4e 100644 --- a/compiler/syntaxes.nim +++ b/compiler/syntaxes.nim @@ -142,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) @@ -160,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/vm.nim b/compiler/vm.nim index 5eb4d70f34..c81f97e43a 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -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. @@ -1358,7 +1358,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 +1373,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 +1392,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: diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index 5f3cc2d49d..45d6b81c3c 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -11,7 +11,7 @@ import ast, types, msgs, os, streams, options, idents 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) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 177ddc1b94..12484ed100 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -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 diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim index 5c505ddf77..bea33684a6 100644 --- a/nimsuggest/nimsuggest.nim +++ b/nimsuggest/nimsuggest.nim @@ -74,8 +74,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)) @@ -173,8 +173,8 @@ proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int; 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 @@ -432,7 +432,7 @@ 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) + for cm in cachedMsgs: errorHook(conf, cm.info, cm.msg, cm.sev) execute(conf.ideCmd, orig, dirtyfile, line, col, graph, cache) sentinel() @@ -477,7 +477,7 @@ proc mainThread(graph: ModuleGraph; cache: IdentCache) = conf.ideCmd = ideChk msgs.writelnHook = proc (s: string) = discard cachedMsgs.setLen 0 - msgs.structuredErrorHook = proc (info: TLineInfo; msg: string; sev: Severity) = + msgs.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) From b2c7910fb60528520d71686ac7c80a30553f9320 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 17 May 2018 15:45:41 +0200 Subject: [PATCH 010/158] compiler/ropes.nim has no global error handler anymore --- compiler/cgen.nim | 18 ++++++++++++------ compiler/depends.nim | 6 ++---- compiler/docgen.nim | 20 ++++++++++++++++---- compiler/extccomp.nim | 13 +++++++++---- compiler/msgs.nim | 7 ------- compiler/ropes.nim | 26 +++++++++----------------- 6 files changed, 48 insertions(+), 42 deletions(-) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 98223e9764..666b39ec82 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -19,7 +19,8 @@ import import strutils except `%` # collides with ropes.`%` from modulegraphs import ModuleGraph -from configuration import warnGcMem, errXMustBeCompileTime, hintDependency, errGenerated +from configuration import + warnGcMem, errXMustBeCompileTime, hintDependency, errGenerated, errCannotOpenFile import dynlib when not declared(dynlib.libCandidates): @@ -1347,7 +1348,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 = @@ -1395,12 +1397,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 @@ -1435,7 +1439,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 @@ -1456,7 +1461,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) diff --git a/compiler/depends.nim b/compiler/depends.nim index 7324042328..38b84aafc0 100644 --- a/compiler/depends.nim +++ b/compiler/depends.nim @@ -14,8 +14,6 @@ import from modulegraphs import ModuleGraph -proc generateDot*(project: string) - type TGen = object of TPassContext module*: PSym @@ -44,8 +42,8 @@ proc addDotDependency(c: PPassContext, n: PNode): PNode = else: discard -proc generateDot(project: string) = - writeRope("digraph $1 {$n$2}$n" % [ +proc generateDot*(project: string) = + discard writeRope("digraph $1 {$n$2}$n" % [ rope(changeFileExt(extractFilename(project), "")), gDotGraph], changeFileExt(project, "dot")) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 73bed84959..c40d524d85 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -777,10 +777,16 @@ 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 + var filename: string if optStdout in d.conf.globalOptions: writeRope(stdout, content) + filename = "" else: - writeRope(content, getOutFile2(d.conf, filename, outExt, "htmldocs"), useWarning) + filename = getOutFile2(d.conf, filename, outExt, "htmldocs") + success = writeRope(content, filename) + if not success: + rawMessage(d.conf, if useWarning: warnCannotOpenFile else: errCannotOpenFile, filename) proc writeOutputJson*(d: PDoc, filename, outExt: string, useWarning = false) = @@ -861,7 +867,9 @@ 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) @@ -876,7 +884,9 @@ 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) = var content = mergeIndexes(conf.projectFull).rope @@ -887,4 +897,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/extccomp.nim b/compiler/extccomp.nim index f14193e2a0..18d198e11f 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -438,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[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") @@ -872,4 +875,6 @@ proc writeMapping*(conf: ConfigRef; symbolMapping: Rope) = 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/msgs.nim b/compiler/msgs.nim index 4c02a134e2..b3256e3bbc 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -618,13 +618,6 @@ proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope = 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) - proc listWarnings*(conf: ConfigRef) = msgWriteln(conf, "Warnings:") for warn in warnMin..warnMax: diff --git a/compiler/ropes.nim b/compiler/ropes.nim index 05d5e840c3..713d5c8a21 100644 --- a/compiler/ropes.nim +++ b/compiler/ropes.nim @@ -72,15 +72,6 @@ type 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 @@ -204,13 +195,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. @@ -254,7 +246,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,10 +257,11 @@ 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': @@ -278,7 +271,7 @@ proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope = add(result, rnl) 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 +343,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 From a4e2b0c1538134ebf105b669da13db13b5356998 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 18 May 2018 15:18:00 +0200 Subject: [PATCH 011/158] platform.nim doesn't use globals anymore; prepare msgs.nim for not using globals --- compiler/ccgcalls.nim | 30 +++---- compiler/ccgexprs.nim | 146 +++++++++++++++---------------- compiler/ccgmerge.nim | 32 +++---- compiler/ccgstmts.nim | 24 +++--- compiler/ccgtrav.nim | 3 +- compiler/ccgtypes.nim | 140 +++++++++--------------------- compiler/ccgutils.nim | 4 +- compiler/cgen.nim | 87 ++++++++++--------- compiler/commands.nim | 36 ++++---- compiler/extccomp.nim | 32 +++---- compiler/guards.nim | 24 +++--- compiler/jsgen.nim | 32 +++---- compiler/jstypes.nim | 12 +-- compiler/lexer.nim | 4 +- compiler/lowerings.nim | 4 +- compiler/magicsys.nim | 4 +- compiler/main.nim | 2 +- compiler/msgs.nim | 2 +- compiler/nimsets.nim | 55 ++++++------ compiler/options.nim | 42 ++++----- compiler/platform.nim | 48 +++++------ compiler/ropes.nim | 9 +- compiler/semasgn.nim | 2 +- compiler/semexprs.nim | 14 +-- compiler/semfold.nim | 62 ++++++------- compiler/semmagic.nim | 6 +- compiler/semparallel.nim | 4 +- compiler/semstmts.nim | 6 +- compiler/semtypes.nim | 92 ++++++++++---------- compiler/semtypinst.nim | 2 +- compiler/sigmatch.nim | 32 ++++--- compiler/transf.nim | 8 +- compiler/types.nim | 182 +++++++++++++-------------------------- compiler/vm.nim | 20 ++--- compiler/vmgen.nim | 10 +-- 35 files changed, 536 insertions(+), 676 deletions(-) diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 7d355db5fd..43a9f570a4 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 @@ -102,7 +102,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: @@ -129,13 +129,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)) @@ -152,9 +152,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) @@ -164,7 +164,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: @@ -231,7 +231,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): @@ -241,12 +241,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: @@ -509,20 +509,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 8f4eefcb94..f26526b059 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -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) @@ -354,10 +354,10 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($1[0])*$1Len_0);$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, @@ -369,7 +369,7 @@ 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), + addrLoc(p.config, dest), rope getSize(p.config, dest.t), makeCString(toFullPath(p.config, p.currLineInfo)), rope p.currLineInfo.safeLineNm) @@ -379,31 +379,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) 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, @@ -489,15 +489,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 @@ -545,8 +545,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 @@ -602,8 +602,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)]) @@ -656,7 +656,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.} = @@ -665,7 +665,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? @@ -724,12 +724,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 @@ -844,21 +844,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, @@ -880,10 +880,10 @@ proc genIndexCheck(p: BProc; arr, idx: TLoc) = linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)) #raiseIndexError();$n", rdLoc(idx), 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 ($1 < $2 || $1 > $3) #raiseIndexError();$n", - rdCharLoc(idx), first, intLiteral(lastOrd(ty))) + rdCharLoc(idx), first, intLiteral(lastOrd(p.config, ty))) of tySequence, tyString: linefmt(p, cpsStmts, "if (!$2 || (NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n", @@ -978,7 +978,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 @@ -1000,7 +1000,7 @@ proc genEcho(p: BProc, n: PNode) = when false: p.module.includeHeader("") 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) = @@ -1114,7 +1114,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) = @@ -1299,7 +1299,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: @@ -1413,7 +1413,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 @@ -1424,7 +1424,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), @@ -1437,7 +1437,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) @@ -1491,8 +1491,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) = @@ -1530,19 +1530,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! @@ -1550,10 +1550,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)") @@ -1565,11 +1565,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 @@ -1613,7 +1613,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 @@ -1680,7 +1680,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) @@ -1917,7 +1917,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", @@ -1929,14 +1929,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: @@ -1945,13 +1945,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 @@ -2069,7 +2069,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: @@ -2290,7 +2290,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) @@ -2360,7 +2360,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) @@ -2457,8 +2457,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 4b4f9c0e68..664f89b73b 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 adbf6c7dff..dd0e80f0e6 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)] @@ -168,7 +168,7 @@ proc genGotoState(p: BProc, n: PNode) = lineF(p, cpsStmts, "switch ($1) {$n", [rdLoc(a)]) p.beforeRetNeeded = true lineF(p, cpsStmts, "case -1: 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 @@ -227,7 +227,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 @@ -401,12 +401,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: @@ -650,7 +650,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]) @@ -974,14 +974,14 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope = if x[j] in {'"', ':'}: # don't modify the line if already in quotes or # some clobber register list: - add(result, x); add(result, tnl) + add(result, x); add(result, "\L") elif x[j] != '\0': # 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) = @@ -1037,7 +1037,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) = @@ -1068,7 +1068,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/ccgtrav.nim b/compiler/ccgtrav.nim index c265064a19..4514ce7dc7 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 fb4ba86e28..1a7cd1dad9 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -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: @@ -529,7 +516,7 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope, 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) + add(result, "};\L") if tfPacked in typ.flags and hasAttribute notin CC[m.config.cCompiler].props: - result.add "#pragma pack(pop)" & tnl + 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 @@ -866,11 +806,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) @@ -1006,7 +946,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 +970,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 +1080,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 +1106,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 +1130,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 46b5d7cfa3..f481e4d632 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 diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 666b39ec82..89d8ed936f 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -121,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: @@ -212,7 +212,7 @@ proc genLineDir(p: BProc, t: PNode) = let line = tt.info.safeLineNm if optEmbedOrigSrc in p.config.globalOptions: - add(p.s(cpsStmts), ~"//" & sourceLine(p.config, tt.info) & rnl) + add(p.s(cpsStmts), ~"//" & sourceLine(p.config, tt.info) & "\L") genCLineDir(p.s(cpsStmts), toFullPath(p.config, tt.info), line, p.config) if ({optStackTrace, optEndb} * p.options == {optStackTrace, optEndb}) and (p.prc == nil or sfPure notin p.prc.flags): @@ -250,9 +250,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 = @@ -282,7 +282,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 @@ -311,10 +311,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) @@ -323,7 +323,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) @@ -340,7 +340,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) = @@ -387,7 +387,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, @@ -415,7 +415,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) @@ -652,33 +652,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 @@ -728,7 +728,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) @@ -742,7 +742,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: @@ -919,10 +919,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: @@ -937,8 +937,8 @@ 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(platform.OS[conf.target.targetOS].name), + rope(platform.CPU[conf.target.targetCPU].name), rope(extccomp.CC[conf.cCompiler].name), rope(getCompileCFileCmd(conf, cfile))] @@ -1048,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 @@ -1057,13 +1057,13 @@ proc genMainProc(m: BModule) = nimMain = WinNimDllMain otherMain = WinCDllMain m.includeHeader("") - elif platform.targetOS == osGenode: + elif m.config.target.targetOS == osGenode: nimMain = GenodeNimMain otherMain = ComponentConstruct 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: @@ -1074,12 +1074,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, @@ -1089,7 +1089,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() @@ -1370,7 +1370,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) = diff --git a/compiler/commands.nim b/compiler/commands.nim index b8ca918f95..50bc6a7708 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -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 = @@ -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": @@ -592,17 +590,17 @@ 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) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 18d198e11f..45db8d6bfa 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -331,8 +331,8 @@ const hExt* = ".h" -proc libNameTmpl(): string {.inline.} = - result = if targetOS == osWindows: "$1.lib" else: "lib$1.a" +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 @@ -355,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: @@ -439,7 +439,7 @@ proc execExternalProgram*(conf: ConfigRef; cmd: string, msg = hintExecuting) = proc generateScript(conf: ConfigRef; projectFile: string, script: Rope) = let (dir, name, ext) = splitFile(projectFile) let filename = getNimcacheDir(conf) / addFileExt("compile_" & name, - platform.OS[targetOS].scriptExt) + platform.OS[conf.target.targetOS].scriptExt) if writeRope(script, filename): copyFile(conf.libpath / "nimbase.h", getNimcacheDir(conf) / "nimbase.h") else: @@ -500,8 +500,8 @@ proc getLinkOptions(conf: ConfigRef): string = 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"): @@ -526,7 +526,7 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile): string = 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 @@ -573,8 +573,8 @@ 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 & + platform.OS[conf.target.targetOS].name & + platform.CPU[conf.target.targetCPU].name & extccomp.CC[conf.cCompiler].name & getCompileCFileCmd(conf, cfile)) @@ -619,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: @@ -629,7 +629,7 @@ proc getLinkCmd(conf: ConfigRef; projectfile, objfiles: string): string = if not libname.isAbsolute(): libname = getCurrentDir() / libname else: - libname = (libNameTmpl() % splitFile(conf.projectName).name) + libname = (libNameTmpl(conf) % splitFile(conf.projectName).name) result = CC[conf.cCompiler].buildLib % ["libfile", libname, "objfiles", objfiles] else: @@ -643,10 +643,10 @@ proc getLinkCmd(conf: ConfigRef; projectfile, objfiles: string): string = else: "" var exefile, builddll: string if optGenDynLib in conf.globalOptions: - exefile = platform.OS[targetOS].dllFrmt % splitFile(projectfile).name + 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 @@ -758,7 +758,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 diff --git a/compiler/guards.nim b/compiler/guards.nim index d39ea799bd..1748254d66 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -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/jsgen.nim b/compiler/jsgen.nim index 697d01d868..96f35c76be 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -606,11 +606,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 +648,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 +669,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 +775,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 +798,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 +969,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 +1366,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 +1680,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,7 +1901,7 @@ 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): @@ -1926,7 +1926,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 +1968,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 diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim index f9e4246ebe..d86b09a03d 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/lexer.nim b/compiler/lexer.nim index 0a31f80495..cf23c94790 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -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) diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index 9d4401fc4e..38e9800d38 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -515,7 +515,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) @@ -581,7 +581,7 @@ 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) diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim index b5577d9610..3accf90b47 100644 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -87,7 +87,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 +139,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): diff --git a/compiler/main.nim b/compiler/main.nim index b41cdc8d63..7e0ac102cd 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -91,7 +91,7 @@ proc commandJsonScript(graph: ModuleGraph; cache: IdentCache) = proc commandCompileToJS(graph: ModuleGraph; cache: IdentCache) = #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") diff --git a/compiler/msgs.nim b/compiler/msgs.nim index b3256e3bbc..9824f7c1cd 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -83,7 +83,7 @@ proc makeCString*(s: string): Rope = for i in countup(0, len(s) - 1): if (i + 1) mod MaxLineLength == 0: add(res, '\"') - add(res, tnl) + add(res, '\L') add(res, '\"') add(res, toCChar(s[i])) add(res, '\"') diff --git a/compiler/nimsets.nim b/compiler/nimsets.nim index 6cb675ed82..c7a12433f7 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, msgs, 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/options.nim b/compiler/options.nim index 3570611500..d1907b1a59 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -136,6 +136,7 @@ type CfileList* = seq[Cfile] ConfigRef* = ref object ## eventually all global configuration should be moved here + target*: Target linesCompiled*: int # all lines that have been compiled options*: TOptions globalOptions*: TGlobalOptions @@ -264,6 +265,7 @@ proc newConfigRef*(): ConfigRef = ccompilerpath: "", toCompile: @[] ) + setTargetFromSystem(result.target) # enable colors by default on terminals if terminal.isatty(stderr): incl(result.globalOptions, optUseColors) @@ -285,39 +287,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 diff --git a/compiler/platform.nim b/compiler/platform.nim index 8b3bf6b744..173cfa8e3a 100644 --- a/compiler/platform.nim +++ b/compiler/platform.nim @@ -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 +type + Target* = object + targetCPU*, hostCPU*: TSystemCPU + targetOS*, hostOS*: TSystemOS + intSize*: int + floatSize*: int + ptrSize*: int + tnl*: string # target newline -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) = +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 + 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 = +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/ropes.nim b/compiler/ropes.nim index 713d5c8a21..297343a39f 100644 --- a/compiler/ropes.nim +++ b/compiler/ropes.nim @@ -217,11 +217,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) @@ -265,10 +260,10 @@ proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope = 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: doAssert false, "invalid format string: " & frmt diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim index 4a923ef2b9..3577abee89 100644 --- a/compiler/semasgn.nim +++ b/compiler/semasgn.nim @@ -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), diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index c4edd6252e..d323f3c0eb 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -137,7 +137,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 +152,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 +167,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 @@ -251,7 +251,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 @@ -407,7 +407,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 @@ -2043,7 +2043,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): diff --git a/compiler/semfold.nim b/compiler/semfold.nim index daf9ce983a..b60f6b8bdc 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -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,9 @@ 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 mCard: result = newIntNodeT(nimsets.cardSet(g.config, a), n, g) of mBitnotI: result = newIntNodeT(not getInt(a), n, g) - of mLengthArray: result = newIntNodeT(lengthOrd(a.typ), 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 +229,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) @@ -304,21 +304,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 +415,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 +472,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 +547,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 +599,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 {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 +630,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/semmagic.nim b/compiler/semmagic.nim index caf821ab5b..e60d34e82d 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -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) @@ -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]) diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim index 9e7aead13c..33d24a0776 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: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 2faf6b39ba..1cb06c75f5 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -90,12 +90,12 @@ proc semWhile(c: PContext, n: PNode): PNode = if n.sons[1].typ == enforceVoidContext: result.typ = enforceVoidContext -proc toCover(t: PType): BiggestInt = +proc toCover(c: PContext, t: PType): BiggestInt = var 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 @@ -230,7 +230,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") diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 69f1be0a1d..17566548d5 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -66,7 +66,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 @@ -133,7 +133,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") @@ -553,7 +553,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 +584,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 +603,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) @@ -1585,7 +1585,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 +1596,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 +1609,73 @@ 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) + setMagicType(c.config, m, tyTypeDesc, 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 61d92bb196..993cacb5e3 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -27,7 +27,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: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 0df52d0aff..a83fe080bc 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -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 @@ -634,10 +638,10 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} = let - a0 = firstOrd(a) - a1 = lastOrd(a) - f0 = firstOrd(f) - f1 = lastOrd(f) + a0 = firstOrd(nil, a) + a1 = lastOrd(nil, a) + f0 = firstOrd(nil, f) + f1 = lastOrd(nil, f) if a0 == f0 and a1 == f1: result = isEqual elif a0 >= f0 and a1 <= f1: @@ -887,17 +891,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}: @@ -1176,7 +1180,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 +1346,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 diff --git a/compiler/transf.nim b/compiler/transf.nim index 81085af96b..4bd57d9d37 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -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: diff --git a/compiler/types.nim b/compiler/types.nim index b5f4fbf54d..a3476a3530 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -589,18 +589,18 @@ 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 +610,38 @@ 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)) + 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 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 @@ -654,27 +654,27 @@ proc lastOrd*(t: PType; fixedUnsigned = false): BiggestInt = 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)) + 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 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 ----------------------------------------------- @@ -1205,81 +1205,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) @@ -1292,20 +1235,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 @@ -1319,7 +1262,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 @@ -1337,30 +1280,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 @@ -1370,7 +1313,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 @@ -1379,12 +1322,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 @@ -1392,61 +1335,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) + result = computeSizeAux(conf, typ.lastSon, a) of tyGenericInst, tyDistinct, tyGenericBody, tyAlias: - result = computeSizeAux(lastSon(typ), a) + 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 diff --git a/compiler/vm.nim b/compiler/vm.nim index c81f97e43a..afc203059a 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -370,7 +370,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: @@ -700,12 +700,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 +853,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] diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 12484ed100..282530d276 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -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) @@ -1574,7 +1574,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) From f86ca02960bfe853e19e38b008981c0469588828 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 18 May 2018 15:59:44 +0200 Subject: [PATCH 012/158] make semantic checking free of globals --- compiler/semdata.nim | 2 ++ compiler/semexprs.nim | 4 ++-- compiler/semstmts.nim | 24 +++++++++++------------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 392f00c018..e88f19768c 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -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 @@ -212,6 +213,7 @@ proc newOptionEntry*(conf: ConfigRef): POptionEntry = proc newContext*(graph: ModuleGraph; module: PSym; cache: IdentCache): PContext = new(result) + result.enforceVoidContext = PType(kind: tyStmt) result.ambiguousSymbols = initIntSet() result.optionStack = @[] result.libs = @[] diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index d323f3c0eb..64a5861e2c 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -58,7 +58,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) @@ -1450,7 +1450,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: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 1cb06c75f5..c0dc47b6ff 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,8 +85,8 @@ 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(c: PContext, t: PType): BiggestInt = var t2 = skipTypes(t, abstractVarRange-{tyTypeDesc}) @@ -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 @@ -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) @@ -746,8 +744,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 = @@ -1834,9 +1832,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 From 3af5a5d9e3604a903505a04d2747c04986e6042f Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 18 May 2018 20:16:30 +0200 Subject: [PATCH 013/158] os.nim: don't use echo for error reporting --- lib/pure/os.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 03445a0353..9f3045224e 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -907,7 +907,7 @@ proc rawCreateDir(dir: string): bool = elif errno == EEXIST: result = false else: - echo res + #echo res raiseOSError(osLastError()) else: when useWinUnicode: From feef109e60fd33ff350cbcf82298a7cae83bbd72 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 18 May 2018 20:53:41 +0200 Subject: [PATCH 014/158] make tests green again --- compiler/astalgo.nim | 40 ++++++++++++++++++++------------------ compiler/ccgtypes.nim | 2 +- compiler/cgmeth.nim | 2 +- compiler/dfa.nim | 3 ++- compiler/extccomp.nim | 9 ++++++--- compiler/lambdalifting.nim | 2 +- compiler/lookups.nim | 28 +++++++++++++------------- compiler/magicsys.nim | 2 +- compiler/msgs.nim | 2 ++ compiler/procfind.nim | 2 +- compiler/rodread.nim | 3 ++- compiler/semcall.nim | 5 +++-- compiler/semstmts.nim | 9 +++++---- compiler/sigmatch.nim | 2 +- compiler/types.nim | 4 ++-- lib/pure/osproc.nim | 2 +- 16 files changed, 65 insertions(+), 52 deletions(-) diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 2ad1e24cba..1b64179648 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -61,11 +61,12 @@ type 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*(conf: ConfigRef; n: PSym) {.deprecated.} -proc debug*(conf: ConfigRef; n: PType) {.deprecated.} -proc debug*(conf: ConfigRef; n: PNode) {.deprecated.} +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*(conf: ConfigRef; n: PSym) {.deprecated.} + proc debug*(conf: ConfigRef; n: PType) {.deprecated.} + proc debug*(conf: ConfigRef; n: PNode) {.deprecated.} template mdbg*: bool {.dirty.} = when compiles(c.module): @@ -445,22 +446,23 @@ proc debugTree(conf: ConfigRef; n: PNode, indent: int, maxRecDepth: int; addf(result, "$N$1]", [istr]) addf(result, "$N$1}", [rspaces(indent)]) -proc debug(conf: ConfigRef; 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(conf, n.info), $n.kind]) +when declared(echo): + proc debug(conf: ConfigRef; 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(conf, n.info), $n.kind]) -proc debug(conf: ConfigRef; n: PType) = - echo($debugType(conf, n)) + proc debug(conf: ConfigRef; n: PType) = + echo($debugType(conf, n)) -proc debug(conf: ConfigRef; n: PNode) = - echo($debugTree(conf, n, 0, 100)) + proc debug(conf: ConfigRef; n: PNode) = + echo($debugTree(conf, n, 0, 100)) proc nextTry(h, maxHash: Hash): Hash = result = ((5 * h) + 1) and maxHash diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 1a7cd1dad9..d0433f9aef 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -912,7 +912,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") diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim index c739381bba..da3ffaa612 100644 --- a/compiler/cgmeth.nim +++ b/compiler/cgmeth.nim @@ -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/dfa.nim b/compiler/dfa.nim index 95fbe28726..aab1d9b4b2 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -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/extccomp.nim b/compiler/extccomp.nim index 45db8d6bfa..23db9cbe19 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -734,7 +734,8 @@ proc callCCompiler*(conf: ConfigRef; projectfile: string) = var cmds: TStringSeq = @[] var prettyCmds: TStringSeq = @[] let prettyCb = proc (idx: int) = - echo prettyCmds[idx] + when declared(echo): + echo prettyCmds[idx] compileCFile(conf, conf.toCompile, script, cmds, prettyCmds) if optCompileOnly notin conf.globalOptions: execCmdsInParallel(conf, cmds, prettyCb) @@ -846,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 = diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 2789e4b8f7..f9795a7662 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -279,7 +279,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]]) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 46617680ee..a67b027cbb 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -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): @@ -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) = diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim index 3accf90b47..2f021743e3 100644 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -187,7 +187,7 @@ 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)) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 9824f7c1cd..151291ffb2 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -282,6 +282,8 @@ proc toFileLineCol*(conf: ConfigRef; info: TLineInfo): string {.inline.} = proc `$`*(conf: ConfigRef; info: TLineInfo): string = toFileLineCol(conf, info) +proc `$`*(info: TLineInfo): string {.error.} = discard + proc `??`* (conf: ConfigRef; info: TLineInfo, filename: string): bool = # only for debugging purposes result = filename in toFilename(conf, info) diff --git a/compiler/procfind.nim b/compiler/procfind.nim index 042947e72f..3f47e7e8a8 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/rodread.nim b/compiler/rodread.nim index 3eeee1127b..9c834a410b 100644 --- a/compiler/rodread.nim +++ b/compiler/rodread.nim @@ -792,7 +792,8 @@ proc findSomeWhere(id: int) = if rd != nil: var d = iiTableGet(rd.index.tab, id) if d != InvalidKey: - echo "found id ", id, " in ", gMods[i].filename + when declared(echo): + 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 diff --git a/compiler/semcall.nim b/compiler/semcall.nim index e9a31d3d6c..1de5a55ccd 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 @@ -344,7 +344,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) = diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index c0dc47b6ff..055ac34251 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -144,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) @@ -696,7 +696,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 @@ -832,7 +833,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) @@ -1541,7 +1542,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) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index a83fe080bc..8fdf10afbf 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -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 diff --git a/compiler/types.nim b/compiler/types.nim index a3476a3530..4b6cb105c7 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -102,7 +102,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,7 +118,7 @@ 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 = diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 664446d546..e8bca4bdd1 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 From e55a1051eaee1833f60e8da317075e2654070057 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 21 May 2018 19:53:52 +0200 Subject: [PATCH 015/158] fixes yet another index out of bounds issue in renderer.nim --- compiler/renderer.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 75c19a1638..53e16edd20 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -811,8 +811,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 From b885fc906f5df52d73cfcc0e41846d9d20504192 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 25 May 2018 14:13:37 +0200 Subject: [PATCH 016/158] make travis use GCC instead of clang --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a3fa3f1da1..3bc9a9778b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,7 +29,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 From 669a5644926290e19a3fc32fd319c056183ff2d9 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 27 May 2018 11:10:56 +0200 Subject: [PATCH 017/158] remove more global variables in the Nim compiler --- compiler/ast.nim | 2 +- compiler/astalgo.nim | 98 +++++------- compiler/ccgutils.nim | 2 +- compiler/cgen.nim | 8 +- compiler/cgendata.nim | 3 +- compiler/cgmeth.nim | 2 +- compiler/commands.nim | 10 +- compiler/configuration.nim | 184 +---------------------- compiler/destroyer.nim | 2 +- compiler/dfa.nim | 2 +- compiler/docgen.nim | 2 +- compiler/docgen2.nim | 2 +- compiler/evaltempl.nim | 9 +- compiler/extccomp.nim | 4 +- compiler/filter_tmpl.nim | 2 +- compiler/gorgeimpl.nim | 3 +- compiler/guards.nim | 2 +- compiler/hlo.nim | 6 +- compiler/importer.nim | 2 +- compiler/jsgen.nim | 2 +- compiler/lambdalifting.nim | 2 +- compiler/lexer.nim | 36 ++--- compiler/liftlocals.nim | 2 +- compiler/lineinfos.nim | 264 +++++++++++++++++++++++++++++++++ compiler/lookups.nim | 2 +- compiler/lowerings.nim | 3 +- compiler/magicsys.nim | 2 +- compiler/main.nim | 2 +- compiler/modulegraphs.nim | 2 +- compiler/modulepaths.nim | 2 +- compiler/modules.nim | 6 +- compiler/msgs.nim | 239 ++++++++++------------------- compiler/nim.nim | 2 +- compiler/nimblecmd.nim | 2 +- compiler/nimconf.nim | 2 +- compiler/nimfix/pretty.nim | 2 +- compiler/nimfix/prettybase.nim | 2 +- compiler/nimsets.nim | 2 +- compiler/nversion.nim | 2 +- compiler/options.nim | 36 ++++- compiler/parser.nim | 2 +- compiler/passaux.nim | 2 +- compiler/passes.nim | 2 +- compiler/pragmas.nim | 8 +- compiler/prefixmatches.nim | 6 +- compiler/renderer.nim | 2 +- compiler/reorder.nim | 2 +- compiler/rod.nim | 2 +- compiler/rodread.nim | 4 +- compiler/rodwrite.nim | 2 +- compiler/ropes.nim | 6 +- compiler/scriptconfig.nim | 2 +- compiler/sem.nim | 32 ++-- compiler/semcall.nim | 4 +- compiler/semdata.nim | 2 +- compiler/semexprs.nim | 20 +-- compiler/semfold.nim | 2 +- compiler/semgnrc.nim | 6 +- compiler/seminst.nim | 12 +- compiler/semmagic.nim | 2 +- compiler/sempass2.nim | 6 +- compiler/semstmts.nim | 18 +-- compiler/semtempl.nim | 4 +- compiler/semtypinst.nim | 19 +-- compiler/service.nim | 4 +- compiler/sigmatch.nim | 10 +- compiler/suggest.nim | 129 +++++++--------- compiler/syntaxes.nim | 2 +- compiler/transf.nim | 6 +- compiler/types.nim | 3 +- compiler/vm.nim | 2 +- compiler/vmdef.nim | 2 +- compiler/vmdeps.nim | 2 +- compiler/vmgen.nim | 2 +- compiler/vmmarshal.nim | 2 +- compiler/writetracking.nim | 3 +- nimsuggest/nimsuggest.nim | 65 ++++---- 77 files changed, 679 insertions(+), 679 deletions(-) create mode 100644 compiler/lineinfos.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index ec727544e9..85278f9ef3 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 diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 1b64179648..7079e77ccb 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -12,7 +12,8 @@ # 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*(conf: ConfigRef; n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope @@ -23,44 +24,6 @@ proc typeToYaml*(conf: ConfigRef; n: PType, indent: int = 0, maxRecDepth: int = proc symToYaml*(conf: ConfigRef; n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope proc lineInfoToStr*(conf: ConfigRef; 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 - 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: @@ -470,7 +433,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: @@ -494,12 +457,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: @@ -517,7 +480,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): @@ -573,7 +536,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) @@ -609,7 +572,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] @@ -617,13 +580,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] @@ -636,6 +599,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) @@ -659,20 +628,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/ccgutils.nim b/compiler/ccgutils.nim index f481e4d632..75cd3d35d8 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -57,7 +57,7 @@ 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 = diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 1c5544d4de..2d4fbf1740 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -14,12 +14,12 @@ import nversion, nimsets, msgs, std / sha1, bitsets, idents, types, ccgutils, os, ropes, math, passes, rodread, 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 +from lineinfos import warnGcMem, errXMustBeCompileTime, hintDependency, errGenerated, errCannotOpenFile import dynlib @@ -949,8 +949,8 @@ proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope = proc genFilenames(m: BModule): Rope = discard cgsym(m, "dbgRegisterFilename") result = nil - for i in 0.. 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 23db9cbe19..615b8c1e1c 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -9,12 +9,12 @@ # 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 + lineinfos, std / sha1, streams type TInfoCCProp* = enum # properties of the C compiler: diff --git a/compiler/filter_tmpl.nim b/compiler/filter_tmpl.nim index 6c16a0b4ea..09455ced74 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 b175d23c50..44ad461363 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] = "" diff --git a/compiler/guards.nim b/compiler/guards.nim index 1748254d66..99bb51fce2 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, diff --git a/compiler/hlo.nim b/compiler/hlo.nim index 8251e3179c..bbbcb4e561 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/importer.nim b/compiler/importer.nim index 2dec05d66b..22e90b50e7 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -11,7 +11,7 @@ import intsets, strutils, os, ast, astalgo, msgs, options, idents, rodread, lookups, - semdata, passes, renderer, modulepaths, sigmatch, configuration + semdata, passes, renderer, modulepaths, sigmatch, lineinfos proc evalImport*(c: PContext, n: PNode): PNode proc evalFrom*(c: PContext, n: PNode): PNode diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 96f35c76be..747572dd3e 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -32,7 +32,7 @@ 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 + intsets, cgmeth, lowerings, sighashes, lineinfos from modulegraphs import ModuleGraph diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index f9795a7662..02682af447 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -12,7 +12,7 @@ import intsets, strutils, options, ast, astalgo, trees, treetab, msgs, os, idents, renderer, types, magicsys, rodread, lowerings, tables, - modulegraphs + modulegraphs, lineinfos discard """ The basic approach is that captured vars need to be put on the heap and diff --git a/compiler/lexer.nim b/compiler/lexer.nim index cf23c94790..1249f84b0b 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 @@ -1121,9 +1121,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 +1142,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 diff --git a/compiler/liftlocals.nim b/compiler/liftlocals.nim index 4603d357b2..bf8f9f994b 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 diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim new file mode 100644 index 0000000000..0ff6a5ccd8 --- /dev/null +++ b/compiler/lineinfos.nim @@ -0,0 +1,264 @@ +# +# +# 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, + 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" + +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 "" 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) + +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 + 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/lookups.nim b/compiler/lookups.nim index a67b027cbb..8be843161b 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -11,7 +11,7 @@ import intsets, ast, astalgo, idents, semdata, types, msgs, options, rodread, - renderer, wordrecg, idgen, nimfix.prettybase, configuration, strutils + renderer, wordrecg, idgen, nimfix.prettybase, lineinfos, strutils proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index 38e9800d38..b5be6ebbb2 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.} = diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim index 2f021743e3..1ca1dc5d83 100644 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -11,7 +11,7 @@ import ast, astalgo, hashes, msgs, platform, nversion, times, idents, rodread, - modulegraphs + modulegraphs, lineinfos export createMagic diff --git a/compiler/main.nim b/compiler/main.nim index 7e0ac102cd..bf37a107d4 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -16,7 +16,7 @@ import cgen, jsgen, json, nversion, platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen, docgen2, service, parser, modules, ccgutils, sigmatch, ropes, - modulegraphs, tables, rod, configuration + modulegraphs, tables, rod, lineinfos from magicsys import resetSysTypes diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index d8fa4cedd7..00f230a12a 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -25,7 +25,7 @@ ## - Its dependent module stays the same. ## -import ast, intsets, tables, options, rod, msgs, hashes, idents +import ast, intsets, tables, options, rod, lineinfos, hashes, idents type ModuleGraph* = ref object diff --git a/compiler/modulepaths.nim b/compiler/modulepaths.nim index 8d21fa7551..e9ee172a4f 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 diff --git a/compiler/modules.nim b/compiler/modules.nim index 09d5d60b5a..5dde755fab 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -12,7 +12,7 @@ import ast, astalgo, magicsys, std / sha1, rodread, msgs, cgendata, sigmatch, options, idents, os, lexer, idgen, passes, syntaxes, llstream, modulegraphs, rod, - configuration + lineinfos proc resetSystemArtifacts*(g: ModuleGraph) = magicsys.resetSysTypes(g) @@ -118,8 +118,8 @@ proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex; proc compileSystemModule*(graph: ModuleGraph; cache: IdentCache) = if graph.systemModule == nil: - systemFileIdx = fileInfoIdx(graph.config, graph.config.libpath / "system.nim") - discard graph.compileModule(systemFileIdx, cache, {sfSystemModule}) + graph.config.m.systemFileIdx = fileInfoIdx(graph.config, graph.config.libpath / "system.nim") + discard graph.compileModule(graph.config.m.systemFileIdx, cache, {sfSystemModule}) proc wantMainModule*(conf: ConfigRef) = if conf.projectFull.len == 0: diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 151291ffb2..197d6ca2a9 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, '\L') - 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 @@ -162,36 +107,19 @@ proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} = 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 (config: ConfigRef; info: TLineInfo; msg: string; severity: Severity) {.closure.} - proc concat(strings: openarray[string]): string = var totalLen = 0 for s in strings: totalLen += s.len 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 @@ -212,47 +140,47 @@ 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*(conf: ConfigRef; fileIdx: FileIndex): string = - (if fileIdx.int32 < 0: "???" else: fileInfos[fileIdx.int32].projPath) + (if fileIdx.int32 < 0: "???" else: conf.m.fileInfos[fileIdx.int32].projPath) proc toFullPath*(conf: ConfigRef; fileIdx: FileIndex): string = if fileIdx.int32 < 0: result = "???" - else: result = fileInfos[fileIdx.int32].fullPath + else: result = conf.m.fileInfos[fileIdx.int32].fullPath 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*(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*(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*(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*(conf: ConfigRef; info: TLineInfo): string = toFilename(conf, info.fileIndex) @@ -264,9 +192,9 @@ 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 @@ -288,12 +216,6 @@ proc `??`* (conf: ConfigRef; info: TLineInfo, filename: string): bool = # only for debugging purposes result = filename in toFilename(conf, info) -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. - type MsgFlag* = enum ## flags altering msgWriteln behavior msgStdout, ## force writing to stdout, even stderr is default @@ -310,14 +232,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): @@ -348,17 +270,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: @@ -389,7 +311,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" & @@ -420,19 +342,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(conf, 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 @@ -468,8 +390,9 @@ proc rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) = inc(conf.hintCounter) let s = msgKindToString(msg) % args - if structuredErrorHook != nil: - structuredErrorHook(conf, 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: @@ -518,7 +441,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 @@ -542,8 +465,8 @@ proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, let s = getMessageStr(msg, arg) if not ignoreMsg: - if structuredErrorHook != nil: - structuredErrorHook(conf, 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, @@ -557,7 +480,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 = "") = @@ -579,12 +502,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) @@ -596,36 +519,36 @@ template internalAssert*(conf: ConfigRef, e: bool) = if not e: internalError(conf, $instantiationInfo()) proc addSourceLine*(conf: ConfigRef; fileIdx: FileIndex, line: string) = - fileInfos[fileIdx.int32].lines.add line.rope + conf.m.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: + 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 < fileInfos.len + assert i.fileIndex.int32 < conf.m.fileInfos.len # can happen if the error points to EOF: - if i.line.int > fileInfos[i.fileIndex.int32].lines.len: return nil + if i.line.int > conf.m.fileInfos[i.fileIndex.int32].lines.len: return nil - result = fileInfos[i.fileIndex.int32].lines[i.line.int-1] + result = conf.m.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 + 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) = @@ -633,5 +556,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/nim.nim b/compiler/nim.nim index d3e00017f5..456a7bdac2 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -21,7 +21,7 @@ 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 + nodejs, scriptconfig, idents, modulegraphs, lineinfos when hasTinyCBackend: import tccgen diff --git a/compiler/nimblecmd.nim b/compiler/nimblecmd.nim index 8305b01f53..9a23535bf5 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 6cb5bab0fb..04903e6901 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 diff --git a/compiler/nimfix/pretty.nim b/compiler/nimfix/pretty.nim index 2fa2a2c3d3..cd05325b0b 100644 --- a/compiler/nimfix/pretty.nim +++ b/compiler/nimfix/pretty.nim @@ -14,7 +14,7 @@ import strutils, os, intsets, strtabs import ".." / [options, ast, astalgo, msgs, semdata, ropes, idents, - configuration] + lineinfos] import prettybase type diff --git a/compiler/nimfix/prettybase.nim b/compiler/nimfix/prettybase.nim index 89c48ae6ed..f530c8012c 100644 --- a/compiler/nimfix/prettybase.nim +++ b/compiler/nimfix/prettybase.nim @@ -8,7 +8,7 @@ # import strutils, lexbase, streams -import ".." / [ast, msgs, idents, options] +import ".." / [ast, msgs, lineinfos, idents, options] from os import splitFile type diff --git a/compiler/nimsets.nim b/compiler/nimsets.nim index c7a12433f7..b00353e200 100644 --- a/compiler/nimsets.nim +++ b/compiler/nimsets.nim @@ -10,7 +10,7 @@ # 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 = diff --git a/compiler/nversion.nim b/compiler/nversion.nim index caa818d790..4b8cf71005 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 6acf789093..48713161c1 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 @@ -135,11 +136,33 @@ type 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 ## eventually all global configuration should be moved here target*: Target linesCompiled*: int # all lines that have been compiled options*: TOptions globalOptions*: TGlobalOptions + m*: MsgConfig + evalTemplateCounter*: int exitcode*: int8 cmd*: TCommands # the command selectedGC*: TGCMode # the selected GC @@ -202,6 +225,13 @@ type 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.} const oldExperimentalFeatures* = {implicitDeref, dotOperators, callOperator, parallel} @@ -229,6 +259,7 @@ proc newConfigRef*(): ConfigRef = verbosity: 1, options: DefaultOptions, globalOptions: DefaultGlobalOptions, + m: initMsgConfig(), evalExpr: "", cppDefines: initSet[string](), headerFile: "", features: {}, foreignPackageNotes: {hintProcessing, warnUnknownMagic, @@ -264,7 +295,8 @@ proc newConfigRef*(): ConfigRef = compileOptions: "", ccompilerpath: "", toCompile: @[], - arguments: "" + arguments: "", + suggestMaxResults: 10_000 ) setTargetFromSystem(result.target) # enable colors by default on terminals diff --git a/compiler/parser.nim b/compiler/parser.nim index 82e6549ed2..621eabeb26 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -27,7 +27,7 @@ when isMainModule: outp.close import - llstream, lexer, idents, strutils, ast, astalgo, msgs, options, configuration + llstream, lexer, idents, strutils, ast, astalgo, msgs, options, lineinfos type TParser* = object # A TParser object represents a file that diff --git a/compiler/passaux.nim b/compiler/passaux.nim index 568fb4c23c..1ac4611884 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 diff --git a/compiler/passes.nim b/compiler/passes.nim index 26e33185cf..3fe7ce4816 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -14,7 +14,7 @@ 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 + lineinfos type diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 19431613c9..7c7f1a792d 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 + rodread, types, lookups, lineinfos const FirstCallConv* = wNimcall @@ -549,7 +549,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] @@ -1047,13 +1047,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") diff --git a/compiler/prefixmatches.nim b/compiler/prefixmatches.nim index 00e2c537d0..246d1ae5e0 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/renderer.nim b/compiler/renderer.nim index 53e16edd20..748cf9d1c4 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 diff --git a/compiler/reorder.nim b/compiler/reorder.nim index e4ba221e3e..932df9bcab 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 diff --git a/compiler/rod.nim b/compiler/rod.nim index c144f15ef0..82f556b3b1 100644 --- a/compiler/rod.nim +++ b/compiler/rod.nim @@ -9,7 +9,7 @@ ## This module implements the canonalization for the various caching mechanisms. -import ast, idgen, msgs +import ast, idgen, lineinfos, msgs when not defined(nimSymbolfiles): template setupModuleCache* = discard diff --git a/compiler/rodread.nim b/compiler/rodread.nim index 9c834a410b..dc9b1c61dc 100644 --- a/compiler/rodread.nim +++ b/compiler/rodread.nim @@ -91,7 +91,7 @@ import os, options, strutils, nversion, ast, astalgo, msgs, platform, condsyms, ropes, idents, std / sha1, idgen, types, rodutils, memfiles, tables, - configuration + lineinfos type TReasonForRecompile* = enum ## all the reasons that can trigger recompilation @@ -899,7 +899,7 @@ proc checkDep(fileIdx: FileIndex; cache: IdentCache; conf: ConfigRef): TReasonFo # 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) + var res = checkDep(conf.m.systemFileIdx, cache, conf) if res != rrNone: result = rrModDeps for i in countup(0, high(r.modDeps)): res = checkDep(r.modDeps[i], cache, conf) diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim index a5574fdf0a..0b27418b33 100644 --- a/compiler/rodwrite.nim +++ b/compiler/rodwrite.nim @@ -14,7 +14,7 @@ import intsets, os, options, strutils, nversion, ast, astalgo, msgs, platform, condsyms, ropes, idents, std / sha1, rodread, passes, idgen, - rodutils, modulepaths + rodutils, modulepaths, lineinfos from modulegraphs import ModuleGraph diff --git a/compiler/ropes.nim b/compiler/ropes.nim index 297343a39f..973f169169 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,8 +70,6 @@ type length*: int data*: string # != nil if a leaf - RopeSeq* = seq[Rope] - proc len*(a: Rope): int = ## the rope's length if a == nil: result = 0 @@ -93,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): diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim index 30e5e48031..32ee04b588 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 diff --git a/compiler/sem.nim b/compiler/sem.nim index 8b616bd84f..b7dd1df7af 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -16,7 +16,7 @@ import procfind, lookups, rodread, 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 + semparallel, lowerings, pluginsupport, plugins.active, rod, lineinfos from modulegraphs import ModuleGraph @@ -52,10 +52,10 @@ 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; @@ -307,9 +307,9 @@ 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: @@ -324,7 +324,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,9 +340,9 @@ proc semConstExpr(c: PContext, n: PNode): PNode = result = evalConstExpr(c.module, c.cache, 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: @@ -380,8 +380,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) @@ -420,8 +420,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 @@ -429,7 +429,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) @@ -449,7 +449,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, 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) @@ -582,14 +582,14 @@ 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 diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 1de5a55ccd..894d2ebb20 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -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: @@ -332,7 +332,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: diff --git a/compiler/semdata.nim b/compiler/semdata.nim index e88f19768c..be682189e1 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -14,7 +14,7 @@ import wordrecg, ropes, msgs, platform, os, condsyms, idents, renderer, types, extccomp, math, magicsys, nversion, nimsets, parser, times, passes, rodread, vmdef, - modulegraphs, configuration + modulegraphs, lineinfos type TOptionEntry* = object # entries to put on a stack for pragma parsing diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 64a5861e2c..5e244fd09b 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -26,10 +26,10 @@ 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) proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode @@ -742,7 +742,7 @@ 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 c.graph.emptyNode @@ -1082,7 +1082,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: @@ -1783,9 +1783,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 @@ -1807,10 +1807,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 @@ -2309,7 +2309,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: diff --git a/compiler/semfold.nim b/compiler/semfold.nim index b60f6b8bdc..10a223ea2b 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 diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 8f06e748e2..d3b1695ae8 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -55,7 +55,7 @@ template macroToExpandSym(s): untyped = proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, ctx: var GenericCtx; fromDotExpr=false): PNode = - semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody) + semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody) incl(s.flags, sfUsed) case s.kind of skUnknown: @@ -129,7 +129,7 @@ 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} @@ -170,7 +170,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: diff --git a/compiler/seminst.nim b/compiler/seminst.nim index e95b945535..95b631850d 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) @@ -236,7 +236,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) @@ -278,7 +278,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 +309,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: @@ -353,7 +353,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 e60d34e82d..bd1cd5a7f1 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -76,7 +76,7 @@ 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: toFullPath(c.config, info) else: toFilename(c.config, info) var line = newNodeIT(nkIntLit, n.info, getSysType(c.graph, n.info, tyInt)) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 2ba2a6ace2..db6bb32ff5 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): @@ -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: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 055ac34251..eb3376ff5d 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -89,7 +89,7 @@ proc semWhile(c: PContext, n: PNode): PNode = result.typ = c.enforceVoidContext proc toCover(c: PContext, t: PType): BiggestInt = - var t2 = skipTypes(t, abstractVarRange-{tyTypeDesc}) + let t2 = skipTypes(t, abstractVarRange-{tyTypeDesc}) if t2.kind == tyEnum and enumHasHoles(t2): result = sonsLen(t2.n) else: @@ -201,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: @@ -1396,14 +1396,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.. 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,7 +316,7 @@ 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, toFullPath(c.config, n.info)) if fullPath.len == 0: @@ -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: @@ -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 cb12629b4e..069f65eee6 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 diff --git a/compiler/transf.nim b/compiler/transf.nim index 4bd57d9d37..d5edb4ec82 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -22,7 +22,7 @@ import intsets, strutils, options, ast, astalgo, trees, treetab, msgs, os, idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread, lambdalifting, sempass2, lowerings, lookups, destroyer, liftlocals, - modulegraphs + modulegraphs, lineinfos type PTransNode* = distinct PNode @@ -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 diff --git a/compiler/types.nim b/compiler/types.nim index 4b6cb105c7..887f6249fb 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 diff --git a/compiler/vm.nim b/compiler/vm.nim index ecee319d08..03f63013cf 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 from semfold import leValueConv, ordinalValToString from evaltempl import evalTemplate diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index e10a620066..9c31dda24d 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -10,7 +10,7 @@ ## 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 const byteExcess* = 128 # we use excess-K for immediates diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index 45d6b81c3c..d4a4c0b88d 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -7,7 +7,7 @@ # 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: diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 282530d276..4a7ab9b792 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, rodread, magicsys, options, lowerings, lineinfos import platform from os import splitFile diff --git a/compiler/vmmarshal.nim b/compiler/vmmarshal.nim index f38be7c290..c396500483 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 diff --git a/compiler/writetracking.nim b/compiler/writetracking.nim index c0f7b7b20c..1ea1deb2d2 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/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim index bea33684a6..1e55924833 100644 --- a/nimsuggest/nimsuggest.nim +++ b/nimsuggest/nimsuggest.nim @@ -17,7 +17,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 @@ -109,7 +109,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,20 +141,19 @@ 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) = @@ -163,12 +162,12 @@ proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int; "[" & $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) @@ -176,14 +175,14 @@ proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int; 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 + if conf.suggestVersion == 0 and conf.ideCmd in {ideUse, ideDus} and dirtyfile.len == 0: discard "no need to recompile anything" else: @@ -193,11 +192,11 @@ proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int; if conf.ideCmd != ideMod: graph.compileProject(cache, 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) = @@ -457,16 +456,16 @@ 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 + conf.writelnHook = wrHook + conf.suggestionResultHook = sugResultHook execCmd(req, graph, cache, cachedMsgs) idle = 0 else: @@ -475,11 +474,11 @@ 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 (conf: ConfigRef; 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 + conf.suggestionResultHook = proc (s: Suggest) = discard recompileFullProject(graph, cache) var @@ -502,8 +501,8 @@ 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: @@ -555,8 +554,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 +567,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 +588,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) From dd8a6ef3a2aa1f0200d6ddff0f1f38532e84647b Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 27 May 2018 14:00:44 +0200 Subject: [PATCH 018/158] implements --hint[globalvar]:on switch for quickly finding global variables --- compiler/lineinfos.nim | 8 +++++--- compiler/semstmts.nim | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 0ff6a5ccd8..31fbe5197f 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -44,6 +44,7 @@ type hintConditionAlwaysTrue, hintName, hintPattern, hintExecuting, hintLinking, hintDependency, hintSource, hintPerformance, hintStackTrace, hintGCStats, + hintGlobalVar, hintUser, hintUserRaw const @@ -113,6 +114,7 @@ const hintPerformance: "$1", hintStackTrace: "$1", hintGCStats: "$1", + hintGlobalVar: "global variable declared here", hintUser: "$1", hintUserRaw: "$1"] @@ -134,7 +136,7 @@ const "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf", "Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency", - "Source", "Performance", "StackTrace", "GCStats", + "Source", "Performance", "StackTrace", "GCStats", "GlobalVar", "User", "UserRaw"] const @@ -166,7 +168,7 @@ const hintExecuting, hintLinking, hintCodeBegin, hintCodeEnd, hintSource, hintStackTrace, - hintGCStats}, + hintGlobalVar, hintGCStats}, {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit, warnProveField, warnProveIndex, warnGcUnsafe, @@ -174,7 +176,7 @@ const hintDependency, hintCodeBegin, hintCodeEnd, hintSource, hintStackTrace, - hintGCStats}, + hintGlobalVar, hintGCStats}, {low(TNoteKind)..high(TNoteKind)} - {hintStackTrace, warnUninit}, {low(TNoteKind)..high(TNoteKind)}] diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index eb3376ff5d..aa4d85789d 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -569,6 +569,8 @@ 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) From 18a3833d6085ae4edf28433ba12a27a4be3ae3b1 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 27 May 2018 15:22:17 +0200 Subject: [PATCH 019/158] more refactoring --- compiler/ast.nim | 4 ++-- compiler/docgen2.nim | 2 +- compiler/main.nim | 2 +- compiler/modules.nim | 4 ++-- compiler/nim.nim | 21 ++++++++++++++++++++- compiler/options.nim | 1 + compiler/parser.nim | 2 +- compiler/renderer.nim | 5 +++-- compiler/sem.nim | 2 +- compiler/service.nim | 19 ------------------- tools/nimpretty.nim | 7 ++++--- 11 files changed, 36 insertions(+), 33 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 85278f9ef3..f49d44c972 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -998,8 +998,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 diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim index 0db3b89d19..2d63220ded 100644 --- a/compiler/docgen2.nim +++ b/compiler/docgen2.nim @@ -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: diff --git a/compiler/main.nim b/compiler/main.nim index bf37a107d4..e174bea490 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -15,7 +15,7 @@ import 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, + docgen2, parser, modules, ccgutils, sigmatch, ropes, modulegraphs, tables, rod, lineinfos from magicsys import resetSysTypes diff --git a/compiler/modules.nim b/compiler/modules.nim index 5dde755fab..ead4ce0b5a 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -65,7 +65,7 @@ proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; cache: IdentCache, f var rd: PRodReader result.flags = result.flags + flags if sfMainModule in result.flags: - gMainPackageId = result.owner.id + graph.config.mainPackageId = result.owner.id when false: if conf.cmd in {cmdCompileToC, cmdCompileToCpp, cmdCheck, cmdIdeTools}: @@ -107,7 +107,7 @@ proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex; # localError(result.info, errAttemptToRedefine, result.name.s) # restore the notes for outer module: graph.config.notes = - if s.owner.id == gMainPackageId: graph.config.mainPackageNotes + if s.owner.id == graph.config.mainPackageId: graph.config.mainPackageNotes else: graph.config.foreignPackageNotes proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex; diff --git a/compiler/nim.nim b/compiler/nim.nim index 456a7bdac2..ac861b2d70 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -20,7 +20,7 @@ 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, + extccomp, strutils, os, osproc, platform, main, parseopt, nodejs, scriptconfig, idents, modulegraphs, lineinfos when hasTinyCBackend: @@ -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: diff --git a/compiler/options.nim b/compiler/options.nim index 48713161c1..d9ab9bf612 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -186,6 +186,7 @@ type foreignPackageNotes*: TNoteKinds notes*: TNoteKinds mainPackageNotes*: TNoteKinds + mainPackageId*: int errorCounter*: int hintCounter*: int warnCounter*: int diff --git a/compiler/parser.nim b/compiler/parser.nim index 621eabeb26..aedee85387 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -133,7 +133,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: diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 748cf9d1c4..e209112e80 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -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 = "" diff --git a/compiler/sem.nim b/compiler/sem.nim index 6b3287df57..12c067c229 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -505,7 +505,7 @@ 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 diff --git a/compiler/service.nim b/compiler/service.nim index 47fe25ea17..0e82c03f8b 100644 --- a/compiler/service.nim +++ b/compiler/service.nim @@ -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 diff --git a/tools/nimpretty.nim b/tools/nimpretty.nim index 396f17b0b8..386eddfde3 100644 --- a/tools/nimpretty.nim +++ b/tools/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 @@ -40,8 +40,9 @@ proc writeVersion() = quit(0) proc prettyPrint(infile: string) = - let fileIdx = fileInfoIdx(infile) - let tree = parseFile(fileIdx, newIdentCache()) + let conf = newConfigRef() + let fileIdx = fileInfoIdx(conf, infile) + let tree = parseFile(fileIdx, newIdentCache(), conf) let outfile = changeFileExt(infile, ".pretty.nim") renderModule(tree, infile, outfile, {}, fileIdx) From a325692fb29e07911adc5c95aa0c38018602ea21 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 27 May 2018 19:19:12 +0200 Subject: [PATCH 020/158] refactoring: fewer global variables --- compiler/plugins/active.nim | 13 ++++++++++- compiler/plugins/itersgen.nim | 6 ++--- compiler/plugins/{locals => }/locals.nim | 6 ++--- compiler/pluginsupport.nim | 28 ++++++------------------ compiler/reorder.nim | 24 ++++++++++---------- compiler/semmagic.nim | 2 +- 6 files changed, 36 insertions(+), 43 deletions(-) rename compiler/plugins/{locals => }/locals.nim (89%) diff --git a/compiler/plugins/active.nim b/compiler/plugins/active.nim index 5da623e49c..4e82e58cb4 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 7af97904e3..847d13ccf7 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] -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: @@ -46,5 +46,3 @@ proc iterToProcImpl(c: PContext, n: PNode): PNode = 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 similarity index 89% rename from compiler/plugins/locals/locals.nim rename to compiler/plugins/locals.nim index ff7f3be581..0048ff9853 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 f67942c979..a44436f11a 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/reorder.nim b/compiler/reorder.nim index 932df9bcab..bf5c3b4a93 100644 --- a/compiler/reorder.nim +++ b/compiler/reorder.nim @@ -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,7 +30,7 @@ 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 = @@ -49,16 +49,16 @@ proc addDecl(n: PNode; declares: var IntSet) = of nkPragmaExpr: addDecl(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) declares.incl a.id - when not defined(release): + when defined(debugReorder): idNames[a.id] = a.s of nkEnumFieldDef: addDecl(n[0], declares) @@ -216,7 +216,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.. Date: Sun, 27 May 2018 22:09:15 +0200 Subject: [PATCH 021/158] refactoring: remove idents.legacy global variable and pass the IdentCache around explicitly --- compiler/ast.nim | 2 +- compiler/destroyer.nim | 8 ++--- compiler/docgen.nim | 72 +++++++++++++++++++------------------ compiler/docgen2.nim | 2 +- compiler/idents.nim | 36 ++++++++----------- compiler/importer.nim | 4 +-- compiler/lambdalifting.nim | 40 ++++++++++----------- compiler/liftlocals.nim | 7 ++-- compiler/lookups.nim | 26 +++++++------- compiler/lowerings.nim | 74 +++++++++++++++++++------------------- compiler/magicsys.nim | 20 +++-------- compiler/main.nim | 12 +++---- compiler/modulegraphs.nim | 13 ++++--- compiler/modulepaths.nim | 2 +- compiler/modules.nim | 6 ++-- compiler/nim.nim | 2 +- compiler/nimfix/pretty.nim | 14 ++++---- compiler/pragmas.nim | 12 +++---- compiler/reorder.nim | 26 +++++++------- compiler/scriptconfig.nim | 2 +- compiler/sem.nim | 14 ++++---- compiler/semasgn.nim | 14 ++++---- compiler/semcall.nim | 10 +++--- compiler/semdata.nim | 4 +-- compiler/semexprs.nim | 36 +++++++++---------- compiler/semfields.nim | 10 +++--- compiler/semgnrc.nim | 16 ++++----- compiler/semmagic.nim | 4 +-- compiler/semobjconstr.nim | 6 ++-- compiler/semparallel.nim | 2 +- compiler/sempass2.nim | 28 +++++++-------- compiler/semstmts.nim | 20 +++++------ compiler/semtempl.nim | 14 ++++---- compiler/semtypes.nim | 28 +++++++-------- compiler/sigmatch.nim | 10 +++--- compiler/transf.nim | 6 ++-- compiler/vm.nim | 12 +++---- compiler/vmdeps.nim | 52 +++++++++++++-------------- compiler/vmgen.nim | 2 +- compiler/vmmarshal.nim | 21 +++++------ nimsuggest/nimsuggest.nim | 2 +- 41 files changed, 338 insertions(+), 353 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index f49d44c972..592c8fbe9a 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1699,7 +1699,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: diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim index dfb11883f5..cf56c6e36c 100644 --- a/compiler/destroyer.nim +++ b/compiler/destroyer.nim @@ -136,7 +136,7 @@ type 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) @@ -251,7 +251,7 @@ proc dropBit(c: var Con; s: PSym): PSym = 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, c.emptyNode, trueVal) @@ -323,7 +323,7 @@ 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) @@ -428,7 +428,7 @@ 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) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 708340f356..4be49da6da 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -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) @@ -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.." + noidentError(c.config, n, origin) + result = getIdent(c.cache, "") 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.. 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) diff --git a/compiler/nim.nim b/compiler/nim.nim index ac861b2d70..70de254b4e 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -94,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), cache) if optHints in conf.options and hintGCStats in conf.notes: echo(GC_getStatistics()) #echo(GC_getStatistics()) if conf.errorCounter == 0: diff --git a/compiler/nimfix/pretty.nim b/compiler/nimfix/pretty.nim index cd05325b0b..b825b5c6fa 100644 --- a/compiler/nimfix/pretty.nim +++ b/compiler/nimfix/pretty.nim @@ -111,32 +111,32 @@ proc replaceInFile(conf: ConfigRef; info: TLineInfo; newName: string) = system.shallowCopy(gSourceFiles[info.fileIndex.int].lines[info.line.int-1], x) gSourceFiles[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) + 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 {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) template styleCheckDef*(info: TLineInfo; s: PSym; k: TSymKind) = 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) -proc styleCheckUseImpl(conf: ConfigRef; info: TLineInfo; s: PSym) = +proc styleCheckUseImpl(conf: ConfigRef; cache: IdentCache; info: TLineInfo; s: PSym) = if info.fileIndex.int < 0: return # we simply convert it to what it looks like in the definition # for consistency @@ -152,4 +152,4 @@ proc styleCheckUseImpl(conf: ConfigRef; info: TLineInfo; s: PSym) = template styleCheckUse*(info: TLineInfo; s: PSym) = when defined(nimfix): - if gStyleCheck != StyleCheck.None: styleCheckUseImpl(conf, info, s) + if gStyleCheck != StyleCheck.None: styleCheckUseImpl(conf, cache, info, s) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 7c7f1a792d..77f75922c0 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -480,7 +480,7 @@ 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) incl(e.flags, sfUsed) @@ -634,7 +634,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) @@ -657,7 +657,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}) @@ -710,7 +710,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 @@ -1017,9 +1017,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: diff --git a/compiler/reorder.nim b/compiler/reorder.nim index bf5c3b4a93..dbd34c69c4 100644 --- a/compiler/reorder.nim +++ b/compiler/reorder.nim @@ -33,7 +33,7 @@ proc newDepN(id: int, pnode: PNode): DepN = 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,12 +41,12 @@ 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 defined(debugReorder): @@ -56,18 +56,18 @@ proc addDecl(n: PNode; declares: var IntSet) = 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 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.. `[]=`(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 +1419,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 +1427,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: @@ -1593,13 +1593,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,7 +1612,7 @@ 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 not onlyCurrentScope and considerQuotedIdent(c, 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): @@ -1711,7 +1711,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 +1722,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] @@ -2366,12 +2366,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) diff --git a/compiler/semfields.nim b/compiler/semfields.nim index b432ee9638..869f5ae742 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) diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index d3b1695ae8..727fa4f3f6 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -103,7 +103,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) @@ -140,7 +140,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, 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} @@ -207,7 +207,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) @@ -275,12 +275,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 +293,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 +448,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 diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index b2dbe3d31e..1975fb77b8 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 @@ -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: diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index 1e0802953e..8b639806d8 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 33d24a0776..0d780bdeeb 100644 --- a/compiler/semparallel.nim +++ b/compiler/semparallel.nim @@ -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 db6bb32ff5..4d3ee0408a 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -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(""), + 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: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index aa4d85789d..b48703e3d2 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -440,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)) @@ -664,7 +664,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: @@ -796,8 +796,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) @@ -989,7 +989,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 @@ -1131,7 +1131,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 +1150,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 +1162,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: @@ -1605,7 +1605,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]) diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index bc2621e425..086d090912 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,7 +206,7 @@ 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)) @@ -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) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 17566548d5..d15ff8412b 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -157,7 +157,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)) @@ -267,7 +267,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") @@ -462,7 +462,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: @@ -770,7 +770,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 +804,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 +813,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,11 +850,11 @@ 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}) @@ -873,7 +871,9 @@ 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])) @@ -1322,7 +1322,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,7 +1345,7 @@ 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) @@ -1393,7 +1393,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 diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index e1961e6660..0b51cafdcf 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -768,11 +768,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 = @@ -2126,10 +2126,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) @@ -2194,7 +2194,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 diff --git a/compiler/transf.nim b/compiler/transf.nim index d5edb4ec82..7aae2e4b50 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -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) @@ -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}: @@ -981,7 +981,7 @@ 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) incl(result.flags, nfTransf) diff --git a/compiler/vm.nim b/compiler/vm.nim index 03f63013cf..e5613718f3 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1291,7 +1291,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 +1305,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: @@ -1453,7 +1453,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,7 +1566,7 @@ 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) of opcTypeTrait: @@ -1583,7 +1583,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 diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index d4a4c0b88d..865ecd36ed 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -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,44 +34,44 @@ 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 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 0: - result.add objectNode(t.n) + result.add objectNode(cache, t.n) else: result.add newNodeI(nkEmpty, info) else: @@ -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 4a7ab9b792..7d8a1b45f6 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -965,7 +965,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) diff --git a/compiler/vmmarshal.nim b/compiler/vmmarshal.nim index c396500483..eb01b35140 100644 --- a/compiler/vmmarshal.nim +++ b/compiler/vmmarshal.nim @@ -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/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim index 1e55924833..5e04a57789 100644 --- a/nimsuggest/nimsuggest.nim +++ b/nimsuggest/nimsuggest.nim @@ -627,7 +627,7 @@ 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) From 545b1582cddb27aba75ac791f8f89e33c6d1ca5a Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 27 May 2018 22:30:36 +0200 Subject: [PATCH 022/158] refactoring: make projectMainIdx typesafe --- compiler/astalgo.nim | 16 ++++++++-------- compiler/docgen.nim | 6 +++--- compiler/main.nim | 2 +- compiler/modules.nim | 4 ++-- compiler/nim.nim | 2 +- compiler/nimconf.nim | 4 ---- compiler/options.nim | 4 ++-- 7 files changed, 17 insertions(+), 21 deletions(-) diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 5912f1359e..fff1527d32 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -33,22 +33,22 @@ when declared(echo): template mdbg*: bool {.dirty.} = when compiles(c.module): - c.module.fileIdx.int32 == c.config.projectMainIdx + c.module.fileIdx == c.config.projectMainIdx elif compiles(c.c.module): - c.c.module.fileIdx.int32 == c.c.config.projectMainIdx + c.c.module.fileIdx == c.c.config.projectMainIdx elif compiles(m.c.module): - m.c.module.fileIdx.int32 == m.c.config.projectMainIdx + m.c.module.fileIdx == m.c.config.projectMainIdx elif compiles(cl.c.module): - cl.c.module.fileIdx.int32 == cl.c.config.projectMainIdx + cl.c.module.fileIdx == cl.c.config.projectMainIdx elif compiles(p): when compiles(p.lex): - p.lex.fileIdx.int32 == p.lex.config.projectMainIdx + p.lex.fileIdx == p.lex.config.projectMainIdx else: - p.module.module.fileIdx.int32 == p.config.projectMainIdx + p.module.module.fileIdx == p.config.projectMainIdx elif compiles(m.module.fileIdx): - m.module.fileIdx.int32 == m.config.projectMainIdx + m.module.fileIdx == m.config.projectMainIdx elif compiles(L.fileIdx): - L.fileIdx.int32 == L.config.projectMainIdx + L.fileIdx == L.config.projectMainIdx else: error() diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 4be49da6da..e39b38365e 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -807,7 +807,7 @@ proc writeOutputJson*(d: PDoc, filename, outExt: string, discard "fixme: error report" proc commandDoc*(cache: IdentCache, conf: ConfigRef) = - var ast = parseFile(conf.projectMainIdx.FileIndex, newIdentCache(), conf) + var ast = parseFile(conf.projectMainIdx, cache, conf) if ast == nil: return var d = newDocumentor(conf.projectFull, cache, conf) d.hasToc = true @@ -857,7 +857,7 @@ proc commandRst2TeX*(cache: IdentCache, conf: ConfigRef) = commandRstAux(cache, conf, conf.projectFull, TexExt) proc commandJson*(cache: IdentCache, conf: ConfigRef) = - var ast = parseFile(conf.projectMainIdx.FileIndex, newIdentCache(), conf) + var ast = parseFile(conf.projectMainIdx, cache, conf) if ast == nil: return var d = newDocumentor(conf.projectFull, cache, conf) d.hasToc = true @@ -874,7 +874,7 @@ proc commandJson*(cache: IdentCache, conf: ConfigRef) = rawMessage(conf, errCannotOpenFile, filename) proc commandTags*(cache: IdentCache, conf: ConfigRef) = - var ast = parseFile(conf.projectMainIdx.FileIndex, newIdentCache(), conf) + var ast = parseFile(conf.projectMainIdx, cache, conf) if ast == nil: return var d = newDocumentor(conf.projectFull, cache, conf) d.hasToc = true diff --git a/compiler/main.nim b/compiler/main.nim index fe95f3b9b6..3b5370fa51 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -261,7 +261,7 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) = 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) diff --git a/compiler/modules.nim b/compiler/modules.nim index 2ba9e8170f..9ac3dbb0bb 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -124,7 +124,7 @@ proc compileSystemModule*(graph: ModuleGraph; cache: IdentCache) = 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)) + conf.projectMainIdx = fileInfoIdx(conf, addFileExt(conf.projectFull, NimExt)) passes.gIncludeFile = includeModule passes.gImportModule = importModule @@ -134,7 +134,7 @@ proc compileProject*(graph: ModuleGraph; cache: IdentCache; 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}) diff --git a/compiler/nim.nim b/compiler/nim.nim index 70de254b4e..756ddd7f5a 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -79,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) diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index 04903e6901..1bc2c27e3f 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -253,7 +253,3 @@ proc loadConfigs*(cfg: string; cache: IdentCache; conf: ConfigRef) = if not fileExists(projectConfig): projectConfig = changeFileExt(conf.projectFull, "nim.cfg") readConfigFile(projectConfig, cache, conf) - -proc loadConfigs*(cfg: string; conf: ConfigRef) = - # for backwards compatibility only. - loadConfigs(cfg, newIdentCache(), conf) diff --git a/compiler/options.nim b/compiler/options.nim index d9ab9bf612..6cfaad9fb7 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -204,7 +204,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 @@ -278,7 +278,7 @@ 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 From c640bd2d1bb283bcbd87da11d6b16e5f9bd39bdc Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 27 May 2018 22:52:10 +0200 Subject: [PATCH 023/158] cleanup compiler/prettybase to not use redudant global variables --- compiler/lexer.nim | 5 ---- compiler/lineinfos.nim | 3 +- compiler/msgs.nim | 37 +++++++++++------------ compiler/nimfix/pretty.nim | 22 +++++++------- compiler/nimfix/prettybase.nim | 55 ++++------------------------------ 5 files changed, 36 insertions(+), 86 deletions(-) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 1249f84b0b..6e5506b096 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -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.config, L.fileIdx, line) - case L.buf[pos] of CR: registerLine() diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 31fbe5197f..0384fda26c 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -194,7 +194,7 @@ type quotedFullName*: Rope # cached quoted full name for codegen # purposes - lines*: seq[Rope] # the source code of the module + lines*: seq[string] # the source code of the module # used for better error messages and # embedding the original source in the # generated code @@ -202,6 +202,7 @@ type # 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 diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 197d6ca2a9..62948e81e6 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -105,7 +105,6 @@ proc newLineInfo*(conf: ConfigRef; filename: string, line, col: int): TLineInfo proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} = raise newException(ERecoverableError, msg) -proc sourceLine*(conf: ConfigRef; i: TLineInfo): Rope proc concat(strings: openarray[string]): string = var totalLen = 0 @@ -409,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)) @@ -518,24 +535,6 @@ template assertNotNil*(conf: ConfigRef; e): untyped = template internalAssert*(conf: ConfigRef, e: bool) = if not e: internalError(conf, $instantiationInfo()) -proc addSourceLine*(conf: ConfigRef; fileIdx: FileIndex, line: string) = - conf.m.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 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 nil - - result = conf.m.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: diff --git a/compiler/nimfix/pretty.nim b/compiler/nimfix/pretty.nim index b825b5c6fa..f3fff781f1 100644 --- a/compiler/nimfix/pretty.nim +++ b/compiler/nimfix/pretty.nim @@ -27,19 +27,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) @@ -94,9 +94,7 @@ proc beautifyName(s: string, k: TSymKind): string = inc i proc replaceInFile(conf: ConfigRef; info: TLineInfo; newName: string) = - loadFile(conf, info) - - let line = gSourceFiles[info.fileIndex.int].lines[info.line.int-1] + 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) @@ -108,8 +106,8 @@ proc replaceInFile(conf: ConfigRef; 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; cache: IdentCache; info: TLineInfo, s: string, k: TSymKind; sym: PSym) = let beau = beautifyName(s, k) diff --git a/compiler/nimfix/prettybase.nim b/compiler/nimfix/prettybase.nim index f530c8012c..d1ad41a6ec 100644 --- a/compiler/nimfix/prettybase.nim +++ b/compiler/nimfix/prettybase.nim @@ -11,45 +11,6 @@ import strutils, lexbase, streams import ".." / [ast, msgs, lineinfos, idents, options] from os import splitFile -type - TSourceFile* = object - lines*: seq[string] - dirty*, isNimfixFile*: bool - fullpath*, newline*: string - fileIdx*: FileIndex - -var - gSourceFiles*: seq[TSourceFile] = @[] - -proc loadFile*(conf: ConfigRef; 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 = toFullPath(conf, info) - 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', '_'} @@ -62,9 +23,7 @@ proc differ*(line: string, a, b: int, x: string): bool = result = cmpIgnoreStyle(y, x) == 0 and y != x proc replaceDeprecated*(conf: ConfigRef; info: TLineInfo; oldSym, newSym: PIdent) = - loadFile(conf, info) - - let line = gSourceFiles[info.fileIndex.int32].lines[info.line.int-1] + 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 +34,18 @@ proc replaceDeprecated*(conf: ConfigRef; 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*(conf: ConfigRef; info: TLineInfo; oldSym, newSym: PSym) = replaceDeprecated(conf, info, oldSym.name, newSym.name) proc replaceComment*(conf: ConfigRef; info: TLineInfo) = - loadFile(conf, info) - - let line = gSourceFiles[info.fileIndex.int32].lines[info.line.int-1] + 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 From 73799903eec8fce83ccd0dbf6520b29ae1049ecc Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 27 May 2018 23:38:49 +0200 Subject: [PATCH 024/158] removed global variable from rstgen.nim --- compiler/docgen.nim | 1 - lib/packages/docutils/rstgen.nim | 13 ++++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index e39b38365e..d463dc3c0f 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -853,7 +853,6 @@ proc commandRst2Html*(cache: IdentCache, conf: ConfigRef) = commandRstAux(cache, conf, conf.projectFull, HtmlExt) proc commandRst2TeX*(cache: IdentCache, conf: ConfigRef) = - splitter = "\\-" commandRstAux(cache, conf, conf.projectFull, TexExt) proc commandJson*(cache: IdentCache, conf: ConfigRef) = diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 03a27017a7..ef456f0938 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 = "" - 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, "") + 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 From 006ee79a91905ea3516ed69cfa3c9fe5c55b35d3 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 28 May 2018 00:04:55 +0200 Subject: [PATCH 025/158] make dependency analyser free of global variables --- compiler/depends.nim | 25 ++++++++++++++++--------- compiler/main.nim | 2 +- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/compiler/depends.nim b/compiler/depends.nim index 38b84aafc0..34ab5b157f 100644 --- a/compiler/depends.nim +++ b/compiler/depends.nim @@ -16,35 +16,39 @@ from modulegraphs import ModuleGraph 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) = +proc generateDot*(graph: ModuleGraph; project: string) = + let b = Backend(graph.backend) discard writeRope("digraph $1 {$n$2}$n" % [ - rope(changeFileExt(extractFilename(project), "")), gDotGraph], + rope(changeFileExt(extractFilename(project), "")), b.dotGraph], changeFileExt(project, "dot")) proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = @@ -52,6 +56,9 @@ proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = 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/main.nim b/compiler/main.nim index 3b5370fa51..2489b1c503 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -48,7 +48,7 @@ proc commandGenDepend(graph: ModuleGraph; cache: IdentCache) = compileProject(graph, cache) 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")) From db4bcd80e5c83545b21171744d92bebc7192b669 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 28 May 2018 12:24:18 +0200 Subject: [PATCH 026/158] jsgen refactoring: use graph.backend field instead of a global variable --- compiler/jsgen.nim | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 747572dd3e..00fe46eb6a 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -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 @@ -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 = ( @@ -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,6 +2262,7 @@ 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(m.config, FileIndex m.module.position) else: "nimsystem" @@ -2280,10 +2282,7 @@ proc myOpenCached(graph: ModuleGraph; s: PSym, rd: PRodReader): PPassContext = result = nil proc myOpen(graph: ModuleGraph; s: PSym; cache: IdentCache): PPassContext = - var r = newModule(s) - r.graph = graph - r.config = graph.config - result = r + result = newModule(graph, s) const JSgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose) From 5d45e630c55c211f15efcd3820bd752b658db538 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 28 May 2018 13:57:16 +0200 Subject: [PATCH 027/158] vm.nim: evalMacroCounter is not a global variable anymore --- compiler/options.nim | 1 + compiler/vm.nim | 11 +++++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/options.nim b/compiler/options.nim index 6cfaad9fb7..04444f7c9e 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -163,6 +163,7 @@ type globalOptions*: TGlobalOptions m*: MsgConfig evalTemplateCounter*: int + evalMacroCounter*: int exitcode*: int8 cmd*: TCommands # the command selectedGC*: TGCMode # the selected GC diff --git a/compiler/vm.nim b/compiler/vm.nim index e5613718f3..8ae8523cf5 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1748,13 +1748,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; 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 @@ -1795,12 +1794,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 +1807,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 From 382bc34f932eb5e9728ee5380a5666ca1ffd2f92 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 28 May 2018 14:41:49 +0200 Subject: [PATCH 028/158] vm now free of global variables --- compiler/modulegraphs.nim | 2 ++ compiler/scriptconfig.nim | 3 +-- compiler/vm.nim | 22 ++++++++-------------- nimsuggest/nimsuggest.nim | 2 +- 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 4817bc466b..100f69c1de 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -41,6 +41,8 @@ type 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] diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim index 305768d15d..ac86e8e0ce 100644 --- a/compiler/scriptconfig.nim +++ b/compiler/scriptconfig.nim @@ -166,14 +166,13 @@ proc runNimScript*(cache: IdentCache; scriptName: string; 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) # 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/vm.nim b/compiler/vm.nim index 8ae8523cf5..c7b68a24cf 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1654,18 +1654,12 @@ 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) + if graph.vm.isNil: + graph.vm = newCtx(module, cache, graph) + registerAdditionalOps(PCtx graph.vm) else: - refresh(globalCtx, module) + refresh(PCtx graph.vm, module) proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = #var c = newEvalContext(module, emRepl) @@ -1674,9 +1668,9 @@ proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = # XXX produce a new 'globals' environment here: setupGlobalCtx(module, cache, graph) - result = globalCtx + 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) @@ -1698,7 +1692,7 @@ proc evalConstExprAux(module: PSym; cache: IdentCache; mode: TEvalMode): PNode = let n = transformExpr(g, module, n) setupGlobalCtx(module, cache, g) - var c = globalCtx + var c = PCtx g.vm let oldMode = c.mode defer: c.mode = oldMode c.mode = mode @@ -1763,7 +1757,7 @@ proc evalMacroCall*(module: PSym; cache: IdentCache; g: ModuleGraph; n.renderTree, $(n.safeLen-1), $(sym.typ.len-1)]) setupGlobalCtx(module, cache, g) - var c = globalCtx + var c = PCtx g.vm c.comesFromHeuristic.line = 0'u16 c.callsite = nOrig diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim index 5e04a57789..4956f4bf27 100644 --- a/nimsuggest/nimsuggest.nim +++ b/nimsuggest/nimsuggest.nim @@ -438,7 +438,7 @@ proc execCmd(cmd: string; graph: ModuleGraph; cache: IdentCache; cachedMsgs: Cac proc recompileFullProject(graph: ModuleGraph; cache: IdentCache) = #echo "recompiling full project" resetSystemArtifacts(graph) - vm.globalCtx = nil + graph.vm = nil graph.resetAllModules() GC_fullcollect() compileProject(graph, cache) From e5281f9356916aeb13ed16c0abe4c5d943872aa8 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 28 May 2018 18:01:56 +0200 Subject: [PATCH 029/158] rename 'nimrodVM' to 'nimVM' --- compiler/main.nim | 2 -- compiler/nimeval.nim | 2 +- compiler/platform.nim | 8 ++++---- lib/system/platforms.nim | 4 ++-- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/compiler/main.nim b/compiler/main.nim index 2489b1c503..aa0cc9b392 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -100,8 +100,6 @@ proc commandCompileToJS(graph: ModuleGraph; cache: IdentCache) = compileProject(graph, cache) proc interactivePasses(graph: ModuleGraph; cache: IdentCache) = - #incl(gGlobalOptions, optSafeCode) - #setTarget(osNimrodVM, cpuNimrodVM) initDefines(graph.config.symbols) defineSymbol(graph.config.symbols, "nimscript") when hasFFI: defineSymbol(graph.config.symbols, "nimffi") diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim index ff91861d08..714efcdda0 100644 --- a/compiler/nimeval.nim +++ b/compiler/nimeval.nim @@ -19,7 +19,7 @@ proc execute*(program: string) = loadConfigs(DefaultConfig) initDefines() - defineSymbol("nimrodvm") + defineSymbol("nimvm") defineSymbol("nimscript") when hasFFI: defineSymbol("nimffi") registerPass(verbosePass) diff --git a/compiler/platform.nim b/compiler/platform.nim index 173cfa8e3a..0db16f26c8 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), diff --git a/lib/system/platforms.nim b/lib/system/platforms.nim index 97f97e8ae6..b561cd3ba2 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. From 5ad1acc60ce6d25e6491c60afc1605d6cd76fc5a Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 28 May 2018 18:18:43 +0200 Subject: [PATCH 030/158] remove the last global variables in the C code generator --- compiler/ccgthreadvars.nim | 27 +++++++-------------------- compiler/cgendata.nim | 13 ++++++++++++- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/compiler/ccgthreadvars.nim b/compiler/ccgthreadvars.nim index da5c624b79..3e8a870410 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/cgendata.nim b/compiler/cgendata.nim index daad1b1ce9..aba317e7c6 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -119,6 +119,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] @@ -177,7 +188,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): From cd1b0aa87e9b549e301d5770b47c7b91232c44c5 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 28 May 2018 19:45:12 +0200 Subject: [PATCH 031/158] cleanup old allany.nim example --- examples/allany.nim | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/examples/allany.nim b/examples/allany.nim index 6ff055aa66..8a5ab81f05 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" From 83834be938d64e58888b13d8321104da29686559 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 28 May 2018 19:49:20 +0200 Subject: [PATCH 032/158] cleanup examples/readme.txt --- examples/readme.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/examples/readme.txt b/examples/readme.txt index 176bc8239f..686271660d 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. From b92fcacb9995064a500e0d45485ebd5a1db1f237 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 28 May 2018 23:48:25 +0200 Subject: [PATCH 033/158] more compiler API cleanups --- compiler/main.nim | 42 +++++++++++++++++++-------------------- compiler/passes.nim | 4 ++-- compiler/scriptconfig.nim | 7 +++---- 3 files changed, 25 insertions(+), 28 deletions(-) diff --git a/compiler/main.nim b/compiler/main.nim index aa0cc9b392..c5b2ddca5a 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -22,14 +22,14 @@ from magicsys import resetSysTypes proc rodPass(g: ModuleGraph) = if g.config.symbolFiles in {enabledSf, writeOnlySf}: - registerPass(rodwritePass) + registerPass(g, 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) @@ -42,8 +42,8 @@ proc writeDepsFile(g: ModuleGraph; project: string) = f.close() proc commandGenDepend(graph: ModuleGraph; cache: IdentCache) = - semanticPasses() - registerPass(gendependPass) + semanticPasses(graph) + registerPass(graph, gendependPass) #registerPass(cleanupPass) compileProject(graph, cache) let project = graph.config.projectFull @@ -55,15 +55,15 @@ proc commandGenDepend(graph: ModuleGraph; cache: IdentCache) = proc commandCheck(graph: ModuleGraph; cache: IdentCache) = 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 + semanticPasses(graph) # use an empty backend for semantic checking only rodPass(graph) compileProject(graph, cache) proc commandDoc2(graph: ModuleGraph; cache: IdentCache; json: bool) = graph.config.errorMax = high(int) # do not stop after first error - semanticPasses() - if json: registerPass(docgen2JsonPass) - else: registerPass(docgen2Pass) + semanticPasses(graph) + if json: registerPass(graph, docgen2JsonPass) + else: registerPass(graph, docgen2Pass) #registerPass(cleanupPass()) compileProject(graph, cache) finishDoc2Pass(graph.config.projectName) @@ -71,8 +71,8 @@ proc commandDoc2(graph: ModuleGraph; cache: IdentCache; json: bool) = proc commandCompileToC(graph: ModuleGraph; cache: IdentCache) = let conf = graph.config extccomp.initVars(conf) - semanticPasses() - registerPass(cgenPass) + semanticPasses(graph) + registerPass(graph, cgenPass) rodPass(graph) #registerPass(cleanupPass()) @@ -95,17 +95,17 @@ proc commandCompileToJS(graph: ModuleGraph; cache: IdentCache) = #initDefines() defineSymbol(graph.config.symbols, "ecmascript") # For backward compatibility defineSymbol(graph.config.symbols, "js") - semanticPasses() - registerPass(JSgenPass) + semanticPasses(graph) + registerPass(graph, JSgenPass) compileProject(graph, cache) proc interactivePasses(graph: ModuleGraph; cache: IdentCache) = 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) = graph.config.errorMax = high(int) # do not stop after first error @@ -156,7 +156,7 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) = setupModuleCache() # In "nim serve" scenario, each command must reset the registered passes - clearPasses() + clearPasses(graph) conf.lastCmdTime = epochTime() conf.searchPaths.add(conf.libpath) setId(100) @@ -300,5 +300,3 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) = ffDecimal, 3) resetAttributes(conf) - -#proc mainCommand*() = mainCommand(newModuleGraph(newConfigRef()), newIdentCache()) diff --git a/compiler/passes.nim b/compiler/passes.nim index 3fe7ce4816..9be7d060ae 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -74,10 +74,10 @@ 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) diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim index ac86e8e0ce..30bdf162ed 100644 --- a/compiler/scriptconfig.nim +++ b/compiler/scriptconfig.nim @@ -152,15 +152,14 @@ 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(cache, conf) 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) From ae1f6895faa645625a7e3486988d25c6d06b0e49 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 29 May 2018 00:55:31 +0200 Subject: [PATCH 034/158] active.nim: fixes the import path so that compilation with --noNimblePath works --- compiler/plugins/active.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/plugins/active.nim b/compiler/plugins/active.nim index 4e82e58cb4..7b5306f9cd 100644 --- a/compiler/plugins/active.nim +++ b/compiler/plugins/active.nim @@ -10,7 +10,7 @@ ## Include file that imports all plugins that are active. import - compiler / [pluginsupport, idents, ast], locals, itersgen + "../compiler" / [pluginsupport, idents, ast], locals, itersgen const plugins: array[2, Plugin] = [ From 06122ff7116e699a50c968bb0192e6faf00fa6ef Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 29 May 2018 01:18:50 +0200 Subject: [PATCH 035/158] rewrote nimeval.nim; added tcompilerapi example to show how the compiler can be used as an API --- changelog.md | 5 ++ compiler/nimeval.nim | 121 ++++++++++++++++++++++++----- tests/compilerapi/exposed.nim | 3 + tests/compilerapi/myscript.nim | 7 ++ tests/compilerapi/tcompilerapi.nim | 36 +++++++++ 5 files changed, 152 insertions(+), 20 deletions(-) create mode 100644 tests/compilerapi/exposed.nim create mode 100644 tests/compilerapi/myscript.nim create mode 100644 tests/compilerapi/tcompilerapi.nim diff --git a/changelog.md b/changelog.md index 813c4caf84..46a2217006 100644 --- a/changelog.md +++ b/changelog.md @@ -78,6 +78,11 @@ - Added the parameter ``val`` for the ``CritBitTree[int].inc`` proc. - An exception raised from ``test`` block of ``unittest`` now show 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. + ### Language additions diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim index 714efcdda0..d5df6a9df7 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,108 @@ ## exposes the Nim VM to clients. import - ast, modules, passes, passaux, condsyms, - options, nimconf, sem, semdata, llstream, vm, modulegraphs, idents + ast, astalgo, modules, passes, condsyms, + options, sem, semdata, llstream, vm, vmdef, + modulegraphs, idents, os -proc execute*(program: string) = - passes.gIncludeFile = includeModule - passes.gImportModule = importModule - initDefines() - loadConfigs(DefaultConfig) +type + Interpreter* = ref object ## Use Nim as an interpreter with this object + mainModule: PSym + graph: ModuleGraph + scriptName: string - initDefines() - defineSymbol("nimvm") - defineSymbol("nimscript") - when hasFFI: defineSymbol("nimffi") - registerPass(verbosePass) - registerPass(semPass) - registerPass(evalPass) +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) - searchPaths.add options.libpath - var graph = newModuleGraph() +proc selectUniqueSymbol*(i: Interpreter; name: string; + symKinds: set[TSymKind]): 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 declareRoutine*(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, nil, i.graph.cache) + +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) + 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(cache) + result = Interpreter(mainModule: m, graph: graph, scriptName: scriptName) + +proc destroyInterpreter*(i: Interpreter) = + ## destructor. + discard "currently nothing to do." diff --git a/tests/compilerapi/exposed.nim b/tests/compilerapi/exposed.nim new file mode 100644 index 0000000000..73becd93da --- /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 0000000000..083385b6f3 --- /dev/null +++ b/tests/compilerapi/myscript.nim @@ -0,0 +1,7 @@ + +import exposed + +echo "top level statements are executed!" + +proc hostProgramRunsThis*(a, b: float): float = + result = addFloats(a, b, 1.0) diff --git a/tests/compilerapi/tcompilerapi.nim b/tests/compilerapi/tcompilerapi.nim new file mode 100644 index 0000000000..00c9bc5231 --- /dev/null +++ b/tests/compilerapi/tcompilerapi.nim @@ -0,0 +1,36 @@ +discard """ + output: '''top level statements are executed! +2.0 +''' +""" + +## 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.declareRoutine("*", "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!" + destroyInterpreter(intr) + +main() \ No newline at end of file From a5701d6b71853e683f4d655b6b5ea4a13bec993b Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 29 May 2018 08:22:09 +0200 Subject: [PATCH 036/158] ensure compiler API doesn't import the RST engine --- compiler/sigmatch.nim | 2 +- compiler/suggest.nim | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 0b51cafdcf..ab5ac7b455 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -15,7 +15,7 @@ import magicsys, condsyms, idents, lexer, options, parampatterns, strutils, trees, nimfix / pretty, lineinfos -when not defined(noDocgen): +when defined(booting) or defined(nimsuggest): import docgen type diff --git a/compiler/suggest.nim b/compiler/suggest.nim index 391906c3f0..b66dbce689 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -115,7 +115,7 @@ 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(conf, infox) @@ -153,7 +153,7 @@ 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 suggest.version == 0: result.add(sep) From 688c54d8f158ff977161a234374b1cd321ba95f3 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 29 May 2018 09:07:24 +0200 Subject: [PATCH 037/158] compiler API: final cleanups; improve security by diabling 'gorge' and friends --- compiler/main.nim | 3 +++ compiler/nim.cfg | 2 +- compiler/nimeval.nim | 9 ++++++--- compiler/vm.nim | 19 +++++++++++-------- compiler/vmops.nim | 23 ++++++++++++----------- nimsuggest/nimsuggest.nim | 9 ++++++--- nimsuggest/nimsuggest.nim.cfg | 3 ++- tests/compilerapi/myscript.nim | 2 ++ tests/compilerapi/tcompilerapi.nim | 13 ++++++++++++- 9 files changed, 55 insertions(+), 28 deletions(-) diff --git a/compiler/main.nim b/compiler/main.nim index c5b2ddca5a..ba2537ef8f 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -9,6 +9,9 @@ # 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, diff --git a/compiler/nim.cfg b/compiler/nim.cfg index 853ae7e00d..f4211fae58 100644 --- a/compiler/nim.cfg +++ b/compiler/nim.cfg @@ -5,6 +5,7 @@ path:"llvm" path:"$projectPath/.." define:booting +define:nimcore #import:"$projectpath/testability" @if windows: @@ -13,6 +14,5 @@ define:booting define:useStdoutAsStdmsg -cs:partial #define:useNodeIds #gc:markAndSweep diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim index d5df6a9df7..dde6039ba8 100644 --- a/compiler/nimeval.nim +++ b/compiler/nimeval.nim @@ -29,7 +29,7 @@ iterator exportedSymbols*(i: Interpreter): PSym = s = nextIter(it, i.mainModule.tab) proc selectUniqueSymbol*(i: Interpreter; name: string; - symKinds: set[TSymKind]): PSym = + symKinds: set[TSymKind] = {skLet, skVar}): PSym = ## Can be used to access a unique symbol of ``name`` and ## the given ``symKinds`` filter. assert i != nil @@ -55,8 +55,11 @@ proc callRoutine*(i: Interpreter; routine: PSym; args: openArray[PNode]): PNode assert i != nil result = vm.execProc(PCtx i.graph.vm, routine, args) -proc declareRoutine*(i: Interpreter; pkg, module, name: string; - impl: proc (a: VmArgs) {.closure, gcsafe.}) = +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) diff --git a/compiler/vm.nim b/compiler/vm.nim index c7b68a24cf..b1b8132e2d 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -972,7 +972,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: @@ -1336,14 +1336,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 + 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] + 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 diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 617295b0da..a7d47d7a34 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/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim index 4956f4bf27..dd52e03838 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. @@ -486,9 +489,9 @@ var proc mainCommand(graph: ModuleGraph; cache: IdentCache) = 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) diff --git a/nimsuggest/nimsuggest.nim.cfg b/nimsuggest/nimsuggest.nim.cfg index 38e74b3c7d..820db0dba9 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/compilerapi/myscript.nim b/tests/compilerapi/myscript.nim index 083385b6f3..539b07de11 100644 --- a/tests/compilerapi/myscript.nim +++ b/tests/compilerapi/myscript.nim @@ -5,3 +5,5 @@ 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 index 00c9bc5231..90d3432644 100644 --- a/tests/compilerapi/tcompilerapi.nim +++ b/tests/compilerapi/tcompilerapi.nim @@ -1,6 +1,7 @@ discard """ output: '''top level statements are executed! 2.0 +my secret ''' """ @@ -16,7 +17,7 @@ proc main() = quit "cannot find Nim's standard library" var intr = createInterpreter("myscript.nim", [std, getAppDir()]) - intr.declareRoutine("*", "exposed", "addFloats", proc (a: VmArgs) = + intr.implementRoutine("*", "exposed", "addFloats", proc (a: VmArgs) = setResult(a, getFloat(a, 0) + getFloat(a, 1) + getFloat(a, 2)) ) @@ -31,6 +32,16 @@ proc main() = 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 From ddc6cec69e66f4cc28275f61b73962042e257575 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 30 May 2018 10:15:41 +0200 Subject: [PATCH 038/158] refactoring: move DB model to incremental.nim --- compiler/btrees.nim | 186 +++++++++++++++++++++++++++++++++++++ compiler/incremental.nim | 188 ++++++++++++++++++++++++++++++++++++++ compiler/main.nim | 2 +- compiler/modulegraphs.nim | 11 ++- compiler/modules.nim | 6 +- compiler/passes.nim | 2 +- compiler/rod.nim | 19 ++-- compiler/rodimpl.nim | 149 +++--------------------------- compiler/rodwrite.nim | 6 -- compiler/sem.nim | 4 +- 10 files changed, 410 insertions(+), 163 deletions(-) create mode 100644 compiler/btrees.nim create mode 100644 compiler/incremental.nim diff --git a/compiler/btrees.nim b/compiler/btrees.nim new file mode 100644 index 0000000000..2284816928 --- /dev/null +++ b/compiler/btrees.nim @@ -0,0 +1,186 @@ +# +# +# 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] + height: int + 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 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 + inc b.height + +proc toString[Key, Val](h: Node[Key, Val], indent: string; result: var string) = + if not h.isInternal: + for j in 0.. 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) + +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) + + 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 + echo b2.height + + 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/incremental.nim b/compiler/incremental.nim new file mode 100644 index 0000000000..69dfb727ef --- /dev/null +++ b/compiler/incremental.nim @@ -0,0 +1,188 @@ +# +# +# 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, intsets, btrees, db_sqlite + + 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 + + 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(fileIdx) + if result.len == 0: + result = $secureHashFile(fullpath) + msgs.setHash(fileIdx, result) + + + 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*(incr: var IncrementalCtx; conf: ConfigRef; dbId: int): FileIndex = + 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(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 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)" + + +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/main.nim b/compiler/main.nim index ba2537ef8f..0929974bd3 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -157,7 +157,7 @@ const proc mainCommand*(graph: ModuleGraph; cache: IdentCache) = let conf = graph.config - setupModuleCache() + setupModuleCache(graph) # In "nim serve" scenario, each command must reset the registered passes clearPasses(graph) conf.lastCmdTime = epochTime() diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 100f69c1de..7c9837f54b 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, lineinfos, hashes, idents +import ast, intsets, tables, options, lineinfos, hashes, idents, + incremental type ModuleGraph* = ref object @@ -54,6 +55,7 @@ type intTypeCache*: array[-5..64, PType] opContains*, opNot*: PSym emptyNode*: PNode + incr*: IncrementalCtx proc hash*(x: FileIndex): Hash {.borrow.} @@ -82,6 +84,7 @@ proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph = result.opNot = createMagic(result, "not", mNot) result.opContains = createMagic(result, "contains", mInSet) result.emptyNode = newNode(nkEmpty) + init(result.incr) proc resetAllModules*(g: ModuleGraph) = initStrTable(packageSyms) @@ -103,7 +106,7 @@ 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. @@ -111,7 +114,7 @@ proc addDep*(g: ModuleGraph; m: PSym, dep: FileIndex) = #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/modules.nim b/compiler/modules.nim index 9ac3dbb0bb..61568e67c2 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -75,7 +75,7 @@ proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; cache: IdentCache, f return else: discard - result.id = getModuleId(fileIdx, toFullPath(graph.config, fileIdx)) + 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) @@ -107,8 +107,8 @@ proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex; # localError(result.info, errAttemptToRedefine, result.name.s) # restore the notes for outer module: graph.config.notes = - if s.owner.id == graph.config.mainPackageId: 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.} = diff --git a/compiler/passes.nim b/compiler/passes.nim index 9be7d060ae..4ba17adcc9 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -180,7 +180,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream, var stmtIndex = 0 var doContinue = true while doContinue: - let n = loadNode(module, stmtIndex) + let n = loadNode(graph, module, stmtIndex) if n == nil or graph.stopCompile(): break #if n.kind == nkImportStmt: # echo "yes and it's ", n diff --git a/compiler/rod.nim b/compiler/rod.nim index 82f556b3b1..1b5331ba72 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, lineinfos, 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; index: var int): PNode = PNode(nil) - 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 aff4f6909e..1a92ac64b6 100644 --- a/compiler/rodimpl.nim +++ b/compiler/rodimpl.nim @@ -22,15 +22,10 @@ import strutils, os, intsets, tables, ropes, db_sqlite, msgs, options, types, ## - Depencency computation should use signature hashes in order to ## avoid recompiling dependent modules. -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 needsRecompile(fileIdx: int32; fullpath: string; cycleCheck: var IntSet): bool = +proc needsRecompile(g: ModuleGraph; fileIdx: int32; 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 @@ -43,11 +38,11 @@ proc needsRecompile(fileIdx: int32; fullpath: string; cycleCheck: var IntSet): b 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, dep.fileInfoIdx, dep, cycleCheck): return true return false -proc getModuleId*(fileIdx: int32; fullpath: string): int = +proc getModuleId*(g: ModuleGraph; fileIdx: int32; fullpath: string): int = if gSymbolFiles != v2Sf: return getID() let module = db.getRow( sql"select id, fullHash from modules where fullpath = ?", fullpath) @@ -103,27 +98,6 @@ proc pushSym(w: PRodWriter, 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) - proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode, result: var string) = if n == nil: @@ -395,7 +369,7 @@ proc storeType(w: PRodWriter; t: PType) = var w = initRodWriter(nil) -proc storeNode*(module: PSym; n: PNode) = +proc storeNode*(g: ModuleGraph; module: PSym; n: PNode) = if gSymbolFiles != v2Sf: return w.module = module var buf = newStringOfCap(160) @@ -421,7 +395,7 @@ proc storeNode*(module: PSym; n: PNode) = break inc i -proc storeRemaining*(module: PSym) = +proc storeRemaining*(g: ModuleGraph; module: PSym) = if gSymbolFiles != v2Sf: return w.module = module for s in w.forwardedSyms: @@ -437,7 +411,7 @@ type #tstack: seq[(PType, ptr PType)] # a stack of types to process #tmarks, smarks: IntSet - syms: Table[int, PSym] ## XXX make this more efficients + syms: Table[int, PSym] ## XXX make this more efficient types: Table[int, PType] cache: IdentCache @@ -813,7 +787,7 @@ proc loadModuleSymTab(r; module: PSym) = if sfSystemModule in module.flags: magicsys.systemModule = module -proc loadNode*(module: PSym; index: int): PNode = +proc loadNode*(g: ModuleGraph; module: PSym; index: int): PNode = assert gSymbolFiles == v2Sf if index == 0: loadModuleSymTab(gr, module) @@ -828,110 +802,9 @@ proc loadNode*(module: PSym; index: int): PNode = gr.module = module result = decodeNode(gr, b, module.info) -proc addModuleDep*(module, fileIdx: int32; isIncludeFile: bool) = +proc setupModuleCache*(g: ModuleGraph) = 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" + let dbfile = getNimcacheDir(g.config) / "rodfiles.db" if not fileExists(dbfile): db = open(connection=dbfile, user="nim", password="", database="nim") diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim index 0b27418b33..968cb9e7fe 100644 --- a/compiler/rodwrite.nim +++ b/compiler/rodwrite.nim @@ -622,12 +622,6 @@ proc process(c: PPassContext, n: PNode): PNode = # 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) diff --git a/compiler/sem.nim b/compiler/sem.nim index 52042fa7d1..d0f1065261 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -597,7 +597,7 @@ proc myProcess(context: PPassContext, n: PNode): PNode = else: 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.config, c.module.info) @@ -627,7 +627,7 @@ proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode = 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, From 31d9df9e75f2e3e1499d1510d196edaeeb2db2f3 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 30 May 2018 11:34:03 +0200 Subject: [PATCH 039/158] runnableExamples: use the self exe to test the examples --- compiler/sem.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/sem.nim b/compiler/sem.nim index d0f1065261..01e816f5cc 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -607,7 +607,7 @@ proc testExamples(c: PContext) = 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) From a36c779f398d786082dc8d53412f8a9aaebf637b Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 30 May 2018 20:27:41 +0200 Subject: [PATCH 040/158] incremental compilation subsystem compiles again --- compiler/incremental.nim | 26 ++-- compiler/nim.cfg | 1 + compiler/rodimpl.nim | 310 +++++++++++++++++---------------------- 3 files changed, 149 insertions(+), 188 deletions(-) diff --git a/compiler/incremental.nim b/compiler/incremental.nim index 69dfb727ef..378ba0665a 100644 --- a/compiler/incremental.nim +++ b/compiler/incremental.nim @@ -15,7 +15,8 @@ const nimIncremental* = defined(nimIncremental) import options, lineinfos when nimIncremental: - import ast, intsets, btrees, db_sqlite + import ast, msgs, intsets, btrees, db_sqlite, std / sha1 + from strutils import parseInt type Writer* = object @@ -44,30 +45,29 @@ when nimIncremental: proc hashFileCached*(conf: ConfigRef; fileIdx: FileIndex; fullpath: string): string = - result = msgs.getHash(fileIdx) + result = msgs.getHash(conf, fileIdx) if result.len == 0: result = $secureHashFile(fullpath) - msgs.setHash(fileIdx, result) + msgs.setHash(conf, fileIdx, result) - - 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 = ?", + 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(fileIdx, fullpath) + let fullhash = hashFileCached(conf, fileIdx, fullpath) if id.len == 0: - result = int db.insertID(sql"insert into filenames(fullpath, fullhash) values (?, ?)", + result = int incr.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) + 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 -1 - let fullpath = db.getValue(sql"select fullpath from filenames where id = ?", dbId) + 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) diff --git a/compiler/nim.cfg b/compiler/nim.cfg index f4211fae58..9cdb9c6c9a 100644 --- a/compiler/nim.cfg +++ b/compiler/nim.cfg @@ -6,6 +6,7 @@ path:"$projectPath/.." define:booting define:nimcore +define:nimIncremental #import:"$projectpath/testability" @if windows: diff --git a/compiler/rodimpl.nim b/compiler/rodimpl.nim index 1a92ac64b6..0bb94bf229 100644 --- a/compiler/rodimpl.nim +++ b/compiler/rodimpl.nim @@ -10,7 +10,7 @@ ## 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 ## Todo: ## - Implement the 'import' replay logic so that the codegen runs over @@ -19,45 +19,44 @@ import strutils, os, intsets, tables, ropes, db_sqlite, msgs, options, types, ## dependencies. ## - Test multi methods. ## - Implement the limited VM support based on sets. -## - Depencency computation should use signature hashes in order to +## - Depencency computation should use *signature* hashes in order to ## avoid recompiling dependent modules. template db(): DbConn = g.incr.db -proc needsRecompile(g: ModuleGraph; fileIdx: int32; fullpath: string; +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(g, dep.fileInfoIdx, dep, cycleCheck): + if needsRecompile(g, g.config.fileInfoIdx(dep), dep, cycleCheck): return true return false -proc getModuleId*(g: ModuleGraph; fileIdx: int32; fullpath: string): int = - if gSymbolFiles != v2Sf: return getID() - let module = db.getRow( +proc getModuleId*(g: ModuleGraph; fileIdx: FileIndex; fullpath: string): int = + if g.config.symbolFiles != v2Sf: return getID() + let module = g.incr.db.getRow( sql"select id, fullHash from modules where fullpath = ?", fullpath) - let currentFullhash = hashFileCached(fileIdx, 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) else: result = parseInt(module[0]) 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): return -result db.exec(sql"update modules set fullHash = ? where id = ?", currentFullhash, module[0]) db.exec(sql"delete from deps where module = ?", module[0]) @@ -66,20 +65,6 @@ proc getModuleId*(g: ModuleGraph; 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 = "" @@ -87,18 +72,17 @@ when false: 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 encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode, +template w: untyped = g.incr.w + +proc encodeNode(g: ModuleGraph; fInfo: TLineInfo, n: PNode, result: var string) = if n == nil: # nil nodes have to be stored too: @@ -113,14 +97,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) @@ -156,10 +140,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) @@ -171,7 +155,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) + encodeNode(g, unknownLineInfo(), loc.lode, result) #encodeVInt(cast[int32](loc.t.id), result) #pushType(w, loc.t) if loc.r != nil: @@ -183,13 +167,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, '[') @@ -197,7 +181,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) @@ -243,7 +227,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, "^()") @@ -252,15 +236,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') @@ -273,7 +257,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("{}") @@ -291,9 +275,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) @@ -312,11 +296,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: @@ -324,13 +308,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: @@ -348,47 +332,44 @@ 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. 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, abs(getModule(s).id), 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) db.exec(sql"insert into types(nimid, module, data) values (?, ?, ?)", - t.id, abs(w.module.id), buf) - -var w = initRodWriter(nil) + t.id, abs(getModule(t.owner).id), buf) proc storeNode*(g: ModuleGraph; module: PSym; n: PNode) = - if gSymbolFiles != v2Sf: return - w.module = module + if g.config.symbolFiles != v2Sf: 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: @@ -396,57 +377,39 @@ proc storeNode*(g: ModuleGraph; module: PSym; n: PNode) = inc i proc storeRemaining*(g: ModuleGraph; module: PSym) = - if gSymbolFiles != v2Sf: return - w.module = module + if g.config.symbolFiles != v2Sf: return for s in w.forwardedSyms: assert sfForward notin s.flags - storeSym(w, s) + storeSym(g, s) w.forwardedSyms.setLen 0 # ---------------- 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 efficient - types: Table[int, PType] - cache: IdentCache - 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 + r: var Reader 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] == '(' @@ -462,7 +425,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] == '(': @@ -471,14 +434,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] == '!': @@ -499,16 +462,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] != ')': @@ -519,17 +482,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'}: @@ -548,7 +511,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 @@ -558,19 +521,19 @@ 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) -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) @@ -585,10 +548,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))) @@ -597,10 +560,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) @@ -620,65 +583,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] == '}': @@ -691,26 +654,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))) @@ -720,8 +683,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) @@ -729,28 +690,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 @@ -760,37 +721,37 @@ 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) + s = loadSymFromBlob(g, b, module.info) assert s != nil strTableAdd(module.tab, s) if sfSystemModule in module.flags: - magicsys.systemModule = module + g.systemModule = module proc loadNode*(g: ModuleGraph; module: PSym; index: int): PNode = - assert gSymbolFiles == v2Sf + assert g.config.symbolFiles == v2Sf if index == 0: - loadModuleSymTab(gr, module) + loadModuleSymTab(g, module) #index = parseInt db.getValue( # sql"select min(id) from toplevelstmts where module = ?", abs module.id) var b = BlobReader(pos: 0) @@ -799,16 +760,15 @@ proc loadNode*(g: ModuleGraph; module: PSym; index: int): PNode = 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) + result = decodeNode(g, b, module.info) proc setupModuleCache*(g: ModuleGraph) = - if gSymbolFiles != v2Sf: return + if g.config.symbolFiles != v2Sf: return let dbfile = getNimcacheDir(g.config) / "rodfiles.db" if not fileExists(dbfile): db = open(connection=dbfile, user="nim", password="", database="nim") - createDb() + createDb(db) else: db = open(connection=dbfile, user="nim", password="", database="nim") From 61fb83ecbb4c691c03d500f6c71499e59a67cef2 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 30 May 2018 23:50:34 +0200 Subject: [PATCH 041/158] baby steps for incremental compilation --- compiler/ast.nim | 2 + compiler/cgen.nim | 17 +- compiler/commands.nim | 4 +- compiler/destroyer.nim | 2 +- compiler/evaltempl.nim | 2 +- compiler/importer.nim | 5 +- compiler/jsgen.nim | 4 +- compiler/lambdalifting.nim | 2 +- compiler/lookups.nim | 14 +- compiler/magicsys.nim | 11 +- compiler/main.nim | 11 +- compiler/modules.nim | 6 +- compiler/options.nim | 4 +- compiler/passes.nim | 15 +- compiler/plugins/itersgen.nim | 2 +- compiler/pragmas.nim | 5 +- compiler/rodimpl.nim | 31 +- compiler/rodread.nim | 1245 --------------------------------- compiler/rodwrite.nim | 653 ----------------- compiler/sem.nim | 9 +- compiler/semdata.nim | 2 +- compiler/transf.nim | 4 +- compiler/vmgen.nim | 2 +- 23 files changed, 70 insertions(+), 1982 deletions(-) delete mode 100644 compiler/rodread.nim delete mode 100644 compiler/rodwrite.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 592c8fbe9a..1f5b4927e3 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1724,3 +1724,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/cgen.nim b/compiler/cgen.nim index 2d4fbf1740..92e4898e49 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -12,7 +12,7 @@ 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, lineinfos @@ -1306,7 +1306,9 @@ proc rawNewModule(g: BModuleList; module: PSym; conf: ConfigRef): 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, conf) - growCache g.modules, module.position + 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.} = @@ -1417,7 +1419,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: @@ -1476,7 +1478,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) @@ -1499,13 +1500,9 @@ 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) diff --git a/compiler/commands.nim b/compiler/commands.nim index efb7e24279..cd9ebbe7d3 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -626,9 +626,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 diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim index cf56c6e36c..0395728c28 100644 --- a/compiler/destroyer.nim +++ b/compiler/destroyer.nim @@ -116,7 +116,7 @@ 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, + strutils, options, dfa, lowerings, tables, modulegraphs, lineinfos const diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index e3dd0f3426..d6c630e79b 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 + lineinfos type TemplCtx = object diff --git a/compiler/importer.nim b/compiler/importer.nim index dd3eb95d49..f30c23731b 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -10,7 +10,7 @@ # This module implements the symbol importing mechanism. import - intsets, strutils, os, ast, astalgo, msgs, options, idents, rodread, lookups, + intsets, strutils, os, ast, astalgo, msgs, options, idents, lookups, semdata, passes, renderer, modulepaths, sigmatch, lineinfos proc evalImport*(c: PContext, n: PNode): PNode @@ -74,7 +74,8 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym) = 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 diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 00fe46eb6a..3c57f97afd 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, lineinfos + times, ropes, math, passes, ccgutils, wordrecg, renderer, + intsets, cgmeth, lowerings, sighashes, lineinfos, rodutils from modulegraphs import ModuleGraph diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 738d110506..6fb273164d 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -11,7 +11,7 @@ import intsets, strutils, options, ast, astalgo, trees, treetab, msgs, os, - idents, renderer, types, magicsys, rodread, lowerings, tables, + idents, renderer, types, magicsys, lowerings, tables, modulegraphs, lineinfos discard """ diff --git a/compiler/lookups.nim b/compiler/lookups.nim index cf77f5f7ad..87694988ad 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -10,7 +10,7 @@ # This module implements lookup helpers. import - intsets, ast, astalgo, idents, semdata, types, msgs, options, rodread, + intsets, ast, astalgo, idents, semdata, types, msgs, options, renderer, wordrecg, idgen, nimfix.prettybase, lineinfos, strutils proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) @@ -288,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 @@ -343,7 +344,8 @@ 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 @@ -392,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 @@ -443,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/magicsys.nim b/compiler/magicsys.nim index af89c6d69a..d40b9d732f 100644 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -10,7 +10,7 @@ # Built-in types and compilerprocs are registered here. import - ast, astalgo, hashes, msgs, platform, nversion, times, idents, rodread, + ast, astalgo, hashes, msgs, platform, nversion, times, idents, modulegraphs, lineinfos export createMagic @@ -31,7 +31,6 @@ proc getSysSym*(g: ModuleGraph; info: TLineInfo; name: string): PSym = localError(g.config, info, "system module needs: " & name) 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 proc getSysMagic*(g: ModuleGraph; info: TLineInfo; name: string, m: TMagic): PSym = @@ -39,7 +38,6 @@ proc getSysMagic*(g: ModuleGraph; info: TLineInfo; name: string, m: TMagic): PSy 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 @@ -159,13 +157,6 @@ proc setIntLitType*(g: ModuleGraph; result: PNode) = proc getCompilerProc*(g: ModuleGraph; name: string): PSym = 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) diff --git a/compiler/main.nim b/compiler/main.nim index 0929974bd3..97e96dcff8 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -14,7 +14,7 @@ when not defined(nimcore): 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, @@ -23,10 +23,6 @@ import from magicsys import resetSysTypes -proc rodPass(g: ModuleGraph) = - if g.config.symbolFiles in {enabledSf, writeOnlySf}: - registerPass(g, rodwritePass) - proc codegenPass(g: ModuleGraph) = registerPass g, cgenPass @@ -47,7 +43,6 @@ proc writeDepsFile(g: ModuleGraph; project: string) = proc commandGenDepend(graph: ModuleGraph; cache: IdentCache) = semanticPasses(graph) registerPass(graph, gendependPass) - #registerPass(cleanupPass) compileProject(graph, cache) let project = graph.config.projectFull writeDepsFile(graph, project) @@ -59,7 +54,6 @@ proc commandCheck(graph: ModuleGraph; cache: IdentCache) = graph.config.errorMax = high(int) # do not stop after first error defineSymbol(graph.config.symbols, "nimcheck") semanticPasses(graph) # use an empty backend for semantic checking only - rodPass(graph) compileProject(graph, cache) proc commandDoc2(graph: ModuleGraph; cache: IdentCache; json: bool) = @@ -67,7 +61,6 @@ proc commandDoc2(graph: ModuleGraph; cache: IdentCache; json: bool) = semanticPasses(graph) if json: registerPass(graph, docgen2JsonPass) else: registerPass(graph, docgen2Pass) - #registerPass(cleanupPass()) compileProject(graph, cache) finishDoc2Pass(graph.config.projectName) @@ -76,8 +69,6 @@ proc commandCompileToC(graph: ModuleGraph; cache: IdentCache) = extccomp.initVars(conf) semanticPasses(graph) registerPass(graph, cgenPass) - rodPass(graph) - #registerPass(cleanupPass()) compileProject(graph, cache) cgenWriteModules(graph.backend, conf) diff --git a/compiler/modules.nim b/compiler/modules.nim index 61568e67c2..9b04578c0a 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -10,7 +10,7 @@ ## 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, lineinfos @@ -42,7 +42,9 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = 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) diff --git a/compiler/options.nim b/compiler/options.nim index 04444f7c9e..cb4f1e8850 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -121,7 +121,7 @@ type notnil SymbolFilesOption* = enum - disabledSf, enabledSf, writeOnlySf, readOnlySf, v2Sf + disabledSf, writeOnlySf, readOnlySf, v2Sf TSystemCC* = enum ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc, @@ -362,7 +362,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} + conf.symbolFiles in {v2Sf, writeOnlySf} # {optCaasEnabled, optSymbolFiles} * gGlobalOptions != {} template optPreserveOrigSource*(conf: ConfigRef): untyped = diff --git a/compiler/passes.nim b/compiler/passes.nim index 4ba17adcc9..e8fd89025d 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -13,13 +13,13 @@ 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, + nimsets, syntaxes, times, idgen, modulegraphs, reorder, rod, lineinfos type + PRodReader* = ref object TPassContext* = object of RootObj # the pass's context - rd*: PRodReader # != nil if created by "openCached" PPassContext* = ref TPassContext @@ -107,8 +107,6 @@ proc openPassesCached(g: ModuleGraph; a: var TPassContextArray, module: PSym, 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 @@ -198,7 +196,7 @@ 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: + else: openPasses(graph, a, module, cache) if stream == nil: let filename = toFullPathConsiderDirty(graph.config, fileIdx) @@ -241,11 +239,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/plugins/itersgen.nim b/compiler/plugins/itersgen.nim index 847d13ccf7..440d2e0814 100644 --- a/compiler/plugins/itersgen.nim +++ b/compiler/plugins/itersgen.nim @@ -11,7 +11,7 @@ import ".." / [ast, astalgo, magicsys, lookups, semdata, - lambdalifting, rodread, msgs] + lambdalifting, msgs] proc iterToProcImpl*(c: PContext, n: PNode): PNode = result = newNodeI(nkStmtList, n.info) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 77f75922c0..061bbacfa1 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, lineinfos + types, lookups, lineinfos const FirstCallConv* = wNimcall @@ -482,7 +482,8 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode = if 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: diff --git a/compiler/rodimpl.nim b/compiler/rodimpl.nim index 0bb94bf229..360c82a6eb 100644 --- a/compiler/rodimpl.nim +++ b/compiler/rodimpl.nim @@ -43,7 +43,7 @@ proc needsRecompile(g: ModuleGraph; fileIdx: FileIndex; fullpath: string; return false proc getModuleId*(g: ModuleGraph; fileIdx: FileIndex; fullpath: string): int = - if g.config.symbolFiles != v2Sf: return getID() + if g.config.symbolFiles in {disabledSf, writeOnlySf}: return getID() let module = g.incr.db.getRow( sql"select id, fullHash from modules where fullpath = ?", fullpath) let currentFullhash = hashFileCached(g.config, fileIdx, fullpath) @@ -57,6 +57,7 @@ proc getModuleId*(g: ModuleGraph; fileIdx: FileIndex; fullpath: string): int = doAssert(result != 0) var cycleCheck = initIntSet() 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]) @@ -342,17 +343,21 @@ proc storeSym(g: ModuleGraph; s: PSym) = 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(getModule(s).id), s.name.s, buf, ord(sfExported in s.flags)) + s.id, mid, s.name.s, buf, ord(sfExported in s.flags)) proc storeType(g: ModuleGraph; t: PType) = var buf = newStringOfCap(160) 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(getModule(t.owner).id), buf) + t.id, mid, buf) proc storeNode*(g: ModuleGraph; module: PSym; n: PNode) = - if g.config.symbolFiles != v2Sf: return + if g.config.symbolFiles == disabledSf: return var buf = newStringOfCap(160) encodeNode(g, module.info, n, buf) db.exec(sql"insert into toplevelstmts(module, position, data) values (?, ?, ?)", @@ -377,11 +382,14 @@ proc storeNode*(g: ModuleGraph; module: PSym; n: PNode) = inc i proc storeRemaining*(g: ModuleGraph; module: PSym) = - if g.config.symbolFiles != v2Sf: return + if g.config.symbolFiles == disabledSf: return + var stillForwarded: seq[PSym] = @[] for s in w.forwardedSyms: - assert sfForward notin s.flags - storeSym(g, s) - w.forwardedSyms.setLen 0 + if sfForward notin s.flags: + storeSym(g, s) + else: + stillForwarded.add s + swap w.forwardedSyms, stillForwarded # ---------------- decoder ----------------------------------- @@ -529,6 +537,8 @@ proc loadBlob(g; query: SqlQuery; id: int): BlobReader = 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(g; id: int; info: TLineInfo): PType = result = g.incr.r.types.getOrDefault(id) @@ -742,6 +752,8 @@ proc loadModuleSymTab(g; module: PSym) = if s == nil: var b = BlobReader(pos: 0) shallowCopy(b.s, row[1]) + # ensure we can read without index checks: + b.s.add '\0' s = loadSymFromBlob(g, b, module.info) assert s != nil strTableAdd(module.tab, s) @@ -749,7 +761,6 @@ proc loadModuleSymTab(g; module: PSym) = g.systemModule = module proc loadNode*(g: ModuleGraph; module: PSym; index: int): PNode = - assert g.config.symbolFiles == v2Sf if index == 0: loadModuleSymTab(g, module) #index = parseInt db.getValue( @@ -763,7 +774,7 @@ proc loadNode*(g: ModuleGraph; module: PSym; index: int): PNode = result = decodeNode(g, b, module.info) proc setupModuleCache*(g: ModuleGraph) = - if g.config.symbolFiles != v2Sf: return + if g.config.symbolFiles == disabledSf: return let dbfile = getNimcacheDir(g.config) / "rodfiles.db" if not fileExists(dbfile): db = open(connection=dbfile, user="nim", password="", diff --git a/compiler/rodread.nim b/compiler/rodread.nim deleted file mode 100644 index dc9b1c61dc..0000000000 --- a/compiler/rodread.nim +++ /dev/null @@ -1,1245 +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( -# \n # fileidx is the LINE in the file section! -# ) -# - a module dependency section: -# DEPS: \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, - lineinfos - -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 = toFullPath(r.config, r.files[decodeVInt(r.s, r.pos)]) - 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: - when declared(echo): - 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*(conf: ConfigRef; fileIdx: FileIndex): SecureHash = - if fileIdx.int32 <% gMods.len and gMods[fileIdx.int32].hashDone: - return gMods[fileIdx.int32].hash - - result = secureHashFile(toFullPath(conf, fileIdx)) - 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 = toFilename(conf, fileIdx) - var hash = getHash(conf, 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(conf.m.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 newNodeI(nkEmpty, s.info) - 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(")\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 968cb9e7fe..0000000000 --- a/compiler/rodwrite.nim +++ /dev/null @@ -1,653 +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, lineinfos - -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(w.config, 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 = toFullPath(config, module.info) - 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, toFullPath(w.config, info)) - encodeVInt(fileIdx(w, resolved), w.modDeps) - -const - rodNL = "\x0A" - -proc addInclDep(w: PRodWriter, dep: string; info: TLineInfo) = - let resolved = findModule(w.config, dep, toFullPath(w.config, info)) - 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(w.config, 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(w.config, 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! - 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(g.config, 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/sem.nim b/compiler/sem.nim index 01e816f5cc..7872302fdf 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -13,7 +13,7 @@ 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, lineinfos @@ -513,10 +513,7 @@ proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = 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) + result = myOpen(graph, module, graph.cache) proc isImportSystemStmt(g: ModuleGraph; n: PNode): bool = if g.systemModule == nil: return false @@ -623,8 +620,6 @@ 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.graph, c.module) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 02e8be85cf..c858b68392 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -13,7 +13,7 @@ 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, + magicsys, nversion, nimsets, parser, times, passes, vmdef, modulegraphs, lineinfos type diff --git a/compiler/transf.nim b/compiler/transf.nim index 7aae2e4b50..e71afcec14 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -20,7 +20,7 @@ import intsets, strutils, options, ast, astalgo, trees, treetab, msgs, os, - idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread, + idents, renderer, types, passes, semfold, magicsys, cgmeth, lambdalifting, sempass2, lowerings, lookups, destroyer, liftlocals, modulegraphs, lineinfos @@ -916,7 +916,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) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 7d8a1b45f6..c641cc8445 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, lineinfos + trees, intsets, magicsys, options, lowerings, lineinfos import platform from os import splitFile From cae19738562f14fbb76004748bed8d2f337d6f0b Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 1 Jun 2018 22:11:32 +0200 Subject: [PATCH 042/158] document how the incremental compilation scheme could work --- compiler/lineinfos.nim | 6 +- compiler/modulegraphs.nim | 2 +- compiler/options.nim | 23 ++++--- doc/intern.txt | 130 +++++++++++++++++++++++++++----------- 4 files changed, 110 insertions(+), 51 deletions(-) diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 0384fda26c..cad1fe6aa6 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -246,10 +246,10 @@ const trackPosInvalidFileIdx* = FileIndex(-2) # special marker so that no sugges # are produced within comments and string literals type - MsgConfig* = object + 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. + trackPosAttached*: bool ## whether the tracking position was attached to + ## some close token. errorOutputs*: TErrorOutputs msgContext*: seq[TLineInfo] diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 7c9837f54b..02307ca9f5 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -47,7 +47,7 @@ type 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 diff --git a/compiler/options.nim b/compiler/options.nim index cb4f1e8850..044461b554 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -156,24 +156,27 @@ type version*: int Suggestions* = seq[Suggest] - ConfigRef* = ref object ## eventually all global configuration should be moved here - target*: Target + 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 @@ -220,13 +223,13 @@ type cLinkedLibs*: seq[string] # libraries to link externalToLink*: seq[string] # files to link in addition to the file - # we compiled + # we compiled (*) linkOptionsCmd*: string compileOptionsCmd*: seq[string] - linkOptions*: string - compileOptions*: string + linkOptions*: string # (*) + compileOptions*: string # (*) ccompilerpath*: string - toCompile*: CfileList + toCompile*: CfileList # (*) suggestionResultHook*: proc (result: Suggest) {.closure.} suggestVersion*: int suggestMaxResults*: int diff --git a/doc/intern.txt b/doc/intern.txt index dadb0eb05a..a4545583e5 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,86 @@ 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. -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 +304,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 +322,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 +334,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 +373,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 From 826c1e2d7850026335d33e3be2fce54dee4f6698 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 2 Jun 2018 09:41:27 +0200 Subject: [PATCH 043/158] incremental compilation: implemented basic replay logic --- compiler/ast.nim | 36 ++++++++++-------- compiler/cgen.nim | 15 ++++---- compiler/depends.nim | 2 +- compiler/docgen2.nim | 2 +- compiler/importer.nim | 24 ++++++------ compiler/jsgen.nim | 8 +--- compiler/main.nim | 75 +++++++++++++++++++------------------ compiler/modulegraphs.nim | 5 +++ compiler/modules.nim | 56 +++++++++------------------ compiler/nim.nim | 2 +- compiler/nimeval.nim | 1 + compiler/options.nim | 13 +++++++ compiler/passaux.nim | 2 +- compiler/passes.nim | 72 ++++++++--------------------------- compiler/pragmas.nim | 53 +++++++++++++++++--------- compiler/reorder.nim | 18 ++++----- compiler/rod.nim | 2 +- compiler/rodimpl.nim | 79 +++++++++++++++++++++++++++++++-------- compiler/scriptconfig.nim | 5 ++- compiler/sem.nim | 13 +++---- compiler/semexprs.nim | 6 +-- compiler/semstmts.nim | 9 +++-- compiler/suggest.nim | 2 +- compiler/vm.nim | 34 ++++++++--------- nimsuggest/nimsuggest.nim | 32 ++++++++-------- 25 files changed, 295 insertions(+), 271 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 1f5b4927e3..7758bffd33 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1058,22 +1058,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 @@ -1221,6 +1205,26 @@ 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 = @[] diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 92e4898e49..bf3c3a851d 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1316,7 +1316,7 @@ 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, graph.config) if optGenIndex in graph.config.globalOptions and g.generatedHeader == nil: @@ -1360,11 +1360,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, graph.config) - 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 @@ -1506,4 +1507,4 @@ proc cgenWriteModules*(backend: RootRef, config: ConfigRef) = 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/depends.nim b/compiler/depends.nim index 34ab5b157f..d0a1139ef8 100644 --- a/compiler/depends.nim +++ b/compiler/depends.nim @@ -51,7 +51,7 @@ proc generateDot*(graph: ModuleGraph; project: string) = 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 diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim index 9ec8d51628..068c47bb3c 100644 --- a/compiler/docgen2.nim +++ b/compiler/docgen2.nim @@ -51,7 +51,7 @@ 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 diff --git a/compiler/importer.nim b/compiler/importer.nim index f30c23731b..c013b93ab1 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -133,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 @@ -147,7 +147,7 @@ proc myImportModule(c: PContext, n: PNode): PSym = 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 @@ -162,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``! @@ -173,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: @@ -185,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 @@ -201,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/jsgen.nim b/compiler/jsgen.nim index 3c57f97afd..b52c148425 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -2277,12 +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 = +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/main.nim b/compiler/main.nim index 97e96dcff8..cd05ded62b 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -40,37 +40,37 @@ proc writeDepsFile(g: ModuleGraph; project: string) = f.writeLine(toFullPath(g.config, k)) f.close() -proc commandGenDepend(graph: ModuleGraph; cache: IdentCache) = +proc commandGenDepend(graph: ModuleGraph) = semanticPasses(graph) registerPass(graph, gendependPass) - compileProject(graph, cache) + compileProject(graph) let project = graph.config.projectFull writeDepsFile(graph, 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(graph) # use an empty backend for semantic checking only - compileProject(graph, cache) + 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(graph) if json: registerPass(graph, docgen2JsonPass) else: registerPass(graph, docgen2Pass) - compileProject(graph, cache) + compileProject(graph) finishDoc2Pass(graph.config.projectName) -proc commandCompileToC(graph: ModuleGraph; cache: IdentCache) = +proc commandCompileToC(graph: ModuleGraph) = let conf = graph.config extccomp.initVars(conf) semanticPasses(graph) registerPass(graph, cgenPass) - compileProject(graph, cache) + compileProject(graph) cgenWriteModules(graph.backend, conf) if conf.cmd != cmdRun: let proj = changeFileExt(conf.projectFull, "") @@ -79,11 +79,11 @@ 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(graph.config.target, osJS, cpuJS) #initDefines() @@ -91,9 +91,9 @@ proc commandCompileToJS(graph: ModuleGraph; cache: IdentCache) = defineSymbol(graph.config.symbols, "js") semanticPasses(graph) registerPass(graph, JSgenPass) - compileProject(graph, cache) + compileProject(graph) -proc interactivePasses(graph: ModuleGraph; cache: IdentCache) = +proc interactivePasses(graph: ModuleGraph) = initDefines(graph.config.symbols) defineSymbol(graph.config.symbols, "nimscript") when hasFFI: defineSymbol(graph.config.symbols, "nimffi") @@ -101,29 +101,29 @@ proc interactivePasses(graph: ModuleGraph; cache: IdentCache) = 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) @@ -145,8 +145,9 @@ 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(graph) # In "nim serve" scenario, each command must reset the registered passes @@ -158,25 +159,25 @@ 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 @@ -186,7 +187,7 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) = 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) @@ -207,7 +208,7 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) = loadConfigs(DocConfig, cache, conf) wantMainModule(conf) defineSymbol(conf.symbols, "nimdoc") - commandDoc2(graph, cache, true) + commandDoc2(graph, true) of "ctags": wantMainModule(conf) conf.cmd = cmdDoc @@ -220,7 +221,7 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) = commandBuildIndex(cache, conf) of "gendepend": conf.cmd = cmdGenDepend - commandGenDepend(graph, cache) + commandGenDepend(graph) of "dump": conf.cmd = cmdDump if getConfigVar(conf, "dump.format") == "json": @@ -249,7 +250,7 @@ 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) @@ -261,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) diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 02307ca9f5..f214309a50 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -56,6 +56,9 @@ type 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.} proc hash*(x: FileIndex): Hash {.borrow.} @@ -85,6 +88,8 @@ proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph = result.opContains = createMagic(result, "contains", mInSet) result.emptyNode = newNode(nkEmpty) init(result.incr) + result.recordStmt = proc (graph: ModuleGraph; m: PSym; n: PNode) {.nimcall.} = + discard proc resetAllModules*(g: ModuleGraph) = initStrTable(packageSyms) diff --git a/compiler/modules.nim b/compiler/modules.nim index 9b04578c0a..04b1506f4f 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -58,52 +58,30 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = # 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: graph.config.mainPackageId = 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(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) @@ -112,37 +90,37 @@ proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex; 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 compileSystemModule*(graph: ModuleGraph) = if graph.systemModule == nil: graph.config.m.systemFileIdx = fileInfoIdx(graph.config, graph.config.libpath / "system.nim") - discard graph.compileModule(graph.config.m.systemFileIdx, cache, {sfSystemModule}) + 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 = fileInfoIdx(conf, addFileExt(conf.projectFull, NimExt)) -passes.gIncludeFile = includeModule -passes.gImportModule = importModule +proc connectCallbacks*(graph: ModuleGraph) = + graph.includeFileCallback = includeModule + graph.importModuleCallback = importModule -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: 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/nim.nim b/compiler/nim.nim index 756ddd7f5a..90049bdfbf 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -94,7 +94,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = processCmdLine(passCmd2, "", conf) if conf.command == "": rawMessage(conf, errGenerated, "command missing") - mainCommand(newModuleGraph(cache, 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/nimeval.nim b/compiler/nimeval.nim index dde6039ba8..308560010c 100644 --- a/compiler/nimeval.nim +++ b/compiler/nimeval.nim @@ -95,6 +95,7 @@ proc createInterpreter*(scriptName: string; var conf = newConfigRef() var cache = newIdentCache() var graph = newModuleGraph(cache, conf) + connectCallbacks(graph) initDefines(conf.symbols) defineSymbol(conf.symbols, "nimscript") defineSymbol(conf.symbols, "nimconfig") diff --git a/compiler/options.nim b/compiler/options.nim index 044461b554..7ee8f8d4c5 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -238,6 +238,19 @@ type structuredErrorHook*: proc (config: ConfigRef; info: TLineInfo; msg: string; severity: Severity) {.closure.} +template depConfigFields*(fn) {.dirty.} = + fn(target) + fn(options) + fn(globalOptions) + fn(selectedGC) + +template serializeConfigFields(fn) {.dirty.} = + fn(cppDefines) + fn(externalToLink) + fn(linkOptions) + fn(compileOptions) + fn(toCompile) + const oldExperimentalFeatures* = {implicitDeref, dotOperators, callOperator, parallel} const diff --git a/compiler/passaux.nim b/compiler/passaux.nim index 1ac4611884..eabce88222 100644 --- a/compiler/passaux.nim +++ b/compiler/passaux.nim @@ -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 e8fd89025d..45c726f2ad 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -18,19 +18,15 @@ import type - PRodReader* = ref object TPassContext* = object of RootObj # the pass's context 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 @@ -81,35 +68,27 @@ 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) - else: - a[i] = nil - proc closePasses(graph: ModuleGraph; a: var TPassContextArray) = var m: PNode = nil for i in countup(0, gPassesLen - 1): @@ -125,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: @@ -159,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 @@ -171,24 +136,17 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream, # new module caching mechanism: for i in 0.. 0: recorded.add newStrNode(val2, n.info) + c.graph.recordStmt(c.graph, c.module, recorded) + const errStringLiteralExpected = "string literal expected" errIntLiteralExpected = "integer literal expected" @@ -227,7 +234,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) @@ -412,6 +419,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]) @@ -428,11 +439,8 @@ proc processCompile(c: PContext, n: PNode) = let dest = getStrLit(c, it, 1) 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(toFullPath(c.config, n.info)) / s @@ -441,15 +449,19 @@ proc processCompile(c: PContext, n: PNode) = 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, changeFileExt(found, ""), false)) + docompile(c, it, found, obj) proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) = 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) = @@ -724,7 +736,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: @@ -891,8 +903,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 @@ -902,7 +920,9 @@ 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) @@ -1066,8 +1086,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 @@ -1079,7 +1098,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/reorder.nim b/compiler/reorder.nim index dbd34c69c4..27b19a3738 100644 --- a/compiler/reorder.nim +++ b/compiler/reorder.nim @@ -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 @@ -158,9 +156,9 @@ proc expandIncludes(graph: ModuleGraph, module: PSym, n: PNode, 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 @@ -427,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 = 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..= 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) + else: + internalAssert g.config, false + of nkImportStmt: + for x in n: + if x.kind == nkStrLit: + # XXX check that importModuleCallback implements the right logic + 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) + shallowCopy b.s, row[0] + # ensure we can read without index checks: + b.s.add '\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 not fileExists(dbfile): db = open(connection=dbfile, user="nim", password="", diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim index 30bdf162ed..ae7e030b8c 100644 --- a/compiler/scriptconfig.nim +++ b/compiler/scriptconfig.nim @@ -154,6 +154,7 @@ proc runNimScript*(cache: IdentCache; scriptName: string; rawMessage(conf, hintConf, scriptName) let graph = newModuleGraph(cache, conf) + connectCallbacks(graph) if freshDefines: initDefines(conf.symbols) defineSymbol(conf.symbols, "nimscript") @@ -167,8 +168,8 @@ proc runNimScript*(cache: IdentCache; scriptName: string; incl(m.flags, sfMainModule) 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) diff --git a/compiler/sem.nim b/compiler/sem.nim index 7872302fdf..733ea2eaaa 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -314,7 +314,7 @@ proc tryConstExpr(c: PContext, n: PNode): PNode = 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: @@ -338,7 +338,7 @@ 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(c.config, n.info) @@ -446,7 +446,7 @@ 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) @@ -482,7 +482,7 @@ proc addCodeForGenerics(c: PContext, n: PNode) = addSon(n, prc.ast) c.lastGenericIdx = c.generics.len -proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = +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 @@ -512,9 +512,6 @@ proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = graph.config.notes = graph.config.foreignPackageNotes result = c -proc myOpenCached(graph: ModuleGraph; module: PSym; rd: PRodReader): PPassContext = - result = myOpen(graph, module, graph.cache) - proc isImportSystemStmt(g: ModuleGraph; n: PNode): bool = if g.systemModule == nil: return false case n.kind @@ -625,5 +622,5 @@ proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode = 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/semexprs.nim b/compiler/semexprs.nim index 28bbce337b..f0cda504fb 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -617,12 +617,12 @@ 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: @@ -631,7 +631,7 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = proc semStaticExpr(c: PContext, n: PNode): PNode = let a = semExpr(c, n.sons[0]) 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 = c.graph.emptyNode diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index b48703e3d2..322ea1bd6b 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -572,7 +572,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = 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) + c.graph.recordStmt(c.graph, c.module, result) proc semConst(c: PContext, n: PNode): PNode = result = copyNode(n) @@ -1059,7 +1060,7 @@ proc semAllTypeSections(c: PContext; n: PNode): PNode = if containsOrIncl(c.includedFiles, f.int): 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: @@ -1741,7 +1742,7 @@ proc evalInclude(c: PContext, n: PNode): PNode = if containsOrIncl(c.includedFiles, f.int): 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) = @@ -1770,7 +1771,7 @@ proc semStaticStmt(c: PContext, n: PNode): PNode = let a = semStmt(c, n.sons[0]) dec c.inStaticContext n.sons[0] = a - evalStaticStmt(c.module, c.cache, c.graph, a, c.p.owner) + evalStaticStmt(c.module, c.graph, a, c.p.owner) result = newNodeI(nkDiscardStmt, n.info, 1) result.sons[0] = c.graph.emptyNode diff --git a/compiler/suggest.nim b/compiler/suggest.nim index b66dbce689..a21d643389 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -323,7 +323,7 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) # 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): diff --git a/compiler/vm.nim b/compiler/vm.nim index b1b8132e2d..2ba5e7ebf8 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1657,20 +1657,20 @@ proc getGlobalValue*(c: PCtx; s: PSym): PNode = include vmops -proc setupGlobalCtx(module: PSym; cache: IdentCache; graph: ModuleGraph) = +proc setupGlobalCtx(module: PSym; graph: ModuleGraph) = if graph.vm.isNil: - graph.vm = newCtx(module, cache, graph) + graph.vm = newCtx(module, graph.cache, graph) registerAdditionalOps(PCtx graph.vm) else: 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) + setupGlobalCtx(module, graph) result = PCtx graph.vm when hasFFI: PCtx(graph.vm).features = {allowFFI, allowCast} @@ -1688,13 +1688,13 @@ 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) + setupGlobalCtx(module, g) var c = PCtx g.vm let oldMode = c.mode defer: c.mode = oldMode @@ -1709,17 +1709,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 @@ -1746,7 +1746,7 @@ iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) = # to prevent endless recursion in macro instantiation const evalMacroLimit = 1000 -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(g.config.evalMacroCounter) @@ -1759,7 +1759,7 @@ 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) + setupGlobalCtx(module, g) var c = PCtx g.vm c.comesFromHeuristic.line = 0'u16 diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim index dd52e03838..9f93150802 100644 --- a/nimsuggest/nimsuggest.nim +++ b/nimsuggest/nimsuggest.nim @@ -159,7 +159,7 @@ proc symFromInfo(graph: ModuleGraph; trackPos: TLineInfo): PSym = 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 & "]") @@ -184,7 +184,7 @@ proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int; if conf.suggestVersion == 1: graph.usageSym = nil if not isKnownFile: - graph.compileProject(cache) + graph.compileProject() if conf.suggestVersion == 0 and conf.ideCmd in {ideUse, ideDus} and dirtyfile.len == 0: discard "no need to recompile anything" @@ -193,7 +193,7 @@ 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 conf.suggestVersion != 1: graph.symFromInfo(conf.m.trackPos) else: graph.usageSym if u != nil: @@ -202,7 +202,7 @@ proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int; 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 @@ -210,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") = @@ -379,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() = @@ -435,19 +435,19 @@ proc execCmd(cmd: string; graph: ModuleGraph; cache: IdentCache; cachedMsgs: Cac else: if conf.ideCmd == ideChk: for cm in cachedMsgs: errorHook(conf, cm.info, cm.msg, cm.sev) - execute(conf.ideCmd, orig, dirtyfile, line, col, graph, cache) + 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) 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: @@ -469,7 +469,7 @@ proc mainThread(graph: ModuleGraph; cache: IdentCache) = if hasData: conf.writelnHook = wrHook conf.suggestionResultHook = sugResultHook - execCmd(req, graph, cache, cachedMsgs) + execCmd(req, graph, cachedMsgs) idle = 0 else: os.sleep 250 @@ -482,12 +482,12 @@ proc mainThread(graph: ModuleGraph; cache: IdentCache) = conf.structuredErrorHook = proc (conf: ConfigRef; info: TLineInfo; msg: string; sev: Severity) = cachedMsgs.add(CachedMsg(info: info, msg: msg, sev: sev)) conf.suggestionResultHook = proc (s: Suggest) = discard - recompileFullProject(graph, cache) + recompileFullProject(graph) var inputThread: Thread[ThreadParams] -proc mainCommand(graph: ModuleGraph; cache: IdentCache) = +proc mainCommand(graph: ModuleGraph) = let conf = graph.config clearPasses(graph) registerPass graph, verbosePass @@ -509,7 +509,7 @@ proc mainCommand(graph: ModuleGraph; cache: IdentCache) = # 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) @@ -522,7 +522,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) @@ -632,6 +632,6 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = let graph = newModuleGraph(cache, conf) graph.suggestMode = true - mainCommand(graph, cache) + mainCommand(graph) handleCmdline(newIdentCache(), newConfigRef()) From 9d27079606ce1d04e834a0cfe0f86dd60890ff47 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 3 Jun 2018 12:37:03 +0200 Subject: [PATCH 044/158] support iteration over BTrees for the compilation cache --- compiler/btrees.nim | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/compiler/btrees.nim b/compiler/btrees.nim index 2284816928..6bc92de2d2 100644 --- a/compiler/btrees.nim +++ b/compiler/btrees.nim @@ -122,6 +122,39 @@ 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.. 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) + when isMainModule: import random, tables @@ -154,6 +187,9 @@ when isMainModule: 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 From 6d19d1eeb21487b62883852dc543cc891c129f62 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 3 Jun 2018 20:15:12 +0200 Subject: [PATCH 045/158] macros.nim: remove deprecated symbols --- lib/core/macros.nim | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index fa5cd67dfb..1f251b73ee 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} From 1ed7751daca70e74a76e8afa0e7ffc8a730006b3 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 3 Jun 2018 20:15:37 +0200 Subject: [PATCH 046/158] added btrees.contains --- compiler/btrees.nim | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/compiler/btrees.nim b/compiler/btrees.nim index 6bc92de2d2..6cd6e51f4c 100644 --- a/compiler/btrees.nim +++ b/compiler/btrees.nim @@ -26,7 +26,6 @@ type links: array[M, Node[Key, Val]] BTree*[Key, Val] = object root: Node[Key, Val] - height: int entries: int ## number of key-value pairs proc initBTree*[Key, Val](): BTree[Key, Val] = @@ -46,6 +45,18 @@ proc getOrDefault*[Key, Val](b: BTree[Key, Val], key: Key): Val = 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] @@ -106,7 +117,6 @@ proc add*[Key, Val](b: var BTree[Key, Val]; key: Key; val: Val) = t.keys[1] = u.keys[0] t.links[1] = u b.root = t - inc b.height proc toString[Key, Val](h: Node[Key, Val], indent: string; result: var string) = if not h.isInternal: @@ -155,6 +165,8 @@ iterator pairs*[Key, Val](b: BTree[Key, Val]): (Key, Val) = i = i2 yield (k, v) +proc len*[Key, Val](b: BTree[Key, Val]): int {.inline.} = b.entries + when isMainModule: import random, tables @@ -200,7 +212,6 @@ when isMainModule: if x != $(iters - i): echo "got ", x, ", but expected ", iters - i echo b2.entries - echo b2.height when true: var b2 = initBTree[int, string]() From 9ab92824f6876f4dfffb865edfd0f1d9da28bbd2 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 3 Jun 2018 20:16:29 +0200 Subject: [PATCH 047/158] AST change: keep nkStaticStmt in the AST for incremental compilation support --- compiler/ccgexprs.nim | 2 +- compiler/jsgen.nim | 2 +- compiler/semstmts.nim | 9 ++++++--- compiler/transf.nim | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index f26526b059..9401f89c36 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2306,7 +2306,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) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index b52c148425..ef54841ae1 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -2155,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 diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 322ea1bd6b..0f8e77fc9b 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -573,7 +573,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = message(c.config, v.info, hintGlobalVar) if hasCompileTime: vm.setupCompileTimeVar(c.module, c.graph, result) - c.graph.recordStmt(c.graph, c.module, result) + # handled by the VM codegen: + #c.graph.recordStmt(c.graph, c.module, result) proc semConst(c: PContext, n: PNode): PNode = result = copyNode(n) @@ -1772,8 +1773,10 @@ proc semStaticStmt(c: PContext, n: PNode): PNode = dec c.inStaticContext n.sons[0] = a evalStaticStmt(c.module, c.graph, a, c.p.owner) - result = newNodeI(nkDiscardStmt, n.info, 1) - result.sons[0] = c.graph.emptyNode + # for incremental replays, keep the AST as required for replays: + result = n + #result = newNodeI(nkDiscardStmt, n.info, 1) + #result.sons[0] = c.graph.emptyNode proc usesResult(n: PNode): bool = # nkStmtList(expr) properly propagates the void context, diff --git a/compiler/transf.nim b/compiler/transf.nim index e71afcec14..72bcb6d711 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -863,7 +863,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`` From b5194f592c033c02e9eaf93fde6ee0de2b7623e2 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 3 Jun 2018 20:18:43 +0200 Subject: [PATCH 048/158] WIP: an API for VM replay global state support --- compiler/ast.nim | 10 ++-- compiler/macrocacheimpl.nim | 49 ++++++++++++++++++++ compiler/rodimpl.nim | 27 ++++------- compiler/vm.nim | 91 ++++++++++++++++++++++++++++++++++++- compiler/vmdef.nim | 14 +++++- compiler/vmgen.nim | 38 ++++++++++++---- doc/intern.txt | 47 ++++++++++++++++++- lib/core/macrocache.nim | 47 +++++++++++++++++++ 8 files changed, 288 insertions(+), 35 deletions(-) create mode 100644 compiler/macrocacheimpl.nim create mode 100644 lib/core/macrocache.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 7758bffd33..40c1b064d2 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -634,14 +634,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 diff --git a/compiler/macrocacheimpl.nim b/compiler/macrocacheimpl.nim new file mode 100644 index 0000000000..5c27f9265c --- /dev/null +++ b/compiler/macrocacheimpl.nim @@ -0,0 +1,49 @@ +# +# +# 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 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/rodimpl.nim b/compiler/rodimpl.nim index e6c6b63745..e2160aa846 100644 --- a/compiler/rodimpl.nim +++ b/compiler/rodimpl.nim @@ -10,16 +10,12 @@ ## This module implements the new compilation cache. import strutils, os, intsets, tables, ropes, db_sqlite, msgs, options, types, - renderer, rodutils, idents, astalgo, btrees, magicsys, cgmeth, extccomp + renderer, rodutils, idents, astalgo, btrees, magicsys, cgmeth, extccomp, vm ## 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 replays. -## - Depencency computation should use *signature* hashes in order to +## dependencies. Also include the rodfile "version". +## - Dependency computation should use *signature* hashes in order to ## avoid recompiling dependent modules. template db(): DbConn = g.incr.db @@ -157,8 +153,6 @@ proc encodeLoc(g: ModuleGraph; loc: TLoc, result: var string) = if loc.lode != nil: add(result, '^') encodeNode(g, 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) @@ -765,11 +759,9 @@ proc loadModuleSymTab(g; module: PSym) = proc replay(g: ModuleGraph; module: PSym; n: PNode) = case n.kind of nkStaticStmt: - #evalStaticStmt() - discard "XXX to implement" - of nkVarSection, nkLetSection: - #setupCompileTimeVar() - discard "XXX to implement" + 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: @@ -798,10 +790,9 @@ proc replay(g: ModuleGraph; module: PSym; n: PNode) = internalAssert g.config, false of nkImportStmt: for x in n: - if x.kind == nkStrLit: - # XXX check that importModuleCallback implements the right logic - let imported = g.importModuleCallback(g, module, fileInfoIdx(g.config, n[0].strVal)) - internalAssert g.config, imported.id < 0 + 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" diff --git a/compiler/vm.nim b/compiler/vm.nim index 2ba5e7ebf8..019aa08e85 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, lineinfos + vmmarshal, gorgeimpl, lineinfos, tables, btrees, macrocacheimpl from semfold import leValueConv, ordinalValToString from evaltempl import evalTemplate @@ -1572,6 +1572,95 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = var sym = newSym(k.TSymKind, getIdent(c.cache, name), c.module.owner, c.debug[pc]) incl(sym.flags, sfGenSym) regs[ra].node = newSymNode(sym) + + of opcNccValue: + decodeB(rkInt) + let destKey = regs[rb].node.strVal + regs[ra].intVal = getOrDefault(c.cacheCounters, destKey) + of opcNccInc: + let rb = instr.regB + let destKey = regs[ra].node.strVal + let by = regs[rb].intVal + let v = getOrDefault(c.cacheCounters, destKey) + c.cacheCounters[destKey] = v+by + recordInc(c, c.debug[pc], destKey, by) + of opcNcsAdd: + let destKey = regs[ra].node.strVal + let val = regs[instr.regB].node + if not contains(c.cacheSeqs, destKey): + c.cacheSeqs[destKey] = newTree(nkStmtList, val) + # newNodeI(nkStmtList, c.debug[pc]) + else: + c.cacheSeqs[destKey].add val + recordAdd(c, c.debug[pc], destKey, val) + of opcNcsIncl: + let destKey = regs[ra].node.strVal + let val = regs[instr.regB].node + if not contains(c.cacheSeqs, destKey): + c.cacheSeqs[destKey] = newTree(nkStmtList, val) + else: + block search: + for existing in c.cacheSeqs[destKey]: + if exprStructuralEquivalent(existing, val, strictSymEquality=true): + break search + c.cacheSeqs[destKey].add val + recordIncl(c, c.debug[pc], destKey, val) + of opcNcsLen: + decodeB(rkInt) + let destKey = regs[rb].node.strVal + regs[ra].intVal = + if contains(c.cacheSeqs, destKey): c.cacheSeqs[destKey].len else: 0 + of opcNcsAt: + decodeBC(rkNode) + let idx = regs[rc].intVal + let destKey = regs[rb].node.strVal + if contains(c.cacheSeqs, destKey) and idx <% c.cacheSeqs[destKey].len: + regs[ra].node = c.cacheSeqs[destKey][idx.int] + else: + stackTrace(c, tos, pc, errIndexOutOfBounds) + of opcNctPut: + let destKey = regs[ra].node.strVal + let key = regs[instr.regB].node.strVal + let val = regs[instr.regC].node + if not contains(c.cacheTables, destKey): + c.cacheTables[destKey] = initBTree[string, PNode]() + if not contains(c.cacheTables[destKey], key): + c.cacheTables[destKey].add(key, val) + recordPut(c, c.debug[pc], destKey, key, val) + else: + stackTrace(c, tos, pc, "key already exists: " & key) + of opcNctLen: + decodeB(rkInt) + let destKey = regs[rb].node.strVal + regs[ra].intVal = + if contains(c.cacheTables, destKey): c.cacheTables[destKey].len else: 0 + of opcNctGet: + decodeBC(rkNode) + let destKey = regs[rb].node.strVal + let key = regs[rc].node.strVal + if contains(c.cacheTables, destKey): + if contains(c.cacheTables[destKey], key): + regs[ra].node = getOrDefault(c.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: + decodeBC(rkInt) + let destKey = regs[rb].node.strVal + regs[ra].intVal = + btrees.hasNext(c.cacheTables[destKey], regs[rc].intVal.int) else: 0 + of opcNctNext: + decodeBC(rkNode) + let destKey = regs[rb].node.strVal + let index = regs[rc].intVal + if contains(c.cacheTables, destKey): + let (k, v, nextIndex) = btrees.next(c.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 diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 9c31dda24d..1ef9844662 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, lineinfos +import ast, passes, msgs, idents, intsets, options, modulegraphs, lineinfos, + tables, btrees const byteExcess* = 128 # we use excess-K for immediates @@ -91,6 +92,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, @@ -209,6 +213,9 @@ type config*: ConfigRef graph*: ModuleGraph oldErrorCount*: int + cacheSeqs*: Table[string, PNode] + cacheCounters*: Table[string, BiggestInt] + cacheTables*: Table[string, BTree[string, PNode]] TPosition* = distinct int @@ -219,7 +226,10 @@ proc newCtx*(module: PSym; cache: IdentCache; g: ModuleGraph): PCtx = globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[], prc: PProc(blocks: @[]), module: module, loopIterations: MaxLoopIterations, comesFromHeuristic: unknownLineInfo(), callbacks: @[], errorFlag: "", - cache: cache, config: g.config, graph: g) + cache: cache, config: g.config, graph: g, + cacheSeqs: initTable[string, PNode]() + cacheCounters: initTable[string, BiggestInt]() + cacheTables: initTable[string, BTree[string, PNode]]()) proc refresh*(c: PCtx, module: PSym) = c.module = module diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index c641cc8445..9444e41d83 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -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: TRegister, opcode: TOpcode) + 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, 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) @@ -1067,20 +1078,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) @@ -1795,6 +1811,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) diff --git a/doc/intern.txt b/doc/intern.txt index a4545583e5..b253cac422 100644 --- a/doc/intern.txt +++ b/doc/intern.txt @@ -268,7 +268,52 @@ severe way. Plenty of different solutions have been proposed: 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. +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. diff --git a/lib/core/macrocache.nim b/lib/core/macrocache.nim new file mode 100644 index 0000000000..bd48b5bd46 --- /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 From 268260953c3575576e420807c7e3ffd12efa8bd2 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 4 Jun 2018 01:01:08 +0200 Subject: [PATCH 049/158] completed VM support for incremental compilations --- compiler/macrocacheimpl.nim | 88 +++++++++++++++++++++++++------------ compiler/modulegraphs.nim | 8 +++- compiler/rodimpl.nim | 41 +++++++++++++++-- compiler/vm.nim | 64 ++++++++++++++++----------- compiler/vmdef.nim | 8 +--- compiler/vmgen.nim | 12 ++--- 6 files changed, 149 insertions(+), 72 deletions(-) diff --git a/compiler/macrocacheimpl.nim b/compiler/macrocacheimpl.nim index 5c27f9265c..d23040763a 100644 --- a/compiler/macrocacheimpl.nim +++ b/compiler/macrocacheimpl.nim @@ -11,39 +11,69 @@ import lineinfos, ast, modulegraphs, vmdef, magicsys -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) + 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) = - let g = c.graph - record genCall3(mNctPut, "[]=", nodeFrom key, nodeFrom k, nodeFrom val) + 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) = - let g = c.graph - record genCall2(mNcsAdd, "add", nodeFrom key, nodeFrom val) + 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) = - let g = c.graph - record genCall2(mNcsIncl, "incl", nodeFrom key, nodeFrom val) + 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/modulegraphs.nim b/compiler/modulegraphs.nim index f214309a50..334cd1ae6f 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -26,7 +26,7 @@ ## import ast, intsets, tables, options, lineinfos, hashes, idents, - incremental + incremental, btrees type ModuleGraph* = ref object @@ -59,6 +59,9 @@ type 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.} @@ -90,6 +93,9 @@ proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph = 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) diff --git a/compiler/rodimpl.nim b/compiler/rodimpl.nim index e2160aa846..fffec00043 100644 --- a/compiler/rodimpl.nim +++ b/compiler/rodimpl.nim @@ -10,7 +10,8 @@ ## This module implements the new compilation cache. import strutils, os, intsets, tables, ropes, db_sqlite, msgs, options, types, - renderer, rodutils, idents, astalgo, btrees, magicsys, cgmeth, extccomp, vm + renderer, rodutils, idents, astalgo, btrees, magicsys, cgmeth, extccomp, + btrees, trees ## Todo: ## - Make conditional symbols and the configuration part of a module's @@ -757,9 +758,10 @@ proc loadModuleSymTab(g; module: PSym) = 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 nkStaticStmt: + #evalStaticStmt(module, g, n[0], module) #of nkVarSection, nkLetSection: # nkVarSections are already covered by the vmgen which produces nkStaticStmt of nkMethodDef: @@ -786,6 +788,39 @@ proc replay(g: ModuleGraph; module: PSym; n: PNode) = extccomp.addExternalFileToCompile(g.config, cf) of "link": extccomp.addExternalFileToLink(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: diff --git a/compiler/vm.nim b/compiler/vm.nim index 019aa08e85..3e33e8256d 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1576,86 +1576,98 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcNccValue: decodeB(rkInt) let destKey = regs[rb].node.strVal - regs[ra].intVal = getOrDefault(c.cacheCounters, destKey) + regs[ra].intVal = getOrDefault(c.graph.cacheCounters, destKey) of opcNccInc: - let rb = instr.regB + let g = c.graph let destKey = regs[ra].node.strVal - let by = regs[rb].intVal - let v = getOrDefault(c.cacheCounters, destKey) - c.cacheCounters[destKey] = v+by + 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(c.cacheSeqs, destKey): - c.cacheSeqs[destKey] = newTree(nkStmtList, val) + if not contains(g.cacheSeqs, destKey): + g.cacheSeqs[destKey] = newTree(nkStmtList, val) # newNodeI(nkStmtList, c.debug[pc]) else: - c.cacheSeqs[destKey].add val + 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(c.cacheSeqs, destKey): - c.cacheSeqs[destKey] = newTree(nkStmtList, val) + if not contains(g.cacheSeqs, destKey): + g.cacheSeqs[destKey] = newTree(nkStmtList, val) else: block search: - for existing in c.cacheSeqs[destKey]: + for existing in g.cacheSeqs[destKey]: if exprStructuralEquivalent(existing, val, strictSymEquality=true): break search - c.cacheSeqs[destKey].add val + 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(c.cacheSeqs, destKey): c.cacheSeqs[destKey].len else: 0 + 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(c.cacheSeqs, destKey) and idx <% c.cacheSeqs[destKey].len: - regs[ra].node = c.cacheSeqs[destKey][idx.int] + 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(c.cacheTables, destKey): - c.cacheTables[destKey] = initBTree[string, PNode]() - if not contains(c.cacheTables[destKey], key): - c.cacheTables[destKey].add(key, val) + 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(c.cacheTables, destKey): c.cacheTables[destKey].len else: 0 + 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(c.cacheTables, destKey): - if contains(c.cacheTables[destKey], key): - regs[ra].node = getOrDefault(c.cacheTables[destKey], key) + 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 = - btrees.hasNext(c.cacheTables[destKey], regs[rc].intVal.int) else: 0 + 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(c.cacheTables, destKey): - let (k, v, nextIndex) = btrees.next(c.cacheTables[destKey], index.int) + 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: diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 1ef9844662..cec61ade52 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -213,9 +213,6 @@ type config*: ConfigRef graph*: ModuleGraph oldErrorCount*: int - cacheSeqs*: Table[string, PNode] - cacheCounters*: Table[string, BiggestInt] - cacheTables*: Table[string, BTree[string, PNode]] TPosition* = distinct int @@ -226,10 +223,7 @@ proc newCtx*(module: PSym; cache: IdentCache; g: ModuleGraph): PCtx = globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[], prc: PProc(blocks: @[]), module: module, loopIterations: MaxLoopIterations, comesFromHeuristic: unknownLineInfo(), callbacks: @[], errorFlag: "", - cache: cache, config: g.config, graph: g, - cacheSeqs: initTable[string, PNode]() - cacheCounters: initTable[string, BiggestInt]() - cacheTables: initTable[string, BTree[string, PNode]]()) + cache: cache, config: g.config, graph: g) proc refresh*(c: PCtx, module: PSym) = c.module = module diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 9444e41d83..4c58ea789a 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -804,12 +804,12 @@ 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: TRegister, opcode: TOpcode) +proc genVoidABC(c: PCtx, n: PNode, dest: TRegister, opcode: TOpcode) = unused(c, n, dest) var - tmp1 = c.genx(n.sons[1]) - tmp2 = c.genx(n.sons[2]) - tmp3 = c.genx(n.sons[3]) + 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) @@ -1087,8 +1087,8 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = 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 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) From d7913419d7a7626ffa774529ae7e20b360d6257e Mon Sep 17 00:00:00 2001 From: Koki Fushimi Date: Mon, 4 Jun 2018 15:03:08 +0900 Subject: [PATCH 050/158] Add log proc for base b of x --- lib/pure/math.nim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 6be19a3395..6969b55e3c 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -141,6 +141,8 @@ when not defined(JS): # C proc ln*(x: float32): float32 {.importc: "logf", header: "".} proc ln*(x: float64): float64 {.importc: "log", header: "".} ## Computes the natural log of `x` + proc log*(b, x: float32): float32 = ln(x) / ln(b) + ## Computes the logarithm base ``b`` of ``x`` proc log10*(x: float32): float32 {.importc: "log10f", header: "".} proc log10*(x: float64): float64 {.importc: "log10", header: "".} ## Computes the common logarithm (base 10) of `x` @@ -372,7 +374,7 @@ when not defined(JS): # C proc `mod`*(x, y: float32): float32 {.importc: "fmodf", header: "".} proc `mod`*(x, y: float64): float64 {.importc: "fmod", header: "".} - ## Computes the modulo operation for float operators. + ## Computes the modulo operation for float operators. else: # JS proc hypot*[T: float32|float64](x, y: T): T = return sqrt(x*x + y*y) proc pow*(x, y: float32): float32 {.importC: "Math.pow", nodecl.} From f849db1c5c38a36c32628e3e95f3e9e8cee55d20 Mon Sep 17 00:00:00 2001 From: Koki Fushimi Date: Mon, 4 Jun 2018 15:09:17 +0900 Subject: [PATCH 051/158] Generalize and add test --- lib/pure/math.nim | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 6969b55e3c..cd7bf5fe2e 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -141,8 +141,17 @@ when not defined(JS): # C proc ln*(x: float32): float32 {.importc: "logf", header: "".} proc ln*(x: float64): float64 {.importc: "log", header: "".} ## Computes the natural log of `x` - proc log*(b, x: float32): float32 = ln(x) / ln(b) - ## Computes the logarithm base ``b`` 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](b, x: T): T = ln(x) / ln(b) + ## Computes the logarithm base ``b`` of ``x`` + +when not defined(JS): # C proc log10*(x: float32): float32 {.importc: "log10f", header: "".} proc log10*(x: float64): float64 {.importc: "log10", header: "".} ## Computes the common logarithm (base 10) of `x` @@ -200,11 +209,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.} @@ -665,3 +669,6 @@ when isMainModule: doAssert floorMod(8.0, -3.0) ==~ -1.0 doAssert floorMod(-8.5, 3.0) ==~ 0.5 + + block: # log + doAssert log(3.0, 4.0) == log(4.0) / log(3.0) From abf8ee049a00676e2b45faaf79b5eff44f20f467 Mon Sep 17 00:00:00 2001 From: Koki Fushimi Date: Mon, 4 Jun 2018 16:48:11 +0900 Subject: [PATCH 052/158] Broaden the argument types --- lib/pure/math.nim | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index cd7bf5fe2e..9cadfb6b74 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -148,8 +148,13 @@ else: # JS proc ln*(x: float32): float32 {.importc: "Math.log", nodecl.} proc ln*(x: float64): float64 {.importc: "Math.log", nodecl.} -proc log*[T: SomeFloat](b, x: T): T = ln(x) / ln(b) +proc log*[B, X: SomeFloat](b: B, x: X): auto = ## Computes the logarithm base ``b`` of ``x`` + when B is float64 or X is float64 + var r: float64 + else: + var r: float32 + result = ln(x) / ln(b) when not defined(JS): # C proc log10*(x: float32): float32 {.importc: "log10f", header: "".} From 2e9d486378965046227517f15a311cb72a84009e Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 4 Jun 2018 11:59:02 +0200 Subject: [PATCH 053/158] wip --- compiler/rodimpl.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rodimpl.nim b/compiler/rodimpl.nim index fffec00043..46a0e16db5 100644 --- a/compiler/rodimpl.nim +++ b/compiler/rodimpl.nim @@ -839,12 +839,12 @@ proc loadNode*(g: ModuleGraph; module: PSym): PNode = abs module.id): var b = BlobReader(pos: 0) - shallowCopy b.s, row[0] # ensure we can read without index checks: - b.s.add '\0' + b.s = row[0] & '\0' result.add decodeNode(g, b, module.info) db.exec(sql"insert into controlblock(idgen) values (?)", gFrontEndId) + echo result replay(g, module, result) proc setupModuleCache*(g: ModuleGraph) = From 05f81482f44215d6694298f351296ccab4e10dc3 Mon Sep 17 00:00:00 2001 From: Koki Fushimi Date: Tue, 5 Jun 2018 00:22:40 +0900 Subject: [PATCH 054/158] Change to use log(x, base) --- lib/pure/math.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 9cadfb6b74..84d4005c30 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -148,13 +148,13 @@ else: # JS proc ln*(x: float32): float32 {.importc: "Math.log", nodecl.} proc ln*(x: float64): float64 {.importc: "Math.log", nodecl.} -proc log*[B, X: SomeFloat](b: B, x: X): auto = - ## Computes the logarithm base ``b`` of ``x`` +proc log*[X, B: SomeFloat](x: X, base: B): auto = + ## Computes the logarithm ``base`` of ``x`` when B is float64 or X is float64 var r: float64 else: var r: float32 - result = ln(x) / ln(b) + result = ln(x) / ln(base) when not defined(JS): # C proc log10*(x: float32): float32 {.importc: "log10f", header: "".} From 41601e93074c1ff657b5e696f9d5e73e57ac660c Mon Sep 17 00:00:00 2001 From: Koki Fushimi Date: Tue, 5 Jun 2018 00:27:04 +0900 Subject: [PATCH 055/158] Bug fix --- lib/pure/math.nim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 84d4005c30..9fe24e358e 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -150,11 +150,12 @@ else: # JS proc log*[X, B: SomeFloat](x: X, base: B): auto = ## Computes the logarithm ``base`` of ``x`` - when B is float64 or X is float64 + when B is float64 or X is float64: var r: float64 else: var r: float32 - result = ln(x) / ln(base) + r = ln(x) / ln(base) + return r when not defined(JS): # C proc log10*(x: float32): float32 {.importc: "log10f", header: "".} From 090ffa386961459d529c2561d3e196be6df83c50 Mon Sep 17 00:00:00 2001 From: Koki Fushimi Date: Tue, 5 Jun 2018 02:32:39 +0900 Subject: [PATCH 056/158] Use concrete expression --- lib/pure/math.nim | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 9fe24e358e..95a0a345c5 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -148,14 +148,13 @@ else: # JS proc ln*(x: float32): float32 {.importc: "Math.log", nodecl.} proc ln*(x: float64): float64 {.importc: "Math.log", nodecl.} -proc log*[X, B: SomeFloat](x: X, base: B): auto = +proc log*[T: SomeFloat](x, base: T): T = ## Computes the logarithm ``base`` of ``x`` - when B is float64 or X is float64: - var r: float64 - else: - var r: float32 - r = ln(x) / ln(base) - return r + ln(x) / ln(base) +proc log*(x: float64, base: float32): float64 = + ln(x) / ln(base) +proc log*(x: float32, base: float64): float64 = + ln(x) / ln(base) when not defined(JS): # C proc log10*(x: float32): float32 {.importc: "log10f", header: "".} From e03b3bdde75f5e82f9c5512fcd00033704de4a34 Mon Sep 17 00:00:00 2001 From: cooldome Date: Wed, 6 Jun 2018 23:41:19 +0100 Subject: [PATCH 057/158] fixes 7980 --- compiler/hlo.nim | 50 +++++++++++++--------- tests/template/tpattern_with_converter.nim | 27 ++++++++++++ 2 files changed, 56 insertions(+), 21 deletions(-) create mode 100644 tests/template/tpattern_with_converter.nim diff --git a/compiler/hlo.nim b/compiler/hlo.nim index 8251e3179c..511712b541 100644 --- a/compiler/hlo.nim +++ b/compiler/hlo.nim @@ -36,27 +36,35 @@ proc applyPatterns(c: PContext, n: PNode): PNode = # we apply the last pattern first, so that pattern overriding is possible; # however the resulting AST would better not trigger the old rule then # anymore ;-) - for i in countdown(c.patterns.len-1, 0): - let pattern = c.patterns[i] - if not isNil(pattern): - let x = applyRule(c, pattern, result) - if not isNil(x): - assert x.kind in {nkStmtList, nkCall} - # better be safe than sorry, so check evalTemplateCounter too: - inc(evalTemplateCounter) - if evalTemplateCounter > evalTemplateLimit: - globalError(c.config, n.info, "template instantiation too nested") - # deactivate this pattern: - c.patterns[i] = nil - if x.kind == nkStmtList: - assert x.len == 3 - x.sons[1] = evalPattern(c, x.sons[1], result) - result = flattenStmts(x) - else: - result = evalPattern(c, x, result) - dec(evalTemplateCounter) - # activate this pattern again: - c.patterns[i] = pattern + if c.patterns.len > 0: + + # temporary disable converters + var ctx_converters: TSymSeq + shallowCopy(ctx_converters, c.converters) + c.converters = @[] + defer: shallowCopy(c.converters, ctx_converters) + + for i in countdown(c.patterns.len-1, 0): + let pattern = c.patterns[i] + if not isNil(pattern): + let x = applyRule(c, pattern, result) + if not isNil(x): + assert x.kind in {nkStmtList, nkCall} + # better be safe than sorry, so check evalTemplateCounter too: + inc(evalTemplateCounter) + if evalTemplateCounter > evalTemplateLimit: + globalError(c.config, n.info, "template instantiation too nested") + # deactivate this pattern: + c.patterns[i] = nil + if x.kind == nkStmtList: + assert x.len == 3 + x.sons[1] = evalPattern(c, x.sons[1], result) + result = flattenStmts(x) + else: + result = evalPattern(c, x, result) + dec(evalTemplateCounter) + # activate this pattern again: + c.patterns[i] = pattern proc hlo(c: PContext, n: PNode): PNode = inc(c.hloLoopDetector) diff --git a/tests/template/tpattern_with_converter.nim b/tests/template/tpattern_with_converter.nim new file mode 100644 index 0000000000..e0632552bf --- /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 From 8ba7e7d807c37a0988abd69ef9824b4822fe04f3 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 7 Jun 2018 18:24:16 +0200 Subject: [PATCH 058/158] toy program works with incremental compilation --- compiler/astalgo.nim | 3 +-- compiler/incremental.nim | 1 + compiler/modules.nim | 9 +++++---- compiler/rodimpl.nim | 12 +++++++----- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index fff1527d32..0afe56bb70 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -66,7 +66,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.} @@ -157,7 +156,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 diff --git a/compiler/incremental.nim b/compiler/incremental.nim index 378ba0665a..c0d89a5986 100644 --- a/compiler/incremental.nim +++ b/compiler/incremental.nim @@ -104,6 +104,7 @@ when nimIncremental: 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, diff --git a/compiler/modules.nim b/compiler/modules.nim index 04b1506f4f..b3a1e90d6e 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -95,8 +95,13 @@ proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PNode {.pr graph.addDep(s, fileIdx) graph.addIncludeDep(s.position.FileIndex, fileIdx) +proc connectCallbacks*(graph: ModuleGraph) = + graph.includeFileCallback = includeModule + graph.importModuleCallback = importModule + proc compileSystemModule*(graph: ModuleGraph) = if graph.systemModule == nil: + connectCallbacks(graph) graph.config.m.systemFileIdx = fileInfoIdx(graph.config, graph.config.libpath / "system.nim") discard graph.compileModule(graph.config.m.systemFileIdx, {sfSystemModule}) @@ -105,10 +110,6 @@ proc wantMainModule*(conf: ConfigRef) = fatal(conf, newLineInfo(conf, "command line", 1, 1), errGenerated, "command expects a filename") conf.projectMainIdx = fileInfoIdx(conf, addFileExt(conf.projectFull, NimExt)) -proc connectCallbacks*(graph: ModuleGraph) = - graph.includeFileCallback = includeModule - graph.importModuleCallback = importModule - proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIDX) = connectCallbacks(graph) let conf = graph.config diff --git a/compiler/rodimpl.nim b/compiler/rodimpl.nim index 46a0e16db5..e9054fd266 100644 --- a/compiler/rodimpl.nim +++ b/compiler/rodimpl.nim @@ -42,13 +42,14 @@ proc needsRecompile(g: ModuleGraph; fileIdx: FileIndex; fullpath: string; proc getModuleId*(g: ModuleGraph; fileIdx: FileIndex; fullpath: string): int = if g.config.symbolFiles in {disabledSf, writeOnlySf}: return getID() let module = g.incr.db.getRow( - sql"select id, fullHash from modules where fullpath = ?", fullpath) + 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: doAssert(result != 0) @@ -844,13 +845,14 @@ proc loadNode*(g: ModuleGraph; module: PSym): PNode = result.add decodeNode(g, b, module.info) db.exec(sql"insert into controlblock(idgen) values (?)", gFrontEndId) - echo result 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") From 2662d713f71da542e662860a3cadb5c57e4f7592 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 10 Jun 2018 17:06:02 +0200 Subject: [PATCH 059/158] implement passL and passC for the compilation cache --- compiler/options.nim | 6 +----- compiler/pragmas.nim | 11 ++++++++--- compiler/rodimpl.nim | 4 ++++ 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/compiler/options.nim b/compiler/options.nim index bebbf45443..d3f2f9d1cd 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -246,11 +246,7 @@ template depConfigFields*(fn) {.dirty.} = fn(selectedGC) template serializeConfigFields(fn) {.dirty.} = - fn(cppDefines) - fn(externalToLink) - fn(linkOptions) - fn(compileOptions) - fn(toCompile) + fn(cppDefines) # XXX TODO: Replay this. const oldExperimentalFeatures* = {implicitDeref, dotOperators, callOperator, parallel} diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index de9636809c..a51d250a24 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -82,7 +82,6 @@ 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) @@ -933,8 +932,14 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, 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: diff --git a/compiler/rodimpl.nim b/compiler/rodimpl.nim index e9054fd266..8cac5d0305 100644 --- a/compiler/rodimpl.nim +++ b/compiler/rodimpl.nim @@ -789,6 +789,10 @@ proc replay(g: ModuleGraph; module: PSym; n: PNode) = 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 "inc": let destKey = n[1].strVal let by = n[2].intVal From 4ab6dd51b03bfb5d180927fac34d76ed855ed4b7 Mon Sep 17 00:00:00 2001 From: Vindaar Date: Sun, 10 Jun 2018 14:13:55 +0200 Subject: [PATCH 060/158] fix #7997 Fixes issue #7997, which was caused by an export of a `release` proc in `locks`. Thus the `release` in `defined(release)` of the `ifDebug` template, was of kind `nkSym` instead of `nkIdent`. We fix this by getting the `PIdent` of the argument to `defined` using `considerQuotedIdent`. This has the nice property of also checking for a valid identifier for us. E.g. `defined(123)` would fail with ``` Error: in expression 'defined(123)': identifier expected, but found '123' ``` The `localError` is removed, due to a clear distinction between `declared` and `defined` now. --- compiler/semexprs.nim | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 92b9c365ad..1296bddaa7 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1613,10 +1613,8 @@ proc semDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PNode = # 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 + let d = considerQuotedIdent(c.config, 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 From 9c5f38850d5ebc3fba8ea2e6b0a561c75d70675b Mon Sep 17 00:00:00 2001 From: Vindaar Date: Sun, 10 Jun 2018 17:09:26 +0200 Subject: [PATCH 061/158] add test case for fix to #7997 --- tests/template/tdefined_overload.nim | 46 ++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 tests/template/tdefined_overload.nim diff --git a/tests/template/tdefined_overload.nim b/tests/template/tdefined_overload.nim new file mode 100644 index 0000000000..bcc83e4145 --- /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" From 8db27d30e6153fc943ef54e837ed9a3df7d56004 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 10 Jun 2018 18:05:43 +0200 Subject: [PATCH 062/158] record cppdefine for incremental compilation --- compiler/options.nim | 3 --- compiler/pragmas.nim | 3 +++ compiler/rodimpl.nim | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/options.nim b/compiler/options.nim index d3f2f9d1cd..74a217fbc0 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -245,9 +245,6 @@ template depConfigFields*(fn) {.dirty.} = fn(globalOptions) fn(selectedGC) -template serializeConfigFields(fn) {.dirty.} = - fn(cppDefines) # XXX TODO: Replay this. - const oldExperimentalFeatures* = {implicitDeref, dotOperators, callOperator, parallel} const diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index a51d250a24..bfb06e00ae 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -748,10 +748,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: @@ -844,6 +846,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) diff --git a/compiler/rodimpl.nim b/compiler/rodimpl.nim index 8cac5d0305..955e881e22 100644 --- a/compiler/rodimpl.nim +++ b/compiler/rodimpl.nim @@ -793,6 +793,8 @@ proc replay(g: ModuleGraph; module: PSym; n: PNode) = 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 From 5f2cdcd4fa0f3d5dd0156026c0685aa59d9f7f92 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sun, 10 Jun 2018 16:44:49 +0300 Subject: [PATCH 063/158] fix #7653 --- compiler/ccgtypes.nim | 2 -- compiler/sighashes.nim | 25 +++++++++++++++---------- tests/cpp/tempty_generic_obj.nim | 16 ++++++++++++++++ tests/generics/t3977.nim | 4 +++- 4 files changed, 34 insertions(+), 13 deletions(-) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 7b44cddad3..4b0fd49aad 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -791,8 +791,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 diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 46b83c3862..0b95387cd9 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: diff --git a/tests/cpp/tempty_generic_obj.nim b/tests/cpp/tempty_generic_obj.nim index b4c746a30b..b61c699f64 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: "", nodecl.} [T] = object +proc mkVector*[T]: VectorAlt[T] {.importcpp: "std::vector<'*0>()", header: "", constructor, nodecl.} + +proc foo(): VectorAlt[cint] = + mkVector[cint]() + +proc bar(): VectorAlt[cstring] = + mkVector[cstring]() + +var x = foo() +var y = bar() + diff --git a/tests/generics/t3977.nim b/tests/generics/t3977.nim index 3140177447..eed1a7d634 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 From 5c449c8cd112139f430d3be67effffb304aeb521 Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Sun, 10 Jun 2018 23:42:53 +0300 Subject: [PATCH 064/158] Removed oldIterTranf feature --- compiler/lambdalifting.nim | 88 +------------------------------------- compiler/options.nim | 3 +- compiler/semexprs.nim | 2 - compiler/transf.nim | 2 +- tests/async/tasynctry2.nim | 18 -------- 5 files changed, 4 insertions(+), 109 deletions(-) delete mode 100644 tests/async/tasynctry2.nim diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 40e5bb6b08..b7240f7e92 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -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 @@ -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": diff --git a/compiler/options.nim b/compiler/options.nim index 150e67a185..9779f2020e 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -116,8 +116,7 @@ type callOperator, parallel, destructor, - notnil, - oldIterTransf + notnil SymbolFilesOption* = enum disabledSf, enabledSf, writeOnlySf, readOnlySf, v2Sf diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 92b9c365ad..3722989b29 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1556,8 +1556,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 diff --git a/compiler/transf.nim b/compiler/transf.nim index c2add13ff3..c37462e12e 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -985,7 +985,7 @@ proc transformBody*(g: ModuleGraph; module: PSym, n: PNode, prc: PSym): PNode = 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/tests/async/tasynctry2.nim b/tests/async/tasynctry2.nim deleted file mode 100644 index 4b3f17cc54..0000000000 --- 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 From f1185658cce0f74683d903ba8997f1d59e7dbaef Mon Sep 17 00:00:00 2001 From: cooldome Date: Sun, 10 Jun 2018 23:03:54 +0100 Subject: [PATCH 065/158] manual merge --- compiler/msgs.nim | 470 ---------------------------------------------- 1 file changed, 470 deletions(-) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index d1e0054649..533d3a57f0 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -11,476 +11,6 @@ import options, strutils, os, tables, ropes, platform, terminal, macros, configuration -const - explanationsBaseUrl* = "https://nim-lang.org/docs/manual" - -type - TMsgKind* = enum - errUnknown, errInternal, errIllFormedAstX, errCannotOpenFile, errGenerated, - errStringLiteralExpected, - errIntLiteralExpected, errInvalidCharacterConstant, - errClosingTripleQuoteExpected, errClosingQuoteExpected, - errTabulatorsAreNotAllowed, errInvalidToken, - errInvalidNumber, errInvalidNumberOctalCode, errNumberOutOfRange, - errNnotAllowedInCharacter, errClosingBracketExpected, errMissingFinalQuote, - errIdentifierExpected, errNewlineExpected, errInvalidModuleName, - errOperatorExpected, errTokenExpected, - errRecursiveDependencyX, errOnOrOffExpected, errNoneSpeedOrSizeExpected, - errInvalidPragma, errUnknownPragma, errInvalidDirectiveX, - errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation, - errExceptionAlreadyHandled, - errYieldNotAllowedHere, errYieldNotAllowedInTryStmt, - errInvalidNumberOfYieldExpr, errCannotReturnExpr, - errNoReturnWithReturnTypeNotAllowed, errAttemptToRedefine, - errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel, - errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected, - errInvalidVarSubstitution, errUnknownVar, errUnknownCcompiler, - errOnOrOffExpectedButXFound, errOnOffOrListExpectedButXFound, - errNoneBoehmRefcExpectedButXFound, - errNoneSpeedOrSizeExpectedButXFound, errGuiConsoleOrLibExpectedButXFound, - errUnknownOS, errUnknownCPU, errGenOutExpectedButXFound, - errArgsNeedRunOption, errInvalidMultipleAsgn, errColonOrEqualsExpected, - errExprExpected, errUndeclaredField, - errUndeclaredRoutine, errUseQualifier, - errTypeExpected, - errSystemNeeds, errExecutionOfProgramFailed, errNotOverloadable, - errInvalidArgForX, errStmtHasNoEffect, errXExpectsTypeOrValue, - errXExpectsArrayType, errIteratorCannotBeInstantiated, errExprXAmbiguous, - errConstantDivisionByZero, errOrdinalTypeExpected, - errOrdinalOrFloatTypeExpected, errFloatTypeExpected, errStringTypeExpected, - errOverOrUnderflow, - errCannotEvalXBecauseIncompletelyDefined, errChrExpectsRange0_255, - errDynlibRequiresExportc, errUndeclaredFieldX, errNilAccess, - errIndexOutOfBounds, errIndexTypesDoNotMatch, errBracketsInvalidForType, - errValueOutOfSetBounds, errFieldInitTwice, errFieldNotInit, - errExprXCannotBeCalled, errExprHasNoType, errExprXHasNoType, - errCastNotInSafeMode, errExprCannotBeCastToX, errCommaOrParRiExpected, - errCurlyLeOrParLeExpected, errSectionExpected, errRangeExpected, - errMagicOnlyInSystem, errPowerOfTwoExpected, - errStringMayNotBeEmpty, errCallConvExpected, errProcOnlyOneCallConv, - errSymbolMustBeImported, errExprMustBeBool, errConstExprExpected, - errDuplicateCaseLabel, errRangeIsEmpty, errSelectorMustBeOfCertainTypes, - errSelectorMustBeOrdinal, errOrdXMustNotBeNegative, errLenXinvalid, - errWrongNumberOfVariables, errExprCannotBeRaised, errBreakOnlyInLoop, - errTypeXhasUnknownSize, errConstNeedsConstExpr, errConstNeedsValue, - errResultCannotBeOpenArray, errSizeTooBig, errSetTooBig, - errBaseTypeMustBeOrdinal, errInheritanceOnlyWithNonFinalObjects, - errInheritanceOnlyWithEnums, errIllegalRecursionInTypeX, - errCannotInstantiateX, errExprHasNoAddress, errXStackEscape, - errVarForOutParamNeededX, - errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX, - errAmbiguousCallXYZ, errWrongNumberOfArguments, - errWrongNumberOfArgumentsInCall, - errMissingGenericParamsForTemplate, - errXCannotBePassedToProcVar, - errPragmaOnlyInHeaderOfProcX, errImplOfXNotAllowed, - errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX, - errInvalidDiscard, errIllegalConvFromXtoY, errCannotBindXTwice, - errInvalidOrderInArrayConstructor, - errInvalidOrderInEnumX, errEnumXHasHoles, errExceptExpected, errInvalidTry, - errOptionExpected, errXisNoLabel, errNotAllCasesCovered, - errUnknownSubstitionVar, errComplexStmtRequiresInd, errXisNotCallable, - errNoPragmasAllowedForX, errNoGenericParamsAllowedForX, - errInvalidParamKindX, errDefaultArgumentInvalid, errNamedParamHasToBeIdent, - errNoReturnTypeForX, errConvNeedsOneArg, errInvalidPragmaX, - errXNotAllowedHere, errInvalidControlFlowX, - errXisNoType, errCircumNeedsPointer, errInvalidExpression, - errInvalidExpressionX, errEnumHasNoValueX, errNamedExprExpected, - errNamedExprNotAllowed, errXExpectsOneTypeParam, - errArrayExpectsTwoTypeParams, errInvalidVisibilityX, errInitHereNotAllowed, - errXCannotBeAssignedTo, errIteratorNotAllowed, errXNeedsReturnType, - errNoReturnTypeDeclared, - errNoCommand, errInvalidCommandX, errXOnlyAtModuleScope, - errXNeedsParamObjectType, - errTemplateInstantiationTooNested, errMacroInstantiationTooNested, - errInstantiationFrom, - errInvalidIndexValueForTuple, errCommandExpectsFilename, - errMainModuleMustBeSpecified, - errXExpected, - errTIsNotAConcreteType, - errCastToANonConcreteType, - errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError, - errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile, - errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitly, - errOnlyACallOpCanBeDelegator, errUsingNoSymbol, - errMacroBodyDependsOnGenericTypes, - errDestructorNotGenericEnough, - errInlineIteratorsAsProcParams, - errXExpectsTwoArguments, - errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations, - errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX, - errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument, - errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate, - errXhasSideEffects, errIteratorExpected, errLetNeedsInit, - errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX, - errXCannotBeClosure, errXMustBeCompileTime, - errCannotInferTypeOfTheLiteral, - errCannotInferReturnType, - errCannotInferStaticParam, - errGenericLambdaNotAllowed, - errProcHasNoConcreteType, - errCompilerDoesntSupportTarget, - errInOutFlagNotExtern, - errUser, - warnCannotOpenFile, - warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, - warnDeprecated, warnConfigDeprecated, - warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel, - warnUnknownSubstitutionX, warnLanguageXNotSupported, - warnFieldXNotSupported, warnCommentXIgnored, - warnNilStatement, warnTypelessParam, - warnUseBase, warnWriteToForeignHeap, warnUnsafeCode, - warnEachIdentIsTuple, warnShadowIdent, - warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, - warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed, - 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\'", - errGenerated: "$1", - errStringLiteralExpected: "string literal expected", - errIntLiteralExpected: "integer literal expected", - errInvalidCharacterConstant: "invalid character constant", - errClosingTripleQuoteExpected: "closing \"\"\" expected, but end of file reached", - errClosingQuoteExpected: "closing \" expected", - errTabulatorsAreNotAllowed: "tabulators are not allowed", - errInvalidToken: "invalid token: $1", - errInvalidNumber: "$1 is not a valid number", - errInvalidNumberOctalCode: "$1 is not a valid number; did you mean octal? Then use one of '0o', '0c' or '0C'.", - errNumberOutOfRange: "number $1 out of valid range", - errNnotAllowedInCharacter: "\\n not allowed in character literal", - errClosingBracketExpected: "closing ']' expected, but end of file reached", - errMissingFinalQuote: "missing final \' for character literal", - errIdentifierExpected: "identifier expected, but found \'$1\'", - errNewlineExpected: "newline expected, but found \'$1\'", - errInvalidModuleName: "invalid module name: '$1'", - errOperatorExpected: "operator expected, but found \'$1\'", - errTokenExpected: "\'$1\' expected", - errRecursiveDependencyX: "recursive dependency: \'$1\'", - errOnOrOffExpected: "\'on\' or \'off\' expected", - errNoneSpeedOrSizeExpected: "\'none\', \'speed\' or \'size\' expected", - errInvalidPragma: "invalid pragma", - errUnknownPragma: "unknown pragma: \'$1\'", - errInvalidDirectiveX: "invalid directive: \'$1\'", - errAtPopWithoutPush: "\'pop\' without a \'push\' pragma", - errEmptyAsm: "empty asm statement", - errInvalidIndentation: "invalid indentation", - errExceptionAlreadyHandled: "exception already handled", - errYieldNotAllowedHere: "'yield' only allowed in an iterator", - errYieldNotAllowedInTryStmt: "'yield' cannot be used within 'try' in a non-inlined iterator", - errInvalidNumberOfYieldExpr: "invalid number of \'yield\' expressions", - errCannotReturnExpr: "current routine cannot return an expression", - errNoReturnWithReturnTypeNotAllowed: "routines with NoReturn pragma are not allowed to have return type", - errAttemptToRedefine: "redefinition of \'$1\'", - 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", - errNoneBoehmRefcExpectedButXFound: "'none', 'boehm' or 'refc' expected, but '$1' found", - errNoneSpeedOrSizeExpectedButXFound: "'none', 'speed' or 'size' expected, but '$1' found", - errGuiConsoleOrLibExpectedButXFound: "'gui', 'console' or 'lib' expected, but '$1' found", - errUnknownOS: "unknown OS: '$1'", - errUnknownCPU: "unknown CPU: '$1'", - errGenOutExpectedButXFound: "'c', 'c++' or 'yaml' expected, but '$1' found", - errArgsNeedRunOption: "arguments can only be given if the '--run' option is selected", - errInvalidMultipleAsgn: "multiple assignment is not allowed", - errColonOrEqualsExpected: "\':\' or \'=\' expected, but found \'$1\'", - errExprExpected: "expression 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: "overloaded \'$1\' leads to ambiguous calls", - errInvalidArgForX: "invalid argument for \'$1\'", - errStmtHasNoEffect: "statement has no effect", - errXExpectsTypeOrValue: "\'$1\' expects a type or value", - 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: "division by zero", - errOrdinalTypeExpected: "ordinal type expected", - errOrdinalOrFloatTypeExpected: "ordinal or float type expected", - errFloatTypeExpected: "float type expected", - errStringTypeExpected: "string type expected", - errOverOrUnderflow: "over- or underflow", - errCannotEvalXBecauseIncompletelyDefined: "cannot evaluate '$1' because type is not defined completely", - errChrExpectsRange0_255: "\'chr\' expects an int in the range 0..255", - errDynlibRequiresExportc: "\'dynlib\' requires \'exportc\'", - errUndeclaredFieldX: "undeclared field: \'$1\'", - 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", - errFieldInitTwice: "field initialized twice: \'$1\'", - errFieldNotInit: "field \'$1\' not initialized", - errExprXCannotBeCalled: "expression \'$1\' cannot be called", - errExprHasNoType: "expression has no type", - errExprXHasNoType: "expression \'$1\' has no type (or is ambiguous)", - errCastNotInSafeMode: "\'cast\' not allowed in safe mode", - errExprCannotBeCastToX: "expression cannot be cast to $1", - 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", - errSelectorMustBeOfCertainTypes: "selector must be of an ordinal type, float or string", - errSelectorMustBeOrdinal: "selector must be of an ordinal type", - errOrdXMustNotBeNegative: "ord($1) must not be negative", - errLenXinvalid: "len($1) must be less than 32768", - errWrongNumberOfVariables: "wrong number of variables", - errExprCannotBeRaised: "only a 'ref object' can be raised", - errBreakOnlyInLoop: "'break' only allowed in loop construct", - 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", - errSetTooBig: "set is too large", - errBaseTypeMustBeOrdinal: "base type of a set must be an ordinal", - errInheritanceOnlyWithNonFinalObjects: "inheritance only works with non-final objects", - errInheritanceOnlyWithEnums: "inheritance only works with an enum", - errIllegalRecursionInTypeX: "illegal recursion in type \'$1\'", - errCannotInstantiateX: "cannot instantiate: \'$1\'", - errExprHasNoAddress: "expression has no address", - errXStackEscape: "address of '$1' may not escape its stack frame", - errVarForOutParamNeededX: "for a \'var\' type a variable needs to be passed; but '$1' is immutable", - errPureTypeMismatch: "type mismatch", - 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: "\'$1\' cannot be passed to a procvar", - errPragmaOnlyInHeaderOfProcX: "pragmas are only allowed in the header of a proc; redefinition of $1", - errImplOfXNotAllowed: "implementation of \'$1\' is not allowed", - errImplOfXexpected: "implementation of \'$1\' expected", - errNoSymbolToBorrowFromFound: "no symbol to borrow from found", - errDiscardValueX: "value of type '$1' has to be discarded", - errInvalidDiscard: "statement returns no value that can be discarded", - errIllegalConvFromXtoY: "conversion from $1 to $2 is invalid", - errCannotBindXTwice: "cannot bind parameter \'$1\' twice", - errInvalidOrderInArrayConstructor: "invalid order in array constructor", - 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: "option expected, but found \'$1\'", - 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", - errNoGenericParamsAllowedForX: "no generic parameters 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: "invalid pragma: $1", - errXNotAllowedHere: "$1 not allowed here", - errInvalidControlFlowX: "invalid control flow: $1", - errXisNoType: "invalid type: \'$1\'", - errCircumNeedsPointer: "'[]' needs a pointer or reference type", - errInvalidExpression: "invalid expression", - errInvalidExpressionX: "invalid expression: \'$1\'", - errEnumHasNoValueX: "enum has no value \'$1\'", - errNamedExprExpected: "named expression expected", - errNamedExprNotAllowed: "named expression not allowed here", - errXExpectsOneTypeParam: "\'$1\' expects one type parameter", - errArrayExpectsTwoTypeParams: "array expects two type parameters", - errInvalidVisibilityX: "invalid visibility: \'$1\'", - errInitHereNotAllowed: "initialization not allowed here", - errXCannotBeAssignedTo: "\'$1\' cannot be assigned to", - errIteratorNotAllowed: "iterators can only be defined at the module\'s top level", - errXNeedsReturnType: "$1 needs a return type", - errNoReturnTypeDeclared: "no return type declared", - errNoCommand: "no command given", - errInvalidCommandX: "invalid command: \'$1\'", - errXOnlyAtModuleScope: "\'$1\' is only allowed at top level", - errXNeedsParamObjectType: "'$1' needs a parameter that has an object type", - 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", - errTIsNotAConcreteType: "\'$1\' is not a concrete type.", - 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: "type \'var var\' is not allowed", - errInstantiateXExplicitly: "instantiate '$1' explicitly", - errOnlyACallOpCanBeDelegator: "only a call operator can be a delegator", - 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", - errCannotInterpretNodeX: "cannot evaluate \'$1\'", - 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", - errIteratorExpected: "iterator within for loop context expected", - errLetNeedsInit: "'let' symbol requires an initialization", - errThreadvarCannotInit: "a thread var cannot be initialized explicitly; this would only run for the main thread", - errWrongSymbolX: "usage of \'$1\' is a user-defined error", - errIllegalCaptureX: "illegal capture '$1'", - errXCannotBeClosure: "'$1' cannot have 'closure' calling convention", - errXMustBeCompileTime: "'$1' can only be used in compile-time context", - errCannotInferTypeOfTheLiteral: "cannot infer the type of the $1", - errCannotInferReturnType: "cannot infer the return type of the proc", - errCannotInferStaticParam: "cannot infer the value of the static param `$1`", - errGenericLambdaNotAllowed: "A nested proc can have generic parameters only when " & - "it is used as an operand to another routine and the types " & - "of the generic paramers can be inferred from the expected signature.", - errProcHasNoConcreteType: "'$1' doesn't have a concrete type, due to unspecified generic parameters.", - errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target", - errInOutFlagNotExtern: "The `$1` modifier can be used only with imported types", - 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", - warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead", - 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.", - 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", "NilStmt", - "TypelessParam", "UseBase", "WriteToForeignHeap", - "UnsafeCode", "EachIdentIsTuple", "ShadowIdent", - "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit", - "GcMem", "Destructor", "LockLevel", "ResultShadowed", "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 # MsgConfig* = ref object of RootObj From 17ab7aff449865937c7901dc0314e5990b032c01 Mon Sep 17 00:00:00 2001 From: cooldome Date: Sun, 10 Jun 2018 23:05:50 +0100 Subject: [PATCH 066/158] manual merge continues --- compiler/types.nim | 46 ++++++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/compiler/types.nim b/compiler/types.nim index 30c52e127b..1fab842cc2 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -86,11 +86,11 @@ proc isPureObject*(typ: PType): bool = t = t.sons[0].skipTypes(skipPtrs) result = t.sym != nil and sfPure in t.sym.flags -proc getValue*[T:BiggestInt|BiggestFloat|string](n: PNode): T = - ## get value of liternal node +proc getOrdValue*(n: PNode): BiggestInt = case n.kind - of nkNilLit: reset(result) - of nkHiddenStdConv: result = getValue[T](n.sons[1]) + 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 @@ -589,15 +589,28 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = result = typeToStr[t.kind] result.addTypeFlags(t) -proc firstValue*[T:BiggestInt|BiggestFloat](t: PType): T = - case t.kind: - of tyVar: result = firstValue[T](t.sons[0]) - of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias: - result = firstValue[T](lastSon(t)) - of tyRange: - assert(t.n != nil) # range directly given: - assert(t.n.kind == nkRange) - result = getValue[T](t.n.sons[0]) +proc firstOrd*(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 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 + else: result = 0x8000000000000000'i64 + of tyInt8: result = - 128 + of tyInt16: result = - 32768 + of tyInt32: result = - 2147483646 - 2 + of tyInt64: result = 0x8000000000000000'i64 + of tyUInt..tyUInt64: result = 0 + of tyEnum: + # if basetype <> nil then return firstOrd of basetype + if sonsLen(t) > 0 and t.sons[0] != nil: + result = firstOrd(t.sons[0]) else: assert(t.n.sons[0].kind == nkSym) result = t.n.sons[0].sym.position @@ -612,9 +625,10 @@ proc firstValue*[T:BiggestInt|BiggestFloat](t: PType): T = proc lastOrd*(t: PType; fixedUnsigned = false): BiggestInt = case t.kind - of tyVar: result = lastValue[T](t.sons[0]) - of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias: - result = lastValue[T](lastSon(t)) + of tyBool: result = 1 + of tyChar: result = 255 + of tySet, tyVar: result = lastOrd(t.sons[0]) + of tyArray: result = lastOrd(t.sons[0]) of tyRange: assert(t.n != nil) # range directly given: assert(t.n.kind == nkRange) From 4d87d666ee12d9a16a0a3b21ab30b46f9e933cb0 Mon Sep 17 00:00:00 2001 From: cooldome Date: Sun, 10 Jun 2018 23:07:05 +0100 Subject: [PATCH 067/158] manual merge --- compiler/semtypes.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 366ba9cb79..602c5eeaf7 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -232,9 +232,9 @@ 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, errPureTypeMismatch) + localError(c.config, n.info, "type mismatch") elif not rangeT[0].isOrdinalType and rangeT[0].kind notin tyFloat..tyFloat128: - localError(c.config, n.info, errOrdinalOrFloatTypeExpected) + 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])) From 90b1030dec0a5fb6ac0f2a4332fecfa7264bbf44 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 11 Jun 2018 00:14:29 +0200 Subject: [PATCH 068/158] incremental compilation: save the configuration --- compiler/incremental.nim | 7 +++++++ compiler/rodimpl.nim | 33 ++++++++++++++++++++++----------- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/compiler/incremental.nim b/compiler/incremental.nim index c0d89a5986..2008d35de0 100644 --- a/compiler/incremental.nim +++ b/compiler/incremental.nim @@ -33,6 +33,7 @@ when nimIncremental: db*: DbConn w*: Writer r*: Reader + configChanged*: bool proc init*(incr: var IncrementalCtx) = incr.w.sstack = @[] @@ -92,6 +93,12 @@ when nimIncremental: ); """) + 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, diff --git a/compiler/rodimpl.nim b/compiler/rodimpl.nim index 955e881e22..7d24e4e67b 100644 --- a/compiler/rodimpl.nim +++ b/compiler/rodimpl.nim @@ -11,16 +11,29 @@ import strutils, os, intsets, tables, ropes, db_sqlite, msgs, options, types, renderer, rodutils, idents, astalgo, btrees, magicsys, cgmeth, extccomp, - btrees, trees + btrees, trees, condsyms, nversion ## Todo: -## - Make conditional symbols and the configuration part of a module's -## dependencies. Also include the rodfile "version". ## - 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. template db(): DbConn = g.incr.db +proc encodeConfig(g: ModuleGraph): string = + result = newStringOfCap(100) + result.add RodFileVersion + for d in definedSymbolNames(g.config.symbols): + result.add ' ' + result.add d + + 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 = ?", @@ -40,7 +53,9 @@ proc needsRecompile(g: ModuleGraph; fileIdx: FileIndex; fullpath: string; return false proc getModuleId*(g: ModuleGraph; fileIdx: FileIndex; fullpath: string): int = - if g.config.symbolFiles in {disabledSf, writeOnlySf}: return getID() + 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) @@ -64,13 +79,6 @@ proc getModuleId*(g: ModuleGraph; fileIdx: FileIndex; fullpath: string): int = db.exec(sql"delete from toplevelstmts where module = ?", module[0]) db.exec(sql"delete from statics where module = ?", module[0]) -when false: - proc getDefines(): string = - result = "" - for d in definedSymbolNames(): - if result.len != 0: add(result, " ") - add(result, d) - proc pushType(w: var Writer, t: PType) = if not containsOrIncl(w.tmarks, t.id): w.tstack.add(t) @@ -863,9 +871,12 @@ proc setupModuleCache*(g: ModuleGraph) = db = open(connection=dbfile, user="nim", password="", database="nim") 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") From a65e9c0e2520911d16b81e7af47fea8c3fdd2958 Mon Sep 17 00:00:00 2001 From: cooldome Date: Sun, 10 Jun 2018 23:27:45 +0100 Subject: [PATCH 069/158] fix comments --- compiler/sigmatch.nim | 27 ++++++++++-------------- compiler/types.nim | 48 ++++++++++++++++++++++++++++++++++++------- 2 files changed, 52 insertions(+), 23 deletions(-) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index ec13b7557a..bdae01d7d2 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -633,26 +633,21 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = else: discard proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} = - template check_range_in(t: typedesc): untyped = - let - a0 = firstValue[t](a) - a1 = lastValue[t](a) - f0 = firstValue[t](f) - f1 = lastValue[t](f) - if a0 == f0 and a1 == f1: - result = isEqual - elif a0 >= f0 and a1 <= f1: - result = isConvertible - elif a0 <= f1 and f0 <= a1: + template check_range[T](a_first, a_last, f_first, f_last: T): TTypeRelation = + if a_first == f_first and a_last == f_last: + isEqual + elif a_first >= f_first and a_last <= f_last: + isConvertible + elif a_first <= f_last and f_first <= a_last: # X..Y and C..D overlap iff (X <= D and C <= Y) - result = isConvertible + isConvertible else: - result = isNone + isNone if f.isOrdinalType: - check_range_in(BiggestInt) - else: - check_range_in(BiggestFloat) + check_range(firstOrd(a), lastOrd(a), firstOrd(f), lastOrd(f)) + else: + check_range(firstFloat(a), lastFloat(a), firstFloat(f), lastFloat(f)) proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType = diff --git a/compiler/types.nim b/compiler/types.nim index 1fab842cc2..83e20c38e5 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -88,13 +88,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 @@ -623,6 +626,21 @@ proc firstOrd*(t: PType): BiggestInt = internalError(newPartialConfigRef(), "invalid kind for firstOrd(" & $t.kind & ')') result = 0 + +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, tyStatic, tyInferred: + firstFloat(lastSon(t)) + else: + internalError(newPartialConfigRef(), "invalid kind for firstValue(" & $t.kind & ')') + NaN + proc lastOrd*(t: PType; fixedUnsigned = false): BiggestInt = case t.kind of tyBool: result = 1 @@ -663,6 +681,22 @@ proc lastOrd*(t: PType; fixedUnsigned = false): BiggestInt = internalError(newPartialConfigRef(), "invalid kind for lastOrd(" & $t.kind & ')') result = 0 + +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, tyStatic, tyInferred: + lastFloat(lastSon(t)) + else: + internalError(newPartialConfigRef(), "invalid kind for lastOrd(" & $t.kind & ')') + NaN + + proc lengthOrd*(t: PType): BiggestInt = case t.kind of tyInt64, tyInt32, tyInt: result = lastOrd(t) From 61e189792220b7eb3b6a6b2ac543c6de84862aae Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 11 Jun 2018 01:23:14 +0200 Subject: [PATCH 070/158] make tests green again --- compiler/nimeval.nim | 4 ++-- compiler/semstmts.nim | 14 ++++++++------ tests/macros/tmacros1.nim | 6 +++--- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim index 308560010c..f20b5642c8 100644 --- a/compiler/nimeval.nim +++ b/compiler/nimeval.nim @@ -73,7 +73,7 @@ proc evalScript*(i: Interpreter; scriptStream: PLLStream = nil) = let s = if scriptStream != nil: scriptStream else: llStreamOpen(findFile(i.graph.config, i.scriptName), fmRead) - processModule(i.graph, i.mainModule, s, nil, i.graph.cache) + processModule(i.graph, i.mainModule, s) proc findNimStdLib*(): string = ## Tries to find a path to a valid "system.nim" file. @@ -112,7 +112,7 @@ proc createInterpreter*(scriptName: string; vm.mode = emRepl vm.features = flags graph.vm = vm - graph.compileSystemModule(cache) + graph.compileSystemModule() result = Interpreter(mainModule: m, graph: graph, scriptName: scriptName) proc destroyInterpreter*(i: Interpreter) = diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 0f8e77fc9b..0e143e7c15 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1771,12 +1771,14 @@ proc semStaticStmt(c: PContext, n: PNode): PNode = inc c.inStaticContext let a = semStmt(c, n.sons[0]) dec c.inStaticContext - n.sons[0] = a - evalStaticStmt(c.module, c.graph, a, c.p.owner) - # for incremental replays, keep the AST as required for replays: - result = n - #result = newNodeI(nkDiscardStmt, n.info, 1) - #result.sons[0] = c.graph.emptyNode + when false: + n.sons[0] = a + evalStaticStmt(c.module, c.graph, a, c.p.owner) + # 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, diff --git a/tests/macros/tmacros1.nim b/tests/macros/tmacros1.nim index 9e3ab028bd..80afb66410 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))) From 657062145ba5303d68e107a6f5fc50513b9d7f49 Mon Sep 17 00:00:00 2001 From: Jimmie Houchin Date: Sun, 10 Jun 2018 23:53:42 -0500 Subject: [PATCH 071/158] Added FileMode to comment on asyncfile openAsync (#8008) --- lib/pure/asyncfile.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/asyncfile.nim b/lib/pure/asyncfile.nim index 1df7c3fc0e..37339d3d1a 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) From 26133a33a4e61a593463fa4c62d237ab0b9ffe47 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 11 Jun 2018 07:18:34 +0200 Subject: [PATCH 072/158] make tests green properly --- compiler/semstmts.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index af853ec52d..455e06e821 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1771,9 +1771,9 @@ proc semStaticStmt(c: PContext, n: PNode): PNode = inc c.inStaticContext let a = semStmt(c, n.sons[0]) dec c.inStaticContext + n.sons[0] = a + evalStaticStmt(c.module, c.graph, a, c.p.owner) when false: - n.sons[0] = a - evalStaticStmt(c.module, c.graph, a, c.p.owner) # for incremental replays, keep the AST as required for replays: result = n else: From b0ecc1aca6f68df81e5d774fe389975e41b967f1 Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 11 Jun 2018 17:08:25 +0200 Subject: [PATCH 073/158] disable -d:nimIncremental for devel --- compiler/nim.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/nim.cfg b/compiler/nim.cfg index 9cdb9c6c9a..1bd3fbfd6c 100644 --- a/compiler/nim.cfg +++ b/compiler/nim.cfg @@ -6,7 +6,7 @@ path:"$projectPath/.." define:booting define:nimcore -define:nimIncremental +#define:nimIncremental #import:"$projectpath/testability" @if windows: From 8f067634919eeb232ee04f759c4c1786ddd26ea6 Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Mon, 11 Jun 2018 17:57:19 +0300 Subject: [PATCH 074/158] Fixes #6803 --- compiler/cgen.nim | 21 ++--- compiler/semexprs.nim | 3 + tests/errmsgs/tproper_stacktrace.nim | 124 +++++++++++++++++++++++++-- 3 files changed, 129 insertions(+), 19 deletions(-) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index ee60e62d2e..cd344f096e 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -204,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) & "\L") - genCLineDir(p.s(cpsStmts), toFullPath(p.config, tt.info), 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(p.config, 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) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 64c145e51d..135c1b32c2 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -31,6 +31,9 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym, if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, s, flags) 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 proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = diff --git a/tests/errmsgs/tproper_stacktrace.nim b/tests/errmsgs/tproper_stacktrace.nim index 57e65fa6fb..4e5c5fbf85 100644 --- a/tests/errmsgs/tproper_stacktrace.nim +++ b/tests/errmsgs/tproper_stacktrace.nim @@ -1,11 +1,123 @@ 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() + + + echo "ok" From ac0f9860081ce20cd1ce1f823db7361ad284b1be Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Mon, 11 Jun 2018 23:19:00 +0200 Subject: [PATCH 075/158] Correct field lookup in concept types Fixes #6770 --- compiler/ccgexprs.nim | 2 +- tests/concepts/t6770.nim | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 tests/concepts/t6770.nim diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index bcfee0aabd..b5dcc76e52 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -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) diff --git a/tests/concepts/t6770.nim b/tests/concepts/t6770.nim new file mode 100644 index 0000000000..1787ee670b --- /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) From 4619ee70580b6dfb12dc3f8063173d5e0c07b9be Mon Sep 17 00:00:00 2001 From: cooldome Date: Mon, 11 Jun 2018 23:20:31 +0100 Subject: [PATCH 076/158] remove underscores --- compiler/sigmatch.nim | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index bdae01d7d2..3e278b3dc1 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -633,19 +633,19 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = else: discard proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} = - template check_range[T](a_first, a_last, f_first, f_last: T): TTypeRelation = - if a_first == f_first and a_last == f_last: + template checkRange[T](afirst, alast, ffirst, flast: T): TTypeRelation = + if afirst == ffirst and alast == flast: isEqual - elif a_first >= f_first and a_last <= f_last: + elif afirst >= ffirst and alast <= flast: isConvertible - elif a_first <= f_last and f_first <= a_last: + elif afirst <= flast and ffirst <= a_last: # X..Y and C..D overlap iff (X <= D and C <= Y) isConvertible else: isNone if f.isOrdinalType: - check_range(firstOrd(a), lastOrd(a), firstOrd(f), lastOrd(f)) + checkRange(firstOrd(a), lastOrd(a), firstOrd(f), lastOrd(f)) else: check_range(firstFloat(a), lastFloat(a), firstFloat(f), lastFloat(f)) From 1b7cf3df5192ab1d7612ba0b349117a8960156e3 Mon Sep 17 00:00:00 2001 From: cooldome Date: Tue, 12 Jun 2018 00:20:08 +0100 Subject: [PATCH 077/158] New approach --- compiler/hlo.nim | 50 ++++++++++++++++++------------------------- compiler/patterns.nim | 2 +- compiler/sigmatch.nim | 7 ++++-- 3 files changed, 27 insertions(+), 32 deletions(-) diff --git a/compiler/hlo.nim b/compiler/hlo.nim index 511712b541..8251e3179c 100644 --- a/compiler/hlo.nim +++ b/compiler/hlo.nim @@ -36,35 +36,27 @@ proc applyPatterns(c: PContext, n: PNode): PNode = # we apply the last pattern first, so that pattern overriding is possible; # however the resulting AST would better not trigger the old rule then # anymore ;-) - if c.patterns.len > 0: - - # temporary disable converters - var ctx_converters: TSymSeq - shallowCopy(ctx_converters, c.converters) - c.converters = @[] - defer: shallowCopy(c.converters, ctx_converters) - - for i in countdown(c.patterns.len-1, 0): - let pattern = c.patterns[i] - if not isNil(pattern): - let x = applyRule(c, pattern, result) - if not isNil(x): - assert x.kind in {nkStmtList, nkCall} - # better be safe than sorry, so check evalTemplateCounter too: - inc(evalTemplateCounter) - if evalTemplateCounter > evalTemplateLimit: - globalError(c.config, n.info, "template instantiation too nested") - # deactivate this pattern: - c.patterns[i] = nil - if x.kind == nkStmtList: - assert x.len == 3 - x.sons[1] = evalPattern(c, x.sons[1], result) - result = flattenStmts(x) - else: - result = evalPattern(c, x, result) - dec(evalTemplateCounter) - # activate this pattern again: - c.patterns[i] = pattern + for i in countdown(c.patterns.len-1, 0): + let pattern = c.patterns[i] + if not isNil(pattern): + let x = applyRule(c, pattern, result) + if not isNil(x): + assert x.kind in {nkStmtList, nkCall} + # better be safe than sorry, so check evalTemplateCounter too: + inc(evalTemplateCounter) + if evalTemplateCounter > evalTemplateLimit: + globalError(c.config, n.info, "template instantiation too nested") + # deactivate this pattern: + c.patterns[i] = nil + if x.kind == nkStmtList: + assert x.len == 3 + x.sons[1] = evalPattern(c, x.sons[1], result) + result = flattenStmts(x) + else: + result = evalPattern(c, x, result) + dec(evalTemplateCounter) + # activate this pattern again: + c.patterns[i] = pattern proc hlo(c: PContext, n: PNode): PNode = inc(c.hloLoopDetector) diff --git a/compiler/patterns.nim b/compiler/patterns.nim index 5409a48112..8c1017a95d 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, from_hlo = 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/sigmatch.nim b/compiler/sigmatch.nim index fcfdda8bbe..dd0270443f 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -2359,14 +2359,17 @@ 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, from_hlo = false): bool = var m: TCandidate initCandidate(c, m, f) let res = paramTypesMatch(m, f, a, ast.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 from_hlo: + res != nil + else: + 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.} = From 746da12d74028a0f5c1cfcc0eb14bc157df79837 Mon Sep 17 00:00:00 2001 From: cooldome Date: Tue, 12 Jun 2018 00:22:11 +0100 Subject: [PATCH 078/158] add comment --- compiler/sigmatch.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index dd0270443f..f5a8d225aa 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -2369,6 +2369,7 @@ proc argtypeMatches*(c: PContext, f, a: PType, from_hlo = false): bool = if not from_hlo: 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; From 44d82d94927424ce643aecba5231adb698bf3965 Mon Sep 17 00:00:00 2001 From: cooldome Date: Tue, 12 Jun 2018 09:17:24 +0100 Subject: [PATCH 079/158] style improvements --- compiler/patterns.nim | 2 +- compiler/sigmatch.nim | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/patterns.nim b/compiler/patterns.nim index 8c1017a95d..2d2aeba76a 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, from_hlo = true) + 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/sigmatch.nim b/compiler/sigmatch.nim index f5a8d225aa..bde3a5b1ec 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -2359,14 +2359,14 @@ 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, from_hlo = false): 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) #instantiateGenericConverters(c, res, m) # XXX this is used by patterns.nim too; I think it's better to not # instantiate generic converters for that - if not from_hlo: + if not fromHlo: res != nil else: # pattern templates do not allow for conversions except from int literal From 77f33db3ed5b3f3c116dcb8159e68a8d51daede8 Mon Sep 17 00:00:00 2001 From: cooldome Date: Tue, 12 Jun 2018 09:27:37 +0100 Subject: [PATCH 080/158] style fixes, typos --- compiler/sigmatch.nim | 10 +++++----- doc/manual.rst | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 3e278b3dc1..8db14f1cf5 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -633,12 +633,12 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = else: discard proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} = - template checkRange[T](afirst, alast, ffirst, flast: T): TTypeRelation = - if afirst == ffirst and alast == flast: + template checkRange[T](a0, a1, f0, f1: T): TTypeRelation = + if a0 == f0 and a1 == f1: isEqual - elif afirst >= ffirst and alast <= flast: + elif a0 >= f0 and a1 <= f1: isConvertible - elif afirst <= flast and ffirst <= a_last: + elif a0 <= f1 and f0 <= a1: # X..Y and C..D overlap iff (X <= D and C <= Y) isConvertible else: @@ -647,7 +647,7 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} = if f.isOrdinalType: checkRange(firstOrd(a), lastOrd(a), firstOrd(f), lastOrd(f)) else: - check_range(firstFloat(a), lastFloat(a), firstFloat(f), lastFloat(f)) + checkRange(firstFloat(a), lastFloat(a), firstFloat(f), lastFloat(f)) proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType = diff --git a/doc/manual.rst b/doc/manual.rst index 0ebd782bfa..9f55654a11 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -741,7 +741,7 @@ For further details, see `Convertible relation Subrange types -------------- -A subrange type is a range of values from an ordinal or float point 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: From a877a58f32790d9ef574bcd466b327989cdd6acf Mon Sep 17 00:00:00 2001 From: cooldome Date: Tue, 12 Jun 2018 09:31:45 +0100 Subject: [PATCH 081/158] Fix error message --- compiler/types.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/types.nim b/compiler/types.nim index 83e20c38e5..f31096b57a 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -638,7 +638,7 @@ proc firstFloat*(t: PType): BiggestFloat = of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic, tyInferred: firstFloat(lastSon(t)) else: - internalError(newPartialConfigRef(), "invalid kind for firstValue(" & $t.kind & ')') + internalError(newPartialConfigRef(), "invalid kind for firstFloat(" & $t.kind & ')') NaN proc lastOrd*(t: PType; fixedUnsigned = false): BiggestInt = @@ -693,7 +693,7 @@ proc lastFloat*(t: PType): BiggestFloat = of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic, tyInferred: lastFloat(lastSon(t)) else: - internalError(newPartialConfigRef(), "invalid kind for lastOrd(" & $t.kind & ')') + internalError(newPartialConfigRef(), "invalid kind for lastFloat(" & $t.kind & ')') NaN From 7e7b85afc7b915781c37fcf43487a99a30144dcd Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Tue, 12 Jun 2018 12:17:03 +0300 Subject: [PATCH 082/158] Allow stacktrace and linetrace pragmas on procs --- compiler/pragmas.nim | 4 ++-- compiler/semstmts.nim | 3 +++ tests/errmsgs/tproper_stacktrace.nim | 18 ++++++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index bfb06e00ae..bfb8e78eb1 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -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, diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 455e06e821..b4c327362a 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1489,6 +1489,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, # before compiling the proc body, set as current the scope # where the proc was declared let oldScope = c.currentScope + let oldOptions = c.config.options #c.currentScope = s.scope pushOwner(c, s) openScope(c) @@ -1569,6 +1570,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, popOwner(c) pushOwner(c, s) s.options = c.config.options + c.config.options = oldOptions + 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 == {}: diff --git a/tests/errmsgs/tproper_stacktrace.nim b/tests/errmsgs/tproper_stacktrace.nim index 4e5c5fbf85..134946651f 100644 --- a/tests/errmsgs/tproper_stacktrace.nim +++ b/tests/errmsgs/tproper_stacktrace.nim @@ -119,5 +119,23 @@ when isMainModule: 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" From 7a4b343d8d40193138b54967bfef2b66c387f877 Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Tue, 12 Jun 2018 12:39:54 +0300 Subject: [PATCH 083/158] Added NIM_STRLIT_FLAG to seq literals --- compiler/ccgexprs.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index b5dcc76e52..82cc3a1fb0 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -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(", {") From f555338ce8b4053bec48ee2ba8299a757db1ee67 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 12 Jun 2018 21:08:41 +0200 Subject: [PATCH 084/158] bugfix: allow 'export' in the VM --- compiler/condsyms.nim | 1 + compiler/vmgen.nim | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 773c0faf95..56587a4faf 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -70,3 +70,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimNoNil") defineSymbol("nimNoZeroTerminator") defineSymbol("nimNotNil") + defineSymbol("nimVmExportFixed") diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 7911abc8ec..59fed3cf76 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1899,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) From 3dd61d6fff794d29ae48f682488819e1c320f346 Mon Sep 17 00:00:00 2001 From: cooldome Date: Tue, 12 Jun 2018 23:20:34 +0100 Subject: [PATCH 085/158] Manual merge --- compiler/types.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/types.nim b/compiler/types.nim index 6120877cb5..4c8c244db0 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -593,6 +593,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = result = typeToStr[t.kind] result.addTypeFlags(t) + proc firstOrd*(conf: ConfigRef; t: PType): BiggestInt = case t.kind of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyProxy: From 101f23f3a65fabd5cba2f03070be1e157e572d79 Mon Sep 17 00:00:00 2001 From: cooldome Date: Tue, 12 Jun 2018 23:23:21 +0100 Subject: [PATCH 086/158] manual merge 2 --- compiler/sigmatch.nim | 2 +- compiler/types.nim | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index af0e9648f4..915eca8c59 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -649,7 +649,7 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} = isNone if f.isOrdinalType: - checkRange(firstOrd(a), lastOrd(a), firstOrd(f), lastOrd(f)) + checkRange(firstOrd(nil, a), lastOrd(nil, a), firstOrd(nil, f), lastOrd(nil, f)) else: checkRange(firstFloat(a), lastFloat(a), firstFloat(f), lastFloat(f)) diff --git a/compiler/types.nim b/compiler/types.nim index 4c8c244db0..23350a1e39 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -643,7 +643,7 @@ proc firstFloat*(t: PType): BiggestFloat = internalError(newPartialConfigRef(), "invalid kind for firstFloat(" & $t.kind & ')') NaN -proc lastOrd*(t: PType; fixedUnsigned = false): BiggestInt = +proc lastOrd*(conf: ConfigRef; t: PType; fixedUnsigned = false): BiggestInt = case t.kind of tyBool: result = 1 of tyChar: result = 255 @@ -699,7 +699,7 @@ proc lastFloat*(t: PType): BiggestFloat = NaN -proc lengthOrd*(t: PType): BiggestInt = +proc lengthOrd*(conf: ConfigRef; t: PType): BiggestInt = case t.kind of tyInt64, tyInt32, tyInt: result = lastOrd(conf, t) of tyDistinct: result = lengthOrd(conf, t.sons[0]) From 5348fef00326aabbb596b2ee7d0925fe10bbf7c6 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 13 Jun 2018 01:44:19 +0200 Subject: [PATCH 087/158] implements a --nep1:on switch to make the compiler enforce the naming conventions outlined in NEP-1 --- compiler/commands.nim | 2 + compiler/{nimfix/pretty.nim => linter.nim} | 49 ++++++++++++++++------ compiler/nimfix/nimfix.nim | 2 +- compiler/nimfix/prettybase.nim | 16 ++----- compiler/options.nim | 3 +- compiler/sem.nim | 2 +- compiler/semexprs.nim | 2 +- compiler/semgnrc.nim | 2 +- compiler/semstmts.nim | 4 +- compiler/semtempl.nim | 8 ++-- compiler/semtypes.nim | 11 +++-- compiler/sigmatch.nim | 2 +- nimsuggest/nimsuggest.nim | 1 - 13 files changed, 58 insertions(+), 46 deletions(-) rename compiler/{nimfix/pretty.nim => linter.nim} (76%) diff --git a/compiler/commands.nim b/compiler/commands.nim index cd9ebbe7d3..330504a76e 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -717,6 +717,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/nimfix/pretty.nim b/compiler/linter.nim similarity index 76% rename from compiler/nimfix/pretty.nim rename to compiler/linter.nim index f3fff781f1..7c9cdec833 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, - lineinfos] -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 @@ -93,12 +102,16 @@ proc beautifyName(s: string, k: TSymKind): string = result.add s[i] inc i +proc differ*(line: string, a, b: int, x: string): bool = + let y = line[a..b] + result = cmpIgnoreStyle(y, x) == 0 and y != x + 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 @@ -120,27 +133,37 @@ proc checkStyle(conf: ConfigRef; cache: IdentCache; info: TLineInfo, s: string, 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, cache, info, s.name.s, k, s) -template styleCheckDef*(info: TLineInfo; s: PSym; k: TSymKind) = +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*(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, 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(conf: ConfigRef; cache: IdentCache; 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 @@ -150,4 +173,4 @@ proc styleCheckUseImpl(conf: ConfigRef; cache: IdentCache; info: TLineInfo; s: P template styleCheckUse*(info: TLineInfo; s: PSym) = when defined(nimfix): - if gStyleCheck != StyleCheck.None: styleCheckUseImpl(conf, cache, info, s) + if gStyleCheck != StyleCheck.None: styleCheckUseImpl(conf, info, s) diff --git a/compiler/nimfix/nimfix.nim b/compiler/nimfix/nimfix.nim index c3e29f9a12..58b019cd3f 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 d1ad41a6ec..c3e16e5ba2 100644 --- a/compiler/nimfix/prettybase.nim +++ b/compiler/nimfix/prettybase.nim @@ -7,21 +7,11 @@ # distribution, for details about the copyright. # -import strutils, lexbase, streams -import ".." / [ast, msgs, lineinfos, idents, options] +import strutils except Letters +import lexbase, streams +import ".." / [ast, msgs, lineinfos, idents, options, linter] from os import splitFile -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*(conf: ConfigRef; info: TLineInfo; oldSym, newSym: PIdent) = let line = sourceLine(conf, info) var first = min(info.col.int, line.len) diff --git a/compiler/options.nim b/compiler/options.nim index 69d5275d5c..2e66a722c2 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -54,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 @@ -372,7 +372,6 @@ proc usesNativeGC*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc template compilationCachePresent*(conf: ConfigRef): untyped = conf.symbolFiles in {v2Sf, writeOnlySf} -# {optCaasEnabled, optSymbolFiles} * gGlobalOptions != {} template optPreserveOrigSource*(conf: ConfigRef): untyped = optEmbedOrigSrc in conf.globalOptions diff --git a/compiler/sem.nim b/compiler/sem.nim index e5d9401e47..3b16e0938b 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -15,7 +15,7 @@ import magicsys, parser, nversion, nimsets, semfold, modulepaths, importer, procfind, lookups, pragmas, passes, semdata, semtypinst, sigmatch, intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting, - evaltempl, patterns, parampatterns, sempass2, nimfix.pretty, semmacrosanity, + evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity, semparallel, lowerings, pluginsupport, plugins.active, rod, lineinfos from modulegraphs import ModuleGraph diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 135c1b32c2..9d7c493a7b 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2161,7 +2161,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 diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 727fa4f3f6..cc03db1c23 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -160,7 +160,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 = diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index b4c327362a..439ef8fcaa 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -364,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 @@ -627,7 +627,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 diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 086d090912..75c6bc4bb8 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -213,7 +213,7 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) = 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 @@ -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) @@ -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 d15ff8412b..3e62652a71 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -114,8 +114,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) @@ -451,7 +450,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, @@ -491,7 +490,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 @@ -685,7 +684,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 @@ -1062,7 +1061,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, 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: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index cd8d2f45d8..9a3c752618 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -13,7 +13,7 @@ import intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst, magicsys, condsyms, idents, lexer, options, parampatterns, strutils, trees, - nimfix / pretty, lineinfos + linter, lineinfos when defined(booting) or defined(nimsuggest): import docgen diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim index 9f93150802..3ba1120b29 100644 --- a/nimsuggest/nimsuggest.nim +++ b/nimsuggest/nimsuggest.nim @@ -493,7 +493,6 @@ proc mainCommand(graph: ModuleGraph) = registerPass graph, verbosePass registerPass graph, semPass conf.cmd = cmdIdeTools - incl conf.globalOptions, optCaasEnabled wantMainModule(conf) if not fileExists(conf.projectFull): From 51fdb071cb274530ce865cae2b1e011788ce5c8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arne=20D=C3=B6ring?= Date: Wed, 13 Jun 2018 12:18:21 +0200 Subject: [PATCH 088/158] fix #5930 --- tests/macros/tmacrostmt.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/macros/tmacrostmt.nim b/tests/macros/tmacrostmt.nim index 6f648958f7..849a32ea3c 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() From dd06c468656e2c218c373be58702074def9c7ae6 Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Wed, 13 Jun 2018 15:39:45 +0300 Subject: [PATCH 089/158] Fixed uncaught exception in vm gen --- compiler/vmgen.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 59fed3cf76..d2243376c1 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -804,7 +804,7 @@ 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: TRegister, opcode: TOpcode) = +proc genVoidABC(c: PCtx, n: PNode, dest: TDest, opcode: TOpcode) = unused(c, n, dest) var tmp1 = c.genx(n[1]) From ed4f1f4c4368238efc1686189e04e87b419e276a Mon Sep 17 00:00:00 2001 From: Charles Blake Date: Wed, 13 Jun 2018 10:36:47 -0400 Subject: [PATCH 090/158] hotfix 3221ac094398492e09ea618638204793b0990eca broke gc:regions/aka gc:stack by underallocating for sequences of any type larger than 1 byte. This does the necessary multiply to restore basic functionality. --- lib/system/mmdisp.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index 2c9b1e5021..b33ca93f26 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): From e80be6173d854ecb50e19ba1459529432fc5efbe Mon Sep 17 00:00:00 2001 From: Vindaar Date: Wed, 13 Jun 2018 19:32:12 +0200 Subject: [PATCH 091/158] Add parse bin int, fixes #8018 (#8020) * clarify `parseHexInt`, `parseOctInt` docstring and exception msgs * add `parseBinInt` based on `parseutil.parseBin` implementation Adds a `parseBinInt`, which parses a binary integer string and returns it as an integer. This is based on the implementation of `parseutil.parseBin`, removing the unnecessary parts. * add tests for all `parse(Hex|Oct|Bin)Int` procs * replace `parse*Int` proc impls by call to parseutil procs Replaces the `parse(Hex|Oct|Bin)Int` procedure implementation by calls to the `parseutil` procs, which receive a mutable argument. Has the main advantage that the empty string as well as a "prefix only" string, e.g. "0x" counts as an invalid integer. Also moves the `parseOctInt` proc further up in the file so that all `parse` procs are below one another. * replace `var L` by `let L` in `parse` procs There's no reason for the usage of `var` here. * add `maxLen` optional arg for `parseutil.parse(Oct|Bin)` Plus small change to test cases. * update changelog about `parse*Int` procs * fix `rejectParse` template in `tstrutils` * make sure only `s.len` chars are parsed, if `maxLen+start` > s.len Fixes a previous bug in `parseHex` (and now affected `parseOct` and `parseBin`), which allowed to set `start + maxLen` to be larger than the strings length. This resulted in an out of bounds access. * move `parse*Int` proc change to breaking changes, add double ` --- changelog.md | 7 +++- lib/pure/parseutils.nim | 38 ++++++++++++++------- lib/pure/strutils.nim | 70 +++++++++++++++++---------------------- tests/stdlib/tstrutil.nim | 49 ++++++++++++++++++++++++++- 4 files changed, 111 insertions(+), 53 deletions(-) diff --git a/changelog.md b/changelog.md index 959990900d..8919cf7021 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,8 @@ - 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`` ### Library changes @@ -100,7 +106,6 @@ - Added the parameter ``val`` for the ``CritBitTree[T].incl`` proc. - The proc ``tgamma`` was renamed to ``gamma``. ``tgamma`` is deprecated. - ### Language additions - Dot calls combined with explicit generic instantiations can now be written diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim index d54f1454b1..e633d8cf7c 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/strutils.nim b/lib/pure/strutils.nim index bea0a02431..5de013c26c 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -844,7 +844,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 +853,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 +862,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 +871,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 +880,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 +1625,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/tests/stdlib/tstrutil.nim b/tests/stdlib/tstrutil.nim index 6f78a91ac6..4d4081d390 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") From d1e03c20d22916a7f2165e12e1fafa94eaf8c67e Mon Sep 17 00:00:00 2001 From: Federico Ceratto Date: Wed, 13 Jun 2018 23:11:29 +0100 Subject: [PATCH 092/158] Add hint on JSON serialization --- lib/pure/marshal.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pure/marshal.nim b/lib/pure/marshal.nim index b88c9dd858..9b6482c778 100644 --- a/lib/pure/marshal.nim +++ b/lib/pure/marshal.nim @@ -270,6 +270,7 @@ 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) From 127556ad63a6486954bc8a020cffeb91490dee53 Mon Sep 17 00:00:00 2001 From: Federico Ceratto Date: Wed, 13 Jun 2018 23:22:07 +0100 Subject: [PATCH 093/158] Update marshal.nim --- lib/pure/marshal.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pure/marshal.nim b/lib/pure/marshal.nim index 9b6482c778..b90d2899c1 100644 --- a/lib/pure/marshal.nim +++ b/lib/pure/marshal.nim @@ -270,6 +270,7 @@ 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 From 5fc5e3719457846964bff8a77078598f01b56974 Mon Sep 17 00:00:00 2001 From: Koki Fushimi Date: Thu, 14 Jun 2018 13:17:17 +0900 Subject: [PATCH 094/158] Use one same type for two parameters --- lib/pure/math.nim | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 95a0a345c5..52ed0a0a59 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -151,10 +151,6 @@ else: # JS proc log*[T: SomeFloat](x, base: T): T = ## Computes the logarithm ``base`` of ``x`` ln(x) / ln(base) -proc log*(x: float64, base: float32): float64 = - ln(x) / ln(base) -proc log*(x: float32, base: float64): float64 = - ln(x) / ln(base) when not defined(JS): # C proc log10*(x: float32): float32 {.importc: "log10f", header: "".} @@ -676,4 +672,4 @@ when isMainModule: doAssert floorMod(-8.5, 3.0) ==~ 0.5 block: # log - doAssert log(3.0, 4.0) == log(4.0) / log(3.0) + doAssert log(4.0, 3.0) == log(4.0) / log(3.0) From 5332da2e7cdf7a52e607f7b90acd67dc16fe2754 Mon Sep 17 00:00:00 2001 From: Koki Fushimi Date: Thu, 14 Jun 2018 13:37:37 +0900 Subject: [PATCH 095/158] Fix a test --- lib/pure/math.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 52ed0a0a59..60750138f3 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -672,4 +672,4 @@ when isMainModule: doAssert floorMod(-8.5, 3.0) ==~ 0.5 block: # log - doAssert log(4.0, 3.0) == log(4.0) / log(3.0) + doAssert log(4.0, 3.0) == ln(4.0) / ln(3.0) From 466af35d4d7ad645e1de5a38beeadbbe5ddd7998 Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Thu, 14 Jun 2018 13:58:44 +0300 Subject: [PATCH 096/158] Fixed stacktrace/linetrace proc pragmas when appended by macro pragma --- compiler/pragmas.nim | 129 +++++++++++++++++++++++------------------- compiler/semstmts.nim | 5 +- 2 files changed, 74 insertions(+), 60 deletions(-) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index bfb8e78eb1..c78a3519cf 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -223,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) @@ -313,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: @@ -373,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) @@ -964,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) @@ -1000,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: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 439ef8fcaa..dc42f9a215 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1486,10 +1486,11 @@ 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 - let oldOptions = c.config.options #c.currentScope = s.scope pushOwner(c, s) openScope(c) @@ -1569,8 +1570,6 @@ 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 - c.config.options = oldOptions if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n) if s.name.s[0] in {'.', '('}: From 894f21eb737942bc54071611f90b93cb9794793f Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 14 Jun 2018 15:01:30 +0200 Subject: [PATCH 097/158] RST hotfix: don't crash in rstnodeToRefname --- lib/packages/docutils/rst.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index adac167774..7ee071c79f 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] From 1578d222e278efad6751f648b55e06927cfc29fa Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 14 Jun 2018 16:12:02 +0200 Subject: [PATCH 098/158] implements pkg/ prefix to enforce searching for the module ignore the stdlib paths; refs #7250 --- compiler/options.nim | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/compiler/options.nim b/compiler/options.nim index 2e66a722c2..1d6ddb09f7 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -516,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) @@ -541,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: @@ -556,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 = From 21a17f3911c0e23381f41cbca6200a09f128bbbb Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 14 Jun 2018 16:40:06 +0200 Subject: [PATCH 099/158] document the 'pkg' and 'std' pseudo directories; closes #7250 --- doc/manual.rst | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index d0a778bddb..8e548afdc1 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -6251,6 +6251,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 +6330,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 ----------- From bf5d619a52da04c857a6f7fb3d68afc12182bc22 Mon Sep 17 00:00:00 2001 From: Dmitry Atamanov Date: Thu, 14 Jun 2018 19:34:26 +0300 Subject: [PATCH 100/158] Add MemMapFileStream. Fixes in memFiles. (#7944) * Add MemMapFileStream * Added tests * Fixed bug in memfiles (zero index for string) * Added flush to changelog * Attempt to fix Win's nuances * Fix attempt to fix * Continue... * And again... * Reworked tests (all for win on Win) * Fixes in flush (Win) * Replace fn vars to consts * Added the attempts parameter to the flush * Replace while to for * Move to memfiles * Use Natural instead of uint * Better error messages for append mode. Handle specific cases. --- changelog.md | 2 + lib/pure/memfiles.nim | 118 +++++++++++++++++++++++++++++--- lib/windows/winlean.nim | 42 ++++++++---- tests/stdlib/tmemfiles2.nim | 3 +- tests/stdlib/tmemmapstreams.nim | 53 ++++++++++++++ 5 files changed, 195 insertions(+), 23 deletions(-) create mode 100644 tests/stdlib/tmemmapstreams.nim diff --git a/changelog.md b/changelog.md index 8919cf7021..4067cb6939 100644 --- a/changelog.md +++ b/changelog.md @@ -78,6 +78,8 @@ - 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 diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index bf6795b0c2..c7b8ebbd81 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 + ## `_ 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/windows/winlean.nim b/lib/windows/winlean.nim index 7e22f98c7c..3263f1d370 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/tests/stdlib/tmemfiles2.nim b/tests/stdlib/tmemfiles2.nim index 7ea94cffc5..d6cfa533a4 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 0000000000..243574f1a3 --- /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) From 951157a4e9937eea895a95df0bd46c03b330f7c2 Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Fri, 15 Jun 2018 12:54:59 +0300 Subject: [PATCH 101/158] Fixed proto options --- compiler/semstmts.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index dc42f9a215..292238dc9e 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1559,6 +1559,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] From 78cbf6734a2bb0514703232e623fac344f4b7b74 Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Fri, 15 Jun 2018 19:46:17 +0300 Subject: [PATCH 102/158] Added more tests to toverflw --- tests/overflw/toverflw.nim | 75 +++++++++++++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 6 deletions(-) diff --git a/tests/overflw/toverflw.nim b/tests/overflw/toverflw.nim index 771a43303b..20bc56a535 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" From a1bd4a6cbd820cac7b3534e03c27bcd960775e76 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 16 Jun 2018 01:46:39 +0200 Subject: [PATCH 103/158] nimpretty: first version that seems useful for others to test it --- compiler/layouter.nim | 201 ++++++++++++++++++++++++++++++++++++++++ compiler/lexer.nim | 12 ++- compiler/parser.nim | 16 ++++ compiler/renderer.nim | 5 +- tools/nimpretty.nim | 9 +- tools/nimpretty.nim.cfg | 2 + 6 files changed, 239 insertions(+), 6 deletions(-) create mode 100644 compiler/layouter.nim create mode 100644 tools/nimpretty.nim.cfg diff --git a/compiler/layouter.nim b/compiler/layouter.nim new file mode 100644 index 0000000000..8c6237294c --- /dev/null +++ b/compiler/layouter.nim @@ -0,0 +1,201 @@ +# +# +# 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. +## TODO +## - Make indentations consistent. +## - Align 'if' and 'case' expressions properly. + +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: 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*(em: var Emitter, config: ConfigRef, fileIdx: FileIndex) = + 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) = + em.f.llStreamWrite em.content + llStreamClose(em.f) + +proc countNewlines(s: string): int = + result = 0 + for i in 0..= 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 = 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: + 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 diff --git a/compiler/lexer.nim b/compiler/lexer.nim index f2af3f51c6..13460f7c1f 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -949,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 @@ -957,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 @@ -996,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 @@ -1016,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 @@ -1055,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 diff --git a/compiler/parser.nim b/compiler/parser.nim index aedee85387..c0465b05ea 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -29,6 +29,9 @@ when isMainModule: import llstream, lexer, idents, strutils, ast, astalgo, msgs, options, lineinfos +when defined(nimpretty2): + import layouter + type TParser* = object # A TParser object represents a file that # is being parsed @@ -41,6 +44,8 @@ type inPragma*: int # Pragma level inSemiStmtList*: int emptyNode: PNode + when defined(nimpretty2): + em: Emitter SymbolMode = enum smNormal, smAllowNil, smAfterDot @@ -83,6 +88,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; @@ -91,6 +101,8 @@ 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, config, fileIdx) getTok(p) # read the first token p.firstTok = true p.strongSpaces = strongSpaces @@ -104,6 +116,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. @@ -907,6 +921,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) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 1f5ab7a13a..ba87838db3 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -1453,11 +1453,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/tools/nimpretty.nim b/tools/nimpretty.nim index 386eddfde3..89e6ef9052 100644 --- a/tools/nimpretty.nim +++ b/tools/nimpretty.nim @@ -42,9 +42,12 @@ proc writeVersion() = proc prettyPrint(infile: string) = let conf = newConfigRef() let fileIdx = fileInfoIdx(conf, infile) - let tree = parseFile(fileIdx, newIdentCache(), conf) - let outfile = changeFileExt(infile, ".pretty.nim") - renderModule(tree, infile, outfile, {}, fileIdx) + when defined(nimpretty2): + discard parseFile(fileIdx, newIdentCache(), conf) + else: + let tree = parseFile(fileIdx, newIdentCache(), conf) + let outfile = changeFileExt(infile, ".pretty.nim") + renderModule(tree, infile, outfile, {}, fileIdx, conf) proc main = var infile: string diff --git a/tools/nimpretty.nim.cfg b/tools/nimpretty.nim.cfg new file mode 100644 index 0000000000..5fafa6d2ae --- /dev/null +++ b/tools/nimpretty.nim.cfg @@ -0,0 +1,2 @@ +--define: nimpretty +--define: nimpretty2 From 97398edc05af108968b861ad39d4c7f9d7ba37ad Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 16 Jun 2018 01:58:52 +0200 Subject: [PATCH 104/158] nimpretty: render and/or/notin/in properly --- compiler/layouter.nim | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/compiler/layouter.nim b/compiler/layouter.nim index 8c6237294c..90e9d6fd79 100644 --- a/compiler/layouter.nim +++ b/compiler/layouter.nim @@ -9,6 +9,7 @@ ## Layouter for nimpretty. Still primitive but useful. ## TODO +## - Fix 'echo ()' vs 'echo()' difference! ## - Make indentations consistent. ## - Align 'if' and 'case' expressions properly. @@ -132,13 +133,19 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = case tok.tokType of tokKeywordLow..tokKeywordHigh: - if endsInAlpha(em): wr(" ") + if endsInAlpha(em): + wr(" ") + elif not em.inquote and not endsInWhite(em): + wr(" ") + wr(TokTypeToStr[tok.tokType]) case tok.tokType of tkAnd: rememberSplit(splitAnd) of tkOr: rememberSplit(splitOr) - of tkIn: rememberSplit(splitIn) + of tkIn, tkNotin: + rememberSplit(splitIn) + wr(" ") else: discard of tkColon: From 7819e63f7704424625c55d128e874b4ba5736f65 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 16 Jun 2018 09:53:36 +0200 Subject: [PATCH 105/158] nimpretty: more features --- compiler/layouter.nim | 38 +++++++++++++++++++++++++++----------- compiler/parser.nim | 26 ++++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 13 deletions(-) diff --git a/compiler/layouter.nim b/compiler/layouter.nim index 90e9d6fd79..6a9a12d821 100644 --- a/compiler/layouter.nim +++ b/compiler/layouter.nim @@ -9,7 +9,6 @@ ## Layouter for nimpretty. Still primitive but useful. ## TODO -## - Fix 'echo ()' vs 'echo()' difference! ## - Make indentations consistent. ## - Align 'if' and 'case' expressions properly. @@ -31,6 +30,7 @@ type lastTok: TTokType inquote: bool col, lastLineNumber, lineSpan, indentLevel: int + doIndentMore*: int content: string fixedUntil: int # marks where we must not go in the content altSplitPos: array[SplitKind, int] # alternative split positions @@ -77,6 +77,8 @@ template rememberSplit(kind) = if goodCol(em.col): em.altSplitPos[kind] = em.content.len +template moreIndent(em): int = (if em.doIndentMore > 0: 4 else: 2) + proc softLinebreak(em: var Emitter, lit: string) = # XXX Use an algorithm that is outlined here: # https://llvm.org/devmtg/2013-04/jasper-slides.pdf @@ -85,12 +87,12 @@ proc softLinebreak(em: var Emitter, lit: string) = if em.lastTok in splitters: wr("\L") em.col = 0 - for i in 1..em.indentLevel+2: wr(" ") + 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: - let ws = "\L" & repeat(' ',em.indentLevel+2) + let ws = "\L" & repeat(' ',em.indentLevel+moreIndent(em)) em.col = em.content.len - a em.content.insert(ws, a) break @@ -155,15 +157,19 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = wr(TokTypeToStr[tok.tokType]) wr(" ") rememberSplit(splitComma) - of tkParLe, tkParRi, tkBracketLe, - tkBracketRi, tkCurlyLe, tkCurlyRi, - tkBracketDotLe, tkBracketDotRi, - tkCurlyDotLe, tkCurlyDotRi, - tkParDotLe, tkParDotRi, - tkColonColon, tkDot, tkBracketLeColon: + 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]) - if tok.tokType in splitters: - rememberSplit(splitParLe) of tkEquals: if not em.endsInWhite: wr(" ") wr(TokTypeToStr[tok.tokType]) @@ -206,3 +212,13 @@ proc starWasExportMarker*(em: var Emitter) = setLen(em.content, em.content.len-3) em.content.add("*") dec em.col, 2 + +proc commaWasSemicolon*(em: var Emitter) = + if 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/parser.nim b/compiler/parser.nim index c0465b05ea..9a9fed0cfd 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -408,6 +408,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 @@ -417,6 +419,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}) @@ -837,7 +841,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 @@ -912,8 +920,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): + curlyRiWasPragma(p.em) + getTok(p) + else: + parMessage(p, "expected '.}'") dec p.inPragma proc identVis(p: var TParser; allowDot=false): PNode = @@ -1000,6 +1012,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) @@ -1034,6 +1048,8 @@ proc parseParamList(p: var TParser, retColon = true): PNode = var a: PNode result = newNodeP(nkFormalParams, p) 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) @@ -1053,6 +1069,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) @@ -1066,6 +1084,8 @@ proc parseParamList(p: var TParser, retColon = true): PNode = elif not retColon and not hasParle: # Mark as "not there" in order to mark for deprecation in the semantic pass: 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)): @@ -1668,6 +1688,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) From 7ab1aafc6bbb94da8becc1a42bd1bf839df90746 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sat, 9 Jun 2018 11:52:03 +0300 Subject: [PATCH 106/158] stdlib work --- lib/pure/collections/deques.nim | 57 +++++++++++++++++++++++++++++---- lib/pure/streams.nim | 16 +++++++-- lib/system/nimscript.nim | 2 +- 3 files changed, 66 insertions(+), 9 deletions(-) diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim index 328308a9b3..409240b377 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 `_ 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/streams.nim b/lib/pure/streams.nim index a0bba05a4f..1ab73faea3 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/system/nimscript.nim b/lib/system/nimscript.nim index 7671e5962d..5bf69dd94b 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 From 8633b1b3097e1627f16a52fc648f0e14c647a688 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Fri, 20 Apr 2018 12:54:06 +0300 Subject: [PATCH 107/158] Starting test recording the current state of the parser In the next commit, I'll introduce changes to the parser bringing consistent handling of all type modifiers (ref, ptr, var, static and type). The goal of this commit is to record precisely what is going to be changed (i.e. by allowing you to look at the diff). To preserve the diff, please don't squash upon merging. --- tests/parser/ttypemodifiers.nim | 481 ++++++++++++++++++++++++++++++++ 1 file changed, 481 insertions(+) create mode 100644 tests/parser/ttypemodifiers.nim diff --git a/tests/parser/ttypemodifiers.nim b/tests/parser/ttypemodifiers.nim new file mode 100644 index 0000000000..db7ab903f3 --- /dev/null +++ b/tests/parser/ttypemodifiers.nim @@ -0,0 +1,481 @@ +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 "GenericStatic" + Empty + StaticTy + Ident "int" + TypeDef + Ident "PrefixStatic" + Empty + StaticExpr + Ident "int" + TypeDef + Ident "StaticTupleCl" + Empty + StaticExpr + TupleClassTy + TypeDef + Ident "StaticTuple" + Empty + StaticExpr + 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 "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 "TypeOfTuple1" + Empty + Call + Ident "type" + Ident "a" + TypeDef + Ident "TypeOfTuple2" + Empty + Call + Ident "type" + Ident "a" + Ident "b" + 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 "genStatic" + StaticTy + Ident "int" + Empty + IdentDefs + Ident "staticInt" + StaticExpr + Ident "int" + Empty + IdentDefs + Ident "staticVal1" + StaticExpr + IntLit 10 + Empty + IdentDefs + Ident "staticVal2" + StaticExpr + Par + StrLit "str" + Empty + IdentDefs + Ident "staticVal3" + StaticExpr + StrLit "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" + StaticExpr + IntLit 10 + Asgn + Ident "staticA" + StaticExpr + Par + Ident "a" + Asgn + Ident "staticAspace" + StaticExpr + Par + Ident "a" + Asgn + Ident "staticAtuple" + StaticExpr + TupleConstr + Ident "a" + Asgn + Ident "staticTuple" + StaticExpr + Par + Ident "a" + Ident "b" + Asgn + Ident "staticTypeTuple" + StaticExpr + Par + Ident "int" + Ident "string" + Asgn + Ident "staticCall" + StaticExpr + Call + Ident "foo" + IntLit 1 + Asgn + Ident "staticStrCall" + StaticExpr + CallStrLit + Ident "foo" + RStrLit "x" + Asgn + Ident "staticChainCall" + StaticExpr + 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 # 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 # Error: invalid indentation + TypeInstance = type Foo[ref] + bareTypeDesc = typedesc + TypeOfVar = type(a) + # TypeOfVarAlt= type (a) # Error: invalid indentation + TypeOfTuple1 = type(a,) + TypeOfTuple2 = type(a,b) + # TypeOfTuple1A = type (a,) # Error: invalid indentation + # TypeOfTuple2A = type (a,b) # Error: invalid indentation + # TypeTuple = type (int, string) # 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, # Error: ')' expected + # bareStatic : static, # Error: expression expected, but found ',' + genStatic : static[int], + staticInt : static int, + staticVal1 : static 10, + staticVal2 : static("str"), + staticVal3 : static "str", + # staticVal4 : static"str", # 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) + staticAtuple = static (a,) + staticTuple = static (a,b) + staticTypeTuple = static (int,string) + 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 + From fb27357b6217f53dc882e83fc128da578ad51764 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Fri, 20 Apr 2018 11:44:13 +0300 Subject: [PATCH 108/158] A minimal patch enabling the new typedesc and static types syntax --- compiler/ast.nim | 4 +- compiler/condsyms.nim | 1 + compiler/parser.nim | 63 +++++++------ compiler/sem.nim | 3 + compiler/semexprs.nim | 39 ++++++-- compiler/semtypes.nim | 38 +++++--- compiler/sigmatch.nim | 14 +-- lib/system.nim | 22 ++++- tests/lexer/tmissingnl.nim | 2 +- tests/parser/ttypemodifiers.nim | 152 +++++++++++++++++++++----------- 10 files changed, 228 insertions(+), 110 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 40c1b064d2..d266e20b09 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -571,8 +571,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, diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 56587a4faf..8b2d36722c 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") diff --git a/compiler/parser.nim b/compiler/parser.nim index c0465b05ea..33ee8c9e6d 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -50,6 +50,9 @@ type 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 @@ -81,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) = @@ -332,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 @@ -340,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) @@ -525,9 +533,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 @@ -625,7 +630,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) @@ -741,7 +746,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? @@ -758,7 +768,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 @@ -774,9 +791,15 @@ 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: - if p.inPragma == 0 and (isUnary(p) or p.tok.tokType notin {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 mode == pmTypeDef or + (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 let a = result @@ -800,9 +823,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 @@ -1107,9 +1127,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 @@ -1170,7 +1190,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) @@ -1183,7 +1202,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 @@ -1213,14 +1232,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) @@ -1235,7 +1246,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 diff --git a/compiler/sem.nim b/compiler/sem.nim index 3b16e0938b..3c3ffa1948 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -48,6 +48,9 @@ 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 isArrayConstr(n: PNode): bool {.inline.} = result = n.kind == nkBracket and diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 9d7c493a7b..4b45ccf874 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -191,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 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}: @@ -632,7 +650,7 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = # 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.graph, a, c.p.owner) if result.isNil: @@ -1034,7 +1052,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) @@ -1261,8 +1279,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, 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: @@ -2426,8 +2454,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/semtypes.nim b/compiler/semtypes.nim index 3e62652a71..597522f6fe 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) @@ -363,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 @@ -388,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) @@ -856,9 +862,9 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, 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 type + return if tfUnresolved in paramType.flags: return # already lifted let base = paramType.base.maybeLift if base.isMetaType and procKind == skMacro: @@ -879,7 +885,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, 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 tySequence, tySet, tyArray, tyOpenArray, tyVar, tyLent, tyPtr, tyRef, tyProc: @@ -1349,6 +1355,12 @@ proc symFromExpectedTypeNode(c: PContext, n: PNode): PSym = 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 @@ -1456,7 +1468,8 @@ 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)) + of mStaticTy: result = semStaticType(c, n[1], prev) of mExpr: result = semTypeNode(c, n.sons[0], nil) if result != nil: @@ -1545,11 +1558,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) @@ -1645,9 +1654,12 @@ proc processMagicType(c: PContext, m: PSym) = of mStmt: setMagicType(c.config, m, tyStmt, 0) if m.name.s == "stmt": m.typ.flags.incl tfOldSchoolExprStmt - of mTypeDesc: + of mTypeDesc, mType: setMagicType(c.config, m, tyTypeDesc, 0) rawAddSon(m.typ, newTypeS(tyNone, c)) + of mStatic: + setMagicType(m, tyStatic, 0) + rawAddSon(m.typ, newTypeS(tyNone, c)) of mVoidType: setMagicType(c.config, m, tyVoid, 0) of mArray: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 9a3c752618..5a556732b8 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1666,13 +1666,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 diff --git a/lib/system.nim b/lib/system.nim index fb02fde23e..15f952feb0 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/tests/lexer/tmissingnl.nim b/tests/lexer/tmissingnl.nim index 33b7debf10..095d9570e5 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/parser/ttypemodifiers.nim b/tests/parser/ttypemodifiers.nim index db7ab903f3..2c322b44ba 100644 --- a/tests/parser/ttypemodifiers.nim +++ b/tests/parser/ttypemodifiers.nim @@ -53,25 +53,33 @@ StmtList Par Ident "a" Ident "b" + TypeDef + Ident "BareStatic" + Empty + Ident "static" TypeDef Ident "GenericStatic" Empty - StaticTy + BracketExpr + Ident "static" Ident "int" TypeDef Ident "PrefixStatic" Empty - StaticExpr + Command + Ident "static" Ident "int" TypeDef Ident "StaticTupleCl" Empty - StaticExpr + Command + Ident "static" TupleClassTy TypeDef Ident "StaticTuple" Empty - StaticExpr + Command + Ident "static" Par Ident "int" Ident "string" @@ -91,6 +99,12 @@ StmtList BracketExpr Ident "type" TupleClassTy + TypeDef + Ident "TypeTupleCl" + Empty + Command + Ident "type" + TupleClassTy TypeDef Ident "TypeInstance" Empty @@ -109,6 +123,13 @@ StmtList Call Ident "type" Ident "a" + TypeDef + Ident "TypeOfVarAlt" + Empty + Command + Ident "type" + Par + Ident "a" TypeDef Ident "TypeOfTuple1" Empty @@ -122,6 +143,29 @@ StmtList 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 @@ -172,32 +216,52 @@ StmtList Ident "foo" Ident "type" Empty + IdentDefs + Ident "typeTupleCl" + Command + Ident "type" + TupleClassTy + Empty + IdentDefs + Ident "bareStatic" + Ident "static" + Empty IdentDefs Ident "genStatic" - StaticTy + BracketExpr + Ident "static" Ident "int" Empty IdentDefs Ident "staticInt" - StaticExpr + Command + Ident "static" Ident "int" Empty IdentDefs Ident "staticVal1" - StaticExpr + Command + Ident "static" IntLit 10 Empty IdentDefs Ident "staticVal2" - StaticExpr - Par - StrLit "str" + Call + Ident "static" + StrLit "str" Empty IdentDefs Ident "staticVal3" - StaticExpr + Command + Ident "static" StrLit "str" Empty + IdentDefs + Ident "staticVal4" + CallStrLit + Ident "static" + RStrLit "str" + Empty IdentDefs Ident "staticDotVal" DotExpr @@ -285,50 +349,32 @@ StmtList StmtList Asgn Ident "staticTen" - StaticExpr + Command + Ident "static" IntLit 10 Asgn Ident "staticA" - StaticExpr - Par - Ident "a" - Asgn - Ident "staticAspace" - StaticExpr - Par - Ident "a" - Asgn - Ident "staticAtuple" - StaticExpr - TupleConstr - Ident "a" - Asgn - Ident "staticTuple" - StaticExpr - Par - Ident "a" - Ident "b" - Asgn - Ident "staticTypeTuple" - StaticExpr - Par - Ident "int" - Ident "string" + Call + Ident "static" + Ident "a" Asgn Ident "staticCall" - StaticExpr + Command + Ident "static" Call Ident "foo" IntLit 1 Asgn Ident "staticStrCall" - StaticExpr + Command + Ident "static" CallStrLit Ident "foo" RStrLit "x" Asgn Ident "staticChainCall" - StaticExpr + Command + Ident "static" Command Ident "foo" Ident "bar" @@ -399,7 +445,7 @@ dumpTree: RefTupleCl = ref tuple RefTupleType = ref (int, string) RefTupleVars = ref (a, b) - # BareStatic = static # Error: invalid indentation + BareStatic = static # Used to be Error: invalid indentation GenericStatic = static[int] PrefixStatic = static int StaticTupleCl = static tuple @@ -407,16 +453,16 @@ dumpTree: BareType = type GenericType = type[float] TypeTupleGen = type[tuple] - # TypeTupleCl = type tuple # Error: invalid indentation + TypeTupleCl = type tuple # Used to be Error: invalid indentation TypeInstance = type Foo[ref] bareTypeDesc = typedesc TypeOfVar = type(a) - # TypeOfVarAlt= type (a) # Error: invalid indentation + TypeOfVarAlt = type (a) # Used to be Error: invalid indentation TypeOfTuple1 = type(a,) TypeOfTuple2 = type(a,b) - # TypeOfTuple1A = type (a,) # Error: invalid indentation - # TypeOfTuple2A = type (a,b) # Error: invalid indentation - # TypeTuple = type (int, string) # Error: invalid indentation + 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 @@ -427,14 +473,14 @@ dumpTree: typeIntAlt : type(int), typeOfVar : type(a), typeDotType : foo.type, - # typeTupleCl : type tuple, # Error: ')' expected - # bareStatic : static, # Error: expression expected, but found ',' + 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", # Error: expression expected, but found 'str' + staticVal4 : static"str", # Used to be Error: expression expected, but found 'str' staticDotVal : 10.static, bareRef : ref, refTuple1 : ref (int), @@ -451,10 +497,10 @@ dumpTree: ): type = staticTen = static 10 staticA = static(a) - staticAspace = static (a) - staticAtuple = static (a,) - staticTuple = static (a,b) - staticTypeTuple = static (int,string) + # 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 From 509d6e923284f1f02c5dbc64e43aee9df1a012d3 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sun, 22 Apr 2018 17:26:10 +0300 Subject: [PATCH 109/158] Bugfix: aliases to generic types were not considered implicit generic parameters --- compiler/semtypes.nim | 3 +++ tests/statictypes/tstatictypes.nim | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 tests/statictypes/tstatictypes.nim diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 597522f6fe..19bb53209a 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -887,6 +887,9 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, # disable the bindOnce behavior for the type class result = liftingWalk(paramType.base, true) + of tyAlias: + result = liftingWalk(paramType.base) + of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyLent, tyPtr, tyRef, tyProc: # XXX: this is a bit strange, but proc(s: seq) diff --git a/tests/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim new file mode 100644 index 0000000000..a646b61f7f --- /dev/null +++ b/tests/statictypes/tstatictypes.nim @@ -0,0 +1,17 @@ +discard """ +nimout: ''' +staticAlialProc instantiated with 4 +staticAlialProc instantiated with 6 +''' +""" + +type + StaticTypeAlias = static[int] + +proc staticAliasProc(s: StaticTypeAlias) = + static: echo "staticAlialProc instantiated with ", s + 1 + +staticAliasProc 1+2 +staticAliasProc 3 +staticAliasProc 5 + From ab9969ed3be2d57fdeda170cc9960be7ba628149 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sun, 22 Apr 2018 18:11:22 +0300 Subject: [PATCH 110/158] Bugfix: the size of an array may be a static tuple element --- compiler/semtypes.nim | 3 ++- tests/statictypes/tstatictypes.nim | 40 ++++++++++++++++++++++++------ 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 19bb53209a..6b27c80329 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -301,7 +301,8 @@ 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 + hasGenericArguments(e): if not isOrdinalType(e.typ): localError(c.config, n[1].info, errOrdinalTypeExpected) # This is an int returning call, depending on an diff --git a/tests/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim index a646b61f7f..5234866fa6 100644 --- a/tests/statictypes/tstatictypes.nim +++ b/tests/statictypes/tstatictypes.nim @@ -5,13 +5,39 @@ staticAlialProc instantiated with 6 ''' """ -type - StaticTypeAlias = static[int] +import macros -proc staticAliasProc(s: StaticTypeAlias) = - static: echo "staticAlialProc instantiated with ", s + 1 +proc plus(a, b: int): int = a + b -staticAliasProc 1+2 -staticAliasProc 3 -staticAliasProc 5 +when true: + type + StaticTypeAlias = static[int] + + proc staticAliasProc(s: StaticTypeAlias) = + static: echo "staticAlialProc instantiated with ", s + 1 + echo s + + staticAliasProc 1+2 + staticAliasProc 3 + staticAliasProc 5 + +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 From a49b06a52a3c24258e9eb04593a2f83ae057755f Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Mon, 23 Apr 2018 17:23:14 +0300 Subject: [PATCH 111/158] Implement the `is` operator for the new static and typedesc type classes This also makes the first baby steps towards a sound treatment of higher-order kinds (type type int). Adds test cases showcasing the new features. * Also fixes breakage after the rebase --- compiler/parser.nim | 3 +- compiler/semdata.nim | 3 +- compiler/semexprs.nim | 81 +++++++++++++++++++++------- compiler/semtypes.nim | 7 ++- tests/metatype/ttypedesc1.nim | 34 +++++++++--- tests/metatype/ttypedesc3.nim | 6 +-- tests/metatype/ttypeselectors.nim | 6 +-- tests/parser/ttypeclasses.nim | 39 +++++++++++--- tests/statictypes/tstatictypes.nim | 84 ++++++++++++++++++++++++++---- tests/types/tisopr.nim | 2 +- 10 files changed, 210 insertions(+), 55 deletions(-) diff --git a/compiler/parser.nim b/compiler/parser.nim index 33ee8c9e6d..5c99363c9b 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -798,8 +798,7 @@ proc primarySuffix(p: var TParser, r: PNode, # `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 mode == pmTypeDef or - (p.inPragma == 0 and (isUnary(p) or p.tok.tokType notin {tkOpr, tkDotDot})): + 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 let a = result diff --git a/compiler/semdata.nim b/compiler/semdata.nim index c858b68392..aa0cb6e8ec 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -288,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 4b45ccf874..a072deb7da 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -194,7 +194,7 @@ proc semConv(c: PContext, n: PNode): PNode = var targetType = semTypeNode(c, n.sons[0], nil) if targetType.kind == tyTypeDesc: - internalAssert targetType.len > 0 + internalAssert c.config, targetType.len > 0 if targetType.base.kind == tyNone: return semTypeOf(c, n[1]) else: @@ -316,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} diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 6b27c80329..64783f8903 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1472,8 +1472,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, mTypeTy: result = makeTypeDesc(c, semTypeNode(c, n[1], nil)) - of mStaticTy: result = semStaticType(c, n[1], prev) + 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: diff --git a/tests/metatype/ttypedesc1.nim b/tests/metatype/ttypedesc1.nim index e9eee581f8..837c8eccca 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/ttypedesc3.nim b/tests/metatype/ttypedesc3.nim index 9f19bd6e38..3d1cf2ec95 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 1209fe78f2..2a2455adb5 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/parser/ttypeclasses.nim b/tests/parser/ttypeclasses.nim index 9f487c7a8c..46fd206864 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/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim index 5234866fa6..789bd75886 100644 --- a/tests/statictypes/tstatictypes.nim +++ b/tests/statictypes/tstatictypes.nim @@ -1,25 +1,91 @@ discard """ nimout: ''' -staticAlialProc instantiated with 4 -staticAlialProc instantiated with 6 +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(s: StaticTypeAlias) = - static: echo "staticAlialProc instantiated with ", s + 1 - echo s + 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 - staticAliasProc 1+2 - staticAliasProc 3 - staticAliasProc 5 + 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 @@ -35,7 +101,7 @@ when true: var aw1: ArrayWrapper1[5] var aw2: ArrayWrapper2[5] var aw3: ArrayWrapper3[(10, "str")] - + static: assert aw1.data.high == 5 assert aw2.data.high == 6 diff --git a/tests/types/tisopr.nim b/tests/types/tisopr.nim index 2f9dbf245c..f05f443def 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 = From ea36e0ebbe0033b304a1261de802e2ed9308abf4 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Mon, 23 Apr 2018 22:30:23 +0300 Subject: [PATCH 112/158] document the new `type[T]` and `static[T]` features --- changelog.md | 7 +++ doc/docgen.rst | 6 +- doc/manual.rst | 156 ++++++++++++++++++++++++++++--------------------- doc/tut2.rst | 6 +- 4 files changed, 104 insertions(+), 71 deletions(-) diff --git a/changelog.md b/changelog.md index 4067cb6939..2e75d8ad93 100644 --- a/changelog.md +++ b/changelog.md @@ -116,6 +116,13 @@ - In order to make ``for`` loops and iterators more flexible to use Nim now supports so called "for-loop macros". See the `manual `_ 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 diff --git a/doc/docgen.rst b/doc/docgen.rst index e6693e1537..d196b3a18c 100644 --- a/doc/docgen.rst +++ b/doc/docgen.rst @@ -303,9 +303,9 @@ symbols in the `system module `_. `#len,seq[T] `_ * ``iterator pairs[T](a: seq[T]): tuple[key: int, val: T] {.inline.}`` **=>** `#pairs.i,seq[T] `_ -* ``template newException[](exceptn: typedesc; message: string): expr`` **=>** - `#newException.t,typedesc,string - `_ +* ``template newException[](exceptn: type; message: string): expr`` **=>** + `#newException.t,type,string + `_ Index (idx) file format diff --git a/doc/manual.rst b/doc/manual.rst index 8e548afdc1..88dae89b0a 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -1732,7 +1732,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 +1740,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 +2396,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 +4274,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 +4427,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 +4490,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 +4500,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 +4519,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 +4531,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 +4559,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 +4748,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 +4917,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 +5085,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 +5147,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 +5469,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 +5489,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 +5499,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]``. This means that you can omit the +type param to obtain the type class of all values, known at compile-time +and you can restrict the matched values 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: -`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). +.. code-block:: nim + import math -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: + echo static(fac(5)), " ", static[bool](16.isPowerOfTwo) + +The complier will report any failure to evaluate the expression or a +possible type mismatch error. + +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. + +``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 will be 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 +5576,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 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 @@ -7458,7 +7484,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/tut2.rst b/doc/tut2.rst index 91cb528341..39e3bd89ad 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. From 5bcf8bcb598d2ca0162f50d2b7250a847026b2c9 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Tue, 12 Jun 2018 23:45:18 +0300 Subject: [PATCH 113/158] fixes #7222; fixes #5595; fixes #3747 * late instantiation for the generic procs' default param values * automatic mixin behaviour in concepts Other fixes: * don't render the automatically inserted default params in calls * better rendering of tyFromExpr --- compiler/ast.nim | 1 + compiler/renderer.nim | 6 +- compiler/sem.nim | 2 +- compiler/semcall.nim | 23 +++- compiler/semexprs.nim | 30 +++++ compiler/semgnrc.nim | 31 ++++-- compiler/seminst.nim | 37 +++--- compiler/semtypes.nim | 35 ++++-- compiler/semtypinst.nim | 24 ++-- compiler/sigmatch.nim | 23 +++- compiler/types.nim | 5 +- tests/concepts/libs/trie.nim | 26 +++++ tests/concepts/libs/trie_database.nim | 12 ++ tests/concepts/ttrieconcept.nim | 7 ++ tests/generics/tlateboundgenericparams.nim | 124 +++++++++++++++++++++ 15 files changed, 321 insertions(+), 65 deletions(-) create mode 100644 tests/concepts/libs/trie.nim create mode 100644 tests/concepts/libs/trie_database.nim create mode 100644 tests/concepts/ttrieconcept.nim create mode 100644 tests/generics/tlateboundgenericparams.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index d266e20b09..085a243b3d 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -454,6 +454,7 @@ 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 TNodeFlags* = set[TNodeFlag] TTypeFlag* = enum # keep below 32 for efficiency reasons (now: beyond that) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index ba87838db3..3ce2e157de 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -387,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! diff --git a/compiler/sem.nim b/compiler/sem.nim index 3c3ffa1948..afc794a379 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -51,7 +51,7 @@ 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 diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 5d3df064f4..0de22cfb3d 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -391,7 +391,21 @@ 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 + call[i] = calleeParams[i].sym.ast + +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) @@ -424,8 +438,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; @@ -447,7 +462,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: # @@ -458,7 +473,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/semexprs.nim b/compiler/semexprs.nim index a072deb7da..82bd761367 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -542,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.config, n) + let sym = searchInScopes(c, ident) + if sym != nil: + return isUnresolvedSym(sym) + else: + return false + else: + for i in 0.. 1: resetIdTable(cl.symMap) resetIdTable(cl.localCache) - result.sons[i] = replaceTypeVarsT(cl, result.sons[i]) - propagateToOwner(result, result.sons[i]) + let needsStaticSkipping = result[i].kind == tyFromExpr + result[i] = replaceTypeVarsT(cl, result[i]) + if needsStaticSkipping: + result[i] = result[i].skipTypes({tyStatic}) 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) + let oldParam = originalParams[i].sym + let param = copySym(oldParam) + param.owner = prc + param.typ = result[i] + 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) + param.ast = fitNode(c, param.typ, def, def.info) + param.typ = param.ast.typ - # 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) + result.n[i] = newSymNode(param) + result[i] = param.typ + propagateToOwner(result, result[i]) + addDecl(c, param) resetIdTable(cl.symMap) resetIdTable(cl.localCache) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 64783f8903..85c6e30565 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -244,7 +244,7 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = 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: @@ -301,8 +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 + {nkBracketExpr}) 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 @@ -1006,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: @@ -1022,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 @@ -1030,26 +1028,38 @@ 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 typ == nil: typ = def.typ - elif def != nil: - # and def.typ != nil and def.typ.kind != tyNone: + 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) + 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}: @@ -1065,7 +1075,8 @@ 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)) @@ -1320,7 +1331,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, diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index a24972d040..f02c45e469 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -144,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.. Date: Wed, 13 Jun 2018 01:35:46 +0300 Subject: [PATCH 114/158] Support default type parameters progress on #7516 --- compiler/seminst.nim | 19 ++++++++++++++++--- compiler/semtypes.nim | 4 ++++ tests/metatype/ttypedesc2.nim | 14 ++++++++++++++ 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 752a2b5994..0047fd68e1 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -247,26 +247,39 @@ proc instantiateProcType(c: PContext, pt: TIdTable, if i > 1: resetIdTable(cl.symMap) resetIdTable(cl.localCache) + + # take a note of the original type. If't a free type parameter + # we'll need to keep it unbount 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: + typeToFit = result[i] + internalAssert c.config, originalParams[i].kind == nkSym 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) - param.ast = fitNode(c, param.typ, def, def.info) - param.typ = param.ast.typ + param.ast = fitNode(c, typeToFit, def, def.info) + param.typ = result[i] result.n[i] = newSymNode(param) - result[i] = param.typ propagateToOwner(result, result[i]) addDecl(c, param) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 85c6e30565..2f16151f2e 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1045,6 +1045,10 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, if typ == nil: typ = def.typ + if typ.kind == tyTypeDesc: + # default typedesc values are mapped to 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: diff --git a/tests/metatype/ttypedesc2.nim b/tests/metatype/ttypedesc2.nim index 4b6cfe6bc7..94a7367e78 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" + From 59d19946c034d343ab4f5b8ad57683ff9f80de85 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Wed, 13 Jun 2018 01:46:44 +0300 Subject: [PATCH 115/158] fix some breakage after rebasing --- compiler/semexprs.nim | 2 +- compiler/semtypes.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 82bd761367..238ae01945 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -561,7 +561,7 @@ proc hasUnresolvedArgs(c: PContext, n: PNode): bool = of nkSym: return isUnresolvedSym(n.sym) of nkIdent, nkAccQuoted: - let ident = considerQuotedIdent(c.config, n) + let ident = considerQuotedIdent(c, n) let sym = searchInScopes(c, ident) if sym != nil: return isUnresolvedSym(sym) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 2f16151f2e..f60b57d33d 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1680,7 +1680,7 @@ proc processMagicType(c: PContext, m: PSym) = setMagicType(c.config, m, tyTypeDesc, 0) rawAddSon(m.typ, newTypeS(tyNone, c)) of mStatic: - setMagicType(m, tyStatic, 0) + setMagicType(c.config, m, tyStatic, 0) rawAddSon(m.typ, newTypeS(tyNone, c)) of mVoidType: setMagicType(c.config, m, tyVoid, 0) From e719f211c634d2be7b5ae118db7051a2382a8e3e Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Thu, 14 Jun 2018 12:47:07 +0300 Subject: [PATCH 116/158] fix #6928; fix #7208 --- compiler/astalgo.nim | 9 +++++++++ compiler/seminst.nim | 7 ++++--- compiler/semtypes.nim | 3 +++ tests/generics/tlateboundgenericparams.nim | 21 +++++++++++++++++++++ 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 0afe56bb70..a4a14405e5 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -31,6 +31,15 @@ when declared(echo): proc debug*(conf: ConfigRef; n: PType) {.deprecated.} proc debug*(conf: ConfigRef; n: PNode) {.deprecated.} + template debug*(x: PSym|PType|PNode) {.deprecated.} = + when compiles(c.config): + debug(c.config, x) + else: + error() + + template debug*(x: auto) {.deprecated.} = + echo x + template mdbg*: bool {.dirty.} = when compiles(c.module): c.module.fileIdx == c.config.projectMainIdx diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 0047fd68e1..0ad1fb8729 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -248,8 +248,8 @@ proc instantiateProcType(c: PContext, pt: TIdTable, resetIdTable(cl.symMap) resetIdTable(cl.localCache) - # take a note of the original type. If't a free type parameter - # we'll need to keep it unbount for the `fitNode` operation below... + # 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 @@ -258,7 +258,8 @@ proc instantiateProcType(c: PContext, pt: TIdTable, result[i] = result[i].skipTypes({tyStatic}) # ...otherwise, we use the instantiated type in `fitNode` - if typeToFit.kind != tyTypeDesc or typeToFit.base.kind != tyNone: + if (typeToFit.kind != tyTypeDesc or typeToFit.base.kind != tyNone) and + (typeToFit.kind != tyStatic): typeToFit = result[i] internalAssert c.config, originalParams[i].kind == nkSym diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index f60b57d33d..ff2820ec8f 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1056,6 +1056,9 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, 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") diff --git a/tests/generics/tlateboundgenericparams.nim b/tests/generics/tlateboundgenericparams.nim index 02f99dc551..9f0580fd27 100644 --- a/tests/generics/tlateboundgenericparams.nim +++ b/tests/generics/tlateboundgenericparams.nim @@ -1,3 +1,11 @@ +discard """ + output: "1\n10\n1\n10" + nimout: ''' +bar instantiated with 1 +bar instantiated with 10 +''' +""" + import typetraits type @@ -122,3 +130,16 @@ when true: 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) + From 31651ecd613b7fddea037cb0cc7d433bec6c902d Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sat, 16 Jun 2018 03:51:31 +0300 Subject: [PATCH 117/158] allow referencing other parameters in default parameter values fix #7756 fix #1201 fix #7000 fix #3002 fix #1046 --- compiler/ast.nim | 8 ++- compiler/astalgo.nim | 42 ++++++------ compiler/lowerings.nim | 12 ++++ compiler/sem.nim | 18 +++-- compiler/semcall.nim | 4 +- compiler/seminst.nim | 26 +++++++- compiler/semtypes.nim | 2 + compiler/sigmatch.nim | 3 +- compiler/transf.nim | 46 +++++++++++++ tests/misc/tparamsindefault.nim | 114 ++++++++++++++++++++++++++++++++ 10 files changed, 244 insertions(+), 31 deletions(-) create mode 100644 tests/misc/tparamsindefault.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 085a243b3d..6302c21b98 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -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 @@ -455,6 +459,8 @@ type 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) @@ -972,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 diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index a4a14405e5..290ac05ee9 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -34,32 +34,36 @@ when declared(echo): 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: error() template debug*(x: auto) {.deprecated.} = echo x -template mdbg*: bool {.dirty.} = - when 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 + 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: - 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() + error() # --------------------------- ident tables ---------------------------------- proc idTableGet*(t: TIdTable, key: PIdObj): RootRef diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index 24a4f186e7..1b17f620c8 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -336,6 +336,18 @@ 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(g.cache, genPrefix), owner, varSection.info, diff --git a/compiler/sem.nim b/compiler/sem.nim index afc794a379..299286545e 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -73,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: " & @@ -88,13 +98,7 @@ 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 diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 0de22cfb3d..67fe992321 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -402,7 +402,9 @@ proc updateDefaultParams(call: PNode) = for i in countdown(call.len - 1, 1): if nfDefaultParam notin call[i].flags: return - call[i] = calleeParams[i].sym.ast + 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 = diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 0ad1fb8729..fac04e3a0b 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -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.. Date: Sat, 16 Jun 2018 05:05:53 +0300 Subject: [PATCH 118/158] requested pull-request changes --- compiler/semexprs.nim | 1 + compiler/semtypes.nim | 9 +++++++-- compiler/semtypinst.nim | 5 +++-- compiler/sigmatch.nim | 20 ++++++++++---------- doc/manual.rst | 8 ++++---- 5 files changed, 25 insertions(+), 18 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 238ae01945..a9258fb622 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -857,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) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index f0f22e87c8..a0144500e9 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -863,7 +863,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, of tyStatic: if paramType.base.kind != tyNone and paramType.n != nil: - # this is a concrete type + # this is a concrete static value return if tfUnresolved in paramType.flags: return # already lifted let base = paramType.base.maybeLift @@ -1048,7 +1048,12 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, if typ == nil: typ = def.typ if typ.kind == tyTypeDesc: - # default typedesc values are mapped to the unbound typedesc type: + # 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: diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index f02c45e469..22ea09af1e 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -223,8 +223,9 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym = # 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. + # 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: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 0bb7c4fdd3..784b5c11cc 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1089,13 +1089,8 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, else: isNone of tyAnything: - if f.kind in {tyAnything}: - return isGeneric - - if tfWildCard in a.flags and f.kind == tyTypeDesc: - return isGeneric - - return isNone + if f.kind == tyAnything: return isGeneric + else: return isNone of tyUserTypeClass, tyUserTypeClassInst: if c.c.matchedConcept != nil and c.c.matchedConcept.depth <= 4: @@ -2366,6 +2361,14 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = m.firstMismatch = f break else: + 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) @@ -2375,9 +2378,6 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = put(m, formal.typ, def.typ) def.flags.incl nfDefaultParam setSon(m.call, formal.position + 1, def) - # XXX: Instead of setting a default value here, we may place a special - # marker value instead. Later, we will replace it in `semResolvedCall`. - # Unfortunately, this causes some breakage at the moment. inc(f) # forget all inferred types if the overload matching failed if m.state == csNoMatch: diff --git a/doc/manual.rst b/doc/manual.rst index 88dae89b0a..c267c706f8 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -5500,9 +5500,9 @@ Static params can also appear in the signatures of generic types: 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]``. This means that you can omit the -type param to obtain the type class of all values, known at compile-time -and you can restrict the matched values by instantiating ``static`` with +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. You can force the evaluation of a certain expression at compile-time by @@ -5529,7 +5529,7 @@ 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 will be considered implicitly generic. +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: From dba26656f6285bc0a0fc6767810981997dcdc68a Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 16 Jun 2018 19:02:47 +0200 Subject: [PATCH 119/158] nimpretty: stuff that doesn't work --- compiler/layouter.nim | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/compiler/layouter.nim b/compiler/layouter.nim index 6a9a12d821..ec5d3d0884 100644 --- a/compiler/layouter.nim +++ b/compiler/layouter.nim @@ -29,7 +29,8 @@ type fid: FileIndex lastTok: TTokType inquote: bool - col, lastLineNumber, lineSpan, indentLevel: int + col, lastLineNumber, lineSpan, indentLevel, indWidth: int + lastIndent: int doIndentMore*: int content: string fixedUntil: int # marks where we must not go in the content @@ -69,15 +70,18 @@ template wr(x) = template goodCol(col): bool = col in 40..MaxLineLen -const splitters = {tkComma, tkSemicolon, tkParLe, tkParDotLe, - tkBracketLe, tkBracketLeColon, tkCurlyDotLe, - tkCurlyLe} +const + splitters = {tkComma, tkSemicolon, tkParLe, tkParDotLe, + tkBracketLe, tkBracketLeColon, tkCurlyDotLe, + tkCurlyLe} + sectionKeywords = {tkType, tkVar, tkConst, tkLet, tkUsing} template rememberSplit(kind) = if goodCol(em.col): em.altSplitPos[kind] = em.content.len -template moreIndent(em): int = (if em.doIndentMore > 0: 4 else: 2) +template moreIndent(em): int = + max(if em.doIndentMore > 0: em.indWidth*2 else: em.indWidth, 2) proc softLinebreak(em: var Emitter, lit: string) = # XXX Use an algorithm that is outlined here: @@ -115,6 +119,10 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = wr lit var preventComment = false + if em.indWidth == 0 and tok.indent > 0: + # first indentation determines how many number of spaces to use: + em.indWidth = tok.indent + 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) @@ -129,9 +137,29 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = wr("\L") for i in 2..tok.line - em.lastLineNumber: wr("\L") em.col = 0 - for i in 1..tok.indent: + #[ we only correct the indentation if it is slightly off, + so that code like + + const splitters = {tkComma, tkSemicolon, tkParLe, tkParDotLe, + tkBracketLe, tkBracketLeColon, tkCurlyDotLe, + tkCurlyLe} + + is not touched. + ]# + when false: + if tok.indent > em.lastIndent and em.indWidth > 1 and (tok.indent mod em.indWidth) == 1: + em.indentLevel = 0 + while em.indentLevel < tok.indent-1: + inc em.indentLevel, em.indWidth + when false: + if em.indWidth != 0 and + abs(tok.indent - em.nested*em.indWidth) <= em.indWidth and + em.lastTok notin sectionKeywords: + em.indentLevel = em.nested*em.indWidth + for i in 1..em.indentLevel: wr(" ") em.fixedUntil = em.content.high + em.lastIndent = tok.indent case tok.tokType of tokKeywordLow..tokKeywordHigh: From 8081a9b3d0e31d144d0aa7c4d954c905fa564a7a Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Fri, 4 May 2018 15:36:54 +0100 Subject: [PATCH 120/158] Exports unicode.toUpper/toLower in strutils module. --- lib/pure/strutils.nim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 5de013c26c..055e91faf8 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -17,6 +17,9 @@ import parseutils from math import pow, round, floor, log10 from algorithm import reverse +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 From 9e8623785577aff8ecffaea27f8cdc0d98c1edb1 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Fri, 4 May 2018 15:44:36 +0100 Subject: [PATCH 121/158] Adds smaller code samples from Chapters 1-3 to the tester. --- tests/niminaction/Chapter1/various1.nim | 45 +++ .../niminaction/Chapter2/explicit_discard.nim | 7 + tests/niminaction/Chapter2/no_def_eq.nim | 16 + tests/niminaction/Chapter2/no_iterator.nim | 7 + tests/niminaction/Chapter2/no_seq_type.nim | 6 + tests/niminaction/Chapter2/resultaccept.nim | 28 ++ tests/niminaction/Chapter2/resultreject.nim | 33 ++ tests/niminaction/Chapter2/various2.nim | 369 ++++++++++++++++++ tests/niminaction/Chapter3/various3.nim | 93 +++++ tests/niminaction/Chapter3/various3.nim.cfg | 1 + tests/testament/categories.nim | 38 +- 11 files changed, 642 insertions(+), 1 deletion(-) create mode 100644 tests/niminaction/Chapter1/various1.nim create mode 100644 tests/niminaction/Chapter2/explicit_discard.nim create mode 100644 tests/niminaction/Chapter2/no_def_eq.nim create mode 100644 tests/niminaction/Chapter2/no_iterator.nim create mode 100644 tests/niminaction/Chapter2/no_seq_type.nim create mode 100644 tests/niminaction/Chapter2/resultaccept.nim create mode 100644 tests/niminaction/Chapter2/resultreject.nim create mode 100644 tests/niminaction/Chapter2/various2.nim create mode 100644 tests/niminaction/Chapter3/various3.nim create mode 100644 tests/niminaction/Chapter3/various3.nim.cfg diff --git a/tests/niminaction/Chapter1/various1.nim b/tests/niminaction/Chapter1/various1.nim new file mode 100644 index 0000000000..688180fd2b --- /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 0000000000..3e94c335bd --- /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 0000000000..77f0a7dd8b --- /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 0000000000..331d69480a --- /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 0000000000..493be270a7 --- /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 0000000000..7dd976b40e --- /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 0000000000..de59af7d95 --- /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 0000000000..3f6a3f4537 --- /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 .. 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 0000000000..478229b00d --- /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 0000000000..6c1ded992e --- /dev/null +++ b/tests/niminaction/Chapter3/various3.nim.cfg @@ -0,0 +1 @@ +threads:on \ No newline at end of file diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index 84e536636a..f513090a47 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 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)) + + let 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"]: From c70706f4bd672487293b82fd5b1a0ddfbb3a6a78 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 17 Jun 2018 14:15:42 +0200 Subject: [PATCH 122/158] nimpretty: fixes pragma rendering --- compiler/parser.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/parser.nim b/compiler/parser.nim index 9a9fed0cfd..649f9d5066 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -922,7 +922,7 @@ proc parsePragma(p: var TParser): PNode = optPar(p) if p.tok.tokType in {tkCurlyDotRi, tkCurlyRi}: when defined(nimpretty2): - curlyRiWasPragma(p.em) + if p.tok.tokType == tkCurlyRi: curlyRiWasPragma(p.em) getTok(p) else: parMessage(p, "expected '.}'") From dbcdc4331a2d87aafa06ce49d50277474595064d Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 17 Jun 2018 14:16:01 +0200 Subject: [PATCH 123/158] testament: minor code formating change --- tests/testament/tester.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim index 0185156ec4..0764b6363c 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) From 98f3daea651b5e2f9ef620f6b9bb5a55e3530358 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 17 Jun 2018 14:25:23 +0200 Subject: [PATCH 124/158] nimpretty: don't touch dense binary operators --- compiler/layouter.nim | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/compiler/layouter.nim b/compiler/layouter.nim index ec5d3d0884..45ae8d305a 100644 --- a/compiler/layouter.nim +++ b/compiler/layouter.nim @@ -104,7 +104,7 @@ proc softLinebreak(em: var Emitter, lit: string) = 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'} + 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+{'_'} @@ -203,14 +203,18 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = 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 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) or em.lastTok in {tkOpr, tkDotDot}: - wr(" ") - rememberSplit(splitBinary) + if not isUnary(tok) or em.lastTok in {tkOpr, tkDotDot}: + wr(" ") + rememberSplit(splitBinary) of tkAccent: wr(TokTypeToStr[tok.tokType]) em.inquote = not em.inquote From 4616b28c7b592c8204d9f77f3c1f7db2fb7186d8 Mon Sep 17 00:00:00 2001 From: Kaushal Modi Date: Sun, 17 Jun 2018 12:37:41 -0400 Subject: [PATCH 125/158] Add support for italic and strikethrough ANSI escape codes (#8048) \e[3m -> italic \e[9m -> strikethrough On terminals not supporting italic text, the text is shown in reverse instead, and on terminals not supporting strikethrough text, the text is shown as is. --- lib/pure/terminal.nim | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index fcca4d5d74..8ee95957d6 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 @@ -843,6 +845,7 @@ when not defined(testing) and isMainModule: write(stdout, "never mind") stdout.eraseLine() stdout.styledWriteLine("styled text ", {styleBright, styleBlink, styleUnderscore}) + stdout.styledWriteLine("italic text ", {styleItalic}) stdout.setBackGroundColor(bgCyan, true) stdout.setForeGroundColor(fgBlue) stdout.writeLine("ordinary text") From 03b073d541e899585951fb51896ce4440f94387b Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 5 May 2018 21:14:48 +0100 Subject: [PATCH 126/158] Workaround VM bug in strutils --- lib/pure/strutils.nim | 5 +++-- tests/testament/categories.nim | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 055e91faf8..ab34a0b2d0 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -17,8 +17,9 @@ import parseutils from math import pow, round, floor, log10 from algorithm import reverse -from unicode import toLower, toUpper -export toLower, toUpper +when defined(nimVmExportFixed): + from unicode import toLower, toUpper + export toLower, toUpper {.deadCodeElim: on.} # dce option deprecated diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index f513090a47..9affbc1599 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -293,13 +293,13 @@ proc testNimInAction(r: var TResults, cat: Category, options: string) = # 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 so I can easily compile an errata later. + # 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)) - let refHashes = @[ + const refHashes = @[ "51afdfa84b3ca3d810809d6c4e5037ba", "30f07e4cd5eaec981f67868d4e91cfcf", "d14e7c032de36d219c9548066a97e846", "2e40bfd5daadb268268727da91bb4e81", "c5d3853ed0aba04bf6d35ba28a98dca0", "058603145ff92d46c009006b06e5b228", From 70664bd1a25cac6ce1a3d49ff82796153abbbc4f Mon Sep 17 00:00:00 2001 From: mboratko Date: Mon, 18 Jun 2018 02:05:35 -0400 Subject: [PATCH 127/158] Changed tutorial documentation referring to 'constant' to 'immutable' (#8056) * Changed tutorial documentation referring to 'constant' to 'immutable' * Clarification regarding side-effect free procedures fo:r multiple assignments --- doc/tut1.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/tut1.rst b/doc/tut1.rst index f2251c4637..aa6114cf78 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 From 3b5b3deecd8d49515dfa4836c452647c7ab7a506 Mon Sep 17 00:00:00 2001 From: Kaushal Modi Date: Fri, 15 Jun 2018 11:26:09 -0400 Subject: [PATCH 128/158] Add styledWrite macro Also: - Move the tests block to the end of the file - Fix the older tests - Add tests for existing styledEcho - Add new tests for styledWrite Fixes https://github.com/nim-lang/Nim/issues/8046. --- lib/pure/terminal.nim | 86 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 74 insertions(+), 12 deletions(-) diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index 8ee95957d6..3cdfe466de 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -692,6 +692,40 @@ template styledEchoProcessArg(f: File, cmd: TerminalCmd) = when cmd == bgColor: fgSetColor = false +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. + ## + ## Example: + ## + ## .. code-block:: nim + ## + ## stdout.styledWrite(fgRed, "red text ") + ## stdout.styledWrite(fgGreen, "green text") + ## + let m = callsite() + var reset = false + result = newNimNode(nnkStmtList) + + for i in countup(2, m.len - 1): + let item = m[i] + case item.kind + of nnkStrLit..nnkTripleStrLit: + if i == m.len - 1: + # 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: + # if it is string literal just call write, do not enable reset + result.add(newCall(bindSym"write", f, item)) + else: + result.add(newCall(bindSym"styledEchoProcessArg", f, item)) + reset = true + if reset: result.add(newCall(bindSym"resetAttributes", f)) + macro styledWriteLine*(f: File, m: varargs[typed]): untyped = ## Similar to ``writeLine``, but treating terminal style arguments specially. ## When some argument is ``Style``, ``set[Style]``, ``ForegroundColor``, @@ -781,7 +815,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: @@ -840,17 +874,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.styledWriteLine("italic text ", {styleItalic}) - 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 @@ -901,3 +924,42 @@ 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 ") + # Below, resetStyle is needed to prevent leaking the set bgRed to the next + # newline. + stdout.styledWriteLine(fgWhite, bgRed, "white text in red background", resetStyle) + 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" From abbf9ba9f74fa2415efcad4d1792d68527eba09d Mon Sep 17 00:00:00 2001 From: Kaushal Modi Date: Fri, 15 Jun 2018 12:41:25 -0400 Subject: [PATCH 129/158] Convert styledWriteLine and styledEcho to templates This also fixes a bug in the styledWriteLine behavior where the background color leaked onto the next newline if that command did not end with resetStyle. Now it is not necessary to end styledWriteLine calls that set BackgroundColor to end in resetStyle. --- lib/pure/terminal.nim | 42 +++++++----------------------------------- 1 file changed, 7 insertions(+), 35 deletions(-) diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index 3cdfe466de..7ad243150a 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -726,11 +726,8 @@ macro styledWrite*(f: File, m: varargs[typed]): untyped = reset = true if reset: result.add(newCall(bindSym"resetAttributes", f)) -macro styledWriteLine*(f: File, m: varargs[typed]): untyped = - ## Similar to ``writeLine``, 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. +template styledWriteLine*(f: File, args: varargs[untyped]) = + ## Calls ``styledWrite`` and appends a newline at the end. ## ## Example: ## @@ -739,35 +736,12 @@ macro styledWriteLine*(f: File, m: varargs[typed]): untyped = ## proc error(msg: string) = ## styledWriteLine(stderr, fgRed, "Error: ", resetStyle, msg) ## - let m = callsite() - var reset = false - result = newNimNode(nnkStmtList) + styledWrite(f, args) + write(f, "\n") - for i in countup(2, m.len - 1): - let item = m[i] - 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)) - if reset: result.add(newCall(bindSym"resetAttributes", f)) - return - else: - # if it is string literal just call write, do not enable reset - result.add(newCall(bindSym"write", f, item)) - 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 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. @@ -951,9 +925,7 @@ when not defined(testing) and isMainModule: echo "ordinary text" stdout.styledWriteLine(fgRed, "red text ") - # Below, resetStyle is needed to prevent leaking the set bgRed to the next - # newline. - stdout.styledWriteLine(fgWhite, bgRed, "white text in red background", resetStyle) + stdout.styledWriteLine(fgWhite, bgRed, "white text in red background") stdout.styledWriteLine(" ordinary text ") stdout.styledWriteLine(fgGreen, "green text") From dd81d9d5b711a9a78d5b4a26a62d02da2e495b21 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 19 Jun 2018 09:42:33 +0200 Subject: [PATCH 130/158] nimpretty improvements --- compiler/layouter.nim | 87 +++++++++++++++++++++++-------------------- compiler/lexer.nim | 14 ++++++- compiler/parser.nim | 2 +- 3 files changed, 60 insertions(+), 43 deletions(-) diff --git a/compiler/layouter.nim b/compiler/layouter.nim index 45ae8d305a..e07bde786f 100644 --- a/compiler/layouter.nim +++ b/compiler/layouter.nim @@ -7,10 +7,7 @@ # distribution, for details about the copyright. # -## Layouter for nimpretty. Still primitive but useful. -## TODO -## - Make indentations consistent. -## - Align 'if' and 'case' expressions properly. +## Layouter for nimpretty. import idents, lexer, lineinfos, llstream, options, msgs, strutils from os import changeFileExt @@ -30,14 +27,20 @@ type lastTok: TTokType inquote: bool col, lastLineNumber, lineSpan, indentLevel, indWidth: int - lastIndent: 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, config: ConfigRef, fileIdx: FileIndex) = - let outfile = changeFileExt(config.toFullPath(fileIdx), ".pretty.nim") +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 + let outfile = changeFileExt(fullPath, ".pretty.nim") em.f = llStreamOpen(outfile, fmWrite) em.config = config em.fid = fileIdx @@ -45,6 +48,8 @@ proc openEmitter*(em: var Emitter, config: ConfigRef, fileIdx: FileIndex) = em.inquote = false em.col = 0 em.content = newStringOfCap(16_000) + em.indentStack = newSeqOfCap[int](30) + em.indentStack.add 0 if em.f == nil: rawMessage(config, errGenerated, "cannot open file: " & outfile) @@ -74,14 +79,15 @@ const splitters = {tkComma, tkSemicolon, tkParLe, tkParDotLe, tkBracketLe, tkBracketLeColon, tkCurlyDotLe, tkCurlyLe} - sectionKeywords = {tkType, tkVar, tkConst, tkLet, tkUsing} + 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 = - max(if em.doIndentMore > 0: em.indWidth*2 else: em.indWidth, 2) + (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: @@ -96,7 +102,8 @@ proc softLinebreak(em: var Emitter, lit: string) = # search backwards for a good split position: for a in em.altSplitPos: if a > em.fixedUntil: - let ws = "\L" & repeat(' ',em.indentLevel+moreIndent(em)) + let ws = "\L" & repeat(' ',em.indentLevel+moreIndent(em) - + ord(em.content[a] == ' ')) em.col = em.content.len - a em.content.insert(ws, a) break @@ -119,10 +126,6 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = wr lit var preventComment = false - if em.indWidth == 0 and tok.indent > 0: - # first indentation determines how many number of spaces to use: - em.indWidth = tok.indent - 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) @@ -130,14 +133,17 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = 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 - #[ we only correct the indentation if it is slightly off, + 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, @@ -146,20 +152,15 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = is not touched. ]# - when false: - if tok.indent > em.lastIndent and em.indWidth > 1 and (tok.indent mod em.indWidth) == 1: - em.indentLevel = 0 - while em.indentLevel < tok.indent-1: - inc em.indentLevel, em.indWidth - when false: - if em.indWidth != 0 and - abs(tok.indent - em.nested*em.indWidth) <= em.indWidth and - em.lastTok notin sectionKeywords: - em.indentLevel = em.nested*em.indWidth + # 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 - em.lastIndent = tok.indent case tok.tokType of tokKeywordLow..tokKeywordHigh: @@ -168,15 +169,19 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = elif not em.inquote and not endsInWhite(em): wr(" ") - wr(TokTypeToStr[tok.tokType]) + 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 + 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]) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 13460f7c1f..c5afa6e97c 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -867,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: @@ -1220,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/parser.nim b/compiler/parser.nim index 649f9d5066..f575f3d7ef 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -102,7 +102,7 @@ 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, config, fileIdx) + openEmitter(p.em, cache, config, fileIdx) getTok(p) # read the first token p.firstTok = true p.strongSpaces = strongSpaces From 8508dc46c8c5b1535f42d2ade3687bb85d7037e7 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 19 Jun 2018 17:12:51 +0200 Subject: [PATCH 131/158] Fix minor codegen issue with static data types --- compiler/ccgexprs.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 82cc3a1fb0..0e8af5af5d 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: @@ -385,7 +385,7 @@ proc genDeepCopy(p: BProc; dest, src: TLoc) = else: 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 From db68bbe4f7e3ed2c6321e46d9b4d4977f1855a4e Mon Sep 17 00:00:00 2001 From: gemath <33119724+gemath@users.noreply.github.com> Date: Tue, 19 Jun 2018 17:13:33 +0000 Subject: [PATCH 132/158] Pegs AST read access (#8050) * Make PEG AST nodes readable from outside the module. * Added a test module for the pegs stdlib module. * Edited changelog. * Renamed ``sons`` iterator to ``items``, added ``pairs``, inlined both. * Updated entry and moved it to the right category. --- changelog.md | 3 ++ lib/pure/pegs.nim | 23 +++++++++++-- tests/stdlib/tpegs.nim | 78 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 tests/stdlib/tpegs.nim diff --git a/changelog.md b/changelog.md index 4067cb6939..4a490dfdde 100644 --- a/changelog.md +++ b/changelog.md @@ -107,6 +107,9 @@ use the Nim VM in a native Nim application. - Added the parameter ``val`` for the ``CritBitTree[T].incl`` proc. - 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 diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index 39c5790ed8..d16527a561 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/tests/stdlib/tpegs.nim b/tests/stdlib/tpegs.nim new file mode 100644 index 0000000000..e5b709a661 --- /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 From 837d0c7270a67ea632d492586843807075eefb88 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 19 Jun 2018 20:06:04 +0200 Subject: [PATCH 133/158] nimpretty: proper command line handling; added tests; travis ensures these stay green --- .travis.yml | 2 + compiler/layouter.nim | 12 +- koch.nim | 8 +- {tools => nimpretty}/nimpretty.nim | 13 +- {tools => nimpretty}/nimpretty.nim.cfg | 0 nimpretty/tester.nim | 29 +++ nimpretty/tests/exhaustive.nim | 284 +++++++++++++++++++++++ nimpretty/tests/expected/exhaustive.nim | 291 ++++++++++++++++++++++++ 8 files changed, 622 insertions(+), 17 deletions(-) rename {tools => nimpretty}/nimpretty.nim (84%) rename {tools => nimpretty}/nimpretty.nim.cfg (100%) create mode 100644 nimpretty/tester.nim create mode 100644 nimpretty/tests/exhaustive.nim create mode 100644 nimpretty/tests/expected/exhaustive.nim diff --git a/.travis.yml b/.travis.yml index b7880cd366..5a091d0c70 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,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/compiler/layouter.nim b/compiler/layouter.nim index e07bde786f..eb591b6e19 100644 --- a/compiler/layouter.nim +++ b/compiler/layouter.nim @@ -21,7 +21,6 @@ type splitComma, splitParLe, splitAnd, splitOr, splitIn, splitBinary Emitter* = object - f: PLLStream config: ConfigRef fid: FileIndex lastTok: TTokType @@ -40,8 +39,6 @@ proc openEmitter*(em: var Emitter, cache: IdentCache; em.indWidth = getIndentWidth(fileIdx, llStreamOpen(fullPath, fmRead), cache, config) if em.indWidth == 0: em.indWidth = 2 - let outfile = changeFileExt(fullPath, ".pretty.nim") - em.f = llStreamOpen(outfile, fmWrite) em.config = config em.fid = fileIdx em.lastTok = tkInvalid @@ -50,12 +47,13 @@ proc openEmitter*(em: var Emitter, cache: IdentCache; em.content = newStringOfCap(16_000) em.indentStack = newSeqOfCap[int](30) em.indentStack.add 0 - if em.f == nil: - rawMessage(config, errGenerated, "cannot open file: " & outfile) proc closeEmitter*(em: var Emitter) = - em.f.llStreamWrite em.content - llStreamClose(em.f) + 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 diff --git a/koch.nim b/koch.nim index 4f85c6583b..97e1da7761 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) diff --git a/tools/nimpretty.nim b/nimpretty/nimpretty.nim similarity index 84% rename from tools/nimpretty.nim rename to nimpretty/nimpretty.nim index 89e6ef9052..aa9756c457 100644 --- a/tools/nimpretty.nim +++ b/nimpretty/nimpretty.nim @@ -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,18 +40,18 @@ proc writeVersion() = stdout.flushFile() quit(0) -proc prettyPrint(infile: string) = - let conf = newConfigRef() +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) - let outfile = changeFileExt(infile, ".pretty.nim") 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 @@ -61,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/tools/nimpretty.nim.cfg b/nimpretty/nimpretty.nim.cfg similarity index 100% rename from tools/nimpretty.nim.cfg rename to nimpretty/nimpretty.nim.cfg diff --git a/nimpretty/tester.nim b/nimpretty/tester.nim new file mode 100644 index 0000000000..7db245b5f3 --- /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 " & produced & " " & expected) + 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 0000000000..5d37b08828 --- /dev/null +++ b/nimpretty/tests/exhaustive.nim @@ -0,0 +1,284 @@ +discard """ + outputsub: '''ObjectAssignmentError''' + exitcode: "1" +""" + +import verylongnamehere,verylongnamehere,verylongnamehereverylongnamehereverylong,namehere,verylongnamehere + +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, "#" + +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..= 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 diff --git a/nimpretty/tests/expected/exhaustive.nim b/nimpretty/tests/expected/exhaustive.nim new file mode 100644 index 0000000000..b8c55033ce --- /dev/null +++ b/nimpretty/tests/expected/exhaustive.nim @@ -0,0 +1,291 @@ +discard """ + outputsub: '''ObjectAssignmentError''' + exitcode: "1" +""" + +import verylongnamehere, verylongnamehere, + verylongnamehereverylongnamehereverylong, namehere, verylongnamehere + +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, "#" + +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..= 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 From fb62dd1fae7a405d80f0d4257ddb1f4d48e204f0 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Mon, 18 Jun 2018 14:30:18 +0200 Subject: [PATCH 134/158] Fix constant folding for shl/not Since the source and destination types are the same the result should be trimmed to fit. --- compiler/semfold.nim | 11 ++++++-- tests/arithm/tnot.nim | 58 +++++++++++++++++++++++++++++++++++++++++++ tests/arithm/tshl.nim | 34 +++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 tests/arithm/tnot.nim create mode 100644 tests/arithm/tshl.nim diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 10a223ea2b..eceb104700 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -211,7 +211,12 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode = of mUnaryMinusF64: result = newFloatNodeT(- getFloat(a), n, g) of mNot: result = newIntNodeT(1 - getInt(a), n, g) of mCard: result = newIntNodeT(nimsets.cardSet(g.config, a), n, g) - of mBitnotI: result = newIntNodeT(not getInt(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: @@ -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 diff --git a/tests/arithm/tnot.nim b/tests/arithm/tnot.nim new file mode 100644 index 0000000000..6a4877b2c6 --- /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 0000000000..0aa46d021c --- /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 From c3090fcb48b0323cb7be79d2fcd009e11360dab5 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 19 Jun 2018 23:45:11 +0200 Subject: [PATCH 135/158] nimpretty: don't produce trailing whitespace; fixes the rendering of unary operators --- compiler/layouter.nim | 13 +++++++++---- nimpretty/tests/exhaustive.nim | 2 +- nimpretty/tests/expected/exhaustive.nim | 12 ++++++------ 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/compiler/layouter.nim b/compiler/layouter.nim index eb591b6e19..d0c9a77b93 100644 --- a/compiler/layouter.nim +++ b/compiler/layouter.nim @@ -93,6 +93,8 @@ proc softLinebreak(em: var Emitter, lit: string) = # +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(" ") @@ -100,8 +102,11 @@ proc softLinebreak(em: var Emitter, lit: string) = # search backwards for a good split position: for a in em.altSplitPos: if a > em.fixedUntil: - let ws = "\L" & repeat(' ',em.indentLevel+moreIndent(em) - - ord(em.content[a] == ' ')) + 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 @@ -186,8 +191,8 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = wr(" ") of tkSemicolon, tkComma: wr(TokTypeToStr[tok.tokType]) - wr(" ") rememberSplit(splitComma) + wr(" ") of tkParDotLe, tkParLe, tkBracketDotLe, tkBracketLe, tkCurlyLe, tkCurlyDotLe, tkBracketLeColon: if tok.strongSpaceA > 0 and not em.endsInWhite: @@ -215,7 +220,7 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = template isUnary(tok): bool = tok.strongSpaceB == 0 and tok.strongSpaceA > 0 - if not isUnary(tok) or em.lastTok in {tkOpr, tkDotDot}: + if not isUnary(tok): wr(" ") rememberSplit(splitBinary) of tkAccent: diff --git a/nimpretty/tests/exhaustive.nim b/nimpretty/tests/exhaustive.nim index 5d37b08828..1039e308bd 100644 --- a/nimpretty/tests/exhaustive.nim +++ b/nimpretty/tests/exhaustive.nim @@ -16,7 +16,7 @@ type var x*: string var y: seq[string] #[ yay inline comments. So nice I have to care bout these. ]# -echo "#", x, "##", y, "#" +echo "#", x, "##", y, "#" & "string" & $test echo (tup, here) echo(argA, argB) diff --git a/nimpretty/tests/expected/exhaustive.nim b/nimpretty/tests/expected/exhaustive.nim index b8c55033ce..8f8a03b0b5 100644 --- a/nimpretty/tests/expected/exhaustive.nim +++ b/nimpretty/tests/expected/exhaustive.nim @@ -3,7 +3,7 @@ discard """ exitcode: "1" """ -import verylongnamehere, verylongnamehere, +import verylongnamehere, verylongnamehere, verylongnamehereverylongnamehereverylong, namehere, verylongnamehere type @@ -17,7 +17,7 @@ type var x*: string var y: seq[string] #[ yay inline comments. So nice I have to care bout these. ]# -echo "#", x, "##", y, "#" +echo "#", x, "##", y, "#" & "string" & $test echo (tup, here) echo(argA, argB) @@ -104,7 +104,7 @@ type 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; +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) @@ -173,13 +173,13 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = 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, + 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, + if em.lineSpan == 0 and max(em.col, LineCommentColumn) + lit.len <= MaxLineLen: for i in 1 .. LineCommentColumn - em.col: wr(" ") wr lit @@ -264,7 +264,7 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = of tkComment: if not preventComment: emitComment(em, tok) - of tkIntLit..tkStrLit, tkRStrLit, tkTripleStrLit, tkGStrLit, + of tkIntLit..tkStrLit, tkRStrLit, tkTripleStrLit, tkGStrLit, tkGTripleStrLit, tkCharLit: let lit = fileSection(em.config, em.fid, tok.offsetA, tok.offsetB) softLinebreak(em, lit) From 26568dff004cf210736cccc8103ce6f2d47cb82f Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 19 Jun 2018 23:48:27 +0200 Subject: [PATCH 136/158] nimpretty: also test on Windows/Appveyor --- appveyor.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index a79d32e41c..daa1d4e48f 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 From 0725003a8cdb90e149d91fc6c5affcb270223be7 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 20 Jun 2018 00:56:29 +0200 Subject: [PATCH 137/158] nimpretty: fixes more reported issues --- compiler/layouter.nim | 7 ++++--- nimpretty/tests/exhaustive.nim | 3 +++ nimpretty/tests/expected/exhaustive.nim | 3 +++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/compiler/layouter.nim b/compiler/layouter.nim index d0c9a77b93..409b656c96 100644 --- a/compiler/layouter.nim +++ b/compiler/layouter.nim @@ -169,7 +169,7 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = of tokKeywordLow..tokKeywordHigh: if endsInAlpha(em): wr(" ") - elif not em.inquote and not endsInWhite(em): + elif not em.inquote and not endsInWhite(em) and tok.tokType in oprSet: wr(" ") if not em.inquote: @@ -207,9 +207,9 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = tkColonColon, tkDot: wr(TokTypeToStr[tok.tokType]) of tkEquals: - if not em.endsInWhite: wr(" ") + if not em.inquote and not em.endsInWhite: wr(" ") wr(TokTypeToStr[tok.tokType]) - wr(" ") + 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: @@ -224,6 +224,7 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = wr(" ") rememberSplit(splitBinary) of tkAccent: + if not em.inquote and endsInAlpha(em): wr(" ") wr(TokTypeToStr[tok.tokType]) em.inquote = not em.inquote of tkComment: diff --git a/nimpretty/tests/exhaustive.nim b/nimpretty/tests/exhaustive.nim index 1039e308bd..bca42d4d45 100644 --- a/nimpretty/tests/exhaustive.nim +++ b/nimpretty/tests/exhaustive.nim @@ -5,6 +5,9 @@ discard """ import verylongnamehere,verylongnamehere,verylongnamehereverylongnamehereverylong,namehere,verylongnamehere +proc `[]=`() = discard "index setter" +proc `putter=`() = discard cast[pointer](cast[int](buffer) + size) + type GeneralTokenizer* = object of RootObj ## comment here kind*: TokenClass ## and here diff --git a/nimpretty/tests/expected/exhaustive.nim b/nimpretty/tests/expected/exhaustive.nim index 8f8a03b0b5..4520fb9bdd 100644 --- a/nimpretty/tests/expected/exhaustive.nim +++ b/nimpretty/tests/expected/exhaustive.nim @@ -6,6 +6,9 @@ discard """ import verylongnamehere, verylongnamehere, verylongnamehereverylongnamehereverylong, namehere, verylongnamehere +proc `[]=`() = discard "index setter" +proc `putter=`() = discard cast[pointer](cast[int](buffer) + size) + type GeneralTokenizer* = object of RootObj ## comment here kind*: TokenClass ## and here From 2a662250d4b12a6dfdc488ec369439101fac209c Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 20 Jun 2018 00:59:01 +0200 Subject: [PATCH 138/158] nimpretty: added more code claimed to not be working --- nimpretty/tests/exhaustive.nim | 13 +++++++++++++ nimpretty/tests/expected/exhaustive.nim | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/nimpretty/tests/exhaustive.nim b/nimpretty/tests/exhaustive.nim index bca42d4d45..0ce3bde895 100644 --- a/nimpretty/tests/exhaustive.nim +++ b/nimpretty/tests/exhaustive.nim @@ -285,3 +285,16 @@ proc starWasExportMarker*(em: var Emitter) = 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 index 4520fb9bdd..dd3ff74e89 100644 --- a/nimpretty/tests/expected/exhaustive.nim +++ b/nimpretty/tests/expected/exhaustive.nim @@ -292,3 +292,16 @@ proc starWasExportMarker*(em: var Emitter) = 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 From 9adfaa7f0704b46f29f3b2f1cdf7d2e83f73ce07 Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 20 Jun 2018 10:20:42 +0200 Subject: [PATCH 139/158] fixes #8076 --- compiler/ccgstmts.nim | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 71d212282f..f9654bb1fb 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -978,16 +978,17 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope = 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, "\L") - 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("\L") result = res.rope From 1be82d96a682338ba53832bcc2772b7865b47b57 Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 20 Jun 2018 11:35:36 +0200 Subject: [PATCH 140/158] nimpretty: bugfixes; refs #8078 --- compiler/layouter.nim | 11 +++++++---- nimpretty/tester.nim | 2 +- nimpretty/tests/exhaustive.nim | 16 ++++++++++++++++ nimpretty/tests/expected/exhaustive.nim | 18 ++++++++++++++++++ 4 files changed, 42 insertions(+), 5 deletions(-) diff --git a/compiler/layouter.nim b/compiler/layouter.nim index 409b656c96..62844db4bc 100644 --- a/compiler/layouter.nim +++ b/compiler/layouter.nim @@ -74,9 +74,10 @@ template wr(x) = template goodCol(col): bool = col in 40..MaxLineLen const - splitters = {tkComma, tkSemicolon, tkParLe, tkParDotLe, - tkBracketLe, tkBracketLeColon, tkCurlyDotLe, - tkCurlyLe} + 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} @@ -169,7 +170,9 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = of tokKeywordLow..tokKeywordHigh: if endsInAlpha(em): wr(" ") - elif not em.inquote and not endsInWhite(em) and tok.tokType in oprSet: + elif not em.inquote and not endsInWhite(em) and + em.lastTok notin openPars: + #and tok.tokType in oprSet wr(" ") if not em.inquote: diff --git a/nimpretty/tester.nim b/nimpretty/tester.nim index 7db245b5f3..8798ce06ae 100644 --- a/nimpretty/tester.nim +++ b/nimpretty/tester.nim @@ -16,7 +16,7 @@ proc test(infile, outfile: string) = let produced = dir / nimFile & ".pretty" if strip(readFile(expected)) != strip(readFile(produced)): echo "FAILURE: files differ: ", nimFile - discard execShellCmd("diff -uNdr " & produced & " " & expected) + discard execShellCmd("diff -uNdr " & expected & " " & produced) failures += 1 else: echo "SUCCESS: files identical: ", nimFile diff --git a/nimpretty/tests/exhaustive.nim b/nimpretty/tests/exhaustive.nim index 0ce3bde895..9f2141fbba 100644 --- a/nimpretty/tests/exhaustive.nim +++ b/nimpretty/tests/exhaustive.nim @@ -8,6 +8,22 @@ import verylongnamehere,verylongnamehere,verylongnamehereverylongnamehereverylon 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 diff --git a/nimpretty/tests/expected/exhaustive.nim b/nimpretty/tests/expected/exhaustive.nim index dd3ff74e89..95071fce33 100644 --- a/nimpretty/tests/expected/exhaustive.nim +++ b/nimpretty/tests/expected/exhaustive.nim @@ -9,6 +9,24 @@ import verylongnamehere, 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 From cc11aa9698e6537fd412bb64a3f99393bc51c90d Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 22 Jun 2018 03:19:30 +0200 Subject: [PATCH 141/158] varints module: critical bugfix --- lib/std/varints.nim | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/std/varints.nim b/lib/std/varints.nim index bfc1945fef..483d5c96cd 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) From af66258dca695d932e76ea31bdee2b2f185139cb Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Thu, 21 Jun 2018 22:21:24 +0200 Subject: [PATCH 142/158] Discriminate gensym'd type names in sigHash The root cause of #7905 lies in the codegen phase. The two template instantiations generate two different MyType types with different members but same t.sym.name leading the caching mechanism to confuse the two. Fixes #7905 --- compiler/sighashes.nim | 6 ++++-- tests/types/t7905.nim | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 tests/types/t7905.nim diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 0b95387cd9..720d1848db 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -189,10 +189,11 @@ 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): + let wasAnon = sfAnon in t.sym.flags excl t.sym.flags, sfAnon let n = t.n for i in 0 ..< n.len: @@ -200,7 +201,8 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) = let s = n[i].sym c.hashSym s c.hashType s.typ, flags - incl t.sym.flags, sfAnon + if wasAnon: + incl t.sym.flags, sfAnon else: c &= t.id if t.len > 0 and t.sons[0] != nil: diff --git a/tests/types/t7905.nim b/tests/types/t7905.nim new file mode 100644 index 0000000000..ddb3710395 --- /dev/null +++ b/tests/types/t7905.nim @@ -0,0 +1,18 @@ +discard """ + output: ''' +(member: "hello world") +(member: 123.456) +''' +""" + +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) From e39baf46fc523245a7d73b263808b944c6f225db Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Fri, 22 Jun 2018 15:09:35 +0200 Subject: [PATCH 143/158] Don't blow up with recursive objects --- compiler/sighashes.nim | 7 +++---- tests/types/t7905.nim | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 720d1848db..0bf2b84598 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -193,16 +193,15 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) = # 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): - let wasAnon = sfAnon in t.sym.flags - 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 - if wasAnon: - 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/tests/types/t7905.nim b/tests/types/t7905.nim index ddb3710395..ef75bb86cf 100644 --- a/tests/types/t7905.nim +++ b/tests/types/t7905.nim @@ -2,6 +2,8 @@ discard """ output: ''' (member: "hello world") (member: 123.456) +(member: "hello world", x: ...) +(member: 123.456, x: ...) ''' """ @@ -16,3 +18,16 @@ template foobar(arg: typed): untyped = 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) From 7f0d7871eda6ec54a941b6a52f00ceb217bfe425 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 23 Jun 2018 13:37:35 +0200 Subject: [PATCH 144/158] Use the resolved typedesc in semVarOrLet By leaving the unsemanticized node in the AST we'd trip some passes like the Transf one as seen in #7936. --- compiler/semstmts.nim | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 3687e50e9c..3528d0155d 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -521,7 +521,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 +562,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: From 371f87e9626ce84a55b3d8e1aa1bc3a97ebcc91b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oscar=20Nihlg=C3=A5rd?= Date: Sat, 23 Jun 2018 15:01:55 +0200 Subject: [PATCH 145/158] Change inconsistent paramaters in times.nim (#8091) --- lib/pure/times.nim | 47 +++++++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 7cecc31ab5..7fd60b818f 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) From 059ddeee10a7b6e3805891b68205e01c045e038d Mon Sep 17 00:00:00 2001 From: Federico Ceratto Date: Sat, 23 Jun 2018 14:32:24 +0100 Subject: [PATCH 146/158] Minor doc fix --- lib/pure/httpclient.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 8530e4c42f..8b4fb0f8c1 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. ## From 4c2e712056b1e23d89be51f9cdcbfe573c399819 Mon Sep 17 00:00:00 2001 From: Kaushal Modi Date: Sun, 24 Jun 2018 03:53:20 -0400 Subject: [PATCH 147/158] Support setting foreground/background colors to terminal defaults (#8073) * Support setting foreground/background colors to terminal defaults Adds fgDefault to ForegroundColor and bgDefault to BackgroundColor enums. For Windows console, the default foreground color is assumed to be white, and the default background color black. * Add default fg/bg color caching for Windows Console --- lib/pure/terminal.nim | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index 7ad243150a..ac41a0aad3 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -542,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 @@ -552,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) @@ -584,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) @@ -935,3 +956,6 @@ when not defined(testing) and isMainModule: 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" From bfa3d62cc1a9485cc7030216f5859258131ea5bb Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sun, 24 Jun 2018 15:13:34 +0200 Subject: [PATCH 148/158] More concept fixes Fixes #7705, #7703, #7702 --- compiler/semexprs.nim | 4 ++-- compiler/semfold.nim | 2 +- compiler/types.nim | 2 +- tests/concepts/treversable.nim | 31 +++++++++++++++++++++++++++++++ 4 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 tests/concepts/treversable.nim diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 9d7c493a7b..d7b5667b9a 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -271,7 +271,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) @@ -1261,7 +1261,7 @@ 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, + let arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyUserTypeClassInst, tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink}) case arr.kind of tyArray, tyOpenArray, tyVarargs, tySequence, tyString, diff --git a/compiler/semfold.nim b/compiler/semfold.nim index eceb104700..2f495bc7f1 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -619,7 +619,7 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode = of mLow: 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(g.config, skipTypes(n[1].typ, abstractVar)), n, g) else: diff --git a/compiler/types.nim b/compiler/types.nim index 98343c6889..a1bdc77305 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -615,7 +615,7 @@ proc firstOrd*(conf: ConfigRef; t: PType): BiggestInt = else: assert(t.n.sons[0].kind == nkSym) result = t.n.sons[0].sym.position - of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic, tyInferred: + of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic, tyInferred, tyUserTypeClassInst: result = firstOrd(conf, lastSon(t)) of tyOrdinal: if t.len > 0: result = firstOrd(conf, lastSon(t)) diff --git a/tests/concepts/treversable.nim b/tests/concepts/treversable.nim new file mode 100644 index 0000000000..6ebc077d90 --- /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 From 95436893061176d51b1c11fdf5418b3ed17a8455 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sun, 24 Jun 2018 18:27:40 +0200 Subject: [PATCH 149/158] Make `static` blocks introduce their own scope Treat the static block as a normal block, don't leak any identifier in the outer scope. Fixes #5958 --- compiler/semstmts.nim | 2 ++ tests/global/t5958.nim | 9 +++++++++ 2 files changed, 11 insertions(+) create mode 100644 tests/global/t5958.nim diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 292238dc9e..fe4318de50 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1772,7 +1772,9 @@ 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.graph, a, c.p.owner) diff --git a/tests/global/t5958.nim b/tests/global/t5958.nim new file mode 100644 index 0000000000..5abcad4a9c --- /dev/null +++ b/tests/global/t5958.nim @@ -0,0 +1,9 @@ +discard """ + line: 9 + errormsg: "undeclared identifier: 'a'" +""" + +static: + var a = 1 + +echo a From 589d6bc8d81408df88298dd6cd3ee176d4bdbc46 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Mon, 25 Jun 2018 15:46:38 +0200 Subject: [PATCH 150/158] Warn the user if the specified verbosity level is wrong --- compiler/commands.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/commands.nim b/compiler/commands.nim index 330504a76e..866405f9f5 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -606,7 +606,10 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; 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) From f559e62e45b4be227d494e75fe82f571ee410840 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Mon, 25 Jun 2018 15:56:13 +0200 Subject: [PATCH 151/158] Adjust some tests to make them pass The non-scoped behaviour of static blocks was exploited by those tests, replace all the variables declared whithin one with compileTime marked ones. --- tests/pragmas/treorder.nim | 3 +-- tests/vm/tnimnode.nim | 20 +++++++++----------- tests/vm/tvmmisc.nim | 4 ++-- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/tests/pragmas/treorder.nim b/tests/pragmas/treorder.nim index 6a6bbff4db..1006af5274 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/vm/tnimnode.nim b/tests/vm/tnimnode.nim index 188bac9bf3..4210ab41dd 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/tvmmisc.nim b/tests/vm/tvmmisc.nim index 4af824cf47..80f5aeee00 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] From aef441101fcfd8390f9a28777ae2bef8b1c5b5b2 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 26 Jun 2018 01:34:59 +0200 Subject: [PATCH 152/158] fixes docgen regression caused by refactorings; fixes #8097 --- compiler/docgen.nim | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index d463dc3c0f..db4e301d4b 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -582,7 +582,7 @@ proc traceDeps(d: PDoc, it: PNode) = if d.section[k] != nil: add(d.section[k], ", ") dispA(d.conf, d.section[k], "$1", - "$1", [rope(getModuleName(d.conf, it))]) + "$1", [rope(splitFile(getModuleName(d.conf, it)).name)]) proc generateDoc*(d: PDoc, n: PNode) = case n.kind @@ -780,13 +780,11 @@ 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 - var filename: string if optStdout in d.conf.globalOptions: writeRope(stdout, content) - filename = "" else: - filename = getOutFile2(d.conf, filename, outExt, "htmldocs") - success = writeRope(content, filename) + let outfile = getOutFile2(d.conf, filename, outExt, "htmldocs") + success = writeRope(content, outfile) if not success: rawMessage(d.conf, if useWarning: warnCannotOpenFile else: errCannotOpenFile, filename) From ba3c6d022bc10f5518c23d2e9ccaf84ddd4336ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arne=20D=C3=B6ring?= Date: Tue, 26 Jun 2018 01:42:36 +0200 Subject: [PATCH 153/158] make basic debugging possible --- compiler/astalgo.nim | 12 ++++++------ compiler/msgs.nim | 4 ++-- koch.nim | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 0afe56bb70..df0c06bccb 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -27,9 +27,9 @@ 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*(conf: ConfigRef; n: PSym) {.deprecated.} - proc debug*(conf: ConfigRef; n: PType) {.deprecated.} - proc debug*(conf: ConfigRef; n: PNode) {.deprecated.} + 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 mdbg*: bool {.dirty.} = when compiles(c.module): @@ -409,7 +409,7 @@ proc debugTree(conf: ConfigRef; n: PNode, indent: int, maxRecDepth: int; addf(result, "$N$1}", [rspaces(indent)]) when declared(echo): - proc debug(conf: ConfigRef; n: PSym) = + proc debug(n: PSym; conf: ConfigRef) = if n == nil: echo("null") elif n.kind == skUnknown: @@ -420,10 +420,10 @@ when declared(echo): n.name.s, $n.id, $flagsToStr(n.flags), $flagsToStr(n.loc.flags), $lineInfoToStr(conf, n.info), $n.kind]) - proc debug(conf: ConfigRef; n: PType) = + proc debug(n: PType; conf: ConfigRef) = echo($debugType(conf, n)) - proc debug(conf: ConfigRef; n: PNode) = + proc debug(n: PNode; conf: ConfigRef) = echo($debugTree(conf, n, 0, 100)) proc nextTry(h, maxHash: Hash): Hash = diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 62948e81e6..be2ece9117 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -155,10 +155,10 @@ proc getInfoContext*(conf: ConfigRef; index: int): TLineInfo = else: result = conf.m.msgContext[i] template toFilename*(conf: ConfigRef; fileIdx: FileIndex): string = - (if fileIdx.int32 < 0: "???" else: conf.m.fileInfos[fileIdx.int32].projPath) + (if fileIdx.int32 < 0 or conf == nil: "???" else: conf.m.fileInfos[fileIdx.int32].projPath) proc toFullPath*(conf: ConfigRef; fileIdx: FileIndex): string = - if fileIdx.int32 < 0: result = "???" + if fileIdx.int32 < 0 or conf == nil: result = "???" else: result = conf.m.fileInfos[fileIdx.int32].fullPath proc setDirtyFile*(conf: ConfigRef; fileIdx: FileIndex; filename: string) = diff --git a/koch.nim b/koch.nim index 97e1da7761..dd451008c8 100644 --- a/koch.nim +++ b/koch.nim @@ -470,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 --debugger:native " & bootArgs & " compiler" / "nim", 125) copyExe(output, finalDest) if programArgs.len > 0: exec(finalDest & " " & programArgs) From 5976bd96be3d14e033005333d8ddab85b2ee7874 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 26 Jun 2018 01:43:41 +0200 Subject: [PATCH 154/158] nimpretty: detect '; vs ,' style based on the first usage of the token in parameter lists --- compiler/layouter.nim | 8 +++++++- nimpretty/tests/exhaustive.nim | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/compiler/layouter.nim b/compiler/layouter.nim index 62844db4bc..36ad086968 100644 --- a/compiler/layouter.nim +++ b/compiler/layouter.nim @@ -20,11 +20,15 @@ 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 @@ -258,7 +262,9 @@ proc starWasExportMarker*(em: var Emitter) = dec em.col, 2 proc commaWasSemicolon*(em: var Emitter) = - if em.content.endsWith(", "): + 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("; ") diff --git a/nimpretty/tests/exhaustive.nim b/nimpretty/tests/exhaustive.nim index 9f2141fbba..a2501a1937 100644 --- a/nimpretty/tests/exhaustive.nim +++ b/nimpretty/tests/exhaustive.nim @@ -122,7 +122,7 @@ type 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} = +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 From 7e89f9a09a2b181fae6e6e3caaf7bea781c3e864 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Wollenschl=C3=A4ger?= Date: Tue, 26 Jun 2018 20:08:40 +0900 Subject: [PATCH 155/158] Don't remove extension of source files twice, if filenames contain dots --- compiler/extccomp.nim | 2 +- compiler/pragmas.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 615b8c1e1c..17133624b4 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -603,7 +603,7 @@ proc addExternalFileToCompile*(conf: ConfigRef; c: var Cfile) = 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) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index c78a3519cf..afe60e9dd9 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -466,7 +466,7 @@ proc processCompile(c: PContext, n: PNode) = else: found = findFile(c.config, s) if found.len == 0: found = s - let obj = toObjFile(c.config, completeCFilePath(c.config, changeFileExt(found, ""), false)) + let obj = toObjFile(c.config, completeCFilePath(c.config, found, false)) docompile(c, it, found, obj) proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) = From 0eb9da5f044553a5c88115476f79d0599be36bf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arne=20D=C3=B6ring?= Date: Tue, 26 Jun 2018 13:34:22 +0200 Subject: [PATCH 156/158] readded -d:debug flag --- koch.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/koch.nim b/koch.nim index dd451008c8..9411a95de3 100644 --- a/koch.nim +++ b/koch.nim @@ -470,7 +470,7 @@ proc temp(args: string) = # commit. let (bootArgs, programArgs) = splitArgs(args) let nimexec = findNim() - exec(nimexec & " c --debugger:native " & 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) From 236bc06b5f1a1a1fc4a711007aed0b60b6656a4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oscar=20Nihlg=C3=A5rd?= Date: Tue, 26 Jun 2018 21:18:55 +0200 Subject: [PATCH 157/158] Improve vm support for ref types --- compiler/vm.nim | 47 ++++++++++++++++++++++++-------------------- compiler/vmdef.nim | 1 - tests/vm/tnilref.nim | 7 +++++++ tests/vm/tref.nim | 47 +++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 79 insertions(+), 23 deletions(-) create mode 100644 tests/vm/tnilref.nim diff --git a/compiler/vm.nim b/compiler/vm.nim index 3e33e8256d..b16eb0fd40 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -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 @@ -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) @@ -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 @@ -1572,7 +1577,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = 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 diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index cec61ade52..f7466b3929 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -36,7 +36,6 @@ type opcAsgnFloat, opcAsgnRef, opcAsgnComplex, - opcRegToNode, opcNodeToReg, opcLdArr, # a = b[c] diff --git a/tests/vm/tnilref.nim b/tests/vm/tnilref.nim new file mode 100644 index 0000000000..5e27cf0cba --- /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/tref.nim b/tests/vm/tref.nim index 517a67fb00..27b7bf3132 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 From 19ea3a70d2d6d404235e0f5250d6b681d56903a5 Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 27 Jun 2018 00:21:31 +0200 Subject: [PATCH 158/158] compiler/types.nim: make low/high know about 'sink' parameters; cleans up #7736 --- compiler/types.nim | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/compiler/types.nim b/compiler/types.nim index 16a84dee61..c5af855cc5 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -128,7 +128,7 @@ proc getProcHeader*(conf: ConfigRef; sym: PSym; prefer: TPreferedDesc = preferNa 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) @@ -138,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, @@ -168,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): @@ -218,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): @@ -261,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): @@ -622,7 +622,8 @@ proc firstOrd*(conf: ConfigRef; t: PType): BiggestInt = else: assert(t.n.sons[0].kind == nkSym) result = t.n.sons[0].sym.position - of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic, tyInferred, tyUserTypeClassInst: + of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink, + tyStatic, tyInferred, tyUserTypeClassInst: result = firstOrd(conf, lastSon(t)) of tyOrdinal: if t.len > 0: result = firstOrd(conf, lastSon(t)) @@ -640,7 +641,8 @@ proc firstFloat*(t: PType): BiggestFloat = assert(t.n.kind == nkRange) getFloatValue(t.n.sons[0]) of tyVar: firstFloat(t.sons[0]) - of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic, tyInferred: + of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink, + tyStatic, tyInferred: firstFloat(lastSon(t)) else: internalError(newPartialConfigRef(), "invalid kind for firstFloat(" & $t.kind & ')') @@ -676,7 +678,8 @@ proc lastOrd*(conf: ConfigRef; 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: + of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink, + tyStatic, tyInferred: result = lastOrd(conf, lastSon(t)) of tyProxy: result = 0 of tyOrdinal: @@ -695,7 +698,8 @@ proc lastFloat*(t: PType): BiggestFloat = assert(t.n != nil) # range directly given: assert(t.n.kind == nkRange) getFloatValue(t.n.sons[1]) - of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic, tyInferred: + of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink, + tyStatic, tyInferred: lastFloat(lastSon(t)) else: internalError(newPartialConfigRef(), "invalid kind for lastFloat(" & $t.kind & ')') @@ -1395,7 +1399,7 @@ proc computeSizeAux(conf: ConfigRef; typ: PType, a: var BiggestInt): BiggestInt of tyInferred: if typ.len > 1: result = computeSizeAux(conf, typ.lastSon, a) - of tyGenericInst, tyDistinct, tyGenericBody, tyAlias: + of tyGenericInst, tyDistinct, tyGenericBody, tyAlias, tySink: result = computeSizeAux(conf, lastSon(typ), a) of tyTypeClasses: result = if typ.isResolvedUserTypeClass: computeSizeAux(conf, typ.lastSon, a) @@ -1574,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 =