Compare commits

..

5 Commits

Author SHA1 Message Date
ringabout
7c1a2f0ab7 Merge branch 'devel' into pr_mumu 2026-08-18 18:27:24 +08:00
ringabout
1124bb88f8 Add tests for nimvm scope handling and undeclared identifiers 2026-08-14 22:45:17 +08:00
ringabout
b6d94353c0 Update nimvm scope test expectation 2026-08-14 21:23:54 +08:00
ringabout
85d5fef236 Merge branch 'devel' into pr_mumu 2026-08-13 19:23:53 +08:00
ringabout
bcd4cb1201 test openShadowScope for nimvm 2026-07-28 18:19:34 +08:00
23 changed files with 111 additions and 179 deletions

View File

@@ -99,7 +99,6 @@ parameter and result types, not just their source-level shape. Use
works without single-quoting.
- `std/uri`: The `?` operator now appends query parameters to an existing query
string instead of replacing it. Fixes [#19782](https://github.com/nim-lang/Nim/issues/19782).
- `std/jsonutils`: `fromJson` now throws an exception when converting to `array`/`seq` if the JSON isn't an array instead of silently failing
## Language changes

View File

@@ -8,7 +8,7 @@ const
nkBracketExpr, nkDerefExpr, nkHiddenDeref,
nkAddr, nkHiddenAddr,
nkObjDownConv, nkObjUpConv}
PathKinds1* = {nkHiddenStdConv, nkHiddenSubConv, nkCast}
PathKinds1* = {nkHiddenStdConv, nkHiddenSubConv}
proc skipConvDfa*(n: PNode): PNode =
result = n
@@ -125,3 +125,4 @@ proc aliases*(obj, field: PNode): AliasKind =
else:
result = maybe
else: assert false # unreachable

View File

@@ -800,11 +800,7 @@ proc writeSymNode(w: var Writer; dest: var IcBuilder; n: PNode; sym: PSym) =
dest.addSymUse pool.syms.getOrIncl(w.toNifSymName(sym)), info
proc writeNodeFlags(dest: var IcBuilder; flags: set[TNodeFlag]) {.inline.} =
# Comment text is not stored in NIF; `nfHasComment` is process-local
# (see `comment` in ast.nim). Emitting it made IC non-deterministic:
# `copyTree` from a parsed generic kept the comment (`"sh"`) while
# `copyTree` from a cache-loaded generic did not (`"s"`).
writeFlags(dest, flags - {nfHasComment})
writeFlags(dest, flags)
template withNode(w: var Writer; dest: var IcBuilder; n: PNode; body: untyped) =
dest.addParLe pool.tags.getOrIncl(toNifTag(n.kind)), trLineInfo(w, n.info)

View File

@@ -1854,16 +1854,10 @@ proc genVarPrototype(m: BModule, n: PNode) =
typ = ptrType(typ)
if lfDynamicLib in sym.loc.flags:
typ = ptrType(typ)
if sfCodegenDecl in sym.flags:
m.s[cfsVars].addDeclWithVisibility(vis):
m.s[cfsVars].addVar(m, sym,
name = sym.loc.snippet,
typ = typ)
else:
m.s[cfsVars].addVar(m, sym,
name = sym.loc.snippet,
typ = typ,
visibility = vis)
m.s[cfsVars].addVar(m, sym,
name = sym.loc.snippet,
typ = typ,
visibility = vis)
if m.hcrOn:
m.initProc.procSec(cpsLocals).add('\t')
m.initProc.procSec(cpsLocals).addAssignment(sym.loc.snippet,

View File

@@ -454,7 +454,7 @@ proc gen(c: var Con; n: PNode) =
of nkPragmaBlock: gen(c, n.lastSon)
of nkDiscardStmt, nkObjDownConv, nkObjUpConv, nkStringToCString, nkCStringToString:
gen(c, n[0])
of nkConv, nkExprColonExpr, nkExprEqExpr, PathKinds1:
of nkConv, nkExprColonExpr, nkExprEqExpr, nkCast, PathKinds1:
gen(c, n[1])
of nkVarSection, nkLetSection: genVarSection(c, n)
of nkDefer: raiseAssert "dfa construction pass requires the elimination of 'defer'"

View File

@@ -1931,7 +1931,7 @@ proc borrowCheck(c: PContext, n, le, ri: PNode) =
PathKinds0 = {nkDotExpr, nkCheckedFieldExpr,
nkBracketExpr, nkAddr, nkHiddenAddr,
nkObjDownConv, nkObjUpConv}
PathKinds1 = {nkHiddenStdConv, nkHiddenSubConv, nkCast}
PathKinds1 = {nkHiddenStdConv, nkHiddenSubConv}
proc getRoot(n: PNode; followDeref: bool): PNode =
result = n
@@ -2715,9 +2715,13 @@ proc semNimvmBranch(c: PContext, n: PNode, flags: TExprFlags): PNode =
oldNotes = c.config.notes
oldWarningAsErrors = c.config.warningAsErrors
oldFeatures = c.features
# Both branches are checked, but their declarations cannot affect later code.
c.openShadowScope()
try:
result = semExpr(c, n, flags)
finally:
c.closeScope()
c.optionStack = oldOptionStack
c.config.options = oldOptions
c.config.notes = oldNotes

View File

@@ -129,8 +129,6 @@ proc collectObjectTree(graph: ModuleGraph, n: PNode) =
else:
graph.objectTree[root].add (depthLevel, typ)
proc markSideEffect(a: PEffects; reason: PNode | PSym; useLoc: TLineInfo)
proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo; explicit = false) =
if typ == nil or (sfGeneratedOp in tracked.owner.flags and not explicit):
# don't create type bound ops for anything in a function with a `nodestroy` pragma
@@ -149,10 +147,6 @@ proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo; explicit
if op != nil and sfNeverRaises notin op.flags:
tracked.canRaiseDefect = true
break
let destructor = getAttachedOp(tracked.graph, typ, attachedDestructor)
if destructor != nil and sfOverridden in destructor.flags and
tfNoSideEffect notin destructor.typ.flags:
markSideEffect(tracked, destructor, info)
if tracked.config.selectedGC == gcRefc or
optSeqDestructors in tracked.config.globalOptions or
tfHasAsgn in typ.flags:

