summary refs log tree commit diff stats
path: root/tests/generics
diff options
context:
space:
mode:
Diffstat (limited to 'tests/generics')
-rw-r--r--tests/generics/m14509.nim16
-rw-r--r--tests/generics/m22373a.nim7
-rw-r--r--tests/generics/m22373b.nim18
-rw-r--r--tests/generics/m3770.nim11
-rw-r--r--tests/generics/mdotlookup.nim16
-rw-r--r--tests/generics/mfriends.nim11
-rw-r--r--tests/generics/module_with_generics.nim2
-rw-r--r--tests/generics/mopensymimport1.nim34
-rw-r--r--tests/generics/mopensymimport2.nim16
-rw-r--r--tests/generics/mtypenodes.nim6
-rw-r--r--tests/generics/muninstantiatedgenericcalls.nim26
-rw-r--r--tests/generics/t12938.nim9
-rw-r--r--tests/generics/t13525.nim8
-rw-r--r--tests/generics/t14193.nim6
-rw-r--r--tests/generics/t14509.nim4
-rw-r--r--tests/generics/t1500.nim8
-rw-r--r--tests/generics/t17509.nim25
-rw-r--r--tests/generics/t18823.nim6
-rw-r--r--tests/generics/t18859.nim17
-rw-r--r--tests/generics/t19848.nim16
-rw-r--r--tests/generics/t20996.nim15
-rw-r--r--tests/generics/t21742.nim10
-rw-r--r--tests/generics/t21760.nim8
-rw-r--r--tests/generics/t21958.nim11
-rw-r--r--tests/generics/t22373.nim16
-rw-r--r--tests/generics/t22826.nim8
-rw-r--r--tests/generics/t23186.nim155
-rw-r--r--tests/generics/t23790.nim14
-rw-r--r--tests/generics/t23853.nim91
-rw-r--r--tests/generics/t23854.nim71
-rw-r--r--tests/generics/t23855.nim61
-rw-r--r--tests/generics/t3770.nim13
-rw-r--r--tests/generics/t4668.nim166
-rw-r--r--tests/generics/t500.nim8
-rw-r--r--tests/generics/t5926.nim22
-rw-r--r--tests/generics/t6060.nim11
-rw-r--r--tests/generics/t6137.nim4
-rw-r--r--tests/generics/t6637.nim9
-rw-r--r--tests/generics/t7446.nim10
-rw-r--r--tests/generics/t7839.nim9
-rw-r--r--tests/generics/taliashijack.nim8
-rw-r--r--tests/generics/tbadcache.nim26
-rw-r--r--tests/generics/tbaddeprecated.nim55
-rw-r--r--tests/generics/tbintree.nim4
-rw-r--r--tests/generics/tbracketinstantiation.nim86
-rw-r--r--tests/generics/tcalltype.nim26
-rw-r--r--tests/generics/tcan.nim2
-rw-r--r--tests/generics/tconstraints.nim16
-rw-r--r--tests/generics/texplicitgeneric2.nim7
-rw-r--r--tests/generics/tfriends.nim11
-rw-r--r--tests/generics/tgeneric0.nim43
-rw-r--r--tests/generics/tgeneric3.nim40
-rw-r--r--tests/generics/tgeneric_recursionlimit.nim123
-rw-r--r--tests/generics/tgenerics_issues.nim126
-rw-r--r--tests/generics/tgenerics_various.nim99
-rw-r--r--tests/generics/tgenericwhen.nim58
-rw-r--r--tests/generics/tgensyminst.nim29
-rw-r--r--tests/generics/timpl_ast.nim80
-rw-r--r--tests/generics/timplicit_and_explicit.nim65
-rw-r--r--tests/generics/timports.nim22
-rw-r--r--tests/generics/tinheritable_importcpp.nim10
-rw-r--r--tests/generics/tmacroinjectedsym.nim186
-rw-r--r--tests/generics/tmacroinjectedsymwarning.nim59
-rw-r--r--tests/generics/tmetafield.nim20
-rw-r--r--tests/generics/tnestedissues.nim24
-rw-r--r--tests/generics/tnestedtemplate.nim9
-rw-r--r--tests/generics/tnullary_generics.nim26
-rw-r--r--tests/generics/tobjecttyperel.nim1
-rw-r--r--tests/generics/topensymimport.nim5
-rw-r--r--tests/generics/toverloading_typedesc.nim5
-rw-r--r--tests/generics/tparam_binding.nim3
-rw-r--r--tests/generics/tparser_generator.nim16
-rw-r--r--tests/generics/tpointerprocs.nim28
-rw-r--r--tests/generics/tprevent_double_bind.nim2
-rw-r--r--tests/generics/trecursivegenerics.nim96
-rw-r--r--tests/generics/treentranttypes.nim19
-rw-r--r--tests/generics/treturn_inference.nim184
-rw-r--r--tests/generics/trtree.nim640
-rw-r--r--tests/generics/tstatic_constrained.nim79
-rw-r--r--tests/generics/tthread_generic.nim2
-rw-r--r--tests/generics/tuninstantiated_failure.nim16
-rw-r--r--tests/generics/tuninstantiatedgenericcalls.nim517
-rw-r--r--tests/generics/twrong_explicit_typeargs.nim16
-rw-r--r--tests/generics/twrong_generic_object.nim4
84 files changed, 3089 insertions, 778 deletions
diff --git a/tests/generics/m14509.nim b/tests/generics/m14509.nim
new file mode 100644
index 000000000..cabc4f308
--- /dev/null
+++ b/tests/generics/m14509.nim
@@ -0,0 +1,16 @@
+import macros
+
+type float32x4 = array[4, float32]
+type float32x8 = array[8, float32]
+
+{.experimental: "dynamicBindSym".}
+macro dispatch(N: static int, T: type SomeNumber): untyped =
+  let BaseT = getTypeInst(T)[1]
+  result = bindSym($BaseT & "x" & $N)
+
+type
+  VecIntrin*[N: static int, T: SomeNumber] = dispatch(N, T)
+
+func `$`*[N, T](vec: VecIntrin[N, T]): string =
+  ## Display a vector
+  $cast[array[N, T]](vec)
diff --git a/tests/generics/m22373a.nim b/tests/generics/m22373a.nim
new file mode 100644
index 000000000..28e087ca6
--- /dev/null
+++ b/tests/generics/m22373a.nim
@@ -0,0 +1,7 @@
+# module a for t22373
+
+# original:
+type LightClientHeader* = object
+
+# simplified:
+type TypeOrTemplate* = object
diff --git a/tests/generics/m22373b.nim b/tests/generics/m22373b.nim
new file mode 100644
index 000000000..67ee4211b
--- /dev/null
+++ b/tests/generics/m22373b.nim
@@ -0,0 +1,18 @@
+# module b for t22373
+
+import m22373a
+
+# original:
+type
+  LightClientDataFork* {.pure.} = enum
+    None = 0,
+    Altair = 1
+template LightClientHeader*(kind: static LightClientDataFork): auto =
+  when kind == LightClientDataFork.Altair:
+    typedesc[m22373a.LightClientHeader]
+  else:
+    static: raiseAssert "Unreachable"
+
+# simplified:
+template TypeOrTemplate*(num: int): untyped =
+  typedesc[m22373a.TypeOrTemplate]
diff --git a/tests/generics/m3770.nim b/tests/generics/m3770.nim
new file mode 100644
index 000000000..7f5714a2b
--- /dev/null
+++ b/tests/generics/m3770.nim
@@ -0,0 +1,11 @@
+type
+  Noice* = object
+    hidden: int
+
+template jjj*: Noice =
+  Noice(hidden: 15)
+
+type Opt* = object
+  o: int
+
+template none*(O: type Opt): Opt = Opt(o: 0)
diff --git a/tests/generics/mdotlookup.nim b/tests/generics/mdotlookup.nim
index 470fa0a51..090b97771 100644
--- a/tests/generics/mdotlookup.nim
+++ b/tests/generics/mdotlookup.nim
@@ -1,9 +1,9 @@
-proc baz(o: any): int = 5 # if bar is exported, it works
+proc baz(o: auto): int = 5 # if bar is exported, it works
 
 type MyObj = object
   x: int
 
-proc foo*(b: any) =
+proc foo*(b: auto) =
   var o: MyObj
   echo b.baz, " ", o.x.baz, " ", b.baz()
 
@@ -14,3 +14,15 @@ var intset = initHashSet[int]()
 proc fn*[T](a: T) =
   if a in intset: echo("true")
   else: echo("false")
+
+import strutils
+
+proc doStrip*[T](a: T): string =
+  result = ($a).strip()
+
+type Foo = int32
+proc baz2*[T](y: int): auto =
+  result = y.Foo
+
+proc set*(x: var int, a, b: string) =
+  x = a.len + b.len
diff --git a/tests/generics/mfriends.nim b/tests/generics/mfriends.nim
new file mode 100644
index 000000000..19672289e
--- /dev/null
+++ b/tests/generics/mfriends.nim
@@ -0,0 +1,11 @@
+
+type
+  TMyObj = object
+    x: int
+
+proc gen*[T](): T =
+  var d: TMyObj
+  # access private field here
+  d.x = 3
+  result = d.x
+
diff --git a/tests/generics/module_with_generics.nim b/tests/generics/module_with_generics.nim
index e801a4790..960a694d7 100644
--- a/tests/generics/module_with_generics.nim
+++ b/tests/generics/module_with_generics.nim
@@ -1,5 +1,5 @@
 type
-  Base[T] = ref object {.inheritable.}
+  Base[T] {.inheritable.} = ref object
     value*: T
 
   Derived[T] = ref object of Base[T]
diff --git a/tests/generics/mopensymimport1.nim b/tests/generics/mopensymimport1.nim
new file mode 100644
index 000000000..912db1302
--- /dev/null
+++ b/tests/generics/mopensymimport1.nim
@@ -0,0 +1,34 @@
+type
+  Result*[T, E] = object
+    when T is void:
+      when E is void:
+        oResultPrivate*: bool
+      else:
+        case oResultPrivate*: bool
+        of false:
+          eResultPrivate*: E
+        of true:
+          discard
+    else:
+      when E is void:
+        case oResultPrivate*: bool
+        of false:
+          discard
+        of true:
+          vResultPrivate*: T
+      else:
+        case oResultPrivate*: bool
+        of false:
+          eResultPrivate*: E
+        of true:
+          vResultPrivate*: T
+
+template valueOr*[T: not void, E](self: Result[T, E], def: untyped): untyped =
+  let s = (self) # TODO avoid copy
+  case s.oResultPrivate
+  of true:
+    s.vResultPrivate
+  of false:
+    when E isnot void:
+      template error: untyped {.used, inject.} = s.eResultPrivate
+    def
diff --git a/tests/generics/mopensymimport2.nim b/tests/generics/mopensymimport2.nim
new file mode 100644
index 000000000..c17aafd00
--- /dev/null
+++ b/tests/generics/mopensymimport2.nim
@@ -0,0 +1,16 @@
+{.experimental: "openSym".}
+
+import mopensymimport1
+
+type Xxx = enum
+  error
+  value
+
+proc f(): Result[int, cstring] =
+  Result[int, cstring](oResultPrivate: false, eResultPrivate: "f")
+
+proc g*(T: type): string =
+  let x = f().valueOr:
+    return $error
+
+  "ok"
diff --git a/tests/generics/mtypenodes.nim b/tests/generics/mtypenodes.nim
new file mode 100644
index 000000000..e1132241b
--- /dev/null
+++ b/tests/generics/mtypenodes.nim
@@ -0,0 +1,6 @@
+# issue #22699
+
+type Private = distinct int
+
+proc chop*[T](x: int): int =
+  cast[int](cast[tuple[field: Private]](x))
diff --git a/tests/generics/muninstantiatedgenericcalls.nim b/tests/generics/muninstantiatedgenericcalls.nim
new file mode 100644
index 000000000..caed07c98
--- /dev/null
+++ b/tests/generics/muninstantiatedgenericcalls.nim
@@ -0,0 +1,26 @@
+import std/bitops
+
+const
+  lengths = block:
+    var v: array[64, int8]
+    for i in 0..<64:
+      v[i] = int8((i + 7) div 7)
+    v
+
+type
+  Leb128* = object
+
+{.push checks: off.}
+func len(T: type Leb128, x: SomeUnsignedInt): int8 =
+  if x == 0: 1
+  else: lengths[fastLog2(x)]
+{.pop.}
+
+# note private to test scoping issue:
+func maxLen(T: type Leb128, I: type): int8 =
+  Leb128.len(I.high)
+
+type
+  Leb128Buf*[T: SomeUnsignedInt] = object
+    data*: array[maxLen(Leb128, T), byte] 
+    len*: int8
diff --git a/tests/generics/t12938.nim b/tests/generics/t12938.nim
new file mode 100644
index 000000000..e09d65c7a
--- /dev/null
+++ b/tests/generics/t12938.nim
@@ -0,0 +1,9 @@
+type
+  ExampleArray[Size, T] = array[Size, T]
+
+var integerArray: ExampleArray[32, int]  # Compiler crash!
+doAssert integerArray.len == 32
+
+const Size = 2
+var integerArray2: ExampleArray[Size, int]
+doAssert integerArray2.len == 2
diff --git a/tests/generics/t13525.nim b/tests/generics/t13525.nim
index d1b8df78c..1fd84852b 100644
--- a/tests/generics/t13525.nim
+++ b/tests/generics/t13525.nim
@@ -1,6 +1,6 @@
 # https://github.com/nim-lang/Nim/issues/13524
 template fun(field): untyped = astToStr(field)
