summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorTimothee Cour <timothee.cour2@gmail.com>2021-04-23 05:36:38 -0700
committerGitHub <noreply@github.com>2021-04-23 14:36:38 +0200
commit2abc936d511342265d2ef27c4b079dd49332d65a (patch)
tree9c6b945447a8a2b31c9c752e92f8984cb630f3ae
parent3516f57e172cef22f583f43c7b16fd1f63921c6e (diff)
downloadNim-2abc936d511342265d2ef27c4b079dd49332d65a.tar.gz
`typeof(voidStmt)` now works (#17807)
* `typeof(voidStmt)` now works
* remove typeOrVoid
* add condsyms, and reference cligen https://github.com/c-blake/cligen/pull/193
* fixup
* changelog [skip ci]
* fixup
-rw-r--r--changelog.md2
-rw-r--r--compiler/condsyms.nim1
-rw-r--r--compiler/sem.nim1
-rw-r--r--compiler/semdata.nim3
-rw-r--r--compiler/semexprs.nim4
-rw-r--r--lib/js/asyncjs.nim7
-rw-r--r--testament/lib/stdtest/testutils.nim11
-rw-r--r--tests/typerel/tvoid.nim61
8 files changed, 78 insertions, 12 deletions
diff --git a/changelog.md b/changelog.md
index 7b4a2438a..d7b568aa0 100644
--- a/changelog.md
+++ b/changelog.md
@@ -331,6 +331,8 @@
 - Added a new module `std/importutils`, and an API `privateAccess`, which allows access to private fields
   for an object type in the current scope.
 
+- `typeof(voidStmt)` now works and returns `void`.
+
 ## Compiler changes
 
 - Added `--declaredlocs` to show symbol declaration location in messages.
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index bdecd7e53..6a49584c8 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -133,3 +133,4 @@ proc initDefines*(symbols: StringTableRef) =
   defineSymbol("nimHasCustomLiterals")
   defineSymbol("nimHasUnifiedTuple")
   defineSymbol("nimHasIterable")
+  defineSymbol("nimHasTypeofVoid")
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 4020fc4d6..b80795354 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -525,6 +525,7 @@ proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext
   var c = newContext(graph, module)
   c.idgen = idgen
   c.enforceVoidContext = newType(tyTyped, nextTypeId(idgen), nil)
+  c.voidType = newType(tyVoid, nextTypeId(idgen), nil)
 
   if c.p != nil: internalError(graph.config, module.info, "sem.myOpen")
   c.semConstExpr = semConstExpr
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index 1c1a5159c..082a4813e 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -90,6 +90,9 @@ type
   TContext* = object of TPassContext # a context represents the module
                                      # that is currently being compiled
     enforceVoidContext*: PType
+      # for `if cond: stmt else: foo`, `foo` will be evaluated under
+      # enforceVoidContext != nil
+    voidType*: PType # for typeof(stmt)
     module*: PSym              # the module sym belonging to the context
     currentScope*: PScope      # current scope
     moduleScope*: PScope       # scope for modules
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 97f88869e..2e229d861 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -83,7 +83,9 @@ proc semExprCheck(c: PContext, n: PNode, flags: TExprFlags): PNode =
 
 proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   result = semExprCheck(c, n, flags)
-  if result.typ == nil or result.typ == c.enforceVoidContext:
+  if result.typ == nil and efInTypeof in flags:
+    result.typ = c.voidType
+  elif result.typ == nil or result.typ == c.enforceVoidContext:
     localError(c.config, n.info, errExprXHasNoType %
                 renderTree(result, {renderNoComments}))
     result.typ = errorType(c)
diff --git a/lib/js/asyncjs.nim b/lib/js/asyncjs.nim
index c62ac633f..861fe3fe2 100644
--- a/lib/js/asyncjs.nim
+++ b/lib/js/asyncjs.nim
@@ -158,10 +158,6 @@ proc newPromise*(handler: proc(resolve: proc())): Future[void] {.importjs: "(new
   ## A helper for wrapping callback-based functions
   ## into promises and async procedures.
 
-template typeOrVoid[T](a: T): type =
-  # xxx this is useful, make it public in std/typetraits in future work
-  T
-
 template maybeFuture(T): untyped =
   # avoids `Future[Future[T]]`
   when T is Future: T
@@ -221,7 +217,8 @@ when defined(nimExperimentalAsyncjsThen):
             assert witness == 3
 
       template impl(call): untyped =
-        when typeOrVoid(call) is void:
+        # see D20210421T014713
+        when typeof(block: call) is void:
           var ret: Future[void]
         else:
           var ret = default(maybeFuture(typeof(call)))
diff --git a/testament/lib/stdtest/testutils.nim b/testament/lib/stdtest/testutils.nim
index 44048adbd..241ab1770 100644
--- a/testament/lib/stdtest/testutils.nim
+++ b/testament/lib/stdtest/testutils.nim
@@ -91,10 +91,6 @@ template disableVm*(body) =
   when nimvm: discard
   else: body
 
-template typeOrVoid[T](a: T): type =
-  # FACTOR with asyncjs.typeOrVoid
-  T
-
 macro assertAll*(body) =
   ## works in VM, unlike `check`, `require`
   runnableExamples:
@@ -106,6 +102,9 @@ macro assertAll*(body) =
   result = newStmtList()
   for a in body:
     result.add genAst(a) do:
-      # better than: `when not compiles(typeof(a)):`
-      when typeOrVoid(a) is void: a
+      # D20210421T014713:here
+      # xxx pending https://github.com/nim-lang/Nim/issues/12030,
+      # `typeof` should introduce its own scope, so that this
+      # is sufficient: `typeof(a)` instead of `typeof(block: a)`
+      when typeof(block: a) is void: a
       else: doAssert a
diff --git a/tests/typerel/tvoid.nim b/tests/typerel/tvoid.nim
index 1e46d016e..0b7b525cc 100644
--- a/tests/typerel/tvoid.nim
+++ b/tests/typerel/tvoid.nim
@@ -35,3 +35,64 @@ ReturnT[void]()
 echo ReturnT[string]("abc")
 nothing()
 
+block: # typeof(stmt)
+  proc fn1(): auto =
+    discard
+  proc fn2(): auto =
+    1
+  doAssert type(fn1()) is void
+  doAssert typeof(fn1()) is void
+  doAssert typeof(fn1()) isnot int
+
+  doAssert type(fn2()) isnot void
+  doAssert typeof(fn2()) isnot void
+  when typeof(fn1()) is void: discard
+  else: doAssert false
+
+  doAssert typeof(1+1) is int
+  doAssert typeof((discard)) is void
+
+  type A1 = typeof(fn1())
+  doAssert A1 is void
+  type A2 = type(fn1())
+  doAssert A2 is void
+  doAssert A2 is A1
+
+  when false:
+    # xxx: MCS/UFCS doesn't work here: Error: expression 'fn1()' has no type (or is ambiguous)
+    type A3 = fn1().type
+  proc bar[T](a: T): string = $T
+  doAssert bar(1) == "int"
+  doAssert bar(fn1()) == "void"
+
+  proc bar2[T](a: T): bool = T is void
+  doAssert not bar2(1)
+  doAssert bar2(fn1())
+
+  block:
+    proc bar3[T](a: T): T = a
+    let a1 = bar3(1)
+    doAssert compiles(block:
+      let a1 = bar3(fn2()))
+    doAssert not compiles(block:
+      let a2 = bar3(fn1()))
+    doAssert compiles(block: bar3(fn1()))
+    doAssert compiles(bar3(fn1()))
+    doAssert typeof(bar3(fn1())) is void
+    doAssert not compiles(sizeof(bar3(fn1())))
+
+  block:
+    var a = 1
+    doAssert typeof((a = 2)) is void
+    doAssert typeof((a = 2; a = 3)) is void
+    doAssert typeof(block:
+      a = 2; a = 3) is void
+
+  block:
+    var a = 1
+    template bad1 = echo (a; a = 2)
+    doAssert not compiles(bad1())
+
+  block:
+    template bad2 = echo (nonexistant; discard)
+    doAssert not compiles(bad2())