Merge branch 'devel' into mratsim-patch-nimble

This commit is contained in:
ringabout
2026-07-03 17:29:00 +08:00
committed by GitHub
15 changed files with 169 additions and 17 deletions

View File

@@ -2940,6 +2940,13 @@ proc genEnumToStr(p: BProc, e: PNode, d: var TLoc) =
proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
case op
of mAsgn:
let kind = if e[0].sym.name.s == "=sink": nkSinkAsgn else: nkAsgn
let lhs = e[1].skipHiddenAddr
let n = newTreeI(kind, e.info, lhs, e[2])
n.typ = e.typ
cow(p, e[2])
genAsgn(p, n, fastAsgn = kind != nkAsgn)
of mOr, mAnd: genAndOr(p, e, d, op)
of mNot..mUnaryMinusF64: unaryArith(p, e, d, op)
of mUnaryMinusI..mAbsI: unaryArithOverflow(p, e, d, op)

View File

@@ -2279,3 +2279,21 @@ proc genTypeSection(m: BModule, n: PNode) =
discard getTypeDescAux(m, s.typ, intSet, descKindFromSymKind(s.kind))
if m.g.generatedHeader != nil:
discard getTypeDescAux(m.g.generatedHeader, s.typ, intSet, descKindFromSymKind(s.kind))
# Unlike genCppInitializer which returns just the braced value list (e.g. "{a, b}"),
# genCppConstructorExpr returns a full type-prefixed expression (e.g. "Foo(a, b)").
# This is used when a standalone construction expression is needed — e.g. on the
# right-hand side of an assignment — whereas genCppInitializer is used in variable
# declarations where the type is already written separately before the initializer.
proc genCppConstructorExpr(m: BModule, prc: BProc; typ: PType; didGenTemp: var bool): Snippet =
var params = ""
if typ.itemId in m.g.graph.initializersPerType:
let call = m.g.graph.initializersPerType[typ.itemId]
if call != nil:
var p = prc
if p == nil:
p = BProc(module: m)
params = genCppParamsForCtor(p, call, didGenTemp)
if prc == nil:
assert p.blocks.len == 0, "BProc belongs to a struct doesnt have blocks"
result = getTypeDesc(m, typ, dkVar) & "(" & params & ")"

View File

@@ -613,7 +613,7 @@ proc resetLoc(p: BProc, loc: var TLoc) =
if isImportedCppType(typ):
var didGenTemp = false
let rl = rdLoc(loc)
let init = genCppInitializer(p.module, p, typ, didGenTemp)
let init = genCppConstructorExpr(p.module, p, typ, didGenTemp)
p.s(cpsStmts).addAssignment(rl, init)
return
if optSeqDestructors in p.config.globalOptions and typ.kind in {tyString, tySequence}:

View File

@@ -34,7 +34,7 @@ import
ropes, wordrecg, renderer,
cgmeth, lowerings, sighashes, modulegraphs, lineinfos,
transf, injectdestructors, sourcemap, astmsgs, pushpoppragmas,
mangleutils
mangleutils, varpartitions
import pipelineutils
@@ -1298,14 +1298,16 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
xtyp = etySeq
case xtyp
of etySeq:
if x.typ.kind in {tyVar, tyLent} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded:
if x.typ.kind in {tyVar, tyLent} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded or
(x.kind == nkSym and sfCursor in x.sym.flags):
lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
else:
useMagic(p, "nimCopy")
lineF(p, "$1 = nimCopy(null, $2, $3);$n",
[a.rdLoc, b.res, genTypeInfo(p, y.typ)])
of etyObject:
if x.typ.kind in {tyVar, tyLent, tyOpenArray, tyVarargs} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded:
if x.typ.kind in {tyVar, tyLent, tyOpenArray, tyVarargs} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded or
(x.kind == nkSym and sfCursor in x.sym.flags):
lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
else:
useMagic(p, "nimCopy")
@@ -2092,7 +2094,8 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
gen(p, n, a)
case mapType(p, v.typ)
of etyObject, etySeq:
if v.typ.kind in {tyOpenArray, tyVarargs} or needsNoCopy(p, n):
if v.typ.kind in {tyOpenArray, tyVarargs} or needsNoCopy(p, n) or
sfCursor in v.flags:
s = a.res
else:
useMagic(p, "nimCopy")
@@ -2798,6 +2801,11 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
var transformedBody = transformBody(p.module.graph, p.module.idgen, prc, {})
if sfInjectDestructors in prc.flags:
transformedBody = injectDestructorCalls(p.module.graph, p.module.idgen, prc, transformedBody)
else:
# JS has a GC, so the destructor pass is off; but the cursor (alias) analysis
# is independent of ownership and always memory-safe on a traced target.
# Running it lets last-use `var b = a` aliases skip the deep `nimCopy`.
computeCursors(prc, transformedBody, p.module.graph)
p.nested: genStmt(p, transformedBody)

View File

