diff options
Diffstat (limited to 'tests/concepts')
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" + |