diff --git a/changelog.md b/changelog.md index aa0485975e..af91b49773 100644 --- a/changelog.md +++ b/changelog.md @@ -35,6 +35,10 @@ errors. - Adds a new warning `--warning:ImplicitRangeConversion` that detects downsizing implicit conversions to range types (e.g., `int -> range[0..255]` or `range[1..256] -> range[0..255]`) that could cause runtime panics. Safe conversions like `range[0..255] -> range[0..65535]` and explicit casts do not trigger warnings. `int` to `Natural` and `Positive` conversions do not trigger warnings, which can be enabled with `--warning:systemRangeConversion`. +- Procedure compatibility also checks the backend representation of the +parameter and result types, not just their source-level shape. Use +`--legacy:procParamTypeBackendAliases` to restore the older behavior. + ## Standard library additions and changes [//]: # "Additions:" @@ -77,6 +81,9 @@ errors. - `std/re` and `std/nre` are deprecated as PCRE library is obsolete. Use https://github.com/nitely/nim-regex or `std/nre2`. See: https://github.com/nim-lang/Nim/issues/23668. +- `std/pegs` now correctly lexes UTF-8 bytes inside bare identifier-style + terminals, so case-insensitive matching of non-ASCII terms (e.g. ``\i café``) + works without single-quoting. ## Language changes diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 2cf187e687..62302146f1 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2816,9 +2816,9 @@ proc genWasMoved(p: BProc; n: PNode) = # [addrLoc(p.config, a), getTypeDesc(p.module, a.t)]) proc genMove(p: BProc; n: PNode; d: var TLoc) = - var a: TLoc = initLocExpr(p, n[1].skipAddr, {lfEnforceDeref, lfPrepareForMutation}) if n.len == 4: # generated by liftdestructors: + var a: TLoc = initLocExpr(p, n[1].skipAddr, {lfEnforceDeref, lfPrepareForMutation}) var src: TLoc = initLocExpr(p, n[2]) let destVal = rdLoc(a) let srcVal = rdLoc(src) @@ -2838,29 +2838,16 @@ proc genMove(p: BProc; n: PNode; d: var TLoc) = else: if d.k == locNone: d = getTemp(p, n.typ) if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc, gcYrc}: - genAssignment(p, d, a, {}) var op = getAttachedOp(p.module.g.graph, n.typ, attachedWasMoved) - if op == nil: + if op == nil or sfOverridden notin op.flags: + var a: TLoc = initLocExpr(p, n[1].skipAddr, {lfEnforceDeref, lfPrepareForMutation}) + genAssignment(p, d, a, {}) resetLoc(p, a) else: - var b = initLocExpr(p, newSymNode(op)) - case skipTypes(a.t, abstractVar+{tyStatic}).kind - of tyOpenArray, tyVarargs: # todo fixme generated `wasMoved` hooks for - # openarrays, but it probably shouldn't? - let ra = rdLoc(a) - var s: string - if reifiedOpenArray(a.lode): - if a.t.kind in {tyVar, tyLent}: - s = derefField(ra, "Field0") & cArgumentSeparator & derefField(ra, "Field1") - else: - s = dotField(ra, "Field0") & cArgumentSeparator & dotField(ra, "Field1") - else: - s = ra & cArgumentSeparator & ra & "Len_0" - p.s(cpsStmts).addCallStmt(rdLoc(b), s) - else: - let val = if p.module.compileToCpp: rdLoc(a) else: byRefLoc(p, a) - p.s(cpsStmts).addCallStmt(rdLoc(b), val) + n[1] = makeAddr(n[1], p.module.idgen) + genCall(p, n, d) else: + var a: TLoc = initLocExpr(p, n[1].skipAddr, {lfEnforceDeref, lfPrepareForMutation}) genAssignment(p, d, a, {}) resetLoc(p, a) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 5ea23d1f80..83f5e90172 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -341,9 +341,9 @@ proc genCppParamsForCtor(p: BProc; call: PNode; didGenTemp: var bool): Snippet = call[i][0] else: call[i] - if param.kind != nkBracketExpr or param.typ.kind in + if not param.typ.isCompileTimeOnly and (param.kind != nkBracketExpr or param.typ.kind in {tyRef, tyPtr, tyUncheckedArray, tyArray, tyOpenArray, - tyVarargs, tySequence, tyString, tyCstring, tyTuple}: + tyVarargs, tySequence, tyString, tyCstring, tyTuple}): let tempLoc = initLocExprSingleUse(p, param) didGenTemp = didGenTemp or tempLoc.k == locTemp genOtherArg(p, call, i, typ, res, argBuilder) diff --git a/compiler/commands.nim b/compiler/commands.nim index f7de0978ed..a37cb348ae 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -930,7 +930,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; if m.len == 0: localError(conf, info, "Cannot resolve filename: " & arg) else: - conf.implicitImports.add m + conf.implicitImports.add(if arg.startsWith(stdPrefix): arg else: m) of "include": expectArg(conf, switch, arg, pass, info) if pass in {passCmd2, passPP}: diff --git a/compiler/deps.nim b/compiler/deps.nim index 116c0ebdf3..cfe0f0975f 100644 --- a/compiler/deps.nim +++ b/compiler/deps.nim @@ -488,10 +488,19 @@ proc generateBuildFile(c: DepContext): string = let exeFile = changeFileExt(c.nodes[0].files[0].nimFile, ExeExt) b.addTree "do" b.addIdent "nim_nifc" - # Input: .nim file (expanded as argument) and .nif file (dependency) + # Input: .nim file (expanded as argument) b.addTree "input" b.addStrLit mainNif b.endTree() + # Also depend on the semmed .nif files of the main module and all its + # dependencies. nifmake's topological sort orders nodes by depth; without + # these inputs the nim_nifc node sits at depth 1 (no recognized inputs) + # alongside the nifler nodes and runs *before* the nim_m steps that + # produce the .nif files it needs to read. + for node in c.nodes: + b.addTree "input" + b.addStrLit c.semmedFile(node.files[0]) + b.endTree() b.addTree "output" b.addStrLit exeFile b.endTree() diff --git a/compiler/importer.nim b/compiler/importer.nim index a02a5e96a1..ac10720f90 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -13,7 +13,7 @@ import ast, msgs, options, idents, lookups, semdata, modulepaths, sigmatch, lineinfos, modulegraphs, wordrecg -from std/strutils import `%`, startsWith +from std/strutils import `%`, startsWith, replace from std/sequtils import addUnique import std/[sets, tables, intsets] @@ -304,9 +304,9 @@ proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym = var prefix = "" if realModule.constraint != nil: prefix = realModule.constraint.strVal & "; " message(c.config, n.info, warnDeprecated, prefix & realModule.name.s & " is deprecated") - let moduleName = getModuleName(c.config, n) - if belongsToStdlib(c.graph, result) and not startsWith(moduleName, stdPrefix) and - not startsWith(moduleName, "system/") and not startsWith(moduleName, "packages/"): + let moduleNameNorm = getModuleName(c.config, n).replace("\\", "/") + if belongsToStdlib(c.graph, result) and not startsWith(moduleNameNorm, stdPrefix) and + not startsWith(moduleNameNorm, "system/") and not startsWith(moduleNameNorm, "packages/"): message(c.config, n.info, warnStdPrefix, realModule.name.s) proc suggestMod(n: PNode; s: PSym) = diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 9ebec89be5..9c0b803602 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -1349,7 +1349,7 @@ proc rawGetTok*(L: var Lexer, tok: var Token) = lexMessage(L, errGenerated, "invalid token: no whitespace between number and identifier") of '-': if L.buf[L.bufpos+1] in {'0'..'9'} and - (L.bufpos-1 == 0 or L.buf[L.bufpos-1] in UnaryMinusWhitelist): + (L.bufpos == 0 or L.buf[L.bufpos-1] in UnaryMinusWhitelist): # x)-23 # binary minus # ,-23 # unary minus # \n-78 # unary minus? Yes. diff --git a/compiler/options.nim b/compiler/options.nim index bd84e0ee7b..7a28b1dc6f 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -259,6 +259,9 @@ type ## Old transformation for closures in JS backend noPanicOnExcept ## don't panic on bare except + procParamTypeBackendAliases + ## Keep the old proc type compatibility rules that ignore backend + ## c type aliases. SymbolFilesOption* = enum disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 15d8b14fe7..32c98cdb31 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -637,6 +637,11 @@ proc renderNotLValue*(n: PNode): string = elif n.kind in {nkHiddenStdConv, nkHiddenSubConv} and n.len == 2: result = typeToString(n.typ.skipTypes(abstractVar)) & "(" & result & ")" +proc isSsoStringIndex*(conf: ConfigRef; n: PNode): bool = + result = conf.usesSso() and n.kind == nkBracketExpr and n.len >= 1 and + n[0].typ != nil and + n[0].typ.skipTypes(abstractVar + abstractInst - {tyTypeDesc}).kind == tyString + proc isAssignable(c: PContext, n: PNode): TAssignableResult = result = parampatterns.isAssignable(c.p.owner, n) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 64a8d2a4f9..aa0489cd22 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -652,6 +652,9 @@ proc overloadedCallOpr(c: PContext, n: PNode): PNode = result = semExpr(c, result, flags = {efNoUndeclared}) proc changeType(c: PContext; n: PNode, newType: PType, check: bool) = + template isViewTarget(t: PType): bool = + t.skipTypes({tyGenericInst, tyAlias, tySink}).kind in {tyVar, tyLent} + case n.kind of nkCurly: for i in 0.. 1 and op.typ.firstParamType.kind != tyVar: + wasMovedCall.add ri.skipAddr + else: + wasMovedCall.add makeAddr(ri.skipAddr, idgen) + + result.add wasMovedCall + else: + result = newFastMoveStmt(g, le, ri) + proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; idgen: IdGenerator; owner: PSym; typ: PType; v: PNode; useShallowCopy=false): PSym = result = newSym(skTemp, getIdent(g.cache, genPrefix), idgen, owner, varSection.info, @@ -68,10 +86,10 @@ proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; idgen: IdGenerator; if varInit != nil: if g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc, gcYrc}: # inject destructors pass will do its own analysis - varInit.add newFastMoveStmt(g, newSymNode(result), v) + varInit.add newSpawnMoveStmt(g, idgen, newSymNode(result), v) else: if useShallowCopy and typeNeedsNoDeepCopy(typ) or optTinyRtti in g.config.globalOptions: - varInit.add newFastMoveStmt(g, newSymNode(result), v) + varInit.add newSpawnMoveStmt(g, idgen, newSymNode(result), v) else: let deepCopyCall = newNodeI(nkCall, varInit.info, 3) deepCopyCall[0] = newSymNode(getSysMagic(g, varSection.info, "deepCopy", mDeepCopy)) diff --git a/compiler/types.nim b/compiler/types.nim index 62831624b9..de24471e7d 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -897,7 +897,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = c.flags = oldFlags if x == y: return true - let aliasSkipSet = maybeSkipRange({tyAlias}) + let aliasSkipSet = maybeSkipRange({tyAlias, tyInferred}) var a = skipTypes(x, aliasSkipSet) while a.kind == tyUserTypeClass and tfResolved in a.flags: a = skipTypes(a.last, aliasSkipSet) diff --git a/doc/ic.md b/doc/ic.md index 9027f8ba63..9fd7b55d2b 100644 --- a/doc/ic.md +++ b/doc/ic.md @@ -33,7 +33,7 @@ The text representation is particularly valuable for debugging and introspection Each ``.nim`` module produces its own ``.nif`` file during compilation. The NIF format contains: -- **Header** - Version information (e.g., `(.nif26)`) +- **Header** - Version information (e.g., `(.nif27)`) - **Dependencies** - List of source files and dependencies - **Interface** - Exported symbols and their indices - **Body** - The intermediate representation of the module's code in Lisp-like syntax diff --git a/doc/manual.md b/doc/manual.md index ab06f7aa4b..0970d0f5b8 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -34,10 +34,10 @@ To learn how to compile Nim programs and generate documentation see the [Compiler User Guide](nimc.html) and the [DocGen Tools Guide](docgen.html). The language constructs are explained using an extended BNF, in which `(a)*` -means 0 or more `a`'s, `a+` means 1 or more `a`'s, and `(a)?` means an +means 0 or more *a*'s, `a+` means 1 or more *a*'s, and `(a)?` means an optional *a*. Parentheses may be used to group elements. -`&` is the lookahead operator; `&a` means that an `a` is expected but +`&` is the lookahead operator; `&a` means that an *a* is expected but not consumed. It will be consumed in the following rule. The `|`, `/` symbols are used to mark alternatives and have the lowest @@ -1024,6 +1024,9 @@ These are the major type classes: * procedural type * generic type +The compiler's internal type zoo is richer than this summary suggests: +some types that are structurally equal still differ in backend representation. + Ordinal types ------------- @@ -2174,6 +2177,10 @@ Procedural type A procedural type is internally a pointer to a procedure. `nil` is an allowed value for a variable of a procedural type. +Procedure compatibility also checks the backend representation of the +parameter and result types, not just their source-level shape. Use +`--legacy:procParamTypeBackendAliases` to restore the older behavior. + Examples: ```nim @@ -8867,7 +8874,7 @@ Byref pragma The `byref` pragma can be applied to an object or tuple type or a proc param. When applied to a type it instructs the compiler to pass the type by reference (hidden pointer) to procs. When applied to a param it will take precedence, even -if the the type was marked as `bycopy`. When an `importc` type has a `byref` pragma or +if the type was marked as `bycopy`. When an `importc` type has a `byref` pragma or parameters are marked as `byref` in an `importc` proc, these params translate to pointers. When an `importcpp` type has a `byref` pragma, these params translate to C++ references `&`. diff --git a/doc/tut1.md b/doc/tut1.md index 072d8f3ba6..be13f8fbb5 100644 --- a/doc/tut1.md +++ b/doc/tut1.md @@ -1144,7 +1144,7 @@ there is a difference between the `$` and `repr` outputs: echo myCharacter, ":", repr(myCharacter) # --> n:'n' echo myString, ":", repr(myString) - # --> nim:0x10fa8c050"nim" + # --> nim:"nim" echo myInteger, ":", repr(myInteger) # --> 42:42 echo myFloat, ":", repr(myFloat) diff --git a/koch.nim b/koch.nim index c9269303b1..0ea083fb26 100644 --- a/koch.nim +++ b/koch.nim @@ -16,11 +16,11 @@ const ChecksumsStableCommit = "0b8e46379c5bc1bf73d8b3011908389c60fb9b98" # 2.0.1 SatStableCommit = "e63eaea8baf00bed8bcd5a29ffd8823abb265b39" - NimonyStableCommit = "c189ef438598878b2f02f6a2ff91d08febafc04b" # unversioned \ + NimonyStableCommit = "750aa47f2139fe5ad69f04b44428b752011fe873" # unversioned \ # Note that Nimony uses Nim as a git submodule but we don't want to install # Nimony's dependency to Nim as we are Nim. So a `git clone` without --recursive # is **required** here. - # Commit from 2026-04-27 + # Commit from 2026-05-05 # examples of possible values for fusion: #head, #ea82b54, 1.2.3 FusionStableHash = "#562467452b32cb7a97410ea177f083e6d8405734" diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index a88a2d2e43..f757301ed8 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -153,7 +153,7 @@ proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] = protocol) result.orig = protocol i.inc protocol.parseSaturatedNatural(result.major, i) - i.inc # Skip . + if i < protocol.len: inc i # Skip . i.inc protocol.parseSaturatedNatural(result.minor, i) proc sendStatus(client: AsyncSocket, status: string): Future[void] = diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim index 6435abd0d0..c8ce7a4d20 100644 --- a/lib/pure/collections/critbits.nim +++ b/lib/pure/collections/critbits.nim @@ -495,13 +495,16 @@ func `$`*[T](c: CritBitTree[T]): string = const avgItemLen = 16 result = newStringOfCap(c.count * avgItemLen) result.add("{") + var first = true when T is void: for key in keys(c): - if result.len > 1: result.add(", ") + if first: first = false + else: result.add(", ") result.addQuoted(key) else: for key, val in pairs(c): - if result.len > 1: result.add(", ") + if first: first = false + else: result.add(", ") result.addQuoted(key) result.add(": ") result.addQuoted(val) diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim index 5d67b361ea..0f797fad7d 100644 --- a/lib/pure/collections/deques.nim +++ b/lib/pure/collections/deques.nim @@ -454,8 +454,10 @@ proc `$`*[T](deq: Deque[T]): string = assert $a == "[10, 20, 30]" result = "[" + var first = true for x in deq: - if result.len > 1: result.add(", ") + if first: first = false + else: result.add(", ") result.addQuoted(x) result.add("]") diff --git a/lib/pure/collections/heapqueue.nim b/lib/pure/collections/heapqueue.nim index e83e5abbef..e2b6a6b52a 100644 --- a/lib/pure/collections/heapqueue.nim +++ b/lib/pure/collections/heapqueue.nim @@ -260,7 +260,9 @@ proc `$`*[T](heap: HeapQueue[T]): string = assert $heap == "[1, 2]" result = "[" + var first = true for x in heap.data: - if result.len > 1: result.add(", ") + if first: first = false + else: result.add(", ") result.addQuoted(x) result.add("]") diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim index 6e7de204f1..7ca2242e5f 100644 --- a/lib/pure/collections/lists.nim +++ b/lib/pure/collections/lists.nim @@ -304,8 +304,10 @@ proc `$`*[T](L: SomeLinkedCollection[T]): string = assert $a == "[1, 2, 3, 4]" result = "[" + var first = true for x in nodes(L): - if result.len > 1: result.add(", ") + if first: first = false + else: result.add(", ") result.addQuoted(x.value) result.add("]") diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index 97d586a7c1..eaece6fa12 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -1668,7 +1668,10 @@ func getSymbol(c: var PegLexer, tok: var Token) = while pos < c.buf.len: add(tok.literal, c.buf[pos]) inc(pos) - if pos < c.buf.len and c.buf[pos] notin strutils.IdentChars: break + if pos < c.buf.len: + let ch = c.buf[pos] + # Keep non-ASCII bytes so UTF-8 terminals reach the rune-aware matchers. + if ch notin strutils.IdentChars and ord(ch) < 0x80: break c.bufpos = pos tok.kind = tkIdentifier diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim index 4b07aca5a3..663d4c832b 100644 --- a/lib/pure/strtabs.nim +++ b/lib/pure/strtabs.nim @@ -380,8 +380,10 @@ proc `$`*(t: StringTableRef): string {.rtlFunc, extern: "nstDollar".} = result = "{:}" else: result = "{" + var first = true for key, val in pairs(t): - if result.len > 1: result.add(", ") + if first: first = false + else: result.add(", ") result.add(key) result.add(": ") result.add(val) diff --git a/lib/system.nim b/lib/system.nim index c76d096426..26232971bd 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -166,7 +166,7 @@ proc wasMoved*[T](obj: var T) {.magic: "WasMoved", noSideEffect.} ## it was "moved" and to signify its destructor should do nothing and ## ideally be optimized away. -proc move*[T](x: var T): T {.magic: "Move", noSideEffect.} = +proc move*[T](x: var T): T {.magic: "Move", noSideEffect, nodestroy.} = result = x {.cast(raises: []), cast(tags: []).}: `=wasMoved`(x) @@ -2694,7 +2694,9 @@ when hasAlloc or defined(nimscript): setLen(x, xl+item.len) var j = xl-1 while j >= i: - when defined(gcArc) or defined(gcOrc) or defined(gcYrc) or defined(gcAtomicArc): + when defined(nimsso): + x[j+item.len] = x[j] + elif defined(gcArc) or defined(gcOrc) or defined(gcYrc) or defined(gcAtomicArc): x[j+item.len] = move x[j] else: shallowCopy(x[j+item.len], x[j]) diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index 925f20d906..256c8afd80 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -691,7 +691,7 @@ proc getBigChunk(a: var MemRegion, size: int): PBigChunk = removeChunkFromMatrix2(a, result, fl, sl) if result.size >= size + PageSize: splitChunk(a, result, size) - # set 'used' to to true: + # set 'used' to true: result.prevSize = 1 track("setUsedToFalse", addr result.size, sizeof(int)) sysAssert result.owner == addr a, "getBigChunk: No owner set!" @@ -708,7 +708,7 @@ proc getHugeChunk(a: var MemRegion; size: int): PBigChunk = result.next = nil result.prev = nil result.size = size - # set 'used' to to true: + # set 'used' to true: result.prevSize = 1 result.owner = addr a incl(a, a.chunkStarts, pageIndex(result)) diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim index 08e8798b08..1569d12e14 100644 --- a/lib/system/gc_common.nim +++ b/lib/system/gc_common.nim @@ -143,7 +143,7 @@ when nimCoroutines: proc find(first: var GcStack, bottom: pointer): ptr GcStack = ## Find stack struct based on bottom pointer. If `bottom` is nil then main - ## thread stack is is returned. + ## thread stack is returned. if bottom == nil: return addr(gch.stack) diff --git a/lib/system/indices.nim b/lib/system/indices.nim index 6230b36788..8f20af5ec5 100644 --- a/lib/system/indices.nim +++ b/lib/system/indices.nim @@ -59,16 +59,35 @@ template `[]=`*(s: string; i: int; val: char) = arrPut(s, i, val) template `^^`(s, i: untyped): untyped = (when i is BackwardsIndex: s.len - int(i) else: int(i)) -template spliceImpl(s, a, L, b: typed): untyped = +template spliceStringImpl(s, a, L, b: typed): untyped = # make room for additional elements or cut: var shift = b.len - max(0,L) # ignore negative slice size var newLen = s.len + shift if shift > 0: # enlarge: setLen(s, newLen) - for i in countdown(newLen-1, a+b.len): movingCopy(s[i], s[i-shift]) + for i in countdown(newLen-1, a+b.len): + s[i] = s[i-shift] else: - for i in countup(a+b.len, newLen-1): movingCopy(s[i], s[i-shift]) + for i in countup(a+b.len, newLen-1): + s[i] = s[i-shift] + # cut down: + setLen(s, newLen) + # fill the hole: + for i in 0 ..< b.len: s[a+i] = b[i] + +template spliceSeqImpl(s, a, L, b: typed): untyped = + # make room for additional elements or cut: + var shift = b.len - max(0,L) # ignore negative slice size + var newLen = s.len + shift + if shift > 0: + # enlarge: + setLen(s, newLen) + for i in countdown(newLen-1, a+b.len): + movingCopy(s[i], s[i-shift]) + else: + for i in countup(a+b.len, newLen-1): + movingCopy(s[i], s[i-shift]) # cut down: setLen(s, newLen) # fill the hole: @@ -102,7 +121,7 @@ proc `[]=`*[T, U: Ordinal](s: var string, x: HSlice[T, U], b: string) {.systemRa if L == b.len: for i in 0.. PayloadSize: a.more.fullLen else: aslen let lb = if bslen > PayloadSize: b.more.fullLen else: bslen let minLen = min(la, lb) + let pfxLen = min(minLen, AlwaysAvail) + result = cmpInlineBytes(inlinePtrOf(a), inlinePtrOf(b), pfxLen) + if result != 0: return if minLen <= AlwaysAvail: result = la - lb return diff --git a/tests/ccgbugs2/m25800.h b/tests/ccgbugs2/m25800.h new file mode 100644 index 0000000000..7961eceda0 --- /dev/null +++ b/tests/ccgbugs2/m25800.h @@ -0,0 +1,7 @@ +/*TYPESECTION*/ +struct CppRef { + int* data; + CppRef() : data(new int(42)) {} + ~CppRef() { delete data; data = nullptr; } + void reset() { delete data; data = nullptr; } +}; \ No newline at end of file diff --git a/tests/ccgbugs2/t25800.nim b/tests/ccgbugs2/t25800.nim new file mode 100644 index 0000000000..9574c35009 --- /dev/null +++ b/tests/ccgbugs2/t25800.nim @@ -0,0 +1,23 @@ +discard """ + cmd: "nim cpp $file" + action: "compile" +""" + +# Bug Report 1: {.importcpp.} on =wasMoved generates invalid preprocessor directive #. + + +type CppRef* {.importcpp, bycopy, noInit, header: "m25800.h".} = object + +proc `=destroy`(x: var CppRef) {.importcpp: "#.~CppRef()".} +proc `=wasMoved`(x: var CppRef) {.importcpp: "#.reset()".} +proc `=copy`(dest: var CppRef; src: CppRef) {.importcpp: "dest = src".} +proc `=sink`(dest: var CppRef; src: CppRef) {.importcpp: "dest = std::move(src)".} + +# This triggers =wasMoved when passing to sink parameter +proc consume(x: sink CppRef) = discard + +proc test() = + var x: CppRef + consume(move(x)) # =wasMoved MUST be called here after the move + +test() \ No newline at end of file diff --git a/tests/compiler/tcmdline_import_std_prefix.nim b/tests/compiler/tcmdline_import_std_prefix.nim new file mode 100644 index 0000000000..9b9eca776f --- /dev/null +++ b/tests/compiler/tcmdline_import_std_prefix.nim @@ -0,0 +1,10 @@ +discard """ + matrix: "-d:nimPreviewSlimSystem --warning:StdPrefix:on --warningAsError:StdPrefix:on --import:std/objectdollar" + output: "(a: 23, b: 45)" +""" + +type Foo = object + a, b: int + +let x = Foo(a: 23, b: 45) +echo x diff --git a/tests/concepts/tconcepts_issues.nim b/tests/concepts/tconcepts_issues.nim index c6d0267c5c..02d9d25bf1 100644 --- a/tests/concepts/tconcepts_issues.nim +++ b/tests/concepts/tconcepts_issues.nim @@ -176,6 +176,42 @@ block t6462: var s = SeqGen[int](fil: FilterMixin[int](test: nil, trans: nil)) doAssert s.test() == nil +block concept_with_cint: + # Generic proc matching through concepts with cint should still work + type + FilterMixin[T] = ref object + test: (T) -> bool + trans: (T) -> T + + SeqGen[T] = ref object + fil: FilterMixin[T] + + WithFilter[T] = concept a + a.fil is FilterMixin[T] + + proc test[T](a: WithFilter[T]): (T) -> bool = + a.fil.test + + var s = SeqGen[cint](fil: FilterMixin[cint](test: nil, trans: nil)) + doAssert s.test() == nil + +block concept_with_int: + type + FilterMixin[T] = ref object + test: (T) -> bool + trans: (T) -> T + + SeqGen[T] = ref object + fil: FilterMixin[T] + + WithFilter[T] = concept a + a.fil is FilterMixin[T] + + proc test[T](a: WithFilter[T]): (T) -> bool = + a.fil.test + + var s = SeqGen[int](fil: FilterMixin[int](test: nil, trans: nil)) + doAssert s.test() == nil block t6770: diff --git a/tests/destructor/twasmoved.nim b/tests/destructor/twasmoved.nim index 5663227022..f9a0ad363a 100644 --- a/tests/destructor/twasmoved.nim +++ b/tests/destructor/twasmoved.nim @@ -12,3 +12,51 @@ proc foo = doAssert m.id == 999 foo() + +block: + type Foo = object + a,b,c: int + + var dest: Foo + + # proc `=wasMoved`(x: var Foo) = + # debugEcho "wasMoved called" + + proc main() = + var x = Foo(a:11, b:12, c:13) + dest = move(x) + + main() + +block: + type Foo = object + a,b,c: int + + var dest: Foo + + proc `=wasMoved`(x: var Foo) = + discard "wasMoved called" + + proc main() = + var x = Foo(a:11, b:12, c:13) + dest = move(x) + + main() + + +import std/threadpool + +block: + type Foo = object + data: string + + proc `=wasMoved`(x: var Foo) = + discard + + proc work(x: Foo) = + discard + + var x = Foo(data: "hello") + spawn work(x) + sync() + diff --git a/tests/errmsgs/tsso_string_index_var.nim b/tests/errmsgs/tsso_string_index_var.nim new file mode 100644 index 0000000000..401491e0a5 --- /dev/null +++ b/tests/errmsgs/tsso_string_index_var.nim @@ -0,0 +1,13 @@ +discard """ + cmd: "nim check --strings:sso --mm:orc --hints:off $file" + action: "reject" + nimout: ''' +tsso_string_index_var.nim(13, 12) Error: expression 's[0]' is immutable, not 'var' +''' +""" + +proc passByVar(c: var char) = + c = 'x' + +var s = "abc" +passByVar(s[0]) diff --git a/tests/lent/tlent_tuple_address.nim b/tests/lent/tlent_tuple_address.nim new file mode 100644 index 0000000000..f9c8d4542d --- /dev/null +++ b/tests/lent/tlent_tuple_address.nim @@ -0,0 +1,12 @@ +discard """ + errormsg: "expression has no address" +""" + +iterator foo(x: int): (lent int, lent int) = + yield (x, x + 1) + + +var x = 12 +for i in foo(x): + echo i[0] + echo i[1] diff --git a/tests/metatype/ttypetraits.nim b/tests/metatype/ttypetraits.nim index 0107f6b049..46601c1070 100644 --- a/tests/metatype/ttypetraits.nim +++ b/tests/metatype/ttypetraits.nim @@ -434,3 +434,32 @@ block: # bug #24378 type Win222[T] = typeof("foobar") doAssert not supportsCopyMem((int, Win222[int])) doAssert not supportsCopyMem(tuple[a: int, b: Win222[int]]) + +block: # bug #25789 + type + L[T; N: static int] = distinct seq[T] + EPF = distinct L[int, 100] + + var e: EPF = EPF(L[int, 100](@[1, 2, 3])) + + template classifyGeneric[T](x: T): bool = + when typeof(x) is L: + true + else: + false + + template classifyConcrete[T](x: T): bool = + when typeof(x) is L[int, 100]: + true + else: + false + + let viaConv = L[int, 100](e) + doAssert $type(viaConv) == "L[system.int, 100]" + doAssert classifyGeneric(viaConv) + doAssert classifyConcrete(viaConv) + + let viaDB = distinctBase(e, recursive = false) + doAssert $type(viaDB) == "L[system.int, 100]" + doAssert classifyGeneric(viaDB) + doAssert classifyConcrete(viaDB) diff --git a/tests/objects/tobject_default_value.nim b/tests/objects/tobject_default_value.nim index 5b0a5cd8e6..69e35bf826 100644 --- a/tests/objects/tobject_default_value.nim +++ b/tests/objects/tobject_default_value.nim @@ -833,4 +833,37 @@ proc overloaded[T: object](x: T) = var v: typeof(val) overloaded(v) -overloaded(Thing()) \ No newline at end of file +overloaded(Thing()) + +block: + type + Foo = object + x = Bar() + + Bar = object + x: int + + var f = Foo() + doassert f.x.x == 0 + +block: + type + Foo = object + x = Bar(x: 55) + + Bar = object + x: int + + var f = Foo() + doassert f.x.x == 55 + +block: + type + Bar = object + x: int + + Foo = object + x = Bar() + + var f = Foo() + doassert f.x.x == 0 diff --git a/tests/proc/tbackendtypealias.nim b/tests/proc/tbackendtypealias.nim new file mode 100644 index 0000000000..eaae0c2917 --- /dev/null +++ b/tests/proc/tbackendtypealias.nim @@ -0,0 +1,44 @@ +# bug #25617 +# Ensure that proc types with backend type alias mismatches +# (e.g. uint vs csize_t) are rejected at the Nim level rather +# than producing invalid C code. + +discard """ + cmd: "nim check --hints:off --warnings:off --errorMax:0 $file" + action: "reject" + nimout: ''' +tbackendtypealias.nim(21, 7) Error: type mismatch: got but expected 'proc (len: uint){.closure.}' +tbackendtypealias.nim(28, 7) Error: type mismatch: got but expected 'proc (len: csize_t){.closure.}' +''' +""" + +block direct_assignment: + # Direct proc variable assignment with backend type alias mismatch + var + a: proc (len: uint) + b: proc (len: csize_t) + c = a + c = b + +block direct_assignment_reverse: + var + a: proc (len: csize_t) + b: proc (len: uint) + c = a + c = b + +block same_backend_type: + # Same backend type should still work + var + a: proc (len: uint) + b: proc (len: uint) + c = a + c = b + +block cint_same_type: + # cint to cint should work + var + a: proc (len: cint) + b: proc (len: cint) + c = a + c = b diff --git a/tests/stdlib/tdeques.nim b/tests/stdlib/tdeques.nim index 7d379a5975..e1ad50a84c 100644 --- a/tests/stdlib/tdeques.nim +++ b/tests/stdlib/tdeques.nim @@ -241,3 +241,12 @@ proc main() = static: main() main() + +# https://github.com/nim-lang/Nim/issues/18583 +# $ separator must be emitted even when the item's string repr is empty +type EmptyStr18583 = object +proc `$`(x: EmptyStr18583): string = "" + +block: + var d = [EmptyStr18583(), EmptyStr18583()].toDeque + doAssert $d == "[, ]", "got: " & $d diff --git a/tests/stdlib/theapqueue.nim b/tests/stdlib/theapqueue.nim index afb09c7e3f..92964c5641 100644 --- a/tests/stdlib/theapqueue.nim +++ b/tests/stdlib/theapqueue.nim @@ -104,3 +104,15 @@ template main() = static: main() main() + +# https://github.com/nim-lang/Nim/issues/18583 +type EmptyStr18583HeapQ = object +proc `$`(x: EmptyStr18583HeapQ): string = "" +proc `<`(a, b: EmptyStr18583HeapQ): bool = false + +block: + var h = initHeapQueue[EmptyStr18583HeapQ]() + push(h, EmptyStr18583HeapQ()) + push(h, EmptyStr18583HeapQ()) + let s = $h + doAssert s == "[, ]", "got: " & s diff --git a/tests/stdlib/tlists.nim b/tests/stdlib/tlists.nim index 9339a6df05..cffb00d586 100644 --- a/tests/stdlib/tlists.nim +++ b/tests/stdlib/tlists.nim @@ -287,3 +287,14 @@ template main = static: main() main() + +# https://github.com/nim-lang/Nim/issues/18583 +type EmptyStr18583List = object +proc `$`(x: EmptyStr18583List): string = "" + +block: + var L: SinglyLinkedList[EmptyStr18583List] + L.prepend(EmptyStr18583List()) + L.prepend(EmptyStr18583List()) + let s = $L + doAssert s == "[, ]", "got: " & s diff --git a/tests/stdlib/tpegs.nim b/tests/stdlib/tpegs.nim index 18753a9bea..04774aa35b 100644 --- a/tests/stdlib/tpegs.nim +++ b/tests/stdlib/tpegs.nim @@ -259,6 +259,11 @@ block: doAssert match("EINE ÜBERSICHT UND AUSSERDEM", peg"(\upper \white*)+") doAssert(not match("456678", peg"(\letter)+")) + block: + doAssert match("CAFÉ", peg"\i café") + doAssert match("Café", peg"\i café") + doAssert "two cafés: Café and CAFÉ".findAll(peg"\i café").len == 3 + doAssert("var1 = key; var2 = key2".replacef( peg"\skip(\s*) {\ident}'='{\ident}", "$1<-$2$2") == "var1<-keykey;var2<-key2key2") diff --git a/tests/stdlib/tstrformat.nim b/tests/stdlib/tstrformat.nim index 74f23b953b..258c190a1d 100644 --- a/tests/stdlib/tstrformat.nim +++ b/tests/stdlib/tstrformat.nim @@ -544,6 +544,12 @@ proc main() = var x = 5 doAssert fmt"{(x=7;123.456)=:13e}" == "(x=7;123.456)= 1.234560e+02" doAssert x==7 + + block: # binary operators in interpolated expressions + let n = 1 + doAssert &"{n-1}" == "0" + doAssert fmt"{n-1}" == "0" + block: #curly bracket expressions and tuples proc formatValue(result: var string; value:Table|bool|JsonNode; specifier:string) = result.add $value