Merge pull request #5319 from nim-lang/feature/3691

Implements #3691.
This commit is contained in:
Andreas Rumpf
2017-02-07 20:39:05 +01:00
committed by GitHub
8 changed files with 143 additions and 8 deletions

View File

@@ -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 <typ>` 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

View File

@@ -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"

View File

@@ -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]

View File

@@ -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])

View File

@@ -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:

View File

@@ -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:

View File

@@ -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()

View File

@@ -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 <https://github.com/nim-lang/Nim/issues/5090>`_)
- Fixed "await inside array/dict literal produces invalid code"
(`#5314 <https://github.com/nim-lang/Nim/issues/5314>`_)
(`#5314 <https://github.com/nim-lang/Nim/issues/5314>`_)