diff --git a/compiler/ast.nim b/compiler/ast.nim index be846e8dfc..8fbec64cf1 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1585,6 +1585,22 @@ proc skipStmtList*(n: PNode): PNode = else: result = n +proc toRef*(typ: PType): PType = + ## If ``typ`` is a tyObject then it is converted into a `ref ` and + ## returned. Otherwise ``typ`` is simply returned as-is. + result = typ + if typ.kind == tyObject: + result = newType(tyRef, typ.owner) + rawAddSon(result, typ) + +proc toObject*(typ: PType): PType = + ## If ``typ`` is a tyRef then its immediate son is returned (which in many + ## cases should be a ``tyObject``). + ## Otherwise ``typ`` is simply returned as-is. + result = typ + if result.kind == tyRef: + result = result.lastSon + when false: proc containsNil*(n: PNode): bool = # only for debugging diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 61ecdb24b2..19a4da07b7 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -434,3 +434,6 @@ proc pickSym*(c: PContext, n: PNode; kind: TSymKind; if a.kind == kind and flags <= a.flags: return a a = nextOverloadIter(o, c, n) + +proc isInfixAs*(n: PNode): bool = + return n.kind == nkInfix and considerQuotedIdent(n[0]).s == "as" \ No newline at end of file diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index ab0ce7c4c2..bc80c41ad1 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -338,9 +338,16 @@ proc semGenericStmt(c: PContext, n: PNode, var a = n.sons[i] checkMinSonsLen(a, 1) var L = sonsLen(a) + openScope(c) for j in countup(0, L-2): - a.sons[j] = semGenericStmt(c, a.sons[j], flags+{withinTypeDesc}, ctx) + if a.sons[j].isInfixAs(): + addTempDecl(c, getIdentNode(a.sons[j][2]), skLet) + a.sons[j].sons[1] = semGenericStmt(c, a.sons[j][1], flags+{withinTypeDesc}, ctx) + else: + a.sons[j] = semGenericStmt(c, a.sons[j], flags+{withinTypeDesc}, ctx) a.sons[L-1] = semGenericStmtScope(c, a.sons[L-1], flags, ctx) + closeScope(c) + of nkVarSection, nkLetSection: for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index bbc83eca46..25d4b3c745 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -263,36 +263,64 @@ proc semTry(c: PContext, n: PNode): PNode = result = n inc c.p.inTryStmt checkMinSonsLen(n, 2) + var typ = commonTypeBegin n.sons[0] = semExprBranchScope(c, n.sons[0]) typ = commonType(typ, n.sons[0].typ) + var check = initIntSet() var last = sonsLen(n) - 1 for i in countup(1, last): var a = n.sons[i] checkMinSonsLen(a, 1) var length = sonsLen(a) + openScope(c) if a.kind == nkExceptBranch: # so that ``except [a, b, c]`` is supported: if length == 2 and a.sons[0].kind == nkBracket: a.sons[0..0] = a.sons[0].sons length = a.sonsLen + # Iterate through each exception type in the except branch. for j in countup(0, length-2): - var typ = semTypeNode(c, a.sons[j], nil) - if typ.kind == tyRef: typ = typ.sons[0] + var typeNode = a.sons[j] # e.g. `Exception` + var symbolNode: PNode = nil # e.g. `foobar` + # Handle the case where the `Exception as foobar` syntax is used. + if typeNode.isInfixAs(): + typeNode = a.sons[j].sons[1] + symbolNode = a.sons[j].sons[2] + + # Resolve the type ident into a PType. + var typ = semTypeNode(c, typeNode, nil).toObject() if typ.kind != tyObject: localError(a.sons[j].info, errExprCannotBeRaised) - a.sons[j] = newNodeI(nkType, a.sons[j].info) - a.sons[j].typ = typ + + let newTypeNode = newNodeI(nkType, typeNode.info) + newTypeNode.typ = typ + if symbolNode.isNil: + a.sons[j] = newTypeNode + else: + a.sons[j].sons[1] = newTypeNode + # Add the exception ident to the symbol table. + let symbol = newSymG(skLet, symbolNode, c) + symbol.typ = typ.toRef() + addDecl(c, symbol) + # Overwrite symbol in AST with the symbol in the symbol table. + let symNode = newNodeI(nkSym, typeNode.info) + symNode.sym = symbol + a.sons[j].sons[2] = symNode + if containsOrIncl(check, typ.id): localError(a.sons[j].info, errExceptionAlreadyHandled) elif a.kind != nkFinally: illFormedAst(n) + # last child of an nkExcept/nkFinally branch is a statement: a.sons[length-1] = semExprBranchScope(c, a.sons[length-1]) if a.kind != nkFinally: typ = commonType(typ, a.sons[length-1].typ) else: dec last + closeScope(c) + dec c.p.inTryStmt if isEmptyType(typ) or typ.kind == tyNil: discardCheck(c, n.sons[0]) diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 5dba125c2f..a69fe477b9 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -398,9 +398,15 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = var a = n.sons[i] checkMinSonsLen(a, 1) var L = sonsLen(a) + openScope(c) for j in countup(0, L-2): - a.sons[j] = semTemplBody(c, a.sons[j]) + if a.sons[j].isInfixAs(): + addLocalDecl(c, a.sons[j].sons[2], skLet) + a.sons[j].sons[1] = semTemplBody(c, a.sons[j][1]) + else: + a.sons[j] = semTemplBody(c, a.sons[j]) a.sons[L-1] = semTemplBodyScope(c, a.sons[L-1]) + closeScope(c) of nkVarSection: semTemplSomeDecl(c, n, skVar) of nkLetSection: semTemplSomeDecl(c, n, skLet) of nkFormalParams: diff --git a/compiler/transf.nim b/compiler/transf.nim index 6eed17b2a9..13c6dd8fe2 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -21,7 +21,7 @@ import intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os, idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread, - lambdalifting, sempass2, lowerings + lambdalifting, sempass2, lowerings, lookups # implementation @@ -701,6 +701,36 @@ proc transformCall(c: PTransf, n: PNode): PTransNode = else: result = s.PTransNode +proc transformExceptBranch(c: PTransf, n: PNode): PTransNode = + result = transformSons(c, n) + if n[0].isInfixAs(): + let excTypeNode = n[0][1] + let actions = newTransNode(nkStmtList, n[1].info, 2) + # Generating `let exc = (excType)(getCurrentException())` + # -> getCurrentException() + let excCall = PTransNode(callCodegenProc("getCurrentException", ast.emptyNode)) + # -> (excType) + let convNode = newTransNode(nkHiddenSubConv, n[1].info, 2) + convNode[0] = PTransNode(ast.emptyNode) + convNode[1] = excCall + PNode(convNode).typ = excTypeNode.typ.toRef() + # -> let exc = ... + let identDefs = newTransNode(nkIdentDefs, n[1].info, 3) + identDefs[0] = PTransNode(n[0][2]) + identDefs[1] = PTransNode(ast.emptyNode) + identDefs[2] = convNode + + let letSection = newTransNode(nkLetSection, n[1].info, 1) + letSection[0] = identDefs + # Place the let statement and body of the 'except' branch into new stmtList. + actions[0] = letSection + actions[1] = transformSons(c, n[1]) + # Overwrite 'except' branch body with our stmtList. + result[1] = actions + + # Replace the `Exception as foobar` with just `Exception`. + result[0] = result[0][1] + proc dontInlineConstant(orig, cnst: PNode): bool {.inline.} = # symbols that expand to a complex constant (array, etc.) should not be # inlined, unless it's the empty array: @@ -851,6 +881,8 @@ proc transform(c: PTransf, n: PNode): PTransNode = if a.kind == nkSym: n.sons[1] = transformSymAux(c, a) return PTransNode(n) + of nkExceptBranch: + result = transformExceptBranch(c, n) else: result = transformSons(c, n) when false: diff --git a/tests/exception/texcas.nim b/tests/exception/texcas.nim new file mode 100644 index 0000000000..4b4ebe4488 --- /dev/null +++ b/tests/exception/texcas.nim @@ -0,0 +1,25 @@ +discard """ + output: '''Hello +Hello + ''' +""" +proc test[T]() = + try: + raise newException(T, "Hello") + except T as foobar: + echo(foobar.msg) + doAssert(not declared(foobar)) + +template testTemplate(excType: typedesc) = + try: + raise newException(excType, "Hello") + except excType as foobar: + echo(foobar.msg) + doAssert(not declared(foobar)) + +proc test2() = + testTemplate(Exception) + doAssert(not declared(foobar)) + +test[Exception]() +test2() \ No newline at end of file diff --git a/web/news/e031_version_0_16_2.rst b/web/news/e031_version_0_16_2.rst index ed73524534..6246a33c49 100644 --- a/web/news/e031_version_0_16_2.rst +++ b/web/news/e031_version_0_16_2.rst @@ -25,6 +25,23 @@ Compiler Additions Language Additions ------------------ +- The ``try`` statement's ``except`` branches now support the binding of a +caught exception to a variable: + +.. code-block:: nim + try: + raise newException(Exception, "Hello World") + except Exception as exc: + echo(exc.msg) + +This replaces the ``getCurrentException`` and ``getCurrentExceptionMsg()`` +procedures, although these procedures will remain in the stdlib for the +foreseeable future. This new language feature is actually implemented using +these procedures. + +In the near future we will be converting all exception types to refs to +remove the need for the ``newException`` template. + Bugfixes -------- @@ -88,4 +105,5 @@ via a commit, for a full list see - Fixed "Cannot implement distinct seq with setLen" (`#5090 `_) - Fixed "await inside array/dict literal produces invalid code" - (`#5314 `_) \ No newline at end of file + (`#5314 `_) +