summary refs log tree commit diff stats
path: root/tests/overload
diff options
context:
space:
mode:
Diffstat (limited to 'tests/overload')
-rw-r--r--tests/overload/importA.nim5
-rw-r--r--tests/overload/importB.nim15
-rw-r--r--tests/overload/issue22142/tfail_implicit_ambiguous.nim10
-rw-r--r--tests/overload/issue22142/tfail_nested_pointers.nim12
-rw-r--r--tests/overload/issue22142/tfail_object_is_generic.nim16
-rw-r--r--tests/overload/issue22142/tfail_typeclass_var_invar.nim9
-rw-r--r--tests/overload/issue22142/tissue22142_shouldpass.nim68
-rw-r--r--tests/overload/m19737.nim10
-rw-r--r--tests/overload/mvaruintconv.nim145
-rw-r--r--tests/overload/t19737.nim15
-rw-r--r--tests/overload/t23249.nim17
-rw-r--r--tests/overload/t23755.nim62
-rw-r--r--tests/overload/t7416.nim9
-rw-r--r--tests/overload/t8829.nim18
-rw-r--r--tests/overload/tconverter_to_string.nim22
-rw-r--r--tests/overload/tgenericalias.nim13
-rw-r--r--tests/overload/tissue966.nim12
-rw-r--r--tests/overload/tnamedparamoverloading.nim14
-rw-r--r--tests/overload/tor_isnt_better.nim41
-rw-r--r--tests/overload/toverl.nim11
-rw-r--r--tests/overload/toverl4.nim101
-rw-r--r--tests/overload/toverload_issues.nim200
-rw-r--r--tests/overload/toverload_various.nim568
-rw-r--r--tests/overload/tparam_forwarding.nim53
-rw-r--r--tests/overload/tproc_types_dont_like_subtypes.nim20
-rw-r--r--tests/overload/tspec.nim118
-rw-r--r--tests/overload/tstatic_with_converter.nim48
-rw-r--r--tests/overload/tsystemcmp.nim24
-rw-r--r--tests/overload/tuntypedarg.nim19
-rw-r--r--tests/overload/tvartypeclass.nim11
-rw-r--r--tests/overload/tvaruintconv.nim207
31 files changed, 1893 insertions, 0 deletions
diff --git a/tests/overload/importA.nim b/tests/overload/importA.nim
new file mode 100644
index 000000000..f045d11b4
--- /dev/null
+++ b/tests/overload/importA.nim
@@ -0,0 +1,5 @@
+type
+  Field* = object
+    elemSize*: int
+
+template `+`*(x: untyped, y: Field): untyped = x
diff --git a/tests/overload/importB.nim b/tests/overload/importB.nim
new file mode 100644
index 000000000..2dc3adf7a
--- /dev/null
+++ b/tests/overload/importB.nim
@@ -0,0 +1,15 @@
+type
+  Foo*[T] = object
+    v*: T
+
+template `+`*(x: Foo, y: Foo): untyped = x
+
+template newvar*(r: untyped): untyped {.dirty.} =
+  var r: float
+
+template t1*(x: Foo): untyped =
+  newvar(y1)
+  x
+template t2*(x: Foo): untyped =
+  newvar(y2)
+  x
diff --git a/tests/overload/issue22142/tfail_implicit_ambiguous.nim b/tests/overload/issue22142/tfail_implicit_ambiguous.nim
new file mode 100644
index 000000000..2586e0877
--- /dev/null
+++ b/tests/overload/issue22142/tfail_implicit_ambiguous.nim
@@ -0,0 +1,10 @@
+discard """
+  errormsg: "ambiguous call"
+"""
+type
+  A[T] = object
+  C = object
+
+proc test[T: A](param: T): bool = false
+proc test(param: A): bool = true
+doAssert test(A[C]()) == true  # previously would pass
diff --git a/tests/overload/issue22142/tfail_nested_pointers.nim b/tests/overload/issue22142/tfail_nested_pointers.nim
new file mode 100644
index 000000000..1603d98cb
--- /dev/null
+++ b/tests/overload/issue22142/tfail_nested_pointers.nim
@@ -0,0 +1,12 @@
+discard """
+  errormsg: "ambiguous call"
+"""
+
+type
+  A[T] = object
+  C = object
+    x:int
+proc p[T: A[ptr]](x:ptr[T]):bool = false
+proc p(x: ptr[A[ptr]]):bool = true
+var a: A[ptr[C]]
+doAssert p(a.addr) == true
diff --git a/tests/overload/issue22142/tfail_object_is_generic.nim b/tests/overload/issue22142/tfail_object_is_generic.nim
new file mode 100644
index 000000000..b46795bd5
--- /dev/null
+++ b/tests/overload/issue22142/tfail_object_is_generic.nim
@@ -0,0 +1,16 @@
+discard """
+  errormsg: "ambiguous call"
+"""
+
+#[
+As of the time of writing `object` needs some special
+treament in order to be considered "generic" in the right
+context when used implicitly
+]#
+
+type
+  C = object
+
+proc test[T: object](param: T): bool = false
+proc test(param: object): bool = true  
+doAssert test(C()) == true  # previously would pass
diff --git a/tests/overload/issue22142/tfail_typeclass_var_invar.nim b/tests/overload/issue22142/tfail_typeclass_var_invar.nim
new file mode 100644
index 000000000..07db65fef
--- /dev/null
+++ b/tests/overload/issue22142/tfail_typeclass_var_invar.nim
@@ -0,0 +1,9 @@
+discard """
+  errormsg: "ambiguous call"
+"""
+
+type C = object
+proc test[T: ptr](param: var T): bool = false
+proc test(param: var ptr): bool = true
+var d: ptr[C]
+doAssert test(d) == true  # previously would pass
diff --git a/tests/overload/issue22142/tissue22142_shouldpass.nim b/tests/overload/issue22142/tissue22142_shouldpass.nim
new file mode 100644
index 000000000..90d4efe51
--- /dev/null
+++ b/tests/overload/issue22142/tissue22142_shouldpass.nim
@@ -0,0 +1,68 @@
+type
+  A[T] = object of RootObj
+  B[T] = object
+  C = object
+    x:int
+
+# change (previously true)
+block:
+  proc test[J;H: A[J];T: B[H]](param: T): bool = false
+  proc test[T](param: B[T]): bool = true
+  doAssert test(B[A[int]]()) == false
+block:  # object is more specific then `T`
+  proc p[H:object;T:ptr[H]](param:T):bool = false
+  proc p[T](param:ptr[T]):bool= true
+  var l: ptr[C]
+  doAssert p(l) == false
+block:
+  proc p[T:A[object]](param:T):bool = false
+  proc p[T](param: A[T]):bool= true
+  doAssert p(A[C]()) == false
+block:
+  proc test[H;T: A[H]](param: T): bool = false
+  proc test(param: A): bool = true
+  doAssert test(A[C]()) == false
+
+# change (previously ambiguous)
+block:
+  proc p[T](a: A[T]): bool = false
+  proc p[T: object](a: T): bool = true
+  doAssert p(A[int]()) == false
+block:  # A is more specific than `object`
+  proc test[T: A](param: T): bool = false
+  proc test[T: object](param: T): bool = true
+  doAssert test(A[int]()) == false
+block:
+  proc test[T: A](param: T): bool = false
+  proc test(param: object): bool = true
+  doAssert test(A[int]()) == false
+block:
+  proc test[H;T: A[H]](param: T): bool = false
+  proc test(param: object): bool = true
+  doAssert test(A[C]()) == false
+block:
+  proc test[H;T: A[B[H]]](param: T): bool = false
+  proc test[T: object](param: T): bool = true
+  doAssert test(A[B[int]]()) == false
+block:
+  #[
+  This was referenced in the nim compiler source (`sumGeneric`) as a case
+  that was supposed to not be ambiguous, yet it was
+  ]#
+  proc test[J;H:A[J]; T: A[H]](param: T): bool = false
+  proc test[H;T: A[H]](param: T): bool = true
+  doAssert test(A[A[C]]()) == false
+block:
+  proc test[J;T:A[J]](param: A[T]): bool = false
+  proc test[T](param: A[T]): bool = true
+  doAssert test(A[A[C]]()) == false
+block:
+  proc test[T](param: A[T]): bool = false
+  proc test[T: object](param: A[T]): bool = true
+  doAssert test(A[C]()) == true
+
+
+block: #anti-regression (object is more specific then `T`)
+  proc test[J;T:A[J]](param: A[T]): bool = false
+  proc test(param: A[A[object]]): bool = true
+  doAssert test(A[A[C]]()) == true
\ No newline at end of file
diff --git a/tests/overload/m19737.nim b/tests/overload/m19737.nim
new file mode 100644
index 000000000..7f7ac98e2
--- /dev/null
+++ b/tests/overload/m19737.nim
@@ -0,0 +1,10 @@
+type
+  UInt128* = object
+    lo, hi: uint64
+
+func `<`*(x, y: UInt128): bool =
+  (x.hi < y.hi) or ((x.hi == y.hi) and (x.lo < y.lo))
+
+when not defined(works):
+  func `>`*(x, y: UInt128): bool =
+    (x.hi > y.hi) or ((x.hi == y.hi) and (x.lo > y.lo))
diff --git a/tests/overload/mvaruintconv.nim b/tests/overload/mvaruintconv.nim
new file mode 100644
index 000000000..b889c90cf
--- /dev/null
+++ b/tests/overload/mvaruintconv.nim
@@ -0,0 +1,145 @@
+import
+  std/[macros, tables, hashes]
+
+export
+  macros
+
+type
+  FieldDescription* = object
+    name*: NimNode
+    isPublic*: bool
+    isDiscriminator*: bool
+    typ*: NimNode
+    pragmas*: NimNode
+    caseField*: NimNode
+    caseBranch*: NimNode
+
+{.push raises: [].}
+
+func isTuple*(t: NimNode): bool =
+  t.kind == nnkBracketExpr and t[0].kind == nnkSym and eqIdent(t[0], "tuple")
+
+macro isTuple*(T: type): untyped =
+  newLit(isTuple(getType(T)[1]))
+
+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 nnkRecWhen:
+    for branch in n:
+      case branch.kind:
+      of nnkElifBranch:
+        collectFieldsFromRecList result, branch[1],
+                                 parentCaseField, parentCaseBranch
+      of nnkElse:
+        collectFieldsFromRecList result, branch[0],
+                                 parentCaseField, parentCaseBranch
+      else:
+        doAssert false
+
+  of nnkRecCase:
+    collectFieldsFromRecList result, n[0],
+                             parentCaseField,
+                             parentCaseBranch,
+                             isDiscriminator = true
+
+    for i in 1 ..< n.len:
+      let branch = n[i]
+      case branch.kind
+      of nnkOfBranch:
+        collectFieldsFromRecList result, branch[^1], n[0], branch
+      of nnkElse:
+        collectFieldsFromRecList result, branch[0], n[0], branch
+      else:
+        doAssert false
+
+  of nnkIdentDefs:
+    let fieldType = n[^2]
+    for i in 0 ..< n.len - 2:
+      var field: FieldDescription
+      field.name = n[i]
+      field.typ = fieldType
+      field.caseField = parentCaseField
+      field.caseBranch = parentCaseBranch
+      field.isDiscriminator = isDiscriminator
+
+      if field.name.kind == nnkPragmaExpr:
+        field.pragmas = field.name[1]
+        field.name = field.name[0]
+
+      if field.name.kind == nnkPostfix:
+        field.isPublic = true
+        field.name = field.name[1]
+
+      result.add field
+
+  of nnkSym:
+    result.add FieldDescription(
+      name: n,
+      typ: getType(n),
+      caseField: parentCaseField,
+      caseBranch: parentCaseBranch,
+      isDiscriminator: isDiscriminator)
+
+  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
+
+  objectType.expectKind {nnkObjectTy, nnkRefTy}
+
+  if objectType.kind == nnkRefTy:
+    objectType = objectType[0]
+
+  objectType.expectKind nnkObjectTy
+
+  var baseType = objectType[1]
+  if baseType.kind != nnkEmpty:
+    baseType.expectKind nnkOfInherit
+    baseType = baseType[0]
+    baseType.expectKind nnkSym
+    baseType = getImpl(baseType)
+    baseType.expectKind nnkTypeDef
+    baseType = baseType[2]
+    baseType.expectKind {nnkObjectTy, nnkRefTy}
+    collectFieldsInHierarchy result, baseType
+
+  let recList = objectType[2]
+  collectFieldsFromRecList result, recList
+
+proc recordFields*(typeImpl: NimNode): seq[FieldDescription] =
+  if typeImpl.isTuple:
+    for i in 1 ..< typeImpl.len:
+      result.add FieldDescription(typ: typeImpl[i], name: ident("Field" & $(i - 1)))
+    return
+
+  let objectType = case typeImpl.kind
+    of nnkObjectTy: typeImpl
+    of nnkTypeDef: typeImpl[2]
+    else:
+      macros.error("object type expected", typeImpl)
+      return
+
+  collectFieldsInHierarchy(result, objectType)
+
+macro field*(obj: typed, fieldName: static string): untyped =
+  newDotExpr(obj, ident fieldName)
+
+proc skipPragma*(n: NimNode): NimNode =
+  if n.kind == nnkPragmaExpr: n[0]
+  else: n
+
+
+{.pop.}
diff --git a/tests/overload/t19737.nim b/tests/overload/t19737.nim
new file mode 100644
index 000000000..b33ba9d8b
--- /dev/null
+++ b/tests/overload/t19737.nim
@@ -0,0 +1,15 @@
+# issue #19737
+
+import ./m19737
+
+var m: seq[uint64]
+
+proc foo(x: bool) = discard
+
+proc test[T: uint64|uint32](s: var seq[T]) =
+  var tmp = newSeq[T](1)
+  s = newSeq[T](1)
+
+  foo s[0] > tmp[0]
+
+test(m)
diff --git a/tests/overload/t23249.nim b/tests/overload/t23249.nim
new file mode 100644
index 000000000..f4657833b
--- /dev/null
+++ b/tests/overload/t23249.nim
@@ -0,0 +1,17 @@
+# issue #23249
+
+type Control* = object
+proc onAction*(c: Control, handler: proc(e: int) {.gcsafe.}) = discard
+proc onAction*(c: Control, handler: proc() {.gcsafe.}) = discard
+
+template setControlHandlerBlock(c: Control, p: untyped, a: untyped) =
+    when compiles(c.p(nil)):
+        c.p() do() {.gcsafe.}: a
+    else:
+        c.p = proc() {.gcsafe.} =
+            a
+
+proc mkLayout() =
+  var b: Control
+  setControlHandlerBlock(b, onAction):
+    echo "hi"
diff --git a/tests/overload/t23755.nim b/tests/overload/t23755.nim
new file mode 100644
index 000000000..de338a2ce
--- /dev/null
+++ b/tests/overload/t23755.nim
@@ -0,0 +1,62 @@
+type
+  BigInt[bits: static int] = object
+    limbs: array[8, uint64]
+
+block:
+  proc view[N](a: array[N, uint64]) =
+    discard
+
+  proc view[N](a: var array[N, uint64]) =
+    discard
+
+  var r: BigInt[64]
+  r.limbs.view()
+
+
+type Limbs[N: static int] = array[N, uint64]
+
+block:
+  proc view(a: Limbs) =
+    discard
+
+  proc view(a: var Limbs) =
+    discard
+
+  var r: BigInt[64]
+  r.limbs.view()
+
+
+block:
+  type IntArray[N: static[int]] = array[N, int]
+
+  proc p[T](a: IntArray[T]): bool= true
+  proc p(a: IntArray[5]): bool= false
+
+  var s: IntArray[5]
+  doAssert s.p == false
+
+block:
+  type IntArray[N: static[int]] = array[N, int]
+
+  proc `$`(a: IntArray): string =
+    return "test"
+
+  var s: IntArray[5] = [1,1,1,1,1]
+  doAssert `$`(s) == "test"
+
+block: 
+  proc p[n:static[int]](a: array[n, char]):bool=true
+  proc p[T, IDX](a: array[IDX, T]):bool=false
+
+  var g: array[32, char]
+  doAssert p(g)
+
+block:  # issue #23823
+  func p[N,T](a, b: array[N,T]) =
+    discard
+
+  func p[N: static int; T](x, y: array[N, T]) =
+    discard
+
+  var a: array[5, int]
+  p(a,a)
diff --git a/tests/overload/t7416.nim b/tests/overload/t7416.nim
new file mode 100644
index 000000000..4a9b2e7cb
--- /dev/null
+++ b/tests/overload/t7416.nim
@@ -0,0 +1,9 @@
+type
+  Foo[T] = object
+  IntFoo = Foo[int]
+
+proc bar(b: object|tuple) = discard
+proc bar(b: IntFoo) = discard
+
+var f: IntFoo
+bar(f)
\ No newline at end of file
diff --git a/tests/overload/t8829.nim b/tests/overload/t8829.nim
new file mode 100644
index 000000000..85d87f136
--- /dev/null
+++ b/tests/overload/t8829.nim
@@ -0,0 +1,18 @@
+block:
+  let txt = "Hello World"
+
+  template `[]`[T](p: ptr T, span: Slice[int]): untyped =
+    toOpenArray(cast[ptr UncheckedArray[T]](p), span.a, span.b)
+
+  doAssert $cast[ptr uint8](txt[0].addr)[0 ..< txt.len] == 
+                "[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]"
+
+
+block:
+  let txt = "Hello World"
+
+  template `[]`[T](p: ptr T, span: Slice[int]): untyped =
+    toOpenArray(cast[ptr UncheckedArray[T]](p), span.a, span.b)
+
+  doAssert $cast[ptr uint8](txt[0].addr)[0 ..< txt.len] == 
+                "[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]"
diff --git a/tests/overload/tconverter_to_string.nim b/tests/overload/tconverter_to_string.nim
new file mode 100644
index 000000000..1960372d8
--- /dev/null
+++ b/tests/overload/tconverter_to_string.nim
@@ -0,0 +1,22 @@
+discard """
+  output: '''123
+c is not nil'''
+"""
+
+# bug #9149
+
+type
+  Container = ref object
+    data: int
+
+converter containerToString*(x: Container): string = $x.data
+
+var c = Container(data: 123)
+var str = string c
+echo str
+
+if c == nil: # this line can compile on v0.18, but not on 0.19
+  echo "c is nil"
+
+if not c.isNil:
+  echo "c is not nil"
diff --git a/tests/overload/tgenericalias.nim b/tests/overload/tgenericalias.nim
new file mode 100644
index 000000000..50a44bd32
--- /dev/null
+++ b/tests/overload/tgenericalias.nim
@@ -0,0 +1,13 @@
+block: # issue #13799
+  type
+    X[A, B] = object
+      a: A
+      b: B
+
+    Y[A] = X[A, int]
+  template s(T: type X): X = T()
+  template t[A, B](T: type X[A, B]): X[A, B] = T()
+  proc works1(): Y[int] = s(X[int, int])
+  proc works2(): Y[int] = t(X[int, int])
+  proc works3(): Y[int] = t(Y[int])
+  proc broken(): Y[int] = s(Y[int])
diff --git a/tests/overload/tissue966.nim b/tests/overload/tissue966.nim
new file mode 100644
index 000000000..d0a723875
--- /dev/null
+++ b/tests/overload/tissue966.nim
@@ -0,0 +1,12 @@
+discard """
+  errormsg: "type mismatch: got <PTest>"
+"""
+
+type
+  PTest = ref object
+
+proc test(x: PTest, y: int) = discard
+
+var buf: PTest
+buf.test()
+
diff --git a/tests/overload/tnamedparamoverloading.nim b/tests/overload/tnamedparamoverloading.nim
new file mode 100644
index 000000000..a7c37ba52
--- /dev/null
+++ b/tests/overload/tnamedparamoverloading.nim
@@ -0,0 +1,14 @@
+discard """
+  output: '''
+Using x: 2
+Using y: 2
+'''
+"""
+
+proc foo(x: int) =
+  echo "Using x: ", x
+proc foo(y: int) =
+  echo "Using y: ", y
+
+foo(x = 2)
+foo(y = 2)
\ No newline at end of file
diff --git a/tests/overload/tor_isnt_better.nim b/tests/overload/tor_isnt_better.nim
new file mode 100644
index 000000000..bee125386
--- /dev/null
+++ b/tests/overload/tor_isnt_better.nim
@@ -0,0 +1,41 @@
+type
+  D[T] = object
+  E[T] = object
+
+block: # PR #22261
+  proc d(x: D):bool= false
+  proc d(x: int | D[SomeInteger]):bool= true
+  doAssert d(D[5]()) == false
+
+block: # bug #8568
+#[
+  Since PR #22261 and amendment has been made. Since D is a subset of D | E but
+  not the other way around `checkGeneric` should favor proc g(a: D) instead
+  of asserting ambiguity
+]#
+  proc g(a: D|E): string = "foo D|E"
+  proc g(a: D): string = "foo D"
+  doAssert g(D[int]()) == "foo D"
+
+type Obj1[T] = object
+  v: T
+converter toObj1[T](t: T): Obj1[T] = return Obj1[T](v: t)
+block: # issue #10019
+  proc fun1[T](elements: seq[T]): string = "fun1 seq"
+  proc fun1(o: object|tuple): string = "fun1 object|tuple"
+  proc fun2[T](elements: openArray[T]): string = "fun2 openarray"
+  proc fun2(o: object): string = "fun2 object"
+  proc fun_bug[T](elements: openArray[T]): string = "fun_bug openarray"
+  proc fun_bug(o: object|tuple):string = "fun_bug object|tuple"
+  proc main() =
+    var x = @["hello", "world"]
+    block:
+      # no ambiguity error shown here even though this would compile if we remove either 1st or 2nd overload of fun1
+      doAssert fun1(x) == "fun1 seq"
+    block:
+      # ditto
+      doAssert fun2(x) == "fun2 openarray"
+    block:
+      # Error: ambiguous call; both t0065.fun_bug(elements: openarray[T])[declared in t0065.nim(17, 5)] and t0065.fun_bug(o: object or tuple)[declared in t0065.nim(20, 5)] match for: (array[0..1, string])
+      doAssert fun_bug(x) == "fun_bug openarray"
+  main()
diff --git a/tests/overload/toverl.nim b/tests/overload/toverl.nim
new file mode 100644
index 000000000..64257be77
--- /dev/null
+++ b/tests/overload/toverl.nim
@@ -0,0 +1,11 @@
+discard """
+  errormsg: "redefinition of \'TNone\'"
+  file: "toverl.nim"
+  line: 11
+"""
+# Test for overloading
+
+type
+  TNone {.exportc: "_NONE", final.} = object
+
+proc TNone(a, b: int) = nil #ERROR_MSG attempt to redefine 'TNone'
diff --git a/tests/overload/toverl4.nim b/tests/overload/toverl4.nim
new file mode 100644
index 000000000..21cedaa96
--- /dev/null
+++ b/tests/overload/toverl4.nim
@@ -0,0 +1,101 @@
+discard """
+  output: '''true
+5.0'''
+"""
+
+#bug #592
+
+type
+  ElementKind = enum inner, leaf
+  TElement[TKey, TData] = object
+    case kind: ElementKind
+    of inner:
+      key: TKey
+      left, right: ref TElement[Tkey, TData]
+    of leaf:
+      data: TData
+  PElement[TKey, TData] = ref TElement[TKey, TData]
+
+proc newElement[Tkey, TData](other: PElement[TKey, TData]): PElement[Tkey, TData] =
+  case other.kind:
+  of inner:
+    PElement[TKey, TData](kind: ElementKind.inner, key: other.key, left: other.left, right: other.right)
+  of leaf:
+    PElement[TKey, TData](kind: ElementKind.leaf, data: other.data)
+
+proc newElement[TKey, TData](key: TKey, left: PElement[TKey, TData] = nil, right: PElement[TKey, TData] = nil) : PElement[TKey, TData] =
+  PElement[TKey, TData](kind: ElementKind.inner, key: key, left: left, right: right)
+
+proc newElement[Tkey, TData](key: Tkey, data: TData) : PElement[Tkey, TData] =
+  PElement[TKey, TData](kind: ElementKind.leaf, data: data)
+
+proc find*[TKey, TData](root: PElement[TKey, TData], key: TKey): TData {.raises: [KeyError].} =
+  if root.left == nil:
+    raise newException(KeyError, "key does not exist: " & key)
+
+  var tmp_element = addr(root)
+
+  while tmp_element.kind == inner and tmp_element.right != nil:
+    tmp_element = if tmp_element.key > key:
+                    addr(tmp_element.left)
+                  else:
+                    addr(tmp_element.right)
+
+  if tmp_element.key == key:
+    return tmp_element.left.data
+  else:
+    raise newException(KeyError, "key does not exist: " & key)
+
+proc add*[TKey, TData](root: var PElement[TKey, TData], key: TKey, data: TData) : bool =
+  if root.left == nil:
+    root.key = key
+    root.left = newElement[TKey, TData](key, data)
+    return true
+
+  var tmp_element = addr(root)
+
+  while tmp_element.kind == ElementKind.inner and tmp_element.right != nil:
+    tmp_element = if tmp_element.key > key:
+                    addr(tmp_element.left)
+                  else:
+                    addr(tmp_element.right)
+
+  if tmp_element.key == key:
+    return false
+
+  var old_element = newElement[TKey, TData](tmp_element[])
+  var new_element = newElement[TKey, TData](key, data)
+
+  tmp_element[] = if tmp_element.key < key:
+                    newElement(key, old_element, new_element)
+                  else:
+                    newElement(tmp_element.key, new_element, old_element)
+
+  return true
+
+var tree = PElement[int, int](kind: ElementKind.inner, key: 0, left: nil, right: nil)
+let result = add(tree, 1, 1)
+echo(result)
+
+# bug #3748
+type
+  Foo = object
+    bar: int
+
+proc bar(cur: Foo, val: int, s:seq[string]) =
+  discard cur.bar
+
+proc does_fail(): Foo =
+  let a = @["a"]
+  result.bar(5, a)
+
+doAssert does_fail().bar == 0
+
+# bug #20645
+
+type Zzz[Gen] = object
+
+proc testZ(z: Zzz) =
+  echo z.Gen(5)
+
+testZ(Zzz[float]())
diff --git a/tests/overload/toverload_issues.nim b/tests/overload/toverload_issues.nim
new file mode 100644
index 000000000..26bf89091
--- /dev/null
+++ b/tests/overload/toverload_issues.nim
@@ -0,0 +1,200 @@
+discard """
+  output: '''
+Version 2 was called.
+This has the highest precedence.
+This has the second-highest precedence.
+This has the lowest precedence.
+baseobj ==
+true
+even better! ==
+true
+done extraI=0
+test 0 complete, loops=0
+done extraI=1
+test 1.0 complete, loops=1
+done extraI=0
+done extraI passed 0
+test no extra complete, loops=2
+1
+'''
+"""
+
+
+# issue 4675
+import importA  # comment this out to make it work
+import importB
+
+var x: Foo[float]
+var y: Foo[float]
+let r = t1(x) + t2(y)
+
+
+
+# Bug: https://github.com/nim-lang/Nim/issues/4475
+# Fix: https://github.com/nim-lang/Nim/pull/4477
+proc test(x: varargs[string], y: int) = discard
+test(y = 1)
+
+
+
+# bug #2220
+when true:
+  type A[T] = object
+  type B = A[int]
+
+  proc q[X](x: X) =
+    echo "Version 1 was called."
+
+  proc q(x: B) =
+    echo "Version 2 was called."
+
+  q(B()) # This call reported as ambiguous.
+
+
+
+# bug #2219
+template testPred(a: untyped) =
+  block:
+    type A = object of RootObj
+    type B = object of A
+    type SomeA = A|A # A hack to make "A" a typeclass.
+
+    when a >= 3:
+      proc p[X: A](x: X) =
+        echo "This has the highest precedence."
+    when a == 2:
+      proc p[X: SomeA](x: X) =
+        echo "This has the second-highest precedence."
+    when a >= 1:
+      proc p[X](x: X) =
+        echo "This has the lowest precedence."
+
+    p(B())
+
+testPred(3)
+testPred(2)
+testPred(1)
+
+
+
+block: # bug #6526
+  type
+    BaseObj = ref object of RootObj
+    DerivedObj = ref object of BaseObj
+    OtherDerivate = ref object of BaseObj
+
+  proc p[T](a: T, b: T): bool =
+    assert false
+
+  proc p[T1, T2: BaseObj](a: T1, b: T2): bool =
+    echo "baseobj =="
+    return true
+
+  let a = DerivedObj()
+  let b = DerivedObj()
+  echo p(a,b)
+
+  proc p[T1, T2: OtherDerivate](a: T1, b: T2): bool =
+    echo "even better! =="
+    return true
+
+  let a2 = OtherDerivate()
+  let b2 = OtherDerivate()
+  echo p(a2, b2)
+
+
+
+# bug #2481
+import math
+
+template test(loopCount: int, extraI: int, testBody: untyped): typed =
+  block:
+    for i in 0..loopCount-1:
+      testBody
+    echo "done extraI=", extraI
+
+template test(loopCount: int, extraF: float, testBody: untyped): typed =
+  block:
+    test(loopCount, round(extraF).int, testBody)
+
+template test(loopCount: int, testBody: untyped): typed =
+  block:
+    test(loopCount, 0, testBody)
+    echo "done extraI passed 0"
+
+var
+  loops = 0
+
+test 0, 0:
+  loops += 1
+echo "test 0 complete, loops=", loops
+
+test 1, 1.0:
+  loops += 1
+echo "test 1.0 complete, loops=", loops
+
+when true:
+  # when true we get the following compile time error:
+  #   b.nim(35, 6) Error: expression 'loops += 1' has no type (or is ambiguous)
+  loops = 0
+  test 2:
+    loops += 1
+  echo "test no extra complete, loops=", loops
+
+# bug #2229
+type
+  Type1 = object
+    id: int
+  Type2 = object
+    id: int
+
+proc init(self: var Type1, a: int, b: ref Type2) =
+  echo "1"
+
+proc init(self: var Type2, a: int) =
+  echo """
+    Works when this proc commented out
+    Otherwise error:
+    test.nim(14, 4) Error: ambiguous call; both test.init(self: var Type1, a: int, b: ref Type2) and test.init(self: var Type1, a: int, b: ref Type2) match for: (Type1, int literal(1), ref Type2)
+  """
+
+var aa: Type1
+init(aa, 1, (
+    var bb = new(Type2);
+    bb
+))
+
+
+
+# bug #4545
+type
+  SomeObject = object
+    a: int
+  AbstractObject = object
+    objet: ptr SomeObject
+
+proc convert(this: var SomeObject): AbstractObject =
+  AbstractObject(objet: this.addr)
+
+proc varargProc(args: varargs[AbstractObject, convert]): int =
+  for arg in args:
+    result += arg.objet.a
+
+var obj = SomeObject(a: 17)
+discard varargProc(obj)
+
+
+
+# bug #11239
+
+type MySeq*[T] = object
+
+proc foo(a: seq[int]): string = "foo: seq[int]"
+proc foo[T](a: seq[T]): string = "foo: seq[T]"
+proc foo(a: MySeq[int]): string = "foo: MySeq[int]"
+proc foo[T](a: MySeq[T]): string = "foo: MySeq[T]"
+
+doAssert foo(@[1,2,3]) == "foo: seq[int]"
+doAssert foo(@["WER"]) == "foo: seq[T]"
+doAssert foo(MySeq[int]()) == "foo: MySeq[int]"
+doAssert foo(MySeq[string]()) == "foo: MySeq[T]"
diff --git a/tests/overload/toverload_various.nim b/tests/overload/toverload_various.nim
new file mode 100644
index 000000000..d195a069d
--- /dev/null
+++ b/tests/overload/toverload_various.nim
@@ -0,0 +1,568 @@
+discard """
+  output: '''
+true012innertrue
+m1
+tup1
+another number: 123
+yay
+helloa 1 b 2 x @[3, 4, 5] y 6 z 7
+yay
+12
+ref ref T ptr S
+dynamic: let
+dynamic: var
+static: const
+static: literal
+static: constant folding
+static: static string
+foo1
+1
+'''
+"""
+
+
+import strutils, sequtils
+
+
+block overl2:
+  # Test new overloading resolution rules
+  proc toverl2(x: int): string = return $x
+  proc toverl2(x: bool): string = return $x
+
+  iterator toverl2(x: int): int =
+    var res = 0
+    while res < x:
+      yield res
+      inc(res)
+
+  var
+    pp: proc (x: bool): string {.nimcall.} = toverl2
+
+  stdout.write(pp(true))
+
+  for x in toverl2(3):
+    stdout.write(toverl2(x))
+
+  block:
+    proc toverl2(x: int): string = return "inner"
+    stdout.write(toverl2(5))
+    stdout.write(true)
+
+  stdout.write("\n")
+  #OUT true012innertrue
+
+
+
+block overl3:
+  # Tests more specific generic match:
+  proc m[T](x: T) = echo "m2"
+  proc m[T](x: var ref T) = echo "m1"
+  proc tup[S, T](x: tuple[a: S, b: ref T]) = echo "tup1"
+  proc tup[S, T](x: tuple[a: S, b: T]) = echo "tup2"
+
+  var
+    obj: ref int
+    tu: tuple[a: int, b: ref bool]
+
+  m(obj)
+  tup(tu)
+
+
+
+block toverprc:
+  # Test overloading of procs when used as function pointers
+  proc parseInt(x: float): int {.noSideEffect.} = discard
+  proc parseInt(x: bool): int {.noSideEffect.} = discard
+  proc parseInt(x: float32): int {.noSideEffect.} = discard
+  proc parseInt(x: int8): int {.noSideEffect.} = discard
+  proc parseInt(x: File): int {.noSideEffect.} = discard
+  proc parseInt(x: char): int {.noSideEffect.} = discard
+  proc parseInt(x: int16): int {.noSideEffect.} = discard
+
+  proc parseInt[T](x: T): int = echo x; 34
+
+  type
+    TParseInt = proc (x: string): int {.noSideEffect.}
+
+  var
+    q = TParseInt(parseInt)
+    p: TParseInt = parseInt
+
+  proc takeParseInt(x: proc (y: string): int {.noSideEffect.}): int =
+    result = x("123")
+
+  if false:
+    echo "Give a list of numbers (separated by spaces): "
+    var x = stdin.readline.split.map(parseInt).max
+    echo x, " is the maximum!"
+  echo "another number: ", takeParseInt(parseInt)
+
+
+  type
+    TFoo[a,b] = object
+      lorem: a
+      ipsum: b
+
+  proc bar[a,b](f: TFoo[a,b], x: a) = echo(x, " ", f.lorem, f.ipsum)
+  proc bar[a,b](f: TFoo[a,b], x: b) = echo(x, " ", f.lorem, f.ipsum)
+
+  discard parseInt[string]("yay")
+
+
+
+block toverwr:
+  # Test the overloading resolution in connection with a qualifier
+  proc write(t: File, s: string) =
+    discard # a nop
+  system.write(stdout, "hello")
+  #OUT hello
+
+
+
+block tparams_after_varargs:
+  proc test(a, b: int, x: varargs[int]; y, z: int) =
+    echo "a ", a, " b ", b, " x ", @x, " y ", y, " z ", z
+
+  test 1, 2, 3, 4, 5, 6, 7
+
+  # XXX maybe this should also work with ``varargs[untyped]``
+  template takesBlockA(a, b: untyped; x: varargs[typed]; blck: untyped): untyped =
+    blck
+    echo a, b
+
+  takesBlockA 1, 2, "some", 0.90, "random stuff":
+    echo "yay"
+
+
+
+block tprefer_specialized_generic:
+  proc foo[T](x: T) =
+    echo "only T"
+
+  proc foo[T](x: ref T) =
+    echo "ref T"
+
+  proc foo[T, S](x: ref ref T; y: ptr S) =
+    echo "ref ref T ptr S"
+
+  proc foo[T, S](x: ref T; y: ptr S) =
+    echo "ref T ptr S"
+
+  proc foo[T](x: ref T; default = 0) =
+    echo "ref T; default"
+
+  var x: ref ref int
+  var y: ptr ptr int
+  foo(x, y)
+
+
+
+block tstaticoverload:
+  proc foo(s: string) =
+    echo "dynamic: ", s
+
+  proc foo(s: static[string]) =
+    echo "static: ", s
+
+  let l = "let"
+  var v = "var"
+  const c = "const"
+
+  type staticString = static[string]
+
+  foo(l)
+  foo(v)
+  foo(c)
+  foo("literal")
+  foo("constant" & " " & "folding")
+  foo(staticString("static string"))
+
+# bug #8568 (2)
+
+proc goo(a: int): string = "int"
+proc goo(a: static[int]): string = "static int"
+proc goo(a: var int): string = "var int"
+proc goo[T: int](a: T): string = "T: int"
+#proc goo[T](a: T): string = "nur T"
+
+const tmp1 = 1
+let tmp2 = 1
+var tmp3 = 1
+
+doAssert goo(1) == "static int"
+doAssert goo(tmp1) == "static int"
+doAssert goo(tmp2) == "int"
+doAssert goo(tmp3) == "var int"
+
+doAssert goo[int](1) == "T: int"
+
+doAssert goo[int](tmp1) == "T: int"
+doAssert goo[int](tmp2) == "T: int"
+doAssert goo[int](tmp3) == "T: int"
+
+# bug #6076
+
+type A[T] = object
+
+proc regr(a: A[void]) = echo "foo1"
+proc regr[T](a: A[T]) = doAssert(false)
+
+regr(A[void]())
+
+
+type Foo[T] = object
+
+proc regr[T](p: Foo[T]): seq[T] =
+  discard
+
+proc regr(p: Foo[void]): seq[int] =
+  discard
+
+
+discard regr(Foo[int]())
+discard regr(Foo[void]())
+
+
+type
+  Sha2Context*[bits: static[int],
+               bsize: static[int],
+               T: uint32|uint64] = object
+    count: array[2, T]
+    state: array[8, T]
+    buffer: array[bsize, byte]
+
+  sha224* = Sha2Context[224, 64, uint32]
+  sha256* = Sha2Context[256, 64, uint32]
+  sha384* = Sha2Context[384, 128, uint64]
+  sha512* = Sha2Context[512, 128, uint64]
+  sha512_224* = Sha2Context[224, 128, uint64]
+  sha512_256* = Sha2Context[256, 128, uint64]
+
+type
+  RipemdContext*[bits: static[int]] = object
+    count: array[2, uint32]
+    state: array[bits div 32, uint32]
+    buffer: array[64, byte]
+
+  ripemd128* = RipemdContext[128]
+  ripemd160* = RipemdContext[160]
+  ripemd256* = RipemdContext[256]
+  ripemd320* = RipemdContext[320]
+
+const
+  MaxHmacBlockSize = 256
+
+type
+  HMAC*[HashType] = object
+    mdctx: HashType
+    opadctx: HashType
+
+template sizeBlock*(h: HMAC[Sha2Context]): uint = 1u
+template sizeBlock*(h: HMAC[RipemdContext]): uint = 0u
+
+proc init*[T](hmctx: HMAC[T], key: ptr byte, ulen: uint) =
+  const sizeBlock = hmctx.sizeBlock
+  echo sizeBlock
+
+proc hmac*[A, B](HashType: typedesc, key: openArray[A],
+                 data: openArray[B]) =
+  var ctx: HMAC[HashType]
+  ctx.init(nil, 0)
+
+sha256.hmac("", "")
+
+
+
+# nested generic types
+block:
+  type
+    Foo[T] = object
+      f: T
+    Bar[T] = object
+      b: T
+    Baz[T] = object
+      z: T
+    FooBar[T] = Foo[Bar[T]]
+    FooBarBaz[T] = FooBar[Baz[T]]
+    #Int = int
+    Int = SomeInteger
+    FooBarBazInt = FooBarBaz[Int]
+    FooBarBazX = FooBarBaz[int]
+
+  proc p00(x: Foo): auto = x.f
+  proc p01[T](x: Foo[T]): auto = x.f
+  proc p02[T:Foo](x: T): auto = x.f
+
+  proc p10(x: FooBar): auto = x.f
+  proc p11[T](x: FooBar[T]): auto = x.f
+  proc p12[T:FooBar](x: T): auto = x.f
+  proc p13(x: Foo[Bar]): auto = x.f
+  proc p14[T](x: Foo[Bar[T]]): auto = x.f
+  proc p15[T:Bar](x: Foo[T]): auto = x.f
+  proc p16[T:Foo[Bar]](x: T): auto = x.f
+
+  proc p20(x: FooBarBaz): auto = x.f
+  proc p21[T](x: FooBarBaz[T]): auto = x.f
+  proc p22[T:FooBarBaz](x: T): auto = x.f
+  proc p23(x: FooBar[Baz]): auto = x.f
+  proc p24[T](x: FooBar[Baz[T]]): auto = x.f
+  proc p25[T:Baz](x: FooBar[T]): auto = x.f
+  proc p26[T:FooBar[Baz]](x: T): auto = x.f
+  proc p27(x: Foo[Bar[Baz]]): auto = x.f
+  proc p28[T](x: Foo[Bar[Baz[T]]]): auto = x.f
+  proc p29[T:Baz](x: Foo[Bar[T]]): auto = x.f
+  proc p2A[T:Bar[Baz]](x: Foo[T]): auto = x.f
+  proc p2B[T:Foo[Bar[Baz]]](x: T): auto = x.f
+
+  proc p30(x: FooBarBazInt): auto = x.f
+  proc p31[T:FooBarBazInt](x: T): auto = x.f
+  proc p32(x: FooBarBaz[Int]): auto = x.f
+  proc p33[T:Int](x: FooBarBaz[T]): auto = x.f
+  proc p34[T:FooBarBaz[Int]](x: T): auto = x.f
+  proc p35(x: FooBar[Baz[Int]]): auto = x.f
+  proc p36[T:Int](x: FooBar[Baz[T]]): auto = x.f
+  proc p37[T:Baz[Int]](x: FooBar[T]): auto = x.f
+  proc p38[T:FooBar[Baz[Int]]](x: T): auto = x.f
+  proc p39(x: Foo[Bar[Baz[Int]]]): auto = x.f
+  proc p3A[T:Int](x: Foo[Bar[Baz[T]]]): auto = x.f
+  proc p3B[T:Baz[Int]](x: Foo[Bar[T]]): auto = x.f
+  proc p3C[T:Bar[Baz[Int]]](x: Foo[T]): auto = x.f
+  proc p3D[T:Foo[Bar[Baz[Int]]]](x: T): auto = x.f
+
+  template test(x: typed) =
+    let t00 = p00(x)
+    let t01 = p01(x)
+    let t02 = p02(x)
+    let t10 = p10(x)
+    let t11 = p11(x)
+    let t12 = p12(x)
+    #let t13 = p13(x)
+    let t14 = p14(x)
+    #let t15 = p15(x)
+    #let t16 = p16(x)
+    let t20 = p20(x)
+    let t21 = p21(x)
+    let t22 = p22(x)
+    #let t23 = p23(x)
+    let t24 = p24(x)
+    #let t25 = p25(x)
+    #let t26 = p26(x)
+    #let t27 = p27(x)
+    let t28 = p28(x)
+    #let t29 = p29(x)
+    #let t2A = p2A(x)
+    #let t2B = p2B(x)
+    let t30 = p30(x)
+    let t31 = p31(x)
+    let t32 = p32(x)
+    let t33 = p33(x)
+    let t34 = p34(x)
+    let t35 = p35(x)
+    let t36 = p36(x)
+    let t37 = p37(x)
+    let t38 = p38(x)
+    let t39 = p39(x)
+    let t3A = p3A(x)
+    let t3B = p3B(x)
+    let t3C = p3C(x)
+    let t3D = p3D(x)
+
+  var a: Foo[Bar[Baz[int]]]
+  test(a)
+  var b: FooBar[Baz[int]]
+  test(b)
+  var c: FooBarBaz[int]
+  test(c)
+  var d: FooBarBazX
+  test(d)
+
+
+# overloading on tuples with generic alias
+block:
+  type
+    Foo[F,T] = object
+      exArgs: T
+    FooUn[F,T] = Foo[F,tuple[a:T]]
+    FooBi[F,T1,T2] = Foo[F,tuple[a:T1,b:T2]]
+
+  proc foo1[F,T](x: Foo[F,tuple[a:T]]): int = 1
+  proc foo1[F,T1,T2](x: Foo[F,tuple[a:T1,b:T2]]): int = 2
+  proc foo2[F,T](x: FooUn[F,T]): int = 1
+  proc foo2[F,T1,T2](x: FooBi[F,T1,T2]):int = 2
+
+  template bar1[F,T](x: Foo[F,tuple[a:T]]): int = 1
+  template bar1[F,T1,T2](x: Foo[F,tuple[a:T1,b:T2]]): int = 2
+  template bar2[F,T](x: FooUn[F,T]): int = 1
+  template bar2[F,T1,T2](x: FooBi[F,T1,T2]): int = 2
+
+  proc test(x: auto, n: int) =
+    doAssert(foo1(x) == n)
+    doAssert(foo2(x) == n)
+    doAssert(bar1(x) == n)
+    doAssert(bar2(x) == n)
+
+  var a: Foo[int, tuple[a:int]]
+  test(a, 1)
+  var b: FooUn[int, int]
+  test(b, 1)
+  var c: Foo[int, tuple[a:int,b:int]]
+  test(c, 2)
+  var d: FooBi[int, int, int]
+  test(d, 2)
+
+
+# inheritance and generics
+block:
+  type
+    Foo[T] = object of RootObj
+      x: T
+    Bar[T] = object of Foo[T]
+      y: T
+    Baz[T] = object of Bar[T]
+      z: T
+
+  template t0(x: Foo[int]): int = 0
+  template t0(x: Bar[int]): int = 1
+  template t0(x: Foo[bool or int]): int = 10
+  template t0(x: Bar[bool or int]): int = 11
+  #template t0[T:bool or int](x: Bar[T]): int = 11
+  template t0[T](x: Foo[T]): int = 20
+  template t0[T](x: Bar[T]): int = 21
+  proc p0(x: Foo[int]): int = 0
+  proc p0(x: Bar[int]): int = 1
+  #proc p0(x: Foo[bool or int]): int = 10
+  #proc p0(x: Bar[bool or int]): int = 11
+  proc p0[T](x: Foo[T]): int = 20
+  proc p0[T](x: Bar[T]): int = 21
+
+  var a: Foo[int]
+  var b: Bar[int]
+  var c: Baz[int]
+  var d: Foo[bool]
+  var e: Bar[bool]
+  var f: Baz[bool]
+  var g: Foo[float]
+  var h: Bar[float]
+  var i: Baz[float]
+  doAssert(t0(a) == 0)
+  doAssert(t0(b) == 1)
+  doAssert(t0(c) == 1)
+  doAssert(t0(d) == 10)
+  doAssert(t0(e) == 11)
+  doAssert(t0(f) == 11)
+  doAssert(t0(g) == 20)
+  doAssert(t0(h) == 21)
+  #doAssert(t0(i) == 21)
+  doAssert(p0(a) == 0)
+  doAssert(p0(b) == 1)
+  doAssert(p0(c) == 1)
+  #doAssert(p0(d) == 10)
+  #doAssert(p0(e) == 11)
+  #doAssert(p0(f) == 11)
+  doAssert(p0(g) == 20)
+  doAssert(p0(h) == 21)
+  doAssert(p0(i) == 21)
+
+  #type
+  #  f0 = proc(x:Foo)
+
+
+block:
+  type
+    TilesetCT[n: static[int]] = distinct int
+    TilesetRT = int
+    Tileset = TilesetCT | TilesetRT
+
+  func prepareTileset(tileset: var Tileset) = discard
+
+  func prepareTileset(tileset: Tileset): Tileset =
+    result = tileset
+    result.prepareTileset
+
+  var parsedTileset: TilesetRT
+  prepareTileset(parsedTileset)
+
+
+block:
+  proc p1[T,U: SomeInteger|SomeFloat](x: T, y: U): int|float =
+    when T is SomeInteger and U is SomeInteger:
+      result = int(x) + int(y)
+    else:
+      result = float(x) + float(y)
+  doAssert(p1(1,2) == 3)
+  doAssert(p1(1.0,2) == 3.0)
+  doAssert(p1(1,2.0) == 3.0)
+  doAssert(p1(1.0,2.0) == 3.0)
+
+  type Foo[T,U] = U
+  template F[T,U](t: typedesc[T], x: U): untyped = Foo[T,U](x)
+  proc p2[T; U,V:Foo[T,SomeNumber]](x: U, y: V): T =
+    T(x) + T(y)
+  #proc p2[T; U:Foo[T,SomeNumber], V:Foo[not T,SomeNumber]](x: U, y: V): T =
+  #  T(x) + T(y)
+  doAssert(p2(F(int,1),F(int,2)) == 3)
+  doAssert(p2(F(float,1),F(float,2)) == 3.0)
+  doAssert(p2(F(float,1),F(float,2.0)) == 3.0)
+  doAssert(p2(F(float,1.0),F(float,2)) == 3.0)
+  doAssert(p2(F(float,1.0),F(float,2.0)) == 3.0)
+  #doAssert(p2(F(float,1),F(int,2.0)) == 3.0)
+
+block: # PR #23870
+  type
+    A {.inheritable.} = object
+    B = object of A
+    C = object of B
+
+  proc p[T: A](x: T): int = 0
+  proc p[T: B](x: T): int = 1
+
+  proc d(x: A): int = 0
+  proc d(x: B): int = 1
+  
+  proc g[T:A](x: typedesc[T]): int = 0
+  proc g[T: B](x: typedesc[T]): int = 1
+  
+  proc f[T](x: typedesc[T]): int = 0
+  proc f[T:B](x: typedesc[T]): int = 1
+
+  assert p(C()) == 1
+  assert d(C()) == 1
+  assert g(C) == 1
+  assert f(C) == 1
+
+block: # PR #23870
+  type
+    A = object of RootObj
+    PT = proc(ev: A) {.closure.}
+    sdt = seq[(PT, PT)]
+
+  proc encap() =
+    proc p(a: A) {.closure.} =
+      discard
+
+    var s: sdt
+    s.add (p, nil)
+
+  encap()
+
+block: # PR #23870
+  type
+    A = object of RootObj
+    B = object of A
+    C = object of B
+
+  proc p(a: B | RootObj): int =
+    0
+
+  proc p(a: A | A): int =
+    1
+
+  assert p(C()) == 0
+
+  proc d(a: RootObj | B): int =
+    0
+
+  proc d(a: A | A): int =
+    1
+
+  assert d(C()) == 0
diff --git a/tests/overload/tparam_forwarding.nim b/tests/overload/tparam_forwarding.nim
new file mode 100644
index 000000000..b0eea42c7
--- /dev/null
+++ b/tests/overload/tparam_forwarding.nim
@@ -0,0 +1,53 @@
+discard """
+output: '''baz
+10
+100
+1000
+a
+b
+c
+x: 1, y: test 1
+x: 2, y: test 2
+x: 10, y: test 3
+x: 4, y: test 4
+'''
+"""
+
+type
+  Foo = object
+    x: int
+
+proc stringVarargs*(strings: varargs[string, `$`]): void =
+  for s in strings: echo s
+
+proc fooVarargs*(foos: varargs[Foo]) =
+  for f in foos: echo f.x
+
+template templateForwarding*(callable: untyped,
+                             condition: bool,
+                             forwarded: varargs[untyped]): untyped =
+  if condition:
+    callable(forwarded)
+
+proc procForwarding(args: varargs[string]) =
+  stringVarargs(args)
+
+templateForwarding stringVarargs, 17 + 4 < 21, "foo", "bar", 100
+templateForwarding stringVarargs, 10 < 21, "baz"
+
+templateForwarding fooVarargs, "test".len > 3, Foo(x: 10), Foo(x: 100), Foo(x: 1000)
+
+procForwarding "a", "b", "c"
+
+proc hasKeywordArgs(x = 10, y = "y") =
+  echo "x: ", x, ", y: ", y
+
+proc hasRegularArgs(x: int, y: string) =
+  echo "x: ", x, ", y: ", y
+
+templateForwarding(hasRegularArgs, true, 1, "test 1")
+templateForwarding hasKeywordArgs, true, 2, "test 2"
+
+templateForwarding(hasKeywordArgs, true, y = "test 3")
+templateForwarding hasKeywordArgs, true, y = "test 4", x = 4
+
diff --git a/tests/overload/tproc_types_dont_like_subtypes.nim b/tests/overload/tproc_types_dont_like_subtypes.nim
new file mode 100644
index 000000000..6774be156
--- /dev/null
+++ b/tests/overload/tproc_types_dont_like_subtypes.nim
@@ -0,0 +1,20 @@
+discard """
+  errormsg: "got <B, proc (b: B){.closure, gcsafe.}>"
+  line: 20
+"""
+
+type
+  A = ref object of RootObj
+  B = ref object of A
+
+  P = proc (a: A)
+
+# bug #16325
+
+proc doThings(a: A, p: P) =
+  p(a)
+
+var x = proc (b: B) {.closure.} =
+  echo "B"
+
+doThings(B(), x)
diff --git a/tests/overload/tspec.nim b/tests/overload/tspec.nim
new file mode 100644
index 000000000..a84bac244
--- /dev/null
+++ b/tests/overload/tspec.nim
@@ -0,0 +1,118 @@
+discard """
+  output: '''not a var
+not a var
+a var
+B
+int
+T
+int16
+T
+ref T
+123
+2
+1
+@[123, 2, 1]
+Called!
+merge with var
+merge no var'''
+"""
+
+# Things that's even in the spec now!
+
+proc byvar(x: var int) = echo "a var"
+proc byvar(x: int) = echo "not a var"
+byvar(89)
+
+let letSym = 0
+var varSym = 13
+
+byvar(letSym)
+byvar(varSym)
+
+type
+  A = object of RootObj
+  B = object of A
+  C = object of B
+
+proc p(obj: A) =
+  echo "A"
+
+proc p(obj: B) =
+  echo "B"
+
+var c = C()
+# not ambiguous, calls 'B', not 'A' since B is a subtype of A
+# but not vice versa:
+p(c)
+
+proc pp(obj: A, obj2: B) = echo "A B"
+proc pp(obj: B, obj2: A) = echo "B A"
+
+# but this is ambiguous:
+#pp(c, c)
+
+proc takesInt(x: int) = echo "int"
+proc takesInt[T](x: T) = echo "T"
+proc takesInt(x: int16) = echo "int16"
+
+takesInt(4) # "int"
+var x: int32
+takesInt(x) # "T"
+var y: int16
+takesInt(y) # "int16"
+var z: range[0..4] = 0
+takesInt(z) # "T"
+
+proc gen[T](x: ref ref T) = echo "ref ref T"
+proc gen[T](x: ref T) = echo "ref T"
+proc gen[T](x: T) = echo "T"
+
+var ri: ref int
+gen(ri) # "ref T"
+
+
+template rem(x) = discard
+#proc rem[T](x: T) = discard
+
+rem unresolvedExpression(undeclaredIdentifier)
+
+
+proc takeV[T](a: varargs[T]) =
+  for x in a: echo x
+
+takeV([123, 2, 1]) # takeV's T is "int", not "array of int"
+echo(@[123, 2, 1])
+
+# bug #2600
+
+type
+  FutureBase* = ref object of RootObj ## Untyped future.
+
+  Future*[T] = ref object of FutureBase ## Typed future.
+    value: T ## Stored value
+
+  FutureVar*[T] = distinct Future[T]
+
+proc newFuture*[T](): Future[T] =
+  new(result)
+
+proc newFutureVar*[T](): FutureVar[T] =
+  result = FutureVar[T](newFuture[T]())
+
+proc mget*[T](future: FutureVar[T]): var T =
+  Future[T](future).value
+
+proc reset*[T](future: FutureVar[T]) =
+  echo "Called!"
+
+proc merge[T](x: Future[T]) = echo "merge no var"
+proc merge[T](x: var Future[T]) = echo "merge with var"
+
+when true:
+  var foo = newFutureVar[string]()
+  foo.mget() = ""
+  foo.mget.add("Foobar")
+  foo.reset()
+  var bar = newFuture[int]()
+  bar.merge # merge with var
+  merge(newFuture[int]()) # merge no var
diff --git a/tests/overload/tstatic_with_converter.nim b/tests/overload/tstatic_with_converter.nim
new file mode 100644
index 000000000..2bc1dfaab
--- /dev/null
+++ b/tests/overload/tstatic_with_converter.nim
@@ -0,0 +1,48 @@
+discard """
+output: '''
+9.0
+'''
+"""
+
+### bug #6773
+
+{.emit: """ /*INCLUDESECTION*/
+typedef double cimported;
+ 
+cimported set1_imported(double x) {
+  return x;
+}
+ 
+"""}
+ 
+type vfloat{.importc: "cimported".} = object
+ 
+proc set1(a: float): vfloat {.importc: "set1_imported".}
+ 
+converter scalar_to_vector(x: float): vfloat =
+  set1(x)
+ 
+proc sqrt(x: vfloat): vfloat =
+  x
+ 
+proc pow(x, y: vfloat): vfloat =
+  y
+ 
+proc `^`(x: vfloat, exp: static[int]): vfloat =
+  when exp == 0:
+    1.0
+  else:
+    x
+ 
+proc `^`(x: vfloat, exp: static[float]): vfloat =
+  when exp == 0.5:
+    sqrt(x)
+  else:
+    pow(x, exp)
+ 
+proc `$`(x: vfloat): string =
+  let y = cast[ptr float](addr x)
+  result = $y[]
+ 
+let x = set1(9.0)
+echo x^0.5
diff --git a/tests/overload/tsystemcmp.nim b/tests/overload/tsystemcmp.nim
new file mode 100644
index 000000000..aa761b759
--- /dev/null
+++ b/tests/overload/tsystemcmp.nim
@@ -0,0 +1,24 @@
+discard """
+  cmd: r"nim c --hints:on $options --threads:on $file"
+  output: '''@["", "a", "ha", "hi", "ho", "huu"]'''
+"""
+
+import algorithm
+
+# bug #1657
+var modules = @["hi", "ho", "", "a", "ha", "huu"]
+sort(modules, system.cmp)
+echo modules
+
+type
+  MyType = object
+    x: string
+
+proc cmp(a, b: MyType): int = cmp(a.x, b.x)
+
+var modulesB = @[MyType(x: "ho"), MyType(x: "ha")]
+sort(modulesB, cmp)
+
+# bug #2397
+
+proc f(x: (proc(a,b: string): int) = system.cmp) = discard
diff --git a/tests/overload/tuntypedarg.nim b/tests/overload/tuntypedarg.nim
new file mode 100644
index 000000000..9aa4fad3b
--- /dev/null
+++ b/tests/overload/tuntypedarg.nim
@@ -0,0 +1,19 @@
+import macros
+
+block: # issue #7385
+  type CustomSeq[T] = object
+    data: seq[T]
+  macro `[]`[T](s: CustomSeq[T], args: varargs[untyped]): untyped =
+    ## The end goal is to replace the joker "_" by something else
+    result = newIntLitNode(10)
+  proc foo1(): CustomSeq[int] =
+    result.data.newSeq(10)
+    # works since no overload matches first argument with type `CustomSeq`
+    # except magic `[]`, which always matches without checking arguments
+    doAssert result[_] == 10
+  doAssert foo1() == CustomSeq[int](data: newSeq[int](10))
+  proc foo2[T](): CustomSeq[T] =
+    result.data.newSeq(10)
+    # works fine with generic return type
+    doAssert result[_] == 10
+  doAssert foo2[int]() == CustomSeq[int](data: newSeq[int](10))
diff --git a/tests/overload/tvartypeclass.nim b/tests/overload/tvartypeclass.nim
new file mode 100644
index 000000000..04f3f5a91
--- /dev/null
+++ b/tests/overload/tvartypeclass.nim
@@ -0,0 +1,11 @@
+# issue #13302
+
+proc foo(x: object): int = x.i*2
+proc foo(x: var object) = x.i*=2
+type Foo = object
+  i: int
+let x = Foo(i: 3)
+var y = Foo(i: 4)
+doAssert foo(x) == 6
+foo(y)
+doAssert y.i == 8
diff --git a/tests/overload/tvaruintconv.nim b/tests/overload/tvaruintconv.nim
new file mode 100644
index 000000000..87ebd285d
--- /dev/null
+++ b/tests/overload/tvaruintconv.nim
@@ -0,0 +1,207 @@
+discard """
+  action: compile
+"""
+
+# https://github.com/status-im/nimbus-eth2/pull/6554#issuecomment-2354977102
+# failed with "for a 'var' type a variable needs to be passed; but 'uint64(result)' is immutable"
+
+import
+  std/[typetraits, macros]
+
+type
+  DefaultFlavor = object
+
+template serializationFormatImpl(Name: untyped) {.dirty.} =
+  type Name = object
+
+template serializationFormat(Name: untyped) =
+  serializationFormatImpl(Name)
+
+template setReader(Format, FormatReader: distinct type) =
+  when arity(FormatReader) > 1:
+    template Reader(T: type Format, F: distinct type = DefaultFlavor): type = FormatReader[F]
+  else:
+    template ReaderType(T: type Format): type = FormatReader
+    template Reader(T: type Format): type = FormatReader
+
+template useDefaultReaderIn(T: untyped, Flavor: type) =
+  mixin Reader
+
+  template readValue(r: var Reader(Flavor), value: var T) =
+    mixin readRecordValue
+    readRecordValue(r, value)
+
+import mvaruintconv
+
+type
+  FieldTag[RecordType: object; fieldName: static string] = distinct void
+
+func declval*(T: type): T {.compileTime.} =
+  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`
+
+    let field =
+      if isSymbol:
+        quote do: declval(`T`).`fieldIdent`
+      else:
+        quote do: declval(`T`)[`fieldIndex`]
+
+    result.add quote do:
+      block:
+        `fieldNameDefs`
+
+        template FieldType: untyped {.inject, used.} = typeof(`field`)
+
+        `body`
+
+  # echo repr(result)
+
+template enumAllSerializedFields(T: type, body): untyped =
+  enumAllSerializedFieldsImpl(T, body)
+
+type
+  FieldReader[RecordType, Reader] = tuple[
+    fieldName: string,
+    reader: proc (rec: var RecordType, reader: var Reader)
+                 {.gcsafe, nimcall.}
+  ]
+
+proc totalSerializedFieldsImpl(T: type): int =
+  mixin enumAllSerializedFields
+  enumAllSerializedFields(T): inc result
+
+template totalSerializedFields(T: type): int =
+  (static(totalSerializedFieldsImpl(T)))
+
+template GetFieldType(FT: type FieldTag): type =
+  typeof field(declval(FT.RecordType), FT.fieldName)
+
+proc makeFieldReadersTable(RecordType, ReaderType: distinct type,
+                           numFields: static[int]):
+                           array[numFields, FieldReader[RecordType, ReaderType]] =
+  mixin enumAllSerializedFields, handleReadException
+  var idx = 0
+
+  enumAllSerializedFields(RecordType):
+    proc readField(obj: var RecordType, reader: var ReaderType)
+                  {.gcsafe, nimcall.} =
+
+      mixin readValue
+
+      type F = FieldTag[RecordType, realFieldName]
+      field(obj, realFieldName) = reader.readValue(GetFieldType(F))
+
+    result[idx] = (fieldName, readField)
+    inc idx
+
+proc fieldReadersTable(RecordType, ReaderType: distinct type): auto =
+  mixin readValue
+  type T = RecordType
+  const numFields = totalSerializedFields(T)
+  var tbl {.threadvar.}: ref array[numFields, FieldReader[RecordType, ReaderType]]
+  if tbl == nil:
+    tbl = new typeof(tbl)
+    tbl[] = makeFieldReadersTable(RecordType, ReaderType, numFields)
+  return addr(tbl[])
+
+proc readValue(reader: var auto, T: type): T =
+  mixin readValue
+  reader.readValue(result)
+
+template decode(Format: distinct type,
+                 input: string,
+                 RecordType: distinct type): auto =
+  mixin Reader
+  block:  # https://github.com/nim-lang/Nim/issues/22874
+    var reader: Reader(Format)
+    reader.readValue(RecordType)
+
+template readValue(Format: type,
+                    ValueType: type): untyped =
+  mixin Reader, init, readValue
+  var reader: Reader(Format)
+  readValue reader, ValueType
+
+template parseArrayImpl(numElem: untyped,
+                        actionValue: untyped) =
+  actionValue
+
+serializationFormat Json
+template createJsonFlavor(FlavorName: untyped,
+                           skipNullFields = false) {.dirty.} =
+  type FlavorName = object
+
+  template Reader(T: type FlavorName): type = Reader(Json, FlavorName)
+type
+  JsonReader[Flavor = DefaultFlavor] = object
+
+Json.setReader JsonReader
+
+template parseArray(r: var JsonReader; body: untyped) =
+  parseArrayImpl(idx): body
+
+template parseArray(r: var JsonReader; idx: untyped; body: untyped) =
+  parseArrayImpl(idx): body
+
+proc readRecordValue[T](r: var JsonReader, value: var T) =
+  type
+    ReaderType {.used.} = type r
+    T = type value
+
+  discard T.fieldReadersTable(ReaderType)
+
+proc readValue[T](r: var JsonReader, value: var T) =
+  mixin readValue
+
+  when value is seq:
+    r.parseArray:
+      readValue(r, value[0])
+
+  elif value is object:
+    readRecordValue(r, value)
+
+type
+  RemoteSignerInfo = object
+    id: uint32
+  RemoteKeystore = object
+
+proc readValue(reader: var JsonReader, value: var RemoteKeystore) =
+  discard reader.readValue(seq[RemoteSignerInfo])
+
+createJsonFlavor RestJson
+useDefaultReaderIn(RemoteSignerInfo, RestJson)
+proc readValue(reader: var JsonReader[RestJson], value: var uint64) =
+  discard reader.readValue(string)
+
+discard Json.decode("", RemoteKeystore)
+block:  # https://github.com/nim-lang/Nim/issues/22874
+  var reader: Reader(RestJson)
+  discard reader.readValue(RemoteSignerInfo)