diff --git a/.gitignore b/.gitignore index f2037c1b83..eb29dfc047 100644 --- a/.gitignore +++ b/.gitignore @@ -48,7 +48,10 @@ xcuserdata/ /compiler/nimrod.dot /reject.json /run.json -/testresults.html +# for `nim doc foo.nim` +/*.html +#/testresults.html #covered by /*.html + /testresults.json testament.db /csources @@ -62,3 +65,4 @@ dist/ testresults/ test.txt /test.ini + diff --git a/.travis.yml b/.travis.yml index b07de1df1f..0f74c56acf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -50,4 +50,4 @@ script: - ./koch web - ./koch csource - ./koch nimsuggest -# - nim c -r nimsuggest/tester + - nim c -r nimsuggest/tester diff --git a/changelog.md b/changelog.md index 8d4dc81c01..0fe2f6099d 100644 --- a/changelog.md +++ b/changelog.md @@ -72,6 +72,7 @@ - ``lineInfoObj`` now returns absolute path instead of project path. It's used by ``lineInfo``, ``check``, ``expect``, ``require``, etc. +- ``net.sendTo`` no longer returns an int and now raises an ``OSError``. - `threadpool`'s `await` and derivatives have been renamed to `blockUntil` to avoid confusions with `await` from the `async` macro. @@ -107,6 +108,7 @@ - ``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``. +- Added a simple interpreting event parser template ``eventParser`` to the ``pegs`` module. - Added ``macros.copyLineInfo`` to copy lineInfo from other node. - Added ``system.ashr`` an arithmetic right shift for integers. @@ -139,6 +141,8 @@ - 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. +- The ``accept`` socket procedure defined in the ``net`` module can now accept + a nil socket. ### Language additions diff --git a/compiler/ast.nim b/compiler/ast.nim index 21e129d06a..6949446312 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -628,6 +628,7 @@ type mIsPartOf, mAstToStr, mParallel, mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast, mNewString, mNewStringOfCap, mParseBiggestFloat, + mMove, mWasMoved, mReset, mArray, mOpenArray, mRange, mSet, mSeq, mOpt, mVarargs, mRef, mPtr, mVar, mDistinct, mVoid, mTuple, @@ -656,7 +657,7 @@ type mNHint, mNWarning, mNError, mInstantiationInfo, mGetTypeInfo, mNimvm, mIntDefine, mStrDefine, mRunnableExamples, - mException, mBuiltinType + mException, mBuiltinType, mSymOwner # things that we can evaluate safely at compile time, even if not asked for it: const @@ -1020,13 +1021,16 @@ proc isCallExpr*(n: PNode): bool = proc discardSons*(father: PNode) proc len*(n: PNode): int {.inline.} = - if isNil(n.sons): result = 0 - else: result = len(n.sons) + when defined(nimNoNilSeqs): + result = len(n.sons) + else: + if isNil(n.sons): result = 0 + else: result = len(n.sons) proc safeLen*(n: PNode): int {.inline.} = ## works even for leaves. - if n.kind in {nkNone..nkNilLit} or isNil(n.sons): result = 0 - else: result = len(n.sons) + if n.kind in {nkNone..nkNilLit}: result = 0 + else: result = len(n) proc safeArrLen*(n: PNode): int {.inline.} = ## works for array-like objects (strings passed as openArray in VM). @@ -1036,7 +1040,8 @@ proc safeArrLen*(n: PNode): int {.inline.} = proc add*(father, son: PNode) = assert son != nil - if isNil(father.sons): father.sons = @[] + when not defined(nimNoNilSeqs): + if isNil(father.sons): father.sons = @[] add(father.sons, son) type Indexable = PNode | PType @@ -1088,9 +1093,9 @@ proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym, result.id = getID() when debugIds: registerId(result) - #if result.id == 93289: + #if result.id == 77131: # writeStacktrace() - # MessageOut(name.s & " has id: " & toString(result.id)) + # echo name.s proc isMetaType*(t: PType): bool = return t.kind in tyMetaTypes or @@ -1134,19 +1139,16 @@ const # for all kind of hash tables: proc copyStrTable*(dest: var TStrTable, src: TStrTable) = dest.counter = src.counter - if isNil(src.data): return setLen(dest.data, len(src.data)) for i in countup(0, high(src.data)): dest.data[i] = src.data[i] proc copyIdTable*(dest: var TIdTable, src: TIdTable) = dest.counter = src.counter - if isNil(src.data): return newSeq(dest.data, len(src.data)) for i in countup(0, high(src.data)): dest.data[i] = src.data[i] proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet) = dest.counter = src.counter - if isNil(src.data): return setLen(dest.data, len(src.data)) for i in countup(0, high(src.data)): dest.data[i] = src.data[i] @@ -1243,7 +1245,8 @@ proc newStrNode*(strVal: string; info: TLineInfo): PNode = proc addSon*(father, son: PNode) = assert son != nil - if isNil(father.sons): father.sons = @[] + when not defined(nimNoNilSeqs): + if isNil(father.sons): father.sons = @[] add(father.sons, son) proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode, @@ -1268,14 +1271,14 @@ proc newType*(kind: TTypeKind, owner: PSym): PType = new(result) result.kind = kind result.owner = owner - result.size = - 1 + result.size = -1 result.align = 2 # default alignment result.id = getID() result.lockLevel = UnspecifiedLockLevel when debugIds: registerId(result) when false: - if result.id == 205734: + if result.id == 76426: echo "KNID ", kind writeStackTrace() @@ -1287,16 +1290,22 @@ proc mergeLoc(a: var TLoc, b: TLoc) = if a.r == nil: a.r = b.r proc newSons*(father: PNode, length: int) = - if isNil(father.sons): - newSeq(father.sons, length) - else: + when defined(nimNoNilSeqs): setLen(father.sons, length) + else: + if isNil(father.sons): + newSeq(father.sons, length) + else: + setLen(father.sons, length) proc newSons*(father: PType, length: int) = - if isNil(father.sons): - newSeq(father.sons, length) - else: + when defined(nimNoNilSeqs): setLen(father.sons, length) + else: + if isNil(father.sons): + newSeq(father.sons, length) + else: + setLen(father.sons, length) proc sonsLen*(n: PType): int = n.sons.len proc len*(n: PType): int = n.sons.len @@ -1464,20 +1473,26 @@ proc propagateToOwner*(owner, elem: PType) = owner.flags.incl tfHasGCedMem proc rawAddSon*(father, son: PType) = - if isNil(father.sons): father.sons = @[] + when not defined(nimNoNilSeqs): + if isNil(father.sons): father.sons = @[] add(father.sons, son) if not son.isNil: propagateToOwner(father, son) proc rawAddSonNoPropagationOfTypeFlags*(father, son: PType) = - if isNil(father.sons): father.sons = @[] + when not defined(nimNoNilSeqs): + if isNil(father.sons): father.sons = @[] add(father.sons, son) proc addSonNilAllowed*(father, son: PNode) = - if isNil(father.sons): father.sons = @[] + when not defined(nimNoNilSeqs): + if isNil(father.sons): father.sons = @[] add(father.sons, son) proc delSon*(father: PNode, idx: int) = - if isNil(father.sons): return + when defined(nimNoNilSeqs): + if father.len == 0: return + else: + if isNil(father.sons): return var length = sonsLen(father) for i in countup(idx, length - 2): father.sons[i] = father.sons[i + 1] setLen(father.sons, length - 1) @@ -1579,15 +1594,17 @@ proc getInt*(a: PNode): BiggestInt = case a.kind of nkCharLit..nkUInt64Lit: result = a.intVal else: + raiseRecoverableError("cannot extract number from invalid AST node") #internalError(a.info, "getInt") - doAssert false, "getInt" + #doAssert false, "getInt" #result = 0 proc getFloat*(a: PNode): BiggestFloat = case a.kind of nkFloatLiterals: result = a.floatVal else: - doAssert false, "getFloat" + raiseRecoverableError("cannot extract number from invalid AST node") + #doAssert false, "getFloat" #internalError(a.info, "getFloat") #result = 0.0 @@ -1601,7 +1618,8 @@ proc getStr*(a: PNode): string = else: result = nil else: - doAssert false, "getStr" + raiseRecoverableError("cannot extract string from invalid AST node") + #doAssert false, "getStr" #internalError(a.info, "getStr") #result = "" @@ -1610,7 +1628,8 @@ proc getStrOrChar*(a: PNode): string = of nkStrLit..nkTripleStrLit: result = a.strVal of nkCharLit..nkUInt64Lit: result = $chr(int(a.intVal)) else: - doAssert false, "getStrOrChar" + raiseRecoverableError("cannot extract string from invalid AST node") + #doAssert false, "getStrOrChar" #internalError(a.info, "getStrOrChar") #result = "" diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 152802ba16..34963ee838 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -206,7 +206,7 @@ proc makeYamlString*(s: string): Rope = const MaxLineLength = 64 result = nil var res = "\"" - for i in countup(0, if s.isNil: -1 else: (len(s)-1)): + for i in 0 ..< s.len: if (i + 1) mod MaxLineLength == 0: add(res, '\"') add(res, "\n") @@ -314,10 +314,7 @@ proc treeToYamlAux(conf: ConfigRef; n: PNode, marker: var IntSet, indent: int, addf(result, ",$N$1\"floatVal\": $2", [istr, rope(n.floatVal.toStrMaxPrecision)]) of nkStrLit..nkTripleStrLit: - if n.strVal.isNil: - addf(result, ",$N$1\"strVal\": null", [istr]) - else: - addf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)]) + addf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)]) of nkSym: addf(result, ",$N$1\"sym\": $2", [istr, symToYamlAux(conf, n.sym, marker, indent + 2, maxRecDepth)]) @@ -395,10 +392,7 @@ proc debugTree(conf: ConfigRef; n: PNode, indent: int, maxRecDepth: int; addf(result, ",$N$1\"floatVal\": $2", [istr, rope(n.floatVal.toStrMaxPrecision)]) of nkStrLit..nkTripleStrLit: - if n.strVal.isNil: - addf(result, ",$N$1\"strVal\": null", [istr]) - else: - addf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)]) + addf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)]) of nkSym: addf(result, ",$N$1\"sym\": $2_$3", [istr, rope(n.sym.name.s), rope(n.sym.id)]) @@ -759,10 +753,6 @@ proc idNodeTableGet(t: TIdNodeTable, key: PIdObj): PNode = if index >= 0: result = t.data[index].val else: result = nil -proc idNodeTableGetLazy*(t: TIdNodeTable, key: PIdObj): PNode = - if not isNil(t.data): - result = idNodeTableGet(t, key) - proc idNodeTableRawInsert(data: var TIdNodePairSeq, key: PIdObj, val: PNode) = var h: Hash h = key.id and high(data) @@ -789,10 +779,6 @@ proc idNodeTablePut(t: var TIdNodeTable, key: PIdObj, val: PNode) = idNodeTableRawInsert(t.data, key, val) inc(t.counter) -proc idNodeTablePutLazy*(t: var TIdNodeTable, key: PIdObj, val: PNode) = - if isNil(t.data): initIdNodeTable(t) - idNodeTablePut(t, key, val) - iterator pairs*(t: TIdNodeTable): tuple[key: PIdObj, val: PNode] = for i in 0 .. high(t.data): if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val) diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 2621574a69..33b07a5a7e 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -124,15 +124,19 @@ proc openArrayLoc(p: BProc, n: PNode): Rope = of tyString, tySequence: if skipTypes(n.typ, abstractInst).kind == tyVar and not compileToCpp(p.module): - result = "(*$1)$3, (*$1 ? (*$1)->$2 : 0)" % [a.rdLoc, lenField(p), dataField(p)] + var t: TLoc + t.r = "(*$1)" % [a.rdLoc] + result = "(*$1)$3, $2" % [a.rdLoc, lenExpr(p, t), dataField(p)] else: - result = "$1$3, ($1 ? $1->$2 : 0)" % [a.rdLoc, lenField(p), dataField(p)] + result = "$1$3, $2" % [a.rdLoc, lenExpr(p, a), dataField(p)] of tyArray: result = "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))] of tyPtr, tyRef: case lastSon(a.t).kind of tyString, tySequence: - result = "(*$1)$3, (*$1 ? (*$1)->$2 : 0)" % [a.rdLoc, lenField(p), dataField(p)] + var t: TLoc + t.r = "(*$1)" % [a.rdLoc] + result = "(*$1)$3, $2" % [a.rdLoc, lenExpr(p, t), dataField(p)] of tyArray: result = "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, lastSon(a.t)))] else: diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index b30d216f25..56ecf5ba3b 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -65,13 +65,13 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = of tyString: # with the new semantics for 'nil' strings, we can map "" to nil and # save tons of allocations: - if n.strVal.len == 0 and optNilSeqs notin p.options: + if n.strVal.len == 0 and optNilSeqs notin p.options and + p.config.selectedGc != gcDestructors: result = genNilStringLiteral(p.module, n.info) else: result = genStringLiteral(p.module, n) else: - if n.strVal.isNil: result = rope("NIM_NIL") - else: result = makeCString(n.strVal) + result = makeCString(n.strVal) of nkFloatLit, nkFloat64Lit: result = rope(n.floatVal.toStrMaxPrecision) of nkFloat32Lit: @@ -165,7 +165,7 @@ proc canMove(n: PNode): bool = # result = false proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = - if dest.storage == OnStack or not usesNativeGC(p.config): + if dest.storage == OnStack or not usesWriteBarrier(p.config): linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) elif dest.storage == OnHeap: # location is on heap @@ -255,9 +255,13 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = # tfShallow flag for the built-in string type too! So we check only # here for this flag, where it is reasonably safe to do so # (for objects, etc.): - if needToCopy notin flags or + if p.config.selectedGC == gcDestructors: + linefmt(p, cpsStmts, + "$1.len = $2.len; $1.p = $2.p;$n", + rdLoc(dest), rdLoc(src)) + elif needToCopy notin flags or tfShallow in skipTypes(dest.t, abstractVarRange).flags: - if dest.storage == OnStack or not usesNativeGC(p.config): + if dest.storage == OnStack or not usesWriteBarrier(p.config): linefmt(p, cpsStmts, "#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n", addrLoc(p.config, dest), addrLoc(p.config, src), rdLoc(dest)) @@ -280,17 +284,21 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = of tyRef: genRefAssign(p, dest, src, flags) of tySequence: - if (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode): + if p.config.selectedGC == gcDestructors: + genGenericAsgn(p, dest, src, flags) + elif (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode): genRefAssign(p, dest, src, flags) else: linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n", 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): + if p.config.selectedGC == gcDestructors: + genGenericAsgn(p, dest, src, flags) + elif (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode): genRefAssign(p, dest, src, flags) else: - if dest.storage == OnStack or not usesNativeGC(p.config): + if dest.storage == OnStack or not usesWriteBarrier(p.config): linefmt(p, cpsStmts, "$1 = #copyString($2);$n", dest.rdLoc, src.rdLoc) elif dest.storage == OnHeap: # we use a temporary to care for the dreaded self assignment: @@ -453,6 +461,13 @@ proc binaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) = initLocExpr(p, e.sons[2], b) lineCg(p, cpsStmts, frmt, rdLoc(a), rdLoc(b)) +proc binaryStmtAddr(p: BProc, e: PNode, d: var TLoc, frmt: string) = + var a, b: TLoc + if d.k != locNone: internalError(p.config, e.info, "binaryStmtAddr") + initLocExpr(p, e.sons[1], a) + initLocExpr(p, e.sons[2], b) + lineCg(p, cpsStmts, frmt, addrLoc(p.config, a), rdLoc(b)) + proc unaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) = var a: TLoc if d.k != locNone: internalError(p.config, e.info, "unaryStmt") @@ -889,8 +904,8 @@ proc genBoundsCheck(p: BProc; arr, a, b: TLoc) = of tySequence, tyString: linefmt(p, cpsStmts, "if ($2-$1 != -1 && " & - "(!$3 || (NU)($1) >= (NU)($3->$4) || (NU)($2) >= (NU)($3->$4))) #raiseIndexError();$n", - rdLoc(a), rdLoc(b), rdLoc(arr), lenField(p)) + "((NU)($1) >= (NU)$3 || (NU)($2) >= (NU)$3)) #raiseIndexError();$n", + rdLoc(a), rdLoc(b), lenExpr(p, arr)) else: discard proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = @@ -914,12 +929,12 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) = if optBoundsCheck in p.options: if ty.kind == tyString and (not defined(nimNoZeroTerminator) or optLaxStrings in p.options): linefmt(p, cpsStmts, - "if (!$2 || (NU)($1) > (NU)($2->$3)) #raiseIndexError();$n", - rdLoc(b), rdLoc(a), lenField(p)) + "if ((NU)($1) > (NU)$2) #raiseIndexError();$n", + rdLoc(b), lenExpr(p, a)) else: linefmt(p, cpsStmts, - "if (!$2 || (NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n", - rdLoc(b), rdLoc(a), lenField(p)) + "if ((NU)($1) >= (NU)$2) #raiseIndexError();$n", + rdLoc(b), lenExpr(p, a)) if d.k == locNone: d.storage = OnHeap if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}: a.r = ropecg(p.module, "(*$1)", a.r) @@ -1010,6 +1025,12 @@ proc genEcho(p: BProc, n: PNode) = proc gcUsage(conf: ConfigRef; n: PNode) = if conf.selectedGC == gcNone: message(conf, n.info, warnGcMem, n.renderTree) +proc strLoc(p: BProc; d: TLoc): Rope = + if p.config.selectedGc == gcDestructors: + result = addrLoc(p.config, d) + else: + result = rdLoc(d) + proc genStrConcat(p: BProc, e: PNode, d: var TLoc) = # # s = 'Hello ' & name & ', how do you feel?' & 'z' @@ -1037,13 +1058,14 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) = initLocExpr(p, e.sons[i + 1], a) if skipTypes(e.sons[i + 1].typ, abstractVarRange).kind == tyChar: inc(L) - add(appends, ropecg(p.module, "#appendChar($1, $2);$n", tmp.r, rdLoc(a))) + add(appends, ropecg(p.module, "#appendChar($1, $2);$n", strLoc(p, tmp), rdLoc(a))) else: if e.sons[i + 1].kind in {nkStrLit..nkTripleStrLit}: inc(L, len(e.sons[i + 1].strVal)) else: - addf(lens, "($1 ? $1->$2 : 0) + ", [rdLoc(a), lenField(p)]) - add(appends, ropecg(p.module, "#appendString($1, $2);$n", tmp.r, rdLoc(a))) + add(lens, lenExpr(p, a)) + add(lens, " + ") + add(appends, ropecg(p.module, "#appendString($1, $2);$n", strLoc(p, tmp), rdLoc(a))) linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", tmp.r, lens, rope(L)) add(p.s(cpsStmts), appends) if d.k == locNone: @@ -1076,19 +1098,24 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) = if skipTypes(e.sons[i + 2].typ, abstractVarRange).kind == tyChar: inc(L) add(appends, ropecg(p.module, "#appendChar($1, $2);$n", - rdLoc(dest), rdLoc(a))) + strLoc(p, dest), rdLoc(a))) else: if e.sons[i + 2].kind in {nkStrLit..nkTripleStrLit}: inc(L, len(e.sons[i + 2].strVal)) else: - addf(lens, "($1 ? $1->$2 : 0) + ", [rdLoc(a), lenField(p)]) + add(lens, lenExpr(p, a)) + add(lens, " + ") add(appends, ropecg(p.module, "#appendString($1, $2);$n", - rdLoc(dest), rdLoc(a))) - initLoc(call, locCall, e, OnHeap) - call.r = ropecg(p.module, "#resizeString($1, $2$3)", [rdLoc(dest), lens, rope(L)]) - genAssignment(p, dest, call, {}) + strLoc(p, dest), rdLoc(a))) + if p.config.selectedGC == gcDestructors: + linefmt(p, cpsStmts, "#prepareAdd($1, $2$3);$n", + addrLoc(p.config, dest), lens, rope(L)) + else: + initLoc(call, locCall, e, OnHeap) + call.r = ropecg(p.module, "#resizeString($1, $2$3)", [rdLoc(dest), lens, rope(L)]) + genAssignment(p, dest, call, {}) + gcUsage(p.config, e) add(p.s(cpsStmts), appends) - gcUsage(p.config, e) proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = # seq &= x --> @@ -1151,7 +1178,7 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) = addf(p.module.s[cfsTypeInit3], "$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)]) let args = [getTypeDesc(p.module, typ), ti, sizeExpr] - if a.storage == OnHeap and usesNativeGC(p.config): + if a.storage == OnHeap and usesWriteBarrier(p.config): # use newObjRC1 as an optimization if canFormAcycle(a.t): linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", a.rdLoc) @@ -1182,7 +1209,7 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope; lenIsZero: bool) = genTypeInfo(p.module, seqtype, dest.lode.info), length] var call: TLoc initLoc(call, locExpr, dest.lode, OnHeap) - if dest.storage == OnHeap and usesNativeGC(p.config): + if dest.storage == OnHeap and usesWriteBarrier(p.config): if canFormAcycle(dest.t): linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", dest.rdLoc) else: @@ -1201,10 +1228,16 @@ proc genNewSeq(p: BProc, e: PNode) = var a, b: TLoc initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) - let lenIsZero = optNilSeqs notin p.options and - e[2].kind == nkIntLit and e[2].intVal == 0 - genNewSeqAux(p, a, b.rdLoc, lenIsZero) - gcUsage(p.config, e) + if p.config.selectedGC == gcDestructors: + let seqtype = skipTypes(e.sons[1].typ, abstractVarRange) + linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n", + a.rdLoc, b.rdLoc, getTypeDesc(p.module, seqtype.lastSon), + getSeqPayloadType(p.module, seqtype)) + else: + let lenIsZero = optNilSeqs notin p.options and + e[2].kind == nkIntLit and e[2].intVal == 0 + genNewSeqAux(p, a, b.rdLoc, lenIsZero) + gcUsage(p.config, e) proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) = let seqtype = skipTypes(e.typ, abstractVarRange) @@ -1448,7 +1481,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) = putIntoDest(p, b, e, "$1, $1Len_0" % [rdLoc(a)], a.storage) of tyString, tySequence: putIntoDest(p, b, e, - "$1$3, ($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p), dataField(p)], a.storage) + "$1$3, $2" % [rdLoc(a), lenExpr(p, a), dataField(p)], a.storage) of tyArray: putIntoDest(p, b, e, "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))], a.storage) @@ -1492,28 +1525,19 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = if op == mHigh: unaryExpr(p, e, d, "($1 ? (#nimCStrLen($1)-1) : -1)") else: unaryExpr(p, e, d, "($1 ? #nimCStrLen($1) : 0)") of tyString: - if not p.module.compileToCpp: - if op == mHigh: unaryExpr(p, e, d, "($1 ? ($1->Sup.len-1) : -1)") - else: unaryExpr(p, e, d, "($1 ? $1->Sup.len : 0)") - else: - if op == mHigh: unaryExpr(p, e, d, "($1 ? ($1->len-1) : -1)") - else: unaryExpr(p, e, d, "($1 ? $1->len : 0)") + var a: TLoc + initLocExpr(p, e.sons[1], a) + var x = lenExpr(p, a) + if op == mHigh: x = "($1-1)" % [x] + putIntoDest(p, d, e, x) of tySequence: + # we go through a temporary here because people write bullshit code. var a, tmp: TLoc initLocExpr(p, e[1], a) getIntTemp(p, tmp) - var frmt: FormatStr - if not p.module.compileToCpp: - if op == mHigh: - frmt = "$1 = ($2 ? ($2->Sup.len-1) : -1);$n" - else: - frmt = "$1 = ($2 ? $2->Sup.len : 0);$n" - else: - if op == mHigh: - frmt = "$1 = ($2 ? ($2->len-1) : -1);$n" - else: - frmt = "$1 = ($2 ? $2->len : 0);$n" - lineCg(p, cpsStmts, frmt, tmp.r, rdLoc(a)) + var x = lenExpr(p, a) + if op == mHigh: x = "($1-1)" % [x] + lineCg(p, cpsStmts, "$1 = $2;$n", tmp.r, x) putIntoDest(p, d, e, tmp.r) of tyArray: # YYY: length(sideeffect) is optimized away incorrectly? @@ -1522,6 +1546,9 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = else: internalError(p.config, e.info, "genArrayLen()") proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = + if p.config.selectedGc == gcDestructors: + genCall(p, e, d) + return var a, b, call: TLoc assert(d.k == locNone) var x = e.sons[1] @@ -1542,16 +1569,19 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = gcUsage(p.config, e) proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) = - var a, b, call: TLoc - if d.k != locNone: internalError(p.config, e.info, "genSetLengthStr") - initLocExpr(p, e.sons[1], a) - initLocExpr(p, e.sons[2], b) + if p.config.selectedGc == gcDestructors: + binaryStmtAddr(p, e, d, "#setLengthStrV2($1, $2);$n") + else: + var a, b, call: TLoc + if d.k != locNone: internalError(p.config, e.info, "genSetLengthStr") + initLocExpr(p, e.sons[1], a) + initLocExpr(p, e.sons[2], b) - initLoc(call, locCall, e, OnHeap) - call.r = ropecg(p.module, "#setLengthStr($1, $2)", [ - rdLoc(a), rdLoc(b)]) - genAssignment(p, a, call, {}) - gcUsage(p.config, e) + initLoc(call, locCall, e, OnHeap) + call.r = ropecg(p.module, "#setLengthStr($1, $2)", [ + rdLoc(a), rdLoc(b)]) + genAssignment(p, a, call, {}) + gcUsage(p.config, e) proc genSwap(p: BProc, e: PNode, d: var TLoc) = # swap(a, b) --> @@ -1803,11 +1833,11 @@ proc genStrEquals(p: BProc, e: PNode, d: var TLoc) = if a.kind in {nkStrLit..nkTripleStrLit} and a.strVal == "": initLocExpr(p, e.sons[2], x) putIntoDest(p, d, e, - ropecg(p.module, "(!($1) || ($1)->$2 == 0)", rdLoc(x), lenField(p))) + ropecg(p.module, "($1 == 0)", lenExpr(p, x))) elif b.kind in {nkStrLit..nkTripleStrLit} and b.strVal == "": initLocExpr(p, e.sons[1], x) putIntoDest(p, d, e, - ropecg(p.module, "(!($1) || ($1)->$2 == 0)", rdLoc(x), lenField(p))) + ropecg(p.module, "($1 == 0)", lenExpr(p, x))) else: binaryExpr(p, e, d, "#eqStrings($1, $2)") @@ -1868,14 +1898,21 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mConStrStr: genStrConcat(p, e, d) of mAppendStrCh: - var dest, b, call: TLoc - initLoc(call, locCall, e, OnHeap) - initLocExpr(p, e.sons[1], dest) - initLocExpr(p, e.sons[2], b) - call.r = ropecg(p.module, "#addChar($1, $2)", [rdLoc(dest), rdLoc(b)]) - genAssignment(p, dest, call, {}) + if p.config.selectedGC == gcDestructors: + binaryStmtAddr(p, e, d, "#nimAddCharV1($1, $2);$n") + else: + var dest, b, call: TLoc + initLoc(call, locCall, e, OnHeap) + initLocExpr(p, e.sons[1], dest) + initLocExpr(p, e.sons[2], b) + call.r = ropecg(p.module, "#addChar($1, $2)", [rdLoc(dest), rdLoc(b)]) + genAssignment(p, dest, call, {}) of mAppendStrStr: genStrAppend(p, e, d) - of mAppendSeqElem: genSeqElemAppend(p, e, d) + of mAppendSeqElem: + if p.config.selectedGc == gcDestructors: + genCall(p, e, d) + else: + genSeqElemAppend(p, e, d) of mEqStr: genStrEquals(p, e, d) of mLeStr: binaryExpr(p, e, d, "(#cmpStrings($1, $2) <= 0)") of mLtStr: binaryExpr(p, e, d, "(#cmpStrings($1, $2) < 0)") @@ -1924,8 +1961,9 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet, mMinusSet, mInSet: genSetOp(p, e, d, op) - of mNewString, mNewStringOfCap, mCopyStr, mCopyStrLast, mExit, - mParseBiggestFloat: + of mCopyStr, mCopyStrLast: + genCall(p, e, d) + of mNewString, mNewStringOfCap, mExit, mParseBiggestFloat: var opr = e.sons[0].sym if lfNoDecl notin opr.loc.flags: discard cgsym(p.module, $opr.loc.r) @@ -2509,7 +2547,7 @@ proc genConstExpr(p: BProc, n: PNode): Rope = var t = skipTypes(n.typ, abstractInst) if t.kind == tySequence: result = genConstSeq(p, n, n.typ) - elif t.kind == tyProc and t.callConv == ccClosure and not n.sons.isNil and + elif t.kind == tyProc and t.callConv == ccClosure and n.len > 0 and n.sons[0].kind == nkNilLit and n.sons[1].kind == nkNilLit: # this hack fixes issue that nkNilLit is expanded to {NIM_NIL,NIM_NIL} # this behaviour is needed since closure_var = nil must be @@ -2522,6 +2560,13 @@ proc genConstExpr(p: BProc, n: PNode): Rope = result = genConstSimpleList(p, n) of nkObjConstr: result = genConstObjConstr(p, n) + of nkStrLit..nkTripleStrLit: + if p.config.selectedGc == gcDestructors: + result = genStringLiteralV2Const(p.module, n) + else: + var d: TLoc + initLocExpr(p, n, d) + result = rdLoc(d) else: var d: TLoc initLocExpr(p, n, d) diff --git a/compiler/ccgliterals.nim b/compiler/ccgliterals.nim index cfe71375e8..34677ec060 100644 --- a/compiler/ccgliterals.nim +++ b/compiler/ccgliterals.nim @@ -53,20 +53,36 @@ proc genStringLiteralV1(m: BModule; n: PNode): Rope = proc genStringLiteralDataOnlyV2(m: BModule, s: string): Rope = result = getTempName(m) - addf(m.s[cfsData], " static const NIM_CHAR $1[$2] = $3;$n", - [result, rope(len(s)+1), makeCString(s)]) + addf(m.s[cfsData], "static const struct {$n" & + " NI cap; void* allocator; NIM_CHAR data[$2];$n" & + "} $1 = { $2, NIM_NIL, $3 };$n", + [result, rope(len(s)), makeCString(s)]) proc genStringLiteralV2(m: BModule; n: PNode): Rope = let id = nodeTableTestOrSet(m.dataCache, n, m.labels) if id == m.labels: + discard cgsym(m, "NimStrPayload") + discard cgsym(m, "NimStringV2") # string literal not found in the cache: let pureLit = genStringLiteralDataOnlyV2(m, n.strVal) result = getTempName(m) - addf(m.s[cfsData], "static const #NimStringV2 $1 = {$2, $2, $3};$n", - [result, rope(len(n.strVal)+1), pureLit]) + addf(m.s[cfsData], "static const NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n", + [result, rope(len(n.strVal)), pureLit]) else: result = m.tmpBase & rope(id) +proc genStringLiteralV2Const(m: BModule; n: PNode): Rope = + let id = nodeTableTestOrSet(m.dataCache, n, m.labels) + var pureLit: Rope + if id == m.labels: + discard cgsym(m, "NimStrPayload") + discard cgsym(m, "NimStringV2") + # string literal not found in the cache: + pureLit = genStringLiteralDataOnlyV2(m, n.strVal) + else: + pureLit = m.tmpBase & rope(id) + result = "{$1, (NimStrPayload*)&$2}" % [rope(len(n.strVal)), pureLit] + # ------ Version selector --------------------------------------------------- proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo): Rope = diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index a7a2b3fee7..69e6558bb6 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -16,7 +16,7 @@ const # above X strings a hash-switch for strings is generated proc registerGcRoot(p: BProc, v: PSym) = - if p.config.selectedGC in {gcMarkAndSweep, gcGenerational, gcV2, gcRefc} and + if p.config.selectedGC in {gcMarkAndSweep, gcDestructors, gcV2, gcRefc} and containsGarbageCollectedRef(v.loc.t): # we register a specialized marked proc here; this has the advantage # that it works out of the box for thread local storage then :-) diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim index 349cf2707c..c69bb2c805 100644 --- a/compiler/ccgtrav.nim +++ b/compiler/ccgtrav.nim @@ -7,8 +7,7 @@ # distribution, for details about the copyright. # -## Generates traversal procs for the C backend. Traversal procs are only an -## optimization; the GC works without them too. +## Generates traversal procs for the C backend. # included from cgen.nim @@ -61,6 +60,7 @@ proc parentObj(accessor: Rope; m: BModule): Rope {.inline.} = else: result = accessor +proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) = if typ == nil: return @@ -93,8 +93,18 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) = let typ = getUniqueType(typ) for i in countup(0, sonsLen(typ) - 1): genTraverseProc(c, ropecg(c.p.module, "$1.Field$2", accessor, i.rope), typ.sons[i]) - of tyRef, tyString, tySequence: + of tyRef: lineCg(p, cpsStmts, c.visitorFrmt, accessor) + of tySequence: + if tfHasAsgn notin typ.flags: + lineCg(p, cpsStmts, c.visitorFrmt, accessor) + elif containsGarbageCollectedRef(typ.lastSon): + # destructor based seqs are themselves not traced but their data is, if + # they contain a GC'ed type: + genTraverseProcSeq(c, accessor, typ) + of tyString: + if tfHasAsgn notin typ.flags: + lineCg(p, cpsStmts, c.visitorFrmt, accessor) of tyProc: if typ.callConv == ccClosure: lineCg(p, cpsStmts, c.visitorFrmt, ropecg(c.p.module, "$1.ClE_0", accessor)) @@ -107,8 +117,11 @@ proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) = var i: TLoc getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo(), tyInt), i) let oldCode = p.s(cpsStmts) - lineF(p, cpsStmts, "for ($1 = 0; $1 < ($2 ? $2->$3 : 0); $1++) {$n", - [i.r, accessor, lenField(c.p)]) + var a: TLoc + a.r = accessor + + lineF(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", + [i.r, lenExpr(c.p, a)]) let oldLen = p.s(cpsStmts).len genTraverseProc(c, "$1$3[$2]" % [accessor, i.r, dataField(c.p)], typ.sons[0]) if p.s(cpsStmts).len == oldLen: diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index a16255f6e1..dd79f4846b 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -282,7 +282,7 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope = of tyString: case detectStrVersion(m) of 2: - discard cgsym(m, "string") + discard cgsym(m, "NimStringV2") result = typeNameOrLiteral(m, typ, "NimStringV2") else: discard cgsym(m, "NimStringDesc") @@ -324,6 +324,10 @@ proc getForwardStructFormat(m: BModule): string = if m.compileToCpp: result = "$1 $2;$n" else: result = "typedef $1 $2 $2;$n" +proc seqStar(m: BModule): string = + if m.config.selectedGC == gcDestructors: result = "" + else: result = "*" + proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope = result = cacheGetType(m.forwTypeCache, sig) if result != nil: return @@ -355,8 +359,11 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope = result = getTypeForward(m, t, hashType(t)) pushType(m, t) of tySequence: - result = getTypeForward(m, t, hashType(t)) & "*" - pushType(m, t) + if m.config.selectedGC == gcDestructors: + result = getTypeDescAux(m, t, check) + else: + result = getTypeForward(m, t, hashType(t)) & seqStar(m) + pushType(m, t) else: result = getTypeDescAux(m, t, check) @@ -442,7 +449,9 @@ proc genRecordFieldsAux(m: BModule, n: PNode, of nkRecCase: if n.sons[0].kind != nkSym: internalError(m.config, n.info, "genRecordFieldsAux") add(result, genRecordFieldsAux(m, n.sons[0], accessExpr, rectype, check)) - let uname = rope(mangle(n.sons[0].sym.name.s) & 'U') + # prefix mangled name with "_U" to avoid clashes with other field names, + # since identifiers are not allowed to start with '_' + let uname = rope("_U" & mangle(n.sons[0].sym.name.s)) let ae = if accessExpr != nil: "$1.$2" % [accessExpr, uname] else: uname var unionBody: Rope = nil @@ -487,7 +496,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode, if fieldType.kind == tyArray and tfUncheckedArray in fieldType.flags: addf(result, "$1 $2[SEQ_DECL_SIZE];$n", [getTypeDescAux(m, fieldType.elemType, check), sname]) - elif fieldType.kind in {tySequence, tyOpt}: + elif fieldType.kind == tySequence and m.config.selectedGC != gcDestructors: # we need to use a weak dependency here for trecursive_table. addf(result, "$1 $2;$n", [getTypeDescWeak(m, field.loc.t, check), sname]) elif field.bitsize != 0: @@ -601,6 +610,15 @@ proc resolveStarsInCppType(typ: PType, idx, stars: int): PType = result = if result.kind == tyGenericInst: result.sons[1] else: result.elemType +proc getSeqPayloadType(m: BModule; t: PType): Rope = + result = getTypeForward(m, t, hashType(t)) & "_Content" + when false: + var check = initIntSet() + # XXX remove this duplication: + appcg(m, m.s[cfsSeqTypes], + "struct $2_Content { NI cap; void* allocator; $1 data[SEQ_DECL_SIZE]; };$n", + [getTypeDescAux(m, t.sons[0], check), result]) + proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = # returns only the type's name var t = origTyp.skipTypes(irrelevantForBackend) @@ -641,7 +659,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = of tySequence: # no restriction! We have a forward declaration for structs let name = getTypeForward(m, et, hashType et) - result = name & "*" & star + result = name & seqStar(m) & star m.typeCache[sig] = result pushType(m, et) else: @@ -705,20 +723,29 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = [structOrUnion(t), result]) m.forwTypeCache[sig] = result assert(cacheGetType(m.typeCache, sig) == nil) - m.typeCache[sig] = result & "*" + m.typeCache[sig] = result & seqStar(m) if not isImportedType(t): if skipTypes(t.sons[0], typedescInst).kind != tyEmpty: const cppSeq = "struct $2 : #TGenericSeq {$n" cSeq = "struct $2 {$n" & " #TGenericSeq Sup;$n" - appcg(m, m.s[cfsSeqTypes], - (if m.compileToCpp: cppSeq else: cSeq) & - " $1 data[SEQ_DECL_SIZE];$n" & + if m.config.selectedGC == gcDestructors: + appcg(m, m.s[cfsTypes], + "typedef struct{ NI cap;void* allocator;$1 data[SEQ_DECL_SIZE];}$2_Content;$n" & + "struct $2 {$n" & + " NI len; $2_Content* p;$n" & "};$n", [getTypeDescAux(m, t.sons[0], check), result]) + else: + appcg(m, m.s[cfsSeqTypes], + (if m.compileToCpp: cppSeq else: cSeq) & + " $1 data[SEQ_DECL_SIZE];$n" & + "};$n", [getTypeDescAux(m, t.sons[0], check), result]) + elif m.config.selectedGC == gcDestructors: + internalError(m.config, "cannot map the empty seq type to a C type") else: result = rope("TGenericSeq") - add(result, "*") + add(result, seqStar(m)) of tyArray: var n: BiggestInt = lengthOrd(m.config, t) if n <= 0: n = 1 # make an array of at least one element @@ -1177,7 +1204,13 @@ proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope = else: let x = fakeClosureType(m, t.owner) genTupleInfo(m, x, x, result, info) - of tySequence, tyRef, tyOptAsRef: + of tySequence: + if tfHasAsgn notin t.flags: + genTypeInfoAux(m, t, t, result, info) + if m.config.selectedGC >= gcMarkAndSweep: + let markerProc = genTraverseProc(m, origType, sig) + addf(m.s[cfsTypeInit3], "$1.marker = $2;$n", [result, markerProc]) + of tyRef, tyOptAsRef: genTypeInfoAux(m, t, t, result, info) if m.config.selectedGC >= gcMarkAndSweep: let markerProc = genTraverseProc(m, origType, sig) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 01a930de65..dea8b1e8af 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -230,22 +230,31 @@ proc getTempName(m: BModule): Rope = result = m.tmpBase & rope(m.labels) inc m.labels +proc rdLoc(a: TLoc): Rope = + # 'read' location (deref if indirect) + result = a.r + if lfIndirect in a.flags: result = "(*$1)" % [result] + proc lenField(p: BProc): Rope = result = rope(if p.module.compileToCpp: "len" else: "Sup.len") +proc lenExpr(p: BProc; a: TLoc): Rope = + if p.config.selectedGc == gcDestructors: + result = rdLoc(a) & ".len" + else: + result = "($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p)] + proc dataField(p: BProc): Rope = - result = rope"->data" + if p.config.selectedGc == gcDestructors: + result = rope".p->data" + else: + result = rope"->data" include ccgliterals include ccgtypes # ------------------------------ Manager of temporaries ------------------ -proc rdLoc(a: TLoc): Rope = - # 'read' location (deref if indirect) - result = a.r - if lfIndirect in a.flags: result = "(*$1)" % [result] - proc addrLoc(conf: ConfigRef; a: TLoc): Rope = result = a.r if lfIndirect notin a.flags and mapType(conf, a.t) != ctArray: @@ -325,7 +334,9 @@ proc resetLoc(p: BProc, loc: var TLoc) = proc constructLoc(p: BProc, loc: TLoc, isTemp = false) = let typ = loc.t - if not isComplexValueType(typ): + if p.config.selectedGc == gcDestructors and skipTypes(typ, abstractInst).kind in {tyString, tySequence}: + linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", rdLoc(loc)) + elif not isComplexValueType(typ): linefmt(p, cpsStmts, "$1 = ($2)0;$n", rdLoc(loc), getTypeDesc(p.module, typ)) else: @@ -694,9 +705,10 @@ proc containsResult(n: PNode): bool = for i in 0.. 0: + result = allPathsAsgnResult(n[0]) + of nkIfStmt, nkIfExpr: + var exhaustive = false + result = InitSkippable + for it in n: + # Every condition must not use 'result': + if it.len == 2 and containsResult(it[0]): + return InitRequired + if it.len == 1: exhaustive = true + allPathsInBranch(it.lastSon) + # if the 'if' statement is not exhaustive and yet it touched 'result' + # in some way, say Unknown. + if not exhaustive: result = Unknown + of nkCaseStmt: + if containsResult(n[0]): return InitRequired + result = InitSkippable + var exhaustive = skipTypes(n[0].typ, + abstractVarRange-{tyTypeDesc}).kind notin {tyFloat..tyFloat128, tyString} + for i in 1.. 0: p.dir else: getCurrentDir() + conf.projectPath = canonicalizePath(conf, dir) + conf.projectName = p.name + else: + conf.projectPath = canonicalizePath(conf, getCurrentDir()) + +proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: ConfigRef): bool = + loadConfigs(DefaultConfig, cache, conf) # load all config files + if self.suggestMode: + conf.command = "nimsuggest" + + proc runNimScriptIfExists(path: string)= + if fileExists(path): + runNimScript(cache, path, freshDefines = false, conf) + + # Caution: make sure this stays in sync with `loadConfigs` + if optSkipSystemConfigFile notin conf.globalOptions: + runNimScriptIfExists(getSystemConfigPath(conf, DefaultConfigNims)) + + if optSkipUserConfigFile notin conf.globalOptions: + runNimScriptIfExists(getUserConfigPath(DefaultConfigNims)) + + if optSkipParentConfigFiles notin conf.globalOptions: + for dir in parentDirs(conf.projectPath, fromRoot = true, inclusive = false): + runNimScriptIfExists(dir / DefaultConfigNims) + + if optSkipProjConfigFile notin conf.globalOptions: + runNimScriptIfExists(conf.projectPath / DefaultConfigNims) + block: + let scriptFile = conf.projectFull.changeFileExt("nims") + if not self.suggestMode: + runNimScriptIfExists(scriptFile) + # 'nim foo.nims' means to just run the NimScript file and do nothing more: + if fileExists(scriptFile) and scriptFile.cmpPaths(conf.projectFull) == 0: + return false + else: + if scriptFile.cmpPaths(conf.projectFull) != 0: + runNimScriptIfExists(scriptFile) + else: + # 'nimsuggest foo.nims' means to just auto-complete the NimScript file + discard + + # now process command line arguments again, because some options in the + # command line can overwite the config file's settings + extccomp.initVars(conf) + self.processCmdLine(passCmd2, "", conf) + if conf.command == "": + rawMessage(conf, errGenerated, "command missing") + + let graph = newModuleGraph(cache, conf) + graph.suggestMode = self.suggestMode + self.mainCommand(graph) + return true diff --git a/compiler/commands.nim b/compiler/commands.nim index 1e5384f167..b47ccf610e 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -131,7 +131,10 @@ proc splitSwitch(conf: ConfigRef; switch: string, cmd, arg: var string, pass: TC else: break inc(i) if i >= len(switch): arg = "" - elif switch[i] in {':', '=', '['}: arg = substr(switch, i + 1) + # cmd:arg => (cmd,arg) + elif switch[i] in {':', '='}: arg = substr(switch, i + 1) + # cmd[sub]:rest => (cmd,[sub]:rest) + elif switch[i] == '[': arg = substr(switch, i) else: invalidCmdLineOption(conf, pass, switch, info) proc processOnOffSwitch(conf: ConfigRef; op: TOptions, arg: string, pass: TCmdLinePass, @@ -167,14 +170,20 @@ proc expectNoArg(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass, info: TLineInfo; orig: string; conf: ConfigRef) = - var id = "" # arg = "X]:on|off" + var id = "" # arg = key:val or [key]:val; with val=on|off var i = 0 var n = hintMin - while i < len(arg) and (arg[i] != ']'): + var isBracket = false + if i < len(arg) and arg[i] == '[': + isBracket = true + inc(i) + while i < len(arg) and (arg[i] notin {':', '=', ']'}): add(id, arg[i]) inc(i) - if i < len(arg) and (arg[i] == ']'): inc(i) - else: invalidCmdLineOption(conf, pass, orig, info) + if isBracket: + if i < len(arg) and arg[i] == ']': inc(i) + else: invalidCmdLineOption(conf, pass, orig, info) + if i < len(arg) and (arg[i] in {':', '='}): inc(i) else: invalidCmdLineOption(conf, pass, orig, info) if state == wHint: @@ -215,7 +224,8 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo of "refc": result = conf.selectedGC == gcRefc of "v2": result = conf.selectedGC == gcV2 of "markandsweep": result = conf.selectedGC == gcMarkAndSweep - of "generational": result = conf.selectedGC == gcGenerational + of "generational": result = false + of "destructors": result = conf.selectedGC == gcDestructors of "go": result = conf.selectedGC == gcGo of "none": result = conf.selectedGC == gcNone of "stack", "regions": result = conf.selectedGC == gcRegions @@ -436,9 +446,9 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "markandsweep": conf.selectedGC = gcMarkAndSweep defineSymbol(conf.symbols, "gcmarkandsweep") - of "generational": - conf.selectedGC = gcGenerational - defineSymbol(conf.symbols, "gcgenerational") + of "destructors": + conf.selectedGC = gcDestructors + defineSymbol(conf.symbols, "gcdestructors") of "go": conf.selectedGC = gcGo defineSymbol(conf.symbols, "gogc") @@ -641,7 +651,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; else: localError(conf, info, "invalid option for --symbolFiles: " & arg) of "skipcfg": expectNoArg(conf, switch, arg, pass, info) - incl(conf.globalOptions, optSkipConfigFile) + incl(conf.globalOptions, optSkipSystemConfigFile) of "skipprojcfg": expectNoArg(conf, switch, arg, pass, info) incl(conf.globalOptions, optSkipProjConfigFile) @@ -742,11 +752,11 @@ proc processCommand*(switch: string, pass: TCmdLinePass; config: ConfigRef) = proc processSwitch*(pass: TCmdLinePass; p: OptParser; config: ConfigRef) = # hint[X]:off is parsed as (p.key = "hint[X]", p.val = "off") - # we fix this here + # we transform it to (key = hint, val = [X]:off) var bracketLe = strutils.find(p.key, '[') if bracketLe >= 0: var key = substr(p.key, 0, bracketLe - 1) - var val = substr(p.key, bracketLe + 1) & ':' & p.val + var val = substr(p.key, bracketLe) & ':' & p.val processSwitch(key, val, pass, gCmdLineInfo, config) else: processSwitch(p.key, p.val, pass, gCmdLineInfo, config) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index ba1c42a741..a22b613f04 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -13,6 +13,7 @@ import strtabs, platform, strutils, idents from options import Feature +from lineinfos import HintsToStr, WarningsToStr const catNone = "false" @@ -74,6 +75,8 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimNoZeroTerminator") defineSymbol("nimNotNil") defineSymbol("nimVmExportFixed") + defineSymbol("nimHasSymOwnerInMacro") + defineSymbol("nimNewRuntime") defineSymbol("nimIncrSeqV3") defineSymbol("nimAshr") defineSymbol("nimNoNilSeqs") @@ -82,3 +85,8 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasNilSeqs") for f in low(Feature)..high(Feature): defineSymbol("nimHas" & $f) + + for s in WarningsToStr: + defineSymbol("nimHasWarning" & s) + for s in HintsToStr: + defineSymbol("nimHasHint" & s) diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim index 0395728c28..bd735560a4 100644 --- a/compiler/destroyer.nim +++ b/compiler/destroyer.nim @@ -100,12 +100,12 @@ Rule Pattern Transformed into finally: `=destroy`(x) 1.2 var x: sink T; stmts var x: sink T; stmts; ensureEmpty(x) 2 x = f() `=sink`(x, f()) -3 x = lastReadOf z `=sink`(x, z) +3 x = lastReadOf z `=sink`(x, z); wasMoved(z) 4.1 y = sinkParam `=sink`(y, sinkParam) 4.2 x = y `=`(x, y) # a copy 5.1 f_sink(g()) f_sink(g()) 5.2 f_sink(y) f_sink(copy y); # copy unless we can see it's the last read -5.3 f_sink(move y) f_sink(y); reset(y) # explicit moves empties 'y' +5.3 f_sink(move y) f_sink(y); wasMoved(y) # explicit moves empties 'y' 5.4 f_noSink(g()) var tmp = bitwiseCopy(g()); f(tmp); `=destroy`(tmp) Remarks: Rule 1.2 is not yet implemented because ``sink`` is currently @@ -258,8 +258,10 @@ proc registerDropBit(c: var Con; s: PSym) = c.toDropBit[s.id] = result # generate: # if not sinkParam_AliveBit: `=destroy`(sinkParam) - c.destroys.add newTree(nkIfStmt, - newTree(nkElifBranch, newSymNode result, genDestroy(c, s.typ, newSymNode s))) + let t = s.typ.skipTypes({tyGenericInst, tyAlias, tySink}) + if t.destructor != nil: + c.destroys.add newTree(nkIfStmt, + newTree(nkElifBranch, newSymNode result, genDestroy(c, t, newSymNode s))) proc p(n: PNode; c: var Con): PNode @@ -282,6 +284,11 @@ proc destructiveMoveSink(n: PNode; c: var Con): PNode = newIntTypeNode(nkIntLit, 0, getSysType(c.graph, n.info, tyBool))) result.add n +proc genMagicCall(n: PNode; c: var Con; magicname: string; m: TMagic): PNode = + result = newNodeI(nkCall, n.info) + result.add(newSymNode(createMagic(c.graph, magicname, m))) + result.add n + proc moveOrCopy(dest, ri: PNode; c: var Con): PNode = if ri.kind in constrExprs: result = genSink(c, ri.typ, dest) @@ -290,8 +297,10 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode = recurse(ri, ri2) result.add ri2 elif ri.kind == nkSym and isHarmlessVar(ri.sym, c): - result = genSink(c, ri.typ, dest) - result.add p(ri, c) + # Rule 3: `=sink`(x, z); wasMoved(z) + var snk = genSink(c, ri.typ, dest) + snk.add p(ri, c) + result = newTree(nkStmtList, snk, genMagicCall(ri, c, "wasMoved", mWasMoved)) elif ri.kind == nkSym and isSinkParam(ri.sym): result = genSink(c, ri.typ, dest) result.add destructiveMoveSink(ri, c) @@ -313,11 +322,9 @@ proc passCopyToSink(n: PNode; c: var Con): PNode = result.add newTree(nkAsgn, tmp, p(n, c)) result.add tmp -proc genReset(n: PNode; c: var Con): PNode = - result = newNodeI(nkCall, n.info) - result.add(newSymNode(createMagic(c.graph, "reset", mReset))) - # The mReset builtin does not take the address: - result.add n +proc genWasMoved(n: PNode; c: var Con): PNode = + # The mWasMoved builtin does not take the address. + result = genMagicCall(n, c, "wasMoved", mWasMoved) proc destructiveMoveVar(n: PNode; c: var Con): PNode = # generate: (let tmp = v; reset(v); tmp) @@ -334,7 +341,7 @@ proc destructiveMoveVar(n: PNode; c: var Con): PNode = add(v, vpart) result.add v - result.add genReset(n, c) + result.add genWasMoved(n, c) result.add tempAsNode proc p(n: PNode; c: var Con): PNode = diff --git a/compiler/docgen.nim b/compiler/docgen.nim index b35452365b..23d156e05a 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -14,7 +14,7 @@ import ast, strutils, strtabs, options, msgs, os, ropes, idents, wordrecg, syntaxes, renderer, lexer, packages/docutils/rstast, - packages/docutils/rst, packages/docutils/rstgen, times, + packages/docutils/rst, packages/docutils/rstgen, packages/docutils/highlite, sempass2, json, xmltree, cgi, typesrenderer, astalgo, modulepaths, lineinfos, sequtils @@ -31,6 +31,7 @@ type isPureRst: bool conf*: ConfigRef cache*: IdentCache + exampleCounter: int PDoc* = ref TDocumentor ## Alias to type less. @@ -284,11 +285,56 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRe dispA(d.conf, result, "$1", "\\spanOther{$1}", [rope(esc(d.target, literal))]) +proc testExample(d: PDoc; ex: PNode) = + if d.conf.errorCounter > 0: return + let outputDir = d.conf.getNimcacheDir / "runnableExamples" + createDir(outputDir) + inc d.exampleCounter + let outp = outputDir / extractFilename(d.filename.changeFileExt"" & + "_examples" & $d.exampleCounter & ".nim") + #let nimcache = outp.changeFileExt"" & "_nimcache" + renderModule(ex, d.filename, outp, conf = d.conf) + let backend = if isDefined(d.conf, "js"): "js" + elif isDefined(d.conf, "cpp"): "cpp" + elif isDefined(d.conf, "objc"): "objc" + else: "c" + if os.execShellCmd(os.getAppFilename() & " " & backend & + " --nimcache:" & outputDir & " -r " & outp) != 0: + quit "[Examples] failed: see " & outp + else: + # keep generated source file `outp` to allow inspection. + rawMessage(d.conf, hintSuccess, ["runnableExamples: " & outp]) + removeFile(outp.changeFileExt(ExeExt)) + +proc extractImports(n: PNode; result: PNode) = + if n.kind in {nkImportStmt, nkImportExceptStmt, nkFromStmt}: + result.add copyTree(n) + n.kind = nkEmpty + return + for i in 0..= 2 and n.lastSon.kind == nkStmtList: + prepareExamples(d, n) dispA(d.conf, dest, "\n

