diff --git a/.github/workflows/ci_packages.yml b/.github/workflows/ci_packages.yml index b050b6871e..c9ed6355b7 100644 --- a/.github/workflows/ci_packages.yml +++ b/.github/workflows/ci_packages.yml @@ -18,12 +18,12 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-14] + os: [ubuntu-latest, macos-latest] batch: ["allowed_failures", "0_3", "1_3", "2_3"] # list of `index_num` include: - os: ubuntu-latest cpu: amd64 - - os: macos-14 + - os: macos-latest cpu: arm64 name: '${{ matrix.os }} (batch: ${{ matrix.batch }})' runs-on: ${{ matrix.os }} diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 6b822dfa9d..e023eeee7b 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -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) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index b1f6f862c7..52c8726485 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1663,7 +1663,15 @@ proc genProcPrototype(m: BModule, sym: PSym) = useHeader(m, sym) if lfNoDecl in sym.loc.flags or sfCppMember * sym.flags != {}: return if lfDynamicLib in sym.loc.flags: - if sym.itemId.module != m.module.position and + if m.config.cmd == cmdNifC and m.config.icBackendStage == "cg": + # Under IC per-module cg every demander emits the dynlib proc's DEFINITION + # locally (findPendingModule returns `m`, so symInDynamicLib follows this + # call and the merge stage keeps one def per C name). Emitting the + # cross-module `extern` proto here would register `sym.id` in + # `m.declaredThings` and thereby make that `symInDynamicLib` skip, leaving + # the `Dl_*` symbol declared-but-never-defined -> undefined at link. + discard "definition emitted by symInDynamicLib" + elif sym.itemId.module != m.module.position and not containsOrIncl(m.declaredThings, sym.id): let vis = if isReloadable(m, sym): StaticProc else: Extern let name = mangleDynLibProc(sym) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index fd82a127f7..a04c8e8f95 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -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) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 325e09f011..05e24894a6 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -1209,8 +1209,16 @@ proc trackCall(tracked: PEffects; n: PNode) = else: if laxEffects notin tracked.c.config.legacyFeatures and a.kind == nkSym and a.sym.kind in routineKinds: - let (isHook, opKind) = findHookKind(a.sym.name.s) - if (not isHook) or opKind notin {attachedAsgn, attachedSink, attachedDup}: + # A hook reaching here has no effect list yet, i.e. it has not been + # effect-tracked. Propagating from its (still unset) type flags would + # spuriously mark the caller GC-unsafe/side-effecting: e.g. under + # `nim ic` a concrete `=destroy` reached through a generic + # instantiation is not analyzed before the instance body is tracked + # here. Skip all such hooks (generalizes #25940, which special-cased + # `=asgn`/`=sink`/`=dup`); once analyzed they carry an effect list and + # take the branch below. + let (isHook, _) = findHookKind(a.sym.name.s) + if not isHook: propagateEffects(tracked, n, a.sym) else: mergeRaises(tracked, effectList[exceptionEffects], n) diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index fea6cc540b..3a01eb0bbd 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -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): diff --git a/koch.nim b/koch.nim index dd8f6577fc..4a3f4ae9ae 100644 --- a/koch.nim +++ b/koch.nim @@ -11,10 +11,10 @@ const # examples of possible values for repos: Head, ea82b54 - NimbleStableCommit = "aa03f886e4a111d6af9090c6a1f1271d64b66f7b" # 0.22.2 - AtlasStableCommit = "ff1f4289482dce94ba9f95b3b0ae16d16e21eb3d" # 0.10.1 - ChecksumsStableCommit = "0b8e46379c5bc1bf73d8b3011908389c60fb9b98" # 2.0.1 - SatStableCommit = "e63eaea8baf00bed8bcd5a29ffd8823abb265b39" + NimbleStableCommit = "42ef70c2102a942c46f13eb76872326edd525cec" # 0.22.3 + AtlasStableCommit = "aa6fb162006f3015aa84c4305e15cb4d230f5ad6" # 0.14.7 + ChecksumsStableCommit = "5c132cd332cce5d64a0da9ac3e4c9664313dccb4" # 0.2.2 + SatStableCommit = "9d52513b3c68bfb929dbd687d4fb2836cfee6936" NimonyStableCommit = "6f9ac6655dc6724ae4e5ccb93b8123c18d54391a" # unversioned \ # Note that Nimony uses Nim as a git submodule but we don't want to install diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 41b1b72b2e..801664a518 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -71,7 +71,6 @@ pkg "easygl", "nim c -o:egl -r src/easygl.nim", "https://github.com/jackmott/eas pkg "elvis", url = "https://github.com/nim-lang/elvis" pkg "eth", "nim c -o:common -r tests/common/all_tests" pkg "faststreams" -pkg "fidget" pkg "fusion" pkg "gara" pkg "ggplotnim", "nim c -d:noCairo -r tests/tests.nim" diff --git a/tests/arc/tisolated_primitive.nim b/tests/arc/tisolated_primitive.nim new file mode 100644 index 0000000000..05e760b155 --- /dev/null +++ b/tests/arc/tisolated_primitive.nim @@ -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()