View File

@@ -3319,31 +3319,31 @@ proc dirInclude(p: var RstParser): PRstNode =
rstMessage(p, meCannotOpenFile, filename)
else:
# XXX: error handling; recursive file inclusion!
let inputString = readFile(path)
let startPosition =
block:
let searchFor = n.getFieldValue("start-after").strip()
if searchFor != "":
let pos = inputString.find(searchFor)
if pos != -1: pos + searchFor.len
else: 0
else:
0
let endPosition =
block:
let searchFor = n.getFieldValue("end-before").strip()
if searchFor != "":
let pos = inputString.find(searchFor, start = startPosition)
if pos != -1: pos - 1
else: 0
else:
inputString.len - 1
if getFieldValue(n, "literal") != "":
result = newRstNode(rnLiteralBlock)
result.add newLeaf(inputString[startPosition..endPosition])
result.add newLeaf(readFile(path))
else:
let inputString = readFile(path)
let startPosition =
block:
let searchFor = n.getFieldValue("start-after").strip()
if searchFor != "":
let pos = inputString.find(searchFor)
if pos != -1: pos + searchFor.len
else: 0
else:
0
let endPosition =
block:
let searchFor = n.getFieldValue("end-before").strip()
if searchFor != "":
let pos = inputString.find(searchFor, start = startPosition)
if pos != -1: pos - 1
else: 0
else:
inputString.len - 1
var q: RstParser
initParser(q, p.s)
let saveFileIdx = p.s.currFileIdx

View File

@@ -152,11 +152,9 @@ proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] =
raise newException(ValueError, "Invalid request protocol. Got: " &
protocol)
result.orig = protocol
var n = protocol.parseSaturatedNatural(result.major, i)
i.inc n
if i < protocol.len and protocol[i] == '.':
inc i
n = protocol.parseSaturatedNatural(result.minor, i)
i.inc protocol.parseSaturatedNatural(result.major, i)
if i < protocol.len: inc i # Skip .
i.inc protocol.parseSaturatedNatural(result.minor, i)
proc sendStatus(client: AsyncSocket, status: string): Future[void] =
client.send("HTTP/1.1 " & status & "\c\L\c\L")

View File

@@ -238,7 +238,6 @@ proc fromJson*[T](a: var T, b: JsonNode, opt = Joptions()) =
a = T()
fromJson(a[], b, opt)
elif T is array:
checkJson b.kind == JArray
checkJson a.len == b.len, "Json array size doesn't match for " & $T
var i = 0
for ai in mitems(a):
@@ -249,7 +248,6 @@ proc fromJson*[T](a: var T, b: JsonNode, opt = Joptions()) =
for val in b.getElems:
incl a, jsonTo(val, E)
elif T is seq:
checkJson b.kind == JArray
a.setLen b.len
for i, val in b.getElems:
fromJson(a[i], val, opt)

