summary refs log tree commit diff stats
path: root/tests/concepts
diff options
context:
space:
mode:
Diffstat (limited to 'tests/concepts')
-rw-r--r--tests/concepts/libs/trie.nim26
-rw-r--r--tests/concepts/libs/trie_database.nim12
-rw-r--r--tests/concepts/matrix.nim14
-rw-r--r--tests/concepts/matrixalgo.nim28
-rw-r--r--tests/concepts/mvarconcept.nim13
-rw-r--r--tests/concepts/t18409.nim37
-rw-r--r--tests/concepts/t19730.nim20
-rw-r--r--tests/concepts/t20237.nim3
-rw-r--r--tests/concepts/t3330.nim70
-rw-r--r--tests/concepts/t4982.nim18
-rw-r--r--tests/concepts/t5888lib/ca.nim4
-rw-r--r--tests/concepts/t5888lib/opt.nim6
-rw-r--r--tests/concepts/t8012.nim15
-rw-r--r--tests/concepts/t8558.nim26
-rw-r--r--tests/concepts/t976.nim57
-rw-r--r--tests/concepts/tcomparable.nim13
-rw-r--r--tests/concepts/tconceptinclosure.nim53
-rw-r--r--tests/concepts/tconcepts.nim451
-rw-r--r--tests/concepts/tconcepts_issues.nim556
-rw-r--r--tests/concepts/tconcepts_overload_precedence.nim69
-rw-r--r--tests/concepts/templatesinconcepts.nim56
-rw-r--r--tests/concepts/texplain.nim182
-rw-r--r--tests/concepts/titerable.nim20
-rw-r--r--tests/concepts/tmanual.nim42
-rw-r--r--tests/concepts/tmapconcept.nim102
-rw-r--r--tests/concepts/tmatrixconcept.nim81
-rw-r--r--tests/concepts/tmatrixlib.nim31
-rw-r--r--tests/concepts/tmodifiersinplace.nim30
-rw-r--r--tests/concepts/tmonoid.nim25
-rw-r--r--tests/concepts/trandomvars.nim63
-rw-r--r--tests/concepts/trandomvars2.nim42
-rw-r--r--tests/concepts/treversable.nim31
-rw-r--r--tests/concepts/tseqofconcept.nim19
-rw-r--r--tests/concepts/tspec.nim105
-rw-r--r--tests/concepts/tstackconcept.nim62
-rw-r--r--tests/concepts/tswizzle.nim80
-rw-r--r--tests/concepts/ttrieconcept.nim7
-rw-r--r--tests/concepts/tusertypeclasses.nim131
-rw-r--r--tests/concepts/tusertypeclasses2.nim63
-rw-r--r--tests/concepts/tvarconcept.nim9
-rw-r--r--tests/concepts/tvectorspace.nim19
-rw-r--r--tests/concepts/twrapconcept.nim21
42 files changed, 2712 insertions, 0 deletions
diff --git a/tests/concepts/libs/trie.nim b/tests/concepts/libs/trie.nim
new file mode 100644
index 000000000..bdd318492
--- /dev/null
+++ b/tests/concepts/libs/trie.nim
@@ -0,0 +1,26 @@
+import
+  hashes, tables, trie_database
+
+type
+  MemDBTable = Table[KeccakHash, string]
+
+  MemDB* = object
+    tbl: MemDBTable
+
+proc hash*(key: KeccakHash): int =
+  hashes.hash(key.data)
+
+proc get*(db: MemDB, key: KeccakHash): string =
+  db.tbl[key]
+
+proc del*(db: var MemDB, key: KeccakHash): bool =
+  if db.tbl.hasKey(key):
+    db.tbl.del(key)
+    return true
+  else:
+    return false
+
+proc put*(db: var MemDB, key: KeccakHash, value: string): bool =
+  db.tbl[key] = value
+  return true
+
diff --git a/tests/concepts/libs/trie_database.nim b/tests/concepts/libs/trie_database.nim
new file mode 100644
index 000000000..a45c64842
--- /dev/null
+++ b/tests/concepts/libs/trie_database.nim
@@ -0,0 +1,12 @@
+type
+  KeccakHash* = object
+    data*: string
+
+  BytesRange* = object
+    bytes*: string
+
+  TrieDatabase* = concept db
+    put(var db, KeccakHash, string) is bool
+    del(var db, KeccakHash) is bool
+    get(db, KeccakHash) is string
+
diff --git a/tests/concepts/matrix.nim b/tests/concepts/matrix.nim
new file mode 100644
index 000000000..47a709f80
--- /dev/null
+++ b/tests/concepts/matrix.nim
@@ -0,0 +1,14 @@
+type
+  Matrix*[M, N: static[int]; T] = object
+    data: array[M*N, T]
+
+proc `[]`*(M: Matrix; m, n: int): M.T =
+  M.data[m * M.N + n]
+
+proc `[]=`*(M: var Matrix; m, n: int; v: M.T) =
+  M.data[m * M.N + n] = v
+
+# Adapt the Matrix type to the concept's requirements
+template Rows*(M: type Matrix): untyped = M.M
+template Cols*(M: type Matrix): untyped = M.N
+template ValueType*(M: type Matrix): typedesc = M.T
diff --git a/tests/concepts/matrixalgo.nim b/tests/concepts/matrixalgo.nim
new file mode 100644
index 000000000..98e5b8b4f
--- /dev/null
+++ b/tests/concepts/matrixalgo.nim
@@ -0,0 +1,28 @@
+import typetraits
+
+type
+  AnyMatrix*[R, C: static[int]; T] = concept m, var mvar, type M
+    M.ValueType is T
+    M.Rows == R
+    M.Cols == C
+
+    m[int, int] is T
+    mvar[int, int] = T
+
+    type TransposedType = stripGenericParams(M)[C, R, T]
+
+  AnySquareMatrix*[N: static[int], T] = AnyMatrix[N, N, T]
+
+  AnyTransform3D* = AnyMatrix[4, 4, float]
+
+proc transposed*(m: AnyMatrix): m.TransposedType =
+  for r in 0 ..< m.R:
+    for c in 0 ..< m.C:
+      result[r, c] = m[c, r]
+
+proc determinant*(m: AnySquareMatrix): int =
+  return 0
+
+proc setPerspectiveProjection*(m: AnyTransform3D) =
+  discard
+
diff --git a/tests/concepts/mvarconcept.nim b/tests/concepts/mvarconcept.nim
new file mode 100644
index 000000000..0f9d0beff
--- /dev/null
+++ b/tests/concepts/mvarconcept.nim
@@ -0,0 +1,13 @@
+type RNG* = concept var rng
+  rng.randomUint32() is uint32
+
+type MersenneTwister* = object
+
+proc randomUint32*(self: var MersenneTwister): uint32 = 5
+
+proc randomInt*(rng: var RNG; max: Positive): Natural = 5
+
+var mersenneTwisterInst = MersenneTwister()
+
+proc randomInt*(max: Positive): Natural =
+  mersenneTwisterInst.randomInt(max)
diff --git a/tests/concepts/t18409.nim b/tests/concepts/t18409.nim
new file mode 100644
index 000000000..0edba2d31
--- /dev/null
+++ b/tests/concepts/t18409.nim
@@ -0,0 +1,37 @@
+discard """
+  action: "compile"
+"""
+
+# A vector space over a field F concept.
+type VectorSpace*[F] = concept x, y, type V
+  vector_add(x, y) is V
+  scalar_mul(x, F) is V
+  dimension(V) is Natural
+
+# Real numbers (here floats) form a vector space.
+func vector_add*(v: float, w: float): float =  v + w
+func scalar_mul*(v: float, s: float): float =   v * s
+func dimension*(x: typedesc[float]): Natural = 1
+
+# 2-tuples of real numbers form a vector space.
+func vector_add*(v, w: (float, float)): (float, float) =
+  (vector_add(v[0], w[0]), vector_add(v[1], w[1]))
+
+func scalar_mul*(v: (float, float), s: float): (float, float) =
+  (scalar_mul(v[0], s), scalar_mul(v[1], s))
+
+func dimension*(x: typedesc[(float, float)]): Natural = 2
+
+# Check concept requirements.
+assert float is VectorSpace
+assert (float, float) is VectorSpace
+
+# Commutivity axiom for vector spaces over the same field.
+func axiom_commutivity*[F](u, v: VectorSpace[F]): bool =
+  vector_add(u, v) == vector_add(v, u)
+
+# This is okay.
+assert axiom_commutivity(2.2, 3.3)
+
+# This is not.
+assert axiom_commutivity((2.2, 3.3), (4.4, 5.5))
diff --git a/tests/concepts/t19730.nim b/tests/concepts/t19730.nim
new file mode 100644
index 000000000..575d45dda
--- /dev/null
+++ b/tests/concepts/t19730.nim
@@ -0,0 +1,20 @@
+discard """
+  output: '''1.01.01.01.0
+1.01.01.01.0
+'''
+"""
+
+type
+  Color = concept c
+    c.r is SomeFloat
+    c.g is SomeFloat
+    c.b is SomeFloat
+    c.a is SomeFloat
+
+proc useColor(color: Color) =
+  echo(color.r, color.g, color.b, color.a)
+
+let color = (r: 1.0, g: 1.0, b: 1.0, a: 1.0)
+useColor(color)
+
+useColor((r: 1.0, g: 1.0, b: 1.0, a: 1.0))
diff --git a/tests/concepts/t20237.nim b/tests/concepts/t20237.nim
new file mode 100644
index 000000000..175c7a9d1
--- /dev/null
+++ b/tests/concepts/t20237.nim
@@ -0,0 +1,3 @@
+type Foo* = concept
+  ## doc comment
+  proc foo(x: Self)
diff --git a/tests/concepts/t3330.nim b/tests/concepts/t3330.nim
new file mode 100644
index 000000000..901f8d2f4
--- /dev/null
+++ b/tests/concepts/t3330.nim
@@ -0,0 +1,70 @@
+discard """
+matrix: "--mm:refc"
+errormsg: "type mismatch: got <Bar[system.int]>"
+nimout: '''
+t3330.nim(70, 4) Error: type mismatch: got <Bar[system.int]>
+but expected one of:
+proc test(foo: Foo[int])
+  first type mismatch at position: 1
+  required type for foo: Foo[int]
+  but expression 'bar' is of type: Bar[system.int]
+t3330.nim(55, 8) Hint: Non-matching candidates for add(k, string, T)
+proc add(x: var string; y: char)
+  first type mismatch at position: 1
+  required type for x: var string
+  but expression 'k' is of type: Alias
+proc add(x: var string; y: cstring)
+  first type mismatch at position: 1
+  required type for x: var string
+  but expression 'k' is of type: Alias
+proc add(x: var string; y: string)
+  first type mismatch at position: 1
+  required type for x: var string
+  but expression 'k' is of type: Alias
+proc add[T](x: var seq[T]; y: openArray[T])
+  first type mismatch at position: 1
+  required type for x: var seq[T]
+  but expression 'k' is of type: Alias
+proc add[T](x: var seq[T]; y: sink T)
+  first type mismatch at position: 1
+  required type for x: var seq[T]
+  but expression 'k' is of type: Alias
+
+t3330.nim(55, 8) template/generic instantiation of `add` from here
+t3330.nim(62, 6) Foo: 'bar.value' cannot be assigned to
+t3330.nim(55, 8) template/generic instantiation of `add` from here
+t3330.nim(63, 6) Foo: 'bar.x' cannot be assigned to
+
+expression: test(bar)'''
+"""
+
+
+
+
+
+
+
+
+
+
+
+
+## line 60
+type
+  Foo[T] = concept k
+    add(k, string, T)
+
+  Bar[T] = object
+    value: T
+    x: string
+
+proc add[T](bar: Bar[T], x: string, val: T) =
+  bar.value = val
+  bar.x = x
+
+proc test(foo: Foo[int]) =
+  foo.add("test", 42)
+  echo(foo.x)
+
+var bar = Bar[int]()
+bar.test()
diff --git a/tests/concepts/t4982.nim b/tests/concepts/t4982.nim
new file mode 100644
index 000000000..9d82c83c9
--- /dev/null
+++ b/tests/concepts/t4982.nim
@@ -0,0 +1,18 @@
+discard """
+errormsg: "undeclared identifier: 'x'"
+line: 10
+"""
+
+import typetraits # without this import the program compiles (and echos false)
+
+type
+  SomeTestConcept = concept t
+    x.name is string # typo: t.name was intended (which would result in echo true)
+
+type
+  TestClass = ref object of RootObj
+    name: string
+
+var test = TestClass(name: "mytest")
+echo $(test is SomeTestConcept)
+
diff --git a/tests/concepts/t5888lib/ca.nim b/tests/concepts/t5888lib/ca.nim
new file mode 100644
index 000000000..4a811f797
--- /dev/null
+++ b/tests/concepts/t5888lib/ca.nim
@@ -0,0 +1,4 @@
+type
+  CA* = concept c
+    c.x is int
+
diff --git a/tests/concepts/t5888lib/opt.nim b/tests/concepts/t5888lib/opt.nim
new file mode 100644
index 000000000..65d16addc
--- /dev/null
+++ b/tests/concepts/t5888lib/opt.nim
@@ -0,0 +1,6 @@
+import ca
+
+type
+  Opt* = object
+    x*: int
+
diff --git a/tests/concepts/t8012.nim b/tests/concepts/t8012.nim
new file mode 100644
index 000000000..ec2aa6e5c
--- /dev/null
+++ b/tests/concepts/t8012.nim
@@ -0,0 +1,15 @@
+type
+  MyTypeCon = concept c
+    c.counter is int
+  MyType = object
+    counter: int
+
+proc foo(conc: var MyTypeCon) =
+  conc.counter.inc
+  if conc.counter < 5:
+    foo(conc)
+
+var x: MyType
+
+x.foo
+discard x.repr
diff --git a/tests/concepts/t8558.nim b/tests/concepts/t8558.nim
new file mode 100644
index 000000000..acb2de30e
--- /dev/null
+++ b/tests/concepts/t8558.nim
@@ -0,0 +1,26 @@
+discard """
+  output: '''10
+9
+8
+7
+6
+5
+4
+3
+2
+1
+go!
+'''
+"""
+
+type Integral = concept x
+  x == 0 is bool
+  x - 1 is type(x)
+
+proc countToZero(n: Integral) =
+  if n == 0: echo "go!"
+  else:
+    echo n
+    countToZero(n-1)
+
+countToZero(10)
\ No newline at end of file
diff --git a/tests/concepts/t976.nim b/tests/concepts/t976.nim
new file mode 100644
index 000000000..776d53827
--- /dev/null
+++ b/tests/concepts/t976.nim
@@ -0,0 +1,57 @@
+discard """
+  output: '''
+Printable
+'''
+joinable: false
+"""
+
+#[
+  The converter is a proper example of a confounding variable
+  Moved to an isolated file
+]#
+
+type
+  Obj1[T] = object
+      v: T
+converter toObj1[T](t: T): Obj1[T] =
+  return Obj1[T](v: t)
+block t976:
+  type
+    int1 = distinct int
+    int2 = distinct int
+    int1g = concept x
+      x is int1
+    int2g = concept x
+      x is int2
+
+  proc take[T: int1g](value: int1) =
+    when T is int2:
+      static: error("killed in take(int1)")
+
+  proc take[T: int2g](vale: int2) =
+    when T is int1:
+      static: error("killed in take(int2)")
+
+  var i1: int1 = 1.int1
+  var i2: int2 = 2.int2
+
+  take[int1](i1)
+  take[int2](i2)
+
+  template reject(e) =
+    static: assert(not compiles(e))
+
+  reject take[string](i2)
+  reject take[int1](i2)
+
+  # bug #6249
+  type
+      Obj2 = ref object
+      PrintAble = concept x
+          $x is string
+
+  proc `$`[T](nt: Obj1[T]): string =
+      when T is PrintAble: result = "Printable"
+      else: result = "Non Printable"
+
+  echo Obj2()
\ No newline at end of file
diff --git a/tests/concepts/tcomparable.nim b/tests/concepts/tcomparable.nim
new file mode 100644
index 000000000..06612a47e
--- /dev/null
+++ b/tests/concepts/tcomparable.nim
@@ -0,0 +1,13 @@
+type
+  Comparable = concept a
+    (a < a) is bool
+
+proc myMax(a, b: Comparable): Comparable =
+  if a < b:
+    return b
+  else:
+    return a
+
+doAssert myMax(5, 10) == 10
+doAssert myMax(31.3, 1.23124) == 31.3
+
diff --git a/tests/concepts/tconceptinclosure.nim b/tests/concepts/tconceptinclosure.nim
new file mode 100644
index 000000000..23c1bf293
--- /dev/null
+++ b/tests/concepts/tconceptinclosure.nim
@@ -0,0 +1,53 @@
+discard """
+  output: '''
+10
+20
+int
+20
+3
+'''
+"""
+
+import typetraits
+
+type
+  FonConcept = concept x
+    x.x is int
+
+  GenericConcept[T] = concept x
+    x.x is T
+    const L = T.name.len
+
+  Implementation = object
+    x: int
+
+  Closure = object
+    f: proc()
+
+proc f1(x: FonConcept): Closure =
+  result.f = proc () =
+    echo x.x
+
+proc f2(x: GenericConcept): Closure =
+  result.f = proc () =
+    echo x.x
+    echo GenericConcept.T.name
+
+proc f3[T](x: GenericConcept[T]): Closure =
+  result.f = proc () =
+    echo x.x
+    echo x.L
+
+let x = Implementation(x: 10)
+let y = Implementation(x: 20)
+
+let a = x.f1
+let b = x.f2
+let c = x.f1
+let d = y.f2
+let e = y.f3
+
+a.f()
+d.f()
+e.f()
+
diff --git a/tests/concepts/tconcepts.nim b/tests/concepts/tconcepts.nim
new file mode 100644
index 000000000..ea3ddc401
--- /dev/null
+++ b/tests/concepts/tconcepts.nim
@@ -0,0 +1,451 @@
+discard """
+output: '''
+10
+20
+int
+20
+3
+x as ParameterizedType[T]
+x as ParameterizedType[T]
+x as ParameterizedType[T]
+x as ParameterizedType
+x as ParameterizedType
+x as CustomTypeClass
+1
+2
+3
+4
+5
+6
+a
+b
+t
+e
+s
+t
+z
+e
+1
+2
+3
+20
+10
+5
+9
+'''
+"""
+
+
+import typetraits, strutils
+
+
+block tcomparable:
+  type
+    Comparable = concept a
+      (a < a) is bool
+
+  proc myMax(a, b: Comparable): Comparable =
+    if a < b:
+      return b
+    else:
+      return a
+
+  doAssert myMax(5, 10) == 10
+  doAssert myMax(31.3, 1.23124) == 31.3
+
+
+
+block tconceptinclosure:
+  type
+    FonConcept = concept x
+      x.x is int
+    GenericConcept[T] = concept x
+      x.x is T
+      const L = T.name.len
+    Implementation = object
+      x: int
+    Closure = object
+      f: proc()
+
+  proc f1(x: FonConcept): Closure =
+    result.f = proc () =
+      echo x.x
+
+  proc f2(x: GenericConcept): Closure =
+    result.f = proc () =
+      echo x.x
+      echo GenericConcept.T.name
+
+  proc f3[T](x: GenericConcept[T]): Closure =
+    result.f = proc () =
+      echo x.x
+      echo x.L
+
+  let x = Implementation(x: 10)
+  let y = Implementation(x: 20)
+
+  let a = x.f1
+  let b = x.f2
+  let c = x.f1
+  let d = y.f2
+  let e = y.f3
+
+  a.f()
+  d.f()
+  e.f()
+
+
+
+block overload_precedence:
+  type ParameterizedType[T] = object
+
+  type CustomTypeClass = concept c
+    true
+
+  # 3 competing procs
+  proc a[T](x: ParameterizedType[T]) =
+    echo "x as ParameterizedType[T]"
+
+  proc a(x: ParameterizedType) =
+    echo "x as ParameterizedType"
+
+  proc a(x: CustomTypeClass) =
+    echo "x as CustomTypeClass"
+
+  # the same procs in different order
+  proc b(x: ParameterizedType) =
+    echo "x as ParameterizedType"
+
+  proc b(x: CustomTypeClass) =
+    echo "x as CustomTypeClass"
+
+  proc b[T](x: ParameterizedType[T]) =
+    echo "x as ParameterizedType[T]"
+
+  # and yet another order
+  proc c(x: CustomTypeClass) =
+    echo "x as CustomTypeClass"
+
+  proc c(x: ParameterizedType) =
+    echo "x as ParameterizedType"
+
+  proc c[T](x: ParameterizedType[T]) =
+    echo "x as ParameterizedType[T]"
+
+  # remove the most specific one
+  proc d(x: ParameterizedType) =
+    echo "x as ParameterizedType"
+
+  proc d(x: CustomTypeClass) =
+    echo "x as CustomTypeClass"
+
+  # then shuffle the order again
+  proc e(x: CustomTypeClass) =
+    echo "x as CustomTypeClass"
+
+  proc e(x: ParameterizedType) =
+    echo "x as ParameterizedType"
+
+  # the least specific one is a match
+  proc f(x: CustomTypeClass) =
+    echo "x as CustomTypeClass"
+
+  a(ParameterizedType[int]())
+  b(ParameterizedType[int]())
+  c(ParameterizedType[int]())
+  d(ParameterizedType[int]())
+  e(ParameterizedType[int]())
+  f(ParameterizedType[int]())
+
+
+
+block templates:
+  template typeLen(x): int = x.type.name.len
+
+  template bunchOfChecks(x) =
+    x.typeLen > 3
+    x != 10 is bool
+
+  template stmtListExprTmpl(x: untyped): untyped =
+    x is int
+    x
+
+  type
+    Obj = object
+      x: int
+
+    Gen[T] = object
+      x: T
+
+    Eq = concept x, y
+      (x == y) is bool
+
+    NotEq = concept x, y
+      (x != y) is bool
+
+    ConceptUsingTemplate1 = concept x
+      echo x
+      sizeof(x) is int
+      bunchOfChecks x
+
+    ConceptUsingTemplate2 = concept x
+      stmtListExprTmpl x
+
+  template ok(x) =
+    static: assert(x)
+
+  template no(x) =
+    static: assert(not(x))
+
+  ok int is Eq
+  ok int is NotEq
+  ok string is Eq
+  ok string is NotEq
+  ok Obj is Eq
+  ok Obj is NotEq
+  ok Gen[string] is Eq
+  ok Gen[int] is NotEq
+
+  no int is ConceptUsingTemplate1
+  ok float is ConceptUsingTemplate1
+  no string is ConceptUsingTemplate1
+
+  ok int is ConceptUsingTemplate2
+  no float is ConceptUsingTemplate2
+  no string is ConceptUsingTemplate2
+
+
+
+block titerable:
+  type
+    Iterable[T] = concept x
+      for value in x:
+        type(value) is T
+
+  proc sum[T](iter: Iterable[T]): T =
+    static: echo T.name
+    for element in iter:
+      static: echo element.type.name
+      result += element
+
+  doAssert sum([1, 2, 3, 4, 5]) == 15
+
+
+
+block tmanual:
+  template accept(e) =
+    static: assert compiles(e)
+
+  template reject(e) =
+    static: assert(not compiles(e))
+
+  type
+    Container[T] = concept c
+      c.len is Ordinal
+      items(c) is T
+      for value in c:
+        type(value) is T
+
+  proc takesIntContainer(c: Container[int]) =
+    for e in c: echo e
+
+  takesIntContainer(@[1, 2, 3])
+  reject takesIntContainer(@["x", "y"])
+
+  proc takesContainer(c: Container) =
+    for e in c: echo e
+
+  takesContainer(@[4, 5, 6])
+  takesContainer(@["a", "b"])
+  takesContainer "test"
+  reject takesContainer(10)
+
+
+
+block modifiers_in_place:
+  type
+    VarContainer[T] = concept c
+      put(var c, T)
+    AltVarContainer[T] = concept var c
+      put(c, T)
+    NonVarContainer[T] = concept c
+      put(c, T)
+    GoodContainer = object
+      x: int
+    BadContainer = object
+      x: int
+
+  proc put(x: BadContainer, y: int) = discard
+  proc put(x: var GoodContainer, y: int) = discard
+
+  template ok(x) = assert(x)
+  template no(x) = assert(not(x))
+
+  static:
+    ok GoodContainer is VarContainer[int]
+    ok GoodContainer is AltVarContainer[int]
+    no BadContainer is VarContainer[int]
+    no BadContainer is AltVarContainer[int]
+    ok GoodContainer is NonVarContainer[int]
+    ok BadContainer is NonVarContainer[int]
+
+
+
+block treversable:
+  type
+    Reversable[T] = concept a
+      a[int] is T
+      a.high is int
+      a.len is int
+      a.low is int
+
+  proc get[T](s: Reversable[T], n: int): T =
+    s[n]
+
+  proc hi[T](s: Reversable[T]): int =
+    s.high
+
+  proc lo[T](s: Reversable[T]): int =
+    s.low
+
+  iterator reverse[T](s: Reversable[T]): T =
+    assert hi(s) - lo(s) == len(s) - 1
+    for z in hi(s).countdown(lo(s)):
+      yield s.get(z)
+
+  for s in @["e", "z"].reverse:
+    echo s
+
+
+
+block tmonoid:
+  type Monoid = concept x, y
+    x + y is type(x)
+    type(z(type(x))) is type(x)
+
+  proc z(x: typedesc[int]): int = 0
+
+  doAssert(int is Monoid)
+
+  # https://github.com/nim-lang/Nim/issues/8126
+  type AdditiveMonoid = concept x, y, type T
+    x + y is T
+
+    # some redundant checks to test an alternative approaches:
+    type TT = type(x)
+    x + y is type(x)
+    x + y is TT
+
+  doAssert(1 is AdditiveMonoid)
+
+
+
+block tesqofconcept:
+  type
+    MyConcept = concept x
+      someProc(x)
+    SomeSeq = seq[MyConcept]
+
+  proc someProc(x:int) = echo x
+
+  proc work (s: SomeSeq) =
+    for item in s:
+      someProc item
+
+  var s = @[1, 2, 3]
+  work s
+
+
+
+block tvectorspace:
+  type VectorSpace[K] = concept x, y
+    x + y is type(x)
+    zero(type(x)) is type(x)
+    -x is type(x)
+    x - y is type(x)
+    var k: K
+    k * x is type(x)
+
+  proc zero(T: typedesc): T = 0
+
+  static:
+    assert float is VectorSpace[float]
+    # assert float is VectorSpace[int]
+    # assert int is VectorSpace
+
+
+
+block tstack:
+  template reject(e) =
+    static: assert(not compiles(e))
+
+  type
+    ArrayStack = object
+      data: seq[int]
+
+  proc push(s: var ArrayStack, item: int) =
+    s.data.add item
+
+  proc pop(s: var ArrayStack): int =
+    return s.data.pop()
+
+  type
+    Stack[T] = concept var s
+      s.push(T)
+      s.pop() is T
+
+      type ValueType = T
+      const ValueTypeName = T.name.toUpperAscii
+
+  proc genericAlgorithm[T](s: var Stack[T], y: T) =
+    static:
+      echo "INFERRED ", T.name
+      echo "VALUE TYPE ", s.ValueType.name
+      echo "VALUE TYPE NAME ", s.ValueTypeName
+
+    s.push(y)
+    echo s.pop
+
+  proc implicitGeneric(s: var Stack): auto =
+    static:
+      echo "IMPLICIT INFERRED ", s.T.name, " ", Stack.T.name
+      echo "IMPLICIT VALUE TYPE ", s.ValueType.name, " ", Stack.ValueType.name
+      echo "IMPLICIT VALUE TYPE NAME ", s.ValueTypeName, " ", Stack.ValueTypeName
+
+    return s.pop()
+
+  var s = ArrayStack(data: @[])
+
+  s.push 10
+  s.genericAlgorithm 20
+  echo s.implicitGeneric
+
+  reject s.genericAlgorithm "x"
+  reject s.genericAlgorithm 1.0
+  reject "str".implicitGeneric
+  reject implicitGeneric(10)
+
+
+
+import libs/[trie_database, trie]
+block ttrie:
+  proc takeDb(d: TrieDatabase) = discard
+  var mdb: MemDB
+  takeDb(mdb)
+
+
+
+import mvarconcept
+block tvar:
+  # bug #2346, bug #2404
+  echo randomInt(5)
+
+block tcomment:
+  type
+    Foo = concept
+      ## Some comment
+      proc bar(x: Self)
+
+  proc bar(x: int) = echo x
+  proc foo(x: Foo) = x.bar
+  foo(9)
diff --git a/tests/concepts/tconcepts_issues.nim b/tests/concepts/tconcepts_issues.nim
new file mode 100644
index 000000000..c6d0267c5
--- /dev/null
+++ b/tests/concepts/tconcepts_issues.nim
@@ -0,0 +1,556 @@
+discard """
+  output: '''
+20.0 USD
+true
+true
+true
+true
+true
+f
+0
+10
+10
+5
+()
+false
+10
+true
+true
+true
+true
+p has been called.
+p has been called.
+implicit generic
+generic
+false
+true
+-1
+Meow
+10 0.0
+1 2.0
+'''
+joinable: false
+"""
+
+import macros, typetraits, os, posix
+
+
+block t5983:
+  const currencies = ["USD", "EUR"] # in real code 120 currencies
+
+  type USD = distinct float # in real code 120 types generates using macro
+  type EUR = distinct float
+
+  type CurrencyAmount = concept c
+    type t = c.type
+    const name = c.type.name
+    name in currencies
+
+  proc `$`(x: CurrencyAmount): string =
+    $float(x) & " " & x.name
+
+  let amount = 20.USD
+  echo amount
+
+
+block t3414:
+  type
+    View[T] = concept v
+      v.empty is bool
+      v.front is T
+      popFront v
+
+  proc find(view: View; target: View.T): View =
+    result = view
+
+    while not result.empty:
+      if view.front == target:
+        return
+
+      mixin popFront
+      popFront result
+
+  proc popFront[T](s: var seq[T]) = discard
+  proc empty[T](s: seq[T]): bool = false
+
+  var s1 = @[1, 2, 3]
+  let s2 = s1.find(10)
+
+
+block t1128:
+  type
+    TFooContainer[T] = object
+
+    TContainer[T] = concept var c
+      foo(c, T)
+
+  proc foo[T](c: var TFooContainer[T], val: T) =
+    discard
+
+  proc bar(c: var TContainer) =
+    discard
+
+  var fooContainer: TFooContainer[int]
+  echo fooContainer is TFooContainer # true.
+  echo fooContainer is TFooContainer[int] # true.
+  fooContainer.bar()
+
+
+
+block t5642:
+  type DataTable = concept x
+    x is object
+    for f in fields(x):
+      f is seq
+
+  type Students = object
+    id : seq[int]
+    name : seq[string]
+    age: seq[int]
+
+  proc nrow(dt: DataTable) : Natural =
+    var totalLen = 0
+    for f in fields(dt):
+      totalLen += f.len
+    return totalLen
+
+  let
+    stud = Students(id: @[1,2,3], name: @["Vas", "Pas", "NafNaf"], age: @[10,16,32])
+
+  doAssert nrow(stud) == 9
+
+
+
+import t5888lib/ca, t5888lib/opt
+block t5888:
+  type LocalCA = ca.CA
+
+  proc f(c: CA) =
+    echo "f"
+    echo c.x
+
+  var o = new(Opt)
+
+  echo o is CA
+  echo o is LocalCA
+  echo o is ca.CA
+
+  o.f()
+
+
+
+import json
+block t5968:
+  type
+    Enumerable[T] = concept e
+      for it in e:
+        it is T
+
+  proc cmap[T, G](e: Enumerable[T], fn: proc(t: T): G): seq[G] =
+    result = @[]
+    for it in e: result.add(fn(it))
+
+  var x = %["hello", "world"]
+
+  var z = x.cmap(proc(it: JsonNode): string = it.getStr & "!")
+  assert z == @["hello!", "world!"]
+
+
+
+import sugar
+block t6462:
+  type
+    FilterMixin[T] = ref object
+      test: (T) -> bool
+      trans: (T) -> T
+
+    SeqGen[T] = ref object
+      fil: FilterMixin[T]
+
+    WithFilter[T] = concept a
+      a.fil is FilterMixin[T]
+
+  proc test[T](a: WithFilter[T]): (T) -> bool =
+    a.fil.test
+
+  var s = SeqGen[int](fil: FilterMixin[int](test: nil, trans: nil))
+  doAssert s.test() == nil
+
+
+
+block t6770:
+  type GA = concept c
+    c.a is int
+
+  type A = object
+    a: int
+
+  type AA = object
+    case exists: bool
+    of true:
+      a: int
+    else:
+      discard
+
+  proc print(inp: GA) =
+    echo inp.a
+
+  let failing = AA(exists: true, a: 10)
+  let working = A(a:10)
+  print(working)
+  print(failing)
+
+
+
+block t7952:
+  type
+    HasLen = concept iter
+      len(iter) is int
+
+  proc echoLen(x: HasLen) =
+    echo len(x)
+
+  echoLen([1, 2, 3, 4, 5])
+
+
+
+block t8280:
+  type
+    Iterable[T] = concept x
+      for elem in x:
+        elem is T
+
+  proc max[A](iter: Iterable[A]): A =
+    discard
+
+  type
+    MyType = object
+
+  echo max(@[MyType()])
+
+
+
+import math
+block t3452:
+  type
+    Node = concept n
+      `==`(n, n) is bool
+    Graph1 = concept g
+      type N = Node
+      distance(g, N, N) is float
+    Graph2 = concept g
+      distance(g, Node, Node) is float
+    Graph3 = concept g
+      var x: Node
+      distance(g, x, x) is float
+    XY = tuple[x, y: int]
+    MyGraph = object
+      points: seq[XY]
+
+  static:
+    assert XY is Node
+
+  proc distance( g: MyGraph, a, b: XY): float =
+    sqrt( pow(float(a.x - b.x), 2) + pow(float(a.y - b.y), 2) )
+
+  static:
+    assert MyGraph is Graph1
+    assert MyGraph is Graph2
+    assert MyGraph is Graph3
+
+
+
+block t6691:
+  type
+    ConceptA = concept c
+    ConceptB = concept c
+        c.myProc(ConceptA)
+    Obj = object
+
+  proc myProc(obj: Obj, x: ConceptA) = discard
+
+  echo Obj is ConceptB
+
+
+
+block t6782:
+  type
+    Reader = concept c
+      c.read(openArray[byte], int, int) is int
+    Rdr = concept c
+      c.rd(openArray[byte], int, int) is int
+
+  type TestFile = object
+
+  proc read(r: TestFile, dest: openArray[byte], offset: int, limit: int): int =
+      result = 0
+  proc rd(r: TestFile, dest: openArray[byte], offset: int, limit: int): int =
+      result = 0
+
+  doAssert TestFile is Reader
+  doAssert TestFile is Rdr
+
+
+
+block t7114:
+  type
+    MyConcept = concept x
+      x.close() # error, doesn't work
+    MyConceptImplementer = object
+
+  proc close(self: MyConceptImplementer) = discard
+  proc takeConcept(window: MyConcept) =
+    discard
+
+  takeConcept(MyConceptImplementer())
+
+
+
+block t7510:
+  type
+    A[T] = concept a
+        a.x is T
+    B[T] = object
+        x: T
+  proc getx(v: A): v.T = v.x
+  var v = B[int32](x: 10)
+  echo v.getx
+
+
+
+block misc_issues:
+  # https://github.com/nim-lang/Nim/issues/1147
+  type TTest = object
+    vals: seq[int]
+
+  proc add(self: var TTest, val: int) =
+    self.vals.add(val)
+
+  type CAddable = concept x
+    x[].add(int)
+
+  echo((ref TTest) is CAddable) # true
+
+  # https://github.com/nim-lang/Nim/issues/1570
+  type ConcretePointOfFloat = object
+    x, y: float
+
+  type ConcretePoint[Value] = object
+    x, y: Value
+
+  type AbstractPointOfFloat = concept p
+    p.x is float and p.y is float
+
+  let p1 = ConcretePointOfFloat(x: 0, y: 0)
+  let p2 = ConcretePoint[float](x: 0, y: 0)
+
+  echo p1 is AbstractPointOfFloat      # true
+  echo p2 is AbstractPointOfFloat      # true
+  echo p2.x is float and p2.y is float # true
+
+  # https://github.com/nim-lang/Nim/issues/2018
+  type ProtocolFollower = concept c
+    true # not a particularly involved protocol
+
+  type ImplementorA = object
+  type ImplementorB = object
+
+  proc p[A: ProtocolFollower, B: ProtocolFollower](a: A, b: B) =
+    echo "p has been called."
+
+  p(ImplementorA(), ImplementorA())
+  p(ImplementorA(), ImplementorB())
+
+  # https://github.com/nim-lang/Nim/issues/2423
+  proc put[T](c: seq[T], x: T) = echo "generic"
+  proc put(c: seq) = echo "implicit generic"
+
+  type
+    Container[T] = concept c
+      put(c)
+      put(c, T)
+
+  proc c1(x: Container) = echo "implicit generic"
+  c1(@[1])
+
+  proc c2[T](x: Container[T]) = echo "generic"
+  c2(@[1])
+
+  # https://github.com/nim-lang/Nim/issues/2882
+  type
+    Paper = object
+      name: string
+
+    Bendable = concept x
+      bend(x is Bendable)
+
+  proc bend(p: Paper): Paper = Paper(name: "bent-" & p.name)
+
+  var paper = Paper(name: "red")
+  echo paper is Bendable
+
+  type
+    A = concept self
+      size(self) is int
+
+    B = object
+
+  proc size(self: B): int =
+    return -1
+
+  proc size(self: A): int =
+    return 0
+
+  let b = B()
+  echo b is A
+  echo b.size()
+
+  # https://github.com/nim-lang/Nim/issues/7125
+  type
+    Thing = concept x
+      x.hello is string
+    Cat = object
+
+  proc hello(d: Cat): string = "Meow"
+
+  proc sayHello(c: Thing) = echo(c.hello)
+
+  # used to be 'var a: Thing = Cat()' but that's not valid Nim code
+  # anyway and will be an error soon.
+  var a: Cat = Cat()
+  a.sayHello()
+
+
+# bug #16897
+
+type
+  Fp[N: static int, T] = object
+    big: array[N, T]
+
+type
+  QuadraticExt* = concept x
+    ## Quadratic Extension concept (like complex)
+    type BaseField = auto
+    x.c0 is BaseField
+    x.c1 is BaseField
+var address = pointer(nil)
+proc prod(r: var QuadraticExt, b: QuadraticExt) =
+  if address == nil:
+    address = addr b
+    prod(r, b)
+  else:
+    assert address == addr b
+
+type
+  Fp2[N: static int, T] {.byref.} = object
+    c0, c1: Fp[N, T]
+
+# This should be passed by reference,
+# but concepts do not respect the 24 bytes rule
+# or `byref` pragma.
+var r, b: Fp2[6, uint64]
+
+prod(r, b)
+
+
+block: # bug #21263
+  type
+    DateDayFraction = concept # no T, an atom
+      proc date(a: Self): int
+      proc fraction(b: Self): float
+    Date = distinct int
+    DateDayFractionImpl = object
+      date : int
+      fraction : float
+
+  proc date(a: Date): int = a.int
+  proc fraction(a:Date): float = 0.0
+
+  proc date(a: DateDayFractionImpl): int = a.date
+  proc fraction(b: DateDayFractionImpl): float = b.fraction
+
+
+  proc print(a: DateDayFraction) =
+    echo a.date, " ", a.fraction
+
+  print(10.Date) # ok
+  print(DateDayFractionImpl(date: 1, fraction: 2))  # error
+
+import sets
+import deques
+
+type AnyTree[V] = concept t, type T
+  for v in t.leaves(V):
+    v is V
+
+type BreadthOrder[V] = ref object
+  frontier: Deque[V]
+  visited: HashSet[V]
+
+proc expand[V, T](order: ref BreadthOrder[T], tree: AnyTree[V], node: V, transform: (V) -> (T)) =
+  for leaf in tree.leaves(node):
+    if not order.visited.containsOrIncl(transform(leaf)):
+      order.frontier.addLast(transform(leaf))
+
+proc hasNext[V](order: ref BreadthOrder[V]): bool =
+  order.frontier.len > 0
+
+proc init[V](_: typedesc[BreadthOrder]): ref BreadthOrder[V] =
+  result.new()
+  result[] = BreadthOrder[V](frontier: initDeque[V](), visited: initHashSet[V]())
+
+proc popNext[V](order: ref BreadthOrder[V]): V =
+  order.frontier.popFirst()
+
+type LevelNode[V] = tuple
+  depth: uint
+  node: V
+
+proc depthOf*[V](orderType: typedesc[BreadthOrder], tree: AnyTree[V], root, goal: V): uint =
+  if root == goal:
+    return 0
+  var order = init[LevelNode[V]](orderType)
+  order.expand(tree, root, (leaf) => (1.uint, leaf))
+  while order.hasNext():
+    let depthNode: LevelNode[V] = order.popNext()
+    if depthNode.node == goal:
+      return depthNode.depth
+    order.expand(tree, depthNode.node, (leaf) => (depthNode.depth + 1, leaf))
+
+type CappedStringTree = ref object
+  symbols: string
+  cap: Natural
+
+iterator leaves*(t: CappedStringTree, s: string): string =
+  if s.len < t.cap:
+    for c in t.symbols:
+      yield s & c
+
+block: # bug #12852
+  var tree = CappedStringTree(symbols: "^v><", cap: 5)
+
+  doAssert BreadthOrder.depthOf(tree, "", ">>>") == 3
+
+block: #bug #22723
+  type
+    Node  = concept n, type T 
+      for i in n.children:
+        i is T
+      n.parent is T
+
+    Nd = ref object
+      parent: Nd
+      children: seq[Nd]
+
+  proc addChild(parent, child: Node) =
+    parent.children.add(child)
+    child.parent = parent
+
+  proc foo =
+    var
+      a = Nd()
+      b = Nd()
+    a.addChild(b)
+    doAssert a.children.len == 1
+
+  foo()
diff --git a/tests/concepts/tconcepts_overload_precedence.nim b/tests/concepts/tconcepts_overload_precedence.nim
new file mode 100644
index 000000000..c580d2688
--- /dev/null
+++ b/tests/concepts/tconcepts_overload_precedence.nim
@@ -0,0 +1,69 @@
+discard """
+  output: '''x as ParameterizedType[T]
+x as ParameterizedType[T]
+x as ParameterizedType[T]
+x as ParameterizedType
+x as ParameterizedType
+x as CustomTypeClass'''
+"""
+
+type ParameterizedType[T] = object
+
+type CustomTypeClass = concept c
+  true
+
+# 3 competing procs
+proc a[T](x: ParameterizedType[T]) =
+  echo "x as ParameterizedType[T]"
+
+proc a(x: ParameterizedType) =
+  echo "x as ParameterizedType"
+
+proc a(x: CustomTypeClass) =
+  echo "x as CustomTypeClass"
+
+# the same procs in different order
+proc b(x: ParameterizedType) =
+  echo "x as ParameterizedType"
+
+proc b(x: CustomTypeClass) =
+  echo "x as CustomTypeClass"
+
+proc b[T](x: ParameterizedType[T]) =
+  echo "x as ParameterizedType[T]"
+
+# and yet another order
+proc c(x: CustomTypeClass) =
+  echo "x as CustomTypeClass"
+
+proc c(x: ParameterizedType) =
+  echo "x as ParameterizedType"
+
+proc c[T](x: ParameterizedType[T]) =
+  echo "x as ParameterizedType[T]"
+
+# remove the most specific one
+proc d(x: ParameterizedType) =
+  echo "x as ParameterizedType"
+
+proc d(x: CustomTypeClass) =
+  echo "x as CustomTypeClass"
+
+# then shuffle the order again
+proc e(x: CustomTypeClass) =
+  echo "x as CustomTypeClass"
+
+proc e(x: ParameterizedType) =
+  echo "x as ParameterizedType"
+
+# the least specific one is a match
+proc f(x: CustomTypeClass) =
+  echo "x as CustomTypeClass"
+
+a(ParameterizedType[int]())
+b(ParameterizedType[int]())
+c(ParameterizedType[int]())
+d(ParameterizedType[int]())
+e(ParameterizedType[int]())
+f(ParameterizedType[int]())
+
diff --git a/tests/concepts/templatesinconcepts.nim b/tests/concepts/templatesinconcepts.nim
new file mode 100644
index 000000000..292b97ea6
--- /dev/null
+++ b/tests/concepts/templatesinconcepts.nim
@@ -0,0 +1,56 @@
+import typetraits
+
+template typeLen(x): int = x.type.name.len
+
+template bunchOfChecks(x) =
+  x.typeLen > 3
+  x != 10 is bool
+
+template stmtListExprTmpl(x: untyped): untyped =
+  x is int
+  x
+
+type
+  Obj = object
+    x: int
+
+  Gen[T] = object
+    x: T
+
+  Eq = concept x, y
+    (x == y) is bool
+
+  NotEq = concept x, y
+    (x != y) is bool
+
+  ConceptUsingTemplate1 = concept x
+    echo x
+    sizeof(x) is int
+    bunchOfChecks x
+
+  ConceptUsingTemplate2 = concept x
+    stmtListExprTmpl x
+
+template ok(x) =
+  static: assert(x)
+
+template no(x) =
+  static: assert(not(x))
+
+ok int is Eq
+ok int is NotEq
+ok string is Eq
+ok string is NotEq
+ok Obj is Eq
+ok Obj is NotEq
+ok Gen[string] is Eq
+ok Gen[int] is NotEq
+
+no int is ConceptUsingTemplate1
+ok float is ConceptUsingTemplate1
+no string is ConceptUsingTemplate1
+
+ok int is ConceptUsingTemplate2
+no float is ConceptUsingTemplate2
+no string is ConceptUsingTemplate2
+
diff --git a/tests/concepts/texplain.nim b/tests/concepts/texplain.nim
new file mode 100644
index 000000000..8cf04ae82
--- /dev/null
+++ b/tests/concepts/texplain.nim
@@ -0,0 +1,182 @@
+discard """
+  cmd: "nim c --verbosity:0 --colors:off $file"
+  nimout: '''
+texplain.nim(162, 10) Hint: Non-matching candidates for e(y)
+proc e(i: int): int
+  first type mismatch at position: 1
+  required type for i: int
+  but expression 'y' is of type: MatchingType
+
+texplain.nim(165, 7) Hint: Non-matching candidates for e(10)
+proc e(o: ExplainedConcept): int
+  first type mismatch at position: 1
+  required type for o: ExplainedConcept
+  but expression '10' is of type: int literal(10)
+texplain.nim(128, 6) ExplainedConcept: undeclared field: 'foo'
+texplain.nim(128, 5) ExplainedConcept: concept predicate failed
+texplain.nim(129, 6) ExplainedConcept: undeclared field: 'bar'
+texplain.nim(128, 5) ExplainedConcept: concept predicate failed
+
+texplain.nim(168, 10) Hint: Non-matching candidates for e(10)
+proc e(o: ExplainedConcept): int
+  first type mismatch at position: 1
+  required type for o: ExplainedConcept
+  but expression '10' is of type: int literal(10)
+texplain.nim(128, 6) ExplainedConcept: undeclared field: 'foo'
+texplain.nim(128, 5) ExplainedConcept: concept predicate failed
+texplain.nim(129, 6) ExplainedConcept: undeclared field: 'bar'
+texplain.nim(128, 5) ExplainedConcept: concept predicate failed
+
+texplain.nim(172, 20) Error: type mismatch: got <NonMatchingType>
+but expected one of:
+proc e(i: int): int
+  first type mismatch at position: 1
+  required type for i: int
+  but expression 'n' is of type: NonMatchingType
+proc e(o: ExplainedConcept): int
+  first type mismatch at position: 1
+  required type for o: ExplainedConcept
+  but expression 'n' is of type: NonMatchingType
+texplain.nim(172, 9) template/generic instantiation of `assert` from here
+texplain.nim(128, 5) ExplainedConcept: concept predicate failed
+
+expression: e(n)
+texplain.nim(173, 20) Error: type mismatch: got <NonMatchingType>
+but expected one of:
+proc r(i: string): int
+  first type mismatch at position: 1
+  required type for i: string
+  but expression 'n' is of type: NonMatchingType
+proc r(o: RegularConcept): int
+  first type mismatch at position: 1
+  required type for o: RegularConcept
+  but expression 'n' is of type: NonMatchingType
+texplain.nim(173, 9) template/generic instantiation of `assert` from here
+texplain.nim(132, 5) RegularConcept: concept predicate failed
+proc r[T](a: SomeNumber; b: T; c: auto)
+  first type mismatch at position: 1
+  required type for a: SomeNumber
+  but expression 'n' is of type: NonMatchingType
+
+expression: r(n)
+texplain.nim(174, 20) Hint: Non-matching candidates for r(y)
+proc r(i: string): int
+  first type mismatch at position: 1
+  required type for i: string
+  but expression 'y' is of type: MatchingType
+proc r[T](a: SomeNumber; b: T; c: auto)
+  first type mismatch at position: 1
+  required type for a: SomeNumber
+  but expression 'y' is of type: MatchingType
+
+texplain.nim(182, 2) Error: type mismatch: got <MatchingType>
+but expected one of:
+proc f(o: NestedConcept)
+  first type mismatch at position: 1
+  required type for o: NestedConcept
+  but expression 'y' is of type: MatchingType
+texplain.nim(132, 6) RegularConcept: undeclared field: 'foo'
+texplain.nim(132, 5) RegularConcept: concept predicate failed
+texplain.nim(133, 6) RegularConcept: undeclared field: 'bar'
+texplain.nim(132, 5) RegularConcept: concept predicate failed
+texplain.nim(136, 5) NestedConcept: concept predicate failed
+
+expression: f(y)'''
+  errormsg: "type mismatch: got <MatchingType>"
+"""
+
+
+
+# proc r[T](a: SomeNumber; b: T; c: auto)
+# proc r(i: string): int
+# proc r(o: RegularConcept): int
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# line 124 HERE
+
+type
+  ExplainedConcept {.explain.} = concept o
+    o.foo is int
+    o.bar is string
+
+  RegularConcept = concept o
+    o.foo is int
+    o.bar is string
+
+  NestedConcept = concept o
+    o.foo is RegularConcept
+
+  NonMatchingType = object
+    foo: int
+    bar: int
+
+  MatchingType = object
+    foo: int
+    bar: string
+
+proc e(o: ExplainedConcept): int = 1
+proc e(i: int): int = i
+
+proc r[T](a: SomeNumber, b: T, c: auto) = discard
+proc r(o: RegularConcept): int = 1
+proc r(i: string): int = 1
+
+proc f(o: NestedConcept) = discard
+
+var n = NonMatchingType(foo: 10, bar: 20)
+var y = MatchingType(foo: 10, bar: "bar")
+
+# no diagnostic here:
+discard e(y)
+
+# explain that e(int) doesn't match
+discard e(y) {.explain.}
+
+# explain that e(ExplainedConcept) doesn't match
+echo(e(10) {.explain.}, 20)
+
+# explain that e(ExplainedConcept) doesn't again
+discard e(10)
+
+static:
+  # provide diagnostics why the compile block failed
+  assert(compiles(e(n)) {.explain.} == false)
+  assert(compiles(r(n)) {.explain.} == false)
+  assert(compiles(r(y)) {.explain.} == true)
+
+  # these should not produce any output
+  assert(compiles(r(10)) == false)
+  assert(compiles(e(10)) == true)
+
+# finally, provide multiple nested explanations for failed matching
+# of regular concepts, even when the explain pragma is not used
+f(y)
diff --git a/tests/concepts/titerable.nim b/tests/concepts/titerable.nim
new file mode 100644
index 000000000..b18658b2a
--- /dev/null
+++ b/tests/concepts/titerable.nim
@@ -0,0 +1,20 @@
+discard """
+  nimout: "int\nint"
+  output: 15
+"""
+
+import typetraits
+
+type
+  Iterable[T] = concept x
+    for value in x:
+      type(value) is T
+
+proc sum*[T](iter: Iterable[T]): T =
+  static: echo T.name
+  for element in iter:
+    static: echo element.type.name
+    result += element
+
+echo sum([1, 2, 3, 4, 5])
+
diff --git a/tests/concepts/tmanual.nim b/tests/concepts/tmanual.nim
new file mode 100644
index 000000000..c917f5022
--- /dev/null
+++ b/tests/concepts/tmanual.nim
@@ -0,0 +1,42 @@
+discard """
+  output: '''1
+2
+3
+4
+5
+6
+a
+b
+t
+e
+s
+t
+'''
+"""
+
+template accept(e) =
+  static: assert compiles(e)
+
+template reject(e) =
+  static: assert(not compiles(e))
+
+type
+  Container[T] = concept c
+    c.len is Ordinal
+    items(c) is T
+    for value in c:
+      type(value) is T
+
+proc takesIntContainer(c: Container[int]) =
+  for e in c: echo e
+
+takesIntContainer(@[1, 2, 3])
+reject takesIntContainer(@["x", "y"])
+
+proc takesContainer(c: Container) =
+  for e in c: echo e
+
+takesContainer(@[4, 5, 6])
+takesContainer(@["a", "b"])
+takesContainer "test"
+reject takesContainer(10)
diff --git a/tests/concepts/tmapconcept.nim b/tests/concepts/tmapconcept.nim
new file mode 100644
index 000000000..cc99737df
--- /dev/null
+++ b/tests/concepts/tmapconcept.nim
@@ -0,0 +1,102 @@
+discard """
+output: '''10
+10
+
+1'''
+nimout: '''
+K=string V=int
+K=int64 V=string
+K=int V=int
+'''
+"""
+
+import tables, typetraits
+
+template ok(check) = assert check
+template no(check) = assert(not check)
+
+type
+  Enumerable[T] = concept e
+    for v in e:
+      v is T
+
+  Map[K, V] = concept m, var mvar
+    m[K] is V
+    mvar[K] = V
+    m.contains(K) is bool
+    m.valuesSeq is Enumerable[V]
+
+  TreeMap[K, V] = object
+    root: int
+
+  SparseSeq = object
+    data: seq[int]
+
+  JudyArray = object
+    data: SparseSeq
+
+static:
+  ok seq[int] is Enumerable[int]
+  ok seq[string] is Enumerable
+  ok seq[int] is Enumerable[SomeNumber]
+  ok SparseSeq.data is Enumerable
+  no seq[string] is Enumerable[int]
+  no int is Enumerable
+  no int is Enumerable[int]
+
+# Complete the map concept implementation for the Table type
+proc valuesSeq[K, V](t: Table[K, V]): seq[V]  =
+  result = @[]
+  for k, v in t:
+    result.add v
+
+# Map concept inplementation for TreeMap
+proc valuesSeq(t: TreeMap): array[1, TreeMap.V] =
+  var v: t.V
+  result = [v]
+
+proc contains[K, V](t: TreeMap[K, V], key: K): bool = true
+
+proc `[]=`[K, V](t: var TreeMap[K, V], key: K, val: V) = discard
+proc `[]`(t: TreeMap, key: t.K): TreeMap.V = discard
+
+# Map concept implementation for the non-generic JudyArray
+proc valuesSeq(j: JudyArray): SparseSeq = j.data
+
+proc contains(t: JudyArray, key: int): bool = true
+
+proc `[]=`(t: var JudyArray, key, val: int) = discard
+proc `[]`(t: JudyArray, key: int): int = discard
+
+iterator items(s: SparseSeq): int =
+  for i in s.data: yield i
+
+# Generic proc defined over map
+proc getFirstValue[K,V](m : Map[K,V]): V =
+  static: echo "K=", K.name, " V=", V.name
+
+  for i in m.valuesSeq:
+    return i
+
+  raise newException(RangeDefect, "no values")
+
+proc useConceptProcInGeneric[K, V](t: Table[K, V]): V =
+  return t.getFirstValue
+
+var t = initTable[string, int]()
+t["test"] = 10
+
+echo t.getFirstValue
+echo t.useConceptProcInGeneric
+
+var tm = TreeMap[int64, string](root: 0)
+echo getFirstValue(tm)
+
+var j = JudyArray(data: SparseSeq(data: @[1, 2, 3]))
+echo getFirstValue(j)
+
+static:
+  ok Table[int, float] is Map
+  ok Table[int, string] is Map[SomeNumber, string]
+  no JudyArray is Map[string, int]
+
diff --git a/tests/concepts/tmatrixconcept.nim b/tests/concepts/tmatrixconcept.nim
new file mode 100644
index 000000000..ca31f5942
--- /dev/null
+++ b/tests/concepts/tmatrixconcept.nim
@@ -0,0 +1,81 @@
+discard """
+output: "0\n0\n0"
+nimout: '''
+R=3 C=3 TE=9 FF=14 FC=20 T=int
+R=3 C=3 T=int
+'''
+"""
+
+import typetraits
+
+template ok(x) = assert x
+template no(x) = assert(not x)
+
+const C = 10
+
+type
+  Matrix[Rows, Cols, TotalElements, FromFoo, FromConst: static[int]; T] = concept m, var mvar, type M
+    M.M == Rows
+    Cols == M.N
+    M.T is T
+
+    m[int, int] is T
+    mvar[int, int] = T
+
+    FromConst == C * 2
+
+    # more complicated static param inference cases
+    m.data is array[TotalElements, T]
+    m.foo(array[0..FromFoo, type m[int, 10]])
+
+  MyMatrix[M, K: static[int]; T] = object
+    data: array[M*K, T]
+
+# adaptor for the concept's non-matching expectations
+template N(M: type MyMatrix): untyped = M.K
+
+proc `[]`(m: MyMatrix; r, c: int): m.T =
+  m.data[r * m.K + c]
+
+proc `[]=`(m: var MyMatrix; r, c: int, v: m.T) =
+  m.data[r * m.K + c] = v
+
+proc foo(x: MyMatrix, arr: array[15, x.T]) = discard
+
+proc genericMatrixProc[R, C, TE, FF, FC, T](m: Matrix[R, C, TE, FF, FC, T]): T =
+  static:
+    echo "R=", R, " C=", C, " TE=", TE, " FF=", FF, " FC=", FC, " T=", T.name
+
+  m[0, 0]
+
+proc implicitMatrixProc(m: Matrix): m.T =
+  static:
+    echo "R=", m.Rows,
+        " C=", m.Cols,
+        # XXX: fix these
+        #" TE=", m.TotalElements,
+        #" FF=", m.FromFoo,
+        #" FC=", m.FromConst,
+        " T=", m.T.name
+
+  m[0, 0]
+
+proc myMatrixProc(x: MyMatrix): MyMatrix.T = genericMatrixProc(x)
+
+var x: MyMatrix[3, 3, int]
+
+static:
+  # ok x is Matrix
+  ok x is Matrix[3, 3, 9, 14, 20, int]
+
+  no x is Matrix[3, 3, 8, 15, 20, int]
+  no x is Matrix[3, 3, 9, 10, 20, int]
+  no x is Matrix[3, 3, 9, 15, 21, int]
+  no x is Matrix[3, 3, 9, 15, 20, float]
+  no x is Matrix[4, 3, 9, 15, 20, int]
+  no x is Matrix[3, 4, 9, 15, 20, int]
+
+echo x.myMatrixProc
+echo x.genericMatrixProc
+echo x.implicitMatrixProc
+
diff --git a/tests/concepts/tmatrixlib.nim b/tests/concepts/tmatrixlib.nim
new file mode 100644
index 000000000..a4ab04061
--- /dev/null
+++ b/tests/concepts/tmatrixlib.nim
@@ -0,0 +1,31 @@
+discard """
+output: "0"
+"""
+
+import matrix, matrixalgo
+
+import typetraits # XXX: this should be removed
+
+var m: Matrix[3, 3, int]
+var projectionMatrix: Matrix[4, 4, float]
+
+echo m.transposed.determinant
+setPerspectiveProjection projectionMatrix
+
+template ok(x) = assert x
+template no(x) = assert(not x)
+
+static:
+  ok projectionMatrix is AnyTransform3D
+  no m is AnyTransform3D
+  
+  type SquareStringMatrix = Matrix[5, 5, string]
+  
+  ok SquareStringMatrix is AnyMatrix
+  ok SquareStringMatrix is AnySquareMatrix
+  no SquareStringMatrix is AnyTransform3D
+  
+  ok Matrix[5, 10, int] is AnyMatrix
+  no Matrix[7, 15, float] is AnySquareMatrix
+  no Matrix[4, 4, int] is AnyTransform3D
+
diff --git a/tests/concepts/tmodifiersinplace.nim b/tests/concepts/tmodifiersinplace.nim
new file mode 100644
index 000000000..db5583929
--- /dev/null
+++ b/tests/concepts/tmodifiersinplace.nim
@@ -0,0 +1,30 @@
+type
+  VarContainer[T] = concept c
+    put(var c, T)
+
+  AltVarContainer[T] = concept var c
+    put(c, T)
+
+  NonVarContainer[T] = concept c
+    put(c, T)
+
+  GoodContainer = object
+    x: int
+
+  BadContainer = object
+    x: int
+
+proc put(x: BadContainer, y: int) = discard
+proc put(x: var GoodContainer, y: int) = discard
+
+template ok(x) = assert(x)
+template no(x) = assert(not(x))
+
+static:
+  ok GoodContainer is VarContainer[int]
+  ok GoodContainer is AltVarContainer[int]
+  no BadContainer is VarContainer[int]
+  no BadContainer is AltVarContainer[int]
+  ok GoodContainer is NonVarContainer[int]
+  ok BadContainer is NonVarContainer[int]
+
diff --git a/tests/concepts/tmonoid.nim b/tests/concepts/tmonoid.nim
new file mode 100644
index 000000000..e0e19adbc
--- /dev/null
+++ b/tests/concepts/tmonoid.nim
@@ -0,0 +1,25 @@
+discard """
+  output: '''true'''
+"""
+
+# bug #3686
+
+type Monoid = concept x, y
+  x + y is type(x)
+  type(z(type(x))) is type(x)
+
+proc z(x: typedesc[int]): int = 0
+
+echo(int is Monoid)
+
+# https://github.com/nim-lang/Nim/issues/8126
+type AdditiveMonoid* = concept x, y, type T
+  x + y is T
+
+  # some redundant checks to test an alternative approaches:
+  type TT = type(x)
+  x + y is type(x)
+  x + y is TT
+
+doAssert(1 is AdditiveMonoid)
+
diff --git a/tests/concepts/trandomvars.nim b/tests/concepts/trandomvars.nim
new file mode 100644
index 000000000..1f04b9ecf
--- /dev/null
+++ b/tests/concepts/trandomvars.nim
@@ -0,0 +1,63 @@
+discard """
+output: '''
+true
+true
+true
+3
+18.0
+324.0
+'''
+"""
+
+type RNG = object
+
+proc random(rng: var RNG): float = 1.0
+
+type
+  RandomVar[A] = concept x
+    var rng: RNG
+    rng.sample(x) is A
+
+  Constant[A] = object
+    value: A
+
+  Uniform = object
+    a, b: float
+
+  ClosureVar[A] = proc(rng: var RNG): A
+
+proc sample[A](rng: var RNG, c: Constant[A]): A = c.value
+
+proc sample(rng: var RNG, u: Uniform): float = u.a + (u.b - u.a) * rng.random()
+
+proc sample[A](rng: var RNG, c: ClosureVar[A]): A = c(rng)
+
+proc constant[A](a: A): Constant[A] = Constant[A](value: a)
+
+proc uniform(a, b: float): Uniform = Uniform(a: a, b: b)
+
+proc lift1[A, B](f: proc(a: A): B, r: RandomVar[A]): ClosureVar[B] =
+  proc inner(rng: var RNG): B = f(rng.sample(r))
+
+  return inner
+
+
+proc main() =
+  proc sq(x: float): float = x * x
+
+  let
+    c = constant(3)
+    u = uniform(2, 18)
+    t = lift1(sq, u)
+
+  var rng: RNG
+
+  echo(c is RandomVar[int])
+  echo(u is RandomVar[float])
+  echo(t is RandomVar[float])
+
+  echo rng.sample(c)
+  echo rng.sample(u)
+  echo rng.sample(t)
+
+main()
diff --git a/tests/concepts/trandomvars2.nim b/tests/concepts/trandomvars2.nim
new file mode 100644
index 000000000..861e876a7
--- /dev/null
+++ b/tests/concepts/trandomvars2.nim
@@ -0,0 +1,42 @@
+discard """
+output: "11.0"
+"""
+
+type
+  # A random number generator
+  Random = object
+    random: proc(): float
+  # A generic typeclass for a random var
+  RandomVar[A] = concept x
+    var rng: Random
+    rng.sample(x) is A
+  # A few concrete instances
+  Uniform = object
+    a, b: float
+  ClosureVar[A] = object
+    f: proc(rng: var Random): A
+
+# How to sample from various concrete instances
+proc sample(rng: var Random, u: Uniform): float = u.a + (u.b - u.a) * rng.random()
+
+proc sample[A](rng: var Random, c: ClosureVar[A]): A = c.f(rng)
+
+proc uniform(a, b: float): Uniform = Uniform(a: a, b: b)
+
+# How to lift a function on values to a function on random variables
+proc map[A, B](x: RandomVar[A], f: proc(a: A): B): ClosureVar[B] =
+  proc inner(rng: var Random): B =
+    f(rng.sample(x))
+
+  result.f = inner
+
+import sugar
+
+proc fakeRandom(): Random =
+  result.random = () => 0.5
+
+let x = uniform(1, 10).map((x: float) => 2 * x)
+
+var rng = fakeRandom()
+
+echo rng.sample(x)
diff --git a/tests/concepts/treversable.nim b/tests/concepts/treversable.nim
new file mode 100644
index 000000000..d30ba0a3c
--- /dev/null
+++ b/tests/concepts/treversable.nim
@@ -0,0 +1,31 @@
+# issue 7705, 7703, 7702
+discard """
+  output: '''
+z
+e
+'''
+"""
+
+type
+  Reversable*[T] = concept a
+    a[int] is T
+    a.high is int
+    a.len is int
+    a.low is int
+
+proc get[T](s: Reversable[T], n: int): T =
+  s[n]
+
+proc hi[T](s: Reversable[T]): int =
+  s.high
+
+proc lo[T](s: Reversable[T]): int =
+  s.low
+
+iterator reverse*[T](s: Reversable[T]): T =
+  assert hi(s) - lo(s) == len(s) - 1
+  for z in hi(s).countdown(lo(s)):
+    yield s.get(z)
+
+for s in @["e", "z"].reverse:
+  echo s
diff --git a/tests/concepts/tseqofconcept.nim b/tests/concepts/tseqofconcept.nim
new file mode 100644
index 000000000..5e44117ea
--- /dev/null
+++ b/tests/concepts/tseqofconcept.nim
@@ -0,0 +1,19 @@
+discard """
+output: "1\n2\n3"
+"""
+
+type
+  MyConcept = concept x
+    someProc(x)
+
+  SomeSeq = seq[MyConcept]
+
+proc someProc(x:int) = echo x
+
+proc work (s: SomeSeq) =
+  for item in s:
+    someProc item
+
+var s = @[1, 2, 3]
+work s
+
diff --git a/tests/concepts/tspec.nim b/tests/concepts/tspec.nim
new file mode 100644
index 000000000..52f13a40a
--- /dev/null
+++ b/tests/concepts/tspec.nim
@@ -0,0 +1,105 @@
+discard """
+  output: '''4
+0
+4
+4
+1
+2
+3
+yes int
+string int'''
+  joinable: false
+"""
+
+import hashes
+
+type
+  Comparable = concept # no T, an atom
+    proc cmp(a, b: Self): int
+
+  ToStringable = concept
+    proc `$`(a: Self): string
+
+  Hashable = concept   ## the most basic of identity assumptions
+    proc hash(x: Self): int
+    proc `==`(x, y: Self): bool
+
+  Swapable = concept
+    proc swap(x, y: var Self)
+
+
+proc h(x: Hashable) =
+  echo x
+
+h(4)
+
+when true:
+  proc compare(a: Comparable) =
+    echo cmp(a, a)
+
+  compare(4)
+
+proc dollar(x: ToStringable) =
+  echo x
+
+when true:
+  dollar 4
+  dollar "4"
+
+#type D = distinct int
+
+#dollar D(4)
+
+when true:
+  type
+    Iterable[Ix] = concept
+      iterator items(c: Self): Ix
+
+  proc g[Tu](it: Iterable[Tu]) =
+    for x in it:
+      echo x
+
+  g(@[1, 2, 3])
+
+proc hs(x: Swapable) =
+  var y = x
+  swap y, y
+
+hs(4)
+
+type
+  Indexable[T] = concept # has a T, a collection
+    proc `[]`(a: Self; index: int): T # we need to describe how to infer 'T'
+    # and then we can use the 'T' and it must match:
+    proc `[]=`(a: var Self; index: int; value: T)
+    proc len(a: Self): int
+
+proc indexOf[T](a: Indexable[T]; value: T) =
+  echo "yes ", T
+
+block:
+  var x = @[1, 2, 3]
+  indexOf(x, 4)
+
+import tables, typetraits
+
+type
+  Dict[K, V] = concept
+    proc `[]`(s: Self; k: K): V
+    proc `[]=`(s: var Self; k: K; v: V)
+
+proc d[K2, V2](x: Dict[K2, V2]) =
+  echo K2, " ", V2
+
+var x = initTable[string, int]()
+d(x)
+
+
+type Monoid = concept
+  proc `+`(x, y: Self): Self
+  proc z(t: typedesc[Self]): Self
+
+proc z(x: typedesc[int]): int = 0
+
+doAssert int is Monoid
+
diff --git a/tests/concepts/tstackconcept.nim b/tests/concepts/tstackconcept.nim
new file mode 100644
index 000000000..fb6032732
--- /dev/null
+++ b/tests/concepts/tstackconcept.nim
@@ -0,0 +1,62 @@
+discard """
+output: "20\n10"
+nimout: '''
+INFERRED int
+VALUE TYPE int
+VALUE TYPE NAME INT
+IMPLICIT INFERRED int int
+IMPLICIT VALUE TYPE int int
+IMPLICIT VALUE TYPE NAME INT INT
+'''
+"""
+
+import typetraits, strutils
+
+template reject(e) =
+  static: assert(not compiles(e))
+
+type
+  ArrayStack = object
+    data: seq[int]
+
+proc push(s: var ArrayStack, item: int) =
+  s.data.add item
+
+proc pop(s: var ArrayStack): int =
+  return s.data.pop()
+
+type
+  Stack[T] = concept var s
+    s.push(T)
+    s.pop() is T
+
+    type ValueType = T
+    const ValueTypeName = T.name.toUpperAscii
+
+proc genericAlgorithm[T](s: var Stack[T], y: T) =
+  static:
+    echo "INFERRED ", T.name
+    echo "VALUE TYPE ", s.ValueType.name
+    echo "VALUE TYPE NAME ", s.ValueTypeName
+
+  s.push(y)
+  echo s.pop
+
+proc implicitGeneric(s: var Stack): auto =
+  static:
+    echo "IMPLICIT INFERRED ", s.T.name, " ", Stack.T.name
+    echo "IMPLICIT VALUE TYPE ", s.ValueType.name, " ", Stack.ValueType.name
+    echo "IMPLICIT VALUE TYPE NAME ", s.ValueTypeName, " ", Stack.ValueTypeName
+
+  return s.pop()
+
+var s = ArrayStack(data: @[])
+
+s.push 10
+s.genericAlgorithm 20
+echo s.implicitGeneric
+
+reject s.genericAlgorithm "x"
+reject s.genericAlgorithm 1.0
+reject "str".implicitGeneric
+reject implicitGeneric(10)
diff --git a/tests/concepts/tswizzle.nim b/tests/concepts/tswizzle.nim
new file mode 100644
index 000000000..07205d454
--- /dev/null
+++ b/tests/concepts/tswizzle.nim
@@ -0,0 +1,80 @@
+discard """
+  output: '''3
+[1, 3]
+[2, 1, 2]
+'''
+  disabled: "true"
+"""
+
+import macros, strutils
+
+template accept(e: expr) =
+  static: assert(compiles(e))
+
+template reject(e: expr) =
+  static: assert(not compiles(e))
+
+proc swizzleIdx(c: char): int =
+  return case c
+    of 'x': 0
+    of 'y': 1
+    of 'z': 2
+    of 'w': 3
+    of 'r': 0
+    of 'g': 1
+    of 'b': 2
+    of 'a': 3
+    else: 0
+
+proc isSwizzle(s: string): bool {.compileTime.} =
+  template trySet(name, set) =
+    block search:
+      for c in s:
+        if c notin set:
+          break search
+      return true
+
+  trySet coords, {'x', 'y', 'z', 'w'}
+  trySet colors, {'r', 'g', 'b', 'a'}
+
+  return false
+
+type
+  StringIsSwizzle = concept value
+    value.isSwizzle
+
+  SwizzleStr = static[string] and StringIsSwizzle
+
+proc foo(x: SwizzleStr) =
+  echo "sw"
+
+#foo("xx")
+reject foo("xe")
+
+type
+  Vec[N: static[int]; T] = array[N, T]
+
+when false:
+  proc card(x: Vec): int = x.N
+  proc `$`(x: Vec): string = x.repr.strip
+
+  macro `.`(x: Vec, swizzle: SwizzleStr): expr =
+    var
+      cardinality = swizzle.len
+      values = newNimNode(nnkBracket)
+      v = genSym()
+
+    for c in swizzle:
+      values.add newNimNode(nnkBracketExpr).add(
+        v, c.swizzleIdx.newIntLitNode)
+
+    return quote do:
+      let `v` = `x`
+      Vec[`cardinality`, `v`.T](`values`)
+
+var z = Vec([1, 2, 3])
+
+#echo z.card
+#echo z.xz
+#echo z.yxy
+
diff --git a/tests/concepts/ttrieconcept.nim b/tests/concepts/ttrieconcept.nim
new file mode 100644
index 000000000..a26e6b146
--- /dev/null
+++ b/tests/concepts/ttrieconcept.nim
@@ -0,0 +1,7 @@
+import libs/[trie_database, trie]
+
+proc takeDb(d: TrieDatabase) = discard
+var mdb: MemDB
+
+takeDb(mdb)
+
diff --git a/tests/concepts/tusertypeclasses.nim b/tests/concepts/tusertypeclasses.nim
new file mode 100644
index 000000000..83e2b176e
--- /dev/null
+++ b/tests/concepts/tusertypeclasses.nim
@@ -0,0 +1,131 @@
+discard """
+  matrix: "--mm:refc"
+  output: '''Sortable
+Sortable
+Container
+TObj
+int
+111 111
+(id: @[1, 2, 3], name: @["Vas", "Pas", "NafNaf"], age: @[10, 16, 18])
+'''
+"""
+
+# todo wait for https://github.com/nim-lang/Nim/pull/20380
+
+import typetraits
+
+template reject(expr) = assert(not compiles(x))
+
+type
+  TObj = object
+    x: int
+
+  JSonValue = object
+    val: string
+
+  Sortable = concept x, y
+    (x < y) is bool
+
+  ObjectContainer = concept C
+    C.len is Ordinal
+    for v in items(C):
+      v.type is tuple|object
+
+proc foo(c: ObjectContainer) =
+  echo "Container"
+
+proc foo(x: Sortable) =
+  echo "Sortable"
+
+foo 10
+foo "test"
+foo(@[TObj(x: 10), TObj(x: 20)])
+
+proc intval(x: int): int = 10
+
+type
+  TFoo = concept o, type T, ref r, var v, ptr p, static s
+    o.x
+    y(o) is int
+
+    var str: string
+    var intref: ref int
+
+    refproc(ref T, ref int)
+    varproc(var T)
+    ptrproc(ptr T, str)
+
+    staticproc(static[T])
+
+    typeproc T
+    T.typeproc
+    typeproc o.type
+    o.type.typeproc
+
+    o.to(type string)
+    o.to(type JsonValue)
+
+    refproc(r, intref)
+    varproc(v)
+    p.ptrproc(string)
+    staticproc s
+    typeproc(T)
+
+    const TypeName = T.name
+    type MappedType = type(o.y)
+
+    intval y(o)
+    let z = intval(o.y)
+
+    static:
+      assert T.name.len == 4
+      reject o.name
+      reject o.typeproc
+      reject staticproc(o)
+      reject o.varproc
+      reject T.staticproc
+      reject p.staticproc
+
+proc y(x: TObj): int = 10
+
+proc varproc(x: var TObj) = discard
+proc refproc(x: ref TObj, y: ref int) = discard
+proc ptrproc(x: ptr TObj, y: string) = discard
+proc staticproc(x: static[TObj]) = discard
+proc typeproc(t: type TObj) = discard
+proc to(x: TObj, t: type string) = discard
+proc to(x: TObj, t: type JSonValue) = discard
+
+proc testFoo(x: TFoo) =
+  echo x.TypeName
+  echo x.MappedType.name
+
+testFoo(TObj(x: 10))
+
+# bug #7092
+
+type stringTest = concept x
+  x is string
+
+let usedToFail: stringTest = "111"
+let working: string = "111"
+
+echo usedToFail, " ", working
+
+# bug #5868
+
+type TaggedType[T; Key: static[string]] = T
+
+proc setKey*[DT](dt: DT, key: static[string]): TaggedType[DT, key] =
+  result = cast[type(result)](dt)
+
+type Students = object
+   id : seq[int]
+   name : seq[string]
+   age: seq[int]
+
+let
+  stud = Students(id : @[1,2,3], name : @["Vas", "Pas", "NafNaf"], age : @[10,16,18])
+  stud2 = stud.setkey("id")
+
+echo stud2
diff --git a/tests/concepts/tusertypeclasses2.nim b/tests/concepts/tusertypeclasses2.nim
new file mode 100644
index 000000000..6132bc2d8
--- /dev/null
+++ b/tests/concepts/tusertypeclasses2.nim
@@ -0,0 +1,63 @@
+discard """
+  targets: "c js"
+"""
+
+block:
+  type
+    hasFieldX = concept z
+      z.x is int
+
+    obj_x = object
+      x: int
+
+    ref_obj_x = ref object
+      x: int
+
+    ref_to_obj_x = ref obj_x
+
+    p_o_x = ptr obj_x
+    v_o_x = var obj_x
+
+  template check(x) =
+    static: assert(x)
+
+  check obj_x is hasFieldX
+  check ref_obj_x is hasFieldX
+  check ref_to_obj_x is hasFieldX
+  check p_o_x is hasFieldX
+  check v_o_x is hasFieldX
+
+block:
+  type
+    Foo = concept x
+      x.isFoo
+    Bar = distinct float
+  template isFoo(x: Bar): untyped = true
+  proc foo(x: var Foo) =
+    float(x) = 1.0
+  proc foo2(x: var Bar) =
+    float(x) = 1.0
+  proc foo3(x: var (Bar|SomeNumber)) =
+    float(x) = 1.0
+  proc foo4(x: var any) =
+    float(x) = 1.0
+  var x: Bar
+  foo(x)
+  foo2(x)
+  foo3(x)
+  foo4(x)
+
+block: # bug #9550
+  block:
+    type Foo = concept c
+      for v in c: (v is char)
+
+    func foo(c: Foo) = (for v in c: discard)
+    foo @['a', 'b' ,'c']
+
+  block:
+    type Foo = concept c
+      for v in c: (v is char)
+
+    func foo(c: Foo) = (for v in c: discard)
+    foo ['a', 'b' ,'c']
diff --git a/tests/concepts/tvarconcept.nim b/tests/concepts/tvarconcept.nim
new file mode 100644
index 000000000..203ef3cdc
--- /dev/null
+++ b/tests/concepts/tvarconcept.nim
@@ -0,0 +1,9 @@
+discard """
+  output: "5"
+"""
+
+# bug #2346, bug #2404
+
+import mvarconcept
+
+echo randomInt(5)
diff --git a/tests/concepts/tvectorspace.nim b/tests/concepts/tvectorspace.nim
new file mode 100644
index 000000000..7a18c1762
--- /dev/null
+++ b/tests/concepts/tvectorspace.nim
@@ -0,0 +1,19 @@
+discard """
+  joinable: false
+"""
+
+type VectorSpace[K] = concept x, y
+  x + y is type(x)
+  zero(type(x)) is type(x)
+  -x is type(x)
+  x - y is type(x)
+  var k: K
+  k * x is type(x)
+
+proc zero(T: typedesc): T = 0
+
+static:
+  assert float is VectorSpace[float]
+  # assert float is VectorSpace[int]
+  # assert int is VectorSpace
+
diff --git a/tests/concepts/twrapconcept.nim b/tests/concepts/twrapconcept.nim
new file mode 100644
index 000000000..c3dea2ff9
--- /dev/null
+++ b/tests/concepts/twrapconcept.nim
@@ -0,0 +1,21 @@
+discard """
+  errormsg: "type mismatch: got <string>"
+  nimout: "twrapconcept.nim(10, 5) Foo: concept predicate failed"
+"""
+
+# https://github.com/nim-lang/Nim/issues/5127
+
+type
+  Foo = concept foo
+    foo.get is int
+
+  FooWrap[F: Foo] = object
+    foo: F
+
+proc get(x: int): int = x
+
+proc wrap[F: Foo](foo: F): FooWrap[F] = FooWrap[F](foo: foo)
+
+let x = wrap(12)
+let y = wrap "string"
+