-proc test1(): string = fun(nonexistant1)
-proc test2[T](): string = fun(nonexistant2) # used to cause: Error: undeclared identifier: 'nonexistant2'
-doAssert test1() == "nonexistant1"
-doAssert test2[int]() == "nonexistant2"
+proc test1(): string = fun(nonexistent1)
+proc test2[T](): string = fun(nonexistent2) # used to cause: Error: undeclared identifier: 'nonexistent2'
+doAssert test1() == "nonexistent1"
+doAssert test2[int]() == "nonexistent2"
diff --git a/tests/generics/t14193.nim b/tests/generics/t14193.nim
new file mode 100644
index 000000000..213b1a8e6
--- /dev/null
+++ b/tests/generics/t14193.nim
@@ -0,0 +1,6 @@
+type
+  Task*[N: int] = object
+    env*: array[N, byte]
+
+var task14193: Task[20]
+doAssert task14193.env.len == 20
diff --git a/tests/generics/t14509.nim b/tests/generics/t14509.nim
new file mode 100644
index 000000000..ef3143ee4
--- /dev/null
+++ b/tests/generics/t14509.nim
@@ -0,0 +1,4 @@
+import m14509
+
+var v: VecIntrin[4, float32]
+doAssert $v == "[0.0, 0.0, 0.0, 0.0]"
diff --git a/tests/generics/t1500.nim b/tests/generics/t1500.nim
new file mode 100644
index 000000000..6dd457d33
--- /dev/null
+++ b/tests/generics/t1500.nim
@@ -0,0 +1,8 @@
+#issue 1500
+
+type
+  TFtpBase*[SockType] = object
+    job: TFTPJob[SockType]
+  PFtpBase*[SockType] = ref TFtpBase[SockType]
+  TFtpClient* = TFtpBase[string]
+  TFTPJob[T] = object
\ No newline at end of file
diff --git a/tests/generics/t17509.nim b/tests/generics/t17509.nim
new file mode 100644
index 000000000..89f507577
--- /dev/null
+++ b/tests/generics/t17509.nim
@@ -0,0 +1,25 @@
+type List[O] = object
+  next: ptr List[O]
+
+proc initList[O](l: ptr List[O]) =
+  l[].next = l
+
+type
+  PolytopeVertex[R] = object
+    list: List[PolytopeVertex[R]]
+
+  PolytopeEdge[R] = object
+    list: List[PolytopeEdge[R]]
+
+  Polytope[R] = object
+    vertices: List[PolytopeVertex[R]]
+    edges: List[PolytopeEdge[R]]
+
+var pt: Polytope[float]
+
+static:
+  doAssert pt.vertices.next is (ptr List[PolytopeVertex[float]])
+  doAssert pt.edges.next is (ptr List[PolytopeEdge[float]])
+
+initList(addr pt.vertices)
+initList(addr pt.edges)
\ No newline at end of file
diff --git a/tests/generics/t18823.nim b/tests/generics/t18823.nim
new file mode 100644
index 000000000..94c79aebe
--- /dev/null
+++ b/tests/generics/t18823.nim
@@ -0,0 +1,6 @@
+type BitsRange[T] = range[0..sizeof(T)*8-1]
+
+proc bar[T](a: T; b: BitsRange[T]) =
+  discard
+
+bar(1, 2.Natural)
diff --git a/tests/generics/t18859.nim b/tests/generics/t18859.nim
new file mode 100644
index 000000000..ca6c3d10b
--- /dev/null
+++ b/tests/generics/t18859.nim
@@ -0,0 +1,17 @@
+import macros
+
+macro symFromDesc(T: typedesc): untyped =
+  let typ = getType(T)
+  typ[1]
+
+template produceType(T: typedesc): untyped =
+  type
+    XT = object
+      x: symFromDesc(T)
+
+  XT
+
+type
+  X[T] = produceType(T)
+
+var x: X[int]
diff --git a/tests/generics/t19848.nim b/tests/generics/t19848.nim
new file mode 100644
index 000000000..f80f0e298
--- /dev/null
+++ b/tests/generics/t19848.nim
@@ -0,0 +1,16 @@
+discard """
+  output: '''
+todo
+'''
+"""
+
+type
+  Maybe[T] = object
+  List[T] = object
+
+proc dump[M: Maybe](a: List[M]) =
+  echo "todo"
+
+var a: List[Maybe[int]]
+  
+dump(a)
diff --git a/tests/generics/t20996.nim b/tests/generics/t20996.nim
new file mode 100644
index 000000000..8aa83c4e2
--- /dev/null
+++ b/tests/generics/t20996.nim
@@ -0,0 +1,15 @@
+discard """
+  action: compile
+"""
+
+import std/macros
+
+macro matchMe(x: typed): untyped =
+  discard x.getTypeImpl
+
+type
+  ElementRT = object
+  Element[Z] = ElementRT # this version is needed, even though we don't use it
+
+let ar = ElementRT()
+matchMe(ar)
diff --git a/tests/generics/t21742.nim b/tests/generics/t21742.nim
new file mode 100644
index 000000000..c49c8ee97
--- /dev/null
+++ b/tests/generics/t21742.nim
@@ -0,0 +1,10 @@
+type
+  Foo[T] = object
+    x:T
+  Bar[T,R] = Foo[T]
+  Baz = Bar[int,float]
+
+proc qux[T,R](x: Bar[T,R]) = discard
+
+var b:Baz
+b.qux()
\ No newline at end of file
diff --git a/tests/generics/t21760.nim b/tests/generics/t21760.nim
new file mode 100644
index 000000000..5343279bb
--- /dev/null
+++ b/tests/generics/t21760.nim
@@ -0,0 +1,8 @@
+import std/tables
+
+type Url = object
+
+proc myInit(_: type[Url], params = default(Table[string, string])): Url =
+  discard
+
+discard myInit(Url)
\ No newline at end of file
diff --git a/tests/generics/t21958.nim b/tests/generics/t21958.nim
new file mode 100644
index 000000000..f566b57cb
--- /dev/null
+++ b/tests/generics/t21958.nim
@@ -0,0 +1,11 @@
+discard """
+  action: compile
+"""
+
+type
+  Ct*[T: SomeUnsignedInt] = distinct T
+
+template `shr`*[T: Ct](x: T, y: SomeInteger): T = T(T.T(x) shr y)
+
+var x: Ct[uint64]
+let y {.used.} = x shr 2
\ No newline at end of file
diff --git a/tests/generics/t22373.nim b/tests/generics/t22373.nim
new file mode 100644
index 000000000..ecfaf0f1b
--- /dev/null
+++ b/tests/generics/t22373.nim
@@ -0,0 +1,16 @@
+# issue #22373
+
+import m22373a
+import m22373b
+
+# original:
+template lazy_header(name: untyped): untyped {.dirty.} =
+  var `name _ ptr`: ptr[data_fork.LightClientHeader]  # this data_fork.Foo part seems required to reproduce
+proc createLightClientUpdates(data_fork: static LightClientDataFork) =
+  lazy_header(attested_header)
+createLightClientUpdates(LightClientDataFork.Altair)
+
+# simplified:
+proc generic[T](abc: T) =
+  var x: abc.TypeOrTemplate
+generic(123)
diff --git a/tests/generics/t22826.nim b/tests/generics/t22826.nim
new file mode 100644
index 000000000..914d4243a
--- /dev/null
+++ b/tests/generics/t22826.nim
@@ -0,0 +1,8 @@
+import std/tables
+
+var a: Table[string, float]
+
+type Value*[T] = object
+  table: Table[string, Value[T]]
+
+discard toTable({"a": Value[float]()})
\ No newline at end of file
diff --git a/tests/generics/t23186.nim b/tests/generics/t23186.nim
new file mode 100644
index 000000000..76f38da6b
--- /dev/null
+++ b/tests/generics/t23186.nim
@@ -0,0 +1,155 @@
+# issue #23186
+
+block: # simplified
+  template typedTempl(x: int, body): untyped =
+    body
+  proc generic1[T]() =
+    discard
+  proc generic2[T]() =
+    typedTempl(1):
+      let x = generic1[T]
+  generic2[int]()
+
+import std/macros
+
+when not compiles(len((1, 2))):
+  import std/typetraits
+
+  func len(x: tuple): int =
+    arity(type(x))
+
+block: # full issue example
+  type FieldDescription = object
+    name: NimNode
+  func isTuple(t: NimNode): bool =
+    t.kind == nnkBracketExpr and t[0].kind == nnkSym and eqIdent(t[0], "tuple")
+  proc collectFieldsFromRecList(result: var seq[FieldDescription],
+                                n: NimNode,
+                                parentCaseField: NimNode = nil,
+                                parentCaseBranch: NimNode = nil,
+                                isDiscriminator = false) =
+    case n.kind
+    of nnkRecList:
+      for entry in n:
+        collectFieldsFromRecList result, entry,
+                                parentCaseField, parentCaseBranch
+    of nnkIdentDefs:
+      for i in 0 ..< n.len - 2:
+        var field: FieldDescription
+        field.name = n[i]
+        if field.name.kind == nnkPragmaExpr:
+          field.name = field.name[0]
+        if field.name.kind == nnkPostfix:
+          field.name = field.name[1]
+        result.add field
+    of nnkNilLit, nnkDiscardStmt, nnkCommentStmt, nnkEmpty:
+      discard
+    else:
+      doAssert false, "Unexpected nodes in recordFields:\n" & n.treeRepr
+  proc collectFieldsInHierarchy(result: var seq[FieldDescription],
+                                objectType: NimNode) =
+    var objectType = objectType
+    if objectType.kind == nnkRefTy:
+      objectType = objectType[0]
+    let recList = objectType[2]
+    collectFieldsFromRecList result, recList
+  proc recordFields(typeImpl: NimNode): seq[FieldDescription] =
+    let objectType = case typeImpl.kind
+      of nnkObjectTy: typeImpl
+      of nnkTypeDef: typeImpl[2]
+      else:
+        macros.error("object type expected", typeImpl)
+        return
+    collectFieldsInHierarchy(result, objectType)
+  proc skipPragma(n: NimNode): NimNode =
+    if n.kind == nnkPragmaExpr: n[0]
+    else: n
+  func declval(T: type): T =
+    doAssert false,
+      "declval should be used only in `typeof` expressions and concepts"
+    default(ptr T)[]
+  macro enumAllSerializedFieldsImpl(T: type, body: untyped): untyped =
+    var typeAst = getType(T)[1]
+    var typeImpl: NimNode
+    let isSymbol = not typeAst.isTuple
+    if not isSymbol:
+      typeImpl = typeAst
+    else:
+      typeImpl = getImpl(typeAst)
+    result = newStmtList()
+    var i = 0
+    for field in recordFields(typeImpl):
+      let
+        fieldIdent = field.name
+        realFieldName = newLit($fieldIdent.skipPragma)
+        fieldName = realFieldName
+        fieldIndex = newLit(i)
+      let fieldNameDefs =
+        if isSymbol:
+          quote:
+            const fieldName {.inject, used.} = `fieldName`
+            const realFieldName {.inject, used.} = `realFieldName`
+        else:
+          quote:
+            const fieldName {.inject, used.} = $`fieldIndex`
+            const realFieldName {.inject, used.} = $`fieldIndex`
+            # we can't access .Fieldn, so our helper knows
+            # to parseInt this
+      let field =
+        if isSymbol:
+          quote do: declval(`T`).`fieldIdent`
+        else:
+          quote do: declval(`T`)[`fieldIndex`]
+      result.add quote do:
+        block:
+          `fieldNameDefs`
+          type FieldType {.inject, used.} = type(`field`)
+          `body`
+      i += 1
+  template enumAllSerializedFields(T: type, body): untyped =
+    when T is ref|ptr:
+      type TT = type(default(T)[])
+      enumAllSerializedFieldsImpl(TT, body)
+    else:
+      enumAllSerializedFieldsImpl(T, body)
+  type
+    MemRange = object
+      startAddr: ptr byte
+      length: int
+    SszNavigator[T] = object
+      m: MemRange
+  func sszMount(data: openArray[byte], T: type): SszNavigator[T] =
+    let startAddr = unsafeAddr data[0]
+    SszNavigator[T](m: MemRange(startAddr: startAddr, length: data.len))
+  func sszMount(data: openArray[char], T: type): SszNavigator[T] =
+    let startAddr = cast[ptr byte](unsafeAddr data[0])
+    SszNavigator[T](m: MemRange(startAddr: startAddr, length: data.len))
+  template sszMount(data: MemRange, T: type): SszNavigator[T] =
+    SszNavigator[T](m: data)
+  func navigateToField[T](
+      n: SszNavigator[T],
+      FieldType: type): SszNavigator[FieldType] =
+    default(SszNavigator[FieldType])
+  type
+    FieldInfo = ref object
+      navigator: proc (m: MemRange): MemRange {.
+        gcsafe, noSideEffect, raises: [IOError] .}
+  func fieldNavigatorImpl[RecordType; FieldType; fieldName: static string](
+      m: MemRange): MemRange =
+    var typedNavigator = sszMount(m, RecordType)
+    discard navigateToField(typedNavigator, FieldType)
+    default(MemRange)
+  func genTypeInfo(T: type) =
+    when T is object:
+      enumAllSerializedFields(T):
+        discard FieldInfo(navigator: fieldNavigatorImpl[T, FieldType, fieldName])
+  type
+    Foo = object
+      bar: Bar
+    BarList = seq[uint64]
+    Bar = object
+      b: BarList
+      baz: Baz
+    Baz = object
+      i: uint64
+  genTypeInfo(Foo)
diff --git a/tests/generics/t23790.nim b/tests/generics/t23790.nim
new file mode 100644
index 000000000..9ac0df6a1
--- /dev/null
+++ b/tests/generics/t23790.nim
@@ -0,0 +1,14 @@
+# bug #23790
+
+discard compiles($default(seq[seq[ref int]]))
+discard compiles($default(seq[seq[ref uint]]))
+discard compiles($default(seq[seq[ref int8]]))
+discard compiles($default(seq[seq[ref uint8]]))
+discard compiles($default(seq[seq[ref int16]]))
+discard compiles($default(seq[seq[ref uint16]]))
+discard compiles($default(seq[seq[ref int32]]))
+discard compiles($default(seq[seq[ref uint32]]))
+discard compiles($default(seq[seq[ref int64]]))
+discard compiles($default(seq[seq[ref uint64]]))
+proc s(_: int | string) = discard
+s(0)
diff --git a/tests/generics/t23853.nim b/tests/generics/t23853.nim
new file mode 100644
index 000000000..bc9514a53
--- /dev/null
+++ b/tests/generics/t23853.nim
@@ -0,0 +1,91 @@
+# issue #23853
+
+block simplified:
+  type QuadraticExt[F] = object
+    coords: array[2, F]
+  template Name(E: type QuadraticExt): int = 123
+  template getBigInt(Name: static int): untyped = int
+  type Foo[GT] = object
+    a: getBigInt(GT.Name)
+  var x: Foo[QuadraticExt[int]]
+  
+import std/macros
+
+type
+  Algebra* = enum
+    BN254_Snarks
+    BLS12_381
+
+  Fp*[Name: static Algebra] = object
+    limbs*: array[4, uint64]
+
+  QuadraticExt*[F] = object
+    ## Quadratic Extension field
+    coords*: array[2, F]
+
+  CubicExt*[F] = object
+    ## Cubic Extension field
+    coords*: array[3, F]
+
+  ExtensionField*[F] = QuadraticExt[F] or CubicExt[F]
+
+  Fp2*[Name: static Algebra] =
+    QuadraticExt[Fp[Name]]
+
+  Fp4*[Name: static Algebra] =
+    QuadraticExt[Fp2[Name]]
+
+  Fp6*[Name: static Algebra] =
+    CubicExt[Fp2[Name]]
+
+  Fp12*[Name: static Algebra] =
+    CubicExt[Fp4[Name]]
+    # QuadraticExt[Fp6[Name]]
+
+template Name*(E: type ExtensionField): Algebra =
+  E.F.Name
+
+const BLS12_381_Order = [uint64 0x1, 0x2, 0x3, 0x4]
+const BLS12_381_Modulus = [uint64 0x5, 0x6, 0x7, 0x8]
+
+
+{.experimental: "dynamicBindSym".}
+
+macro baseFieldModulus*(Name: static Algebra): untyped =
+  result = bindSym($Name & "_Modulus")
+
+macro scalarFieldModulus*(Name: static Algebra): untyped =
+  result = bindSym($Name & "_Order")
+
+type FieldKind* = enum
+  kBaseField
+  kScalarField
+
+template getBigInt*(Name: static Algebra, kind: static FieldKind): untyped =
+  # Workaround:
+  # in `ptr UncheckedArray[BigInt[EC.getScalarField().bits()]]
+  # EC.getScalarField is not accepted by the compiler
+  #
+  # and `ptr UncheckedArray[BigInt[Fr[EC.F.Name].bits]]` gets undeclared field: 'Name'
+  #
+  # but `ptr UncheckedArray[getBigInt(EC.getName(), kScalarField)]` works fine
+  when kind == kBaseField:
+    Name.baseFieldModulus().typeof()
+  else:
+    Name.scalarFieldModulus().typeof()
+
+# ------------------------------------------------------------------------------
+
+type BenchMultiexpContext*[GT] = object
+  elems: seq[GT]
+  exponents: seq[getBigInt(GT.Name, kScalarField)]
+
+proc createBenchMultiExpContext*(GT: typedesc, inputSizes: openArray[int]): BenchMultiexpContext[GT] =
+  discard
+
+# ------------------------------------------------------------------------------
+
+proc main() =
+  let ctx = createBenchMultiExpContext(Fp12[BLS12_381], [2, 4, 8, 16])
+
+main()
diff --git a/tests/generics/t23854.nim b/tests/generics/t23854.nim
new file mode 100644
index 000000000..f1175c8b2
--- /dev/null
+++ b/tests/generics/t23854.nim
@@ -0,0 +1,71 @@
+# issue #23854, not entirely fixed
+
+import std/bitops
+
+const WordBitWidth = sizeof(pointer) * 8
+
+func wordsRequired*(bits: int): int {.inline.} =
+  const divShiftor = fastLog2(uint32(WordBitWidth))
+  result = (bits + WordBitWidth - 1) shr divShiftor
+
+type
+  Algebra* = enum
+    BLS12_381
+
+  BigInt*[bits: static int] = object
+    limbs*: array[wordsRequired(bits), uint]
+
+  Fr*[Name: static Algebra] = object
+    residue_form*: BigInt[255]
+
+  Fp*[Name: static Algebra] = object
+    residue_form*: BigInt[381]
+
+  FF*[Name: static Algebra] = Fp[Name] or Fr[Name]
+
+template getBigInt*[Name: static Algebra](T: type FF[Name]): untyped =
+  ## Get the underlying BigInt type.
+  typeof(default(T).residue_form)
+
+type
+  EC_ShortW_Aff*[F] = object
+    ## Elliptic curve point for a curve in Short Weierstrass form
+    ##   y² = x³ + a x + b
+    ##
+    ## over a field F
+    x*, y*: F
+
+type FieldKind* = enum
+  kBaseField
+  kScalarField
+
+func bits*[Name: static Algebra](T: type FF[Name]): static int =
+  T.getBigInt().bits
+
+template getScalarField*(EC: type EC_ShortW_Aff): untyped =
+  Fr[EC.F.Name]
+
+# ------------------------------------------------------------------------------
+
+type
+  ECFFT_Descriptor*[EC] = object
+    ## Metadata for FFT on Elliptic Curve
+    order*: int
+    rootsOfUnity1*: ptr UncheckedArray[BigInt[EC.getScalarField().bits()]]  # Error: in expression 'EC.getScalarField()': identifier expected, but found 'EC.getScalarField'
+    rootsOfUnity2*: ptr UncheckedArray[BigInt[getScalarField(EC).bits()]] # Compiler SIGSEGV: Illegal Storage Access
+
+func new*(T: type ECFFT_Descriptor): T =
+  discard
+
+# ------------------------------------------------------------------------------
+
+template getBits[bits: static int](x: ptr UncheckedArray[BigInt[bits]]): int = bits
+
+proc main() =
+  let ctx = ECFFT_Descriptor[EC_ShortW_Aff[Fp[BLS12_381]]].new()
+  doAssert getBits(ctx.rootsOfUnity1) == 255
+  doAssert getBits(ctx.rootsOfUnity2) == 255
+  doAssert ctx.rootsOfUnity1[0].limbs.len == wordsRequired(255)
+  doAssert ctx.rootsOfUnity2[0].limbs.len == wordsRequired(255)
+
+main()
diff --git a/tests/generics/t23855.nim b/tests/generics/t23855.nim
new file mode 100644
index 000000000..da8135a98
--- /dev/null
+++ b/tests/generics/t23855.nim
@@ -0,0 +1,61 @@
+# issue #23855, not entirely fixed
+
+import std/bitops
+
+const WordBitWidth = sizeof(pointer) * 8
+
+func wordsRequired*(bits: int): int {.inline.} =
+  const divShiftor = fastLog2(uint32(WordBitWidth))
+  result = (bits + WordBitWidth - 1) shr divShiftor
+
+type
+  Algebra* = enum
+    BLS12_381
+
+  BigInt*[bits: static int] = object
+    limbs*: array[wordsRequired(bits), uint]
+
+  Fr*[Name: static Algebra] = object
+    residue_form*: BigInt[255]
+
+  Fp*[Name: static Algebra] = object
+    residue_form*: BigInt[381]
+
+  FF*[Name: static Algebra] = Fp[Name] or Fr[Name]
+
+template getBigInt*[Name: static Algebra](T: type FF[Name]): untyped =
+  ## Get the underlying BigInt type.
+  typeof(default(T).residue_form)
+
+type
+  EC_ShortW_Aff*[F] = object
+    ## Elliptic curve point for a curve in Short Weierstrass form
+    ##   y² = x³ + a x + b
+    ##
+    ## over a field F
+    x*, y*: F
+
+func bits*[Name: static Algebra](T: type FF[Name]): static int =
+  T.getBigInt().bits
+
+# ------------------------------------------------------------------------------
+
+type
+  ECFFT_Descriptor*[EC] = object
+    ## Metadata for FFT on Elliptic Curve
+    order*: int
+    rootsOfUnity*: ptr UncheckedArray[BigInt[Fr[EC.F.Name].bits()]] # Undeclared identifier `Name`
+
+func new*(T: type ECFFT_Descriptor): T =
+  discard
+
+# ------------------------------------------------------------------------------
+
+template getBits[bits: static int](x: ptr UncheckedArray[BigInt[bits]]): int = bits
+
+proc main() =
+  let ctx = ECFFT_Descriptor[EC_ShortW_Aff[Fp[BLS12_381]]].new()
+  doAssert getBits(ctx.rootsOfUnity) == 255
+  doAssert ctx.rootsOfUnity[0].limbs.len == wordsRequired(255)
+
+main()
diff --git a/tests/generics/t3770.nim b/tests/generics/t3770.nim
new file mode 100644
index 000000000..ffccbeeb5
--- /dev/null
+++ b/tests/generics/t3770.nim
@@ -0,0 +1,13 @@
+# bug #3770
+import m3770
+
+doAssert $jjj() == "(hidden: 15)"  # works
+
+proc someGeneric(_: type) =
+  doAssert $jjj() == "(hidden: 15)" # fails: "Error: the field 'hidden' is not accessible."
+
+someGeneric(int)
+
+# bug #20900
+proc c(y: int | int, w: Opt = Opt.none) = discard
+c(0)
diff --git a/tests/generics/t4668.nim b/tests/generics/t4668.nim
new file mode 100644
index 000000000..bd44cc975
--- /dev/null
+++ b/tests/generics/t4668.nim
@@ -0,0 +1,166 @@
+discard """
+  output: '''
+foo1
+foo2
+'''
+"""
+
+block:
+  type
+    FooObj[T] = object
+      v: T
+    Foo1[T] = FooObj[T]
+    Foo2 = FooObj
+
+  proc foo1(x: Foo1) = echo "foo1"
+  proc foo2(x: Foo2) = echo "foo2"
+
+  var x: FooObj[float]
+  foo1(x)  # works
+  foo2(x)  # works
+
+block:
+  type
+    FooObj[T] = T
+    Foo1[T] = FooObj[T]
+    Foo2 = FooObj
+    Foo3 = Foo1
+    Foo4x = FooObj[SomeInteger]
+    Foo4 = FooObj[SomeFloat]
+    Foo5x = Foo1[SomeInteger]
+    Foo5 = Foo1[SomeFloat]
+
+  proc foo0(x: FooObj): int = 0
+  proc foo1(x: Foo1): int = 1
+  proc foo2(x: Foo2): int = 2
+  proc foo3(x: Foo3): int = 3
+  proc foo4(x: Foo4x): int = 40
+  proc foo4(x: Foo4): int = 4
+  proc foo5(x: Foo5x): int = 50
+  proc foo5(x: Foo5): int = 5
+
+  block:
+    var x: FooObj[float]
+    doAssert(foo0(x) == 0)
+    doAssert(foo1(x) == 1)
+    doAssert(foo2(x) == 2)
+    doAssert(foo3(x) == 3)
+    doAssert(foo4(x) == 4)
+    doAssert(foo5(x) == 5)
+
+  block:
+    var x: Foo1[float]
+    doAssert(foo0(x) == 0)
+    doAssert(foo1(x) == 1)
+    doAssert(foo2(x) == 2)
+    doAssert(foo3(x) == 3)
+    doAssert(foo4(x) == 4)
+    doAssert(foo5(x) == 5)
+
+  block:
+    var x: Foo2[float]
+    doAssert(foo0(x) == 0)
+    doAssert(foo1(x) == 1)
+    doAssert(foo2(x) == 2)
+    doAssert(foo3(x) == 3)
+    doAssert(foo4(x) == 4)
+    doAssert(foo5(x) == 5)
+
+block:
+  type
+    FooObj[T,U] = object
+      x: T
+      y: U
+    Foo1[U,T] = FooObj[T,U]
+    Foo2 = FooObj
+    Foo3 = Foo1
+    Foo4x = FooObj[SomeInteger,SomeInteger]
+    Foo4y = FooObj[SomeInteger,SomeFloat]
+    Foo4z = FooObj[SomeFloat,SomeFloat]
+    Foo4 = FooObj[SomeFloat,SomeInteger]
+    Foo5x = Foo1[SomeInteger,SomeInteger]
+    Foo5y = Foo1[SomeFloat,SomeInteger]
+    Foo5z = Foo1[SomeFloat,SomeFloat]
+    Foo5 = Foo1[SomeInteger,SomeFloat]
+
+  proc foo0(x: FooObj): int = 0
+  proc foo1(x: Foo1): int = 1
+  proc foo2(x: Foo2): int = 2
+  proc foo3(x: Foo3): int = 3
+  proc foo4(x: Foo4x): int = 40
+  proc foo4(x: Foo4y): int = 41
+  proc foo4(x: Foo4z): int = 42
+  proc foo4(x: Foo4): int = 4
+  proc foo5(x: Foo5x): int = 50
+  proc foo5(x: Foo5y): int = 51
+  proc foo5(x: Foo5z): int = 52
+  proc foo5(x: Foo5): int = 5
+
+  block:
+    var x: FooObj[float,int]
+    doAssert(foo0(x) == 0)
+    doAssert(foo1(x) == 1)
+    doAssert(foo2(x) == 2)
+    doAssert(foo3(x) == 3)
+    doAssert(foo4(x) == 4)
+    doAssert(foo5(x) == 5)
+
+  block:
+    var x: Foo1[int,float]
+    doAssert(foo0(x) == 0)
+    doAssert(foo1(x) == 1)
+    doAssert(foo2(x) == 2)
+    doAssert(foo3(x) == 3)
+    doAssert(foo4(x) == 4)
+    doAssert(foo5(x) == 5)
+
+block:
+  type
+    FooObj[T] = object of RootObj
+      v: T
+    FooObj2[T] = object of FooObj[T]
+    Foo1[T] = FooObj[T]
+    Foo2 = FooObj
+    Foo3 = Foo1
+    Foo4x = FooObj[SomeInteger]
+    Foo4 = FooObj[SomeFloat]
+    Foo5x = Foo1[SomeInteger]
+    Foo5 = Foo1[SomeFloat]
+
+  proc foo0(x: FooObj): int = 0
+  proc foo1(x: Foo1): int = 1
+  proc foo2(x: Foo2): int = 2
+  proc foo3(x: Foo3): int = 3
+  proc foo4(x: Foo4x): int = 40
+  proc foo4(x: Foo4): int = 4
+  proc foo5(x: Foo5x): int = 50
+  proc foo5(x: Foo5): int = 5
+
+  block:
+    var x: FooObj[float]
+    doAssert(foo0(x) == 0)
+    doAssert(foo1(x) == 1)
+    doAssert(foo2(x) == 2)
+    doAssert(foo3(x) == 3)
+    doAssert(foo4(x) == 4)
+    doAssert(foo5(x) == 5)
+
+  block:
+    var x: Foo1[float]
+    doAssert(foo0(x) == 0)
+    doAssert(foo1(x) == 1)
+    doAssert(foo2(x) == 2)
+    doAssert(foo3(x) == 3)
+    doAssert(foo4(x) == 4)
+    doAssert(foo5(x) == 5)
+
+  #[ XXX These still fail
+  block:
+    var x: FooObj2[float]
+    doAssert(foo0(x) == 0)
+    doAssert(foo1(x) == 1)
+    doAssert(foo2(x) == 2)
+    doAssert(foo3(x) == 3)
+    doAssert(foo4(x) == 4)
+    doAssert(foo5(x) == 5)
+  ]#
diff --git a/tests/generics/t500.nim b/tests/generics/t500.nim
new file mode 100644
index 000000000..2486359aa
--- /dev/null
+++ b/tests/generics/t500.nim
@@ -0,0 +1,8 @@
+discard """
+action: compile
+"""
+
+type
+  TTest = tuple[x: range[0..80], y: range[0..25]]
+
+let x: TTest = (2, 23)
diff --git a/tests/generics/t5926.nim b/tests/generics/t5926.nim
new file mode 100644
index 000000000..bb14c3af5
--- /dev/null
+++ b/tests/generics/t5926.nim
@@ -0,0 +1,22 @@
+discard """
+action: compile
+"""
+
+type
+  SomeObj[T] = object
+
+template useSomeObj[T]() =
+  var retObj: SomeObj[T]
+
+useSomeObj[void]()
+useSomeObj[int]()
+
+
+type
+  Data*[T] = object
+    x: T
+
+template test*[T](xxx: T) =
+  let data = Data[T](x: xxx)
+
+test(1)
diff --git a/tests/generics/t6060.nim b/tests/generics/t6060.nim
new file mode 100644
index 000000000..6b1856f1c
--- /dev/null
+++ b/tests/generics/t6060.nim
@@ -0,0 +1,11 @@
+import tables
+
+type MyTab[A,B] = distinct TableRef[A,B]
+
+proc `$`[A,B](t: MyTab[A,B]): string =
+  "My special table " & $TableRef[A,B](t)
+
+proc create[A,B](): MyTab[A,B] = MyTab(newTable[A,B]())
+
+var a = create[int,int]()
+doAssert $a == "My special table {:}"
diff --git a/tests/generics/t6137.nim b/tests/generics/t6137.nim
index 991f39dd1..fb7db22f8 100644
--- a/tests/generics/t6137.nim
+++ b/tests/generics/t6137.nim
@@ -1,6 +1,6 @@
 discard """
-  errormsg: "\'vectFunc\' doesn't have a concrete type, due to unspecified generic parameters."
-  line: 28
+  errormsg: "cannot instantiate: 'T'"
+  line: 19
 """
 
 type
