diff --git a/changelog.md b/changelog.md index a137b3e42606f..92abb26146a30 100644 --- a/changelog.md +++ b/changelog.md @@ -17,6 +17,8 @@ - `bindMethod` in `std/jsffi` is deprecated, don't use it with closures. +- JS backend now supports lambda lifting for closures. Use `--legacy:jsNoLambdaLifting` to emulate old behavior. + ## Standard library additions and changes [//]: # "Changes:" @@ -37,6 +39,9 @@ slots when enlarging a sequence. objects the cyclic collector did free. If the number is zero that is a strong indicator that you can use `--mm:arc` instead of `--mm:orc`. - A `$` template is provided for `Path` in `std/paths`. +- `nimPreviewHashFarm` has been added to `lib/pure/hashes.nim` to default to a +64-bit string `Hash` (based upon Google's Farm Hash) which is also faster than +the present one. At present, this is incompatible with `--jsbigint=off` mode. [//]: # "Deprecations:" diff --git a/compiler/ast.nim b/compiler/ast.nim index 4de277ba9a237..8b3c6afcde33b 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -41,7 +41,7 @@ type TNodeKinds* = set[TNodeKind] type - TSymFlag* = enum # 51 flags! + TSymFlag* = enum # 52 flags! sfUsed, # read access of sym (for warnings) or simply used sfExported, # symbol is exported from module sfFromGeneric, # symbol is instantiation of a generic; this is needed @@ -126,6 +126,7 @@ type sfByCopy # param is marked as pass bycopy sfMember # proc is a C++ member of a type sfCodegenDecl # type, proc, global or proc param is marked as codegenDecl + sfWasGenSym # symbol was 'gensym'ed TSymFlags* = set[TSymFlag] diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 6b716f75951ca..81c0fb555a4f2 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -76,6 +76,23 @@ proc isHarmlessStore(p: BProc; canRaise: bool; d: TLoc): bool = else: result = false +proc cleanupTemp(p: BProc; returnType: PType, tmp: TLoc): bool = + if returnType.kind in {tyVar, tyLent}: + # we don't need to worry about var/lent return types + result = false + elif hasDestructor(returnType) and getAttachedOp(p.module.g.graph, returnType, attachedDestructor) != nil: + let dtor = getAttachedOp(p.module.g.graph, returnType, attachedDestructor) + var op = initLocExpr(p, newSymNode(dtor)) + var callee = rdLoc(op) + let destroy = if dtor.typ.firstParamType.kind == tyVar: + callee & "(&" & rdLoc(tmp) & ")" + else: + callee & "(" & rdLoc(tmp) & ")" + raiseExitCleanup(p, destroy) + result = true + else: + result = false + proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, callee, params: Rope) = let canRaise = p.config.exc == excGoto and canRaiseDisp(p, ri[0]) @@ -128,18 +145,25 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, if canRaise: raiseExit(p) elif isHarmlessStore(p, canRaise, d): - if d.k == locNone: d = getTemp(p, typ.returnType) + var useTemp = false + if d.k == locNone: + useTemp = true + d = getTemp(p, typ.returnType) assert(d.t != nil) # generate an assignment to d: var list = initLoc(locCall, d.lode, OnUnknown) list.r = pl genAssignment(p, d, list, flags) # no need for deep copying - if canRaise: raiseExit(p) + if canRaise: + if not (useTemp and cleanupTemp(p, typ.returnType, d)): + raiseExit(p) else: var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true) var list = initLoc(locCall, d.lode, OnUnknown) list.r = pl genAssignment(p, tmp, list, flags) # no need for deep copying - if canRaise: raiseExit(p) + if canRaise: + if not cleanupTemp(p, typ.returnType, tmp): + raiseExit(p) genAssignment(p, d, tmp, {}) else: pl.add(");\n") diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 18fac8c7110bd..a2f97f12c9d36 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -3239,7 +3239,8 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode, getNullValueAux(p, t, it, constOrNil, result, count, isConst, info) of nkRecCase: getNullValueAux(p, t, obj[0], constOrNil, result, count, isConst, info) - if count > 0: result.add ", " + var res = "" + if count > 0: res.add ", " var branch = Zero if constOrNil != nil: ## find kind value, default is zero if not specified @@ -3253,18 +3254,21 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode, break let selectedBranch = caseObjDefaultBranch(obj, branch) - result.add "{" + res.add "{" var countB = 0 let b = lastSon(obj[selectedBranch]) # designated initilization is the only way to init non first element of unions # branches are allowed to have no members (b.len == 0), in this case they don't need initializer if b.kind == nkRecList and not isEmptyCaseObjectBranch(b): - result.add "._" & mangleRecFieldName(p.module, obj[0].sym) & "_" & $selectedBranch & " = {" - getNullValueAux(p, t, b, constOrNil, result, countB, isConst, info) - result.add "}" + res.add "._" & mangleRecFieldName(p.module, obj[0].sym) & "_" & $selectedBranch & " = {" + getNullValueAux(p, t, b, constOrNil, res, countB, isConst, info) + res.add "}" elif b.kind == nkSym: - result.add "." & mangleRecFieldName(p.module, b.sym) & " = " - getNullValueAux(p, t, b, constOrNil, result, countB, isConst, info) + res.add "." & mangleRecFieldName(p.module, b.sym) & " = " + getNullValueAux(p, t, b, constOrNil, res, countB, isConst, info) + else: + return + result.add res result.add "}" of nkSym: diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index a55512466beb8..c9783105b87bb 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -301,13 +301,13 @@ proc genCppParamsForCtor(p: BProc; call: PNode; didGenTemp: var bool): string = result.add genCppInitializer(p.module, p, call[i][0].sym.typ, didGenTemp) else: #We need to test for temp in globals, see: #23657 - let param = + let param = if typ[i].kind in {tyVar} and call[i].kind == nkHiddenAddr: call[i][0] else: call[i] - if param.kind != nkBracketExpr or param.typ.kind in - {tyRef, tyPtr, tyUncheckedArray, tyArray, tyOpenArray, + if param.kind != nkBracketExpr or param.typ.kind in + {tyRef, tyPtr, tyUncheckedArray, tyArray, tyOpenArray, tyVarargs, tySequence, tyString, tyCstring, tyTuple}: let tempLoc = initLocExprSingleUse(p, param) didGenTemp = didGenTemp or tempLoc.k == locTemp @@ -755,6 +755,18 @@ proc raiseExit(p: BProc) = lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) goto LA$1_;$n", [p.nestedTryStmts[^1].label]) +proc raiseExitCleanup(p: BProc, destroy: string) = + assert p.config.exc == excGoto + if nimErrorFlagDisabled notin p.flags: + p.flags.incl nimErrorFlagAccessed + if p.nestedTryStmts.len == 0: + p.flags.incl beforeRetNeeded + # easy case, simply goto 'ret': + lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) {$1; goto BeforeRet_;}$n", [destroy]) + else: + lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) {$2; goto LA$1_;}$n", + [p.nestedTryStmts[^1].label, destroy]) + proc finallyActions(p: BProc) = if p.config.exc != excGoto and p.nestedTryStmts.len > 0 and p.nestedTryStmts[^1].inExcept: # if the current try stmt have a finally block, @@ -784,10 +796,14 @@ proc genRaiseStmt(p: BProc, t: PNode) = var e = rdLoc(a) discard getTypeDesc(p.module, t[0].typ) var typ = skipTypes(t[0].typ, abstractPtrs) - # XXX For reasons that currently escape me, this is only required by the new - # C++ based exception handling: - if p.config.exc == excCpp: + case p.config.exc + of excCpp: blockLeaveActions(p, howManyTrys = 0, howManyExcepts = p.inExceptBlockLen) + of excGoto: + blockLeaveActions(p, howManyTrys = 0, + howManyExcepts = (if p.nestedTryStmts.len > 0 and p.nestedTryStmts[^1].inExcept: 1 else: 0)) + else: + discard genLineDir(p, t) if isImportedException(typ, p.config): lineF(p, cpsStmts, "throw $1;$n", [e]) @@ -1566,14 +1582,14 @@ proc genAsmStmt(p: BProc, t: PNode) = if whichPragma(i) == wAsmSyntax: asmSyntax = i[1].strVal - if asmSyntax != "" and + if asmSyntax != "" and not ( asmSyntax == "gcc" and hasGnuAsm in CC[p.config.cCompiler].props or asmSyntax == "vcc" and hasGnuAsm notin CC[p.config.cCompiler].props): localError( - p.config, t.info, + p.config, t.info, "Your compiler does not support the specified inline assembler") - + genAsmOrEmitStmt(p, t, isAsmStmt=true, s) # see bug #2362, "top level asm statements" seem to be a mis-feature # but even if we don't do this, the example in #2362 cannot possibly diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 75b520f3aa7c4..1497381ffc435 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -277,9 +277,12 @@ proc isInvalidReturnType(conf: ConfigRef; typ: PType, isProc = true): bool = if rettype.isImportedCppType or t.isImportedCppType or (typ.callConv == ccCDecl and conf.selectedGC in {gcArc, gcAtomicArc, gcOrc}): # prevents nrvo for cdecl procs; # bug #23401 - return false - result = containsGarbageCollectedRef(t) or - (t.kind == tyObject and not isObjLackingTypeField(t)) + result = false + else: + result = containsGarbageCollectedRef(t) or + (t.kind == tyObject and not isObjLackingTypeField(t)) or + (getSize(conf, rettype) == szUnknownSize and (t.sym == nil or sfImportc notin t.sym.flags)) + else: result = false const diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index 6b106984cb306..c0e5741863eca 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -90,6 +90,9 @@ proc ccgIntroducedPtr*(conf: ConfigRef; s: PSym, retType: PType): bool = if s.typ.sym != nil and sfForward in s.typ.sym.flags: # forwarded objects are *always* passed by pointers for consistency! result = true + elif s.typ.kind == tySink and conf.selectedGC notin {gcArc, gcAtomicArc, gcOrc, gcHooks}: + # bug #23354: + result = false elif (optByRef in s.options) or (getSize(conf, pt) > conf.target.floatSize * 3): result = true # requested anyway elif (tfFinal in pt.flags) and (pt[0] == nil): diff --git a/compiler/cgen.nim b/compiler/cgen.nim index c69e12a2071f7..437928039f9b4 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -752,6 +752,7 @@ proc intLiteral(i: BiggestInt; result: var Rope) proc genLiteral(p: BProc, n: PNode; result: var Rope) proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope; argsCounter: var int) proc raiseExit(p: BProc) +proc raiseExitCleanup(p: BProc, destroy: string) proc initLocExpr(p: BProc, e: PNode, flags: TLocFlags = {}): TLoc = result = initLoc(locNone, e, OnUnknown, flags) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 085576c6b2f40..aba6de49dbec3 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -166,3 +166,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasWarnStdPrefix") defineSymbol("nimHasVtables") + defineSymbol("nimHasJsNoLambdaLifting") diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index bd0875213fc8d..5895368bb56b2 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -56,6 +56,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = # internalAssert c.config, false idTablePut(c.mapping, s, x) if sfGenSym in s.flags: + # TODO: getIdent(c.ic, "`" & x.name.s & "`gensym" & $c.instID) result.add newIdentNode(getIdent(c.ic, x.name.s & "`gensym" & $c.instID), if c.instLines: actual.info else: templ.info) else: diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index c9f546c76a952..22b886ff4db10 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -59,8 +59,8 @@ type emittedTypeInfo*: seq[string] backendFlags*: set[ModuleBackendFlag] - syms*: seq[PackedSym] - types*: seq[PackedType] + syms*: OrderedTable[int32, PackedSym] + types*: OrderedTable[int32, PackedType] strings*: BiTable[string] # we could share these between modules. numbers*: BiTable[BiggestInt] # we also store floats in here so # that we can assure that every bit is kept @@ -362,10 +362,10 @@ proc storeType(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemI result = PackedItemId(module: toLitId(t.uniqueId.module.FileIndex, c, m), item: t.uniqueId.item) if t.uniqueId.module == c.thisModule and not c.typeMarker.containsOrIncl(t.uniqueId.item): - if t.uniqueId.item >= m.types.len: - setLen m.types, t.uniqueId.item+1 + #if t.uniqueId.item >= m.types.len: + # setLen m.types, t.uniqueId.item+1 - var p = PackedType(kind: t.kind, flags: t.flags, callConv: t.callConv, + var p = PackedType(id: t.uniqueId.item, kind: t.kind, flags: t.flags, callConv: t.callConv, size: t.size, align: t.align, nonUniqueId: t.itemId.item, paddingAtEnd: t.paddingAtEnd) storeNode(p, t, n) @@ -396,12 +396,12 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId result = PackedItemId(module: toLitId(s.itemId.module.FileIndex, c, m), item: s.itemId.item) if s.itemId.module == c.thisModule and not c.symMarker.containsOrIncl(s.itemId.item): - if s.itemId.item >= m.syms.len: - setLen m.syms, s.itemId.item+1 + #if s.itemId.item >= m.syms.len: + # setLen m.syms, s.itemId.item+1 assert sfForward notin s.flags - var p = PackedSym(kind: s.kind, flags: s.flags, info: s.info.toPackedInfo(c, m), magic: s.magic, + var p = PackedSym(id: s.itemId.item, kind: s.kind, flags: s.flags, info: s.info.toPackedInfo(c, m), magic: s.magic, position: s.position, offset: s.offset, disamb: s.disamb, options: s.options, name: s.name.s.toLitId(m)) @@ -613,6 +613,10 @@ proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef f.loadSection section f.loadSeq data + template loadTableSection(section, data) {.dirty.} = + f.loadSection section + f.loadOrderedTable data + template loadTabSection(section, data) {.dirty.} = f.loadSection section f.load data @@ -645,8 +649,8 @@ proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef loadTabSection topLevelSection, m.topLevel loadTabSection bodiesSection, m.bodies - loadSeqSection symsSection, m.syms - loadSeqSection typesSection, m.types + loadTableSection symsSection, m.syms + loadTableSection typesSection, m.types loadSeqSection typeInstCacheSection, m.typeInstCache loadSeqSection procInstCacheSection, m.procInstCache @@ -691,6 +695,10 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var Pac f.storeSection section f.store data + template storeTableSection(section, data) {.dirty.} = + f.storeSection section + f.storeOrderedTable data + storeTabSection stringsSection, m.strings storeSeqSection checkSumsSection, m.includes @@ -714,9 +722,9 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var Pac storeTabSection topLevelSection, m.topLevel storeTabSection bodiesSection, m.bodies - storeSeqSection symsSection, m.syms + storeTableSection symsSection, m.syms - storeSeqSection typesSection, m.types + storeTableSection typesSection, m.types storeSeqSection typeInstCacheSection, m.typeInstCache storeSeqSection procInstCacheSection, m.procInstCache @@ -767,8 +775,8 @@ type status*: ModuleStatus symsInit, typesInit, loadedButAliveSetChanged*: bool fromDisk*: PackedModule - syms: seq[PSym] # indexed by itemId - types: seq[PType] + syms: OrderedTable[int32, PSym] # indexed by itemId + types: OrderedTable[int32, PType] module*: PSym # the one true module symbol. iface, ifaceHidden: Table[PIdent, seq[PackedItemId]] # PackedItemId so that it works with reexported symbols too @@ -961,11 +969,11 @@ proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; s: loadToReplayNodes(g, c.config, c.cache, m, g[int m]) assert g[si].status in {loaded, storing, stored} - if not g[si].symsInit: - g[si].symsInit = true - setLen g[si].syms, g[si].fromDisk.syms.len + #if not g[si].symsInit: + # g[si].symsInit = true + # setLen g[si].syms, g[si].fromDisk.syms.len - if g[si].syms[s.item] == nil: + if g[si].syms.getOrDefault(s.item) == nil: if g[si].fromDisk.syms[s.item].kind != skModule: result = symHeaderFromPacked(c, g, g[si].fromDisk.syms[s.item], si, s.item) # store it here early on, so that recursions work properly: @@ -1012,11 +1020,11 @@ proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; t assert g[si].status in {loaded, storing, stored} assert t.item > 0 - if not g[si].typesInit: - g[si].typesInit = true - setLen g[si].types, g[si].fromDisk.types.len + #if not g[si].typesInit: + # g[si].typesInit = true + # setLen g[si].types, g[si].fromDisk.types.len - if g[si].types[t.item] == nil: + if g[si].types.getOrDefault(t.item) == nil: result = typeHeaderFromPacked(c, g, g[si].fromDisk.types[t.item], si, t.item) # store it here early on, so that recursions work properly: g[si].types[t.item] = result @@ -1155,10 +1163,7 @@ proc loadProcBody*(config: ConfigRef, cache: IdentCache; proc loadTypeFromId*(config: ConfigRef, cache: IdentCache; g: var PackedModuleGraph; module: int; id: PackedItemId): PType = bench g.loadType: - if id.item < g[module].types.len: - result = g[module].types[id.item] - else: - result = nil + result = g[module].types.getOrDefault(id.item) if result == nil: var decoder = PackedDecoder( lastModule: int32(-1), @@ -1171,10 +1176,7 @@ proc loadTypeFromId*(config: ConfigRef, cache: IdentCache; proc loadSymFromId*(config: ConfigRef, cache: IdentCache; g: var PackedModuleGraph; module: int; id: PackedItemId): PSym = bench g.loadSym: - if id.item < g[module].syms.len: - result = g[module].syms[id.item] - else: - result = nil + result = g[module].syms.getOrDefault(id.item) if result == nil: var decoder = PackedDecoder( lastModule: int32(-1), @@ -1190,19 +1192,6 @@ proc translateId*(id: PackedItemId; g: PackedModuleGraph; thisModule: int; confi else: ItemId(module: toFileIndex(id.module, g[thisModule].fromDisk, config).int32, item: id.item) -proc checkForHoles(m: PackedModule; config: ConfigRef; moduleId: int) = - var bugs = 0 - for i in 1 .. high(m.syms): - if m.syms[i].kind == skUnknown: - echo "EMPTY ID ", i, " module ", moduleId, " ", toFullPath(config, FileIndex(moduleId)) - inc bugs - assert bugs == 0 - when false: - var nones = 0 - for i in 1 .. high(m.types): - inc nones, m.types[i].kind == tyNone - assert nones < 1 - proc simulateLoadedModule*(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache; moduleSym: PSym; m: PackedModule) = # For now only used for heavy debugging. In the future we could use this to reduce the diff --git a/compiler/ic/integrity.nim b/compiler/ic/integrity.nim index d78e5684797c8..3e8ea25034db6 100644 --- a/compiler/ic/integrity.nim +++ b/compiler/ic/integrity.nim @@ -10,7 +10,7 @@ ## Integrity checking for a set of .rod files. ## The set must cover a complete Nim project. -import std/sets +import std/[sets, tables] when defined(nimPreviewSlimSystem): import std/assertions @@ -108,18 +108,18 @@ proc checkModule(c: var CheckedContext; m: PackedModule) = # We check that: # - Every symbol references existing types and symbols. # - Every tree node references existing types and symbols. - for i in 0..high(m.syms): - checkLocalSym c, int32(i) + for _, v in pairs(m.syms): + checkLocalSym c, v.id checkTree c, m.toReplay checkTree c, m.topLevel for e in m.exports: - assert e[1] >= 0 and e[1] < m.syms.len + #assert e[1] >= 0 and e[1] < m.syms.len assert e[0] == m.syms[e[1]].name for e in m.compilerProcs: - assert e[1] >= 0 and e[1] < m.syms.len + #assert e[1] >= 0 and e[1] < m.syms.len assert e[0] == m.syms[e[1]].name checkLocalSymIds c, m, m.converters diff --git a/compiler/ic/navigator.nim b/compiler/ic/navigator.nim index da8e7e597930e..ca2f981a1a379 100644 --- a/compiler/ic/navigator.nim +++ b/compiler/ic/navigator.nim @@ -11,7 +11,7 @@ ## IDE-like features. It uses the set of .rod files to accomplish ## its task. The set must cover a complete Nim project. -import std/sets +import std/[sets, tables] from std/os import nil from std/private/miscdollars import toLocation diff --git a/compiler/ic/packed_ast.nim b/compiler/ic/packed_ast.nim index 2599e07d11d9a..392f6b325909b 100644 --- a/compiler/ic/packed_ast.nim +++ b/compiler/ic/packed_ast.nim @@ -47,6 +47,7 @@ type path*: NodeId PackedSym* = object + id*: int32 kind*: TSymKind name*: LitId typ*: PackedItemId @@ -71,6 +72,7 @@ type instantiatedFrom*: PackedItemId PackedType* = object + id*: int32 kind*: TTypeKind callConv*: TCallingConvention #nodekind*: TNodeKind diff --git a/compiler/ic/rodfiles.nim b/compiler/ic/rodfiles.nim index 5eef3874a2995..ac995dd2eb4a0 100644 --- a/compiler/ic/rodfiles.nim +++ b/compiler/ic/rodfiles.nim @@ -19,6 +19,8 @@ from std/typetraits import supportsCopyMem when defined(nimPreviewSlimSystem): import std/[syncio, assertions] +import std / tables + ## Overview ## ======== ## `RodFile` represents a Rod File (versioned binary format), and the @@ -170,6 +172,18 @@ proc storeSeq*[T](f: var RodFile; s: seq[T]) = for i in 0..= high(int32): + setError f, tooBig + return + var lenPrefix = int32(s.len) + if writeBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix): + setError f, ioFailure + else: + for _, v in s: + storePrim(f, v) + proc loadPrim*(f: var RodFile; s: var string) = ## Read a string, the length was stored as a prefix if f.err != ok: return @@ -211,6 +225,19 @@ proc loadSeq*[T](f: var RodFile; s: var seq[T]) = for i in 0..= 3: # echo "BEGIN generating code for: " & prc.name.s var p = newProc(oldProc.g, oldProc.module, prc.ast, prc.options) + p.up = oldProc var returnStmt: Rope = "" var resultAsgn: Rope = "" var name = mangleName(p.module, prc) @@ -2911,14 +2951,17 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = else: genCall(p, n, r) of nkClosure: - let tmp = getTemp(p) - var a: TCompRes = default(TCompRes) - var b: TCompRes = default(TCompRes) - gen(p, n[0], a) - gen(p, n[1], b) - lineF(p, "$1 = $2.bind($3); $1.ClP_0 = $2; $1.ClE_0 = $3;$n", [tmp, a.rdLoc, b.rdLoc]) - r.res = tmp - r.kind = resVal + if jsNoLambdaLifting in p.config.legacyFeatures: + gen(p, n[0], r) + else: + let tmp = getTemp(p) + var a: TCompRes = default(TCompRes) + var b: TCompRes = default(TCompRes) + gen(p, n[0], a) + gen(p, n[1], b) + lineF(p, "$1 = $2.bind($3); $1.ClP_0 = $2; $1.ClE_0 = $3;$n", [tmp, a.rdLoc, b.rdLoc]) + r.res = tmp + r.kind = resVal of nkCurly: genSetConstr(p, n, r) of nkBracket: genArrayConstr(p, n, r) of nkPar, nkTupleConstr: genTupleConstr(p, n, r) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index faa043cb49e75..e51fb3ae01ccb 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -239,6 +239,11 @@ proc interestingIterVar(s: PSym): bool {.inline.} = template isIterator*(owner: PSym): bool = owner.kind == skIterator and owner.typ.callConv == ccClosure +template liftingHarmful(conf: ConfigRef; owner: PSym): bool = + ## lambda lifting can be harmful for JS-like code generators. + let isCompileTime = sfCompileTime in owner.flags or owner.kind == skMacro + jsNoLambdaLifting in conf.legacyFeatures and conf.backend == backendJs and not isCompileTime + proc createTypeBoundOpsLL(g: ModuleGraph; refType: PType; info: TLineInfo; idgen: IdGenerator; owner: PSym) = if owner.kind != skMacro: createTypeBoundOps(g, nil, refType.elementType, info, idgen) @@ -255,6 +260,7 @@ proc genCreateEnv(env: PNode): PNode = proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode = # transforms (iter) to (let env = newClosure[iter](); (iter, env)) + if liftingHarmful(g.config, owner): return n let iter = n.sym assert iter.isIterator @@ -879,7 +885,8 @@ proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool; idgen: IdGenerator; flags: TransformFlags): PNode = let isCompileTime = sfCompileTime in fn.flags or fn.kind == skMacro - if body.kind == nkEmpty or + if body.kind == nkEmpty or (jsNoLambdaLifting in g.config.legacyFeatures and + g.config.backend == backendJs and not isCompileTime) or (fn.skipGenericOwner.kind != skModule and force notin flags): # ignore forward declaration: @@ -939,6 +946,7 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym): break ... """ + if liftingHarmful(g.config, owner): return body if not (body.kind == nkForStmt and body[^2].kind in nkCallKinds): localError(g.config, body.info, "ignored invalid for loop") return body diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index f62927cec7714..0bbaf3b33e36d 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -157,7 +157,7 @@ proc genWasMovedCall(c: var TLiftCtx; op: PSym; x: PNode): PNode = result.add(newSymNode(op)) result.add genAddr(c, x) -proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) = +proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool, enforceWasMoved = false) = case n.kind of nkSym: if c.filterDiscriminator != nil: return @@ -167,6 +167,8 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) enforceDefaultOp: defaultOp(c, f.typ, body, x.dotField(f), b) else: + if enforceWasMoved: + body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x.dotField(f)) fillBody(c, f.typ, body, x.dotField(f), b) of nkNilLit: discard of nkRecCase: @@ -205,7 +207,7 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) branch[^1] = newNodeI(nkStmtList, c.info) fillBodyObj(c, n[i].lastSon, branch[^1], x, y, - enforceDefaultOp = localEnforceDefaultOp) + enforceDefaultOp = localEnforceDefaultOp, enforceWasMoved = c.kind == attachedAsgn) if branch[^1].len == 0: inc emptyBranches caseStmt.add(branch) if emptyBranches != n.len-1: @@ -216,7 +218,7 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) fillBodyObj(c, n[0], body, x, y, enforceDefaultOp = false) c.filterDiscriminator = oldfilterDiscriminator of nkRecList: - for t in items(n): fillBodyObj(c, t, body, x, y, enforceDefaultOp) + for t in items(n): fillBodyObj(c, t, body, x, y, enforceDefaultOp, enforceWasMoved) else: illFormedAstLocal(n, c.g.config) @@ -282,6 +284,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) = c.kind = attachedDestructor fillBodyObjTImpl(c, t, body, blob, y) c.kind = prevKind + else: fillBodyObjTImpl(c, t, body, x, y) diff --git a/compiler/options.nim b/compiler/options.nim index 356aa6cc80912..a6a2b084e3e00 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -246,6 +246,8 @@ type emitGenerics ## generics are emitted in the module that contains them. ## Useful for libraries that rely on local passC + jsNoLambdaLifting + ## Old transformation for closures in JS backend SymbolFilesOption* = enum disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest diff --git a/compiler/sem.nim b/compiler/sem.nim index 760b83941d109..d813a791d2a90 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -254,6 +254,8 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym = result.owner = getCurrOwner(c) else: result = newSym(kind, considerQuotedIdent(c, n), c.idgen, getCurrOwner(c), n.info) + if find(result.name.s, '`') >= 0: + result.flags.incl sfWasGenSym #if kind in {skForVar, skLet, skVar} and result.owner.kind == skModule: # incl(result.flags, sfGlobal) when defined(nimsuggest): @@ -263,7 +265,7 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, allowed: TSymFlags): PSym # identifier with visibility proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, - allowed: TSymFlags): PSym + allowed: TSymFlags, fromTopLevel = false): PSym proc typeAllowedCheck(c: PContext; info: TLineInfo; typ: PType; kind: TSymKind; flags: TTypeAllowedFlags = {}) = diff --git a/compiler/seminst.nim b/compiler/seminst.nim index e9b46c382ef4f..37f081dbc72bc 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -56,8 +56,11 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TypeMapping): PS elif t.kind in {tyGenericParam, tyConcept}: localError(c.config, a.info, errCannotInstantiateX % q.name.s) t = errorType(c) - elif isUnresolvedStatic(t) and c.inGenericContext == 0 and - c.matchedConcept == nil: + elif isUnresolvedStatic(t) and (q.typ.kind == tyStatic or + (q.typ.kind == tyGenericParam and + q.typ.genericParamHasConstraints and + q.typ.genericConstraint.kind == tyStatic)) and + c.inGenericContext == 0 and c.matchedConcept == nil: # generic/concept type bodies will try to instantiate static values but # won't actually use them localError(c.config, a.info, errCannotInstantiateX % q.name.s) @@ -396,6 +399,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TypeMapping, for _, param in paramTypes(result.typ): entry.concreteTypes[i] = param inc i + #echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " ", entry.concreteTypes.len if tfTriggersCompileTime in result.typ.flags: incl(result.flags, sfCompileTime) n[genericParamsPos] = c.graph.emptyNode @@ -424,7 +428,9 @@ proc generateInstance(c: PContext, fn: PSym, pt: TypeMapping, if result.magic notin {mSlice, mTypeOf}: # 'toOpenArray' is special and it is allowed to return 'openArray': paramsTypeCheck(c, result.typ) + #echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " <-- NEW PROC!", " ", entry.concreteTypes.len else: + #echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " <-- CACHED! ", typeToString(oldPrc.typ), " ", entry.concreteTypes.len result = oldPrc popProcCon(c) popInfoContext(c.config) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 8c7dda4e7c2c2..e4f22f4359eb4 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -480,7 +480,7 @@ proc identWithin(n: PNode, s: PIdent): bool = proc semIdentDef(c: PContext, n: PNode, kind: TSymKind, reportToNimsuggest = true): PSym = if isTopLevel(c): - result = semIdentWithPragma(c, kind, n, {sfExported}) + result = semIdentWithPragma(c, kind, n, {sfExported}, fromTopLevel = true) incl(result.flags, sfGlobal) #if kind in {skVar, skLet}: # echo "global variable here ", n.info, " ", result.name.s diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index c79c63a3f2685..38daa9513441a 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -540,7 +540,7 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, result = newSymG(kind, n, c) proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, - allowed: TSymFlags): PSym = + allowed: TSymFlags, fromTopLevel = false): PSym = if n.kind == nkPragmaExpr: checkSonsLen(n, 2, c.config) result = semIdentVis(c, kind, n[0], allowed) @@ -555,11 +555,15 @@ proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, else: discard else: result = semIdentVis(c, kind, n, allowed) + let invalidPragmasForPush = if fromTopLevel and sfWasGenSym notin result.flags: + {} + else: + {wExportc, wExportCpp, wDynlib} case kind of skField: implicitPragmas(c, result, n.info, fieldPragmas) - of skVar: implicitPragmas(c, result, n.info, varPragmas) - of skLet: implicitPragmas(c, result, n.info, letPragmas) - of skConst: implicitPragmas(c, result, n.info, constPragmas) + of skVar: implicitPragmas(c, result, n.info, varPragmas-invalidPragmasForPush) + of skLet: implicitPragmas(c, result, n.info, letPragmas-invalidPragmasForPush) + of skConst: implicitPragmas(c, result, n.info, constPragmas-invalidPragmasForPush) else: discard proc checkForOverlap(c: PContext, t: PNode, currentEx, branchIndex: int) = diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 242dd4ec6fa84..64d5df7887cc5 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -2491,7 +2491,7 @@ proc arrayConstr(c: PContext, n: PNode): PType = result = newTypeS(tyArray, c) rawAddSon(result, makeRangeType(c, 0, 0, n.info)) addSonSkipIntLit(result, skipTypes(n.typ, - {tyGenericInst, tyVar, tyLent, tyOrdinal}), c.idgen) + {tyVar, tyLent, tyOrdinal}), c.idgen) proc arrayConstr(c: PContext, info: TLineInfo): PType = result = newTypeS(tyArray, c) diff --git a/compiler/transf.nim b/compiler/transf.nim index 6888fc223d560..8dcb747292781 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -513,6 +513,7 @@ proc generateThunk(c: PTransf; prc: PNode, dest: PType): PNode = # we cannot generate a proper thunk here for GC-safety reasons # (see internal documentation): + if jsNoLambdaLifting in c.graph.config.legacyFeatures and c.graph.config.backend == backendJs: return prc result = newNodeIT(nkClosure, prc.info, dest) var conv = newNodeIT(nkHiddenSubConv, prc.info, dest) conv.add(newNodeI(nkEmpty, prc.info)) diff --git a/compiler/types.nim b/compiler/types.nim index e5ce0aea14989..880f811dd24d8 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1316,9 +1316,17 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = result = sameTypeOrNilAux(a.elementType, b.elementType, c) and sameValue(a.n[0], b.n[0]) and sameValue(a.n[1], b.n[1]) - of tyGenericInst, tyAlias, tyInferred, tyIterable: + of tyAlias, tyInferred, tyIterable: cycleCheck() result = sameTypeAux(a.skipModifier, b.skipModifier, c) + of tyGenericInst: + # BUG #23445 + # The type system must distinguish between `T[int] = object #[empty]#` + # and `T[float] = object #[empty]#`! + cycleCheck() + for ff, aa in underspecifiedPairs(a, b, 1, -1): + if not sameTypeAux(ff, aa, c): return false + result = sameTypeAux(a.skipModifier, b.skipModifier, c) of tyNone: result = false of tyConcept: result = exprStructuralEquivalent(a.n, b.n) diff --git a/doc/manual.md b/doc/manual.md index bb7b603a8580e..16c116328ac32 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -8703,7 +8703,7 @@ after the last specified parameter. Nim string values will be converted to C strings automatically: ```Nim - proc printf(formatstr: cstring) {.nodecl, varargs.} + proc printf(formatstr: cstring) {.header: "", varargs.} printf("hallo %s", "world") # "world" will be passed as C string ``` diff --git a/lib/nimbase.h b/lib/nimbase.h index 2204987db0c64..c66be40c24abd 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -272,11 +272,15 @@ __EMSCRIPTEN__ #elif defined(__cplusplus) #define NIM_STATIC_ASSERT(x, msg) static_assert((x), msg) #else -#define NIM_STATIC_ASSERT(x, msg) typedef int NIM_STATIC_ASSERT_AUX[(x) ? 1 : -1]; +#define _NIM_STATIC_ASSERT_FINAL(x, append_name) typedef int NIM_STATIC_ASSERT_AUX ## append_name[(x) ? 1 : -1]; +#define _NIM_STATIC_ASSERT_STAGE_3(x, line) _NIM_STATIC_ASSERT_FINAL(x, _AT_LINE_##line) +#define _NIM_STATIC_ASSERT_STAGE_2(x, line) _NIM_STATIC_ASSERT_STAGE_3(x, line) +#define NIM_STATIC_ASSERT(x, msg) _NIM_STATIC_ASSERT_STAGE_2(x,__LINE__) // On failure, your C compiler will say something like: -// "error: 'NIM_STATIC_ASSERT_AUX' declared as an array with a negative size" -// we could use a better fallback to also show line number, using: -// http://www.pixelbeat.org/programming/gcc/static_assert.html +// "error: 'NIM_STATIC_ASSERT_AUX_AT_LINE_XXX' declared as an array with a negative size" +// Adding the line number helps to avoid redefinitions which are not allowed in +// old GCC versions, however the order of evaluation for __LINE__ is a little tricky, +// hence all the helper macros. See https://stackoverflow.com/a/3385694 for more info. #endif /* C99 compiler? */ diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index 51829951166be..9e16cea9eab5f 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -379,6 +379,145 @@ proc hashVmImplChar(x: openArray[char], sPos, ePos: int): Hash = proc hashVmImplByte(x: openArray[byte], sPos, ePos: int): Hash = raiseAssert "implementation override in compiler/vmops.nim" +const k0 = 0xc3a5c85c97cb3127u64 # Primes on (2^63, 2^64) for various uses +const k1 = 0xb492b66fbe98f273u64 +const k2 = 0x9ae16a3b2f90404fu64 + +proc load4e(s: openArray[byte], o=0): uint32 {.inline.} = + uint32(s[o + 3]) shl 24 or uint32(s[o + 2]) shl 16 or + uint32(s[o + 1]) shl 8 or uint32(s[o + 0]) + +proc load8e(s: openArray[byte], o=0): uint64 {.inline.} = + uint64(s[o + 7]) shl 56 or uint64(s[o + 6]) shl 48 or + uint64(s[o + 5]) shl 40 or uint64(s[o + 4]) shl 32 or + uint64(s[o + 3]) shl 24 or uint64(s[o + 2]) shl 16 or + uint64(s[o + 1]) shl 8 or uint64(s[o + 0]) + +proc load4(s: openArray[byte], o=0): uint32 {.inline.} = + when nimvm: result = load4e(s, o) + else: + when declared copyMem: copyMem result.addr, s[o].addr, result.sizeof + else: result = load4e(s, o) + +proc load8(s: openArray[byte], o=0): uint64 {.inline.} = + when nimvm: result = load8e(s, o) + else: + when declared copyMem: copyMem result.addr, s[o].addr, result.sizeof + else: result = load8e(s, o) + +proc lenU(s: openArray[byte]): uint64 {.inline.} = s.len.uint64 + +proc shiftMix(v: uint64): uint64 {.inline.} = v xor (v shr 47) + +proc rotR(v: uint64; bits: cint): uint64 {.inline.} = + (v shr bits) or (v shl (64 - bits)) + +proc len16(u: uint64; v: uint64; mul: uint64): uint64 {.inline.} = + var a = (u xor v)*mul + a = a xor (a shr 47) + var b = (v xor a)*mul + b = b xor (b shr 47) + b*mul + +proc len0_16(s: openArray[byte]): uint64 {.inline.} = + if s.len >= 8: + let mul = k2 + 2*s.lenU + let a = load8(s) + k2 + let b = load8(s, s.len - 8) + let c = rotR(b, 37)*mul + a + let d = (rotR(a, 25) + b)*mul + len16 c, d, mul + elif s.len >= 4: + let mul = k2 + 2*s.lenU + let a = load4(s).uint64 + len16 s.lenU + (a shl 3), load4(s, s.len - 4), mul + elif s.len > 0: + let a = uint32(s[0]) + let b = uint32(s[s.len shr 1]) + let c = uint32(s[s.len - 1]) + let y = a + (b shl 8) + let z = s.lenU + (c shl 2) + shiftMix(y*k2 xor z*k0)*k2 + else: k2 # s.len == 0 + +proc len17_32(s: openArray[byte]): uint64 {.inline.} = + let mul = k2 + 2*s.lenU + let a = load8(s)*k1 + let b = load8(s, 8) + let c = load8(s, s.len - 8)*mul + let d = load8(s, s.len - 16)*k2 + len16 rotR(a + b, 43) + rotR(c, 30) + d, a + rotR(b + k2, 18) + c, mul + +proc len33_64(s: openArray[byte]): uint64 {.inline.} = + let mul = k2 + 2*s.lenU + let a = load8(s)*k2 + let b = load8(s, 8) + let c = load8(s, s.len - 8)*mul + let d = load8(s, s.len - 16)*k2 + let y = rotR(a + b, 43) + rotR(c, 30) + d + let z = len16(y, a + rotR(b + k2, 18) + c, mul) + let e = load8(s, 16)*mul + let f = load8(s, 24) + let g = (y + load8(s, s.len - 32))*mul + let h = (z + load8(s, s.len - 24))*mul + len16 rotR(e + f, 43) + rotR(g, 30) + h, e + rotR(f + a, 18) + g, mul + +type Pair = tuple[first, second: uint64] + +proc weakLen32withSeeds2(w, x, y, z, a, b: uint64): Pair {.inline.} = + var a = a + w + var b = rotR(b + a + z, 21) + let c = a + a += x + a += y + b += rotR(a, 44) + result[0] = a + z + result[1] = b + c + +proc weakLen32withSeeds(s: openArray[byte]; o: int; a,b: uint64): Pair {.inline.} = + weakLen32withSeeds2 load8(s, o ), load8(s, o + 8), + load8(s, o + 16), load8(s, o + 24), a, b + +proc hashFarm(s: openArray[byte]): uint64 {.inline.} = + if s.len <= 16: return len0_16(s) + if s.len <= 32: return len17_32(s) + if s.len <= 64: return len33_64(s) + const seed = 81u64 # not const to use input `h` + var + o = 0 # s[] ptr arith -> variable origin variable `o` + x = seed + y = seed*k1 + 113 + z = shiftMix(y*k2 + 113)*k2 + v, w: Pair + x = x*k2 + load8(s) + let eos = ((s.len - 1) div 64)*64 + let last64 = eos + ((s.len - 1) and 63) - 63 + while true: + x = rotR(x + y + v[0] + load8(s, o+8), 37)*k1 + y = rotR(y + v[1] + load8(s, o+48), 42)*k1 + x = x xor w[1] + y += v[0] + load8(s, o+40) + z = rotR(z + w[0], 33)*k1 + v = weakLen32withSeeds(s, o+0 , v[1]*k1, x + w[0]) + w = weakLen32withSeeds(s, o+32, z + w[1], y + load8(s, o+16)) + swap z, x + inc o, 64 + if o == eos: break + let mul = k1 + ((z and 0xff) shl 1) + o = last64 + w[0] += (s.lenU - 1) and 63 + v[0] += w[0] + w[0] += v[0] + x = rotR(x + y + v[0] + load8(s, o+8), 37)*mul + y = rotR(y + v[1] + load8(s, o+48), 42)*mul + x = x xor w[1]*9 + y += v[0]*9 + load8(s, o+40) + z = rotR(z + w[0], 33)*mul + v = weakLen32withSeeds(s, o+0 , v[1]*mul, x + w[0]) + w = weakLen32withSeeds(s, o+32, z + w[1], y + load8(s, o+16)) + swap z, x + len16 len16(v[0],w[0],mul) + shiftMix(y)*k0 + z, len16(v[1],w[1],mul) + x, mul + proc hash*(x: string): Hash = ## Efficient hashing of strings. ## @@ -388,10 +527,13 @@ proc hash*(x: string): Hash = runnableExamples: doAssert hash("abracadabra") != hash("AbracadabrA") - when nimvm: - result = hashVmImpl(x, 0, high(x)) + when defined nimPreviewHashFarm: # Default switched -> `not nimStringHash2` + result = cast[Hash](hashFarm(toOpenArrayByte(x, 0, x.high))) else: - result = murmurHash(toOpenArrayByte(x, 0, high(x))) + when nimvm: + result = hashVmImpl(x, 0, high(x)) + else: + result = murmurHash(toOpenArrayByte(x, 0, high(x))) proc hash*(x: cstring): Hash = ## Efficient hashing of null-terminated strings. @@ -400,14 +542,21 @@ proc hash*(x: cstring): Hash = doAssert hash(cstring"AbracadabrA") == hash("AbracadabrA") doAssert hash(cstring"abracadabra") != hash(cstring"AbracadabrA") - when nimvm: - hashVmImpl(x, 0, high(x)) + when defined nimPreviewHashFarm: # Default switched -> `not nimStringHash2` + when defined js: + let xx = $x + result = cast[Hash](hashFarm(toOpenArrayByte(xx, 0, xx.high))) + else: + result = cast[Hash](hashFarm(toOpenArrayByte(x, 0, x.high))) else: - when not defined(js): - murmurHash(toOpenArrayByte(x, 0, x.high)) + when nimvm: + hashVmImpl(x, 0, high(x)) else: - let xx = $x - murmurHash(toOpenArrayByte(xx, 0, high(xx))) + when not defined(js): + murmurHash(toOpenArrayByte(x, 0, x.high)) + else: + let xx = $x + murmurHash(toOpenArrayByte(xx, 0, high(xx))) proc hash*(sBuf: string, sPos, ePos: int): Hash = ## Efficient hashing of a string buffer, from starting @@ -418,7 +567,10 @@ proc hash*(sBuf: string, sPos, ePos: int): Hash = var a = "abracadabra" doAssert hash(a, 0, 3) == hash(a, 7, 10) - murmurHash(toOpenArrayByte(sBuf, sPos, ePos)) + when defined nimPreviewHashFarm: # Default switched -> `not nimStringHash2` + result = cast[Hash](hashFarm(toOpenArrayByte(sBuf, sPos, ePos))) + else: + murmurHash(toOpenArrayByte(sBuf, sPos, ePos)) proc hashIgnoreStyle*(x: string): Hash = ## Efficient hashing of strings; style is ignored. @@ -553,12 +705,18 @@ proc hash*[A](x: openArray[A]): Hash = ## Efficient hashing of arrays and sequences. ## There must be a `hash` proc defined for the element type `A`. when A is byte: - result = murmurHash(x) + when defined nimPreviewHashFarm: # Default switched -> `not nimStringHash2` + result = cast[Hash](hashFarm(x)) + else: + result = murmurHash(x) elif A is char: - when nimvm: - result = hashVmImplChar(x, 0, x.high) + when defined nimPreviewHashFarm: # Default switched -> `not nimStringHash2` + result = cast[Hash](hashFarm(toOpenArrayByte(x, 0, x.high))) else: - result = murmurHash(toOpenArrayByte(x, 0, x.high)) + when nimvm: + result = hashVmImplChar(x, 0, x.high) + else: + result = murmurHash(toOpenArrayByte(x, 0, x.high)) else: result = 0 for a in x: @@ -576,15 +734,21 @@ proc hash*[A](aBuf: openArray[A], sPos, ePos: int): Hash = doAssert hash(a, 0, 1) == hash(a, 3, 4) when A is byte: - when nimvm: - result = hashVmImplByte(aBuf, sPos, ePos) + when defined nimPreviewHashFarm: # Default switched -> `not nimStringHash2` + result = cast[Hash](hashFarm(toOpenArray(aBuf, sPos, ePos))) else: - result = murmurHash(toOpenArray(aBuf, sPos, ePos)) + when nimvm: + result = hashVmImplByte(aBuf, sPos, ePos) + else: + result = murmurHash(toOpenArray(aBuf, sPos, ePos)) elif A is char: - when nimvm: - result = hashVmImplChar(aBuf, sPos, ePos) + when defined nimPreviewHashFarm: # Default switched -> `not nimStringHash2` + result = cast[Hash](hashFarm(toOpenArrayByte(aBuf, sPos, ePos))) else: - result = murmurHash(toOpenArrayByte(aBuf, sPos, ePos)) + when nimvm: + result = hashVmImplChar(aBuf, sPos, ePos) + else: + result = murmurHash(toOpenArrayByte(aBuf, sPos, ePos)) else: for i in sPos .. ePos: result = result !& hash(aBuf[i]) diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index df5b8c46ff1ef..b10f7ad91661f 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -35,12 +35,12 @@ proc newEIO(msg: string): ref IOError = new(result) result.msg = msg -proc setFileSize(fh: FileHandle, newFileSize = -1): OSErrorCode = - ## Set the size of open file pointed to by `fh` to `newFileSize` if != -1. - ## Space is only allocated if that is cheaper than writing to the file. This - ## routine returns the last OSErrorCode found rather than raising to support - ## old rollback/clean-up code style. [ Should maybe move to std/osfiles. ] - if newFileSize == -1: +proc setFileSize(fh: FileHandle, newFileSize = -1, oldSize = -1): OSErrorCode = + ## Set the size of open file pointed to by `fh` to `newFileSize` if != -1, + ## allocating | freeing space from the file system. This routine returns the + ## last OSErrorCode found rather than raising to support old rollback/clean-up + ## code style. [ Should maybe move to std/osfiles. ] + if newFileSize < 0 or newFileSize == oldSize: return when defined(windows): var sizeHigh = int32(newFileSize shr 32) @@ -51,14 +51,18 @@ proc setFileSize(fh: FileHandle, newFileSize = -1): OSErrorCode = setEndOfFile(fh) == 0: result = lastErr else: - var e: cint # posix_fallocate truncates up when needed. - when declared(posix_fallocate): - while (e = posix_fallocate(fh, 0, newFileSize); e == EINTR): - discard - if e in [EINVAL, EOPNOTSUPP] and ftruncate(fh, newFileSize) == -1: - result = osLastError() # fallback arguable; Most portable, but allows SEGV - elif e != 0: - result = osLastError() + if newFileSize > oldSize: # grow the file + var e: cint # posix_fallocate truncates up when needed. + when declared(posix_fallocate): + while (e = posix_fallocate(fh, 0, newFileSize); e == EINTR): + discard + if e in [EINVAL, EOPNOTSUPP] and ftruncate(fh, newFileSize) == -1: + result = osLastError() # fallback arguable; Most portable BUT allows SEGV + elif e != 0: + result = osLastError() + else: # shrink the file + if ftruncate(fh.cint, newFileSize) == -1: + result = osLastError() type MemFile* = object ## represents a memory mapped file @@ -255,41 +259,31 @@ proc open*(filename: string, mode: FileMode = fmRead, flags = flags or O_CREAT or O_TRUNC var permissionsMode = S_IRUSR or S_IWUSR result.handle = open(filename, flags, permissionsMode) + if result.handle != -1: + if (let e = setFileSize(result.handle.FileHandle, newFileSize); + e != 0.OSErrorCode): fail(e, "error setting file size") else: result.handle = open(filename, flags) if result.handle == -1: - # XXX: errno is supposed to be set here - # Is there an exception that wraps it? fail(osLastError(), "error opening file") - if (let e = setFileSize(result.handle.FileHandle, newFileSize); - e != 0.OSErrorCode): fail(e, "error setting file size") - - if mappedSize != -1: - result.size = mappedSize - else: - var stat: Stat + if mappedSize != -1: #XXX Logic here differs from `when windows` branch .. + result.size = mappedSize #.. which always fstats&Uses min(mappedSize, st). + else: # if newFileSize!=-1: result.size=newFileSize # if trust setFileSize + var stat: Stat #^^.. BUT some FSes (eg. Linux HugeTLBfs) round to 2MiB. if fstat(result.handle, stat) != -1: - # XXX: Hmm, this could be unsafe - # Why is mmap taking int anyway? - result.size = int(stat.st_size) + result.size = stat.st_size.int # int may be 32-bit-unsafe for 2..<4 GiB else: fail(osLastError(), "error getting file size") result.flags = if mapFlags == cint(-1): MAP_SHARED else: mapFlags - #Ensure exactly one of MAP_PRIVATE cr MAP_SHARED is set + # Ensure exactly one of MAP_PRIVATE cr MAP_SHARED is set if int(result.flags and MAP_PRIVATE) == 0: result.flags = result.flags or MAP_SHARED - result.mem = mmap( - nil, - result.size, - if readonly: PROT_READ else: PROT_READ or PROT_WRITE, - result.flags, - result.handle, - offset) - + let pr = if readonly: PROT_READ else: PROT_READ or PROT_WRITE + result.mem = mmap(nil, result.size, pr, result.flags, result.handle, offset) if result.mem == cast[pointer](MAP_FAILED): fail(osLastError(), "file mapping failed") @@ -353,7 +347,7 @@ proc resize*(f: var MemFile, newFileSize: int) {.raises: [IOError, OSError].} = raise newException(IOError, "Cannot resize MemFile opened with allowRemap=false") if newFileSize != f.size: - if (let e = setFileSize(f.handle.FileHandle, newFileSize); + if (let e = setFileSize(f.handle.FileHandle, newFileSize, f.size); e != 0.OSErrorCode): raiseOSError(e) when defined(linux): #Maybe NetBSD, too? # On Linux this can be over 100 times faster than a munmap,mmap cycle. diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 6a7b4af1b25d8..78ebb1c88aa2f 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -692,7 +692,10 @@ proc getAppDir*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noWeirdT proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect], noWeirdTarget.} = ## Sleeps `milsecs` milliseconds. + ## A negative `milsecs` causes sleep to return immediately. when defined(windows): + if milsecs < 0: + return # fixes #23732 winlean.sleep(int32(milsecs)) else: var a, b: Timespec diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim index ce642fcf7396d..2ca255fa031f4 100644 --- a/lib/pure/parseutils.nim +++ b/lib/pure/parseutils.nim @@ -460,6 +460,8 @@ proc parseBiggestInt*(s: openArray[char], number: var BiggestInt): int {. var res: BiggestInt doAssert parseBiggestInt("9223372036854775807", res) == 19 doAssert res == 9223372036854775807 + doAssert parseBiggestInt("-2024_05_09", res) == 11 + doAssert res == -20240509 var res = BiggestInt(0) # use 'res' for exception safety (don't write to 'number' in case of an # overflow exception): @@ -474,10 +476,8 @@ proc parseInt*(s: openArray[char], number: var int): int {. ## `ValueError` is raised if the parsed integer is out of the valid range. runnableExamples: var res: int - doAssert parseInt("2019", res, 0) == 4 - doAssert res == 2019 - doAssert parseInt("2019", res, 2) == 2 - doAssert res == 19 + doAssert parseInt("-2024_05_02", res) == 11 + doAssert res == -20240502 var res = BiggestInt(0) result = parseBiggestInt(s, res) when sizeof(int) <= 4: @@ -992,6 +992,10 @@ proc parseBiggestInt*(s: string, number: var BiggestInt, start = 0): int {.noSid var res: BiggestInt doAssert parseBiggestInt("9223372036854775807", res, 0) == 19 doAssert res == 9223372036854775807 + doAssert parseBiggestInt("-2024_05_09", res) == 11 + doAssert res == -20240509 + doAssert parseBiggestInt("-2024_05_02", res, 7) == 4 + doAssert res == 502 parseBiggestInt(s.toOpenArray(start, s.high), number) proc parseInt*(s: string, number: var int, start = 0): int {.noSideEffect, raises: [ValueError].} = @@ -1000,10 +1004,10 @@ proc parseInt*(s: string, number: var int, start = 0): int {.noSideEffect, raise ## `ValueError` is raised if the parsed integer is out of the valid range. runnableExamples: var res: int - doAssert parseInt("2019", res, 0) == 4 - doAssert res == 2019 - doAssert parseInt("2019", res, 2) == 2 - doAssert res == 19 + doAssert parseInt("-2024_05_02", res) == 11 + doAssert res == -20240502 + doAssert parseInt("-2024_05_02", res, 7) == 4 + doAssert res == 502 parseInt(s.toOpenArray(start, s.high), number) diff --git a/lib/system/orc.nim b/lib/system/orc.nim index 463c40c4db4ce..c02a249895da3 100644 --- a/lib/system/orc.nim +++ b/lib/system/orc.nim @@ -146,7 +146,7 @@ proc unregisterCycle(s: Cell) = let idx = s.rootIdx-1 when false: if idx >= roots.len or idx < 0: - cprintf("[Bug!] %ld\n", idx) + cprintf("[Bug!] %ld %ld\n", idx, roots.len) rawQuit 1 roots.d[idx] = roots.d[roots.len-1] roots.d[idx][0].rootIdx = idx+1 @@ -303,6 +303,14 @@ proc collectColor(s: Cell; desc: PNimTypeV2; col: int; j: var GcEnv) = t.setColor(colBlack) trace(t, desc, j) +const + defaultThreshold = when defined(nimFixedOrc): 10_000 else: 128 + +when defined(nimStressOrc): + const rootsThreshold = 10 # broken with -d:nimStressOrc: 10 and for havlak iterations 1..8 +else: + var rootsThreshold {.threadvar.}: int + proc collectCyclesBacon(j: var GcEnv; lowMark: int) = # pretty direct translation from # https://researcher.watson.ibm.com/researcher/files/us-bacon/Bacon01Concurrent.pdf @@ -341,22 +349,25 @@ proc collectCyclesBacon(j: var GcEnv; lowMark: int) = s.rootIdx = 0 collectColor(s, roots.d[i][1], colToCollect, j) + # Bug #22927: `free` calls destructors which can append to `roots`. + # We protect against this here by setting `roots.len` to 0 and also + # setting the threshold so high that no cycle collection can be triggered + # until we are out of this critical section: + when not defined(nimStressOrc): + let oldThreshold = rootsThreshold + rootsThreshold = high(int) + roots.len = 0 + for i in 0 ..< j.toFree.len: when orcLeakDetector: writeCell("CYCLIC OBJECT FREED", j.toFree.d[i][0], j.toFree.d[i][1]) free(j.toFree.d[i][0], j.toFree.d[i][1]) + when not defined(nimStressOrc): + rootsThreshold = oldThreshold + inc j.freed, j.toFree.len deinit j.toFree - #roots.len = 0 - -const - defaultThreshold = when defined(nimFixedOrc): 10_000 else: 128 - -when defined(nimStressOrc): - const rootsThreshold = 10 # broken with -d:nimStressOrc: 10 and for havlak iterations 1..8 -else: - var rootsThreshold {.threadvar.}: int when defined(nimOrcStats): var freedCyclicObjects {.threadvar.}: int @@ -396,7 +407,8 @@ proc collectCycles() = collectCyclesBacon(j, 0) deinit j.traceStack - deinit roots + if roots.len == 0: + deinit roots when not defined(nimStressOrc): # compute the threshold based on the previous history diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index e4d6479ec7cfa..3621c4960a5a9 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -220,7 +220,10 @@ proc appendChar(dest: NimString, c: char) {.compilerproc, inline.} = proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} = let n = max(newLen, 0) if s == nil: - result = mnewString(n) + if n == 0: + return s + else: + result = mnewString(n) elif n <= s.space: result = s else: @@ -301,7 +304,10 @@ proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {. compilerRtl.} = sysAssert typ.kind == tySequence, "setLengthSeqV2: type is not a seq" if s == nil: - result = cast[PGenericSeq](newSeq(typ, newLen)) + if newLen == 0: + result = s + else: + result = cast[PGenericSeq](newSeq(typ, newLen)) else: let elemSize = typ.base.size let elemAlign = typ.base.align diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim index 67fc2f8c42820..ee05f97a0a03b 100644 --- a/nimsuggest/nimsuggest.nim +++ b/nimsuggest/nimsuggest.nim @@ -134,7 +134,8 @@ const #List of currently supported capabilities. So lang servers/ides can iterate over and check for what's enabled Capabilities = [ "con", #current NimSuggest supports the `con` commmand - "exceptionInlayHints" + "exceptionInlayHints", + "unknownFile", #current NimSuggest can handle unknown files ] proc parseQuoted(cmd: string; outp: var string; start: int): int = @@ -758,15 +759,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = if gMode != mstdin: conf.writelnHook = proc (msg: string) = discard - # Find Nim's prefix dir. - let binaryPath = findExe("nim") - if binaryPath == "": - raise newException(IOError, - "Cannot find Nim standard library: Nim compiler not in PATH") - conf.prefixDir = AbsoluteDir binaryPath.splitPath().head.parentDir() - if not dirExists(conf.prefixDir / RelativeDir"lib"): - conf.prefixDir = AbsoluteDir"" - + conf.prefixDir = conf.getPrefixDir() #msgs.writelnHook = proc (line: string) = log(line) myLog("START " & conf.projectFull.string) @@ -1065,10 +1058,6 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile, var fileIndex: FileIndex if not (cmd in {ideRecompile, ideGlobalSymbols}): - if not fileInfoKnown(conf, file): - myLog fmt "{file} is unknown, returning no results" - return - fileIndex = fileInfoIdx(conf, file) msgs.setDirtyFile( conf, @@ -1344,13 +1333,7 @@ else: conf.writelnHook = proc (msg: string) = discard # Find Nim's prefix dir. if nimPath == "": - let binaryPath = findExe("nim") - if binaryPath == "": - raise newException(IOError, - "Cannot find Nim standard library: Nim compiler not in PATH") - conf.prefixDir = AbsoluteDir binaryPath.splitPath().head.parentDir() - if not dirExists(conf.prefixDir / RelativeDir"lib"): - conf.prefixDir = AbsoluteDir"" + conf.prefixDir = conf.getPrefixDir() else: conf.prefixDir = AbsoluteDir nimPath diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 3f8d375e23638..f234b0e876043 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -63,7 +63,7 @@ pkg "criterion", allowFailure = true # needs testing binary pkg "datamancer" pkg "dashing", "nim c tests/functional.nim" pkg "delaunay" -pkg "dnsclient" +pkg "dnsclient", allowFailure = true # super fragile pkg "docopt" pkg "dotenv" # when defined(linux): pkg "drchaos" @@ -144,7 +144,6 @@ pkg "polypbren" pkg "presto" pkg "prologue", "nimble tcompile" pkg "protobuf", "nim c -o:protobuff -r src/protobuf.nim" -pkg "pylib" pkg "rbtree" pkg "react", "nimble example" pkg "regex", "nim c src/regex" diff --git a/tests/arc/tcaseobj.nim b/tests/arc/tcaseobj.nim index be1d722edccef..3499f5c1e3f09 100644 --- a/tests/arc/tcaseobj.nim +++ b/tests/arc/tcaseobj.nim @@ -338,3 +338,29 @@ block: doAssert ff.s == 12 mainSync() + +import std/sequtils + +# bug #23690 +type + SomeObj* = object of RootObj + + Item* = object + case kind*: 0..1 + of 0: + a*: int + b*: SomeObj + of 1: + c*: string + + ItemExt* = object + a*: Item + b*: string + +proc do1(x: int): seq[(string, Item)] = + result = @[("zero", Item(kind: 1, c: "first"))] + +proc do2(x: int, e: ItemExt): seq[(string, ItemExt)] = + do1(x).map(proc(v: (string, Item)): auto = (v[0], ItemExt(a: v[1], b: e.b))) + +doAssert $do2(0, ItemExt(a: Item(kind: 1, c: "second"), b: "third")) == """@[("zero", (a: (kind: 1, c: "first"), b: "third"))]""" diff --git a/tests/destructor/tgotoexc_leak.nim b/tests/destructor/tgotoexc_leak.nim new file mode 100644 index 0000000000000..c8a234085bd63 --- /dev/null +++ b/tests/destructor/tgotoexc_leak.nim @@ -0,0 +1,19 @@ +discard """ + output: '''0 +true''' + cmd: "nim c --gc:arc $file" +""" + +# bug #22398 + +for i in 0 ..< 10_000: + try: + try: + raise newException(ValueError, "") + except CatchableError: + discard + raise newException(ValueError, "") # or raise getCurrentException(), just raise works ok + except ValueError: + discard +echo getOccupiedMem() +echo getCurrentException() == nil diff --git a/tests/errmsgs/t22852.nim b/tests/errmsgs/t22852.nim new file mode 100644 index 0000000000000..7c352a49c9cf7 --- /dev/null +++ b/tests/errmsgs/t22852.nim @@ -0,0 +1,9 @@ +discard """ + exitcode: 1 + outputsub: ''' +Error: unhandled exception: value out of range: -2 notin 0 .. 9223372036854775807 [RangeDefect] +''' +""" + +# bug #22852 +echo [0][2..^2] diff --git a/tests/generics/tuninstantiatedgenericcalls.nim b/tests/generics/tuninstantiatedgenericcalls.nim index bac334e95592c..63ba3908f5641 100644 --- a/tests/generics/tuninstantiatedgenericcalls.nim +++ b/tests/generics/tuninstantiatedgenericcalls.nim @@ -140,3 +140,8 @@ block: # issue #1771 var a: Foo[range[0..2], float] doAssert test(a) == 0.0 + +block: # issue #23730 + proc test(M: static[int]): array[1 shl M, int] = discard + doAssert len(test(3)) == 8 + doAssert len(test(5)) == 32 diff --git a/tests/js/tjsffi.nim b/tests/js/tjsffi.nim index 265ae52e90475..f27ea554651e4 100644 --- a/tests/js/tjsffi.nim +++ b/tests/js/tjsffi.nim @@ -1,4 +1,5 @@ discard """ +matrix: "--legacy:jsnolambdalifting;" output: ''' 3 2 diff --git a/tests/metatype/twrong_same_type.nim b/tests/metatype/twrong_same_type.nim new file mode 100644 index 0000000000000..ea903b7a3ef4a --- /dev/null +++ b/tests/metatype/twrong_same_type.nim @@ -0,0 +1,28 @@ +# bug #23418 + +template mapIt*(x: untyped): untyped = + type OutType {.gensym.} = typeof(x) #typeof(x, typeOfProc) + newSeq[OutType](5) + +type F[E] = object + +proc start(v: int): F[(ValueError,)] = discard +proc stop(v: int): F[tuple[]] = discard + +assert $typeof(mapIt(start(9))) == "seq[F[(ValueError,)]]" +assert $typeof(mapIt(stop(9))) == "seq[F[tuple[]]]" + +# bug #23445 + +type F2[T; I: static int] = distinct int + +proc start2(v: int): F2[void, 22] = discard +proc stop2(v: int): F2[void, 33] = discard + +var a = mapIt(start2(5)) + +assert $type(a) == "seq[F2[system.void, 22]]", $type(a) + +var b = mapIt(stop2(5)) + +assert $type(b) == "seq[F2[system.void, 33]]", $type(b) diff --git a/tests/pragmas/tpush.nim b/tests/pragmas/tpush.nim index 8ebbfe3d3d6f5..cb411714e2ddd 100644 --- a/tests/pragmas/tpush.nim +++ b/tests/pragmas/tpush.nim @@ -99,3 +99,28 @@ block: # bug #23019 k(w) {.pop.} {.pop.} + +{.push exportC.} + +block: + proc foo11() = + const factor = [1, 2, 3, 4] + doAssert factor[0] == 1 + proc foo21() = + const factor = [1, 2, 3, 4] + doAssert factor[0] == 1 + + foo11() + foo21() + +template foo31() = + let factor = [1, 2, 3, 4] + doAssert factor[0] == 1 +template foo41() = + let factor = [1, 2, 3, 4] + doAssert factor[0] == 1 + +foo31() +foo41() + +{.pop.} diff --git a/tests/refc/tsinkbug.nim b/tests/refc/tsinkbug.nim new file mode 100644 index 0000000000000..2cd762f405cad --- /dev/null +++ b/tests/refc/tsinkbug.nim @@ -0,0 +1,17 @@ +discard """ + matrix: "--gc:refc; --gc:arc" + output: ''' +Value is: 42 +Value is: 42''' +""" + +type AnObject* = object of RootObj + value*: int + +proc mutate(a: sink AnObject) = + a.value = 1 + +var obj = AnObject(value: 42) +echo "Value is: ", obj.value +mutate(obj) +echo "Value is: ", obj.value diff --git a/tests/stdlib/thashes.nim b/tests/stdlib/thashes.nim index 3974b01c81bf8..f7a1db2328d5c 100644 --- a/tests/stdlib/thashes.nim +++ b/tests/stdlib/thashes.nim @@ -1,5 +1,5 @@ discard """ - matrix: "--mm:refc; --mm:orc; --backend:cpp; --backend:js --jsbigint64:on; --backend:js --jsbigint64:off" + matrix: "--mm:refc; --mm:orc; --backend:cpp; --backend:js --jsbigint64:on; --backend:js --jsbigint64:off; --backend:c -d:nimPreviewHashFarm; --backend:cpp -d:nimPreviewHashFarm; --backend:js -d:nimPreviewHashFarm" """ import std/hashes @@ -47,19 +47,22 @@ block hashes: doAssert hashWangYi1(456) == -6421749900419628582 block empty: + const emptyStrHash = # Hash=int=4B on js even w/--jsbigint64:on => cast[Hash] + when defined nimPreviewHashFarm: cast[Hash](-7286425919675154353i64) + else: 0 var a = "" b = newSeq[char]() c = newSeq[int]() d = cstring"" e = "abcd" - doAssert hash(a) == 0 - doAssert hash(b) == 0 + doAssert hash(a) == emptyStrHash + doAssert hash(b) == emptyStrHash doAssert hash(c) == 0 - doAssert hash(d) == 0 + doAssert hash(d) == emptyStrHash doAssert hashIgnoreCase(a) == 0 doAssert hashIgnoreStyle(a) == 0 - doAssert hash(e, 3, 2) == 0 + doAssert hash(e, 3, 2) == emptyStrHash block sameButDifferent: doAssert hash("aa bb aaaa1234") == hash("aa bb aaaa1234", 0, 13) @@ -93,7 +96,11 @@ block largeSize: # longer than 4 characters proc main() = doAssert hash(0.0) == hash(0) # bug #16061 - doAssert hash(cstring"abracadabra") == 97309975 + when defined nimPreviewHashFarm: # Default switched -> `not nimStringHash2` + # Hash=int=4B on js even w/--jsbigint64:on => cast[Hash] + doAssert hash(cstring"abracadabra") == cast[Hash](-1119910118870047694i64) + else: + doAssert hash(cstring"abracadabra") == 97309975 doAssert hash(cstring"abracadabra") == hash("abracadabra") when sizeof(int) == 8 or defined(js): diff --git a/tests/stdlib/tjson.nim b/tests/stdlib/tjson.nim index 691bedeaabe71..f0e8c8bb762fd 100644 --- a/tests/stdlib/tjson.nim +++ b/tests/stdlib/tjson.nim @@ -1,5 +1,5 @@ discard """ - matrix: "--mm:refc; --backend:cpp --mm:refc; --backend:js --jsbigint64:off; --backend:js --jsbigint64:on" + matrix: "; --backend:cpp; --backend:js --jsbigint64:off; --backend:js --jsbigint64:on" """ @@ -51,7 +51,7 @@ for i in 0 .. 10000: except: discard # memory diff should less than 4M -doAssert(abs(getOccupiedMem() - startMemory) < 4 * 1024 * 1024) # todo fixme doesn;t work for ORC +doAssert(abs(getOccupiedMem() - startMemory) < 4 * 1024 * 1024) # test `$` diff --git a/tests/views/tviews1.nim b/tests/views/tviews1.nim index 1cc9fcdec33c5..9785d25e5e515 100644 --- a/tests/views/tviews1.nim +++ b/tests/views/tviews1.nim @@ -124,3 +124,13 @@ proc bug22597 = # bug #22597 doAssert i == 1 bug22597() + +block: # bug #20048 + type + Test = object + tokens: openArray[string] + + func init(Self: typedesc[Test], tokens: openArray[string]): Self = Self(tokens: tokens) + + let data = Test.init(["123"]) + doAssert @(data.tokens) == @["123"] diff --git a/tests/views/tviews2.nim b/tests/views/tviews2.nim index e06cc2d5359f7..29aff76df167b 100644 --- a/tests/views/tviews2.nim +++ b/tests/views/tviews2.nim @@ -58,6 +58,18 @@ block: # bug #16671 f() +block: # bug #15746 + type + Reader = object + data: openArray[char] + current: int + + proc initReader(data: openArray[char], offset = 0): Reader = + result = Reader(data: data, current: offset) + + let s = "\x01\x00\x00\x00" + doAssert initReader(s).data[0].int == 1 + block: proc foo(x: openArray[char]) = discard x