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
commit 4285db6e02
8 changed files with 143 additions and 8 deletions

View file

@ -1585,6 +1585,22 @@ proc skipStmtList*(n: PNode): PNode =
else: else:
result = n 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: when false:
proc containsNil*(n: PNode): bool = proc containsNil*(n: PNode): bool =
# only for debugging # 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: if a.kind == kind and flags <= a.flags:
return a return a
a = nextOverloadIter(o, c, n) 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] var a = n.sons[i]
checkMinSonsLen(a, 1) checkMinSonsLen(a, 1)
var L = sonsLen(a) var L = sonsLen(a)
openScope(c)
for j in countup(0, L-2): 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) a.sons[L-1] = semGenericStmtScope(c, a.sons[L-1], flags, ctx)
closeScope(c)
of nkVarSection, nkLetSection: of nkVarSection, nkLetSection:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i] var a = n.sons[i]

View file

@ -263,36 +263,64 @@ proc semTry(c: PContext, n: PNode): PNode =
result = n result = n
inc c.p.inTryStmt inc c.p.inTryStmt
checkMinSonsLen(n, 2) checkMinSonsLen(n, 2)
var typ = commonTypeBegin var typ = commonTypeBegin
n.sons[0] = semExprBranchScope(c, n.sons[0]) n.sons[0] = semExprBranchScope(c, n.sons[0])
typ = commonType(typ, n.sons[0].typ) typ = commonType(typ, n.sons[0].typ)
var check = initIntSet() var check = initIntSet()
var last = sonsLen(n) - 1 var last = sonsLen(n) - 1
for i in countup(1, last): for i in countup(1, last):
var a = n.sons[i] var a = n.sons[i]
checkMinSonsLen(a, 1) checkMinSonsLen(a, 1)
var length = sonsLen(a) var length = sonsLen(a)
openScope(c)
if a.kind == nkExceptBranch: if a.kind == nkExceptBranch:
# so that ``except [a, b, c]`` is supported: # so that ``except [a, b, c]`` is supported:
if length == 2 and a.sons[0].kind == nkBracket: if length == 2 and a.sons[0].kind == nkBracket:
a.sons[0..0] = a.sons[0].sons a.sons[0..0] = a.sons[0].sons
length = a.sonsLen length = a.sonsLen
# Iterate through each exception type in the except branch.
for j in countup(0, length-2): for j in countup(0, length-2):
var typ = semTypeNode(c, a.sons[j], nil) var typeNode = a.sons[j] # e.g. `Exception`
if typ.kind == tyRef: typ = typ.sons[0] 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: if typ.kind != tyObject:
localError(a.sons[j].info, errExprCannotBeRaised) 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): if containsOrIncl(check, typ.id):
localError(a.sons[j].info, errExceptionAlreadyHandled) localError(a.sons[j].info, errExceptionAlreadyHandled)
elif a.kind != nkFinally: elif a.kind != nkFinally:
illFormedAst(n) illFormedAst(n)
# last child of an nkExcept/nkFinally branch is a statement: # last child of an nkExcept/nkFinally branch is a statement:
a.sons[length-1] = semExprBranchScope(c, a.sons[length-1]) a.sons[length-1] = semExprBranchScope(c, a.sons[length-1])
if a.kind != nkFinally: typ = commonType(typ, a.sons[length-1].typ) if a.kind != nkFinally: typ = commonType(typ, a.sons[length-1].typ)
else: dec last else: dec last
closeScope(c)
dec c.p.inTryStmt dec c.p.inTryStmt
if isEmptyType(typ) or typ.kind == tyNil: if isEmptyType(typ) or typ.kind == tyNil:
discardCheck(c, n.sons[0]) discardCheck(c, n.sons[0])

View file

@ -398,9 +398,15 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
var a = n.sons[i] var a = n.sons[i]
checkMinSonsLen(a, 1) checkMinSonsLen(a, 1)
var L = sonsLen(a) var L = sonsLen(a)
openScope(c)
for j in countup(0, L-2): 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]) a.sons[L-1] = semTemplBodyScope(c, a.sons[L-1])
closeScope(c)
of nkVarSection: semTemplSomeDecl(c, n, skVar) of nkVarSection: semTemplSomeDecl(c, n, skVar)
of nkLetSection: semTemplSomeDecl(c, n, skLet) of nkLetSection: semTemplSomeDecl(c, n, skLet)
of nkFormalParams: of nkFormalParams:

View file

@ -21,7 +21,7 @@
import import
intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os, intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os,
idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread, idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread,
lambdalifting, sempass2, lowerings lambdalifting, sempass2, lowerings, lookups
# implementation # implementation
@ -701,6 +701,36 @@ proc transformCall(c: PTransf, n: PNode): PTransNode =
else: else:
result = s.PTransNode 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.} = proc dontInlineConstant(orig, cnst: PNode): bool {.inline.} =
# symbols that expand to a complex constant (array, etc.) should not be # symbols that expand to a complex constant (array, etc.) should not be
# inlined, unless it's the empty array: # inlined, unless it's the empty array:
@ -851,6 +881,8 @@ proc transform(c: PTransf, n: PNode): PTransNode =
if a.kind == nkSym: if a.kind == nkSym:
n.sons[1] = transformSymAux(c, a) n.sons[1] = transformSymAux(c, a)
return PTransNode(n) return PTransNode(n)
of nkExceptBranch:
result = transformExceptBranch(c, n)
else: else:
result = transformSons(c, n) result = transformSons(c, n)
when false: 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 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 Bugfixes
-------- --------
@ -89,3 +106,4 @@ via a commit, for a full list see
(`#5090 <https://github.com/nim-lang/Nim/issues/5090>`_) (`#5090 <https://github.com/nim-lang/Nim/issues/5090>`_)
- Fixed "await inside array/dict literal produces invalid code" - 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>`_)