diff --git a/tests/generics/t6637.nim b/tests/generics/t6637.nim
new file mode 100644
index 000000000..dd4f339a2
--- /dev/null
+++ b/tests/generics/t6637.nim
@@ -0,0 +1,9 @@
+
+type
+  Grid2D*[I: SomeInteger, w, h: static[I], T] = object
+    grid: array[w, array[h, T]]
+  Grid2DIns = Grid2D[int, 2, 3, uint8]
+
+let a = Grid2DIns()
+doAssert a.grid.len == 2
+doAssert a.grid[0].len == 3
diff --git a/tests/generics/t7446.nim b/tests/generics/t7446.nim
new file mode 100644
index 000000000..71aa8f0e8
--- /dev/null
+++ b/tests/generics/t7446.nim
@@ -0,0 +1,10 @@
+proc foo(x: Natural or SomeUnsignedInt):int = 
+  when x is int:
+    result = 1
+  else:
+    result = 2
+let a = 10
+doAssert foo(a) == 1
+
+let b = 10'u8
+doAssert foo(b) == 2
\ No newline at end of file
diff --git a/tests/generics/t7839.nim b/tests/generics/t7839.nim
new file mode 100644
index 000000000..4d17b438b
--- /dev/null
+++ b/tests/generics/t7839.nim
@@ -0,0 +1,9 @@
+type A[I: SomeOrdinal, E] = tuple # same for object
+  length: int
+
+doAssert A.sizeof == sizeof(int) # works without the following proc
+
+proc newA*[I: SomeOrdinal, E](): A[I, E] = # works without `SomeOrdinal`
+  discard
+
+discard newA[uint8, int]()
diff --git a/tests/generics/taliashijack.nim b/tests/generics/taliashijack.nim
new file mode 100644
index 000000000..fdebadded
--- /dev/null
+++ b/tests/generics/taliashijack.nim
@@ -0,0 +1,8 @@
+# issue #23977
+
+type Foo[T] = int
+
+proc foo(T: typedesc) =
+  var a: T
+
+foo(int)
diff --git a/tests/generics/tbadcache.nim b/tests/generics/tbadcache.nim
new file mode 100644
index 000000000..33e65be3a
--- /dev/null
+++ b/tests/generics/tbadcache.nim
@@ -0,0 +1,26 @@
+# issue #16128
+
+import std/[tables, hashes]
+
+type
+  NodeId*[L] = object
+    isSource: bool
+    index: Table[NodeId[L], seq[NodeId[L]]]
+
+func hash*[L](id: NodeId[L]): Hash = discard
+func `==`[L](a, b: NodeId[L]): bool = discard
+
+proc makeIndex*[T, L](tree: T) =
+  var parent = NodeId[L]()
+  var tmp: Table[NodeId[L], seq[NodeId[L]]]
+  tmp[parent] = @[parent]
+
+proc simpleTreeDiff*[T, L](source, target: T) =
+  # Swapping these two lines makes error disappear
+  var m: Table[NodeId[L], NodeId[L]]
+  makeIndex[T, L](target)
+
+var tmp: Table[string, seq[string]] # removing this forward declaration also removes error
+
+proc diff(x1, x2: string): auto =
+  simpleTreeDiff[int, string](12, 12)
diff --git a/tests/generics/tbaddeprecated.nim b/tests/generics/tbaddeprecated.nim
new file mode 100644
index 000000000..335234a25
--- /dev/null
+++ b/tests/generics/tbaddeprecated.nim
@@ -0,0 +1,55 @@
+discard """
+  output: '''
+not deprecated
+not deprecated
+not error
+not error
+'''
+"""
+
+# issue #21724
+
+block: # deprecated
+  {.push warningAsError[Deprecated]: on.}
+  type
+    SomeObj = object
+      hey: bool
+  proc hey() {.deprecated: "Shouldn't use this".} = echo "hey"
+  proc gen(o: auto) =
+    doAssert not compiles(o.hey())
+    if o.hey:
+      echo "not deprecated"
+  gen(SomeObj(hey: true))
+  doAssert not (compiles do:
+    proc hey(o: SomeObj) {.deprecated: "Shouldn't use this".} = echo "hey"
+    proc gen2(o: auto) =
+      if o.hey():
+        echo "not deprecated"
+    gen2(SomeObj(hey: true)))
+  proc hey(o: SomeObj) {.deprecated: "Shouldn't use this".} = echo "hey"
+  proc gen3(o: auto) =
+    if o.hey:
+      echo "not deprecated"
+  gen3(SomeObj(hey: true))
+  {.pop.}
+block: # error
+  type
+    SomeObj = object
+      hey: bool
+  proc hey() {.error: "Shouldn't use this".} = echo "hey"
+  proc gen(o: auto) =
+    doAssert not compiles(o.hey())
+    if o.hey:
+      echo "not error"
+  gen(SomeObj(hey: true))
+  doAssert not (compiles do:
+    proc hey(o: SomeObj) {.error: "Shouldn't use this".} = echo "hey"
+    proc gen2(o: auto) =
+      if o.hey():
+        echo "not error"
+    gen2(SomeObj(hey: true)))
+  proc hey(o: SomeObj) {.error: "Shouldn't use this".} = echo "hey"
+  proc gen3(o: auto) =
+    if o.hey:
+      echo "not error"
+  gen3(SomeObj(hey: true))
diff --git a/tests/generics/tbintree.nim b/tests/generics/tbintree.nim
index a1a13c7b5..83f14406b 100644
--- a/tests/generics/tbintree.nim
+++ b/tests/generics/tbintree.nim
@@ -55,8 +55,8 @@ proc find*[Ty2](b: PBinaryTree[Ty2], data: Ty2): bool =
 
 iterator preorder*[T](root: PBinaryTree[T]): T =
   # Preorder traversal of a binary tree.
-  # Since recursive iterators are not yet implemented,
-  # this uses an explicit stack:
+  # This uses an explicit stack (which is more efficient than
+  # a recursive iterator factory).
   var stack: seq[PBinaryTree[T]] = @[root]
   while stack.len > 0:
     var n = stack.pop()
diff --git a/tests/generics/tbracketinstantiation.nim b/tests/generics/tbracketinstantiation.nim
new file mode 100644
index 000000000..22a86af4c
--- /dev/null
+++ b/tests/generics/tbracketinstantiation.nim
@@ -0,0 +1,86 @@
+discard """
+  nimout: '''
+type
+  Bob = object
+type
+  Another = object
+'''
+"""
+
+block: # issue #22645
+  type
+    Opt[T] = object
+    FutureBase = ref object of RootObj
+    Future[T] = ref object of FutureBase ## Typed future.
+      internalValue: T ## Stored value
+  template err[T](E: type Opt[T]): E = E()
+  proc works(): Future[Opt[int]] {.stackTrace: off, gcsafe, raises: [].} =
+    var chronosInternalRetFuture: FutureBase
+    template result(): untyped {.used.} =
+      Future[Opt[int]](chronosInternalRetFuture).internalValue
+    result = err(type(result))
+  proc breaks(): Future[Opt[int]] {.stackTrace: off, gcsafe, raises: [].} =
+    var chronosInternalRetFuture: FutureBase
+    template result(): untyped {.used.} =
+      cast[Future[Opt[int]]](chronosInternalRetFuture).internalValue
+    result = err(type(result))
+
+import macros
+
+block: # issue #16118
+  macro thing(name: static[string]) =
+    result = newStmtList(
+      nnkTypeSection.newTree(
+        nnkTypeDef.newTree(
+          ident(name),
+          newEmptyNode(),
+          nnkObjectTy.newTree(
+            newEmptyNode(),
+            newEmptyNode(),
+            nnkRecList.newTree()))))
+  template foo(name: string): untyped =
+    thing(name)
+  expandMacros:
+    foo("Bob")
+  block:
+    expandMacros:
+      foo("Another")
+
+block: # issue #19670
+  type
+    Past[Z] = object
+    OpenObject = object
+
+  macro rewriter(prc: untyped): untyped =
+    prc.body.add(nnkCall.newTree(
+      prc.params[0]
+    ))
+    prc
+    
+  macro macroAsync(name, restype: untyped): untyped =
+    quote do:
+      proc `name`(): Past[seq[`restype`]] {.rewriter.} = discard
+      
+  macroAsync(testMacro, OpenObject)
+
+import asyncdispatch
+
+block: # issue #11838 long
+  type
+    R[P] = object
+      updates: seq[P]
+    D[T, P] = ref object
+      ps: seq[P]
+      t: T
+  proc newD[T, P](ps: seq[P], t: T): D[T, P] =
+    D[T, P](ps: ps, t: t)
+  proc loop[T, P](d: D[T, P]) =
+    var results = newSeq[Future[R[P]]](10)
+  let d = newD[string, int](@[1], "")
+  d.loop()
+
+block: # issue #11838 minimal
+  type R[T] = object
+  proc loop[T]() =
+    discard newSeq[R[R[T]]]()
+  loop[int]()
diff --git a/tests/generics/tcalltype.nim b/tests/generics/tcalltype.nim
new file mode 100644
index 000000000..cba691f77
--- /dev/null
+++ b/tests/generics/tcalltype.nim
@@ -0,0 +1,26 @@
+discard """
+  joinable: false # breaks everything because of #23977
+"""
+
+# issue #23406
+
+template helper(_: untyped): untyped =
+  int
+
+type # Each of them should always be `int`.
+  GenA[T] = helper int
+  GenB[T] = helper(int)
+  GenC[T] = helper helper(int)
+
+block:
+  template helper(_: untyped): untyped =
+    float
+
+  type
+    A = GenA[int]
+    B = GenB[int]
+    C = GenC[int]
+
+  assert A is int # OK.
+  assert B is int # Fails; it is `float`!
+  assert C is int # OK.
diff --git a/tests/generics/tcan.nim b/tests/generics/tcan.nim
index eea69cdb7..bbefa7233 100644
--- a/tests/generics/tcan.nim
+++ b/tests/generics/tcan.nim
@@ -31,7 +31,7 @@ block tinherit:
 
 block tspecialise:
   type
-    TGen[T] = object {.inheritable.}
+    TGen[T] {.inheritable.} = object
     TSpef = object of TGen[string]
 
 
diff --git a/tests/generics/tconstraints.nim b/tests/generics/tconstraints.nim
new file mode 100644
index 000000000..3ca01cfd5
--- /dev/null
+++ b/tests/generics/tconstraints.nim
@@ -0,0 +1,16 @@
+discard """
+  errormsg: "type mismatch: got <int literal(232)>"
+  line: 16
+"""
+
+proc myGenericProc[T: object|tuple|ptr|ref|distinct](x: T): string =
+  result = $x
+
+type
+  TMyObj = tuple[x, y: int]
+
+var
+  x: TMyObj
+
+assert myGenericProc(x) == "(x: 0, y: 0)"
+assert myGenericProc(232) == "232"
diff --git a/tests/generics/texplicitgeneric2.nim b/tests/generics/texplicitgeneric2.nim
index 573b10ae8..37b133130 100644
--- a/tests/generics/texplicitgeneric2.nim
+++ b/tests/generics/texplicitgeneric2.nim
@@ -1,6 +1,5 @@
 discard """
   output: "Key: 12 value: 12Key: 13 value: 13 Key: A value: 12 Key: B value: 13"
-  disabled: true
 """
 
 # test explicit type instantiation
@@ -14,12 +13,12 @@ proc newDict*[TKey, TValue](): PDict[TKey, TValue] =
   new(result)
   result.data = @[]
 
-proc add*(d: PDict, k: TKey, v: TValue) =
+proc add*(d: PDict, k: d.TKey, v: d.TValue) =
   d.data.add((k, v))
 
 
-#iterator items*(d: PDict): tuple[k: TKey, v: TValue] =
-#  for k, v in items(d.data): yield (k, v)
+iterator items*(d: PDict): tuple[k: d.TKey, v: d.TValue] =
+  for k, v in items(d.data): yield (k, v)
 
 var d = newDict[int, string]()
 d.add(12, "12")
