diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 171d656230..df50d93954 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/ccgtypes.nim b/compiler/ccgtypes.nim index 58343f2c44..98a85b38ae 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -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 & ")" diff --git a/compiler/cgen.nim b/compiler/cgen.nim index b54160edf8..b1f6f862c7 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -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}: 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/sem.nim b/compiler/sem.nim index a9f87cd006..7f777a7add 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -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] diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 008f2317ba..73e3b4072a 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -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) 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/config/nim.cfg b/config/nim.cfg index 6a259321fb..26948ba928 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -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" diff --git a/lib/posix/kqueue.nim b/lib/posix/kqueue.nim index 2450cdb424..f5edcaf2e0 100644 --- a/lib/posix/kqueue.nim +++ b/lib/posix/kqueue.nim @@ -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 diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim index 4177b47b1f..db7f4c1482 100644 --- a/lib/system/osalloc.nim +++ b/lib/system/osalloc.nim @@ -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): 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() diff --git a/tests/ccgbugs2/tcodegen.nim b/tests/ccgbugs2/tcodegen.nim index bca361e813..da5ab045cc 100644 --- a/tests/ccgbugs2/tcodegen.nim +++ b/tests/ccgbugs2/tcodegen.nim @@ -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]) diff --git a/tests/cpp/tcpp_default_ctor_assignment.h b/tests/cpp/tcpp_default_ctor_assignment.h new file mode 100644 index 0000000000..356cbb9381 --- /dev/null +++ b/tests/cpp/tcpp_default_ctor_assignment.h @@ -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 \ No newline at end of file diff --git a/tests/cpp/tcpp_default_ctor_assignment.nim b/tests/cpp/tcpp_default_ctor_assignment.nim new file mode 100644 index 0000000000..88d9671ae6 --- /dev/null +++ b/tests/cpp/tcpp_default_ctor_assignment.nim @@ -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() \ No newline at end of file diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim index 906ced7b83..c8ed29f7ce 100644 --- a/tests/vm/tvmmisc.nim +++ b/tests/vm/tvmmisc.nim @@ -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() +