diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 140564c444..8ce802d07b 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -15,9 +15,13 @@ proc canRaiseDisp(p: BProc; n: AnyNode): bool = # keeps whatever the PREVIOUS call left in it and the early return below # attributes this answer to a branch that did not execute — which is how the # first run of this differential came to claim effect-list coverage it did - # not have. + # not have. Both short-circuits below leave it at 5. markCanRaiseBranch 5 - if n.kind == nkSym and {sfNeverRaises, sfImportc, sfCompilerProc} * n.sym.flags != {}: + if n.kind == nkSym and n.sym.kind == skMethod: + # A base method may be overridden by a branch with a wider exception set. + # Its inferred effects describe only the base body, not every vtable target. + result = true + elif n.kind == nkSym and {sfNeverRaises, sfImportc, sfCompilerProc} * n.sym.flags != {}: result = false elif optPanics in p.config.globalOptions or (n.kind == nkSym and sfSystemModule in getModule(n.sym).flags and diff --git a/compiler/concepts.nim b/compiler/concepts.nim index dc99bcaa5f..87af3179a0 100644 --- a/compiler/concepts.nim +++ b/compiler/concepts.nim @@ -11,7 +11,8 @@ ## for details. Note this is a first implementation and only the "Concept matching" ## section has been implemented. -import ast, semdata, lookups, lineinfos, idents, msgs, renderer, types, layeredtable +import ast, semdata, lookups, lineinfos, idents, msgs, renderer, types, + layeredtable, semtypinst import std/sets @@ -71,7 +72,8 @@ proc semConceptDeclaration*(c: PContext; n: PNode): PNode = type MatchFlags* = enum - mfDontBind # Do not bind generic parameters + mfDontBind # Do not export bindings from the concept match + mfBindGenericParam # Export inferred invocation parameters despite mfDontBind mfCheckGeneric # formal <- formal comparison as opposed to formal <- operand ConceptTypePair = tuple[conceptId, typeId: ItemId] @@ -573,7 +575,17 @@ proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool = # error was reported earlier. result = false -proc fixBindings(bindings: var LayeredIdTable; concpt: PType; invocation: PType; m: var MatchCon) = +proc resolvedBinding(c: PContext; t: PType; m: MatchCon): PType = + ## An inferred concept parameter can refer to an implementation-local + ## generic parameter, for example `Elem[Impl.T]`. Resolve it while the + ## matcher's private bindings (`Impl.T -> int`) are still available. + if t.containsUnresolvedType: + prepareMetatypeForSigmatch(c, m.bindings, m.concpt.sym.info, t) + else: + t + +proc fixBindings(c: PContext; bindings: var LayeredIdTable; concpt: PType; + invocation: PType; m: var MatchCon) = # invocation != nil means we have a non-atomic concept: if invocation != nil and invocation.kind == tyGenericInvocation: assert concpt.sym.typ.kind == tyGenericBody @@ -585,8 +597,9 @@ proc fixBindings(bindings: var LayeredIdTable; concpt: PType; invocation: PType; continue let found = m.bindings.lookup(thisSym) if found != nil: - when logBindings: echo "Invocation bind: ", thisSym, " ", found - bindings.put(thisSym, found) + let resolved = resolvedBinding(c, found, m) + when logBindings: echo "Invocation bind: ", thisSym, " ", resolved + bindings.put(thisSym, resolved) # bind even more generic parameters let genBody = invocation.base @@ -602,6 +615,20 @@ proc fixBindings(bindings: var LayeredIdTable; concpt: PType; invocation: PType; bindings.put(invocation[i], boundV) bindings.put(concpt, m.potentialImplementation) +proc fixConstraintBindings(c: PContext; bindings: var LayeredIdTable; + invocation: PType; m: MatchCon) = + ## Propagates only the dependent parameters of a concept constraint. The + ## concept itself and its private matcher bindings must remain unbound so + ## that independent constraints using the same concept don't get coupled. + if invocation != nil and invocation.kind == tyGenericInvocation: + let genBody = invocation.base + assert genBody.kind == tyGenericBody + for i in FirstGenericParamAt ..< invocation.kidsLen: + if lookup(bindings, invocation[i]) == nil: + let boundValue = m.bindings.lookup(genBody[i - 1]) + if boundValue != nil: + bindings.put(invocation[i], resolvedBinding(c, boundValue, m)) + proc processConcept(c: PContext; concpt, invocation: PType, bindings: var LayeredIdTable; m: var MatchCon): bool = m.bindings = m.bindings.newTypeMapLayer() if invocation != nil and invocation.kind == tyGenericInst: @@ -611,8 +638,11 @@ proc processConcept(c: PContext; concpt, invocation: PType, bindings: var Layere if invocation[i].kind != tyVoid: bindParam(c, m, genericBody[i-1], invocation[i]) result = conceptMatchNode(c, concpt.conceptBody, m) - if result and mfDontBind notin m.flags: - fixBindings(bindings, concpt, invocation, m) + if result: + if mfDontBind notin m.flags: + fixBindings(c, bindings, concpt, invocation, m) + elif mfBindGenericParam in m.flags: + fixConstraintBindings(c, bindings, invocation, m) proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var LayeredIdTable; invocation: PType, flags: set[MatchFlags] = {}): bool = ## Entry point from sigmatch. 'concpt' is the concept we try to match (here still a PType but diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index ba05ffb6a6..0871a17a60 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -423,6 +423,20 @@ proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode = result.add(newSymNode(createMagic(c.graph, c.idgen, "default", mDefault))) result.typ = t +proc stabilizeBracketIndex(n: PNode; c: var Con; body: var PNode): PNode = + ## Evaluate a side-effecting index once and return the stable access. + doAssert n.kind == nkBracketExpr and not isAtom(n[1]) + let temp = newSym(skLet, getIdent(c.graph.cache, "bracketTmp"), c.idgen, + c.owner, n[1].info) + temp.typ = n[1].typ + let tempAsNode = newSymNode(temp) + body.add newTree(nkLetSection, n[1].info, + newTree(nkIdentDefs, tempAsNode, + newNodeI(nkEmpty, tempAsNode.info), n[1])) + result = copyNode(n) + result.add n[0] + result.add tempAsNode + proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode = # generate: (let tmp = v; reset(v); tmp) if (not hasDestructor(c, n.typ)) and c.inEnsureMove == 0: @@ -434,6 +448,10 @@ proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode = else: result = newNodeIT(nkStmtListExpr, n.info, n.typ) + var n = n + if n.kind == nkBracketExpr and not isAtom(n[1]): + n = stabilizeBracketIndex(n, c, result) + var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), c.idgen, c.owner, n.info) temp.typ = n.typ var v = newNodeI(nkLetSection, n.info) @@ -1155,24 +1173,11 @@ proc sameLocation*(a, b: PNode): bool = else: false proc genFieldAccessSideEffects(c: var Con; s: var Scope; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode = - # with side effects - var temp = newSym(skLet, getIdent(c.graph.cache, "bracketTmp"), c.idgen, c.owner, ri[1].info) - temp.typ = ri[1].typ - var v = newNodeI(nkLetSection, ri[1].info) - let tempAsNode = newSymNode(temp) - - var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3) - vpart[0] = tempAsNode - vpart[1] = newNodeI(nkEmpty, tempAsNode.info) - vpart[2] = ri[1] - v.add(vpart) - - var newAccess = copyNode(ri) - newAccess.add ri[0] - newAccess.add tempAsNode - - var snk = c.genSink(s, dest, newAccess, flags) - result = newTree(nkStmtList, v, snk, c.genWasMoved(newAccess)) + result = newNodeI(nkStmtList, ri.info) + let newAccess = stabilizeBracketIndex(ri, c, result) + let snk = c.genSink(s, dest, newAccess, flags) + result.add snk + result.add c.genWasMoved(newAccess) proc ownsData(c: var Con; s: var Scope; orig: PNode; flags: set[MoveOrCopyFlag]): PNode = var n = orig diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 6f9696cea6..094127d23b 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -2160,47 +2160,54 @@ proc checkedForDestructor(t: PType): bool = return true result = false -proc whereToBindTypeHook(c: PContext; t: PType): PType = +proc normalizeTypeHook(t: PType; markAsgn = false): PType = result = t while true: - if result.kind in {tyGenericBody, tyGenericInst}: result = result.skipModifier - elif result.kind == tyGenericInvocation: result = result[0] - else: break + if markAsgn: + incl(result, tfHasAsgn) + if result.kind == tyCompositeTypeClass and result.base.kind == tyGenericBody: + result = result.base + elif result.kind in {tyGenericBody, tyGenericInst}: + result = result.skipModifier + elif result.kind == tyGenericInvocation: + result = result.genericHead + else: + break + +proc whereToBindTypeHook(c: PContext; t: PType): PType = + result = normalizeTypeHook(t) if result.kind in {tyObject, tyDistinct, tySequence, tyString}: result = canonType(c, result) +proc bindHookToType(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp; + typeToBind: PType): bool = + var obj = typeToBind + if obj.kind notin {tyObject, tyDistinct, tySequence, tyString}: + return false + obj = canonType(c, obj) + let ao = getAttachedOp(c.graph, obj, op) + if ao == s: + discard "forward declared hook" + elif ao.isNil and not checkedForDestructor(obj): + setAttachedOp(c.graph, c.module.position, obj, op, s) + else: + prevDestructor(c, op, ao, obj, n.info) + if obj.owner.getModule != s.getModule: + localError(c.config, n.info, errGenerated, + "type bound operation `" & s.name.s & "` can be defined only in the same module with its type (" & obj.typeToString() & ")") + result = true + proc bindDupHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) = let t = s.typ var noError = false let cond = t.len == 2 and t.returnType != nil if cond: - var obj = t.firstParamType - while true: - incl(obj, tfHasAsgn) - if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.skipModifier - elif obj.kind == tyGenericInvocation: obj = obj.genericHead - else: break + var obj = normalizeTypeHook(t.firstParamType, markAsgn = true) + let res = normalizeTypeHook(t.returnType) - var res = t.returnType - while true: - if res.kind in {tyGenericBody, tyGenericInst}: res = res.skipModifier - elif res.kind == tyGenericInvocation: res = res.genericHead - else: break - - if obj.kind in {tyObject, tyDistinct, tySequence, tyString} and sameType(obj, res): - obj = canonType(c, obj) - let ao = getAttachedOp(c.graph, obj, op) - if ao == s: - discard "forward declared destructor" - elif ao.isNil and not checkedForDestructor(obj): - setAttachedOp(c.graph, c.module.position, obj, op, s) - else: - prevDestructor(c, op, ao, obj, n.info) - noError = true - if obj.owner.getModule != s.getModule: - localError(c.config, n.info, errGenerated, - "type bound operation `" & s.name.s & "` can be defined only in the same module with its type (" & obj.typeToString() & ")") + if sameType(obj, res): + noError = bindHookToType(c, s, n, op, obj) if not noError and sfSystemModule notin s.owner.flags: localError(c.config, n.info, errGenerated, @@ -2230,25 +2237,8 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) = t.len >= 2 and t.returnType == nil if cond: - var obj = t.firstParamType.skipTypes({tyVar}) - while true: - incl(obj, tfHasAsgn) - if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.skipModifier - elif obj.kind == tyGenericInvocation: obj = obj.genericHead - else: break - if obj.kind in {tyObject, tyDistinct, tySequence, tyString}: - obj = canonType(c, obj) - let ao = getAttachedOp(c.graph, obj, op) - if ao == s: - discard "forward declared destructor" - elif ao.isNil and not checkedForDestructor(obj): - setAttachedOp(c.graph, c.module.position, obj, op, s) - else: - prevDestructor(c, op, ao, obj, n.info) - noError = true - if obj.owner.getModule != s.getModule: - localError(c.config, n.info, errGenerated, - "type bound operation `" & s.name.s & "` can be defined only in the same module with its type (" & obj.typeToString() & ")") + var obj = normalizeTypeHook(t.firstParamType.skipTypes({tyVar}), markAsgn = true) + noError = bindHookToType(c, s, n, op, obj) if not noError and sfSystemModule notin s.owner.flags: case op of attachedTrace: @@ -2315,35 +2305,12 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = message(c.config, n.info, warnDeprecated, "Overriding `=` hook is deprecated; Override `=copy` hook instead") let t = s.typ if t.len == 3 and t.returnType == nil and t.firstParamType.kind == tyVar: - var obj = t.firstParamType.elementType - while true: - incl(obj, tfHasAsgn) - if obj.kind == tyGenericBody: obj = obj.skipModifier - elif obj.kind == tyGenericInvocation: obj = obj.genericHead - else: break - var objB = t[2] - while true: - if objB.kind == tyGenericBody: objB = objB.skipModifier - elif objB.kind in {tyGenericInvocation, tyGenericInst}: - objB = objB.genericHead - else: break - if obj.kind in {tyObject, tyDistinct, tySequence, tyString} and sameType(obj, objB): + var obj = normalizeTypeHook(t.firstParamType.elementType, markAsgn = true) + let objB = normalizeTypeHook(t[2]) + if sameType(obj, objB): # attach these ops to the canonical tySequence - obj = canonType(c, obj) - #echo "ATTACHING TO ", obj.id, " ", s.name.s, " ", cast[int](obj) let k = if name == "=" or name == "=copy": attachedAsgn else: attachedSink - let ao = getAttachedOp(c.graph, obj, k) - if ao == s: - discard "forward declared op" - elif ao.isNil and not checkedForDestructor(obj): - setAttachedOp(c.graph, c.module.position, obj, k, s) - else: - prevDestructor(c, k, ao, obj, n.info) - if obj.owner.getModule != s.getModule: - localError(c.config, n.info, errGenerated, - "type bound operation `" & name & "` can be defined only in the same module with its type (" & obj.typeToString() & ")") - - return + if bindHookToType(c, s, n, k, obj): return if sfSystemModule notin s.owner.flags: localError(c.config, n.info, errGenerated, "signature for '" & s.name.s & "' must be proc[T: object](x: var T; y: T)") diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 447767d7eb..6c07541f3d 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -209,7 +209,11 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi # backend spelling instead of collapsing into the generic Nim builtin: c &= char(t.kind) if t.sym != nil and {sfImportc, sfExportc} * t.sym.flags != {}: - c.hashSym(t.sym) + # Aliases inherit the external name, but have a different symbol. + if t.sym.loc.snippet != "": + c &= t.sym.loc.snippet + else: + c.hashSym(t.sym) of tyObject, tyEnum: if t.typeInstImpl != nil: # prevent against infinite recursions here, see bug #8883: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 8ef5c4e974..16d0057c36 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1166,6 +1166,8 @@ proc enterConceptMatch(c: var TCandidate; f,a: PType, flags: TTypeRelFlags): TTy return typeRel(c, prev, a, flags) if trDontBind in flags: conceptFlags.incl mfDontBind + if trBindGenericParam in flags: + conceptFlags.incl mfBindGenericParam if trCheckGeneric in flags: conceptFlags.incl mfCheckGeneric let mres = concepts.conceptMatch(c.c, concpt, a, c.bindings, container, flags = conceptFlags) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index c34b634fb6..e1d7476268 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -3304,13 +3304,21 @@ proc dirInclude(p: var RstParser): PRstNode = ## Only the content before the first occurrence of the specified ## text (but after any after text) will be included. If text is ## not found inclusion will happen until the end of the file. - #literal : flag (empty) - # The entire included text is inserted into the document as a single - # literal block (useful for program listings). - #encoding : name of text encoding - # The text encoding of the external data file. Defaults to the document's - # encoding (if specified). - # + ## + ## :literal: flag (empty) + ## + ## The entire included text is inserted into the document as a single + ## literal block (useful for program listings). + ## + ## :code: language (if empty, `nim` is assumed by default) + ## + ## The argument and the included content are passed to the code directive + ## (useful for program listings). + ## + ## :encoding: name of text encoding + ## + ## The text encoding of the external data file. Defaults to the document's + ## encoding (if specified). result = nil var n = parseDirective(p, rnDirective, {hasArg, argIsFile, hasOptions}, nil) var filename = strip(addNodes(n.sons[0])) @@ -3343,6 +3351,19 @@ proc dirInclude(p: var RstParser): PRstNode = if getFieldValue(n, "literal") != "": result = newRstNode(rnLiteralBlock) result.add newLeaf(inputString[startPosition..endPosition]) + elif getFieldValue(n, "code") != "": + result = newRstNode(rnCodeBlock) + result.sons.setLen(3) + let lang = getFieldValue(n, "code").strip() + if lang notin ["", "\x01\x01"]: + var codeArg = newRstNode(rnDirArg) + codeArg.add(newLeaf(lang)) + result.sons[0] = codeArg + result.sons[1] = newRstNode(rnFieldList) + defaultCodeLangNim(p, result) + var litBlock = newRstNode(rnLiteralBlock) + litBlock.add newLeaf(inputString[startPosition..endPosition]) + result.sons[2] = litBlock else: var q: RstParser initParser(q, p.s) diff --git a/lib/system.nim b/lib/system.nim index d4896bdc4a..6c8f966f46 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1254,7 +1254,9 @@ proc del*[T](x: var seq[T], i: Natural) {.noSideEffect.} = a.del(2) assert a == @[10, 11, 14, 13] let xl = x.len - 1 - movingCopy(x[i], x[xl]) + # Avoid moving the element onto itself when deleting the last item. + if i != xl: + movingCopy(x[i], x[xl]) setLen(x, xl) proc insert*[T](x: var seq[T], item: sink T, i = 0.Natural) {.noSideEffect.} = diff --git a/tests/arc/tarc_orc.nim b/tests/arc/tarc_orc.nim index 790511314c..9a32d42c6e 100644 --- a/tests/arc/tarc_orc.nim +++ b/tests/arc/tarc_orc.nim @@ -35,6 +35,20 @@ proc bug20303() = bug20303() +block: # bug #26143 + var indexCalls = 0 + + proc nextIndex(): int = + result = indexCalls + inc indexCalls + + proc consume(value: sink string) = + doAssert value == "A" + + var values = @["A", "B"] + consume(values[nextIndex()]) + doAssert indexCalls == 1 + proc main() = # todo bug with templates block: # bug #11267 var a: seq[char] = block: @[] diff --git a/tests/ccgbugs/mseq_importc_alias.nim b/tests/ccgbugs/mseq_importc_alias.nim new file mode 100644 index 0000000000..5b3f97747d --- /dev/null +++ b/tests/ccgbugs/mseq_importc_alias.nim @@ -0,0 +1,5 @@ +proc resizeCints*(s: var seq[cint], n: int) = + s.setLen(n) + +proc cintLen*(s: seq[cint]): int = + result = s.len diff --git a/tests/ccgbugs/tseq_importc_alias_crossmod.nim b/tests/ccgbugs/tseq_importc_alias_crossmod.nim new file mode 100644 index 0000000000..a4c94d2b2e --- /dev/null +++ b/tests/ccgbugs/tseq_importc_alias_crossmod.nim @@ -0,0 +1,15 @@ +discard """ + action: run + targets: "c cpp" +""" + +import mseq_importc_alias + +type CIntAlias = cint + +var fds: seq[CIntAlias] +doAssert cintLen(@[1.cint, 2.cint]) == 2 +doAssert cintLen(fds) == 0 +resizeCints(fds, 3) +fds[1] = CIntAlias(7) +doAssert cintLen(fds) == 3 diff --git a/tests/ccgbugs/tseq_importc_alias_mangle.nim b/tests/ccgbugs/tseq_importc_alias_mangle.nim new file mode 100644 index 0000000000..a6a9074ec4 --- /dev/null +++ b/tests/ccgbugs/tseq_importc_alias_mangle.nim @@ -0,0 +1,20 @@ +discard """ + action: run + targets: "c cpp" +""" + +type CIntAlias = cint + +var x: (cint,) = (1.cint,) +var y: (CIntAlias,) = x +x = y +doAssert x[0] == 1.cint + +var a: seq[cint] +var b: seq[CIntAlias] +a.add 1.cint +a.add 2.cint +b = a +a = b +doAssert a[0] == 1.cint +doAssert b[1] == CIntAlias(2) diff --git a/tests/concepts/t26147.nim b/tests/concepts/t26147.nim new file mode 100644 index 0000000000..77eef87691 --- /dev/null +++ b/tests/concepts/t26147.nim @@ -0,0 +1,75 @@ +discard """ + action: run +""" + +type Indexable[T] = concept + proc `[]`(a: Self; index: int): T + proc len(a: Self): int + +iterator items[T; I: Indexable[T]](indexable: I): T = + for index in 0 ..< indexable.len: + yield indexable[index] + +type Dummy[T] = distinct seq[T] + +proc `[]`[T](d: Dummy[T], i: int): T = seq[T](d)[i] +proc len[T](d: Dummy[T]): int = seq[T](d).len + +var acc = 0 +for x in Dummy(@[1, 2, 3]): + acc += x +doAssert acc == 6 + +# Inferred concept parameters are resolved through the implementation's own +# generic bindings before being exported to the surrounding routine. +type + Elem[T] = object + value: T + NestedDummy[T] = ref object + data: seq[T] + +proc `[]`[T](d: NestedDummy[T], i: int): Elem[T] = + Elem[T](value: d.data[i]) +proc len[T](d: NestedDummy[T]): int = d.data.len + +iterator directItems[T](indexable: Indexable[T]): T = + for index in 0 ..< indexable.len: + yield indexable[index] + +var nestedAcc = 0 +for x in NestedDummy[int](data: @[4, 5, 6]): + nestedAcc += x.value +doAssert nestedAcc == 15 + +var directNestedAcc = 0 +for x in directItems(NestedDummy[int](data: @[7, 8, 9])): + directNestedAcc += x.value +doAssert directNestedAcc == 24 + +# All dependent parameters inferred while checking a concept constraint must +# be propagated to the constrained routine. +type + KeyValue[K, V] = concept + proc key(x: Self): K + proc value(x: Self): V + Pair[K, V] = object + k: K + v: V + +proc key[K, V](x: Pair[K, V]): K = x.k +proc value[K, V](x: Pair[K, V]): V = x.v + +proc unpack[K, V; P: KeyValue[K, V]](x: P): (K, V) = + (x.key, x.value) + +let pair = Pair[int, string](k: 7, v: "seven") +doAssert unpack(pair) == (7, "seven") +doAssert not compiles(unpack[string, int](pair)) + +proc unpackBoth[K1, V1, K2, V2; + P1: KeyValue[K1, V1]; P2: KeyValue[K2, V2]]( + x: P1; y: P2): ((K1, V1), (K2, V2)) = + (unpack(x), unpack(y)) + +let otherPair = Pair[string, float](k: "eight", v: 8.0) +doAssert unpackBoth(pair, otherPair) == ((7, "seven"), ("eight", 8.0)) diff --git a/tests/destructor/tdestructor.nim b/tests/destructor/tdestructor.nim index bb5889f345..d44d54365b 100644 --- a/tests/destructor/tdestructor.nim +++ b/tests/destructor/tdestructor.nim @@ -166,3 +166,99 @@ type Vector*[T] = object # proc `=destroy`*(x: var Vector[int]) = discard # this will remove error proc `=destroy`*[T](x: var Vector[T]) = discard var a: Vector[int] # Error: unresolved generic parameter + +# issue #26132 + +block: + type UnparameterizedGeneric[T] = object + + proc `=destroy`(x: var UnparameterizedGeneric) = discard + proc `=wasMoved`(x: var UnparameterizedGeneric) = discard + proc `=trace`(x: var UnparameterizedGeneric; env: pointer) = discard + + var x: UnparameterizedGeneric[int] + discard x + +# Exercise every type-bound hook with the generic parameter omitted. +block: + type + Generic[T] = object + value: T + + var destroys, moves, traces, copies, sinks, dups: int + + proc `=destroy`(x: var Generic) = inc destroys + proc `=wasMoved`(x: var Generic) = + inc moves + x.value = default(typeof(x.value)) + proc `=trace`(x: var Generic; env: pointer) = inc traces + proc `=copy`(dest: var Generic; src: Generic) = + inc copies + dest.value = src.value + proc `=sink`(dest: var Generic; src: Generic) = + inc sinks + dest.value = src.value + proc `=dup`(src: Generic): Generic = + inc dups + Generic(value: src.value) + proc deepCopy(src: ref Generic): ref Generic = src + + proc exercise[T]() = + var first = Generic[T](value: default(T)) + var second = Generic[T](value: default(T)) + second = first + doAssert second.value == first.value + second = Generic[T](value: default(T)) + doAssert second.value == default(T) + `=trace`(first, nil) + `=wasMoved`(first) + let implicitDuplicate = first + discard implicitDuplicate + let duplicate = `=dup`(first) + discard duplicate + let original = new(Generic[T]) + doAssert deepCopy(original) == original + + exercise[string]() + exercise[int]() + exercise[seq[int]]() + + doAssert copies > 0 + doAssert sinks > 0 + doAssert dups > 0 + doAssert moves > 0 + doAssert traces > 0 + doAssert destroys > 0 + + block: + type GenericDistinct[T] = distinct Generic[T] + + proc `=destroy`(x: var GenericDistinct) = discard + proc `=wasMoved`(x: var GenericDistinct) = discard + proc `=trace`(x: var GenericDistinct; env: pointer) = discard + proc `=copy`(dest: var GenericDistinct; src: GenericDistinct) = discard + proc `=sink`(dest: var GenericDistinct; src: GenericDistinct) = discard + proc `=dup`(src: GenericDistinct): GenericDistinct = src + proc deepCopy(src: ref GenericDistinct): ref GenericDistinct = src + + var first = GenericDistinct[string](Generic[string](value: "first")) + var second = GenericDistinct[string](Generic[string](value: "second")) + second = first + second = GenericDistinct[string](Generic[string](value: "third")) + `=trace`(first, nil) + `=wasMoved`(first) + let moved = move(first) + let duplicate = `=dup`(moved) + discard duplicate + let original = new(GenericDistinct[string]) + doAssert deepCopy(original) == original + + block: + type GenericPair[A, B] = object + left: A + right: B + + proc `=destroy`(x: var GenericPair) = discard + + var pair = GenericPair[int, string](left: 42, right: "pair") + discard pair diff --git a/tests/ic/timportcalias.nim b/tests/ic/timportcalias.nim new file mode 100644 index 0000000000..2f6c20f4b5 --- /dev/null +++ b/tests/ic/timportcalias.nim @@ -0,0 +1,7 @@ +import ../ccgbugs/mseq_importc_alias + +type CIntAlias = cint + +var values: seq[CIntAlias] +resizeCints(values, 2) +doAssert cintLen(values) == 2 diff --git a/tests/method/tmethod_virtual_raise.nim b/tests/method/tmethod_virtual_raise.nim new file mode 100644 index 0000000000..6bc1575282 --- /dev/null +++ b/tests/method/tmethod_virtual_raise.nim @@ -0,0 +1,20 @@ +discard """ + output: '''caught''' +""" + +type + Base = ref object of RootObj + Child = ref object of Base + +method run(value: Base): string {.base.} = + result = "base" + +method run(value: Child): string = + raise newException(ValueError, "child") + +let value: Base = Child() +try: + discard value.run() + quit "virtual method did not raise" +except ValueError: + echo "caught" diff --git a/tests/stdlib/t26134.nim b/tests/stdlib/t26134.nim new file mode 100644 index 0000000000..2d2d916665 --- /dev/null +++ b/tests/stdlib/t26134.nim @@ -0,0 +1,24 @@ +discard """ + matrix: "--mm:orc --undef:nimPreviewNonVarDestructor" + output: "hello" +""" + +# bug #26134 + +type MyObject = object + +proc `=destroy`(v: var MyObject) = + echo "hello" + +proc remove(v: var seq[MyObject]) = + v.del(0) + +proc aaa(v: var seq[MyObject], i: sink MyObject) = + v.add(i) + +proc main = + var v: seq[MyObject] + v.aaa(MyObject()) + v.remove() + +main()