diff --git a/tests/generics/tfriends.nim b/tests/generics/tfriends.nim
new file mode 100644
index 000000000..1e70d50a5
--- /dev/null
+++ b/tests/generics/tfriends.nim
@@ -0,0 +1,11 @@
+discard """
+  output: "3"
+"""
+
+# Tests that a generic instantiation from a different module may access
+# private object fields:
+
+import mfriends
+
+echo gen[int]()
+
diff --git a/tests/generics/tgeneric0.nim b/tests/generics/tgeneric0.nim
index 44c34917d..16a148f7b 100644
--- a/tests/generics/tgeneric0.nim
+++ b/tests/generics/tgeneric0.nim
@@ -9,7 +9,7 @@ float32
 """
 
 
-import tables
+import std/tables
 
 
 block tgeneric0:
@@ -153,3 +153,44 @@ proc unzip*[T,U](xs: List[tuple[t: T, u: U]]): (List[T], List[U]) = discard
 
 proc unzip2*[T,U](xs: List[(T,U)]): (List[T], List[U]) = discard
 
+type
+  AtomicType = pointer|ptr|int
+
+  Atomic[T: AtomicType] = distinct T
+
+  Block[T: AtomicType] = object
+
+  AtomicContainer[T: AtomicType] = object
+    b: Atomic[ptr Block[T]]
+
+# bug #8295
+var x = AtomicContainer[int]()
+doAssert (ptr Block[int])(x.b) == nil
+
+
+# bug #23233
+type
+  JsonObjectType*[T: string or uint64] = Table[string, JsonValueRef[T]]
+
+  JsonValueRef*[T: string or uint64] = object
+    objVal*: JsonObjectType[T]
+
+proc scanValue[K](val: var K) =
+  var map: JsonObjectType[K.T]
+  var newVal: K
+  map["one"] = newVal
+
+block:
+  var a: JsonValueRef[uint64]
+  scanValue(a)
+
+  var b: JsonValueRef[string]
+  scanValue(b)
+
+block: # bug #21347
+  type K[T] = object
+  template s[T]() = discard
+  proc b1(n: bool | bool) = s[K[K[int]]]()
+  proc b2(n: bool) =        s[K[K[int]]]()
+  b1(false)   # Error: 's' has unspecified generic parameters
+  b2(false)   # Builds, on its own
diff --git a/tests/generics/tgeneric3.nim b/tests/generics/tgeneric3.nim
index 4cb12f91b..29a73afc6 100644
--- a/tests/generics/tgeneric3.nim
+++ b/tests/generics/tgeneric3.nim
@@ -1,9 +1,9 @@
 discard """
 output: '''
 312
-1000000
-1000000
-500000
+1000
+1000
+500
 0
 '''
 """
@@ -12,7 +12,7 @@ import strutils
 
 type
   PNode[T,D] = ref TNode[T,D]
-  TItem {.acyclic, pure, final, shallow.} [T,D] = object
+  TItem[T,D] {.acyclic, pure, final, shallow.} = object
         key: T
         value: D
         node: PNode[T,D]
@@ -20,7 +20,7 @@ type
           val_set: bool
 
   TItems[T,D] = seq[ref TItem[T,D]]
-  TNode {.acyclic, pure, final, shallow.} [T,D] = object
+  TNode[T,D] {.acyclic, pure, final, shallow.} = object
         slots: TItems[T,D]
         left: PNode[T,D]
         count: int32
@@ -243,7 +243,7 @@ proc InsertItem[T,D](APath: RPath[T,D], ANode:PNode[T,D], Akey: T, Avalue: D) =
   of cLenCenter: setLen(APath.Nd.slots, cLen4)
   of cLen4: setLen(APath.Nd.slots, cLenMax)
   else: discard
-  for i in countdown(APath.Nd.count.int - 1, x + 1): shallowCopy(APath.Nd.slots[i], APath.Nd.slots[i - 1])
+  for i in countdown(APath.Nd.count.int - 1, x + 1): APath.Nd.slots[i] = move APath.Nd.slots[i - 1]
   APath.Nd.slots[x] = setItem(Akey, Avalue, ANode)
 
 
@@ -255,31 +255,39 @@ proc SplitPage[T,D](n, left: PNode[T,D], xi: int, Akey:var T, Avalue:var D): PNo
   result.slots.newSeq(cLenCenter)
   result.count = cCenter
   if x == cCenter:
-    for i in 0..cCenter-1: shallowCopy(it1[i], left.slots[i])
-    for i in 0..cCenter-1: shallowCopy(result.slots[i], left.slots[cCenter + i])
+    for i in 0..cCenter-1: 
+      it1[i] = move left.slots[i]
+    for i in 0..cCenter-1:
+      result.slots[i] = move left.slots[cCenter + i]
     result.left = n
   else :
     if x < cCenter :
-      for i in 0..x-1: shallowCopy(it1[i], left.slots[i])
+      for i in 0..x-1:
+        it1[i] = move left.slots[i]
       it1[x] = setItem(Akey, Avalue, n)
-      for i in x+1 .. cCenter-1: shallowCopy(it1[i], left.slots[i-1])
+      for i in x+1 .. cCenter-1:
+        it1[i] = move left.slots[i-1]
       var w = left.slots[cCenter-1]
       Akey = w.key
       Avalue = w.value
       result.left = w.node
-      for i in 0..cCenter-1: shallowCopy(result.slots[i], left.slots[cCenter + i])
+      for i in 0..cCenter-1:
+        result.slots[i] = move left.slots[cCenter + i]
     else :
-      for i in 0..cCenter-1: shallowCopy(it1[i], left.slots[i])
+      for i in 0..cCenter-1:
+        it1[i] = move left.slots[i]
       x = x - (cCenter + 1)
-      for i in 0..x-1: shallowCopy(result.slots[i], left.slots[cCenter + i + 1])
+      for i in 0..x-1:
+        result.slots[i] = move left.slots[cCenter + i + 1]
       result.slots[x] = setItem(Akey, Avalue, n)
-      for i in x+1 .. cCenter-1: shallowCopy(result.slots[i], left.slots[cCenter + i])
+      for i in x+1 .. cCenter-1:
+        result.slots[i] = move left.slots[cCenter + i]
       var w = left.slots[cCenter]
       Akey = w.key
       Avalue = w.value
       result.left = w.node
   left.count = cCenter
-  shallowCopy(left.slots, it1)
+  left.slots = move it1
 
 
 proc internalPut[T,D](ANode: ref TNode[T,D], Akey: T, Avalue: D, Oldvalue: var D): ref TNode[T,D] =
@@ -437,7 +445,7 @@ proc test() =
   var it1 = internalFind(root, 312)
   echo it1.value
 
-  for i in 1..1_000_000:
+  for i in 1..1_000:
     root = internalPut(root, i, i, oldvalue)
 
   var cnt = 0
diff --git a/tests/generics/tgeneric_recursionlimit.nim b/tests/generics/tgeneric_recursionlimit.nim
new file mode 100644
index 000000000..5fe9b43c6
--- /dev/null
+++ b/tests/generics/tgeneric_recursionlimit.nim
@@ -0,0 +1,123 @@
+discard """
+  action: "compile"
+"""
+
+# https://github.com/nim-lang/Nim/issues/20348
+
+type
+  Payload[T] = object
+    payload: T
+  Carrier[T] = object
+    val: T
+
+type
+  Payload0*[T] = object
+    payload: Payload[T]
+  Payload1*[T] = object
+    payload: Payload[T]
+  Payload2*[T] = object
+    payload: Payload[T]
+  Payload3*[T] = object
+    payload: Payload[T]
+  Payload4*[T] = object
+    payload: Payload[T]
+  Payload5*[T] = object
+    payload: Payload[T]
+  Payload6*[T] = object
+    payload: Payload[T]
+  Payload7*[T] = object
+    payload: Payload[T]
+  Payload8*[T] = object
+    payload: Payload[T]
+  Payload9*[T] = object
+    payload: Payload[T]
+  Payload10*[T] = object
+    payload: Payload[T]
+  Payload11*[T] = object
+    payload: Payload[T]
+  Payload12*[T] = object
+    payload: Payload[T]
+  Payload13*[T] = object
+    payload: Payload[T]
+  Payload14*[T] = object
+    payload: Payload[T]
+  Payload15*[T] = object
+    payload: Payload[T]
+  Payload16*[T] = object
+    payload: Payload[T]
+  Payload17*[T] = object
+    payload: Payload[T]
+  Payload18*[T] = object
+    payload: Payload[T]
+  Payload19*[T] = object
+    payload: Payload[T]
+  Payload20*[T] = object
+    payload: Payload[T]
+  Payload21*[T] = object
+    payload: Payload[T]
+  Payload22*[T] = object
+    payload: Payload[T]
+  Payload23*[T] = object
+    payload: Payload[T]
+  Payload24*[T] = object
+    payload: Payload[T]
+  Payload25*[T] = object
+    payload: Payload[T]
+  Payload26*[T] = object
+    payload: Payload[T]
+  Payload27*[T] = object
+    payload: Payload[T]
+  Payload28*[T] = object
+    payload: Payload[T]
+  Payload29*[T] = object
+    payload: Payload[T]
+  Payload30*[T] = object
+    payload: Payload[T]
+  Payload31*[T] = object
+    payload: Payload[T]
+  Payload32*[T] = object
+    payload: Payload[T]
+  Payload33*[T] = object
+    payload: Payload[T]
+
+type
+  Carriers*[T] = object
+    c0*: Carrier[Payload0[T]]
+    c1*: Carrier[Payload1[T]]
+    c2*: Carrier[Payload2[T]]
+    c3*: Carrier[Payload3[T]]
+    c4*: Carrier[Payload4[T]]
+    c5*: Carrier[Payload5[T]]
+    c6*: Carrier[Payload6[T]]
+    c7*: Carrier[Payload7[T]]
+    c8*: Carrier[Payload8[T]]
+    c9*: Carrier[Payload9[T]]
+    c10*: Carrier[Payload10[T]]
+    c11*: Carrier[Payload11[T]]
+    c12*: Carrier[Payload12[T]]
+    c13*: Carrier[Payload13[T]]
+    c14*: Carrier[Payload14[T]]
+    c15*: Carrier[Payload15[T]]
+    c16*: Carrier[Payload16[T]]
+    c17*: Carrier[Payload17[T]]
+    c18*: Carrier[Payload18[T]]
+    c19*: Carrier[Payload19[T]]
+    c20*: Carrier[Payload20[T]]
+    c21*: Carrier[Payload21[T]]
+    c22*: Carrier[Payload22[T]]
+    c23*: Carrier[Payload23[T]]
+    c24*: Carrier[Payload24[T]]
+    c25*: Carrier[Payload25[T]]
+    c26*: Carrier[Payload26[T]]
+    c27*: Carrier[Payload27[T]]
+    c28*: Carrier[Payload28[T]]
+    c29*: Carrier[Payload29[T]]
+    c30*: Carrier[Payload30[T]]
+    c31*: Carrier[Payload31[T]]
+    c32*: Carrier[Payload32[T]]
+    c33*: Carrier[Payload33[T]]
+
+var carriers : Carriers[int]
+
+static:
+  assert $(typeof(carriers.c33.val)) == "Payload33[system.int]"
diff --git a/tests/generics/tgenerics_issues.nim b/tests/generics/tgenerics_issues.nim
index 54360f178..3068a22f2 100644
--- a/tests/generics/tgenerics_issues.nim
+++ b/tests/generics/tgenerics_issues.nim
@@ -23,6 +23,10 @@ concrete 88
 G:0,1:0.1
 G:0,1:0.1
 H:1:0.1
+0
+(foo: none(seq[Foo]), s: "")
+(foo: some(@[(a: "world", bar: none(Bar))]), s: "hello,")
+@[(a: "hey", bar: none(Bar))]
 '''
 joinable: false
 """
@@ -51,8 +55,8 @@ block t88:
 
   let c = ChildClass[string].new("Base", "Child")
 
-  assert c.baseMethod == "Base"
-  assert c.overriddenMethod == "Child"
+  doAssert c.baseMethod == "Base"
+  doAssert c.overriddenMethod == "Child"
 
 
 
@@ -124,7 +128,7 @@ block t1789:
       bar: array[N, T]
 
   proc `[]`[N, T](f: Bar[N, T], n: range[0..(N - 1)]): T =
-    assert high(n) == N-1
+    doAssert high(n) == N-1
     result = f.bar[n]
 
   var b: Bar[3, int]
@@ -599,7 +603,7 @@ block t7854:
 
 
 block t5864:
-  proc defaultStatic(s: openarray, N: static[int] = 1): int = N
+  proc defaultStatic(s: openArray, N: static[int] = 1): int = N
   proc defaultGeneric[T](a: T = 2): int = a
 
   let a = [1, 2, 3, 4].defaultStatic()
@@ -730,7 +734,7 @@ block t1684:
   proc newDerived(idx: int): DerivedType {.inline.} = DerivedType(idx: idx)
 
   let d = newDerived(2)
-  assert(d.index == 2)
+  doAssert(d.index == 2)
 
 
 
@@ -770,13 +774,121 @@ block: # issue #9458
     Option[T] = object
       val: T
       has: bool
-    
+
     Bar = object
 
   proc none(T: typedesc): Option[T] =
     discard
 
-  proc foo[T](self: T; x: Option[Bar] = Bar.none) = 
+  proc foo[T](self: T; x: Option[Bar] = Bar.none) =
     discard
 
   foo(1)
+
+
+# bug #8426
+type
+  MyBool[T: uint] = range[T(0)..T(1)] # Works
+
+var x: MyBool[uint]
+echo x
+
+# x = 2 # correctly prevented
+
+type
+  MyBool2 = range[uint(0)..uint(1)] # Error ordinal or float type expected
+
+
+# bug #10396
+import options, strutils
+
+type
+  Foo {.acyclic.} = object
+    a: string
+    bar: Option[Bar]
+
+  Bar {.acyclic.} = object
+    foo: Option[seq[Foo]]   # if this was just Option[Foo], everything works fine
+    s: string
+
+proc getBar(x: string): Bar
+
+proc intoFoos(ss: seq[string]): seq[Foo] =
+  result = @[]
+  for s in ss:
+    let spl = s.split(',')
+    if spl.len > 1:
+      result.add Foo(a: spl[0],
+                     bar: some(getBar(spl[1])))
+    else:
+      result.add Foo(a: s,
+                     bar: none[Bar]())
+
+proc getBar(x: string): Bar =
+  let spl = x.split(' ')
+  result =
+    if spl.len > 1:
+      Bar(foo: some(spl[1..high(spl)].intoFoos),
+          s: spl[0])
+    else:
+      Bar(foo: none[seq[Foo]](),
+          s: "")
+
+proc fakeReadLine(): string = "hey"
+
+echo getBar(fakeReadLine()) # causes error
+
+echo getBar("hello, world") # causes error
+
+discard $getBar(fakeReadLine()) # causes error
+
+discard $getBar("hello, world") # causes error
+
+discard getBar(fakeReadLine()) # no error
+
+discard getBar("hello, world") # no error
+
+echo intoFoos(fakeReadLine().split(' ')) # no error, works as expected
+
+
+# bug #14990
+type
+  Tile3 = Tile2
+  Tile2 = Tile
+  Tile[n] = object
+    a: n
+
+var a: Tile3[int]
+
+block: # Ensure no segfault from constraint
+  type
+    Regex[A: SomeOrdinal] = ref object
+      val: Regex[A]
+    MyConstraint = (seq or enum or set)
+    MyOtherType[A: MyConstraint] = ref object
+      val: MyOtherType[A]
+
+  var
+    a = Regex[int]()
+    b = Regex[bool]()
+    c = MyOtherType[seq[int]]()
+
+block: # https://github.com/nim-lang/Nim/issues/20416
+  type
+    Item[T] = object
+      link:ptr Item[T]
+      data:T
+
+    KVSeq[A,B] = seq[(A,B)]
+
+    MyTable[A,B] = object
+      data: KVSeq[A,B]
+
+    Container[T] = object
+      a: MyTable[int,ref Item[T]]
+
+  proc p1(sg:Container) = discard # Make sure that a non parameterized 'Container' argument still compiles
+
+  proc p2[T](sg:Container[T]) = discard
+  var v : Container[int]
+  p2(v)
diff --git a/tests/generics/tgenerics_various.nim b/tests/generics/tgenerics_various.nim
index 22d3cff7a..53661236e 100644
--- a/tests/generics/tgenerics_various.nim
+++ b/tests/generics/tgenerics_various.nim
@@ -58,23 +58,23 @@ block tgenericdefaults:
   var x1: TFoo[int, float]
 
   static:
-    assert type(x1.x) is int
-    assert type(x1.y) is float
-    assert type(x1.z) is int
+    doAssert type(x1.x) is int
+    doAssert type(x1.y) is float
+    doAssert type(x1.z) is int
 
   var x2: TFoo[string, R = float, U = seq[int]]
 
   static:
-    assert type(x2.x) is string
-    assert type(x2.y) is seq[int]
-    assert type(x2.z) is float
+    doAssert type(x2.x) is string
+    doAssert type(x2.y) is seq[int]
+    doAssert type(x2.z) is float
 
   var x3: TBar[float]
 
   static:
-    assert type(x3.x) is float
-    assert type(x3.y) is array[4, float]
-    assert type(x3.z) is float
+    doAssert type(x3.x) is float
+    doAssert type(x3.y) is array[4, float]
+    doAssert type(x3.z) is float
 
 
 
@@ -127,54 +127,18 @@ block trefs:
 
 
 
-block tsharedcases:
-  proc typeNameLen(x: typedesc): int {.compileTime.} =
-    result = x.name.len
-  macro selectType(a, b: typedesc): typedesc =
-    result = a
-
-  type
-    Foo[T] = object
-      data1: array[T.high, int]
-      data2: array[typeNameLen(T), float]
-      data3: array[0..T.typeNameLen, selectType(float, int)]
-    MyEnum = enum A, B, C, D
-
-  var f1: Foo[MyEnum]
-  var f2: Foo[int8]
-
-  doAssert high(f1.data1) == 2 # (D = 3) - 1 == 2
-  doAssert high(f1.data2) == 5 # (MyEnum.len = 6) - 1 == 5
-
-  doAssert high(f2.data1) == 126 # 127 - 1 == 126
-  doAssert high(f2.data2) == 3 # int8.len - 1 == 3
-
-  static:
-    assert high(f1.data1) == ord(C)
-    assert high(f1.data2) == 5 # length of MyEnum minus one, because we used T.high
-
-    assert high(f2.data1) == 126
-    assert high(f2.data2) == 3
-
-    assert high(f1.data3) == 6 # length of MyEnum
-    assert high(f2.data3) == 4 # length of int8
-
-    assert f2.data3[0] is float
-
-
-
 block tmap_auto:
   let x = map(@[1, 2, 3], x => x+10)
-  assert x == @[11, 12, 13]
+  doAssert x == @[11, 12, 13]
 
   let y = map(@[(1,"a"), (2,"b"), (3,"c")], x => $x[0] & x[1])
-  assert y == @["1a", "2b", "3c"]
+  doAssert y == @["1a", "2b", "3c"]
 
   proc eatsTwoArgProc[T,S,U](a: T, b: S, f: proc(t: T, s: S): U): U =
     f(a,b)
 
   let z = eatsTwoArgProc(1, "a", (t,s) => $t & s)
-  assert z == "1a"
+  doAssert z == "1a"
 
 
 
@@ -230,8 +194,8 @@ block tvarargs_vs_generics:
     echo "direct"
   proc withDirectType[T](arg: T) =
     echo "generic"
-  proc withOpenArray(args: openarray[string]) =
-    echo "openarray"
+  proc withOpenArray(args: openArray[string]) =
+    echo "openArray"
   proc withOpenArray[T](arg: T) =
     echo "generic"
   proc withVarargs(args: varargs[string]) =
@@ -253,3 +217,38 @@ block:
 
   var x: Que[int]
   doAssert(x.x == 0)
