From 16394c3772cb2564b5aa33c26641e4b1e36ad61f Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 18 Sep 2025 19:44:39 +0200 Subject: [PATCH 1/3] fixes #24361 (#25179) --- compiler/sem.nim | 14 ++++++++++---- compiler/semexprs.nim | 15 ++++++++++----- compiler/semmacrosanity.nim | 20 +++++++++++--------- compiler/vm.nim | 7 ++++--- 4 files changed, 35 insertions(+), 21 deletions(-) diff --git a/compiler/sem.nim b/compiler/sem.nim index 3392db7a9d..0739c6e162 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -321,7 +321,7 @@ proc hasCycle(n: PNode): bool = break excl n.flags, nfNone -proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode): PNode = +proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode; producedClosure: var bool): PNode = # recompute the types as 'eval' isn't guaranteed to construct types nor # that the types are sound: when true: @@ -333,7 +333,7 @@ proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode): PNode = if hasCycle(result): result = localErrorNode(c, eOrig, "the resulting AST is cyclic and cannot be processed further") else: - semmacrosanity.annotateType(result, expectedType, c.config) + semmacrosanity.annotateType(result, expectedType, c.config, producedClosure) else: result = semExprWithType(c, evaluated) #result = fitNode(c, e.typ, result) inlined with special case: @@ -370,7 +370,10 @@ proc tryConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode = if result == nil or result.kind == nkEmpty: result = nil else: - result = fixupTypeAfterEval(c, result, e) + var producedClosure = false + result = fixupTypeAfterEval(c, result, e, producedClosure) + if producedClosure: + result = nil except ERecoverableError: result = nil @@ -407,7 +410,10 @@ proc semConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode = # error correction: result = e else: - result = fixupTypeAfterEval(c, result, e) + var producedClosure = false + result = fixupTypeAfterEval(c, result, e, producedClosure) + if producedClosure: + result = nil proc semExprFlagDispatched(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = if efNeedStatic in flags: diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index c7a0994099..c1b49a19e9 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -951,11 +951,15 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = result = evalStaticExpr(c.module, c.idgen, c.graph, call, c.p.owner) if result.isNil: localError(c.config, n.info, errCannotInterpretNodeX % renderTree(call)) - else: result = fixupTypeAfterEval(c, result, n) + else: + var producedClosure = false + result = fixupTypeAfterEval(c, result, n, producedClosure) else: result = evalConstExpr(c.module, c.idgen, c.graph, call) if result.isNil: result = n - else: result = fixupTypeAfterEval(c, result, n) + else: + var producedClosure = false + result = fixupTypeAfterEval(c, result, n, producedClosure) else: result = n #if result != n: @@ -973,7 +977,8 @@ proc semStaticExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode = localError(c.config, n.info, errCannotInterpretNodeX % renderTree(n)) result = c.graph.emptyNode else: - result = fixupTypeAfterEval(c, result, a) + var producedClosure = false + result = fixupTypeAfterEval(c, result, a, producedClosure) proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = @@ -3070,10 +3075,10 @@ proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp proc isExplicitGenericCall(c: PContext, n: PNode): bool = ## checks if a call node `n` is a routine call with explicit generic params - ## + ## ## the callee node needs to be either an nkBracketExpr or a call to a ## symchoice of `[]` in which case it will be transformed into nkBracketExpr - ## + ## ## the LHS of the bracket expr has to either be a symchoice or resolve to ## a routine symbol template checkCallee(n: PNode) = diff --git a/compiler/semmacrosanity.nim b/compiler/semmacrosanity.nim index cba6b4a46a..1675114c2b 100644 --- a/compiler/semmacrosanity.nim +++ b/compiler/semmacrosanity.nim @@ -86,7 +86,7 @@ proc ithField(t: PType, field: var FieldTracker): FieldInfo = base = b.baseClass result = ithField(t.n, field) -proc annotateType*(n: PNode, t: PType; conf: ConfigRef) = +proc annotateType*(n: PNode, t: PType; conf: ConfigRef; producedClosure: var bool) = let x = t.skipTypes(abstractInst+{tyRange}) # Note: x can be unequal to t and we need to be careful to use 't' # to not to skip tyGenericInst @@ -102,7 +102,7 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) = globalError conf, n.info, "invalid field at index " & $i else: internalAssert(conf, n[i].kind == nkExprColonExpr) - annotateType(n[i][1], field.sym.typ, conf) + annotateType(n[i][1], field.sym.typ, conf, producedClosure) if field.delete: # only codegen fields from active case branches incl(n[i].flags, nfPreventCg) @@ -111,9 +111,11 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) = n.typ() = t for i in 0..= x.kidsLen: globalError conf, n.info, "invalid field at index " & $i - else: annotateType(n[i], x[i], conf) + else: annotateType(n[i], x[i], conf, producedClosure) elif x.kind == tyProc and x.callConv == ccClosure: n.typ() = t + if n.len > 1 and n[1].kind notin {nkEmpty, nkNilLit}: + producedClosure = true elif x.kind == tyOpenArray: # `opcSlice` transforms slices into tuples if n.kind == nkTupleConstr: let @@ -125,11 +127,11 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) = of nkStrKinds: for i in left..right: bracketExpr.add newIntNode(nkCharLit, BiggestInt n[0].strVal[i]) - annotateType(bracketExpr[^1], x.elementType, conf) + annotateType(bracketExpr[^1], x.elementType, conf, producedClosure) of nkBracket: for i in left..right: bracketExpr.add n[0][i] - annotateType(bracketExpr[^1], x.elementType, conf) + annotateType(bracketExpr[^1], x.elementType, conf, producedClosure) else: globalError(conf, n.info, "Incorrectly generated tuple constr") n[] = bracketExpr[] @@ -140,7 +142,7 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) = of nkBracket: if x.kind in {tyArray, tySequence, tyOpenArray}: n.typ() = t - for m in n: annotateType(m, x.elemType, conf) + for m in n: annotateType(m, x.elemType, conf, producedClosure) else: globalError(conf, n.info, "[] must have some form of array type") of nkCurly: @@ -148,10 +150,10 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) = n.typ() = t for m in n: if m.kind == nkRange: - annotateType(m[0], x.elemType, conf) - annotateType(m[1], x.elemType, conf) + annotateType(m[0], x.elemType, conf, producedClosure) + annotateType(m[1], x.elemType, conf, producedClosure) else: - annotateType(m, x.elemType, conf) + annotateType(m, x.elemType, conf, producedClosure) else: globalError(conf, n.info, "{} must have the set type") of nkFloatLit..nkFloat128Lit: diff --git a/compiler/vm.nim b/compiler/vm.nim index 1355dd1efd..2a97140d64 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -859,9 +859,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcLdObj: # a = b.c decodeBC(rkNode) - if rb >= regs.len or regs[rb].kind == rkNone or + if rb >= regs.len or regs[rb].kind == rkNone or (regs[rb].kind == rkNode and regs[rb].node == nil) or - (regs[rb].kind == rkNodeAddr and regs[rb].nodeAddr[] == nil): + (regs[rb].kind == rkNodeAddr and regs[rb].nodeAddr[] == nil): stackTrace(c, tos, pc, errNilAccess) else: let src = if regs[rb].kind == rkNode: regs[rb].node else: regs[rb].nodeAddr[] @@ -1472,7 +1472,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let node = regs[rb+i].regToNode node.info = c.debug[pc] if prc.typ[i].kind notin {tyTyped, tyUntyped}: - node.annotateType(prc.typ[i], c.config) + var producedClosure = false + node.annotateType(prc.typ[i], c.config, producedClosure) macroCall.add(node) var a = evalTemplate(macroCall, prc, genSymOwner, c.config, c.cache, c.templInstCounter, c.idgen) From 87ee9c84cb8cba3a404af235755c80037aefea6b Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 19 Sep 2025 02:50:46 +0800 Subject: [PATCH 2/3] makes `DuplicateModuleImport` back to an error (#25178) fixes #24998 Basically it retraces back to the situation before https://github.com/nim-lang/Nim/pull/18366 and https://github.com/nim-lang/Nim/pull/18362, i.e. ```nim import fuzz/a import fuzz/a ``` ```nim import fuzz/a from buzz/a ``` ```nim import fuzz/a except nil from fuzz/a import addInt ``` All of these cases are now flagged as invalid and triggers a redefinition error, i.e., each module name importing is treated as consistent as the symbol definition kinda annoying for importing/exporting with `when conditions` though ref https://github.com/nim-lang/Nim/issues/18762 https://github.com/nim-lang/Nim/issues/20907 ```nim from std/strutils import toLower when not defined(js): from std/strutils import toUpper ``` --- changelog.md | 2 ++ compiler/condsyms.nim | 1 + compiler/lookups.nim | 2 +- compiler/nim.cfg | 2 ++ compiler/suggest.nim | 2 +- lib/pure/terminal.nim | 7 +------ lib/system/alloc.nim | 1 - lib/system/strmantle.nim | 4 +--- 8 files changed, 9 insertions(+), 12 deletions(-) diff --git a/changelog.md b/changelog.md index cf2d4cb170..959f105669 100644 --- a/changelog.md +++ b/changelog.md @@ -25,6 +25,8 @@ errors. - `std/parsesql` has been moved to a nimble package, use `nimble` or `atlas` to install it. +- With `-d:nimPreviewDuplicateModuleError`, importing two modules that share the same name becomes a compile-time error. This includes importing the same module more than once. Use `import foo as foo1` (or other aliases) to avoid collisions. + ## Standard library additions and changes [//]: # "Additions:" diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 54b0ea49af..fcd4cf218e 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -173,4 +173,5 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasXorSet") defineSymbol("nimHasSetLengthSeqUninitMagic") + defineSymbol("nimHasPreviewDuplicateModuleError") diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 34f65973cf..acaad9d9b4 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -388,7 +388,7 @@ proc addDeclAt*(c: PContext; scope: PScope, sym: PSym, info: TLineInfo) = if sym.name.id == ord(wUnderscore): return let conflict = scope.addUniqueSym(sym) if conflict != nil: - if sym.kind == skModule and conflict.kind == skModule: + if sym.kind == skModule and conflict.kind == skModule and not c.config.isDefined("nimPreviewDuplicateModuleError"): # e.g.: import foo; import foo # xxx we could refine this by issuing a different hint for the case # where a duplicate import happens inside an include. diff --git a/compiler/nim.cfg b/compiler/nim.cfg index 0cc8c476ec..9dab29eeed 100644 --- a/compiler/nim.cfg +++ b/compiler/nim.cfg @@ -12,6 +12,8 @@ define:nimPreviewNonVarDestructor define:nimPreviewCheckedClose define:nimPreviewAsmSemSymbol define:nimPreviewCStringComparisons +define:nimPreviewDuplicateModuleError + threads:off #import:"$projectpath/testability" diff --git a/compiler/suggest.nim b/compiler/suggest.nim index 1317fb2e48..3953936eb6 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -35,7 +35,7 @@ import prefixmatches, suggestsymdb from wordrecg import wDeprecated, wError, wAddr, wYield -import std/[algorithm, sets, parseutils, tables, os] +import std/[algorithm, sets, parseutils, os] when defined(nimsuggest): import pathutils # importer diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index 91f0910585..895f658e4d 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -100,7 +100,7 @@ const stylePrefix = "\e[" when defined(windows): - import std/[winlean, os] + import std/os const DUPLICATE_SAME_ACCESS = 2 @@ -926,8 +926,6 @@ when defined(windows): stdout.write "\n" else: - import std/termios - proc readPasswordFromStdin*(prompt: string, password: var string): bool {.tags: [ReadIOEffect, WriteIOEffect].} = password.setLen(0) @@ -981,9 +979,6 @@ proc isTrueColorSupported*(): bool = ## Returns true if a terminal supports true color. return getTerminal().trueColorIsSupported -when defined(windows): - import std/os - proc enableTrueColors*() = ## Enables true color. var term = getTerminal() diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index 63a0970536..fcb7ccb0c8 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -12,7 +12,6 @@ include osalloc import std/private/syslocks -import std/sysatomics template track(op, address, size) = when defined(memTracker): diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim index 694ef64257..92a8653f2a 100644 --- a/lib/system/strmantle.nim +++ b/lib/system/strmantle.nim @@ -8,9 +8,7 @@ # # Compilerprocs for strings that do not depend on the string implementation. - -import std/private/digitsutils - +import std/private/digitsutils as digitsutils2 proc cmpStrings(a, b: string): int {.inline, compilerproc.} = let alen = a.len From 3f48576113bcf6e00d7bf1cafb202c757569a60a Mon Sep 17 00:00:00 2001 From: bptato <60043228+bptato@users.noreply.github.com> Date: Fri, 19 Sep 2025 08:07:43 +0200 Subject: [PATCH 3/3] Disable strict aliasing on clang (#25067) Workaround for #24596. I also took the liberty to disable it on all targets with GCC, since their documentation claims that it is also enabled on -Os. --------- Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com> --- config/nim.cfg | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/config/nim.cfg b/config/nim.cfg index 7c99581396..038c3c9bec 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -257,10 +257,12 @@ clang.objc.options.linker = "-lobjc -lgnustep-base" gcc.options.linker %= "-L $WIND_BASE/target/lib/usr/lib/ppc/PPC32/common -mrtp -fno-strict-aliasing -D_C99 -D_HAS_C9X -std=c99 -fasm -Wall -Wno-write-strings" @end +# seqs_v2 violates strict aliasing. +gcc.options.always %= "${gcc.options.always} -fno-strict-aliasing" # -fno-math-errno is default in OSX, iOS, BSD, Musl, Libm, LLVM, Clang, ICC. # See https://itnext.io/why-standard-c-math-functions-are-slow-d10d02554e33 # and https://gcc.gnu.org/onlinedocs/gcc-12.2.0/gcc/Optimize-Options.html#Optimize-Options -gcc.options.speed = "-O3 -fno-strict-aliasing -fno-ident -fno-math-errno" +gcc.options.speed = "-O3 -fno-ident -fno-math-errno" gcc.options.size = "-Os -fno-ident" @if windows: gcc.options.debug = "-g3 -Og -gdwarf-3" @@ -281,7 +283,7 @@ llvm_gcc.options.size = "-Os" # Configuration for the LLVM CLang compiler: clang.options.debug = "-g" clang.cpp.options.debug = "-g" -clang.options.always = "-w -ferror-limit=3" +clang.options.always = "-w -ferror-limit=3 -fno-strict-aliasing" clang.options.speed = "-O3" clang.options.size = "-Os"