diff options
Diffstat (limited to 'compiler/semstmts.nim')
-rw-r--r-- | compiler/semstmts.nim | 131 |
1 files changed, 89 insertions, 42 deletions
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index d5c5b7f86..e0542e1e7 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -41,8 +41,13 @@ proc semDiscard(c: PContext, n: PNode): PNode = checkSonsLen(n, 1, c.config) if n.sons[0].kind != nkEmpty: n.sons[0] = semExprWithType(c, n.sons[0]) - if isEmptyType(n.sons[0].typ) or n.sons[0].typ.kind == tyNone or n.sons[0].kind == nkTypeOfExpr: + let sonType = n.sons[0].typ + let sonKind = n.sons[0].kind + if isEmptyType(sonType) or sonType.kind == tyNone or n.sons[0].kind == nkTypeOfExpr: localError(c.config, n.info, errInvalidDiscard) + if sonType.kind == tyProc and sonKind notin nkCallKinds: + # tyProc is disallowed to prevent ``discard foo`` to be valid, when ``discard foo()`` is meant. + localError(c.config, n.info, "illegal discard proc, did you mean: " & $n[0] & "()") proc semBreakOrContinue(c: PContext, n: PNode): PNode = result = n @@ -61,12 +66,12 @@ proc semBreakOrContinue(c: PContext, n: PNode): PNode = incl(s.flags, sfUsed) n.sons[0] = x suggestSym(c.config, x.info, s, c.graph.usageSym) - styleCheckUse(x.info, s) + onUse(x.info, s) else: localError(c.config, n.info, errInvalidControlFlowX % s.name.s) else: localError(c.config, n.info, errGenerated, "'continue' cannot have a label") - elif (c.p.nestedLoopCounter <= 0) and (c.p.nestedBlockCounter <= 0): + elif (c.p.nestedLoopCounter <= 0) and ((c.p.nestedBlockCounter <= 0) or n.kind == nkContinueStmt): localError(c.config, n.info, errInvalidControlFlowX % renderTree(n, {renderNoComments})) @@ -207,6 +212,8 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags): PNode = typ = commonType(typ, n[0].typ) var last = sonsLen(n) - 1 + var catchAllExcepts = 0 + for i in countup(1, last): let a = n.sons[i] checkMinSonsLen(a, 1, c.config) @@ -227,8 +234,16 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags): PNode = # Overwrite symbol in AST with the symbol in the symbol table. a[0][2] = newSymNode(symbol, a[0][2].info) + elif a.len == 1: + # count number of ``except: body`` blocks + inc catchAllExcepts + else: # support ``except KeyError, ValueError, ... : body`` + if catchAllExcepts > 0: + # if ``except: body`` already encountered, + # cannot be followed by a ``except KeyError, ... : body`` block + inc catchAllExcepts var is_native, is_imported: bool for j in 0..a.len-2: let tmp = semExceptBranchType(a[j]) @@ -238,9 +253,18 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags): PNode = if is_native and is_imported: localError(c.config, a[0].info, "Mix of imported and native exception types is not allowed in one except branch") - elif a.kind != nkFinally: + elif a.kind == nkFinally: + if i != n.len-1: + localError(c.config, a.info, "Only one finally is allowed after all other branches") + + else: illFormedAst(n, c.config) + if catchAllExcepts > 1: + # if number of ``except: body`` blocks is greater than 1 + # or more specific exception follows a general except block, it is invalid + localError(c.config, a.info, "Only one general except clause is allowed after more specific exceptions") + # last child of an nkExcept/nkFinally branch is a statement: a[^1] = semExprBranchScope(c, a[^1]) if a.kind != nkFinally: typ = commonType(typ, a[^1]) @@ -299,7 +323,6 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym = if result.owner.kind == skModule: incl(result.flags, sfGlobal) suggestSym(c.config, n.info, result, c.graph.usageSym) - styleCheckDef(c.config, result) proc checkNilable(c: PContext; v: PSym) = if {sfGlobal, sfImportC} * v.flags == {sfGlobal} and @@ -340,6 +363,8 @@ proc semUsing(c: PContext; n: PNode): PNode = let typ = semTypeNode(c, a.sons[length-2], nil) for j in countup(0, length-3): let v = semIdentDef(c, a.sons[j], skParam) + styleCheckDef(c.config, v) + onDef(a[j].info, v) v.typ = typ strTableIncl(c.signatures, v) else: @@ -470,6 +495,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = addToVarSection(c, result, n, a) continue var v = semIdentDef(c, a.sons[j], symkind) + styleCheckDef(c.config, v) + onDef(a[j].info, v) if sfGenSym notin v.flags and not isDiscardUnderscore(v): addInterfaceDecl(c, v) when oKeepVariableNames: @@ -523,6 +550,8 @@ proc semConst(c: PContext, n: PNode): PNode = if a.kind != nkConstDef: illFormedAst(a, c.config) checkSonsLen(a, 3, c.config) var v = semIdentDef(c, a.sons[0], skConst) + styleCheckDef(c.config, v) + onDef(a[0].info, v) var typ: PType = nil if a.sons[1].kind != nkEmpty: typ = semTypeNode(c, a.sons[1], nil) @@ -566,6 +595,7 @@ proc symForVar(c: PContext, n: PNode): PSym = let m = if n.kind == nkPragmaExpr: n.sons[0] else: n result = newSymG(skForVar, m, c) styleCheckDef(c.config, result) + onDef(n.info, result) if n.kind == nkPragmaExpr: pragma(c, result, n.sons[1], forVarPragmas) @@ -671,7 +701,7 @@ proc handleCaseStmtMacro(c: PContext; n: PNode): PNode = if r.state == csMatch: var match = r.calleeSym markUsed(c.config, n[0].info, match, c.graph.usageSym) - styleCheckUse(n[0].info, match) + onUse(n[0].info, match) # but pass 'n' to the 'match' macro, not 'n[0]': r.call.sons[1] = n @@ -804,9 +834,10 @@ proc semRaise(c: PContext, n: PNode): PNode = checkSonsLen(n, 1, c.config) if n[0].kind != nkEmpty: n[0] = semExprWithType(c, n[0]) - let typ = n[0].typ + var typ = n[0].typ if not isImportedException(typ, c.config): - if typ.kind != tyRef or typ.lastSon.kind != tyObject: + typ = typ.skipTypes({tyAlias, tyGenericInst}) + if typ.kind != tyRef: localError(c.config, n.info, errExprCannotBeRaised) if typ.len > 0 and not isException(typ.lastSon): localError(c.config, n.info, "raised object of type $1 does not inherit from Exception", @@ -853,6 +884,8 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = let typsym = pkg.tab.strTableGet(typName) if typsym.isNil: s = semIdentDef(c, name[1], skType) + styleCheckDef(c.config, s) + onDef(name[1].info, s) s.typ = newTypeS(tyObject, c) s.typ.sym = s s.flags.incl sfForward @@ -866,6 +899,8 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = s = typsym else: s = semIdentDef(c, name, skType) + styleCheckDef(c.config, s) + onDef(name.info, s) s.typ = newTypeS(tyForward, c) s.typ.sym = s # process pragmas: if name.kind == nkPragmaExpr: @@ -1199,13 +1234,6 @@ proc copyExcept(n: PNode, i: int): PNode = for j in 0..<n.len: if j != i: result.add(n.sons[j]) -proc lookupMacro(c: PContext, n: PNode): PSym = - if n.kind == nkSym: - result = n.sym - if result.kind notin {skMacro, skTemplate}: result = nil - else: - result = searchInScopes(c, considerQuotedIdent(c, n), {skMacro, skTemplate}) - proc semProcAnnotation(c: PContext, prc: PNode; validPragmas: TSpecialWords): PNode = var n = prc.sons[pragmasPos] @@ -1213,39 +1241,53 @@ proc semProcAnnotation(c: PContext, prc: PNode; for i in countup(0, n.len-1): var it = n.sons[i] var key = if it.kind in nkPragmaCallKinds and it.len >= 1: it.sons[0] else: it - let m = lookupMacro(c, key) - if m == nil: - if key.kind == nkIdent and key.ident.id == ord(wDelegator): - if considerQuotedIdent(c, prc.sons[namePos]).s == "()": - prc.sons[namePos] = newIdentNode(c.cache.idDelegator, prc.info) - prc.sons[pragmasPos] = copyExcept(n, i) - else: - localError(c.config, prc.info, "only a call operator can be a delegator") + + if whichPragma(it) != wInvalid: + # Not a custom pragma + continue + elif strTableGet(c.userPragmas, considerQuotedIdent(c, key)) != nil: + # User-defined pragma continue - elif sfCustomPragma in m.flags: - continue # semantic check for custom pragma happens later in semProcAux # we transform ``proc p {.m, rest.}`` into ``m(do: proc p {.rest.})`` and # let the semantic checker deal with it: - var x = newNodeI(nkCall, n.info) - x.add(newSymNode(m)) - prc.sons[pragmasPos] = copyExcept(n, i) - if prc[pragmasPos].kind != nkEmpty and prc[pragmasPos].len == 0: - prc.sons[pragmasPos] = c.graph.emptyNode + var x = newNodeI(nkCall, key.info) + x.add(key) if it.kind in nkPragmaCallKinds and it.len > 1: # pass pragma arguments to the macro too: for i in 1..<it.len: x.add(it.sons[i]) + + # Drop the pragma from the list, this prevents getting caught in endless + # recursion when the nkCall is semanticized + prc.sons[pragmasPos] = copyExcept(n, i) + if prc[pragmasPos].kind != nkEmpty and prc[pragmasPos].len == 0: + prc.sons[pragmasPos] = c.graph.emptyNode + x.add(prc) # recursion assures that this works for multiple macro annotations too: - result = semExpr(c, x) + var r = semOverloadedCall(c, x, x, {skMacro}, {efNoUndeclared}) + if r == nil: + # Restore the old list of pragmas since we couldn't process this + prc.sons[pragmasPos] = n + # No matching macro was found but there's always the possibility this may + # be a .pragma. template instead + continue + + doAssert r.sons[0].kind == nkSym + # Expand the macro here + result = semMacroExpr(c, r, r, r.sons[0].sym, {}) + + doAssert result != nil + # since a proc annotation can set pragmas, we process these here again. # This is required for SqueakNim-like export pragmas. if result.kind in procDefs and result[namePos].kind == nkSym and result[pragmasPos].kind != nkEmpty: pragma(c, result[namePos].sym, result[pragmasPos], validPragmas) + return proc setGenericParamsMisc(c: PContext; n: PNode): PNode = @@ -1301,8 +1343,8 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode = pushProcCon(c, s) addResult(c, s.typ.sons[0], n.info, skProc) addResultNode(c, n) - let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) - n.sons[bodyPos] = transformBody(c.graph, c.module, semBody, s) + s.ast[bodyPos] = hloBody(c, semProcBody(c, n.sons[bodyPos])) + trackProc(c.graph, s, s.ast[bodyPos]) popProcCon(c) elif efOperand notin flags: localError(c.config, n.info, errGenericLambdaNotAllowed) @@ -1342,8 +1384,8 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode = pushProcCon(c, s) addResult(c, n.typ.sons[0], n.info, skProc) addResultNode(c, n) - let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) - n.sons[bodyPos] = transformBody(c.graph, c.module, semBody, s) + s.ast[bodyPos] = hloBody(c, semProcBody(c, n.sons[bodyPos])) + trackProc(c.graph, s, s.ast[bodyPos]) popProcCon(c) popOwner(c) closeScope(c) @@ -1490,7 +1532,7 @@ proc semMethodPrototype(c: PContext; s: PSym; n: PNode) = tyAlias, tySink}) if x.kind == tyObject and t.len-1 == n.sons[genericParamsPos].len: foundObj = true - x.methods.safeAdd((col,s)) + x.methods.add((col,s)) if not foundObj: message(c.config, n.info, warnDeprecated, "generic method not attachable to object type") else: @@ -1589,6 +1631,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, pragma(c, s, n.sons[pragmasPos], validPragmas) else: implicitPragmas(c, s, n, validPragmas) + styleCheckDef(c.config, s) + onDef(n[namePos].info, s) else: if n.sons[pragmasPos].kind != nkEmpty: pragma(c, s, n.sons[pragmasPos], validPragmas) @@ -1601,6 +1645,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if proto.typ.callConv != s.typ.callConv or proto.typ.flags < s.typ.flags: localError(c.config, n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProcX % ("'" & proto.name.s & "' from " & c.config$proto.info)) + styleCheckDef(c.config, s) + onDefResolveForward(n[namePos].info, proto) if sfForward notin proto.flags and proto.magic == mNone: wrongRedefinition(c, n.info, proto.name.s, proto.info) excl(proto.flags, sfForward) @@ -1633,7 +1679,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, localError(c.config, n.info, "the overloaded " & s.name.s & " operator has to be enabled with {.experimental: \"callOperator\".}") - if n.sons[bodyPos].kind != nkEmpty: + if n.sons[bodyPos].kind != nkEmpty and sfError notin s.flags: # for DLL generation it is annoying to check for sfImportc! if sfBorrow in s.flags: localError(c.config, n.sons[bodyPos].info, errImplOfXNotAllowed % s.name.s) @@ -1656,10 +1702,10 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if lfDynamicLib notin s.loc.flags: # no semantic checking for importc: - let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) + s.ast[bodyPos] = hloBody(c, semProcBody(c, n.sons[bodyPos])) # unfortunately we cannot skip this step when in 'system.compiles' # context as it may even be evaluated in 'system.compiles': - n.sons[bodyPos] = transformBody(c.graph, c.module, semBody, s) + trackProc(c.graph, s, s.ast[bodyPos]) else: if s.typ.sons[0] != nil and kind != skIterator: addDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nil, n.info)) @@ -1677,7 +1723,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, else: if s.kind == skMethod: semMethodPrototype(c, s, n) if proto != nil: localError(c.config, n.info, errImplOfXexpected % proto.name.s) - if {sfImportc, sfBorrow} * s.flags == {} and s.magic == mNone: + if {sfImportc, sfBorrow, sfError} * s.flags == {} and s.magic == mNone: incl(s.flags, sfForward) elif sfBorrow in s.flags: semBorrow(c, n, s) sideEffectsCheck(c, s) @@ -1814,7 +1860,7 @@ proc semPragmaBlock(c: PContext, n: PNode): PNode = for i in 0 ..< pragmaList.len: case whichPragma(pragmaList.sons[i]) of wLine: setLine(result, pragmaList.sons[i].info) - of wLocks, wGcSafe: + of wLocks, wGcSafe, wNosideeffect: result = n result.typ = n.sons[1].typ of wNoRewrite: @@ -1917,7 +1963,8 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = case n.sons[j].kind of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr, nkBlockStmt, nkState: discard - else: localError(c.config, n.sons[j].info, "unreachable statement after 'return'") + else: localError(c.config, n.sons[j].info, + "unreachable statement after 'return' statement or '{.noReturn.}' proc") else: discard if result.len == 1 and |