summary refs log tree commit diff stats
path: root/tests/proc
diff options
context:
space:
mode:
authormetagn <metagngn@gmail.com>2024-08-26 07:54:38 +0300
committerGitHub <noreply@github.com>2024-08-26 06:54:38 +0200
commit69ea1336fb43feb9a4f121d590b31581e859cbe5 (patch)
tree57bb53a043f7a3a1e12179b5d22908c9f3e3c7ee /tests/proc
parent09dcff71c82147b28059c6470bd921afab9c825e (diff)
downloadNim-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.nim206
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[])