mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-01 11:23:40 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2c7e1788e | ||
|
|
d9e28aac8e | ||
|
|
c84764a097 | ||
|
|
1d7510dff0 | ||
|
|
9b80b2e868 | ||
|
|
f5930d0bb3 | ||
|
|
4497d89267 | ||
|
|
f959a02037 | ||
|
|
3c6449dbdd | ||
|
|
f1ff8b6d9e | ||
|
|
46259cd0b8 | ||
|
|
4b374eb0a6 | ||
|
|
c8e805a2fa |
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1559,10 +1559,9 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
message(tracked.config, n.info, warnPtrToCstringConv,
|
||||
$n[1].typ)
|
||||
|
||||
# Check for implicit range conversions. Compile-time constants are already
|
||||
# fully known here, so only non-constant values need the downsizing warning.
|
||||
# Check for implicit range conversions
|
||||
if n.kind == nkHiddenStdConv and (not tracked.isArrayIndexing) and
|
||||
getConstExpr(tracked.ownerModule, n[1], tracked.c.idgen, tracked.graph) == nil and
|
||||
n[1].kind notin {nkCharLit..nkUInt64Lit, nkFloatLit..nkFloat128Lit} and
|
||||
shouldWarnRangeConversion(tracked.config, n.info, n.typ, n[1].typ):
|
||||
message(tracked.config, n.info, warnImplicitRangeConversion,
|
||||
typeToString(n[1].typ) & " -> " & typeToString(n.typ))
|
||||
|
||||
@@ -2642,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
|
||||
|
||||
@@ -1759,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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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?
|
||||
|
||||
4
koch.nim
4
koch.nim
@@ -16,11 +16,11 @@ const
|
||||
ChecksumsStableCommit = "0b8e46379c5bc1bf73d8b3011908389c60fb9b98" # 2.0.1
|
||||
SatStableCommit = "e63eaea8baf00bed8bcd5a29ffd8823abb265b39"
|
||||
|
||||
NimonyStableCommit = "750aa47f2139fe5ad69f04b44428b752011fe873" # 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-05-05
|
||||
# Commit from 2026-06-08
|
||||
|
||||
# examples of possible values for fusion: #head, #ea82b54, 1.2.3
|
||||
FusionStableHash = "#562467452b32cb7a97410ea177f083e6d8405734"
|
||||
|
||||
@@ -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)
|
||||
|
||||
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)
|
||||
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)
|
||||
@@ -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
|
||||
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"
|
||||
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)
|
||||
@@ -1,30 +0,0 @@
|
||||
discard """
|
||||
cmd: "nim check $options --hints:off --warning:ImplicitRangeConversion --warningaserror:ImplicitRangeConversion $file"
|
||||
action: "compile"
|
||||
"""
|
||||
|
||||
type
|
||||
E = enum
|
||||
ea, eb
|
||||
|
||||
R = range[eb..eb]
|
||||
I = range[0..3]
|
||||
|
||||
proc accept(r: R) = discard
|
||||
proc accept(i: I) = discard
|
||||
|
||||
var r: R
|
||||
var i: I
|
||||
const enumOk = eb
|
||||
const enumAlias = enumOk
|
||||
const intOk = 1 + 2
|
||||
|
||||
r = eb
|
||||
r = enumOk
|
||||
r = enumAlias
|
||||
accept(eb)
|
||||
accept(enumOk)
|
||||
accept(enumAlias)
|
||||
|
||||
i = intOk
|
||||
accept(intOk)
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user