From 7295f578334beaf9d12de24b336041164582b420 Mon Sep 17 00:00:00 2001 From: Nils-Hero Lindemann Date: Fri, 8 May 2026 06:48:48 +0200 Subject: [PATCH 1/7] Write all variables italic in section "About this document" (#25797) Makes more sense. One variable was already written italic. --- doc/manual.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual.md b/doc/manual.md index 40897ac108..4b474888d9 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 From 4c8052a45bc12b0dc1114ca606d142d33495d8f2 Mon Sep 17 00:00:00 2001 From: Ryan McConnell Date: Fri, 8 May 2026 00:50:13 -0400 Subject: [PATCH 2/7] fix: implicit imports drop `std/` prefix (#25780) Preserves implicit imports instead of always storing the resolved absolute filename. That lets the later StdPrefix warning check see the original std/objectdollar spelling. This is for situations where in cfg or cli warnings are enabled for the prefix. Essentially a niche combination of compiler switches don't get along e.g. `-d:nimPreviewSlimSystem --warning:StdPrefix:on --warningAsError:StdPrefix:on --import:std/objectdollar` will cause: `Error: objectdollar needs the 'std' prefix [StdPrefix]` --- compiler/commands.nim | 2 +- compiler/importer.nim | 8 ++++---- tests/compiler/tcmdline_import_std_prefix.nim | 10 ++++++++++ 3 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 tests/compiler/tcmdline_import_std_prefix.nim 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/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/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 From f0c60b06e5cff8064bf0a9a32ec2d2bc14a694d9 Mon Sep 17 00:00:00 2001 From: Nils-Hero Lindemann Date: Sat, 9 May 2026 08:55:39 +0200 Subject: [PATCH 3/7] Update outdated string representation in example (#25802) See [here](https://nim-lang.github.io/Nim/tut1.html#internal-type-representation). --- doc/tut1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) From 6204e48ba597972569eab9148b630bbf0ed66fce Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 12 May 2026 23:20:10 +0200 Subject: [PATCH 4/7] SSO strings: bugfix (#25810) --- compiler/semdata.nim | 5 +++++ compiler/sempass2.nim | 4 ++++ lib/system.nim | 4 +++- lib/system/indices.nim | 29 ++++++++++++++++++++----- lib/system/strs_v3.nim | 9 ++++---- tests/errmsgs/tsso_string_index_var.nim | 13 +++++++++++ 6 files changed, 54 insertions(+), 10 deletions(-) create mode 100644 tests/errmsgs/tsso_string_index_var.nim 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/sempass2.nim b/compiler/sempass2.nim index 35901ed960..9c84b721ad 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -809,6 +809,10 @@ proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, formals: PType; ar markSideEffect(tracked, a, n.info) let paramType = if formals != nil and argIndex < formals.signatureLen: formals[argIndex] else: nil if paramType != nil and paramType.kind in {tyVar}: + let arg = n.skipAddr() + if isSsoStringIndex(tracked.config, arg): + localError(tracked.config, arg.info, + "expression '$1' is immutable, not 'var'" % renderNotLValue(arg)) invalidateFacts(tracked.guards, n) if n.kind == nkSym and isLocalSym(tracked, n.sym): makeVolatile(tracked, n.sym) diff --git a/lib/system.nim b/lib/system.nim index c76d096426..63989b1502 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -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/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/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]) From bbc5bbdcc72c5398c9b495826c3c64e7e491ba6a Mon Sep 17 00:00:00 2001 From: oab24413gmai Date: Thu, 14 May 2026 01:02:11 -0500 Subject: [PATCH 5/7] fix: duplicated words in manual.md and gc_common.nim comment (#25812) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two one-line typo fixes for duplicated words: - `doc/manual.md` — "if the the type was marked as `bycopy`" → "if the type was marked as `bycopy`" - `lib/system/gc_common.nim` — "## thread stack is is returned." → "## thread stack is returned." No code/behavior change. Co-authored-by: Mira Sato <275437409+oab24413gmai@users.noreply.github.com> --- doc/manual.md | 2 +- lib/system/gc_common.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual.md b/doc/manual.md index 4b474888d9..0970d0f5b8 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -8874,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/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) From 2c946950f40e5512b41667aa41518ac487aa8d71 Mon Sep 17 00:00:00 2001 From: vip892766gma Date: Thu, 14 May 2026 01:02:36 -0500 Subject: [PATCH 6/7] fix: duplicated "to" in alloc.nim comments (#25813) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two one-line typo fixes for duplicated "to" in `lib/system/alloc.nim`: - "# set 'used' to to true:" → "# set 'used' to true:" (occurs twice, lines ~694 and ~711) No code/behavior change. Co-authored-by: Aiden Park <275402320+vip892766gma@users.noreply.github.com> --- lib/system/alloc.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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)) From f9647276d8a9279a7d6eb4591a2feb356a9a4ca7 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 18 May 2026 13:55:33 +0800 Subject: [PATCH 7/7] fixes #25821; unary minus off by one mistake [backport] (#25823) fixes #25821 This pull request includes a minor bug fix in the lexer and adds new test cases for string formatting with binary operators in interpolated expressions. Lexer bug fix: * Fixed an off-by-one error in the unary minus detection logic in the `rawGetTok` procedure in `lexer.nim`, ensuring that the start-of-buffer condition is correctly checked. Testing improvements: * Added tests to `tstrformat.nim` to verify that binary operators (such as subtraction) work correctly inside interpolated string expressions using both `&` and `fmt`. --- compiler/lexer.nim | 2 +- tests/stdlib/tstrformat.nim | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) 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/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