diff --git a/compiler/ast.nim b/compiler/ast.nim index be11e80be2..08a991468d 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -738,6 +738,8 @@ type name*: Rope path*: PNode # can be a string literal! + CompilesId* = int ## id that is used for the caching logic within + ## ``system.compiles``. See the seminst module. TInstantiation* = object sym*: PSym concreteTypes*: seq[PType] @@ -745,6 +747,7 @@ type # needed in caas mode for purging the cache # XXX: it's possible to switch to a # simple ref count here + compilesId*: CompilesId PInstantiation* = ref TInstantiation diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 88e32404af..e88589c3ef 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -116,7 +116,7 @@ proc errorSym*(c: PContext, n: PNode): PSym = result.typ = errorType(c) incl(result.flags, sfDiscardable) # pretend it's imported from some unknown module to prevent cascading errors: - if gCmd != cmdInteractive and c.inCompilesContext == 0: + if gCmd != cmdInteractive and c.compilesContextId == 0: c.importTable.addSym(result) type diff --git a/compiler/semcall.nim b/compiler/semcall.nim index f9fadeec75..3810935318 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -95,7 +95,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) = # Gives a detailed error message; this is separated from semOverloadedCall, # as semOverlodedCall is already pretty slow (and we need this information # only in case of an error). - if c.inCompilesContext > 0: + if c.compilesContextId > 0: # fail fast: globalError(n.info, errTypeMismatch, "") if errors.isNil or errors.len == 0: @@ -235,7 +235,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode, internalAssert result.state == csMatch #writeMatches(result) #writeMatches(alt) - if c.inCompilesContext > 0: + if c.compilesContextId > 0: # quick error message for performance of 'compiles' built-in: globalError(n.info, errGenerated, "ambiguous call") elif gErrorCounter == 0: diff --git a/compiler/semdata.nim b/compiler/semdata.nim index e6456293cd..9b2f2e2ce7 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -71,7 +71,8 @@ type inTypeClass*: int # > 0 if we are in a user-defined type class inGenericContext*: int # > 0 if we are in a generic type inUnrolledContext*: int # > 0 if we are unrolling a loop - inCompilesContext*: int # > 0 if we are in a ``compiles`` magic + compilesContextId*: int # > 0 if we are in a ``compiles`` magic + compilesContextIdGenerator*: int inGenericInst*: int # > 0 if we are instantiating a generic converters*: TSymSeq # sequence of converters patterns*: TSymSeq # sequence of pattern matchers diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 0e1d52fd4c..4792702dc8 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -818,7 +818,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = # This is a proc variable, apply normal overload resolution let m = resolveIndirectCall(c, n, nOrig, t) if m.state != csMatch: - if c.inCompilesContext > 0: + if c.compilesContextId > 0: # speed up error generation: globalError(n.info, errTypeMismatch, "") return emptyNode @@ -1636,7 +1636,9 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # watch out, hacks ahead: let oldErrorCount = msgs.gErrorCounter let oldErrorMax = msgs.gErrorMax - inc c.inCompilesContext + let oldCompilesId = c.compilesContextId + inc c.compilesContextIdGenerator + c.compilesContextId = c.compilesContextIdGenerator # do not halt after first error: msgs.gErrorMax = high(int) @@ -1660,6 +1662,7 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = except ERecoverableError: discard # undo symbol table changes (as far as it's possible): + c.compilesContextId = oldCompilesId c.generics = oldGenerics c.inGenericContext = oldInGenericContext c.inUnrolledContext = oldInUnrolledContext @@ -1668,7 +1671,6 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = msgs.setInfoContextLen(oldContextLen) setLen(gOwners, oldOwnerLen) c.currentScope = oldScope - dec c.inCompilesContext errorOutputs = oldErrorOutputs msgs.gErrorCounter = oldErrorCount msgs.gErrorMax = oldErrorMax diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 48d725ea88..89b8847f34 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -402,7 +402,7 @@ proc semGenericStmt(c: PContext, n: PNode, of nkEnumFieldDef: a = n.sons[i].sons[0] of nkIdent: a = n.sons[i] else: illFormedAst(n) - addDecl(c, newSymS(skUnknown, getIdentNode(a.sons[i]), c)) + addDecl(c, newSymS(skUnknown, getIdentNode(a), c)) of nkObjectTy, nkTupleTy, nkTupleClassTy: discard of nkFormalParams: diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 1c1d71a2f6..abc5600c3a 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -47,10 +47,11 @@ proc sameInstantiation(a, b: TInstantiation): bool = flags = {ExactTypeDescValues}): return result = true -proc genericCacheGet(genericSym: PSym, entry: TInstantiation): PSym = +proc genericCacheGet(genericSym: PSym, entry: TInstantiation; + id: CompilesId): PSym = if genericSym.procInstCache != nil: for inst in genericSym.procInstCache: - if sameInstantiation(entry, inst[]): + if inst.compilesId == id and sameInstantiation(entry, inst[]): return inst.sym proc removeDefaultParamValues(n: PNode) = @@ -249,13 +250,15 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, if tfTriggersCompileTime in result.typ.flags: incl(result.flags, sfCompileTime) n.sons[genericParamsPos] = ast.emptyNode - var oldPrc = genericCacheGet(fn, entry[]) + var oldPrc = genericCacheGet(fn, entry[], c.compilesContextId) if oldPrc == nil: # we MUST not add potentially wrong instantiations to the caching mechanism. # This means recursive instantiations behave differently when in # a ``compiles`` context but this is the lesser evil. See # bug #1055 (tevilcompiles). - if c.inCompilesContext == 0: fn.procInstCache.safeAdd(entry) + #if c.compilesContextId == 0: + entry.compilesId = c.compilesContextId + fn.procInstCache.safeAdd(entry) c.generics.add(makeInstPair(fn, entry)) if n.sons[pragmasPos].kind != nkEmpty: pragma(c, result, n.sons[pragmasPos], allRoutinePragmas) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 1cfbc368bb..65cb9421b3 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -986,8 +986,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, if r.sym == nil or sfAnon notin r.sym.flags: let lifted = liftParamType(c, kind, genericParams, r, "result", n.sons[0].info) - if lifted != nil: r = lifted - r.flags.incl tfRetType + if lifted != nil: + r = lifted + #if r.kind != tyGenericParam: + #echo "came here for ", typeToString(r) + r.flags.incl tfRetType r = skipIntLit(r) if kind == skIterator: # see tchainediterators diff --git a/compiler/suggest.nim b/compiler/suggest.nim index c15a56c54c..ed21aa4eae 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -335,8 +335,8 @@ proc suggestExpr*(c: PContext, node: PNode) = if cp == cpNone: return var outputs = 0 # This keeps semExpr() from coming here recursively: - if c.inCompilesContext > 0: return - inc(c.inCompilesContext) + if c.compilesContextId > 0: return + inc(c.compilesContextId) if gIdeCmd == ideSug: var n = if nfIsCursor in node.flags: node else: findClosestDot(node) @@ -365,7 +365,7 @@ proc suggestExpr*(c: PContext, node: PNode) = addSon(a, x) suggestCall(c, a, n, outputs) - dec(c.inCompilesContext) + dec(c.compilesContextId) if outputs > 0 and gIdeCmd != ideUse: suggestQuit() proc suggestStmt*(c: PContext, n: PNode) = diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index d91507a859..ab462c57b2 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -992,8 +992,8 @@ else: proc closeSocket*(sock: AsyncFD) = let disp = getGlobalDispatcher() - sock.SocketHandle.close() disp.selector.unregister(sock.SocketHandle) + sock.SocketHandle.close() proc unregister*(fd: AsyncFD) = getGlobalDispatcher().selector.unregister(fd.SocketHandle) diff --git a/lib/system.nim b/lib/system.nim index 8f529b8c0f..af10f57735 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -243,7 +243,7 @@ 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; + proc `[]=`*[I: Ordinal;T,S](a: T; i: I; x: S) {.noSideEffect, magic: "ArrPut".} proc `=`*[T](dest: var T; src: T) {.noSideEffect, magic: "Asgn".} diff --git a/tests/compiles/trecursive_generic_in_compiles.nim b/tests/compiles/trecursive_generic_in_compiles.nim new file mode 100644 index 0000000000..77bf0bb027 --- /dev/null +++ b/tests/compiles/trecursive_generic_in_compiles.nim @@ -0,0 +1,98 @@ +# bug #3313 +import unittest, future + +type + ListNodeKind = enum + lnkNil, lnkCons + List*[T] = ref object + ## List ADT + case kind: ListNodeKind + of lnkNil: + discard + of lnkCons: + value: T + next: List[T] not nil + +proc Cons*[T](head: T, tail: List[T]): List[T] = + ## Constructs non empty list + List[T](kind: lnkCons, value: head, next: tail) + +proc Nil*[T](): List[T] = + ## Constructs empty list + List[T](kind: lnkNil) + +proc head*[T](xs: List[T]): T = + ## Returns list's head + xs.value + +# TODO +# proc headOption*[T](xs: List[T]): Option[T] = ??? + +proc tail*[T](xs: List[T]): List[T] = + ## Returns list's tail + case xs.kind + of lnkCons: xs.next + else: xs + +proc isEmpty*(xs: List): bool = + ## Checks if list is empty + xs.kind == lnkNil + +proc `==`*[T](xs, ys: List[T]): bool = + ## Compares two lists + if (xs.isEmpty, ys.isEmpty) == (true, true): true + elif (xs.isEmpty, ys.isEmpty) == (false, false): xs.head == ys.head and xs.tail == ys.tail + else: false + +proc asList*[T](xs: varargs[T]): List[T] = + ## Creates list from varargs + proc initListImpl(i: int, xs: openarray[T]): List[T] = + if i > high(xs): + Nil[T]() + else: + Cons(xs[i], initListImpl(i+1, xs)) + initListImpl(0, xs) + +proc foldRight*[T,U](xs: List[T], z: U, f: (T, U) -> U): U = + case xs.isEmpty + of true: z + else: f(xs.head, xs.tail.foldRight(z, f)) + +proc dup*[T](xs: List[T]): List[T] = + ## Duplicates the list + xs.foldRight(Nil[T](), (x: T, xs: List[T]) => Cons(x, xs)) + +type + ListFormat = enum + lfADT, lfSTD + +proc asString[T](xs: List[T], f = lfSTD): string = + proc asAdt(xs: List[T]): string = + case xs.isEmpty + of true: "Nil" + else: "Cons(" & $xs.head & ", " & xs.tail.asAdt & ")" + + proc asStd(xs: List[T]): string = + "List(" & xs.foldLeft("", (s: string, v: T) => + (if s == "": $v else: s & ", " & $v)) & ")" + + case f + of lfADT: xs.asAdt + else: xs.asStd + +proc `$`*[T](xs: List[T]): string = + ## Converts list to string + result = xs.asString + +proc foldLeft*[T,U](xs: List[T], z: U, f: (U, T) -> U): U = + case xs.isEmpty + of true: z + else: foldLeft(xs.tail, f(z, xs.head), f) + +suite "unittest compilation error": + + test "issue 3313": + let lst = lc[$x | (x <- 'a'..'z'), string].asList + + let lstCopy = lst.dup + check: lstCopy == lst diff --git a/tests/metatype/tprocbothmeta.nim b/tests/metatype/tprocbothmeta.nim index ad12c5d26f..ba061dda2f 100644 --- a/tests/metatype/tprocbothmeta.nim +++ b/tests/metatype/tprocbothmeta.nim @@ -1,5 +1,5 @@ -proc myFun[A,B](x: A): B = +proc myFun[A](x: A): auto = result = float(x+10) proc myMap[T,S](sIn: seq[T], f: proc (q: T): S): seq[S] = diff --git a/tests/metatype/tunresolved_return_type.nim b/tests/metatype/tunresolved_return_type.nim new file mode 100644 index 0000000000..f67e065eaa --- /dev/null +++ b/tests/metatype/tunresolved_return_type.nim @@ -0,0 +1,20 @@ +discard """ + errormsg: "cannot instantiate: 'T'" + line: 12 +""" + +# bug #2594 + + +type + ResultValue* = int64 + +proc toNumber[T: int|uint|int64|uint64](v: ResultValue): T = + if v < low(T) or v > high(T): + raise newException(RangeError, "protocol error") + return T(v) + +#proc toNumber[T](v: int32): T = +# return (v) + +echo toNumber(23)