bugfix: finally sections are executed before return/break

This commit is contained in:
Araq 2010-09-20 22:33:23 +02:00
commit 0a57f662fa
7 changed files with 71 additions and 49 deletions

View file

@ -1232,6 +1232,9 @@ template accumulateResult*(iter: expr) =
result = @[] result = @[]
for x in iter: add(result, x) for x in iter: add(result, x)
# we have to compute this here before turning it off in except.nim anyway ...
const nimrodStackTrace = compileOption("stacktrace")
{.push checks: off, line_dir: off, debugger: off.} {.push checks: off, line_dir: off, debugger: off.}
# obviously we cannot generate checking operations here :-) # obviously we cannot generate checking operations here :-)
# because it would yield into an endless recursion # because it would yield into an endless recursion

View file

@ -61,7 +61,6 @@ proc popCurrentException {.compilerRtl, inl.} =
# some platforms have native support for stack traces: # some platforms have native support for stack traces:
const const
nimrodStackTrace = compileOption("stacktrace")
nativeStackTrace = (defined(macosx) or defined(linux)) and nativeStackTrace = (defined(macosx) or defined(linux)) and
not nimrodStackTrace and false not nimrodStackTrace and false

View file

@ -27,17 +27,6 @@ proc genLineDir(p: BProc, t: PNode) =
appf(p.s[cpsStmts], "F.line = $1;F.filename = $2;$n", appf(p.s[cpsStmts], "F.line = $1;F.filename = $2;$n",
[toRope(line), makeCString(toFilename(t.info).extractFilename)]) [toRope(line), makeCString(toFilename(t.info).extractFilename)])
proc popSafePoints(p: BProc, howMany: int) =
for i in countup(1, howMany):
appcg(p, cpsStmts, "#popSafePoint();$n", [])
proc genReturnStmt(p: BProc, t: PNode) =
p.beforeRetNeeded = true
genLineDir(p, t)
if (t.sons[0] != nil): genStmts(p, t.sons[0])
popSafePoints(p, p.nestedTryStmts)
appff(p.s[cpsStmts], "goto BeforeRet;$n", "br label %BeforeRet$n", [])
proc genVarTuple(p: BProc, n: PNode) = proc genVarTuple(p: BProc, n: PNode) =
var var
L: int L: int
@ -143,6 +132,31 @@ proc genIfStmt(p: BProc, n: PNode) =
else: internalError(n.info, "genIfStmt()") else: internalError(n.info, "genIfStmt()")
if sonsLen(n) > 1: fixLabel(p, Lend) if sonsLen(n) > 1: fixLabel(p, Lend)
proc popSafePoints(p: BProc, howMany: int) =
var L = p.nestedTryStmts.len
# danger of endless recursion! we workaround this here, by a temp stack
var stack: seq[PNode]
newSeq(stack, howMany)
for i in countup(1, howMany):
stack[i-1] = p.nestedTryStmts[L-i]
setLen(p.nestedTryStmts, L-howMany)
for tryStmt in items(stack):
appcg(p, cpsStmts, "#popSafePoint();$n", [])
var finallyStmt = lastSon(tryStmt)
if finallyStmt.kind == nkFinally:
genStmts(p, finallyStmt.sons[0])
# push old elements again:
for i in countdown(howMany-1, 0):
p.nestedTryStmts.add(stack[i])
proc genReturnStmt(p: BProc, t: PNode) =
p.beforeRetNeeded = true
popSafePoints(p, min(1, p.nestedTryStmts.len))
genLineDir(p, t)
if (t.sons[0] != nil): genStmts(p, t.sons[0])
appff(p.s[cpsStmts], "goto BeforeRet;$n", "br label %BeforeRet$n", [])
proc genWhileStmt(p: BProc, t: PNode) = proc genWhileStmt(p: BProc, t: PNode) =
# we don't generate labels here as for example GCC would produce # we don't generate labels here as for example GCC would produce
# significantly worse code # significantly worse code
@ -157,7 +171,7 @@ proc genWhileStmt(p: BProc, t: PNode) =
length = len(p.blocks) length = len(p.blocks)
setlen(p.blocks, length + 1) setlen(p.blocks, length + 1)
p.blocks[length].id = - p.labels # negative because it isn't used yet p.blocks[length].id = - p.labels # negative because it isn't used yet
p.blocks[length].nestedTryStmts = p.nestedTryStmts p.blocks[length].nestedTryStmts = p.nestedTryStmts.len
app(p.s[cpsStmts], "while (1) {" & tnl) app(p.s[cpsStmts], "while (1) {" & tnl)
initLocExpr(p, t.sons[0], a) initLocExpr(p, t.sons[0], a)
if (t.sons[0].kind != nkIntLit) or (t.sons[0].intVal == 0): if (t.sons[0].kind != nkIntLit) or (t.sons[0].intVal == 0):
@ -178,8 +192,8 @@ proc genBlock(p: BProc, t: PNode, d: var TLoc) =
sym.loc.k = locOther sym.loc.k = locOther
sym.loc.a = idx sym.loc.a = idx
setlen(p.blocks, idx + 1) setlen(p.blocks, idx + 1)
p.blocks[idx].id = - p.labels # negative because it isn't used yet p.blocks[idx].id = -p.labels # negative because it isn't used yet
p.blocks[idx].nestedTryStmts = p.nestedTryStmts p.blocks[idx].nestedTryStmts = p.nestedTryStmts.len
if t.kind == nkBlockExpr: genStmtListExpr(p, t.sons[1], d) if t.kind == nkBlockExpr: genStmtListExpr(p, t.sons[1], d)
else: genStmts(p, t.sons[1]) else: genStmts(p, t.sons[1])
if p.blocks[idx].id > 0: if p.blocks[idx].id > 0:
@ -187,7 +201,6 @@ proc genBlock(p: BProc, t: PNode, d: var TLoc) =
setlen(p.blocks, idx) setlen(p.blocks, idx)
proc genBreakStmt(p: BProc, t: PNode) = proc genBreakStmt(p: BProc, t: PNode) =
genLineDir(p, t)
var idx = len(p.blocks) - 1 var idx = len(p.blocks) - 1
if t.sons[0] != nil: if t.sons[0] != nil:
# named break? # named break?
@ -196,7 +209,8 @@ proc genBreakStmt(p: BProc, t: PNode) =
assert(sym.loc.k == locOther) assert(sym.loc.k == locOther)
idx = sym.loc.a idx = sym.loc.a
p.blocks[idx].id = abs(p.blocks[idx].id) # label is used p.blocks[idx].id = abs(p.blocks[idx].id) # label is used
popSafePoints(p, p.nestedTryStmts - p.blocks[idx].nestedTryStmts) popSafePoints(p, p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts)
genLineDir(p, t)
appf(p.s[cpsStmts], "goto LA$1;$n", [toRope(p.blocks[idx].id)]) appf(p.s[cpsStmts], "goto LA$1;$n", [toRope(p.blocks[idx].id)])
proc genAsmStmt(p: BProc, t: PNode) = proc genAsmStmt(p: BProc, t: PNode) =
@ -286,7 +300,7 @@ proc genCaseSecondPass(p: BProc, t: PNode, labId: int) =
proc genCaseGeneric(p: BProc, t: PNode, rangeFormat, eqFormat: TFormatStr) = proc genCaseGeneric(p: BProc, t: PNode, rangeFormat, eqFormat: TFormatStr) =
# generate a C-if statement for a Nimrod case statement # generate a C-if statement for a Nimrod case statement
var a: TLoc var a: TLoc
initLocExpr(p, t.sons[0], a) # fist pass: gnerate ifs+goto: initLocExpr(p, t.sons[0], a) # fist pass: generate ifs+goto:
var labId = p.labels var labId = p.labels
for i in countup(1, sonsLen(t) - 1): for i in countup(1, sonsLen(t) - 1):
inc(p.labels) inc(p.labels)
@ -448,11 +462,10 @@ proc genCaseStmt(p: BProc, t: PNode) =
genOrdinalCase(p, t) genOrdinalCase(p, t)
proc hasGeneralExceptSection(t: PNode): bool = proc hasGeneralExceptSection(t: PNode): bool =
var length, i, blen: int var length = sonsLen(t)
length = sonsLen(t) var i = 1
i = 1
while (i < length) and (t.sons[i].kind == nkExceptBranch): while (i < length) and (t.sons[i].kind == nkExceptBranch):
blen = sonsLen(t.sons[i]) var blen = sonsLen(t.sons[i])
if blen == 1: if blen == 1:
return true return true
inc(i) inc(i)
@ -494,7 +507,7 @@ proc genTryStmtCpp(p: BProc, t: PNode) =
if optStackTrace in p.Options: if optStackTrace in p.Options:
app(p.s[cpsStmts], "framePtr = (TFrame*)&F;" & tnl) app(p.s[cpsStmts], "framePtr = (TFrame*)&F;" & tnl)
app(p.s[cpsStmts], "try {" & tnl) app(p.s[cpsStmts], "try {" & tnl)
inc(p.nestedTryStmts) add(p.nestedTryStmts, t)
genStmts(p, t.sons[0]) genStmts(p, t.sons[0])
length = sonsLen(t) length = sonsLen(t)
if t.sons[1].kind == nkExceptBranch: if t.sons[1].kind == nkExceptBranch:
@ -520,8 +533,8 @@ proc genTryStmtCpp(p: BProc, t: PNode) =
inc(i) inc(i)
if t.sons[1].kind == nkExceptBranch: if t.sons[1].kind == nkExceptBranch:
app(p.s[cpsStmts], "}}" & tnl) # end of catch-switch statement app(p.s[cpsStmts], "}}" & tnl) # end of catch-switch statement
popSafePoints(p, p.nestedTryStmts) appcg(p, cpsStmts, "#popSafePoint();")
dec(p.nestedTryStmts) discard pop(p.nestedTryStmts)
if (i < length) and (t.sons[i].kind == nkFinally): if (i < length) and (t.sons[i].kind == nkFinally):
genStmts(p, t.sons[i].sons[0]) genStmts(p, t.sons[i].sons[0])
if rethrowFlag != nil: if rethrowFlag != nil:
@ -560,7 +573,7 @@ proc genTryStmt(p: BProc, t: PNode) =
app(p.s[cpsStmts], "framePtr = (TFrame*)&F;" & tnl) app(p.s[cpsStmts], "framePtr = (TFrame*)&F;" & tnl)
appf(p.s[cpsStmts], "if ($1.status == 0) {$n", [safePoint]) appf(p.s[cpsStmts], "if ($1.status == 0) {$n", [safePoint])
var length = sonsLen(t) var length = sonsLen(t)
inc(p.nestedTryStmts) add(p.nestedTryStmts, t)
genStmts(p, t.sons[0]) genStmts(p, t.sons[0])
appcg(p, cpsStmts, "#popSafePoint();$n} else {$n#popSafePoint();$n") appcg(p, cpsStmts, "#popSafePoint();$n} else {$n#popSafePoint();$n")
var i = 1 var i = 1
@ -587,7 +600,7 @@ proc genTryStmt(p: BProc, t: PNode) =
[safePoint]) [safePoint])
inc(i) inc(i)
app(p.s[cpsStmts], '}' & tnl) # end of if statement app(p.s[cpsStmts], '}' & tnl) # end of if statement
dec(p.nestedTryStmts) discard pop(p.nestedTryStmts)
if i < length and t.sons[i].kind == nkFinally: if i < length and t.sons[i].kind == nkFinally:
genStmts(p, t.sons[i].sons[0]) genStmts(p, t.sons[i].sons[0])
appcg(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", [safePoint]) appcg(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", [safePoint])

