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/mbind_bracket.nim17
-rw-r--r--tests/generics/mclosed_sym.nim10
-rw-r--r--tests/generics/mdotlookup.nim28
-rw-r--r--tests/generics/mfriends.nim11
-rw-r--r--tests/generics/mmodule_same_as_proc.nim2
-rw-r--r--tests/generics/module_with_generics.nim14
-rw-r--r--tests/generics/mopensymimport1.nim34
-rw-r--r--tests/generics/mopensymimport2.nim16
-rw-r--r--tests/generics/moverloading_typedesc.nim11
-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.nim6
-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/t2tables.nim15
-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/t5602_inheritence.nim24
-rw-r--r--tests/generics/t5926.nim22
-rw-r--r--tests/generics/t6060.nim11
-rw-r--r--tests/generics/t6137.nim28
-rw-r--r--tests/generics/t6637.nim9
-rw-r--r--tests/generics/t7141.nim9
-rw-r--r--tests/generics/t7446.nim10
-rw-r--r--tests/generics/t7839.nim9
-rw-r--r--tests/generics/t8270.nim7
-rw-r--r--tests/generics/taliashijack.nim8
-rw-r--r--tests/generics/tarc_misc.nim21
-rw-r--r--tests/generics/tbadcache.nim26
-rw-r--r--tests/generics/tbaddeprecated.nim55
-rw-r--r--tests/generics/tbadgenericlambda.nim7
-rw-r--r--tests/generics/tbindoncevsbindmany.nim68
-rw-r--r--tests/generics/tbintree.nim106
-rw-r--r--tests/generics/tbracketinstantiation.nim86
-rw-r--r--tests/generics/tcalltype.nim26
-rw-r--r--tests/generics/tcan.nim46
-rw-r--r--tests/generics/tcannot_pass_empty_seq_to_generic.nim17
-rw-r--r--tests/generics/tconstraints.nim16
-rw-r--r--tests/generics/tcritical.nim19
-rw-r--r--tests/generics/texplicitgeneric1.nim36
-rw-r--r--tests/generics/texplicitgeneric2.nim35
-rw-r--r--tests/generics/tfakecovariance.nim78
-rw-r--r--tests/generics/tforwardgeneric.nim28
-rw-r--r--tests/generics/tforwardgenericconstrained.nim73
-rw-r--r--tests/generics/tfriends.nim11
-rw-r--r--tests/generics/tgeneric0.nim196
-rw-r--r--tests/generics/tgeneric3.nim487
-rw-r--r--tests/generics/tgeneric_recursionlimit.nim123
-rw-r--r--tests/generics/tgenericdotrettype.nim29
-rw-r--r--tests/generics/tgenericlambda.nim23
-rw-r--r--tests/generics/tgenericmatcher.nim40
-rw-r--r--tests/generics/tgenericprocvar.nim37
-rw-r--r--tests/generics/tgenerics_issues.nim894
-rw-r--r--tests/generics/tgenerics_various.nim254
-rw-r--r--tests/generics/tgenerictmpl2.nim31
-rw-r--r--tests/generics/tgenericvariant.nim36
-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.nim63
-rw-r--r--tests/generics/tinheritable_importcpp.nim10
-rw-r--r--tests/generics/tlateboundgenericparams.nim145
-rw-r--r--tests/generics/tlateboundstatic.nim16
-rw-r--r--tests/generics/tmacroinjectedsym.nim186
-rw-r--r--tests/generics/tmacroinjectedsymwarning.nim59
-rw-r--r--tests/generics/tmapping_generic_alias.nim28
-rw-r--r--tests/generics/tmetafield.nim30
-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.nim89
-rw-r--r--tests/generics/tobjecttyperel2.nim42
-rw-r--r--tests/generics/topensymimport.nim5
-rw-r--r--tests/generics/toverloading_typedesc.nim19
-rw-r--r--tests/generics/tparam_binding.nim29
-rw-r--r--tests/generics/tparser_generator.nim415
-rw-r--r--tests/generics/tpointerprocs.nim28
-rw-r--r--tests/generics/tprevent_double_bind.nim21
-rw-r--r--tests/generics/trecursivegenerics.nim96
-rw-r--r--tests/generics/treentranttypes.nim114
-rw-r--r--tests/generics/treturn_inference.nim184
-rw-r--r--tests/generics/tstatic_constrained.nim79
-rw-r--r--tests/generics/tsubclassgenericerror.nim11
-rw-r--r--tests/generics/tthread_generic.nim39
-rw-r--r--tests/generics/tuninstantiated_failure.nim16
-rw-r--r--tests/generics/tuninstantiatedgenericcalls.nim517
-rw-r--r--tests/generics/tunique_type.nim67
-rw-r--r--tests/generics/tvarseq_caching.nim48
-rw-r--r--tests/generics/twrong_field_caching.nim68
-rw-r--r--tests/generics/twrong_floatlit_type.nim118
-rw-r--r--tests/generics/twrong_generic_object.nim21
114 files changed, 6881 insertions, 0 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/mbind_bracket.nim b/tests/generics/mbind_bracket.nim
new file mode 100644
index 000000000..4bf18b471
--- /dev/null
+++ b/tests/generics/mbind_bracket.nim
@@ -0,0 +1,17 @@
+
+import tables
+
+type
+  UUIDObject* = ref object
+    uuid: string
+
+  Registry*[T] = ref object
+    objects: Table[string, T]
+
+proc newRegistry*[T](): Registry[T] =
+  result = Registry[T]()
+  result.objects = initTable[string, T](128)
+
+proc register*[T](self: Registry[T], obj: T) =
+  self.objects[obj.uuid] = obj
+
diff --git a/tests/generics/mclosed_sym.nim b/tests/generics/mclosed_sym.nim
new file mode 100644
index 000000000..bcccd9a85
--- /dev/null
+++ b/tests/generics/mclosed_sym.nim
@@ -0,0 +1,10 @@
+
+type R* = object
+
+type Data*[T] = object
+  d*: T
+
+proc same(r:R, d:int) = echo "TEST2"
+
+proc doIt*(d:Data, r:R) =
+  r.same(1)      # Expecting this to invoke the local `same()` method
diff --git a/tests/generics/mdotlookup.nim b/tests/generics/mdotlookup.nim
new file mode 100644
index 000000000..090b97771
--- /dev/null
+++ b/tests/generics/mdotlookup.nim
@@ -0,0 +1,28 @@
+proc baz(o: auto): int = 5 # if bar is exported, it works
+
+type MyObj = object
+  x: int
+
+proc foo*(b: auto) =
+  var o: MyObj
+  echo b.baz, " ", o.x.baz, " ", b.baz()
+
+import sets
+
+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/mmodule_same_as_proc.nim b/tests/generics/mmodule_same_as_proc.nim
new file mode 100644
index 000000000..048b98336
--- /dev/null
+++ b/tests/generics/mmodule_same_as_proc.nim
@@ -0,0 +1,2 @@
+
+proc mmodule_same_as_proc*(x: string) = discard
diff --git a/tests/generics/module_with_generics.nim b/tests/generics/module_with_generics.nim
new file mode 100644
index 000000000..960a694d7
--- /dev/null
+++ b/tests/generics/module_with_generics.nim
@@ -0,0 +1,14 @@
+type
+  Base[T] {.inheritable.} = ref object
+    value*: T
+
+  Derived[T] = ref object of Base[T]
+    derivedValue*: T
+
+proc makeDerived*[T](v: T): Derived[T] =
+  new result
+  result.value = v
+
+proc setBaseValue*[T](a: Base[T], value: T) =
+  a.value = value
+
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/moverloading_typedesc.nim b/tests/generics/moverloading_typedesc.nim
new file mode 100644
index 000000000..92805fb7b
--- /dev/null
+++ b/tests/generics/moverloading_typedesc.nim
@@ -0,0 +1,11 @@
+import tables
+
+type
+  FFoo* = object
+  FBar* = object
+
+proc new*(_: typedesc[FFoo]): int = 2
+proc new*[T](_: typedesc[T]): int = 3
+proc new*(_: typedesc): int = 4
+proc new*(_: typedesc[seq[Table[int, seq[Table[int, string]]]]]): int = 5
+proc new*(_: typedesc[seq[Table[int, seq[Table[int, typedesc]]]]]): int = 6
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
new file mode 100644
index 000000000..1fd84852b
--- /dev/null
+++ b/tests/generics/t13525.nim
@@ -0,0 +1,6 @@
+# https://github.com/nim-lang/Nim/issues/13524
+template fun(field): untyped = astToStr(field)
+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/t2tables.nim b/tests/generics/t2tables.nim
new file mode 100644
index 000000000..e4b1fb967
--- /dev/null
+++ b/tests/generics/t2tables.nim
@@ -0,0 +1,15 @@
+discard """
+action: compile
+"""
+
+# bug #3669
+
+import tables
+
+type
+  G[T] = object
+    inodes: Table[int, T]
+    rnodes: Table[T, int]
+
+var g: G[string]
+echo g.rnodes["foo"]
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/t5602_inheritence.nim b/tests/generics/t5602_inheritence.nim
new file mode 100644
index 000000000..ee5ba89d5
--- /dev/null
+++ b/tests/generics/t5602_inheritence.nim
@@ -0,0 +1,24 @@
+discard """
+  output: "seq[float]\n0"
+  targets: "c cpp"
+"""
+
+# https://github.com/nim-lang/Nim/issues/5602
+
+import typetraits, module_with_generics
+
+type
+  Foo[T] = object of RootObj
+  Bar[T] = object of Foo[seq[T]]
+
+proc p[T](f: Foo[T]): T =
+  echo T.name
+
+var s: Bar[float]
+echo p(s).len # the bug was: p(s) should return seq[float], but returns float instead
+
+# Test overloading and code generation when
+# downcasting is required for generic types:
+var d = makeDerived(10)
+setBaseValue(d, 20)
+
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
new file mode 100644
index 000000000..fb7db22f8
--- /dev/null
+++ b/tests/generics/t6137.nim
@@ -0,0 +1,28 @@
+discard """
+  errormsg: "cannot instantiate: 'T'"
+  line: 19
+"""
+
+type
+  # simple vector of declared fixed length
+  vector[N : static[int]] = array[0..N-1, float]
+
+proc `*`[T](x: float, a: vector[T]): vector[T] =
+  # multiplication by scalar
+  for ii in 0..high(a):
+    result[ii] = a[ii]*x
+
+let
+  # define a vector of length 3
+  x: vector[3] = [1.0, 3.0, 5.0]
+
+proc vectFunc[T](x: vector[T]): vector[T] =
+  # Define a vector function
+  result = 2.0*x
+
+proc passVectFunction[T](g: proc(x: vector[T]): vector[T], x: vector[T]): vector[T] =
+  # pass a vector function as input in another procedure
+  result = g(x)
+
+let
+  xNew = passVectFunction(vectFunc,x)
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/t7141.nim b/tests/generics/t7141.nim
new file mode 100644
index 000000000..b1e9cbf43
--- /dev/null
+++ b/tests/generics/t7141.nim
@@ -0,0 +1,9 @@
+discard """
+  errormsg: "cannot instantiate: \'T\'"
+  line: 6
+"""
+
+proc foo[T](x: T) =
+  discard
+
+var fun = if true: foo else: foo
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/t8270.nim b/tests/generics/t8270.nim
new file mode 100644
index 000000000..1e731d1d4
--- /dev/null
+++ b/tests/generics/t8270.nim
@@ -0,0 +1,7 @@
+discard """
+  errormsg: "cannot instantiate: \'T\'"
+  line: 6
+"""
+
+proc m[T](x: T): int = discard
+echo [m]
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/tarc_misc.nim b/tests/generics/tarc_misc.nim
new file mode 100644
index 000000000..3e7762556
--- /dev/null
+++ b/tests/generics/tarc_misc.nim
@@ -0,0 +1,21 @@
+discard """
+  output: ''''''
+  cmd: "nim c --gc:arc $file"
+"""
+
+# bug #13519
+
+var unrelated: seq[proc() {.closure, gcsafe.}]
+
+unrelated.add proc () =
+  echo "gcsafe"
+
+import tables, sequtils
+let t = newTable[int, proc()]()
+
+type
+  MyProc = proc() {.closure.}
+
+var result: seq[MyProc] = @[]
+for x in t.values:
+  result.add(x)
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/tbadgenericlambda.nim b/tests/generics/tbadgenericlambda.nim
new file mode 100644
index 000000000..9fac150c1
--- /dev/null
+++ b/tests/generics/tbadgenericlambda.nim
@@ -0,0 +1,7 @@
+discard """
+  errormsg: "nested proc can have generic parameters only when"
+  line: 6
+"""
+
+let x = proc (x, y: auto): auto = x + y
+
diff --git a/tests/generics/tbindoncevsbindmany.nim b/tests/generics/tbindoncevsbindmany.nim
new file mode 100644
index 000000000..01e801f0e
--- /dev/null
+++ b/tests/generics/tbindoncevsbindmany.nim
@@ -0,0 +1,68 @@
+template accept(x) =
+  static: assert(compiles(x))
+
+template reject(x) =
+  static: assert(not compiles(x))
+
+type
+  ObjectWithNumber = concept obj
+    obj.number is int
+
+  Foo[T] = object
+    x: T
+
+type A = object
+  anumber: int
+
+type B = object
+  bnumber: int
+
+proc number(a: A): int = a.anumber
+proc number(b: B): int = b.bnumber
+
+proc notDistincConcept1(a: ObjectWithNumber, b: ObjectWithNumber) = discard
+proc notDistincConcept2(a, b: ObjectWithNumber) = discard
+proc distinctConcept1(a, b: distinct ObjectWithNumber) = discard
+proc distinctConcept2(a: ObjectWithNumber, b: distinct ObjectWithNumber) = discard
+proc distinctConcept3(a: distinct ObjectWithNumber, b: ObjectWithNumber) = discard
+proc distinctConcept4(a: distinct ObjectWithNumber, b: distinct ObjectWithNumber) = discard
+
+var a = A(anumber: 5)
+var b = B(bnumber: 6)
+
+accept notDistincConcept1(a, a)
+accept notDistincConcept1(b, b)
+reject notDistincConcept2(a, b)
+
+accept notDistincConcept2(a, a)
+accept notDistincConcept2(b, b)
+reject notDistincConcept2(a, b)
+
+accept distinctConcept1(a, b)
+accept distinctConcept2(a, b)
+accept distinctConcept3(a, b)
+accept distinctConcept4(a, b)
+
+proc nonDistincGeneric1(a: Foo, b: Foo) = discard
+proc nonDistincGeneric2(a, b: Foo) = discard
+proc distinctGeneric1(a, b: distinct Foo) = discard
+proc distinctGeneric2(a: distinct Foo, b: Foo) = discard
+proc distinctGeneric3(a: Foo, b: distinct Foo) = discard
+proc distinctGeneric4(a: distinct Foo, b: distinct Foo) = discard
+
+var f1 = Foo[int](x: 10)
+var f2 = Foo[string](x: "x")
+
+accept nonDistincGeneric1(f1, f1)
+accept nonDistincGeneric1(f2, f2)
+reject nonDistincGeneric1(f1, f2)
+
+accept nonDistincGeneric2(f1, f1)
+accept nonDistincGeneric2(f2, f2)
+reject nonDistincGeneric2(f1, f2)
+
+accept distinctGeneric1(f1, f1)
+accept distinctGeneric2(f1, f1)
+accept distinctGeneric3(f1, f1)
+accept distinctGeneric4(f1, f1)
+
diff --git a/tests/generics/tbintree.nim b/tests/generics/tbintree.nim
new file mode 100644
index 000000000..83f14406b
--- /dev/null
+++ b/tests/generics/tbintree.nim
@@ -0,0 +1,106 @@
+discard """
+  output: '''hello
+world
+99
+110
+223'''
+"""
+type
+  TBinaryTree[T] = object      # TBinaryTree is a generic type with
+                               # with generic param ``T``
+    le, ri: ref TBinaryTree[T] # left and right subtrees; may be nil
+    data: T                    # the data stored in a node
+  PBinaryTree*[A] = ref TBinaryTree[A] # type that is exported
+
+proc newNode*[T](data: T): PBinaryTree[T] =
+  # constructor for a node
+  new(result)
+  result.data = data
+
+proc add*[Ty](root: var PBinaryTree[Ty], n: PBinaryTree[Ty]) =
+  # insert a node into the tree
+  if root == nil:
+    root = n
+  else:
+    var it = root
+    while it != nil:
+      # compare the data items; uses the generic ``cmp`` proc that works for
+      # any type that has a ``==`` and ``<`` operator
+      var c = cmp(n.data, it.data)
+      if c < 0:
+        if it.le == nil:
+          it.le = n
+          return
+        it = it.le
+      else:
+        if it.ri == nil:
+          it.ri = n
+          return
+        it = it.ri
+
+proc add*[Ty](root: var PBinaryTree[Ty], data: Ty) =
+  # convenience proc:
+  add(root, newNode(data))
+
+proc find*[Ty2](b: PBinaryTree[Ty2], data: Ty2): bool =
+  # for testing this needs to be recursive, so that the
+  # instantiated type is checked for proper tyGenericInst envelopes
+  if b == nil:
+    result = false
+  else:
+    var c = cmp(data, b.data)
+    if c < 0: result = find(b.le, data)
+    elif c > 0: result = find(b.ri, data)
+    else: result = true
+
+iterator preorder*[T](root: PBinaryTree[T]): T =
+  # Preorder traversal of a binary tree.
+  # 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()
+    while n != nil:
+      yield n.data
+      add(stack, n.ri)  # push right subtree onto the stack
+      n = n.le          # and follow the left pointer
+
+iterator items*[T](root: PBinaryTree[T]): T =
+  ## Inorder traversal of the binary tree.
+  var stack: seq[PBinaryTree[T]] = @[]
+  var n = root
+  while true:
+    while n != nil:
+      add(stack, n)
+      n = n.le
+    if stack.len > 0:
+      n = stack.pop()
+      yield n.data
+      n = n.ri
+    if stack.len == 0 and n == nil: break
+
+proc debug[T](a: PBinaryTree[T]) =
+  if a != nil:
+    debug(a.le)
+    echo a.data
+    debug(a.ri)
+
+when true:
+  var
+    root: PBinaryTree[string]
+    x = newNode("hello")
+  add(root, x)
+  add(root, "world")
+  if find(root, "world"):
+    for str in items(root):
+      echo(str)
+  else:
+    echo("BUG")
+
+  var
+    r2: PBinaryTree[int]
+  add(r2, newNode(110))
+  add(r2, 223)
+  add(r2, 99)
+  for y in items(r2):
+    echo(y)
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
new file mode 100644
index 000000000..bbefa7233
--- /dev/null
+++ b/tests/generics/tcan.nim
@@ -0,0 +1,46 @@
+discard """
+  output: '''
+'''
+"""
+
+# Created by Eric Doughty-Papassideris on 2011-02-16.
+
+block talias_generic:
+  type
+    TGen[T] = object
+    TGen2[T] = TGen[T]
+
+
+block talias_specialised:
+  type
+    TGen[T] = object
+    TSpef = TGen[string]
+  var s: TSpef
+
+
+block tinherit:
+  type
+    TGen[T] = object of RootObj
+      x, y: T
+    TSpef[T] = object of TGen[T]
+
+  var s: TSpef[float]
+  s.x = 0.4
+  s.y = 0.6
+
+
+block tspecialise:
+  type
+    TGen[T] {.inheritable.} = object
+    TSpef = object of TGen[string]
+
+
+block tspecialised_equivalent:
+  type
+    TGen[T] = tuple[a: T]
+    TSpef = tuple[a: string]
+
+  var
+    a: TGen[string]
+    b: TSpef
+  a = b
diff --git a/tests/generics/tcannot_pass_empty_seq_to_generic.nim b/tests/generics/tcannot_pass_empty_seq_to_generic.nim
new file mode 100644
index 000000000..e33dbc10b
--- /dev/null
+++ b/tests/generics/tcannot_pass_empty_seq_to_generic.nim
@@ -0,0 +1,17 @@
+discard """
+  errormsg: "type mismatch: got <seq[empty]>"
+  line: 16
+"""
+
+# bug #836
+
+type
+  TOption*[T] = object
+    case FIsSome: bool
+    of false: nil
+    of true: FData: T
+
+proc some*[T](value: T): TOption[T] = TOption[T](FIsSome: true, FData: value)
+
+echo some(@[]).FIsSome
+
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/tcritical.nim b/tests/generics/tcritical.nim
new file mode 100644
index 000000000..e84c03618
--- /dev/null
+++ b/tests/generics/tcritical.nim
@@ -0,0 +1,19 @@
+discard """
+  errormsg: "type mismatch"
+  line: 18
+"""
+
+# bug #3998
+
+type Vec3[T] = array[3, T]
+
+var vg: Vec3[float32] = Vec3([1.0f, 2.0f, 3.0f])
+
+echo "vg[0]: " & $vg[0]  # prints 1.0    OK
+echo "vg[1]: " & $vg[1]  # prints 2.0    OK
+echo "vg[2]: " & $vg[2]  # prints 3.0    OK
+echo ""
+
+var ve: Vec3[float64]
+ve = vg     # compiles, this MUST NOT be allowed!
+
diff --git a/tests/generics/texplicitgeneric1.nim b/tests/generics/texplicitgeneric1.nim
new file mode 100644
index 000000000..16f4f7330
--- /dev/null
+++ b/tests/generics/texplicitgeneric1.nim
@@ -0,0 +1,36 @@
+discard """
+  output: "Key: 12 value: 12Key: 13 value: 13 Key: A value: 12 Key: B value: 13"
+"""
+# test explicit type instantiation
+
+type
+  TDict*[TKey, TValue] = object
+    data: seq[tuple[k: TKey, v: TValue]]
+  PDict*[TKey, #with `==`(a, b: TKey): bool
+               #     hash(a: TKey): int,
+         TValue] = ref TDict[TKey, TValue]
+
+proc newDict*[TKey, TValue](): PDict[TKey, TValue] =
+  new(result)
+  result.data = @[]
+
+proc add*[TKey, TValue](d: PDict[TKey, TValue], k: TKey, v: TValue) =
+  d.data.add((k, v))
+
+iterator items*[Tkey, TValue](d: PDict[TKey, TValue]): tuple[k: TKey,
+               v: TValue] =
+  for k, v in items(d.data): yield (k, v)
+
+var d = newDict[int, string]()
+d.add(12, "12")
+d.add(13, "13")
+for k, v in items(d):
+  stdout.write("Key: ", $k, " value: ", v)
+
+var c = newDict[char, string]()
+c.add('A', "12")
+c.add('B', "13")
+for k, v in items(c):
+  stdout.write(" Key: ", $k, " value: ", v)
+
+stdout.write "\n"
diff --git a/tests/generics/texplicitgeneric2.nim b/tests/generics/texplicitgeneric2.nim
new file mode 100644
index 000000000..37b133130
--- /dev/null
+++ b/tests/generics/texplicitgeneric2.nim
@@ -0,0 +1,35 @@
+discard """
+  output: "Key: 12 value: 12Key: 13 value: 13 Key: A value: 12 Key: B value: 13"
+"""
+
+# test explicit type instantiation
+
+type
+  TDict*[TKey, TValue] = object
+    data: seq[tuple[k: TKey, v: TValue]]
+  PDict*[TKey, TValue] = ref TDict[TKey, TValue]
+
+proc newDict*[TKey, TValue](): PDict[TKey, TValue] =
+  new(result)
+  result.data = @[]
+
+proc add*(d: PDict, k: d.TKey, v: d.TValue) =
+  d.data.add((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")
+d.add(13, "13")
+for k, v in items(d):
+  stdout.write("Key: ", $k, " value: ", v)
+
+var c = newDict[char, string]()
+c.add('A', "12")
+c.add('B', "13")
+for k, v in items(c):
+  stdout.write(" Key: ", $k, " value: ", v)
+
+stdout.write "\n"
diff --git a/tests/generics/tfakecovariance.nim b/tests/generics/tfakecovariance.nim
new file mode 100644
index 000000000..0920cb504
--- /dev/null
+++ b/tests/generics/tfakecovariance.nim
@@ -0,0 +1,78 @@
+template accept(x) =
+  static: assert(compiles(x))
+
+template reject(x) =
+  static: assert(not compiles(x))
+
+type
+  BaseObj = object of RootObj
+  DerivedObj = object of BaseObj
+  NonDerivedObj = object
+
+  Container[T] = object
+
+var base: BaseObj
+var derived: DerivedObj
+var nonDerived: NonDerivedObj
+
+var baseContainer: Container[BaseObj]
+var derivedContainer: Container[DerivedObj]
+var nonDerivedContainer: Container[NonDerivedObj]
+
+# We can fake covariance by listing some specific derived types that
+# will be allowed with our overload. This is not a real covariance,
+# because there will be multiple instantiations of the proc, but for
+# many purposes, it will suffice:
+
+proc wantsSpecificContainers(c: Container[BaseObj or DerivedObj]) = discard
+
+accept wantsSpecificContainers(baseContainer)
+accept wantsSpecificContainers(derivedContainer)
+
+reject wantsSpecificContainers(nonDerivedContainer)
+reject wantsSpecificContainers(derived)
+
+# Now, let's make a more general solution able to catch all derived types:
+
+type
+  DerivedFrom[T] = concept type D
+    var derived: ref D
+    var base: ref T = derived
+
+proc wantsDerived(x: DerivedFrom[BaseObj]) = discard
+
+accept wantsDerived(base)
+accept wantsDerived(derived)
+
+reject wantsDerived(nonDerived)
+reject wantsDerived(baseContainer)
+
+proc wantsDerivedContainer(c: Container[DerivedFrom[BaseObj]]) = discard
+
+accept wantsDerivedContainer(baseContainer)
+accept wantsDerivedContainer(derivedContainer)
+
+reject wantsDerivedContainer(nonDerivedContainer)
+
+# The previous solutions were solving the problem for a single overload.
+# Let's solve it for multiple overloads by introducing a converter:
+
+type
+  OtherContainer[T] = object
+
+proc wantsBaseContainer1(c: OtherContainer[BaseObj]) = discard
+proc wantsBaseContainer2(c: OtherContainer[BaseObj]) = discard
+
+converter derivedToBase(c: OtherContainer[DerivedFrom[BaseObj]]): OtherContainer[BaseObj] = discard
+
+block:
+  var baseContainer: OtherContainer[BaseObj]
+  var derivedContainer: OtherContainer[DerivedObj]
+  var nonDerivedContainer: OtherContainer[NonDerivedObj]
+
+  accept wantsBaseContainer1(derivedContainer)
+  reject wantsBaseContainer1(nonDerivedContainer)
+
+  accept wantsBaseContainer2(derivedContainer)
+  reject wantsBaseContainer2(nonDerivedContainer)
+
diff --git a/tests/generics/tforwardgeneric.nim b/tests/generics/tforwardgeneric.nim
new file mode 100644
index 000000000..9f68bf332
--- /dev/null
+++ b/tests/generics/tforwardgeneric.nim
@@ -0,0 +1,28 @@
+discard """
+  output: "1.1 11\n42\n0"
+  ccodecheck: "!@'ClEnv'"
+"""
+
+proc p[T](a, b: T): T
+
+echo p(0.9, 0.1), " ", p(9, 1)
+
+proc p[T](a, b: T): T =
+  let c = b
+  result = a + b + c
+
+# https://github.com/nim-lang/Nim/issues/4908
+proc foo(t: typedesc[int]): int
+proc bar(): int = foo(int)
+proc foo(t: typedesc[int]): int =
+  return 0
+
+# https://github.com/nim-lang/Nim/issues/4104
+proc print[T](t: T) # Error: implementation of 'print.print(t: int)' expected
+print 42 # moving this line after the implementation fixes the error,
+         # but such behaviour makes forward declaration pointless
+proc print[T](t: T) =
+  echo t
+
+echo bar()
+
diff --git a/tests/generics/tforwardgenericconstrained.nim b/tests/generics/tforwardgenericconstrained.nim
new file mode 100644
index 000000000..6163ea1a8
--- /dev/null
+++ b/tests/generics/tforwardgenericconstrained.nim
@@ -0,0 +1,73 @@
+discard """
+output: '''
+hello some integer
+hello range
+hello tuple
+hello seq
+hello object
+hello distinct
+hello enum
+'''
+"""
+
+
+
+# SomeInteger
+
+proc foo[T : SomeInteger](arg: T)
+proc foo[T : SomeInteger](arg: T) =
+  echo "hello some integer"
+foo(123)
+
+# range
+
+proc foo2[T : range[0..100]](arg: T)
+proc foo2[T : range[0..100]](arg: T) =
+  echo "hello range"
+foo2(7)
+
+# tuple
+
+proc foo3[T : tuple](arg: T)
+proc foo3[T : tuple](arg: T) =
+  echo "hello tuple"
+
+foo3((a:123,b:321))
+
+# seq
+
+proc foo4[T: seq](arg: T)
+proc foo4[T: seq](arg: T) =
+  echo "hello seq"
+
+foo4(@[1,2,3])
+
+# object
+
+proc foo5[T : object](arg: T)
+proc foo5[T : object](arg: T) =
+  echo "hello object"
+
+type MyType = object
+var mt: MyType
+foo5(mt)
+
+# distinct
+
+proc foo6[T : distinct](arg: T)
+proc foo6[T : distinct](arg: T) =
+  echo "hello distinct"
+
+type MyDistinct = distinct string
+var md: MyDistinct
+foo6(md)
+
+# enum
+
+proc foo7[T : enum](arg: T)
+proc foo7[T : enum](arg: T) =
+  echo "hello enum"
+
+type MyEnum = enum
+  ValueA
+foo7(ValueA)
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
new file mode 100644
index 000000000..16a148f7b
--- /dev/null
+++ b/tests/generics/tgeneric0.nim
@@ -0,0 +1,196 @@
+discard """
+  output: '''
+100
+0
+float32
+float32
+(name: "Resource 1", readers: ..., writers: ...)
+'''
+"""
+
+
+import std/tables
+
+
+block tgeneric0:
+  type
+    TX = Table[string, int]
+
+  proc foo(models: seq[Table[string, float]]): seq[float] =
+    result = @[]
+    for model in models.items:
+      result.add model["foobar"]
+
+  # bug #686
+  type TType[T; A] = array[A, T]
+
+  proc foo[T](p: TType[T, range[0..1]]) =
+    echo "foo"
+  proc foo[T](p: TType[T, range[0..2]]) =
+    echo "bar"
+
+  #bug #1366
+
+  proc reversed(x: auto) =
+    for i in countdown(x.low, x.high):
+      echo i
+
+  reversed(@[-19, 7, -4, 6])
+
+
+
+block tgeneric1:
+  type
+    TNode[T] = tuple[priority: int, data: T]
+    TBinHeap[T] = object
+      heap: seq[TNode[T]]
+      last: int
+    PBinHeap[T] = ref TBinHeap[T]
+
+  proc newBinHeap[T](heap: var PBinHeap[T], size: int) =
+    new(heap)
+    heap.last = 0
+    newSeq(heap.heap, size)
+    #newSeq(heap.seq, size)
+
+  proc parent(elem: int): int {.inline.} =
+    return (elem-1) div 2
+
+  proc siftUp[T](heap: PBinHeap[T], elem: int) =
+    var idx = elem
+    while idx != 0:
+      var p = parent(idx)
+      if heap.heap[idx].priority < heap.heap[p].priority:
+        swap(heap.heap[idx], heap.heap[p])
+        idx = p
+      else:
+        break
+
+  proc add[T](heap: PBinHeap[T], priority: int, data: T) =
+    var node: TNode[T]
+    node.priority = priority
+    node.data = data
+    heap.heap[heap.last] = node
+    siftUp(heap, heap.last)
+    inc(heap.last)
+
+  proc print[T](heap: PBinHeap[T]) =
+    for i in countup(0, heap.last):
+      stdout.write($heap.heap[i].data, "\n")
+
+  var heap: PBinHeap[int]
+
+  newBinHeap(heap, 256)
+  add(heap, 1, 100)
+  print(heap)
+
+
+
+block tgeneric2:
+  type
+    TX = Table[string, int]
+
+  proc foo(models: seq[TX]): seq[int] =
+    result = @[]
+    for model in models.items:
+      result.add model["foobar"]
+
+  type
+    Obj = object
+      field: Table[string, string]
+  var t: Obj
+  discard initTable[type(t.field), string]()
+
+
+
+block tgeneric4:
+  type
+    TIDGen[A: Ordinal] = object
+      next: A
+      free: seq[A]
+
+  proc newIDGen[A]: TIDGen[A] =
+      newSeq result.free, 0
+
+  var x = newIDGen[int]()
+
+block tgeneric5:
+  # bug #12528
+  proc foo[T](a: T; b: T) =
+    echo T
+
+  foo(0.0'f32, 0.0)
+
+  proc bar[T](a: T; b: T = 0.0) =
+    echo T
+
+  bar(0.0'f32)
+
+# bug #13378
+
+type
+  Resource = ref object of RootObj
+    name: string
+    readers, writers: seq[RenderTask]
+
+  RenderTask = ref object
+    name: string
+
+var res = Resource(name: "Resource 1")
+
+(proc (r: typeof(res)) =
+   echo r[])(res)
+
+# bug #4061
+
+type List[T] = object
+  e: T
+  n: ptr List[T]
+
+proc zip*[T,U](xs: List[T], ys: List[U]): List[(T,U)] = discard
+
+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
new file mode 100644
index 000000000..29a73afc6
--- /dev/null
+++ b/tests/generics/tgeneric3.nim
@@ -0,0 +1,487 @@
+discard """
+output: '''
+312
+1000
+1000
+500
+0
+'''
+"""
+
+import strutils
+
+type
+  PNode[T,D] = ref TNode[T,D]
+  TItem[T,D] {.acyclic, pure, final, shallow.} = object
+        key: T
+        value: D
+        node: PNode[T,D]
+        when not (D is string):
+          val_set: bool
+
+  TItems[T,D] = seq[ref TItem[T,D]]
+  TNode[T,D] {.acyclic, pure, final, shallow.} = object
+        slots: TItems[T,D]
+        left: PNode[T,D]
+        count: int32
+
+
+  RPath[T,D] = tuple[
+    Xi: int,
+    Nd: PNode[T,D] ]
+
+const
+  cLen1 = 2
+  cLen2 = 16
+  cLen3 = 32
+  cLenCenter = 80
+  clen4 = 96
+  cLenMax = 128
+  cCenter = cLenMax div 2
+
+proc len[T,D] (n:PNode[T,D]): int {.inline.} =
+  return n.count
+
+proc clean[T: SomeOrdinal|SomeNumber](o: var T) {.inline.} = discard
+
+proc clean[T: string|seq](o: var T) {.inline.} =
+  o = nil
+
+proc clean[T,D] (o: ref TItem[T,D]) {.inline.} =
+  when (D is string) :
+    o.value = nil
+  else :
+    o.val_set = false
+
+proc isClean[T,D] (it: ref TItem[T,D]): bool {.inline.} =
+  when (D is string) :
+    return it.value == nil
+  else :
+    return not it.val_set
+
+proc isClean[T,D](n: PNode[T,D], x: int): bool {.inline.} =
+  when (D is string):
+    return n.slots[x].value == nil
+  else:
+    return not n.slots[x].val_set
+
+proc setItem[T,D](Akey: T, Avalue: D, ANode: PNode[T,D]): ref TItem[T,D] {.inline.} =
+  new(result)
+  result.key = Akey
+  result.value = Avalue
+  result.node = ANode
+  when not (D is string) :
+    result.val_set = true
+
+proc cmp[T:int8|int16|int32|int64|int] (a,b: T): T {.inline.} =
+  return a-b
+
+template binSearchImpl *(docmp: untyped) =
+  var bFound = false
+  result = 0
+  var H = haystack.len - 1
+  while result <= H :
+    var I {.inject.} = (result + H) shr 1
+    var SW = docmp
+    if SW < 0: result = I + 1
+    else:
+      H = I - 1
+      if SW == 0 : bFound = true
+  if bFound: inc(result)
+  else: result = - result
+
+proc bSearch[T,D] (haystack: PNode[T,D], needle:T): int {.inline.} =
+  binSearchImpl(haystack.slots[I].key.cmp(needle))
+
+proc DeleteItem[T,D] (n: PNode[T,D], x: int): PNode[T,D] {.inline.} =
+  var w = n.slots[x]
+  if w.node != nil :
+    clean(w)
+    return n
+  dec(n.count)
+  if n.count > 0 :
+    for i in countup(x, n.count - 1) : n.slots[i] = n.slots[i + 1]
+    n.slots[n.count] = nil
+    case n.count
+    of cLen1 : setLen(n.slots, cLen1)
+    of cLen2 : setLen(n.slots, cLen2)
+    of cLen3 : setLen(n.slots, cLen3)
+    of cLenCenter : setLen(n.slots, cLenCenter)
+    of cLen4 : setLen(n.slots, cLen4)
+    else: discard
+    result = n
+
+  else :
+    result = n.left
+    n.slots = @[]
+    n.left = nil
+
+proc internalDelete[T,D] (ANode: PNode[T,D], key: T, Avalue: var D): PNode[T,D] =
+  var Path: array[0..20, RPath[T,D]]
+  var n = ANode
+  result = n
+  clean(Avalue)
+  var h = 0
+  while n != nil:
+    var x = bSearch(n, key)
+    if x <= 0 :
+      Path[h].Nd = n
+      Path[h].Xi = - x
+      inc(h)
+      if x == 0 :
+        n = n.left
+      else :
+        x = (-x) - 1
+        if x < n.count :
+          n = n.slots[x].node
+        else :
+          n = nil
+    else :
+      dec(x)
+      if isClean(n, x) : return
+      Avalue = n.slots[x].value
+      var n2 = DeleteItem(n, x)
+      dec(h)
+      while (n2 != n) and (h >= 0) :
+        n = n2
+        var w = addr Path[h]
+        x  = w.Xi - 1
+        if x >= 0 :
+          if (n == nil) and isClean(w.Nd, x) :
+            n = w.Nd
+            n.slots[x].node = nil
+            n2 = DeleteItem(n, x)
+          else :
+            w.Nd.slots[x].node = n
+            return
+        else :
+          w.Nd.left = n
+          return
+        dec(h)
+      if h < 0:
+        result = n2
+      return
+
+proc internalFind[T,D] (n: PNode[T,D], key: T): ref TItem[T,D] {.inline.} =
+  var wn = n
+  while wn != nil :
+    var x = bSearch(wn, key)
+    if x <= 0 :
+      if x == 0 :
+        wn = wn.left
+      else :
+        x = (-x) - 1
+        if x < wn.count :
+          wn = wn.slots[x].node
+        else :
+          return nil
+
+    else :
+      return wn.slots[x - 1]
+  return nil
+
+proc traceTree[T,D](root: PNode[T,D]) =
+  proc traceX(x: int) =
+    write stdout, "("
+    write stdout, x
+    write stdout, ") "
+
+  proc traceEl(el: ref TItem[T,D]) =
+    write stdout, " key: "
+    write stdout, el.key
+    write stdout, " value: "
+    write stdout, el.value
+
+
+  proc traceln(space: string) =
+    writeLine stdout, ""
+    write stdout, space
+
+  proc doTrace(n: PNode[T,D], level: int) =
+    var space = spaces(2 * level)
+    traceln(space)
+    write stdout, "node: "
+    if n == nil:
+      writeLine stdout, "is empty"
+      return
+    write stdout, n.count
+    write stdout, " elements: "
+    if n.left != nil:
+      traceln(space)
+      write stdout, "left: "
+      doTrace(n.left, level+1)
+    for i, el in n.slots:
+      if el != nil and not isClean(el):
+        traceln(space)
+        traceX(i)
+        if i >= n.count:
+          write stdout, "error "
+        else:
+          traceEl(el)
+          if el.node != nil: doTrace(el.node, level+1)
+          else : write stdout, " empty "
+      elif i < n.count :
+        traceln(space)
+        traceX(i)
+        write stdout, "clean: "
+        when T is string :
+          if el.key != nil: write stdout, el.key
+        else : write stdout, el.key
+        if el.node != nil: doTrace(el.node, level+1)
+        else : write stdout, " empty "
+    writeLine stdout,""
+
+  doTrace(root, 0)
+
+proc InsertItem[T,D](APath: RPath[T,D], ANode:PNode[T,D], Akey: T, Avalue: D) =
+  var x = - APath.Xi
+  inc(APath.Nd.count)
+  case APath.Nd.count
+  of cLen1: setLen(APath.Nd.slots, cLen2)
+  of cLen2: setLen(APath.Nd.slots, cLen3)
+  of cLen3: setLen(APath.Nd.slots, cLenCenter)
+  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): APath.Nd.slots[i] = move APath.Nd.slots[i - 1]
+  APath.Nd.slots[x] = setItem(Akey, Avalue, ANode)
+
+
+proc SplitPage[T,D](n, left: PNode[T,D], xi: int, Akey:var T, Avalue:var D): PNode[T,D] =
+  var x = -xi
+  var it1: TItems[T,D]
+  it1.newSeq(cLenCenter)
+  new(result)
+  result.slots.newSeq(cLenCenter)
+  result.count = cCenter
+  if x == cCenter:
+    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:
+        it1[i] = move left.slots[i]
+      it1[x] = setItem(Akey, Avalue, n)
+      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:
+        result.slots[i] = move left.slots[cCenter + i]
+    else :
+      for i in 0..cCenter-1:
+        it1[i] = move left.slots[i]
+      x = x - (cCenter + 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:
+        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
+  left.slots = move it1
+
+
+proc internalPut[T,D](ANode: ref TNode[T,D], Akey: T, Avalue: D, Oldvalue: var D): ref TNode[T,D] =
+  var h: int
+  var Path: array[0..30, RPath[T,D]]
+  var left: PNode[T,D]
+  var n = ANode
+
+
+  result = ANode
+  h = 0
+  while n != nil:
+    var x = bSearch[T,D](n, Akey)
+    if x <= 0 :
+      Path[h].Nd = n
+      Path[h].Xi = x
+      inc(h)
+      if x == 0 :
+        n = n.left
+      else :
+        x = (-x)-1
+        if x < n.count :
+          n = n.slots[x].node
+        else :
+          n = nil
+    else :
+      var w = n.slots[x - 1]
+      Oldvalue = w.value
+      w.value = Avalue
+      return
+
+  dec(h)
+  left = nil
+  var lkey = Akey
+  var lvalue = Avalue
+  while h >= 0 :
+    if Path[h].Nd.count < cLenMax :
+      InsertItem(Path[h], n, lkey, lvalue)
+      return
+    else :
+      left = Path[h].Nd
+      n = SplitPage(n, left, Path[h].Xi, lkey, lvalue)
+    dec(h)
+
+  new(result)
+  result.slots.newSeq(cLen1)
+  result.count = 1
+  result.left = left
+  result.slots[0] = setItem(lkey, lvalue, n)
+
+
+proc CleanTree[T,D](n: PNode[T,D]): PNode[T,D] =
+  if n.left != nil :
+    n.left = CleanTree(n.left)
+  for i in 0 .. n.count - 1 :
+    var w = n.slots[i]
+    if w.node != nil :
+        w.node = CleanTree(w.node)
+    clean(w.value)
+    clean(w.key)
+  n.slots = nil
+  return nil
+
+
+proc VisitAllNodes[T,D](n: PNode[T,D], visit: proc(n: PNode[T,D]): PNode[T,D] {.closure.} ): PNode[T,D] =
+  if n != nil :
+    if n.left != nil :
+      n.left = VisitAllNodes(n.left, visit)
+    for i in 0 .. n.count - 1 :
+      var w = n.slots[i]
+      if w.node != nil :
+        w.node = VisitAllNodes(w.node, visit)
+    return visit(n)
+  return nil
+
+proc VisitAllNodes[T,D](n: PNode[T,D], visit: proc(n: PNode[T,D]) {.closure.} ) =
+  if n != nil:
+    if n.left != nil :
+      VisitAllNodes(n.left, visit)
+    for i in 0 .. n.count - 1 :
+      var w = n.slots[i]
+      if w.node != nil :
+        VisitAllNodes(w.node, visit)
+    visit(n)
+
+proc VisitAll[T,D](n: PNode[T,D], visit: proc(Akey: T, Avalue: D) {.closure.} ) =
+  if n != nil:
+    if n.left != nil :
+      VisitAll(n.left, visit)
+    for i in 0 .. n.count - 1 :
+      var w = n.slots[i]
+      if not w.isClean :
+        visit(w.key, w.value)
+      if w.node != nil :
+        VisitAll(w.node, visit)
+
+proc VisitAll[T,D](n: PNode[T,D], visit: proc(Akey: T, Avalue: var D):bool {.closure.} ): PNode[T,D] =
+  if n != nil:
+    var n1 = n.left
+    if n1 != nil :
+      var n2 = VisitAll(n1, visit)
+      if n1 != n2 :
+        n.left = n2
+    var i = 0
+    while i < n.count :
+      var w = n.slots[i]
+      if not w.isClean :
+        if visit(w.key, w.value) :
+          result = DeleteItem(n, i)
+          if result == nil : return
+          dec(i)
+      n1 = w.node
+      if n1 != nil :
+        var n2 = VisitAll(n1, visit)
+        if n1 != n2 :
+          w.node = n2
+      inc(i)
+  return n
+
+iterator keys* [T,D] (n: PNode[T,D]): T =
+  if n != nil :
+    var Path: array[0..20, RPath[T,D]]
+    var level = 0
+    var nd = n
+    var i = -1
+    while true :
+      if i < nd.count :
+        Path[level].Nd = nd
+        Path[level].Xi = i
+        if i < 0 :
+          if nd.left != nil :
+            nd = nd.left
+            inc(level)
+          else : inc(i)
+        else :
+          var w = nd.slots[i]
+          if not w.isClean() :
+            yield w.key
+          if w.node != nil :
+            nd = w.node
+            i = -1
+            inc(level)
+          else : inc(i)
+      else :
+        dec(level)
+        if level < 0 : break
+        nd = Path[level].Nd
+        i = Path[level].Xi
+        inc(i)
+
+proc test() =
+  var oldvalue: int
+  var root = internalPut[int, int](nil, 312, 312, oldvalue)
+  var someOtherRoot = internalPut[string, int](nil, "312", 312, oldvalue)
+  var it1 = internalFind(root, 312)
+  echo it1.value
+
+  for i in 1..1_000:
+    root = internalPut(root, i, i, oldvalue)
+
+  var cnt = 0
+  oldvalue = -1
+  when true : # code compiles, when this or the other when is switched to false
+    for k in root.keys :
+      if k <= oldvalue :
+        echo k
+      oldvalue = k
+      inc(cnt)
+    echo cnt
+  when true :
+    cnt = 0
+    VisitAll(root, proc(key, val: int) = inc(cnt))
+    echo cnt
+    when true :
+      root = VisitAll(root, proc(key: int, value: var int): bool =
+        return key mod 2 == 0 )
+    cnt = 0
+    oldvalue = -1
+    VisitAll(root, proc(key: int, value: int) {.closure.} =
+      if key <= oldvalue :
+        echo key
+      oldvalue = key
+      inc(cnt) )
+    echo cnt
+    root = VisitAll(root, proc(key: int, value: var int): bool =
+      return key mod 2 != 0 )
+    cnt = 0
+    oldvalue = -1
+    VisitAll(root, proc(key: int, value: int) {.closure.} =
+      if key <= oldvalue :
+        echo "error ", key
+      oldvalue = key
+      inc(cnt) )
+    echo cnt
+    #traceTree(root)
+
+test()
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/tgenericdotrettype.nim b/tests/generics/tgenericdotrettype.nim
new file mode 100644
index 000000000..3e4614752
--- /dev/null
+++ b/tests/generics/tgenericdotrettype.nim
@@ -0,0 +1,29 @@
+discard """
+output: '''string
+int
+(int, string)
+'''
+"""
+
+import typetraits
+
+type
+  Foo[T, U] = object
+    x: T
+    y: U
+
+proc bar[T](a: T): T.U =
+  echo result.type.name
+
+proc bas(x: auto): x.T =
+  echo result.type.name
+
+proc baz(x: Foo): (Foo.T, x.U) =
+  echo result.type.name
+
+var
+  f: Foo[int, string]
+  x = bar f
+  z = bas f
+  y = baz f
+
diff --git a/tests/generics/tgenericlambda.nim b/tests/generics/tgenericlambda.nim
new file mode 100644
index 000000000..41ee74557
--- /dev/null
+++ b/tests/generics/tgenericlambda.nim
@@ -0,0 +1,23 @@
+discard """
+  output: "10\n10\n1\n2\n3\n15"
+"""
+
+proc test(x: proc (a, b: int): int) =
+  echo x(5, 5)
+
+test(proc (a, b: auto): auto = a + b)
+
+test do (a, b: auto) -> auto: a + b
+
+proc foreach[T](s: seq[T], body: proc(x: T)) =
+  for e in s:
+    body(e)
+
+foreach(@[1,2,3]) do (x: auto):
+  echo x
+
+proc foo =
+  let x = proc (a, b: int): auto = a + b
+  echo x(5, 10)
+
+foo()
diff --git a/tests/generics/tgenericmatcher.nim b/tests/generics/tgenericmatcher.nim
new file mode 100644
index 000000000..ca4dc2a4d
--- /dev/null
+++ b/tests/generics/tgenericmatcher.nim
@@ -0,0 +1,40 @@
+discard """
+  output: '''
+'''
+"""
+
+
+block tmatcher1:
+  type
+    TMatcherKind = enum
+      mkTerminal, mkSequence, mkAlternation, mkRepeat
+    TMatcher[T] = object
+      case kind: TMatcherKind
+      of mkTerminal:
+        value: T
+      of mkSequence, mkAlternation:
+        matchers: seq[TMatcher[T]]
+      of mkRepeat:
+        matcher: PMatcher[T]
+        min, max: int
+    PMatcher[T] = ref TMatcher[T]
+
+  var m: PMatcher[int]
+
+
+block tmatcher2:
+  type
+    TMatcherKind = enum
+      mkTerminal, mkSequence, mkAlternation, mkRepeat
+    TMatcher[T] = object
+      case kind: TMatcherKind
+      of mkTerminal:
+        value: T
+      of mkSequence, mkAlternation:
+        matchers: seq[TMatcher[T]]
+      of mkRepeat:
+        matcher: ref TMatcher[T]
+        min, max: int
+  
+  var m: ref TMatcher[int]
+
diff --git a/tests/generics/tgenericprocvar.nim b/tests/generics/tgenericprocvar.nim
new file mode 100644
index 000000000..7935d90c2
--- /dev/null
+++ b/tests/generics/tgenericprocvar.nim
@@ -0,0 +1,37 @@
+discard """
+  output: "0false12"
+"""
+
+# Test multiple generic instantiation of generic proc vars:
+
+proc threadProcWrapper[TMsg]() =
+  var x: TMsg
+  stdout.write($x)
+
+#var x = threadProcWrapper[int]
+#x()
+
+#var y = threadProcWrapper[bool]
+#y()
+
+threadProcWrapper[int]()
+threadProcWrapper[bool]()
+
+type
+  TFilterProc[T,D] = proc (item: T, env:D): bool {.nimcall.}
+
+proc filter[T,D](data: seq[T], env:D, pred: TFilterProc[T,D]): seq[T] =
+  result = @[]
+  for e in data:
+    if pred(e, env): result.add(e)
+
+proc predTest(item: int, value: int): bool =
+  return item <= value
+
+proc test(data: seq[int], value: int): seq[int] =
+  return filter(data, value, predTest)
+
+for x in items(test(@[1,2,3], 2)):
+  stdout.write(x)
+
+stdout.write "\n"
diff --git a/tests/generics/tgenerics_issues.nim b/tests/generics/tgenerics_issues.nim
new file mode 100644
index 000000000..3068a22f2
--- /dev/null
+++ b/tests/generics/tgenerics_issues.nim
@@ -0,0 +1,894 @@
+discard """
+  output: '''
+4
+3
+(weight: 17.0, color: 100)
+perm: 22 det: 22
+TMatrix[3, 3, system.int]
+3
+@[0.9, 0.1]
+U[3]
+U[(f: 3)]
+U[[3]]
+b()
+@[1, 2]
+@[3, 4]
+1
+concrete 88
+123
+1
+2
+3
+!!Hi!!
+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
+"""
+
+import macros, sequtils, sets, sugar, tables, typetraits
+
+block t88:
+  type
+    BaseClass[V] = object of RootObj
+      b: V
+
+  proc new[V](t: typedesc[BaseClass], v: V): BaseClass[V] =
+    BaseClass[V](b: v)
+
+  proc baseMethod[V](v: BaseClass[V]): V = v.b
+  proc overriddenMethod[V](v: BaseClass[V]): V = v.baseMethod
+
+  type
+    ChildClass[V] = object of BaseClass[V]
+      c: V
+
+  proc new[V](t: typedesc[ChildClass], v1, v2: V): ChildClass[V] =
+    ChildClass[V](b: v1, c: v2)
+
+  proc overriddenMethod[V](v: ChildClass[V]): V = v.c
+
+  let c = ChildClass[string].new("Base", "Child")
+
+  doAssert c.baseMethod == "Base"
+  doAssert c.overriddenMethod == "Child"
+
+
+
+block t4528:
+  type GenericBase[T] = ref object of RootObj
+  type GenericSubclass[T] = ref object of GenericBase[T]
+  proc foo[T](g: GenericBase[T]) = discard
+  var bar: GenericSubclass[int]
+  foo(bar)
+
+
+
+block t1050_5597:
+  type ArrayType[T] = distinct T
+
+  proc arrayItem(a: ArrayType): auto =
+    static: echo(name(type(a).T))
+    result = (type(a).T)(4)
+
+  var arr: ArrayType[int]
+  echo arrayItem(arr)
+
+  # bug #5597
+
+  template fail() = "what"
+
+  proc g[T](x: var T) =
+    x.fail = 3
+
+  type
+    Obj = object
+      fail: int
+
+  var y: Obj
+  g y
+
+
+
+block t1789:
+  type
+    Foo[N: static[int]] = object
+
+  proc bindStaticN[N](foo: Foo[N]) =
+    var ar0: array[3, int]
+    var ar1: array[N, int]
+    var ar2: array[1..N, int]
+    var ar3: array[0..(N+10), float]
+    echo N
+
+  var f: Foo[3]
+  f.bindStaticN
+
+  # case 2
+
+  type
+    ObjectWithStatic[X, Y: static[int], T] = object
+      bar: array[X * Y, T]   # this one works
+
+    AliasWithStatic[X, Y: static[int], T] = array[X * Y, T]
+
+  var
+    x: ObjectWithStatic[1, 2, int]
+    y: AliasWithStatic[2, 3, int]
+
+  # case 3
+
+  type
+    Bar[N: static[int], T] = object
+      bar: array[N, T]
+
+  proc `[]`[N, T](f: Bar[N, T], n: range[0..(N - 1)]): T =
+    doAssert high(n) == N-1
+    result = f.bar[n]
+
+  var b: Bar[3, int]
+  doAssert b[2] == 0
+
+
+
+block t3977:
+  type Foo[N: static[int]] = object
+
+  proc foo[N](x: Foo[N]) =
+    let n = N
+    doAssert N == n
+
+  var f1: Foo[42]
+  f1.foo
+
+
+
+block t5570:
+  type
+    BaseFruit[T] = object of RootObj
+      color: T
+
+    Banana[T] = object of BaseFruit[uint32]
+      weight: T
+
+  macro printTypeName(typ: typed): untyped =
+    echo "type ", getType(typ).repr
+
+  proc setColor[K](self: var BaseFruit[K], c: int) =
+    printTypeName(self.color)
+    self.color = uint32(c)
+
+  var x: Banana[float64]
+  x.weight = 17
+  printTypeName(x.color)
+  x.setColor(100)
+  echo x
+
+
+
+block t5643:
+  type
+    Matrix[M, N: static[int], T: SomeFloat] = object
+      data: ref array[N * M, T]
+    Matrix64[M, N: static[int]] = Matrix[M, N, float64]
+
+  proc zeros64(M,N: static[int]): Matrix64[M,N] =
+    new result.data
+    for i in 0 ..< (M * N):
+      result.data[i] = 0'f64
+
+  proc bar[M,N: static[int], T](a: Matrix[M,N,T], b: Matrix[M,N,T]) =
+    discard
+
+  let a = zeros64(2,2)
+  bar(a,a)
+    # https://github.com/nim-lang/Nim/issues/5643
+    #
+    # The test case was failing here, because the compiler failed to
+    # detect the two matrix instantiations as the same type.
+    #
+    # The root cause was that the `T` type variable is a different
+    # type after the first Matrix type has been matched.
+    #
+    # Sigmatch was failing to match the second version of `T`, but
+    # due to some complex interplay between tyOr, tyTypeDesc and
+    # tyGenericParam this was allowed to went through. The generic
+    # instantiation of the second matrix was incomplete and the
+    # generic cache lookup failed, producing two separate types.
+
+
+
+block t5683:
+  type Matrix[M,N: static[int]] = array[M, array[N, float]]
+
+  proc det[M,N](a: Matrix[M,N]): int = N*10 + M
+  proc perm[M,N](a: Matrix[M,N]): int = M*10 + N
+
+  const
+    a = [ [1.0, 2.0]
+        , [3.0, 4.0]
+        ]
+
+  echo "perm: ", a.perm, " det: ", a.det
+
+  # This tests multiple instantiations of a generic
+  # proc involving static params:
+  type
+    Vector64[N: static[int]] = ref array[N, float64]
+    Array64[N: static[int]] = array[N, float64]
+
+  proc vector[N: static[int]](xs: Array64[N]): Vector64[N] =
+    new result
+    for i in 0 ..< N:
+      result[i] = xs[i]
+
+  let v1 = vector([1.0, 2.0, 3.0, 4.0, 5.0])
+  let v2 = vector([1.0, 2.0, 3.0, 4.0, 5.0])
+  let v3 = vector([1.0, 2.0, 3.0, 4.0])
+
+
+
+block t7794:
+  type
+    Data[T:SomeNumber, U:SomeFloat] = ref object
+      x: T
+      value*: U
+
+  var d = Data[int, float64](x:10.int, value:2'f64)
+  doAssert d.x == 10
+  doAssert d.value == 2.0
+
+
+
+block t8403:
+  proc sum[T](s: seq[T], R: typedesc): R =
+    var sum: R = 0
+    for x in s:
+      sum += R(x)
+    return sum
+
+  doAssert @[1, 2, 3].sum(float) == 6.0
+
+
+
+block t8439:
+  type
+    Cardinal = enum
+      north, east, south, west
+
+  proc foo[cardinal: static[Cardinal]](): int = 1
+  doAssert foo[north]() == 1
+
+
+
+block t8694:
+  when true:
+    # Error: undeclared identifier: '|'
+    proc bar[T](t:T): bool =
+      runnableExamples:
+        type Foo = int | float
+      true
+    doAssert bar(0)
+
+  when true:
+    # ok
+    proc bar(t:int): bool =
+      runnableExamples:
+        type Foo = int | float
+      true
+    doAssert bar(0)
+
+  when true:
+    # Error: undeclared identifier: '|'
+    proc bar(t:typedesc): bool =
+      runnableExamples:
+        type Foo = int | float
+      true
+    doAssert bar(int)
+
+
+
+block t9130:
+  when true:
+    # stack overflow
+    template baz1(iter: untyped): untyped =
+      runnableExamples:
+        import sugar
+        proc fun(a: proc(x:int): int) = discard
+        baz1(fun(x:int => x))
+      discard
+
+    proc foo1[A](ts: A) =
+      baz1(ts)
+
+  when true:
+    # ok
+    template baz2(iter: untyped): untyped =
+      runnableExamples:
+        import sugar
+        proc fun(a: proc(x:int): int) = discard
+        baz2(fun(x:int => x))
+      discard
+
+    proc foo2(ts: int) =
+      baz2(ts)
+
+  when true:
+    # stack overflow
+    template baz3(iter: untyped): untyped =
+      runnableExamples:
+        baz3(fun(x:int => x))
+      discard
+
+    proc foo3[A](ts: A) =
+      baz3(ts)
+
+
+
+block t1056:
+  type
+    TMatrix[N,M: static[int], T] = object
+      data: array[0..N*M-1, T]
+
+    TMat2[T] = TMatrix[2,2,T]
+
+  proc echoMatrix(a: TMatrix) =
+    echo a.type.name
+    echo TMatrix.N
+
+  proc echoMat2(a: TMat2) =
+    echo TMat2.M
+
+  var m = TMatrix[3,3,int](data: [1,2,3,4,5,6,7,8,9])
+
+  echoMatrix m
+
+
+
+block t4884:
+  type
+    Vec[N: static[int], T] = object
+      arr*: array[N, T]
+
+    Mat[N,M: static[int], T] = object
+      arr: array[N, Vec[M,T]]
+
+  var m : Mat[3,3,float]
+  var strMat : Mat[m.N, m.M, string]
+  var lenMat : Mat[m.N, m.M, int]
+
+
+
+block t2221:
+  var tblo: TableRef[string, int]
+  doAssert tblo == nil
+
+
+
+block t2304:
+  type TV2[T:SomeNumber] = array[0..1, T]
+  proc newV2T[T](x, y: T=0): TV2[T] = [x, y]
+
+  let x = newV2T[float](0.9, 0.1)
+  echo(@x)
+
+
+
+block t2752:
+  proc myFilter[T](it: (iterator(): T), f: (proc(anything: T):bool)): (iterator(): T) =
+    iterator aNameWhichWillConflict(): T {.closure.}=
+      for x in it():
+        if f(x):
+          yield x
+    result = aNameWhichWillConflict
+
+  iterator testIt():int {.closure.}=
+    yield -1
+    yield 2
+
+  #let unusedVariable = myFilter(testIt, (x: int) => x > 0)
+
+  proc onlyPos(it: (iterator(): int)): (iterator(): int)=
+    iterator aNameWhichWillConflict(): int {.closure.}=
+      var filtered = onlyPos(myFilter(it, (x:int) => x > 0))
+      for x in filtered():
+        yield x
+    result = aNameWhichWillConflict
+
+  let x = onlyPos(testIt)
+
+
+
+block t5106:
+  block:
+    type T = distinct int
+
+    proc `+`(a, b: T): T =
+      T(int(a) + int(b))
+
+    type U[F: static[T]] = distinct int
+
+    proc `+`[P1, P2: static[T]](a: U[P1], b: U[P2]): U[P1 + P2] =
+      U[P1 + P2](int(a) + int(b))
+
+    var a = U[T(1)](1)
+    var b = U[T(2)](2)
+    var c = a + b
+    echo c.type.name
+
+  block:
+    type T = object
+      f: int
+
+    proc `+`(a, b: T): T =
+      T(f: a.f + b.f)
+
+    type U[F: static[T]] = distinct int
+
+    proc `+`[P1, P2: static[T]](a: U[P1], b: U[P2]): U[P1 + P2] =
+      U[P1 + P2](int(a) + int(b))
+
+    var a = U[T(f: 1)](1)
+    var b = U[T(f: 2)](2)
+    var c = a + b
+    echo c.type.name
+
+  block:
+    type T = distinct array[0..0, int]
+
+    proc `+`(a, b: T): T =
+      T([array[0..0, int](a)[0] + array[0..0, int](b)[0]])
+
+    type U[F: static[T]] = distinct int
+
+    proc `+`[P1, P2: static[T]](a: U[P1], b: U[P2]): U[P1 + P2] =
+      U[P1 + P2](int(a) + int(b))
+
+    var a = U[T([1])](1)
+    var b = U[T([2])](2)
+    var c = a + b
+    echo c.type.name
+
+
+
+block t3055:
+  proc b(t: int | string)
+  proc a(t: int) = b(t)
+  proc b(t: int | string) = echo "b()"
+  a(1)
+
+  # test recursive generics still work:
+  proc fac[T](x: T): T =
+    if x == 0: return 1
+    else: return fac(x-1)*x
+
+  doAssert fac(6) == 720
+  doAssert fac(5.0) == 120.0
+
+
+  # test recursive generic with forwarding:
+  proc fac2[T](x: T): T
+
+  doAssert fac2(6) == 720
+  doAssert fac2(5.0) == 120.0
+
+  proc fac2[T](x: T): T =
+    if x == 0: return 1
+    else: return fac2(x-1)*x
+
+
+
+block t1187:
+  type
+    TEventArgs = object
+      skip: bool
+    TEventHandler[T] = proc (e: var TEventArgs, data: T) {.closure.}
+    TEvent[T] = object
+      #handlers: seq[TEventHandler[T]] # Does not work
+      handlers: seq[proc (e: var TEventArgs, d: T) {.closure.}] # works
+
+    TData = object
+      x: int
+
+    TSomething = object
+      s: TEvent[TData]
+
+  proc init[T](e: var TEvent[T]) =
+    e.handlers.newSeq(0)
+
+  #proc add*[T](e: var TEvent[T], h: proc (e: var TEventArgs, data: T) {.closure.}) =
+  # this line works
+  proc add[T](e: var TEvent[T], h: TEventHandler[T]) =
+    # this line does not work
+    e.handlers.add(h)
+
+  proc main () =
+    var something: TSomething
+    something.s.init()
+    var fromOutside = 4711
+
+    something.s.add() do (e: var TEventArgs, data: TData):
+      var x = data.x
+      x = fromOutside
+
+  main()
+
+
+
+block t1919:
+  type
+    Base[M] = object of RootObj
+      a : M
+    Sub1[M] = object of Base[M]
+      b : int
+    Sub2[M] = object of Sub1[M]
+      c : int
+
+  var x: Sub2[float]
+  doAssert x.a == 0.0
+
+
+
+block t5756:
+  type
+    Vec[N : static[int]] = object
+      x: int
+      arr: array[N, int32]
+
+    Mat[M,N: static[int]] = object
+      x: int
+      arr: array[M, Vec[N]]
+
+  proc vec2(x,y:int32) : Vec[2] =
+    result.arr = [x,y]
+    result.x = 10
+
+  proc mat2(a,b: Vec[2]): Mat[2,2] =
+    result.arr = [a,b]
+    result.x = 20
+
+  const M = mat2(vec2(1, 2), vec2(3, 4))
+
+  let m1 = M
+  echo @(m1.arr[0].arr)
+  echo @(m1.arr[1].arr)
+
+  proc foo =
+    let m2 = M
+    echo m1.arr[0].arr[0]
+
+  foo()
+
+
+
+block t7854:
+  type
+    Stream = ref StreamObj
+    StreamObj = object of RootObj
+
+    InhStream = ref InhStreamObj
+    InhStreamObj = object of Stream
+      f: string
+
+  proc newInhStream(f: string): InhStream =
+    new(result)
+    result.f = f
+
+  var val: int
+  let str = newInhStream("input_file.json")
+
+  block:
+    # works:
+    proc load[T](data: var T, s: Stream) =
+      discard
+    load(val, str)
+
+  block:
+    # works
+    proc load[T](s: Stream, data: T) =
+      discard
+    load(str, val)
+
+  block:
+    # broken
+    proc load[T](s: Stream, data: var T) =
+      discard
+    load(str, val)
+
+
+
+block t5864:
+  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()
+  let b = defaultGeneric()
+
+  doAssert a == 1
+  doAssert b == 2
+
+
+
+block t3498:
+  template defaultOf[T](t: T): untyped = (var d: T; d)
+
+  doAssert defaultOf(1) == 0
+
+  # assignment using template
+
+  template tassign[T](x: var seq[T]) =
+    x = @[1, 2, 3]
+
+  var y: seq[int]
+  tassign(y) #<- x is expected = @[1, 2, 3]
+  tassign(y)
+
+  doAssert y[0] == 1
+  doAssert y[1] == 2
+  doAssert y[2] == 3
+
+
+
+block t3499:
+  proc foo[T](x: proc(): T) =
+    echo "generic ", x()
+
+  proc foo(x: proc(): int) =
+    echo "concrete ", x()
+
+  # note the following 'proc' is not .closure!
+  foo(proc (): auto {.nimcall.} = 88)
+
+  # bug #3499 last snippet fixed
+  # bug 705  last snippet fixed
+
+
+
+
+block t797:
+  proc foo[T](s:T):string = $s
+
+  type IntStringProc = proc(x: int): string
+
+  var f1 = IntStringProc(foo)
+  var f2: proc(x: int): string = foo
+  var f3: IntStringProc = foo
+
+  echo f1(1), f2(2), f3(3)
+
+  for x in map([1,2,3], foo): echo x
+
+
+
+block t4658:
+  var x = 123
+  proc twice[T](f: T -> T): T -> T = (x: T) => f(f(x))
+  proc quote(s: string): string = "!" & s & "!"
+  echo twice(quote)("Hi")
+
+
+
+block t4589:
+  type SimpleTable[TKey, TVal] = TableRef[TKey, TVal]
+  template newSimpleTable(TKey, TVal: typedesc): SimpleTable[TKey, TVal] = newTable[TKey, TVal]()
+  var fontCache : SimpleTable[string, SimpleTable[int32, int]]
+  fontCache = newSimpleTable(string, SimpleTable[int32, int])
+
+
+
+block t4600:
+  template foo(x: untyped): untyped = echo 1
+  template foo(x,y: untyped): untyped = echo 2
+
+  proc bar1[T](x: T) = foo(x)
+  proc bar2(x: float) = foo(x,x)
+  proc bar3[T](x: T) = foo(x,x)
+
+
+
+block t4672:
+  type
+    EnumContainer[T: enum] = object
+      v: T
+    SomeEnum {.pure.} = enum
+      A,B,C
+
+  proc value[T: enum](this: EnumContainer[T]): T =
+    this.v
+
+  var enumContainer: EnumContainer[SomeEnum]
+  discard enumContainer.value()
+
+
+
+block t4863:
+  type
+    G[i,j: static[int]] = object
+      v:float
+    H[j: static[int]] = G[0,j]
+  proc p[i,j: static[int]](x:G[i,j]) = echo "G:", i, ",", j, ":", x.v
+  proc q[j: static[int]](x:H[j]) = echo "H:", j, ":", x.v
+
+  var
+    g0 = G[0,1](v: 0.1)
+    h0:H[1] = g0
+  p(g0)
+  p(h0)
+  q(h0)
+
+
+
+block t1684:
+  type
+    BaseType {.inheritable pure.} = object
+      idx: int
+
+    DerivedType {.final pure.} = object of BaseType
+
+  proc index[Toohoo: BaseType](h: Toohoo): int {.inline.} = h.idx
+  proc newDerived(idx: int): DerivedType {.inline.} = DerivedType(idx: idx)
+
+  let d = newDerived(2)
+  doAssert(d.index == 2)
+
+
+
+block t5632:
+  type Option[T] = object
+
+  proc point[A](v: A, t: typedesc[Option[A]]): Option[A] =
+    discard
+
+  discard point(1, Option)
+
+
+
+block t7247:
+  type n8 = range[0'i8..127'i8]
+  var tab = initHashSet[n8]()
+  doAssert tab.contains(8) == false
+
+
+
+block t3717:
+  type
+    Foo[T] = object
+      a: T
+    Foo1[T] = Foo[T] | int
+
+  proc foo[T](s: Foo1[Foo[T]]): T =
+    5
+
+  var f: Foo[Foo[int]]
+  discard foo(f)
+
+
+
+block: # issue #9458
+  type
+    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) =
+    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
new file mode 100644
index 000000000..53661236e
--- /dev/null
+++ b/tests/generics/tgenerics_various.nim
@@ -0,0 +1,254 @@
+discard """
+  output: '''
+we
+direct
+generic
+generic
+'''
+joinable: false
+"""
+
+import algorithm, sugar, sequtils, typetraits, asyncdispatch
+
+block tconfusing_arrow:
+  type Deck = object
+    value: int
+
+  proc sort(h: var seq[Deck]) =
+    # works:
+    h.sort(proc (x, y: Deck): auto =
+      cmp(x.value, y.value))
+    # fails:
+    h.sort((x, y: Deck) => cmp(ord(x.value), ord(y.value)))
+
+  var player: seq[Deck] = @[]
+  player.sort()
+
+
+
+block tdictdestruct:
+  type
+    TDict[TK, TV] = object
+      k: TK
+      v: TV
+    PDict[TK, TV] = ref TDict[TK, TV]
+
+  proc fakeNew[T](x: var ref T, destroy: proc (a: ref T) {.nimcall.}) =
+    discard
+
+  proc destroyDict[TK, TV](a: PDict[TK, TV]) =
+      return
+  proc newDict[TK, TV](a: TK, b: TV): PDict[TK, TV] =
+      fakeNew(result, destroyDict[TK, TV])
+
+  # Problem: destroyDict is not instantiated when newDict is instantiated!
+  discard newDict("a", "b")
+
+
+
+block tgenericdefaults:
+  type
+    TFoo[T, U, R = int] = object
+      x: T
+      y: U
+      z: R
+
+    TBar[T] = TFoo[T, array[4, T], T]
+
+  var x1: TFoo[int, float]
+
+  static:
+    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:
+    doAssert type(x2.x) is string
+    doAssert type(x2.y) is seq[int]
+    doAssert type(x2.z) is float
+
+  var x3: TBar[float]
+
+  static:
+    doAssert type(x3.x) is float
+    doAssert type(x3.y) is array[4, float]
+    doAssert type(x3.z) is float
+
+
+
+block tprop:
+  type
+    TProperty[T] = object of RootObj
+      getProc: proc(property: TProperty[T]): T {.nimcall.}
+      setProc: proc(property: TProperty[T], value: T) {.nimcall.}
+      value: T
+
+  proc newProperty[T](value: RootObj): TProperty[T] =
+    result.getProc = proc (property: TProperty[T]) =
+      return property.value
+
+
+
+block trefs:
+  type
+    PA[T] = ref TA[T]
+    TA[T] = object
+      field: T
+  var a: PA[string]
+  new(a)
+  a.field = "some string"
+
+  proc someOther[T](len: string): seq[T] = discard
+  proc someOther[T](len: int): seq[T] = echo "we"
+
+  proc foo[T](x: T) =
+    var s = someOther[T](34)
+    #newSeq[T](34)
+
+  foo 23
+
+  when false:
+    # Compiles unless you use var a: PA[string]
+    type
+      PA = ref TA
+      TA[T] = object
+
+    # Cannot instantiate:
+    type
+      TA[T] = object
+        a: PA[T]
+      PA[T] = ref TA[T]
+
+    type
+      PA[T] = ref TA[T]
+      TA[T] = object
+
+
+
+block tmap_auto:
+  let x = map(@[1, 2, 3], x => x+10)
+  doAssert x == @[11, 12, 13]
+
+  let y = map(@[(1,"a"), (2,"b"), (3,"c")], x => $x[0] & x[1])
+  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)
+  doAssert z == "1a"
+
+
+
+block tproctypecache_falsepositive:
+  type
+    Callback = proc() {.closure, gcsafe.}
+    GameState = ref object
+      playerChangeHandlers: seq[Callback]
+
+  proc newGameState(): GameState =
+    result = GameState(
+      playerChangeHandlers: newSeq[Callback]() # this fails
+    )
+
+
+
+block tptrinheritance:
+  type NSPasteboardItem = ptr object
+  type NSPasteboard = ptr object
+  type NSArrayAbstract {.inheritable.} = ptr object
+  type NSMutableArrayAbstract = ptr object of NSArrayAbstract
+  type NSArray[T] = ptr object of NSArrayAbstract
+  type NSMutableArray[T] = ptr object of NSArray[T]
+
+  proc newMutableArrayAbstract(): NSMutableArrayAbstract = discard
+
+  template newMutableArray(T: typedesc): NSMutableArray[T] =
+    cast[NSMutableArray[T]](newMutableArrayAbstract())
+
+  proc writeObjects(p: NSPasteboard, o: NSArray[NSPasteboardItem]) = discard
+
+  let a = newMutableArray NSPasteboardItem
+  var x: NSMutableArray[NSPasteboardItem]
+  var y: NSArray[NSPasteboardItem] = x
+
+  writeObjects(nil, a)
+
+
+
+block tsigtypeop:
+  type Vec3[T] = array[3, T]
+
+  proc foo(x: Vec3, y: Vec3.T, z: x.T): x.type.T =
+    return 10
+
+  var y: Vec3[int] = [1, 2, 3]
+  var z: int = foo(y, 3, 4)
+
+
+
+block tvarargs_vs_generics:
+  proc withDirectType(args: string) =
+    echo "direct"
+  proc withDirectType[T](arg: T) =
+    echo "generic"
+  proc withOpenArray(args: openArray[string]) =
+    echo "openArray"
+  proc withOpenArray[T](arg: T) =
+    echo "generic"
+  proc withVarargs(args: varargs[string]) =
+    echo "varargs"
+  proc withVarargs[T](arg: T) =
+    echo "generic"
+
+  withDirectType "string"
+  withOpenArray "string"
+  withVarargs "string"
+
+block:
+  type
+    Que[T] {.gcsafe.} = object
+      x: T
+
+  proc `=`[T](q: var Que[T]; x: Que[T]) =
+    discard
+
+  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/tgenerictmpl2.nim b/tests/generics/tgenerictmpl2.nim
new file mode 100644
index 000000000..2efb000b3
--- /dev/null
+++ b/tests/generics/tgenerictmpl2.nim
@@ -0,0 +1,31 @@
+discard """
+  output: '''1
+1
+1
+1
+999
+999
+999
+2'''
+"""
+
+# test if we can pass explicit generic arguments to generic templates
+# based on bug report #3496
+
+proc     tproc[T](t: T = 999) = echo t
+template ttmpl[T](t: T = 999) = echo t
+
+tproc(1)
+tproc[int](1)
+ttmpl(1)
+ttmpl[int](1) #<- crash case #1
+
+tproc[int]()
+let _ = tproc[int]
+ttmpl[int]()  #<- crash case #2
+ttmpl[int]    #<- crash case #3
+
+# but still allow normal use of [] on non-generic templates
+
+template tarr: untyped = [1, 2, 3, 4]
+echo tarr[1]
diff --git a/tests/generics/tgenericvariant.nim b/tests/generics/tgenericvariant.nim
new file mode 100644
index 000000000..5ba3a2e7c
--- /dev/null
+++ b/tests/generics/tgenericvariant.nim
@@ -0,0 +1,36 @@
+discard """
+output: '''
+Test
+abcxyz123
+'''
+"""
+
+proc fakeReadLine(): string =
+  "abcxyz123"
+
+type
+  TMaybe[T] = object
+    case empty: bool
+    of false: value: T
+    else: nil
+
+proc Just*[T](val: T): TMaybe[T] =
+  result.empty = false
+  result.value = val
+
+proc Nothing[T](): TMaybe[T] =
+  result.empty = true
+
+proc safeReadLine(): TMaybe[string] =
+  var r = fakeReadLine()
+  if r == "": return Nothing[string]()
+  else: return Just(r)
+
+proc main() =
+  var Test = Just("Test")
+  echo(Test.value)
+  var mSomething = safeReadLine()
+  echo(mSomething.value)
+  mSomething = safeReadLine()
+
+main()
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
new file mode 100644
index 000000000..e252ad194
--- /dev/null
+++ b/tests/generics/timports.nim
@@ -0,0 +1,63 @@
+discard """
+  output: '''
+317
+TEST2
+5 5 5
+false
+'''
+"""
+
+import mbind_bracket, mclosed_sym, mdotlookup, mmodule_same_as_proc, mtypenodes
+
+
+block tbind_bracket:
+  # bug #2599
+  # also test that `[]` can be passed now as a first class construct:
+
+  template takeBracket(x, a, i: untyped) =
+    echo x(a, i)
+
+  var a: array[10, int]
+  a[8] = 317
+
+  takeBracket(`[]`, a, 8)
+
+  let reg = newRegistry[UUIDObject]()
+  reg.register(UUIDObject())
+
+
+block tclosed_sym:
+  # bug #2664
+  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/tlateboundgenericparams.nim b/tests/generics/tlateboundgenericparams.nim
new file mode 100644
index 000000000..9f0580fd2
--- /dev/null
+++ b/tests/generics/tlateboundgenericparams.nim
@@ -0,0 +1,145 @@
+discard """
+  output: "1\n10\n1\n10"
+  nimout: '''
+bar instantiated with 1
+bar instantiated with 10
+'''
+"""
+
+import typetraits
+
+type
+  Foo = object
+
+proc defaultFoo: Foo = discard
+proc defaultInt: int = 1
+proc defaultTInt(T: type): int = 2
+proc defaultTFoo[T](x: typedesc[T]): Foo = discard
+proc defaultTOldSchool[T](x: typedesc[T]): T = discard
+proc defaultTModern(T: type): T = discard
+
+proc specializedDefault(T: type int): int = 10
+proc specializedDefault(T: type string): string = "default"
+
+converter intFromFoo(x: Foo): int = 3
+
+proc consumeInt(x: int) =
+  discard
+
+const activeTests = {1..100}
+
+when true:
+  template test(n, body) =
+    when n in activeTests:
+      block:
+        body
+
+  template reject(x) =
+    static: assert(not compiles(x))
+
+  test 1:
+    proc t[T](val: T = defaultInt()) =
+      consumeInt val
+
+    t[int]()
+    reject t[string]()
+
+  test 2:
+    proc t1[T](val: T = defaultFoo()) =
+      static:
+        assert type(val).name == "int"
+        assert T.name == "int"
+
+      consumeInt val
+
+    # here, the converter should kick in, but notice
+    # how `val` is still typed `int` inside the proc.
+    t1[int]()
+
+    proc t2[T](val: T = defaultFoo()) =
+      discard
+
+    reject t2[string]()
+
+  test 3:
+    proc tInt[T](val = defaultInt()): string =
+      return type(val).name
+
+    doAssert tInt[int]() == "int"
+    doAssert tInt[string]() == "int"
+
+    proc tInt2[T](val = defaultTInt(T)): string =
+      return type(val).name
+
+    doAssert tInt2[int]() == "int"
+    doAssert tInt2[string]() == "int"
+
+    proc tDefTModern[T](val = defaultTModern(T)): string =
+      return type(val).name
+
+    doAssert tDefTModern[int]() == "int"
+    doAssert tDefTModern[string]() == "string"
+    doAssert tDefTModern[Foo]() == "Foo"
+
+    proc tDefTOld[T](val = defaultTOldSchool(T)): string =
+      return type(val).name
+
+    doAssert tDefTOld[int]() == "int"
+    doAssert tDefTOld[string]() == "string"
+    doAssert tDefTOld[Foo]() == "Foo"
+
+  test 4:
+    proc t[T](val: T = defaultTFoo(T)): string =
+      return type(val).name
+
+    doAssert t[int]() == "int"
+    doAssert t[Foo]() == "Foo"
+    reject t[string]()
+
+  test 5:
+    proc t1[T](a: T = specializedDefault(T)): T =
+      return a
+
+    doAssert t1[int]() == 10
+    doAssert t1[string]() == "default"
+
+    proc t2[T](a: T, b = specializedDefault(T)): auto =
+      return $a & $b
+
+    doAssert t2(5) == "510"
+    doAssert t2("string ") == "string default"
+
+    proc t3[T](a: T, b = specializedDefault(type(a))): auto =
+      return $a & $b
+
+    doAssert t3(100) == "10010"
+    doAssert t3("another ") == "another default"
+
+  test 6:
+    # https://github.com/nim-lang/Nim/issues/5595
+    type
+      Point[T] = object
+        x, y: T
+
+    proc getOrigin[T](): Point[T] = Point[T](x: 0, y: 0)
+
+    proc rotate[T](point: Point[T], radians: float,
+                   origin = getOrigin[T]()): Point[T] =
+      discard
+
+    var p = getOrigin[float]()
+    var rotated = p.rotate(2.1)
+
+  test 7:
+    proc bar(x: static[int]) =
+      static: echo "bar instantiated with ", x
+      echo x
+
+    proc foo(x: static[int] = 1) =
+      bar(x)
+
+    foo()
+    foo(10)
+    foo(1)
+    foo(10)
+
diff --git a/tests/generics/tlateboundstatic.nim b/tests/generics/tlateboundstatic.nim
new file mode 100644
index 000000000..90b44aa8e
--- /dev/null
+++ b/tests/generics/tlateboundstatic.nim
@@ -0,0 +1,16 @@
+discard """
+  nimout: "array[0..3, int]"
+"""
+
+type
+  KK[I: static[int]] = object
+   x: array[I, int]
+
+proc foo(a: static[string]): KK[a.len] =
+  result.x[0] = 12
+
+var x = foo "test"
+
+import typetraits
+static: echo x.x.type.name
+
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/tmapping_generic_alias.nim b/tests/generics/tmapping_generic_alias.nim
new file mode 100644
index 000000000..efdf32ead
--- /dev/null
+++ b/tests/generics/tmapping_generic_alias.nim
@@ -0,0 +1,28 @@
+discard """
+output: '''type(c) = GenAlias[system.int]
+T = int
+seq[int]
+'''
+"""
+
+import typetraits
+
+type
+  Gen[T] = object
+    x: T
+
+  GenAlias[T] = Gen[seq[T]]
+
+proc f1[T](x: Gen[T]) =
+  echo T.name
+
+proc f2[T](x: GenAlias[T]) =
+  echo "type(c) = ", type(x).name
+  echo "T = ", T.name
+  f1 x
+
+let
+  y = Gen[seq[int]](x: @[10])
+
+f2 y
+
diff --git a/tests/generics/tmetafield.nim b/tests/generics/tmetafield.nim
new file mode 100644
index 000000000..cf30a936d
--- /dev/null
+++ b/tests/generics/tmetafield.nim
@@ -0,0 +1,30 @@
+discard """
+  cmd: "nim check $options $file"
+  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
+
+  TBaseMed =  object
+    doSmth: proc
+    data: seq[Foo]
+
+var a: TBaseMed
+
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
new file mode 100644
index 000000000..6f223c154
--- /dev/null
+++ b/tests/generics/tobjecttyperel.nim
@@ -0,0 +1,89 @@
+discard """
+  matrix: "-d:nimInternalNonVtablesTesting"
+  output: '''(peel: 0, color: 15)
+(color: 15)
+17
+(width: 0.0, taste: "", color: 13)
+(width: 0.0, taste: "", color: 15)
+cool
+test'''
+"""
+
+# bug #5241
+type
+  BaseFruit[T] = object of RootObj
+    color: T
+
+  MidLevel[T] = object of BaseFruit[T]
+
+  Mango = object of MidLevel[int]
+    peel: int
+
+  Peach[X, T, Y] = object of T
+    width: X
+    taste: Y
+
+proc setColor[T](self: var BaseFruit[T]) =
+  self.color = 15
+
+proc setColor[T](self: var BaseFruit[T], c: int) =
+  self.color = c
+
+var c: Mango
+setColor(c)
+echo c
+
+var d: MidLevel[int]
+setColor(d)
+echo d
+
+type
+  FooBase[T] = ref object of RootRef
+    v: T
+  BarClient = ref object of FooBase[int]
+
+proc getColor[T](f: FooBase[T]): T = 17
+var b: BarClient
+echo getColor(b)
+
+var z: Peach[float64, BaseFruit[int], string]
+z.setColor(13)
+echo z
+
+z.setColor()
+echo z
+
+# bug #5411
+type
+  Foo[T] = ref object of RootRef
+    v: T
+  Bar = ref object of Foo[int]
+
+method m(o: RootRef) {.base.} = assert(false, "Abstract method called")
+method m[T](o: Foo[T]) = echo "cool"
+
+var v: Bar
+v.new()
+v.m() # Abstract method not called anymore
+
+
+# bug #88
+
+type
+  TGen[T] = object of RootObj
+    field: T
+
+  TDerived[T] = object of TGen[T]
+    nextField: T
+
+proc doSomething[T](x: ref TGen[T]) =
+  type
+    Ty = ref TDerived[T]
+  echo Ty(x).nextField
+
+var
+  x: ref TDerived[string]
+new(x)
+x.nextField = "test"
+
+doSomething(x)
diff --git a/tests/generics/tobjecttyperel2.nim b/tests/generics/tobjecttyperel2.nim
new file mode 100644
index 000000000..d8c0751b7
--- /dev/null
+++ b/tests/generics/tobjecttyperel2.nim
@@ -0,0 +1,42 @@
+discard """
+  output: '''1
+a
+13'''
+"""
+
+# bug #5621 #5615
+type
+  Obj5[T] = ref object of RootObj
+    x_impl: T
+
+proc x[T](v476205: Obj5[T]): T {.used.} =
+  v476205.x_impl
+
+type
+  Obj6[T, U] = ref object of Obj5[T]
+    y_impl: U
+
+proc newObj6[T, U](x: T; y: U): Obj6[T, U] =
+  new(result)
+  result.x_impl = x
+  result.y_impl = y
+
+proc x[T, U](v477606: Obj6[T, U]): T {.used.} =
+  v477606.x_impl
+
+proc y[T, U](v477608: Obj6[T, U]): U {.used.} =
+  v477608.y_impl
+
+let e = newObj6(1, "a")
+echo e.x
+echo e.y
+
+type
+  Fruit[T] = ref object of RootObj
+  Apple[T] = ref object of Fruit[T]
+
+proc getColor[T](v: Fruit[T]): T = 13
+
+var w: Apple[int]
+let r = getColor(w)
+echo r
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
new file mode 100644
index 000000000..4d748bfee
--- /dev/null
+++ b/tests/generics/toverloading_typedesc.nim
@@ -0,0 +1,19 @@
+import moverloading_typedesc
+import tables
+
+type
+  LFoo = object
+  LBar = object
+
+when true:
+  doAssert FBar.new() == 3
+
+  proc new(_: typedesc[LFoo]): int = 0
+  proc new[T](_: typedesc[T]): int = 1
+  proc new*(_: typedesc[seq[Table[int, seq[Table[int, typedesc]]]]]): int = 7
+
+  doAssert LFoo.new() == 0     # Tests selecting more precise type
+  doAssert LBar.new() == 1     # Tests preferring function from local scope
+  doAssert FBar.new() == 1
+  doAssert FFoo.new() == 2     # Tests selecting more precise type from other module
+  doAssert seq[Table[int, seq[Table[int, string]]]].new() == 5     # Truly complex type test
diff --git a/tests/generics/tparam_binding.nim b/tests/generics/tparam_binding.nim
new file mode 100644
index 000000000..fa7558613
--- /dev/null
+++ b/tests/generics/tparam_binding.nim
@@ -0,0 +1,29 @@
+discard """
+  matrix: "--mm:arc; --mm:refc"
+  errormsg: "got <ref Matrix[2, 2, system.float], ref Matrix[2, 1, system.float]>"
+  line: 28
+"""
+
+type
+  Matrix[M,N: static[int]; T: SomeFloat] = distinct array[0..(M*N - 1), T]
+
+let a = new Matrix[2,2,float]
+let b = new Matrix[2,1,float]
+
+proc foo[M,N: static[int],T](a: ref Matrix[M, N, T], b: ref Matrix[M, N, T])=
+  discard
+
+foo(a, a)
+
+proc bar[M,N: static[int],T](a: ref Matrix[M, M, T], b: ref Matrix[M, N, T])=
+  discard
+
+bar(a, b)
+bar(a, a)
+
+proc baz[M,N: static[int],T](a: ref Matrix[N, N, T], b: ref Matrix[M, N, T])=
+  discard
+
+baz(a, a)
+baz(a, b)
+
diff --git a/tests/generics/tparser_generator.nim b/tests/generics/tparser_generator.nim
new file mode 100644
index 000000000..ac921c0e5
--- /dev/null
+++ b/tests/generics/tparser_generator.nim
@@ -0,0 +1,415 @@
+discard """
+  output: '''Match failed: spam
+Match failed: ham'''
+joinable: false
+"""
+
+# bug #6220
+
+import nre
+import options
+import strutils except isAlpha, isLower, isUpper, isSpace
+from unicode import isAlpha, isLower, isUpper, isTitle, isWhiteSpace
+import os
+
+const debugLex = false
+
+template debug(enable: bool, text: string): typed =
+  when enable:
+    echo(text)
+
+type
+  Parser[N, T] = proc(text: T, start: int, nodes: var seq[Node[N]]): int {.closure.}
+
+  RuleObj[N, T] = object
+    parser: Parser[N, T]
+    kind: N
+
+  Rule[N, T] = ref RuleObj[N, T]
+
+  NodeKind = enum
+    terminal,
+    nonterminal
+
+  Node*[N] = object of RootObj
+    # Uncomment the following lines and the compiler crashes
+    # case nodeKind: NodeKind
+    #   of nonterminal:
+    #     kids: Node[N]
+    #   of terminal:
+    #     discard
+    start*: int
+    length*: int
+    kind*: N
+
+
+  NonTerminal[N] = object of Node
+    children: seq[Node[N]]
+
+proc newRule[N, T](parser: Parser, kind: N): Rule[N, T] =
+  new(result)
+  result.parser = parser
+  result.kind = kind
+
+proc newRule[N, T](kind: N): Rule[N, T] =
+  new(result)
+  result.kind = kind
+
+proc initNode[N](start: int, length: int, kind: N): Node[N] =
+  result.start = start
+  result.length = length
+  result.kind = kind
+
+proc initNode[N](start: int, length: int, children: seq[Node[N]], kind: N): NonTerminal[N] =
+  result.start = start
+  result.length = length
+  result.kind = kind
+  result.children = children
+
+proc substr[T](text: T, first, last: int): T =
+  text[first .. last]
+
+proc continuesWith[N](text: seq[Node[N]], subtext: seq[N], start: Natural): bool =
+  let length = len(text)
+  var pos = 0
+  while pos < len(subtext):
+    let textpos = start + pos
+    if textpos == len(text):
+      return false
+    if text[textpos].kind != subtext[pos].kind:
+      return false
+    pos+=1
+  return true
+
+
+proc render*[N, T](text: T, nodes: seq[Node[N]]): string =
+  ## Uses a sequence of Nodes to render a given text string
+  result = ""
+  for node in nodes:
+    result.add("<" & node.value(text) & ">")
+
+proc render*[N, T](rule: Rule[N, T], text: string): string =
+  ## Uses a rule to render a given text string
+  render(text, rule.parse(text))
+
+proc render*[N, T](text: T, nodes: seq[Node[N]], source: string): string =
+  result = ""
+  for node in nodes:
+    result.add("[" & node.value(text, source) & "]")
+
+proc render*[N, T, X](rule: Rule[N, T], text: seq[Node[X]], source: string): string =
+  ## Uses a rule to render a given series of nodes, providing the source string
+  text.render(rule.parse(text, source = source), source)
+
+proc annotate*[N, T](node: Node[N], text: T): string =
+  result = "<" & node.value(text) & ":" & $node.kind & ">"
+
+proc annotate*[N, T](nodes: seq[Node[N]], text: T): string =
+  result = ""
+  for node in nodes:
+    result.add(node.annotate(text))
+
+proc annotate*[N, T](rule: Rule[N, T], text: T): string =
+  annotate(rule.parse(text), text)
+
+proc value*[N, T](node: Node[N], text: T): string =
+  result = $text.substr(node.start, node.start + node.length - 1)
+
+proc value*[N, X](node: Node[N], text: seq[Node[X]], source: string): string =
+  result = ""
+  for n in node.start ..< node.start + node.length:
+    result &= text[n].annotate(source)
+
+proc parse*[N, T](rule: Rule[N, T], text: T, start = 0, source: string = ""): seq[Node[N]] =
+  result = newSeq[Node[N]]()
+  debug(debugLex, "Parsing: " & $text)
+  let length = rule.parser(text, start, result)
+
+  when T is string:
+    if length == -1:
+      echo("Match failed: " & $text)
+      result = @[]
+    elif length == len(text):
+      debug(debugLex, "Matched: " & $text & " => " & $len(result) & " tokens: " & text.render(result))
+    else:
+      echo("Matched first " & $length & " symbols: " & $text & " => " & $len(result) & " tokens: " & text.render(result))
+  else:
+    if length == -1:
+      echo("Match failed: " & $text)
+      result = @[]
+    elif length == len(text):
+      debug(debugLex, "Matched: " & $text & " => " & $len(result) & " tokens: " & text.render(result, source))
+    else:
+      echo("Matched first " & $length & " symbols: " & $text & " => " & $len(result) & " tokens: " & text.render(result, source))
+
+
+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
+    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):
+        let node = initNode(start, len(pattern), kind)
+        nodes.add(node)
+        debug(debugLex, "Literal: matched <" & $text[start ..< start+node.length] & ":" & $node.length & ">" )
+        return node.length
+    elif P is char:
+      debug(debugLex, "Literal[" & $kind & "]: testing " & $pattern & " at " & $start & ": " & $text[start])
+      if text[start] == pattern:
+        let node = initNode(start, 1, kind)
+        nodes.add(node)
+        return 1
+    else:
+      debug(debugLex, "Literal[" & $kind & "]: testing " & $pattern & " at " & $start & ": " & $text[start])
+      if text[start].kind == pattern:
+        let node = initNode(start, 1, kind)
+        nodes.add(node)
+        return 1
+    return -1
+  result = newRule[N, T](parser, kind)
+
+proc token[N, T](pattern: T, kind: N): Rule[N, T] =
+  when T is not string:
+     {.fatal: "Token is only supported for strings".}
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    debug(debugLex, "Token[" & $kind & "]: testing " & pattern & " at " & $start)
+    if start == len(text):
+      return -1
+    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)
+      nodes.add(node)
+      result = node.length
+      debug(debugLex, "Token: matched <" & text[start ..< start+node.length] & ":" & $node.length & ">" )
+    else:
+      result = -1
+  result = newRule[N, T](parser, kind)
+
+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
+    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
+    else:
+      result = -1
+  result = newRule[N, T](parser, kind)
+
+proc any*[N, T, S](symbols: T, kind: N): Rule[N, T] =
+  let test = proc(s: S): bool =
+    when S is string:
+      debug(debugLex, "Any[" & $kind & "]: testing for " & symbols.replace("\n", "\\n").replace("\r", "\\r"))
+    else:
+      debug(debugLex, "Any[" & $kind & "]: testing for " & $symbols)
+    result = s in symbols
+  result = chartest[N, T, S](test, kind)
+
+proc ignore*[N, T](rule: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var mynodes = newSeq[Node[N]]()
+    result = rule.parser(text, start, mynodes)
+  result = newRule[N, T](parser, rule.kind)
+
+proc combine*[N, T](rule: Rule[N, T], kind: N): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var mynodes = newSeq[Node[N]]()
+    result = rule.parser(text, start, mynodes)
+    nodes.add(initNode(start, result, kind))
+  result = newRule[N, T](parser, kind)
+
+proc build*[N, T](rule: Rule[N, T], kind: N): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var mynodes = newSeq[Node[N]]()
+    result = rule.parser(text, start, mynodes)
+    let nonTerminal = initNode(start, result, mynodes, kind)
+    nodes.add(nonTerminal)
+  result = newRule[N, T](parser, kind)
+
+proc fail*[N, T](message: string, kind: N): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    let lineno = countLines(text[0..start])
+    var startline = start
+    var endline = start
+    while startline>0:
+      if text[startline] in NewLines:
+        break
+      startline-=1
+    while endline < len(text):
+      if text[endline] in NewLines:
+        break
+      endline+=1
+    let charno = start-startline
+    echo text.substr(startline, endline)
+    echo ' '.repeat(max(charno,0)) & '^'
+    raise newException(ValueError, "Position: " & $start & " Line: " & $lineno & ", Symbol: " & $charno & ": " & message)
+  result = newRule[N, T](parser, kind)
+
+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]]()
+    doAssert(not isNil(left.parser), "Left hand side parser is nil")
+    let leftlength = left.parser(text, start, mynodes)
+    if leftlength == -1:
+      return leftlength
+    doAssert(not isNil(right.parser), "Right hand side parser is nil")
+    let rightlength = right.parser(text, start+leftlength, mynodes)
+    if rightlength == -1:
+      return rightlength
+    result = leftlength + rightlength
+    nodes.add(mynodes)
+  result = newRule[N, T](parser, left.kind)
+
+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]]()
+    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]]()
+    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
+    nodes.add(mynodes)
+    return rightlength
+  result = newRule[N, T](parser, left.kind)
+
+proc `?`*[N, T](rule: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    let success = rule.parser(text, start, nodes)
+    return if success != -1: success else: 0
+  result = newRule[N, T](parser, rule.kind)
+
+proc `+`*[N, T](rule: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var success = rule.parser(text, start, nodes)
+    if success == -1:
+      return success
+    var total = 0
+    while success != -1 and start+total < len(text):
+      total += success
+      success = rule.parser(text, start+total, nodes)
+    return total
+  result = newRule[N, T](parser, rule.kind)
+
+proc `*`*[N, T](rule: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    let success = (+rule).parser(text, start, nodes)
+    return if success != -1: success else: 0
+  result = newRule[N, T](parser, rule.kind)
+
+#Note: this consumes - for zero-width lookahead see !
+proc `^`*[N, T](rule: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var mynodes = newSeq[Node[N]]()
+    let success = rule.parser(text, start, mynodes)
+    return if success == -1: 1 else: -1
+  result = newRule[N, T](parser, rule.kind)
+
+proc `*`*[N, T](repetitions: int, rule: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var mynodes = newSeq[Node[N]]()
+    var total = 0
+    for i in 0..<repetitions:
+      let success = rule.parser(text, start+total, mynodes)
+      if success == -1:
+        return success
+      else:
+        total += success
+    nodes.add(mynodes)
+    return total
+  result = newRule[N, T](parser, rule.kind)
+
+# Positive zero-width lookahead
+proc `&`*[N, T](rule: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var mynodes = newSeq[Node[N]]()
+    let success = rule.parser(text, start, mynodes)
+    return if success != -1: 0 else: -1
+  result = newRule[N, T](parser, rule.kind)
+
+# Negative zero-width lookahead
+proc `!`*[N, T](rule: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var mynodes = newSeq[Node[N]]()
+    let failure = rule.parser(text, start, mynodes)
+    return if failure == -1: 0 else: -1
+  result = newRule[N, T](parser, rule.kind)
+
+proc `/`*[N, T](rule: Rule[N, T]): Rule[N, T] =
+  let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int =
+    var mynodes = newSeq[Node[N]]()
+    var length = 0
+    var success = rule.parser(text, start+length, mynodes)
+    while success == -1 and start+length < len(text):
+      length += 1
+      success = rule.parser(text, start+length, mynodes)
+    if start+length >= len(text):
+      result = -1
+    else:
+      nodes.add(initNode(start, length, rule.kind))
+      nodes.add(mynodes)
+      result = length + success
+  result = newRule[N, T](parser, rule.kind)
+
+proc `->`*(rule: Rule, production: Rule) =
+  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.} =
+
+    proc newRule(): Rule[Kind, Text] {.inject.} = newRule[Kind, Text](default)
+    proc chartest(testfunc: proc(c: Symbol): bool): Rule[Kind, Text] {.inject.} = chartest[Kind, Text, Symbol](testfunc, default)
+    proc literal[P](pattern: P, kind: K): Rule[Kind, Text] {.inject.} = literal[Kind, Text, P](pattern, kind)
+    proc literal[P](pattern: P): Rule[Kind, Text] {.inject.} = literal[Kind, Text, P](pattern, default)
+
+    when Text is string:
+      proc token(pattern: string): Rule[Kind, Text] {.inject.} = token(pattern, default)
+      proc fail(message: string): Rule[Kind, Text] {.inject.} = fail[Kind, Text](message, default)
+      let alpha {.inject.} = chartest[Kind, Text, Symbol](isAlphaAscii, default)
+      let alphanumeric {.inject.}= chartest[Kind, Text, Symbol](isAlphaNumeric, default)
+      let digit {.inject.} = chartest[Kind, Text, Symbol](isDigit, default)
+      let lower {.inject.} = chartest[Kind, Text, Symbol](isLowerAscii, default)
+      let upper {.inject.} = chartest[Kind, Text, Symbol](isUpperAscii, default)
+      let isspace = proc (x: char): bool = x.isSpaceAscii and not (x in NewLines)
+      let space {.inject.} = chartest[Kind, Text, Symbol](isspace, default)
+      let isnewline = proc (x: char): bool = x in NewLines
+      let newline {.inject.} = chartest[Kind, Text, Symbol](isnewline, default)
+      let alphas {.inject.} = combine(+alpha, default)
+      let alphanumerics {.inject.} = combine(+alphanumeric, default)
+      let digits {.inject.} = combine(+digit, default)
+      let lowers {.inject.} = combine(+lower, default)
+      let uppers {.inject.} = combine(+upper, default)
+      let spaces {.inject.} = combine(+space, default)
+      let newlines {.inject.} = combine(+newline, default)
+
+    proc any(chars: Text): Rule[Kind, Text] {.inject.} = any[Kind, Text, Symbol](chars, default)
+    proc combine(rule: Rule[Kind, Text]): Rule[Kind, Text] {.inject.} = combine[Kind, Text](rule, default)
+
+    code
+
+template grammar*[K](Kind: typedesc; default: K, code: untyped): typed {.hint[XDeclaredButNotUsed]: off.} =
+  grammar(Kind, string, char, default, code)
+
+block:
+  type DummyKind = enum dkDefault
+  grammar(DummyKind, string, char, dkDefault):
+    let rule = token("h[a]+m") + ignore(token(r"\s+")) + (literal("eggs") / literal("beans"))
+    var text = "ham beans"
+    discard rule.parse(text)
+
+    var recursive = newRule()
+    recursive -> (literal("(") + recursive + literal(")")) / token(r"\d+")
+    for test in ["spam", "57", "(25)", "((25))"]:
+      discard recursive.parse(test)
+
+    let repeated = +literal("spam") + ?literal("ham") + *literal("salami")
+    for test in ["ham", "spam", "spamspamspam" , "spamham", "spamsalami", "spamsalamisalami"]:
+      discard  repeated.parse(test)
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
new file mode 100644
index 000000000..d8fc6e5d3
--- /dev/null
+++ b/tests/generics/tprevent_double_bind.nim
@@ -0,0 +1,21 @@
+discard """
+  errormsg: "type mismatch: got <TT[seq[string]], proc (v: int){.gcsafe.}>"
+  line: 20
+"""
+
+# bug #6732
+import typetraits
+
+type
+  TT[T] = ref object of RootObj
+    val: T
+  CB[T] = proc (v: T)
+
+proc testGeneric[T](val: TT[T], cb: CB[T]) =
+  echo val.type.name
+  echo $val.val
+
+var tt = new(TT[seq[string]])
+echo tt.type.name
+tt.testGeneric( proc (v: int) =
+    echo $v )
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
new file mode 100644
index 000000000..801f0e444
--- /dev/null
+++ b/tests/generics/treentranttypes.nim
@@ -0,0 +1,114 @@
+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]
+@[1, 2]@[3, 4]
+'''
+"""
+
+# https://github.com/nim-lang/Nim/issues/5962
+
+type
+  ArrayLike[A, B] = (A, B)
+  VectorLike*[SIZE, T] = ArrayLike[SIZE, T]
+  MatrixLike*[M, N, T] = VectorLike[M, VectorLike[N, T]]
+
+proc tupleTest =
+  let m: MatrixLike[int, string, float] = (10, ("test", 1.2))
+  echo m
+
+tupleTest()
+
+type
+  Vector*[K: static[int], T] =
+    array[K, T]
+
+  Matrix*[M: static[int]; N: static[int]; T] =
+    Vector[M, Vector[N, T]]
+
+proc arrayTest =
+  # every kind of square matrix works just fine
+  let mat_good: Matrix[3, 3, float] = [[0.0, 2.0, 3.0],
+                                       [2.0, 0.0, 5.0],
+                                       [2.0, 0.0, 5.0]]
+  echo "3x3 Matrix ", repr(mat_good)
+
+  # this does not work with explicit type signature (the matrix seems to always think it is NxN instead)
+  let mat_fail: Matrix[2, 3, float] = [[0.0, 2.0, 3.0],
+                                       [2.0, 0.0, 5.0]]
+  echo "2x3 Matrix ", repr(mat_fail)
+
+  # this literal seems to work just fine
+  let mat_also_good = [[0.0, 2.0, 3.0],
+                       [2.0, 0.0, 5.0]]
+
+  echo "2x3 Literal ", repr(mat_also_good)
+
+  # but making a named type out of this leads to pretty nasty runtime behavior
+  var mat_fail_runtime: Matrix[2, 3, float]
+  echo "2x3 Matrix ", repr(mat_fail_runtime)
+
+  # cutting out the matrix type middle man seems to solve our problem
+  var mat_ok_runtime: array[2, array[3, float]]
+  echo "2x2 ArrayArray", repr(mat_ok_runtime)
+
+  # this is fine too
+  var mat_ok_runtime_2: array[2, Vector[3, float]]
+  echo "2x3 ArrayVector", repr(mat_ok_runtime_2)
+
+  # here we are in trouble again
+  var mat_fail_runtime_2: Vector[2, Vector[3, float]]
+  echo "2x3 VectorVector ", repr(mat_fail_runtime_2)
+
+  # and here we are fine again
+  var mat_ok_runtime_3: Vector[2, array[3, float]]
+  echo "2x3 VectorArray ", repr(mat_ok_runtime_3)
+
+arrayTest()
+
+# https://github.com/nim-lang/Nim/issues/5756
+
+type
+  Vec*[N : static[int]] = object
+    arr*: array[N, int32]
+
+  Mat*[M,N: static[int]] = object
+    arr*: array[M, Vec[N]]
+
+proc vec2*(x,y:int32) : Vec[2] =
+  result.arr = [x,y]
+
+proc mat2*(a,b: Vec[2]): Mat[2,2] =
+  result.arr = [a,b]
+
+const a = vec2(1,2)
+echo @(a.arr)
+let x = a
+echo @(x.arr)
+
+const b = mat2(vec2(1, 2), vec2(3, 4))
+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/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/tsubclassgenericerror.nim b/tests/generics/tsubclassgenericerror.nim
new file mode 100644
index 000000000..87f8a8e64
--- /dev/null
+++ b/tests/generics/tsubclassgenericerror.nim
@@ -0,0 +1,11 @@
+discard """
+  errormsg: "cannot instantiate 'GenericParentType[T]' inside of type definition: 'GenericChildType'; Maybe generic arguments are missing?"
+  line: 8
+"""
+
+type
+  GenericParentType[T] = ref object of RootObj
+  GenericChildType[T] = ref object of GenericParentType # missing the [T]
+    val: T
+
+var instance : GenericChildType[int] = nil
diff --git a/tests/generics/tthread_generic.nim b/tests/generics/tthread_generic.nim
new file mode 100644
index 000000000..300da56a6
--- /dev/null
+++ b/tests/generics/tthread_generic.nim
@@ -0,0 +1,39 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  action: compile
+"""
+
+type
+  ThreadFuncArgs[T] = object of RootObj
+    a: proc(): T {.thread.}
+    b: proc(val: T) {.thread.}
+
+proc handleThreadFunc(arg: ThreadFuncArgs[int]){.thread.} =
+  var fn = arg.a
+  var callback = arg.b
+  var output = fn()
+  callback(output)
+
+proc `@||->`*[T](fn: proc(): T {.thread.},
+                 callback: proc(val: T){.thread.}): Thread[ThreadFuncArgs[T]] =
+  var thr: Thread[ThreadFuncArgs[T]]
+  var args: ThreadFuncArgs[T]
+  args.a = fn
+  args.b = callback
+  createThread(thr, handleThreadFunc, args)
+  return thr
+
+proc `||->`*[T](fn: proc(): T{.thread.}, callback: proc(val: T){.thread.}) =
+  discard fn @||-> callback
+
+when true:
+  import os
+  proc testFunc(): int {.thread.} =
+    return 1
+  proc callbackFunc(val: int) {.thread.} =
+    echo($(val))
+
+  var thr = (testFunc @||-> callbackFunc)
+  echo("test")
+  joinThread(thr)
+  os.sleep(3000)
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/tunique_type.nim b/tests/generics/tunique_type.nim
new file mode 100644
index 000000000..1150dea49
--- /dev/null
+++ b/tests/generics/tunique_type.nim
@@ -0,0 +1,67 @@
+# Bug #2022
+
+discard """
+  output: '''@[97, 45]
+@[true, false]
+@[false, false]'''
+"""
+
+## The goal of this snippet is to provide and test a construct for general-
+## purpose, random-access mapping. I use an AST-manipulation-based approach
+## because it's more efficient than using procedure pointers and less
+## verbose than defining a new callable type for every invocation of `map`.
+
+import sugar
+import macros
+import strutils
+
+#===============================================================================
+# Define a system for storing copies of ASTs as static strings.
+# This serves the same purpose as D's `alias` parameters for types, used heavily
+# in its popular `ranges` and `algorithm` modules.
+
+var exprNodes {.compileTime.} = newSeq[NimNode]()
+
+proc refExpr(exprNode: NimNode): string {.compileTime.} =
+  exprNodes.add exprNode.copy
+  "expr" & $(exprNodes.len - 1)
+
+proc derefExpr(exprRef: string): NimNode {.compileTime.} =
+  exprNodes[parseInt(exprRef[4 .. ^1])]
+
+#===============================================================================
+# Define a type that allows a callable expression to be mapped onto elements
+# of an indexable collection.
+
+type Mapped[Input; predicate: static[string]] = object
+  input: Input
+
+macro map(input, predicate: untyped): untyped =
+  let predicate = callsite()[2]
+  newNimNode(nnkObjConstr).add(
+    newNimNode(nnkBracketExpr).add(
+      ident"Mapped",
+      newNimNode(nnkTypeOfExpr).add(input),
+      newLit(refExpr(predicate))),
+    newNimNode(nnkExprColonExpr).add(
+      ident"input", input))
+
+proc `[]`(m: Mapped, i: int): auto =
+  macro buildResult: untyped =
+    newCall(
+      derefExpr(m.predicate),
+      newNimNode(nnkBracketExpr).add(
+        newDotExpr(ident"m", ident"input"),
+        ident"i"))
+  buildResult()
+
+#===============================================================================
+# Test out our generic mapping construct.
+
+let a = "a-string".map(ord)
+let b = @["a", "seq"].map((e: string) => e == "a")
+let c = "another-string".map((e: char) => e == 'o')
+
+echo(@[a[0], a[1]]) # @[97, 45]
+echo(@[b[0], b[1]]) # @[true, false]
+echo(@[c[0], c[1]]) # @[false, false]
diff --git a/tests/generics/tvarseq_caching.nim b/tests/generics/tvarseq_caching.nim
new file mode 100644
index 000000000..f617b9335
--- /dev/null
+++ b/tests/generics/tvarseq_caching.nim
@@ -0,0 +1,48 @@
+discard """
+  output: '''@[1, 2, 3]
+@[4.0, 5.0, 6.0]
+@[1, 2, 3]
+@[4.0, 5.0, 6.0]
+@[1, 2, 3]
+@[4, 5, 6]'''
+"""
+
+# bug #3476
+
+proc foo[T]: var seq[T] =
+  ## Problem! Bug with generics makes every call to this proc generate
+  ## a new seq[T] instead of retrieving the `items {.global.}` variable.
+  var items {.global.}: seq[T]
+  return items
+
+proc foo2[T]: ptr seq[T] =
+  ## Workaround! By returning by `ptr` instead of `var` we can get access to
+  ## the `items` variable, but that means we have to explicitly deref at callsite.
+  var items {.global.}: seq[T]
+  return addr items
+
+proc bar[T]: var seq[int] =
+  ## Proof. This proc correctly retrieves the `items` variable. Notice the only thing
+  ## that's changed from `foo` is that it returns `seq[int]` instead of `seq[T]`.
+  var items {.global.}: seq[int]
+  return items
+
+
+foo[int]() = @[1, 2, 3]
+foo[float]() = @[4.0, 5.0, 6.0]
+
+foo2[int]()[] = @[1, 2, 3]
+foo2[float]()[] = @[4.0, 5.0, 6.0]
+
+bar[int]() = @[1, 2, 3]
+bar[float]() = @[4, 5, 6]
+
+
+echo foo[int]()      # prints 'nil' - BUG!
+echo foo[float]()    # prints 'nil' - BUG!
+
+echo foo2[int]()[]   # prints '@[1, 2, 3]'
+echo foo2[float]()[] # prints '@[4.0, 5.0, 6.0]'
+
+echo bar[int]()      # prints '@[1, 2, 3]'
+echo bar[float]()    # prints '@[4, 5, 6]'
diff --git a/tests/generics/twrong_field_caching.nim b/tests/generics/twrong_field_caching.nim
new file mode 100644
index 000000000..667ffbbe5
--- /dev/null
+++ b/tests/generics/twrong_field_caching.nim
@@ -0,0 +1,68 @@
+discard """
+  output: '''a23: 2x3
+a32: 3x2
+transpose A
+t32: 3x2
+transpose B
+x23: 2x3 (2x3)
+x32: 3x2 (3x2)'''
+"""
+
+# bug #2125
+# Suppose we have the following type for a rectangular array:
+
+type
+  RectArray*[R, C: static[int], T] = distinct array[R * C, T]
+
+var a23: RectArray[2, 3, int]
+var a32: RectArray[3, 2, int]
+
+echo "a23: ", a23.R, "x", a23.C
+echo "a32: ", a32.R, "x", a32.C
+
+# Output:
+# a23: 2x3
+# a32: 3x2
+
+# Looking good. Let's add a proc:
+proc transpose*[R, C, T](m: RectArray[R, C, T]): RectArray[C, R, T] =
+  echo "transpose A"
+
+var t32 = a23.transpose
+
+echo "t32: ", t32.R, "x", t32.C
+
+# Output:
+# t32: 3x2
+
+
+# Everything is still OK. Now let's use the rectangular array inside another
+# generic type:
+type
+  Matrix*[R, C: static[int], T] = object
+    theArray*: RectArray[R, C, T]
+
+#var m23: Matrix[2, 3, int]
+#var m32: Matrix[3, 2, int]
+
+#echo "m23: ", m23.R, "x", m23.C, " (", m23.theArray.R, "x", m23.theArray.C, ")"
+#echo "m32: ", m32.R, "x", m32.C, " (", m32.theArray.R, "x", m32.theArray.C, ")"
+
+# Output:
+# m23: 2x3 (2x3)
+# m32: 3x2 (3x2)
+
+
+# Everything is still as expected. Now let's add the following proc:
+proc transpose*[R, C, T](m: Matrix[R, C, T]): Matrix[C, R, T] =
+  echo "transpose B"
+
+var x23: Matrix[2, 3, int]
+var x32 = x23.transpose
+
+echo "x23: ", x23.R, "x", x23.C, " (", x23.theArray.R, "x", x23.theArray.C, ")"
+echo "x32: ", x32.R, "x", x32.C, " (", x32.theArray.R, "x", x32.theArray.C, ")"
+
+# Output:
+# x23: 2x3 (2x3)
+# x32: 3x2 (3x2)  <--- this is incorrect. R and C do not match!
diff --git a/tests/generics/twrong_floatlit_type.nim b/tests/generics/twrong_floatlit_type.nim
new file mode 100644
index 000000000..04bacc0d9
--- /dev/null
+++ b/tests/generics/twrong_floatlit_type.nim
@@ -0,0 +1,118 @@
+discard """
+  errormsg: "type mismatch"
+  line: 116
+"""
+
+# bug #2169
+import strutils, math
+
+type
+  Point2D*[S] = object
+    x*, y*: S
+  Matrix2x3*[S] = distinct array[6, S] ## Row major order
+
+  Vector2D*[S] = object
+    x*, y*: S
+
+proc `[]`*[T](m: Matrix2x3[T], i: int): T = array[6, T](m)[i]
+
+template M11*[T](m: Matrix2x3[T]): T = m[0]
+template M12*[T](m: Matrix2x3[T]): T = m[1]
+template M13*[T](m: Matrix2x3[T]): T = m[2]
+template M21*[T](m: Matrix2x3[T]): T = m[3]
+template M22*[T](m: Matrix2x3[T]): T = m[4]
+template M23*[T](m: Matrix2x3[T]): T = m[5]
+
+proc identity*[T](): Matrix2x3[T] =
+    Matrix2x3[T]([T(1.0), 0.0, 0.0,   0.0, 1.0, 0.0])
+
+proc translation*[T](p: Point2D[T]): Matrix2x3[T] =
+    Matrix2x3[T]([T(1.0), T(0.0), p.x, T(0.0), T(1.0), p.y])
+
+proc translation*[T](p: Vector2D[T]): Matrix2x3[T] =
+    Matrix2x3[T]([T(1.0), T(0.0), p.x, T(0.0), T(1.0), p.y])
+
+proc scale*[T](v: Vector2D[T]): Matrix2x3[T] =
+    Matrix2x3[T]([v.x, T(0.0), T(0.0), T(0.0), v.y, T(0.0)])
+
+proc rotation*[T](th: T): Matrix2x3[T] =
+    let
+        c = T(cos(th.float))
+        s = T(sin(th.float))
+
+    Matrix2x3[T]([c, -s, T(0.0),   s, c, T(0.0)])
+
+proc `*`*[T](a, b: Matrix2x3[T]): Matrix2x3[T] =
+    # Here we pretend that row 3 is [0,0,0,1] without
+    # actually storing it in the matrix.
+    Matrix2x3[T]([a.M11*b.M11 + a.M12*b.M21,
+                  a.M11*b.M12 + a.M12*b.M22,
+                  a.M11*b.M13 + a.M12*b.M23 + a.M13,
+
+                  a.M21*b.M11 + a.M22*b.M21,
+                  a.M21*b.M12 + a.M22*b.M22,
+                  a.M21*b.M13 + a.M22*b.M23 + a.M23])
+
+proc `*`*[T](a: Matrix2x3[T], p: Point2D[T]): Point2D[T] =
+    let
+        x = a.M11*p.x + a.M12*p.y + a.M13
+        y = a.M21*p.x + a.M22*p.y + a.M23
+
+    Point2D[T](x: x, y: y)
+
+# making these so things like "line" that need a constructor don't stick out.
+# 2x2 determinant:  |a b|
+#                   |c d|  = ad - bc
+
+# String rendering
+#
+template ff[S](x: S): string =
+    formatFloat(float(x), ffDefault, 0)
+
+proc `$`*[S](p: Point2D[S]): string =
+    "P($1, $2)" % [ff(p.x), ff(p.y)]
+
+proc `$`*[S](p: Vector2D[S]): string =
+    "V($1, $2)" % [ff(p.x), ff(p.y)]
+
+proc `$`*[S](m: Matrix2x3[S]): string =
+    "M($1 $2 $3/$4 $5 $6)" % [ff(m.M11), ff(m.M12), ff(m.M13),
+                              ff(m.M21), ff(m.M22), ff(m.M23)]
+
+#
+# Vector operators.
+proc `-`*[S](a: Vector2D[S]): Vector2D[S] =
+  Vector2D[S](x: -a.x, y: -a.y)
+
+proc `+`*[S](a, b: Vector2D[S]): Vector2D[S] =
+  Vector2D[S](x: a.x + b.x, y: a.y + b.y)
+
+proc `-`*[S](a, b: Vector2D[S]): Vector2D[S] =
+  Vector2D[S](x: a.x - b.x, y: a.y - b.y)
+
+proc `*`*[S](v: Vector2D[S], sc: S): Vector2D[S] =
+  Vector2D[S](x: v.x*sc, y: v.y*sc)
+
+proc `*`*[S](sc: S, v: Vector2D[S]): Vector2D[S] =
+  Vector2D[S](x: v.x*sc, y: v.y*sc)
+
+proc `/`*[S](v: Vector2D[S], sc: S): Vector2D[S] =
+  Vector2D[S](x: v.x/sc, y: v.y/sc)
+
+proc `/`*[S](sc: S; v: Vector2D[S]): Vector2D[S] =
+  Vector2D[S](x: sc/v.x, y: sc/v.y)
+
+proc `/`*[S](a, b: Vector2D[S]): Vector2D[S] =
+  Vector2D[S](x: a.x/b.x, y: a.y/b.y)
+#proc vec[S](x, y: S): Vector2D[S]
+proc vec[S](x, y: S): Vector2D[S] =
+  Vector2D[S](x: x, y: y)
+
+if true:
+  # Comment out this let, and the program will fail to
+  # compile with a type mismatch, as expected.
+
+  let s3 = scale(vec(4.0, 4.0))
+  let barf = translation(Point2D[float32](x: 1, y: 1)) * rotation(float(0.7))
+
+  echo "Badness ", barf
diff --git a/tests/generics/twrong_generic_object.nim b/tests/generics/twrong_generic_object.nim
new file mode 100644
index 000000000..4951f735f
--- /dev/null
+++ b/tests/generics/twrong_generic_object.nim
@@ -0,0 +1,21 @@
+discard """
+  errormsg: "'Node' is not a concrete type"
+  line: 11
+"""
+# bug #2509
+type
+  GenericNodeObj[T] = ref object
+    obj: T
+
+  Node* = ref object
+    children*: seq[Node]
+    parent*: Node
+
+    nodeObj*: GenericNodeObj # [int]
+
+proc newNode*(nodeObj: GenericNodeObj): Node =
+  result = Node(nodeObj: nodeObj)
+  newSeq(result.children, 10)
+
+var genericObj = GenericNodeObj[int]()
+var myNode = newNode(genericObj)