$1

\n", "\n\\textbf{$1}\n", [rope"Examples:"]) inc d.listingCounter @@ -627,6 +673,10 @@ proc generateDoc*(d: PDoc, n: PNode) = of nkImportStmt: for i in 0 .. sonsLen(n)-1: traceDeps(d, n.sons[i]) of nkFromStmt, nkImportExceptStmt: traceDeps(d, n.sons[0]) + of nkCallKinds: + var comm: Rope = nil + getAllRunnableExamples(d, n, comm) + if comm > nil: add(d.modDesc, comm) else: discard proc add(d: PDoc; j: JsonNode) = @@ -787,14 +837,13 @@ proc getOutFile2(conf: ConfigRef; filename, ext, dir: string): string = proc writeOutput*(d: PDoc, filename, outExt: string, useWarning = false) = var content = genOutFile(d) - var success = true if optStdout in d.conf.globalOptions: writeRope(stdout, content) else: let outfile = getOutFile2(d.conf, filename, outExt, "htmldocs") - success = writeRope(content, outfile) - if not success: - rawMessage(d.conf, if useWarning: warnCannotOpenFile else: errCannotOpenFile, filename) + createDir(outfile.parentDir) + if not writeRope(content, outfile): + rawMessage(d.conf, if useWarning: warnCannotOpenFile else: errCannotOpenFile, outfile) proc writeOutputJson*(d: PDoc, filename, outExt: string, useWarning = false) = diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim index 068c47bb3c..22fef0d471 100644 --- a/compiler/docgen2.nim +++ b/compiler/docgen2.nim @@ -21,12 +21,15 @@ type module: PSym PGen = ref TGen +template shouldProcess(g): bool = + (g.module.owner.id == g.doc.conf.mainPackageId and optWholeProject in g.doc.conf.globalOptions) or + sfMainModule in g.module.flags + 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 == g.doc.conf.mainPackageId and optWholeProject in g.doc.conf.globalOptions) or - sfMainModule in g.module.flags: + if shouldProcess(g): body try: generateIndex(g.doc) @@ -35,27 +38,29 @@ template closeImpl(body: untyped) {.dirty.} = proc close(graph: ModuleGraph; p: PPassContext, n: PNode): PNode = closeImpl: - writeOutput(g.doc, toFilename(graph.config, FileIndex g.module.position), HtmlExt, useWarning) + writeOutput(g.doc, toFullPath(graph.config, FileIndex g.module.position), HtmlExt, useWarning) proc closeJson(graph: ModuleGraph; p: PPassContext, n: PNode): PNode = closeImpl: - writeOutputJson(g.doc, toFilename(graph.config, FileIndex g.module.position), ".json", useWarning) + writeOutputJson(g.doc, toFullPath(graph.config, FileIndex g.module.position), ".json", useWarning) proc processNode(c: PPassContext, n: PNode): PNode = result = n var g = PGen(c) - generateDoc(g.doc, n) + if shouldProcess(g): + generateDoc(g.doc, n) proc processNodeJson(c: PPassContext, n: PNode): PNode = result = n var g = PGen(c) - generateJson(g.doc, n) + if shouldProcess(g): + generateJson(g.doc, n) proc myOpen(graph: ModuleGraph; module: PSym): PPassContext = var g: PGen new(g) g.module = module - var d = newDocumentor(toFilename(graph.config, FileIndex module.position), graph.cache, graph.config) + var d = newDocumentor(toFullPath(graph.config, FileIndex module.position), graph.cache, graph.config) d.hasToc = true g.doc = d result = g diff --git a/compiler/importer.nim b/compiler/importer.nim index c013b93ab1..73d2e65991 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -24,9 +24,15 @@ proc readExceptSet*(c: PContext, n: PNode): IntSet = result.incl(ident.id) proc importPureEnumField*(c: PContext; s: PSym) = - var check = strTableGet(c.importTable.symbols, s.name) + let check = strTableGet(c.importTable.symbols, s.name) if check == nil: - strTableAdd(c.pureEnumFields, s) + let checkB = strTableGet(c.pureEnumFields, s.name) + if checkB == nil: + strTableAdd(c.pureEnumFields, s) + else: + # mark as ambigous: + incl(c.ambiguousSymbols, checkB.id) + incl(c.ambiguousSymbols, s.id) proc rawImportSymbol(c: PContext, s: PSym) = # This does not handle stubs, because otherwise loading on demand would be @@ -164,7 +170,16 @@ proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym = suggestSym(c.config, n.info, result, c.graph.usageSym, false) importStmtResult.add newStrNode(toFullPath(c.config, f), n.info) +proc transformImportAs(c: PContext; n: PNode): PNode = + if n.kind == nkInfix and considerQuotedIdent(c, n[0]).s == "as": + result = newNodeI(nkImportAs, n.info) + result.add n.sons[1] + result.add n.sons[2] + else: + result = n + proc impMod(c: PContext; it: PNode; importStmtResult: PNode) = + let it = transformImportAs(c, it) let m = myImportModule(c, it, importStmtResult) if m != nil: var emptySet: IntSet @@ -174,26 +189,33 @@ proc impMod(c: PContext; it: PNode; importStmtResult: PNode) = #importForwarded(c, m.ast, emptySet) proc evalImport(c: PContext, n: PNode): PNode = - #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: let sep = it[0] let dir = it[1] - let a = newNodeI(nkInfix, it.info) - a.add sep - a.add dir - a.add sep # dummy entry, replaced in the loop + var imp = newNodeI(nkInfix, it.info) + imp.add sep + imp.add dir + imp.add sep # dummy entry, replaced in the loop for x in it[2]: - a.sons[2] = x - impMod(c, a, result) + # transform `a/b/[c as d]` to `/a/b/c as d` + if x.kind == nkInfix and x.sons[0].ident.s == "as": + let impAs = copyTree(x) + imp.sons[2] = x.sons[1] + impAs.sons[1] = imp + impMod(c, imp, result) + else: + imp.sons[2] = x + impMod(c, imp, result) else: impMod(c, it, result) proc evalFrom(c: PContext, n: PNode): PNode = result = newNodeI(nkImportStmt, n.info) checkMinSonsLen(n, 2, c.config) + n.sons[0] = transformImportAs(c, n.sons[0]) var m = myImportModule(c, n.sons[0], result) if m != nil: n.sons[0] = newSymNode(m) @@ -205,6 +227,7 @@ proc evalFrom(c: PContext, n: PNode): PNode = proc evalImportExcept*(c: PContext, n: PNode): PNode = result = newNodeI(nkImportStmt, n.info) checkMinSonsLen(n, 2, c.config) + n.sons[0] = transformImportAs(c, n.sons[0]) var m = myImportModule(c, n.sons[0], result) if m != nil: n.sons[0] = newSymNode(m) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 462c622aaa..1b00ddbfa5 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -243,7 +243,8 @@ proc mangleName(m: BModule, s: PSym): Rope = x.add("HEX" & toHex(ord(c), 2)) inc i result = rope(x) - if s.name.s != "this" and s.kind != skField: + # From ES5 on reserved words can be used as object field names + if s.kind != skField: if optHotCodeReloading in m.config.options: # When hot reloading is enabled, we must ensure that the names # of functions and types will be preserved across rebuilds: @@ -271,9 +272,7 @@ proc escapeJSString(s: string): string = result.add("\"") proc makeJSString(s: string, escapeNonAscii = true): Rope = - if s.isNil: - result = "null".rope - elif escapeNonAscii: + if escapeNonAscii: result = strutils.escape(s).rope else: result = escapeJSString(s).rope @@ -1369,7 +1368,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = let length = int(lengthOrd(p.config, t)) let e = elemType(t) let jsTyp = arrayTypeForElemType(e) - if not jsTyp.isNil: + if jsTyp.len > 0: result = "new $1($2)" % [rope(jsTyp), rope(length)] elif length > 32: useMagic(p, "arrayConstr") diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index c5a6417138..41f3806d42 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -223,6 +223,9 @@ type proc `==`*(a, b: FileIndex): bool {.borrow.} +proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} = + raise newException(ERecoverableError, msg) + const InvalidFileIDX* = FileIndex(-1) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 1b5bee0081..ec9c130e31 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -244,7 +244,7 @@ else: template fixSpelling(n: PNode; ident: PIdent; op: untyped) = discard proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) = - var err = "Error: ambiguous identifier: '" & s.name.s & "'" + var err = "ambiguous identifier: '" & s.name.s & "'" var ti: TIdentIter var candidate = initIdentIter(ti, c.importTable.symbols, s.name) var i = 0 @@ -259,7 +259,7 @@ proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) = proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string) = var err = "undeclared identifier: '" & name & "'" if c.recursiveDep.len > 0: - err.add "\nThis might be caused by a recursive module dependency: " + err.add "\nThis might be caused by a recursive module dependency:\n" err.add c.recursiveDep # prevent excessive errors for 'nim check' c.recursiveDep = "" diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim index d40b9d732f..aeeb489c08 100644 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -120,7 +120,8 @@ proc skipIntLit*(t: PType): PType {.inline.} = result = t proc addSonSkipIntLit*(father, son: PType) = - if isNil(father.sons): father.sons = @[] + when not defined(nimNoNilSeqs): + if isNil(father.sons): father.sons = @[] let s = son.skipIntLit add(father.sons, s) propagateToOwner(father, s) diff --git a/compiler/modulepaths.nim b/compiler/modulepaths.nim index e5cbf3a2c7..118002fcff 100644 --- a/compiler/modulepaths.nim +++ b/compiler/modulepaths.nim @@ -126,13 +126,6 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string = of nkInfix: let n0 = n[0] let n1 = n[1] - if n0.kind == nkIdent and n0.ident.s == "as": - # XXX hack ahead: - n.kind = nkImportAs - n.sons[0] = n.sons[1] - n.sons[1] = n.sons[2] - n.sons.setLen(2) - return getModuleName(conf, n.sons[0]) when false: if n1.kind == nkPrefix and n1[0].kind == nkIdent and n1[0].ident.s == "$": if n0.kind == nkIdent and n0.ident.s == "/": diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 1d79391420..b7b7c84746 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -102,9 +102,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) -proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} = - raise newException(ERecoverableError, msg) - proc concat(strings: openarray[string]): string = var totalLen = 0 @@ -176,7 +173,7 @@ proc getHash*(conf: ConfigRef; fileIdx: FileIndex): string = proc toFullPathConsiderDirty*(conf: ConfigRef; fileIdx: FileIndex): string = if fileIdx.int32 < 0: result = "???" - elif not conf.m.fileInfos[fileIdx.int32].dirtyFile.isNil: + elif conf.m.fileInfos[fileIdx.int32].dirtyFile.len > 0: result = conf.m.fileInfos[fileIdx.int32].dirtyFile else: result = conf.m.fileInfos[fileIdx.int32].fullPath diff --git a/compiler/nim.nim b/compiler/nim.nim index 90049bdfbf..0fed72dc74 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, - nodejs, scriptconfig, idents, modulegraphs, lineinfos + nodejs, scriptconfig, idents, modulegraphs, lineinfos, cmdlinehelper when hasTinyCBackend: import tccgen @@ -57,69 +57,43 @@ proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) = rawMessage(config, errGenerated, errArgsNeedRunOption) proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = - condsyms.initDefines(conf.symbols) + let self = NimProg( + supportsStdinFile: true, + processCmdLine: processCmdLine, + mainCommand: mainCommand + ) + self.initDefinesProg(conf, "nim_compiler") if paramCount() == 0: writeCommandLineUsage(conf, conf.helpWritten) - else: - # Process command line arguments: - processCmdLine(passCmd1, "", conf) - if conf.projectName == "-": - conf.projectName = "stdinfile" - conf.projectFull = "stdinfile" - conf.projectPath = canonicalizePath(conf, getCurrentDir()) - conf.projectIsStdin = true - elif conf.projectName != "": - try: - conf.projectFull = canonicalizePath(conf, conf.projectName) - except OSError: - conf.projectFull = conf.projectName - let p = splitFile(conf.projectFull) - let dir = if p.dir.len > 0: p.dir else: getCurrentDir() - conf.projectPath = canonicalizePath(conf, dir) - conf.projectName = p.name + return + + self.processCmdLineAndProjectPath(conf) + if not self.loadConfigsAndRunMainCommand(cache, conf): return + if optHints in conf.options and hintGCStats in conf.notes: echo(GC_getStatistics()) + #echo(GC_getStatistics()) + if conf.errorCounter != 0: return + when hasTinyCBackend: + if conf.cmd == cmdRun: + tccgen.run(conf.arguments) + if optRun in conf.globalOptions: + if conf.cmd == cmdCompileToJS: + var ex: string + if conf.outFile.len > 0: + ex = conf.outFile.prependCurDir.quoteShell + else: + ex = quoteShell( + completeCFilePath(conf, changeFileExt(conf.projectFull, "js").prependCurDir)) + execExternalProgram(conf, findNodeJs() & " " & ex & ' ' & conf.arguments) else: - conf.projectPath = canonicalizePath(conf, getCurrentDir()) - loadConfigs(DefaultConfig, cache, conf) # load all config files - let scriptFile = conf.projectFull.changeFileExt("nims") - if fileExists(scriptFile): - runNimScript(cache, scriptFile, freshDefines=false, conf) - # 'nim foo.nims' means to just run the NimScript file and do nothing more: - if scriptFile == conf.projectFull: return - elif fileExists(conf.projectPath / "config.nims"): - # directory wide NimScript file - runNimScript(cache, conf.projectPath / "config.nims", freshDefines=false, conf) - # now process command line arguments again, because some options in the - # command line can overwite the config file's settings - extccomp.initVars(conf) - processCmdLine(passCmd2, "", conf) - if conf.command == "": - rawMessage(conf, errGenerated, "command missing") - mainCommand(newModuleGraph(cache, conf)) - if optHints in conf.options and hintGCStats in conf.notes: echo(GC_getStatistics()) - #echo(GC_getStatistics()) - if conf.errorCounter == 0: - when hasTinyCBackend: - if conf.cmd == cmdRun: - tccgen.run(conf.arguments) - if optRun in conf.globalOptions: - if conf.cmd == cmdCompileToJS: - var ex: string - if conf.outFile.len > 0: - ex = conf.outFile.prependCurDir.quoteShell - else: - ex = quoteShell( - completeCFilePath(conf, changeFileExt(conf.projectFull, "js").prependCurDir)) - execExternalProgram(conf, findNodeJs() & " " & ex & ' ' & conf.arguments) - else: - var binPath: string - if conf.outFile.len > 0: - # If the user specified an outFile path, use that directly. - binPath = conf.outFile.prependCurDir - else: - # Figure out ourselves a valid binary name. - binPath = changeFileExt(conf.projectFull, ExeExt).prependCurDir - var ex = quoteShell(binPath) - execExternalProgram(conf, ex & ' ' & conf.arguments) + var binPath: string + if conf.outFile.len > 0: + # If the user specified an outFile path, use that directly. + binPath = conf.outFile.prependCurDir + else: + # Figure out ourselves a valid binary name. + binPath = changeFileExt(conf.projectFull, ExeExt).prependCurDir + var ex = quoteShell(binPath) + execExternalProgram(conf, ex & ' ' & conf.arguments) when declared(GC_setMaxPause): GC_setMaxPause 2_000 diff --git a/compiler/nimblecmd.nim b/compiler/nimblecmd.nim index 9a23535bf5..c5521735b1 100644 --- a/compiler/nimblecmd.nim +++ b/compiler/nimblecmd.nim @@ -140,18 +140,19 @@ when isMainModule: doAssert v"#aaaqwe" < v"1.1" # We cannot assume that a branch is newer. doAssert v"#a111" < v"#head" + let conf = newConfigRef() var rr = newStringTable() - addPackage rr, "irc-#a111" - addPackage rr, "irc-#head" - addPackage rr, "irc-0.1.0" - addPackage rr, "irc" - addPackage rr, "another" - addPackage rr, "another-0.1" + addPackage conf, rr, "irc-#a111", unknownLineInfo() + addPackage conf, rr, "irc-#head", unknownLineInfo() + addPackage conf, rr, "irc-0.1.0", unknownLineInfo() + #addPackage conf, rr, "irc", unknownLineInfo() + #addPackage conf, rr, "another", unknownLineInfo() + addPackage conf, rr, "another-0.1", unknownLineInfo() - addPackage rr, "ab-0.1.3" - addPackage rr, "ab-0.1" - addPackage rr, "justone" + addPackage conf, rr, "ab-0.1.3", unknownLineInfo() + addPackage conf, rr, "ab-0.1", unknownLineInfo() + addPackage conf, rr, "justone-1.0", unknownLineInfo() doAssert toSeq(rr.chosen) == - @["irc-#head", "another-0.1", "ab-0.1.3", "justone"] + @["irc-#head", "another-0.1", "ab-0.1.3", "justone-1.0"] diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index 1a8a0acb5b..5f6889a6fb 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -175,9 +175,9 @@ proc parseAssignment(L: var TLexer, tok: var TToken; confTok(L, tok, config, condStack) if tok.tokType == tkBracketLe: # BUGFIX: val, not s! - # BUGFIX: do not copy '['! confTok(L, tok, config, condStack) checkSymbol(L, tok) + add(val, '[') add(val, tokToStr(tok)) confTok(L, tok, config, condStack) if tok.tokType == tkBracketRi: confTok(L, tok, config, condStack) @@ -219,10 +219,10 @@ proc readConfigFile( closeLexer(L) return true -proc getUserConfigPath(filename: string): string = +proc getUserConfigPath*(filename: string): string = result = joinPath([getConfigDir(), "nim", filename]) -proc getSystemConfigPath(conf: ConfigRef; filename: string): string = +proc getSystemConfigPath*(conf: ConfigRef; filename: string): string = # try standard configuration file (installation did not distribute files # the UNIX way) let p = getPrefixDir(conf) @@ -241,7 +241,7 @@ proc loadConfigs*(cfg: string; cache: IdentCache; conf: ConfigRef) = if readConfigFile(configPath, cache, conf): add(configFiles, configPath) - if optSkipConfigFile notin conf.globalOptions: + if optSkipSystemConfigFile notin conf.globalOptions: readConfigFile(getSystemConfigPath(conf, cfg)) if optSkipUserConfigFile notin conf.globalOptions: @@ -263,4 +263,5 @@ proc loadConfigs*(cfg: string; cache: IdentCache; conf: ConfigRef) = readConfigFile(projectConfig) for filename in configFiles: + # delayed to here so that `hintConf` is honored rawMessage(conf, hintConf, filename) diff --git a/compiler/options.nim b/compiler/options.nim index f2701dadd4..4a484e6e22 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -12,6 +12,7 @@ import prefixmatches from terminal import isatty +from times import utc, fromUnix, local, getTime, format, DateTime const hasTinyCBackend* = defined(tinyc) @@ -54,10 +55,10 @@ type # please make sure we have under 32 options optGenMapping, # generate a mapping file optRun, # run the compiled project 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 - optSkipParentConfigFiles, # skip parent dir's config files + optSkipSystemConfigFile, # skip the system's cfg/nims config file + optSkipProjConfigFile, # skip the project's cfg/nims config file + optSkipUserConfigFile, # skip the users's cfg/nims config file + optSkipParentConfigFiles, # skip parent dir's cfg/nims config files optNoMain, # do not generate a "main" proc optUseColors, # use colors for hints, warnings, and errors optThreads, # support for multi-threading @@ -104,8 +105,8 @@ type cmdJsonScript # compile a .json build file TStringSeq* = seq[string] TGCMode* = enum # the selected GC - gcNone, gcBoehm, gcGo, gcRegions, gcMarkAndSweep, gcRefc, - gcV2, gcGenerational + gcNone, gcBoehm, gcGo, gcRegions, gcMarkAndSweep, gcDestructors, + gcRefc, gcV2 IdeCmd* = enum ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod, @@ -260,6 +261,24 @@ const optPatterns, optNilCheck, optMoveCheck} DefaultGlobalOptions* = {optThreadAnalysis} +proc getSrcTimestamp(): DateTime = + try: + result = utc(fromUnix(parseInt(getEnv("SOURCE_DATE_EPOCH", + "not a number")))) + except ValueError: + # Environment variable malformed. + # https://reproducible-builds.org/specs/source-date-epoch/: "If the + # value is malformed, the build process SHOULD exit with a non-zero + # error code", which this doesn't do. This uses local time, because + # that maintains compatibility with existing usage. + result = utc getTime() + +proc getDateStr*(): string = + result = format(getSrcTimestamp(), "yyyy-MM-dd") + +proc getClockStr*(): string = + result = format(getSrcTimestamp(), "HH:mm:ss") + template newPackageCache*(): untyped = newStringTable(when FileSystemCaseSensitive: modeCaseInsensitive @@ -374,7 +393,7 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool = else: discard proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd in {cmdDoc, cmdIdeTools} -proc usesNativeGC*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc +proc usesWriteBarrier*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc template compilationCachePresent*(conf: ConfigRef): untyped = conf.symbolFiles in {v2Sf, writeOnlySf} @@ -392,6 +411,7 @@ const TexExt* = "tex" IniExt* = "ini" DefaultConfig* = "nim.cfg" + DefaultConfigNims* = "config.nims" DocConfig* = "nimdoc.cfg" DocTexConfig* = "nimdoc.tex.cfg" diff --git a/compiler/packagehandling.nim b/compiler/packagehandling.nim index 2efab58b06..7414aeb71d 100644 --- a/compiler/packagehandling.nim +++ b/compiler/packagehandling.nim @@ -33,7 +33,8 @@ proc getPackageName*(conf: ConfigRef; path: string): string = result = file.splitFile.name break packageSearch # we also store if we didn't find anything: - if result.isNil: result = "" + when not defined(nimNoNilSeqs): + if result.isNil: result = "" for d in myParentDirs(path): #echo "set cache ", d, " |", result, "|", parents conf.packageCache[d] = result diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 0ef87720d1..910ee799c5 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -71,6 +71,7 @@ const letPragmas* = varPragmas procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect, wThread, wRaises, wLocks, wTags, wGcSafe} + forVarPragmas* = {wInject, wGensym} allRoutinePragmas* = methodPragmas + iteratorPragmas + lambdaPragmas proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode = @@ -95,7 +96,9 @@ const errIntLiteralExpected = "integer literal expected" proc invalidPragma*(c: PContext; n: PNode) = - localError(c.config, n.info, "invalid pragma: " % renderTree(n, {renderNoComments})) + localError(c.config, n.info, "invalid pragma: " & renderTree(n, {renderNoComments})) +proc illegalCustomPragma*(c: PContext, n: PNode, s: PSym) = + localError(c.config, n.info, "cannot attach a custom pragma to '" & s.name.s & "'") proc pragmaAsm*(c: PContext, n: PNode): char = result = '\0' @@ -336,6 +339,20 @@ proc pragmaToOptions(w: TSpecialWord): TOptions {.inline.} = of wPatterns: {optPatterns} else: {} +proc processExperimental(c: PContext; n: PNode) = + if n.kind notin nkPragmaCallKinds or n.len != 2: + c.features.incl oldExperimentalFeatures + else: + n[1] = c.semConstExpr(c, n[1]) + case n[1].kind + of nkStrLit, nkRStrLit, nkTripleStrLit: + try: + c.features.incl parseEnum[Feature](n[1].strVal) + except ValueError: + localError(c.config, n[1].info, "unknown experimental feature") + else: + localError(c.config, n.info, errStringLiteralExpected) + proc tryProcessOption(c: PContext, n: PNode, resOptions: var TOptions): bool = result = true if n.kind notin nkPragmaCallKinds or n.len != 2: result = false @@ -343,6 +360,9 @@ proc tryProcessOption(c: PContext, n: PNode, resOptions: var TOptions): bool = elif n.sons[0].kind != nkIdent: result = false else: let sw = whichKeyword(n.sons[0].ident) + if sw == wExperimental: + processExperimental(c, n) + return true let opts = pragmaToOptions(sw) if opts != {}: onOff(c, n, opts, resOptions) @@ -381,6 +401,7 @@ proc processPush(c: PContext, n: PNode, start: int) = x.defaultCC = y.defaultCC x.dynlib = y.dynlib x.notes = c.config.notes + x.features = c.features c.optionStack.add(x) for i in countup(start, sonsLen(n) - 1): if not tryProcessOption(c, n.sons[i], c.config.options): @@ -400,6 +421,7 @@ proc processPop(c: PContext, n: PNode) = else: c.config.options = c.optionStack[^1].options c.config.notes = c.optionStack[^1].notes + c.features = c.optionStack[^1].features c.optionStack.setLen(c.optionStack.len - 1) proc processDefine(c: PContext, n: PNode) = @@ -710,9 +732,7 @@ proc semCustomPragma(c: PContext, n: PNode): PNode = elif n.kind == nkExprColonExpr: result.kind = n.kind # pragma(arg) -> pragma: arg -proc processExperimental(c: PContext; n: PNode; s: PSym) = - if not isTopLevel(c): - localError(c.config, n.info, "'experimental' pragma only valid as toplevel statement") +proc processExperimental(c: PContext; n: PNode) = if n.kind notin nkPragmaCallKinds or n.len != 2: c.features.incl oldExperimentalFeatures else: @@ -1065,7 +1085,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, else: it.sons[1] = c.semExpr(c, it.sons[1]) of wExperimental: - processExperimental(c, it, sym) + if not isTopLevel(c): + localError(c.config, n.info, "'experimental' pragma only valid as toplevel statement or in a 'push' environment") + processExperimental(c, it) of wThis: if it.kind in nkPragmaCallKinds and it.len == 2: c.selfName = considerQuotedIdent(c, it[1]) @@ -1090,9 +1112,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, else: sym.flags.incl sfUsed of wLiftLocals: discard else: invalidPragma(c, it) - else: + elif sym.kind in {skField,skProc,skFunc,skConverter,skMethod,skType}: n.sons[i] = semCustomPragma(c, it) - + else: + illegalCustomPragma(c, it, sym) proc implicitPragmas*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) = diff --git a/compiler/renderer.nim b/compiler/renderer.nim index c3e151f5a8..a8f3f4afc0 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -175,7 +175,7 @@ proc put(g: var TSrcGen, kind: TTokType, s: string) = g.pendingWhitespace = s.len proc putComment(g: var TSrcGen, s: string) = - if s.isNil: return + if s.len == 0: return var i = 0 let hi = len(s) - 1 var isCode = (len(s) >= 2) and (s[1] != ' ') @@ -216,7 +216,7 @@ proc putComment(g: var TSrcGen, s: string) = optNL(g) proc maxLineLength(s: string): int = - if s.isNil: return 0 + if s.len == 0: return 0 var i = 0 let hi = len(s) - 1 var lineLen = 0 @@ -752,7 +752,8 @@ proc gproc(g: var TSrcGen, n: PNode) = gsub(g, n.sons[genericParamsPos]) g.inGenericParams = oldInGenericParams gsub(g, n.sons[paramsPos]) - gsub(g, n.sons[pragmasPos]) + if renderNoPragmas notin g.flags: + gsub(g, n.sons[pragmasPos]) if renderNoBody notin g.flags: if n.sons[bodyPos].kind != nkEmpty: put(g, tkSpaces, Space) @@ -1297,17 +1298,16 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = putWithSpace(g, tkContinue, "continue") gsub(g, n, 0) of nkPragma: - if renderNoPragmas notin g.flags: - if g.inPragma <= 0: - inc g.inPragma - #if not previousNL(g): - put(g, tkSpaces, Space) - put(g, tkCurlyDotLe, "{.") - gcomma(g, n, emptyContext) - put(g, tkCurlyDotRi, ".}") - dec g.inPragma - else: - gcomma(g, n, emptyContext) + if g.inPragma <= 0: + inc g.inPragma + #if not previousNL(g): + put(g, tkSpaces, Space) + put(g, tkCurlyDotLe, "{.") + gcomma(g, n, emptyContext) + put(g, tkCurlyDotRi, ".}") + dec g.inPragma + else: + gcomma(g, n, emptyContext) of nkImportStmt, nkExportStmt: if n.kind == nkImportStmt: putWithSpace(g, tkImport, "import") diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim index 659206a40c..184b60733d 100644 --- a/compiler/scriptconfig.nim +++ b/compiler/scriptconfig.nim @@ -171,7 +171,7 @@ proc runNimScript*(cache: IdentCache; scriptName: string; incl(m.flags, sfMainModule) graph.vm = setupVM(m, cache, scriptName, graph) - graph.compileSystemModule() + graph.compileSystemModule() # TODO: see why this unsets hintConf in conf.notes discard graph.processModule(m, llStreamOpen(scriptName, fmRead)) # ensure we load 'system.nim' again for the real non-config stuff! diff --git a/compiler/sem.nim b/compiler/sem.nim index 6128c02d15..7a83c30799 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -607,28 +607,6 @@ proc myProcess(context: PPassContext, n: PNode): PNode = #if c.config.cmd == cmdIdeTools: findSuggest(c, n) rod.storeNode(c.graph, c.module, result) -proc testExamples(c: PContext) = - let outputDir = c.config.getNimcacheDir / "runnableExamples" - createDir(outputDir) - let inp = toFullPath(c.config, c.module.info) - let outp = outputDir / extractFilename(inp.changeFileExt"" & "_examples.nim") - let nimcache = outp.changeFileExt"" & "_nimcache" - renderModule(c.runnableExamples, inp, outp, conf = c.config) - let backend = if isDefined(c.config, "js"): "js" - elif isDefined(c.config, "cpp"): "cpp" - elif isDefined(c.config, "objc"): "objc" - else: "c" - if os.execShellCmd(os.getAppFilename() & " " & backend & " --nimcache:" & nimcache & " -r " & outp) != 0: - quit "[Examples] failed: see " & outp - else: - # keep generated source file `outp` to allow inspection. - rawMessage(c.config, hintSuccess, ["runnableExamples: " & outp]) - removeFile(outp.changeFileExt(ExeExt)) - try: - removeDir(nimcache) - except OSError: - discard - proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode = var c = PContext(context) if c.config.cmd == cmdIdeTools and not c.suggestionsMade: @@ -644,7 +622,6 @@ proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode = popOwner(c) popProcCon(c) storeRemaining(c.graph, c.module) - if c.runnableExamples != nil: testExamples(c) const semPass* = makePass(myOpen, myProcess, myClose, isFrontend = true) diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim index a05ef7a283..8b2e20efce 100644 --- a/compiler/semasgn.nim +++ b/compiler/semasgn.nim @@ -197,13 +197,10 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) = case t.kind of tyNone, tyEmpty, tyVoid: discard of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString, - tyPtr, tyString, tyRef, tyOpt: + tyPtr, tyRef, tyOpt: defaultOp(c, t, body, x, y) - of tyArray, tySequence: + of tyArray: if {tfHasAsgn, tfUncheckedArray} * t.flags == {tfHasAsgn}: - if t.kind == tySequence: - # XXX add 'nil' handling here - body.add newSeqCall(c.c, x, y) let i = declareCounter(c, body, firstOrd(c.c.config, t)) let whileLoop = genWhileLoop(c, i, x) let elemType = t.lastSon @@ -213,6 +210,27 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add whileLoop else: defaultOp(c, t, body, x, y) + of tySequence: + # note that tfHasAsgn is propagated so we need the check on + # 'selectedGC' here to determine if we have the new runtime. + if c.c.config.selectedGC == gcDestructors: + discard considerOverloadedOp(c, t, body, x, y) + elif tfHasAsgn in t.flags: + body.add newSeqCall(c.c, x, y) + 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), + y.at(i, elemType)) + addIncStmt(c, whileLoop.sons[1], i) + body.add whileLoop + else: + defaultOp(c, t, body, x, y) + of tyString: + if tfHasAsgn in t.flags: + discard considerOverloadedOp(c, t, body, x, y) + else: + defaultOp(c, t, body, x, y) of tyObject, tyDistinct: if not considerOverloadedOp(c, t, body, x, y): if t.sons[0] != nil: diff --git a/compiler/semcall.nim b/compiler/semcall.nim index dc71f2567e..53f7045dd7 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -138,7 +138,9 @@ proc effectProblem(f, a: PType; result: var string) = proc renderNotLValue(n: PNode): string = result = $n - if n.kind in {nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv} and n.len == 2: + if n.kind == nkHiddenCallConv and n.len > 1: + result = $n[0] & "(" & result & ")" + elif n.kind in {nkHiddenStdConv, nkHiddenSubConv} and n.len == 2: result = typeToString(n.typ.skipTypes(abstractVar)) & "(" & result & ")" proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): @@ -164,8 +166,20 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): prefer = preferModuleInfo break + when false: + # we pretend procs are attached to the type of the first + # argument in order to remove plenty of candidates. This is + # comparable to what C# does and C# is doing fine. + var filterOnlyFirst = false + for err in errors: + if err.firstMismatch > 1: + filterOnlyFirst = true + break + var candidates = "" for err in errors: + when false: + if filterOnlyFirst and err.firstMismatch == 1: continue if err.sym.kind in routineKinds and err.sym.ast != nil: add(candidates, renderTree(err.sym.ast, {renderNoBody, renderNoComments, renderNoPragmas})) @@ -175,15 +189,18 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): if err.firstMismatch != 0 and n.len > 1: let cond = n.len > 2 if cond: - candidates.add(" first type mismatch at position: " & $err.firstMismatch & - "\n required type: ") + candidates.add(" first type mismatch at position: " & $abs(err.firstMismatch)) + if err.firstMismatch >= 0: candidates.add("\n required type: ") + else: candidates.add("\n unknown named parameter: " & $n[-err.firstMismatch][0]) var wanted, got: PType = nil - if err.firstMismatch < err.sym.typ.len: + if err.firstMismatch < 0: + discard + elif err.firstMismatch < err.sym.typ.len: wanted = err.sym.typ.sons[err.firstMismatch] if cond: candidates.add typeToString(wanted) else: if cond: candidates.add "none" - if err.firstMismatch < n.len: + if err.firstMismatch > 0 and err.firstMismatch < n.len: if cond: candidates.add "\n but expression '" candidates.add renderTree(n[err.firstMismatch]) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 4189a52147..6d6627690c 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -22,6 +22,7 @@ type defaultCC*: TCallingConvention dynlib*: PLib notes*: TNoteKinds + features*: set[Feature] otherPragmas*: PNode # every pragma can be pushed POptionEntry* = ref TOptionEntry @@ -140,7 +141,6 @@ type # the generic type has been constructed completely. See # tests/destructor/topttree.nim for an example that # would otherwise fail. - runnableExamples*: PNode template config*(c: PContext): ConfigRef = c.graph.config diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 91bee54ac0..ce953f17c7 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -800,7 +800,9 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode = analyseIfAddressTakenInCall(c, result) if callee.magic != mNone: result = magicsAfterOverloadResolution(c, result, flags) - if result.typ != nil: liftTypeBoundOps(c, result.typ, n.info) + if result.typ != nil and + not (result.typ.kind == tySequence and result.typ.sons[0].kind == tyEmpty): + liftTypeBoundOps(c, result.typ, n.info) #result = patchResolvedTypeBoundOp(c, result) if c.matchedConcept == nil: result = evalAtCompileTime(c, result) @@ -1389,8 +1391,8 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = n.sons[1] = semConstExpr(c, n.sons[1]) if skipTypes(n.sons[1].typ, {tyGenericInst, tyRange, tyOrdinal, tyAlias, tySink}).kind in {tyInt..tyInt64}: - var idx = getOrdValue(n.sons[1]) - if idx >= 0 and idx < sonsLen(arr): n.typ = arr.sons[int(idx)] + let idx = getOrdValue(n.sons[1]) + if idx >= 0 and idx < len(arr): n.typ = arr.sons[int(idx)] else: localError(c.config, n.info, "invalid index value for tuple subscript") result = n else: @@ -1565,6 +1567,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = n.sons[1] = fitNode(c, le, rhs, n.info) liftTypeBoundOps(c, lhs.typ, lhs.info) + #liftTypeBoundOps(c, n.sons[0].typ, n.sons[0].info) fixAbstractType(c, n) asgnToResultVar(c, n, n.sons[0], n.sons[1]) @@ -1950,13 +1953,6 @@ proc setMs(n: PNode, s: PSym): PNode = n.sons[0] = newSymNode(s) n.sons[0].info = n.info -proc extractImports(n: PNode; result: PNode) = - if n.kind in {nkImportStmt, nkImportExceptStmt, nkFromStmt}: - result.add copyTree(n) - n.kind = nkEmpty - return - for i in 0..= 2 and n.lastSon.kind == nkStmtList: - if sfMainModule in c.module.flags: - let inp = toFullPath(c.config, c.module.info) - if c.runnableExamples == nil: - c.runnableExamples = newTree(nkStmtList, - newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp)))) - let imports = newTree(nkStmtList) - var saved_lastSon = copyTree n.lastSon - extractImports(saved_lastSon, imports) - for imp in imports: c.runnableExamples.add imp - c.runnableExamples.add newTree(nkBlockStmt, c.graph.emptyNode, copyTree saved_lastSon) + when false: + if sfMainModule in c.module.flags: + let inp = toFullPath(c.config, c.module.info) + if c.runnableExamples == nil: + c.runnableExamples = newTree(nkStmtList, + newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp)))) + let imports = newTree(nkStmtList) + var savedLastSon = copyTree n.lastSon + extractImports(savedLastSon, imports) + for imp in imports: c.runnableExamples.add imp + c.runnableExamples.add newTree(nkBlockStmt, c.graph.emptyNode, copyTree savedLastSon) result = setMs(n, s) else: result = c.graph.emptyNode @@ -2082,7 +2079,7 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = typ = commonType(typ, it.sons[1].typ) result = n # when nimvm is not elimited until codegen else: - var e = semConstExpr(c, it.sons[0]) + let e = forceBool(c, semConstExpr(c, it.sons[0])) if e.kind != nkIntLit: # can happen for cascading errors, assume false # InternalError(n.info, "semWhen") diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 4449401440..27a6af1f47 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -11,7 +11,7 @@ # and evaluation phase import - strutils, options, ast, astalgo, trees, treetab, nimsets, times, + strutils, options, ast, astalgo, trees, treetab, nimsets, nversion, platform, math, msgs, os, condsyms, idents, renderer, types, commands, magicsys, modulegraphs, strtabs, lineinfos @@ -450,21 +450,38 @@ proc rangeCheck(n: PNode, value: BiggestInt; g: ModuleGraph) = localError(g.config, n.info, "cannot convert " & $value & " to " & typeToString(n.typ)) -proc foldConv*(n, a: PNode; g: ModuleGraph; check = false): PNode = +proc foldConv(n, a: PNode; g: ModuleGraph; check = false): PNode = + let dstTyp = skipTypes(n.typ, abstractRange) + let srcTyp = skipTypes(a.typ, abstractRange) + # XXX range checks? - case skipTypes(n.typ, abstractRange).kind - of tyInt..tyInt64, tyUInt..tyUInt64: - case skipTypes(a.typ, abstractRange).kind + case dstTyp.kind + of tyInt..tyInt64, tyUint..tyUInt64: + case srcTyp.kind of tyFloat..tyFloat64: result = newIntNodeT(int(getFloat(a)), n, g) - of tyChar: result = newIntNodeT(getOrdValue(a), n, g) + of tyChar: + result = newIntNodeT(getOrdValue(a), n, g) + of tyUInt8..tyUInt32, tyInt8..tyInt32: + let fromSigned = srcTyp.kind in tyInt..tyInt64 + let toSigned = dstTyp.kind in tyInt..tyInt64 + + let mask = lastOrd(g.config, dstTyp, fixedUnsigned=true) + + var val = + if toSigned: + a.getOrdValue mod mask + else: + a.getOrdValue and mask + + result = newIntNodeT(val, n, g) else: result = a result.typ = n.typ if check and result.kind in {nkCharLit..nkUInt64Lit}: rangeCheck(n, result.intVal, g) of tyFloat..tyFloat64: - case skipTypes(a.typ, abstractRange).kind + case srcTyp.kind of tyInt..tyInt64, tyEnum, tyBool, tyChar: result = newFloatNodeT(toBiggestFloat(getOrdValue(a)), n, g) else: @@ -549,19 +566,6 @@ proc newSymNodeTypeDesc*(s: PSym; info: TLineInfo): PNode = proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode = result = nil - - proc getSrcTimestamp(): DateTime = - try: - result = utc(fromUnix(parseInt(getEnv("SOURCE_DATE_EPOCH", - "not a number")))) - except ValueError: - # Environment variable malformed. - # https://reproducible-builds.org/specs/source-date-epoch/: "If the - # value is malformed, the build process SHOULD exit with a non-zero - # error code", which this doesn't do. This uses local time, because - # that maintains compatibility with existing usage. - result = local(getTime()) - case n.kind of nkSym: var s = n.sym @@ -571,10 +575,8 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode = of skConst: case s.magic of mIsMainModule: result = newIntNodeT(ord(sfMainModule in m.flags), n, g) - of mCompileDate: result = newStrNodeT(format(getSrcTimestamp(), - "yyyy-MM-dd"), n, g) - of mCompileTime: result = newStrNodeT(format(getSrcTimestamp(), - "HH:mm:ss"), n, g) + of mCompileDate: result = newStrNodeT(getDateStr(), n, g) + of mCompileTime: result = newStrNodeT(getClockStr(), 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) @@ -742,7 +744,8 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode = of nkHiddenStdConv, nkHiddenSubConv, nkConv: var a = getConstExpr(m, n.sons[1], g) if a == nil: return - result = foldConv(n, a, g, check=n.kind == nkHiddenStdConv) + # XXX: we should enable `check` for other conversion types too + result = foldConv(n, a, g, check=n.kind == nkHiddenSubConv) of nkCast: var a = getConstExpr(m, n.sons[1], g) if a == nil: return diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index e3c750f5b7..7be0610a2c 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -215,7 +215,7 @@ proc semGenericStmt(c: PContext, n: PNode, checkMinSonsLen(n, 1, c.config) let fn = n.sons[0] var s = qualifiedLookUp(c, fn, {}) - if s == nil and + if s == nil and {withinMixin, withinConcept}*flags == {} and fn.kind in {nkIdent, nkAccQuoted} and considerQuotedIdent(c, fn).id notin ctx.toMixin: @@ -225,7 +225,7 @@ proc semGenericStmt(c: PContext, n: PNode, var mixinContext = false if s != nil: incl(s.flags, sfUsed) - mixinContext = s.magic in {mDefined, mDefinedInScope, mCompiles} + mixinContext = s.magic in {mDefined, mDefinedInScope, mCompiles, mRunnableExamples} let sc = symChoice(c, fn, s, if s.isMixedIn: scForceOpen else: scOpen) case s.kind of skMacro: diff --git a/compiler/seminst.nim b/compiler/seminst.nim index f9d7c37545..4bf1e6ef29 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -326,7 +326,8 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, # no need to instantiate generic templates/macros: internalAssert c.config, fn.kind notin {skMacro, skTemplate} # generates an instantiated proc - if c.instCounter > 1000: internalError(c.config, fn.ast.info, "nesting too deep") + if c.instCounter > 50: + globalError(c.config, info, "generic instantiation too nested") inc(c.instCounter) # careful! we copy the whole AST including the possibly nil body! var n = copyTree(fn.ast) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index bdea07ea8a..0a9de674b5 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -323,13 +323,13 @@ proc catches(tracked: PEffects, e: PType) = dec L else: inc i - if not isNil(tracked.exc.sons): + if tracked.exc.len > 0: setLen(tracked.exc.sons, L) else: assert L == 0 proc catchesAll(tracked: PEffects) = - if not isNil(tracked.exc.sons): + if tracked.exc.len > 0: setLen(tracked.exc.sons, tracked.bottom) proc track(tracked: PEffects, n: PNode) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 170ac799ef..fb01127fc9 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -567,6 +567,8 @@ proc symForVar(c: PContext, n: PNode): PSym = let m = if n.kind == nkPragmaExpr: n.sons[0] else: n result = newSymG(skForVar, m, c) styleCheckDef(c.config, result) + if n.kind == nkPragmaExpr: + pragma(c, result, n.sons[1], forVarPragmas) proc semForVars(c: PContext, n: PNode): PNode = result = n @@ -802,7 +804,7 @@ proc semRaise(c: PContext, n: PNode): PNode = if not isImportedException(typ, c.config): if typ.kind != tyRef or typ.lastSon.kind != tyObject: localError(c.config, n.info, errExprCannotBeRaised) - if not isException(typ.lastSon): + if typ.len > 0 and not isException(typ.lastSon): localError(c.config, n.info, "raised object of type $1 does not inherit from Exception", [typeToString(typ)]) @@ -1056,8 +1058,8 @@ proc checkForMetaFields(c: PContext; n: PNode) = case t.kind of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyLent, tyPtr, tyRef, tyProc, tyGenericInvocation, tyGenericInst, tyAlias, tySink: - let start = int ord(t.kind in {tyGenericInvocation, tyGenericInst}) - for i in start ..< t.sons.len: + let start = ord(t.kind in {tyGenericInvocation, tyGenericInst}) + for i in start ..< t.len: checkMeta(t.sons[i]) else: checkMeta(t) @@ -1376,7 +1378,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.lastSon elif obj.kind == tyGenericInvocation: obj = obj.sons[0] else: break - if obj.kind in {tyObject, tyDistinct}: + if obj.kind in {tyObject, tyDistinct, tySequence, tyString}: if obj.destructor.isNil: obj.destructor = s else: @@ -1398,7 +1400,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = if t.kind == tyGenericBody: t = t.lastSon elif t.kind == tyGenericInvocation: t = t.sons[0] else: break - if t.kind in {tyObject, tyDistinct, tyEnum}: + if t.kind in {tyObject, tyDistinct, tyEnum, tySequence, tyString}: if t.deepCopy.isNil: t.deepCopy = s else: localError(c.config, n.info, errGenerated, @@ -1427,7 +1429,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = elif objB.kind in {tyGenericInvocation, tyGenericInst}: objB = objB.sons[0] else: break - if obj.kind in {tyObject, tyDistinct} and sameType(obj, objB): + if obj.kind in {tyObject, tyDistinct, tySequence, tyString} and sameType(obj, objB): let opr = if s.name.s == "=": addr(obj.assignment) else: addr(obj.sink) if opr[].isNil: opr[] = s @@ -1592,7 +1594,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, 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 " & c.config$proto.info)) - if sfForward notin proto.flags: + if sfForward notin proto.flags and proto.magic == mNone: wrongRedefinition(c, n.info, proto.name.s) excl(proto.flags, sfForward) closeScope(c) # close scope with wrong parameter symbols @@ -1609,7 +1611,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, n.sons[pragmasPos] = proto.ast.sons[pragmasPos] if n.sons[namePos].kind != nkSym: internalError(c.config, n.info, "semProcAux") n.sons[namePos].sym = proto - if importantComments(c.config) and not isNil(proto.ast.comment): + if importantComments(c.config) and proto.ast.comment.len > 0: n.comment = proto.ast.comment proto.ast = n # needed for code generation popOwner(c) @@ -1658,7 +1660,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, openScope(c) n.sons[bodyPos] = semGenericStmt(c, n.sons[bodyPos]) closeScope(c) - fixupInstantiatedSymbols(c, s) + if s.magic == mNone: + fixupInstantiatedSymbols(c, s) if s.kind == skMethod: semMethodPrototype(c, s, n) if sfImportc in s.flags: # so we just ignore the body after semantic checking for importc: diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 2952831e9e..3966964220 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -210,7 +210,7 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) = if s != nil and s.owner == c.owner and sfGenSym in s.flags: styleCheckUse(n.info, s) replaceIdentBySym(c.c, n, newSymNode(s, n.info)) - else: + elif not (n.kind == nkSym and sfGenSym in n.sym.flags): let local = newGenSym(k, ident, c) addPrelimDecl(c.c, local) styleCheckDef(c.c.config, n.info, local) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index dd1e96bde1..86f3a17ab2 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -145,9 +145,7 @@ proc semSet(c: PContext, n: PNode, prev: PType): PType = localError(c.config, n.info, errXExpectsOneTypeParam % "set") addSonSkipIntLit(result, errorType(c)) -proc semContainer(c: PContext, n: PNode, kind: TTypeKind, kindStr: string, - prev: PType): PType = - result = newOrPrevType(kind, prev, c) +proc semContainerArg(c: PContext; n: PNode, kindStr: string; result: PType) = if sonsLen(n) == 2: var base = semTypeNode(c, n.sons[1], nil) if base.kind == tyVoid: @@ -157,6 +155,11 @@ proc semContainer(c: PContext, n: PNode, kind: TTypeKind, kindStr: string, localError(c.config, n.info, errXExpectsOneTypeParam % kindStr) addSonSkipIntLit(result, errorType(c)) +proc semContainer(c: PContext, n: PNode, kind: TTypeKind, kindStr: string, + prev: PType): PType = + result = newOrPrevType(kind, prev, c) + semContainerArg(c, n, kindStr, result) + proc semVarargs(c: PContext, n: PNode, prev: PType): PType = result = newOrPrevType(tyVarargs, prev, c) if sonsLen(n) == 2 or sonsLen(n) == 3: @@ -1507,7 +1510,24 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of mRange: result = semRange(c, n, prev) of mSet: result = semSet(c, n, prev) of mOrdinal: result = semOrdinal(c, n, prev) - of mSeq: result = semContainer(c, n, tySequence, "seq", prev) + of mSeq: + if c.config.selectedGc == gcDestructors: + let s = c.graph.sysTypes[tySequence] + assert s != nil + assert prev == nil + result = copyType(s, s.owner, keepId=false) + # XXX figure out why this has children already... + result.sons.setLen 0 + result.n = nil + if c.config.selectedGc == gcDestructors: + result.flags = {tfHasAsgn} + else: + result.flags = {} + semContainerArg(c, n, "seq", result) + else: + result = semContainer(c, n, tySequence, "seq", prev) + if c.config.selectedGc == gcDestructors: + incl result.flags, tfHasAsgn of mOpt: result = semContainer(c, n, tyOpt, "opt", prev) of mVarargs: result = semVarargs(c, n, prev) of mTypeDesc, mTypeTy: @@ -1687,6 +1707,9 @@ proc processMagicType(c: PContext, m: PSym) = of mString: setMagicType(c.config, m, tyString, c.config.target.ptrSize) rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar)) + when false: + if c.config.selectedGc == gcDestructors: + incl m.typ.flags, tfHasAsgn of mCstring: setMagicType(c.config, m, tyCString, c.config.target.ptrSize) rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar)) @@ -1726,6 +1749,10 @@ proc processMagicType(c: PContext, m: PSym) = setMagicType(c.config, m, tySet, 0) of mSeq: setMagicType(c.config, m, tySequence, 0) + if c.config.selectedGc == gcDestructors: + incl m.typ.flags, tfHasAsgn + assert c.graph.sysTypes[tySequence] == nil + c.graph.sysTypes[tySequence] = m.typ of mOpt: setMagicType(c.config, m, tyOpt, 0) of mOrdinal: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 9321630550..407e346191 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1921,6 +1921,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, result.typ.n = arg return + let oldInheritancePenalty = m.inheritancePenalty var r = typeRel(m, f, a) # This special typing rule for macros and templates is not documented @@ -2002,7 +2003,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, if arg.typ == nil: result = arg elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple or - m.inheritancePenalty > 0: + m.inheritancePenalty > oldInheritancePenalty: result = implicitConv(nkHiddenSubConv, f, arg, m, c) elif arg.typ.isEmptyContainer: result = arg.copyTree @@ -2131,6 +2132,10 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType, styleCheckUse(arg.info, arg.sons[best].sym) result = paramTypesMatchAux(m, f, arg.sons[best].typ, arg.sons[best], argOrig) + when false: + if m.calleeSym != nil and m.calleeSym.name.s == "[]": + echo m.c.config $ arg.info, " for ", m.calleeSym.name.s, " ", m.c.config $ m.calleeSym.info + writeMatches(m) proc setSon(father: PNode, at: int, son: PNode) = let oldLen = father.len @@ -2226,12 +2231,20 @@ proc matchesAux(c: PContext, n, nOrig: PNode, if a >= formalLen-1 and f < formalLen and m.callee.n[f].typ.isVarargsUntyped: formal = m.callee.n.sons[f].sym incl(marker, formal.position) - if container.isNil: - container = newNodeIT(nkArgList, n.sons[a].info, arrayConstr(c, n.info)) - setSon(m.call, formal.position + 1, container) + + if n.sons[a].kind == nkHiddenStdConv: + doAssert n.sons[a].sons[0].kind == nkEmpty and + n.sons[a].sons[1].kind == nkArgList and + n.sons[a].sons[1].len == 0 + # Steal the container and pass it along + setSon(m.call, formal.position + 1, n.sons[a].sons[1]) else: - incrIndexType(container.typ) - addSon(container, n.sons[a]) + if container.isNil: + container = newNodeIT(nkArgList, n.sons[a].info, arrayConstr(c, n.info)) + setSon(m.call, formal.position + 1, container) + else: + incrIndexType(container.typ) + addSon(container, n.sons[a]) elif n.sons[a].kind == nkExprEqExpr: # named param # check if m.callee has such a param: @@ -2239,11 +2252,13 @@ proc matchesAux(c: PContext, n, nOrig: PNode, 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 + m.firstMismatch = -a return formal = getSymFromList(m.callee.n, n.sons[a].sons[0].ident, 1) if formal == nil: # no error message! m.state = csNoMatch + m.firstMismatch = -a return if containsOrIncl(marker, formal.position): # already in namedParams, so no match @@ -2261,6 +2276,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, n.sons[a].sons[1], n.sons[a].sons[1]) if arg == nil: m.state = csNoMatch + m.firstMismatch = a return checkConstraint(n.sons[a].sons[1]) if m.baseTypeMatch: @@ -2379,6 +2395,11 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = if m.magic in {mArrGet, mArrPut}: m.state = csMatch m.call = n + # Note the following doesn't work as it would produce ambiguities. + # Instead we patch system.nim, see bug #8049. + when false: + inc m.genericMatches + inc m.exactMatches return var marker = initIntSet() matchesAux(c, n, nOrig, m, marker) @@ -2390,7 +2411,10 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = if not containsOrIncl(marker, formal.position): if formal.ast == nil: if formal.typ.kind == tyVarargs: - var container = newNodeIT(nkBracket, n.info, arrayConstr(c, n.info)) + # For consistency with what happens in `matchesAux` select the + # container node kind accordingly + let cnKind = if formal.typ.isVarargsUntyped: nkArgList else: nkBracket + var container = newNodeIT(cnKind, n.info, arrayConstr(c, n.info)) setSon(m.call, formal.position + 1, implicitConv(nkHiddenStdConv, formal.typ, container, m, c)) else: diff --git a/compiler/suggest.nim b/compiler/suggest.nim index b52632c67b..f99a2d432c 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -47,7 +47,7 @@ template origModuleName(m: PSym): string = m.name.s proc findDocComment(n: PNode): PNode = if n == nil: return nil - if not isNil(n.comment): return n + if n.comment.len > 0: return n if n.kind in {nkStmtList, nkStmtListExpr, nkObjectTy, nkRecList} and n.len > 0: result = findDocComment(n.sons[0]) if result != nil: return @@ -434,7 +434,7 @@ proc suggestSym*(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym; ## misnamed: should be 'symDeclared' when defined(nimsuggest): if conf.suggestVersion == 0: - if s.allUsages.isNil: + if s.allUsages.len == 0: s.allUsages = @[info] else: s.addNoDup(info) diff --git a/compiler/tccgen.nim b/compiler/tccgen.nim index ea0fb590fc..2301ad4049 100644 --- a/compiler/tccgen.nim +++ b/compiler/tccgen.nim @@ -39,24 +39,24 @@ proc setupEnvironment = addIncludePath(gTinyC, libpath) when defined(windows): - addSysincludePath(gTinyC, nimrodDir / "tinyc/win32/include") - addSysincludePath(gTinyC, nimrodDir / "tinyc/include") + addSysincludePath(gTinyC, nimDir / "tinyc/win32/include") + addSysincludePath(gTinyC, nimDir / "tinyc/include") when defined(windows): defineSymbol(gTinyC, "_WIN32", nil) # we need Mingw's headers too: var gccbin = getConfigVar("gcc.path") % ["nim", nimDir] addSysincludePath(gTinyC, gccbin /../ "include") - #addFile(nimrodDir / r"tinyc\win32\wincrt1.o") - addFile(nimrodDir / r"tinyc\win32\alloca86.o") - addFile(nimrodDir / r"tinyc\win32\chkstk.o") - #addFile(nimrodDir / r"tinyc\win32\crt1.o") + #addFile(nimDir / r"tinyc\win32\wincrt1.o") + addFile(nimDir / r"tinyc\win32\alloca86.o") + addFile(nimDir / r"tinyc\win32\chkstk.o") + #addFile(nimDir / r"tinyc\win32\crt1.o") - #addFile(nimrodDir / r"tinyc\win32\dllcrt1.o") - #addFile(nimrodDir / r"tinyc\win32\dllmain.o") - addFile(nimrodDir / r"tinyc\win32\libtcc1.o") + #addFile(nimDir / r"tinyc\win32\dllcrt1.o") + #addFile(nimDir / r"tinyc\win32\dllmain.o") + addFile(nimDir / r"tinyc\win32\libtcc1.o") - #addFile(nimrodDir / r"tinyc\win32\lib\crt1.c") - #addFile(nimrodDir / r"tinyc\lib\libtcc1.c") + #addFile(nimDir / r"tinyc\win32\lib\crt1.c") + #addFile(nimDir / r"tinyc\lib\libtcc1.c") else: addSysincludePath(gTinyC, "/usr/include") when defined(amd64): diff --git a/compiler/transf.nim b/compiler/transf.nim index 3a276dc38b..84297aa6a1 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -1050,8 +1050,8 @@ proc transformStmt*(g: ModuleGraph; module: PSym, n: PNode): PNode = when useEffectSystem: trackTopLevelStmt(g, module, result) #if n.info ?? "temp.nim": # echo renderTree(result, {renderIds}) - if c.needsDestroyPass: - result = injectDestructorCalls(g, module, result) + #if c.needsDestroyPass: + # result = injectDestructorCalls(g, module, result) incl(result.flags, nfTransf) proc transformExpr*(g: ModuleGraph; module: PSym, n: PNode): PNode = @@ -1063,6 +1063,6 @@ proc transformExpr*(g: ModuleGraph; module: PSym, n: PNode): PNode = liftDefer(c, result) # expressions are not to be injected with destructor calls as that # the list of top level statements needs to be collected before. - if c.needsDestroyPass: - result = injectDestructorCalls(g, module, result) + #if c.needsDestroyPass: + # result = injectDestructorCalls(g, module, result) incl(result.flags, nfTransf) diff --git a/compiler/treetab.nim b/compiler/treetab.nim index e6eb8c666a..f15974f612 100644 --- a/compiler/treetab.nim +++ b/compiler/treetab.nim @@ -29,8 +29,7 @@ proc hashTree(n: PNode): Hash = if (n.floatVal >= - 1000000.0) and (n.floatVal <= 1000000.0): result = result !& toInt(n.floatVal) of nkStrLit..nkTripleStrLit: - if not n.strVal.isNil: - result = result !& hash(n.strVal) + result = result !& hash(n.strVal) else: for i in countup(0, sonsLen(n) - 1): result = result !& hashTree(n.sons[i]) diff --git a/compiler/types.nim b/compiler/types.nim index d065ae29ab..d0eec35cf1 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -278,6 +278,8 @@ proc analyseObjectWithTypeField(t: PType): TTypeFieldResult = proc isGCRef(t: PType): bool = result = t.kind in GcTypeKinds or (t.kind == tyProc and t.callConv == ccClosure) + if result and t.kind in {tyString, tySequence} and tfHasAsgn in t.flags: + result = false proc containsGarbageCollectedRef*(typ: PType): bool = # returns true if typ contains a reference, sequence or string (all the @@ -759,9 +761,10 @@ proc initSameTypeClosure: TSameTypeClosure = discard proc containsOrIncl(c: var TSameTypeClosure, a, b: PType): bool = - result = not isNil(c.s) and c.s.contains((a.id, b.id)) + result = c.s.len > 0 and c.s.contains((a.id, b.id)) if not result: - if isNil(c.s): c.s = @[] + when not defined(nimNoNilSeqs): + if isNil(c.s): c.s = @[] c.s.add((a.id, b.id)) proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool @@ -1341,14 +1344,23 @@ proc computeSizeAux(conf: ConfigRef; typ: PType, a: var BiggestInt): BiggestInt if typ.callConv == ccClosure: result = 2 * conf.target.ptrSize else: result = conf.target.ptrSize a = conf.target.ptrSize - of tyString, tyNil: + of tyString: + if tfHasAsgn in typ.flags: + result = conf.target.ptrSize * 2 + else: + result = conf.target.ptrSize + of tyNil: 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 = conf.target.ptrSize + else: + if typ.kind == tySequence and tfHasAsgn in typ.flags: + result = conf.target.ptrSize * 2 + else: + result = conf.target.ptrSize a = result of tyArray: let elemSize = computeSizeAux(conf, typ.sons[1], a) diff --git a/compiler/typesrenderer.nim b/compiler/typesrenderer.nim index 4d75d5d058..0c4fe01e1f 100644 --- a/compiler/typesrenderer.nim +++ b/compiler/typesrenderer.nim @@ -29,7 +29,6 @@ proc renderPlainSymbolName*(n: PNode): string = else: result = "" #internalError(n.info, "renderPlainSymbolName() with " & $n.kind) - assert(not result.isNil) proc renderType(n: PNode): string = ## Returns a string with the node type or the empty string. @@ -80,7 +79,6 @@ proc renderType(n: PNode): string = for i in 1 ..< len(n): result.add(renderType(n[i]) & ',') result[len(result)-1] = ']' else: result = "" - assert(not result.isNil) proc renderParamTypes(found: var seq[string], n: PNode) = diff --git a/compiler/vm.nim b/compiler/vm.nim index c49b66b82b..e38642de82 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -81,14 +81,16 @@ proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int; recursionLimit=100) = msgWriteln(c.config, s) proc stackTrace(c: PCtx, tos: PStackFrame, pc: int, - msg: string, n: PNode = nil) = + msg: string, lineInfo: TLineInfo) = msgWriteln(c.config, "stack trace: (most recent call last)") stackTraceAux(c, tos, pc) # XXX test if we want 'globalError' for every mode - let lineInfo = if n == nil: c.debug[pc] else: n.info if c.mode == emRepl: globalError(c.config, lineInfo, msg) else: localError(c.config, lineInfo, msg) +proc stackTrace(c: PCtx, tos: PStackFrame, pc: int, msg: string) = + stackTrace(c, tos, pc, msg, c.debug[pc]) + proc bailOut(c: PCtx; tos: PStackFrame) = stackTrace(c, tos, c.exceptionInstr, "unhandled exception: " & c.currentExceptionA.sons[3].skipColon.strVal) @@ -242,7 +244,8 @@ template getstr(a: untyped): untyped = (if a.kind == rkNode: a.node.strVal else: $chr(int(a.intVal))) proc pushSafePoint(f: PStackFrame; pc: int) = - if f.safePoints.isNil: f.safePoints = @[] + when not defined(nimNoNilSeqs): + if f.safePoints.isNil: f.safePoints = @[] f.safePoints.add(pc) proc popSafePoint(f: PStackFrame) = @@ -255,7 +258,7 @@ proc cleanUpOnException(c: PCtx; tos: PStackFrame): let raisedType = c.currentExceptionA.typ.skipTypes(abstractPtrs) var f = tos while true: - while f.safePoints.isNil or f.safePoints.len == 0: + while f.safePoints.len == 0: f = f.next if f.isNil: return (-1, nil) var pc2 = f.safePoints[f.safePoints.high] @@ -270,7 +273,7 @@ proc cleanUpOnException(c: PCtx; tos: PStackFrame): abstractPtrs) else: nil #echo typeToString(exceptType), " ", typeToString(raisedType) - if exceptType.isNil or inheritanceDiff(exceptType, raisedType) <= 0: + if exceptType.isNil or inheritanceDiff(raisedType, exceptType) <= 0: # mark exception as handled but keep it in B for # the getCurrentException() builtin: c.currentExceptionB = c.currentExceptionA @@ -297,7 +300,6 @@ proc cleanUpOnException(c: PCtx; tos: PStackFrame): discard f.safePoints.pop proc cleanUpOnReturn(c: PCtx; f: PStackFrame): int = - if f.safePoints.isNil: return -1 for s in f.safePoints: var pc = s while c.code[pc].opcode == opcExcept: @@ -531,9 +533,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = decodeBC(rkInt) let idx = regs[rc].intVal.int let s = regs[rb].node.strVal - if s.isNil: - stackTrace(c, tos, pc, errNilAccess) - elif idx <% s.len: + if idx <% s.len: regs[ra].intVal = s[idx].ord elif idx == s.len and optLaxStrings in c.config.options: regs[ra].intVal = 0 @@ -820,7 +820,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].intVal = ord((regs[rb].node.kind == nkNilLit and regs[rc].node.kind == nkNilLit) or regs[rb].node == regs[rc].node) - of opcEqNimrodNode: + of opcEqNimNode: decodeBC(rkInt) regs[ra].intVal = ord(exprStructuralEquivalent(regs[rb].node, regs[rc].node, @@ -920,6 +920,15 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].node.flags.incl nfIsRef else: stackTrace(c, tos, pc, "node is not a symbol") + of opcSymOwner: + decodeB(rkNode) + let a = regs[rb].node + if a.kind == nkSym: + regs[ra].node = if a.sym.owner.isNil: newNode(nkNilLit) + else: newSymNode(a.sym.skipGenericOwner) + regs[ra].node.flags.incl nfIsRef + else: + stackTrace(c, tos, pc, "node is not a symbol") of opcEcho: let rb = instr.regB if rb == 1: @@ -1220,7 +1229,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = # Note that `nfIsRef` + `nkNilLit` represents an allocated # reference with the value `nil`, so `isNil` should be false! (node.kind == nkNilLit and nfIsRef notin node.flags) or - (node.kind in {nkStrLit..nkTripleStrLit} and node.strVal.isNil) or (not node.typ.isNil and node.typ.kind == tyProc and node.typ.callConv == ccClosure and node.sons[0].kind == nkNilLit and node.sons[1].kind == nkNilLit)) @@ -1325,6 +1333,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = ensureKind(rkNode) if regs[rb].kind == rkNode and regs[rb].node.typ != nil: regs[ra].node = opMapTypeToAst(c.cache, regs[rb].node.typ, c.debug[pc]) + elif regs[rb].kind == rkNode and regs[rb].node.kind == nkSym and regs[rb].node.sym.typ != nil: + regs[ra].node = opMapTypeToAst(c.cache, regs[rb].node.sym.typ, c.debug[pc]) else: stackTrace(c, tos, pc, "node has no type") of 1: @@ -1332,6 +1342,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = ensureKind(rkInt) if regs[rb].kind == rkNode and regs[rb].node.typ != nil: regs[ra].intVal = ord(regs[rb].node.typ.kind) + elif regs[rb].kind == rkNode and regs[rb].node.kind == nkSym and regs[rb].node.sym.typ != nil: + regs[ra].intVal = ord(regs[rb].node.sym.typ.kind) #else: # stackTrace(c, tos, pc, "node has no type") of 2: @@ -1339,6 +1351,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = ensureKind(rkNode) if regs[rb].kind == rkNode and regs[rb].node.typ != nil: regs[ra].node = opMapTypeInstToAst(c.cache, regs[rb].node.typ, c.debug[pc]) + elif regs[rb].kind == rkNode and regs[rb].node.kind == nkSym and regs[rb].node.sym.typ != nil: + regs[ra].node = opMapTypeInstToAst(c.cache, regs[rb].node.sym.typ, c.debug[pc]) else: stackTrace(c, tos, pc, "node has no type") else: @@ -1346,6 +1360,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = ensureKind(rkNode) if regs[rb].kind == rkNode and regs[rb].node.typ != nil: regs[ra].node = opMapTypeImplToAst(c.cache, regs[rb].node.typ, c.debug[pc]) + elif regs[rb].kind == rkNode and regs[rb].node.kind == nkSym and regs[rb].node.sym.typ != nil: + regs[ra].node = opMapTypeImplToAst(c.cache, regs[rb].node.sym.typ, c.debug[pc]) else: stackTrace(c, tos, pc, "node has no type") of opcNStrVal: @@ -1380,15 +1396,17 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = c.debug[pc], c.config)[0] else: globalError(c.config, c.debug[pc], "VM is not built with 'gorge' support") - of opcNError: + of opcNError, opcNWarning, opcNHint: decodeB(rkNode) let a = regs[ra].node let b = regs[rb].node - stackTrace(c, tos, pc, a.strVal, if b.kind == nkNilLit: nil else: b) - of opcNWarning: - message(c.config, c.debug[pc], warnUser, regs[ra].node.strVal) - of opcNHint: - message(c.config, c.debug[pc], hintUser, regs[ra].node.strVal) + let info = if b.kind == nkNilLit: c.debug[pc] else: b.info + if instr.opcode == opcNError: + stackTrace(c, tos, pc, a.strVal, info) + elif instr.opcode == opcNWarning: + message(c.config, info, warnUser, a.strVal) + elif instr.opcode == opcNHint: + message(c.config, info, hintUser, a.strVal) of opcParseExprToAst: decodeB(rkNode) # c.debug[pc].line.int - countLines(regs[rb].strVal) ? @@ -1396,9 +1414,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let ast = parseString(regs[rb].node.strVal, c.cache, c.config, 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: + if error.len == 0 and msg <= errMax: error = formatMsg(conf, info, msg, arg)) - if not error.isNil: + if error.len > 0: c.errorFlag = error elif sonsLen(ast) != 1: c.errorFlag = formatMsg(c.config, c.debug[pc], errGenerated, @@ -1411,9 +1429,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let ast = parseString(regs[rb].node.strVal, c.cache, c.config, 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: + if error.len == 0 and msg <= errMax: error = formatMsg(conf, info, msg, arg)) - if not error.isNil: + if error.len > 0: c.errorFlag = error else: regs[ra].node = ast @@ -1726,7 +1744,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = inc pc let typ = c.types[c.code[pc].regBx - wordExcess] createStrKeepNode(regs[ra]) - if regs[ra].node.strVal.isNil: regs[ra].node.strVal = newStringOfCap(1000) + when not defined(nimNoNilSeqs): + if regs[ra].node.strVal.isNil: regs[ra].node.strVal = newStringOfCap(1000) storeAny(regs[ra].node.strVal, typ, regs[rb].regToNode, c.config) of opcToNarrowInt: decodeBC(rkInt) diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 1abd9ae4ad..d642043dcf 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -62,7 +62,7 @@ type opcBitandInt, opcBitorInt, opcBitxorInt, opcAddu, opcSubu, opcMulu, opcDivu, opcModu, opcEqInt, opcLeInt, opcLtInt, opcEqFloat, opcLeFloat, opcLtFloat, opcLeu, opcLtu, - opcEqRef, opcEqNimrodNode, opcSameNodeType, + opcEqRef, opcEqNimNode, opcSameNodeType, opcXor, opcNot, opcUnaryMinusInt, opcUnaryMinusFloat, opcBitnotInt, opcEqStr, opcLeStr, opcLtStr, opcEqSet, opcLeSet, opcLtSet, opcMulSet, opcPlusSet, opcMinusSet, opcSymdiffSet, opcConcatStr, @@ -141,7 +141,8 @@ type opcSetType, # dest.typ = types[Bx] opcTypeTrait, opcMarshalLoad, opcMarshalStore, - opcToNarrowInt + opcToNarrowInt, + opcSymOwner TBlock* = object label*: PSym diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index a36f559ca7..e87347ec87 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1118,6 +1118,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mStaticExec: genBinaryABCD(c, n, dest, opcGorge) of mNLen: genUnaryABI(c, n, dest, opcLenSeq, nimNodeFlag) of mGetImpl: genUnaryABC(c, n, dest, opcGetImpl) + of mSymOwner: genUnaryABC(c, n, dest, opcSymOwner) of mNChild: genBinaryABC(c, n, dest, opcNChild) of mNSetChild: genVoidABC(c, n, dest, opcNSetChild) of mNDel: genVoidABC(c, n, dest, opcNDel) @@ -1178,7 +1179,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mNBindSym: genBindSym(c, n, dest) of mStrToIdent: genUnaryABC(c, n, dest, opcStrToIdent) of mEqIdent: genBinaryABC(c, n, dest, opcEqIdent) - of mEqNimrodNode: genBinaryABC(c, n, dest, opcEqNimrodNode) + of mEqNimrodNode: genBinaryABC(c, n, dest, opcEqNimNode) of mSameNodeType: genBinaryABC(c, n, dest, opcSameNodeType) of mNLineInfo: case n[0].sym.name.s @@ -1192,10 +1193,10 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = else: internalAssert c.config, false of mNHint: unused(c, n, dest) - genUnaryStmt(c, n, opcNHint) + genBinaryStmt(c, n, opcNHint) of mNWarning: unused(c, n, dest) - genUnaryStmt(c, n, opcNWarning) + genBinaryStmt(c, n, opcNWarning) of mNError: if n.len <= 1: # query error condition: diff --git a/compiler/vmmarshal.nim b/compiler/vmmarshal.nim index eb01b35140..149d2e08f9 100644 --- a/compiler/vmmarshal.nim +++ b/compiler/vmmarshal.nim @@ -127,7 +127,7 @@ proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet; storeAny(s, t.lastSon, a, stored, conf) s.add("]") of tyString, tyCString: - if a.kind == nkNilLit or a.strVal.isNil: s.add("null") + if a.kind == nkNilLit: s.add("null") else: s.add(escapeJson(a.strVal)) of tyInt..tyInt64, tyUInt..tyUInt64: s.add($a.intVal) of tyFloat..tyFloat128: s.add($a.floatVal) diff --git a/doc/manual.rst b/doc/manual.rst index bb2650799b..6dc6794f12 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -17,8 +17,7 @@ About this document =================== **Note**: This document is a draft! Several of Nim's features may need more -precise wording. This manual is constantly evolving until the 1.0 release and is -not to be considered as the final proper specification. +precise wording. This manual is constantly evolving into a proper specification. This document describes the lexis, the syntax, and the semantics of Nim. @@ -433,7 +432,7 @@ Numerical constants are of a single type and have the form:: UINT64_LIT = INT_LIT ['\''] ('u' | 'U') '64' exponent = ('e' | 'E' ) ['+' | '-'] digit ( ['_'] digit )* - FLOAT_LIT = digit (['_'] digit)* (('.' (['_'] digit)* [exponent]) |exponent) + FLOAT_LIT = digit (['_'] digit)* (('.' digit (['_'] digit)* [exponent]) |exponent) FLOAT32_SUFFIX = ('f' | 'F') ['32'] FLOAT32_LIT = HEX_LIT '\'' FLOAT32_SUFFIX | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] FLOAT32_SUFFIX @@ -504,7 +503,7 @@ following characters:: These keywords are also operators: ``and or not xor shl shr div mod in notin is isnot of``. -`=`:tok:, `:`:tok:, `::`:tok: are not available as general operators; they +`.`:tok: `=`:tok:, `:`:tok:, `::`:tok: are not available as general operators; they are used for other notational purposes. ``*:`` is as a special case treated as the two tokens `*`:tok: and `:`:tok: @@ -618,6 +617,55 @@ The grammar's start symbol is ``module``. +Order of evaluation +=================== + +Order of evaluation is strictly left-to-right, inside-out as it is typical for most others +imperative programming languages: + +.. code-block:: nim + :test: "nim c $1" + + var s = "" + + proc p(arg: int): int = + s.add $arg + result = arg + + discard p(p(1) + p(2)) + + doAssert s == "123" + + +Assignments are not special, the left-hand-side expression is evaluated before the +right-hand side: + +.. code-block:: nim + :test: "nim c $1" + + var v = 0 + proc getI(): int = + result = v + inc v + + var a, b: array[0..2, int] + + proc someCopy(a: var int; b: int) = a = b + + a[getI()] = getI() + + doAssert a == [1, 0, 0] + + v = 0 + someCopy(b[getI()], getI()) + + doAssert b == [1, 0, 0] + + +Rationale: Consistency with overloaded assignment or assignment-like operations, +``a = b`` can be read as ``performSomeCopy(a, b)``. + + Types ===== @@ -888,8 +936,12 @@ Now the following holds:: ord(south) == 2 ord(west) == 3 + # Also allowed: + ord(Direction.west) == 3 + Thus, north < east < south < west. The comparison operators can be used -with enumeration types. +with enumeration types. Instead of ``north`` etc, the enum value can also +be qualified with the enum type that it resides in, ``Direction.north``. For better interfacing to other programming languages, the fields of enum types can be assigned an explicit ordinal value. However, the ordinal values @@ -925,18 +977,25 @@ As can be seen from the example, it is possible to both specify a field's ordinal value and its string value by using a tuple. It is also possible to only specify one of them. -An enum can be marked with the ``pure`` pragma so that it's fields are not -added to the current scope, so they always need to be accessed -via ``MyEnum.value``: +An enum can be marked with the ``pure`` pragma so that it's fields are +added to a special module specific hidden scope that is only queried +as the last attempt. Only non-ambiguous symbols are added to this scope. +But one can always access these via type qualification written +as ``MyEnum.value``: .. code-block:: nim type MyEnum {.pure.} = enum - valueA, valueB, valueC, valueD + valueA, valueB, valueC, valueD, amb - echo valueA # error: Unknown identifier - echo MyEnum.valueA # works + OtherEnum {.pure.} = enum + valueX, valueY, valueZ, amb + + + echo valueA # MyEnum.valueA + echo amb # Error: Unclear whether it's MyEnum.amb or OtherEnum.amb + echo MyEnum.amb # OK. String type @@ -945,6 +1004,11 @@ All string literals are of the type ``string``. A string in Nim is very similar to a sequence of characters. However, strings in Nim are both zero-terminated and have a length field. One can retrieve the length with the builtin ``len`` procedure; the length never counts the terminating zero. + +The terminating zero cannot be accessed unless the string is converted +to the ``cstring`` type first. The terminating zero assures that this +conversion can be done in O(1) and without any allocations. + The assignment operator for strings always copies the string. The ``&`` operator concatenates strings. @@ -2925,6 +2989,10 @@ name ``c`` should default to type ``Context``, ``n`` should default to proc bar(c, n, counter) = ... proc baz(c, n) = ... + proc mixedMode(c, n; x, y: int) = + # 'c' is inferred to be of the type 'Context' + # 'n' is inferred to be of the type 'Node' + # But 'x' and 'y' are of type 'int'. The ``using`` section uses the same indentation based grouping syntax as a ``var`` or ``let`` section. @@ -2932,6 +3000,9 @@ a ``var`` or ``let`` section. Note that ``using`` is not applied for ``template`` since untyped template parameters default to the type ``system.untyped``. +Mixing parameters that should use the ``using`` declaration with parameters +that are explicitly typed is possible and requires a semicolon between them. + If expression ------------- @@ -3045,6 +3116,19 @@ the address of variables, but one can't use it on variables declared through # Error: expression has no address +The unsafeAddr operator +----------------------- + +For easier interoperability with other compiled languages such as C, retrieving +the address of a ``let`` variable, a parameter or a ``for`` loop variable, the +``unsafeAddr`` operation can be used: + +.. code-block:: nim + + let myArray = [1, 2, 3] + foreignProcThatTakesAnAddr(unsafeAddr myArray) + + Procedures ========== @@ -4327,6 +4411,34 @@ be inferred to have the equivalent of the `any` type class and thus they will match anything without discrimination. +Generic inference restrictions +------------------------------ + +The types ``var T`` and ``typedesc[T]`` cannot be inferred in a generic +instantiation. The following is not allowed: + +.. code-block:: nim + :test: "nim c $1" + :status: 1 + + proc g[T](f: proc(x: T); x: T) = + f(x) + + proc c(y: int) = echo y + proc v(y: var int) = + y += 100 + var i: int + + # allowed: infers 'T' to be of type 'int' + g(c, 42) + + # not valid: 'T' is not inferred to be of type 'var int' + g(v, i) + + # also not allowed: explict instantiation via 'var int' + g[var int](v, i) + + Concepts -------- @@ -4573,7 +4685,7 @@ type is an instance of it: .. code-block:: nim :test: "nim c $1" - import future, typetraits + import sugar, typetraits type Functor[A] = concept f @@ -5399,12 +5511,17 @@ type ``system.ForLoopStmt`` can rewrite the entirety of a ``for`` loop: newFor.add x[^2][1] newFor.add body result.add newFor + # now wrap the whole macro in a block to create a new scope + result = quote do: + block: `result` for a, b in enumerate(items([1, 2, 3])): echo a, " ", b - for a2, b2 in enumerate([1, 2, 3, 5]): - echo a2, " ", b2 + # without wrapping the macro in a block, we'd need to choose different + # names for `a` and `b` here to avoid redefinition errors + for a, b in enumerate([1, 2, 3, 5]): + echo a, " ", b Currently for loop macros must be enabled explicitly @@ -5414,12 +5531,11 @@ via ``{.experimental: "forLoopMacros".}``. Case statement macros --------------------- -A macro that needs to be called `match`:idx: can be used to -rewrite ``case`` statements in order to -implement `pattern matching`:idx: for certain types. The following -example implements a simplistic form of pattern matching for tuples, -leveraging the existing equality operator for tuples (as provided in - ``system.==``): +A macro that needs to be called `match`:idx: can be used to rewrite +``case`` statements in order to implement `pattern matching`:idx: for +certain types. The following example implements a simplistic form of +pattern matching for tuples, leveraging the existing equality operator +for tuples (as provided in ``system.==``): .. code-block:: nim :test: "nim c $1" @@ -6448,6 +6564,11 @@ The deprecated pragma is used to mark a symbol as deprecated: proc p() {.deprecated.} var x {.deprecated.}: char +This pragma can also take in an optional warning string to relay to developers. + +.. code-block:: nim + proc thing(x: bool) {.deprecated: "See arguments of otherThing()".} + It can also be used as a statement, in that case it takes a list of *renamings*. .. code-block:: nim @@ -6456,7 +6577,6 @@ It can also be used as a statement, in that case it takes a list of *renamings*. Stream = ref object {.deprecated: [TFile: File, PStream: Stream].} - noSideEffect pragma ------------------- The ``noSideEffect`` pragma is used to mark a proc/iterator to have no side @@ -6942,12 +7062,36 @@ Example: .. code-block:: nim {.experimental: "parallel".} - proc useUsing(bar, foo) = + proc useParallel() = parallel: for i in 0..4: echo "echo in parallel" +As a top level statement, the experimental pragma enables a feature for the +rest of the module it's enabled in. This is problematic for macro and generic +instantiations that cross a module scope. Currently these usages have to be +put into a ``.push/pop`` environment: + +.. code-block:: nim + + # client.nim + proc useParallel*[T](unused: T) = + # use a generic T here to show the problem. + {.push experimental: "parallel".} + parallel: + for i in 0..4: + echo "echo in parallel" + + {.pop.} + + +.. code-block:: nim + + import client + useParallel(1) + + Implementation Specific Pragmas =============================== diff --git a/doc/nep1.rst b/doc/nep1.rst index c4d4456815..1ef8c3c24b 100644 --- a/doc/nep1.rst +++ b/doc/nep1.rst @@ -60,7 +60,7 @@ Spacing and Whitespace Conventions Naming Conventions -------------------------- +------------------ Note: While the rules outlined below are the *current* naming conventions, these conventions have not always been in place. Previously, the naming @@ -147,6 +147,80 @@ changed in the future. an in-place version should get an ``-In`` suffix (``replaceIn`` for this example). +The stdlib API is designed to be **easy to use** and consistent. Ease of use is +measured by the number of calls to achieve a concrete high level action. The +ultimate goal is that the programmer can *guess* a name. + +The library uses a simple naming scheme that makes use of common abbreviations +to keep the names short but meaningful. + + +------------------- ------------ -------------------------------------- +English word To use Notes +------------------- ------------ -------------------------------------- +initialize initT ``init`` is used to create a + value type ``T`` +new newP ``new`` is used to create a + reference type ``P`` +find find should return the position where + something was found; for a bool result + use ``contains`` +contains contains often short for ``find() >= 0`` +append add use ``add`` instead of ``append`` +compare cmp should return an int with the + ``< 0`` ``== 0`` or ``> 0`` semantics; + for a bool result use ``sameXYZ`` +put put, ``[]=`` consider overloading ``[]=`` for put +get get, ``[]`` consider overloading ``[]`` for get; + consider to not use ``get`` as a + prefix: ``len`` instead of ``getLen`` +length len also used for *number of elements* +size size, len size should refer to a byte size +capacity cap +memory mem implies a low-level operation +items items default iterator over a collection +pairs pairs iterator over (key, value) pairs +delete delete, del del is supposed to be faster than + delete, because it does not keep + the order; delete keeps the order +remove delete, del inconsistent right now +include incl +exclude excl +command cmd +execute exec +environment env +variable var +value value, val val is preferred, inconsistent right + now +executable exe +directory dir +path path path is the string "/usr/bin" (for + example), dir is the content of + "/usr/bin"; inconsistent right now +extension ext +separator sep +column col, column col is preferred, inconsistent right + now +application app +configuration cfg +message msg +argument arg +object obj +parameter param +operator opr +procedure proc +function func +coordinate coord +rectangle rect +point point +symbol sym +literal lit +string str +identifier ident +indentation indent +------------------- ------------ -------------------------------------- + + Coding Conventions ------------------ diff --git a/doc/nimc.rst b/doc/nimc.rst index 0682fac03d..4082b53785 100644 --- a/doc/nimc.rst +++ b/doc/nimc.rst @@ -143,6 +143,9 @@ which may be used in conjunction with the `compile time define pragmas`_ to override symbols during build time. +Compile time symbols are completely **case insensitive** and underscores are +ignored too. ``--define:FOO`` and ``--define:foo`` are identical. + Configuration files ------------------- @@ -326,40 +329,41 @@ The standard library supports a growing number of ``useX`` conditional defines affecting how some features are implemented. This section tries to give a complete list. -================== ========================================================= -Define Effect -================== ========================================================= -``release`` Turns off runtime checks and turns on the optimizer. -``useWinAnsi`` Modules like ``os`` and ``osproc`` use the Ansi versions - of the Windows API. The default build uses the Unicode - version. -``useFork`` Makes ``osproc`` use ``fork`` instead of ``posix_spawn``. -``useNimRtl`` Compile and link against ``nimrtl.dll``. -``useMalloc`` Makes Nim use C's `malloc`:idx: instead of Nim's - own memory manager, ableit prefixing each allocation with - its size to support clearing memory on reallocation. - This only works with ``gc:none``. -``useRealtimeGC`` Enables support of Nim's GC for *soft* realtime - systems. See the documentation of the `gc `_ - for further information. -``nodejs`` The JS target is actually ``node.js``. -``ssl`` Enables OpenSSL support for the sockets module. -``memProfiler`` Enables memory profiling for the native GC. -``uClibc`` Use uClibc instead of libc. (Relevant for Unix-like OSes) -``checkAbi`` When using types from C headers, add checks that compare - what's in the Nim file with what's in the C header - (requires a C compiler with _Static_assert support, like - any C11 compiler) -``tempDir`` This symbol takes a string as its value, like - ``--define:tempDir:/some/temp/path`` to override the - temporary directory returned by ``os.getTempDir()``. - The value **should** end with a directory separator - character. (Relevant for the Android platform) -``useShPath`` This symbol takes a string as its value, like - ``--define:useShPath:/opt/sh/bin/sh`` to override the - path for the ``sh`` binary, in cases where it is not - located in the default location ``/bin/sh`` -================== ========================================================= +====================== ========================================================= +Define Effect +====================== ========================================================= +``release`` Turns off runtime checks and turns on the optimizer. +``useWinAnsi`` Modules like ``os`` and ``osproc`` use the Ansi versions + of the Windows API. The default build uses the Unicode + version. +``useFork`` Makes ``osproc`` use ``fork`` instead of ``posix_spawn``. +``useNimRtl`` Compile and link against ``nimrtl.dll``. +``useMalloc`` Makes Nim use C's `malloc`:idx: instead of Nim's + own memory manager, ableit prefixing each allocation with + its size to support clearing memory on reallocation. + This only works with ``gc:none``. +``useRealtimeGC`` Enables support of Nim's GC for *soft* realtime + systems. See the documentation of the `gc `_ + for further information. +``nodejs`` The JS target is actually ``node.js``. +``ssl`` Enables OpenSSL support for the sockets module. +``memProfiler`` Enables memory profiling for the native GC. +``uClibc`` Use uClibc instead of libc. (Relevant for Unix-like OSes) +``checkAbi`` When using types from C headers, add checks that compare + what's in the Nim file with what's in the C header + (requires a C compiler with _Static_assert support, like + any C11 compiler) +``tempDir`` This symbol takes a string as its value, like + ``--define:tempDir:/some/temp/path`` to override the + temporary directory returned by ``os.getTempDir()``. + The value **should** end with a directory separator + character. (Relevant for the Android platform) +``useShPath`` This symbol takes a string as its value, like + ``--define:useShPath:/opt/sh/bin/sh`` to override the + path for the ``sh`` binary, in cases where it is not + located in the default location ``/bin/sh``. +``noSignalHandler`` Disable the crash handler from ``system.nim``. +====================== ========================================================= @@ -529,6 +533,16 @@ See the documentation of Nim's soft realtime `GC `_ for further information. +Signal handling in Nim +====================== + +The Nim programming language has no concept of Posix's signal handling +mechanisms. However, the standard library offers some rudimentary support +for signal handling, in particular, segmentation faults are turned into +fatal errors that produce a stack trace. This can be disabled with the +``-d:noSignalHandler`` switch. + + Debugging with Nim ================== diff --git a/doc/tut1.rst b/doc/tut1.rst index e200cfe8ba..a7f1b741ad 100644 --- a/doc/tut1.rst +++ b/doc/tut1.rst @@ -1112,21 +1112,12 @@ proc can convert it to its underlying integer value. For better interfacing to other programming languages, the symbols of enum types can be assigned an explicit ordinal value. However, the ordinal values -must be in ascending order. A symbol whose ordinal value is not -explicitly given is assigned the value of the previous symbol + 1. - -An explicit ordered enum can have *holes*: - -.. code-block:: nim - :test: "nim c $1" - type - MyEnum = enum - a = 2, b = 4, c = 89 +must be in ascending order. Ordinal types ------------- -Enumerations without holes, integer types, ``char`` and ``bool`` (and +Enumerations, integer types, ``char`` and ``bool`` (and subranges) are called ordinal types. Ordinal types have quite a few special operations: diff --git a/examples/c++iface/irrlichtex.nim b/examples/c++iface/irrlichtex.nim deleted file mode 100644 index 373a78ce7a..0000000000 --- a/examples/c++iface/irrlichtex.nim +++ /dev/null @@ -1,114 +0,0 @@ -# Horrible example of how to interface with a C++ engine ... ;-) - -{.link: "/usr/lib/libIrrlicht.so".} - -{.emit: """ -using namespace irr; -using namespace core; -using namespace scene; -using namespace video; -using namespace io; -using namespace gui; -""".} - -const - irr = "" - -type - TDimension2d {.final, header: irr, importc: "dimension2d".} = object - Tvector3df {.final, header: irr, importc: "vector3df".} = object - TColor {.final, header: irr, importc: "SColor".} = object - - TIrrlichtDevice {.final, header: irr, importc: "IrrlichtDevice".} = object - TIVideoDriver {.final, header: irr, importc: "IVideoDriver".} = object - TISceneManager {.final, header: irr, importc: "ISceneManager".} = object - TIGUIEnvironment {.final, header: irr, importc: "IGUIEnvironment".} = object - TIAnimatedMesh {.final, header: irr, importc: "IAnimatedMesh".} = object - TIAnimatedMeshSceneNode {.final, header: irr, - importc: "IAnimatedMeshSceneNode".} = object - TITexture {.final, header: irr, importc: "ITexture".} = object - - PIrrlichtDevice = ptr TIrrlichtDevice - PIVideoDriver = ptr TIVideoDriver - PISceneManager = ptr TISceneManager - PIGUIEnvironment = ptr TIGUIEnvironment - PIAnimatedMesh = ptr TIAnimatedMesh - PIAnimatedMeshSceneNode = ptr TIAnimatedMeshSceneNode - PITexture = ptr TITexture - -proc dimension2d(x, y: cint): TDimension2d {. - header: irr, importc: "dimension2d".} -proc vector3df(x,y,z: cint): Tvector3df {. - header: irr, importc: "vector3df".} -proc SColor(r,g,b,a: cint): TColor {. - header: irr, importc: "SColor".} - -proc createDevice(): PIrrlichtDevice {. - header: irr, importc: "createDevice".} -proc run(device: PIrrlichtDevice): bool {. - header: irr, importcpp: "run".} - -proc getVideoDriver(dev: PIrrlichtDevice): PIVideoDriver {. - header: irr, importcpp: "getVideoDriver".} -proc getSceneManager(dev: PIrrlichtDevice): PISceneManager {. - header: irr, importcpp: "getSceneManager".} -proc getGUIEnvironment(dev: PIrrlichtDevice): PIGUIEnvironment {. - header: irr, importcpp: "getGUIEnvironment".} - -proc getMesh(smgr: PISceneManager, path: cstring): PIAnimatedMesh {. - header: irr, importcpp: "getMesh".} - -proc drawAll(smgr: PISceneManager) {. - header: irr, importcpp: "drawAll".} -proc drawAll(guienv: PIGUIEnvironment) {. - header: irr, importcpp: "drawAll".} - -proc drop(dev: PIrrlichtDevice) {. - header: irr, importcpp: "drop".} - -proc getTexture(driver: PIVideoDriver, path: cstring): PITexture {. - header: irr, importcpp: "getTexture".} -proc endScene(driver: PIVideoDriver) {. - header: irr, importcpp: "endScene".} -proc beginScene(driver: PIVideoDriver, a, b: bool, c: TColor) {. - header: irr, importcpp: "beginScene".} - -proc addAnimatedMeshSceneNode( - smgr: PISceneManager, mesh: PIAnimatedMesh): PIAnimatedMeshSceneNode {. - header: irr, importcpp: "addAnimatedMeshSceneNode".} - -proc setMaterialTexture(n: PIAnimatedMeshSceneNode, x: cint, t: PITexture) {. - header: irr, importcpp: "setMaterialTexture".} -proc addCameraSceneNode(smgr: PISceneManager, x: cint, a, b: TVector3df) {. - header: irr, importcpp: "addCameraSceneNode".} - - -var device = createDevice() -if device == nil: quit "device is nil" - -var driver = device.getVideoDriver() -var smgr = device.getSceneManager() -var guienv = device.getGUIEnvironment() - -var mesh = smgr.getMesh("/home/andreas/download/irrlicht-1.7.2/media/sydney.md2") -if mesh == nil: - device.drop() - quit "no mesh!" - -var node = smgr.addAnimatedMeshSceneNode(mesh) - -if node != nil: - #node->setMaterialFlag(EMF_LIGHTING, false) - #node->setMD2Animation(scene::EMAT_STAND) - node.setMaterialTexture(0, - driver.getTexture( - "/home/andreas/download/irrlicht-1.7.2/media/media/sydney.bmp")) - -smgr.addCameraSceneNode(0, vector3df(0,30,-40), vector3df(0,5,0)) -while device.run(): - driver.beginScene(true, true, SColor(255,100,101,140)) - smgr.drawAll() - guienv.drawAll() - driver.endScene() -device.drop() - diff --git a/examples/cgi/cgi_server.py b/examples/cgi/cgi_server.py deleted file mode 100644 index 1907515e80..0000000000 --- a/examples/cgi/cgi_server.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python -import BaseHTTPServer -import CGIHTTPServer - -server = BaseHTTPServer.HTTPServer -handler = CGIHTTPServer.CGIHTTPRequestHandler -server_address = ('localhost', 8008) -handler.cgi_directories = ['/'] - -httpd = server(server_address, handler) -httpd.serve_forever() diff --git a/examples/cgi/cgi_stacktrace.nim b/examples/cgi/cgi_stacktrace.nim deleted file mode 100644 index e9f2f567c2..0000000000 --- a/examples/cgi/cgi_stacktrace.nim +++ /dev/null @@ -1,5 +0,0 @@ -import cgi -cgi.setStackTraceStdout() - -var a: string = nil -a.add "foobar" diff --git a/examples/cgi/example.nim b/examples/cgi/example.nim deleted file mode 100644 index 761197cdbc..0000000000 --- a/examples/cgi/example.nim +++ /dev/null @@ -1,7 +0,0 @@ -import cgi - -write(stdout, "Content-type: text/html\n\n") -write(stdout, "\n") -write(stdout, "Test\n") -write(stdout, "Hello!") -writeLine(stdout, "") diff --git a/examples/cgiex.nim b/examples/cgiex.nim deleted file mode 100644 index fb55a731a5..0000000000 --- a/examples/cgiex.nim +++ /dev/null @@ -1,12 +0,0 @@ -# Test/show CGI module -import strtabs, cgi - -var myData = readData() -validateData(myData, "name", "password") -writeContentType() - -write(stdout, "\n") -write(stdout, "Test\n") -writeLine(stdout, "name: " & myData["name"]) -writeLine(stdout, "password: " & myData["password"]) -writeLine(stdout, "") diff --git a/examples/cross_calculator/.gitignore b/examples/cross_calculator/.gitignore deleted file mode 100644 index e521bf3389..0000000000 --- a/examples/cross_calculator/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -# Android specific absolute paths. -android/bin/ -android/gen/ -android/jni/backend-jni.h -android/libs/ -android/local.properties -android/obj/ -android/tags -# iOS specific absolute paths -ios/resources/ui/*.m -ios/tags - diff --git a/examples/cross_calculator/android/AndroidManifest.xml b/examples/cross_calculator/android/AndroidManifest.xml deleted file mode 100644 index 05b96fb503..0000000000 --- a/examples/cross_calculator/android/AndroidManifest.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - diff --git a/examples/cross_calculator/android/build.xml b/examples/cross_calculator/android/build.xml deleted file mode 100644 index d7c88432a7..0000000000 --- a/examples/cross_calculator/android/build.xml +++ /dev/null @@ -1,92 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/cross_calculator/android/custom_rules.xml b/examples/cross_calculator/android/custom_rules.xml deleted file mode 100644 index 91783a50e4..0000000000 --- a/examples/cross_calculator/android/custom_rules.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/examples/cross_calculator/android/jni/Android.mk b/examples/cross_calculator/android/jni/Android.mk deleted file mode 100644 index c1a0feac3a..0000000000 --- a/examples/cross_calculator/android/jni/Android.mk +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (C) 2009 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := nimrod -LOCAL_SRC_FILES := nimcache/backend.c nimcache/system.c -LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/nimcache - -include $(BUILD_STATIC_LIBRARY) - -include $(CLEAR_VARS) - -LOCAL_MODULE := backend-jni -LOCAL_SRC_FILES := backend-jni.c -LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog -LOCAL_STATIC_LIBRARIES := nimrod - -include $(BUILD_SHARED_LIBRARY) diff --git a/examples/cross_calculator/android/jni/backend-jni.c b/examples/cross_calculator/android/jni/backend-jni.c deleted file mode 100644 index 3d65458ec8..0000000000 --- a/examples/cross_calculator/android/jni/backend-jni.c +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -#include -#include -#include "backend-jni.h" -#include "backend.h" - -#define TAG "backend-jni.c" - -jint JNICALL Java_com_github_nimrod_crosscalculator_CrossCalculator_myAdd - (JNIEnv *env, jobject thiz, jint a, jint b) -{ - char buf[256]; - const jint ret = myAdd(a, b); - // Using logging from inside the native bridge to log-debug. - sprintf(buf, "a %d + b %d = ret %d", a, b, ret); - __android_log_write(ANDROID_LOG_DEBUG, TAG, buf); - return ret; -} - -void JNICALL Java_com_github_nimrod_crosscalculator_CrossCalculator_initNimMain - (JNIEnv *env, jclass thiz) -{ - NimMain(); - __android_log_write(ANDROID_LOG_DEBUG, TAG, "Nimrod initialised"); -} - -// vim:tabstop=2 shiftwidth=2 syntax=c diff --git a/examples/cross_calculator/android/project.properties b/examples/cross_calculator/android/project.properties deleted file mode 100644 index 9fb894d9b4..0000000000 --- a/examples/cross_calculator/android/project.properties +++ /dev/null @@ -1,14 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system edit -# "ant.properties", and override values to adapt the script to your -# project structure. -# -# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): -#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt - -# Project target. -target=android-3 diff --git a/examples/cross_calculator/android/readme.txt b/examples/cross_calculator/android/readme.txt deleted file mode 100644 index 96bd403ca9..0000000000 --- a/examples/cross_calculator/android/readme.txt +++ /dev/null @@ -1,24 +0,0 @@ -In this directory you will find the Android platform cross-calculator sample. - -Due to the nature of Android being java and Nim generating C code, the build -process is slightly more complex because jni code has to be written to bridge -both languages. In a distant future it may be possible for Nim to generate -the whole jni bridge, but for the moment this is manual work. - -For the jni bridge to work first the java code is compiled with the Nim code -just declared as a native method which will be resolved at runtime. The scripts -nimbuild.sh and jnibuild.sh are in charge of building the Nim code and -generating the jni bridge from the java code respectively. Finally, the -ndk-build command from the android ndk tools has to be run to build the binary -library which will be installed along the final apk. - -All these steps are wrapped in the ant build script through the customization -of the -post-compile rule. If you have the android ndk tools installed and you -modify scripts/nimbuild.sh to point to the directory where you have Nim -installed on your system, you can simply run "ant debug" to build everything. - -Once the apk is built you can install it on your device or emulator with the -command "adb install bin/CrossCalculator-debug.apk". - -This example runs against the Android level 3 API, meaning devices from -Android 1.5 and above should be able to run the generated binary. diff --git a/examples/cross_calculator/android/res/layout/cross_calculator.xml b/examples/cross_calculator/android/res/layout/cross_calculator.xml deleted file mode 100644 index 11531334c5..0000000000 --- a/examples/cross_calculator/android/res/layout/cross_calculator.xml +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - - - - - - - -