summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/semexprs.nim20
-rw-r--r--compiler/semtypes.nim21
-rw-r--r--compiler/semtypinst.nim25
-rw-r--r--compiler/sigmatch.nim46
-rw-r--r--tests/generics/t23854.nim3
-rw-r--r--tests/generics/t23855.nim2
-rw-r--r--tests/generics/tuninstantiatedgenericcalls.nim62
-rw-r--r--tests/proc/tstaticsignature.nim206
8 files changed, 354 insertions, 31 deletions
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 8332d404d..f52de6750 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -1482,20 +1482,28 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
 
 proc tryReadingGenericParam(c: PContext, n: PNode, i: PIdent, t: PType): PNode =
   case t.kind
-  of tyTypeParamsHolders:
+  of tyGenericInst:
     result = readTypeParameter(c, t, i, n.info)
     if result == c.graph.emptyNode:
-      result = semGenericStmt(c, n)
-      result.typ = makeTypeFromExpr(c, result.copyTree)
+      if c.inGenericContext > 0:
+        result = semGenericStmt(c, n)
+        result.typ = makeTypeFromExpr(c, result.copyTree)
+      else:
+        result = nil
   of tyUserTypeClasses:
     if t.isResolvedUserTypeClass:
       result = readTypeParameter(c, t, i, n.info)
+    elif c.inGenericContext > 0:
+      result = semGenericStmt(c, n)
+      result.typ = makeTypeFromExpr(c, copyTree(result))
     else:
+      result = nil
+  elif t.containsGenericType:
+    if c.inGenericContext > 0:
       result = semGenericStmt(c, n)
       result.typ = makeTypeFromExpr(c, copyTree(result))
-  of tyFromExpr, tyGenericParam, tyAnything:
-    result = semGenericStmt(c, n)
-    result.typ = makeTypeFromExpr(c, copyTree(result))
+    else:
+      result = nil
   else:
     result = nil
 
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index bdf8fb8ca..2a5b6c751 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -1312,6 +1312,9 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
   result = newProcType(c, n.info, prev)
   var check = initIntSet()
   var counter = 0
+  template isCurrentlyGeneric: bool =
+    # genericParams might update as implicit generic params are added
+    genericParams != nil and genericParams.len > 0
 
   for i in 1..<n.len:
     var a = n[i]
@@ -1332,7 +1335,10 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
       hasDefault = a[^1].kind != nkEmpty
 
     if hasType:
+      let isGeneric = isCurrentlyGeneric()
+      inc c.inGenericContext, ord(isGeneric)
       typ = semParamType(c, a[^2], constraint)
+      dec c.inGenericContext, ord(isGeneric)
       # TODO: Disallow typed/untyped in procs in the compiler/stdlib
       if kind in {skProc, skFunc} and (typ.kind == tyTyped or typ.kind == tyUntyped):
         if not isMagic(getCurrOwner(c)):
@@ -1353,7 +1359,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
         message(c.config, a.info, warnImplicitDefaultValue, msg)
       block determineType:
         var defTyp = typ
-        if genericParams != nil and genericParams.len > 0:
+        if isCurrentlyGeneric():
           defTyp = nil
           def = semGenericStmt(c, def)
           if hasUnresolvedArgs(c, def):
@@ -1432,11 +1438,12 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
       onDef(a[j].info, arg)
       a[j] = newSymNode(arg)
 
-  var r: PType =
-    if n[0].kind != nkEmpty:
-      semTypeNode(c, n[0], nil)
-    else:
-      nil
+  var r: PType = nil
+  if n[0].kind != nkEmpty:
+    let isGeneric = isCurrentlyGeneric()
+    inc c.inGenericContext, ord(isGeneric)
+    r = semTypeNode(c, n[0], nil)
+    dec c.inGenericContext, ord(isGeneric)
 
   if r != nil and kind in {skMacro, skTemplate} and r.kind == tyTyped:
     # XXX: To implement the proposed change in the warning, just
@@ -1489,7 +1496,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
           result.flags.excl tfHasMeta
       result.n.typ = r
 
-  if genericParams != nil and genericParams.len > 0:
+  if isCurrentlyGeneric():
     for n in genericParams:
       if {sfUsed, sfAnon} * n.sym.flags == {}:
         result.flags.incl tfUnresolved
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index 428d897ec..483db0bbb 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -678,8 +678,31 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
     elif t.elementType.kind != tyNone:
       result = makeTypeDesc(cl.c, replaceTypeVarsT(cl, t.elementType))
 
-  of tyUserTypeClass, tyStatic:
+  of tyUserTypeClass:
     result = t
