summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/parser.nim3
-rw-r--r--compiler/semdata.nim3
-rw-r--r--compiler/semexprs.nim81
-rw-r--r--compiler/semtypes.nim7
-rw-r--r--tests/metatype/ttypedesc1.nim34
-rw-r--r--tests/metatype/ttypedesc3.nim6
-rw-r--r--tests/metatype/ttypeselectors.nim6
-rw-r--r--tests/parser/ttypeclasses.nim39
-rw-r--r--tests/statictypes/tstatictypes.nim84
-rw-r--r--tests/types/tisopr.nim2
10 files changed, 210 insertions, 55 deletions
diff --git a/compiler/parser.nim b/compiler/parser.nim
index 33ee8c9e6..5c99363c9 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -798,8 +798,7 @@ proc primarySuffix(p: var TParser, r: PNode,
         # `foo ref` or `foo ptr`. Unfortunately, these two are also
         # used as infix operators for the memory regions feature and
         # the current parsing rules don't play well here.
-      if mode == pmTypeDef or
-        (p.inPragma == 0 and (isUnary(p) or p.tok.tokType notin {tkOpr, tkDotDot})):
+      if p.inPragma == 0 and (isUnary(p) or p.tok.tokType notin {tkOpr, tkDotDot}):
         # actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet
         # solution, but pragmas.nim can't handle that
         let a = result
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index c858b6839..aa0cb6e8e 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -288,7 +288,8 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType =
     result.addSonSkipIntLit(typ)
 
 proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
-  let typedesc = makeTypeDesc(c, typ)
+  let typedesc = newTypeS(tyTypeDesc, c)
+  typedesc.addSonSkipIntLit(assertNotNil(c.config, typ))
   let sym = newSym(skType, c.cache.idAnon, getCurrOwner(c), info,
                    c.config.options).linkTo(typedesc)
   return newSymNode(sym, info)
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 4b45ccf87..a072deb7d 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -194,7 +194,7 @@ proc semConv(c: PContext, n: PNode): PNode =
 
   var targetType = semTypeNode(c, n.sons[0], nil)
   if targetType.kind == tyTypeDesc:
-    internalAssert targetType.len > 0
+    internalAssert c.config, targetType.len > 0
     if targetType.base.kind == tyNone:
       return semTypeOf(c, n[1])
     else:
@@ -316,57 +316,98 @@ proc semSizeof(c: PContext, n: PNode): PNode =
   n.typ = getSysType(c.graph, n.info, tyInt)
   result = n
 
+proc fixupStaticType(c: PContext, n: PNode) =
+  # This proc can be applied to evaluated expressions to assign
+  # them a static type.
+  #
+  # XXX: with implicit static, this should not be necessary,
+  # because the output type of operations such as `semConstExpr`
+  # should be a static type (as well as the type of any other
+  # expression that can be implicitly evaluated). For now, we
+  # apply this measure only in code that is enlightened to work
+  # with static types.
+  if n.typ.kind != tyStatic:
+    n.typ = newTypeWithSons(getCurrOwner(c), tyStatic, @[n.typ])
+    n.typ.n = n # XXX: cycles like the one here look dangerous.
+                # Consider using `n.copyTree`
+
 proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode =
-  internalAssert c.config, n.sonsLen == 3 and
-    n[1].typ != nil and n[1].typ.kind == tyTypeDesc and
+  internalAssert c.config,
+    n.sonsLen == 3 and
+    n[1].typ != nil and
     n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
 
-  let t1 = n[1].typ.skipTypes({tyTypeDesc})
+  var
+    res = false
+    t1 = n[1].typ
+    t2 = n[2].typ
+
+  if t1.kind == tyTypeDesc and t2.kind != tyTypeDesc:
+    t1 = t1.base
 
   if n[2].kind in {nkStrLit..nkTripleStrLit}:
     case n[2].strVal.normalize
     of "closure":
       let t = skipTypes(t1, abstractRange)
-      result = newIntNode(nkIntLit, ord(t.kind == tyProc and
-                                        t.callConv == ccClosure and
-                                        tfIterator notin t.flags))
+      res = t.kind == tyProc and
+            t.callConv == ccClosure and
+            tfIterator notin t.flags
     else:
-      result = newIntNode(nkIntLit, 0)
+      res = false
   else:
-    var rhsOrigType = n[2].typ
-    var t2 = rhsOrigType.skipTypes({tyTypeDesc})
     maybeLiftType(t2, c, n.info)
     var m: TCandidate
     initCandidate(c, m, t2)
     if efExplain in flags:
       m.diagnostics = @[]
       m.diagnosticsEnabled = true
-    let match = typeRel(m, t2, t1) >= isSubtype # isNone
-    result = newIntNode(nkIntLit, ord(match))
+    res = typeRel(m, t2, t1) >= isSubtype # isNone
 
+  result = newIntNode(nkIntLit, ord(res))
   result.typ = n.typ
 
 proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode =
   if sonsLen(n) != 3:
     localError(c.config, n.info, "'is' operator takes 2 arguments")
 
+  let boolType = getSysType(c.graph, n.info, tyBool)
   result = n
-  n.typ = getSysType(c.graph, n.info, tyBool)
+  n.typ = boolType
+  var liftLhs = true
 
   n.sons[1] = semExprWithType(c, n[1], {efDetermineType, efWantIterator})
   if n[2].kind notin {nkStrLit..nkTripleStrLit}:
     let t2 = semTypeNode(c, n[2], nil)
     n.sons[2] = newNodeIT(nkType, n[2].info, t2)
+    if t2.kind == tyStatic:
+      let evaluated = tryConstExpr(c, n[1])
+      if evaluated != nil:
+        c.fixupStaticType(evaluated)
+        n[1] = evaluated
+      else:
+        result = newIntNode(nkIntLit, 0)
+        result.typ = boolType
+        return
+    elif t2.kind == tyTypeDesc and
+        (t2.base.kind == tyNone or tfExplicit in t2.flags):
+      # When the right-hand side is an explicit type, we must
+      # not allow regular values to be matched against the type:
+      liftLhs = false
 
-  let lhsType = n[1].typ
+  var lhsType = n[1].typ
   if lhsType.kind != tyTypeDesc:
-    n.sons[1] = makeTypeSymNode(c, lhsType, n[1].info)
-  elif lhsType.base.kind == tyNone:
-    # this is a typedesc variable, leave for evals
-    return
+    if liftLhs:
+      n[1] = makeTypeSymNode(c, lhsType, n[1].info)
+      lhsType = n[1].typ
+  else:
+    if lhsType.base.kind == tyNone:
+      # this is a typedesc variable, leave for evals
+      return
+    if lhsType.base.containsGenericType:
+      # BUGFIX: don't evaluate this too early: ``T is void``
+      return
 
-  # BUGFIX: don't evaluate this too early: ``T is void``
-  if not n[1].typ.base.containsGenericType: result = isOpImpl(c, n, flags)
+  result = isOpImpl(c, n, flags)
 
 proc semOpAux(c: PContext, n: PNode) =
   const flags = {efDetermineType}
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 6b27c8032..64783f890 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -1472,8 +1472,11 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
     of mSeq: result = semContainer(c, n, tySequence, "seq", prev)
     of mOpt: result = semContainer(c, n, tyOpt, "opt", prev)
     of mVarargs: result = semVarargs(c, n, prev)
-    of mTypeDesc, mTypeTy: result = makeTypeDesc(c, semTypeNode(c, n[1], nil))
-    of mStaticTy: result = semStaticType(c, n[1], prev)
+    of mTypeDesc, mTypeTy:
+      result = makeTypeDesc(c, semTypeNode(c, n[1], nil))
+      result.flags.incl tfExplicit
+    of mStaticTy:
+      result = semStaticType(c, n[1], prev)
     of mExpr:
       result = semTypeNode(c, n.sons[0], nil)
       if result != nil:
diff --git a/tests/metatype/ttypedesc1.nim b/tests/metatype/ttypedesc1.nim
index e9eee581f..837c8eccc 100644
--- a/tests/metatype/ttypedesc1.nim
+++ b/tests/metatype/ttypedesc1.nim
@@ -5,15 +5,16 @@ type
     x: T
     y: U
 
-proc getTypeName(t: typedesc): string = t.name
+proc getTypeName1(t: typedesc): string = t.name
+proc getTypeName2(t: type): string = t.name
 
-proc foo(T: typedesc[float], a: auto): string =
+proc foo(T: type float, a: auto): string =
   result = "float " & $(a.len > 5)
 
 proc foo(T: typedesc[TFoo], a: int): string =
   result = "TFoo "  & $(a)
 
-proc foo(T: typedesc[int or bool]): string =
+proc foo(T: type[int or bool]): string =
   var a: T
   a = 10
   result = "int or bool " & ($a)
@@ -23,8 +24,8 @@ template foo(T: typedesc[seq]): string = "seq"
 test "types can be used as proc params":
   # XXX: `check` needs to know that TFoo[int, float] is a type and
   # cannot be assigned for a local variable for later inspection
-  check ((string.getTypeName == "string"))
-  check ((getTypeName(int) == "int"))
+  check ((string.getTypeName1 == "string"))
+  check ((getTypeName2(int) == "int"))
 
   check ((foo(TFoo[int, float], 1000) == "TFoo 1000"))
 
@@ -37,6 +38,25 @@ test "types can be used as proc params":
   check ((foo(seq[int]) == "seq"))
   check ((foo(seq[TFoo[bool, string]]) == "seq"))
 
-when false:
-  proc foo(T: typedesc[seq], s: T) = nil
+template accept(x) =
+  static: assert(compiles(x))
+
+template reject(x) =
+  static: assert(not compiles(x))
+
+var
+  si: seq[int]
+  ss: seq[string]
+
+proc foo(T: typedesc[seq], s: T) =
+  discard
+
+accept:
+  foo seq[int], si
+
+reject:
+  foo seq[string], si
+
+reject:
+  foo seq[int], ss
 
diff --git a/tests/metatype/ttypedesc3.nim b/tests/metatype/ttypedesc3.nim
index 9f19bd6e3..3d1cf2ec9 100644
--- a/tests/metatype/ttypedesc3.nim
+++ b/tests/metatype/ttypedesc3.nim
@@ -4,9 +4,9 @@ type
   Base = object of RootObj
   Child = object of Base
 
-proc pr(T: typedesc[Base]) = echo "proc " & T.name
-method me(T: typedesc[Base]) = echo "method " & T.name
-iterator it(T: typedesc[Base]): auto = yield "yield " & T.name
+proc pr(T: type[Base]) = echo "proc " & T.name
+method me(T: type[Base]) = echo "method " & T.name
+iterator it(T: type[Base]): auto = yield "yield " & T.name
 
 Base.pr
 Child.pr
diff --git a/tests/metatype/ttypeselectors.nim b/tests/metatype/ttypeselectors.nim
index 1209fe78f..2a2455adb 100644
--- a/tests/metatype/ttypeselectors.nim
+++ b/tests/metatype/ttypeselectors.nim
@@ -5,16 +5,16 @@ output: "8\n8\n4"
 import
   macros, typetraits
 
-template selectType(x: int): typeDesc =
+template selectType(x: int): type =
   when x < 10:
     int
   else:
     string
 
-template simpleTypeTempl: typeDesc =
+template simpleTypeTempl: type =
   string
 
-macro typeFromMacro: typedesc = string
+macro typeFromMacro: type = string
 
 # The tests below check that the result variable of the
 # selected type matches the literal types in the code:
diff --git a/tests/parser/ttypeclasses.nim b/tests/parser/ttypeclasses.nim
index 9f487c7a8..46fd20686 100644
--- a/tests/parser/ttypeclasses.nim
+++ b/tests/parser/ttypeclasses.nim
@@ -1,17 +1,42 @@
-discard """
-    action: run
-"""
-
 type
     R = ref
     V = var
     D = distinct
     P = ptr
+    T = type
+    S = static
+    OBJ = object
+    TPL = tuple
+    SEQ = seq
 
+var i: int
 var x: ref int
 var y: distinct int
 var z: ptr int
+const C = @[1, 2, 3]
+
+static:
+  assert x is ref
+  assert y is distinct
+  assert z is ptr
+  assert C is static
+  assert C[1] is static[int]
+  assert C[0] is static[SomeInteger]
+  assert C isnot static[string]
+  assert C is SEQ|OBJ
+  assert C isnot OBJ|TPL
+  assert int is int
+  assert int is T
+  assert int is SomeInteger
+  assert seq[int] is type
+  assert seq[int] is type[seq]
+  assert seq[int] isnot type[seq[float]]
+  assert i isnot type[int]
+  assert type(i) is type[int]
+  assert x isnot T
+  assert y isnot S
+  assert z isnot enum
+  assert x isnot object
+  assert y isnot tuple
+  assert z isnot seq
 
-doAssert x is ref
-doAssert y is distinct
-doAssert z is ptr
\ No newline at end of file
diff --git a/tests/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim
index 5234866fa..789bd7588 100644
--- a/tests/statictypes/tstatictypes.nim
+++ b/tests/statictypes/tstatictypes.nim
@@ -1,25 +1,91 @@
 discard """
 nimout: '''
-staticAlialProc instantiated with 4
-staticAlialProc instantiated with 6
+staticAlialProc instantiated with 358
+staticAlialProc instantiated with 368
+'''
+output: '''
+16
+16
+b is 2 times a
+17
 '''
 """
 
 import macros
 
+template ok(x) = assert(x)
+template no(x) = assert(not x)
+
+template accept(x) =
+  static: assert(compiles(x))
+
+template reject(x) =
+  static: assert(not compiles(x))
+
 proc plus(a, b: int): int = a + b
 
+template isStatic(x: static): bool = true
+template isStatic(x: auto): bool = false
+
+var v = 1
+
 when true:
+  # test that `isStatic` works as expected
+  const C = 2
+
+  static:
+    ok C.isStatic
+    ok isStatic(plus(1, 2))
+    ok plus(C, 2).isStatic
+
+    no isStatic(v)
+    no plus(1, v).isStatic
+
+when true:
+  # test that proc instantiation works as expected
   type
     StaticTypeAlias = static[int]
 
-  proc staticAliasProc(s: StaticTypeAlias) =
-    static: echo "staticAlialProc instantiated with ", s + 1
-    echo s
+  proc staticAliasProc(a: StaticTypeAlias,
+                       b: static[int],
+                       c: static int) =
+    static:
+      assert a.isStatic and b.isStatic and c.isStatic
+      assert isStatic(a + plus(b, c))
+      echo "staticAlialProc instantiated with ", a, b, c
+
+    when b mod a == 0:
+      echo "b is ", b div a, " times a"
+
+    echo a + b + c
 
-  staticAliasProc 1+2
-  staticAliasProc 3
-  staticAliasProc 5
+  staticAliasProc 1+2, 5, 8
+  staticAliasProc 3, 2+3, 9-1
+  staticAliasProc 3, 3+3, 4+4
+
+when true:
+  # test static coercions. normal cases that should work:
+  accept:
+    var s1 = static[int] plus(1, 2)
+    var s2 = static(plus(1,2))
+    var s3 = static plus(1,2)
+    var s4 = static[SomeInteger](1 + 2)
+
+  # the sub-script operator can be used only with types:
+  reject:
+    var just_static3 = static[plus(1,2)]
+
+  # static coercion takes into account the type:
+  reject:
+    var x = static[string](plus(1, 2))
+  reject:
+    var x = static[string] plus(1, 2)
+  reject:
+    var x = static[SomeFloat] plus(3, 4)
+
+  # you cannot coerce a run-time variable
+  reject:
+    var x = static(v)
 
 when true:
   type
@@ -35,7 +101,7 @@ when true:
   var aw1: ArrayWrapper1[5]
   var aw2: ArrayWrapper2[5]
   var aw3: ArrayWrapper3[(10, "str")]
-  
+
   static:
     assert aw1.data.high == 5
     assert aw2.data.high == 6
diff --git a/tests/types/tisopr.nim b/tests/types/tisopr.nim
index 2f9dbf245..f05f443de 100644
--- a/tests/types/tisopr.nim
+++ b/tests/types/tisopr.nim
@@ -5,7 +5,7 @@ false
 false
 true
 true
-no'''
+yes'''
 """
 
 proc IsVoid[T](): string =