+
+
+# bug #4466
+proc identity[T](t: T): T = t
+
+proc doSomething[A, B](t: tuple[a: A, b: B]) = discard
+
+discard identity((c: 1, d: 2))
+doSomething(identity((1, 2)))
+
+# bug #6231
+proc myProc[T, U](x: T or U) = discard
+
+myProc[int, string](x = 2)
+
+block: # issue #8390
+  proc x[T:SomeFloat](q: openarray[T], y: T = 1): string =
+    doAssert $q.type == $openarray[y.type]
+    $y.type
+
+  doAssert x(@[1.0]) == $1.0.type
+
+
+block: # issue #9381
+  var evalCount {.compileTime.} = 0
+
+  macro test(t: typed): untyped =
+    inc evalCount
+    t
+
+  type GenericObj[T] = object
+    f: test(T)
+
+  var x: GenericObj[int]
+  static: doAssert evalCount == 1
diff --git a/tests/generics/tgenericwhen.nim b/tests/generics/tgenericwhen.nim
new file mode 100644
index 000000000..87672a699
--- /dev/null
+++ b/tests/generics/tgenericwhen.nim
@@ -0,0 +1,58 @@
+discard """
+  targets: "c js"
+"""
+
+block: # issue #24041
+  type ArrayBuf[N: static int, T = byte] = object
+    when sizeof(int) > sizeof(uint8):
+      when N <= int(uint8.high):
+        n: uint8
+      else:
+        when sizeof(int) > sizeof(uint16):
+          when N <= int(uint16.high):
+            n: uint16
+          else:
+            when sizeof(int) > sizeof(uint32):
+              when N <= int(uint32.high):
+                n: uint32
+              else:
+                n: int
+            else:
+              n: int
+        else:
+          n: int
+    else:
+      n: int
+
+  var x: ArrayBuf[8]
+  doAssert x.n is uint8
+  when sizeof(int) > sizeof(uint32):
+    var y: ArrayBuf[int(uint32.high) * 8]
+    doAssert y.n is int
+
+block: # constant condition after dynamic one
+  type Foo[T] = object
+    when T is int:
+      a: int
+    elif true:
+      a: string
+    else:
+      a: bool
+  var x: Foo[string]
+  doAssert x.a is string
+  var y: Foo[int]
+  doAssert y.a is int
+  var z: Foo[float]
+  doAssert z.a is string
+
+block: # issue #4774, but not with threads
+  const hasThreadSupport = not defined(js)
+  when hasThreadSupport:
+    type Channel[T] = object
+      value: T
+  type
+    SomeObj[T] = object
+      when hasThreadSupport:
+        channel: ptr Channel[T]
+  var x: SomeObj[int]
+  doAssert compiles(x.channel) == hasThreadSupport
diff --git a/tests/generics/tgensyminst.nim b/tests/generics/tgensyminst.nim
new file mode 100644
index 000000000..3f30188d8
--- /dev/null
+++ b/tests/generics/tgensyminst.nim
@@ -0,0 +1,29 @@
+# issue #24048
+
+import macros
+
+proc map(fn: proc(val: int): void) = fn(1)
+
+# This works fine, and is the exact same function call as what's
+# generated by the macro `aBug`.
+map proc(val: auto): void =
+  let variable = 123
+
+macro aBug() =
+  # 1. let sym = ident("variable")
+  let sym = genSym(nskLet, "variable")
+  let letStmt = newLetStmt(sym, newLit(123))
+
+  let lambda = newProc(
+    params = @[
+      ident("void"),
+      newIdentDefs(ident("val"), ident("auto")),
+      # 2. newIdentDefs(ident("val"), ident("int")),
+    ],
+    body = newStmtList(letStmt),
+    procType = nnkLambda
+  )
+
+  result = newCall(bindSym("map"), lambda)
+
+aBug()
diff --git a/tests/generics/timpl_ast.nim b/tests/generics/timpl_ast.nim
new file mode 100644
index 000000000..97fe128cd
--- /dev/null
+++ b/tests/generics/timpl_ast.nim
@@ -0,0 +1,80 @@
+import std/macros
+import std/assertions
+
+block: # issue #16639
+  type Foo[T] = object
+    when true:
+      x: float
+
+  type Bar = object
+    when true:
+      x: float
+
+  macro test() =
+    let a = getImpl(bindSym"Foo")[^1]
+    let b = getImpl(bindSym"Bar")[^1]
+    doAssert treeRepr(a) == treeRepr(b)
+
+  test()
+
+import strutils
+
+block: # issues #9899, ##14708
+  macro implRepr(a: typed): string =
+    result = newLit(repr(a.getImpl))
+
+  type
+    Option[T] = object
+      when false: discard # issue #14708
+      when false: x: int
+      when T is (ref | ptr):
+        val: T
+      else:
+        val: T
+        has: bool
+
+  static: # check information is retained
+    let r = implRepr(Option)
+    doAssert "when T is" in r
+    doAssert r.count("val: T") == 2
+    doAssert "has: bool" in r
+
+  block: # try to compile the output
+    macro parse(s: static string) =
+      result = parseStmt(s)
+    parse("type " & implRepr(Option))
+
+block: # issue #22639
+  type
+    Spectrum[N: static int] = object
+      data: array[N, float]
+    AngleInterpolator = object
+      data: seq[Spectrum[60]]
+  proc initInterpolator(num: int): AngleInterpolator =
+    result = AngleInterpolator()
+    for i in 0 ..< num:
+      result.data.add Spectrum[60]()
+  macro genCompatibleTuple(t: typed): untyped =
+    let typ = t.getType[1].getTypeImpl[2]
+    result = nnkTupleTy.newTree()
+    for i, ch in typ: # is `nnkObjectTy`
+      result.add nnkIdentDefs.newTree(ident(ch[0].strVal), # ch is `nnkIdentDefs`
+                                      ch[1],
+                                      newEmptyNode())
+  proc fullSize[T: object | tuple](x: T): int =
+    var tmp: genCompatibleTuple(T)
+    result = 0
+    for field, val in fieldPairs(x):
+      result += sizeof(val)
+    doAssert result == sizeof(tmp)
+
+  let reflectivity = initInterpolator(1)
+  for el in reflectivity.data:
+    doAssert fullSize(el) == sizeof(el)
+  doAssert fullSize(reflectivity.data[0]) == sizeof(reflectivity.data[0])
+  doAssert genCompatibleTuple(Spectrum[60]) is tuple[data: array[60, float]]
+  doAssert genCompatibleTuple(Spectrum[120]) is tuple[data: array[120, float]]
+  type Foo[T] = object
+    data: T
+  doAssert genCompatibleTuple(Foo[int]) is tuple[data: int]
+  doAssert genCompatibleTuple(Foo[float]) is tuple[data: float]
diff --git a/tests/generics/timplicit_and_explicit.nim b/tests/generics/timplicit_and_explicit.nim
new file mode 100644
index 000000000..7220b7429
--- /dev/null
+++ b/tests/generics/timplicit_and_explicit.nim
@@ -0,0 +1,65 @@
+
+block: # basic test
+  proc doStuff[T](a: SomeInteger): T = discard
+  proc doStuff[T;Y](a: SomeInteger, b: Y): Y = discard
+  assert typeof(doStuff[int](100)) is int
+  assert typeof(doStuff[int, float](100, 1.0)) is float
+  assert typeof(doStuff[int, string](100, "Hello")) is string
+
+  proc t[T](x: T; z: int | float): seq[T] = result.add(x & $z)
+
+  assert t[string]("Hallo", 2.0) == @["Hallo" & $2.0]
+
+  proc t2[T](z: int | float): seq[T] = result.add($z)
+
+  assert t2[string](2.0) == @[$2.0]
+
+block: # template test
+  template someThing[T;Y](a: SomeFloat, b: SomeOrdinal): (T, Y) = (a, b)
+  assert typeof(someThing[float64, int](1.0, 100)) is (float64, int)
+
+block: # static test
+  proc t[T](s: static bool) = discard
+  proc t2[T](s: static string) = discard
+  t[string](true)
+  t2[int]("hello")
+  t2[string]("world")
+  t2[float]("test222222")
+
+block: #11152
+  proc f[T](X: typedesc) = discard
+  f[int](string)
+
+block: #15622
+  proc test1[T](a: T, b: static[string] = "") = discard
+  test1[int64](123)
+  proc test2[T](a: T, b: static[string] = "") = discard
+  doAssert not (compiles do:
+    test2[int64, static[string]](123))
+
+block: #4688
+  proc convertTo[T](v: int or float): T = (T)(v)
+  discard convertTo[float](1)
+
+block: #4164
+  proc printStr[T](s: static[string]): T = discard
+  discard printStr[int]("hello static")
+
+import macros
+
+block: # issue #9040, statics with template, macro, symchoice explicit generics
+  block: # macro
+    macro fun[N: static int](): untyped =
+      newLit 1
+    const a = fun[2]()
+    doAssert a == 1
+  block: # template
+    template fun[N: static int](): untyped =
+      1
+    const a = fun[2]()
+    doAssert a == 1
+  block: # symchoice
+    proc newSeq[x: static int](): int = 1
+    template foo: int =
+      newSeq[2]()
+    doAssert foo() == 1
diff --git a/tests/generics/timports.nim b/tests/generics/timports.nim
index 800ae7f88..e252ad194 100644
--- a/tests/generics/timports.nim
+++ b/tests/generics/timports.nim
@@ -7,7 +7,7 @@ false
 '''
 """
 
-import mbind_bracket, mclosed_sym, mdotlookup, mmodule_same_as_proc
+import mbind_bracket, mclosed_sym, mdotlookup, mmodule_same_as_proc, mtypenodes
 
 
 block tbind_bracket:
@@ -31,15 +31,33 @@ block tclosed_sym:
   proc same(r:R, d:int) = echo "TEST1"
   doIt(Data[int](d:123), R())
 
+import strutils, unicode # ambiguous `strip`
 
 block tdotlookup:
   foo(7)
   # bug #1444
   fn(4)
-
+  doAssert doStrip(123) == "123"
+  # bug #14254
+  doAssert baz2[float](1'i8) == 1
+  # bug #21883
+  proc abc[T: not not int](x: T): T =
+    var x = x
+    x.set("hello", "world")
+    result = x
+  doAssert abc(5) == 10
+  block: # ensure normal call is consistent with dot call 
+    proc T(x: int): float = x.float
+    proc foo[T](x: int) =
+      doAssert typeof(T(x)) is typeof(x.T)
+    foo[uint](123)
 
 block tmodule_same_as_proc:
   # bug #1965
   proc test[T](t: T) =
     mmodule_same_as_proc"a"
   test(0)
+
+block ttypenodes:
+  # issue #22699
+  doAssert chop[bool](42) == 42
diff --git a/tests/generics/tinheritable_importcpp.nim b/tests/generics/tinheritable_importcpp.nim
new file mode 100644
index 000000000..8ee18b70b
--- /dev/null
+++ b/tests/generics/tinheritable_importcpp.nim
@@ -0,0 +1,10 @@
+discard """
+  targets: "cpp"
+  action: compile
+"""
+
+# #4651
+type
+  Vector[T] {.importcpp: "std::vector<'0 >", header: "vector", inheritable.} = object
+  VectorDerived {.importcpp: "SomeVectorDerived", nodecl.} = object of Vector[int]
+  # Error: inheritance only works with non-final objects
diff --git a/tests/generics/tmacroinjectedsym.nim b/tests/generics/tmacroinjectedsym.nim
new file mode 100644
index 000000000..985e415f2
--- /dev/null
+++ b/tests/generics/tmacroinjectedsym.nim
@@ -0,0 +1,186 @@
+{.experimental: "openSym".}
+
+block: # issue #22605, normal call syntax
+  const error = "bad"
+
+  template valueOr(self: int, def: untyped): untyped =
+    case false
+    of true: ""
+    of false:
+      template error: untyped {.used, inject.} = "good"
+      def
+
+  proc g(T: type): string =
+    let x = valueOr 123:
+      return $error
+
+    "ok"
+
+  doAssert g(int) == "good"
+
+  proc g2(T: type): string =
+    bind error # use the bad version on purpose
+    let x = valueOr 123:
+      return $error
+
+    "ok"
+
+  doAssert g2(int) == "bad"
+
+block: # issue #22605, method call syntax
+  const error = "bad"
+
+  template valueOr(self: int, def: untyped): untyped =
+    case false
+    of true: ""
+    of false:
+      template error: untyped {.used, inject.} = "good"
+      def
+
+  proc g(T: type): string =
+    let x = 123.valueOr:
+      return $error
+
+    "ok"
+
+  doAssert g(int) == "good"
+
+  proc g2(T: type): string =
+    bind error # use the bad version on purpose
+    let x = 123.valueOr:
+      return $error
+
+    "ok"
+
+  doAssert g2(int) == "bad"
+
+block: # issue #22605, original complex example
+  type Xxx = enum
+    error
+    value
+
+  type
+    Result[T, E] = object
+      when T is void:
+        when E is void:
+          oResultPrivate*: bool
+        else:
+          case oResultPrivate*: bool
+          of false:
+            eResultPrivate*: E
+          of true:
+            discard
+      else:
+        when E is void:
+          case oResultPrivate*: bool
+          of false:
+            discard
+          of true:
+            vResultPrivate*: T
+        else:
+          case oResultPrivate*: bool
+          of false:
+            eResultPrivate*: E
+          of true:
+            vResultPrivate*: T
+
+  template valueOr[T: not void, E](self: Result[T, E], def: untyped): untyped =
+    let s = (self) # TODO avoid copy
+    case s.oResultPrivate
+    of true:
+      s.vResultPrivate
+    of false:
+      when E isnot void:
+        template error: untyped {.used, inject.} = s.eResultPrivate
+      def
+
+  proc f(): Result[int, cstring] =
+    Result[int, cstring](oResultPrivate: false, eResultPrivate: "f")
+
+  proc g(T: type): string =
+    let x = f().valueOr:
+      return $error
+
+    "ok"
+
+  doAssert g(int) == "f"
+
+  proc g2(T: type): string =
+    bind error # use the bad version on purpose
+    let x = f().valueOr:
+      return $error
+
+    "ok"
+
+  doAssert g2(int) == "error"
+
+block: # issue #23865
+  type Xxx = enum
+    error
+    value
+
+  type
+    Result[T, E] = object
+      when T is void:
+        when E is void:
+          oResultPrivate: bool
+        else:
+          case oResultPrivate: bool
+          of false:
+            eResultPrivate: E
+          of true:
+            discard
+      else:
+        when E is void:
+          case oResultPrivate: bool
+          of false:
+            discard
+          of true:
+            vResultPrivate: T
+        else:
+          case oResultPrivate: bool
+          of false:
+            eResultPrivate: E
+          of true:
+            vResultPrivate: T
+
+  func error[T, E](self: Result[T, E]): E =
+    ## Fetch error of result if set, or raise Defect
+    case self.oResultPrivate
+    of true:
+      when T isnot void:
+        raiseResultDefect("Trying to access error when value is set", self.vResultPrivate)
+      else:
+        raiseResultDefect("Trying to access error when value is set")
+    of false:
+      when E isnot void:
+        self.eResultPrivate
+
+  template valueOr[T: not void, E](self: Result[T, E], def: untyped): untyped =
+    let s = (self) # TODO avoid copy
+    case s.oResultPrivate
+    of true:
+      s.vResultPrivate
+    of false:
+      when E isnot void:
+        template error: untyped {.used, inject.} = s.eResultPrivate
+      def
+  proc f(): Result[int, cstring] =
+    Result[int, cstring](oResultPrivate: false, eResultPrivate: "f")
+  proc g(T: type): string =
+    let x = f().valueOr:
+      return $error
+    "ok"
+  doAssert g(int) == "f"
+
+import sequtils
+
+block: # issue #12283
+  var b = 5
+  type Foo[T] = object
+    h, w: int
+  proc bar[T](foos: seq[Foo[T]]): T =
+    let w = foldl(foos, a + b.w, 0)
+    w
+  let foos = @[Foo[int](h: 3, w: 5), Foo[int](h: 4, w: 6)]
+  doAssert bar(foos) == 11
diff --git a/tests/generics/tmacroinjectedsymwarning.nim b/tests/generics/tmacroinjectedsymwarning.nim
new file mode 100644
index 000000000..77119004b
--- /dev/null
+++ b/tests/generics/tmacroinjectedsymwarning.nim
@@ -0,0 +1,59 @@
+discard """
+  matrix: "--skipParentCfg --filenames:legacyRelProj"
+"""
+
+type Xxx = enum
+  error
+  value
+
+type
+  Result[T, E] = object
+    when T is void:
+      when E is void:
+        oResultPrivate*: bool
+      else:
+        case oResultPrivate*: bool
+        of false:
+          eResultPrivate*: E
+        of true:
+          discard
+    else:
+      when E is void:
+        case oResultPrivate*: bool
+        of false:
+          discard
+        of true:
+          vResultPrivate*: T
+      else:
+        case oResultPrivate*: bool
+        of false:
+          eResultPrivate*: E
+        of true:
+          vResultPrivate*: T
+
+template valueOr[T: not void, E](self: Result[T, E], def: untyped): untyped =
+  let s = (self) # TODO avoid copy
+  case s.oResultPrivate
+  of true:
+    s.vResultPrivate
+  of false:
+    when E isnot void:
+      template error: untyped {.used, inject.} = s.eResultPrivate
+    def
+
+proc f(): Result[int, cstring] =
+  Result[int, cstring](oResultPrivate: false, eResultPrivate: "f")
+
+proc g(T: type): string =
+  let x = f().valueOr:
+    {.push warningAsError[IgnoredSymbolInjection]: on.}
+    # test spurious error
+    discard true
+    let _ = f
+    {.pop.}
+    return $error #[tt.Warning
+            ^ a new symbol 'error' has been injected during template or generic instantiation, however 'error' [enumField declared in tmacroinjectedsymwarning.nim(6, 3)] captured at the proc declaration will be used instead; either enable --experimental:openSym to use the injected symbol, or `bind` this captured symbol explicitly [IgnoredSymbolInjection]]#
+
+  "ok"
+
+discard g(int)
diff --git a/tests/generics/tmetafield.nim b/tests/generics/tmetafield.nim
index 7a2375abe..cf30a936d 100644
--- a/tests/generics/tmetafield.nim
+++ b/tests/generics/tmetafield.nim
@@ -1,10 +1,23 @@
 discard """
   cmd: "nim check $options $file"
-  errormsg: "'proc' is not a concrete type"
-  errormsg: "'Foo' is not a concrete type."
-  errormsg: "invalid type: 'proc' in this context: 'TBaseMed'"
+  action: "reject"
+  nimout: '''
+tmetafield.nim(26, 5) Error: 'proc' is not a concrete type; for a callback without parameters use 'proc()'
+tmetafield.nim(27, 5) Error: 'Foo' is not a concrete type
+tmetafield.nim(29, 5) Error: invalid type: 'proc' in this context: 'TBaseMed' for var
+'''
 """
 
+# bug #188
+
+
+
+
+
+
+
+
+# line 20
 type
   Foo[T] = object
     x: T
@@ -15,4 +28,3 @@ type
 
 var a: TBaseMed
 
-# issue 188
diff --git a/tests/generics/tnestedissues.nim b/tests/generics/tnestedissues.nim
new file mode 100644
index 000000000..e96a1927e
--- /dev/null
+++ b/tests/generics/tnestedissues.nim
@@ -0,0 +1,24 @@
+block: # issue #23568
+  type G[T] = object
+    j: T
+  proc s[T](u: int) = discard
+  proc s[T]() = discard
+  proc c(e: int | int): G[G[G[int]]] = s[G[G[int]]]()
+  discard c(0)
+
+import std/options
+
+block: # issue #23310
+  type
+    BID = string or uint64
+    Future[T] = ref object of RootObj
+      internalValue: T
+    InternalRaisesFuture[T] = ref object of Future[T]
+  proc newInternalRaisesFutureImpl[T](): InternalRaisesFuture[T] =
+    let fut = InternalRaisesFuture[T]()
+  template newFuture[T](): auto =
+    newInternalRaisesFutureImpl[T]()
+  proc problematic(blockId: BID): Future[Option[seq[int]]] =
+    let resultFuture = newFuture[Option[seq[int]]]()
+    return resultFuture
+  let x = problematic("latest")
diff --git a/tests/generics/tnestedtemplate.nim b/tests/generics/tnestedtemplate.nim
new file mode 100644
index 000000000..22d0a2d3c
--- /dev/null
+++ b/tests/generics/tnestedtemplate.nim
@@ -0,0 +1,9 @@
+block: # issue #13979
+  var s: seq[int]
+  proc filterScanline[T](input: openArray[T]) =
+    template currPix: untyped = input[i]
+    for i in 0..<input.len:
+      s.add currPix
+  let pix = [1, 2, 3]
+  filterScanline(pix)
+  doAssert s == @[1, 2, 3]
diff --git a/tests/generics/tnullary_generics.nim b/tests/generics/tnullary_generics.nim
new file mode 100644
index 000000000..c79558ee3
--- /dev/null
+++ b/tests/generics/tnullary_generics.nim
@@ -0,0 +1,26 @@
+discard """
+  nimout: '''
+hah
+hey
+hey
+hah
+'''
+"""
+
+# non-generic
+proc foo(s: string) =
+  static: echo "hah"
+  echo s
+
+static: echo "hey"
+
+foo("hoo")
+
+# nullary generic
+proc bar[](s: string) =
+  static: echo "hah"
+  echo s
+
+static: echo "hey"
+
+bar("hoo")
diff --git a/tests/generics/tobjecttyperel.nim b/tests/generics/tobjecttyperel.nim
index 80fe23459..6f223c154 100644
--- a/tests/generics/tobjecttyperel.nim
+++ b/tests/generics/tobjecttyperel.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "-d:nimInternalNonVtablesTesting"
   output: '''(peel: 0, color: 15)
 (color: 15)
 17
diff --git a/tests/generics/topensymimport.nim b/tests/generics/topensymimport.nim
new file mode 100644
index 000000000..a47496827
--- /dev/null
+++ b/tests/generics/topensymimport.nim
@@ -0,0 +1,5 @@
+# issue #23386
+
+import mopensymimport2
+
+doAssert g(int) == "f"
diff --git a/tests/generics/toverloading_typedesc.nim b/tests/generics/toverloading_typedesc.nim
index 5ab700828..4d748bfee 100644
--- a/tests/generics/toverloading_typedesc.nim
+++ b/tests/generics/toverloading_typedesc.nim
@@ -1,7 +1,3 @@
-discard """
-  exitcode: 0
-  disabled: '''true'''
-"""
 import moverloading_typedesc
 import tables
 
@@ -9,7 +5,6 @@ type
   LFoo = object
   LBar = object
 
-
 when true:
   doAssert FBar.new() == 3
 
diff --git a/tests/generics/tparam_binding.nim b/tests/generics/tparam_binding.nim
index cd0d58e02..fa7558613 100644
--- a/tests/generics/tparam_binding.nim
+++ b/tests/generics/tparam_binding.nim
@@ -1,6 +1,7 @@
 discard """
+  matrix: "--mm:arc; --mm:refc"
   errormsg: "got <ref Matrix[2, 2, system.float], ref Matrix[2, 1, system.float]>"
-  line: 27
+  line: 28
 """
 
 type
diff --git a/tests/generics/tparser_generator.nim b/tests/generics/tparser_generator.nim
index 8f8fea382..ac921c0e5 100644
--- a/tests/generics/tparser_generator.nim
+++ b/tests/generics/tparser_generator.nim
@@ -147,7 +147,7 @@ proc literal*[N, T, P](pattern: P, kind: N): Rule[N, T] =
   let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
     if start == len(text):
       return -1
-    assert(len(text)>start, "Attempting to match at $#, string length is $# " % [$start, $len(text)])
+    doAssert(len(text)>start, "Attempting to match at $#, string length is $# " % [$start, $len(text)])
     when P is string or P is seq[N]:
       debug(debugLex, "Literal[" & $kind & "]: testing " & $pattern & " at " & $start & ": " & $text[start..start+len(pattern)-1])
       if text.continuesWith(pattern, start):
@@ -177,7 +177,7 @@ proc token[N, T](pattern: T, kind: N): Rule[N, T] =
     debug(debugLex, "Token[" & $kind & "]: testing " & pattern & " at " & $start)
     if start == len(text):
       return -1
-    assert(len(text)>start, "Attempting to match at $#, string length is $# " % [$start, $len(text)])
+    doAssert(len(text)>start, "Attempting to match at $#, string length is $# " % [$start, $len(text)])
     let m = text.match(re(pattern), start)
     if m.isSome:
       let node = initNode(start, len(m.get.match), kind)
@@ -192,7 +192,7 @@ proc chartest[N, T, S](testfunc: proc(s: S): bool, kind: N): Rule[N, T] =
   let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
     if start == len(text):
       return -1
-    assert(len(text)>start, "Attempting to match at $#, string length is $# " % [$start, $len(text)])
+    doAssert(len(text)>start, "Attempting to match at $#, string length is $# " % [$start, $len(text)])
     if testfunc(text[start]):
       nodes.add(initNode(start, 1, kind))
       result = 1
@@ -252,11 +252,11 @@ proc fail*[N, T](message: string, kind: N): Rule[N, T] =
 proc `+`*[N, T](left: Rule[N, T], right: Rule[N, T]): Rule[N, T] =
   let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
     var mynodes = newSeq[Node[N]]()
-    assert(not isNil(left.parser), "Left hand side parser is nil")
+    doAssert(not isNil(left.parser), "Left hand side parser is nil")
     let leftlength = left.parser(text, start, mynodes)
     if leftlength == -1:
       return leftlength
-    assert(not isNil(right.parser), "Right hand side parser is nil")
+    doAssert(not isNil(right.parser), "Right hand side parser is nil")
     let rightlength = right.parser(text, start+leftlength, mynodes)
     if rightlength == -1:
       return rightlength
@@ -267,13 +267,13 @@ proc `+`*[N, T](left: Rule[N, T], right: Rule[N, T]): Rule[N, T] =
 proc `/`*[N, T](left: Rule[N, T], right: Rule[N, T]): Rule[N, T] =
   let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
     var mynodes = newSeq[Node[N]]()
-    assert(not isNil(left.parser), "Left hand side of / is not fully defined")
+    doAssert(not isNil(left.parser), "Left hand side of / is not fully defined")
     let leftlength = left.parser(text, start, mynodes)
     if leftlength != -1:
       nodes.add(mynodes)
       return leftlength
     mynodes = newSeq[Node[N]]()
-    assert(not isNil(right.parser), "Right hand side of / is not fully defined")
+    doAssert(not isNil(right.parser), "Right hand side of / is not fully defined")
     let rightlength = right.parser(text, start, mynodes)
     if rightlength == -1:
       return rightlength
@@ -360,7 +360,7 @@ proc `/`*[N, T](rule: Rule[N, T]): Rule[N, T] =
   result = newRule[N, T](parser, rule.kind)
 
 proc `->`*(rule: Rule, production: Rule) =
-  assert(not isnil(production.parser), "Right hand side of -> is nil - has the rule been defined yet?")
+  doAssert(not isnil(production.parser), "Right hand side of -> is nil - has the rule been defined yet?")
   rule.parser = production.parser
 
 template grammar*[K](Kind, Text, Symbol: typedesc; default: K, code: untyped): typed {.hint[XDeclaredButNotUsed]: off.} =
