diff options
Diffstat (limited to 'tests/generics')
114 files changed, 6881 insertions, 0 deletions
diff --git a/tests/generics/m14509.nim b/tests/generics/m14509.nim new file mode 100644 index 000000000..cabc4f308 --- /dev/null +++ b/tests/generics/m14509.nim @@ -0,0 +1,16 @@ +import macros + +type float32x4 = array[4, float32] +type float32x8 = array[8, float32] + +{.experimental: "dynamicBindSym".} +macro dispatch(N: static int, T: type SomeNumber): untyped = + let BaseT = getTypeInst(T)[1] + result = bindSym($BaseT & "x" & $N) + +type + VecIntrin*[N: static int, T: SomeNumber] = dispatch(N, T) + +func `$`*[N, T](vec: VecIntrin[N, T]): string = + ## Display a vector + $cast[array[N, T]](vec) diff --git a/tests/generics/m22373a.nim b/tests/generics/m22373a.nim new file mode 100644 index 000000000..28e087ca6 --- /dev/null +++ b/tests/generics/m22373a.nim @@ -0,0 +1,7 @@ +# module a for t22373 + +# original: +type LightClientHeader* = object + +# simplified: +type TypeOrTemplate* = object diff --git a/tests/generics/m22373b.nim b/tests/generics/m22373b.nim new file mode 100644 index 000000000..67ee4211b --- /dev/null +++ b/tests/generics/m22373b.nim @@ -0,0 +1,18 @@ +# module b for t22373 + +import m22373a + +# original: +type + LightClientDataFork* {.pure.} = enum + None = 0, + Altair = 1 +template LightClientHeader*(kind: static LightClientDataFork): auto = + when kind == LightClientDataFork.Altair: + typedesc[m22373a.LightClientHeader] + else: + static: raiseAssert "Unreachable" + +# simplified: +template TypeOrTemplate*(num: int): untyped = + typedesc[m22373a.TypeOrTemplate] diff --git a/tests/generics/m3770.nim b/tests/generics/m3770.nim new file mode 100644 index 000000000..7f5714a2b --- /dev/null +++ b/tests/generics/m3770.nim @@ -0,0 +1,11 @@ +type + Noice* = object + hidden: int + +template jjj*: Noice = + Noice(hidden: 15) + +type Opt* = object + o: int + +template none*(O: type Opt): Opt = Opt(o: 0) diff --git a/tests/generics/mbind_bracket.nim b/tests/generics/mbind_bracket.nim new file mode 100644 index 000000000..4bf18b471 --- /dev/null +++ b/tests/generics/mbind_bracket.nim @@ -0,0 +1,17 @@ + +import tables + +type + UUIDObject* = ref object + uuid: string + + Registry*[T] = ref object + objects: Table[string, T] + +proc newRegistry*[T](): Registry[T] = + result = Registry[T]() + result.objects = initTable[string, T](128) + +proc register*[T](self: Registry[T], obj: T) = + self.objects[obj.uuid] = obj + diff --git a/tests/generics/mclosed_sym.nim b/tests/generics/mclosed_sym.nim new file mode 100644 index 000000000..bcccd9a85 --- /dev/null +++ b/tests/generics/mclosed_sym.nim @@ -0,0 +1,10 @@ + +type R* = object + +type Data*[T] = object + d*: T + +proc same(r:R, d:int) = echo "TEST2" + +proc doIt*(d:Data, r:R) = + r.same(1) # Expecting this to invoke the local `same()` method diff --git a/tests/generics/mdotlookup.nim b/tests/generics/mdotlookup.nim new file mode 100644 index 000000000..090b97771 --- /dev/null +++ b/tests/generics/mdotlookup.nim @@ -0,0 +1,28 @@ +proc baz(o: auto): int = 5 # if bar is exported, it works + +type MyObj = object + x: int + +proc foo*(b: auto) = + var o: MyObj + echo b.baz, " ", o.x.baz, " ", b.baz() + +import sets + +var intset = initHashSet[int]() + +proc fn*[T](a: T) = + if a in intset: echo("true") + else: echo("false") + +import strutils + +proc doStrip*[T](a: T): string = + result = ($a).strip() + +type Foo = int32 +proc baz2*[T](y: int): auto = + result = y.Foo + +proc set*(x: var int, a, b: string) = + x = a.len + b.len diff --git a/tests/generics/mfriends.nim b/tests/generics/mfriends.nim new file mode 100644 index 000000000..19672289e --- /dev/null +++ b/tests/generics/mfriends.nim @@ -0,0 +1,11 @@ + +type + TMyObj = object + x: int + +proc gen*[T](): T = + var d: TMyObj + # access private field here + d.x = 3 + result = d.x + diff --git a/tests/generics/mmodule_same_as_proc.nim b/tests/generics/mmodule_same_as_proc.nim new file mode 100644 index 000000000..048b98336 --- /dev/null +++ b/tests/generics/mmodule_same_as_proc.nim @@ -0,0 +1,2 @@ + +proc mmodule_same_as_proc*(x: string) = discard diff --git a/tests/generics/module_with_generics.nim b/tests/generics/module_with_generics.nim new file mode 100644 index 000000000..960a694d7 --- /dev/null +++ b/tests/generics/module_with_generics.nim @@ -0,0 +1,14 @@ +type + Base[T] {.inheritable.} = ref object + value*: T + + Derived[T] = ref object of Base[T] + derivedValue*: T + +proc makeDerived*[T](v: T): Derived[T] = + new result + result.value = v + +proc setBaseValue*[T](a: Base[T], value: T) = + a.value = value + diff --git a/tests/generics/mopensymimport1.nim b/tests/generics/mopensymimport1.nim new file mode 100644 index 000000000..912db1302 --- /dev/null +++ b/tests/generics/mopensymimport1.nim @@ -0,0 +1,34 @@ +type + Result*[T, E] = object + when T is void: + when E is void: + oResultPrivate*: bool + else: + case oResultPrivate*: bool + of false: + eResultPrivate*: E + of true: + discard + else: + when E is void: + case oResultPrivate*: bool + of false: + discard + of true: + vResultPrivate*: T + else: + case oResultPrivate*: bool + of false: + eResultPrivate*: E + of true: + vResultPrivate*: T + +template valueOr*[T: not void, E](self: Result[T, E], def: untyped): untyped = + let s = (self) # TODO avoid copy + case s.oResultPrivate + of true: + s.vResultPrivate + of false: + when E isnot void: + template error: untyped {.used, inject.} = s.eResultPrivate + def diff --git a/tests/generics/mopensymimport2.nim b/tests/generics/mopensymimport2.nim new file mode 100644 index 000000000..c17aafd00 --- /dev/null +++ b/tests/generics/mopensymimport2.nim @@ -0,0 +1,16 @@ +{.experimental: "openSym".} + +import mopensymimport1 + +type Xxx = enum + error + value + +proc f(): Result[int, cstring] = + Result[int, cstring](oResultPrivate: false, eResultPrivate: "f") + +proc g*(T: type): string = + let x = f().valueOr: + return $error + + "ok" diff --git a/tests/generics/moverloading_typedesc.nim b/tests/generics/moverloading_typedesc.nim new file mode 100644 index 000000000..92805fb7b --- /dev/null +++ b/tests/generics/moverloading_typedesc.nim @@ -0,0 +1,11 @@ +import tables + +type + FFoo* = object + FBar* = object + +proc new*(_: typedesc[FFoo]): int = 2 +proc new*[T](_: typedesc[T]): int = 3 +proc new*(_: typedesc): int = 4 +proc new*(_: typedesc[seq[Table[int, seq[Table[int, string]]]]]): int = 5 +proc new*(_: typedesc[seq[Table[int, seq[Table[int, typedesc]]]]]): int = 6 diff --git a/tests/generics/mtypenodes.nim b/tests/generics/mtypenodes.nim new file mode 100644 index 000000000..e1132241b --- /dev/null +++ b/tests/generics/mtypenodes.nim @@ -0,0 +1,6 @@ +# issue #22699 + +type Private = distinct int + +proc chop*[T](x: int): int = + cast[int](cast[tuple[field: Private]](x)) diff --git a/tests/generics/muninstantiatedgenericcalls.nim b/tests/generics/muninstantiatedgenericcalls.nim new file mode 100644 index 000000000..caed07c98 --- /dev/null +++ b/tests/generics/muninstantiatedgenericcalls.nim @@ -0,0 +1,26 @@ +import std/bitops + +const + lengths = block: + var v: array[64, int8] + for i in 0..<64: + v[i] = int8((i + 7) div 7) + v + +type + Leb128* = object + +{.push checks: off.} +func len(T: type Leb128, x: SomeUnsignedInt): int8 = + if x == 0: 1 + else: lengths[fastLog2(x)] +{.pop.} + +# note private to test scoping issue: +func maxLen(T: type Leb128, I: type): int8 = + Leb128.len(I.high) + +type + Leb128Buf*[T: SomeUnsignedInt] = object + data*: array[maxLen(Leb128, T), byte] + len*: int8 diff --git a/tests/generics/t12938.nim b/tests/generics/t12938.nim new file mode 100644 index 000000000..e09d65c7a --- /dev/null +++ b/tests/generics/t12938.nim @@ -0,0 +1,9 @@ +type + ExampleArray[Size, T] = array[Size, T] + +var integerArray: ExampleArray[32, int] # Compiler crash! +doAssert integerArray.len == 32 + +const Size = 2 +var integerArray2: ExampleArray[Size, int] +doAssert integerArray2.len == 2 diff --git a/tests/generics/t13525.nim b/tests/generics/t13525.nim new file mode 100644 index 000000000..1fd84852b --- /dev/null +++ b/tests/generics/t13525.nim @@ -0,0 +1,6 @@ +# https://github.com/nim-lang/Nim/issues/13524 +template fun(field): untyped = astToStr(field) +proc test1(): string = fun(nonexistent1) +proc test2[T](): string = fun(nonexistent2) # used to cause: Error: undeclared identifier: 'nonexistent2' +doAssert test1() == "nonexistent1" +doAssert test2[int]() == "nonexistent2" diff --git a/tests/generics/t14193.nim b/tests/generics/t14193.nim new file mode 100644 index 000000000..213b1a8e6 --- /dev/null +++ b/tests/generics/t14193.nim @@ -0,0 +1,6 @@ +type + Task*[N: int] = object + env*: array[N, byte] + +var task14193: Task[20] +doAssert task14193.env.len == 20 diff --git a/tests/generics/t14509.nim b/tests/generics/t14509.nim new file mode 100644 index 000000000..ef3143ee4 --- /dev/null +++ b/tests/generics/t14509.nim @@ -0,0 +1,4 @@ +import m14509 + +var v: VecIntrin[4, float32] +doAssert $v == "[0.0, 0.0, 0.0, 0.0]" diff --git a/tests/generics/t1500.nim b/tests/generics/t1500.nim new file mode 100644 index 000000000..6dd457d33 --- /dev/null +++ b/tests/generics/t1500.nim @@ -0,0 +1,8 @@ +#issue 1500 + +type + TFtpBase*[SockType] = object + job: TFTPJob[SockType] + PFtpBase*[SockType] = ref TFtpBase[SockType] + TFtpClient* = TFtpBase[string] + TFTPJob[T] = object \ No newline at end of file diff --git a/tests/generics/t17509.nim b/tests/generics/t17509.nim new file mode 100644 index 000000000..89f507577 --- /dev/null +++ b/tests/generics/t17509.nim @@ -0,0 +1,25 @@ +type List[O] = object + next: ptr List[O] + +proc initList[O](l: ptr List[O]) = + l[].next = l + +type + PolytopeVertex[R] = object + list: List[PolytopeVertex[R]] + + PolytopeEdge[R] = object + list: List[PolytopeEdge[R]] + + Polytope[R] = object + vertices: List[PolytopeVertex[R]] + edges: List[PolytopeEdge[R]] + +var pt: Polytope[float] + +static: + doAssert pt.vertices.next is (ptr List[PolytopeVertex[float]]) + doAssert pt.edges.next is (ptr List[PolytopeEdge[float]]) + +initList(addr pt.vertices) +initList(addr pt.edges) \ No newline at end of file diff --git a/tests/generics/t18823.nim b/tests/generics/t18823.nim new file mode 100644 index 000000000..94c79aebe --- /dev/null +++ b/tests/generics/t18823.nim @@ -0,0 +1,6 @@ +type BitsRange[T] = range[0..sizeof(T)*8-1] + +proc bar[T](a: T; b: BitsRange[T]) = + discard + +bar(1, 2.Natural) diff --git a/tests/generics/t18859.nim b/tests/generics/t18859.nim new file mode 100644 index 000000000..ca6c3d10b --- /dev/null +++ b/tests/generics/t18859.nim @@ -0,0 +1,17 @@ +import macros + +macro symFromDesc(T: typedesc): untyped = + let typ = getType(T) + typ[1] + +template produceType(T: typedesc): untyped = + type + XT = object + x: symFromDesc(T) + + XT + +type + X[T] = produceType(T) + +var x: X[int] diff --git a/tests/generics/t19848.nim b/tests/generics/t19848.nim new file mode 100644 index 000000000..f80f0e298 --- /dev/null +++ b/tests/generics/t19848.nim @@ -0,0 +1,16 @@ +discard """ + output: ''' +todo +''' +""" + +type + Maybe[T] = object + List[T] = object + +proc dump[M: Maybe](a: List[M]) = + echo "todo" + +var a: List[Maybe[int]] + +dump(a) diff --git a/tests/generics/t20996.nim b/tests/generics/t20996.nim new file mode 100644 index 000000000..8aa83c4e2 --- /dev/null +++ b/tests/generics/t20996.nim @@ -0,0 +1,15 @@ +discard """ + action: compile +""" + +import std/macros + +macro matchMe(x: typed): untyped = + discard x.getTypeImpl + +type + ElementRT = object + Element[Z] = ElementRT # this version is needed, even though we don't use it + +let ar = ElementRT() +matchMe(ar) diff --git a/tests/generics/t21742.nim b/tests/generics/t21742.nim new file mode 100644 index 000000000..c49c8ee97 --- /dev/null +++ b/tests/generics/t21742.nim @@ -0,0 +1,10 @@ +type + Foo[T] = object + x:T + Bar[T,R] = Foo[T] + Baz = Bar[int,float] + +proc qux[T,R](x: Bar[T,R]) = discard + +var b:Baz +b.qux() \ No newline at end of file diff --git a/tests/generics/t21760.nim b/tests/generics/t21760.nim new file mode 100644 index 000000000..5343279bb --- /dev/null +++ b/tests/generics/t21760.nim @@ -0,0 +1,8 @@ +import std/tables + +type Url = object + +proc myInit(_: type[Url], params = default(Table[string, string])): Url = + discard + +discard myInit(Url) \ No newline at end of file diff --git a/tests/generics/t21958.nim b/tests/generics/t21958.nim new file mode 100644 index 000000000..f566b57cb --- /dev/null +++ b/tests/generics/t21958.nim @@ -0,0 +1,11 @@ +discard """ + action: compile +""" + +type + Ct*[T: SomeUnsignedInt] = distinct T + +template `shr`*[T: Ct](x: T, y: SomeInteger): T = T(T.T(x) shr y) + +var x: Ct[uint64] +let y {.used.} = x shr 2 \ No newline at end of file diff --git a/tests/generics/t22373.nim b/tests/generics/t22373.nim new file mode 100644 index 000000000..ecfaf0f1b --- /dev/null +++ b/tests/generics/t22373.nim @@ -0,0 +1,16 @@ +# issue #22373 + +import m22373a +import m22373b + +# original: +template lazy_header(name: untyped): untyped {.dirty.} = + var `name _ ptr`: ptr[data_fork.LightClientHeader] # this data_fork.Foo part seems required to reproduce +proc createLightClientUpdates(data_fork: static LightClientDataFork) = + lazy_header(attested_header) +createLightClientUpdates(LightClientDataFork.Altair) + +# simplified: +proc generic[T](abc: T) = + var x: abc.TypeOrTemplate +generic(123) diff --git a/tests/generics/t22826.nim b/tests/generics/t22826.nim new file mode 100644 index 000000000..914d4243a --- /dev/null +++ b/tests/generics/t22826.nim @@ -0,0 +1,8 @@ +import std/tables + +var a: Table[string, float] + +type Value*[T] = object + table: Table[string, Value[T]] + +discard toTable({"a": Value[float]()}) \ No newline at end of file diff --git a/tests/generics/t23186.nim b/tests/generics/t23186.nim new file mode 100644 index 000000000..76f38da6b --- /dev/null +++ b/tests/generics/t23186.nim @@ -0,0 +1,155 @@ +# issue #23186 + +block: # simplified + template typedTempl(x: int, body): untyped = + body + proc generic1[T]() = + discard + proc generic2[T]() = + typedTempl(1): + let x = generic1[T] + generic2[int]() + +import std/macros + +when not compiles(len((1, 2))): + import std/typetraits + + func len(x: tuple): int = + arity(type(x)) + +block: # full issue example + type FieldDescription = object + name: NimNode + func isTuple(t: NimNode): bool = + t.kind == nnkBracketExpr and t[0].kind == nnkSym and eqIdent(t[0], "tuple") + proc collectFieldsFromRecList(result: var seq[FieldDescription], + n: NimNode, + parentCaseField: NimNode = nil, + parentCaseBranch: NimNode = nil, + isDiscriminator = false) = + case n.kind + of nnkRecList: + for entry in n: + collectFieldsFromRecList result, entry, + parentCaseField, parentCaseBranch + of nnkIdentDefs: + for i in 0 ..< n.len - 2: + var field: FieldDescription + field.name = n[i] + if field.name.kind == nnkPragmaExpr: + field.name = field.name[0] + if field.name.kind == nnkPostfix: + field.name = field.name[1] + result.add field + of nnkNilLit, nnkDiscardStmt, nnkCommentStmt, nnkEmpty: + discard + else: + doAssert false, "Unexpected nodes in recordFields:\n" & n.treeRepr + proc collectFieldsInHierarchy(result: var seq[FieldDescription], + objectType: NimNode) = + var objectType = objectType + if objectType.kind == nnkRefTy: + objectType = objectType[0] + let recList = objectType[2] + collectFieldsFromRecList result, recList + proc recordFields(typeImpl: NimNode): seq[FieldDescription] = + let objectType = case typeImpl.kind + of nnkObjectTy: typeImpl + of nnkTypeDef: typeImpl[2] + else: + macros.error("object type expected", typeImpl) + return + collectFieldsInHierarchy(result, objectType) + proc skipPragma(n: NimNode): NimNode = + if n.kind == nnkPragmaExpr: n[0] + else: n + func declval(T: type): T = + doAssert false, + "declval should be used only in `typeof` expressions and concepts" + default(ptr T)[] + macro enumAllSerializedFieldsImpl(T: type, body: untyped): untyped = + var typeAst = getType(T)[1] + var typeImpl: NimNode + let isSymbol = not typeAst.isTuple + if not isSymbol: + typeImpl = typeAst + else: + typeImpl = getImpl(typeAst) + result = newStmtList() + var i = 0 + for field in recordFields(typeImpl): + let + fieldIdent = field.name + realFieldName = newLit($fieldIdent.skipPragma) + fieldName = realFieldName + fieldIndex = newLit(i) + let fieldNameDefs = + if isSymbol: + quote: + const fieldName {.inject, used.} = `fieldName` + const realFieldName {.inject, used.} = `realFieldName` + else: + quote: + const fieldName {.inject, used.} = $`fieldIndex` + const realFieldName {.inject, used.} = $`fieldIndex` + # we can't access .Fieldn, so our helper knows + # to parseInt this + let field = + if isSymbol: + quote do: declval(`T`).`fieldIdent` + else: + quote do: declval(`T`)[`fieldIndex`] + result.add quote do: + block: + `fieldNameDefs` + type FieldType {.inject, used.} = type(`field`) + `body` + i += 1 + template enumAllSerializedFields(T: type, body): untyped = + when T is ref|ptr: + type TT = type(default(T)[]) + enumAllSerializedFieldsImpl(TT, body) + else: + enumAllSerializedFieldsImpl(T, body) + type + MemRange = object + startAddr: ptr byte + length: int + SszNavigator[T] = object + m: MemRange + func sszMount(data: openArray[byte], T: type): SszNavigator[T] = + let startAddr = unsafeAddr data[0] + SszNavigator[T](m: MemRange(startAddr: startAddr, length: data.len)) + func sszMount(data: openArray[char], T: type): SszNavigator[T] = + let startAddr = cast[ptr byte](unsafeAddr data[0]) + SszNavigator[T](m: MemRange(startAddr: startAddr, length: data.len)) + template sszMount(data: MemRange, T: type): SszNavigator[T] = + SszNavigator[T](m: data) + func navigateToField[T]( + n: SszNavigator[T], + FieldType: type): SszNavigator[FieldType] = + default(SszNavigator[FieldType]) + type + FieldInfo = ref object + navigator: proc (m: MemRange): MemRange {. + gcsafe, noSideEffect, raises: [IOError] .} + func fieldNavigatorImpl[RecordType; FieldType; fieldName: static string]( + m: MemRange): MemRange = + var typedNavigator = sszMount(m, RecordType) + discard navigateToField(typedNavigator, FieldType) + default(MemRange) + func genTypeInfo(T: type) = + when T is object: + enumAllSerializedFields(T): + discard FieldInfo(navigator: fieldNavigatorImpl[T, FieldType, fieldName]) + type + Foo = object + bar: Bar + BarList = seq[uint64] + Bar = object + b: BarList + baz: Baz + Baz = object + i: uint64 + genTypeInfo(Foo) diff --git a/tests/generics/t23790.nim b/tests/generics/t23790.nim new file mode 100644 index 000000000..9ac0df6a1 --- /dev/null +++ b/tests/generics/t23790.nim @@ -0,0 +1,14 @@ +# bug #23790 + +discard compiles($default(seq[seq[ref int]])) +discard compiles($default(seq[seq[ref uint]])) +discard compiles($default(seq[seq[ref int8]])) +discard compiles($default(seq[seq[ref uint8]])) +discard compiles($default(seq[seq[ref int16]])) +discard compiles($default(seq[seq[ref uint16]])) +discard compiles($default(seq[seq[ref int32]])) +discard compiles($default(seq[seq[ref uint32]])) +discard compiles($default(seq[seq[ref int64]])) +discard compiles($default(seq[seq[ref uint64]])) +proc s(_: int | string) = discard +s(0) diff --git a/tests/generics/t23853.nim b/tests/generics/t23853.nim new file mode 100644 index 000000000..bc9514a53 --- /dev/null +++ b/tests/generics/t23853.nim @@ -0,0 +1,91 @@ +# issue #23853 + +block simplified: + type QuadraticExt[F] = object + coords: array[2, F] + template Name(E: type QuadraticExt): int = 123 + template getBigInt(Name: static int): untyped = int + type Foo[GT] = object + a: getBigInt(GT.Name) + var x: Foo[QuadraticExt[int]] + +import std/macros + +type + Algebra* = enum + BN254_Snarks + BLS12_381 + + Fp*[Name: static Algebra] = object + limbs*: array[4, uint64] + + QuadraticExt*[F] = object + ## Quadratic Extension field + coords*: array[2, F] + + CubicExt*[F] = object + ## Cubic Extension field + coords*: array[3, F] + + ExtensionField*[F] = QuadraticExt[F] or CubicExt[F] + + Fp2*[Name: static Algebra] = + QuadraticExt[Fp[Name]] + + Fp4*[Name: static Algebra] = + QuadraticExt[Fp2[Name]] + + Fp6*[Name: static Algebra] = + CubicExt[Fp2[Name]] + + Fp12*[Name: static Algebra] = + CubicExt[Fp4[Name]] + # QuadraticExt[Fp6[Name]] + +template Name*(E: type ExtensionField): Algebra = + E.F.Name + +const BLS12_381_Order = [uint64 0x1, 0x2, 0x3, 0x4] +const BLS12_381_Modulus = [uint64 0x5, 0x6, 0x7, 0x8] + + +{.experimental: "dynamicBindSym".} + +macro baseFieldModulus*(Name: static Algebra): untyped = + result = bindSym($Name & "_Modulus") + +macro scalarFieldModulus*(Name: static Algebra): untyped = + result = bindSym($Name & "_Order") + +type FieldKind* = enum + kBaseField + kScalarField + +template getBigInt*(Name: static Algebra, kind: static FieldKind): untyped = + # Workaround: + # in `ptr UncheckedArray[BigInt[EC.getScalarField().bits()]] + # EC.getScalarField is not accepted by the compiler + # + # and `ptr UncheckedArray[BigInt[Fr[EC.F.Name].bits]]` gets undeclared field: 'Name' + # + # but `ptr UncheckedArray[getBigInt(EC.getName(), kScalarField)]` works fine + when kind == kBaseField: + Name.baseFieldModulus().typeof() + else: + Name.scalarFieldModulus().typeof() + +# ------------------------------------------------------------------------------ + +type BenchMultiexpContext*[GT] = object + elems: seq[GT] + exponents: seq[getBigInt(GT.Name, kScalarField)] + +proc createBenchMultiExpContext*(GT: typedesc, inputSizes: openArray[int]): BenchMultiexpContext[GT] = + discard + +# ------------------------------------------------------------------------------ + +proc main() = + let ctx = createBenchMultiExpContext(Fp12[BLS12_381], [2, 4, 8, 16]) + +main() diff --git a/tests/generics/t23854.nim b/tests/generics/t23854.nim new file mode 100644 index 000000000..f1175c8b2 --- /dev/null +++ b/tests/generics/t23854.nim @@ -0,0 +1,71 @@ +# issue #23854, not entirely fixed + +import std/bitops + +const WordBitWidth = sizeof(pointer) * 8 + +func wordsRequired*(bits: int): int {.inline.} = + const divShiftor = fastLog2(uint32(WordBitWidth)) + result = (bits + WordBitWidth - 1) shr divShiftor + +type + Algebra* = enum + BLS12_381 + + BigInt*[bits: static int] = object + limbs*: array[wordsRequired(bits), uint] + + Fr*[Name: static Algebra] = object + residue_form*: BigInt[255] + + Fp*[Name: static Algebra] = object + residue_form*: BigInt[381] + + FF*[Name: static Algebra] = Fp[Name] or Fr[Name] + +template getBigInt*[Name: static Algebra](T: type FF[Name]): untyped = + ## Get the underlying BigInt type. + typeof(default(T).residue_form) + +type + EC_ShortW_Aff*[F] = object + ## Elliptic curve point for a curve in Short Weierstrass form + ## y² = x³ + a x + b + ## + ## over a field F + x*, y*: F + +type FieldKind* = enum + kBaseField + kScalarField + +func bits*[Name: static Algebra](T: type FF[Name]): static int = + T.getBigInt().bits + +template getScalarField*(EC: type EC_ShortW_Aff): untyped = + Fr[EC.F.Name] + +# ------------------------------------------------------------------------------ + +type + ECFFT_Descriptor*[EC] = object + ## Metadata for FFT on Elliptic Curve + order*: int + rootsOfUnity1*: ptr UncheckedArray[BigInt[EC.getScalarField().bits()]] # Error: in expression 'EC.getScalarField()': identifier expected, but found 'EC.getScalarField' + rootsOfUnity2*: ptr UncheckedArray[BigInt[getScalarField(EC).bits()]] # Compiler SIGSEGV: Illegal Storage Access + +func new*(T: type ECFFT_Descriptor): T = + discard + +# ------------------------------------------------------------------------------ + +template getBits[bits: static int](x: ptr UncheckedArray[BigInt[bits]]): int = bits + +proc main() = + let ctx = ECFFT_Descriptor[EC_ShortW_Aff[Fp[BLS12_381]]].new() + doAssert getBits(ctx.rootsOfUnity1) == 255 + doAssert getBits(ctx.rootsOfUnity2) == 255 + doAssert ctx.rootsOfUnity1[0].limbs.len == wordsRequired(255) + doAssert ctx.rootsOfUnity2[0].limbs.len == wordsRequired(255) + +main() diff --git a/tests/generics/t23855.nim b/tests/generics/t23855.nim new file mode 100644 index 000000000..da8135a98 --- /dev/null +++ b/tests/generics/t23855.nim @@ -0,0 +1,61 @@ +# issue #23855, not entirely fixed + +import std/bitops + +const WordBitWidth = sizeof(pointer) * 8 + +func wordsRequired*(bits: int): int {.inline.} = + const divShiftor = fastLog2(uint32(WordBitWidth)) + result = (bits + WordBitWidth - 1) shr divShiftor + +type + Algebra* = enum + BLS12_381 + + BigInt*[bits: static int] = object + limbs*: array[wordsRequired(bits), uint] + + Fr*[Name: static Algebra] = object + residue_form*: BigInt[255] + + Fp*[Name: static Algebra] = object + residue_form*: BigInt[381] + + FF*[Name: static Algebra] = Fp[Name] or Fr[Name] + +template getBigInt*[Name: static Algebra](T: type FF[Name]): untyped = + ## Get the underlying BigInt type. + typeof(default(T).residue_form) + +type + EC_ShortW_Aff*[F] = object + ## Elliptic curve point for a curve in Short Weierstrass form + ## y² = x³ + a x + b + ## + ## over a field F + x*, y*: F + +func bits*[Name: static Algebra](T: type FF[Name]): static int = + T.getBigInt().bits + +# ------------------------------------------------------------------------------ + +type + ECFFT_Descriptor*[EC] = object + ## Metadata for FFT on Elliptic Curve + order*: int + rootsOfUnity*: ptr UncheckedArray[BigInt[Fr[EC.F.Name].bits()]] # Undeclared identifier `Name` + +func new*(T: type ECFFT_Descriptor): T = + discard + +# ------------------------------------------------------------------------------ + +template getBits[bits: static int](x: ptr UncheckedArray[BigInt[bits]]): int = bits + +proc main() = + let ctx = ECFFT_Descriptor[EC_ShortW_Aff[Fp[BLS12_381]]].new() + doAssert getBits(ctx.rootsOfUnity) == 255 + doAssert ctx.rootsOfUnity[0].limbs.len == wordsRequired(255) + +main() diff --git a/tests/generics/t2tables.nim b/tests/generics/t2tables.nim new file mode 100644 index 000000000..e4b1fb967 --- /dev/null +++ b/tests/generics/t2tables.nim @@ -0,0 +1,15 @@ +discard """ +action: compile +""" + +# bug #3669 + +import tables + +type + G[T] = object + inodes: Table[int, T] + rnodes: Table[T, int] + +var g: G[string] +echo g.rnodes["foo"] diff --git a/tests/generics/t3770.nim b/tests/generics/t3770.nim new file mode 100644 index 000000000..ffccbeeb5 --- /dev/null +++ b/tests/generics/t3770.nim @@ -0,0 +1,13 @@ +# bug #3770 +import m3770 + +doAssert $jjj() == "(hidden: 15)" # works + +proc someGeneric(_: type) = + doAssert $jjj() == "(hidden: 15)" # fails: "Error: the field 'hidden' is not accessible." + +someGeneric(int) + +# bug #20900 +proc c(y: int | int, w: Opt = Opt.none) = discard +c(0) diff --git a/tests/generics/t4668.nim b/tests/generics/t4668.nim new file mode 100644 index 000000000..bd44cc975 --- /dev/null +++ b/tests/generics/t4668.nim @@ -0,0 +1,166 @@ +discard """ + output: ''' +foo1 +foo2 +''' +""" + +block: + type + FooObj[T] = object + v: T + Foo1[T] = FooObj[T] + Foo2 = FooObj + + proc foo1(x: Foo1) = echo "foo1" + proc foo2(x: Foo2) = echo "foo2" + + var x: FooObj[float] + foo1(x) # works + foo2(x) # works + +block: + type + FooObj[T] = T + Foo1[T] = FooObj[T] + Foo2 = FooObj + Foo3 = Foo1 + Foo4x = FooObj[SomeInteger] + Foo4 = FooObj[SomeFloat] + Foo5x = Foo1[SomeInteger] + Foo5 = Foo1[SomeFloat] + + proc foo0(x: FooObj): int = 0 + proc foo1(x: Foo1): int = 1 + proc foo2(x: Foo2): int = 2 + proc foo3(x: Foo3): int = 3 + proc foo4(x: Foo4x): int = 40 + proc foo4(x: Foo4): int = 4 + proc foo5(x: Foo5x): int = 50 + proc foo5(x: Foo5): int = 5 + + block: + var x: FooObj[float] + doAssert(foo0(x) == 0) + doAssert(foo1(x) == 1) + doAssert(foo2(x) == 2) + doAssert(foo3(x) == 3) + doAssert(foo4(x) == 4) + doAssert(foo5(x) == 5) + + block: + var x: Foo1[float] + doAssert(foo0(x) == 0) + doAssert(foo1(x) == 1) + doAssert(foo2(x) == 2) + doAssert(foo3(x) == 3) + doAssert(foo4(x) == 4) + doAssert(foo5(x) == 5) + + block: + var x: Foo2[float] + doAssert(foo0(x) == 0) + doAssert(foo1(x) == 1) + doAssert(foo2(x) == 2) + doAssert(foo3(x) == 3) + doAssert(foo4(x) == 4) + doAssert(foo5(x) == 5) + +block: + type + FooObj[T,U] = object + x: T + y: U + Foo1[U,T] = FooObj[T,U] + Foo2 = FooObj + Foo3 = Foo1 + Foo4x = FooObj[SomeInteger,SomeInteger] + Foo4y = FooObj[SomeInteger,SomeFloat] + Foo4z = FooObj[SomeFloat,SomeFloat] + Foo4 = FooObj[SomeFloat,SomeInteger] + Foo5x = Foo1[SomeInteger,SomeInteger] + Foo5y = Foo1[SomeFloat,SomeInteger] + Foo5z = Foo1[SomeFloat,SomeFloat] + Foo5 = Foo1[SomeInteger,SomeFloat] + + proc foo0(x: FooObj): int = 0 + proc foo1(x: Foo1): int = 1 + proc foo2(x: Foo2): int = 2 + proc foo3(x: Foo3): int = 3 + proc foo4(x: Foo4x): int = 40 + proc foo4(x: Foo4y): int = 41 + proc foo4(x: Foo4z): int = 42 + proc foo4(x: Foo4): int = 4 + proc foo5(x: Foo5x): int = 50 + proc foo5(x: Foo5y): int = 51 + proc foo5(x: Foo5z): int = 52 + proc foo5(x: Foo5): int = 5 + + block: + var x: FooObj[float,int] + doAssert(foo0(x) == 0) + doAssert(foo1(x) == 1) + doAssert(foo2(x) == 2) + doAssert(foo3(x) == 3) + doAssert(foo4(x) == 4) + doAssert(foo5(x) == 5) + + block: + var x: Foo1[int,float] + doAssert(foo0(x) == 0) + doAssert(foo1(x) == 1) + doAssert(foo2(x) == 2) + doAssert(foo3(x) == 3) + doAssert(foo4(x) == 4) + doAssert(foo5(x) == 5) + +block: + type + FooObj[T] = object of RootObj + v: T + FooObj2[T] = object of FooObj[T] + Foo1[T] = FooObj[T] + Foo2 = FooObj + Foo3 = Foo1 + Foo4x = FooObj[SomeInteger] + Foo4 = FooObj[SomeFloat] + Foo5x = Foo1[SomeInteger] + Foo5 = Foo1[SomeFloat] + + proc foo0(x: FooObj): int = 0 + proc foo1(x: Foo1): int = 1 + proc foo2(x: Foo2): int = 2 + proc foo3(x: Foo3): int = 3 + proc foo4(x: Foo4x): int = 40 + proc foo4(x: Foo4): int = 4 + proc foo5(x: Foo5x): int = 50 + proc foo5(x: Foo5): int = 5 + + block: + var x: FooObj[float] + doAssert(foo0(x) == 0) + doAssert(foo1(x) == 1) + doAssert(foo2(x) == 2) + doAssert(foo3(x) == 3) + doAssert(foo4(x) == 4) + doAssert(foo5(x) == 5) + + block: + var x: Foo1[float] + doAssert(foo0(x) == 0) + doAssert(foo1(x) == 1) + doAssert(foo2(x) == 2) + doAssert(foo3(x) == 3) + doAssert(foo4(x) == 4) + doAssert(foo5(x) == 5) + + #[ XXX These still fail + block: + var x: FooObj2[float] + doAssert(foo0(x) == 0) + doAssert(foo1(x) == 1) + doAssert(foo2(x) == 2) + doAssert(foo3(x) == 3) + doAssert(foo4(x) == 4) + doAssert(foo5(x) == 5) + ]# diff --git a/tests/generics/t500.nim b/tests/generics/t500.nim new file mode 100644 index 000000000..2486359aa --- /dev/null +++ b/tests/generics/t500.nim @@ -0,0 +1,8 @@ +discard """ +action: compile +""" + +type + TTest = tuple[x: range[0..80], y: range[0..25]] + +let x: TTest = (2, 23) diff --git a/tests/generics/t5602_inheritence.nim b/tests/generics/t5602_inheritence.nim new file mode 100644 index 000000000..ee5ba89d5 --- /dev/null +++ b/tests/generics/t5602_inheritence.nim @@ -0,0 +1,24 @@ +discard """ + output: "seq[float]\n0" + targets: "c cpp" +""" + +# https://github.com/nim-lang/Nim/issues/5602 + +import typetraits, module_with_generics + +type + Foo[T] = object of RootObj + Bar[T] = object of Foo[seq[T]] + +proc p[T](f: Foo[T]): T = + echo T.name + +var s: Bar[float] +echo p(s).len # the bug was: p(s) should return seq[float], but returns float instead + +# Test overloading and code generation when +# downcasting is required for generic types: +var d = makeDerived(10) +setBaseValue(d, 20) + diff --git a/tests/generics/t5926.nim b/tests/generics/t5926.nim new file mode 100644 index 000000000..bb14c3af5 --- /dev/null +++ b/tests/generics/t5926.nim @@ -0,0 +1,22 @@ +discard """ +action: compile +""" + +type + SomeObj[T] = object + +template useSomeObj[T]() = + var retObj: SomeObj[T] + +useSomeObj[void]() +useSomeObj[int]() + + +type + Data*[T] = object + x: T + +template test*[T](xxx: T) = + let data = Data[T](x: xxx) + +test(1) diff --git a/tests/generics/t6060.nim b/tests/generics/t6060.nim new file mode 100644 index 000000000..6b1856f1c --- /dev/null +++ b/tests/generics/t6060.nim @@ -0,0 +1,11 @@ +import tables + +type MyTab[A,B] = distinct TableRef[A,B] + +proc `$`[A,B](t: MyTab[A,B]): string = + "My special table " & $TableRef[A,B](t) + +proc create[A,B](): MyTab[A,B] = MyTab(newTable[A,B]()) + +var a = create[int,int]() +doAssert $a == "My special table {:}" diff --git a/tests/generics/t6137.nim b/tests/generics/t6137.nim new file mode 100644 index 000000000..fb7db22f8 --- /dev/null +++ b/tests/generics/t6137.nim @@ -0,0 +1,28 @@ +discard """ + errormsg: "cannot instantiate: 'T'" + line: 19 +""" + +type + # simple vector of declared fixed length + vector[N : static[int]] = array[0..N-1, float] + +proc `*`[T](x: float, a: vector[T]): vector[T] = + # multiplication by scalar + for ii in 0..high(a): + result[ii] = a[ii]*x + +let + # define a vector of length 3 + x: vector[3] = [1.0, 3.0, 5.0] + +proc vectFunc[T](x: vector[T]): vector[T] = + # Define a vector function + result = 2.0*x + +proc passVectFunction[T](g: proc(x: vector[T]): vector[T], x: vector[T]): vector[T] = + # pass a vector function as input in another procedure + result = g(x) + +let + xNew = passVectFunction(vectFunc,x) diff --git a/tests/generics/t6637.nim b/tests/generics/t6637.nim new file mode 100644 index 000000000..dd4f339a2 --- /dev/null +++ b/tests/generics/t6637.nim @@ -0,0 +1,9 @@ + +type + Grid2D*[I: SomeInteger, w, h: static[I], T] = object + grid: array[w, array[h, T]] + Grid2DIns = Grid2D[int, 2, 3, uint8] + +let a = Grid2DIns() +doAssert a.grid.len == 2 +doAssert a.grid[0].len == 3 diff --git a/tests/generics/t7141.nim b/tests/generics/t7141.nim new file mode 100644 index 000000000..b1e9cbf43 --- /dev/null +++ b/tests/generics/t7141.nim @@ -0,0 +1,9 @@ +discard """ + errormsg: "cannot instantiate: \'T\'" + line: 6 +""" + +proc foo[T](x: T) = + discard + +var fun = if true: foo else: foo diff --git a/tests/generics/t7446.nim b/tests/generics/t7446.nim new file mode 100644 index 000000000..71aa8f0e8 --- /dev/null +++ b/tests/generics/t7446.nim @@ -0,0 +1,10 @@ +proc foo(x: Natural or SomeUnsignedInt):int = + when x is int: + result = 1 + else: + result = 2 +let a = 10 +doAssert foo(a) == 1 + +let b = 10'u8 +doAssert foo(b) == 2 \ No newline at end of file diff --git a/tests/generics/t7839.nim b/tests/generics/t7839.nim new file mode 100644 index 000000000..4d17b438b --- /dev/null +++ b/tests/generics/t7839.nim @@ -0,0 +1,9 @@ +type A[I: SomeOrdinal, E] = tuple # same for object + length: int + +doAssert A.sizeof == sizeof(int) # works without the following proc + +proc newA*[I: SomeOrdinal, E](): A[I, E] = # works without `SomeOrdinal` + discard + +discard newA[uint8, int]() diff --git a/tests/generics/t8270.nim b/tests/generics/t8270.nim new file mode 100644 index 000000000..1e731d1d4 --- /dev/null +++ b/tests/generics/t8270.nim @@ -0,0 +1,7 @@ +discard """ + errormsg: "cannot instantiate: \'T\'" + line: 6 +""" + +proc m[T](x: T): int = discard +echo [m] diff --git a/tests/generics/taliashijack.nim b/tests/generics/taliashijack.nim new file mode 100644 index 000000000..fdebadded --- /dev/null +++ b/tests/generics/taliashijack.nim @@ -0,0 +1,8 @@ +# issue #23977 + +type Foo[T] = int + +proc foo(T: typedesc) = + var a: T + +foo(int) diff --git a/tests/generics/tarc_misc.nim b/tests/generics/tarc_misc.nim new file mode 100644 index 000000000..3e7762556 --- /dev/null +++ b/tests/generics/tarc_misc.nim @@ -0,0 +1,21 @@ +discard """ + output: '''''' + cmd: "nim c --gc:arc $file" +""" + +# bug #13519 + +var unrelated: seq[proc() {.closure, gcsafe.}] + +unrelated.add proc () = + echo "gcsafe" + +import tables, sequtils +let t = newTable[int, proc()]() + +type + MyProc = proc() {.closure.} + +var result: seq[MyProc] = @[] +for x in t.values: + result.add(x) diff --git a/tests/generics/tbadcache.nim b/tests/generics/tbadcache.nim new file mode 100644 index 000000000..33e65be3a --- /dev/null +++ b/tests/generics/tbadcache.nim @@ -0,0 +1,26 @@ +# issue #16128 + +import std/[tables, hashes] + +type + NodeId*[L] = object + isSource: bool + index: Table[NodeId[L], seq[NodeId[L]]] + +func hash*[L](id: NodeId[L]): Hash = discard +func `==`[L](a, b: NodeId[L]): bool = discard + +proc makeIndex*[T, L](tree: T) = + var parent = NodeId[L]() + var tmp: Table[NodeId[L], seq[NodeId[L]]] + tmp[parent] = @[parent] + +proc simpleTreeDiff*[T, L](source, target: T) = + # Swapping these two lines makes error disappear + var m: Table[NodeId[L], NodeId[L]] + makeIndex[T, L](target) + +var tmp: Table[string, seq[string]] # removing this forward declaration also removes error + +proc diff(x1, x2: string): auto = + simpleTreeDiff[int, string](12, 12) diff --git a/tests/generics/tbaddeprecated.nim b/tests/generics/tbaddeprecated.nim new file mode 100644 index 000000000..335234a25 --- /dev/null +++ b/tests/generics/tbaddeprecated.nim @@ -0,0 +1,55 @@ +discard """ + output: ''' +not deprecated +not deprecated +not error +not error +''' +""" + +# issue #21724 + +block: # deprecated + {.push warningAsError[Deprecated]: on.} + type + SomeObj = object + hey: bool + proc hey() {.deprecated: "Shouldn't use this".} = echo "hey" + proc gen(o: auto) = + doAssert not compiles(o.hey()) + if o.hey: + echo "not deprecated" + gen(SomeObj(hey: true)) + doAssert not (compiles do: + proc hey(o: SomeObj) {.deprecated: "Shouldn't use this".} = echo "hey" + proc gen2(o: auto) = + if o.hey(): + echo "not deprecated" + gen2(SomeObj(hey: true))) + proc hey(o: SomeObj) {.deprecated: "Shouldn't use this".} = echo "hey" + proc gen3(o: auto) = + if o.hey: + echo "not deprecated" + gen3(SomeObj(hey: true)) + {.pop.} +block: # error + type + SomeObj = object + hey: bool + proc hey() {.error: "Shouldn't use this".} = echo "hey" + proc gen(o: auto) = + doAssert not compiles(o.hey()) + if o.hey: + echo "not error" + gen(SomeObj(hey: true)) + doAssert not (compiles do: + proc hey(o: SomeObj) {.error: "Shouldn't use this".} = echo "hey" + proc gen2(o: auto) = + if o.hey(): + echo "not error" + gen2(SomeObj(hey: true))) + proc hey(o: SomeObj) {.error: "Shouldn't use this".} = echo "hey" + proc gen3(o: auto) = + if o.hey: + echo "not error" + gen3(SomeObj(hey: true)) diff --git a/tests/generics/tbadgenericlambda.nim b/tests/generics/tbadgenericlambda.nim new file mode 100644 index 000000000..9fac150c1 --- /dev/null +++ b/tests/generics/tbadgenericlambda.nim @@ -0,0 +1,7 @@ +discard """ + errormsg: "nested proc can have generic parameters only when" + line: 6 +""" + +let x = proc (x, y: auto): auto = x + y + diff --git a/tests/generics/tbindoncevsbindmany.nim b/tests/generics/tbindoncevsbindmany.nim new file mode 100644 index 000000000..01e801f0e --- /dev/null +++ b/tests/generics/tbindoncevsbindmany.nim @@ -0,0 +1,68 @@ +template accept(x) = + static: assert(compiles(x)) + +template reject(x) = + static: assert(not compiles(x)) + +type + ObjectWithNumber = concept obj + obj.number is int + + Foo[T] = object + x: T + +type A = object + anumber: int + +type B = object + bnumber: int + +proc number(a: A): int = a.anumber +proc number(b: B): int = b.bnumber + +proc notDistincConcept1(a: ObjectWithNumber, b: ObjectWithNumber) = discard +proc notDistincConcept2(a, b: ObjectWithNumber) = discard +proc distinctConcept1(a, b: distinct ObjectWithNumber) = discard +proc distinctConcept2(a: ObjectWithNumber, b: distinct ObjectWithNumber) = discard +proc distinctConcept3(a: distinct ObjectWithNumber, b: ObjectWithNumber) = discard +proc distinctConcept4(a: distinct ObjectWithNumber, b: distinct ObjectWithNumber) = discard + +var a = A(anumber: 5) +var b = B(bnumber: 6) + +accept notDistincConcept1(a, a) +accept notDistincConcept1(b, b) +reject notDistincConcept2(a, b) + +accept notDistincConcept2(a, a) +accept notDistincConcept2(b, b) +reject notDistincConcept2(a, b) + +accept distinctConcept1(a, b) +accept distinctConcept2(a, b) +accept distinctConcept3(a, b) +accept distinctConcept4(a, b) + +proc nonDistincGeneric1(a: Foo, b: Foo) = discard +proc nonDistincGeneric2(a, b: Foo) = discard +proc distinctGeneric1(a, b: distinct Foo) = discard +proc distinctGeneric2(a: distinct Foo, b: Foo) = discard +proc distinctGeneric3(a: Foo, b: distinct Foo) = discard +proc distinctGeneric4(a: distinct Foo, b: distinct Foo) = discard + +var f1 = Foo[int](x: 10) +var f2 = Foo[string](x: "x") + +accept nonDistincGeneric1(f1, f1) +accept nonDistincGeneric1(f2, f2) +reject nonDistincGeneric1(f1, f2) + +accept nonDistincGeneric2(f1, f1) +accept nonDistincGeneric2(f2, f2) +reject nonDistincGeneric2(f1, f2) + +accept distinctGeneric1(f1, f1) +accept distinctGeneric2(f1, f1) +accept distinctGeneric3(f1, f1) +accept distinctGeneric4(f1, f1) + diff --git a/tests/generics/tbintree.nim b/tests/generics/tbintree.nim new file mode 100644 index 000000000..83f14406b --- /dev/null +++ b/tests/generics/tbintree.nim @@ -0,0 +1,106 @@ +discard """ + output: '''hello +world +99 +110 +223''' +""" +type + TBinaryTree[T] = object # TBinaryTree is a generic type with + # with generic param ``T`` + le, ri: ref TBinaryTree[T] # left and right subtrees; may be nil + data: T # the data stored in a node + PBinaryTree*[A] = ref TBinaryTree[A] # type that is exported + +proc newNode*[T](data: T): PBinaryTree[T] = + # constructor for a node + new(result) + result.data = data + +proc add*[Ty](root: var PBinaryTree[Ty], n: PBinaryTree[Ty]) = + # insert a node into the tree + if root == nil: + root = n + else: + var it = root + while it != nil: + # compare the data items; uses the generic ``cmp`` proc that works for + # any type that has a ``==`` and ``<`` operator + var c = cmp(n.data, it.data) + if c < 0: + if it.le == nil: + it.le = n + return + it = it.le + else: + if it.ri == nil: + it.ri = n + return + it = it.ri + +proc add*[Ty](root: var PBinaryTree[Ty], data: Ty) = + # convenience proc: + add(root, newNode(data)) + +proc find*[Ty2](b: PBinaryTree[Ty2], data: Ty2): bool = + # for testing this needs to be recursive, so that the + # instantiated type is checked for proper tyGenericInst envelopes + if b == nil: + result = false + else: + var c = cmp(data, b.data) + if c < 0: result = find(b.le, data) + elif c > 0: result = find(b.ri, data) + else: result = true + +iterator preorder*[T](root: PBinaryTree[T]): T = + # Preorder traversal of a binary tree. + # This uses an explicit stack (which is more efficient than + # a recursive iterator factory). + var stack: seq[PBinaryTree[T]] = @[root] + while stack.len > 0: + var n = stack.pop() + while n != nil: + yield n.data + add(stack, n.ri) # push right subtree onto the stack + n = n.le # and follow the left pointer + +iterator items*[T](root: PBinaryTree[T]): T = + ## Inorder traversal of the binary tree. + var stack: seq[PBinaryTree[T]] = @[] + var n = root + while true: + while n != nil: + add(stack, n) + n = n.le + if stack.len > 0: + n = stack.pop() + yield n.data + n = n.ri + if stack.len == 0 and n == nil: break + +proc debug[T](a: PBinaryTree[T]) = + if a != nil: + debug(a.le) + echo a.data + debug(a.ri) + +when true: + var + root: PBinaryTree[string] + x = newNode("hello") + add(root, x) + add(root, "world") + if find(root, "world"): + for str in items(root): + echo(str) + else: + echo("BUG") + + var + r2: PBinaryTree[int] + add(r2, newNode(110)) + add(r2, 223) + add(r2, 99) + for y in items(r2): + echo(y) diff --git a/tests/generics/tbracketinstantiation.nim b/tests/generics/tbracketinstantiation.nim new file mode 100644 index 000000000..22a86af4c --- /dev/null +++ b/tests/generics/tbracketinstantiation.nim @@ -0,0 +1,86 @@ +discard """ + nimout: ''' +type + Bob = object +type + Another = object +''' +""" + +block: # issue #22645 + type + Opt[T] = object + FutureBase = ref object of RootObj + Future[T] = ref object of FutureBase ## Typed future. + internalValue: T ## Stored value + template err[T](E: type Opt[T]): E = E() + proc works(): Future[Opt[int]] {.stackTrace: off, gcsafe, raises: [].} = + var chronosInternalRetFuture: FutureBase + template result(): untyped {.used.} = + Future[Opt[int]](chronosInternalRetFuture).internalValue + result = err(type(result)) + proc breaks(): Future[Opt[int]] {.stackTrace: off, gcsafe, raises: [].} = + var chronosInternalRetFuture: FutureBase + template result(): untyped {.used.} = + cast[Future[Opt[int]]](chronosInternalRetFuture).internalValue + result = err(type(result)) + +import macros + +block: # issue #16118 + macro thing(name: static[string]) = + result = newStmtList( + nnkTypeSection.newTree( + nnkTypeDef.newTree( + ident(name), + newEmptyNode(), + nnkObjectTy.newTree( + newEmptyNode(), + newEmptyNode(), + nnkRecList.newTree())))) + template foo(name: string): untyped = + thing(name) + expandMacros: + foo("Bob") + block: + expandMacros: + foo("Another") + +block: # issue #19670 + type + Past[Z] = object + OpenObject = object + + macro rewriter(prc: untyped): untyped = + prc.body.add(nnkCall.newTree( + prc.params[0] + )) + prc + + macro macroAsync(name, restype: untyped): untyped = + quote do: + proc `name`(): Past[seq[`restype`]] {.rewriter.} = discard + + macroAsync(testMacro, OpenObject) + +import asyncdispatch + +block: # issue #11838 long + type + R[P] = object + updates: seq[P] + D[T, P] = ref object + ps: seq[P] + t: T + proc newD[T, P](ps: seq[P], t: T): D[T, P] = + D[T, P](ps: ps, t: t) + proc loop[T, P](d: D[T, P]) = + var results = newSeq[Future[R[P]]](10) + let d = newD[string, int](@[1], "") + d.loop() + +block: # issue #11838 minimal + type R[T] = object + proc loop[T]() = + discard newSeq[R[R[T]]]() + loop[int]() diff --git a/tests/generics/tcalltype.nim b/tests/generics/tcalltype.nim new file mode 100644 index 000000000..cba691f77 --- /dev/null +++ b/tests/generics/tcalltype.nim @@ -0,0 +1,26 @@ +discard """ + joinable: false # breaks everything because of #23977 +""" + +# issue #23406 + +template helper(_: untyped): untyped = + int + +type # Each of them should always be `int`. + GenA[T] = helper int + GenB[T] = helper(int) + GenC[T] = helper helper(int) + +block: + template helper(_: untyped): untyped = + float + + type + A = GenA[int] + B = GenB[int] + C = GenC[int] + + assert A is int # OK. + assert B is int # Fails; it is `float`! + assert C is int # OK. diff --git a/tests/generics/tcan.nim b/tests/generics/tcan.nim new file mode 100644 index 000000000..bbefa7233 --- /dev/null +++ b/tests/generics/tcan.nim @@ -0,0 +1,46 @@ +discard """ + output: ''' +''' +""" + +# Created by Eric Doughty-Papassideris on 2011-02-16. + +block talias_generic: + type + TGen[T] = object + TGen2[T] = TGen[T] + + +block talias_specialised: + type + TGen[T] = object + TSpef = TGen[string] + var s: TSpef + + +block tinherit: + type + TGen[T] = object of RootObj + x, y: T + TSpef[T] = object of TGen[T] + + var s: TSpef[float] + s.x = 0.4 + s.y = 0.6 + + +block tspecialise: + type + TGen[T] {.inheritable.} = object + TSpef = object of TGen[string] + + +block tspecialised_equivalent: + type + TGen[T] = tuple[a: T] + TSpef = tuple[a: string] + + var + a: TGen[string] + b: TSpef + a = b diff --git a/tests/generics/tcannot_pass_empty_seq_to_generic.nim b/tests/generics/tcannot_pass_empty_seq_to_generic.nim new file mode 100644 index 000000000..e33dbc10b --- /dev/null +++ b/tests/generics/tcannot_pass_empty_seq_to_generic.nim @@ -0,0 +1,17 @@ +discard """ + errormsg: "type mismatch: got <seq[empty]>" + line: 16 +""" + +# bug #836 + +type + TOption*[T] = object + case FIsSome: bool + of false: nil + of true: FData: T + +proc some*[T](value: T): TOption[T] = TOption[T](FIsSome: true, FData: value) + +echo some(@[]).FIsSome + diff --git a/tests/generics/tconstraints.nim b/tests/generics/tconstraints.nim new file mode 100644 index 000000000..3ca01cfd5 --- /dev/null +++ b/tests/generics/tconstraints.nim @@ -0,0 +1,16 @@ +discard """ + errormsg: "type mismatch: got <int literal(232)>" + line: 16 +""" + +proc myGenericProc[T: object|tuple|ptr|ref|distinct](x: T): string = + result = $x + +type + TMyObj = tuple[x, y: int] + +var + x: TMyObj + +assert myGenericProc(x) == "(x: 0, y: 0)" +assert myGenericProc(232) == "232" diff --git a/tests/generics/tcritical.nim b/tests/generics/tcritical.nim new file mode 100644 index 000000000..e84c03618 --- /dev/null +++ b/tests/generics/tcritical.nim @@ -0,0 +1,19 @@ +discard """ + errormsg: "type mismatch" + line: 18 +""" + +# bug #3998 + +type Vec3[T] = array[3, T] + +var vg: Vec3[float32] = Vec3([1.0f, 2.0f, 3.0f]) + +echo "vg[0]: " & $vg[0] # prints 1.0 OK +echo "vg[1]: " & $vg[1] # prints 2.0 OK +echo "vg[2]: " & $vg[2] # prints 3.0 OK +echo "" + +var ve: Vec3[float64] +ve = vg # compiles, this MUST NOT be allowed! + diff --git a/tests/generics/texplicitgeneric1.nim b/tests/generics/texplicitgeneric1.nim new file mode 100644 index 000000000..16f4f7330 --- /dev/null +++ b/tests/generics/texplicitgeneric1.nim @@ -0,0 +1,36 @@ +discard """ + output: "Key: 12 value: 12Key: 13 value: 13 Key: A value: 12 Key: B value: 13" +""" +# test explicit type instantiation + +type + TDict*[TKey, TValue] = object + data: seq[tuple[k: TKey, v: TValue]] + PDict*[TKey, #with `==`(a, b: TKey): bool + # hash(a: TKey): int, + TValue] = ref TDict[TKey, TValue] + +proc newDict*[TKey, TValue](): PDict[TKey, TValue] = + new(result) + result.data = @[] + +proc add*[TKey, TValue](d: PDict[TKey, TValue], k: TKey, v: TValue) = + d.data.add((k, v)) + +iterator items*[Tkey, TValue](d: PDict[TKey, TValue]): tuple[k: TKey, + v: TValue] = + for k, v in items(d.data): yield (k, v) + +var d = newDict[int, string]() +d.add(12, "12") +d.add(13, "13") +for k, v in items(d): + stdout.write("Key: ", $k, " value: ", v) + +var c = newDict[char, string]() +c.add('A', "12") +c.add('B', "13") +for k, v in items(c): + stdout.write(" Key: ", $k, " value: ", v) + +stdout.write "\n" diff --git a/tests/generics/texplicitgeneric2.nim b/tests/generics/texplicitgeneric2.nim new file mode 100644 index 000000000..37b133130 --- /dev/null +++ b/tests/generics/texplicitgeneric2.nim @@ -0,0 +1,35 @@ +discard """ + output: "Key: 12 value: 12Key: 13 value: 13 Key: A value: 12 Key: B value: 13" +""" + +# test explicit type instantiation + +type + TDict*[TKey, TValue] = object + data: seq[tuple[k: TKey, v: TValue]] + PDict*[TKey, TValue] = ref TDict[TKey, TValue] + +proc newDict*[TKey, TValue](): PDict[TKey, TValue] = + new(result) + result.data = @[] + +proc add*(d: PDict, k: d.TKey, v: d.TValue) = + d.data.add((k, v)) + + +iterator items*(d: PDict): tuple[k: d.TKey, v: d.TValue] = + for k, v in items(d.data): yield (k, v) + +var d = newDict[int, string]() +d.add(12, "12") +d.add(13, "13") +for k, v in items(d): + stdout.write("Key: ", $k, " value: ", v) + +var c = newDict[char, string]() +c.add('A', "12") +c.add('B', "13") +for k, v in items(c): + stdout.write(" Key: ", $k, " value: ", v) + +stdout.write "\n" diff --git a/tests/generics/tfakecovariance.nim b/tests/generics/tfakecovariance.nim new file mode 100644 index 000000000..0920cb504 --- /dev/null +++ b/tests/generics/tfakecovariance.nim @@ -0,0 +1,78 @@ +template accept(x) = + static: assert(compiles(x)) + +template reject(x) = + static: assert(not compiles(x)) + +type + BaseObj = object of RootObj + DerivedObj = object of BaseObj + NonDerivedObj = object + + Container[T] = object + +var base: BaseObj +var derived: DerivedObj +var nonDerived: NonDerivedObj + +var baseContainer: Container[BaseObj] +var derivedContainer: Container[DerivedObj] +var nonDerivedContainer: Container[NonDerivedObj] + +# We can fake covariance by listing some specific derived types that +# will be allowed with our overload. This is not a real covariance, +# because there will be multiple instantiations of the proc, but for +# many purposes, it will suffice: + +proc wantsSpecificContainers(c: Container[BaseObj or DerivedObj]) = discard + +accept wantsSpecificContainers(baseContainer) +accept wantsSpecificContainers(derivedContainer) + +reject wantsSpecificContainers(nonDerivedContainer) +reject wantsSpecificContainers(derived) + +# Now, let's make a more general solution able to catch all derived types: + +type + DerivedFrom[T] = concept type D + var derived: ref D + var base: ref T = derived + +proc wantsDerived(x: DerivedFrom[BaseObj]) = discard + +accept wantsDerived(base) +accept wantsDerived(derived) + +reject wantsDerived(nonDerived) +reject wantsDerived(baseContainer) + +proc wantsDerivedContainer(c: Container[DerivedFrom[BaseObj]]) = discard + +accept wantsDerivedContainer(baseContainer) +accept wantsDerivedContainer(derivedContainer) + +reject wantsDerivedContainer(nonDerivedContainer) + +# The previous solutions were solving the problem for a single overload. +# Let's solve it for multiple overloads by introducing a converter: + +type + OtherContainer[T] = object + +proc wantsBaseContainer1(c: OtherContainer[BaseObj]) = discard +proc wantsBaseContainer2(c: OtherContainer[BaseObj]) = discard + +converter derivedToBase(c: OtherContainer[DerivedFrom[BaseObj]]): OtherContainer[BaseObj] = discard + +block: + var baseContainer: OtherContainer[BaseObj] + var derivedContainer: OtherContainer[DerivedObj] + var nonDerivedContainer: OtherContainer[NonDerivedObj] + + accept wantsBaseContainer1(derivedContainer) + reject wantsBaseContainer1(nonDerivedContainer) + + accept wantsBaseContainer2(derivedContainer) + reject wantsBaseContainer2(nonDerivedContainer) + diff --git a/tests/generics/tforwardgeneric.nim b/tests/generics/tforwardgeneric.nim new file mode 100644 index 000000000..9f68bf332 --- /dev/null +++ b/tests/generics/tforwardgeneric.nim @@ -0,0 +1,28 @@ +discard """ + output: "1.1 11\n42\n0" + ccodecheck: "!@'ClEnv'" +""" + +proc p[T](a, b: T): T + +echo p(0.9, 0.1), " ", p(9, 1) + +proc p[T](a, b: T): T = + let c = b + result = a + b + c + +# https://github.com/nim-lang/Nim/issues/4908 +proc foo(t: typedesc[int]): int +proc bar(): int = foo(int) +proc foo(t: typedesc[int]): int = + return 0 + +# https://github.com/nim-lang/Nim/issues/4104 +proc print[T](t: T) # Error: implementation of 'print.print(t: int)' expected +print 42 # moving this line after the implementation fixes the error, + # but such behaviour makes forward declaration pointless +proc print[T](t: T) = + echo t + +echo bar() + diff --git a/tests/generics/tforwardgenericconstrained.nim b/tests/generics/tforwardgenericconstrained.nim new file mode 100644 index 000000000..6163ea1a8 --- /dev/null +++ b/tests/generics/tforwardgenericconstrained.nim @@ -0,0 +1,73 @@ +discard """ +output: ''' +hello some integer +hello range +hello tuple +hello seq +hello object +hello distinct +hello enum +''' +""" + + + +# SomeInteger + +proc foo[T : SomeInteger](arg: T) +proc foo[T : SomeInteger](arg: T) = + echo "hello some integer" +foo(123) + +# range + +proc foo2[T : range[0..100]](arg: T) +proc foo2[T : range[0..100]](arg: T) = + echo "hello range" +foo2(7) + +# tuple + +proc foo3[T : tuple](arg: T) +proc foo3[T : tuple](arg: T) = + echo "hello tuple" + +foo3((a:123,b:321)) + +# seq + +proc foo4[T: seq](arg: T) +proc foo4[T: seq](arg: T) = + echo "hello seq" + +foo4(@[1,2,3]) + +# object + +proc foo5[T : object](arg: T) +proc foo5[T : object](arg: T) = + echo "hello object" + +type MyType = object +var mt: MyType +foo5(mt) + +# distinct + +proc foo6[T : distinct](arg: T) +proc foo6[T : distinct](arg: T) = + echo "hello distinct" + +type MyDistinct = distinct string +var md: MyDistinct +foo6(md) + +# enum + +proc foo7[T : enum](arg: T) +proc foo7[T : enum](arg: T) = + echo "hello enum" + +type MyEnum = enum + ValueA +foo7(ValueA) diff --git a/tests/generics/tfriends.nim b/tests/generics/tfriends.nim new file mode 100644 index 000000000..1e70d50a5 --- /dev/null +++ b/tests/generics/tfriends.nim @@ -0,0 +1,11 @@ +discard """ + output: "3" +""" + +# Tests that a generic instantiation from a different module may access +# private object fields: + +import mfriends + +echo gen[int]() + diff --git a/tests/generics/tgeneric0.nim b/tests/generics/tgeneric0.nim new file mode 100644 index 000000000..16a148f7b --- /dev/null +++ b/tests/generics/tgeneric0.nim @@ -0,0 +1,196 @@ +discard """ + output: ''' +100 +0 +float32 +float32 +(name: "Resource 1", readers: ..., writers: ...) +''' +""" + + +import std/tables + + +block tgeneric0: + type + TX = Table[string, int] + + proc foo(models: seq[Table[string, float]]): seq[float] = + result = @[] + for model in models.items: + result.add model["foobar"] + + # bug #686 + type TType[T; A] = array[A, T] + + proc foo[T](p: TType[T, range[0..1]]) = + echo "foo" + proc foo[T](p: TType[T, range[0..2]]) = + echo "bar" + + #bug #1366 + + proc reversed(x: auto) = + for i in countdown(x.low, x.high): + echo i + + reversed(@[-19, 7, -4, 6]) + + + +block tgeneric1: + type + TNode[T] = tuple[priority: int, data: T] + TBinHeap[T] = object + heap: seq[TNode[T]] + last: int + PBinHeap[T] = ref TBinHeap[T] + + proc newBinHeap[T](heap: var PBinHeap[T], size: int) = + new(heap) + heap.last = 0 + newSeq(heap.heap, size) + #newSeq(heap.seq, size) + + proc parent(elem: int): int {.inline.} = + return (elem-1) div 2 + + proc siftUp[T](heap: PBinHeap[T], elem: int) = + var idx = elem + while idx != 0: + var p = parent(idx) + if heap.heap[idx].priority < heap.heap[p].priority: + swap(heap.heap[idx], heap.heap[p]) + idx = p + else: + break + + proc add[T](heap: PBinHeap[T], priority: int, data: T) = + var node: TNode[T] + node.priority = priority + node.data = data + heap.heap[heap.last] = node + siftUp(heap, heap.last) + inc(heap.last) + + proc print[T](heap: PBinHeap[T]) = + for i in countup(0, heap.last): + stdout.write($heap.heap[i].data, "\n") + + var heap: PBinHeap[int] + + newBinHeap(heap, 256) + add(heap, 1, 100) + print(heap) + + + +block tgeneric2: + type + TX = Table[string, int] + + proc foo(models: seq[TX]): seq[int] = + result = @[] + for model in models.items: + result.add model["foobar"] + + type + Obj = object + field: Table[string, string] + var t: Obj + discard initTable[type(t.field), string]() + + + +block tgeneric4: + type + TIDGen[A: Ordinal] = object + next: A + free: seq[A] + + proc newIDGen[A]: TIDGen[A] = + newSeq result.free, 0 + + var x = newIDGen[int]() + +block tgeneric5: + # bug #12528 + proc foo[T](a: T; b: T) = + echo T + + foo(0.0'f32, 0.0) + + proc bar[T](a: T; b: T = 0.0) = + echo T + + bar(0.0'f32) + +# bug #13378 + +type + Resource = ref object of RootObj + name: string + readers, writers: seq[RenderTask] + + RenderTask = ref object + name: string + +var res = Resource(name: "Resource 1") + +(proc (r: typeof(res)) = + echo r[])(res) + +# bug #4061 + +type List[T] = object + e: T + n: ptr List[T] + +proc zip*[T,U](xs: List[T], ys: List[U]): List[(T,U)] = discard + +proc unzip*[T,U](xs: List[tuple[t: T, u: U]]): (List[T], List[U]) = discard + +proc unzip2*[T,U](xs: List[(T,U)]): (List[T], List[U]) = discard + +type + AtomicType = pointer|ptr|int + + Atomic[T: AtomicType] = distinct T + + Block[T: AtomicType] = object + + AtomicContainer[T: AtomicType] = object + b: Atomic[ptr Block[T]] + +# bug #8295 +var x = AtomicContainer[int]() +doAssert (ptr Block[int])(x.b) == nil + + +# bug #23233 +type + JsonObjectType*[T: string or uint64] = Table[string, JsonValueRef[T]] + + JsonValueRef*[T: string or uint64] = object + objVal*: JsonObjectType[T] + +proc scanValue[K](val: var K) = + var map: JsonObjectType[K.T] + var newVal: K + map["one"] = newVal + +block: + var a: JsonValueRef[uint64] + scanValue(a) + + var b: JsonValueRef[string] + scanValue(b) + +block: # bug #21347 + type K[T] = object + template s[T]() = discard + proc b1(n: bool | bool) = s[K[K[int]]]() + proc b2(n: bool) = s[K[K[int]]]() + b1(false) # Error: 's' has unspecified generic parameters + b2(false) # Builds, on its own diff --git a/tests/generics/tgeneric3.nim b/tests/generics/tgeneric3.nim new file mode 100644 index 000000000..29a73afc6 --- /dev/null +++ b/tests/generics/tgeneric3.nim @@ -0,0 +1,487 @@ +discard """ +output: ''' +312 +1000 +1000 +500 +0 +''' +""" + +import strutils + +type + PNode[T,D] = ref TNode[T,D] + TItem[T,D] {.acyclic, pure, final, shallow.} = object + key: T + value: D + node: PNode[T,D] + when not (D is string): + val_set: bool + + TItems[T,D] = seq[ref TItem[T,D]] + TNode[T,D] {.acyclic, pure, final, shallow.} = object + slots: TItems[T,D] + left: PNode[T,D] + count: int32 + + + RPath[T,D] = tuple[ + Xi: int, + Nd: PNode[T,D] ] + +const + cLen1 = 2 + cLen2 = 16 + cLen3 = 32 + cLenCenter = 80 + clen4 = 96 + cLenMax = 128 + cCenter = cLenMax div 2 + +proc len[T,D] (n:PNode[T,D]): int {.inline.} = + return n.count + +proc clean[T: SomeOrdinal|SomeNumber](o: var T) {.inline.} = discard + +proc clean[T: string|seq](o: var T) {.inline.} = + o = nil + +proc clean[T,D] (o: ref TItem[T,D]) {.inline.} = + when (D is string) : + o.value = nil + else : + o.val_set = false + +proc isClean[T,D] (it: ref TItem[T,D]): bool {.inline.} = + when (D is string) : + return it.value == nil + else : + return not it.val_set + +proc isClean[T,D](n: PNode[T,D], x: int): bool {.inline.} = + when (D is string): + return n.slots[x].value == nil + else: + return not n.slots[x].val_set + +proc setItem[T,D](Akey: T, Avalue: D, ANode: PNode[T,D]): ref TItem[T,D] {.inline.} = + new(result) + result.key = Akey + result.value = Avalue + result.node = ANode + when not (D is string) : + result.val_set = true + +proc cmp[T:int8|int16|int32|int64|int] (a,b: T): T {.inline.} = + return a-b + +template binSearchImpl *(docmp: untyped) = + var bFound = false + result = 0 + var H = haystack.len - 1 + while result <= H : + var I {.inject.} = (result + H) shr 1 + var SW = docmp + if SW < 0: result = I + 1 + else: + H = I - 1 + if SW == 0 : bFound = true + if bFound: inc(result) + else: result = - result + +proc bSearch[T,D] (haystack: PNode[T,D], needle:T): int {.inline.} = + binSearchImpl(haystack.slots[I].key.cmp(needle)) + +proc DeleteItem[T,D] (n: PNode[T,D], x: int): PNode[T,D] {.inline.} = + var w = n.slots[x] + if w.node != nil : + clean(w) + return n + dec(n.count) + if n.count > 0 : + for i in countup(x, n.count - 1) : n.slots[i] = n.slots[i + 1] + n.slots[n.count] = nil + case n.count + of cLen1 : setLen(n.slots, cLen1) + of cLen2 : setLen(n.slots, cLen2) + of cLen3 : setLen(n.slots, cLen3) + of cLenCenter : setLen(n.slots, cLenCenter) + of cLen4 : setLen(n.slots, cLen4) + else: discard + result = n + + else : + result = n.left + n.slots = @[] + n.left = nil + +proc internalDelete[T,D] (ANode: PNode[T,D], key: T, Avalue: var D): PNode[T,D] = + var Path: array[0..20, RPath[T,D]] + var n = ANode + result = n + clean(Avalue) + var h = 0 + while n != nil: + var x = bSearch(n, key) + if x <= 0 : + Path[h].Nd = n + Path[h].Xi = - x + inc(h) + if x == 0 : + n = n.left + else : + x = (-x) - 1 + if x < n.count : + n = n.slots[x].node + else : + n = nil + else : + dec(x) + if isClean(n, x) : return + Avalue = n.slots[x].value + var n2 = DeleteItem(n, x) + dec(h) + while (n2 != n) and (h >= 0) : + n = n2 + var w = addr Path[h] + x = w.Xi - 1 + if x >= 0 : + if (n == nil) and isClean(w.Nd, x) : + n = w.Nd + n.slots[x].node = nil + n2 = DeleteItem(n, x) + else : + w.Nd.slots[x].node = n + return + else : + w.Nd.left = n + return + dec(h) + if h < 0: + result = n2 + return + +proc internalFind[T,D] (n: PNode[T,D], key: T): ref TItem[T,D] {.inline.} = + var wn = n + while wn != nil : + var x = bSearch(wn, key) + if x <= 0 : + if x == 0 : + wn = wn.left + else : + x = (-x) - 1 + if x < wn.count : + wn = wn.slots[x].node + else : + return nil + + else : + return wn.slots[x - 1] + return nil + +proc traceTree[T,D](root: PNode[T,D]) = + proc traceX(x: int) = + write stdout, "(" + write stdout, x + write stdout, ") " + + proc traceEl(el: ref TItem[T,D]) = + write stdout, " key: " + write stdout, el.key + write stdout, " value: " + write stdout, el.value + + + proc traceln(space: string) = + writeLine stdout, "" + write stdout, space + + proc doTrace(n: PNode[T,D], level: int) = + var space = spaces(2 * level) + traceln(space) + write stdout, "node: " + if n == nil: + writeLine stdout, "is empty" + return + write stdout, n.count + write stdout, " elements: " + if n.left != nil: + traceln(space) + write stdout, "left: " + doTrace(n.left, level+1) + for i, el in n.slots: + if el != nil and not isClean(el): + traceln(space) + traceX(i) + if i >= n.count: + write stdout, "error " + else: + traceEl(el) + if el.node != nil: doTrace(el.node, level+1) + else : write stdout, " empty " + elif i < n.count : + traceln(space) + traceX(i) + write stdout, "clean: " + when T is string : + if el.key != nil: write stdout, el.key + else : write stdout, el.key + if el.node != nil: doTrace(el.node, level+1) + else : write stdout, " empty " + writeLine stdout,"" + + doTrace(root, 0) + +proc InsertItem[T,D](APath: RPath[T,D], ANode:PNode[T,D], Akey: T, Avalue: D) = + var x = - APath.Xi + inc(APath.Nd.count) + case APath.Nd.count + of cLen1: setLen(APath.Nd.slots, cLen2) + of cLen2: setLen(APath.Nd.slots, cLen3) + of cLen3: setLen(APath.Nd.slots, cLenCenter) + of cLenCenter: setLen(APath.Nd.slots, cLen4) + of cLen4: setLen(APath.Nd.slots, cLenMax) + else: discard + for i in countdown(APath.Nd.count.int - 1, x + 1): APath.Nd.slots[i] = move APath.Nd.slots[i - 1] + APath.Nd.slots[x] = setItem(Akey, Avalue, ANode) + + +proc SplitPage[T,D](n, left: PNode[T,D], xi: int, Akey:var T, Avalue:var D): PNode[T,D] = + var x = -xi + var it1: TItems[T,D] + it1.newSeq(cLenCenter) + new(result) + result.slots.newSeq(cLenCenter) + result.count = cCenter + if x == cCenter: + for i in 0..cCenter-1: + it1[i] = move left.slots[i] + for i in 0..cCenter-1: + result.slots[i] = move left.slots[cCenter + i] + result.left = n + else : + if x < cCenter : + for i in 0..x-1: + it1[i] = move left.slots[i] + it1[x] = setItem(Akey, Avalue, n) + for i in x+1 .. cCenter-1: + it1[i] = move left.slots[i-1] + var w = left.slots[cCenter-1] + Akey = w.key + Avalue = w.value + result.left = w.node + for i in 0..cCenter-1: + result.slots[i] = move left.slots[cCenter + i] + else : + for i in 0..cCenter-1: + it1[i] = move left.slots[i] + x = x - (cCenter + 1) + for i in 0..x-1: + result.slots[i] = move left.slots[cCenter + i + 1] + result.slots[x] = setItem(Akey, Avalue, n) + for i in x+1 .. cCenter-1: + result.slots[i] = move left.slots[cCenter + i] + var w = left.slots[cCenter] + Akey = w.key + Avalue = w.value + result.left = w.node + left.count = cCenter + left.slots = move it1 + + +proc internalPut[T,D](ANode: ref TNode[T,D], Akey: T, Avalue: D, Oldvalue: var D): ref TNode[T,D] = + var h: int + var Path: array[0..30, RPath[T,D]] + var left: PNode[T,D] + var n = ANode + + + result = ANode + h = 0 + while n != nil: + var x = bSearch[T,D](n, Akey) + if x <= 0 : + Path[h].Nd = n + Path[h].Xi = x + inc(h) + if x == 0 : + n = n.left + else : + x = (-x)-1 + if x < n.count : + n = n.slots[x].node + else : + n = nil + else : + var w = n.slots[x - 1] + Oldvalue = w.value + w.value = Avalue + return + + dec(h) + left = nil + var lkey = Akey + var lvalue = Avalue + while h >= 0 : + if Path[h].Nd.count < cLenMax : + InsertItem(Path[h], n, lkey, lvalue) + return + else : + left = Path[h].Nd + n = SplitPage(n, left, Path[h].Xi, lkey, lvalue) + dec(h) + + new(result) + result.slots.newSeq(cLen1) + result.count = 1 + result.left = left + result.slots[0] = setItem(lkey, lvalue, n) + + +proc CleanTree[T,D](n: PNode[T,D]): PNode[T,D] = + if n.left != nil : + n.left = CleanTree(n.left) + for i in 0 .. n.count - 1 : + var w = n.slots[i] + if w.node != nil : + w.node = CleanTree(w.node) + clean(w.value) + clean(w.key) + n.slots = nil + return nil + + +proc VisitAllNodes[T,D](n: PNode[T,D], visit: proc(n: PNode[T,D]): PNode[T,D] {.closure.} ): PNode[T,D] = + if n != nil : + if n.left != nil : + n.left = VisitAllNodes(n.left, visit) + for i in 0 .. n.count - 1 : + var w = n.slots[i] + if w.node != nil : + w.node = VisitAllNodes(w.node, visit) + return visit(n) + return nil + +proc VisitAllNodes[T,D](n: PNode[T,D], visit: proc(n: PNode[T,D]) {.closure.} ) = + if n != nil: + if n.left != nil : + VisitAllNodes(n.left, visit) + for i in 0 .. n.count - 1 : + var w = n.slots[i] + if w.node != nil : + VisitAllNodes(w.node, visit) + visit(n) + +proc VisitAll[T,D](n: PNode[T,D], visit: proc(Akey: T, Avalue: D) {.closure.} ) = + if n != nil: + if n.left != nil : + VisitAll(n.left, visit) + for i in 0 .. n.count - 1 : + var w = n.slots[i] + if not w.isClean : + visit(w.key, w.value) + if w.node != nil : + VisitAll(w.node, visit) + +proc VisitAll[T,D](n: PNode[T,D], visit: proc(Akey: T, Avalue: var D):bool {.closure.} ): PNode[T,D] = + if n != nil: + var n1 = n.left + if n1 != nil : + var n2 = VisitAll(n1, visit) + if n1 != n2 : + n.left = n2 + var i = 0 + while i < n.count : + var w = n.slots[i] + if not w.isClean : + if visit(w.key, w.value) : + result = DeleteItem(n, i) + if result == nil : return + dec(i) + n1 = w.node + if n1 != nil : + var n2 = VisitAll(n1, visit) + if n1 != n2 : + w.node = n2 + inc(i) + return n + +iterator keys* [T,D] (n: PNode[T,D]): T = + if n != nil : + var Path: array[0..20, RPath[T,D]] + var level = 0 + var nd = n + var i = -1 + while true : + if i < nd.count : + Path[level].Nd = nd + Path[level].Xi = i + if i < 0 : + if nd.left != nil : + nd = nd.left + inc(level) + else : inc(i) + else : + var w = nd.slots[i] + if not w.isClean() : + yield w.key + if w.node != nil : + nd = w.node + i = -1 + inc(level) + else : inc(i) + else : + dec(level) + if level < 0 : break + nd = Path[level].Nd + i = Path[level].Xi + inc(i) + +proc test() = + var oldvalue: int + var root = internalPut[int, int](nil, 312, 312, oldvalue) + var someOtherRoot = internalPut[string, int](nil, "312", 312, oldvalue) + var it1 = internalFind(root, 312) + echo it1.value + + for i in 1..1_000: + root = internalPut(root, i, i, oldvalue) + + var cnt = 0 + oldvalue = -1 + when true : # code compiles, when this or the other when is switched to false + for k in root.keys : + if k <= oldvalue : + echo k + oldvalue = k + inc(cnt) + echo cnt + when true : + cnt = 0 + VisitAll(root, proc(key, val: int) = inc(cnt)) + echo cnt + when true : + root = VisitAll(root, proc(key: int, value: var int): bool = + return key mod 2 == 0 ) + cnt = 0 + oldvalue = -1 + VisitAll(root, proc(key: int, value: int) {.closure.} = + if key <= oldvalue : + echo key + oldvalue = key + inc(cnt) ) + echo cnt + root = VisitAll(root, proc(key: int, value: var int): bool = + return key mod 2 != 0 ) + cnt = 0 + oldvalue = -1 + VisitAll(root, proc(key: int, value: int) {.closure.} = + if key <= oldvalue : + echo "error ", key + oldvalue = key + inc(cnt) ) + echo cnt + #traceTree(root) + +test() diff --git a/tests/generics/tgeneric_recursionlimit.nim b/tests/generics/tgeneric_recursionlimit.nim new file mode 100644 index 000000000..5fe9b43c6 --- /dev/null +++ b/tests/generics/tgeneric_recursionlimit.nim @@ -0,0 +1,123 @@ +discard """ + action: "compile" +""" + +# https://github.com/nim-lang/Nim/issues/20348 + +type + Payload[T] = object + payload: T + Carrier[T] = object + val: T + +type + Payload0*[T] = object + payload: Payload[T] + Payload1*[T] = object + payload: Payload[T] + Payload2*[T] = object + payload: Payload[T] + Payload3*[T] = object + payload: Payload[T] + Payload4*[T] = object + payload: Payload[T] + Payload5*[T] = object + payload: Payload[T] + Payload6*[T] = object + payload: Payload[T] + Payload7*[T] = object + payload: Payload[T] + Payload8*[T] = object + payload: Payload[T] + Payload9*[T] = object + payload: Payload[T] + Payload10*[T] = object + payload: Payload[T] + Payload11*[T] = object + payload: Payload[T] + Payload12*[T] = object + payload: Payload[T] + Payload13*[T] = object + payload: Payload[T] + Payload14*[T] = object + payload: Payload[T] + Payload15*[T] = object + payload: Payload[T] + Payload16*[T] = object + payload: Payload[T] + Payload17*[T] = object + payload: Payload[T] + Payload18*[T] = object + payload: Payload[T] + Payload19*[T] = object + payload: Payload[T] + Payload20*[T] = object + payload: Payload[T] + Payload21*[T] = object + payload: Payload[T] + Payload22*[T] = object + payload: Payload[T] + Payload23*[T] = object + payload: Payload[T] + Payload24*[T] = object + payload: Payload[T] + Payload25*[T] = object + payload: Payload[T] + Payload26*[T] = object + payload: Payload[T] + Payload27*[T] = object + payload: Payload[T] + Payload28*[T] = object + payload: Payload[T] + Payload29*[T] = object + payload: Payload[T] + Payload30*[T] = object + payload: Payload[T] + Payload31*[T] = object + payload: Payload[T] + Payload32*[T] = object + payload: Payload[T] + Payload33*[T] = object + payload: Payload[T] + +type + Carriers*[T] = object + c0*: Carrier[Payload0[T]] + c1*: Carrier[Payload1[T]] + c2*: Carrier[Payload2[T]] + c3*: Carrier[Payload3[T]] + c4*: Carrier[Payload4[T]] + c5*: Carrier[Payload5[T]] + c6*: Carrier[Payload6[T]] + c7*: Carrier[Payload7[T]] + c8*: Carrier[Payload8[T]] + c9*: Carrier[Payload9[T]] + c10*: Carrier[Payload10[T]] + c11*: Carrier[Payload11[T]] + c12*: Carrier[Payload12[T]] + c13*: Carrier[Payload13[T]] + c14*: Carrier[Payload14[T]] + c15*: Carrier[Payload15[T]] + c16*: Carrier[Payload16[T]] + c17*: Carrier[Payload17[T]] + c18*: Carrier[Payload18[T]] + c19*: Carrier[Payload19[T]] + c20*: Carrier[Payload20[T]] + c21*: Carrier[Payload21[T]] + c22*: Carrier[Payload22[T]] + c23*: Carrier[Payload23[T]] + c24*: Carrier[Payload24[T]] + c25*: Carrier[Payload25[T]] + c26*: Carrier[Payload26[T]] + c27*: Carrier[Payload27[T]] + c28*: Carrier[Payload28[T]] + c29*: Carrier[Payload29[T]] + c30*: Carrier[Payload30[T]] + c31*: Carrier[Payload31[T]] + c32*: Carrier[Payload32[T]] + c33*: Carrier[Payload33[T]] + +var carriers : Carriers[int] + +static: + assert $(typeof(carriers.c33.val)) == "Payload33[system.int]" diff --git a/tests/generics/tgenericdotrettype.nim b/tests/generics/tgenericdotrettype.nim new file mode 100644 index 000000000..3e4614752 --- /dev/null +++ b/tests/generics/tgenericdotrettype.nim @@ -0,0 +1,29 @@ +discard """ +output: '''string +int +(int, string) +''' +""" + +import typetraits + +type + Foo[T, U] = object + x: T + y: U + +proc bar[T](a: T): T.U = + echo result.type.name + +proc bas(x: auto): x.T = + echo result.type.name + +proc baz(x: Foo): (Foo.T, x.U) = + echo result.type.name + +var + f: Foo[int, string] + x = bar f + z = bas f + y = baz f + diff --git a/tests/generics/tgenericlambda.nim b/tests/generics/tgenericlambda.nim new file mode 100644 index 000000000..41ee74557 --- /dev/null +++ b/tests/generics/tgenericlambda.nim @@ -0,0 +1,23 @@ +discard """ + output: "10\n10\n1\n2\n3\n15" +""" + +proc test(x: proc (a, b: int): int) = + echo x(5, 5) + +test(proc (a, b: auto): auto = a + b) + +test do (a, b: auto) -> auto: a + b + +proc foreach[T](s: seq[T], body: proc(x: T)) = + for e in s: + body(e) + +foreach(@[1,2,3]) do (x: auto): + echo x + +proc foo = + let x = proc (a, b: int): auto = a + b + echo x(5, 10) + +foo() diff --git a/tests/generics/tgenericmatcher.nim b/tests/generics/tgenericmatcher.nim new file mode 100644 index 000000000..ca4dc2a4d --- /dev/null +++ b/tests/generics/tgenericmatcher.nim @@ -0,0 +1,40 @@ +discard """ + output: ''' +''' +""" + + +block tmatcher1: + type + TMatcherKind = enum + mkTerminal, mkSequence, mkAlternation, mkRepeat + TMatcher[T] = object + case kind: TMatcherKind + of mkTerminal: + value: T + of mkSequence, mkAlternation: + matchers: seq[TMatcher[T]] + of mkRepeat: + matcher: PMatcher[T] + min, max: int + PMatcher[T] = ref TMatcher[T] + + var m: PMatcher[int] + + +block tmatcher2: + type + TMatcherKind = enum + mkTerminal, mkSequence, mkAlternation, mkRepeat + TMatcher[T] = object + case kind: TMatcherKind + of mkTerminal: + value: T + of mkSequence, mkAlternation: + matchers: seq[TMatcher[T]] + of mkRepeat: + matcher: ref TMatcher[T] + min, max: int + + var m: ref TMatcher[int] + diff --git a/tests/generics/tgenericprocvar.nim b/tests/generics/tgenericprocvar.nim new file mode 100644 index 000000000..7935d90c2 --- /dev/null +++ b/tests/generics/tgenericprocvar.nim @@ -0,0 +1,37 @@ +discard """ + output: "0false12" +""" + +# Test multiple generic instantiation of generic proc vars: + +proc threadProcWrapper[TMsg]() = + var x: TMsg + stdout.write($x) + +#var x = threadProcWrapper[int] +#x() + +#var y = threadProcWrapper[bool] +#y() + +threadProcWrapper[int]() +threadProcWrapper[bool]() + +type + TFilterProc[T,D] = proc (item: T, env:D): bool {.nimcall.} + +proc filter[T,D](data: seq[T], env:D, pred: TFilterProc[T,D]): seq[T] = + result = @[] + for e in data: + if pred(e, env): result.add(e) + +proc predTest(item: int, value: int): bool = + return item <= value + +proc test(data: seq[int], value: int): seq[int] = + return filter(data, value, predTest) + +for x in items(test(@[1,2,3], 2)): + stdout.write(x) + +stdout.write "\n" diff --git a/tests/generics/tgenerics_issues.nim b/tests/generics/tgenerics_issues.nim new file mode 100644 index 000000000..3068a22f2 --- /dev/null +++ b/tests/generics/tgenerics_issues.nim @@ -0,0 +1,894 @@ +discard """ + output: ''' +4 +3 +(weight: 17.0, color: 100) +perm: 22 det: 22 +TMatrix[3, 3, system.int] +3 +@[0.9, 0.1] +U[3] +U[(f: 3)] +U[[3]] +b() +@[1, 2] +@[3, 4] +1 +concrete 88 +123 +1 +2 +3 +!!Hi!! +G:0,1:0.1 +G:0,1:0.1 +H:1:0.1 +0 +(foo: none(seq[Foo]), s: "") +(foo: some(@[(a: "world", bar: none(Bar))]), s: "hello,") +@[(a: "hey", bar: none(Bar))] +''' +joinable: false +""" + +import macros, sequtils, sets, sugar, tables, typetraits + +block t88: + type + BaseClass[V] = object of RootObj + b: V + + proc new[V](t: typedesc[BaseClass], v: V): BaseClass[V] = + BaseClass[V](b: v) + + proc baseMethod[V](v: BaseClass[V]): V = v.b + proc overriddenMethod[V](v: BaseClass[V]): V = v.baseMethod + + type + ChildClass[V] = object of BaseClass[V] + c: V + + proc new[V](t: typedesc[ChildClass], v1, v2: V): ChildClass[V] = + ChildClass[V](b: v1, c: v2) + + proc overriddenMethod[V](v: ChildClass[V]): V = v.c + + let c = ChildClass[string].new("Base", "Child") + + doAssert c.baseMethod == "Base" + doAssert c.overriddenMethod == "Child" + + + +block t4528: + type GenericBase[T] = ref object of RootObj + type GenericSubclass[T] = ref object of GenericBase[T] + proc foo[T](g: GenericBase[T]) = discard + var bar: GenericSubclass[int] + foo(bar) + + + +block t1050_5597: + type ArrayType[T] = distinct T + + proc arrayItem(a: ArrayType): auto = + static: echo(name(type(a).T)) + result = (type(a).T)(4) + + var arr: ArrayType[int] + echo arrayItem(arr) + + # bug #5597 + + template fail() = "what" + + proc g[T](x: var T) = + x.fail = 3 + + type + Obj = object + fail: int + + var y: Obj + g y + + + +block t1789: + type + Foo[N: static[int]] = object + + proc bindStaticN[N](foo: Foo[N]) = + var ar0: array[3, int] + var ar1: array[N, int] + var ar2: array[1..N, int] + var ar3: array[0..(N+10), float] + echo N + + var f: Foo[3] + f.bindStaticN + + # case 2 + + type + ObjectWithStatic[X, Y: static[int], T] = object + bar: array[X * Y, T] # this one works + + AliasWithStatic[X, Y: static[int], T] = array[X * Y, T] + + var + x: ObjectWithStatic[1, 2, int] + y: AliasWithStatic[2, 3, int] + + # case 3 + + type + Bar[N: static[int], T] = object + bar: array[N, T] + + proc `[]`[N, T](f: Bar[N, T], n: range[0..(N - 1)]): T = + doAssert high(n) == N-1 + result = f.bar[n] + + var b: Bar[3, int] + doAssert b[2] == 0 + + + +block t3977: + type Foo[N: static[int]] = object + + proc foo[N](x: Foo[N]) = + let n = N + doAssert N == n + + var f1: Foo[42] + f1.foo + + + +block t5570: + type + BaseFruit[T] = object of RootObj + color: T + + Banana[T] = object of BaseFruit[uint32] + weight: T + + macro printTypeName(typ: typed): untyped = + echo "type ", getType(typ).repr + + proc setColor[K](self: var BaseFruit[K], c: int) = + printTypeName(self.color) + self.color = uint32(c) + + var x: Banana[float64] + x.weight = 17 + printTypeName(x.color) + x.setColor(100) + echo x + + + +block t5643: + type + Matrix[M, N: static[int], T: SomeFloat] = object + data: ref array[N * M, T] + Matrix64[M, N: static[int]] = Matrix[M, N, float64] + + proc zeros64(M,N: static[int]): Matrix64[M,N] = + new result.data + for i in 0 ..< (M * N): + result.data[i] = 0'f64 + + proc bar[M,N: static[int], T](a: Matrix[M,N,T], b: Matrix[M,N,T]) = + discard + + let a = zeros64(2,2) + bar(a,a) + # https://github.com/nim-lang/Nim/issues/5643 + # + # The test case was failing here, because the compiler failed to + # detect the two matrix instantiations as the same type. + # + # The root cause was that the `T` type variable is a different + # type after the first Matrix type has been matched. + # + # Sigmatch was failing to match the second version of `T`, but + # due to some complex interplay between tyOr, tyTypeDesc and + # tyGenericParam this was allowed to went through. The generic + # instantiation of the second matrix was incomplete and the + # generic cache lookup failed, producing two separate types. + + + +block t5683: + type Matrix[M,N: static[int]] = array[M, array[N, float]] + + proc det[M,N](a: Matrix[M,N]): int = N*10 + M + proc perm[M,N](a: Matrix[M,N]): int = M*10 + N + + const + a = [ [1.0, 2.0] + , [3.0, 4.0] + ] + + echo "perm: ", a.perm, " det: ", a.det + + # This tests multiple instantiations of a generic + # proc involving static params: + type + Vector64[N: static[int]] = ref array[N, float64] + Array64[N: static[int]] = array[N, float64] + + proc vector[N: static[int]](xs: Array64[N]): Vector64[N] = + new result + for i in 0 ..< N: + result[i] = xs[i] + + let v1 = vector([1.0, 2.0, 3.0, 4.0, 5.0]) + let v2 = vector([1.0, 2.0, 3.0, 4.0, 5.0]) + let v3 = vector([1.0, 2.0, 3.0, 4.0]) + + + +block t7794: + type + Data[T:SomeNumber, U:SomeFloat] = ref object + x: T + value*: U + + var d = Data[int, float64](x:10.int, value:2'f64) + doAssert d.x == 10 + doAssert d.value == 2.0 + + + +block t8403: + proc sum[T](s: seq[T], R: typedesc): R = + var sum: R = 0 + for x in s: + sum += R(x) + return sum + + doAssert @[1, 2, 3].sum(float) == 6.0 + + + +block t8439: + type + Cardinal = enum + north, east, south, west + + proc foo[cardinal: static[Cardinal]](): int = 1 + doAssert foo[north]() == 1 + + + +block t8694: + when true: + # Error: undeclared identifier: '|' + proc bar[T](t:T): bool = + runnableExamples: + type Foo = int | float + true + doAssert bar(0) + + when true: + # ok + proc bar(t:int): bool = + runnableExamples: + type Foo = int | float + true + doAssert bar(0) + + when true: + # Error: undeclared identifier: '|' + proc bar(t:typedesc): bool = + runnableExamples: + type Foo = int | float + true + doAssert bar(int) + + + +block t9130: + when true: + # stack overflow + template baz1(iter: untyped): untyped = + runnableExamples: + import sugar + proc fun(a: proc(x:int): int) = discard + baz1(fun(x:int => x)) + discard + + proc foo1[A](ts: A) = + baz1(ts) + + when true: + # ok + template baz2(iter: untyped): untyped = + runnableExamples: + import sugar + proc fun(a: proc(x:int): int) = discard + baz2(fun(x:int => x)) + discard + + proc foo2(ts: int) = + baz2(ts) + + when true: + # stack overflow + template baz3(iter: untyped): untyped = + runnableExamples: + baz3(fun(x:int => x)) + discard + + proc foo3[A](ts: A) = + baz3(ts) + + + +block t1056: + type + TMatrix[N,M: static[int], T] = object + data: array[0..N*M-1, T] + + TMat2[T] = TMatrix[2,2,T] + + proc echoMatrix(a: TMatrix) = + echo a.type.name + echo TMatrix.N + + proc echoMat2(a: TMat2) = + echo TMat2.M + + var m = TMatrix[3,3,int](data: [1,2,3,4,5,6,7,8,9]) + + echoMatrix m + + + +block t4884: + type + Vec[N: static[int], T] = object + arr*: array[N, T] + + Mat[N,M: static[int], T] = object + arr: array[N, Vec[M,T]] + + var m : Mat[3,3,float] + var strMat : Mat[m.N, m.M, string] + var lenMat : Mat[m.N, m.M, int] + + + +block t2221: + var tblo: TableRef[string, int] + doAssert tblo == nil + + + +block t2304: + type TV2[T:SomeNumber] = array[0..1, T] + proc newV2T[T](x, y: T=0): TV2[T] = [x, y] + + let x = newV2T[float](0.9, 0.1) + echo(@x) + + + +block t2752: + proc myFilter[T](it: (iterator(): T), f: (proc(anything: T):bool)): (iterator(): T) = + iterator aNameWhichWillConflict(): T {.closure.}= + for x in it(): + if f(x): + yield x + result = aNameWhichWillConflict + + iterator testIt():int {.closure.}= + yield -1 + yield 2 + + #let unusedVariable = myFilter(testIt, (x: int) => x > 0) + + proc onlyPos(it: (iterator(): int)): (iterator(): int)= + iterator aNameWhichWillConflict(): int {.closure.}= + var filtered = onlyPos(myFilter(it, (x:int) => x > 0)) + for x in filtered(): + yield x + result = aNameWhichWillConflict + + let x = onlyPos(testIt) + + + +block t5106: + block: + type T = distinct int + + proc `+`(a, b: T): T = + T(int(a) + int(b)) + + type U[F: static[T]] = distinct int + + proc `+`[P1, P2: static[T]](a: U[P1], b: U[P2]): U[P1 + P2] = + U[P1 + P2](int(a) + int(b)) + + var a = U[T(1)](1) + var b = U[T(2)](2) + var c = a + b + echo c.type.name + + block: + type T = object + f: int + + proc `+`(a, b: T): T = + T(f: a.f + b.f) + + type U[F: static[T]] = distinct int + + proc `+`[P1, P2: static[T]](a: U[P1], b: U[P2]): U[P1 + P2] = + U[P1 + P2](int(a) + int(b)) + + var a = U[T(f: 1)](1) + var b = U[T(f: 2)](2) + var c = a + b + echo c.type.name + + block: + type T = distinct array[0..0, int] + + proc `+`(a, b: T): T = + T([array[0..0, int](a)[0] + array[0..0, int](b)[0]]) + + type U[F: static[T]] = distinct int + + proc `+`[P1, P2: static[T]](a: U[P1], b: U[P2]): U[P1 + P2] = + U[P1 + P2](int(a) + int(b)) + + var a = U[T([1])](1) + var b = U[T([2])](2) + var c = a + b + echo c.type.name + + + +block t3055: + proc b(t: int | string) + proc a(t: int) = b(t) + proc b(t: int | string) = echo "b()" + a(1) + + # test recursive generics still work: + proc fac[T](x: T): T = + if x == 0: return 1 + else: return fac(x-1)*x + + doAssert fac(6) == 720 + doAssert fac(5.0) == 120.0 + + + # test recursive generic with forwarding: + proc fac2[T](x: T): T + + doAssert fac2(6) == 720 + doAssert fac2(5.0) == 120.0 + + proc fac2[T](x: T): T = + if x == 0: return 1 + else: return fac2(x-1)*x + + + +block t1187: + type + TEventArgs = object + skip: bool + TEventHandler[T] = proc (e: var TEventArgs, data: T) {.closure.} + TEvent[T] = object + #handlers: seq[TEventHandler[T]] # Does not work + handlers: seq[proc (e: var TEventArgs, d: T) {.closure.}] # works + + TData = object + x: int + + TSomething = object + s: TEvent[TData] + + proc init[T](e: var TEvent[T]) = + e.handlers.newSeq(0) + + #proc add*[T](e: var TEvent[T], h: proc (e: var TEventArgs, data: T) {.closure.}) = + # this line works + proc add[T](e: var TEvent[T], h: TEventHandler[T]) = + # this line does not work + e.handlers.add(h) + + proc main () = + var something: TSomething + something.s.init() + var fromOutside = 4711 + + something.s.add() do (e: var TEventArgs, data: TData): + var x = data.x + x = fromOutside + + main() + + + +block t1919: + type + Base[M] = object of RootObj + a : M + Sub1[M] = object of Base[M] + b : int + Sub2[M] = object of Sub1[M] + c : int + + var x: Sub2[float] + doAssert x.a == 0.0 + + + +block t5756: + type + Vec[N : static[int]] = object + x: int + arr: array[N, int32] + + Mat[M,N: static[int]] = object + x: int + arr: array[M, Vec[N]] + + proc vec2(x,y:int32) : Vec[2] = + result.arr = [x,y] + result.x = 10 + + proc mat2(a,b: Vec[2]): Mat[2,2] = + result.arr = [a,b] + result.x = 20 + + const M = mat2(vec2(1, 2), vec2(3, 4)) + + let m1 = M + echo @(m1.arr[0].arr) + echo @(m1.arr[1].arr) + + proc foo = + let m2 = M + echo m1.arr[0].arr[0] + + foo() + + + +block t7854: + type + Stream = ref StreamObj + StreamObj = object of RootObj + + InhStream = ref InhStreamObj + InhStreamObj = object of Stream + f: string + + proc newInhStream(f: string): InhStream = + new(result) + result.f = f + + var val: int + let str = newInhStream("input_file.json") + + block: + # works: + proc load[T](data: var T, s: Stream) = + discard + load(val, str) + + block: + # works + proc load[T](s: Stream, data: T) = + discard + load(str, val) + + block: + # broken + proc load[T](s: Stream, data: var T) = + discard + load(str, val) + + + +block t5864: + proc defaultStatic(s: openArray, N: static[int] = 1): int = N + proc defaultGeneric[T](a: T = 2): int = a + + let a = [1, 2, 3, 4].defaultStatic() + let b = defaultGeneric() + + doAssert a == 1 + doAssert b == 2 + + + +block t3498: + template defaultOf[T](t: T): untyped = (var d: T; d) + + doAssert defaultOf(1) == 0 + + # assignment using template + + template tassign[T](x: var seq[T]) = + x = @[1, 2, 3] + + var y: seq[int] + tassign(y) #<- x is expected = @[1, 2, 3] + tassign(y) + + doAssert y[0] == 1 + doAssert y[1] == 2 + doAssert y[2] == 3 + + + +block t3499: + proc foo[T](x: proc(): T) = + echo "generic ", x() + + proc foo(x: proc(): int) = + echo "concrete ", x() + + # note the following 'proc' is not .closure! + foo(proc (): auto {.nimcall.} = 88) + + # bug #3499 last snippet fixed + # bug 705 last snippet fixed + + + + +block t797: + proc foo[T](s:T):string = $s + + type IntStringProc = proc(x: int): string + + var f1 = IntStringProc(foo) + var f2: proc(x: int): string = foo + var f3: IntStringProc = foo + + echo f1(1), f2(2), f3(3) + + for x in map([1,2,3], foo): echo x + + + +block t4658: + var x = 123 + proc twice[T](f: T -> T): T -> T = (x: T) => f(f(x)) + proc quote(s: string): string = "!" & s & "!" + echo twice(quote)("Hi") + + + +block t4589: + type SimpleTable[TKey, TVal] = TableRef[TKey, TVal] + template newSimpleTable(TKey, TVal: typedesc): SimpleTable[TKey, TVal] = newTable[TKey, TVal]() + var fontCache : SimpleTable[string, SimpleTable[int32, int]] + fontCache = newSimpleTable(string, SimpleTable[int32, int]) + + + +block t4600: + template foo(x: untyped): untyped = echo 1 + template foo(x,y: untyped): untyped = echo 2 + + proc bar1[T](x: T) = foo(x) + proc bar2(x: float) = foo(x,x) + proc bar3[T](x: T) = foo(x,x) + + + +block t4672: + type + EnumContainer[T: enum] = object + v: T + SomeEnum {.pure.} = enum + A,B,C + + proc value[T: enum](this: EnumContainer[T]): T = + this.v + + var enumContainer: EnumContainer[SomeEnum] + discard enumContainer.value() + + + +block t4863: + type + G[i,j: static[int]] = object + v:float + H[j: static[int]] = G[0,j] + proc p[i,j: static[int]](x:G[i,j]) = echo "G:", i, ",", j, ":", x.v + proc q[j: static[int]](x:H[j]) = echo "H:", j, ":", x.v + + var + g0 = G[0,1](v: 0.1) + h0:H[1] = g0 + p(g0) + p(h0) + q(h0) + + + +block t1684: + type + BaseType {.inheritable pure.} = object + idx: int + + DerivedType {.final pure.} = object of BaseType + + proc index[Toohoo: BaseType](h: Toohoo): int {.inline.} = h.idx + proc newDerived(idx: int): DerivedType {.inline.} = DerivedType(idx: idx) + + let d = newDerived(2) + doAssert(d.index == 2) + + + +block t5632: + type Option[T] = object + + proc point[A](v: A, t: typedesc[Option[A]]): Option[A] = + discard + + discard point(1, Option) + + + +block t7247: + type n8 = range[0'i8..127'i8] + var tab = initHashSet[n8]() + doAssert tab.contains(8) == false + + + +block t3717: + type + Foo[T] = object + a: T + Foo1[T] = Foo[T] | int + + proc foo[T](s: Foo1[Foo[T]]): T = + 5 + + var f: Foo[Foo[int]] + discard foo(f) + + + +block: # issue #9458 + type + Option[T] = object + val: T + has: bool + + Bar = object + + proc none(T: typedesc): Option[T] = + discard + + proc foo[T](self: T; x: Option[Bar] = Bar.none) = + discard + + foo(1) + + +# bug #8426 +type + MyBool[T: uint] = range[T(0)..T(1)] # Works + +var x: MyBool[uint] +echo x + +# x = 2 # correctly prevented + +type + MyBool2 = range[uint(0)..uint(1)] # Error ordinal or float type expected + + +# bug #10396 +import options, strutils + +type + Foo {.acyclic.} = object + a: string + bar: Option[Bar] + + Bar {.acyclic.} = object + foo: Option[seq[Foo]] # if this was just Option[Foo], everything works fine + s: string + +proc getBar(x: string): Bar + +proc intoFoos(ss: seq[string]): seq[Foo] = + result = @[] + for s in ss: + let spl = s.split(',') + if spl.len > 1: + result.add Foo(a: spl[0], + bar: some(getBar(spl[1]))) + else: + result.add Foo(a: s, + bar: none[Bar]()) + +proc getBar(x: string): Bar = + let spl = x.split(' ') + result = + if spl.len > 1: + Bar(foo: some(spl[1..high(spl)].intoFoos), + s: spl[0]) + else: + Bar(foo: none[seq[Foo]](), + s: "") + +proc fakeReadLine(): string = "hey" + +echo getBar(fakeReadLine()) # causes error + +echo getBar("hello, world") # causes error + +discard $getBar(fakeReadLine()) # causes error + +discard $getBar("hello, world") # causes error + +discard getBar(fakeReadLine()) # no error + +discard getBar("hello, world") # no error + +echo intoFoos(fakeReadLine().split(' ')) # no error, works as expected + + +# bug #14990 +type + Tile3 = Tile2 + Tile2 = Tile + Tile[n] = object + a: n + +var a: Tile3[int] + +block: # Ensure no segfault from constraint + type + Regex[A: SomeOrdinal] = ref object + val: Regex[A] + MyConstraint = (seq or enum or set) + MyOtherType[A: MyConstraint] = ref object + val: MyOtherType[A] + + var + a = Regex[int]() + b = Regex[bool]() + c = MyOtherType[seq[int]]() + +block: # https://github.com/nim-lang/Nim/issues/20416 + type + Item[T] = object + link:ptr Item[T] + data:T + + KVSeq[A,B] = seq[(A,B)] + + MyTable[A,B] = object + data: KVSeq[A,B] + + Container[T] = object + a: MyTable[int,ref Item[T]] + + proc p1(sg:Container) = discard # Make sure that a non parameterized 'Container' argument still compiles + + proc p2[T](sg:Container[T]) = discard + var v : Container[int] + p2(v) diff --git a/tests/generics/tgenerics_various.nim b/tests/generics/tgenerics_various.nim new file mode 100644 index 000000000..53661236e --- /dev/null +++ b/tests/generics/tgenerics_various.nim @@ -0,0 +1,254 @@ +discard """ + output: ''' +we +direct +generic +generic +''' +joinable: false +""" + +import algorithm, sugar, sequtils, typetraits, asyncdispatch + +block tconfusing_arrow: + type Deck = object + value: int + + proc sort(h: var seq[Deck]) = + # works: + h.sort(proc (x, y: Deck): auto = + cmp(x.value, y.value)) + # fails: + h.sort((x, y: Deck) => cmp(ord(x.value), ord(y.value))) + + var player: seq[Deck] = @[] + player.sort() + + + +block tdictdestruct: + type + TDict[TK, TV] = object + k: TK + v: TV + PDict[TK, TV] = ref TDict[TK, TV] + + proc fakeNew[T](x: var ref T, destroy: proc (a: ref T) {.nimcall.}) = + discard + + proc destroyDict[TK, TV](a: PDict[TK, TV]) = + return + proc newDict[TK, TV](a: TK, b: TV): PDict[TK, TV] = + fakeNew(result, destroyDict[TK, TV]) + + # Problem: destroyDict is not instantiated when newDict is instantiated! + discard newDict("a", "b") + + + +block tgenericdefaults: + type + TFoo[T, U, R = int] = object + x: T + y: U + z: R + + TBar[T] = TFoo[T, array[4, T], T] + + var x1: TFoo[int, float] + + static: + doAssert type(x1.x) is int + doAssert type(x1.y) is float + doAssert type(x1.z) is int + + var x2: TFoo[string, R = float, U = seq[int]] + + static: + doAssert type(x2.x) is string + doAssert type(x2.y) is seq[int] + doAssert type(x2.z) is float + + var x3: TBar[float] + + static: + doAssert type(x3.x) is float + doAssert type(x3.y) is array[4, float] + doAssert type(x3.z) is float + + + +block tprop: + type + TProperty[T] = object of RootObj + getProc: proc(property: TProperty[T]): T {.nimcall.} + setProc: proc(property: TProperty[T], value: T) {.nimcall.} + value: T + + proc newProperty[T](value: RootObj): TProperty[T] = + result.getProc = proc (property: TProperty[T]) = + return property.value + + + +block trefs: + type + PA[T] = ref TA[T] + TA[T] = object + field: T + var a: PA[string] + new(a) + a.field = "some string" + + proc someOther[T](len: string): seq[T] = discard + proc someOther[T](len: int): seq[T] = echo "we" + + proc foo[T](x: T) = + var s = someOther[T](34) + #newSeq[T](34) + + foo 23 + + when false: + # Compiles unless you use var a: PA[string] + type + PA = ref TA + TA[T] = object + + # Cannot instantiate: + type + TA[T] = object + a: PA[T] + PA[T] = ref TA[T] + + type + PA[T] = ref TA[T] + TA[T] = object + + + +block tmap_auto: + let x = map(@[1, 2, 3], x => x+10) + doAssert x == @[11, 12, 13] + + let y = map(@[(1,"a"), (2,"b"), (3,"c")], x => $x[0] & x[1]) + doAssert y == @["1a", "2b", "3c"] + + proc eatsTwoArgProc[T,S,U](a: T, b: S, f: proc(t: T, s: S): U): U = + f(a,b) + + let z = eatsTwoArgProc(1, "a", (t,s) => $t & s) + doAssert z == "1a" + + + +block tproctypecache_falsepositive: + type + Callback = proc() {.closure, gcsafe.} + GameState = ref object + playerChangeHandlers: seq[Callback] + + proc newGameState(): GameState = + result = GameState( + playerChangeHandlers: newSeq[Callback]() # this fails + ) + + + +block tptrinheritance: + type NSPasteboardItem = ptr object + type NSPasteboard = ptr object + type NSArrayAbstract {.inheritable.} = ptr object + type NSMutableArrayAbstract = ptr object of NSArrayAbstract + type NSArray[T] = ptr object of NSArrayAbstract + type NSMutableArray[T] = ptr object of NSArray[T] + + proc newMutableArrayAbstract(): NSMutableArrayAbstract = discard + + template newMutableArray(T: typedesc): NSMutableArray[T] = + cast[NSMutableArray[T]](newMutableArrayAbstract()) + + proc writeObjects(p: NSPasteboard, o: NSArray[NSPasteboardItem]) = discard + + let a = newMutableArray NSPasteboardItem + var x: NSMutableArray[NSPasteboardItem] + var y: NSArray[NSPasteboardItem] = x + + writeObjects(nil, a) + + + +block tsigtypeop: + type Vec3[T] = array[3, T] + + proc foo(x: Vec3, y: Vec3.T, z: x.T): x.type.T = + return 10 + + var y: Vec3[int] = [1, 2, 3] + var z: int = foo(y, 3, 4) + + + +block tvarargs_vs_generics: + proc withDirectType(args: string) = + echo "direct" + proc withDirectType[T](arg: T) = + echo "generic" + proc withOpenArray(args: openArray[string]) = + echo "openArray" + proc withOpenArray[T](arg: T) = + echo "generic" + proc withVarargs(args: varargs[string]) = + echo "varargs" + proc withVarargs[T](arg: T) = + echo "generic" + + withDirectType "string" + withOpenArray "string" + withVarargs "string" + +block: + type + Que[T] {.gcsafe.} = object + x: T + + proc `=`[T](q: var Que[T]; x: Que[T]) = + discard + + var x: Que[int] + doAssert(x.x == 0) + + +# bug #4466 +proc identity[T](t: T): T = t + +proc doSomething[A, B](t: tuple[a: A, b: B]) = discard + +discard identity((c: 1, d: 2)) +doSomething(identity((1, 2))) + +# bug #6231 +proc myProc[T, U](x: T or U) = discard + +myProc[int, string](x = 2) + +block: # issue #8390 + proc x[T:SomeFloat](q: openarray[T], y: T = 1): string = + doAssert $q.type == $openarray[y.type] + $y.type + + doAssert x(@[1.0]) == $1.0.type + + +block: # issue #9381 + var evalCount {.compileTime.} = 0 + + macro test(t: typed): untyped = + inc evalCount + t + + type GenericObj[T] = object + f: test(T) + + var x: GenericObj[int] + static: doAssert evalCount == 1 diff --git a/tests/generics/tgenerictmpl2.nim b/tests/generics/tgenerictmpl2.nim new file mode 100644 index 000000000..2efb000b3 --- /dev/null +++ b/tests/generics/tgenerictmpl2.nim @@ -0,0 +1,31 @@ +discard """ + output: '''1 +1 +1 +1 +999 +999 +999 +2''' +""" + +# test if we can pass explicit generic arguments to generic templates +# based on bug report #3496 + +proc tproc[T](t: T = 999) = echo t +template ttmpl[T](t: T = 999) = echo t + +tproc(1) +tproc[int](1) +ttmpl(1) +ttmpl[int](1) #<- crash case #1 + +tproc[int]() +let _ = tproc[int] +ttmpl[int]() #<- crash case #2 +ttmpl[int] #<- crash case #3 + +# but still allow normal use of [] on non-generic templates + +template tarr: untyped = [1, 2, 3, 4] +echo tarr[1] diff --git a/tests/generics/tgenericvariant.nim b/tests/generics/tgenericvariant.nim new file mode 100644 index 000000000..5ba3a2e7c --- /dev/null +++ b/tests/generics/tgenericvariant.nim @@ -0,0 +1,36 @@ +discard """ +output: ''' +Test +abcxyz123 +''' +""" + +proc fakeReadLine(): string = + "abcxyz123" + +type + TMaybe[T] = object + case empty: bool + of false: value: T + else: nil + +proc Just*[T](val: T): TMaybe[T] = + result.empty = false + result.value = val + +proc Nothing[T](): TMaybe[T] = + result.empty = true + +proc safeReadLine(): TMaybe[string] = + var r = fakeReadLine() + if r == "": return Nothing[string]() + else: return Just(r) + +proc main() = + var Test = Just("Test") + echo(Test.value) + var mSomething = safeReadLine() + echo(mSomething.value) + mSomething = safeReadLine() + +main() diff --git a/tests/generics/tgenericwhen.nim b/tests/generics/tgenericwhen.nim new file mode 100644 index 000000000..87672a699 --- /dev/null +++ b/tests/generics/tgenericwhen.nim @@ -0,0 +1,58 @@ +discard """ + targets: "c js" +""" + +block: # issue #24041 + type ArrayBuf[N: static int, T = byte] = object + when sizeof(int) > sizeof(uint8): + when N <= int(uint8.high): + n: uint8 + else: + when sizeof(int) > sizeof(uint16): + when N <= int(uint16.high): + n: uint16 + else: + when sizeof(int) > sizeof(uint32): + when N <= int(uint32.high): + n: uint32 + else: + n: int + else: + n: int + else: + n: int + else: + n: int + + var x: ArrayBuf[8] + doAssert x.n is uint8 + when sizeof(int) > sizeof(uint32): + var y: ArrayBuf[int(uint32.high) * 8] + doAssert y.n is int + +block: # constant condition after dynamic one + type Foo[T] = object + when T is int: + a: int + elif true: + a: string + else: + a: bool + var x: Foo[string] + doAssert x.a is string + var y: Foo[int] + doAssert y.a is int + var z: Foo[float] + doAssert z.a is string + +block: # issue #4774, but not with threads + const hasThreadSupport = not defined(js) + when hasThreadSupport: + type Channel[T] = object + value: T + type + SomeObj[T] = object + when hasThreadSupport: + channel: ptr Channel[T] + var x: SomeObj[int] + doAssert compiles(x.channel) == hasThreadSupport diff --git a/tests/generics/tgensyminst.nim b/tests/generics/tgensyminst.nim new file mode 100644 index 000000000..3f30188d8 --- /dev/null +++ b/tests/generics/tgensyminst.nim @@ -0,0 +1,29 @@ +# issue #24048 + +import macros + +proc map(fn: proc(val: int): void) = fn(1) + +# This works fine, and is the exact same function call as what's +# generated by the macro `aBug`. +map proc(val: auto): void = + let variable = 123 + +macro aBug() = + # 1. let sym = ident("variable") + let sym = genSym(nskLet, "variable") + let letStmt = newLetStmt(sym, newLit(123)) + + let lambda = newProc( + params = @[ + ident("void"), + newIdentDefs(ident("val"), ident("auto")), + # 2. newIdentDefs(ident("val"), ident("int")), + ], + body = newStmtList(letStmt), + procType = nnkLambda + ) + + result = newCall(bindSym("map"), lambda) + +aBug() diff --git a/tests/generics/timpl_ast.nim b/tests/generics/timpl_ast.nim new file mode 100644 index 000000000..97fe128cd --- /dev/null +++ b/tests/generics/timpl_ast.nim @@ -0,0 +1,80 @@ +import std/macros +import std/assertions + +block: # issue #16639 + type Foo[T] = object + when true: + x: float + + type Bar = object + when true: + x: float + + macro test() = + let a = getImpl(bindSym"Foo")[^1] + let b = getImpl(bindSym"Bar")[^1] + doAssert treeRepr(a) == treeRepr(b) + + test() + +import strutils + +block: # issues #9899, ##14708 + macro implRepr(a: typed): string = + result = newLit(repr(a.getImpl)) + + type + Option[T] = object + when false: discard # issue #14708 + when false: x: int + when T is (ref | ptr): + val: T + else: + val: T + has: bool + + static: # check information is retained + let r = implRepr(Option) + doAssert "when T is" in r + doAssert r.count("val: T") == 2 + doAssert "has: bool" in r + + block: # try to compile the output + macro parse(s: static string) = + result = parseStmt(s) + parse("type " & implRepr(Option)) + +block: # issue #22639 + type + Spectrum[N: static int] = object + data: array[N, float] + AngleInterpolator = object + data: seq[Spectrum[60]] + proc initInterpolator(num: int): AngleInterpolator = + result = AngleInterpolator() + for i in 0 ..< num: + result.data.add Spectrum[60]() + macro genCompatibleTuple(t: typed): untyped = + let typ = t.getType[1].getTypeImpl[2] + result = nnkTupleTy.newTree() + for i, ch in typ: # is `nnkObjectTy` + result.add nnkIdentDefs.newTree(ident(ch[0].strVal), # ch is `nnkIdentDefs` + ch[1], + newEmptyNode()) + proc fullSize[T: object | tuple](x: T): int = + var tmp: genCompatibleTuple(T) + result = 0 + for field, val in fieldPairs(x): + result += sizeof(val) + doAssert result == sizeof(tmp) + + let reflectivity = initInterpolator(1) + for el in reflectivity.data: + doAssert fullSize(el) == sizeof(el) + doAssert fullSize(reflectivity.data[0]) == sizeof(reflectivity.data[0]) + doAssert genCompatibleTuple(Spectrum[60]) is tuple[data: array[60, float]] + doAssert genCompatibleTuple(Spectrum[120]) is tuple[data: array[120, float]] + type Foo[T] = object + data: T + doAssert genCompatibleTuple(Foo[int]) is tuple[data: int] + doAssert genCompatibleTuple(Foo[float]) is tuple[data: float] diff --git a/tests/generics/timplicit_and_explicit.nim b/tests/generics/timplicit_and_explicit.nim new file mode 100644 index 000000000..7220b7429 --- /dev/null +++ b/tests/generics/timplicit_and_explicit.nim @@ -0,0 +1,65 @@ + +block: # basic test + proc doStuff[T](a: SomeInteger): T = discard + proc doStuff[T;Y](a: SomeInteger, b: Y): Y = discard + assert typeof(doStuff[int](100)) is int + assert typeof(doStuff[int, float](100, 1.0)) is float + assert typeof(doStuff[int, string](100, "Hello")) is string + + proc t[T](x: T; z: int | float): seq[T] = result.add(x & $z) + + assert t[string]("Hallo", 2.0) == @["Hallo" & $2.0] + + proc t2[T](z: int | float): seq[T] = result.add($z) + + assert t2[string](2.0) == @[$2.0] + +block: # template test + template someThing[T;Y](a: SomeFloat, b: SomeOrdinal): (T, Y) = (a, b) + assert typeof(someThing[float64, int](1.0, 100)) is (float64, int) + +block: # static test + proc t[T](s: static bool) = discard + proc t2[T](s: static string) = discard + t[string](true) + t2[int]("hello") + t2[string]("world") + t2[float]("test222222") + +block: #11152 + proc f[T](X: typedesc) = discard + f[int](string) + +block: #15622 + proc test1[T](a: T, b: static[string] = "") = discard + test1[int64](123) + proc test2[T](a: T, b: static[string] = "") = discard + doAssert not (compiles do: + test2[int64, static[string]](123)) + +block: #4688 + proc convertTo[T](v: int or float): T = (T)(v) + discard convertTo[float](1) + +block: #4164 + proc printStr[T](s: static[string]): T = discard + discard printStr[int]("hello static") + +import macros + +block: # issue #9040, statics with template, macro, symchoice explicit generics + block: # macro + macro fun[N: static int](): untyped = + newLit 1 + const a = fun[2]() + doAssert a == 1 + block: # template + template fun[N: static int](): untyped = + 1 + const a = fun[2]() + doAssert a == 1 + block: # symchoice + proc newSeq[x: static int](): int = 1 + template foo: int = + newSeq[2]() + doAssert foo() == 1 diff --git a/tests/generics/timports.nim b/tests/generics/timports.nim new file mode 100644 index 000000000..e252ad194 --- /dev/null +++ b/tests/generics/timports.nim @@ -0,0 +1,63 @@ +discard """ + output: ''' +317 +TEST2 +5 5 5 +false +''' +""" + +import mbind_bracket, mclosed_sym, mdotlookup, mmodule_same_as_proc, mtypenodes + + +block tbind_bracket: + # bug #2599 + # also test that `[]` can be passed now as a first class construct: + + template takeBracket(x, a, i: untyped) = + echo x(a, i) + + var a: array[10, int] + a[8] = 317 + + takeBracket(`[]`, a, 8) + + let reg = newRegistry[UUIDObject]() + reg.register(UUIDObject()) + + +block tclosed_sym: + # bug #2664 + proc same(r:R, d:int) = echo "TEST1" + doIt(Data[int](d:123), R()) + +import strutils, unicode # ambiguous `strip` + +block tdotlookup: + foo(7) + # bug #1444 + fn(4) + doAssert doStrip(123) == "123" + # bug #14254 + doAssert baz2[float](1'i8) == 1 + # bug #21883 + proc abc[T: not not int](x: T): T = + var x = x + x.set("hello", "world") + result = x + doAssert abc(5) == 10 + block: # ensure normal call is consistent with dot call + proc T(x: int): float = x.float + proc foo[T](x: int) = + doAssert typeof(T(x)) is typeof(x.T) + foo[uint](123) + +block tmodule_same_as_proc: + # bug #1965 + proc test[T](t: T) = + mmodule_same_as_proc"a" + test(0) + +block ttypenodes: + # issue #22699 + doAssert chop[bool](42) == 42 diff --git a/tests/generics/tinheritable_importcpp.nim b/tests/generics/tinheritable_importcpp.nim new file mode 100644 index 000000000..8ee18b70b --- /dev/null +++ b/tests/generics/tinheritable_importcpp.nim @@ -0,0 +1,10 @@ +discard """ + targets: "cpp" + action: compile +""" + +# #4651 +type + Vector[T] {.importcpp: "std::vector<'0 >", header: "vector", inheritable.} = object + VectorDerived {.importcpp: "SomeVectorDerived", nodecl.} = object of Vector[int] + # Error: inheritance only works with non-final objects diff --git a/tests/generics/tlateboundgenericparams.nim b/tests/generics/tlateboundgenericparams.nim new file mode 100644 index 000000000..9f0580fd2 --- /dev/null +++ b/tests/generics/tlateboundgenericparams.nim @@ -0,0 +1,145 @@ +discard """ + output: "1\n10\n1\n10" + nimout: ''' +bar instantiated with 1 +bar instantiated with 10 +''' +""" + +import typetraits + +type + Foo = object + +proc defaultFoo: Foo = discard +proc defaultInt: int = 1 +proc defaultTInt(T: type): int = 2 +proc defaultTFoo[T](x: typedesc[T]): Foo = discard +proc defaultTOldSchool[T](x: typedesc[T]): T = discard +proc defaultTModern(T: type): T = discard + +proc specializedDefault(T: type int): int = 10 +proc specializedDefault(T: type string): string = "default" + +converter intFromFoo(x: Foo): int = 3 + +proc consumeInt(x: int) = + discard + +const activeTests = {1..100} + +when true: + template test(n, body) = + when n in activeTests: + block: + body + + template reject(x) = + static: assert(not compiles(x)) + + test 1: + proc t[T](val: T = defaultInt()) = + consumeInt val + + t[int]() + reject t[string]() + + test 2: + proc t1[T](val: T = defaultFoo()) = + static: + assert type(val).name == "int" + assert T.name == "int" + + consumeInt val + + # here, the converter should kick in, but notice + # how `val` is still typed `int` inside the proc. + t1[int]() + + proc t2[T](val: T = defaultFoo()) = + discard + + reject t2[string]() + + test 3: + proc tInt[T](val = defaultInt()): string = + return type(val).name + + doAssert tInt[int]() == "int" + doAssert tInt[string]() == "int" + + proc tInt2[T](val = defaultTInt(T)): string = + return type(val).name + + doAssert tInt2[int]() == "int" + doAssert tInt2[string]() == "int" + + proc tDefTModern[T](val = defaultTModern(T)): string = + return type(val).name + + doAssert tDefTModern[int]() == "int" + doAssert tDefTModern[string]() == "string" + doAssert tDefTModern[Foo]() == "Foo" + + proc tDefTOld[T](val = defaultTOldSchool(T)): string = + return type(val).name + + doAssert tDefTOld[int]() == "int" + doAssert tDefTOld[string]() == "string" + doAssert tDefTOld[Foo]() == "Foo" + + test 4: + proc t[T](val: T = defaultTFoo(T)): string = + return type(val).name + + doAssert t[int]() == "int" + doAssert t[Foo]() == "Foo" + reject t[string]() + + test 5: + proc t1[T](a: T = specializedDefault(T)): T = + return a + + doAssert t1[int]() == 10 + doAssert t1[string]() == "default" + + proc t2[T](a: T, b = specializedDefault(T)): auto = + return $a & $b + + doAssert t2(5) == "510" + doAssert t2("string ") == "string default" + + proc t3[T](a: T, b = specializedDefault(type(a))): auto = + return $a & $b + + doAssert t3(100) == "10010" + doAssert t3("another ") == "another default" + + test 6: + # https://github.com/nim-lang/Nim/issues/5595 + type + Point[T] = object + x, y: T + + proc getOrigin[T](): Point[T] = Point[T](x: 0, y: 0) + + proc rotate[T](point: Point[T], radians: float, + origin = getOrigin[T]()): Point[T] = + discard + + var p = getOrigin[float]() + var rotated = p.rotate(2.1) + + test 7: + proc bar(x: static[int]) = + static: echo "bar instantiated with ", x + echo x + + proc foo(x: static[int] = 1) = + bar(x) + + foo() + foo(10) + foo(1) + foo(10) + diff --git a/tests/generics/tlateboundstatic.nim b/tests/generics/tlateboundstatic.nim new file mode 100644 index 000000000..90b44aa8e --- /dev/null +++ b/tests/generics/tlateboundstatic.nim @@ -0,0 +1,16 @@ +discard """ + nimout: "array[0..3, int]" +""" + +type + KK[I: static[int]] = object + x: array[I, int] + +proc foo(a: static[string]): KK[a.len] = + result.x[0] = 12 + +var x = foo "test" + +import typetraits +static: echo x.x.type.name + diff --git a/tests/generics/tmacroinjectedsym.nim b/tests/generics/tmacroinjectedsym.nim new file mode 100644 index 000000000..985e415f2 --- /dev/null +++ b/tests/generics/tmacroinjectedsym.nim @@ -0,0 +1,186 @@ +{.experimental: "openSym".} + +block: # issue #22605, normal call syntax + const error = "bad" + + template valueOr(self: int, def: untyped): untyped = + case false + of true: "" + of false: + template error: untyped {.used, inject.} = "good" + def + + proc g(T: type): string = + let x = valueOr 123: + return $error + + "ok" + + doAssert g(int) == "good" + + proc g2(T: type): string = + bind error # use the bad version on purpose + let x = valueOr 123: + return $error + + "ok" + + doAssert g2(int) == "bad" + +block: # issue #22605, method call syntax + const error = "bad" + + template valueOr(self: int, def: untyped): untyped = + case false + of true: "" + of false: + template error: untyped {.used, inject.} = "good" + def + + proc g(T: type): string = + let x = 123.valueOr: + return $error + + "ok" + + doAssert g(int) == "good" + + proc g2(T: type): string = + bind error # use the bad version on purpose + let x = 123.valueOr: + return $error + + "ok" + + doAssert g2(int) == "bad" + +block: # issue #22605, original complex example + type Xxx = enum + error + value + + type + Result[T, E] = object + when T is void: + when E is void: + oResultPrivate*: bool + else: + case oResultPrivate*: bool + of false: + eResultPrivate*: E + of true: + discard + else: + when E is void: + case oResultPrivate*: bool + of false: + discard + of true: + vResultPrivate*: T + else: + case oResultPrivate*: bool + of false: + eResultPrivate*: E + of true: + vResultPrivate*: T + + template valueOr[T: not void, E](self: Result[T, E], def: untyped): untyped = + let s = (self) # TODO avoid copy + case s.oResultPrivate + of true: + s.vResultPrivate + of false: + when E isnot void: + template error: untyped {.used, inject.} = s.eResultPrivate + def + + proc f(): Result[int, cstring] = + Result[int, cstring](oResultPrivate: false, eResultPrivate: "f") + + proc g(T: type): string = + let x = f().valueOr: + return $error + + "ok" + + doAssert g(int) == "f" + + proc g2(T: type): string = + bind error # use the bad version on purpose + let x = f().valueOr: + return $error + + "ok" + + doAssert g2(int) == "error" + +block: # issue #23865 + type Xxx = enum + error + value + + type + Result[T, E] = object + when T is void: + when E is void: + oResultPrivate: bool + else: + case oResultPrivate: bool + of false: + eResultPrivate: E + of true: + discard + else: + when E is void: + case oResultPrivate: bool + of false: + discard + of true: + vResultPrivate: T + else: + case oResultPrivate: bool + of false: + eResultPrivate: E + of true: + vResultPrivate: T + + func error[T, E](self: Result[T, E]): E = + ## Fetch error of result if set, or raise Defect + case self.oResultPrivate + of true: + when T isnot void: + raiseResultDefect("Trying to access error when value is set", self.vResultPrivate) + else: + raiseResultDefect("Trying to access error when value is set") + of false: + when E isnot void: + self.eResultPrivate + + template valueOr[T: not void, E](self: Result[T, E], def: untyped): untyped = + let s = (self) # TODO avoid copy + case s.oResultPrivate + of true: + s.vResultPrivate + of false: + when E isnot void: + template error: untyped {.used, inject.} = s.eResultPrivate + def + proc f(): Result[int, cstring] = + Result[int, cstring](oResultPrivate: false, eResultPrivate: "f") + proc g(T: type): string = + let x = f().valueOr: + return $error + "ok" + doAssert g(int) == "f" + +import sequtils + +block: # issue #12283 + var b = 5 + type Foo[T] = object + h, w: int + proc bar[T](foos: seq[Foo[T]]): T = + let w = foldl(foos, a + b.w, 0) + w + let foos = @[Foo[int](h: 3, w: 5), Foo[int](h: 4, w: 6)] + doAssert bar(foos) == 11 diff --git a/tests/generics/tmacroinjectedsymwarning.nim b/tests/generics/tmacroinjectedsymwarning.nim new file mode 100644 index 000000000..77119004b --- /dev/null +++ b/tests/generics/tmacroinjectedsymwarning.nim @@ -0,0 +1,59 @@ +discard """ + matrix: "--skipParentCfg --filenames:legacyRelProj" +""" + +type Xxx = enum + error + value + +type + Result[T, E] = object + when T is void: + when E is void: + oResultPrivate*: bool + else: + case oResultPrivate*: bool + of false: + eResultPrivate*: E + of true: + discard + else: + when E is void: + case oResultPrivate*: bool + of false: + discard + of true: + vResultPrivate*: T + else: + case oResultPrivate*: bool + of false: + eResultPrivate*: E + of true: + vResultPrivate*: T + +template valueOr[T: not void, E](self: Result[T, E], def: untyped): untyped = + let s = (self) # TODO avoid copy + case s.oResultPrivate + of true: + s.vResultPrivate + of false: + when E isnot void: + template error: untyped {.used, inject.} = s.eResultPrivate + def + +proc f(): Result[int, cstring] = + Result[int, cstring](oResultPrivate: false, eResultPrivate: "f") + +proc g(T: type): string = + let x = f().valueOr: + {.push warningAsError[IgnoredSymbolInjection]: on.} + # test spurious error + discard true + let _ = f + {.pop.} + return $error #[tt.Warning + ^ a new symbol 'error' has been injected during template or generic instantiation, however 'error' [enumField declared in tmacroinjectedsymwarning.nim(6, 3)] captured at the proc declaration will be used instead; either enable --experimental:openSym to use the injected symbol, or `bind` this captured symbol explicitly [IgnoredSymbolInjection]]# + + "ok" + +discard g(int) diff --git a/tests/generics/tmapping_generic_alias.nim b/tests/generics/tmapping_generic_alias.nim new file mode 100644 index 000000000..efdf32ead --- /dev/null +++ b/tests/generics/tmapping_generic_alias.nim @@ -0,0 +1,28 @@ +discard """ +output: '''type(c) = GenAlias[system.int] +T = int +seq[int] +''' +""" + +import typetraits + +type + Gen[T] = object + x: T + + GenAlias[T] = Gen[seq[T]] + +proc f1[T](x: Gen[T]) = + echo T.name + +proc f2[T](x: GenAlias[T]) = + echo "type(c) = ", type(x).name + echo "T = ", T.name + f1 x + +let + y = Gen[seq[int]](x: @[10]) + +f2 y + diff --git a/tests/generics/tmetafield.nim b/tests/generics/tmetafield.nim new file mode 100644 index 000000000..cf30a936d --- /dev/null +++ b/tests/generics/tmetafield.nim @@ -0,0 +1,30 @@ +discard """ + cmd: "nim check $options $file" + action: "reject" + nimout: ''' +tmetafield.nim(26, 5) Error: 'proc' is not a concrete type; for a callback without parameters use 'proc()' +tmetafield.nim(27, 5) Error: 'Foo' is not a concrete type +tmetafield.nim(29, 5) Error: invalid type: 'proc' in this context: 'TBaseMed' for var +''' +""" + +# bug #188 + + + + + + + + +# line 20 +type + Foo[T] = object + x: T + + TBaseMed = object + doSmth: proc + data: seq[Foo] + +var a: TBaseMed + diff --git a/tests/generics/tnestedissues.nim b/tests/generics/tnestedissues.nim new file mode 100644 index 000000000..e96a1927e --- /dev/null +++ b/tests/generics/tnestedissues.nim @@ -0,0 +1,24 @@ +block: # issue #23568 + type G[T] = object + j: T + proc s[T](u: int) = discard + proc s[T]() = discard + proc c(e: int | int): G[G[G[int]]] = s[G[G[int]]]() + discard c(0) + +import std/options + +block: # issue #23310 + type + BID = string or uint64 + Future[T] = ref object of RootObj + internalValue: T + InternalRaisesFuture[T] = ref object of Future[T] + proc newInternalRaisesFutureImpl[T](): InternalRaisesFuture[T] = + let fut = InternalRaisesFuture[T]() + template newFuture[T](): auto = + newInternalRaisesFutureImpl[T]() + proc problematic(blockId: BID): Future[Option[seq[int]]] = + let resultFuture = newFuture[Option[seq[int]]]() + return resultFuture + let x = problematic("latest") diff --git a/tests/generics/tnestedtemplate.nim b/tests/generics/tnestedtemplate.nim new file mode 100644 index 000000000..22d0a2d3c --- /dev/null +++ b/tests/generics/tnestedtemplate.nim @@ -0,0 +1,9 @@ +block: # issue #13979 + var s: seq[int] + proc filterScanline[T](input: openArray[T]) = + template currPix: untyped = input[i] + for i in 0..<input.len: + s.add currPix + let pix = [1, 2, 3] + filterScanline(pix) + doAssert s == @[1, 2, 3] diff --git a/tests/generics/tnullary_generics.nim b/tests/generics/tnullary_generics.nim new file mode 100644 index 000000000..c79558ee3 --- /dev/null +++ b/tests/generics/tnullary_generics.nim @@ -0,0 +1,26 @@ +discard """ + nimout: ''' +hah +hey +hey +hah +''' +""" + +# non-generic +proc foo(s: string) = + static: echo "hah" + echo s + +static: echo "hey" + +foo("hoo") + +# nullary generic +proc bar[](s: string) = + static: echo "hah" + echo s + +static: echo "hey" + +bar("hoo") diff --git a/tests/generics/tobjecttyperel.nim b/tests/generics/tobjecttyperel.nim new file mode 100644 index 000000000..6f223c154 --- /dev/null +++ b/tests/generics/tobjecttyperel.nim @@ -0,0 +1,89 @@ +discard """ + matrix: "-d:nimInternalNonVtablesTesting" + output: '''(peel: 0, color: 15) +(color: 15) +17 +(width: 0.0, taste: "", color: 13) +(width: 0.0, taste: "", color: 15) +cool +test''' +""" + +# bug #5241 +type + BaseFruit[T] = object of RootObj + color: T + + MidLevel[T] = object of BaseFruit[T] + + Mango = object of MidLevel[int] + peel: int + + Peach[X, T, Y] = object of T + width: X + taste: Y + +proc setColor[T](self: var BaseFruit[T]) = + self.color = 15 + +proc setColor[T](self: var BaseFruit[T], c: int) = + self.color = c + +var c: Mango +setColor(c) +echo c + +var d: MidLevel[int] +setColor(d) +echo d + +type + FooBase[T] = ref object of RootRef + v: T + BarClient = ref object of FooBase[int] + +proc getColor[T](f: FooBase[T]): T = 17 +var b: BarClient +echo getColor(b) + +var z: Peach[float64, BaseFruit[int], string] +z.setColor(13) +echo z + +z.setColor() +echo z + +# bug #5411 +type + Foo[T] = ref object of RootRef + v: T + Bar = ref object of Foo[int] + +method m(o: RootRef) {.base.} = assert(false, "Abstract method called") +method m[T](o: Foo[T]) = echo "cool" + +var v: Bar +v.new() +v.m() # Abstract method not called anymore + + +# bug #88 + +type + TGen[T] = object of RootObj + field: T + + TDerived[T] = object of TGen[T] + nextField: T + +proc doSomething[T](x: ref TGen[T]) = + type + Ty = ref TDerived[T] + echo Ty(x).nextField + +var + x: ref TDerived[string] +new(x) +x.nextField = "test" + +doSomething(x) diff --git a/tests/generics/tobjecttyperel2.nim b/tests/generics/tobjecttyperel2.nim new file mode 100644 index 000000000..d8c0751b7 --- /dev/null +++ b/tests/generics/tobjecttyperel2.nim @@ -0,0 +1,42 @@ +discard """ + output: '''1 +a +13''' +""" + +# bug #5621 #5615 +type + Obj5[T] = ref object of RootObj + x_impl: T + +proc x[T](v476205: Obj5[T]): T {.used.} = + v476205.x_impl + +type + Obj6[T, U] = ref object of Obj5[T] + y_impl: U + +proc newObj6[T, U](x: T; y: U): Obj6[T, U] = + new(result) + result.x_impl = x + result.y_impl = y + +proc x[T, U](v477606: Obj6[T, U]): T {.used.} = + v477606.x_impl + +proc y[T, U](v477608: Obj6[T, U]): U {.used.} = + v477608.y_impl + +let e = newObj6(1, "a") +echo e.x +echo e.y + +type + Fruit[T] = ref object of RootObj + Apple[T] = ref object of Fruit[T] + +proc getColor[T](v: Fruit[T]): T = 13 + +var w: Apple[int] +let r = getColor(w) +echo r diff --git a/tests/generics/topensymimport.nim b/tests/generics/topensymimport.nim new file mode 100644 index 000000000..a47496827 --- /dev/null +++ b/tests/generics/topensymimport.nim @@ -0,0 +1,5 @@ +# issue #23386 + +import mopensymimport2 + +doAssert g(int) == "f" diff --git a/tests/generics/toverloading_typedesc.nim b/tests/generics/toverloading_typedesc.nim new file mode 100644 index 000000000..4d748bfee --- /dev/null +++ b/tests/generics/toverloading_typedesc.nim @@ -0,0 +1,19 @@ +import moverloading_typedesc +import tables + +type + LFoo = object + LBar = object + +when true: + doAssert FBar.new() == 3 + + proc new(_: typedesc[LFoo]): int = 0 + proc new[T](_: typedesc[T]): int = 1 + proc new*(_: typedesc[seq[Table[int, seq[Table[int, typedesc]]]]]): int = 7 + + doAssert LFoo.new() == 0 # Tests selecting more precise type + doAssert LBar.new() == 1 # Tests preferring function from local scope + doAssert FBar.new() == 1 + doAssert FFoo.new() == 2 # Tests selecting more precise type from other module + doAssert seq[Table[int, seq[Table[int, string]]]].new() == 5 # Truly complex type test diff --git a/tests/generics/tparam_binding.nim b/tests/generics/tparam_binding.nim new file mode 100644 index 000000000..fa7558613 --- /dev/null +++ b/tests/generics/tparam_binding.nim @@ -0,0 +1,29 @@ +discard """ + matrix: "--mm:arc; --mm:refc" + errormsg: "got <ref Matrix[2, 2, system.float], ref Matrix[2, 1, system.float]>" + line: 28 +""" + +type + Matrix[M,N: static[int]; T: SomeFloat] = distinct array[0..(M*N - 1), T] + +let a = new Matrix[2,2,float] +let b = new Matrix[2,1,float] + +proc foo[M,N: static[int],T](a: ref Matrix[M, N, T], b: ref Matrix[M, N, T])= + discard + +foo(a, a) + +proc bar[M,N: static[int],T](a: ref Matrix[M, M, T], b: ref Matrix[M, N, T])= + discard + +bar(a, b) +bar(a, a) + +proc baz[M,N: static[int],T](a: ref Matrix[N, N, T], b: ref Matrix[M, N, T])= + discard + +baz(a, a) +baz(a, b) + diff --git a/tests/generics/tparser_generator.nim b/tests/generics/tparser_generator.nim new file mode 100644 index 000000000..ac921c0e5 --- /dev/null +++ b/tests/generics/tparser_generator.nim @@ -0,0 +1,415 @@ +discard """ + output: '''Match failed: spam +Match failed: ham''' +joinable: false +""" + +# bug #6220 + +import nre +import options +import strutils except isAlpha, isLower, isUpper, isSpace +from unicode import isAlpha, isLower, isUpper, isTitle, isWhiteSpace +import os + +const debugLex = false + +template debug(enable: bool, text: string): typed = + when enable: + echo(text) + +type + Parser[N, T] = proc(text: T, start: int, nodes: var seq[Node[N]]): int {.closure.} + + RuleObj[N, T] = object + parser: Parser[N, T] + kind: N + + Rule[N, T] = ref RuleObj[N, T] + + NodeKind = enum + terminal, + nonterminal + + Node*[N] = object of RootObj + # Uncomment the following lines and the compiler crashes + # case nodeKind: NodeKind + # of nonterminal: + # kids: Node[N] + # of terminal: + # discard + start*: int + length*: int + kind*: N + + + NonTerminal[N] = object of Node + children: seq[Node[N]] + +proc newRule[N, T](parser: Parser, kind: N): Rule[N, T] = + new(result) + result.parser = parser + result.kind = kind + +proc newRule[N, T](kind: N): Rule[N, T] = + new(result) + result.kind = kind + +proc initNode[N](start: int, length: int, kind: N): Node[N] = + result.start = start + result.length = length + result.kind = kind + +proc initNode[N](start: int, length: int, children: seq[Node[N]], kind: N): NonTerminal[N] = + result.start = start + result.length = length + result.kind = kind + result.children = children + +proc substr[T](text: T, first, last: int): T = + text[first .. last] + +proc continuesWith[N](text: seq[Node[N]], subtext: seq[N], start: Natural): bool = + let length = len(text) + var pos = 0 + while pos < len(subtext): + let textpos = start + pos + if textpos == len(text): + return false + if text[textpos].kind != subtext[pos].kind: + return false + pos+=1 + return true + + +proc render*[N, T](text: T, nodes: seq[Node[N]]): string = + ## Uses a sequence of Nodes to render a given text string + result = "" + for node in nodes: + result.add("<" & node.value(text) & ">") + +proc render*[N, T](rule: Rule[N, T], text: string): string = + ## Uses a rule to render a given text string + render(text, rule.parse(text)) + +proc render*[N, T](text: T, nodes: seq[Node[N]], source: string): string = + result = "" + for node in nodes: + result.add("[" & node.value(text, source) & "]") + +proc render*[N, T, X](rule: Rule[N, T], text: seq[Node[X]], source: string): string = + ## Uses a rule to render a given series of nodes, providing the source string + text.render(rule.parse(text, source = source), source) + +proc annotate*[N, T](node: Node[N], text: T): string = + result = "<" & node.value(text) & ":" & $node.kind & ">" + +proc annotate*[N, T](nodes: seq[Node[N]], text: T): string = + result = "" + for node in nodes: + result.add(node.annotate(text)) + +proc annotate*[N, T](rule: Rule[N, T], text: T): string = + annotate(rule.parse(text), text) + +proc value*[N, T](node: Node[N], text: T): string = + result = $text.substr(node.start, node.start + node.length - 1) + +proc value*[N, X](node: Node[N], text: seq[Node[X]], source: string): string = + result = "" + for n in node.start ..< node.start + node.length: + result &= text[n].annotate(source) + +proc parse*[N, T](rule: Rule[N, T], text: T, start = 0, source: string = ""): seq[Node[N]] = + result = newSeq[Node[N]]() + debug(debugLex, "Parsing: " & $text) + let length = rule.parser(text, start, result) + + when T is string: + if length == -1: + echo("Match failed: " & $text) + result = @[] + elif length == len(text): + debug(debugLex, "Matched: " & $text & " => " & $len(result) & " tokens: " & text.render(result)) + else: + echo("Matched first " & $length & " symbols: " & $text & " => " & $len(result) & " tokens: " & text.render(result)) + else: + if length == -1: + echo("Match failed: " & $text) + result = @[] + elif length == len(text): + debug(debugLex, "Matched: " & $text & " => " & $len(result) & " tokens: " & text.render(result, source)) + else: + echo("Matched first " & $length & " symbols: " & $text & " => " & $len(result) & " tokens: " & text.render(result, source)) + + +proc literal*[N, T, P](pattern: P, kind: N): Rule[N, T] = + let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int = + if start == len(text): + return -1 + doAssert(len(text)>start, "Attempting to match at $#, string length is $# " % [$start, $len(text)]) + when P is string or P is seq[N]: + debug(debugLex, "Literal[" & $kind & "]: testing " & $pattern & " at " & $start & ": " & $text[start..start+len(pattern)-1]) + if text.continuesWith(pattern, start): + let node = initNode(start, len(pattern), kind) + nodes.add(node) + debug(debugLex, "Literal: matched <" & $text[start ..< start+node.length] & ":" & $node.length & ">" ) + return node.length + elif P is char: + debug(debugLex, "Literal[" & $kind & "]: testing " & $pattern & " at " & $start & ": " & $text[start]) + if text[start] == pattern: + let node = initNode(start, 1, kind) + nodes.add(node) + return 1 + else: + debug(debugLex, "Literal[" & $kind & "]: testing " & $pattern & " at " & $start & ": " & $text[start]) + if text[start].kind == pattern: + let node = initNode(start, 1, kind) + nodes.add(node) + return 1 + return -1 + result = newRule[N, T](parser, kind) + +proc token[N, T](pattern: T, kind: N): Rule[N, T] = + when T is not string: + {.fatal: "Token is only supported for strings".} + let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int = + debug(debugLex, "Token[" & $kind & "]: testing " & pattern & " at " & $start) + if start == len(text): + return -1 + doAssert(len(text)>start, "Attempting to match at $#, string length is $# " % [$start, $len(text)]) + let m = text.match(re(pattern), start) + if m.isSome: + let node = initNode(start, len(m.get.match), kind) + nodes.add(node) + result = node.length + debug(debugLex, "Token: matched <" & text[start ..< start+node.length] & ":" & $node.length & ">" ) + else: + result = -1 + result = newRule[N, T](parser, kind) + +proc chartest[N, T, S](testfunc: proc(s: S): bool, kind: N): Rule[N, T] = + let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int = + if start == len(text): + return -1 + doAssert(len(text)>start, "Attempting to match at $#, string length is $# " % [$start, $len(text)]) + if testfunc(text[start]): + nodes.add(initNode(start, 1, kind)) + result = 1 + else: + result = -1 + result = newRule[N, T](parser, kind) + +proc any*[N, T, S](symbols: T, kind: N): Rule[N, T] = + let test = proc(s: S): bool = + when S is string: + debug(debugLex, "Any[" & $kind & "]: testing for " & symbols.replace("\n", "\\n").replace("\r", "\\r")) + else: + debug(debugLex, "Any[" & $kind & "]: testing for " & $symbols) + result = s in symbols + result = chartest[N, T, S](test, kind) + +proc ignore*[N, T](rule: Rule[N, T]): Rule[N, T] = + let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int = + var mynodes = newSeq[Node[N]]() + result = rule.parser(text, start, mynodes) + result = newRule[N, T](parser, rule.kind) + +proc combine*[N, T](rule: Rule[N, T], kind: N): Rule[N, T] = + let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int = + var mynodes = newSeq[Node[N]]() + result = rule.parser(text, start, mynodes) + nodes.add(initNode(start, result, kind)) + result = newRule[N, T](parser, kind) + +proc build*[N, T](rule: Rule[N, T], kind: N): Rule[N, T] = + let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int = + var mynodes = newSeq[Node[N]]() + result = rule.parser(text, start, mynodes) + let nonTerminal = initNode(start, result, mynodes, kind) + nodes.add(nonTerminal) + result = newRule[N, T](parser, kind) + +proc fail*[N, T](message: string, kind: N): Rule[N, T] = + let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int = + let lineno = countLines(text[0..start]) + var startline = start + var endline = start + while startline>0: + if text[startline] in NewLines: + break + startline-=1 + while endline < len(text): + if text[endline] in NewLines: + break + endline+=1 + let charno = start-startline + echo text.substr(startline, endline) + echo ' '.repeat(max(charno,0)) & '^' + raise newException(ValueError, "Position: " & $start & " Line: " & $lineno & ", Symbol: " & $charno & ": " & message) + result = newRule[N, T](parser, kind) + +proc `+`*[N, T](left: Rule[N, T], right: Rule[N, T]): Rule[N, T] = + let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int = + var mynodes = newSeq[Node[N]]() + doAssert(not isNil(left.parser), "Left hand side parser is nil") + let leftlength = left.parser(text, start, mynodes) + if leftlength == -1: + return leftlength + doAssert(not isNil(right.parser), "Right hand side parser is nil") + let rightlength = right.parser(text, start+leftlength, mynodes) + if rightlength == -1: + return rightlength + result = leftlength + rightlength + nodes.add(mynodes) + result = newRule[N, T](parser, left.kind) + +proc `/`*[N, T](left: Rule[N, T], right: Rule[N, T]): Rule[N, T] = + let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int = + var mynodes = newSeq[Node[N]]() + doAssert(not isNil(left.parser), "Left hand side of / is not fully defined") + let leftlength = left.parser(text, start, mynodes) + if leftlength != -1: + nodes.add(mynodes) + return leftlength + mynodes = newSeq[Node[N]]() + doAssert(not isNil(right.parser), "Right hand side of / is not fully defined") + let rightlength = right.parser(text, start, mynodes) + if rightlength == -1: + return rightlength + nodes.add(mynodes) + return rightlength + result = newRule[N, T](parser, left.kind) + +proc `?`*[N, T](rule: Rule[N, T]): Rule[N, T] = + let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int = + let success = rule.parser(text, start, nodes) + return if success != -1: success else: 0 + result = newRule[N, T](parser, rule.kind) + +proc `+`*[N, T](rule: Rule[N, T]): Rule[N, T] = + let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int = + var success = rule.parser(text, start, nodes) + if success == -1: + return success + var total = 0 + while success != -1 and start+total < len(text): + total += success + success = rule.parser(text, start+total, nodes) + return total + result = newRule[N, T](parser, rule.kind) + +proc `*`*[N, T](rule: Rule[N, T]): Rule[N, T] = + let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int = + let success = (+rule).parser(text, start, nodes) + return if success != -1: success else: 0 + result = newRule[N, T](parser, rule.kind) + +#Note: this consumes - for zero-width lookahead see ! +proc `^`*[N, T](rule: Rule[N, T]): Rule[N, T] = + let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int = + var mynodes = newSeq[Node[N]]() + let success = rule.parser(text, start, mynodes) + return if success == -1: 1 else: -1 + result = newRule[N, T](parser, rule.kind) + +proc `*`*[N, T](repetitions: int, rule: Rule[N, T]): Rule[N, T] = + let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int = + var mynodes = newSeq[Node[N]]() + var total = 0 + for i in 0..<repetitions: + let success = rule.parser(text, start+total, mynodes) + if success == -1: + return success + else: + total += success + nodes.add(mynodes) + return total + result = newRule[N, T](parser, rule.kind) + +# Positive zero-width lookahead +proc `&`*[N, T](rule: Rule[N, T]): Rule[N, T] = + let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int = + var mynodes = newSeq[Node[N]]() + let success = rule.parser(text, start, mynodes) + return if success != -1: 0 else: -1 + result = newRule[N, T](parser, rule.kind) + +# Negative zero-width lookahead +proc `!`*[N, T](rule: Rule[N, T]): Rule[N, T] = + let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int = + var mynodes = newSeq[Node[N]]() + let failure = rule.parser(text, start, mynodes) + return if failure == -1: 0 else: -1 + result = newRule[N, T](parser, rule.kind) + +proc `/`*[N, T](rule: Rule[N, T]): Rule[N, T] = + let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int = + var mynodes = newSeq[Node[N]]() + var length = 0 + var success = rule.parser(text, start+length, mynodes) + while success == -1 and start+length < len(text): + length += 1 + success = rule.parser(text, start+length, mynodes) + if start+length >= len(text): + result = -1 + else: + nodes.add(initNode(start, length, rule.kind)) + nodes.add(mynodes) + result = length + success + result = newRule[N, T](parser, rule.kind) + +proc `->`*(rule: Rule, production: Rule) = + doAssert(not isnil(production.parser), "Right hand side of -> is nil - has the rule been defined yet?") + rule.parser = production.parser + +template grammar*[K](Kind, Text, Symbol: typedesc; default: K, code: untyped): typed {.hint[XDeclaredButNotUsed]: off.} = + + proc newRule(): Rule[Kind, Text] {.inject.} = newRule[Kind, Text](default) + proc chartest(testfunc: proc(c: Symbol): bool): Rule[Kind, Text] {.inject.} = chartest[Kind, Text, Symbol](testfunc, default) + proc literal[P](pattern: P, kind: K): Rule[Kind, Text] {.inject.} = literal[Kind, Text, P](pattern, kind) + proc literal[P](pattern: P): Rule[Kind, Text] {.inject.} = literal[Kind, Text, P](pattern, default) + + when Text is string: + proc token(pattern: string): Rule[Kind, Text] {.inject.} = token(pattern, default) + proc fail(message: string): Rule[Kind, Text] {.inject.} = fail[Kind, Text](message, default) + let alpha {.inject.} = chartest[Kind, Text, Symbol](isAlphaAscii, default) + let alphanumeric {.inject.}= chartest[Kind, Text, Symbol](isAlphaNumeric, default) + let digit {.inject.} = chartest[Kind, Text, Symbol](isDigit, default) + let lower {.inject.} = chartest[Kind, Text, Symbol](isLowerAscii, default) + let upper {.inject.} = chartest[Kind, Text, Symbol](isUpperAscii, default) + let isspace = proc (x: char): bool = x.isSpaceAscii and not (x in NewLines) + let space {.inject.} = chartest[Kind, Text, Symbol](isspace, default) + let isnewline = proc (x: char): bool = x in NewLines + let newline {.inject.} = chartest[Kind, Text, Symbol](isnewline, default) + let alphas {.inject.} = combine(+alpha, default) + let alphanumerics {.inject.} = combine(+alphanumeric, default) + let digits {.inject.} = combine(+digit, default) + let lowers {.inject.} = combine(+lower, default) + let uppers {.inject.} = combine(+upper, default) + let spaces {.inject.} = combine(+space, default) + let newlines {.inject.} = combine(+newline, default) + + proc any(chars: Text): Rule[Kind, Text] {.inject.} = any[Kind, Text, Symbol](chars, default) + proc combine(rule: Rule[Kind, Text]): Rule[Kind, Text] {.inject.} = combine[Kind, Text](rule, default) + + code + +template grammar*[K](Kind: typedesc; default: K, code: untyped): typed {.hint[XDeclaredButNotUsed]: off.} = + grammar(Kind, string, char, default, code) + +block: + type DummyKind = enum dkDefault + grammar(DummyKind, string, char, dkDefault): + let rule = token("h[a]+m") + ignore(token(r"\s+")) + (literal("eggs") / literal("beans")) + var text = "ham beans" + discard rule.parse(text) + + var recursive = newRule() + recursive -> (literal("(") + recursive + literal(")")) / token(r"\d+") + for test in ["spam", "57", "(25)", "((25))"]: + discard recursive.parse(test) + + let repeated = +literal("spam") + ?literal("ham") + *literal("salami") + for test in ["ham", "spam", "spamspamspam" , "spamham", "spamsalami", "spamsalamisalami"]: + discard repeated.parse(test) diff --git a/tests/generics/tpointerprocs.nim b/tests/generics/tpointerprocs.nim new file mode 100644 index 000000000..2bcaf15b3 --- /dev/null +++ b/tests/generics/tpointerprocs.nim @@ -0,0 +1,28 @@ +discard """ +cmd: "nim check $options --hints:off $file" +action: "reject" +nimout:''' +tpointerprocs.nim(15, 11) Error: 'foo' doesn't have a concrete type, due to unspecified generic parameters. +tpointerprocs.nim(27, 11) Error: cannot instantiate: 'foo[int]'; got 1 typeof(s) but expected 2 +tpointerprocs.nim(27, 14) Error: expression 'foo[int]' has no type (or is ambiguous) +tpointerprocs.nim(28, 11) Error: expression 'bar' has no type (or is ambiguous) +''' +""" + +block: + proc foo(x: int | float): float = result = 1.0 + let + bar = foo + baz = bar + +block: + proc foo(x: int | float): float = result = 1.0 + let + bar = foo[int] + baz = bar + +block: + proc foo(x: int | float, y: int or string): float = result = 1.0 + let + bar = foo[int] + baz = bar \ No newline at end of file diff --git a/tests/generics/tprevent_double_bind.nim b/tests/generics/tprevent_double_bind.nim new file mode 100644 index 000000000..d8fc6e5d3 --- /dev/null +++ b/tests/generics/tprevent_double_bind.nim @@ -0,0 +1,21 @@ +discard """ + errormsg: "type mismatch: got <TT[seq[string]], proc (v: int){.gcsafe.}>" + line: 20 +""" + +# bug #6732 +import typetraits + +type + TT[T] = ref object of RootObj + val: T + CB[T] = proc (v: T) + +proc testGeneric[T](val: TT[T], cb: CB[T]) = + echo val.type.name + echo $val.val + +var tt = new(TT[seq[string]]) +echo tt.type.name +tt.testGeneric( proc (v: int) = + echo $v ) diff --git a/tests/generics/trecursivegenerics.nim b/tests/generics/trecursivegenerics.nim new file mode 100644 index 000000000..1b152b063 --- /dev/null +++ b/tests/generics/trecursivegenerics.nim @@ -0,0 +1,96 @@ +block: # Replicates #18728 + type + FlipFlop[A, B] = ref object + val: A + next: FlipFlop[B, A] + + Trinary[A, B, C] = ref object + next: Trinary[B, C, A] + + assert typeof(FlipFlop[int, string]().next) is FlipFlop[string, int] + assert typeof(FlipFlop[string, int]().next) is FlipFlop[int, string] + assert typeof(Trinary[int, float, string]().next) is Trinary[float, string, int] + assert typeof(Trinary[int, float, string]().next.next) is Trinary[string, int, float] + var a = FlipFlop[int, string](val: 100, next: FlipFlop[string, int](val: "Hello")) + assert a.val == 100 + assert a.next.val == "Hello" + +block: # 18838 + type + DoublyLinkedNodeObj[T] = object + value: T + + DoublyLinkedNode[T] = ref DoublyLinkedNodeObj[T] + + Item[T] = ref object + link: DoublyLinkedNode[Item[T]] + + Box = object + + proc newDoublyLinkedNode[T](value: T): DoublyLinkedNode[T] = + new(result) + result.value = value + + let link = newDoublyLinkedNode(Item[Box]()) + +import lists +block: + type + Box = object + Item[T] = ref object + link:DoublyLinkedNode[ Item[T] ] + + ItemSimple = ref object + link:DoublyLinkedNode[ ItemSimple ] + + let link = newDoublyLinkedNode( Item[Box]() ) + +block: #18897 + type + SkipListObj[T] = object + over: SkipList[T] + down: SkipList[T] + value: T + + SkipList[T] = ref SkipListObj[T] + + GraphObj[N, E; F: static[int]] = object + nodes: SkipList[Node[N, E]] + + Graph[N, E; F: static[int]] = ref GraphObj[N, E, F] + + Node[N, E] = ref NodeObj[N, E] + + NodeObj[N, E] = object + value: N + incoming: SkipList[Edge[N, E]] + outgoing: SkipList[Edge[N, E]] + + Edge[N, E] = ref EdgeObj[N, E] + + EdgeObj[N, E] = object + value: E + id: int + source: Node[N, E] + target: Node[N, E] + + EdgeResult[N, E] = tuple + source: Node[N, E] + edge: Edge[N, E] + target: Node[N, E] + + proc newSkipList[T](value: T): SkipList[T] = + static: echo T, " ", typeof(result.value) + result = SkipList[T](value: value) + + proc toSkipList[T](values: openArray[T] = @[]): SkipList[T] = + for item in items(values): + if result.isNil: + result = newSkipList(item) + + proc newContainer[N, E, F](graph: Graph[N, E, F]; form: typedesc): auto = + result = toSkipList[form]([]) + + var + result = Graph[int, string, 0]() + result.nodes = result.newContainer(Node[int, string]) \ No newline at end of file diff --git a/tests/generics/treentranttypes.nim b/tests/generics/treentranttypes.nim new file mode 100644 index 000000000..801f0e444 --- /dev/null +++ b/tests/generics/treentranttypes.nim @@ -0,0 +1,114 @@ +discard """ +output: ''' +(10, ("test", 1.2)) +3x3 Matrix [[0.0, 2.0, 3.0], [2.0, 0.0, 5.0], [2.0, 0.0, 5.0]] +2x3 Matrix [[0.0, 2.0, 3.0], [2.0, 0.0, 5.0]] +2x3 Literal [[0.0, 2.0, 3.0], [2.0, 0.0, 5.0]] +2x3 Matrix [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]] +2x2 ArrayArray[[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]] +2x3 ArrayVector[[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]] +2x3 VectorVector [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]] +2x3 VectorArray [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]] +@[1, 2] +@[1, 2] +@[1, 2]@[3, 4] +@[1, 2]@[3, 4] +''' +""" + +# https://github.com/nim-lang/Nim/issues/5962 + +type + ArrayLike[A, B] = (A, B) + VectorLike*[SIZE, T] = ArrayLike[SIZE, T] + MatrixLike*[M, N, T] = VectorLike[M, VectorLike[N, T]] + +proc tupleTest = + let m: MatrixLike[int, string, float] = (10, ("test", 1.2)) + echo m + +tupleTest() + +type + Vector*[K: static[int], T] = + array[K, T] + + Matrix*[M: static[int]; N: static[int]; T] = + Vector[M, Vector[N, T]] + +proc arrayTest = + # every kind of square matrix works just fine + let mat_good: Matrix[3, 3, float] = [[0.0, 2.0, 3.0], + [2.0, 0.0, 5.0], + [2.0, 0.0, 5.0]] + echo "3x3 Matrix ", repr(mat_good) + + # this does not work with explicit type signature (the matrix seems to always think it is NxN instead) + let mat_fail: Matrix[2, 3, float] = [[0.0, 2.0, 3.0], + [2.0, 0.0, 5.0]] + echo "2x3 Matrix ", repr(mat_fail) + + # this literal seems to work just fine + let mat_also_good = [[0.0, 2.0, 3.0], + [2.0, 0.0, 5.0]] + + echo "2x3 Literal ", repr(mat_also_good) + + # but making a named type out of this leads to pretty nasty runtime behavior + var mat_fail_runtime: Matrix[2, 3, float] + echo "2x3 Matrix ", repr(mat_fail_runtime) + + # cutting out the matrix type middle man seems to solve our problem + var mat_ok_runtime: array[2, array[3, float]] + echo "2x2 ArrayArray", repr(mat_ok_runtime) + + # this is fine too + var mat_ok_runtime_2: array[2, Vector[3, float]] + echo "2x3 ArrayVector", repr(mat_ok_runtime_2) + + # here we are in trouble again + var mat_fail_runtime_2: Vector[2, Vector[3, float]] + echo "2x3 VectorVector ", repr(mat_fail_runtime_2) + + # and here we are fine again + var mat_ok_runtime_3: Vector[2, array[3, float]] + echo "2x3 VectorArray ", repr(mat_ok_runtime_3) + +arrayTest() + +# https://github.com/nim-lang/Nim/issues/5756 + +type + Vec*[N : static[int]] = object + arr*: array[N, int32] + + Mat*[M,N: static[int]] = object + arr*: array[M, Vec[N]] + +proc vec2*(x,y:int32) : Vec[2] = + result.arr = [x,y] + +proc mat2*(a,b: Vec[2]): Mat[2,2] = + result.arr = [a,b] + +const a = vec2(1,2) +echo @(a.arr) +let x = a +echo @(x.arr) + +const b = mat2(vec2(1, 2), vec2(3, 4)) +echo @(b.arr[0].arr), @(b.arr[1].arr) +let y = b +echo @(y.arr[0].arr), @(y.arr[1].arr) + +import macros + +block: # issue #5121 + type + A = object + AConst[X] = A + + macro dumpType(t: typedesc): untyped = + result = newTree(nnkTupleConstr, newLit $t.getType[1].typeKind, newLit t.getType[1].treeRepr) + + doAssert dumpType(A) == ("ntyObject", "Sym \"A\"") diff --git a/tests/generics/treturn_inference.nim b/tests/generics/treturn_inference.nim new file mode 100644 index 000000000..331a9d4db --- /dev/null +++ b/tests/generics/treturn_inference.nim @@ -0,0 +1,184 @@ + +{.experimental: "inferGenericTypes".} + +import std/tables + +block: + type + MyOption[T, Z] = object + x: T + y: Z + + proc none[T, Z](): MyOption[T, Z] = + when T is int: + result.x = 22 + when Z is float: + result.y = 12.0 + + proc myGenericProc[T, Z](): MyOption[T, Z] = + none() # implied by return type + + let a = myGenericProc[int, float]() + doAssert a.x == 22 + doAssert a.y == 12.0 + + let b: MyOption[int, float] = none() # implied by type of b + doAssert b.x == 22 + doAssert b.y == 12.0 + +# Simple template based result with inferred type for errors +block: + type + ResultKind {.pure.} = enum + Ok + Err + + Result[T] = object + case kind: ResultKind + of Ok: + data: T + of Err: + errmsg: cstring + + template err[T](msg: static cstring): Result[T] = + Result[T](kind : ResultKind.Err, errmsg : msg) + + proc testproc(): Result[int] = + err("Inferred error!") # implied by proc return + let r = testproc() + doAssert r.kind == ResultKind.Err + doAssert r.errmsg == "Inferred error!" + +# Builtin seq +block: + let x: seq[int] = newSeq(1) + doAssert x is seq[int] + doAssert x.len() == 1 + + type + MyType[T, Z] = object + x: T + y: Z + + let y: seq[MyType[int, float]] = newSeq(2) + doAssert y is seq[MyType[int, float]] + doAssert y.len() == 2 + + let z = MyType[seq[float], string]( + x : newSeq(3), + y : "test" + ) + doAssert z.x is seq[float] + doAssert z.x.len() == 3 + doAssert z.y is string + doAssert z.y == "test" + +# array +block: + proc giveArray[N, T](): array[N, T] = + for i in 0 .. N.high: + result[i] = i + var x: array[2, int] = giveArray() + doAssert x == [0, 1] + +# tuples +block: + proc giveTuple[T, Z]: (T, Z, T) = discard + let x: (int, float, int) = giveTuple() + doAssert x is (int, float, int) + doAssert x == (0, 0.0, 0) + + proc giveNamedTuple[T, Z]: tuple[a: T, b: Z] = discard + let y: tuple[a: int, b: float] = giveNamedTuple() + doAssert y is (int, float) + doAssert y is tuple[a: int, b: float] + doAssert y == (0, 0.0) + + proc giveNestedTuple[T, Z]: ((T, Z), Z) = discard + let z: ((int, float), float) = giveNestedTuple() + doAssert z is ((int, float), float) + doAssert z == ((0, 0.0), 0.0) + + # nesting inside a generic type + type MyType[T] = object + x: T + let a = MyType[(int, MyType[float])](x : giveNamedTuple()) + doAssert a.x is (int, MyType[float]) + + +# basic constructors +block: + type MyType[T] = object + x: T + + proc giveValue[T](): T = + when T is int: + 12 + else: + default(T) + + let x = MyType[int](x : giveValue()) + doAssert x.x is int + doAssert x.x == 12 + + let y = MyType[MyType[float]](x : MyType[float](x : giveValue())) + doAssert y.x is MyType[float] + doAssert y.x.x is float + doAssert y.x.x == 0.0 + + # 'MyType[float]' is bound to 'T' directly + # instead of mapping 'T' to 'float' + let z = MyType[MyType[float]](x : giveValue()) + doAssert z.x is MyType[float] + doAssert z.x.x == 0.0 + + type Foo = object + x: Table[int, float] + + let a = Foo(x: initTable()) + doAssert a.x is Table[int, float] + +# partial binding +block: + type + ResultKind = enum + Ok, Error + + Result[T, E] = object + case kind: ResultKind + of Ok: + okVal: T + of Error: + errVal: E + + proc err[T, E](myParam: E): Result[T, E] = + Result[T, E](kind : Error, errVal : myParam) + + proc doStuff(): Result[int, string] = + err("Error") + + let res = doStuff() + doAssert res.kind == Error + doAssert res.errVal == "Error" + +# ufcs +block: + proc getValue[T](_: string): T = + doAssert T is int + 44 + + proc `'test`[T](_: string): T = + 55 + + let a: int = getValue("") + let b: int = "".getValue() + let c: int = "".getValue + let d: int = getValue "" + let e: int = getValue"" + let f: int = 12345'test + doAssert a == 44 + doAssert b == 44 + doAssert c == 44 + doAssert d == 44 + doAssert e == 44 + doAssert f == 55 diff --git a/tests/generics/tstatic_constrained.nim b/tests/generics/tstatic_constrained.nim new file mode 100644 index 000000000..d356b9d1c --- /dev/null +++ b/tests/generics/tstatic_constrained.nim @@ -0,0 +1,79 @@ +discard """ + cmd: "nim check --hints:off --warnings:off $file" + action: "reject" + nimout:''' +tstatic_constrained.nim(44, 22) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(30, 5)] +got: <typedesc[int], int literal(10)> +but expected: <T: float or string, Y> +tstatic_constrained.nim(44, 22) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(30, 5)] +got: <typedesc[int], int literal(10)> +but expected: <T: float or string, Y> +tstatic_constrained.nim(44, 31) Error: object constructor needs an object type [error] +tstatic_constrained.nim(44, 31) Error: expression '' has no type (or is ambiguous) +tstatic_constrained.nim(45, 22) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(30, 5)] +got: <typedesc[byte], uint8> +but expected: <T: float or string, Y> +tstatic_constrained.nim(45, 22) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(30, 5)] +got: <typedesc[byte], uint8> +but expected: <T: float or string, Y> +tstatic_constrained.nim(45, 34) Error: object constructor needs an object type [error] +tstatic_constrained.nim(45, 34) Error: expression '' has no type (or is ambiguous) +tstatic_constrained.nim(77, 14) Error: cannot instantiate MyType [type declared in tstatic_constrained.nim(71, 5)] +got: <typedesc[float], float64> +but expected: <T: MyConstraint, Y> +''' +""" +block: + type + MyType[T; X: static T] = object + data: T + MyOtherType[T: float or string, Y: static T] = object + + func f[T,X](a: MyType[T,X]): MyType[T,X] = + when T is string: + MyType[T,X](data: a.data & X) + else: + MyType[T,X](data: a.data + X) + + discard MyType[int, 2](data: 1) + discard MyType[string, "Helelello"](data: "Hmmm") + discard MyType[int, 2](data: 1).f() + discard MyType[string, "Helelello"](data: "Hmmm").f() + discard MyOtherType[float, 1.3]() + discard MyOtherType[string, "Hello"]() + discard MyOtherType[int, 10]() + discard MyOtherType[byte, 10u8]() + +block: + type + Moduloable = concept m, type M + m mod m is M + Addable = concept a, type A + a + a is A + Modulo[T: Moduloable; Mod: static T] = distinct T + ModuloAdd[T: Moduloable or Addable; Mod: static T] = distinct T + ModuAddable = Addable or Moduloable + ModdAddClass[T: ModuAddable; Mod: static T] = distinct T + + proc toMod[T](val: T, modVal: static T): Modulo[T, modVal] = + mixin `mod` + Modulo[T, modVal](val mod modVal) + var + a = 3231.toMod(10) + b = 5483.toMod(10) + discard ModuloAdd[int, 3](0) + discard ModdAddClass[int, 3](0) + +block: + type + MyConstraint = int or string + MyOtherConstraint[T] = object + MyType[T: MyConstraint; Y: static T] = object + MyOtherType[T: MyOtherConstraint; Y: static T] = object + + var + a: MyType[int, 10] + b: MyType[string, "hello"] + c: MyType[float, 10d] + d: MyOtherType[MyOtherConstraint[float],MyOtherConstraint[float]()] + e: MyOtherType[MyOtherConstraint[int], MyOtherConstraint[int]()] diff --git a/tests/generics/tsubclassgenericerror.nim b/tests/generics/tsubclassgenericerror.nim new file mode 100644 index 000000000..87f8a8e64 --- /dev/null +++ b/tests/generics/tsubclassgenericerror.nim @@ -0,0 +1,11 @@ +discard """ + errormsg: "cannot instantiate 'GenericParentType[T]' inside of type definition: 'GenericChildType'; Maybe generic arguments are missing?" + line: 8 +""" + +type + GenericParentType[T] = ref object of RootObj + GenericChildType[T] = ref object of GenericParentType # missing the [T] + val: T + +var instance : GenericChildType[int] = nil diff --git a/tests/generics/tthread_generic.nim b/tests/generics/tthread_generic.nim new file mode 100644 index 000000000..300da56a6 --- /dev/null +++ b/tests/generics/tthread_generic.nim @@ -0,0 +1,39 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + action: compile +""" + +type + ThreadFuncArgs[T] = object of RootObj + a: proc(): T {.thread.} + b: proc(val: T) {.thread.} + +proc handleThreadFunc(arg: ThreadFuncArgs[int]){.thread.} = + var fn = arg.a + var callback = arg.b + var output = fn() + callback(output) + +proc `@||->`*[T](fn: proc(): T {.thread.}, + callback: proc(val: T){.thread.}): Thread[ThreadFuncArgs[T]] = + var thr: Thread[ThreadFuncArgs[T]] + var args: ThreadFuncArgs[T] + args.a = fn + args.b = callback + createThread(thr, handleThreadFunc, args) + return thr + +proc `||->`*[T](fn: proc(): T{.thread.}, callback: proc(val: T){.thread.}) = + discard fn @||-> callback + +when true: + import os + proc testFunc(): int {.thread.} = + return 1 + proc callbackFunc(val: int) {.thread.} = + echo($(val)) + + var thr = (testFunc @||-> callbackFunc) + echo("test") + joinThread(thr) + os.sleep(3000) diff --git a/tests/generics/tuninstantiated_failure.nim b/tests/generics/tuninstantiated_failure.nim new file mode 100644 index 000000000..f3d5b34b8 --- /dev/null +++ b/tests/generics/tuninstantiated_failure.nim @@ -0,0 +1,16 @@ +discard """ +cmd: "nim check $file" +""" + +type + Test[T, K] = object + name: string + Something = Test[int] + +func `[]`[T, K](x: var Test[T, K], idx: int): var Test[T, K] = + x + +var b: Something +# Should give an error since Something isn't a valid Test +b[0].name = "Test" #[tt.Error + ^ expression '' has no type (or is ambiguous)]# diff --git a/tests/generics/tuninstantiatedgenericcalls.nim b/tests/generics/tuninstantiatedgenericcalls.nim new file mode 100644 index 000000000..f33fc8967 --- /dev/null +++ b/tests/generics/tuninstantiatedgenericcalls.nim @@ -0,0 +1,517 @@ +# Cases that used to only work due to weird workarounds in the compiler +# involving not instantiating calls in generic bodies which are removed +# due to breaking statics. +# The issue was that these calls are compiled as regular expressions at +# the generic declaration with unresolved generic parameter types, +# which are special cased in some places in the compiler, but sometimes +# treated like real types. + +block: + type Base10 = object + + func maxLen(T: typedesc[Base10], I: type): int8 = + when I is uint8: + 3 + elif I is uint16: + 5 + elif I is uint32: + 10 + elif I is uint64: + 20 + else: + when sizeof(uint) == 4: + 10 + else: + 20 + + type + Base10Buf[T: SomeUnsignedInt] = object + data: array[maxLen(Base10, T), byte] + len: int8 + + var x: Base10Buf[uint32] + doAssert x.data.len == 10 + var y: Base10Buf[uint16] + doAssert y.data.len == 5 + +import typetraits + +block thardcases: + proc typeNameLen(x: typedesc): int {.compileTime.} = + result = x.name.len + macro selectType(a, b: typedesc): typedesc = + result = a + + type + Foo[T] = object + data1: array[T.high, int] + data2: array[typeNameLen(T), float] + data3: array[0..T.typeNameLen, selectType(float, int)] + + type MyEnum = enum A, B, C, D + + var f1: Foo[MyEnum] + var f2: Foo[int8] + + doAssert high(f1.data1) == 2 # (D = 3) - 1 == 2 + doAssert high(f1.data2) == 5 # (MyEnum.len = 6) - 1 == 5 + + doAssert high(f2.data1) == 126 # 127 - 1 == 126 + doAssert high(f2.data2) == 3 # int8.len - 1 == 3 + + static: + doAssert high(f1.data1) == ord(C) + doAssert high(f1.data2) == 5 # length of MyEnum minus one, because we used T.high + + doAssert high(f2.data1) == 126 + doAssert high(f2.data2) == 3 + + doAssert high(f1.data3) == 6 # length of MyEnum + doAssert high(f2.data3) == 4 # length of int8 + + doAssert f2.data3[0] is float + +import muninstantiatedgenericcalls + +block: + var x: Leb128Buf[uint32] + doAssert x.data.len == 5 + var y: Leb128Buf[uint16] + doAssert y.data.len == 3 + +import macros + +block: # issue #12415 + macro isSomePointerImpl(t: typedesc): bool = + var impl = t.getTypeInst[1].getTypeImpl + if impl.kind == nnkDistinctTy: + impl = impl[0].getTypeImpl + if impl.kind in {nnkPtrTy,nnkRefTy}: + result = newLit(true) + elif impl.kind == nnkSym and impl.eqIdent("pointer"): + result = newLit(true) + else: + result = newLit(false) + + proc isSomePointer[T](t: typedesc[T]): bool {.compileTime.} = + isSomePointerImpl(t) + + type + Option[T] = object + ## An optional type that stores its value and state separately in a boolean. + when isSomePointer(typedesc(T)): + val: T + else: + val: T + has: bool + var x: Option[ref int] + doAssert not compiles(x.has) + var y: Option[int] + doAssert compiles(y.has) + +block: # issue #2002 + proc isNillable(T: typedesc): bool = + when compiles((let v: T = nil)): + return true + else: + return false + + type + Foo[T] = object + when isNillable(T): + nillable: float + else: + notnillable: int + + var val1: Foo[ref int] + doAssert compiles(val1.nillable) + doAssert not compiles(val1.notnillable) + var val2: Foo[int] + doAssert not compiles(val2.nillable) + doAssert compiles(val2.notnillable) + +block: # issue #1771 + type + Foo[X, T] = object + bar: array[X.low..X.high, T] + + proc test[X, T](f: Foo[X, T]): T = + f.bar[X.low] + + var a: Foo[range[0..2], float] + doAssert test(a) == 0.0 + +block: # issue #23730 + proc test(M: static[int]): array[1 shl M, int] = discard + doAssert len(test(3)) == 8 + doAssert len(test(5)) == 32 + +block: # issue #19819 + type + Example[N: static int] = distinct int + What[E: Example] = Example[E.N + E.N] + +block: # issue #23339 + type + A = object + B = object + template aToB(t: typedesc[A]): typedesc = B + type + Inner[I] = object + innerField: I + Outer[O] = object + outerField: Inner[O.aToB] + var x: Outer[A] + doAssert typeof(x.outerField.innerField) is B + +block: # deref syntax + type + Enqueueable = concept x + x is ptr + Foo[T: Enqueueable] = object + x: typeof(default(T)[]) + + proc p[T](f: Foo[T]) = + var bar: Foo[T] + discard + var foo: Foo[ptr int] + p(foo) + doAssert foo.x is int + foo.x = 123 + doAssert foo.x == 123 + inc foo.x + doAssert foo.x == 124 + +block: + type Generic[T] = object + field: T + macro foo(x: typed): untyped = x + macro bar[T](x: typedesc[Generic[T]]): untyped = x + type + Foo[T] = object + field: Generic[int].foo() + Foo2[T] = object + field: Generic[T].foo() + Bar[T] = object + field: Generic[int].bar() + Bar2[T] = object + field: Generic[T].bar() + var x: Foo[int] + var x2: Foo2[int] + var y: Bar[int] + var y2: Bar2[int] + +block: + macro pick(x: static int): untyped = + if x < 100: + result = bindSym"int" + else: + result = bindSym"float" + + type Foo[T: static int] = object + fixed1: pick(25) + fixed2: pick(125) + unknown: pick(T) + + var a: Foo[123] + doAssert a.fixed1 is int + doAssert a.fixed2 is float + doAssert a.unknown is float + var b: Foo[23] + doAssert b.fixed1 is int + doAssert b.fixed2 is float + doAssert b.unknown is int + +import std/sequtils + +block: # version of #23432 with `typed`, don't delay instantiation + type + Future[T] = object + InternalRaisesFuture[T, E] = object + macro Raising[T](F: typedesc[Future[T]], E: varargs[typed]): untyped = + let raises = nnkTupleConstr.newTree(E.mapIt(it)) + nnkBracketExpr.newTree( + ident "InternalRaisesFuture", + nnkDotExpr.newTree(F, ident"T"), + raises + ) + type X[E] = Future[void].Raising(E) + proc f(x: X) = discard + var v: Future[void].Raising([ValueError]) + f(v) + +block: # issue #22647 + proc c0(n: static int): int = 8 + proc c1(n: static int): int = n div 2 + proc c2(n: static int): int = n * 2 + proc c3(n: static int, n2: int): int = n * n2 + proc `**`(n: static int, n2: int): int = n * n2 + proc c4(n: int, n2: int): int = n * n2 + + type + a[N: static int] = object + f0 : array[N, int] + + b[N: static int] = object + f0 : a[c0(N)] # does not work + f1 : a[c1(N)] # does not work + f2 : a[c2(N)] # does not work + f3 : a[N * 2] # does not work + f4 : a[N] # works + f5: a[c3(N, 2)] + f6: a[N ** 2] + f7: a[2 * N] + f8: a[c4(N, 2)] + + proc p[N: static int](x : a[N]) = discard x.f0[0] + template check(x, s: untyped) = + p(x) + doAssert x is a[s] + doAssert x.N == s + doAssert typeof(x).N == s + doAssert x.f0 == default(array[s, int]) + doAssert x.f0.len == s + proc p2[N: static int](y : a[N]) {.gensym.} = + doAssert y is a[s] + doAssert y.N == s + doAssert typeof(y).N == s + doAssert y.f0 == default(array[s, int]) + doAssert y.f0.len == s + p2(x) + proc p3(z: typeof(x)) {.gensym.} = discard + p3(default(a[s])) + proc p[N: static int](x : b[N]) = + x.f0.check(8) + x.f1.check(2) + x.f2.check(8) + x.f3.check(8) + x.f4.check(4) + x.f5.check(8) + x.f6.check(8) + x.f7.check(8) + x.f8.check(8) + + var x: b[4] + x.p() + +block: # issue #1969 + type ZeroGenerator = object + proc next(g: ZeroGenerator): int = 0 + # This compiles. + type TripleOfInts = tuple + a, b, c: typeof(new(ZeroGenerator)[].next) + # This raises a compiler error before it's even instantiated. + # The `new` proc can't be resolved because `Generator` is not defined. + type TripleLike[Generator] = tuple + a, b, c: typeof(new(Generator)[].next) + +import std/atomics + +block: # issue #12720 + const CacheLineSize = 128 + type + Enqueueable = concept x, type T + x is ptr + x.next is Atomic[pointer] + MyChannel[T: Enqueueable] = object + pad: array[CacheLineSize - sizeof(default(T)[]), byte] + dummy: typeof(default(T)[]) + +block: # issue #12714 + type + Enqueueable = concept x, type T + x is ptr + x.next is Atomic[pointer] + MyChannel[T: Enqueueable] = object + dummy: type(default(T)[]) + +block: # issue #24044 + type ArrayBuf[N: static int, T = byte] = object + buf: array[N, T] + template maxLen(T: type): int = + sizeof(T) * 2 + type MyBuf[I] = ArrayBuf[maxLen(I)] + var v: MyBuf[int] + +block: # issue #15959 + proc my[T](a: T): typeof(a[0]) = discard + proc my2[T](a: T): array[sizeof(a[0]), T] = discard + proc byLent2[T](a: T): lent type(a[0]) = a[0] # Error: type mismatch: got <T, int literal(0)> + proc byLent3[T](a: T): lent typeof(a[0]) = a[0] # ditto + proc byLent4[T](a: T): lent[type(a[0])] = a[0] # Error: no generic parameters allowed for lent + var x = @[1, 2, 3] + doAssert my(x) is int + doAssert my2(x) is array[sizeof(int), seq[int]] + doAssert byLent2(x) == 1 + doAssert byLent2(x) is lent int + doAssert byLent3(x) == 1 + doAssert byLent3(x) is lent int + doAssert byLent4(x) == 1 + doAssert byLent4(x) is lent int + proc fn[U](a: U): auto = a + proc my3[T](a: T, b: typeof(fn(a))) = discard + my3(x, x) + doAssert not compiles(my3(x, x[0])) + +block: # issue #22342, type section version of #22607 + type GenAlias[isInt: static bool] = ( + when isInt: + int + else: + float + ) + doAssert GenAlias[true] is int + doAssert GenAlias[false] is float + proc foo(T: static bool): GenAlias[T] = discard + doAssert foo(true) is int + doAssert foo(false) is float + proc foo[T: static bool](v: var GenAlias[T]) = + v += 1 + var x: int + foo[true](x) + doAssert not compiles(foo[false](x)) + foo[true](x) + doAssert x == 2 + var y: float + foo[false](y) + doAssert not compiles(foo[true](y)) + foo[false](y) + doAssert y == 2 + +block: # `when`, test no constant semchecks + type Foo[T] = ( + when false: + {.error: "bad".} + elif defined(neverDefined): + {.error: "bad 2".} + else: + T + ) + var x: Foo[int] + type Bar[T] = ( + when true: + T + elif defined(js): + {.error: "bad".} + else: + {.error: "bad 2".} + ) + var y: Bar[int] + +block: # weird regression + type + Foo[T] = distinct int + Bar[T, U] = distinct int + proc foo[T, U](x: static Foo[T], y: static Bar[T, U]): Foo[T] = + # signature gives: + # Error: cannot instantiate Bar + # got: <typedesc[T], U> + # but expected: <T, U> + x + doAssert foo(Foo[int](1), Bar[int, int](2)).int == 1 + +block: # issue #24090 + type M[V] = object + template y[V](N: type M, v: V): M[V] = default(M[V]) + proc d(x: int | int, f: M[int] = M.y(0)) = discard + d(0, M.y(0)) + type Foo[T] = object + x: typeof(M.y(default(T))) + var a: Foo[int] + doAssert a.x is M[int] + var b: Foo[float] + doAssert b.x is M[float] + doAssert not (compiles do: + type Bar[T] = object + x: typeof(M()) # actually fails here immediately + var bar: Bar[int]) + doAssert not (compiles do: + type Bar[T] = object + x: typeof(default(M)) + var bar: Bar[int] + # gives "undeclared identifier x" because of #24091, + # normally it should fail in the line above + echo bar.x) + proc foo[T: M](x: T = default(T)) = discard x + foo[M[int]]() + doAssert not compiles(foo()) + +block: # above but encountered by sigmatch using replaceTypeVarsN + type Opt[T] = object + x: T + proc none[T](x: type Opt, y: typedesc[T]): Opt[T] = discard + proc foo[T](x: T, a = Opt.none(int)) = discard + foo(1, a = Opt.none(int)) + foo(1) + +block: # real version of above + type Opt[T] = object + x: T + template none(x: type Opt, T: type): Opt[T] = Opt[T]() + proc foo[T](x: T, a = Opt.none(int)) = discard + foo(1, a = Opt.none(int)) + foo(1) + +block: # issue #20880 + type + Child[n: static int] = object + data: array[n, int] + Parent[n: static int] = object + child: Child[3*n] + const n = 3 + doAssert $(typeof Parent[n*3]()) == "Parent[9]" + doAssert $(typeof Parent[1]().child) == "Child[3]" + doAssert Parent[1]().child.data.len == 3 + +{.experimental: "dynamicBindSym".} +block: # issue #16774 + type SecretWord = distinct uint64 + const WordBitWidth = 8 * sizeof(uint64) + func wordsRequired(bits: int): int {.compileTime.} = + ## Compute the number of limbs required + # from the **announced** bit length + (bits + WordBitWidth - 1) div WordBitWidth + type + Curve = enum BLS12_381 + BigInt[bits: static int] = object + limbs: array[bits.wordsRequired, SecretWord] + const BLS12_381_Modulus = default(BigInt[381]) + macro Mod(C: static Curve): untyped = + ## Get the Modulus associated to a curve + result = bindSym($C & "_Modulus") + macro getCurveBitwidth(C: static Curve): untyped = + result = nnkDotExpr.newTree( + getAST(Mod(C)), + ident"bits" + ) + type Fp[C: static Curve] = object + ## Finite Fields / Modular arithmetic + ## modulo the curve modulus + mres: BigInt[getCurveBitwidth(C)] + var x: Fp[BLS12_381] + doAssert x.mres.limbs.len == wordsRequired(getCurveBitWidth(BLS12_381)) + # minimized, as if we haven't tested it already: + macro makeIntLit(c: static int): untyped = + result = newLit(c) + type Test[T: static int] = object + myArray: array[makeIntLit(T), int] + var y: Test[2] + doAssert y.myArray.len == 2 + var z: Test[4] + doAssert z.myArray.len == 4 + +block: # issue #16175 + type + Thing[D: static uint] = object + when D == 0: + kid: char + else: + kid: Thing[D-1] + var t2 = Thing[3]() + doAssert t2.kid is Thing[2.uint] + doAssert t2.kid.kid is Thing[1.uint] + doAssert t2.kid.kid.kid is Thing[0.uint] + doAssert t2.kid.kid.kid.kid is char + var s = Thing[1]() + doAssert s.kid is Thing[0.uint] + doAssert s.kid.kid is char diff --git a/tests/generics/tunique_type.nim b/tests/generics/tunique_type.nim new file mode 100644 index 000000000..1150dea49 --- /dev/null +++ b/tests/generics/tunique_type.nim @@ -0,0 +1,67 @@ +# Bug #2022 + +discard """ + output: '''@[97, 45] +@[true, false] +@[false, false]''' +""" + +## The goal of this snippet is to provide and test a construct for general- +## purpose, random-access mapping. I use an AST-manipulation-based approach +## because it's more efficient than using procedure pointers and less +## verbose than defining a new callable type for every invocation of `map`. + +import sugar +import macros +import strutils + +#=============================================================================== +# Define a system for storing copies of ASTs as static strings. +# This serves the same purpose as D's `alias` parameters for types, used heavily +# in its popular `ranges` and `algorithm` modules. + +var exprNodes {.compileTime.} = newSeq[NimNode]() + +proc refExpr(exprNode: NimNode): string {.compileTime.} = + exprNodes.add exprNode.copy + "expr" & $(exprNodes.len - 1) + +proc derefExpr(exprRef: string): NimNode {.compileTime.} = + exprNodes[parseInt(exprRef[4 .. ^1])] + +#=============================================================================== +# Define a type that allows a callable expression to be mapped onto elements +# of an indexable collection. + +type Mapped[Input; predicate: static[string]] = object + input: Input + +macro map(input, predicate: untyped): untyped = + let predicate = callsite()[2] + newNimNode(nnkObjConstr).add( + newNimNode(nnkBracketExpr).add( + ident"Mapped", + newNimNode(nnkTypeOfExpr).add(input), + newLit(refExpr(predicate))), + newNimNode(nnkExprColonExpr).add( + ident"input", input)) + +proc `[]`(m: Mapped, i: int): auto = + macro buildResult: untyped = + newCall( + derefExpr(m.predicate), + newNimNode(nnkBracketExpr).add( + newDotExpr(ident"m", ident"input"), + ident"i")) + buildResult() + +#=============================================================================== +# Test out our generic mapping construct. + +let a = "a-string".map(ord) +let b = @["a", "seq"].map((e: string) => e == "a") +let c = "another-string".map((e: char) => e == 'o') + +echo(@[a[0], a[1]]) # @[97, 45] +echo(@[b[0], b[1]]) # @[true, false] +echo(@[c[0], c[1]]) # @[false, false] diff --git a/tests/generics/tvarseq_caching.nim b/tests/generics/tvarseq_caching.nim new file mode 100644 index 000000000..f617b9335 --- /dev/null +++ b/tests/generics/tvarseq_caching.nim @@ -0,0 +1,48 @@ +discard """ + output: '''@[1, 2, 3] +@[4.0, 5.0, 6.0] +@[1, 2, 3] +@[4.0, 5.0, 6.0] +@[1, 2, 3] +@[4, 5, 6]''' +""" + +# bug #3476 + +proc foo[T]: var seq[T] = + ## Problem! Bug with generics makes every call to this proc generate + ## a new seq[T] instead of retrieving the `items {.global.}` variable. + var items {.global.}: seq[T] + return items + +proc foo2[T]: ptr seq[T] = + ## Workaround! By returning by `ptr` instead of `var` we can get access to + ## the `items` variable, but that means we have to explicitly deref at callsite. + var items {.global.}: seq[T] + return addr items + +proc bar[T]: var seq[int] = + ## Proof. This proc correctly retrieves the `items` variable. Notice the only thing + ## that's changed from `foo` is that it returns `seq[int]` instead of `seq[T]`. + var items {.global.}: seq[int] + return items + + +foo[int]() = @[1, 2, 3] +foo[float]() = @[4.0, 5.0, 6.0] + +foo2[int]()[] = @[1, 2, 3] +foo2[float]()[] = @[4.0, 5.0, 6.0] + +bar[int]() = @[1, 2, 3] +bar[float]() = @[4, 5, 6] + + +echo foo[int]() # prints 'nil' - BUG! +echo foo[float]() # prints 'nil' - BUG! + +echo foo2[int]()[] # prints '@[1, 2, 3]' +echo foo2[float]()[] # prints '@[4.0, 5.0, 6.0]' + +echo bar[int]() # prints '@[1, 2, 3]' +echo bar[float]() # prints '@[4, 5, 6]' diff --git a/tests/generics/twrong_field_caching.nim b/tests/generics/twrong_field_caching.nim new file mode 100644 index 000000000..667ffbbe5 --- /dev/null +++ b/tests/generics/twrong_field_caching.nim @@ -0,0 +1,68 @@ +discard """ + output: '''a23: 2x3 +a32: 3x2 +transpose A +t32: 3x2 +transpose B +x23: 2x3 (2x3) +x32: 3x2 (3x2)''' +""" + +# bug #2125 +# Suppose we have the following type for a rectangular array: + +type + RectArray*[R, C: static[int], T] = distinct array[R * C, T] + +var a23: RectArray[2, 3, int] +var a32: RectArray[3, 2, int] + +echo "a23: ", a23.R, "x", a23.C +echo "a32: ", a32.R, "x", a32.C + +# Output: +# a23: 2x3 +# a32: 3x2 + +# Looking good. Let's add a proc: +proc transpose*[R, C, T](m: RectArray[R, C, T]): RectArray[C, R, T] = + echo "transpose A" + +var t32 = a23.transpose + +echo "t32: ", t32.R, "x", t32.C + +# Output: +# t32: 3x2 + + +# Everything is still OK. Now let's use the rectangular array inside another +# generic type: +type + Matrix*[R, C: static[int], T] = object + theArray*: RectArray[R, C, T] + +#var m23: Matrix[2, 3, int] +#var m32: Matrix[3, 2, int] + +#echo "m23: ", m23.R, "x", m23.C, " (", m23.theArray.R, "x", m23.theArray.C, ")" +#echo "m32: ", m32.R, "x", m32.C, " (", m32.theArray.R, "x", m32.theArray.C, ")" + +# Output: +# m23: 2x3 (2x3) +# m32: 3x2 (3x2) + + +# Everything is still as expected. Now let's add the following proc: +proc transpose*[R, C, T](m: Matrix[R, C, T]): Matrix[C, R, T] = + echo "transpose B" + +var x23: Matrix[2, 3, int] +var x32 = x23.transpose + +echo "x23: ", x23.R, "x", x23.C, " (", x23.theArray.R, "x", x23.theArray.C, ")" +echo "x32: ", x32.R, "x", x32.C, " (", x32.theArray.R, "x", x32.theArray.C, ")" + +# Output: +# x23: 2x3 (2x3) +# x32: 3x2 (3x2) <--- this is incorrect. R and C do not match! diff --git a/tests/generics/twrong_floatlit_type.nim b/tests/generics/twrong_floatlit_type.nim new file mode 100644 index 000000000..04bacc0d9 --- /dev/null +++ b/tests/generics/twrong_floatlit_type.nim @@ -0,0 +1,118 @@ +discard """ + errormsg: "type mismatch" + line: 116 +""" + +# bug #2169 +import strutils, math + +type + Point2D*[S] = object + x*, y*: S + Matrix2x3*[S] = distinct array[6, S] ## Row major order + + Vector2D*[S] = object + x*, y*: S + +proc `[]`*[T](m: Matrix2x3[T], i: int): T = array[6, T](m)[i] + +template M11*[T](m: Matrix2x3[T]): T = m[0] +template M12*[T](m: Matrix2x3[T]): T = m[1] +template M13*[T](m: Matrix2x3[T]): T = m[2] +template M21*[T](m: Matrix2x3[T]): T = m[3] +template M22*[T](m: Matrix2x3[T]): T = m[4] +template M23*[T](m: Matrix2x3[T]): T = m[5] + +proc identity*[T](): Matrix2x3[T] = + Matrix2x3[T]([T(1.0), 0.0, 0.0, 0.0, 1.0, 0.0]) + +proc translation*[T](p: Point2D[T]): Matrix2x3[T] = + Matrix2x3[T]([T(1.0), T(0.0), p.x, T(0.0), T(1.0), p.y]) + +proc translation*[T](p: Vector2D[T]): Matrix2x3[T] = + Matrix2x3[T]([T(1.0), T(0.0), p.x, T(0.0), T(1.0), p.y]) + +proc scale*[T](v: Vector2D[T]): Matrix2x3[T] = + Matrix2x3[T]([v.x, T(0.0), T(0.0), T(0.0), v.y, T(0.0)]) + +proc rotation*[T](th: T): Matrix2x3[T] = + let + c = T(cos(th.float)) + s = T(sin(th.float)) + + Matrix2x3[T]([c, -s, T(0.0), s, c, T(0.0)]) + +proc `*`*[T](a, b: Matrix2x3[T]): Matrix2x3[T] = + # Here we pretend that row 3 is [0,0,0,1] without + # actually storing it in the matrix. + Matrix2x3[T]([a.M11*b.M11 + a.M12*b.M21, + a.M11*b.M12 + a.M12*b.M22, + a.M11*b.M13 + a.M12*b.M23 + a.M13, + + a.M21*b.M11 + a.M22*b.M21, + a.M21*b.M12 + a.M22*b.M22, + a.M21*b.M13 + a.M22*b.M23 + a.M23]) + +proc `*`*[T](a: Matrix2x3[T], p: Point2D[T]): Point2D[T] = + let + x = a.M11*p.x + a.M12*p.y + a.M13 + y = a.M21*p.x + a.M22*p.y + a.M23 + + Point2D[T](x: x, y: y) + +# making these so things like "line" that need a constructor don't stick out. +# 2x2 determinant: |a b| +# |c d| = ad - bc + +# String rendering +# +template ff[S](x: S): string = + formatFloat(float(x), ffDefault, 0) + +proc `$`*[S](p: Point2D[S]): string = + "P($1, $2)" % [ff(p.x), ff(p.y)] + +proc `$`*[S](p: Vector2D[S]): string = + "V($1, $2)" % [ff(p.x), ff(p.y)] + +proc `$`*[S](m: Matrix2x3[S]): string = + "M($1 $2 $3/$4 $5 $6)" % [ff(m.M11), ff(m.M12), ff(m.M13), + ff(m.M21), ff(m.M22), ff(m.M23)] + +# +# Vector operators. +proc `-`*[S](a: Vector2D[S]): Vector2D[S] = + Vector2D[S](x: -a.x, y: -a.y) + +proc `+`*[S](a, b: Vector2D[S]): Vector2D[S] = + Vector2D[S](x: a.x + b.x, y: a.y + b.y) + +proc `-`*[S](a, b: Vector2D[S]): Vector2D[S] = + Vector2D[S](x: a.x - b.x, y: a.y - b.y) + +proc `*`*[S](v: Vector2D[S], sc: S): Vector2D[S] = + Vector2D[S](x: v.x*sc, y: v.y*sc) + +proc `*`*[S](sc: S, v: Vector2D[S]): Vector2D[S] = + Vector2D[S](x: v.x*sc, y: v.y*sc) + +proc `/`*[S](v: Vector2D[S], sc: S): Vector2D[S] = + Vector2D[S](x: v.x/sc, y: v.y/sc) + +proc `/`*[S](sc: S; v: Vector2D[S]): Vector2D[S] = + Vector2D[S](x: sc/v.x, y: sc/v.y) + +proc `/`*[S](a, b: Vector2D[S]): Vector2D[S] = + Vector2D[S](x: a.x/b.x, y: a.y/b.y) +#proc vec[S](x, y: S): Vector2D[S] +proc vec[S](x, y: S): Vector2D[S] = + Vector2D[S](x: x, y: y) + +if true: + # Comment out this let, and the program will fail to + # compile with a type mismatch, as expected. + + let s3 = scale(vec(4.0, 4.0)) + let barf = translation(Point2D[float32](x: 1, y: 1)) * rotation(float(0.7)) + + echo "Badness ", barf diff --git a/tests/generics/twrong_generic_object.nim b/tests/generics/twrong_generic_object.nim new file mode 100644 index 000000000..4951f735f --- /dev/null +++ b/tests/generics/twrong_generic_object.nim @@ -0,0 +1,21 @@ +discard """ + errormsg: "'Node' is not a concrete type" + line: 11 +""" +# bug #2509 +type + GenericNodeObj[T] = ref object + obj: T + + Node* = ref object + children*: seq[Node] + parent*: Node + + nodeObj*: GenericNodeObj # [int] + +proc newNode*(nodeObj: GenericNodeObj): Node = + result = Node(nodeObj: nodeObj) + newSeq(result.children, 10) + +var genericObj = GenericNodeObj[int]() +var myNode = newNode(genericObj) |