fixed merge conflict

This commit is contained in:
araq
2025-11-18 18:41:07 +01:00
19 changed files with 385 additions and 318 deletions

View File

@@ -1006,7 +1006,7 @@ proc genTupleElem(p: BProc, e: PNode, d: var TLoc) =
var
i: int = 0
var a: TLoc = initLocExpr(p, e[0])
let tupType = a.t.skipTypes(abstractInst+{tyVar})
let tupType = a.t.skipTypes(abstractInst+{tyVar}+tyUserTypeClasses) # ref #25227
assert tupType.kind == tyTuple
d.inheritLocation(a)
discard getTypeDesc(p.module, a.t) # fill the record's fields.loc

View File

@@ -2452,7 +2452,10 @@ proc handleProcGlobals(m: BModule) =
# fixes recursive calls #24997
swap stmts, m.preInitProc.s(cpsStmts)
genStmts(m.preInitProc, procGlobals[i])
var transformedN = procGlobals[i]
if sfInjectDestructors in m.module.flags:
transformedN = injectDestructorCalls(m.g.graph, m.idgen, m.module, transformedN)
genStmts(m.preInitProc, transformedN)
swap stmts, m.preInitProc.s(cpsStmts)
handleProcGlobals(m)

View File

@@ -137,7 +137,7 @@ proc bindParam(c: PContext, m: var MatchCon; key, v: PType): bool {. discardable
# check previously bound value
if not matchType(c, old, value, m):
return false
elif key.hasElementType and key.elementType.kind != tyNone:
elif key.hasElementType and not key.elementType.isNil and key.elementType.kind != tyNone:
# check constaint
if matchType(c, unrollGenericParam(key), value, m) == false:
return false
@@ -358,6 +358,14 @@ proc matchType(c: PContext; fo, ao: PType; m: var MatchCon): bool =
if not matchType(c, f[i], ea[i], m):
result = false
break
elif f.kind == tyGenericInvocation:
# bind potential generic constraints into body
let body = f.base
for i in 1 ..< len(f):
bindParam(c,m,body[i-1], f[i])
result = matchType(c, body, a, m)
else: # tyGenericInst
result = matchType(c, f.last, a, m)
of tyOrdinal:
result = isOrdinalType(a, allowEnumWithHoles = false) or a.kind == tyGenericParam
of tyStatic:

View File

@@ -968,10 +968,10 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
{sfPure, sfGlobal} <= v.sym.flags and
isInProc
let value = moveOrCopy(v, ri, c, s, if v.kind == nkSym: {IsDecl} else: {})
if isGlobalPragma:
c.graph.procGlobals.add value
c.graph.procGlobals.add n
else:
let value = moveOrCopy(v, ri, c, s, if v.kind == nkSym: {IsDecl} else: {})
result.add value
elif ri.kind == nkEmpty and c.inLoop > 0:
let skipInit = v.kind == nkDotExpr and # Closure var

View File

@@ -1375,7 +1375,8 @@ proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) =
r.typ = etyBaseIndex
let b = if n.kind == nkHiddenAddr: n[0] else: n
gen(p, b[0], a)
if skipTypes(b[0].typ, abstractVarRange).kind == tyTuple:
if skipTypes(b[0].typ, abstractVarRange + tyTypeClasses).kind == tyTuple:
# ref #25227 about `+ tyTypeClasses`
r.res = makeJSString("Field" & $getFieldPosition(p, b[1]))
else:
if b[1].kind != nkSym: internalError(p.config, b[1].info, "genFieldAddr")

View File

@@ -16,7 +16,7 @@ const
ChecksumsStableCommit = "0b8e46379c5bc1bf73d8b3011908389c60fb9b98" # 2.0.1
SatStableCommit = "faf1617f44d7632ee9601ebc13887644925dcc01"
NimonyStableCommit = "3660f375dc0ec25da3401d3eb28603864340dc6d" # unversioned \
NimonyStableCommit = "322178d9af6676363d5237382c6d6c1b4e56d3cd" # unversioned \
# Note that Nimony uses Nim as a git submodule but we don't want to install
# Nimony's dependency to Nim as we are Nim. So a `git clone` without --recursive
# is **required** here.

View File

@@ -19,12 +19,16 @@ import std/private/miscdollars
type InstantiationInfo = tuple[filename: string, line: int, column: int]
{.push overflowChecks: off, rangeChecks: off.}
proc `$`(info: InstantiationInfo): string =
# The +1 is needed here
# instead of overriding `$` (and changing its meaning), consider explicit name.
result = ""
result.toLocation(info.filename, info.line, info.column + 1)
{.pop.}
# ---------------------------------------------------------------------------

View File

@@ -36,13 +36,13 @@ proc utoa2Digits*(buf: var openArray[char]; pos: int; digits: uint32) {.inline.}
buf[pos+1] = digits100[2 * digits + 1]
#copyMem(buf, unsafeAddr(digits100[2 * digits]), 2 * sizeof((char)))
proc trailingZeros2Digits*(digits: uint32): int {.inline.} =
proc trailingZeros2Digits*(digits: uint32): int {.inline, enforceNoRaises.} =
trailingZeros100[digits]
when defined(js):
proc numToString(a: SomeInteger): cstring {.importjs: "((#) + \"\")".}
func addChars[T](result: var string, x: T, start: int, n: int) {.inline.} =
func addChars[T](result: var string, x: T, start: int, n: int) {.inline, enforceNoRaises.} =
let old = result.len
result.setLen old + n
template impl =
@@ -54,10 +54,10 @@ func addChars[T](result: var string, x: T, start: int, n: int) {.inline.} =
{.noSideEffect.}:
copyMem result[old].addr, x[start].unsafeAddr, n
func addChars[T](result: var string, x: T) {.inline.} =
func addChars[T](result: var string, x: T) {.inline, enforceNoRaises.} =
addChars(result, x, 0, x.len)
func addIntImpl(result: var string, x: uint64) {.inline.} =
func addIntImpl(result: var string, x: uint64) {.inline, enforceNoRaises.} =
var tmp {.noinit.}: array[24, char]
var num = x
var next = tmp.len - 1
@@ -81,8 +81,6 @@ func addIntImpl(result: var string, x: uint64) {.inline.} =
dec next
addChars(result, tmp, next, tmp.len - next)
when not defined(nimHasEnforceNoRaises):
{.pragma: enforceNoRaises.}
func addInt*(result: var string, x: uint64) {.enforceNoRaises.} =
when nimvm: addIntImpl(result, x)