View file

@ -59,21 +59,21 @@ type
# has been used (i.e. the label should be emitted) # has been used (i.e. the label should be emitted)
nestedTryStmts*: int # how many try statements is it nested into nestedTryStmts*: int # how many try statements is it nested into
TCProc{.final.} = object # represents C proc that is currently generated TCProc{.final.} = object # represents C proc that is currently generated
s*: TCProcSections # the procs sections; short name for readability s: TCProcSections # the procs sections; short name for readability
prc*: PSym # the Nimrod proc that this C proc belongs to prc: PSym # the Nimrod proc that this C proc belongs to
BeforeRetNeeded*: bool # true iff 'BeforeRet' label for proc is needed BeforeRetNeeded: bool # true iff 'BeforeRet' label for proc is needed
nestedTryStmts*: Natural # in how many nested try statements we are nestedTryStmts: seq[PNode] # in how many nested try statements we are
# (the vars must be volatile then) # (the vars must be volatile then)
labels*: Natural # for generating unique labels in the C proc labels: Natural # for generating unique labels in the C proc
blocks*: seq[TBlock] # nested blocks blocks: seq[TBlock] # nested blocks
options*: TOptions # options that should be used for code options: TOptions # options that should be used for code
# generation; this is the same as prc.options # generation; this is the same as prc.options
# unless prc == nil # unless prc == nil
frameLen*: int # current length of frame descriptor frameLen: int # current length of frame descriptor
sendClosure*: PType # closure record type that we pass sendClosure: PType # closure record type that we pass
receiveClosure*: PType # closure record type that we get receiveClosure: PType # closure record type that we get
module*: BModule # used to prevent excessive parameter passing module: BModule # used to prevent excessive parameter passing
TTypeSeq = seq[PType] TTypeSeq = seq[PType]
TCGen = object of TPassContext # represents a C source file TCGen = object of TPassContext # represents a C source file
@ -158,7 +158,8 @@ proc newProc(prc: PSym, module: BModule): BProc =
result.module = module result.module = module
if prc != nil: result.options = prc.options if prc != nil: result.options = prc.options
else: result.options = gOptions else: result.options = gOptions
result.blocks = @ [] result.blocks = @[]
result.nestedTryStmts = @[]
proc isSimpleConst(typ: PType): bool = proc isSimpleConst(typ: PType): bool =
result = not (skipTypes(typ, abstractVar).kind in result = not (skipTypes(typ, abstractVar).kind in
@ -373,7 +374,7 @@ proc assignLocalVar(p: BProc, s: PSym) =
else: else:
app(p.s[cpsLocals], getTypeDesc(p.module, s.loc.t)) app(p.s[cpsLocals], getTypeDesc(p.module, s.loc.t))
if sfRegister in s.flags: app(p.s[cpsLocals], " register") if sfRegister in s.flags: app(p.s[cpsLocals], " register")
if (sfVolatile in s.flags) or (p.nestedTryStmts > 0): if (sfVolatile in s.flags) or (p.nestedTryStmts.len > 0):
app(p.s[cpsLocals], " volatile") app(p.s[cpsLocals], " volatile")
appf(p.s[cpsLocals], " $1;$n", [s.loc.r]) appf(p.s[cpsLocals], " $1;$n", [s.loc.r])
localDebugInfo(p, s) localDebugInfo(p, s)

View file

@ -19,7 +19,9 @@ tconstr2.nim;69
tcopy.nim;TEMP=C:\Programs\xyz\bin tcopy.nim;TEMP=C:\Programs\xyz\bin
tcurrncy.nim;25 tcurrncy.nim;25
texplicitgeneric1.nim;Key: 12 value: 12Key: 13 value: 13 Key: A value: 12 Key: B value: 13 texplicitgeneric1.nim;Key: 12 value: 12Key: 13 value: 13 Key: A value: 12 Key: B value: 13
tfinally.nim;came here tfinally.nim;came here 3
tfinally2.nim;ABCD
tfinally3.nim;false
tfloat1.nim;Error: unhandled exception: FPU operation caused an overflow [EFloatOverflow] tfloat1.nim;Error: unhandled exception: FPU operation caused an overflow [EFloatOverflow]
tfloat2.nim;Error: unhandled exception: FPU operation caused a NaN result [EFloatInvalidOp] tfloat2.nim;Error: unhandled exception: FPU operation caused a NaN result [EFloatInvalidOp]
tformat.nim;Hi Andreas! How do you feel, Rumpf? tformat.nim;Hi Andreas! How do you feel, Rumpf?

1 tack.nim 125
19 tcopy.nim TEMP=C:\Programs\xyz\bin
20 tcurrncy.nim 25
21 texplicitgeneric1.nim Key: 12 value: 12Key: 13 value: 13 Key: A value: 12 Key: B value: 13
22 tfinally.nim came here came here 3
23 tfinally2.nim ABCD
24 tfinally3.nim false
25 tfloat1.nim Error: unhandled exception: FPU operation caused an overflow [EFloatOverflow]
26 tfloat2.nim Error: unhandled exception: FPU operation caused a NaN result [EFloatInvalidOp]
27 tformat.nim Hi Andreas! How do you feel, Rumpf?

View file

@ -2,9 +2,14 @@
proc main: int = proc main: int =
try: try:
return 1 try:
return 1
finally:
stdout.write("came ")
return 2
finally: finally:
echo "came here" stdout.write("here ")
return 3
discard main() #OUT came here echo main() #OUT came here 1

View file

@ -2,7 +2,6 @@ For version 0.8.10
================== ==================
- fix implicit generic routines - fix implicit generic routines
- bugfix: make ``finally`` more robust
High priority (version 0.9.0) High priority (version 0.9.0)