From 604a15c0aaf6d3df28b3236b9aeb32acea7d653f Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 16 Oct 2017 00:59:51 +0200 Subject: some progress on the nimpretty tool; still not ready --- compiler/msgs.nim | 1 + 1 file changed, 1 insertion(+) (limited to 'compiler/msgs.nim') diff --git a/compiler/msgs.nim b/compiler/msgs.nim index c988141e5..8d43103db 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -500,6 +500,7 @@ type fileIndex*: int32 when defined(nimpretty): offsetA*, offsetB*: int + commentOffsetA*, commentOffsetB*: int TErrorOutput* = enum eStdOut -- cgit 1.4.1-2-gfad0 From 3bd6b7ddc7e7cd8f1551b79f3dbd74c539db14d1 Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 5 Nov 2017 02:51:20 +0100 Subject: improve the error messages for bug #6692 --- compiler/msgs.nim | 4 ++-- compiler/semexprs.nim | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'compiler/msgs.nim') diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 8d43103db..2668c72ae 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -61,7 +61,7 @@ type errBaseTypeMustBeOrdinal, errInheritanceOnlyWithNonFinalObjects, errInheritanceOnlyWithEnums, errIllegalRecursionInTypeX, errCannotInstantiateX, errExprHasNoAddress, errXStackEscape, - errVarForOutParamNeeded, + errVarForOutParamNeededX, errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX, errAmbiguousCallXYZ, errWrongNumberOfArguments, errWrongNumberOfArgumentsInCall, @@ -268,7 +268,7 @@ const errCannotInstantiateX: "cannot instantiate: \'$1\'", errExprHasNoAddress: "expression has no address", errXStackEscape: "address of '$1' may not escape its stack frame", - errVarForOutParamNeeded: "for a \'var\' type a variable needs to be passed", + errVarForOutParamNeededX: "for a \'var\' type a variable needs to be passed; but '$1' is immutable", errPureTypeMismatch: "type mismatch", errTypeMismatch: "type mismatch: got (", errButExpected: "but expected one of: ", diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 7a16f495a..d600b1c48 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -465,7 +465,7 @@ proc newHiddenAddrTaken(c: PContext, n: PNode): PNode = result = newNodeIT(nkHiddenAddr, n.info, makeVarType(c, n.typ)) addSon(result, n) if isAssignable(c, n) notin {arLValue, arLocalLValue}: - localError(n.info, errVarForOutParamNeeded) + localError(n.info, errVarForOutParamNeededX, $n) proc analyseIfAddressTaken(c: PContext, n: PNode): PNode = result = n @@ -509,9 +509,10 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) = for i in countup(1, sonsLen(n) - 1): if i < sonsLen(t) and t.sons[i] != nil and skipTypes(t.sons[i], abstractInst-{tyTypeDesc}).kind == tyVar: - if isAssignable(c, n.sons[i]) notin {arLValue, arLocalLValue}: - if n.sons[i].kind != nkHiddenAddr: - localError(n.sons[i].info, errVarForOutParamNeeded) + let it = n[i] + if isAssignable(c, it) notin {arLValue, arLocalLValue}: + if it.kind != nkHiddenAddr: + localError(it.info, errVarForOutParamNeededX, $it) return for i in countup(1, sonsLen(n) - 1): if n.sons[i].kind == nkHiddenCallConv: -- cgit 1.4.1-2-gfad0 From 2b3ec0a7c66d2246371ed51348aaa87d4c3cf0f9 Mon Sep 17 00:00:00 2001 From: cooldome Date: Mon, 25 Dec 2017 00:22:03 +0300 Subject: Implement language feature #6885 (#6954) --- changelog.md | 22 +++++++++++++++++ compiler/msgs.nim | 6 +++-- compiler/pragmas.nim | 2 ++ compiler/sem.nim | 13 ++++++++++ compiler/semstmts.nim | 24 +++++++++--------- lib/system/chcks.nim | 1 - tests/casestmt/tcasestm.nim | 59 +++++++++++++++++++++++++++++++++++++++++++++ tests/pragmas/tnoreturn.nim | 18 ++++++++++++++ 8 files changed, 131 insertions(+), 14 deletions(-) create mode 100644 tests/pragmas/tnoreturn.nim (limited to 'compiler/msgs.nim') diff --git a/changelog.md b/changelog.md index efdd1f520..5734a4cb1 100644 --- a/changelog.md +++ b/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 function return type placeholders. - 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") +``` diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 2668c72ae..4e6226122 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -26,7 +26,8 @@ type errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation, errExceptionExpected, errExceptionAlreadyHandled, errYieldNotAllowedHere, errYieldNotAllowedInTryStmt, - errInvalidNumberOfYieldExpr, errCannotReturnExpr, errAttemptToRedefine, + errInvalidNumberOfYieldExpr, errCannotReturnExpr, + errNoReturnWithReturnTypeNotAllowed, errAttemptToRedefine, errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel, errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected, errInvalidVarSubstitution, errUnknownVar, errUnknownCcompiler, @@ -179,8 +180,9 @@ const errYieldNotAllowedInTryStmt: "'yield' cannot be used within 'try' in a non-inlined iterator", errInvalidNumberOfYieldExpr: "invalid number of \'yield\' expressions", errCannotReturnExpr: "current routine cannot return an expression", + errNoReturnWithReturnTypeNotAllowed: "routines with NoReturn pragma are not allowed to have return type", 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", errInvalidLabel: "\'$1\' is no label", errInvalidCmdLineOption: "invalid command line option: \'$1\'", diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index b598cadb2..35fedf4ea 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -771,6 +771,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, of wNoreturn: noVal(it) incl(sym.flags, sfNoReturn) + if sym.ast[paramsPos][0].kind != nkEmpty: + localError(sym.ast[paramsPos][0].info, errNoReturnWithReturnTypeNotAllowed) of wDynlib: processDynLib(c, it, sym) of wCompilerproc: diff --git a/compiler/sem.nim b/compiler/sem.nim index bc994201d..ababbd303 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -165,6 +165,19 @@ proc commonType*(x, y: PType): PType = result = newType(k, r.owner) 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 = result = newSym(kind, considerQuotedIdent(n), getCurrOwner(c), n.info) when defined(nimsuggest): diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 8ed120c98..b1fa8c19b 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -165,14 +165,14 @@ proc semIf(c: PContext, n: PNode): PNode = it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0])) when not newScopeForIf: openScope(c) it.sons[1] = semExprBranch(c, it.sons[1]) - typ = commonType(typ, it.sons[1].typ) + typ = commonType(typ, it.sons[1]) closeScope(c) elif it.len == 1: hasElse = true it.sons[0] = semExprBranchScope(c, it.sons[0]) - typ = commonType(typ, it.sons[0].typ) + typ = commonType(typ, it.sons[0]) 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) result.kind = nkIfStmt # propagate any enforced VoidContext: @@ -180,7 +180,8 @@ proc semIf(c: PContext, n: PNode): PNode = else: for it in n: let j = it.len-1 - it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info) + if not endsInNoReturn(it.sons[j]): + it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info) result.kind = nkIfExpr result.typ = typ @@ -213,7 +214,7 @@ proc semCase(c: PContext, n: PNode): PNode = semCaseBranch(c, n, x, i, covered) var last = sonsLen(x)-1 x.sons[last] = semExprBranchScope(c, x.sons[last]) - typ = commonType(typ, x.sons[last].typ) + typ = commonType(typ, x.sons[last]) of nkElifBranch: chckCovered = false checkSonsLen(x, 2) @@ -221,13 +222,13 @@ proc semCase(c: PContext, n: PNode): PNode = x.sons[0] = forceBool(c, semExprWithType(c, x.sons[0])) when not newScopeForIf: openScope(c) x.sons[1] = semExprBranch(c, x.sons[1]) - typ = commonType(typ, x.sons[1].typ) + typ = commonType(typ, x.sons[1]) closeScope(c) of nkElse: chckCovered = false checkSonsLen(x, 1) x.sons[0] = semExprBranchScope(c, x.sons[0]) - typ = commonType(typ, x.sons[0].typ) + typ = commonType(typ, x.sons[0]) hasElse = true else: illFormedAst(x) @@ -237,7 +238,7 @@ proc semCase(c: PContext, n: PNode): PNode = else: localError(n.info, errNotAllCasesCovered) 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) # propagate any enforced VoidContext: if typ == enforceVoidContext: @@ -246,7 +247,8 @@ proc semCase(c: PContext, n: PNode): PNode = for i in 1..n.len-1: var it = n.sons[i] let j = it.len-1 - it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info) + if not endsInNoReturn(it.sons[j]): + it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info) result.typ = typ proc semTry(c: PContext, n: PNode): PNode = @@ -1851,8 +1853,8 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = else: n.typ = n.sons[i].typ if not isEmptyType(n.typ): n.kind = nkStmtListExpr - case n.sons[i].kind - of LastBlockStmts: + if n.sons[i].kind in LastBlockStmts or + 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): case n.sons[j].kind of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr, diff --git a/lib/system/chcks.nim b/lib/system/chcks.nim index 1520f231e..69b680dbd 100644 --- a/lib/system/chcks.nim +++ b/lib/system/chcks.nim @@ -63,7 +63,6 @@ proc chckObj(obj, subclass: PNimType) {.compilerproc.} = while x != subclass: if x == nil: sysFatal(ObjectConversionError, "invalid object conversion") - break x = x.base proc chckObjAsgn(a, b: PNimType) {.compilerproc, inline.} = diff --git a/tests/casestmt/tcasestm.nim b/tests/casestmt/tcasestm.nim index 7ac20bf2f..b005d8120 100644 --- a/tests/casestmt/tcasestm.nim +++ b/tests/casestmt/tcasestm.nim @@ -36,5 +36,64 @@ var z = case i echo z #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") +)) \ No newline at end of file diff --git a/tests/pragmas/tnoreturn.nim b/tests/pragmas/tnoreturn.nim new file mode 100644 index 000000000..2075b352e --- /dev/null +++ b/tests/pragmas/tnoreturn.nim @@ -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 +)) -- cgit 1.4.1-2-gfad0