@@ -89,6 +89,18 @@ proc fitNodePostMatch(c: PContext, formal: PType, arg: PNode): PNode =
changeType(c, x, formal, check=true)
result = arg
result = skipHiddenSubConv(result, c.graph, c.idgen)
# Walk through nested statement-list/block expressions to find the innermost
# value node. Empty containers (e.g. `@[]`) inside `nkStmtListExpr` wrappers
# need their type resolved to match the formal type, otherwise the C codegen
# cannot map `tyEmpty` to a concrete type (fixes #25945).
var tail = result
while tail.kind in {nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkPragmaBlock} and tail.len > 0:
tail = tail.lastSon
if tail.typ != nil and tail.typ.isEmptyContainer and
formal.kind notin {tyUntyped, tyBuiltInTypeClass, tyAnything}:
changeType(c, tail, formal, check=true)
# mark inserted converter as used:
var a = result
if a.kind == nkHiddenDeref: a = a[0]

View File

@@ -93,6 +93,7 @@ type
graph: ModuleGraph
c: PContext
escapingParams: IntSet
inNimvmBranch: int
PEffects = var TEffects
const
@@ -1127,7 +1128,7 @@ proc trackCall(tracked: PEffects; n: PNode) =
# sfCompileTime` path in `semProcAux`.
if a.kind == nkSym and a.sym.magic in {mNLen..mNError, mSlurp..mQuoteAst} and
tracked.owner != nil and tracked.owner.kind in routineKinds and
tracked.config.cmd != cmdNimscript:
tracked.config.cmd != cmdNimscript and tracked.inNimvmBranch == 0:
# ...but NOT under `nim e`: nimscript has no codegen backend to protect, and
# marking a routine `sfCompileTime` makes `semExpr` eagerly fold calls to it
# at sem time (emConst), where module-level globals it reads have no VM slot
@@ -1483,7 +1484,9 @@ proc track(tracked: PEffects, n: PNode) =
of nkCaseStmt: trackCase(tracked, n)
of nkWhen: # This should be a "when nimvm" node.
let oldState = tracked.init.len
inc tracked.inNimvmBranch
track(tracked, n[0][1])
dec tracked.inNimvmBranch
tracked.init.setLen(oldState)
track(tracked, n[1][0])
of nkIfStmt, nkIfExpr: trackIf(tracked, n)

View File

