mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 08:54:53 +00:00
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
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -133,3 +133,4 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimHasCustomLiterals")
|
||||
defineSymbol("nimHasUnifiedTuple")
|
||||
defineSymbol("nimHasIterable")
|
||||
defineSymbol("nimHasTypeofVoid")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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())
|
||||
|
||||
Reference in New Issue
Block a user