View File

@@ -155,17 +155,15 @@ type
MemRegion = object
when usesRegionHandles:
# Keeping the handle here does change the layout, but until proven otherwise
# this layout is more readable and shouldn't regress performance.
regionHandle: ptr RegionHandle
when not defined(gcDestructors):
minLargeObj, maxLargeObj: int
freeSmallChunks: array[0..max(1, SmallChunkSize div MemAlign-1), PSmallChunk]
# List of available chunks per size class. Only one is expected to be active per class.
when defined(gcDestructors) and not usesRegionHandles:
when defined(gcDestructors):
sharedFreeLists: SharedFreeLists
# Remote-free buckets live on the MemRegion when there is no
# RegionHandle. Threaded memory managers with handles keep them on the handle instead.
# Used directly without threads. Threaded builds use RegionHandle but
# retain this 2 KiB spacer: removing it regresses 2-4 KiB allocations.
flBitmap: uint32
slBitmap: array[RealFli, uint32]
matrix: array[RealFli, array[MaxSli, PBigChunk]]
@@ -965,19 +963,13 @@ proc bigChunkAlignOffset(alignment: int): int {.inline.} =
else:
result = align(sizeof(BigChunk) + sizeof(FreeCell), alignment) - sizeof(BigChunk) - sizeof(FreeCell)
template rawAllocAux(aligned: static bool) {.dirty.} =
proc rawAlloc(a: var MemRegion, requestedSize: int, alignment: int = 0): pointer =
when defined(nimTypeNames):
inc(a.allocCounter)
sysAssert(allocInv(a), "rawAlloc: begin")
sysAssert(roundup(65, 8) == 72, "rawAlloc: roundup broken")
when aligned:
var size = roundup(requestedSize, max(MemAlign, alignment))
let alignOff = smallChunkAlignOffset(alignment)
else:
# Common `alloc` path: no custom alignment. Keep this a separate
# instantiation so clang does not emit `smallChunkAlignOffset(0)`.
var size = (requestedSize + (MemAlign - 1)) and not (MemAlign - 1)
const alignOff = 0
var size = roundup(requestedSize, max(MemAlign, alignment))
let alignOff = smallChunkAlignOffset(alignment)
sysAssert(size >= sizeof(FreeCell), "rawAlloc: requested size too small")
sysAssert(size >= requestedSize, "insufficient allocated size!")
#c_fprintf(stdout, "alloc; size: %ld; %ld\n", requestedSize, size)
@@ -994,13 +986,11 @@ template rawAllocAux(aligned: static bool) {.dirty.} =
if atomicLoadN(sharedHead, ATOMIC_RELAXED) != nil:
tc.freeList = atomicExchangeN(sharedHead, nil, ATOMIC_ACQUIRE)
else:
let sharedHead = addr a.sharedFreeLists[s]
tc.freeList = sharedHead[]
sharedHead[] = nil
# Empty peeks are the common local case; skip the walk and the
# `free += 0` / `occ -= 0` stores clang would otherwise keep.
if tc.freeList != nil:
compensateCounters(a, tc, size)
tc.freeList = a.sharedFreeLists[s]
a.sharedFreeLists[s] = nil
# If `tc.freeList` isn't nil, `tc` gains capacity. Calculate how
# much it gained and how many foreign cells are included.
compensateCounters(a, tc, size)
# allocate a small block: for small chunks, we use only its next pointer
let s = size div MemAlign
@@ -1081,7 +1071,7 @@ template rawAllocAux(aligned: static bool) {.dirty.} =
# For big chunks with custom alignment, allocate extra space.
# Since chunks are page-aligned, the needed padding is a compile-time
# deterministic value rather than a worst-case estimate.
let alignPad = when aligned: bigChunkAlignOffset(alignment) else: 0
let alignPad = bigChunkAlignOffset(alignment)
size = requestedSize + bigChunkOverhead() + alignPad
# allocate a large block
var c = if size >= HugeChunkSize: getHugeChunk(a, size)
@@ -1106,12 +1096,6 @@ template rawAllocAux(aligned: static bool) {.dirty.} =
when defined(heaptrack):
heaptrack_malloc(result, requestedSize)
proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
rawAllocAux(false)
proc rawAlloc(a: var MemRegion, requestedSize: int, alignment: int): pointer =
rawAllocAux(true)
proc rawAlloc0(a: var MemRegion, requestedSize: int): pointer =
result = rawAlloc(a, requestedSize)
zeroMem(result, requestedSize)

