parent
6bd3a2826f
commit
2b3ec0a7c6
8 changed files with 131 additions and 14 deletions
22
changelog.md
22
changelog.md
|
|
@ -144,3 +144,25 @@ This now needs to be written as:
|
||||||
- codegenDecl pragma now works for the JavaScript backend. It returns an empty string for
|
- codegenDecl pragma now works for the JavaScript backend. It returns an empty string for
|
||||||
function return type placeholders.
|
function return type placeholders.
|
||||||
- Asynchronous programming for the JavaScript backend using the `asyncjs` module.
|
- Asynchronous programming for the JavaScript backend using the `asyncjs` module.
|
||||||
|
- Extra semantic checks for procs with noreturn pragma: return type is not allowed,
|
||||||
|
statements after call to noreturn procs are no longer allowed.
|
||||||
|
- Noreturn proc calls and raising exceptions branches are now skipped during common type
|
||||||
|
deduction in if and case expressions. The following code snippets now compile:
|
||||||
|
```nim
|
||||||
|
import strutils
|
||||||
|
let str = "Y"
|
||||||
|
let a = case str:
|
||||||
|
of "Y": true
|
||||||
|
of "N": false
|
||||||
|
else: raise newException(ValueError, "Invalid boolean")
|
||||||
|
let b = case str:
|
||||||
|
of nil, "": raise newException(ValueError, "Invalid boolean")
|
||||||
|
elif str.startsWith("Y"): true
|
||||||
|
elif str.startsWith("N"): false
|
||||||
|
else: false
|
||||||
|
let c = if str == "Y": true
|
||||||
|
elif str == "N": false
|
||||||
|
else:
|
||||||
|
echo "invalid bool"
|
||||||
|
quit("this is the end")
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,8 @@ type
|
||||||
errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation,
|
errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation,
|
||||||
errExceptionExpected, errExceptionAlreadyHandled,
|
errExceptionExpected, errExceptionAlreadyHandled,
|
||||||
errYieldNotAllowedHere, errYieldNotAllowedInTryStmt,
|
errYieldNotAllowedHere, errYieldNotAllowedInTryStmt,
|
||||||
errInvalidNumberOfYieldExpr, errCannotReturnExpr, errAttemptToRedefine,
|
errInvalidNumberOfYieldExpr, errCannotReturnExpr,
|
||||||
|
errNoReturnWithReturnTypeNotAllowed, errAttemptToRedefine,
|
||||||
errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel,
|
errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel,
|
||||||
errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected,
|
errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected,
|
||||||
errInvalidVarSubstitution, errUnknownVar, errUnknownCcompiler,
|
errInvalidVarSubstitution, errUnknownVar, errUnknownCcompiler,
|
||||||
|
|
@ -179,8 +180,9 @@ const
|
||||||
errYieldNotAllowedInTryStmt: "'yield' cannot be used within 'try' in a non-inlined iterator",
|
errYieldNotAllowedInTryStmt: "'yield' cannot be used within 'try' in a non-inlined iterator",
|
||||||
errInvalidNumberOfYieldExpr: "invalid number of \'yield\' expressions",
|
errInvalidNumberOfYieldExpr: "invalid number of \'yield\' expressions",
|
||||||
errCannotReturnExpr: "current routine cannot return an expression",
|
errCannotReturnExpr: "current routine cannot return an expression",
|
||||||
|
errNoReturnWithReturnTypeNotAllowed: "routines with NoReturn pragma are not allowed to have return type",
|
||||||
errAttemptToRedefine: "redefinition of \'$1\'",
|
errAttemptToRedefine: "redefinition of \'$1\'",
|
||||||
errStmtInvalidAfterReturn: "statement not allowed after \'return\', \'break\', \'raise\' or \'continue'",
|
errStmtInvalidAfterReturn: "statement not allowed after \'return\', \'break\', \'raise\', \'continue\' or proc call with noreturn pragma",
|
||||||
errStmtExpected: "statement expected",
|
errStmtExpected: "statement expected",
|
||||||
errInvalidLabel: "\'$1\' is no label",
|
errInvalidLabel: "\'$1\' is no label",
|
||||||
errInvalidCmdLineOption: "invalid command line option: \'$1\'",
|
errInvalidCmdLineOption: "invalid command line option: \'$1\'",
|
||||||
|
|
|
||||||
|
|
@ -771,6 +771,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||||
of wNoreturn:
|
of wNoreturn:
|
||||||
noVal(it)
|
noVal(it)
|
||||||
incl(sym.flags, sfNoReturn)
|
incl(sym.flags, sfNoReturn)
|
||||||
|
if sym.ast[paramsPos][0].kind != nkEmpty:
|
||||||
|
localError(sym.ast[paramsPos][0].info, errNoReturnWithReturnTypeNotAllowed)
|
||||||
of wDynlib:
|
of wDynlib:
|
||||||
processDynLib(c, it, sym)
|
processDynLib(c, it, sym)
|
||||||
of wCompilerproc:
|
of wCompilerproc:
|
||||||
|
|
|
||||||
|
|
@ -165,6 +165,19 @@ proc commonType*(x, y: PType): PType =
|
||||||
result = newType(k, r.owner)
|
result = newType(k, r.owner)
|
||||||
result.addSonSkipIntLit(r)
|
result.addSonSkipIntLit(r)
|
||||||
|
|
||||||
|
proc endsInNoReturn(n: PNode): bool =
|
||||||
|
# check if expr ends in raise exception or call of noreturn proc
|
||||||
|
var it = n
|
||||||
|
while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0:
|
||||||
|
it = it.lastSon
|
||||||
|
result = it.kind == nkRaiseStmt or
|
||||||
|
it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags
|
||||||
|
|
||||||
|
proc commonType*(x: PType, y: PNode): PType =
|
||||||
|
# ignore exception raising branches in case/if expressions
|
||||||
|
if endsInNoReturn(y): return x
|
||||||
|
commonType(x, y.typ)
|
||||||
|
|
||||||
proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
|
proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
|
||||||
result = newSym(kind, considerQuotedIdent(n), getCurrOwner(c), n.info)
|
result = newSym(kind, considerQuotedIdent(n), getCurrOwner(c), n.info)
|
||||||
when defined(nimsuggest):
|
when defined(nimsuggest):
|
||||||
|
|
|
||||||
|
|
@ -165,14 +165,14 @@ proc semIf(c: PContext, n: PNode): PNode =
|
||||||
it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0]))
|
it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0]))
|
||||||
when not newScopeForIf: openScope(c)
|
when not newScopeForIf: openScope(c)
|
||||||
it.sons[1] = semExprBranch(c, it.sons[1])
|
it.sons[1] = semExprBranch(c, it.sons[1])
|
||||||
typ = commonType(typ, it.sons[1].typ)
|
typ = commonType(typ, it.sons[1])
|
||||||
closeScope(c)
|
closeScope(c)
|
||||||
elif it.len == 1:
|
elif it.len == 1:
|
||||||
hasElse = true
|
hasElse = true
|
||||||
it.sons[0] = semExprBranchScope(c, it.sons[0])
|
it.sons[0] = semExprBranchScope(c, it.sons[0])
|
||||||
typ = commonType(typ, it.sons[0].typ)
|
typ = commonType(typ, it.sons[0])
|
||||||
else: illFormedAst(it)
|
else: illFormedAst(it)
|
||||||
if isEmptyType(typ) or typ.kind == tyNil or not hasElse:
|
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or not hasElse:
|
||||||
for it in n: discardCheck(c, it.lastSon)
|
for it in n: discardCheck(c, it.lastSon)
|
||||||
result.kind = nkIfStmt
|
result.kind = nkIfStmt
|
||||||
# propagate any enforced VoidContext:
|
# propagate any enforced VoidContext:
|
||||||
|
|
@ -180,6 +180,7 @@ proc semIf(c: PContext, n: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
for it in n:
|
for it in n:
|
||||||
let j = it.len-1
|
let j = it.len-1
|
||||||
|
if not endsInNoReturn(it.sons[j]):
|
||||||
it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info)
|
it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info)
|
||||||
result.kind = nkIfExpr
|
result.kind = nkIfExpr
|
||||||
result.typ = typ
|
result.typ = typ
|
||||||
|
|
@ -213,7 +214,7 @@ proc semCase(c: PContext, n: PNode): PNode =
|
||||||
semCaseBranch(c, n, x, i, covered)
|
semCaseBranch(c, n, x, i, covered)
|
||||||
var last = sonsLen(x)-1
|
var last = sonsLen(x)-1
|
||||||
x.sons[last] = semExprBranchScope(c, x.sons[last])
|
x.sons[last] = semExprBranchScope(c, x.sons[last])
|
||||||
typ = commonType(typ, x.sons[last].typ)
|
typ = commonType(typ, x.sons[last])
|
||||||
of nkElifBranch:
|
of nkElifBranch:
|
||||||
chckCovered = false
|
chckCovered = false
|
||||||
checkSonsLen(x, 2)
|
checkSonsLen(x, 2)
|
||||||
|
|
@ -221,13 +222,13 @@ proc semCase(c: PContext, n: PNode): PNode =
|
||||||
x.sons[0] = forceBool(c, semExprWithType(c, x.sons[0]))
|
x.sons[0] = forceBool(c, semExprWithType(c, x.sons[0]))
|
||||||
when not newScopeForIf: openScope(c)
|
when not newScopeForIf: openScope(c)
|
||||||
x.sons[1] = semExprBranch(c, x.sons[1])
|
x.sons[1] = semExprBranch(c, x.sons[1])
|
||||||
typ = commonType(typ, x.sons[1].typ)
|
typ = commonType(typ, x.sons[1])
|
||||||
closeScope(c)
|
closeScope(c)
|
||||||
of nkElse:
|
of nkElse:
|
||||||
chckCovered = false
|
chckCovered = false
|
||||||
checkSonsLen(x, 1)
|
checkSonsLen(x, 1)
|
||||||
x.sons[0] = semExprBranchScope(c, x.sons[0])
|
x.sons[0] = semExprBranchScope(c, x.sons[0])
|
||||||
typ = commonType(typ, x.sons[0].typ)
|
typ = commonType(typ, x.sons[0])
|
||||||
hasElse = true
|
hasElse = true
|
||||||
else:
|
else:
|
||||||
illFormedAst(x)
|
illFormedAst(x)
|
||||||
|
|
@ -237,7 +238,7 @@ proc semCase(c: PContext, n: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
localError(n.info, errNotAllCasesCovered)
|
localError(n.info, errNotAllCasesCovered)
|
||||||
closeScope(c)
|
closeScope(c)
|
||||||
if isEmptyType(typ) or typ.kind == tyNil or not hasElse:
|
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or not hasElse:
|
||||||
for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon)
|
for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon)
|
||||||
# propagate any enforced VoidContext:
|
# propagate any enforced VoidContext:
|
||||||
if typ == enforceVoidContext:
|
if typ == enforceVoidContext:
|
||||||
|
|
@ -246,6 +247,7 @@ proc semCase(c: PContext, n: PNode): PNode =
|
||||||
for i in 1..n.len-1:
|
for i in 1..n.len-1:
|
||||||
var it = n.sons[i]
|
var it = n.sons[i]
|
||||||
let j = it.len-1
|
let j = it.len-1
|
||||||
|
if not endsInNoReturn(it.sons[j]):
|
||||||
it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info)
|
it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info)
|
||||||
result.typ = typ
|
result.typ = typ
|
||||||
|
|
||||||
|
|
@ -1851,8 +1853,8 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
else:
|
else:
|
||||||
n.typ = n.sons[i].typ
|
n.typ = n.sons[i].typ
|
||||||
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
|
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
|
||||||
case n.sons[i].kind
|
if n.sons[i].kind in LastBlockStmts or
|
||||||
of LastBlockStmts:
|
n.sons[i].kind in nkCallKinds and n.sons[i][0].kind == nkSym and sfNoReturn in n.sons[i][0].sym.flags:
|
||||||
for j in countup(i + 1, length - 1):
|
for j in countup(i + 1, length - 1):
|
||||||
case n.sons[j].kind
|
case n.sons[j].kind
|
||||||
of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr,
|
of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr,
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,6 @@ proc chckObj(obj, subclass: PNimType) {.compilerproc.} =
|
||||||
while x != subclass:
|
while x != subclass:
|
||||||
if x == nil:
|
if x == nil:
|
||||||
sysFatal(ObjectConversionError, "invalid object conversion")
|
sysFatal(ObjectConversionError, "invalid object conversion")
|
||||||
break
|
|
||||||
x = x.base
|
x = x.base
|
||||||
|
|
||||||
proc chckObjAsgn(a, b: PNimType) {.compilerproc, inline.} =
|
proc chckObjAsgn(a, b: PNimType) {.compilerproc, inline.} =
|
||||||
|
|
|
||||||
|
|
@ -36,5 +36,64 @@ var z = case i
|
||||||
echo z
|
echo z
|
||||||
#OUT ayyy
|
#OUT ayyy
|
||||||
|
|
||||||
|
let str1 = "Y"
|
||||||
|
let str2 = "NN"
|
||||||
|
let a = case str1:
|
||||||
|
of "Y": true
|
||||||
|
of "N": false
|
||||||
|
else:
|
||||||
|
echo "no good"
|
||||||
|
quit("quiting")
|
||||||
|
|
||||||
|
let b = case str2:
|
||||||
|
of nil, "": raise newException(ValueError, "Invalid boolean")
|
||||||
|
elif str2[0] == 'Y': true
|
||||||
|
elif str2[0] == 'N': false
|
||||||
|
else: "error".quit(2)
|
||||||
|
|
||||||
|
doAssert(a == true)
|
||||||
|
doAssert(b == false)
|
||||||
|
|
||||||
|
var bb: bool
|
||||||
|
doassert(not compiles(
|
||||||
|
bb = case str2:
|
||||||
|
of nil, "": raise newException(ValueError, "Invalid boolean")
|
||||||
|
elif str.startsWith("Y"): true
|
||||||
|
elif str.startsWith("N"): false
|
||||||
|
))
|
||||||
|
|
||||||
|
doassert(not compiles(
|
||||||
|
bb = case str2:
|
||||||
|
of "Y": true
|
||||||
|
of "N": false
|
||||||
|
))
|
||||||
|
|
||||||
|
doassert(not compiles(
|
||||||
|
bb = case str2:
|
||||||
|
of "Y": true
|
||||||
|
of "N": raise newException(ValueError, "N not allowed")
|
||||||
|
))
|
||||||
|
|
||||||
|
doassert(not compiles(
|
||||||
|
bb = case str2:
|
||||||
|
of "Y": raise newException(ValueError, "Invalid Y")
|
||||||
|
else: raise newException(ValueError, "Invalid N")
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
doassert(not compiles(
|
||||||
|
bb = case str2:
|
||||||
|
of "Y":
|
||||||
|
raise newException(ValueError, "Invalid Y")
|
||||||
|
true
|
||||||
|
else: raise newException(ValueError, "Invalid")
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
doassert(not compiles(
|
||||||
|
bb = case str2:
|
||||||
|
of "Y":
|
||||||
|
"invalid Y".quit(3)
|
||||||
|
true
|
||||||
|
else: raise newException(ValueError, "Invalid")
|
||||||
|
))
|
||||||
18
tests/pragmas/tnoreturn.nim
Normal file
18
tests/pragmas/tnoreturn.nim
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
discard """
|
||||||
|
ccodeCheck: "\\i @'__attribute__((noreturn))' .*"
|
||||||
|
"""
|
||||||
|
|
||||||
|
proc noret1*(i: int) {.noreturn.} =
|
||||||
|
echo i
|
||||||
|
|
||||||
|
var p {.used.}: proc(i: int): int
|
||||||
|
doAssert(not compiles(
|
||||||
|
p = proc(i: int): int {.noreturn.} = i # noreturn lambda returns int
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
doAssert(not compiles(
|
||||||
|
block:
|
||||||
|
noret1(5)
|
||||||
|
echo 1 # statement after noreturn
|
||||||
|
))
|
||||||
Loading…
Add table
Add a link
Reference in a new issue