@@ -677,9 +677,13 @@ proc deps(c: var Partitions; dest, src: PNode) =
else:
let srcid = variableId(c, s)
if srcid >= 0:
if s.kind notin {skResult, skParam} and (
c.s[srcid].aliveEnd < c.s[vid].aliveEnd):
# you cannot borrow from a local that lives shorter than 'vid':
if s.kind notin {skResult, skParam} and
c.s[srcid].aliveEnd < c.s[vid].aliveEnd and
c.g.config.backend != backendJs:
# you cannot borrow from a local that lives shorter than 'vid'.
# On a traced (JS/GC) target the source object stays alive as long
# as the alias references it, so this lifetime rule does not apply;
# value-semantics safety is enforced by `dangerousMutation` instead.
when explainCursors: echo "B not a cursor ", d.sym, " ", c.s[srcid].aliveEnd, " ", c.s[vid].aliveEnd
c.s[vid].flags.incl preventCursor
elif {isReassigned, preventCursor} * c.s[srcid].flags != {}:
@@ -1003,13 +1007,22 @@ proc checkBorrowedLocations*(par: var Partitions; body: PNode; config: ConfigRef
#if par.s[rid].con.kind == isRootOf and dangerousMutation(par.graphs[par.s[rid].con.graphIndex], par.s[i]):
# cannotBorrow(config, s, par.graphs[par.s[rid].con.graphIndex])
proc jsDeepCopied(t: PType): bool =
## On the JS backend `nimCopy` deep-copies these type classes on every
## assignment, so eliding the copy for a safe alias is worthwhile even when
## the type has no C-style destructor.
t.skipTypes({tyGenericInst, tyAlias, tyDistinct, tyVar, tyLent}).kind in
{tyObject, tyTuple, tyArray, tySequence, tyString}
proc computeCursors*(s: PSym; n: PNode; g: ModuleGraph) =
let jsCursors = g.config.backend == backendJs
var par = computeGraphPartitions(s, n, g, {cursorInference})
for i in 0 ..< par.s.len:
let v = addr(par.s[i])
if v.flags * {ownsData, preventCursor, isConditionallyReassigned} == {} and
v.sym.kind notin {skParam, skResult} and
v.sym.flags * {sfThread, sfGlobal} == {} and hasDestructor(v.sym.typ) and
v.sym.flags * {sfThread, sfGlobal} == {} and
(hasDestructor(v.sym.typ) or (jsCursors and jsDeepCopied(v.sym.typ))) and
v.sym.typ.skipTypes({tyGenericInst, tyAlias}).kind != tyOwned and
(getAttachedOp(g, v.sym.typ, attachedAsgn) == nil or
sfError notin getAttachedOp(g, v.sym.typ, attachedAsgn).flags):

View File

@@ -134,11 +134,11 @@ nimblepath="$home/.nimble/pkgs/"
# BSD got posix_spawn only recently, so we deactivate it for osproc:
define:useFork
@elif haiku:
gcc.options.linker = "-Wl,--as-needed -lnetwork"
gcc.cpp.options.linker = "-Wl,--as-needed -lnetwork"
clang.options.linker = "-Wl,--as-needed -lnetwork"
clang.cpp.options.linker = "-Wl,--as-needed -lnetwork"
tcc.options.linker = "-Wl,--as-needed -lnetwork"
gcc.options.linker = "-Wl,--as-needed -lnetwork -lbsd"
gcc.cpp.options.linker = "-Wl,--as-needed -lnetwork -lbsd"
clang.options.linker = "-Wl,--as-needed -lnetwork -lbsd"
clang.cpp.options.linker = "-Wl,--as-needed -lnetwork -lbsd"
tcc.options.linker = "-Wl,--as-needed -lnetwork -lbsd"
@elif not genode:
# -fopenmp
gcc.options.linker = "-ldl"

View File

@@ -28,6 +28,15 @@ elif defined(netbsd):
EVFILT_PROC* = 4 ## attached to struct proc
EVFILT_SIGNAL* = 5 ## attached to struct proc
EVFILT_TIMER* = 6 ## timers (in ms)
elif defined(haiku):
const
EVFILT_READ* = -1
EVFILT_WRITE* = -2
EVFILT_AIO* = -3 ## attached to aio requests
EVFILT_VNODE* = -4 ## attached to vnodes
EVFILT_PROC* = -5 ## attached to struct proc
EVFILT_SIGNAL* = -6 ## attached to struct proc
EVFILT_TIMER* = -7 ## timers
when defined(macosx):
const
EVFILT_MACHPORT* = -8 ## Mach portsets

View File

@@ -89,7 +89,7 @@ elif defined(emscripten) and not defined(StandaloneHeapSize):
var mmapDescrPos = cast[int](result) -% sizeof(EmscriptenMMapBlock)
var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos)
var mmapDescr = cast[PEmscriptenMMapBlock](mmapDescrPos)
mmapDescr.realSize = realSize
mmapDescr.realPointer = realPointer
@@ -99,7 +99,7 @@ elif defined(emscripten) and not defined(StandaloneHeapSize):
proc osDeallocPages(p: pointer, size: int) {.inline.} =
var mmapDescrPos = cast[int](p) -% sizeof(EmscriptenMMapBlock)
var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos)
var mmapDescr = cast[PEmscriptenMMapBlock](mmapDescrPos)
munmap(mmapDescr.realPointer, mmapDescr.realSize)
elif defined(genode) and not defined(StandaloneHeapSize):

View File

@@ -0,0 +1,12 @@
# Issue: genMagicExpr: mAsgn internal error when using Isolated[T] with primitive types
# in tuple assignment to pointer dereference
import std/isolation
proc main() =
var x: ptr Isolated[float]
x = cast[ptr Isolated[float]](alloc0(sizeof(Isolated[float])))
x[] = isolate(42.0)
dealloc(x)
main()

View File

@@ -75,3 +75,15 @@ block: # importc type inheritance
doAssert(cast[cint](b) == 123)
var c = foo(b)
doAssert(cast[cint](c) == 123)
block: # bug #25945
var stateRefund = 0
let authCode =
if true:
if false:
stateRefund += 0
@[]
else:
@([1.byte])
discard (if true: (discard; @[]) else: @[0])

View File

@@ -0,0 +1,30 @@
#ifndef TCPP_DEFAULT_CTOR_ASSIGNMENT_H
#define TCPP_DEFAULT_CTOR_ASSIGNMENT_H
struct AmbiguousAssign {
int x;
const char* y;
AmbiguousAssign(): x(0), y(nullptr) {}
AmbiguousAssign(int x, const char* y): x(x), y(y) {}
AmbiguousAssign& operator=(int v) {
x = v;
y = nullptr;
return *this;
}
AmbiguousAssign& operator=(const char* s) {
x = 0;
y = s;
return *this;
}
AmbiguousAssign& operator=(const AmbiguousAssign& other) {
x = other.x;
y = other.y;
return *this;
}
};
#endif

View File

@@ -0,0 +1,14 @@
discard """
cmd: "nim cpp $file"
"""
type
AmbiguousAssign {.importcpp, header: "tcpp_default_ctor_assignment.h".} = object
x: cint
y: cstring
proc main =
var xs = newSeq[AmbiguousAssign](3)
doAssert xs.len == 3
main()

View File

@@ -855,3 +855,17 @@ block:
var r = Obj(x: 10)
r.value = 42
doAssert r.x == 42
block: # bug #25949
template loadFile(filename: string): auto =
when nimvm:
staticRead(filename)
else:
"something"
proc roundTrip(): bool =
let content = loadFile("tests/tomls/case.toml")
content == "something"
doAssert roundTrip()