diff options
author | metagn <metagngn@gmail.com> | 2024-08-26 07:54:38 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-08-26 06:54:38 +0200 |
commit | 69ea1336fb43feb9a4f121d590b31581e859cbe5 (patch) | |
tree | 57bb53a043f7a3a1e12179b5d22908c9f3e3c7ee /tests/proc | |
parent | 09dcff71c82147b28059c6470bd921afab9c825e (diff) | |
download | Nim-69ea1336fb43feb9a4f121d590b31581e859cbe5.tar.gz |
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.
Diffstat (limited to 'tests/proc')
-rw-r--r-- | tests/proc/tstaticsignature.nim | 206 |
1 files changed, 206 insertions, 0 deletions
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[]) |