diff options
-rw-r--r-- | compiler/ast.nim | 5 | ||||
-rw-r--r-- | compiler/ccgexprs.nim | 7 | ||||
-rw-r--r-- | compiler/cgen.nim | 2 | ||||
-rw-r--r-- | compiler/condsyms.nim | 1 | ||||
-rw-r--r-- | compiler/msgs.nim | 1 | ||||
-rw-r--r-- | compiler/semcall.nim | 5 | ||||
-rw-r--r-- | compiler/semdata.nim | 3 | ||||
-rw-r--r-- | compiler/semexprs.nim | 62 | ||||
-rw-r--r-- | compiler/semfold.nim | 9 | ||||
-rw-r--r-- | compiler/semgnrc.nim | 23 | ||||
-rw-r--r-- | compiler/seminst.nim | 2 | ||||
-rw-r--r-- | compiler/semmagic.nim | 34 | ||||
-rw-r--r-- | compiler/semstmts.nim | 2 | ||||
-rw-r--r-- | compiler/semtempl.nim | 51 | ||||
-rw-r--r-- | compiler/semtypes.nim | 35 | ||||
-rw-r--r-- | compiler/sigmatch.nim | 4 | ||||
-rw-r--r-- | doc/manual/generics.txt | 11 | ||||
-rw-r--r-- | doc/manual/pragmas.txt | 15 | ||||
-rw-r--r-- | doc/manual/types.txt | 24 | ||||
-rw-r--r-- | lib/system.nim | 24 | ||||
-rw-r--r-- | tests/ccgbugs/tnocodegen_for_compiletime.nim | 9 | ||||
-rw-r--r-- | tests/generics/mbind_bracket.nim | 17 | ||||
-rw-r--r-- | tests/generics/tbind_bracket.nim | 20 | ||||
-rw-r--r-- | tests/generics/tthread_generic.nim | 2 | ||||
-rw-r--r-- | tests/metatype/ttypedesc1.nim | 2 | ||||
-rw-r--r-- | tests/types/tauto_excessive.nim | 20 | ||||
-rw-r--r-- | todo.txt | 1 | ||||
-rw-r--r-- | web/news.txt | 55 |
28 files changed, 356 insertions, 90 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 860bf67e8..be11e80be 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -477,6 +477,8 @@ type # wildcard type. tfHasAsgn # type has overloaded assignment operator tfBorrowDot # distinct type borrows '.' + tfTriggersCompileTime # uses the NimNode type which make the proc + # implicitly '.compiletime' TTypeFlags* = set[TTypeFlag] @@ -1380,6 +1382,9 @@ proc propagateToOwner*(owner, elem: PType) = o2.flags.incl tfHasAsgn owner.flags.incl tfHasAsgn + if tfTriggersCompileTime in elem.flags: + owner.flags.incl tfTriggersCompileTime + if owner.kind notin {tyProc, tyGenericInst, tyGenericBody, tyGenericInvocation, tyPtr}: let elemB = elem.skipTypes({tyGenericInst}) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 54063229f..32fc76470 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1741,6 +1741,8 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mEcho: genEcho(p, e[1].skipConv) of mArrToSeq: genArrToSeq(p, e, d) of mNLen..mNError, mSlurp..mQuoteAst: + echo "from here ", p.prc.name.s, " ", p.prc.info + writestacktrace() localError(e.info, errXMustBeCompileTime, e.sons[0].sym.name.s) of mSpawn: let n = lowerings.wrapProcForSpawn(p.module.module, e, e.typ, nil, nil) @@ -1973,6 +1975,9 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = genProc(p.module, sym) putLocIntoDest(p, d, sym.loc) of skProc, skConverter, skIterators: + if sfCompileTime in sym.flags: + localError(n.info, "request to generate code for .compileTime proc: " & + sym.name.s) genProc(p.module, sym) if sym.loc.r == nil or sym.loc.t == nil: internalError(n.info, "expr: proc not init " & sym.name.s) @@ -2126,7 +2131,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = # due to a bug/limitation in the lambda lifting, unused inner procs # are not transformed correctly. We work around this issue (#411) here # by ensuring it's no inner proc (owner is a module): - if prc.skipGenericOwner.kind == skModule: + if prc.skipGenericOwner.kind == skModule and sfCompileTime notin prc.flags: if (optDeadCodeElim notin gGlobalOptions and sfDeadCodeElim notin getModule(prc).flags) or ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 3f88e63ee..f63134b66 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1110,7 +1110,7 @@ proc rawNewModule(module: PSym, filename: string): BModule = proc nullify[T](arr: var T) = for i in low(arr)..high(arr): - arr[i] = nil + arr[i] = Rope(nil) proc resetModule*(m: BModule) = # between two compilations in CAAS mode, we can throw diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 31bd85a06..60e8f2826 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -92,3 +92,4 @@ proc initDefines*() = defineSymbol("nimvarargstyped") defineSymbol("nimtypedescfixed") defineSymbol("nimKnowsNimvm") + defineSymbol("nimArrIdx") diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 8b3b11f4a..c5bc44664 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -514,6 +514,7 @@ const {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit, warnProveField, warnProveIndex, warnGcUnsafe, + hintPath, hintConf, hintDependency, hintExecuting, hintCodeBegin, hintCodeEnd, diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 2f181b5f3..f9fadeec7 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -308,7 +308,10 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode = let gp = finalCallee.ast.sons[genericParamsPos] if gp.kind != nkEmpty: if x.calleeSym.kind notin {skMacro, skTemplate}: - finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info) + if x.calleeSym.magic in {mArrGet, mArrPut}: + finalCallee = x.calleeSym + else: + finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info) else: # For macros and templates, the resolved generic params # are added as normal params. diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 345a8c0d1..e6456293c 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -45,7 +45,8 @@ type TExprFlag* = enum efLValue, efWantIterator, efInTypeof, efWantStmt, efAllowStmt, efDetermineType, - efAllowDestructor, efWantValue, efOperand, efNoSemCheck + efAllowDestructor, efWantValue, efOperand, efNoSemCheck, + efNoProcvarCheck TExprFlags* = set[TExprFlag] TTypeAttachedOp* = enum diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 3ff04a4fc..0e1d52fd4 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -52,7 +52,7 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result.typ = errorType(c) else: # XXX tyGenericInst here? - semProcvarCheck(c, result) + if efNoProcvarCheck notin flags: semProcvarCheck(c, result) if result.typ.kind == tyVar: result = newDeref(result) semDestructorCheck(c, result, flags) @@ -452,18 +452,18 @@ proc changeType(n: PNode, newType: PType, check: bool) = let tup = newType.skipTypes({tyGenericInst}) if tup.kind != tyTuple: if tup.kind == tyObject: return - internalError(n.info, "changeType: no tuple type for constructor") + globalError(n.info, "no tuple type for constructor") elif sonsLen(n) > 0 and n.sons[0].kind == nkExprColonExpr: # named tuple? for i in countup(0, sonsLen(n) - 1): var m = n.sons[i].sons[0] if m.kind != nkSym: - internalError(m.info, "changeType(): invalid tuple constr") + globalError(m.info, "invalid tuple constructor") return if tup.n != nil: var f = getSymFromList(tup.n, m.sym.name) if f == nil: - internalError(m.info, "changeType(): invalid identifier") + globalError(m.info, "unknown identifier: " & m.sym.name.s) return changeType(n.sons[i].sons[1], f.typ, check) else: @@ -1156,7 +1156,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = result.add(x[0]) return checkMinSonsLen(n, 2) - n.sons[0] = semExprWithType(c, n.sons[0]) + n.sons[0] = semExprWithType(c, n.sons[0], {efNoProcvarCheck}) let arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyPtr, tyRef}) case arr.kind of tyArray, tyOpenArray, tyVarargs, tyArrayConstr, tySequence, tyString, @@ -1196,7 +1196,17 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = localError(n.info, errIndexTypesDoNotMatch) result = n else: - c.p.bracketExpr = n.sons[0] + let s = if n.sons[0].kind == nkSym: n.sons[0].sym + elif n[0].kind in nkSymChoices: n.sons[0][0].sym + else: nil + if s != nil and s.kind in {skProc, skMethod, skConverter}+skIterators: + # type parameters: partial generic specialization + n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s) + result = explicitGenericInstantiation(c, n, s) + elif s != nil and s.kind == skType: + result = symNodeFromType(c, semTypeNode(c, n, nil), n.info) + else: + c.p.bracketExpr = n.sons[0] proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = let oldBracketExpr = c.p.bracketExpr @@ -1250,7 +1260,7 @@ proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} = template resultTypeIsInferrable(typ: PType): expr = typ.isMetaType and typ.kind != tyTypeDesc -proc semAsgn(c: PContext, n: PNode): PNode = +proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = checkSonsLen(n, 2) var a = n.sons[0] case a.kind @@ -1273,12 +1283,15 @@ proc semAsgn(c: PContext, n: PNode): PNode = # --> `[]=`(a, i, x) let oldBracketExpr = c.p.bracketExpr a = semSubscript(c, a, {efLValue}) - if a == nil: + if a == nil and mode != noOverloadedSubscript: result = buildOverloadedSubscripts(n.sons[0], getIdent"[]=") add(result, n[1]) result = semExprNoType(c, result) c.p.bracketExpr = oldBracketExpr return result + elif a == nil: + localError(n.info, "could not resolve: " & $n[0]) + return n c.p.bracketExpr = oldBracketExpr of nkCurlyExpr: # a{i} = x --> `{}=`(a, i, x) @@ -1323,7 +1336,8 @@ proc semAsgn(c: PContext, n: PNode): PNode = typeMismatch(n, lhs.typ, rhs.typ) n.sons[1] = fitNode(c, le, rhs) - if tfHasAsgn in lhs.typ.flags and not lhsIsResult: + if tfHasAsgn in lhs.typ.flags and not lhsIsResult and + mode != noOverloadedAsgn: return overloadedAsgn(c, lhs, n.sons[1]) fixAbstractType(c, n) @@ -1715,6 +1729,9 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = of mTypeOf: checkSonsLen(n, 2) result = semTypeOf(c, n.sons[1]) + #of mArrGet: result = semArrGet(c, n, flags) + #of mArrPut: result = semArrPut(c, n, flags) + #of mAsgn: result = semAsgnOpr(c, n) of mDefined: result = semDefined(c, setMs(n, s), false) of mDefinedInScope: result = semDefined(c, setMs(n, s), true) of mCompiles: result = semCompiles(c, setMs(n, s), flags) @@ -2066,6 +2083,19 @@ proc semExport(c: PContext, n: PNode): PNode = c.module.ast.add x result = n +proc shouldBeBracketExpr(n: PNode): bool = + assert n.kind in nkCallKinds + let a = n.sons[0] + if a.kind in nkCallKinds: + let b = a[0] + if b.kind in nkSymChoices: + for i in 0..<b.len: + if b[i].sym.magic == mArrGet: + let be = newNodeI(nkBracketExpr, n.info) + for i in 1..<a.len: be.add(a[i]) + n.sons[0] = be + return true + proc setGenericParams(c: PContext, n: PNode) = for i in 1 .. <n.len: n[i].typ = semTypeNode(c, n[i], nil) @@ -2173,7 +2203,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = else: #liMessage(n.info, warnUser, renderTree(n)); result = semIndirectOp(c, n, flags) - elif n[0].kind == nkBracketExpr and isSymChoice(n[0][0]): + elif (n[0].kind == nkBracketExpr or shouldBeBracketExpr(n)) and + isSymChoice(n[0][0]): # indirectOp can deal with explicit instantiations; the fixes # the 'newSeq[T](x)' bug setGenericParams(c, n.sons[0]) @@ -2194,16 +2225,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result = semExpr(c, result, flags) of nkBracketExpr: checkMinSonsLen(n, 1) - var s = qualifiedLookUp(c, n.sons[0], {checkUndeclared}) - if (s != nil and s.kind in {skProc, skMethod, skConverter}+skIterators) or - n[0].kind in nkSymChoices: - # type parameters: partial generic specialization - n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s) - result = explicitGenericInstantiation(c, n, s) - elif s != nil and s.kind in {skType}: - result = symNodeFromType(c, semTypeNode(c, n, nil), n.info) - else: - result = semArrayAccess(c, n, flags) + result = semArrayAccess(c, n, flags) of nkCurlyExpr: result = semExpr(c, buildOverloadedSubscripts(n, getIdent"{}"), flags) of nkPragmaExpr: diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 70276a6d4..5fe4e3299 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -430,17 +430,10 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = of mCompileOptionArg: result = newIntNodeT(ord( testCompileOptionArg(getStr(a), getStr(b), n.info)), n) - of mNewString, mNewStringOfCap, - mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh, - mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq, - mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait, mDotDot, - mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn, - mParallel, mPlugin, mGetTypeInfo, mTypeOf: - discard of mEqProc: result = newIntNodeT(ord( exprStructuralEquivalent(a, b, strictSymEquality=true)), n) - else: internalError(a.info, "evalOp(" & $m & ')') + else: discard proc getConstIfExpr(c: PSym, n: PNode): PNode = result = nil diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index e3b598919..ed0244b0c 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -251,6 +251,29 @@ proc semGenericStmt(c: PContext, n: PNode, let flags = if mixinContext: flags+{withinMixin} else: flags for i in countup(first, sonsLen(result) - 1): result.sons[i] = semGenericStmt(c, result.sons[i], flags, ctx) + of nkBracketExpr, nkCurlyExpr: + result = newNodeI(nkCall, n.info) + result.add newIdentNode(getIdent(if n.kind == nkBracketExpr:"[]" else:"{}"), + n.info) + for i in 0 ..< n.len: result.add(n[i]) + result = semGenericStmt(c, result, flags, ctx) + of nkAsgn, nkFastAsgn: + checkSonsLen(n, 2) + let a = n.sons[0] + let b = n.sons[1] + + let k = a.kind + case k + of nkBracketExpr, nkCurlyExpr: + result = newNodeI(nkCall, n.info) + result.add newIdentNode(getIdent(if k == nkBracketExpr:"[]=" else:"{}="), + n.info) + for i in 0 ..< a.len: result.add(a[i]) + result.add(b) + result = semGenericStmt(c, result, flags, ctx) + else: + for i in countup(0, sonsLen(n) - 1): + result.sons[i] = semGenericStmt(c, n.sons[i], flags, ctx) of nkIfStmt: for i in countup(0, sonsLen(n)-1): n.sons[i] = semGenericStmtScope(c, n.sons[i], flags, ctx) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 42a39d0df..64e3e8cb8 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -246,6 +246,8 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, inc i pushProcCon(c, result) instantiateProcType(c, pt, result, info) + if tfTriggersCompileTime in result.typ.flags: + incl(result.flags, sfCompileTime) n.sons[genericParamsPos] = ast.emptyNode var oldPrc = genericCacheGet(fn, entry[]) if oldPrc == nil: diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 5d16470b0..65185f762 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -26,6 +26,37 @@ proc semTypeOf(c: PContext; n: PNode): PNode = result.add typExpr result.typ = makeTypeDesc(c, typExpr.typ.skipTypes({tyTypeDesc, tyIter})) +type + SemAsgnMode = enum asgnNormal, noOverloadedSubscript, noOverloadedAsgn + +proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode +proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode + +proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode = + result = newNodeI(nkBracketExpr, n.info) + for i in 1..<n.len: result.add(n[i]) + let oldBracketExpr = c.p.bracketExpr + result = semSubscript(c, result, flags) + c.p.bracketExpr = oldBracketExpr + if result.isNil: + localError(n.info, "could not resolve: " & $n) + result = n + +proc semArrPut(c: PContext; n: PNode; flags: TExprFlags): PNode = + # rewrite `[]=`(a, i, x) back to ``a[i] = x``. + let b = newNodeI(nkBracketExpr, n.info) + for i in 1..n.len-2: b.add(n[i]) + result = newNodeI(nkAsgn, n.info, 2) + result.sons[0] = b + result.sons[1] = n.lastSon + result = semAsgn(c, result, noOverloadedSubscript) + +proc semAsgnOpr(c: PContext; n: PNode): PNode = + result = newNodeI(nkAsgn, n.info, 2) + result.sons[0] = n[1] + result.sons[1] = n[2] + result = semAsgn(c, result, noOverloadedAsgn) + proc semIsPartOf(c: PContext, n: PNode, flags: TExprFlags): PNode = var r = isPartOf(n[1], n[2]) result = newIntNodeT(ord(r), n) @@ -125,6 +156,9 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, of mTypeOf: checkSonsLen(n, 2) result = semTypeOf(c, n.sons[1]) + of mArrGet: result = semArrGet(c, n, flags) + of mArrPut: result = semArrPut(c, n, flags) + of mAsgn: result = semAsgnOpr(c, n) of mIsPartOf: result = semIsPartOf(c, n, flags) of mTypeTrait: result = semTypeTraits(c, n) of mAstToStr: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 4399c0ab0..f67ee2822 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1033,6 +1033,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = "signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T") incl(s.flags, sfUsed) of "=": + if s.magic == mAsgn: return incl(s.flags, sfUsed) let t = s.typ if t.len == 3 and t.sons[0] == nil and t.sons[1].kind == tyVar: @@ -1131,6 +1132,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, # semParamList(c, n.sons[ParamsPos], nil, s) else: s.typ = newProcType(c, n.info) + if tfTriggersCompileTime in s.typ.flags: incl(s.flags, sfCompileTime) if n.sons[patternPos].kind != nkEmpty: n.sons[patternPos] = semPattern(c, n.sons[patternPos]) if s.kind in skIterators: diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 371abe1e3..fc1af7246 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -281,6 +281,35 @@ proc semTemplBodySons(c: var TemplCtx, n: PNode): PNode = for i in 0.. < n.len: result.sons[i] = semTemplBody(c, n.sons[i]) +proc wrapInBind(c: var TemplCtx; n: PNode; opr: string): PNode = + let ident = getIdent(opr) + if ident.id in c.toInject: return n + + let s = searchInScopes(c.c, ident) + if s != nil: + var callee: PNode + if contains(c.toBind, s.id): + callee = symChoice(c.c, n, s, scClosed) + elif contains(c.toMixin, s.name.id): + callee = symChoice(c.c, n, s, scForceOpen) + elif s.owner == c.owner and sfGenSym in s.flags: + # template tmp[T](x: var seq[T]) = + # var yz: T + incl(s.flags, sfUsed) + callee = newSymNode(s, n.info) + styleCheckUse(n.info, s) + else: + callee = semTemplSymbol(c.c, n, s) + + let call = newNodeI(nkCall, n.info) + call.add(callee) + for i in 0 .. n.len-1: call.add(n[i]) + result = newNodeI(nkBind, n.info, 2) + result.sons[0] = n + result.sons[1] = call + else: + result = n + proc semTemplBody(c: var TemplCtx, n: PNode): PNode = result = n semIdeForTemplateOrGenericCheck(n, c.cursorInBody) @@ -423,6 +452,28 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = result.sons[1] = semTemplBody(c, n.sons[1]) of nkPragma: result = onlyReplaceParams(c, n) + of nkBracketExpr, nkCurlyExpr: + result = newNodeI(nkCall, n.info) + result.add newIdentNode(getIdent(if n.kind == nkBracketExpr:"[]" else:"{}"), + n.info) + for i in 0 ..< n.len: result.add(n[i]) + result = semTemplBodySons(c, result) + of nkAsgn, nkFastAsgn: + checkSonsLen(n, 2) + let a = n.sons[0] + let b = n.sons[1] + + let k = a.kind + case k + of nkBracketExpr, nkCurlyExpr: + result = newNodeI(nkCall, n.info) + result.add newIdentNode(getIdent(if k == nkBracketExpr:"[]=" else:"{}="), + n.info) + for i in 0 ..< a.len: result.add(a[i]) + result.add(b) + else: + result = n + result = semTemplBodySons(c, result) else: # dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam', # so we use the generic code for nkDotExpr too diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 2ee17fcaf..1cfbc368b 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -718,12 +718,12 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, if paramType == nil: return # (e.g. proc return type) proc addImplicitGenericImpl(typeClass: PType, typId: PIdent): PType = - let finalTypId = if typId != nil: typId - else: getIdent(paramName & ":type") if genericParams == nil: # This happens with anonymous proc types appearing in signatures # XXX: we need to lift these earlier return + let finalTypId = if typId != nil: typId + else: getIdent(paramName & ":type") # is this a bindOnce type class already present in the param list? for i in countup(0, genericParams.len - 1): if genericParams.sons[i].sym.name.id == finalTypId.id: @@ -757,7 +757,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, case paramType.kind: of tyAnything: - result = addImplicitGeneric(newTypeS(tyGenericParam, c)) + result = addImplicitGenericImpl(newTypeS(tyGenericParam, c), nil) of tyStatic: # proc(a: expr{string}, b: expr{nkLambda}) @@ -868,6 +868,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, of tyExpr: if procKind notin {skMacro, skTemplate}: result = addImplicitGeneric(newTypeS(tyAnything, c)) + #result = addImplicitGenericImpl(newTypeS(tyGenericParam, c), nil) of tyGenericParam: markUsed(info, paramType.sym) @@ -977,7 +978,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, # compiler only checks for 'nil': if skipTypes(r, {tyGenericInst}).kind != tyEmpty: # 'auto' as a return type does not imply a generic: - if r.kind != tyExpr: + if r.kind == tyAnything: + # 'p(): auto' and 'p(): expr' are equivalent, but the rest of the + # compiler is hardly aware of 'auto': + r = newTypeS(tyExpr, c) + elif r.kind != tyExpr: if r.sym == nil or sfAnon notin r.sym.flags: let lifted = liftParamType(c, kind, genericParams, r, "result", n.sons[0].info) @@ -1149,7 +1154,17 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = else: result = semAnonTuple(c, n, prev) of nkCallKinds: - if isRange(n): + let x = n[0] + let ident = case x.kind + of nkIdent: x.ident + of nkSym: x.sym.name + of nkClosedSymChoice, nkOpenSymChoice: x[0].sym.name + else: nil + if ident != nil and ident.s == "[]": + let b = newNodeI(nkBracketExpr, n.info) + for i in 1..<n.len: b.add(n[i]) + result = semTypeNode(c, b, prev) + elif ident != nil and ident.id == ord(wDotDot): result = semRangeAux(c, n, prev) elif n[0].kind notin nkIdentKinds: result = semTypeExpr(c, n) @@ -1336,8 +1351,11 @@ proc processMagicType(c: PContext, m: PSym) = of mIntSetBaseType: setMagicType(m, tyRange, intSize) of mNil: setMagicType(m, tyNil, ptrSize) of mExpr: - setMagicType(m, tyExpr, 0) - if m.name.s == "expr": m.typ.flags.incl tfOldSchoolExprStmt + if m.name.s == "auto": + setMagicType(m, tyAnything, 0) + else: + setMagicType(m, tyExpr, 0) + if m.name.s == "expr": m.typ.flags.incl tfOldSchoolExprStmt of mStmt: setMagicType(m, tyStmt, 0) if m.name.s == "stmt": m.typ.flags.incl tfOldSchoolExprStmt @@ -1365,7 +1383,8 @@ proc processMagicType(c: PContext, m: PSym) = of mOrdinal: setMagicType(m, tyOrdinal, 0) rawAddSon(m.typ, newTypeS(tyNone, c)) - of mPNimrodNode: discard + of mPNimrodNode: + incl m.typ.flags, tfTriggersCompileTime of mShared: setMagicType(m, tyObject, 0) m.typ.n = newNodeI(nkRecList, m.info) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 61f1a7444..f6f029936 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1691,6 +1691,10 @@ proc partialMatch*(c: PContext, n, nOrig: PNode, m: var TCandidate) = matchesAux(c, n, nOrig, m, marker) proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = + if m.calleeSym != nil and m.calleeSym.magic in {mArrGet, mArrPut}: + m.state = csMatch + m.call = n + return var marker = initIntSet() matchesAux(c, n, nOrig, m, marker) if m.state == csNoMatch: return diff --git a/doc/manual/generics.txt b/doc/manual/generics.txt index 8f7dcd580..c6206d030 100644 --- a/doc/manual/generics.txt +++ b/doc/manual/generics.txt @@ -116,7 +116,7 @@ type class matches ``array`` any array type ``set`` any set type ``seq`` any seq type -``auto`` any type +``any`` any type ================== =================================================== Furthermore, every generic type automatically creates a type class of the same @@ -163,15 +163,6 @@ module to illustrate this: Alternatively, the ``distinct`` type modifier can be applied to the type class to allow each param matching the type class to bind to a different type. -If a proc param doesn't have a type specified, Nim will use the -``distinct auto`` type class (also known as ``any``). Note this behavior is -deprecated for procs; templates, however, support them: - -.. code-block:: nim - # allow any combination of param types - proc concat(a, b): string = $a & $b # deprecated - proc concat(a, b: any): string = $a & $b # preferred - Procs written with the implicitly generic style will often need to refer to the type parameters of the matched generic type. They can be easily accessed using the dot syntax: diff --git a/doc/manual/pragmas.txt b/doc/manual/pragmas.txt index 8166994a9..68a88f865 100644 --- a/doc/manual/pragmas.txt +++ b/doc/manual/pragmas.txt @@ -72,7 +72,20 @@ compileTime pragma ------------------ The ``compileTime`` pragma is used to mark a proc or variable to be used at compile time only. No code will be generated for it. Compile time procs are -useful as helpers for macros. +useful as helpers for macros. Since version 0.12.0 of the language, a proc +that uses ``system.NimNode`` within its parameter types is implictly declared +``compileTime``: + +.. code-block:: nim + proc astHelper(n: NimNode): NimNode = + result = n + +Is the same as: + +.. code-block:: nim + proc astHelper(n: NimNode): NimNode {.compileTime.} = + result = n + noReturn pragma --------------- diff --git a/doc/manual/types.txt b/doc/manual/types.txt index 44a20d093..c9ac6f062 100644 --- a/doc/manual/types.txt +++ b/doc/manual/types.txt @@ -1228,3 +1228,27 @@ However, a ``void`` type cannot be inferred in generic code: The ``void`` type is only valid for parameters and return types; other symbols cannot have the type ``void``. + + +Auto type +--------- + +The ``auto`` type can only be used for return types and parameters. For return +types it causes the compiler to infer the type from the routine body: + +.. code-block:: nim + proc returnsInt(): auto = 1984 + +For parameters it currently creates implicitly generic routines: + +.. code-block:: nim + proc foo(a, b: auto) = discard + +Is the same as: + +.. code-block:: nim + proc foo[T1, T2](a: T1, b: T2) = discard + +However later versions of the language might change this to mean "infer the +parameters' types from the body". Then the above ``foo`` would be rejected as +the parameters' types can not be infered from an empty ``discard`` statement. diff --git a/lib/system.nim b/lib/system.nim index 1890ce5be..27c23e0bc 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -78,7 +78,7 @@ type stmt* {.magic: Stmt.} ## meta type to denote a statement (for templates) typedesc* {.magic: TypeDesc.} ## meta type to denote a type description void* {.magic: "VoidType".} ## meta type to denote the absence of any type - auto* = expr ## meta type for automatic type determination + auto* {.magic: Expr.} ## meta type for automatic type determination any* = distinct auto ## meta type for any supported type untyped* {.magic: Expr.} ## meta type to denote an expression that ## is not resolved (for templates) @@ -104,7 +104,7 @@ type SomeNumber* = SomeInteger|SomeReal ## type class matching all number types -proc defined*(x: expr): bool {.magic: "Defined", noSideEffect.} +proc defined*(x: expr): bool {.magic: "Defined", noSideEffect, compileTime.} ## Special compile-time procedure that checks whether `x` is ## defined. ## `x` is an external symbol introduced through the compiler's @@ -125,7 +125,7 @@ when defined(nimalias): TNumber: SomeNumber, TOrdinal: SomeOrdinal].} -proc declared*(x: expr): bool {.magic: "Defined", noSideEffect.} +proc declared*(x: expr): bool {.magic: "Defined", noSideEffect, compileTime.} ## Special compile-time procedure that checks whether `x` is ## declared. `x` has to be an identifier or a qualified identifier. ## This can be used to check whether a library provides a certain @@ -140,11 +140,11 @@ when defined(useNimRtl): {.deadCodeElim: on.} proc definedInScope*(x: expr): bool {. - magic: "DefinedInScope", noSideEffect, deprecated.} + magic: "DefinedInScope", noSideEffect, deprecated, compileTime.} ## **Deprecated since version 0.9.6**: Use ``declaredInScope`` instead. proc declaredInScope*(x: expr): bool {. - magic: "DefinedInScope", noSideEffect.} + magic: "DefinedInScope", noSideEffect, compileTime.} ## Special compile-time procedure that checks whether `x` is ## declared in the current scope. `x` has to be an identifier. @@ -160,7 +160,7 @@ proc unsafeAddr*[T](x: var T): ptr T {.magic: "Addr", noSideEffect.} = ## Cannot be overloaded. discard -proc `type`*(x: expr): typeDesc {.magic: "TypeOf", noSideEffect.} = +proc `type`*(x: expr): typeDesc {.magic: "TypeOf", noSideEffect, compileTime.} = ## Builtin 'type' operator for accessing the type of an expression. ## Cannot be overloaded. discard @@ -239,6 +239,14 @@ type seq*{.magic: "Seq".}[T] ## Generic type to construct sequences. set*{.magic: "Set".}[T] ## Generic type to construct bit sets. +when defined(nimArrIdx): + # :array|openarray|string|seq|cstring|tuple + proc `[]`*[I: Ordinal;T](a: T; i: I): T {. + noSideEffect, magic: "ArrGet".} + proc `[]=`*[I: Ordinal;T,S](a: var T; i: I; + x: S) {.noSideEffect, magic: "ArrPut".} + proc `=`*[T](dest: var T; src: T) {.noSideEffect, magic: "Asgn".} + type Slice*[T] = object ## builtin slice type a*, b*: T ## the bounds @@ -3384,7 +3392,7 @@ when hasAlloc: x[j+i] = item[j] inc(j) -proc compiles*(x: expr): bool {.magic: "Compiles", noSideEffect.} = +proc compiles*(x: expr): bool {.magic: "Compiles", noSideEffect, compileTime.} = ## Special compile-time procedure that checks whether `x` can be compiled ## without any semantic error. ## This can be used to check whether a type supports some operation: @@ -3448,7 +3456,7 @@ when hasAlloc and not defined(nimscript) and not defined(JS): include "system/deepcopy" -proc procCall*(x: expr) {.magic: "ProcCall".} = +proc procCall*(x: expr) {.magic: "ProcCall", compileTime.} = ## special magic to prohibit dynamic binding for `method`:idx: calls. ## This is similar to `super`:idx: in ordinary OO languages. ## diff --git a/tests/ccgbugs/tnocodegen_for_compiletime.nim b/tests/ccgbugs/tnocodegen_for_compiletime.nim new file mode 100644 index 000000000..a88ba4b32 --- /dev/null +++ b/tests/ccgbugs/tnocodegen_for_compiletime.nim @@ -0,0 +1,9 @@ +# bug #1679 +import macros, tables, hashes +proc hash(v: NimNode): Hash = 4 # performance is for suckers +macro test(body: stmt): stmt {.immediate.} = + var a = initCountTable[NimNode]() + a.inc(body) + +test: + 1 + 1 diff --git a/tests/generics/mbind_bracket.nim b/tests/generics/mbind_bracket.nim new file mode 100644 index 000000000..4bf18b471 --- /dev/null +++ b/tests/generics/mbind_bracket.nim @@ -0,0 +1,17 @@ + +import tables + +type + UUIDObject* = ref object + uuid: string + + Registry*[T] = ref object + objects: Table[string, T] + +proc newRegistry*[T](): Registry[T] = + result = Registry[T]() + result.objects = initTable[string, T](128) + +proc register*[T](self: Registry[T], obj: T) = + self.objects[obj.uuid] = obj + diff --git a/tests/generics/tbind_bracket.nim b/tests/generics/tbind_bracket.nim new file mode 100644 index 000000000..d0c5e2c6b --- /dev/null +++ b/tests/generics/tbind_bracket.nim @@ -0,0 +1,20 @@ +discard """ + output: "317" +""" + +# bug #2599 + +import mbind_bracket + +# also test that `[]` can be passed now as a first class construct: + +template takeBracket(x, a, i: untyped) = + echo x(a, i) + +var a: array[10, int] +a[8] = 317 + +takeBracket(`[]`, a, 8) + +let reg = newRegistry[UUIDObject]() +reg.register(UUIDObject()) diff --git a/tests/generics/tthread_generic.nim b/tests/generics/tthread_generic.nim index d34b24628..e8946caf6 100644 --- a/tests/generics/tthread_generic.nim +++ b/tests/generics/tthread_generic.nim @@ -3,7 +3,7 @@ discard """ """ type - TThreadFuncArgs[T] = object of TObject + TThreadFuncArgs[T] = object of RootObj a: proc(): T {.thread.} b: proc(val: T) {.thread.} diff --git a/tests/metatype/ttypedesc1.nim b/tests/metatype/ttypedesc1.nim index 19072d966..d78c62a94 100644 --- a/tests/metatype/ttypedesc1.nim +++ b/tests/metatype/ttypedesc1.nim @@ -7,7 +7,7 @@ type proc getTypeName(t: typedesc): string = t.name -proc foo(T: typedesc[float], a: expr): string = +proc foo(T: typedesc[float], a: auto): string = result = "float " & $(a.len > 5) proc foo(T: typedesc[TFoo], a: int): string = diff --git a/tests/types/tauto_excessive.nim b/tests/types/tauto_excessive.nim new file mode 100644 index 000000000..2626b0cf4 --- /dev/null +++ b/tests/types/tauto_excessive.nim @@ -0,0 +1,20 @@ +discard """ + output: '''10 +10.0 +1.0hiho''' +""" + +# bug #3224 +proc f(x: auto): auto = + result = $(x+10) + +proc f(x, y: auto): auto = + result = $(x+y) + + +echo f(0) # prints 10 +echo f(0.0) # prints 10.0 + +proc `+`(a, b: string): string = a & b + +echo f(0.7, 0.3), f("hi", "ho") diff --git a/todo.txt b/todo.txt index 91c6f1625..306b7008e 100644 --- a/todo.txt +++ b/todo.txt @@ -24,7 +24,6 @@ version 1.0 - The bitwise 'not' operator will be renamed to 'bnot' to prevent 'not 4 == 5' from compiling. -> requires 'mixin' annotation for procs! - split docgen into separate tool -- special rule for ``[]=``, items, pairs - BUG: echo with template `$`*(info: TLineInfo): expr = toFileLineCol(info) - make 'nil' work for 'add': - resizeString diff --git a/web/news.txt b/web/news.txt index 1a312f67b..aaa27cfee 100644 --- a/web/news.txt +++ b/web/news.txt @@ -3,7 +3,7 @@ News ==== .. - 2015-05-05 Version 0.11.4 released + 2015-09-14 Version 0.11.4 released ================================== Changes affecting backwards compatibility @@ -58,7 +58,7 @@ News of all the DLLs the standard library needs. This means that the following DLLs are now split into 32 and 64 versions: - * ``prce.dll``: Split into ``prce32.dll`` and ``prce64.dll``. + * ``pcre.dll``: Split into ``pcre32.dll`` and ``pcre64.dll``. * ``pdcurses.dll``: Split into ``pdcurses32.dll`` and ``pdcurses64.dll``. * ``sqlite3.dll``: Split into ``sqlite3_32.dll`` and ``sqlite3_64.dll``. * ``ssleay32.dll``: Split into ``ssleay32.dll`` and ``ssleay64.dll``. @@ -75,6 +75,13 @@ News with Unix's ``#!``. - An implicit return type for an iterator is now deprecated. Use ``auto`` if you want more type inference. + - The type ``auto`` is now a "multi-bind" metatype, so the following compiles: + + .. code-block:: nim + proc f(x, y: auto): auto = + result = $x & y + + echo f(0, "abc") Library Additions @@ -95,6 +102,9 @@ News - The compiler now supports a new configuration system based on `NimScript <docs/nims.html>`_. + - The compiler finally considers symbol binding rules in templates and + generics for overloaded ``[]``, ``[]=``, ``{}``, ``{}=`` operators + (issue `#2599 <https://github.com/nim-lang/Nim/issues/2599>`_). Language Additions @@ -112,22 +122,21 @@ News this ``let (x, y) == f()`` still needs to be used. - ``when nimvm`` can now be used for compiletime versions of some code sections. Click `here <docs/manual.html#when-nimvm-statement>`_ for details. + - Usage of the type ``NimNode`` in a proc now implicitly annotates the proc + with ``.compileTime``. This means generics work much better for ``NimNode``. Bugfixes -------- - - Fixed "Compiler internal error on iterator it(T: typedesc[Base]) called with - it(Child), where Child = object of Base" + - Fixed "Compiler internal error on iterator it(T: typedesc[Base]) called with it(Child), where Child = object of Base" (`#2662 <https://github.com/Araq/Nim/issues/2662>`_) - Fixed "repr() misses base object field in 2nd level derived object" (`#2749 <https://github.com/Araq/Nim/issues/2749>`_) - Fixed "nimsuggest doesn't work more than once on the non-main file" (`#2694 <https://github.com/Araq/Nim/issues/2694>`_) - - Fixed "JS Codegen. Passing arguments by var in certain cases leads to invali -d JS." + - Fixed "JS Codegen. Passing arguments by var in certain cases leads to invalid JS." (`#2798 <https://github.com/Araq/Nim/issues/2798>`_) - - Fixed ""check" proc in unittest.nim prevents the propagation of changes to v -ar parameters." + - Fixed ""check" proc in unittest.nim prevents the propagation of changes to var parameters." (`#964 <https://github.com/Araq/Nim/issues/964>`_) - Fixed "Excessive letters in integer literals are not an error" (`#2523 <https://github.com/Araq/Nim/issues/2523>`_) @@ -141,8 +150,7 @@ ar parameters." (`#2687 <https://github.com/Araq/Nim/issues/2687>`_) - Fixed "Compile error using object in const array" (`#2774 <https://github.com/Araq/Nim/issues/2774>`_) - - Fixed "httpclient async requests with method httpPOST isn't sending Content- -Length header" + - Fixed "httpclient async requests with method httpPOST isn't sending Content-Length header" (`#2884 <https://github.com/Araq/Nim/issues/2884>`_) - Fixed "Streams module not working with JS backend" (`#2148 <https://github.com/Araq/Nim/issues/2148>`_) @@ -173,8 +181,7 @@ Length header" (`#2974 <https://github.com/Araq/Nim/issues/2974>`_) - Fixed "repr is broken" (`#2992 <https://github.com/Araq/Nim/issues/2992>`_) - - Fixed "Ipv6 devel - add IPv6 support for asyncsockets, make AF_INET6 a defau -lt" + - Fixed "Ipv6 devel - add IPv6 support for asyncsockets, make AF_INET6 a default" (`#2976 <https://github.com/Araq/Nim/issues/2976>`_) - Fixed "Compilation broken on windows" (`#2996 <https://github.com/Araq/Nim/issues/2996>`_) @@ -184,8 +191,7 @@ lt" (`#2672 <https://github.com/Araq/Nim/issues/2672>`_) - Fixed "Uncatched exception in async procedure on raise statement" (`#3014 <https://github.com/Araq/Nim/issues/3014>`_) - - Fixed "nim doc2 fails in Mac OS X due to system.nim (possibly related to #18 -98)" + - Fixed "nim doc2 fails in Mac OS X due to system.nim (possibly related to #1898)" (`#3005 <https://github.com/Araq/Nim/issues/3005>`_) - Fixed "IndexError when rebuilding Nim on iteration 2" (`#3018 <https://github.com/Araq/Nim/issues/3018>`_) @@ -233,8 +239,7 @@ lt" (`#3054 <https://github.com/Araq/Nim/issues/3054>`_) - Fixed "Wrong sharing of static_t instantations" (`#3112 <https://github.com/Araq/Nim/issues/3112>`_) - - Fixed "Automatically generated proc conflicts with user-defined proc when .e -xportc.'ed" + - Fixed "Automatically generated proc conflicts with user-defined proc when .exportc.'ed" (`#3134 <https://github.com/Araq/Nim/issues/3134>`_) - Fixed "getTypeInfo call crashes nim" (`#3099 <https://github.com/Araq/Nim/issues/3099>`_) @@ -254,15 +259,13 @@ xportc.'ed" (`#3149 <https://github.com/Araq/Nim/issues/3149>`_) - Fixed "Inference of `static[T]` in sequences" (`#3144 <https://github.com/Araq/Nim/issues/3144>`_) - - Fixed "Argument named "closure" to proc inside template interfere with closu -re pragma" + - Fixed "Argument named "closure" to proc inside template interfere with closure pragma" (`#3171 <https://github.com/Araq/Nim/issues/3171>`_) - Fixed "Internal error with aliasing inside template" (`#3158 <https://github.com/Araq/Nim/issues/3158>`_) - Fixed "Cardinality of sets prints unexpected value" (`#3135 <https://github.com/Araq/Nim/issues/3135>`_) - - Fixed "Nim crashes on const assignment from function returning var ref objec -t" + - Fixed "Nim crashes on const assignment from function returning var ref object" (`#3103 <https://github.com/Araq/Nim/issues/3103>`_) - Fixed "`repr` cstring" (`#3080 <https://github.com/Araq/Nim/issues/3080>`_) @@ -270,8 +273,7 @@ t" (`#3052 <https://github.com/Araq/Nim/issues/3052>`_) - Fixed "Compiler assertion when evaluating template with static[T]" (`#1858 <https://github.com/Araq/Nim/issues/1858>`_) - - Fixed "Erroneous overflow in iterators when compiler built with overflowChec -ks enabled" + - Fixed "Erroneous overflow in iterators when compiler built with overflowChecks enabled" (`#3140 <https://github.com/Araq/Nim/issues/3140>`_) - Fixed "Unicode dashes as "lisp'ish" alternative to hump and snake notation" (`#2811 <https://github.com/Araq/Nim/issues/2811>`_) @@ -283,8 +285,7 @@ ks enabled" (`#3193 <https://github.com/Araq/Nim/issues/3193>`_) - Fixed "VM crash when accessing array's element" (`#3192 <https://github.com/Araq/Nim/issues/3192>`_) - - Fixed "Unexpected proc invoked when different modules add procs to a type fr -om a 3rd module" + - Fixed "Unexpected proc invoked when different modules add procs to a type from a 3rd module" (`#2664 <https://github.com/Araq/Nim/issues/2664>`_) - Fixed "Nim crashes on conditional declaration inside a template" (`#2670 <https://github.com/Araq/Nim/issues/2670>`_) @@ -292,8 +293,7 @@ om a 3rd module" (`#2752 <https://github.com/Araq/Nim/issues/2752>`_) - Fixed "VM: Cannot assign int value to ref variable" (`#1329 <https://github.com/Araq/Nim/issues/1329>`_) - - Fixed "Incorrect code generated for tagged unions with enums not starting at - zero" + - Fixed "Incorrect code generated for tagged unions with enums not starting at zero" (`#3096 <https://github.com/Araq/Nim/issues/3096>`_) - Fixed "Compile time procs using forward declarations are silently ignored" (`#3066 <https://github.com/Araq/Nim/issues/3066>`_) @@ -301,8 +301,7 @@ om a 3rd module" (`#1965 <https://github.com/Araq/Nim/issues/1965>`_) - Fixed "os.getCreationTime is incorrect/impossible on Posix systems" (`#1058 <https://github.com/Araq/Nim/issues/1058>`_) - - Fixed "Improve error message for osproc.startProcess when command does not e -xist" + - Fixed "Improve error message for osproc.startProcess when command does not exist" (`#2183 <https://github.com/Araq/Nim/issues/2183>`_) - Fixed "gctest segfaults with --gc:markandsweep on x86_64" (`#2305 <https://github.com/Araq/Nim/issues/2305>`_) |