View File

@@ -4,7 +4,13 @@ template toLocation*(result: var string, file: string | cstring, line: int, col:
## avoids spurious allocations
# Hopefully this can be re-used everywhere so that if a user needs to customize,
# it can be done in a single place.
result.add file
when file is cstring:
var i = 0
while file[i] != '\0':
add(result, file[i])
inc i
else:
result.add file
if line > 0:
result.add "("
addInt(result, line)

View File

@@ -1754,6 +1754,15 @@ type
when NimStackTraceMsgs:
frameMsgLen*: int ## end position in frameMsgBuf for this frame.
when notJSnotNims and not gotoBasedExceptions:
type
PSafePoint = ptr TSafePoint
TSafePoint {.compilerproc, final.} = object
prev: PSafePoint # points to next safe point ON THE STACK
status: int
context: C_JmpBuf
SafePoint = TSafePoint
when defined(nimV2):
var
framePtr {.threadvar.}: PFrame
@@ -1766,6 +1775,111 @@ template newException*(exceptn: typedesc, message: string;
## to `message`. Returns the new exception object.
(ref exceptn)(msg: message, parent: parentException)
# we have to compute this here before turning it off in except.nim anyway ...
const NimStackTrace = compileOption("stacktrace")
const
usesDestructors = defined(gcDestructors) or defined(gcHooks)
when notJSnotNims:
proc setControlCHook*(hook: proc () {.noconv.}) {.raises: [], gcsafe.}
## Allows you to override the behaviour of your application when CTRL+C
## is pressed. Only one such hook is supported.
##
## The handler runs inside a C signal handler and comes with similar
## limitations.
##
## Allocating memory and interacting with most system calls, including using
## `echo`, `string`, `seq`, raising or catching exceptions etc is undefined
## behavior and will likely lead to application crashes.
##
## The OS may call the ctrl-c handler from any thread, including threads
## that were not created by Nim, such as happens on Windows.
##
## ## Example:
##
## ```nim
## var stop: Atomic[bool]
## proc ctrlc() {.noconv.} =
## # Using atomics types is safe!
## stop.store(true)
##
## setControlCHook(ctrlc)
##
## while not stop.load():
## echo "Still running.."
## sleep(1000)
## ```
when not defined(noSignalHandler) and not defined(useNimRtl):
proc unsetControlCHook*()
## Reverts a call to setControlCHook.
when hostOS != "standalone":
proc getStackTrace*(): string {.gcsafe.}
## Gets the current stack trace. This only works for debug builds.
proc getStackTrace*(e: ref Exception): string {.gcsafe.}
## Gets the stack trace associated with `e`, which is the stack that
## lead to the `raise` statement. This only works for debug builds.
var
globalRaiseHook*: proc (e: ref Exception): bool {.nimcall, benign.}
## With this hook you can influence exception handling on a global level.
## If not nil, every 'raise' statement ends up calling this hook.
##
## .. warning:: Ordinary application code should never set this hook! You better know what you do when setting this.
##
## If `globalRaiseHook` returns false, the exception is caught and does
## not propagate further through the call stack.
localRaiseHook* {.threadvar.}: proc (e: ref Exception): bool {.nimcall, benign.}
## With this hook you can influence exception handling on a
## thread local level.
## If not nil, every 'raise' statement ends up calling this hook.
##
## .. warning:: Ordinary application code should never set this hook! You better know what you do when setting this.
##
## If `localRaiseHook` returns false, the exception
## is caught and does not propagate further through the call stack.
outOfMemHook*: proc () {.nimcall, tags: [], benign, raises: [].}
## Set this variable to provide a procedure that should be called
## in case of an `out of memory`:idx: event. The standard handler
## writes an error message and terminates the program.
##
## `outOfMemHook` can be used to raise an exception in case of OOM like so:
##
## ```nim
## var gOutOfMem: ref EOutOfMemory
## new(gOutOfMem) # need to be allocated *before* OOM really happened!
## gOutOfMem.msg = "out of memory"
##
## proc handleOOM() =
## raise gOutOfMem
##
## system.outOfMemHook = handleOOM
## ```
##
## If the handler does not raise an exception, ordinary control flow
## continues and the program is terminated.
unhandledExceptionHook*: proc (e: ref Exception) {.nimcall, tags: [], benign, raises: [].}
## Set this variable to provide a procedure that should be called
## in case of an `unhandle exception` event. The standard handler
## writes an error message and terminates the program, except when
## using `--os:any`
{.push stackTrace: off, profiler: off.}
when defined(memtracker):
include "system/memtracker"
when hostOS == "standalone":
include "system/embedded"
else:
include "system/excpt"
{.pop.}
when not defined(nimPreviewSlimSystem):
import std/assertions
export assertions
@@ -1844,9 +1958,6 @@ proc `<`*[T: tuple](x, y: T): bool =
include "system/gc_interface"
# we have to compute this here before turning it off in except.nim anyway ...
const NimStackTrace = compileOption("stacktrace")
import system/coro_detection
{.push checks: off.}
@@ -1855,53 +1966,6 @@ import system/coro_detection
# however, stack-traces are available for most parts
# of the code
when notJSnotNims:
var
globalRaiseHook*: proc (e: ref Exception): bool {.nimcall, benign.}
## With this hook you can influence exception handling on a global level.
## If not nil, every 'raise' statement ends up calling this hook.
##
## .. warning:: Ordinary application code should never set this hook! You better know what you do when setting this.
##
## If `globalRaiseHook` returns false, the exception is caught and does
## not propagate further through the call stack.
localRaiseHook* {.threadvar.}: proc (e: ref Exception): bool {.nimcall, benign.}
## With this hook you can influence exception handling on a
## thread local level.
## If not nil, every 'raise' statement ends up calling this hook.
##
## .. warning:: Ordinary application code should never set this hook! You better know what you do when setting this.
##
## If `localRaiseHook` returns false, the exception
## is caught and does not propagate further through the call stack.
outOfMemHook*: proc () {.nimcall, tags: [], benign, raises: [].}
## Set this variable to provide a procedure that should be called
## in case of an `out of memory`:idx: event. The standard handler
## writes an error message and terminates the program.
##
## `outOfMemHook` can be used to raise an exception in case of OOM like so:
##
## ```nim
## var gOutOfMem: ref EOutOfMemory
## new(gOutOfMem) # need to be allocated *before* OOM really happened!
## gOutOfMem.msg = "out of memory"
##
## proc handleOOM() =
## raise gOutOfMem
##
## system.outOfMemHook = handleOOM
## ```
##
## If the handler does not raise an exception, ordinary control flow
## continues and the program is terminated.
unhandledExceptionHook*: proc (e: ref Exception) {.nimcall, tags: [], benign, raises: [].}
## Set this variable to provide a procedure that should be called
## in case of an `unhandle exception` event. The standard handler
## writes an error message and terminates the program, except when
## using `--os:any`
when defined(js) or defined(nimdoc):
proc add*(x: var string, y: cstring) {.asmNoStackFrame.} =
## Appends `y` to `x` in place.
@@ -2028,6 +2092,16 @@ template unlikely*(val: bool): bool =
import system/dollars
export dollars
when notJSnotNims:
{.push stackTrace: off, profiler: off.}
include "system/chcks"
# we cannot compile this with stack tracing on
# as it would recurse endlessly!
include "system/integerops"
{.pop.}
when defined(nimAuditDelete):
{.pragma: auditDelete, deprecated: "review this call for out of bounds behavior".}
else:
@@ -2110,17 +2184,17 @@ when notJSnotNims:
nimZeroMem(p, size)
when declared(memTrackerOp):
memTrackerOp("zeroMem", p, size)
proc copyMem(dest, source: pointer, size: Natural) =
proc copyMem(dest, source: pointer, size: Natural) {.enforceNoRaises.} =
nimCopyMem(dest, source, size)
when declared(memTrackerOp):
memTrackerOp("copyMem", dest, size)
proc moveMem(dest, source: pointer, size: Natural) =
proc moveMem(dest, source: pointer, size: Natural) {.enforceNoRaises.} =
c_memmove(dest, source, csize_t(size))
when declared(memTrackerOp):
memTrackerOp("moveMem", dest, size)
proc equalMem(a, b: pointer, size: Natural): bool =
proc equalMem(a, b: pointer, size: Natural): bool {.enforceNoRaises.} =
nimCmpMem(a, b, size) == 0
proc cmpMem(a, b: pointer, size: Natural): int =
proc cmpMem(a, b: pointer, size: Natural): int {.enforceNoRaises.} =
nimCmpMem(a, b, size).int
when not defined(js) or defined(nimscript):
@@ -2173,15 +2247,6 @@ when not defined(js) and declared(alloc0) and declared(dealloc):
inc(i)
dealloc(a)
when notJSnotNims and not gotoBasedExceptions:
type
PSafePoint = ptr TSafePoint
TSafePoint {.compilerproc, final.} = object
prev: PSafePoint # points to next safe point ON THE STACK
status: int
context: C_JmpBuf
SafePoint = TSafePoint
when not defined(js):
when hasThreadSupport:
when hostOS != "standalone":
@@ -2194,63 +2259,6 @@ when not defined(js):
when not defined(useNimRtl) and not defined(createNimRtl): initStackBottom()
when declared(initGC): initGC()
when notJSnotNims:
proc setControlCHook*(hook: proc () {.noconv.}) {.raises: [], gcsafe.}
## Allows you to override the behaviour of your application when CTRL+C
## is pressed. Only one such hook is supported.
##
## The handler runs inside a C signal handler and comes with similar
## limitations.
##
## Allocating memory and interacting with most system calls, including using
## `echo`, `string`, `seq`, raising or catching exceptions etc is undefined
## behavior and will likely lead to application crashes.
##
## The OS may call the ctrl-c handler from any thread, including threads
## that were not created by Nim, such as happens on Windows.
##
## ## Example:
##
## ```nim
## var stop: Atomic[bool]
## proc ctrlc() {.noconv.} =
## # Using atomics types is safe!
## stop.store(true)
##
## setControlCHook(ctrlc)
##
## while not stop.load():
## echo "Still running.."
## sleep(1000)
## ```
when not defined(noSignalHandler) and not defined(useNimRtl):
proc unsetControlCHook*()
## Reverts a call to setControlCHook.
when hostOS != "standalone":
proc getStackTrace*(): string {.gcsafe.}
## Gets the current stack trace. This only works for debug builds.
proc getStackTrace*(e: ref Exception): string {.gcsafe.}
## Gets the stack trace associated with `e`, which is the stack that
## lead to the `raise` statement. This only works for debug builds.
{.push stackTrace: off, profiler: off.}
when defined(memtracker):
include "system/memtracker"
when hostOS == "standalone":
include "system/embedded"
else:
include "system/excpt"
include "system/chcks"
# we cannot compile this with stack tracing on
# as it would recurse endlessly!
include "system/integerops"
{.pop.}
when not defined(js):
# this is a hack: without this when statement, you would get:

View File

@@ -42,11 +42,13 @@ proc writeToStdErr(msg: string) {.inline.} =
# fix bug #13115: handles correctly '\0' unlike default implicit conversion to cstring
writeToStdErr(msg.cstring, msg.len)
proc cstrToStrBuiltin(x: cstring): string {.magic: "CStrToStr", noSideEffect.}
proc showErrorMessage(data: cstring, length: int) {.gcsafe, raises: [].} =
var toWrite = true
if errorMessageWriter != nil:
try:
errorMessageWriter($data)
errorMessageWriter(cstrToStrBuiltin data)
toWrite = false
except:
discard
@@ -261,7 +263,10 @@ template addFrameEntry(s: var string, f: StackTraceEntry|PFrame) =
var oldLen = s.len
s.toLocation(f.filename, f.line, 0)
for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ')
add(s, f.procname)
var i = 0
while f.procname[i] != '\0':
add(s, f.procname[i])
inc i
when NimStackTraceMsgs:
when typeof(f) is StackTraceEntry:
add(s, f.frameMsg)
@@ -282,9 +287,35 @@ proc `$`(stackTraceEntries: seq[StackTraceEntry]): string =
elif s[i].line == reraisedFromEnd: result.add "]]\n"
else: addFrameEntry(result, s[i])
when hasSomeStackTrace:
const
Ten = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
proc auxWriteStackTrace(f: PFrame, s: var string) =
proc i2s(x: int64): string =
# quick reimplementation; optimized for code size, no dependencies
if x < 0:
if x == -9223372036854775808:
result = "-9223372036854775808"
else:
result = "-" & i2s(0-x)
elif x < 10:
result = Ten[int x] # saves allocations
else:
var y = x
while true:
result.add char((y mod 10) + int('0'))
y = y div 10
if y == 0: break
let last = result.len-1
var i = 0
let b = result.len div 2
while i < b:
let ch = result[i]
result[i] = result[last-i]
result[last-i] = ch
inc i
when hasSomeStackTrace:
proc auxWriteStackTrace(f: PFrame, s: var string) {.raises: [].} =
when hasThreadSupport:
var
tempFrames: array[maxStackTraceLines, PFrame] # but better than a threadvar
@@ -322,14 +353,14 @@ when hasSomeStackTrace:
for j in countdown(i-1, 0):
if tempFrames[j] == nil:
add(s, "(")
add(s, $skipped)
s.add(i2s(skipped))
add(s, " calls omitted) ...\n")
else:
addFrameEntry(s, tempFrames[j])
proc stackTraceAvailable*(): bool
proc rawWriteStackTrace(s: var string) =
proc rawWriteStackTrace(s: var string) {.raises: [].} =
when defined(nimStackTraceOverride):
add(s, "Traceback (most recent call last, using override)\n")
auxWriteStackTraceWithOverride(s)
@@ -388,7 +419,7 @@ proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy, gcsafe.} =
add(buf, "Error: unhandled exception: ")
add(buf, e.msg)
add(buf, " [")
add(buf, $e.name)
add(buf, cstrToStrBuiltin(e.name))
add(buf, "]\n")
if onUnhandledException != nil:
@@ -418,7 +449,7 @@ proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy, gcsafe.} =
xadd(buf, e.name, e.name.len)
add(buf, "]\n")
if onUnhandledException != nil:
onUnhandledException($cast[cstring](buf.addr))
onUnhandledException(cstrToStrBuiltin(cast[cstring](buf.addr)))
else:
showErrorMessage(cast[cstring](buf.addr), L)
@@ -515,8 +546,7 @@ proc reraiseException() {.compilerRtl.} =
else:
raiseExceptionAux(currException)
proc threadTrouble() =
# also forward declared, it is 'raises: []' hence the try-except.
proc threadTrouble() {.raises: [], gcsafe.} =
try:
if currException != nil: reportUnhandledError(currException)
except:
@@ -559,10 +589,11 @@ const nimCallDepthLimit {.intdefine.} = 2000
proc callDepthLimitReached() {.noinline.} =
writeStackTrace()
let msg = "Error: call depth limit reached in a debug build (" &
$nimCallDepthLimit & " function calls). You can change it with " &
"-d:nimCallDepthLimit=<int> but really try to avoid deep " &
"recursions instead.\n"
var msg = "Error: call depth limit reached in a debug build ("
msg.add(i2s(nimCallDepthLimit))
msg.add(" function calls). You can change it with " &
"-d:nimCallDepthLimit=<int> but really try to avoid deep " &
"recursions instead.\n")
showErrorMessage2(msg)
rawQuit(1)
@@ -601,7 +632,7 @@ when defined(cpp) and appType != "lib" and not gotoBasedExceptions and
{.emit: "#endif".}
except Exception:
msg = currException.getStackTrace() & "Error: unhandled exception: " &
currException.msg & " [" & $currException.name & "]"
currException.msg & " [" & cstrToStrBuiltin(currException.name) & "]"
except StdException as e:
msg = "Error: unhandled cpp exception: " & $e.what()
except:

View File

@@ -1,6 +1,4 @@
# ----------------- GC interface ---------------------------------------------
const
usesDestructors = defined(gcDestructors) or defined(gcHooks)
when not usesDestructors:
{.pragma: nodestroy.}

View File

@@ -1,13 +1,13 @@
when notJSnotNims:
proc zeroMem*(p: pointer, size: Natural) {.inline, noSideEffect,
tags: [], raises: [].}
tags: [], raises: [], enforceNoRaises.}
## Overwrites the contents of the memory at `p` with the value 0.
##
## Exactly `size` bytes will be overwritten. Like any procedure
## dealing with raw memory this is **unsafe**.
proc copyMem*(dest, source: pointer, size: Natural) {.inline, benign,
tags: [], raises: [].}
tags: [], raises: [], enforceNoRaises.}
## Copies the contents from the memory at `source` to the memory
## at `dest`.
## Exactly `size` bytes will be copied. The memory
@@ -15,7 +15,7 @@ when notJSnotNims:
## memory this is **unsafe**.
proc moveMem*(dest, source: pointer, size: Natural) {.inline, benign,
tags: [], raises: [].}
tags: [], raises: [], enforceNoRaises.}
## Copies the contents from the memory at `source` to the memory
## at `dest`.
##
@@ -25,7 +25,7 @@ when notJSnotNims:
## dealing with raw memory this is still **unsafe**, though.
proc equalMem*(a, b: pointer, size: Natural): bool {.inline, noSideEffect,
tags: [], raises: [].}
tags: [], raises: [], enforceNoRaises.}
## Compares the memory blocks `a` and `b`. `size` bytes will
## be compared.
##
@@ -34,7 +34,7 @@ when notJSnotNims:
## **unsafe**.
proc cmpMem*(a, b: pointer, size: Natural): int {.inline, noSideEffect,
tags: [], raises: [].}
tags: [], raises: [], enforceNoRaises.}
## Compares the memory blocks `a` and `b`. `size` bytes will
## be compared.
##