diff --git a/tests/generics/tpointerprocs.nim b/tests/generics/tpointerprocs.nim
new file mode 100644
index 000000000..2bcaf15b3
--- /dev/null
+++ b/tests/generics/tpointerprocs.nim
@@ -0,0 +1,28 @@
+discard """
+cmd: "nim check $options --hints:off $file"
+action: "reject"
+nimout:'''
+tpointerprocs.nim(15, 11) Error: 'foo' doesn't have a concrete type, due to unspecified generic parameters.
+tpointerprocs.nim(27, 11) Error: cannot instantiate: 'foo[int]'; got 1 typeof(s) but expected 2
+tpointerprocs.nim(27, 14) Error: expression 'foo[int]' has no type (or is ambiguous)
+tpointerprocs.nim(28, 11) Error: expression 'bar' has no type (or is ambiguous)
+'''
+"""
+
+block:
+  proc foo(x: int | float): float = result = 1.0
+  let
+    bar = foo
+    baz = bar
+
+block:
+  proc foo(x: int | float): float = result = 1.0
+  let
+    bar = foo[int]
+    baz = bar
+
+block:
+  proc foo(x: int | float, y: int or string): float = result = 1.0
+  let
+    bar = foo[int]
+    baz = bar
\ No newline at end of file
diff --git a/tests/generics/tprevent_double_bind.nim b/tests/generics/tprevent_double_bind.nim
index 86e080ab6..d8fc6e5d3 100644
--- a/tests/generics/tprevent_double_bind.nim
+++ b/tests/generics/tprevent_double_bind.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "type mismatch: got <TT[seq[string]], proc (v: int){.gcsafe, locks: 0.}>"
+  errormsg: "type mismatch: got <TT[seq[string]], proc (v: int){.gcsafe.}>"
   line: 20
 """
 
diff --git a/tests/generics/trecursivegenerics.nim b/tests/generics/trecursivegenerics.nim
new file mode 100644
index 000000000..1b152b063
--- /dev/null
+++ b/tests/generics/trecursivegenerics.nim
@@ -0,0 +1,96 @@
+block: # Replicates #18728
+  type
+    FlipFlop[A, B] = ref object
+      val: A
+      next: FlipFlop[B, A]
+  
+    Trinary[A, B, C] = ref object
+      next: Trinary[B, C, A]
+  
+  assert typeof(FlipFlop[int, string]().next) is FlipFlop[string, int]
+  assert typeof(FlipFlop[string, int]().next) is FlipFlop[int, string]
+  assert typeof(Trinary[int, float, string]().next) is Trinary[float, string, int]
+  assert typeof(Trinary[int, float, string]().next.next) is Trinary[string, int, float]
+  var a = FlipFlop[int, string](val: 100, next: FlipFlop[string, int](val: "Hello"))
+  assert a.val == 100
+  assert a.next.val == "Hello"
+
+block: # 18838
+  type
+    DoublyLinkedNodeObj[T] = object
+      value: T
+
+    DoublyLinkedNode[T] = ref DoublyLinkedNodeObj[T]
+
+    Item[T] = ref object
+      link: DoublyLinkedNode[Item[T]]
+
+    Box = object
+
+  proc newDoublyLinkedNode[T](value: T): DoublyLinkedNode[T] =
+    new(result)
+    result.value = value 
+
+  let link = newDoublyLinkedNode(Item[Box]())
+
+import lists
+block:
+  type
+    Box = object
+    Item[T] = ref object
+      link:DoublyLinkedNode[ Item[T] ]
+
+    ItemSimple = ref object
+      link:DoublyLinkedNode[ ItemSimple ]
+
+  let link = newDoublyLinkedNode( Item[Box]() )
+
+block: #18897
+  type
+    SkipListObj[T] = object
+      over: SkipList[T]
+      down: SkipList[T]
+      value: T
+
+    SkipList[T] = ref SkipListObj[T]
+
+    GraphObj[N, E; F: static[int]] = object
+      nodes: SkipList[Node[N, E]]
+
+    Graph[N, E; F: static[int]] = ref GraphObj[N, E, F]
+
+    Node[N, E] = ref NodeObj[N, E]
+
+    NodeObj[N, E] = object
+      value: N
+      incoming: SkipList[Edge[N, E]]
+      outgoing: SkipList[Edge[N, E]]
+
+    Edge[N, E] = ref EdgeObj[N, E]
+
+    EdgeObj[N, E] = object
+      value: E
+      id: int
+      source: Node[N, E]
+      target: Node[N, E]
+
+    EdgeResult[N, E] = tuple
+      source: Node[N, E]
+      edge: Edge[N, E]
+      target: Node[N, E]
+
+  proc newSkipList[T](value: T): SkipList[T] =
+    static: echo T, " ", typeof(result.value)
+    result = SkipList[T](value: value)
+
+  proc toSkipList[T](values: openArray[T] = @[]): SkipList[T] =
+    for item in items(values):
+      if result.isNil:
+        result = newSkipList(item)
+
+  proc newContainer[N, E, F](graph: Graph[N, E, F]; form: typedesc): auto =
+    result = toSkipList[form]([])
+
+  var
+    result = Graph[int, string, 0]()
+  result.nodes = result.newContainer(Node[int, string])
\ No newline at end of file
diff --git a/tests/generics/treentranttypes.nim b/tests/generics/treentranttypes.nim
index 31fa25293..801f0e444 100644
--- a/tests/generics/treentranttypes.nim
+++ b/tests/generics/treentranttypes.nim
@@ -2,21 +2,13 @@ discard """
 output: '''
 (10, ("test", 1.2))
 3x3 Matrix [[0.0, 2.0, 3.0], [2.0, 0.0, 5.0], [2.0, 0.0, 5.0]]
-
 2x3 Matrix [[0.0, 2.0, 3.0], [2.0, 0.0, 5.0]]
-
 2x3 Literal [[0.0, 2.0, 3.0], [2.0, 0.0, 5.0]]
-
 2x3 Matrix [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]
-
 2x2 ArrayArray[[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]
-
 2x3 ArrayVector[[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]
-
 2x3 VectorVector [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]
-
 2x3 VectorArray [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]
-
 @[1, 2]
 @[1, 2]
 @[1, 2]@[3, 4]
@@ -109,3 +101,14 @@ echo @(b.arr[0].arr), @(b.arr[1].arr)
 let y = b
 echo @(y.arr[0].arr), @(y.arr[1].arr)
 
+import macros
+
+block: # issue #5121
+  type
+    A = object
+    AConst[X] = A
+
+  macro dumpType(t: typedesc): untyped =
+    result = newTree(nnkTupleConstr, newLit $t.getType[1].typeKind, newLit t.getType[1].treeRepr)
+
+  doAssert dumpType(A) == ("ntyObject", "Sym \"A\"")
diff --git a/tests/generics/treturn_inference.nim b/tests/generics/treturn_inference.nim
new file mode 100644
index 000000000..331a9d4db
--- /dev/null
+++ b/tests/generics/treturn_inference.nim
@@ -0,0 +1,184 @@
+
+{.experimental: "inferGenericTypes".}
+
+import std/tables
+
+block:
+  type
+    MyOption[T, Z] = object
+      x: T
+      y: Z
+
+  proc none[T, Z](): MyOption[T, Z] =
+    when T is int:
+      result.x = 22
+    when Z is float:
+      result.y = 12.0
+
+  proc myGenericProc[T, Z](): MyOption[T, Z] =
+    none() # implied by return type
+
+  let a = myGenericProc[int, float]()
+  doAssert a.x == 22
+  doAssert a.y == 12.0
+
+  let b: MyOption[int, float] = none() # implied by type of b
+  doAssert b.x == 22
+  doAssert b.y == 12.0
+
+# Simple template based result with inferred type for errors
+block:
+  type
+    ResultKind {.pure.} = enum
+      Ok
+      Err
+
+    Result[T] = object
+      case kind: ResultKind
+      of Ok:
+        data: T
+      of Err:
+        errmsg: cstring
+
+  template err[T](msg: static cstring): Result[T] =
+    Result[T](kind : ResultKind.Err, errmsg : msg)
+
+  proc testproc(): Result[int] =
+    err("Inferred error!") # implied by proc return
+  let r = testproc()
+  doAssert r.kind == ResultKind.Err
+  doAssert r.errmsg == "Inferred error!"
+
+# Builtin seq
+block:
+  let x: seq[int] = newSeq(1)
+  doAssert x is seq[int]
+  doAssert x.len() == 1
+
+  type
+    MyType[T, Z] = object
+      x: T
+      y: Z
+
+  let y: seq[MyType[int, float]] = newSeq(2)
+  doAssert y is seq[MyType[int, float]]
+  doAssert y.len() == 2
+
+  let z = MyType[seq[float], string](
+    x : newSeq(3),
+    y : "test"
+  )
+  doAssert z.x is seq[float]
+  doAssert z.x.len() == 3
+  doAssert z.y is string
+  doAssert z.y == "test"
+
+# array
+block:
+  proc giveArray[N, T](): array[N, T] =
+    for i in 0 .. N.high:
+      result[i] = i
+  var x: array[2, int] = giveArray()
+  doAssert x == [0, 1]
+
+# tuples
+block:
+  proc giveTuple[T, Z]: (T, Z, T) = discard
+  let x: (int, float, int) = giveTuple()
+  doAssert x is (int, float, int)
+  doAssert x == (0, 0.0, 0)
+
+  proc giveNamedTuple[T, Z]: tuple[a: T, b: Z] = discard
+  let y: tuple[a: int, b: float] = giveNamedTuple()
+  doAssert y is (int, float)
+  doAssert y is tuple[a: int, b: float]
+  doAssert y == (0, 0.0)
+
+  proc giveNestedTuple[T, Z]: ((T, Z), Z) = discard
+  let z: ((int, float), float) = giveNestedTuple()
+  doAssert z is ((int, float), float)
+  doAssert z == ((0, 0.0), 0.0)
+
+  # nesting inside a generic type
+  type MyType[T] = object
+    x: T
+  let a = MyType[(int, MyType[float])](x : giveNamedTuple())
+  doAssert a.x is (int, MyType[float])
+
+
+# basic constructors
+block:
+  type MyType[T] = object
+    x: T
+
+  proc giveValue[T](): T =
+    when T is int:
+      12
+    else:
+      default(T)
+
+  let x = MyType[int](x : giveValue())
+  doAssert x.x is int
+  doAssert x.x == 12
+
+  let y = MyType[MyType[float]](x : MyType[float](x : giveValue()))
+  doAssert y.x is MyType[float]
+  doAssert y.x.x is float
+  doAssert y.x.x == 0.0
+
+  # 'MyType[float]' is bound to 'T' directly
+  #  instead of mapping 'T' to 'float'
+  let z = MyType[MyType[float]](x : giveValue())
+  doAssert z.x is MyType[float]
+  doAssert z.x.x == 0.0
+
+  type Foo = object
+    x: Table[int, float]
+
+  let a = Foo(x: initTable())
+  doAssert a.x is Table[int, float]
+
+# partial binding
+block:
+  type
+    ResultKind = enum
+      Ok, Error
+
+    Result[T, E] = object
+      case kind: ResultKind
+      of Ok:
+        okVal: T
+      of Error:
+        errVal: E
+
+  proc err[T, E](myParam: E): Result[T, E] =
+    Result[T, E](kind : Error, errVal : myParam)
+
+  proc doStuff(): Result[int, string] = 
+    err("Error")
+
+  let res = doStuff()
+  doAssert res.kind == Error
+  doAssert res.errVal == "Error"
+
+# ufcs
+block:
+  proc getValue[T](_: string): T =
+    doAssert T is int
+    44
+  
+  proc `'test`[T](_: string): T =
+    55
+
+  let a: int = getValue("")
+  let b: int = "".getValue()
+  let c: int = "".getValue
+  let d: int = getValue ""
+  let e: int = getValue""
+  let f: int = 12345'test
+  doAssert a == 44
+  doAssert b == 44
+  doAssert c == 44
+  doAssert d == 44
+  doAssert e == 44
+  doAssert f == 55
diff --git a/tests/generics/trtree.nim b/tests/generics/trtree.nim
deleted file mode 100644
index b45ac8c83..000000000
--- a/tests/generics/trtree.nim
+++ /dev/null
@@ -1,640 +0,0 @@
-discard """
-  output: '''1 [2, 3, 4, 7]
-[0, 0]'''
-  target: "c"
-  joinable: false
-disabled: 32bit
-  cmd: "nim c --gc:arc $file"
-"""
-
-# bug #13110: This test failed with --gc:arc.
-
-# this test wasn't written for 32 bit
-# don't join because the code is too messy.
-
-# Nim RTree and R*Tree implementation
-# S. Salewski, 06-JAN-2018
-
-# http://www-db.deis.unibo.it/courses/SI-LS/papers/Gut84.pdf
-# http://dbs.mathematik.uni-marburg.de/publications/myPapers/1990/BKSS90.pdf
-
-# RT: range type like float, int
-# D: Dimension
-# M: Max entries in one node
-# LT: leaf type
-
-type
-  Dim* = static[int]
-  Ext[RT] = tuple[a, b: RT] # extend (range)
-  Box*[D: Dim; RT] = array[D, Ext[RT]] # Rectangle for 2D
-  BoxCenter*[D: Dim; RT] = array[D, RT]
-  L*[D: Dim; RT, LT] = tuple[b: Box[D, RT]; l: LT] # called Index Entry or index record in the Guttman paper
-  H[M, D: Dim; RT, LT] = ref object of RootRef
-    parent: H[M, D, RT, LT]
-    numEntries: int
-    level: int
-  N[M, D: Dim; RT, LT] = tuple[b: Box[D, RT]; n: H[M, D, RT, LT]]
-  LA[M, D: Dim; RT, LT] = array[M, L[D, RT, LT]]
-  NA[M, D: Dim; RT, LT] = array[M, N[M, D, RT, LT]]
-  Leaf[M, D: Dim; RT, LT] = ref object of H[M, D, RT, LT]
-    a: LA[M, D, RT, LT]
-  Node[M, D: Dim; RT, LT] = ref object of H[M, D, RT, LT]
-    a: NA[M, D, RT, LT]
-
-  RTree*[M, D: Dim; RT, LT] = ref object of RootRef
-    root: H[M, D, RT, LT]
-    bigM: int
-    m: int
-
-  RStarTree*[M, D: Dim; RT, LT] = ref object of RTree[M, D, RT, LT]
-    firstOverflow: array[32, bool]
-    p: int
-
-proc newLeaf[M, D: Dim; RT, LT](): Leaf[M, D, RT, LT] =
-  new result
-
-proc newNode[M, D: Dim; RT, LT](): Node[M, D, RT, LT] =
-  new result
-
-proc newRTree*[M, D: Dim; RT, LT](minFill: range[30 .. 50] = 40): RTree[M, D, RT, LT] =
-  assert(M > 1 and M < 101)
-  new result
-  result.bigM = M
-  result.m = M * minFill div 100
-  result.root = newLeaf[M, D, RT, LT]()
-
-proc newRStarTree*[M, D: Dim; RT, LT](minFill: range[30 .. 50] = 40): RStarTree[M, D, RT, LT] =
-  assert(M > 1 and M < 101)
-  new result
-  result.bigM = M
-  result.m = M * minFill div 100
-  result.p = M * 30 div 100
-  result.root = newLeaf[M, D, RT, LT]()
-
-proc center(r: Box): auto =#BoxCenter[r.len, type(r[0].a)] =
-  var res: BoxCenter[r.len, type(r[0].a)]
-  for i in 0 .. r.high:
-    when r[0].a is SomeInteger:
-      res[i] = (r[i].a + r[i].b) div 2
-    elif r[0].a is SomeFloat:
-      res[i] = (r[i].a + r[i].b) / 2
-    else: assert false
-  return res
-
-proc distance(c1, c2: BoxCenter): auto =
-  var res: type(c1[0])
-  for i in 0 .. c1.high:
-    res += (c1[i] - c2[i]) * (c1[i] - c2[i])
-  return res
-
-proc overlap(r1, r2: Box): auto =
-  result = type(r1[0].a)(1)
-  for i in 0 .. r1.high:
-    result *= (min(r1[i].b, r2[i].b) - max(r1[i].a, r2[i].a))
-    if result <= 0: return 0
-
-proc union(r1, r2: Box): Box =
-  for i in 0 .. r1.high:
-    result[i].a = min(r1[i].a, r2[i].a)
-    result[i].b = max(r1[i].b, r2[i].b)
-
-proc intersect(r1, r2: Box): bool =
-  for i in 0 .. r1.high:
-    if r1[i].b < r2[i].a or r1[i].a > r2[i].b:
-      return false
-  return true
-
-proc area(r: Box): auto = #type(r[0].a) =
-  result = type(r[0].a)(1)
-  for i in 0 .. r.high:
-    result *= r[i].b - r[i].a
-
-proc margin(r: Box): auto = #type(r[0].a) =
-  result = type(r[0].a)(0)
-  for i in 0 .. r.high:
-    result += r[i].b - r[i].a
-
-# how much enlargement does r1 need to include r2
-proc enlargement(r1, r2: Box): auto =
-  area(union(r1, r2)) - area(r1)
-
-proc search*[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; b: Box[D, RT]): seq[LT] =
-  proc s[M, D: Dim; RT, LT](n: H[M, D, RT, LT]; b: Box[D, RT]; res: var seq[LT]) =
-    if n of Node[M, D, RT, LT]:
-      let h = Node[M, D, RT, LT](n)
-      for i in 0 ..< n.numEntries:
-        if intersect(h.a[i].b, b):
-          s(h.a[i].n, b, res)
-    elif n of Leaf[M, D, RT, LT]:
-      let h = Leaf[M, D, RT, LT](n)
-      for i in 0 ..< n.numEntries:
-        if intersect(h.a[i].b, b):
-          res.add(h.a[i].l)
-    else: assert false
-  result = newSeq[LT]()
-  s(t.root, b, result)
-
-# Insertion
-# a R*TREE proc
-proc chooseSubtree[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; b: Box[D, RT]; level: int): H[M, D, RT, LT] =
-  assert level >= 0
-  var n = t.root
-  while n.level > level:
-    let nn = Node[M, D, RT, LT](n)
-    var i0 = 0 # selected index
-    var minLoss = type(b[0].a).high
-    if n.level == 1: # childreen are leaves -- determine the minimum overlap costs
-      for i in 0 ..< n.numEntries:
-        let nx = union(nn.a[i].b, b)
-        var loss = 0
-        for j in 0 ..< n.numEntries:
-          if i == j: continue
-          loss += (overlap(nx, nn.a[j].b) - overlap(nn.a[i].b, nn.a[j].b)) # overlap (i, j) == (j, i), so maybe cache that?
-        var rep = loss < minLoss
-        if loss == minLoss:
-          let l2 = enlargement(nn.a[i].b, b) - enlargement(nn.a[i0].b, b)
-          rep = l2 < 0
-          if l2 == 0:
-            let l3 = area(nn.a[i].b) - area(nn.a[i0].b)
-            rep = l3 < 0
-            if l3 == 0:
-              rep = nn.a[i].n.numEntries < nn.a[i0].n.numEntries
-        if rep:
-          i0 = i
-          minLoss = loss
-    else:
-      for i in 0 ..< n.numEntries:
-        let loss = enlargement(nn.a[i].b, b)
-        var rep = loss < minLoss
-        if loss == minLoss:
-          let l3 = area(nn.a[i].b) - area(nn.a[i0].b)
-          rep = l3 < 0
-          if l3 == 0:
-            rep = nn.a[i].n.numEntries < nn.a[i0].n.numEntries
-        if rep:
-          i0 = i
-          minLoss = loss
-    n = nn.a[i0].n
-  return n
-
-proc pickSeeds[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; n: Node[M, D, RT, LT] | Leaf[M, D, RT, LT]; bx: Box[D, RT]): (int, int) =
-  var i0, j0: int
-  var bi, bj: type(bx)
-  var largestWaste = type(bx[0].a).low
-  for i in -1 .. n.a.high:
-    for j in 0 .. n.a.high:
-      if unlikely(i == j): continue
-      if unlikely(i < 0):
-        bi = bx
-      else:
-        bi = n.a[i].b
-      bj = n.a[j].b
-      let b = union(bi, bj)
-      let h = area(b) - area(bi) - area(bj)
-      if h > largestWaste:
-        largestWaste = h
-        i0 = i
-        j0 = j
-  return (i0, j0)
-
-proc pickNext[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; n0, n1, n2: Node[M, D, RT, LT] | Leaf[M, D, RT, LT]; b1, b2: Box[D, RT]): int =
-  let a1 = area(b1)
-  let a2 = area(b2)
-  var d = type(a1).low
-  for i in 0 ..< n0.numEntries:
-    let d1 = area(union(b1, n0.a[i].b)) - a1
-    let d2 = area(union(b2, n0.a[i].b)) - a2
-    if (d1 - d2) * (d1 - d2) > d:
-      result = i
-      d = (d1 - d2) * (d1 - d2)
-
-from algorithm import SortOrder, sort
-proc sortPlus[T](a: var openArray[T], ax: var T, cmp: proc (x, y: T): int {.closure.}, order = algorithm.SortOrder.Ascending) =
-  var j = 0
-  let sign = if order == algorithm.SortOrder.Ascending: 1 else: -1
-  for i in 1 .. a.high:
-    if cmp(a[i], a[j]) * sign < 0:
-      j = i
-  if cmp(a[j], ax) * sign < 0:
-    swap(ax, a[j])
-  a.sort(cmp, order)
-
-# R*TREE procs
-proc rstarSplit[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; n: var Node[M, D, RT, LT] | var Leaf[M, D, RT, LT]; lx: L[D, RT, LT] | N[M, D, RT, LT]): type(n) =
-  type NL = type(lx)
-  var nBest: type(n)
-  new nBest
-  var lx = lx
-  when n is Node[M, D, RT, LT]:
-    lx.n.parent = n
-  var lxbest: type(lx)
-  var m0 = lx.b[0].a.high
-  for d2 in 0 ..< 2 * D:
-    let d = d2 div 2
-    if d2 mod 2 == 0:
-      sortPlus(n.a, lx, proc (x, y: NL): int = cmp(x.b[d].a, y.b[d].a))
-    else:
-      sortPlus(n.a, lx, proc (x, y: NL): int = cmp(x.b[d].b, y.b[d].b))
-    for i in t.m - 1 .. n.a.high - t.m + 1:
-      var b = lx.b
-      for j in 0 ..< i: # we can precalculate union() for range 0 .. t.m - 1, but that seems to give no real benefit.Maybe for very large M?
-        #echo "x",j
-        b = union(n.a[j].b, b)
-      var m = margin(b)
-      b = n.a[^1].b
-      for j in i ..< n.a.high: # again, precalculation of tail would be possible
-        #echo "y",j
-        b = union(n.a[j].b, b)
-      m += margin(b)
-      if m < m0:
-        nbest[] = n[]
-        lxbest = lx
-        m0 = m
-  var i0 = -1
-  var o0 = lx.b[0].a.high
-  for i in t.m - 1 .. n.a.high - t.m + 1:
-    var b1 = lxbest.b
-    for j in 0 ..< i:
-      b1 = union(nbest.a[j].b, b1)
-    var b2 = nbest.a[^1].b
-    for j in i ..< n.a.high:
-      b2 = union(nbest.a[j].b, b2)
-    let o = overlap(b1, b2)
-    if o < o0:
-      i0 = i
-      o0 = o
-  n.a[0] = lxbest
-  for i in 0 ..< i0:
-    n.a[i + 1] = nbest.a[i]
-  new result
-  result.level = n.level
-  result.parent = n.parent
-  for i in i0 .. n.a.high:
-    result.a[i - i0] = nbest.a[i]
-  n.numEntries = i0 + 1
-  result.numEntries = M - i0
-  when n is Node[M, D, RT, LT]:
-    for i in 0 ..< result.numEntries:
-      result.a[i].n.parent = result
-
-proc quadraticSplit[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; n: var Node[M, D, RT, LT] | var Leaf[M, D, RT, LT]; lx: L[D, RT, LT] | N[M, D, RT, LT]): type(n) =
-  var n1, n2: type(n)
-  var s1, s2: int
-  new n1
-  new n2
-  n1.parent = n.parent
-  n2.parent = n.parent
-  n1.level = n.level
-  n2.level = n.level
-  var lx = lx
-  when n is Node[M, D, RT, LT]:
-    lx.n.parent = n
-  (s1, s2) = pickSeeds(t, n, lx.b)
-  assert s1 >= -1 and s2 >= 0
-  if unlikely(s1 < 0):
-    n1.a[0] = lx
-  else:
-    n1.a[0] = n.a[s1]
-    dec(n.numEntries)
-    if s2 == n.numEntries: # important fix
-      s2 = s1
-    n.a[s1] = n.a[n.numEntries]
-  inc(n1.numEntries)
-  var b1 = n1.a[0].b
-  n2.a[0] = n.a[s2]
-  dec(n.numEntries)
-  n.a[s2] = n.a[n.numEntries]
-  inc(n2.numEntries)
-  var b2 = n2.a[0].b
-  if s1 >= 0:
-    n.a[n.numEntries] = lx
-    inc(n.numEntries)
-  while n.numEntries > 0 and n1.numEntries < (t.bigM + 1 - t.m) and n2.numEntries < (t.bigM + 1 - t.m):
-    let next = pickNext(t, n, n1, n2, b1, b2)
-    let d1 = area(union(b1, n.a[next].b)) - area(b1)
-    let d2 = area(union(b2, n.a[next].b)) - area(b2)
-    if (d1 < d2) or (d1 == d2 and ((area(b1) < area(b2)) or (area(b1) == area(b2) and n1.numEntries < n2.numEntries))):
-      n1.a[n1.numEntries] = n.a[next]
-      b1 = union(b1, n.a[next].b)
-      inc(n1.numEntries)
-    else:
-      n2.a[n2.numEntries] = n.a[next]
-      b2 = union(b2, n.a[next].b)
-      inc(n2.numEntries)
-    dec(n.numEntries)
-    n.a[next] = n.a[n.numEntries]
-  if n.numEntries == 0:
-    discard
-  elif n1.numEntries == (t.bigM + 1 - t.m):
-    while n.numEntries > 0:
-      dec(n.numEntries)
-      n2.a[n2.numEntries] = n.a[n.numEntries]
-      inc(n2.numEntries)
-  elif n2.numEntries == (t.bigM + 1 - t.m):
-    while n.numEntries > 0:
-      dec(n.numEntries)
-      n1.a[n1.numEntries] = n.a[n.numEntries]
-      inc(n1.numEntries)
-  when n is Node[M, D, RT, LT]:
-    for i in 0 ..< n2.numEntries:
-      n2.a[i].n.parent = n2
-  n[] = n1[]
-  return n2
-
-proc overflowTreatment[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; n: var Node[M, D, RT, LT] | var Leaf[M, D, RT, LT]; lx: L[D, RT, LT] | N[M, D, RT, LT]): type(n)
-
-proc adjustTree[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; l, ll: H[M, D, RT, LT]; hb: Box[D, RT]) =
-  var n = l
-  var nn = ll
-  assert n != nil
-  while true:
-    if n == t.root:
-      if nn == nil:
-        break
-      t.root = newNode[M, D, RT, LT]()
-      t.root.level = n.level + 1
-      Node[M, D, RT, LT](t.root).a[0].n = n
-      n.parent = t.root
-      nn.parent = t.root
-      t.root.numEntries = 1
-    let p = Node[M, D, RT, LT](n.parent)
-    var i = 0
-    while p.a[i].n != n:
-      inc(i)
-    var b: type(p.a[0].b)
-    if n of Leaf[M, D, RT, LT]:
-      when false:#if likely(nn.isNil): # no performance gain
-        b = union(p.a[i].b, Leaf[M, D, RT, LT](n).a[n.numEntries - 1].b)
-      else:
-        b = Leaf[M, D, RT, LT](n).a[0].b
-        for j in 1 ..< n.numEntries:
-          b = trtree.union(b, Leaf[M, D, RT, LT](n).a[j].b)
-    elif n of Node[M, D, RT, LT]:
-      b = Node[M, D, RT, LT](n).a[0].b
-      for j in 1 ..< n.numEntries:
-        b = union(b, Node[M, D, RT, LT](n).a[j].b)
-    else:
-      assert false
-    #if nn.isNil and p.a[i].b == b: break # no performance gain
-    p.a[i].b = b
-    n = H[M, D, RT, LT](p)
-    if unlikely(nn != nil):
-      if nn of Leaf[M, D, RT, LT]:
-        b = Leaf[M, D, RT, LT](nn).a[0].b
-        for j in 1 ..< nn.numEntries:
-          b = union(b, Leaf[M, D, RT, LT](nn).a[j].b)
-      elif nn of Node[M, D, RT, LT]:
-        b = Node[M, D, RT, LT](nn).a[0].b
-        for j in 1 ..< nn.numEntries:
-          b = union(b, Node[M, D, RT, LT](nn).a[j].b)
-      else:
-        assert false
-      if p.numEntries < p.a.len:
-        p.a[p.numEntries].b = b
-        p.a[p.numEntries].n = nn
-        inc(p.numEntries)
-        assert n != nil
-        nn = nil
-      else:
-        let h: N[M, D, RT, LT] = (b, nn)
-        nn = quadraticSplit(t, p, h)
-    assert n == H[M, D, RT, LT](p)
-    assert n != nil
-    assert t.root != nil
-
-proc insert*[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; leaf: N[M, D, RT, LT] | L[D, RT, LT]; level: int = 0) =
-  when leaf is N[M, D, RT, LT]:
-    assert level > 0
-    type NodeLeaf = Node[M, D, RT, LT]
-  else:
-    assert level == 0
-    type NodeLeaf = Leaf[M, D, RT, LT]
-  for d in leaf.b:
-    assert d.a <= d.b
-  let l = NodeLeaf(chooseSubtree(t, leaf.b, level))
-  if l.numEntries < l.a.len:
-    l.a[l.numEntries] = leaf
-    inc(l.numEntries)
-    when leaf is N[M, D, RT, LT]:
-      leaf.n.parent = l
-    adjustTree(t, l, nil, leaf.b)
-  else:
-    let l2 = quadraticSplit(t, l, leaf)
-    assert l2.level == l.level
-    adjustTree(t, l, l2, leaf.b)
-
-# R*Tree insert procs
-proc rsinsert[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; leaf: N[M, D, RT, LT] | L[D, RT, LT]; level: int)
-
-proc reInsert[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; n: var Node[M, D, RT, LT] | var Leaf[M, D, RT, LT]; lx: L[D, RT, LT] | N[M, D, RT, LT]) =
-  type NL = type(lx)
-  var lx = lx
-  var buf: type(n.a)
-  let p = Node[M, D, RT, LT](n.parent)
-  var i = 0
-  while p.a[i].n != n:
-    inc(i)
-  let c = center(p.a[i].b)
-  sortPlus(n.a, lx, proc (x, y: NL): int = cmp(distance(center(x.b), c), distance(center(y.b), c)))
-  n.numEntries = M - t.p
-  swap(n.a[n.numEntries], lx)
-  inc n.numEntries
-  var b = n.a[0].b
-  for i in 1 ..< n.numEntries:
-    b = union(b, n.a[i].b)
-  p.a[i].b = b
-  for i in M - t.p + 1 .. n.a.high:
-    buf[i] = n.a[i]
-  rsinsert(t, lx, n.level)
-  for i in M - t.p + 1 .. n.a.high:
-    rsinsert(t, buf[i], n.level)
-
-proc overflowTreatment[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; n: var Node[M, D, RT, LT] | var Leaf[M, D, RT, LT]; lx: L[D, RT, LT] | N[M, D, RT, LT]): type(n) =
-  if n.level != t.root.level and t.firstOverflow[n.level]:
-    t.firstOverflow[n.level] = false
-    reInsert(t, n, lx)
-    return nil
-  else:
-    let l2 = rstarSplit(t, n, lx)
-    assert l2.level == n.level
-    return l2
-
-proc rsinsert[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; leaf: N[M, D, RT, LT] | L[D, RT, LT]; level: int) =
-  when leaf is N[M, D, RT, LT]:
-    assert level > 0
-    type NodeLeaf = Node[M, D, RT, LT]
-  else:
-    assert level == 0
-    type NodeLeaf = Leaf[M, D, RT, LT]
-  let l = NodeLeaf(chooseSubtree(t, leaf.b, level))
-  if l.numEntries < l.a.len:
-    l.a[l.numEntries] = leaf
-    inc(l.numEntries)
-    when leaf is N[M, D, RT, LT]:
-      leaf.n.parent = l
-    adjustTree(t, l, nil, leaf.b)
-  else:
-    when leaf is N[M, D, RT, LT]: # TODO do we need this?
-      leaf.n.parent = l
-    let l2 = overflowTreatment(t, l, leaf)
-    if l2 != nil:
-      assert l2.level == l.level
-      adjustTree(t, l, l2, leaf.b)
-
-proc insert*[M, D: Dim; RT, LT](t: RStarTree[M, D, RT, LT]; leaf: L[D, RT, LT]) =
-  for d in leaf.b:
-    assert d.a <= d.b
-  for i in mitems(t.firstOverflow):
-    i = true
-  rsinsert(t, leaf, 0)
-
-# delete
-proc findLeaf[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; leaf: L[D, RT, LT]): Leaf[M, D, RT, LT] =
-  proc fl[M, D: Dim; RT, LT](h: H[M, D, RT, LT]; leaf: L[D, RT, LT]): Leaf[M, D, RT, LT] =
-    var n = h
-    if n of Node[M, D, RT, LT]:
-      for i in 0 ..< n.numEntries:
-        if intersect(Node[M, D, RT, LT](n).a[i].b, leaf.b):
-          let l = fl(Node[M, D, RT, LT](n).a[i].n, leaf)
-          if l != nil:
-            return l
-    elif n of Leaf[M, D, RT, LT]:
-      for i in 0 ..< n.numEntries:
-        if Leaf[M, D, RT, LT](n).a[i] == leaf:
-          return Leaf[M, D, RT, LT](n)
-    else:
-      assert false
-    return nil
-  fl(t.root, leaf)
-
-proc condenseTree[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; leaf: Leaf[M, D, RT, LT]) =
-  var n: H[M, D, RT, LT] = leaf
-  var q = newSeq[H[M, D, RT, LT]]()
-  var b: typeof(leaf.a[0].b)
-  while n != t.root:
-    let p = Node[M, D, RT, LT](n.parent)
-    var i = 0
-    while p.a[i].n != n:
-      inc(i)
-    if n.numEntries < t.m:
-      dec(p.numEntries)
-      p.a[i] = p.a[p.numEntries]
-      q.add(n)
-    else:
-      if n of Leaf[M, D, RT, LT]:
-        b = Leaf[M, D, RT, LT](n).a[0].b
-        for j in 1 ..< n.numEntries:
-          b = union(b, Leaf[M, D, RT, LT](n).a[j].b)
-      elif n of Node[M, D, RT, LT]:
-        b = Node[M, D, RT, LT](n).a[0].b
-        for j in 1 ..< n.numEntries:
-          b = union(b, Node[M, D, RT, LT](n).a[j].b)
-      else:
-        assert false
-      p.a[i].b = b
-    n = n.parent
-  if t of RStarTree[M, D, RT, LT]:
-    for n in q:
-      if n of Leaf[M, D, RT, LT]:
-        for i in 0 ..< n.numEntries:
-          for i in mitems(RStarTree[M, D, RT, LT](t).firstOverflow):
-            i = true
-          rsinsert(RStarTree[M, D, RT, LT](t), Leaf[M, D, RT, LT](n).a[i], 0)
-      elif n of Node[M, D, RT, LT]:
-        for i in 0 ..< n.numEntries:
-          for i in mitems(RStarTree[M, D, RT, LT](t).firstOverflow):
-            i = true
-          rsinsert(RStarTree[M, D, RT, LT](t), Node[M, D, RT, LT](n).a[i], n.level)
-      else:
-        assert false
-  else:
-    for n in q:
-      if n of Leaf[M, D, RT, LT]:
-        for i in 0 ..< n.numEntries:
-          insert(t, Leaf[M, D, RT, LT](n).a[i])
-      elif n of Node[M, D, RT, LT]:
-        for i in 0 ..< n.numEntries:
-          insert(t, Node[M, D, RT, LT](n).a[i], n.level)
-      else:
-        assert false
-
-proc delete*[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; leaf: L[D, RT, LT]): bool {.discardable.} =
-  let l = findLeaf(t, leaf)
-  if l.isNil:
-    return false
-  else:
-    var i = 0
-    while l.a[i] != leaf:
-      inc(i)
-    dec(l.numEntries)
-    l.a[i] = l.a[l.numEntries]
-    condenseTree(t, l)
-    if t.root.numEntries == 1:
-      if t.root of Node[M, D, RT, LT]:
-        t.root = Node[M, D, RT, LT](t.root).a[0].n
-      t.root.parent = nil
-    return true
-
-
-var t = [4, 1, 3, 2]
-var xt = 7
-sortPlus(t, xt, system.cmp, SortOrder.Ascending)
-echo xt, " ", t
-
-type
-  RSE = L[2, int, int]
-  RSeq = seq[RSE]
-
-proc rseq_search(rs: RSeq; rse: RSE): seq[int] =
-  result = newSeq[int]()
-  for i in rs:
-    if intersect(i.b, rse.b):
-      result.add(i.l)
-
-proc rseq_delete(rs: var RSeq; rse: RSE): bool =
-  for i in 0 .. rs.high:
-    if rs[i] == rse:
-      #rs.delete(i)
-      rs[i] = rs[rs.high]
-      rs.setLen(rs.len - 1)
-      return true
-
-import random, algorithm
-
-proc test(n: int) =
-  var b: Box[2, int]
-  echo center(b)
-  var x1, x2, y1, y2: int
-  var t = newRStarTree[8, 2, int, int]()
-  #var t = newRTree[8, 2, int, int]()
-  var rs = newSeq[RSE]()
-  for i in 0 .. 5:
-    for i in 0 .. n - 1:
-      x1 = rand(1000)
-      y1 = rand(1000)
-      x2 = x1 + rand(25)
-      y2 = y1 + rand(25)
-      b = [(x1, x2), (y1, y2)]
-      let el: L[2, int, int] = (b, i + 7)
-      t.insert(el)
-      rs.add(el)
-
-    for i in 0 .. (n div 4):
-      let j = rand(rs.high)
-      var el = rs[j]
-      assert t.delete(el)
-      assert rs.rseq_delete(el)
-
-    for i in 0 .. n - 1:
-      x1 = rand(1000)
-      y1 = rand(1000)
-      x2 = x1 + rand(100)
-      y2 = y1 + rand(100)
-      b = [(x1, x2), (y1, y2)]
-      let el: L[2, int, int] = (b, i)
-      let r = search(t, b)
-      let r2 = rseq_search(rs, el)
-      assert r.len == r2.len
-      assert r.sorted(system.cmp) == r2.sorted(system.cmp)
-
-test(500)
diff --git a/tests/generics/tstatic_constrained.nim b/tests/generics/tstatic_constrained.nim
new file mode 100644
index 000000000..d356b9d1c
--- /dev/null
+++ b/tests/generics/tstatic_constrained.nim
@@ -0,0 +1,79 @@
+discard """
+  cmd: "nim check --hints:off --warnings:off $file"
+  action: "reject"
+  nimout:'''
+tstatic_constrained.nim(44, 22) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(30, 5)]
+got: <typedesc[int], int literal(10)>
+but expected: <T: float or string, Y>
+tstatic_constrained.nim(44, 22) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(30, 5)]
+got: <typedesc[int], int literal(10)>
+but expected: <T: float or string, Y>
+tstatic_constrained.nim(44, 31) Error: object constructor needs an object type [error]
+tstatic_constrained.nim(44, 31) Error: expression '' has no type (or is ambiguous)
+tstatic_constrained.nim(45, 22) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(30, 5)]
+got: <typedesc[byte], uint8>
+but expected: <T: float or string, Y>
+tstatic_constrained.nim(45, 22) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(30, 5)]
+got: <typedesc[byte], uint8>
+but expected: <T: float or string, Y>
+tstatic_constrained.nim(45, 34) Error: object constructor needs an object type [error]
+tstatic_constrained.nim(45, 34) Error: expression '' has no type (or is ambiguous)
+tstatic_constrained.nim(77, 14) Error: cannot instantiate MyType [type declared in tstatic_constrained.nim(71, 5)]
+got: <typedesc[float], float64>
+but expected: <T: MyConstraint, Y>
+'''
+"""
+block:
+  type 
+    MyType[T; X: static T] = object
+      data: T
+    MyOtherType[T: float or string, Y: static T] = object
+
+  func f[T,X](a: MyType[T,X]): MyType[T,X] =
+    when T is string:
+      MyType[T,X](data: a.data & X)
+    else:
+      MyType[T,X](data: a.data + X)
+
+  discard MyType[int, 2](data: 1)
+  discard MyType[string, "Helelello"](data: "Hmmm")
+  discard MyType[int, 2](data: 1).f()
+  discard MyType[string, "Helelello"](data: "Hmmm").f()
+  discard MyOtherType[float, 1.3]()
+  discard MyOtherType[string, "Hello"]()
+  discard MyOtherType[int, 10]()
+  discard MyOtherType[byte, 10u8]()
+
+block:
+  type
+    Moduloable = concept m, type M
+      m mod m is M
+    Addable = concept a, type A
+      a + a is A
+    Modulo[T: Moduloable; Mod: static T] = distinct T
+    ModuloAdd[T: Moduloable or Addable; Mod: static T] = distinct T
+    ModuAddable = Addable or Moduloable
+    ModdAddClass[T: ModuAddable; Mod: static T] = distinct T
+
+  proc toMod[T](val: T, modVal: static T): Modulo[T, modVal] =
+    mixin `mod`
+    Modulo[T, modVal](val mod modVal)
+  var
+    a = 3231.toMod(10)
+    b = 5483.toMod(10)
+  discard ModuloAdd[int, 3](0)
+  discard ModdAddClass[int, 3](0)
+
+block:
+  type
+    MyConstraint = int or string
+    MyOtherConstraint[T] = object
+    MyType[T: MyConstraint; Y: static T] = object
+    MyOtherType[T: MyOtherConstraint; Y: static T] = object
+
+  var 
+    a: MyType[int, 10]
+    b: MyType[string, "hello"]
+    c: MyType[float, 10d]
+    d: MyOtherType[MyOtherConstraint[float],MyOtherConstraint[float]()]
+    e: MyOtherType[MyOtherConstraint[int], MyOtherConstraint[int]()]
diff --git a/tests/generics/tthread_generic.nim b/tests/generics/tthread_generic.nim
index 2af5a7615..300da56a6 100644
--- a/tests/generics/tthread_generic.nim
+++ b/tests/generics/tthread_generic.nim
@@ -1,5 +1,5 @@
 discard """
-  cmd: "nim $target --hints:on --threads:on $options $file"
+  matrix: "--mm:refc; --mm:orc"
   action: compile
 """
 
diff --git a/tests/generics/tuninstantiated_failure.nim b/tests/generics/tuninstantiated_failure.nim
new file mode 100644
index 000000000..f3d5b34b8
--- /dev/null
+++ b/tests/generics/tuninstantiated_failure.nim
@@ -0,0 +1,16 @@
+discard """
+cmd: "nim check $file"
+"""
+
+type
+  Test[T, K] = object
+    name: string
+  Something = Test[int]
+
+func `[]`[T, K](x: var Test[T, K], idx: int): var Test[T, K] =
+  x
+
+var b: Something
+# Should give an error since Something isn't a valid Test
+b[0].name = "Test" #[tt.Error
+ ^  expression '' has no type (or is ambiguous)]#
diff --git a/tests/generics/tuninstantiatedgenericcalls.nim b/tests/generics/tuninstantiatedgenericcalls.nim
new file mode 100644
index 000000000..f33fc8967
--- /dev/null
+++ b/tests/generics/tuninstantiatedgenericcalls.nim
@@ -0,0 +1,517 @@
+# Cases that used to only work due to weird workarounds in the compiler
+# involving not instantiating calls in generic bodies which are removed
+# due to breaking statics.
+# The issue was that these calls are compiled as regular expressions at
+# the generic declaration with unresolved generic parameter types,
+# which are special cased in some places in the compiler, but sometimes
+# treated like real types.
+
+block:
+  type Base10 = object
+
+  func maxLen(T: typedesc[Base10], I: type): int8 =
+    when I is uint8:
+      3
+    elif I is uint16:
+      5
+    elif I is uint32:
+      10
+    elif I is uint64:
+      20
+    else:
+      when sizeof(uint) == 4:
+        10
+      else:
+        20
+  
+  type
+    Base10Buf[T: SomeUnsignedInt] = object
+      data: array[maxLen(Base10, T), byte]
+      len: int8
+
+  var x: Base10Buf[uint32]
+  doAssert x.data.len == 10
+  var y: Base10Buf[uint16]
+  doAssert y.data.len == 5
+
+import typetraits
+
+block thardcases:
+  proc typeNameLen(x: typedesc): int {.compileTime.} =
+    result = x.name.len
+  macro selectType(a, b: typedesc): typedesc =
+    result = a
+
+  type
+    Foo[T] = object
+      data1: array[T.high, int]
+      data2: array[typeNameLen(T), float]
+      data3: array[0..T.typeNameLen, selectType(float, int)]
+  
+  type MyEnum = enum A, B, C, D
+
+  var f1: Foo[MyEnum]
+  var f2: Foo[int8]
+
+  doAssert high(f1.data1) == 2 # (D = 3) - 1 == 2
+  doAssert high(f1.data2) == 5 # (MyEnum.len = 6) - 1 == 5
+
+  doAssert high(f2.data1) == 126 # 127 - 1 == 126
+  doAssert high(f2.data2) == 3 # int8.len - 1 == 3
+
+  static:
+    doAssert high(f1.data1) == ord(C)
+    doAssert high(f1.data2) == 5 # length of MyEnum minus one, because we used T.high
+
+    doAssert high(f2.data1) == 126
+    doAssert high(f2.data2) == 3
+
+    doAssert high(f1.data3) == 6 # length of MyEnum
+    doAssert high(f2.data3) == 4 # length of int8
+
+    doAssert f2.data3[0] is float
+
+import muninstantiatedgenericcalls
+
+block:
+  var x: Leb128Buf[uint32]
+  doAssert x.data.len == 5
+  var y: Leb128Buf[uint16]
+  doAssert y.data.len == 3
+
+import macros
+
+block: # issue #12415
+  macro isSomePointerImpl(t: typedesc): bool =
+    var impl = t.getTypeInst[1].getTypeImpl
+    if impl.kind == nnkDistinctTy:
+      impl = impl[0].getTypeImpl
+    if impl.kind in {nnkPtrTy,nnkRefTy}:
+      result = newLit(true)
+    elif impl.kind == nnkSym and impl.eqIdent("pointer"):
+      result = newLit(true)
+    else:
+      result = newLit(false)
+
+  proc isSomePointer[T](t: typedesc[T]): bool {.compileTime.} =
+    isSomePointerImpl(t)
+
+  type
+    Option[T] = object
+      ## An optional type that stores its value and state separately in a boolean.
+      when isSomePointer(typedesc(T)):
+        val: T
+      else:
+        val: T
+        has: bool
+  var x: Option[ref int]
+  doAssert not compiles(x.has)
+  var y: Option[int]
+  doAssert compiles(y.has)
+
+block: # issue #2002
+  proc isNillable(T: typedesc): bool =
+    when compiles((let v: T = nil)):
+      return true
+    else:
+      return false
+
+  type
+    Foo[T] = object
+      when isNillable(T):
+        nillable: float
+      else:
+        notnillable: int
+
+  var val1: Foo[ref int]
+  doAssert compiles(val1.nillable)
+  doAssert not compiles(val1.notnillable)
+  var val2: Foo[int]
+  doAssert not compiles(val2.nillable)
+  doAssert compiles(val2.notnillable)
+
+block: # issue #1771
+  type
+    Foo[X, T] = object
+      bar: array[X.low..X.high, T]
+
+  proc test[X, T](f: Foo[X, T]): T =
+    f.bar[X.low]
+
+  var a: Foo[range[0..2], float]
+  doAssert test(a) == 0.0
+
+block: # issue #23730
+  proc test(M: static[int]): array[1 shl M, int] = discard
+  doAssert len(test(3)) == 8
+  doAssert len(test(5)) == 32
+
+block: # issue #19819
+  type
+    Example[N: static int] = distinct int
+    What[E: Example] = Example[E.N + E.N]
+
+block: # issue #23339
+  type
+    A = object
+    B = object
+  template aToB(t: typedesc[A]): typedesc = B
+  type
+    Inner[I] = object
+      innerField: I
+    Outer[O] = object
+      outerField: Inner[O.aToB]
+  var x: Outer[A]
+  doAssert typeof(x.outerField.innerField) is B
+
+block: # deref syntax
+  type
+    Enqueueable = concept x
+      x is ptr
+    Foo[T: Enqueueable] = object
+      x: typeof(default(T)[])
+
+  proc p[T](f: Foo[T]) =
+    var bar: Foo[T]
+    discard
+  var foo: Foo[ptr int]
+  p(foo)
+  doAssert foo.x is int
+  foo.x = 123
+  doAssert foo.x == 123
+  inc foo.x
+  doAssert foo.x == 124
+
+block:
+  type Generic[T] = object
+    field: T
+  macro foo(x: typed): untyped = x
+  macro bar[T](x: typedesc[Generic[T]]): untyped = x
+  type
+    Foo[T] = object
+      field: Generic[int].foo()
+    Foo2[T] = object
+      field: Generic[T].foo()
+    Bar[T] = object
+      field: Generic[int].bar()
+    Bar2[T] = object
+      field: Generic[T].bar()
+  var x: Foo[int]
+  var x2: Foo2[int]
+  var y: Bar[int]
+  var y2: Bar2[int]
+
+block:
+  macro pick(x: static int): untyped =
+    if x < 100:
+      result = bindSym"int"
+    else:
+      result = bindSym"float"
+  
+  type Foo[T: static int] = object
+    fixed1: pick(25)
+    fixed2: pick(125)
+    unknown: pick(T)
+  
+  var a: Foo[123]
+  doAssert a.fixed1 is int
+  doAssert a.fixed2 is float
+  doAssert a.unknown is float
+  var b: Foo[23]
+  doAssert b.fixed1 is int
+  doAssert b.fixed2 is float
+  doAssert b.unknown is int
+
+import std/sequtils
+
+block: # version of #23432 with `typed`, don't delay instantiation
+  type
+    Future[T] = object
+    InternalRaisesFuture[T, E] = object
+  macro Raising[T](F: typedesc[Future[T]], E: varargs[typed]): untyped =
+    let raises = nnkTupleConstr.newTree(E.mapIt(it))
+    nnkBracketExpr.newTree(
+      ident "InternalRaisesFuture",
+      nnkDotExpr.newTree(F, ident"T"),
+      raises
+    )
+  type X[E] = Future[void].Raising(E)
+  proc f(x: X) = discard
+  var v: Future[void].Raising([ValueError])
+  f(v)
+
+block: # issue #22647
+  proc c0(n: static int): int = 8
+  proc c1(n: static int): int = n div 2
+  proc c2(n: static int): int = n * 2
+  proc c3(n: static int, n2: int): int = n * n2
+  proc `**`(n: static int, n2: int): int = n * n2
+  proc c4(n: int, n2: int): int = n * n2
+
+  type
+    a[N: static int] = object
+      f0 : array[N, int]
+
+    b[N: static int] = object
+      f0 : a[c0(N)]  # does not work
+      f1 : a[c1(N)]  # does not work
+      f2 : a[c2(N)]  # does not work
+      f3 : a[N * 2]  # does not work
+      f4 : a[N]      # works
+      f5: a[c3(N, 2)]
+      f6: a[N ** 2]
+      f7: a[2 * N]
+      f8: a[c4(N, 2)]
+
+  proc p[N: static int](x : a[N]) = discard x.f0[0]
+  template check(x, s: untyped) =
+    p(x)
+    doAssert x is a[s]
+    doAssert x.N == s
+    doAssert typeof(x).N == s
+    doAssert x.f0 == default(array[s, int])
+    doAssert x.f0.len == s
+    proc p2[N: static int](y : a[N]) {.gensym.} =
+      doAssert y is a[s]
+      doAssert y.N == s
+      doAssert typeof(y).N == s
+      doAssert y.f0 == default(array[s, int])
+      doAssert y.f0.len == s
+    p2(x)
+    proc p3(z: typeof(x)) {.gensym.} = discard
+    p3(default(a[s]))
+  proc p[N: static int](x : b[N]) =
+    x.f0.check(8)
+    x.f1.check(2)
+    x.f2.check(8)
+    x.f3.check(8)
+    x.f4.check(4)
+    x.f5.check(8)
+    x.f6.check(8)
+    x.f7.check(8)
+    x.f8.check(8)
+
+  var x: b[4]
+  x.p()
+
+block: # issue #1969
+  type ZeroGenerator = object
+  proc next(g: ZeroGenerator): int = 0
+  # This compiles.
+  type TripleOfInts = tuple
+    a, b, c: typeof(new(ZeroGenerator)[].next)
+  # This raises a compiler error before it's even instantiated.
+  # The `new` proc can't be resolved because `Generator` is not defined.
+  type TripleLike[Generator] = tuple
+    a, b, c: typeof(new(Generator)[].next)
+
+import std/atomics
+
+block: # issue #12720
+  const CacheLineSize = 128
+  type
+    Enqueueable = concept x, type T
+      x is ptr
+      x.next is Atomic[pointer]
+    MyChannel[T: Enqueueable] = object
+      pad: array[CacheLineSize - sizeof(default(T)[]), byte]
+      dummy: typeof(default(T)[])
+
+block: # issue #12714
+  type
+    Enqueueable = concept x, type T
+      x is ptr
+      x.next is Atomic[pointer]
+    MyChannel[T: Enqueueable] = object
+      dummy: type(default(T)[])
+
+block: # issue #24044
+  type ArrayBuf[N: static int, T = byte] = object
+    buf: array[N, T]
+  template maxLen(T: type): int =
+    sizeof(T) * 2
+  type MyBuf[I] = ArrayBuf[maxLen(I)]
+  var v: MyBuf[int]
+
+block: # issue #15959
+  proc my[T](a: T): typeof(a[0]) = discard
+  proc my2[T](a: T): array[sizeof(a[0]), T] = discard
+  proc byLent2[T](a: T): lent type(a[0]) = a[0] # Error: type mismatch: got <T, int literal(0)>
+  proc byLent3[T](a: T): lent typeof(a[0]) = a[0] # ditto
+  proc byLent4[T](a: T): lent[type(a[0])] = a[0] # Error: no generic parameters allowed for lent
+  var x = @[1, 2, 3]
+  doAssert my(x) is int
+  doAssert my2(x) is array[sizeof(int), seq[int]]
+  doAssert byLent2(x) == 1
+  doAssert byLent2(x) is lent int
+  doAssert byLent3(x) == 1
+  doAssert byLent3(x) is lent int
+  doAssert byLent4(x) == 1
+  doAssert byLent4(x) is lent int
+  proc fn[U](a: U): auto = a
+  proc my3[T](a: T, b: typeof(fn(a))) = discard
+  my3(x, x)
+  doAssert not compiles(my3(x, x[0]))
+
+block: # issue #22342, type section version of #22607
+  type GenAlias[isInt: static bool] = (
+    when isInt:
+      int
+    else:
+      float
+  )
+  doAssert GenAlias[true] is int
+  doAssert GenAlias[false] is float
+  proc foo(T: static bool): GenAlias[T] = discard
+  doAssert foo(true) is int
+  doAssert foo(false) is float
+  proc foo[T: static bool](v: var GenAlias[T]) =
+    v += 1
+  var x: int
+  foo[true](x)
+  doAssert not compiles(foo[false](x))
+  foo[true](x)
+  doAssert x == 2
+  var y: float
+  foo[false](y)
+  doAssert not compiles(foo[true](y))
+  foo[false](y)
+  doAssert y == 2
+
+block: # `when`, test no constant semchecks
+  type Foo[T] = (
+    when false:
+      {.error: "bad".}
+    elif defined(neverDefined):
+      {.error: "bad 2".}
+    else:
+      T
+  )
+  var x: Foo[int]
+  type Bar[T] = (
+    when true:
+      T
+    elif defined(js):
+      {.error: "bad".}
+    else:
+      {.error: "bad 2".}
+  )
+  var y: Bar[int]
+
+block: # weird regression
+  type
+    Foo[T] = distinct int
+    Bar[T, U] = distinct int
+  proc foo[T, U](x: static Foo[T], y: static Bar[T, U]): Foo[T] =
+    # signature gives:
+    # Error: cannot instantiate Bar
+    # got: <typedesc[T], U>
+    # but expected: <T, U>
+    x
+  doAssert foo(Foo[int](1), Bar[int, int](2)).int == 1
+
+block: # issue #24090
+  type M[V] = object
+  template y[V](N: type M, v: V): M[V] = default(M[V])
+  proc d(x: int | int, f: M[int] = M.y(0)) = discard
+  d(0, M.y(0))
+  type Foo[T] = object
+    x: typeof(M.y(default(T)))
+  var a: Foo[int]
+  doAssert a.x is M[int]
+  var b: Foo[float]
+  doAssert b.x is M[float]
+  doAssert not (compiles do:
+    type Bar[T] = object
+      x: typeof(M()) # actually fails here immediately
+    var bar: Bar[int])
+  doAssert not (compiles do:
+    type Bar[T] = object
+      x: typeof(default(M))
+    var bar: Bar[int]
+    # gives "undeclared identifier x" because of #24091,
+    # normally it should fail in the line above
+    echo bar.x)
+  proc foo[T: M](x: T = default(T)) = discard x
+  foo[M[int]]()
+  doAssert not compiles(foo())
+
+block: # above but encountered by sigmatch using replaceTypeVarsN
+  type Opt[T] = object
+    x: T
+  proc none[T](x: type Opt, y: typedesc[T]): Opt[T] = discard
+  proc foo[T](x: T, a = Opt.none(int)) = discard
+  foo(1, a = Opt.none(int))
+  foo(1)
+
+block: # real version of above
+  type Opt[T] = object
+    x: T
+  template none(x: type Opt, T: type): Opt[T] = Opt[T]()
+  proc foo[T](x: T, a = Opt.none(int)) = discard
+  foo(1, a = Opt.none(int))
+  foo(1)
+
+block: # issue #20880
+  type
+    Child[n: static int] = object
+      data: array[n, int]
+    Parent[n: static int] = object
+      child: Child[3*n]
+  const n = 3
+  doAssert $(typeof Parent[n*3]()) == "Parent[9]"
+  doAssert $(typeof Parent[1]().child) == "Child[3]"
+  doAssert Parent[1]().child.data.len == 3
+
+{.experimental: "dynamicBindSym".}
+block: # issue #16774
+  type SecretWord = distinct uint64
+  const WordBitWidth = 8 * sizeof(uint64)
+  func wordsRequired(bits: int): int {.compileTime.} =
+    ## Compute the number of limbs required
+    # from the **announced** bit length
+    (bits + WordBitWidth - 1) div WordBitWidth
+  type
+    Curve = enum BLS12_381
+    BigInt[bits: static int] = object
+      limbs: array[bits.wordsRequired, SecretWord]
+  const BLS12_381_Modulus = default(BigInt[381])
+  macro Mod(C: static Curve): untyped =
+    ## Get the Modulus associated to a curve
+    result = bindSym($C & "_Modulus")
+  macro getCurveBitwidth(C: static Curve): untyped =
+    result = nnkDotExpr.newTree(
+      getAST(Mod(C)),
+      ident"bits"
+    )
+  type Fp[C: static Curve] = object
+    ## Finite Fields / Modular arithmetic
+    ## modulo the curve modulus
+    mres: BigInt[getCurveBitwidth(C)]
+  var x: Fp[BLS12_381]
+  doAssert x.mres.limbs.len == wordsRequired(getCurveBitWidth(BLS12_381))
+  # minimized, as if we haven't tested it already:
+  macro makeIntLit(c: static int): untyped =
+    result = newLit(c)
+  type Test[T: static int] = object
+    myArray: array[makeIntLit(T), int]
+  var y: Test[2]
+  doAssert y.myArray.len == 2
+  var z: Test[4]
+  doAssert z.myArray.len == 4
+
+block: # issue #16175
+  type
+    Thing[D: static uint] = object
+      when D == 0:
+        kid: char
+      else:
+        kid: Thing[D-1]
+  var t2 = Thing[3]()
+  doAssert t2.kid is Thing[2.uint]
+  doAssert t2.kid.kid is Thing[1.uint]
+  doAssert t2.kid.kid.kid is Thing[0.uint]
+  doAssert t2.kid.kid.kid.kid is char
+  var s = Thing[1]()
+  doAssert s.kid is Thing[0.uint]
+  doAssert s.kid.kid is char
diff --git a/tests/generics/twrong_explicit_typeargs.nim b/tests/generics/twrong_explicit_typeargs.nim
deleted file mode 100644
index e47b38e99..000000000
--- a/tests/generics/twrong_explicit_typeargs.nim
+++ /dev/null
@@ -1,16 +0,0 @@
-discard """
-  errormsg: "cannot instantiate: 'newImage[string]'"
-  line: 16
-"""
-
-# bug #4084
-type
-  Image[T] = object
-    data: seq[T]
-
-proc newImage[T: int32|int64](w, h: int): ref Image[T] =
-  new(result)
-  result.data = newSeq[T](w * h)
-
-var correct = newImage[int32](320, 200)
-var wrong = newImage[string](320, 200)
diff --git a/tests/generics/twrong_generic_object.nim b/tests/generics/twrong_generic_object.nim
index 442b89ea1..4951f735f 100644
--- a/tests/generics/twrong_generic_object.nim
+++ b/tests/generics/twrong_generic_object.nim
@@ -1,6 +1,6 @@
 discard """
-  errormsg: "cannot instantiate: 'GenericNodeObj[T]'; Maybe generic arguments are missing?"
-  line: 21
+  errormsg: "'Node' is not a concrete type"
+  line: 11
 """
 # bug #2509
 type