mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-01 11:23:40 +00:00
Compare commits
40 Commits
pr_lent_it
...
pr_ehui
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2c7e1788e | ||
|
|
d9e28aac8e | ||
|
|
c84764a097 | ||
|
|
1d7510dff0 | ||
|
|
9b80b2e868 | ||
|
|
f5930d0bb3 | ||
|
|
4497d89267 | ||
|
|
f959a02037 | ||
|
|
3c6449dbdd | ||
|
|
f1ff8b6d9e | ||
|
|
46259cd0b8 | ||
|
|
4b374eb0a6 | ||
|
|
c8e805a2fa | ||
|
|
73986c03a1 | ||
|
|
88a18de44f | ||
|
|
7813bd8b92 | ||
|
|
645e131739 | ||
|
|
7d2f28b046 | ||
|
|
f4dd00c4cc | ||
|
|
3e2cea21ed | ||
|
|
cfa769fefc | ||
|
|
8771451701 | ||
|
|
43ac102ca8 | ||
|
|
393d27b57d | ||
|
|
9f5c193c1d | ||
|
|
4f6b727d9e | ||
|
|
f9647276d8 | ||
|
|
2c946950f4 | ||
|
|
bbc5bbdcc7 | ||
|
|
6204e48ba5 | ||
|
|
f0c60b06e5 | ||
|
|
4c8052a45b | ||
|
|
7295f57833 | ||
|
|
f0077a12b2 | ||
|
|
568eccd7f8 | ||
|
|
f2e4ae0016 | ||
|
|
df7a114d7a | ||
|
|
e9a0c9634e | ||
|
|
b73908a361 | ||
|
|
cbe02aa9de |
@@ -35,6 +35,10 @@ errors.
|
||||
|
||||
- Adds a new warning `--warning:ImplicitRangeConversion` that detects downsizing implicit conversions to range types (e.g., `int -> range[0..255]` or `range[1..256] -> range[0..255]`) that could cause runtime panics. Safe conversions like `range[0..255] -> range[0..65535]` and explicit casts do not trigger warnings. `int` to `Natural` and `Positive` conversions do not trigger warnings, which can be enabled with `--warning:systemRangeConversion`.
|
||||
|
||||
- Procedure compatibility also checks the backend representation of the
|
||||
parameter and result types, not just their source-level shape. Use
|
||||
`--legacy:procParamTypeBackendAliases` to restore the older behavior.
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
[//]: # "Additions:"
|
||||
@@ -77,6 +81,9 @@ errors.
|
||||
- `std/re` and `std/nre` are deprecated as PCRE library is obsolete.
|
||||
Use https://github.com/nitely/nim-regex or `std/nre2`.
|
||||
See: https://github.com/nim-lang/Nim/issues/23668.
|
||||
- `std/pegs` now correctly lexes UTF-8 bytes inside bare identifier-style
|
||||
terminals, so case-insensitive matching of non-ASCII terms (e.g. ``\i café``)
|
||||
works without single-quoting.
|
||||
|
||||
## Language changes
|
||||
|
||||
|
||||
@@ -1647,9 +1647,13 @@ proc canRaise*(fn: PNode): bool =
|
||||
if fn.typ.n[0].kind == nkSym:
|
||||
result = false
|
||||
else:
|
||||
# A proc-typed value with no explicit raises slot still has
|
||||
# unspecified effects, which sempass2 treats conservatively.
|
||||
# Codegen needs to do the same in order to keep goto-exception
|
||||
# checks after indirect/closure calls.
|
||||
result = ((fn.typ.n[0].len < effectListLen) or
|
||||
(fn.typ.n[0][exceptionEffects] != nil and
|
||||
fn.typ.n[0][exceptionEffects].safeLen > 0))
|
||||
fn.typ.n[0][exceptionEffects] == nil or
|
||||
fn.typ.n[0][exceptionEffects].safeLen > 0)
|
||||
else:
|
||||
result = false
|
||||
|
||||
|
||||
@@ -1904,7 +1904,9 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
|
||||
|
||||
var tmp: TLoc = default(TLoc)
|
||||
var r: Rope
|
||||
let needsZeroMem = p.config.selectedGC notin {gcArc, gcAtomicArc, gcOrc, gcYrc} or nfAllFieldsSet notin e.flags
|
||||
let needsZeroMem =
|
||||
nfAllFieldsSet notin e.flags or
|
||||
(optSeqDestructors notin p.config.globalOptions and containsGarbageCollectedRef(t))
|
||||
if useTemp:
|
||||
tmp = getTemp(p, t)
|
||||
r = rdLoc(tmp)
|
||||
@@ -2816,9 +2818,9 @@ proc genWasMoved(p: BProc; n: PNode) =
|
||||
# [addrLoc(p.config, a), getTypeDesc(p.module, a.t)])
|
||||
|
||||
proc genMove(p: BProc; n: PNode; d: var TLoc) =
|
||||
var a: TLoc = initLocExpr(p, n[1].skipAddr, {lfEnforceDeref, lfPrepareForMutation})
|
||||
if n.len == 4:
|
||||
# generated by liftdestructors:
|
||||
var a: TLoc = initLocExpr(p, n[1].skipAddr, {lfEnforceDeref, lfPrepareForMutation})
|
||||
var src: TLoc = initLocExpr(p, n[2])
|
||||
let destVal = rdLoc(a)
|
||||
let srcVal = rdLoc(src)
|
||||
@@ -2838,29 +2840,16 @@ proc genMove(p: BProc; n: PNode; d: var TLoc) =
|
||||
else:
|
||||
if d.k == locNone: d = getTemp(p, n.typ)
|
||||
if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc, gcYrc}:
|
||||
genAssignment(p, d, a, {})
|
||||
var op = getAttachedOp(p.module.g.graph, n.typ, attachedWasMoved)
|
||||
if op == nil:
|
||||
if op == nil or sfOverridden notin op.flags:
|
||||
var a: TLoc = initLocExpr(p, n[1].skipAddr, {lfEnforceDeref, lfPrepareForMutation})
|
||||
genAssignment(p, d, a, {})
|
||||
resetLoc(p, a)
|
||||
else:
|
||||
var b = initLocExpr(p, newSymNode(op))
|
||||
case skipTypes(a.t, abstractVar+{tyStatic}).kind
|
||||
of tyOpenArray, tyVarargs: # todo fixme generated `wasMoved` hooks for
|
||||
# openarrays, but it probably shouldn't?
|
||||
let ra = rdLoc(a)
|
||||
var s: string
|
||||
if reifiedOpenArray(a.lode):
|
||||
if a.t.kind in {tyVar, tyLent}:
|
||||
s = derefField(ra, "Field0") & cArgumentSeparator & derefField(ra, "Field1")
|
||||
else:
|
||||
s = dotField(ra, "Field0") & cArgumentSeparator & dotField(ra, "Field1")
|
||||
else:
|
||||
s = ra & cArgumentSeparator & ra & "Len_0"
|
||||
p.s(cpsStmts).addCallStmt(rdLoc(b), s)
|
||||
else:
|
||||
let val = if p.module.compileToCpp: rdLoc(a) else: byRefLoc(p, a)
|
||||
p.s(cpsStmts).addCallStmt(rdLoc(b), val)
|
||||
n[1] = makeAddr(n[1], p.module.idgen)
|
||||
genCall(p, n, d)
|
||||
else:
|
||||
var a: TLoc = initLocExpr(p, n[1].skipAddr, {lfEnforceDeref, lfPrepareForMutation})
|
||||
genAssignment(p, d, a, {})
|
||||
resetLoc(p, a)
|
||||
|
||||
|
||||
@@ -230,7 +230,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int, isReturnStmt
|
||||
# Called by return and break stmts.
|
||||
# Deals with issues faced when jumping out of try/except/finally stmts.
|
||||
|
||||
var stack = newSeq[tuple[fin: PNode, inExcept: bool, label: Natural]](0)
|
||||
var stack = newSeq[tuple[fin: PNode, inExcept: bool, isHidden: bool, label: Natural]](0)
|
||||
|
||||
inc p.withinBlockLeaveActions
|
||||
for i in 1..howManyTrys:
|
||||
@@ -341,9 +341,9 @@ proc genCppParamsForCtor(p: BProc; call: PNode; didGenTemp: var bool): Snippet =
|
||||
call[i][0]
|
||||
else:
|
||||
call[i]
|
||||
if param.kind != nkBracketExpr or param.typ.kind in
|
||||
if not param.typ.isCompileTimeOnly and (param.kind != nkBracketExpr or param.typ.kind in
|
||||
{tyRef, tyPtr, tyUncheckedArray, tyArray, tyOpenArray,
|
||||
tyVarargs, tySequence, tyString, tyCstring, tyTuple}:
|
||||
tyVarargs, tySequence, tyString, tyCstring, tyTuple}):
|
||||
let tempLoc = initLocExprSingleUse(p, param)
|
||||
didGenTemp = didGenTemp or tempLoc.k == locTemp
|
||||
genOtherArg(p, call, i, typ, res, argBuilder)
|
||||
@@ -836,12 +836,26 @@ proc raiseExitCleanup(p: BProc, destroy: string) =
|
||||
p.s(cpsStmts).addGoto("LA" & $p.nestedTryStmts[^1].label & "_")
|
||||
|
||||
proc finallyActions(p: BProc) =
|
||||
if p.config.exc != excGoto and p.nestedTryStmts.len > 0 and p.nestedTryStmts[^1].inExcept:
|
||||
# if the current try stmt have a finally block,
|
||||
# we must execute it before reraising
|
||||
let finallyBlock = p.nestedTryStmts[^1].fin
|
||||
if finallyBlock != nil:
|
||||
genSimpleBlock(p, finallyBlock[0])
|
||||
if p.config.exc != excGoto:
|
||||
# Walk past compiler-injected `nkHiddenTryStmt` wrappers (e.g. ARC's
|
||||
# destructor try/finally that wraps `except T as e:` bodies) to reach
|
||||
# the user's actual try. We must NOT walk past a real user try whose
|
||||
# body we are currently in, because a raise from there will be caught
|
||||
# by that try's own except branches rather than escaping outward.
|
||||
#
|
||||
# If after skipping wrappers the next entry is a user try in its
|
||||
# except branch (inExcept=true), inline its finally body before the
|
||||
# raise propagates — without this, the C++ sibling-catch rule would
|
||||
# cause the user's catch(...)/finally pair to be bypassed and the
|
||||
# finally would be silently dropped.
|
||||
for i in countdown(p.nestedTryStmts.high, 0):
|
||||
if p.nestedTryStmts[i].isHidden:
|
||||
continue
|
||||
if p.nestedTryStmts[i].inExcept:
|
||||
let finallyBlock = p.nestedTryStmts[i].fin
|
||||
if finallyBlock != nil:
|
||||
genSimpleBlock(p, finallyBlock[0])
|
||||
return
|
||||
|
||||
proc raiseInstr(p: BProc; result: var Builder) =
|
||||
if p.config.exc == excGoto:
|
||||
@@ -1185,7 +1199,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||
lineCg(p, cpsLocals, "std::exception_ptr T$1_;$n", [etmp])
|
||||
|
||||
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
|
||||
p.nestedTryStmts.add((fin, false, 0.Natural))
|
||||
p.nestedTryStmts.add((fin, false, t.kind == nkHiddenTryStmt, 0.Natural))
|
||||
|
||||
if t.kind == nkHiddenTryStmt:
|
||||
lineCg(p, cpsStmts, "try {$n", [])
|
||||
@@ -1223,6 +1237,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||
else:
|
||||
scope = initScope(p.s(cpsStmts))
|
||||
# we handled the error:
|
||||
linefmt(p, cpsStmts, "T$1_ = nullptr;$n", [etmp])
|
||||
expr(p, t[i][0], d)
|
||||
linefmt(p, cpsStmts, "#popCurrentException();$n", [])
|
||||
endBlockWith(p):
|
||||
@@ -1371,7 +1386,7 @@ proc genTryCppOld(p: BProc, t: PNode, d: var TLoc) =
|
||||
genLineDir(p, t)
|
||||
cgsym(p.module, "popCurrentExceptionEx")
|
||||
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
|
||||
p.nestedTryStmts.add((fin, false, 0.Natural))
|
||||
p.nestedTryStmts.add((fin, false, t.kind == nkHiddenTryStmt, 0.Natural))
|
||||
startBlockWith(p):
|
||||
p.s(cpsStmts).add("try {\n")
|
||||
expr(p, t[0], d)
|
||||
@@ -1450,7 +1465,7 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
|
||||
let lab = p.labels
|
||||
let hasExcept = t[1].kind == nkExceptBranch
|
||||
if hasExcept: inc p.withinTryWithExcept
|
||||
p.nestedTryStmts.add((fin, false, Natural lab))
|
||||
p.nestedTryStmts.add((fin, false, t.kind == nkHiddenTryStmt, Natural lab))
|
||||
|
||||
p.flags.incl nimErrorFlagAccessed
|
||||
|
||||
@@ -1656,7 +1671,7 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
|
||||
initElifBranch(p.s(cpsStmts), nonQuirkyIf, removeSinglePar(
|
||||
cOp(Equal, dotField(safePoint, "status"), cIntValue(0))))
|
||||
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
|
||||
p.nestedTryStmts.add((fin, quirkyExceptions, 0.Natural))
|
||||
p.nestedTryStmts.add((fin, quirkyExceptions, t.kind == nkHiddenTryStmt, 0.Natural))
|
||||
expr(p, t[0], d)
|
||||
var quirkyIf = default(IfBuilder)
|
||||
var quirkyScope = default(ScopeBuilder)
|
||||
|
||||
@@ -75,10 +75,13 @@ type
|
||||
flags*: set[TCProcFlag]
|
||||
lastLineInfo*: TLineInfo # to avoid generating excessive 'nimln' statements
|
||||
currLineInfo*: TLineInfo # AST codegen will make this superfluous
|
||||
nestedTryStmts*: seq[tuple[fin: PNode, inExcept: bool, label: Natural]]
|
||||
nestedTryStmts*: seq[tuple[fin: PNode, inExcept: bool, isHidden: bool, label: Natural]]
|
||||
# in how many nested try statements we are
|
||||
# (the vars must be volatile then)
|
||||
# bool is true when are in the except part of a try block
|
||||
# `inExcept` is true when we are in the except part of a try block.
|
||||
# `isHidden` is true for compiler-injected `nkHiddenTryStmt` wrappers
|
||||
# (e.g. ARC's destructor try/finally around `except T as e:` bodies);
|
||||
# finallyActions walks past such wrappers to reach the user's try.
|
||||
finallySafePoints*: seq[Rope] # For correctly cleaning up exceptions when
|
||||
# using return in finally statements
|
||||
labels*: Natural # for generating unique labels in the C proc
|
||||
|
||||
@@ -930,7 +930,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
if m.len == 0:
|
||||
localError(conf, info, "Cannot resolve filename: " & arg)
|
||||
else:
|
||||
conf.implicitImports.add m
|
||||
conf.implicitImports.add(if arg.startsWith(stdPrefix): arg else: m)
|
||||
of "include":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
if pass in {passCmd2, passPP}:
|
||||
|
||||
@@ -488,10 +488,19 @@ proc generateBuildFile(c: DepContext): string =
|
||||
let exeFile = changeFileExt(c.nodes[0].files[0].nimFile, ExeExt)
|
||||
b.addTree "do"
|
||||
b.addIdent "nim_nifc"
|
||||
# Input: .nim file (expanded as argument) and .nif file (dependency)
|
||||
# Input: .nim file (expanded as argument)
|
||||
b.addTree "input"
|
||||
b.addStrLit mainNif
|
||||
b.endTree()
|
||||
# Also depend on the semmed .nif files of the main module and all its
|
||||
# dependencies. nifmake's topological sort orders nodes by depth; without
|
||||
# these inputs the nim_nifc node sits at depth 1 (no recognized inputs)
|
||||
# alongside the nifler nodes and runs *before* the nim_m steps that
|
||||
# produce the .nif files it needs to read.
|
||||
for node in c.nodes:
|
||||
b.addTree "input"
|
||||
b.addStrLit c.semmedFile(node.files[0])
|
||||
b.endTree()
|
||||
b.addTree "output"
|
||||
b.addStrLit exeFile
|
||||
b.endTree()
|
||||
|
||||
@@ -46,7 +46,7 @@ proc isLocation(n: PNode): bool = not n.isValue
|
||||
|
||||
proc isLet(n: PNode): bool =
|
||||
if n.kind == nkSym:
|
||||
if n.sym.kind in {skLet, skTemp, skForVar}:
|
||||
if n.sym.kind in {skLet, skConst, skTemp, skForVar}: # guard immutable variables
|
||||
result = true
|
||||
elif n.sym.kind == skParam and skipTypes(n.sym.typ,
|
||||
abstractInst).kind notin {tyVar}:
|
||||
|
||||
@@ -13,7 +13,7 @@ import
|
||||
ast, msgs, options, idents, lookups,
|
||||
semdata, modulepaths, sigmatch, lineinfos,
|
||||
modulegraphs, wordrecg
|
||||
from std/strutils import `%`, startsWith
|
||||
from std/strutils import `%`, startsWith, replace
|
||||
from std/sequtils import addUnique
|
||||
import std/[sets, tables, intsets]
|
||||
|
||||
@@ -304,9 +304,9 @@ proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym =
|
||||
var prefix = ""
|
||||
if realModule.constraint != nil: prefix = realModule.constraint.strVal & "; "
|
||||
message(c.config, n.info, warnDeprecated, prefix & realModule.name.s & " is deprecated")
|
||||
let moduleName = getModuleName(c.config, n)
|
||||
if belongsToStdlib(c.graph, result) and not startsWith(moduleName, stdPrefix) and
|
||||
not startsWith(moduleName, "system/") and not startsWith(moduleName, "packages/"):
|
||||
let moduleNameNorm = getModuleName(c.config, n).replace("\\", "/")
|
||||
if belongsToStdlib(c.graph, result) and not startsWith(moduleNameNorm, stdPrefix) and
|
||||
not startsWith(moduleNameNorm, "system/") and not startsWith(moduleNameNorm, "packages/"):
|
||||
message(c.config, n.info, warnStdPrefix, realModule.name.s)
|
||||
|
||||
proc suggestMod(n: PNode; s: PSym) =
|
||||
|
||||
@@ -803,6 +803,23 @@ proc hasCustomDestructor(c: Con, t: PType): bool =
|
||||
obj = skipTypes(obj.baseClass, abstractPtrs)
|
||||
result = result or isCustomDestructor(c, obj)
|
||||
|
||||
const
|
||||
exprBranchKinds = {nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt,
|
||||
nkTryStmt, nkPragmaBlock}
|
||||
|
||||
proc distributeAsgn(asgnKind: TNodeKind; dest, ri: PNode; c: var Con; s: var Scope): PNode =
|
||||
## Distributes an assignment ``dest = ri`` into the leaf expressions of
|
||||
## ``ri`` when ``ri`` is an expression-based control flow construct. This
|
||||
## avoids creating pointless intermediate temporaries (bug #25850). The
|
||||
## descent is recursive so that nestings like ``block: ...; if c: a else: b``
|
||||
## assign directly to ``dest`` instead of going through a temp per branch.
|
||||
if ri.kind in exprBranchKinds:
|
||||
template process(child, s): untyped =
|
||||
distributeAsgn(asgnKind, dest, child, c, s)
|
||||
handleNestedTempl(ri, process, willProduceStmt = true)
|
||||
else:
|
||||
result = newTree(asgnKind, dest, p(ri, c, s, consumed))
|
||||
|
||||
proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}; inReturn = false): PNode =
|
||||
if n.kind in {nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkIfStmt,
|
||||
nkIfExpr, nkCaseStmt, nkWhen, nkWhileStmt, nkParForStmt, nkTryStmt, nkPragmaBlock}:
|
||||
@@ -1004,13 +1021,11 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
result = moveOrCopy(p(n[0], c, s, mode), n[1], c, s, flags)
|
||||
elif isDiscriminantField(n[0]):
|
||||
result = c.genDiscriminantAsgn(s, n)
|
||||
elif n[1].kind in {nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt, nkPragmaBlock}:
|
||||
elif n[1].kind in exprBranchKinds:
|
||||
# Distribute the assignment into each branch to avoid
|
||||
# creating pointless temporaries for expression-based control flow.
|
||||
let dest = p(n[0], c, s, mode)
|
||||
template process(child, s): untyped =
|
||||
newTree(n.kind, dest, p(child, c, s, consumed))
|
||||
handleNestedTempl(n[1], process, willProduceStmt = true)
|
||||
result = distributeAsgn(n.kind, dest, n[1], c, s)
|
||||
else:
|
||||
result = copyNode(n)
|
||||
result.add p(n[0], c, s, mode)
|
||||
|
||||
@@ -1349,7 +1349,7 @@ proc rawGetTok*(L: var Lexer, tok: var Token) =
|
||||
lexMessage(L, errGenerated, "invalid token: no whitespace between number and identifier")
|
||||
of '-':
|
||||
if L.buf[L.bufpos+1] in {'0'..'9'} and
|
||||
(L.bufpos-1 == 0 or L.buf[L.bufpos-1] in UnaryMinusWhitelist):
|
||||
(L.bufpos == 0 or L.buf[L.bufpos-1] in UnaryMinusWhitelist):
|
||||
# x)-23 # binary minus
|
||||
# ,-23 # unary minus
|
||||
# \n-78 # unary minus? Yes.
|
||||
|
||||
@@ -100,6 +100,7 @@ type
|
||||
warnGlobalVarConstructorTemporary = "GlobalVarConstructorTemporary",
|
||||
warnImplicitRangeConversion = "ImplicitRangeConversion",
|
||||
warnSystemRangeConversion = "SystemRangeConversion",
|
||||
warnInvalidCmpOp = "InvalidCmpOp",
|
||||
# hints
|
||||
hintSuccess = "Success", hintSuccessX = "SuccessX",
|
||||
hintCC = "CC",
|
||||
@@ -210,6 +211,7 @@ const
|
||||
warnGlobalVarConstructorTemporary: "global variable '$1' initialization requires a temporary variable",
|
||||
warnImplicitRangeConversion: "implicit range conversion $1",
|
||||
warnSystemRangeConversion: "implicit range conversion $1",
|
||||
warnInvalidCmpOp: "$1",
|
||||
hintSuccess: "operation successful: $#",
|
||||
# keep in sync with `testament.isSuccess`
|
||||
hintSuccessX: "$build\n$loc lines; ${sec}s; $mem; proj: $project; out: $output",
|
||||
|
||||
@@ -183,12 +183,6 @@ func `<`*(a: ExprIndex, b: ExprIndex): bool =
|
||||
func `<=`*(a: ExprIndex, b: ExprIndex): bool =
|
||||
a.int16 <= b.int16
|
||||
|
||||
func `>`*(a: ExprIndex, b: ExprIndex): bool =
|
||||
a.int16 > b.int16
|
||||
|
||||
func `>=`*(a: ExprIndex, b: ExprIndex): bool =
|
||||
a.int16 >= b.int16
|
||||
|
||||
func `==`*(a: ExprIndex, b: ExprIndex): bool =
|
||||
a.int16 == b.int16
|
||||
|
||||
|
||||
@@ -259,6 +259,9 @@ type
|
||||
## Old transformation for closures in JS backend
|
||||
noPanicOnExcept
|
||||
## don't panic on bare except
|
||||
procParamTypeBackendAliases
|
||||
## Keep the old proc type compatibility rules that ignore backend
|
||||
## c type aliases.
|
||||
|
||||
SymbolFilesOption* = enum
|
||||
disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest
|
||||
|
||||
@@ -2241,14 +2241,17 @@ proc parseTypeClassParam(p: var Parser): PNode =
|
||||
|
||||
proc parseTypeClass(p: var Parser): PNode =
|
||||
#| conceptParam = ('var' | 'out' | 'ptr' | 'ref' | 'static' | 'type')? symbol
|
||||
#| conceptDecl = 'concept' conceptParam ^* ',' (pragma)? ('of' typeDesc ^* ',')?
|
||||
#| conceptDecl = 'concept' (conceptParam ^* ',' (pragma)?)? ('of' typeDesc ^* ',')?
|
||||
#| &IND{>} stmt
|
||||
result = newNodeP(nkTypeClassTy, p)
|
||||
getTok(p)
|
||||
if p.tok.tokType == tkComment:
|
||||
skipComment(p, result)
|
||||
|
||||
if p.tok.indent < 0:
|
||||
if p.tok.tokType == tkOf and p.tok.indent < 0:
|
||||
# new-styled `concept of A, B` on the same line as `concept`
|
||||
result.add(p.emptyNode)
|
||||
elif p.tok.indent < 0:
|
||||
var args = newNodeP(nkArgList, p)
|
||||
result.add(args)
|
||||
args.add(p.parseTypeClassParam)
|
||||
@@ -2274,9 +2277,10 @@ proc parseTypeClass(p: var Parser): PNode =
|
||||
result.add(p.emptyNode)
|
||||
if p.tok.tokType == tkComment:
|
||||
skipComment(p, result)
|
||||
# an initial IND{>} HAS to follow:
|
||||
# an initial IND{>} HAS to follow, unless this concept inherits requirements:
|
||||
if not realInd(p):
|
||||
if result.isNewStyleConcept:
|
||||
let hasParents = result[2].kind != nkEmpty
|
||||
if result.isNewStyleConcept and not hasParents:
|
||||
parMessage(p, "routine expected, but found '$1' (empty new-styled concepts are not allowed)", p.tok)
|
||||
result.add(p.emptyNode)
|
||||
else:
|
||||
|
||||
@@ -637,6 +637,11 @@ proc renderNotLValue*(n: PNode): string =
|
||||
elif n.kind in {nkHiddenStdConv, nkHiddenSubConv} and n.len == 2:
|
||||
result = typeToString(n.typ.skipTypes(abstractVar)) & "(" & result & ")"
|
||||
|
||||
proc isSsoStringIndex*(conf: ConfigRef; n: PNode): bool =
|
||||
result = conf.usesSso() and n.kind == nkBracketExpr and n.len >= 1 and
|
||||
n[0].typ != nil and
|
||||
n[0].typ.skipTypes(abstractVar + abstractInst - {tyTypeDesc}).kind == tyString
|
||||
|
||||
proc isAssignable(c: PContext, n: PNode): TAssignableResult =
|
||||
result = parampatterns.isAssignable(c.p.owner, n)
|
||||
|
||||
|
||||
@@ -652,6 +652,9 @@ proc overloadedCallOpr(c: PContext, n: PNode): PNode =
|
||||
result = semExpr(c, result, flags = {efNoUndeclared})
|
||||
|
||||
proc changeType(c: PContext; n: PNode, newType: PType, check: bool) =
|
||||
template isViewTarget(t: PType): bool =
|
||||
t.skipTypes({tyGenericInst, tyAlias, tySink}).kind in {tyVar, tyLent}
|
||||
|
||||
case n.kind
|
||||
of nkCurly:
|
||||
for i in 0..<n.len:
|
||||
@@ -680,12 +683,15 @@ proc changeType(c: PContext; n: PNode, newType: PType, check: bool) =
|
||||
if f == nil:
|
||||
globalError(c.config, m.info, "unknown identifier: " & m.sym.name.s)
|
||||
return
|
||||
changeType(c, n[i][1], f.typ, check)
|
||||
if not isViewTarget(f.typ):
|
||||
changeType(c, n[i][1], f.typ, check)
|
||||
else:
|
||||
changeType(c, n[i][1], tup[i], check)
|
||||
if not isViewTarget(tup[i]):
|
||||
changeType(c, n[i][1], tup[i], check)
|
||||
else:
|
||||
for i in 0..<n.len:
|
||||
changeType(c, n[i], tup[i], check)
|
||||
if not isViewTarget(tup[i]):
|
||||
changeType(c, n[i], tup[i], check)
|
||||
when false:
|
||||
var m = n[i]
|
||||
var a = newNodeIT(nkExprColonExpr, m.info, newType[i])
|
||||
@@ -708,6 +714,7 @@ proc changeType(c: PContext; n: PNode, newType: PType, check: bool) =
|
||||
localError(c.config, n.info, "cannot convert '" & n.sym.name.s &
|
||||
"' to '" & typeNameAndDesc(newType) & "'")
|
||||
else: discard
|
||||
|
||||
n.typ = newType
|
||||
|
||||
proc arrayConstrType(c: PContext, n: PNode): PType =
|
||||
|
||||
@@ -129,7 +129,14 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
result.typ = nil
|
||||
onUse(n.info, s)
|
||||
of skParam:
|
||||
result = n
|
||||
if s.owner == c.p.owner:
|
||||
# Parameters of the routine currently being semchecked stay as local
|
||||
# identifiers
|
||||
result = n
|
||||
else:
|
||||
# Preserve captured outer parameters so nested generic procs can still
|
||||
# see them after the generic pre-pass.
|
||||
result = newSymNode(s, n.info)
|
||||
onUse(n.info, s)
|
||||
of skType:
|
||||
if (s.typ != nil) and
|
||||
|
||||
@@ -65,7 +65,7 @@ proc semTypeOf(c: PContext; n: PNode): PNode =
|
||||
t.incl tfNonConstExpr
|
||||
else:
|
||||
t = base
|
||||
result.typ = makeTypeDesc(c, decayTypeOfView(c, t))
|
||||
result.typ = makeTypeDesc(c, t)
|
||||
|
||||
type
|
||||
SemAsgnMode = enum asgnNormal, noOverloadedSubscript, noOverloadedAsgn
|
||||
@@ -248,10 +248,13 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
|
||||
assert operand.kind == tyTuple, $operand.kind
|
||||
result = newIntNodeT(toInt128(operand.len), traitCall, c.idgen, c.graph)
|
||||
of "distinctBase":
|
||||
var arg = operand.skipTypes({tyGenericInst})
|
||||
var arg = operand.skipTypes(skippedTypes)
|
||||
let rec = semConstExpr(c, traitCall[2]).intVal != 0
|
||||
while arg.kind == tyDistinct:
|
||||
arg = arg.base.skipTypes(skippedTypes + {tyGenericInst})
|
||||
while true:
|
||||
let distinctArg = arg.skipTypes(skippedTypes + {tyGenericInst})
|
||||
if distinctArg.kind != tyDistinct:
|
||||
break
|
||||
arg = distinctArg.base.skipTypes(skippedTypes)
|
||||
if not rec: break
|
||||
result = getTypeDescNode(c, arg, operand.owner, traitCall.info)
|
||||
of "rangeBase":
|
||||
|
||||
@@ -486,6 +486,11 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
|
||||
# we have to watch out, there are also 'owned proc' types that can be used
|
||||
# multiple times as long as they don't have closures.
|
||||
result.typ.incl tfHasOwned
|
||||
if t.kind == tyForward and efDetermineType in flags:
|
||||
# a forward object type does not error during determine-type analysis;
|
||||
# it now stays unresolved long enough for the existing delayed field-default pass to resolve it after the type section finishes.
|
||||
result.typ = t
|
||||
return result
|
||||
if t.kind != tyObject:
|
||||
return localErrorNode(c, result, if t.kind != tyGenericBody:
|
||||
"object constructor needs an object type".dup(addTypeNodeDeclaredLoc(c.config, t))
|
||||
|
||||
@@ -809,6 +809,10 @@ proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, formals: PType; ar
|
||||
markSideEffect(tracked, a, n.info)
|
||||
let paramType = if formals != nil and argIndex < formals.signatureLen: formals[argIndex] else: nil
|
||||
if paramType != nil and paramType.kind in {tyVar}:
|
||||
let arg = n.skipAddr()
|
||||
if isSsoStringIndex(tracked.config, arg):
|
||||
localError(tracked.config, arg.info,
|
||||
"expression '$1' is immutable, not 'var'" % renderNotLValue(arg))
|
||||
invalidateFacts(tracked.guards, n)
|
||||
if n.kind == nkSym and isLocalSym(tracked, n.sym):
|
||||
makeVolatile(tracked, n.sym)
|
||||
@@ -1204,6 +1208,7 @@ type
|
||||
enforcedGcSafety, enforceNoSideEffects: bool
|
||||
oldExc, oldTags, oldForbids: int
|
||||
exc, tags, forbids: PNode
|
||||
excSource, tagsSource, forbidsSource: PNode
|
||||
|
||||
proc createBlockContext(tracked: PEffects): PragmaBlockContext =
|
||||
var oldForbidsLen = 0
|
||||
@@ -1226,17 +1231,18 @@ proc unapplyBlockContext(tracked: PEffects; bc: PragmaBlockContext) =
|
||||
# anything about 'raises' in the 'cast' at all. Same applies for 'tags'.
|
||||
setLen(tracked.exc.sons, bc.oldExc)
|
||||
for e in bc.exc:
|
||||
addRaiseEffect(tracked, e, e)
|
||||
addRaiseEffect(tracked, e, if bc.excSource != nil: bc.excSource else: e)
|
||||
if bc.tags != nil:
|
||||
setLen(tracked.tags.sons, bc.oldTags)
|
||||
for t in bc.tags:
|
||||
addTag(tracked, t, t)
|
||||
addTag(tracked, t, if bc.tagsSource != nil: bc.tagsSource else: t)
|
||||
if bc.forbids != nil:
|
||||
setLen(tracked.forbids.sons, bc.oldForbids)
|
||||
for t in bc.forbids:
|
||||
addNotTag(tracked, t, t)
|
||||
addNotTag(tracked, t, if bc.forbidsSource != nil: bc.forbidsSource else: t)
|
||||
|
||||
proc castBlock(tracked: PEffects, pragma: PNode, bc: var PragmaBlockContext) =
|
||||
proc castBlock(tracked: PEffects, castPragma: PNode, bc: var PragmaBlockContext) =
|
||||
let pragma = castPragma[1]
|
||||
case whichPragma(pragma)
|
||||
of wGcSafe:
|
||||
bc.enforcedGcSafety = true
|
||||
@@ -1249,6 +1255,7 @@ proc castBlock(tracked: PEffects, pragma: PNode, bc: var PragmaBlockContext) =
|
||||
else:
|
||||
bc.tags = newNodeI(nkArgList, pragma.info)
|
||||
bc.tags.add n
|
||||
bc.tagsSource = castPragma
|
||||
of wForbids:
|
||||
let n = pragma[1]
|
||||
if n.kind in {nkCurly, nkBracket}:
|
||||
@@ -1256,6 +1263,7 @@ proc castBlock(tracked: PEffects, pragma: PNode, bc: var PragmaBlockContext) =
|
||||
else:
|
||||
bc.forbids = newNodeI(nkArgList, pragma.info)
|
||||
bc.forbids.add n
|
||||
bc.forbidsSource = castPragma
|
||||
of wRaises:
|
||||
let n = pragma[1]
|
||||
if n.kind in {nkCurly, nkBracket}:
|
||||
@@ -1263,6 +1271,7 @@ proc castBlock(tracked: PEffects, pragma: PNode, bc: var PragmaBlockContext) =
|
||||
else:
|
||||
bc.exc = newNodeI(nkArgList, pragma.info)
|
||||
bc.exc.add n
|
||||
bc.excSource = castPragma
|
||||
of wUncheckedAssign:
|
||||
discard "handled in sempass1"
|
||||
else:
|
||||
@@ -1299,6 +1308,8 @@ proc allowCStringConv(n: PNode): bool =
|
||||
|
||||
proc track(tracked: PEffects, n: PNode) =
|
||||
case n.kind
|
||||
of nkTypeOfExpr:
|
||||
discard "typeof() never evaluates its operand; not a definite-assignment use"
|
||||
of nkSym:
|
||||
useVar(tracked, n)
|
||||
if n.sym.typ != nil and tfHasAsgn in n.sym.typ.flags:
|
||||
@@ -1516,7 +1527,7 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
of wNoSideEffect:
|
||||
bc.enforceNoSideEffects = true
|
||||
of wCast:
|
||||
castBlock(tracked, pragmaList[i][1], bc)
|
||||
castBlock(tracked, pragmaList[i], bc)
|
||||
else:
|
||||
discard
|
||||
applyBlockContext(tracked, bc)
|
||||
|
||||
@@ -815,14 +815,6 @@ proc makeVarTupleSection(c: PContext, n, a, def: PNode, typ: PType, symkind: TSy
|
||||
lastDef[^1] = val
|
||||
result.add(lastDef)
|
||||
|
||||
proc materializeDirectView(n: PNode): PNode =
|
||||
let t = n.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned})
|
||||
if t.kind in {tyVar, tyLent}:
|
||||
result = newNodeIT(nkHiddenDeref, n.info, t.elementType)
|
||||
result.add n
|
||||
else:
|
||||
result = n
|
||||
|
||||
proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||
var b: PNode
|
||||
result = copyNode(n)
|
||||
@@ -889,10 +881,6 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||
#changeType(def.skipConv, typ, check=true)
|
||||
else:
|
||||
typ = def.typ.skipTypes({tyStatic, tySink}).skipIntLit(c.idgen)
|
||||
let directTyp = typ.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned})
|
||||
if directTyp.kind in {tyVar, tyLent}:
|
||||
def = materializeDirectView(def)
|
||||
typ = def.typ.skipTypes({tyStatic, tySink}).skipIntLit(c.idgen)
|
||||
if typ.kind in tyUserTypeClasses and typ.isResolvedUserTypeClass:
|
||||
typ = typ.last
|
||||
if hasEmpty(typ):
|
||||
@@ -920,8 +908,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||
|
||||
if c.matchedConcept != nil:
|
||||
typFlags.incl taConcept
|
||||
if a.kind != nkVarTuple:
|
||||
typeAllowedCheck(c, a.info, typ, symkind, typFlags)
|
||||
typeAllowedCheck(c, a.info, typ, symkind, typFlags)
|
||||
|
||||
var tup = skipTypes(typ, {tyGenericInst, tyAlias, tySink})
|
||||
if a.kind == nkVarTuple:
|
||||
@@ -2655,6 +2642,11 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||
elif s.name.s == "()" and callOperator notin c.features:
|
||||
localError(c.config, n.info, "the overloaded " & s.name.s &
|
||||
" operator has to be enabled with {.experimental: \"callOperator\".}")
|
||||
elif sfImportc notin s.flags and (s.name.s == ">" or s.name.s == ">=" or s.name.s == "!="):
|
||||
# ignore imported procs as these operators in backend language might have different semantics
|
||||
let op1 = if s.name.s == "!=": "==" elif s.name.s == ">": "<" else: "<="
|
||||
message(c.config, n.info, warnInvalidCmpOp, "define `" & op1 & "` instead of `" & s.name.s & "` to implement user defined comparison operator. " &
|
||||
"it allows you to use `" & s.name.s & "` automatically.")
|
||||
|
||||
if sfBorrow in s.flags and c.config.cmd notin cmdDocLike:
|
||||
result[bodyPos] = c.graph.emptyNode
|
||||
|
||||
@@ -370,6 +370,7 @@ proc semFieldDefault(c: PContext; owner, expectedType: PType; field: PNode): PTy
|
||||
propagateToOwner(owner, result)
|
||||
|
||||
proc semDelayedFieldDefault(c: PContext; owner, expectedType: PType; field: PNode) =
|
||||
resetSemFlag(field[^1])
|
||||
fitDefaultNode(c, field[^1], expectedType)
|
||||
propagateToOwner(owner, field[^1].typ.skipIntLit(c.idgen))
|
||||
|
||||
@@ -1879,38 +1880,6 @@ proc fixupTypeOf(c: PContext, prev: PType, typ: PType) =
|
||||
if prev.kind != tyGenericBody:
|
||||
assignType(prev, result)
|
||||
|
||||
proc decayTypeOfView(c: PContext, typ: PType): PType =
|
||||
if typ == nil: return nil
|
||||
let t = typ.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
case t.kind
|
||||
of tyVar, tyLent:
|
||||
result = decayTypeOfView(c, t.elementType)
|
||||
of tyTuple:
|
||||
var changed = false
|
||||
var kids = newSeq[PType](t.len)
|
||||
for i in 0..<t.len:
|
||||
kids[i] = decayTypeOfView(c, t[i])
|
||||
if kids[i] != t[i]: changed = true
|
||||
if changed:
|
||||
result = copyType(t, c.idgen, t.owner)
|
||||
for i in 0..<kids.len:
|
||||
result[i] = kids[i]
|
||||
if t.n != nil:
|
||||
result.n = copyNode(t.n)
|
||||
for it in t.n:
|
||||
if it.kind == nkSym and it.sym.kind == skField:
|
||||
let field = copySym(it.sym, c.idgen)
|
||||
field.ast = it.sym.ast
|
||||
if field.position >= 0 and field.position < kids.len:
|
||||
field.typ = kids[field.position]
|
||||
result.n.add newSymNode(field, it.info)
|
||||
else:
|
||||
result.n.add copyTree(it)
|
||||
else:
|
||||
result = typ
|
||||
else:
|
||||
result = typ
|
||||
|
||||
proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType =
|
||||
var n = semExprWithType(c, n, {efDetermineType})
|
||||
if n.typ.kind == tyTypeDesc:
|
||||
@@ -2110,7 +2079,6 @@ proc semTypeOf(c: PContext; n: PNode; prev: PType): PType =
|
||||
result.incl tfNonConstExpr
|
||||
else:
|
||||
result = base
|
||||
result = decayTypeOfView(c, result)
|
||||
fixupTypeOf(c, prev, result)
|
||||
|
||||
proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType =
|
||||
@@ -2136,7 +2104,6 @@ proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType =
|
||||
result.incl tfNonConstExpr
|
||||
else:
|
||||
result = base
|
||||
result = decayTypeOfView(c, result)
|
||||
fixupTypeOf(c, prev, result)
|
||||
|
||||
proc semTypeIdent(c: PContext, n: PNode): PSym =
|
||||
|
||||
@@ -15,8 +15,6 @@ import
|
||||
magicsys, idents, lexer, options, parampatterns, trees,
|
||||
linter, lineinfos, lowerings, modulegraphs, concepts, layeredtable
|
||||
|
||||
import typeallowed
|
||||
|
||||
import std/[intsets, strutils, tables]
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
@@ -125,58 +123,6 @@ proc initCandidate*(ctx: PContext, callee: PType): TCandidate =
|
||||
result.calleeSym = nil
|
||||
result.bindings = initLayeredTypeMap()
|
||||
|
||||
proc materializeTupleViewType(t: PType; idgen: IdGenerator): PType =
|
||||
case t.kind
|
||||
of tyVar, tyLent:
|
||||
result = materializeTupleViewType(t.elementType, idgen)
|
||||
of tyTuple:
|
||||
if classifyViewType(t) == noView:
|
||||
result = t
|
||||
else:
|
||||
result = copyType(t, idgen, t.owner)
|
||||
for i in 0..<t.len:
|
||||
result[i] = materializeTupleViewType(t[i], idgen)
|
||||
if t.n != nil:
|
||||
result.n = copyNode(t.n)
|
||||
for it in t.n:
|
||||
if it.kind == nkSym and it.sym.kind == skField:
|
||||
let field = copySym(it.sym, idgen)
|
||||
field.ast = it.sym.ast
|
||||
if field.position >= 0 and field.position < result.len:
|
||||
field.typ = result[field.position]
|
||||
result.n.add newSymNode(field, it.info)
|
||||
else:
|
||||
result.n.add copyTree(it)
|
||||
else:
|
||||
result = t
|
||||
|
||||
proc materializeTupleViewArg(c: PContext; targetType: PType; arg: PNode): PNode =
|
||||
let targetTuple = targetType.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct, tyInferred})
|
||||
var tupleArg = arg
|
||||
var prefix: PNode = nil
|
||||
if targetTuple.len > 1 and arg.kind notin {nkHiddenAddr, nkSym}:
|
||||
prefix = evalOnce(c.graph, arg, c.idgen, getCurrOwner(c))
|
||||
tupleArg = prefix[^1]
|
||||
|
||||
let tupleConstr = newNodeIT(nkTupleConstr, arg.info, targetType)
|
||||
for i in 0..<targetTuple.len:
|
||||
let targetField = targetTuple[i]
|
||||
var field = newTupleAccess(c.graph, tupleArg, i)
|
||||
let sourceField = field.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct, tyInferred})
|
||||
if sourceField.kind in {tyVar, tyLent}:
|
||||
field = newDeref(field)
|
||||
elif targetField.kind == tyTuple and classifyViewType(sourceField) != noView:
|
||||
field = materializeTupleViewArg(c, targetField, field)
|
||||
tupleConstr.add field
|
||||
|
||||
if prefix == nil:
|
||||
result = tupleConstr
|
||||
else:
|
||||
result = newNodeIT(nkStmtListExpr, arg.info, targetType)
|
||||
for i in 0..<(prefix.len - 1):
|
||||
result.add prefix[i]
|
||||
result.add tupleConstr
|
||||
|
||||
proc put(c: var TCandidate, key, val: PType) {.inline.} =
|
||||
## Given: proc foo[T](x: T); foo(4)
|
||||
## key: 'T'
|
||||
@@ -189,12 +135,7 @@ proc put(c: var TCandidate, key, val: PType) {.inline.} =
|
||||
writeStackTrace()
|
||||
if c.c.module.name.s == "temp3":
|
||||
echo "binding ", key, " -> ", val
|
||||
|
||||
let normalized = val.skipIntLit(c.c.idgen)
|
||||
if normalized.kind == tyTuple and classifyViewType(normalized) != noView:
|
||||
put(c.bindings, key, materializeTupleViewType(normalized, c.c.idgen))
|
||||
else:
|
||||
put(c.bindings, key, normalized)
|
||||
put(c.bindings, key, val.skipIntLit(c.c.idgen))
|
||||
|
||||
proc typeRel*(c: var TCandidate, f, aOrig: PType,
|
||||
flags: TTypeRelFlags = {}): TTypeRelation
|
||||
@@ -843,6 +784,19 @@ proc procParamTypeRel(c: var TCandidate; f, a: PType): TTypeRelation =
|
||||
# if f is metatype.
|
||||
result = typeRel(c, f, a)
|
||||
|
||||
if result == isEqual and
|
||||
procParamTypeBackendAliases notin c.c.config.legacyFeatures:
|
||||
# Ensure types that are semantically equal also match at the backend level.
|
||||
# E.g. reject assigning proc(csize_t) to proc(uint) since these map to
|
||||
# different C types (size_t vs unsigned long long).
|
||||
let fCheck = concreteType(c, f)
|
||||
let aCheck = concreteType(c, a)
|
||||
# Note that `result` is equal; now check whether they have the same
|
||||
# backend type.
|
||||
if fCheck != nil and aCheck != nil and
|
||||
not sameBackendTypePickyAliases(fCheck, aCheck, {IgnoreFlags}):
|
||||
result = isNone
|
||||
|
||||
if result <= isSubrange or inconsistentVarTypes(f, a):
|
||||
result = isNone
|
||||
|
||||
@@ -1805,6 +1759,21 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
let ff = last(f)
|
||||
if ff != nil:
|
||||
result = typeRel(c, ff, a, flags)
|
||||
if result == isNone and a.kind == tyGenericInst and trBindGenericParam in flags:
|
||||
var depth = -1
|
||||
# Generic-parameter constraints like `F: Future` can miss in `last(f)`
|
||||
# when the actual type inherits from a concrete generic instantiation.
|
||||
# Keep this fallback scoped to generic-parameter matching so typedesc
|
||||
# overloads such as `type Future[T]` still prefer more specific
|
||||
# descendants like `InternalRaisesFuture[T, E]`.
|
||||
if isGenericSubtype(c, a, f, depth, f) and depth > 0:
|
||||
var askip = skippedNone
|
||||
let aobj = a.skipToObject(askip)
|
||||
if aobj != nil and tfFinal notin aobj.flags:
|
||||
# Keep overload ranking consistent with other inheritance-based
|
||||
# matches: deeper descendants are slightly worse candidates.
|
||||
inc c.inheritancePenalty, depth + int(c.inheritancePenalty < 0)
|
||||
result = isGeneric
|
||||
of tyGenericInvocation:
|
||||
var x = a.skipGenericAlias
|
||||
if x.kind == tyGenericParam and x.len > 0:
|
||||
@@ -2259,16 +2228,7 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate,
|
||||
|
||||
if result.typ == nil: internalError(c.graph.config, arg.info, "implicitConv")
|
||||
result.add c.graph.emptyNode
|
||||
let targetTuple = result.typ.skipTypes({tyVar, tyGenericInst, tyAlias, tySink, tyDistinct, tyInferred})
|
||||
let sourceTuple =
|
||||
if arg.typ != nil:
|
||||
arg.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct, tyInferred})
|
||||
else:
|
||||
nil
|
||||
if sourceTuple != nil and sourceTuple.kind == tyTuple and targetTuple.kind == tyTuple and
|
||||
classifyViewType(arg.typ) != noView and classifyViewType(result.typ) == noView:
|
||||
result.add materializeTupleViewArg(c, targetTuple, arg)
|
||||
elif arg.typ != nil and arg.typ.kind == tyLent:
|
||||
if arg.typ != nil and arg.typ.kind == tyLent:
|
||||
let a = newNodeIT(nkHiddenDeref, arg.info, arg.typ.elementType)
|
||||
a.add arg
|
||||
result.add a
|
||||
@@ -2528,6 +2488,10 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
|
||||
return arg
|
||||
elif f.kind == tyStatic and arg.typ.n != nil:
|
||||
return arg.typ.n
|
||||
elif f.kind == tyUntyped:
|
||||
# bug #25693: a different overload candidate may have sem-checked the
|
||||
# operand and left symbols behind; templates expect the pristine AST.
|
||||
return argOrig
|
||||
else:
|
||||
return argSemantized # argOrig
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
## This module implements threadpool's ``spawn``.
|
||||
|
||||
import ast, types, idents, magicsys, msgs, options, modulegraphs,
|
||||
lowerings, liftdestructors, renderer
|
||||
lowerings, liftdestructors, renderer, trees
|
||||
from trees import getMagic, getRoot
|
||||
|
||||
proc callProc(a: PNode): PNode =
|
||||
@@ -53,6 +53,24 @@ proc typeNeedsNoDeepCopy(t: PType): bool =
|
||||
if t.kind in {tyVar, tyLent, tySequence}: t = t.elementType
|
||||
result = not containsGarbageCollectedRef(t)
|
||||
|
||||
proc newSpawnMoveStmt(g: ModuleGraph; idgen: IdGenerator; le, ri: PNode): PNode =
|
||||
let op = getAttachedOp(g, ri.typ.skipTypes({tyGenericInst, tyAlias, tyVar, tySink}), attachedWasMoved)
|
||||
if op != nil and sfOverridden in op.flags:
|
||||
result = newNodeI(nkStmtList, le.info)
|
||||
result.add newFastAsgnStmt(le, ri)
|
||||
|
||||
let wasMovedCall = newNodeI(nkCall, ri.info)
|
||||
wasMovedCall.add newSymNode(op)
|
||||
|
||||
if op.typ != nil and op.typ.signatureLen > 1 and op.typ.firstParamType.kind != tyVar:
|
||||
wasMovedCall.add ri.skipAddr
|
||||
else:
|
||||
wasMovedCall.add makeAddr(ri.skipAddr, idgen)
|
||||
|
||||
result.add wasMovedCall
|
||||
else:
|
||||
result = newFastMoveStmt(g, le, ri)
|
||||
|
||||
proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; idgen: IdGenerator; owner: PSym; typ: PType;
|
||||
v: PNode; useShallowCopy=false): PSym =
|
||||
result = newSym(skTemp, getIdent(g.cache, genPrefix), idgen, owner, varSection.info,
|
||||
@@ -68,10 +86,10 @@ proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; idgen: IdGenerator;
|
||||
if varInit != nil:
|
||||
if g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc, gcYrc}:
|
||||
# inject destructors pass will do its own analysis
|
||||
varInit.add newFastMoveStmt(g, newSymNode(result), v)
|
||||
varInit.add newSpawnMoveStmt(g, idgen, newSymNode(result), v)
|
||||
else:
|
||||
if useShallowCopy and typeNeedsNoDeepCopy(typ) or optTinyRtti in g.config.globalOptions:
|
||||
varInit.add newFastMoveStmt(g, newSymNode(result), v)
|
||||
varInit.add newSpawnMoveStmt(g, idgen, newSymNode(result), v)
|
||||
else:
|
||||
let deepCopyCall = newNodeI(nkCall, varInit.info, 3)
|
||||
deepCopyCall[0] = newSymNode(getSysMagic(g, varSection.info, "deepCopy", mDeepCopy))
|
||||
|
||||
@@ -22,7 +22,7 @@ import std / tables
|
||||
|
||||
import
|
||||
options, ast, astalgo, trees, msgs,
|
||||
idents, renderer, types, semfold, magicsys, cgmeth,
|
||||
idents, renderer, types, semfold, magicsys, cgmeth, parampatterns,
|
||||
lowerings, liftlocals,
|
||||
modulegraphs, lineinfos
|
||||
|
||||
@@ -90,11 +90,21 @@ proc getCurrOwner(c: PTransf): PSym =
|
||||
if c.transCon != nil: result = c.transCon.owner
|
||||
else: result = c.module
|
||||
|
||||
proc freshOwnedSym(c: PTransf; s, owner: PSym): PNode =
|
||||
# We need to copy the symbol here because we might need to change its owner and
|
||||
# we don't want to mess with the original symbol which might be used in other places.
|
||||
# This can happen for example for iterators which are transformed multiple times when
|
||||
# they are used in different contexts.
|
||||
var fresh = copySym(s, c.idgen)
|
||||
if fresh.kind notin routineKinds:
|
||||
incl(fresh.flagsImpl, sfFromGeneric)
|
||||
setOwner(fresh, owner)
|
||||
result = newSymNode(fresh)
|
||||
|
||||
proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PNode =
|
||||
let r = newSym(skTemp, getIdent(c.graph.cache, genPrefix), c.idgen, getCurrOwner(c), info)
|
||||
r.typ = typ #skipTypes(typ, {tyGenericInst, tyAlias, tySink})
|
||||
incl(r.flagsImpl, sfFromGeneric)
|
||||
let owner = getCurrOwner(c)
|
||||
result = newSymNode(r)
|
||||
|
||||
proc transform(c: PTransf, n: PNode, noConstFold = false): PNode
|
||||
@@ -185,11 +195,39 @@ proc transformSym(c: PTransf, n: PNode): PNode =
|
||||
result = transformSymAux(c, n)
|
||||
|
||||
proc freshVar(c: PTransf; v: PSym): PNode =
|
||||
let owner = getCurrOwner(c)
|
||||
var newVar = copySym(v, c.idgen)
|
||||
incl(newVar.flagsImpl, sfFromGeneric)
|
||||
setOwner(newVar, owner)
|
||||
result = newSymNode(newVar)
|
||||
result = freshOwnedSym(c, v, getCurrOwner(c))
|
||||
|
||||
proc introduceNewRoutineHeaderSyms(c: PTransf; n: PNode; oldOwner, newOwner: PSym) =
|
||||
# We need to introduce new symbols for the parameters and result of a routine when
|
||||
# we copy it for inlining or closure generation.
|
||||
# Otherwise, we would have multiple nodes referring to the same parameter symbols which
|
||||
# can lead to problems when we need to change the owner of these symbols.
|
||||
case n.kind
|
||||
of nkSym:
|
||||
if n.sym.owner == oldOwner:
|
||||
c.transCon.mapping[n.sym.itemId] = freshOwnedSym(c, n.sym, newOwner)
|
||||
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit:
|
||||
discard
|
||||
else:
|
||||
for i in 0..<n.len:
|
||||
introduceNewRoutineHeaderSyms(c, n[i], oldOwner, newOwner)
|
||||
|
||||
proc copyRoutineTypeHeader(c: PTransf; oldProc, newProc: PSym) =
|
||||
# We need to copy the routine type header to ensure that
|
||||
# modifications to the newProc do not affect the oldProc.
|
||||
if oldProc.typ != nil and oldProc.typ.kind == tyProc and oldProc.typ.n != nil:
|
||||
newProc.typ = copyType(oldProc.typ, c.idgen, newProc)
|
||||
newProc.typ.n = newNodeI(oldProc.typ.n.kind, oldProc.typ.n.info)
|
||||
if oldProc.typ.n.len > 0:
|
||||
newProc.typ.n.add copyTree(oldProc.typ.n[0])
|
||||
for i in 1..<oldProc.typ.n.len:
|
||||
let oldParam = oldProc.typ.n[i].sym
|
||||
var newParam = getOrDefault(c.transCon.mapping, oldParam.itemId)
|
||||
if newParam == nil:
|
||||
newParam = freshOwnedSym(c, oldParam, newProc)
|
||||
c.transCon.mapping[oldParam.itemId] = newParam
|
||||
doAssert newParam.kind == nkSym
|
||||
newProc.typ.addParam newParam.sym
|
||||
|
||||
proc transformVarSection(c: PTransf, v: PNode): PNode =
|
||||
result = newTransNode(v)
|
||||
@@ -338,11 +376,18 @@ proc introduceNewLocalVars(c: PTransf, n: PNode): PNode =
|
||||
return n
|
||||
of nkLambdaKinds, nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef: # todo optimize nosideeffects?
|
||||
result = newTransNode(n)
|
||||
let x = newSymNode(copySym(n[namePos].sym, c.idgen))
|
||||
c.transCon.mapping[n[namePos].sym.itemId] = x
|
||||
let oldProc = n[namePos].sym
|
||||
let x = freshOwnedSym(c, oldProc, oldProc.owner)
|
||||
c.transCon.mapping[oldProc.itemId] = x
|
||||
introduceNewRoutineHeaderSyms(c, n[paramsPos], oldProc, x.sym)
|
||||
if resultPos < n.len and n[resultPos] != nil:
|
||||
introduceNewRoutineHeaderSyms(c, n[resultPos], oldProc, x.sym)
|
||||
copyRoutineTypeHeader(c, oldProc, x.sym)
|
||||
result[namePos] = x # we have to copy proc definitions for iters
|
||||
for i in 1..<n.len:
|
||||
result[i] = introduceNewLocalVars(c, n[i])
|
||||
if x.sym.typ != nil and x.sym.typ.kind == tyProc:
|
||||
result[paramsPos] = x.sym.typ.n
|
||||
result[namePos].sym.ast = result
|
||||
else:
|
||||
result = newTransNode(n)
|
||||
@@ -675,7 +720,7 @@ type
|
||||
paDirectMapping, paFastAsgn, paFastAsgnTakeTypeFromArg
|
||||
paVarAsgn, paComplexOpenarray, paViaIndirection
|
||||
|
||||
proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
|
||||
proc putArgInto(arg: PNode, formal: PType; borrowedFirstArg = false): TPutArgInto =
|
||||
# This analyses how to treat the mapping "formal <-> arg" in an
|
||||
# inline context.
|
||||
if formal.kind == tyTypeDesc: return paDirectMapping
|
||||
@@ -726,6 +771,13 @@ proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
|
||||
if skipTypes(formal, abstractInst).kind in {tyVar, tyLent}: result = paVarAsgn
|
||||
else: result = paFastAsgn
|
||||
|
||||
if borrowedFirstArg and result == paDirectMapping and parampatterns.exprRoot(arg) == nil and
|
||||
parampatterns.isAssignable(nil, arg) == arNone:
|
||||
# Inline iterators like `items(array)` borrow from the first argument.
|
||||
# If that argument is just a transient expression, materialize it so the
|
||||
# lifted closure keeps the backing storage alive across yields.
|
||||
result = paFastAsgnTakeTypeFromArg
|
||||
|
||||
proc findWrongOwners(c: PTransf, n: PNode) =
|
||||
if n.kind == nkVarSection:
|
||||
let x = n[0][0]
|
||||
@@ -824,13 +876,16 @@ proc transformFor(c: PTransf, n: PNode): PNode =
|
||||
if iter.kind != skIterator: return result
|
||||
# generate access statements for the parameters (unless they are constant)
|
||||
pushTransCon(c, newC)
|
||||
let borrowedIterResult =
|
||||
iter.typ != nil and iter.typ.returnType != nil and
|
||||
skipTypes(iter.typ.returnType, abstractInst).kind in {tyLent, tyVar}
|
||||
for i in 1..<call.len:
|
||||
var arg = transform(c, call[i])
|
||||
let ff = skipTypes(iter.typ, abstractInst)
|
||||
# can happen for 'nim check':
|
||||
if i >= ff.n.len: return result
|
||||
var formal = ff.n[i].sym
|
||||
let pa = putArgInto(arg, formal.typ)
|
||||
let pa = putArgInto(arg, formal.typ, borrowedIterResult and i == 1)
|
||||
case pa
|
||||
of paDirectMapping:
|
||||
newC.mapping[formal.itemId] = arg
|
||||
|
||||
@@ -897,7 +897,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||
c.flags = oldFlags
|
||||
|
||||
if x == y: return true
|
||||
let aliasSkipSet = maybeSkipRange({tyAlias})
|
||||
let aliasSkipSet = maybeSkipRange({tyAlias, tyInferred})
|
||||
var a = skipTypes(x, aliasSkipSet)
|
||||
while a.kind == tyUserTypeClass and tfResolved in a.flags:
|
||||
a = skipTypes(a.last, aliasSkipSet)
|
||||
@@ -1069,9 +1069,10 @@ proc sameBackendTypeIgnoreRange*(x, y: PType): bool =
|
||||
c.cmp = dcEqIgnoreDistinct
|
||||
result = sameTypeAux(x, y, c)
|
||||
|
||||
proc sameBackendTypePickyAliases*(x, y: PType): bool =
|
||||
proc sameBackendTypePickyAliases*(x, y: PType, flags: TTypeCmpFlags = {}): bool =
|
||||
var c = initSameTypeClosure()
|
||||
c.flags.incl {IgnoreTupleFields, IgnoreRangeShallow, PickyCAliases, PickyBackendAliases}
|
||||
c.flags.incl flags
|
||||
c.cmp = dcEqIgnoreDistinct
|
||||
result = sameTypeAux(x, y, c)
|
||||
|
||||
|
||||
@@ -185,6 +185,9 @@ proc root(v: var Partitions; start: int): int =
|
||||
proc potentialMutation(v: var Partitions; s: PSym; level: int; info: TLineInfo) =
|
||||
let id = variableId(v, s)
|
||||
if id >= 0:
|
||||
# mutated here => alive here: keep aliveEnd in sync so dangerousMutation catches
|
||||
# mutations recorded after the var's last use (e.g. via a call arg). See #25595.
|
||||
v.s[id].aliveEnd = max(v.s[id].aliveEnd, v.abstractTime)
|
||||
let r = root(v, id)
|
||||
let flags = if s.kind == skParam:
|
||||
if isConstParam(s):
|
||||
|
||||
@@ -1842,6 +1842,8 @@ proc genArrAccessOpcode(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
|
||||
if dest < 0: dest = c.getTemp(n.typ)
|
||||
if opc in {opcLdArrAddr, opcLdStrIdxAddr} and gfNodeAddr in flags:
|
||||
c.gABC(n, opc, dest, a, b)
|
||||
if c.prc.regInfo[a].kind >= slotTempUnknown:
|
||||
c.prc.regInfo[a].kind = slotTempPerm
|
||||
elif needsRegLoad():
|
||||
var cc = c.getTemp(n.typ)
|
||||
c.gABC(n, opc, cc, a, b)
|
||||
@@ -1858,6 +1860,8 @@ proc genObjAccessAux(c: PCtx; n: PNode; a, b: int, dest: var TDest; flags: TGenF
|
||||
if dest < 0: dest = c.getTemp(n.typ)
|
||||
if {gfNodeAddr} * flags != {}:
|
||||
c.gABC(n, opcLdObjAddr, dest, a, b)
|
||||
if a < c.prc.regInfo.len and c.prc.regInfo[a].kind >= slotTempUnknown:
|
||||
c.prc.regInfo[a].kind = slotTempPerm
|
||||
elif needsRegLoad():
|
||||
var cc = c.getTemp(n.typ)
|
||||
c.gABC(n, opcLdObj, cc, a, b)
|
||||
|
||||
@@ -152,6 +152,8 @@ proc sortVTableDispatchers*(g: ModuleGraph) =
|
||||
rootItemIdCount.inc(baseType.itemId)
|
||||
for idx in 0..<g.methods[bucket].methods.len:
|
||||
let obj = g.methods[bucket].methods[idx].typ.firstParamType.skipTypes(skipPtrs)
|
||||
if obj.itemId notin itemTable:
|
||||
itemTable[obj.itemId] = newSeq[PSym](methodIndexLen)
|
||||
itemTable[obj.itemId][mIndex] = g.methods[bucket].methods[idx]
|
||||
|
||||
for baseType in rootTypeSeq:
|
||||
|
||||
@@ -188,7 +188,7 @@ objectPart = IND{>} objectPart^+IND{=} DED
|
||||
/ objectWhen / objectCase / 'nil' / 'discard' / declColonEquals
|
||||
objectDecl = 'object' ('of' typeDesc)? COMMENT? objectPart
|
||||
conceptParam = ('var' | 'out' | 'ptr' | 'ref' | 'static' | 'type')? symbol
|
||||
conceptDecl = 'concept' conceptParam ^* ',' (pragma)? ('of' typeDesc ^* ',')?
|
||||
conceptDecl = 'concept' (conceptParam ^* ',' (pragma)?)? ('of' typeDesc ^* ',')?
|
||||
&IND{>} stmt
|
||||
typeDef = identVisDot genericParamList? pragma '=' optInd typeDefValue
|
||||
indAndComment?
|
||||
|
||||
@@ -33,7 +33,7 @@ The text representation is particularly valuable for debugging and introspection
|
||||
Each ``.nim`` module produces its own ``.nif`` file during compilation.
|
||||
The NIF format contains:
|
||||
|
||||
- **Header** - Version information (e.g., `(.nif26)`)
|
||||
- **Header** - Version information (e.g., `(.nif27)`)
|
||||
- **Dependencies** - List of source files and dependencies
|
||||
- **Interface** - Exported symbols and their indices
|
||||
- **Body** - The intermediate representation of the module's code in Lisp-like syntax
|
||||
|
||||
@@ -34,10 +34,10 @@ To learn how to compile Nim programs and generate documentation see
|
||||
the [Compiler User Guide](nimc.html) and the [DocGen Tools Guide](docgen.html).
|
||||
|
||||
The language constructs are explained using an extended BNF, in which `(a)*`
|
||||
means 0 or more `a`'s, `a+` means 1 or more `a`'s, and `(a)?` means an
|
||||
means 0 or more *a*'s, `a+` means 1 or more *a*'s, and `(a)?` means an
|
||||
optional *a*. Parentheses may be used to group elements.
|
||||
|
||||
`&` is the lookahead operator; `&a` means that an `a` is expected but
|
||||
`&` is the lookahead operator; `&a` means that an *a* is expected but
|
||||
not consumed. It will be consumed in the following rule.
|
||||
|
||||
The `|`, `/` symbols are used to mark alternatives and have the lowest
|
||||
@@ -1024,6 +1024,9 @@ These are the major type classes:
|
||||
* procedural type
|
||||
* generic type
|
||||
|
||||
The compiler's internal type zoo is richer than this summary suggests:
|
||||
some types that are structurally equal still differ in backend representation.
|
||||
|
||||
|
||||
Ordinal types
|
||||
-------------
|
||||
@@ -2174,6 +2177,10 @@ Procedural type
|
||||
A procedural type is internally a pointer to a procedure. `nil` is
|
||||
an allowed value for a variable of a procedural type.
|
||||
|
||||
Procedure compatibility also checks the backend representation of the
|
||||
parameter and result types, not just their source-level shape. Use
|
||||
`--legacy:procParamTypeBackendAliases` to restore the older behavior.
|
||||
|
||||
Examples:
|
||||
|
||||
```nim
|
||||
@@ -8867,7 +8874,7 @@ Byref pragma
|
||||
The `byref` pragma can be applied to an object or tuple type or a proc param.
|
||||
When applied to a type it instructs the compiler to pass the type by reference
|
||||
(hidden pointer) to procs. When applied to a param it will take precedence, even
|
||||
if the the type was marked as `bycopy`. When an `importc` type has a `byref` pragma or
|
||||
if the type was marked as `bycopy`. When an `importc` type has a `byref` pragma or
|
||||
parameters are marked as `byref` in an `importc` proc, these params translate to pointers.
|
||||
When an `importcpp` type has a `byref` pragma, these params translate to
|
||||
C++ references `&`.
|
||||
|
||||
@@ -1144,7 +1144,7 @@ there is a difference between the `$` and `repr` outputs:
|
||||
echo myCharacter, ":", repr(myCharacter)
|
||||
# --> n:'n'
|
||||
echo myString, ":", repr(myString)
|
||||
# --> nim:0x10fa8c050"nim"
|
||||
# --> nim:"nim"
|
||||
echo myInteger, ":", repr(myInteger)
|
||||
# --> 42:42
|
||||
echo myFloat, ":", repr(myFloat)
|
||||
|
||||
4
koch.nim
4
koch.nim
@@ -16,11 +16,11 @@ const
|
||||
ChecksumsStableCommit = "0b8e46379c5bc1bf73d8b3011908389c60fb9b98" # 2.0.1
|
||||
SatStableCommit = "e63eaea8baf00bed8bcd5a29ffd8823abb265b39"
|
||||
|
||||
NimonyStableCommit = "c189ef438598878b2f02f6a2ff91d08febafc04b" # unversioned \
|
||||
NimonyStableCommit = "fca0e938b04695a3aa4e85abcc976571189f2bd2" # 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.
|
||||
# Commit from 2026-04-27
|
||||
# Commit from 2026-06-08
|
||||
|
||||
# examples of possible values for fusion: #head, #ea82b54, 1.2.3
|
||||
FusionStableHash = "#562467452b32cb7a97410ea177f083e6d8405734"
|
||||
|
||||
@@ -153,7 +153,7 @@ proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] =
|
||||
protocol)
|
||||
result.orig = protocol
|
||||
i.inc protocol.parseSaturatedNatural(result.major, i)
|
||||
i.inc # Skip .
|
||||
if i < protocol.len: inc i # Skip .
|
||||
i.inc protocol.parseSaturatedNatural(result.minor, i)
|
||||
|
||||
proc sendStatus(client: AsyncSocket, status: string): Future[void] =
|
||||
|
||||
@@ -495,13 +495,16 @@ func `$`*[T](c: CritBitTree[T]): string =
|
||||
const avgItemLen = 16
|
||||
result = newStringOfCap(c.count * avgItemLen)
|
||||
result.add("{")
|
||||
var first = true
|
||||
when T is void:
|
||||
for key in keys(c):
|
||||
if result.len > 1: result.add(", ")
|
||||
if first: first = false
|
||||
else: result.add(", ")
|
||||
result.addQuoted(key)
|
||||
else:
|
||||
for key, val in pairs(c):
|
||||
if result.len > 1: result.add(", ")
|
||||
if first: first = false
|
||||
else: result.add(", ")
|
||||
result.addQuoted(key)
|
||||
result.add(": ")
|
||||
result.addQuoted(val)
|
||||
|
||||
@@ -454,8 +454,10 @@ proc `$`*[T](deq: Deque[T]): string =
|
||||
assert $a == "[10, 20, 30]"
|
||||
|
||||
result = "["
|
||||
var first = true
|
||||
for x in deq:
|
||||
if result.len > 1: result.add(", ")
|
||||
if first: first = false
|
||||
else: result.add(", ")
|
||||
result.addQuoted(x)
|
||||
result.add("]")
|
||||
|
||||
|
||||
@@ -260,7 +260,9 @@ proc `$`*[T](heap: HeapQueue[T]): string =
|
||||
assert $heap == "[1, 2]"
|
||||
|
||||
result = "["
|
||||
var first = true
|
||||
for x in heap.data:
|
||||
if result.len > 1: result.add(", ")
|
||||
if first: first = false
|
||||
else: result.add(", ")
|
||||
result.addQuoted(x)
|
||||
result.add("]")
|
||||
|
||||
@@ -304,8 +304,10 @@ proc `$`*[T](L: SomeLinkedCollection[T]): string =
|
||||
assert $a == "[1, 2, 3, 4]"
|
||||
|
||||
result = "["
|
||||
var first = true
|
||||
for x in nodes(L):
|
||||
if result.len > 1: result.add(", ")
|
||||
if first: first = false
|
||||
else: result.add(", ")
|
||||
result.addQuoted(x.value)
|
||||
result.add("]")
|
||||
|
||||
|
||||
@@ -739,7 +739,7 @@ template withValue*[A, B](t: Table[A, B], key: A,
|
||||
discard
|
||||
|
||||
|
||||
iterator pairs*[A, B](t: Table[A, B]): (lent A, lent B) =
|
||||
iterator pairs*[A, B](t: Table[A, B]): (A, B) =
|
||||
## Iterates over any `(key, value)` pair in the table `t`.
|
||||
##
|
||||
## See also:
|
||||
@@ -1201,7 +1201,7 @@ proc `==`*[A, B](s, t: TableRef[A, B]): bool =
|
||||
|
||||
|
||||
|
||||
iterator pairs*[A, B](t: TableRef[A, B]): (lent A, lent B) =
|
||||
iterator pairs*[A, B](t: TableRef[A, B]): (A, B) =
|
||||
## Iterates over any `(key, value)` pair in the table `t`.
|
||||
##
|
||||
## See also:
|
||||
@@ -1789,7 +1789,7 @@ proc `==`*[A, B](s, t: OrderedTable[A, B]): bool =
|
||||
|
||||
|
||||
|
||||
iterator pairs*[A, B](t: OrderedTable[A, B]): (lent A, lent B) =
|
||||
iterator pairs*[A, B](t: OrderedTable[A, B]): (A, B) =
|
||||
## Iterates over any `(key, value)` pair in the table `t` in insertion
|
||||
## order.
|
||||
##
|
||||
@@ -2212,7 +2212,7 @@ proc `==`*[A, B](s, t: OrderedTableRef[A, B]): bool =
|
||||
|
||||
|
||||
|
||||
iterator pairs*[A, B](t: OrderedTableRef[A, B]): (lent A, lent B) =
|
||||
iterator pairs*[A, B](t: OrderedTableRef[A, B]): (A, B) =
|
||||
## Iterates over any `(key, value)` pair in the table `t` in insertion
|
||||
## order.
|
||||
##
|
||||
@@ -2622,7 +2622,7 @@ proc `==`*[A](s, t: CountTable[A]): bool =
|
||||
equalsImpl(s, t)
|
||||
|
||||
|
||||
iterator pairs*[A](t: CountTable[A]): (lent A, int) =
|
||||
iterator pairs*[A](t: CountTable[A]): (A, int) =
|
||||
## Iterates over any `(key, value)` pair in the table `t`.
|
||||
##
|
||||
## See also:
|
||||
@@ -2899,7 +2899,7 @@ proc `==`*[A](s, t: CountTableRef[A]): bool =
|
||||
else: result = s[] == t[]
|
||||
|
||||
|
||||
iterator pairs*[A](t: CountTableRef[A]): (lent A, int) =
|
||||
iterator pairs*[A](t: CountTableRef[A]): (A, int) =
|
||||
## Iterates over any `(key, value)` pair in the table `t`.
|
||||
##
|
||||
## See also:
|
||||
|
||||
@@ -175,23 +175,48 @@ proc parseEscapedUTF16*(buf: cstring, pos: var int): int =
|
||||
else:
|
||||
return -1
|
||||
|
||||
proc addSpan(dst: var string; src: string; startPos, endPos: int) {.inline.} =
|
||||
let n = endPos - startPos
|
||||
if n <= 0:
|
||||
return
|
||||
|
||||
let old = dst.len
|
||||
dst.setLen old + n
|
||||
|
||||
template impl =
|
||||
for i in 0..<n:
|
||||
dst[old + i] = src[startPos + i]
|
||||
|
||||
when nimvm:
|
||||
impl
|
||||
else:
|
||||
when defined(js) or defined(nimscript):
|
||||
impl
|
||||
else:
|
||||
{.noSideEffect.}:
|
||||
copyMem dst[old].addr, src[startPos].unsafeAddr, n
|
||||
|
||||
proc parseString(my: var JsonParser): TokKind =
|
||||
result = tkString
|
||||
var pos = my.bufpos + 1
|
||||
var spanStart = pos
|
||||
if my.rawStringLiterals:
|
||||
add(my.a, '"')
|
||||
while true:
|
||||
case my.buf[pos]
|
||||
of '\0':
|
||||
my.err = errQuoteExpected
|
||||
my.err = errInvalidToken
|
||||
addSpan(my.a, my.buf, spanStart, pos)
|
||||
result = tkError
|
||||
break
|
||||
of '"':
|
||||
addSpan(my.a, my.buf, spanStart, pos)
|
||||
if my.rawStringLiterals:
|
||||
add(my.a, '"')
|
||||
inc(pos)
|
||||
break
|
||||
of '\\':
|
||||
addSpan(my.a, my.buf, spanStart, pos)
|
||||
if my.rawStringLiterals:
|
||||
add(my.a, '\\')
|
||||
case my.buf[pos+1]
|
||||
@@ -251,14 +276,18 @@ proc parseString(my: var JsonParser): TokKind =
|
||||
# don't bother with the error
|
||||
add(my.a, my.buf[pos])
|
||||
inc(pos)
|
||||
spanStart = pos
|
||||
of '\c':
|
||||
addSpan(my.a, my.buf, spanStart, pos)
|
||||
pos = lexbase.handleCR(my, pos)
|
||||
add(my.a, '\c')
|
||||
spanStart = pos
|
||||
of '\L':
|
||||
addSpan(my.a, my.buf, spanStart, pos)
|
||||
pos = lexbase.handleLF(my, pos)
|
||||
add(my.a, '\L')
|
||||
spanStart = pos
|
||||
else:
|
||||
add(my.a, my.buf[pos])
|
||||
inc(pos)
|
||||
my.bufpos = pos # store back
|
||||
|
||||
|
||||
@@ -1668,7 +1668,10 @@ func getSymbol(c: var PegLexer, tok: var Token) =
|
||||
while pos < c.buf.len:
|
||||
add(tok.literal, c.buf[pos])
|
||||
inc(pos)
|
||||
if pos < c.buf.len and c.buf[pos] notin strutils.IdentChars: break
|
||||
if pos < c.buf.len:
|
||||
let ch = c.buf[pos]
|
||||
# Keep non-ASCII bytes so UTF-8 terminals reach the rune-aware matchers.
|
||||
if ch notin strutils.IdentChars and ord(ch) < 0x80: break
|
||||
c.bufpos = pos
|
||||
tok.kind = tkIdentifier
|
||||
|
||||
|
||||
@@ -380,8 +380,10 @@ proc `$`*(t: StringTableRef): string {.rtlFunc, extern: "nstDollar".} =
|
||||
result = "{:}"
|
||||
else:
|
||||
result = "{"
|
||||
var first = true
|
||||
for key, val in pairs(t):
|
||||
if result.len > 1: result.add(", ")
|
||||
if first: first = false
|
||||
else: result.add(", ")
|
||||
result.add(key)
|
||||
result.add(": ")
|
||||
result.add(val)
|
||||
|
||||
@@ -166,7 +166,7 @@ proc wasMoved*[T](obj: var T) {.magic: "WasMoved", noSideEffect.}
|
||||
## it was "moved" and to signify its destructor should do nothing and
|
||||
## ideally be optimized away.
|
||||
|
||||
proc move*[T](x: var T): T {.magic: "Move", noSideEffect.} =
|
||||
proc move*[T](x: var T): T {.magic: "Move", noSideEffect, nodestroy.} =
|
||||
result = x
|
||||
{.cast(raises: []), cast(tags: []).}:
|
||||
`=wasMoved`(x)
|
||||
@@ -2694,7 +2694,9 @@ when hasAlloc or defined(nimscript):
|
||||
setLen(x, xl+item.len)
|
||||
var j = xl-1
|
||||
while j >= i:
|
||||
when defined(gcArc) or defined(gcOrc) or defined(gcYrc) or defined(gcAtomicArc):
|
||||
when defined(nimsso):
|
||||
x[j+item.len] = x[j]
|
||||
elif defined(gcArc) or defined(gcOrc) or defined(gcYrc) or defined(gcAtomicArc):
|
||||
x[j+item.len] = move x[j]
|
||||
else:
|
||||
shallowCopy(x[j+item.len], x[j])
|
||||
|
||||
@@ -691,7 +691,7 @@ proc getBigChunk(a: var MemRegion, size: int): PBigChunk =
|
||||
removeChunkFromMatrix2(a, result, fl, sl)
|
||||
if result.size >= size + PageSize:
|
||||
splitChunk(a, result, size)
|
||||
# set 'used' to to true:
|
||||
# set 'used' to true:
|
||||
result.prevSize = 1
|
||||
track("setUsedToFalse", addr result.size, sizeof(int))
|
||||
sysAssert result.owner == addr a, "getBigChunk: No owner set!"
|
||||
@@ -708,7 +708,7 @@ proc getHugeChunk(a: var MemRegion; size: int): PBigChunk =
|
||||
result.next = nil
|
||||
result.prev = nil
|
||||
result.size = size
|
||||
# set 'used' to to true:
|
||||
# set 'used' to true:
|
||||
result.prevSize = 1
|
||||
result.owner = addr a
|
||||
incl(a, a.chunkStarts, pageIndex(result))
|
||||
|
||||
@@ -143,7 +143,7 @@ when nimCoroutines:
|
||||
|
||||
proc find(first: var GcStack, bottom: pointer): ptr GcStack =
|
||||
## Find stack struct based on bottom pointer. If `bottom` is nil then main
|
||||
## thread stack is is returned.
|
||||
## thread stack is returned.
|
||||
if bottom == nil:
|
||||
return addr(gch.stack)
|
||||
|
||||
|
||||
@@ -59,16 +59,35 @@ template `[]=`*(s: string; i: int; val: char) = arrPut(s, i, val)
|
||||
template `^^`(s, i: untyped): untyped =
|
||||
(when i is BackwardsIndex: s.len - int(i) else: int(i))
|
||||
|
||||
template spliceImpl(s, a, L, b: typed): untyped =
|
||||
template spliceStringImpl(s, a, L, b: typed): untyped =
|
||||
# make room for additional elements or cut:
|
||||
var shift = b.len - max(0,L) # ignore negative slice size
|
||||
var newLen = s.len + shift
|
||||
if shift > 0:
|
||||
# enlarge:
|
||||
setLen(s, newLen)
|
||||
for i in countdown(newLen-1, a+b.len): movingCopy(s[i], s[i-shift])
|
||||
for i in countdown(newLen-1, a+b.len):
|
||||
s[i] = s[i-shift]
|
||||
else:
|
||||
for i in countup(a+b.len, newLen-1): movingCopy(s[i], s[i-shift])
|
||||
for i in countup(a+b.len, newLen-1):
|
||||
s[i] = s[i-shift]
|
||||
# cut down:
|
||||
setLen(s, newLen)
|
||||
# fill the hole:
|
||||
for i in 0 ..< b.len: s[a+i] = b[i]
|
||||
|
||||
template spliceSeqImpl(s, a, L, b: typed): untyped =
|
||||
# make room for additional elements or cut:
|
||||
var shift = b.len - max(0,L) # ignore negative slice size
|
||||
var newLen = s.len + shift
|
||||
if shift > 0:
|
||||
# enlarge:
|
||||
setLen(s, newLen)
|
||||
for i in countdown(newLen-1, a+b.len):
|
||||
movingCopy(s[i], s[i-shift])
|
||||
else:
|
||||
for i in countup(a+b.len, newLen-1):
|
||||
movingCopy(s[i], s[i-shift])
|
||||
# cut down:
|
||||
setLen(s, newLen)
|
||||
# fill the hole:
|
||||
@@ -102,7 +121,7 @@ proc `[]=`*[T, U: Ordinal](s: var string, x: HSlice[T, U], b: string) {.systemRa
|
||||
if L == b.len:
|
||||
for i in 0..<L: s[i+a] = b[i]
|
||||
else:
|
||||
spliceImpl(s, a, L, b)
|
||||
spliceStringImpl(s, a, L, b)
|
||||
|
||||
proc `[]`*[Idx, T; U, V: Ordinal](a: array[Idx, T], x: HSlice[U, V]): seq[T] {.systemRaisesDefect.} =
|
||||
## Slice operation for arrays.
|
||||
@@ -162,4 +181,4 @@ proc `[]=`*[T; U, V: Ordinal](s: var seq[T], x: HSlice[U, V], b: openArray[T]) {
|
||||
if L == b.len:
|
||||
for i in 0 ..< L: s[i+a] = b[i]
|
||||
else:
|
||||
spliceImpl(s, a, L, b)
|
||||
spliceSeqImpl(s, a, L, b)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
when not defined(nimNoLentIterators):
|
||||
when (not defined(nimNoLentIterators)) and not defined(js) and not defined(nimscript):
|
||||
template lent2(T): untyped = lent T
|
||||
else:
|
||||
template lent2(T): untyped = T
|
||||
@@ -37,7 +37,7 @@ iterator mitems*[T](a: var openArray[T]): var T {.inline.} =
|
||||
yield a[i]
|
||||
unCheckedInc(i)
|
||||
|
||||
iterator items*[IX, T](a: array[IX, T]): T {.inline.} =
|
||||
iterator items*[IX, T](a: array[IX, T]): lent2 T {.inline.} =
|
||||
## Iterates over each item of `a`.
|
||||
when a.len > 0:
|
||||
var i = low(IX)
|
||||
@@ -143,7 +143,7 @@ iterator items*[T: Ordinal](s: Slice[T]): T =
|
||||
for x in s.a .. s.b:
|
||||
yield x
|
||||
|
||||
iterator pairs*[T](a: openArray[T]): tuple[key: int, val: T] {.inline.} =
|
||||
iterator pairs*[T](a: openArray[T]): tuple[key: int, val: lent T] {.inline.} =
|
||||
## Iterates over each item of `a`. Yields `(index, a[index])` pairs.
|
||||
var i = 0
|
||||
while i < len(a):
|
||||
@@ -158,7 +158,7 @@ iterator mpairs*[T](a: var openArray[T]): tuple[key: int, val: var T]{.inline.}
|
||||
yield (i, a[i])
|
||||
unCheckedInc(i)
|
||||
|
||||
iterator pairs*[IX, T](a: array[IX, T]): tuple[key: IX, val: T] {.inline.} =
|
||||
iterator pairs*[IX, T](a: array[IX, T]): tuple[key: IX, val: lent T] {.inline.} =
|
||||
## Iterates over each item of `a`. Yields `(index, a[index])` pairs.
|
||||
when a.len > 0:
|
||||
var i = low(IX)
|
||||
@@ -177,7 +177,7 @@ iterator mpairs*[IX, T](a: var array[IX, T]): tuple[key: IX, val: var T] {.inlin
|
||||
if i >= high(IX): break
|
||||
unCheckedInc(i)
|
||||
|
||||
iterator pairs*[T](a: seq[T]): tuple[key: int, val: T] {.inline.} =
|
||||
iterator pairs*[T](a: seq[T]): tuple[key: int, val: lent T] {.inline.} =
|
||||
## Iterates over each item of `a`. Yields `(index, a[index])` pairs.
|
||||
var i = 0
|
||||
let L = len(a)
|
||||
|
||||
@@ -224,13 +224,14 @@ proc cmpStringPtrs(a, b: ptr SmallString): int {.inline.} =
|
||||
minLen - AlwaysAvail)
|
||||
if result == 0: result = aslen - bslen
|
||||
return
|
||||
# At least one is long. Hot prefix: inlinePtr[0..AlwaysAvail-1] mirrors heap data.
|
||||
let pfxLen = min(min(aslen, bslen), AlwaysAvail)
|
||||
result = cmpInlineBytes(inlinePtrOf(a), inlinePtrOf(b), pfxLen)
|
||||
if result != 0: return
|
||||
# At least one is long. Hot prefix mirrors heap data, but only up to fullLen:
|
||||
# shrinking can leave stale bytes in the inline cache past the logical length.
|
||||
let la = if aslen > PayloadSize: a.more.fullLen else: aslen
|
||||
let lb = if bslen > PayloadSize: b.more.fullLen else: bslen
|
||||
let minLen = min(la, lb)
|
||||
let pfxLen = min(minLen, AlwaysAvail)
|
||||
result = cmpInlineBytes(inlinePtrOf(a), inlinePtrOf(b), pfxLen)
|
||||
if result != 0: return
|
||||
if minLen <= AlwaysAvail:
|
||||
result = la - lb
|
||||
return
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
discard """
|
||||
matrix: "--mm:orc"
|
||||
output: '''
|
||||
found entry
|
||||
'''
|
||||
"""
|
||||
|
||||
import std/tables
|
||||
type NoCopies = object
|
||||
|
||||
proc `=copy`(a: var NoCopies, b: NoCopies) {.error.}
|
||||
|
||||
# bug #24720
|
||||
proc foo() =
|
||||
var t: Table[int, NoCopies]
|
||||
t[3] = NoCopies() # only moves
|
||||
for k, v in t.pairs(): # lent values, no need to copy!
|
||||
echo "found entry"
|
||||
|
||||
foo()
|
||||
43
tests/arc/t25595.nim
Normal file
43
tests/arc/t25595.nim
Normal file
@@ -0,0 +1,43 @@
|
||||
discard """
|
||||
matrix: "--mm:orc; --mm:arc; --mm:refc"
|
||||
"""
|
||||
|
||||
# bug #25595: cursor inference must not borrow a case object whose source can be
|
||||
# mutated through the cursor's own ref across a call. `let c = h.w` was inferred as a
|
||||
# non-owning cursor; `clear(c.r)` overwrites `h.w` via the cursor's back-reference,
|
||||
# freeing the ref while the borrow still uses it -> use-after-free. Detected here
|
||||
# deterministically: the element's destructor must not run during the call.
|
||||
|
||||
var destroyed = false
|
||||
|
||||
type
|
||||
O = ref object
|
||||
value: int
|
||||
home: H
|
||||
W = object
|
||||
case k: bool
|
||||
of true: r: O
|
||||
of false: discard
|
||||
H = ref object
|
||||
w: W
|
||||
|
||||
proc `=destroy`(o: var typeof(O()[])) =
|
||||
destroyed = true
|
||||
|
||||
proc clear(o: O): int =
|
||||
o.home.w = W()
|
||||
doAssert not destroyed, "use-after-free: element destroyed during the call"
|
||||
result = o.value
|
||||
|
||||
proc go(h: H): int =
|
||||
let c = h.w
|
||||
result = clear(c.r)
|
||||
|
||||
proc main =
|
||||
let h = H()
|
||||
let o = O(value: 42)
|
||||
o.home = h
|
||||
h.w = W(k: true, r: o)
|
||||
doAssert go(h) == 42
|
||||
|
||||
main()
|
||||
47
tests/arc/t25850.nim
Normal file
47
tests/arc/t25850.nim
Normal file
@@ -0,0 +1,47 @@
|
||||
discard """
|
||||
cmd: '''nim c --mm:orc --expandArc:uIf --expandArc:uCase $file'''
|
||||
nimout: '''
|
||||
--expandArc: uIf
|
||||
|
||||
block :tmp:
|
||||
let s = w()
|
||||
if true:
|
||||
r[] = s
|
||||
else:
|
||||
r[] = s
|
||||
-- end of expandArc ------------------------
|
||||
--expandArc: uCase
|
||||
|
||||
block :tmp:
|
||||
let s = w()
|
||||
case n
|
||||
of 0:
|
||||
r[] = s
|
||||
else:
|
||||
r[] = w()
|
||||
-- end of expandArc ------------------------
|
||||
'''
|
||||
"""
|
||||
|
||||
# bug #25850
|
||||
# Assigning an expression-based control flow construct (an `if`/`case` nested in
|
||||
# a `block`) must distribute the assignment directly into the leaf branches
|
||||
# instead of creating redundant intermediate temporaries per branch.
|
||||
|
||||
proc w(): array[1000, byte] {.noinline.} = discard
|
||||
|
||||
proc uIf(r: ptr array[1000, byte]) =
|
||||
r[] = (block:
|
||||
let s = w()
|
||||
if true: s else: s)
|
||||
|
||||
proc uCase(r: ptr array[1000, byte], n: int) =
|
||||
r[] = (block:
|
||||
let s = w()
|
||||
case n
|
||||
of 0: s
|
||||
else: w())
|
||||
|
||||
var d: array[1000, byte]
|
||||
uIf(addr d)
|
||||
uCase(addr d, 0)
|
||||
36
tests/ccg/tclosure_err_panic_goto.nim
Normal file
36
tests/ccg/tclosure_err_panic_goto.nim
Normal file
@@ -0,0 +1,36 @@
|
||||
discard """
|
||||
matrix: "; --panics:on"
|
||||
"""
|
||||
# issue #25851: --panics:on must not drop the nimErr_ check after a closure
|
||||
# call whose result is consumed directly (e.g. `result.add elem(src)`).
|
||||
# Regression from #25295.
|
||||
|
||||
type
|
||||
Overrun = object of CatchableError
|
||||
Source = object
|
||||
data: seq[bool]
|
||||
cursor: int
|
||||
ElemFn = proc(src: var Source): bool {.closure.}
|
||||
|
||||
proc drawBool(src: var Source): bool =
|
||||
if src.cursor >= src.data.len: raise newException(Overrun, "exhausted")
|
||||
result = src.data[src.cursor]; inc src.cursor
|
||||
|
||||
proc listRun(elem: ElemFn, src: var Source): seq[bool] =
|
||||
result = @[]
|
||||
while true:
|
||||
if not src.drawBool(): break
|
||||
result.add elem(src) # closure call – the result flows straight
|
||||
# into `add`, which previously caused the
|
||||
# compiler to skip the nimErr_ check.
|
||||
|
||||
let elem: ElemFn = proc(src: var Source): bool = src.drawBool()
|
||||
|
||||
# Both --panics:on and --panics:off must propagate the Overrun.
|
||||
var caught = false
|
||||
try:
|
||||
var src = Source(data: @[true])
|
||||
discard listRun(elem, src)
|
||||
except Overrun:
|
||||
caught = true
|
||||
doAssert caught, "Overrun exception was swallowed"
|
||||
13
tests/ccgbugs2/m25294/c.nim
Normal file
13
tests/ccgbugs2/m25294/c.nim
Normal file
@@ -0,0 +1,13 @@
|
||||
template a(T: type): int =
|
||||
when T is uint64: 1 else: 2
|
||||
|
||||
type
|
||||
M*[T] = object
|
||||
data*: seq[T]
|
||||
b: seq[int]
|
||||
indices*: array[a(T), int64]
|
||||
U = distinct uint64
|
||||
D* = object
|
||||
c: M[U]
|
||||
v: array[180000, int64]
|
||||
g*: M[uint64]
|
||||
5
tests/ccgbugs2/m25294/t.nim
Normal file
5
tests/ccgbugs2/m25294/t.nim
Normal file
@@ -0,0 +1,5 @@
|
||||
import ./c
|
||||
|
||||
proc p*(): D =
|
||||
let c = M[uint64](data: @[0], indices: [1])
|
||||
result = D(g: c)
|
||||
7
tests/ccgbugs2/m25800.h
Normal file
7
tests/ccgbugs2/m25800.h
Normal file
@@ -0,0 +1,7 @@
|
||||
/*TYPESECTION*/
|
||||
struct CppRef {
|
||||
int* data;
|
||||
CppRef() : data(new int(42)) {}
|
||||
~CppRef() { delete data; data = nullptr; }
|
||||
void reset() { delete data; data = nullptr; }
|
||||
};
|
||||
18
tests/ccgbugs2/t25294.nim
Normal file
18
tests/ccgbugs2/t25294.nim
Normal file
@@ -0,0 +1,18 @@
|
||||
discard """
|
||||
matrix: "--mm:refc; --mm:orc"
|
||||
"""
|
||||
|
||||
import ./m25294/[c, t]
|
||||
|
||||
block:
|
||||
let a = new D
|
||||
a[] = p()
|
||||
discard a[]
|
||||
block:
|
||||
let a = new D
|
||||
a[] = p()
|
||||
discard a[]
|
||||
block:
|
||||
let a = new D
|
||||
a[] = p()
|
||||
discard a[]
|
||||
23
tests/ccgbugs2/t25800.nim
Normal file
23
tests/ccgbugs2/t25800.nim
Normal file
@@ -0,0 +1,23 @@
|
||||
discard """
|
||||
cmd: "nim cpp $file"
|
||||
action: "compile"
|
||||
"""
|
||||
|
||||
# Bug Report 1: {.importcpp.} on =wasMoved generates invalid preprocessor directive #.
|
||||
|
||||
|
||||
type CppRef* {.importcpp, bycopy, noInit, header: "m25800.h".} = object
|
||||
|
||||
proc `=destroy`(x: var CppRef) {.importcpp: "#.~CppRef()".}
|
||||
proc `=wasMoved`(x: var CppRef) {.importcpp: "#.reset()".}
|
||||
proc `=copy`(dest: var CppRef; src: CppRef) {.importcpp: "dest = src".}
|
||||
proc `=sink`(dest: var CppRef; src: CppRef) {.importcpp: "dest = std::move(src)".}
|
||||
|
||||
# This triggers =wasMoved when passing to sink parameter
|
||||
proc consume(x: sink CppRef) = discard
|
||||
|
||||
proc test() =
|
||||
var x: CppRef
|
||||
consume(move(x)) # =wasMoved MUST be called here after the move
|
||||
|
||||
test()
|
||||
10
tests/compiler/tcmdline_import_std_prefix.nim
Normal file
10
tests/compiler/tcmdline_import_std_prefix.nim
Normal file
@@ -0,0 +1,10 @@
|
||||
discard """
|
||||
matrix: "-d:nimPreviewSlimSystem --warning:StdPrefix:on --warningAsError:StdPrefix:on --import:std/objectdollar"
|
||||
output: "(a: 23, b: 45)"
|
||||
"""
|
||||
|
||||
type Foo = object
|
||||
a, b: int
|
||||
|
||||
let x = Foo(a: 23, b: 45)
|
||||
echo x
|
||||
@@ -176,6 +176,42 @@ block t6462:
|
||||
var s = SeqGen[int](fil: FilterMixin[int](test: nil, trans: nil))
|
||||
doAssert s.test() == nil
|
||||
|
||||
block concept_with_cint:
|
||||
# Generic proc matching through concepts with cint should still work
|
||||
type
|
||||
FilterMixin[T] = ref object
|
||||
test: (T) -> bool
|
||||
trans: (T) -> T
|
||||
|
||||
SeqGen[T] = ref object
|
||||
fil: FilterMixin[T]
|
||||
|
||||
WithFilter[T] = concept a
|
||||
a.fil is FilterMixin[T]
|
||||
|
||||
proc test[T](a: WithFilter[T]): (T) -> bool =
|
||||
a.fil.test
|
||||
|
||||
var s = SeqGen[cint](fil: FilterMixin[cint](test: nil, trans: nil))
|
||||
doAssert s.test() == nil
|
||||
|
||||
block concept_with_int:
|
||||
type
|
||||
FilterMixin[T] = ref object
|
||||
test: (T) -> bool
|
||||
trans: (T) -> T
|
||||
|
||||
SeqGen[T] = ref object
|
||||
fil: FilterMixin[T]
|
||||
|
||||
WithFilter[T] = concept a
|
||||
a.fil is FilterMixin[T]
|
||||
|
||||
proc test[T](a: WithFilter[T]): (T) -> bool =
|
||||
a.fil.test
|
||||
|
||||
var s = SeqGen[int](fil: FilterMixin[int](test: nil, trans: nil))
|
||||
doAssert s.test() == nil
|
||||
|
||||
|
||||
block t6770:
|
||||
|
||||
@@ -12,3 +12,51 @@ proc foo =
|
||||
doAssert m.id == 999
|
||||
|
||||
foo()
|
||||
|
||||
block:
|
||||
type Foo = object
|
||||
a,b,c: int
|
||||
|
||||
var dest: Foo
|
||||
|
||||
# proc `=wasMoved`(x: var Foo) =
|
||||
# debugEcho "wasMoved called"
|
||||
|
||||
proc main() =
|
||||
var x = Foo(a:11, b:12, c:13)
|
||||
dest = move(x)
|
||||
|
||||
main()
|
||||
|
||||
block:
|
||||
type Foo = object
|
||||
a,b,c: int
|
||||
|
||||
var dest: Foo
|
||||
|
||||
proc `=wasMoved`(x: var Foo) =
|
||||
discard "wasMoved called"
|
||||
|
||||
proc main() =
|
||||
var x = Foo(a:11, b:12, c:13)
|
||||
dest = move(x)
|
||||
|
||||
main()
|
||||
|
||||
|
||||
import std/threadpool
|
||||
|
||||
block:
|
||||
type Foo = object
|
||||
data: string
|
||||
|
||||
proc `=wasMoved`(x: var Foo) =
|
||||
discard
|
||||
|
||||
proc work(x: Foo) =
|
||||
discard
|
||||
|
||||
var x = Foo(data: "hello")
|
||||
spawn work(x)
|
||||
sync()
|
||||
|
||||
|
||||
8
tests/effects/tcast_effect_violation.nim
Normal file
8
tests/effects/tcast_effect_violation.nim
Normal file
@@ -0,0 +1,8 @@
|
||||
discard """
|
||||
errormsg: "cast(raises: ValueError) can raise an unlisted exception: ValueError"
|
||||
line: 7
|
||||
"""
|
||||
|
||||
proc fff() {.raises: [].} =
|
||||
{.cast(raises: ValueError).}:
|
||||
discard
|
||||
13
tests/errmsgs/tsso_string_index_var.nim
Normal file
13
tests/errmsgs/tsso_string_index_var.nim
Normal file
@@ -0,0 +1,13 @@
|
||||
discard """
|
||||
cmd: "nim check --strings:sso --mm:orc --hints:off $file"
|
||||
action: "reject"
|
||||
nimout: '''
|
||||
tsso_string_index_var.nim(13, 12) Error: expression 's[0]' is immutable, not 'var'
|
||||
'''
|
||||
"""
|
||||
|
||||
proc passByVar(c: var char) =
|
||||
c = 'x'
|
||||
|
||||
var s = "abc"
|
||||
passByVar(s[0])
|
||||
61
tests/exception/tcpp_handler_raise_finally.nim
Normal file
61
tests/exception/tcpp_handler_raise_finally.nim
Normal file
@@ -0,0 +1,61 @@
|
||||
discard """
|
||||
targets: "cpp"
|
||||
matrix: "--mm:arc; --mm:orc; --mm:refc"
|
||||
output: '''
|
||||
inner: orig
|
||||
finally
|
||||
outer: re:orig
|
||||
inner-typeless: orig
|
||||
finally-typeless
|
||||
outer-typeless: re-tl:orig
|
||||
no-catch-finally
|
||||
caught-propagated: prop
|
||||
'''
|
||||
"""
|
||||
|
||||
# When an `except` handler raises a new exception, the enclosing `finally`
|
||||
# block must still run before the new exception propagates to the outer
|
||||
# try.
|
||||
#
|
||||
# The C++ backend previously emitted the finally's `catch (...)` as a
|
||||
# sibling of the user-written catches. C++ does not allow sibling catches
|
||||
# to catch each other's throws, so a handler-raised exception bypassed the
|
||||
# finally entirely. The fix wraps the inner try/catch sequence in an
|
||||
# outer try, so any escaping exception (whether from the body or from a
|
||||
# handler) is captured before the finally runs.
|
||||
|
||||
block typed_except:
|
||||
try:
|
||||
try:
|
||||
raise newException(CatchableError, "orig")
|
||||
except CatchableError as e:
|
||||
echo "inner: ", e.msg
|
||||
raise newException(CatchableError, "re:" & e.msg)
|
||||
finally:
|
||||
echo "finally"
|
||||
except CatchableError as outer:
|
||||
echo "outer: ", outer.msg
|
||||
|
||||
block typeless_except:
|
||||
try:
|
||||
try:
|
||||
raise newException(CatchableError, "orig")
|
||||
except:
|
||||
let e = getCurrentException()
|
||||
echo "inner-typeless: ", e.msg
|
||||
raise newException(CatchableError, "re-tl:" & e.msg)
|
||||
finally:
|
||||
echo "finally-typeless"
|
||||
except CatchableError as outer:
|
||||
echo "outer-typeless: ", outer.msg
|
||||
|
||||
# try/finally without an except: the body's exception must still propagate
|
||||
# after the finally runs.
|
||||
block no_catch_finally:
|
||||
try:
|
||||
try:
|
||||
raise newException(CatchableError, "prop")
|
||||
finally:
|
||||
echo "no-catch-finally"
|
||||
except CatchableError as e:
|
||||
echo "caught-propagated: ", e.msg
|
||||
@@ -1,5 +1,5 @@
|
||||
discard """
|
||||
matrix: "--mm:refc"
|
||||
matrix: "--mm:refc; --mm:orc"
|
||||
targets: "cpp"
|
||||
output: '''
|
||||
caught as std::exception
|
||||
|
||||
30
tests/exception/treraise_typeless_except_finally.nim
Normal file
30
tests/exception/treraise_typeless_except_finally.nim
Normal file
@@ -0,0 +1,30 @@
|
||||
discard """
|
||||
targets: "cpp"
|
||||
matrix: "--mm:arc; --mm:orc; --mm:refc"
|
||||
output: '''
|
||||
finally
|
||||
after
|
||||
'''
|
||||
"""
|
||||
|
||||
# Regression test: typeless `except:` followed by `finally:` must not
|
||||
# trigger ReraiseDefect at the end of the proc.
|
||||
#
|
||||
# Previously, `genTryCpp` only emitted `T_ = nullptr;` in the *typed*
|
||||
# except branches, leaving the typeless `except:` path with a still-set
|
||||
# `T_`. After the handler body and `popCurrentException`, the trailing
|
||||
# `if (T_) std::rethrow_exception(T_);` in the finally block would still
|
||||
# fire — but with the Nim exception stack already popped, the rethrow
|
||||
# bubbled up as a `ReraiseDefect: no exception to reraise`.
|
||||
|
||||
proc test() =
|
||||
try:
|
||||
raise newException(CatchableError, "x")
|
||||
except:
|
||||
let e = getCurrentException()
|
||||
discard e
|
||||
finally:
|
||||
echo "finally"
|
||||
|
||||
test()
|
||||
echo "after"
|
||||
16
tests/generics/t20811.nim
Normal file
16
tests/generics/t20811.nim
Normal file
@@ -0,0 +1,16 @@
|
||||
discard """
|
||||
output: '''42
|
||||
42'''
|
||||
"""
|
||||
|
||||
proc outer(j: int) =
|
||||
proc genericInner[T](): int =
|
||||
j
|
||||
|
||||
proc plainInner(): int =
|
||||
j
|
||||
|
||||
echo genericInner[int]()
|
||||
echo plainInner()
|
||||
|
||||
outer(42)
|
||||
19
tests/init/t25857.nim
Normal file
19
tests/init/t25857.nim
Normal file
@@ -0,0 +1,19 @@
|
||||
discard """
|
||||
output: "1"
|
||||
"""
|
||||
|
||||
# Regression for #25857: `typeof(result)` inside `result`'s initializer must not be
|
||||
# treated as a use-before-initialization of `result`. `typeof` is a type query and
|
||||
# never evaluates its operand, so this compiles and runs.
|
||||
# (Before the fix this errored: "'result' requires explicit initialization" on
|
||||
# {.requiresInit.} return types, breaking the `ok(typeof(result), v)` idiom.)
|
||||
|
||||
type Box[T] {.requiresInit.} = object
|
||||
v: T
|
||||
|
||||
func make[T](_: typedesc[Box[T]], v: T): Box[T] = Box[T](v: v)
|
||||
|
||||
proc f(): Box[int] =
|
||||
make(typeof(result), 1)
|
||||
|
||||
echo f().v
|
||||
@@ -465,4 +465,24 @@ block: # bug #25724
|
||||
else: yield 1
|
||||
for w in c():
|
||||
let n = w
|
||||
(proc() = discard n)()
|
||||
(proc() = discard n)()
|
||||
|
||||
block:
|
||||
iterator c(): int =
|
||||
yield 1
|
||||
yield 1
|
||||
|
||||
for w in c():
|
||||
proc p(s: int) =
|
||||
let sap = s
|
||||
p(0)
|
||||
|
||||
block: # bug #25725
|
||||
iterator c(): int =
|
||||
when nimvm: yield 0
|
||||
else: yield 1
|
||||
for w in c():
|
||||
let n = w
|
||||
proc p(s: int) =
|
||||
let s = s; discard n
|
||||
p(0)
|
||||
|
||||
38
tests/lent/titems_array_lent.nim
Normal file
38
tests/lent/titems_array_lent.nim
Normal file
@@ -0,0 +1,38 @@
|
||||
discard """
|
||||
targets: "c cpp js"
|
||||
"""
|
||||
|
||||
template sameAddress(a, b): bool =
|
||||
when defined(js):
|
||||
a == b
|
||||
else:
|
||||
a.unsafeAddr == b.unsafeAddr
|
||||
|
||||
proc main() =
|
||||
block:
|
||||
let a = [10, 11, 12]
|
||||
for ai in items(a):
|
||||
doAssert sameAddress(ai, a[0])
|
||||
break
|
||||
|
||||
block:
|
||||
let a = [[1, 2], [1, 2], [1, 2]]
|
||||
for ai in items(a):
|
||||
doAssert sameAddress(ai[0], a[0][0])
|
||||
break
|
||||
|
||||
block:
|
||||
let s = @[(1, 2), (3, 4), (5, 6)]
|
||||
doAssert (3, 4) in s
|
||||
|
||||
main()
|
||||
|
||||
static:
|
||||
main()
|
||||
|
||||
block: # issue #25849
|
||||
static:
|
||||
const key = "NIM_TESTS_TOSENV_KEY"
|
||||
for val in ["val", "", "\xc3\x86"]:
|
||||
let s = @[(key, "val"), (key, ""), (key, "\xc3\x86")]
|
||||
doAssert (key, val) in s
|
||||
12
tests/lent/tlent_tuple_address.nim
Normal file
12
tests/lent/tlent_tuple_address.nim
Normal file
@@ -0,0 +1,12 @@
|
||||
discard """
|
||||
errormsg: "expression has no address"
|
||||
"""
|
||||
|
||||
iterator foo(x: int): (lent int, lent int) =
|
||||
yield (x, x + 1)
|
||||
|
||||
|
||||
var x = 12
|
||||
for i in foo(x):
|
||||
echo i[0]
|
||||
echo i[1]
|
||||
@@ -434,3 +434,32 @@ block: # bug #24378
|
||||
type Win222[T] = typeof("foobar")
|
||||
doAssert not supportsCopyMem((int, Win222[int]))
|
||||
doAssert not supportsCopyMem(tuple[a: int, b: Win222[int]])
|
||||
|
||||
block: # bug #25789
|
||||
type
|
||||
L[T; N: static int] = distinct seq[T]
|
||||
EPF = distinct L[int, 100]
|
||||
|
||||
var e: EPF = EPF(L[int, 100](@[1, 2, 3]))
|
||||
|
||||
template classifyGeneric[T](x: T): bool =
|
||||
when typeof(x) is L:
|
||||
true
|
||||
else:
|
||||
false
|
||||
|
||||
template classifyConcrete[T](x: T): bool =
|
||||
when typeof(x) is L[int, 100]:
|
||||
true
|
||||
else:
|
||||
false
|
||||
|
||||
let viaConv = L[int, 100](e)
|
||||
doAssert $type(viaConv) == "L[system.int, 100]"
|
||||
doAssert classifyGeneric(viaConv)
|
||||
doAssert classifyConcrete(viaConv)
|
||||
|
||||
let viaDB = distinctBase(e, recursive = false)
|
||||
doAssert $type(viaDB) == "L[system.int, 100]"
|
||||
doAssert classifyGeneric(viaDB)
|
||||
doAssert classifyConcrete(viaDB)
|
||||
|
||||
5
tests/method/mvtables_reentry_a.nim
Normal file
5
tests/method/mvtables_reentry_a.nim
Normal file
@@ -0,0 +1,5 @@
|
||||
type
|
||||
VtableBaseA* = ref object of RootObj
|
||||
|
||||
method say*(a: VtableBaseA): string {.base.} =
|
||||
"base"
|
||||
7
tests/method/mvtables_reentry_b.nim
Normal file
7
tests/method/mvtables_reentry_b.nim
Normal file
@@ -0,0 +1,7 @@
|
||||
import mvtables_reentry_a
|
||||
|
||||
type
|
||||
VtableDerivedB* = ref object of VtableBaseA
|
||||
|
||||
method say*(d: VtableDerivedB): string =
|
||||
"derived"
|
||||
19
tests/method/tvtable_reentry.nim
Normal file
19
tests/method/tvtable_reentry.nim
Normal file
@@ -0,0 +1,19 @@
|
||||
discard """
|
||||
targets: "c cpp"
|
||||
"""
|
||||
|
||||
import mvtables_reentry_a
|
||||
|
||||
type
|
||||
MainType = ref object of VtableBaseA
|
||||
|
||||
method say*(m: MainType): string =
|
||||
"main"
|
||||
|
||||
when isMainModule:
|
||||
import mvtables_reentry_b
|
||||
|
||||
let a: VtableBaseA = VtableDerivedB()
|
||||
doAssert a.say() == "derived"
|
||||
let m: VtableBaseA = MainType()
|
||||
doAssert m.say() == "main"
|
||||
@@ -833,4 +833,37 @@ proc overloaded[T: object](x: T) =
|
||||
var v: typeof(val)
|
||||
overloaded(v)
|
||||
|
||||
overloaded(Thing())
|
||||
overloaded(Thing())
|
||||
|
||||
block:
|
||||
type
|
||||
Foo = object
|
||||
x = Bar()
|
||||
|
||||
Bar = object
|
||||
x: int
|
||||
|
||||
var f = Foo()
|
||||
doassert f.x.x == 0
|
||||
|
||||
block:
|
||||
type
|
||||
Foo = object
|
||||
x = Bar(x: 55)
|
||||
|
||||
Bar = object
|
||||
x: int
|
||||
|
||||
var f = Foo()
|
||||
doassert f.x.x == 55
|
||||
|
||||
block:
|
||||
type
|
||||
Bar = object
|
||||
x: int
|
||||
|
||||
Foo = object
|
||||
x = Bar()
|
||||
|
||||
var f = Foo()
|
||||
doassert f.x.x == 0
|
||||
|
||||
@@ -20,3 +20,25 @@ block: # issue #24021
|
||||
discard
|
||||
else:
|
||||
discard foo.z
|
||||
|
||||
|
||||
# bug #22791
|
||||
type Foo = object
|
||||
case a: bool
|
||||
of false:
|
||||
discard
|
||||
of true:
|
||||
case b: bool
|
||||
of false:
|
||||
discard
|
||||
of true:
|
||||
c: bool
|
||||
|
||||
const f = Foo(a: true, b: true, c: true)
|
||||
case f.a
|
||||
of true:
|
||||
case f.b
|
||||
of true:
|
||||
echo f.c
|
||||
else: discard
|
||||
else: discard
|
||||
44
tests/proc/tbackendtypealias.nim
Normal file
44
tests/proc/tbackendtypealias.nim
Normal file
@@ -0,0 +1,44 @@
|
||||
# bug #25617
|
||||
# Ensure that proc types with backend type alias mismatches
|
||||
# (e.g. uint vs csize_t) are rejected at the Nim level rather
|
||||
# than producing invalid C code.
|
||||
|
||||
discard """
|
||||
cmd: "nim check --hints:off --warnings:off --errorMax:0 $file"
|
||||
action: "reject"
|
||||
nimout: '''
|
||||
tbackendtypealias.nim(21, 7) Error: type mismatch: got <proc (len: csize_t){.closure.}> but expected 'proc (len: uint){.closure.}'
|
||||
tbackendtypealias.nim(28, 7) Error: type mismatch: got <proc (len: uint){.closure.}> but expected 'proc (len: csize_t){.closure.}'
|
||||
'''
|
||||
"""
|
||||
|
||||
block direct_assignment:
|
||||
# Direct proc variable assignment with backend type alias mismatch
|
||||
var
|
||||
a: proc (len: uint)
|
||||
b: proc (len: csize_t)
|
||||
c = a
|
||||
c = b
|
||||
|
||||
block direct_assignment_reverse:
|
||||
var
|
||||
a: proc (len: csize_t)
|
||||
b: proc (len: uint)
|
||||
c = a
|
||||
c = b
|
||||
|
||||
block same_backend_type:
|
||||
# Same backend type should still work
|
||||
var
|
||||
a: proc (len: uint)
|
||||
b: proc (len: uint)
|
||||
c = a
|
||||
c = b
|
||||
|
||||
block cint_same_type:
|
||||
# cint to cint should work
|
||||
var
|
||||
a: proc (len: cint)
|
||||
b: proc (len: cint)
|
||||
c = a
|
||||
c = b
|
||||
12
tests/proc/tinvalid_cmp_op1.nim
Normal file
12
tests/proc/tinvalid_cmp_op1.nim
Normal file
@@ -0,0 +1,12 @@
|
||||
discard """
|
||||
cmd: "nim check $file"
|
||||
action: compile
|
||||
nimout: '''
|
||||
tinvalid_cmp_op1.nim(12, 1) Warning: define `<=` instead of `>=` to implement user defined comparison operator. it allows you to use `>=` automatically. [InvalidCmpOp]
|
||||
'''
|
||||
"""
|
||||
|
||||
# issue #25655
|
||||
|
||||
type Foo = distinct int
|
||||
func `>=`(a, b: Foo): bool = int(a) >= int(b)
|
||||
12
tests/proc/tinvalid_cmp_op2.nim
Normal file
12
tests/proc/tinvalid_cmp_op2.nim
Normal file
@@ -0,0 +1,12 @@
|
||||
discard """
|
||||
cmd: "nim check $file"
|
||||
action: compile
|
||||
nimout: '''
|
||||
tinvalid_cmp_op2.nim(12, 1) Warning: define `<` instead of `>` to implement user defined comparison operator. it allows you to use `>` automatically. [InvalidCmpOp]
|
||||
'''
|
||||
"""
|
||||
|
||||
# issue #25655
|
||||
|
||||
type Foo = distinct int
|
||||
func `>`(a, b: Foo): bool = int(a) > int(b)
|
||||
12
tests/proc/tinvalid_cmp_op3.nim
Normal file
12
tests/proc/tinvalid_cmp_op3.nim
Normal file
@@ -0,0 +1,12 @@
|
||||
discard """
|
||||
cmd: "nim check $file"
|
||||
action: compile
|
||||
nimout: '''
|
||||
tinvalid_cmp_op3.nim(12, 1) Warning: define `==` instead of `!=` to implement user defined comparison operator. it allows you to use `!=` automatically. [InvalidCmpOp]
|
||||
'''
|
||||
"""
|
||||
|
||||
# issue #25655
|
||||
|
||||
type Foo = distinct int
|
||||
func `!=`(a, b: Foo): bool = int(a) != int(b)
|
||||
@@ -29,3 +29,30 @@ block tnestprc:
|
||||
result = x + y
|
||||
result = add(x, 3)
|
||||
doAssert Add3(7) == 10
|
||||
|
||||
block:
|
||||
type A = object
|
||||
c: int
|
||||
type H = proc(): lent A {.nimcall.}
|
||||
const u = A(c: 0)
|
||||
proc e(T: typedesc): lent A = u
|
||||
proc y(T: typedesc): H =
|
||||
proc(): lent A {.nimcall.} = T.e
|
||||
discard y(int)
|
||||
|
||||
block:
|
||||
type A = object
|
||||
c: int
|
||||
type H = proc(): lent A {.nimcall.}
|
||||
let u = A(c: 0)
|
||||
proc y(_: int | int): H =
|
||||
proc(): lent A {.nimcall.} = u
|
||||
discard y(0)
|
||||
|
||||
block:
|
||||
type A = object
|
||||
c: int
|
||||
type H = proc(): lent A {.nimcall.}
|
||||
let u = A()
|
||||
let _: H = proc(): lent A {.nimcall.} = u
|
||||
|
||||
|
||||
@@ -241,3 +241,12 @@ proc main() =
|
||||
|
||||
static: main()
|
||||
main()
|
||||
|
||||
# https://github.com/nim-lang/Nim/issues/18583
|
||||
# $ separator must be emitted even when the item's string repr is empty
|
||||
type EmptyStr18583 = object
|
||||
proc `$`(x: EmptyStr18583): string = ""
|
||||
|
||||
block:
|
||||
var d = [EmptyStr18583(), EmptyStr18583()].toDeque
|
||||
doAssert $d == "[, ]", "got: " & $d
|
||||
|
||||
@@ -104,3 +104,15 @@ template main() =
|
||||
|
||||
static: main()
|
||||
main()
|
||||
|
||||
# https://github.com/nim-lang/Nim/issues/18583
|
||||
type EmptyStr18583HeapQ = object
|
||||
proc `$`(x: EmptyStr18583HeapQ): string = ""
|
||||
proc `<`(a, b: EmptyStr18583HeapQ): bool = false
|
||||
|
||||
block:
|
||||
var h = initHeapQueue[EmptyStr18583HeapQ]()
|
||||
push(h, EmptyStr18583HeapQ())
|
||||
push(h, EmptyStr18583HeapQ())
|
||||
let s = $h
|
||||
doAssert s == "[, ]", "got: " & s
|
||||
|
||||
@@ -287,3 +287,14 @@ template main =
|
||||
|
||||
static: main()
|
||||
main()
|
||||
|
||||
# https://github.com/nim-lang/Nim/issues/18583
|
||||
type EmptyStr18583List = object
|
||||
proc `$`(x: EmptyStr18583List): string = ""
|
||||
|
||||
block:
|
||||
var L: SinglyLinkedList[EmptyStr18583List]
|
||||
L.prepend(EmptyStr18583List())
|
||||
L.prepend(EmptyStr18583List())
|
||||
let s = $L
|
||||
doAssert s == "[, ]", "got: " & s
|
||||
|
||||
@@ -259,6 +259,11 @@ block:
|
||||
doAssert match("EINE ÜBERSICHT UND AUSSERDEM", peg"(\upper \white*)+")
|
||||
doAssert(not match("456678", peg"(\letter)+"))
|
||||
|
||||
block:
|
||||
doAssert match("CAFÉ", peg"\i café")
|
||||
doAssert match("Café", peg"\i café")
|
||||
doAssert "two cafés: Café and CAFÉ".findAll(peg"\i café").len == 3
|
||||
|
||||
doAssert("var1 = key; var2 = key2".replacef(
|
||||
peg"\skip(\s*) {\ident}'='{\ident}", "$1<-$2$2") ==
|
||||
"var1<-keykey;var2<-key2key2")
|
||||
|
||||
@@ -544,6 +544,12 @@ proc main() =
|
||||
var x = 5
|
||||
doAssert fmt"{(x=7;123.456)=:13e}" == "(x=7;123.456)= 1.234560e+02"
|
||||
doAssert x==7
|
||||
|
||||
block: # binary operators in interpolated expressions
|
||||
let n = 1
|
||||
doAssert &"{n-1}" == "0"
|
||||
doAssert fmt"{n-1}" == "0"
|
||||
|
||||
block: #curly bracket expressions and tuples
|
||||
proc formatValue(result: var string; value:Table|bool|JsonNode; specifier:string) = result.add $value
|
||||
|
||||
|
||||
@@ -135,6 +135,19 @@ block: # issue #22605 for templates, original complex example
|
||||
|
||||
doAssert g2(int) == "error"
|
||||
|
||||
block: # issue #20811
|
||||
template injectError(body: untyped): untyped =
|
||||
template error: untyped {.used, inject.} = "injected"
|
||||
body
|
||||
|
||||
proc outerOpen(error: string): string =
|
||||
injectError:
|
||||
proc genericInner[T](): string =
|
||||
error
|
||||
genericInner[int]()
|
||||
|
||||
doAssert outerOpen("captured") == "injected"
|
||||
|
||||
block: # issue #23865 for templates
|
||||
type Xxx = enum
|
||||
error
|
||||
|
||||
32
tests/template/toverload_over_untyped.nim
Normal file
32
tests/template/toverload_over_untyped.nim
Normal file
@@ -0,0 +1,32 @@
|
||||
discard """
|
||||
output: "ok"
|
||||
"""
|
||||
|
||||
# bug #25693
|
||||
|
||||
template g(b: untyped) {.dirty.} =
|
||||
template t: untyped = b
|
||||
|
||||
proc d() = discard @[0]
|
||||
proc g(_: int) = discard
|
||||
|
||||
proc f(a: var seq[int], _: string) =
|
||||
let p = @[0]
|
||||
d()
|
||||
a = p
|
||||
|
||||
let q = "a"
|
||||
g:
|
||||
var a: seq[int]
|
||||
try:
|
||||
f(a, q & "1")
|
||||
except CatchableError:
|
||||
discard
|
||||
try:
|
||||
f(a, q & "1")
|
||||
except CatchableError:
|
||||
discard
|
||||
block: t()
|
||||
block: t()
|
||||
echo "ok"
|
||||
|
||||
@@ -5,3 +5,31 @@ type
|
||||
proc newFoo[T](): Foo[T] = Foo[T](newSeq[T]())
|
||||
|
||||
var x = newFoo[Bar[int]]()
|
||||
|
||||
# issue #22936
|
||||
|
||||
import std/macros
|
||||
|
||||
type
|
||||
InternalFutureBase = object of RootObj
|
||||
|
||||
FutureBase = ref object of InternalFutureBase
|
||||
|
||||
Future[T] = ref object of FutureBase
|
||||
internalValue: T
|
||||
|
||||
B[T, E] = ref object of Future[T]
|
||||
|
||||
proc take[F: Future](fut: F) = discard
|
||||
|
||||
proc takeMany[F: Future](futs: seq[F]) = discard
|
||||
|
||||
macro checkFutures[F: Future](futs: seq[F]): untyped =
|
||||
newEmptyNode()
|
||||
|
||||
var future: B[void, void]
|
||||
var futures: seq[B[void, void]]
|
||||
|
||||
take(future)
|
||||
takeMany(futures)
|
||||
checkFutures(futures)
|
||||
|
||||
16
tests/vm/t25849.nim
Normal file
16
tests/vm/t25849.nim
Normal file
@@ -0,0 +1,16 @@
|
||||
discard """
|
||||
targets: "c cpp js"
|
||||
"""
|
||||
|
||||
import std/os
|
||||
from std/sequtils import toSeq
|
||||
|
||||
iterator items(a: array[3, string]): lent string {.inline.} =
|
||||
for i in 0..2:
|
||||
yield a[i]
|
||||
|
||||
static:
|
||||
const key = "NIM_TESTS_TOSENV_KEY"
|
||||
for val in items(["a", "b", "c"]):
|
||||
putEnv(key, val)
|
||||
doAssert (key, val) in toSeq(envPairs())
|
||||
Reference in New Issue
Block a user