View File

@@ -23,6 +23,8 @@ type
const nimStrVersion {.core.} = 2
{.push overflowChecks: off, rangeChecks: off.}
template isLiteral(s): bool = (s.p == nil) or (s.p.cap and strlitFlag) == strlitFlag
template contentSize(cap): int = cap + 1 + sizeof(NimStrPayloadBase)
@@ -141,6 +143,10 @@ proc mnewString(len: int): NimStringV2 {.compilerproc.} =
result = NimStringV2(len: len, p: p)
proc setLengthStrV2(s: var NimStringV2, newLen: int) {.compilerRtl.} =
## Sets the `s` length to `newLen` zeroing memory on growth.
## Terminating zero at `s[newLen]` for cstring compatibility is set
## on length change, **excluding** `newLen == 0`.
## Negative `newLen` is **not** bound to zero.
if newLen == 0:
discard "do not free the buffer here, pattern 's.setLen 0' is common for avoiding allocations"
else:
@@ -223,3 +229,5 @@ func capacity*(self: string): int {.inline.} =
let str = cast[ptr NimStringV2](unsafeAddr self)
result = if str.p != nil: str.p.cap and not strlitFlag else: 0
{.pop.}

View File

@@ -48,6 +48,8 @@ else:
cast[NimString](newObjNoInit(addr(strDesc), size))
proc rawNewStringNoInit(space: int): NimString =
## Returns a newly-allocated NimString with `reserved` set.
## .. warning:: `len` and the terminating null-byte are not set!
let s = max(space, 7)
result = allocStrNoInit(sizeof(TGenericSeq) + s + 1)
result.reserved = s
@@ -55,11 +57,21 @@ proc rawNewStringNoInit(space: int): NimString =
result.elemSize = 1
proc rawNewString(space: int): NimString {.compilerproc.} =
## Returns a newly-allocated and *not* zeroed NimString
## with everything required set:
## - `reserved`
## - `len` (0)
## - terminating null-byte
result = rawNewStringNoInit(space)
result.len = 0
result.data[0] = '\0'
proc mnewString(len: int): NimString {.compilerproc.} =
## Returns a newly-allocated and zeroed NimString
## with everything required set:
## - `reserved`
## - `len`
## - terminating null-byte
result = rawNewStringNoInit(len)
result.len = len
zeroMem(addr result.data[0], len + 1)
@@ -91,29 +103,28 @@ proc toNimStr(str: cstring, len: int): NimString {.compilerproc.} =
copyMem(addr(result.data), str, len)
result.data[len] = '\0'
proc toOwnedCopy(src: NimString): NimString {.inline.} =
## Expects `src` to be not nil and initialized (len and terminating zero set)
result = rawNewStringNoInit(src.len)
result.len = src.len
copyMem(addr(result.data), addr(src.data), src.len + 1)
proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} =
if str == nil: NimString(nil)
else: toNimStr(str, str.len)
proc copyString(src: NimString): NimString {.compilerRtl.} =
## Expects `src` to be initialized (len and terminating zero set)
if src != nil:
if (src.reserved and seqShallowFlag) != 0:
result = src
else:
result = rawNewStringNoInit(src.len)
result.len = src.len
copyMem(addr(result.data), addr(src.data), src.len + 1)
result = toOwnedCopy(src)
sysAssert((seqShallowFlag and result.reserved) == 0, "copyString")
when defined(nimShallowStrings):
if (src.reserved and strlitFlag) != 0:
result.reserved = (result.reserved and not strlitFlag) or seqShallowFlag
proc newOwnedString(src: NimString; n: int): NimString =
result = rawNewStringNoInit(n)
result.len = n
copyMem(addr(result.data), addr(src.data), n)
result.data[n] = '\0'
proc copyStringRC1(src: NimString): NimString {.compilerRtl.} =
if src != nil:
if (src.reserved and seqShallowFlag) != 0:
@@ -129,10 +140,10 @@ proc copyStringRC1(src: NimString): NimString {.compilerRtl.} =
result.reserved = s
when defined(gogc):
result.elemSize = 1
result.len = src.len
copyMem(addr(result.data), addr(src.data), src.len + 1)
else:
result = rawNewStringNoInit(src.len)
result.len = src.len
copyMem(addr(result.data), addr(src.data), src.len + 1)
result = toOwnedCopy(src)
sysAssert((seqShallowFlag and result.reserved) == 0, "copyStringRC1")
when defined(nimShallowStrings):
if (src.reserved and strlitFlag) != 0:
@@ -140,28 +151,9 @@ proc copyStringRC1(src: NimString): NimString {.compilerRtl.} =
proc copyDeepString(src: NimString): NimString {.inline.} =
if src != nil:
result = rawNewStringNoInit(src.len)
result.len = src.len
copyMem(addr(result.data), addr(src.data), src.len + 1)
result = toOwnedCopy(src)
proc addChar(s: NimString, c: char): NimString =
# is compilerproc!
if s == nil:
result = rawNewStringNoInit(1)
result.len = 0
else:
result = s
if result.len >= result.space:
let r = resize(result.space)
result = rawNewStringNoInit(r)
result.len = s.len
copyMem(addr result.data[0], unsafeAddr(s.data[0]), s.len+1)
result.reserved = r
result.data[result.len] = c
result.data[result.len+1] = '\0'
inc(result.len)
# These routines should be used like following:
# The following resize- and append- routines should be used like following:
# <Nim code>
# s &= "Hello " & name & ", how do you feel?"
#
@@ -193,46 +185,61 @@ proc addChar(s: NimString, c: char): NimString =
# s = rawNewString(0);
proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} =
## Prepares `dest` for appending up to `addlen` new bytes.
## .. warning:: Does not update `len`!
if dest == nil:
result = rawNewString(addlen)
elif dest.len + addlen <= dest.space:
return rawNewString(addlen)
let futureLen = dest.len + addlen
if futureLen <= dest.space:
result = dest
else: # slow path:
let sp = max(resize(dest.space), dest.len + addlen)
# growth strategy: next `resize` step or exact `futureLen` if jumping over
let sp = max(resize(dest.space), futureLen)
result = rawNewStringNoInit(sp)
result.len = dest.len
copyMem(addr result.data[0], unsafeAddr(dest.data[0]), dest.len+1)
result.reserved = sp
#result = rawNewString(sp)
#copyMem(result, dest, dest.len + sizeof(TGenericSeq))
# DO NOT UPDATE LEN YET: dest.len = newLen
proc appendString(dest, src: NimString) {.compilerproc, inline.} =
if src != nil:
copyMem(addr(dest.data[dest.len]), addr(src.data), src.len + 1)
inc(dest.len, src.len)
# newFutureLen > space => addlen is never zero, copy terminating null anyway
copyMem(addr(result.data), addr(dest.data), dest.len + 1)
proc appendChar(dest: NimString, c: char) {.compilerproc, inline.} =
dest.data[dest.len] = c
dest.data[dest.len+1] = '\0'
inc(dest.len)
proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} =
let n = max(newLen, 0)
proc addChar(s: NimString, c: char): NimString =
# is compilerproc! used in `ccgexprs.nim`
if s == nil:
if n == 0:
return s
else:
result = mnewString(n)
elif n <= s.space:
result = rawNewStringNoInit(1)
result.len = 0
else:
result = s
if s.len >= s.space: # len.inc would overflow (`>` just in case)
let sp = resize(s.space)
result = rawNewStringNoInit(sp)
copyMem(addr(result.data), addr(s.data), s.len)
result.len = s.len
result.appendChar(c)
proc appendString(dest, src: NimString) {.compilerproc, inline.} =
## Raw, does not prepare `dest` space for copying
if src != nil:
copyMem(addr(dest.data[dest.len]), addr(src.data), src.len + 1)
inc(dest.len, src.len)
proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} =
## Sets the `s` length to `newLen` zeroing memory on growth.
## Terminating zero at `s[newLen]` for cstring compatibility is set
## on any length change, including `newLen == 0`.
## Negative `newLen` is bound to zero.
let n = max(newLen, 0)
if s == nil: # early return check
return if n == 0: s else: mnewString(n) # sets everything required
if n <= s.space:
result = s # len and null-byte still need updating
else:
let sp = max(resize(s.space), n)
result = rawNewStringNoInit(sp)
result.len = s.len
copyMem(addr result.data[0], unsafeAddr(s.data[0]), s.len)
result = rawNewStringNoInit(sp) # len and null-byte not set
copyMem(addr(result.data), addr(s.data), s.len)
zeroMem(addr result.data[s.len], n - s.len)
result.reserved = sp
result.len = n
result.data[n] = '\0'
@@ -252,14 +259,6 @@ proc incrSeq(seq: PGenericSeq, elemSize, elemAlign: int): PGenericSeq {.compiler
result.reserved = r
inc(result.len)
proc incrSeqV2(seq: PGenericSeq, elemSize, elemAlign: int): PGenericSeq {.compilerproc.} =
# incrSeq version 2
result = seq
if result.len >= result.space:
let r = resize(result.space)
result = cast[PGenericSeq](growObj(result, align(GenericSeqSize, elemAlign) + elemSize * r))
result.reserved = r
proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerproc.} =
if s == nil:
result = cast[PGenericSeq](newSeq(typ, 1))
@@ -274,112 +273,68 @@ proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerproc.} =
# since we steal the content from 's', it's crucial to set s's len to 0.
s.len = 0
proc setLengthSeq(seq: PGenericSeq, elemSize, elemAlign, newLen: int): PGenericSeq {.
compilerRtl, inl.} =
result = seq
if result.space < newLen:
let r = max(resize(result.space), newLen)
result = cast[PGenericSeq](growObj(result, align(GenericSeqSize, elemAlign) + elemSize * r))
result.reserved = r
elif newLen < result.len:
# we need to decref here, otherwise the GC leaks!
when not defined(boehmGC) and not defined(nogc) and
not defined(gcMarkAndSweep) and not defined(gogc) and
not defined(gcRegions):
if ntfNoRefs notin extGetCellType(result).base.flags:
for i in newLen..result.len-1:
forAllChildrenAux(dataPointer(result, elemAlign, elemSize, i),
extGetCellType(result).base, waZctDecRef)
proc extendCapacityRaw(src: PGenericSeq; typ: PNimType;
elemSize, elemAlign, newLen: int): PGenericSeq {.inline.} =
## Reallocs `src` to fit `newLen` elements without any checks.
## Capacity always increases to at least next `resize` step.
let newCap = max(resize(src.space), newLen)
result = cast[PGenericSeq](newSeq(typ, newCap))
copyMem(dataPointer(result, elemAlign), dataPointer(src, elemAlign), src.len * elemSize)
# since we steal the content from 's', it's crucial to set s's len to 0.
src.len = 0
# XXX: zeroing out the memory can still result in crashes if a wiped-out
# cell is aliased by another pointer (ie proc parameter or a let variable).
# This is a tough problem, because even if we don't zeroMem here, in the
# presence of user defined destructors, the user will expect the cell to be
# "destroyed" thus creating the same problem. We can destroy the cell in the
# finalizer of the sequence, but this makes destruction non-deterministic.
zeroMem(dataPointer(result, elemAlign, elemSize, newLen), (result.len-%newLen) *% elemSize)
result.len = newLen
proc truncateRaw(src: PGenericSeq; baseFlags: set[TNimTypeFlag]; isTrivial: bool;
elemSize, elemAlign, newLen: int): PGenericSeq {.inline.} =
## Truncates `src` to `newLen` without any checks.
## Does not set `src.len`
# sysAssert src.space > newlen
# sysAssert newLen < src.len
result = src
# we need to decref here, otherwise the GC leaks!
when not defined(boehmGC) and not defined(nogc) and
not defined(gcMarkAndSweep) and not defined(gogc) and
not defined(gcRegions):
if ntfNoRefs notin baseFlags:
for i in newLen..<result.len:
forAllChildrenAux(dataPointer(result, elemAlign, elemSize, i),
extGetCellType(result).base, waZctDecRef)
# XXX: zeroing out the memory can still result in crashes if a wiped-out
# cell is aliased by another pointer (ie proc parameter or a let variable).
# This is a tough problem, because even if we don't zeroMem here, in the
# presence of user defined destructors, the user will expect the cell to be
# "destroyed" thus creating the same problem. We can destroy the cell in the
# finalizer of the sequence, but this makes destruction non-deterministic.
if not isTrivial: # optimization for trivial types
zeroMem(dataPointer(result, elemAlign, elemSize, newLen),
((result.len-%newLen) *% elemSize))
proc setLengthSeqUninit(s: PGenericSeq, typ: PNimType, newLen: int, isTrivial: bool): PGenericSeq {.
compilerRtl.} =
sysAssert typ.kind == tySequence, "setLengthSeqUninit: type is not a seq"
template setLengthSeqImpl(s: PGenericSeq, typ: PNimType, newLen: int; isTrivial: bool;
doInit: static bool) =
if s == nil:
if newLen == 0:
result = s
else:
result = cast[PGenericSeq](newSeq(typ, newLen))
if newLen == 0: return s
else: return cast[PGenericSeq](newSeq(typ, newLen)) # newSeq zeroes!
else:
let elemSize = typ.base.size
let elemAlign = typ.base.align
if s.space < newLen:
let r = max(resize(s.space), newLen)
result = cast[PGenericSeq](newSeq(typ, r))
copyMem(dataPointer(result, elemAlign), dataPointer(s, elemAlign), s.len * elemSize)
# since we steal the content from 's', it's crucial to set s's len to 0.
s.len = 0
elif newLen < s.len:
result = s
# we need to decref here, otherwise the GC leaks!
when not defined(boehmGC) and not defined(nogc) and
not defined(gcMarkAndSweep) and not defined(gogc) and
not defined(gcRegions):
if ntfNoRefs notin typ.base.flags:
for i in newLen..result.len-1:
forAllChildrenAux(dataPointer(result, elemAlign, elemSize, i),
extGetCellType(result).base, waZctDecRef)
# XXX: zeroing out the memory can still result in crashes if a wiped-out
# cell is aliased by another pointer (ie proc parameter or a let variable).
# This is a tough problem, because even if we don't zeroMem here, in the
# presence of user defined destructors, the user will expect the cell to be
# "destroyed" thus creating the same problem. We can destroy the cell in the
# finalizer of the sequence, but this makes destruction non-deterministic.
if not isTrivial: # optimization for trivial types
zeroMem(dataPointer(result, elemAlign, elemSize, newLen), (result.len-%newLen) *% elemSize)
else:
result = s
result = if newLen > s.space:
s.extendCapacityRaw(typ, elemSize, elemAlign, newLen)
elif newLen < s.len:
s.truncateRaw(typ.base.flags, isTrivial, elemSize, elemAlign, newLen)
else:
when doInit:
zeroMem(dataPointer(s, elemAlign, elemSize, s.len), (newLen-%s.len) *% elemSize)
s
result.len = newLen
proc setLengthSeqUninit(s: PGenericSeq; typ: PNimType; newLen: int; isTrivial: bool): PGenericSeq {.
compilerRtl.} =
sysAssert typ.kind == tySequence, "setLengthSeqUninit: type is not a seq"
setLengthSeqImpl(s, typ, newLen, isTrivial, doInit = false)
proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int, isTrivial: bool): PGenericSeq {.
compilerRtl.} =
sysAssert typ.kind == tySequence, "setLengthSeqV2: type is not a seq"
if s == nil:
if newLen == 0:
result = s
else:
result = cast[PGenericSeq](newSeq(typ, newLen))
else:
let elemSize = typ.base.size
let elemAlign = typ.base.align
if s.space < newLen:
let r = max(resize(s.space), newLen)
result = cast[PGenericSeq](newSeq(typ, r))
copyMem(dataPointer(result, elemAlign), dataPointer(s, elemAlign), s.len * elemSize)
# since we steal the content from 's', it's crucial to set s's len to 0.
s.len = 0
elif newLen < s.len:
result = s
# we need to decref here, otherwise the GC leaks!
when not defined(boehmGC) and not defined(nogc) and
not defined(gcMarkAndSweep) and not defined(gogc) and
not defined(gcRegions):
if ntfNoRefs notin typ.base.flags:
for i in newLen..result.len-1:
forAllChildrenAux(dataPointer(result, elemAlign, elemSize, i),
extGetCellType(result).base, waZctDecRef)
# XXX: zeroing out the memory can still result in crashes if a wiped-out
# cell is aliased by another pointer (ie proc parameter or a let variable).
# This is a tough problem, because even if we don't zeroMem here, in the
# presence of user defined destructors, the user will expect the cell to be
# "destroyed" thus creating the same problem. We can destroy the cell in the
# finalizer of the sequence, but this makes destruction non-deterministic.
if not isTrivial: # optimization for trivial types
zeroMem(dataPointer(result, elemAlign, elemSize, newLen), (result.len-%newLen) *% elemSize)
else:
result = s
zeroMem(dataPointer(result, elemAlign, elemSize, result.len), (newLen-%result.len) *% elemSize)
result.len = newLen
setLengthSeqImpl(s, typ, newLen, isTrivial, doInit = true)
func capacity*(self: string): int {.inline.} =
## Returns the current capacity of the string.
@@ -402,3 +357,4 @@ func capacity*[T](self: seq[T]): int {.inline.} =
let sek = cast[PGenericSeq](self)
result = if sek != nil: sek.space else: 0

