summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/lookups.nim2
-rw-r--r--compiler/semcall.nim4
-rw-r--r--compiler/semdata.nim2
-rw-r--r--compiler/semexprs.nim8
-rw-r--r--compiler/seminst.nim6
-rw-r--r--compiler/suggest.nim6
-rw-r--r--tests/compiles/trecursive_generic_in_compiles.nim98
7 files changed, 112 insertions, 14 deletions
diff --git a/compiler/lookups.nim b/compiler/lookups.nim
index 88e32404a..e88589c3e 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 f9fadeec7..381093531 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 48255c83d..9b2f2e2ce 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -71,7 +71,7 @@ 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
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 9b0e02976..4792702dc 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,9 +1636,9 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   # watch out, hacks ahead:
   let oldErrorCount = msgs.gErrorCounter
   let oldErrorMax = msgs.gErrorMax
-  let oldCompilesId = c.inCompilesContext
+  let oldCompilesId = c.compilesContextId
   inc c.compilesContextIdGenerator
-  c.inCompilesContext = c.compilesContextIdGenerator
+  c.compilesContextId = c.compilesContextIdGenerator
   # do not halt after first error:
   msgs.gErrorMax = high(int)
 
@@ -1662,7 +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.inCompilesContext = oldCompilesId
+  c.compilesContextId = oldCompilesId
   c.generics = oldGenerics
   c.inGenericContext = oldInGenericContext
   c.inUnrolledContext = oldInUnrolledContext
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index ca9c7c2c0..abc5600c3 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -250,14 +250,14 @@ 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[], c.inCompilesContext)
+  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:
-    entry.compilesId = c.inCompilesContext
+    #if c.compilesContextId == 0:
+    entry.compilesId = c.compilesContextId
     fn.procInstCache.safeAdd(entry)
     c.generics.add(makeInstPair(fn, entry))
     if n.sons[pragmasPos].kind != nkEmpty:
diff --git a/compiler/suggest.nim b/compiler/suggest.nim
index c15a56c54..ed21aa4ea 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/tests/compiles/trecursive_generic_in_compiles.nim b/tests/compiles/trecursive_generic_in_compiles.nim
new file mode 100644
index 000000000..77bf0bb02
--- /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