+  
+  of tyStatic:
+    if cl.c.matchedConcept != nil:
+      # allow concepts to not instantiate statics for now
+      # they can't always infer them
+      return
+    if not containsGenericType(t) and (t.n == nil or t.n.kind in nkLiterals):
+      # no need to instantiate
+      return
+    bailout()
+    result = instCopyType(cl, t)
+    cl.localCache[t.itemId] = result
+    for i in FirstGenericParamAt..<result.kidsLen:
+      var r = result[i]
+      if r != nil:
+        r = replaceTypeVarsT(cl, r)
+        result[i] = r
+        propagateToOwner(result, r)
+    result.n = replaceTypeVarsN(cl, result.n)
+    if not cl.allowMetaTypes and result.n != nil and
+        result.base.kind != tyNone:
+      result.n = cl.c.semConstExpr(cl.c, result.n)
+      result.n.typ = result.base
 
   of tyGenericInst, tyUserTypeClassInst:
     bailout()
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index d0c9ce029..ee1ad49d0 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -885,6 +885,7 @@ proc maybeSkipDistinct(m: TCandidate; t: PType, callee: PSym): PType =
 
 proc tryResolvingStaticExpr(c: var TCandidate, n: PNode,
                             allowUnresolved = false,
+                            allowCalls = false,
                             expectedType: PType = nil): PNode =
   # Consider this example:
   #   type Value[N: static[int]] = object
@@ -894,7 +895,7 @@ proc tryResolvingStaticExpr(c: var TCandidate, n: PNode,
   # This proc is used to evaluate such static expressions.
   let instantiated = replaceTypesInBody(c.c, c.bindings, n, nil,
                                         allowMetaTypes = allowUnresolved)
-  if instantiated.kind in nkCallKinds:
+  if not allowCalls and instantiated.kind in nkCallKinds:
     return nil
   result = c.c.semExpr(c.c, instantiated)
 
@@ -966,7 +967,8 @@ proc inferStaticParam*(c: var TCandidate, lhs: PNode, rhs: BiggestInt): bool =
 
     else: discard
 
-  elif lhs.kind == nkSym and lhs.typ.kind == tyStatic and lhs.typ.n == nil:
+  elif lhs.kind == nkSym and lhs.typ.kind == tyStatic and
+      (lhs.typ.n == nil or idTableGet(c.bindings, lhs.typ) == nil):
     var inferred = newTypeS(tyStatic, c.c, lhs.typ.elementType)
     inferred.n = newIntNode(nkIntLit, rhs)
     put(c, lhs.typ, inferred)
@@ -1877,7 +1879,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
         elif f.base.kind notin {tyNone, tyGenericParam}:
           result = typeRel(c, f.base, a, flags)
           if result != isNone and f.n != nil:
-            if not exprStructuralEquivalent(f.n, aOrig.n):
+            var r = tryResolvingStaticExpr(c, f.n)
+            if r == nil: r = f.n
+            if not exprStructuralEquivalent(r, aOrig.n) and
+                not (aOrig.n.kind == nkIntLit and
+                  inferStaticParam(c, r, aOrig.n.intVal)):
               result = isNone
         elif f.base.kind == tyGenericParam:
           # Handling things like `type A[T; Y: static T] = object`
@@ -1963,23 +1969,29 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
   of tyFromExpr:
     # fix the expression, so it contains the already instantiated types
     if f.n == nil or f.n.kind == nkEmpty: return isGeneric
-    let reevaluated = tryResolvingStaticExpr(c, f.n)
-    if reevaluated == nil:
+    if c.c.inGenericContext > 0:
+      # need to delay until instantiation
+      # also prevent infinite recursion below
+      return isNone
+    inc c.c.inGenericContext # to generate tyFromExpr again if unresolved
+    let reevaluated = tryResolvingStaticExpr(c, f.n, allowCalls = true).typ
+    dec c.c.inGenericContext
+    case reevaluated.kind
+    of tyFromExpr:
+      # not resolved
       result = isNone
-      return
-    case reevaluated.typ.kind
     of tyTypeDesc:
-      result = typeRel(c, a, reevaluated.typ.base, flags)
+      result = typeRel(c, a, reevaluated.base, flags)
     of tyStatic:
-      result = typeRel(c, a, reevaluated.typ.base, flags)
-      if result != isNone and reevaluated.typ.n != nil:
-        if not exprStructuralEquivalent(aOrig.n, reevaluated.typ.n):
+      result = typeRel(c, a, reevaluated.base, flags)
+      if result != isNone and reevaluated.n != nil:
+        if not exprStructuralEquivalent(aOrig.n, reevaluated.n):
           result = isNone
     else:
       # bug #14136: other types are just like 'tyStatic' here:
-      result = typeRel(c, a, reevaluated.typ, flags)
-      if result != isNone and reevaluated.typ.n != nil:
-        if not exprStructuralEquivalent(aOrig.n, reevaluated.typ.n):
+      result = typeRel(c, a, reevaluated, flags)
+      if result != isNone and reevaluated.n != nil:
+        if not exprStructuralEquivalent(aOrig.n, reevaluated.n):
           result = isNone
   of tyNone:
     if a.kind == tyNone: result = isEqual
@@ -2188,7 +2200,11 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
         a = typ
       else:
         if m.callee.kind == tyGenericBody:
-          if f.kind == tyStatic and typeRel(m, f.base, a) != isNone:
+          # we can't use `makeStaticExpr` if `arg` has a generic type
+          # because it generates `tyStatic`, which semtypinst doesn't touch
+          # not sure if checking for `tyFromExpr` is enough
+          if f.kind == tyStatic and typeRel(m, f.base, a) != isNone and
+              a.kind != tyFromExpr:
             result = makeStaticExpr(m.c, arg)
             result.typ.flags.incl tfUnresolved
             result.typ.n = arg
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[])