View File

@@ -2,7 +2,7 @@ var
nimThreadDestructionHandlers* {.rtlThreadVar.}: seq[proc () {.closure, gcsafe, raises: [].}]
when not defined(boehmgc) and not hasSharedHeap and not defined(gogc) and not defined(gcRegions):
proc deallocOsPages() {.rtl, raises: [].}
proc threadTrouble() {.raises: [], gcsafe.}
# create for the main thread. Note: do not insert this data into the list
# of all threads; it's not to be stopped etc.
when not defined(useNimRtl):

View File

@@ -546,3 +546,42 @@ proc len[T](t: DummyIndexable[T]): int =
let dummyIndexable = DummyIndexable(@[1, 2])
echoAll(dummyIndexable)
block:
type
C = concept
proc a(x: Self, i: int)
AObj[T] = object
x: T
ARef[T] = ref AObj[T]
proc a[T: int](x: ARef[T], i: int) =
discard
assert (ref AObj[int]) is C
block:
type
C = concept
proc a(x: Self, i: int)
AObj[T; B] = object
x: T
ARef[T; B] = ref AObj[T,B]
proc a[T: int, C: float](x: ARef[T, C], i: int) =
discard
assert (ref AObj[int, int]) isnot C
assert (ref AObj[int, float]) is C
block:
type
C = concept
proc a(x: Self, i: int)
AObj[T] = object
ARef[T] = ref AObj[T]
proc a(x: ARef, i: int) =
discard
assert (ref AObj[int]) is C

View File

@@ -55,3 +55,10 @@ block: # bug #24997
doAssert not isNil(u(typeof(B.j)))
R()
discard u(B)
proc f2(str: string): string = str
proc m2() =
let v {.global, used.}: string = f2(f2("123"))
assert v == "123"
m2()

View File

@@ -544,7 +544,7 @@ proc srcdist(c: var ConfigData) =
var dir = getOutputDir(c) / buildDir(osA, cpuA)
if dirExists(dir): removeDir(dir)
createDir(dir)
var cmd = ("$# compile -f --incremental:off --compileonly " &
var cmd = ("$# compile -f --incremental:off --d:nimKochBootstrap --compileonly " &
"--gen_mapping --cc:gcc --skipUserCfg" &
" --os:$# --cpu:$# $# $#") %
[findNim(), osname, cpuname, c.nimArgs, c.mainfile]