View File

@@ -1,4 +0,0 @@
var codegenDeclGlobal* {.codegenDecl: "$# /* custom declaration */ $#".} = 123
proc readCodegenDeclGlobal*(): int {.inline.} =
codegenDeclGlobal

View File

@@ -1,13 +0,0 @@
discard """
output: '''
123
123
'''
ccodecheck: "'extern NI /* custom declaration */ codegenDeclGlobal'"
targets: "c cpp"
"""
import ./mcodegendeclglobal
echo codegenDeclGlobal
echo readCodegenDeclGlobal()

View File

@@ -1,28 +0,0 @@
discard """
matrix: "--mm:orc"
output: "destroy b"
"""
# bug #26123
type
A = ptr AObj
AObj = object
b: B
B = distinct ptr BObj
BObj = object
a: A
proc `=destroy`(r: var B) =
echo "destroy b"
proc main() =
var a = create(AObj)
var b = B(create(BObj))
a.b = b
cast[ptr BObj](b).a = a
main()

View File

@@ -1,13 +0,0 @@
discard """
cmd: "nim check --hints:off $file"
errormsg: "'del' can have side effects"
file: "system.nim"
"""
type MyObject = object
proc `=destroy`(v: var MyObject) =
echo "hello"
func remove(v: var seq[MyObject]) =
v.del(0)

View File

@@ -63,7 +63,7 @@ proc foo2 =
discard
else:
let x = 1
doAssert x == 1
doAssert not declared(x)
when false:
discard

View File

@@ -451,12 +451,6 @@ template fn() =
let json = inner.toJson(ToJsonOptions(enumMode: joptEnumSymbol))
doAssert $json == """{"x":"hello","y":"A"}"""
block arrayTypeCheck:
let json = """{"key": "value"}""".parseJson()
var output: seq[int]
doAssertRaises(ValueError):
output.fromJson(json)
block: # bug #21638
type Something = object

View File

@@ -1630,38 +1630,6 @@ And this should **NOT** be visible in `docs.html`
doAssert "<em>Visible</em>" == rstToHtml(input, {roSandboxDisabled}, defaultConfig())
removeFile("other.rst")
test "`:literal:` flag":
"code.nim".writeFile("""
discard
""")
let input = """
.. include:: code.nim
:literal:
"""
check "<pre>discard\n</pre>" == rstToHtml(input, {roSandboxDisabled}, defaultConfig())
removeFile("code.nim")
test "Include everything between in `:literal:` mode":
"code.nim".writeFile("""
proc notIncluded = discard
#CodeStart
proc included = discard
#CodeEnd
proc notIncluded = discard
""")
let input = """
.. include:: code.nim
:literal:
:start-after: #CodeStart
:end-before: #CodeEnd
"""
check "<pre>\nproc included = discard\n</pre>" == rstToHtml(input, {roSandboxDisabled}, defaultConfig())
removeFile("code.nim")
suite "RST escaping":
test "backspaces":
check("""\ this""".toAst == dedent"""

9
tests/vm/t26048.nim Normal file
View File

@@ -0,0 +1,9 @@
# issue #26048, `$` declarations must not leak from `when nimvm`
type U = object
when nimvm:
proc `$`(_: U): string = "s"
var n: U
doAssert $n != "s"

View File

@@ -0,0 +1,12 @@
# issue #23687
when nimvm:
proc mytest(a: int) =
echo a
else:
template mytest(a: int) =
echo a + 42
proc xxx() =
mytest(100) #[tt.Error
^ undeclared identifier: 'mytest']#

View File

@@ -0,0 +1,13 @@
# issue #23688
when nimvm:
proc mytest(a: int) =
echo a
else:
template mytest(a: untyped) =
echo a + 42
proc xxx() =
mytest(100) #[tt.Error
^ undeclared identifier: 'mytest']#
xxx()

View File

@@ -0,0 +1,10 @@
# issue #13450, example 3
proc bar() =
when nimvm:
let y = 1
else:
let y = 2
discard y #[tt.Error
^ undeclared identifier: 'y']#
bar()

16
tests/whenstmt/t26044.nim Normal file
View File

@@ -0,0 +1,16 @@
# issue #26044
discard """
cmd: "nim check --hints:off --warnings:off $file"
action: reject
nimout: '''
t26044.nim(15, 11) Error: undeclared identifier: 'g'
t26044.nim(15, 11) Error: expression 'g' has no type (or is ambiguous)
'''
"""
proc p =
when nimvm:
var g: int
discard g
p()