From 69ea1336fb43feb9a4f121d590b31581e859cbe5 Mon Sep 17 00:00:00 2001 From: metagn Date: Mon, 26 Aug 2024 07:54:38 +0300 Subject: sem generic proc param types like generic types + static instantiation fixes (#24005) fixes #4228, fixes #4990, fixes #7006, fixes #7008, fixes #8406, fixes #8551, fixes #11112, fixes #20027, fixes #22647, refs #23854 and #23855 (remaining issue fixed), refs #8545 (works properly now with `cast[static[bool]]` changed to `cast[bool]`), refs #22342 and #22607 (disabled tests added), succeeds #23194 Parameter and return type nodes in generic procs now undergo the same `inGenericContext` treatment that nodes in generic type bodies do. This allows many of the fixes in #22029 and followups to also apply to generic proc signatures. Like #23983 however this needs some more compiler fixes, but this time mostly in `sigmatch` and type instantiations. 1. `tryReadingGenericParam` no longer treats `tyCompositeTypeClass` like a concrete type anymore, so expressions like `Foo.T` where `Foo` is a generic type don't look for a parameter of `Foo` in non-generic code anymore. It also doesn't generate `tyFromExpr` in non-generic code for any generic LHS. This is to handle a very specific case in `asyncmacro` which used `FutureVar.astToStr` where `FutureVar` is generic. 2. The `tryResolvingStaticExpr` call when matching `tyFromExpr` in sigmatch now doesn't consider call nodes in general unresolved, only nodes with `tyFromExpr` type, which is emitted on unresolved expressions by increasing `c.inGenericContext`. `c.inGenericContext == 0` is also now required to attempt instantiating `tyFromExpr`. So matching against `tyFromExpr` in proc signatures works in general now, but I'm speculating it depends on constant folding in `semExpr` for statics to match against it properly. 3. `paramTypesMatch` now doesn't try to change nodes with `tyFromExpr` type into `tyStatic` type when fitting to a static type, because it doesn't need to, they'll be handled the same way (this was a workaround in place of the static type instantiation changes, only one of the fields in the #22647 test doesn't work with it). 4. `tyStatic` matching now uses `inferStaticParam` instead of just range type matching, so `Foo[N div 2]` can infer `N` in the same way `array[N div 2, int]` can. `inferStaticParam` also disabled itself if the inferred static param type already had a node, but `makeStaticExpr` generates static types with unresolved nodes, so we only disable it if it also doesn't have a binding. This might not work very well but the static type instantiation changes should really lower the amount of cases where it's encountered. 5. Static types now undergo type instantiation. Previously the branch for `tyStatic` in `semtypinst` was a no-op, now it acts similarly to instantiating any other type with the following differences: - Other types only need instantiation if `containsGenericType` is true, static types also get instantiated if their value node isn't a literal node. Ideally any value node that is "already evaluated" should be ignored, but I'm not sure of a better way to check this, maybe if `evalConstExpr` emitted a flag. This is purely for optimization though. - After instantiation, `semConstExpr` is called on the value node if `not cl.allowMetaTypes` and the type isn't literally a `static` type. Then the type of the node is set to the base type of the static type to deal with `semConstExpr` stripping abstract types. We need to do this because calls like `foo(N)` where `N` is `static int` and `foo`'s first parameter is just `int` do not generate `tyFromExpr`, they are fully typed and so `makeStaticExpr` is called on them, giving a static type with an unresolved node. --- tests/generics/t23854.nim | 3 +- tests/generics/t23855.nim | 2 +- tests/generics/tuninstantiatedgenericcalls.nim | 62 ++++++++ tests/proc/tstaticsignature.nim | 206 +++++++++++++++++++++++++ 4 files changed, 271 insertions(+), 2 deletions(-) create mode 100644 tests/proc/tstaticsignature.nim (limited to 'tests') diff --git a/tests/generics/t23854.nim b/tests/generics/t23854.nim index 675f9d121..f1175c8b2 100644 --- a/tests/generics/t23854.nim +++ b/tests/generics/t23854.nim @@ -63,7 +63,8 @@ template getBits[bits: static int](x: ptr UncheckedArray[BigInt[bits]]): int = b proc main() = let ctx = ECFFT_Descriptor[EC_ShortW_Aff[Fp[BLS12_381]]].new() - when false: echo getBits(ctx.rootsOfUnity2) # doesn't work yet? + 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) diff --git a/tests/generics/t23855.nim b/tests/generics/t23855.nim index 5ae927a94..da8135a98 100644 --- a/tests/generics/t23855.nim +++ b/tests/generics/t23855.nim @@ -55,7 +55,7 @@ template getBits[bits: static int](x: ptr UncheckedArray[BigInt[bits]]): int = b proc main() = let ctx = ECFFT_Descriptor[EC_ShortW_Aff[Fp[BLS12_381]]].new() - when false: echo getBits(ctx.rootsOfUnity) # doesn't work yet? + doAssert getBits(ctx.rootsOfUnity) == 255 doAssert ctx.rootsOfUnity[0].limbs.len == wordsRequired(255) main() diff --git a/tests/generics/tuninstantiatedgenericcalls.nim b/tests/generics/tuninstantiatedgenericcalls.nim index 172a00bd4..52e3560d1 100644 --- a/tests/generics/tuninstantiatedgenericcalls.nim +++ b/tests/generics/tuninstantiatedgenericcalls.nim @@ -239,3 +239,65 @@ block: # version of #23432 with `typed`, don't delay instantiation 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() + +when false: # issue #22342, type section version of #22607 + type GenAlias[isInt: static bool] = ( + when isInt: + int + else: + float + ) diff --git a/tests/proc/tstaticsignature.nim b/tests/proc/tstaticsignature.nim new file mode 100644 index 000000000..1c722606c --- /dev/null +++ b/tests/proc/tstaticsignature.nim @@ -0,0 +1,206 @@ +block: # issue #4228 + template seqType(t: typedesc): typedesc = + when t is int: + seq[int] + else: + seq[string] + + proc mkSeq[T: int|string](v: T): seqType(T) = + result = newSeq[T](1) + result[0] = v + + doAssert mkSeq("a") == @["a"] + doAssert mkSeq(1) == @[1] + +block: # expanded version of t8545 + template bar(a: static[bool]): untyped = + when a: + int + else: + float + + proc main() = + proc foo1(a: static[bool]): auto = 1 + doAssert foo1(true) == 1 + + proc foo2(a: static[bool]): bar(a) = 1 + doAssert foo2(true) == 1 + doAssert foo2(true) is int + doAssert foo2(false) == 1.0 + doAssert foo2(false) is float + + proc foo3(a: static[bool]): bar(cast[bool](a)) = 1 + doAssert foo3(true) == 1 + doAssert foo3(true) is int + doAssert foo3(false) == 1.0 + doAssert foo3(false) is float + + proc foo4(a: static[bool]): bar(static(a)) = 1 + doAssert foo4(true) == 1 + doAssert foo4(true) is int + doAssert foo4(false) == 1.0 + doAssert foo4(false) is float + + static: main() + main() + +block: # issue #8406 + macro f(x: static[int]): untyped = discard + proc g[X: static[int]](v: f(X)) = discard + +import macros + +block: # issue #8551 + macro distinctBase2(T: typedesc): untyped = + let typeNode = getTypeImpl(T) + expectKind(typeNode, nnkBracketExpr) + if typeNode[0].typeKind != ntyTypeDesc: + error "expected typeDesc, got " & $typeNode[0] + var typeSym = typeNode[1] + + typeSym = getTypeImpl(typeSym) + + if typeSym.typeKind != ntyDistinct: + error "type is not distinct: " & $typeSym.typeKind + + typeSym = typeSym[0] + typeSym + + func distinctBase[T](a: T): distinctBase2(T) = distinctBase2(T)(a) + + type T = distinct int + doAssert distinctBase(T(0)) is int + +block: + type Foo[T] = object + x: T + + proc foo(x: Foo): Foo[x.T] = + doAssert typeof(result) is typeof(x) + + var a: Foo[int] + let b: Foo[int] = foo(a) + doAssert b.x is int + +block: + type Foo[T: static int] = object + x: array[T, int] + + proc double(x: int): int = x * 2 + + proc foo[T: static int](x: Foo[T]): Foo[T.double] = + doAssert typeof(result).T == double(typeof(x).T) + + var a: Foo[3] + let b: Foo[6] = foo(a) + doAssert $typeof(foo(a)) == "Foo[6]" + +block: + type Foo[T: static int] = object + x: array[T, int] + + proc foo(x: Foo): Foo[x.T] = + doAssert typeof(result).T == typeof(x).T + doAssert typeof(result) is typeof(x) + + var a: Foo[3] + let b: Foo[3] = foo(a) + doAssert $typeof(foo(a)) == "Foo[3]" + +block: # issue #7006 + type + Node[T] = object + val: T + next: ref Node[T] + HHSet[T, Key] = object + data: seq[Node[T]] + proc rawGet(hhs:HHSet; key: hhs.Key): ptr Node[hhs.T] = + return nil # body doesn't matter + var hhs: HHSet[string, cstring] + discard hhs.rawGet("hello".cstring) + +block: # issue #7008 + type Node[T] = object + val: T + # Compiles fine + proc concreteProc(s: Node[cstring]; key: s.T) = discard + # Also fine + proc implicitGenericProc1(s: Node; key: s.T) = discard + # still fine + proc explicitGenericProc1[T](s: Node[T]; key: T) = discard + # Internal Compiler Error! + proc explicitGenericProc2[T](s: Node[T]; key: s.T) = discard + let n = Node[int](val: 5) + implicitGenericProc1(n, 5) # works + explicitGenericProc1(n, 5) # works + explicitGenericProc2(n, 5) # doesn't + +block: # issue #20027 + block: + type Test[T] = object + proc run(self: Test): self.T = discard + discard run(Test[int]()) + block: + type Test[T] = object + proc run[T](self: Test[T]): self.T = discard + discard run(Test[int]()) + block: + type Test[T] = object + proc run(self: Test[auto]): self.T = discard + discard run(Test[int]()) + +block: # issue #11112 + proc foo[A, B]: type(A.default + B.default) = + discard + doAssert foo[int, int]() is int + +block: # tyStatic and tyFromExpr instantiation mid-match + proc bar(x: int): int = x * 3 + proc bar2(x: static int): int = x * 4 + type Foo[T: static int] = distinct array[T, int] + proc foo[T: static int](x: Foo[T], y: Foo[bar(T)]) = discard + proc foo2[T: static int](x: Foo[T], y: Foo[bar2(T)]) = discard + foo(Foo[1]([1]), Foo[3]([1, 2, 3])) + foo2(Foo[1]([1]), Foo[4]([1, 2, 3, 4])) + +block: # issue #4990 + type Foo[I: static[int], A: static[array[I, int]]] = object + curIndex: int + + proc next[I: static[int], A: static[array[I, int]]](f: Foo[I, A]): string = + discard + const arr = [1, 2, 3] + var f: Foo[arr.len, arr] + discard next(f) + +block: # issue #4990 comment + type + Foo[A: static[int], B: static[int], TokenType: enum, EofToken: static[TokenType]] = object + curIndex: int + MyEnum = enum + meA, meB + Bar = Foo[2, 3, MyEnum, meA] + proc next[A: static[int], B: static[int], TokenType: enum, + EofToken: static[TokenType]](f: Foo[A, B, TokenType, EofToken], + a: static[(array[A, int], array[B, int])]): TokenType = + TokenType(a[0][f.curIndex]) + const + a = [1, 2] + b = [3, 4, 5] + template next(bar: Bar): MyEnum = + next(Foo[2, 3, MyEnum, meA](bar), (a, b)) + let bar = Bar(curIndex: 0) + doAssert bar.next() == meB + +when false: # issue #22607, needs nkWhenStmt to be handled like nkRecWhen + proc test[x: static bool]( + t: ( + when x: + int + else: + float + ) + ) = discard + test[true](1.int) + test[false](1.0) + doAssert not compiles(test[]) -- cgit 1.4.1-2-gfad0