diff options
author | Araq <rumpf_a@web.de> | 2013-05-03 23:51:43 +0200 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2013-05-03 23:51:43 +0200 |
commit | fa0a327dd6f8f18ce935f6bd8993e4af474d1231 (patch) | |
tree | 444367f8069a14968bbc4c9e1d6986c281c6862b | |
parent | 8e5d6834cc51062a9d249fd7730432d32b7f6adc (diff) | |
download | Nim-fa0a327dd6f8f18ce935f6bd8993e4af474d1231.tar.gz |
completed expr/stmt unification
-rw-r--r-- | compiler/ccgexprs.nim | 222 | ||||
-rw-r--r-- | compiler/ccgstmts.nim | 198 | ||||
-rw-r--r-- | compiler/cgen.nim | 14 | ||||
-rw-r--r-- | compiler/parser.nim | 6 | ||||
-rw-r--r-- | compiler/renderer.nim | 5 | ||||
-rw-r--r-- | compiler/sem.nim | 6 | ||||
-rw-r--r-- | compiler/semdestruct.nim | 213 | ||||
-rw-r--r-- | compiler/semexprs.nim | 177 | ||||
-rw-r--r-- | compiler/semstmts.nim | 469 | ||||
-rw-r--r-- | todo.txt | 5 |
10 files changed, 642 insertions, 673 deletions
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 78173b5e8..a50b39d47 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -9,9 +9,6 @@ # included from cgen.nim -proc lenField: PRope {.inline.} = - result = toRope(if gCmd != cmdCompileToCpp: "Sup.len" else: "len") - # -------------------------- constant expressions ------------------------ proc intLiteral(i: biggestInt): PRope = @@ -207,8 +204,6 @@ proc asgnComplexity(n: PNode): int = result += asgnComplexity(t) else: nil -proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) - proc optAsgnLoc(a: TLoc, t: PType, field: PRope): TLoc = result.k = locField result.s = a.s @@ -345,11 +340,6 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) else: InternalError("genAssignment(" & $ty.kind & ')') -proc expr(p: BProc, e: PNode, d: var TLoc) -proc initLocExpr(p: BProc, e: PNode, result: var TLoc) = - initLoc(result, locNone, e.typ, OnUnknown) - expr(p, e, result) - proc getDestLoc(p: BProc, d: var TLoc, typ: PType) = if d.k == locNone: getTemp(p, typ, d) @@ -377,15 +367,15 @@ proc putIntoDest(p: BProc, d: var TLoc, t: PType, r: PRope) = d.a = -1 proc binaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) = - var b: TLoc + var a, b: TLoc if d.k != locNone: InternalError(e.info, "binaryStmt") - InitLocExpr(p, e.sons[1], d) + InitLocExpr(p, e.sons[1], a) InitLocExpr(p, e.sons[2], b) - lineCg(p, cpsStmts, frmt, rdLoc(d), rdLoc(b)) + lineCg(p, cpsStmts, frmt, rdLoc(a), rdLoc(b)) proc unaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) = var a: TLoc - if (d.k != locNone): InternalError(e.info, "unaryStmt") + if d.k != locNone: InternalError(e.info, "unaryStmt") InitLocExpr(p, e.sons[1], a) lineCg(p, cpsStmts, frmt, [rdLoc(a)]) @@ -840,43 +830,6 @@ proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) = else: genAssignment(p, d, tmp, {}) # no need for deep copying -proc genIfExpr(p: BProc, n: PNode, d: var TLoc) = - # - # if (!expr1) goto L1; - # thenPart - # goto LEnd - # L1: - # if (!expr2) goto L2; - # thenPart2 - # goto LEnd - # L2: - # elsePart - # Lend: - # - var - it: PNode - a, tmp: TLoc - Lend, Lelse: TLabel - getTemp(p, n.typ, tmp) # force it into a temp! - Lend = getLabel(p) - for i in countup(0, sonsLen(n) - 1): - it = n.sons[i] - if it.len == 2: - initLocExpr(p, it.sons[0], a) - Lelse = getLabel(p) - lineF(p, cpsStmts, "if (!$1) goto $2;$n", [rdLoc(a), Lelse]) - expr(p, it.sons[1], tmp) - lineF(p, cpsStmts, "goto $1;$n", [Lend]) - fixLabel(p, Lelse) - elif it.len == 1: - expr(p, it.sons[0], tmp) - else: internalError(n.info, "genIfExpr()") - fixLabel(p, Lend) - if d.k == locNone: - d = tmp - else: - genAssignment(p, d, tmp, {}) # no need for deep copying - proc genEcho(p: BProc, n: PNode) = # this unusal way of implementing it ensures that e.g. ``echo("hallo", 45)`` # is threadsafe. @@ -888,8 +841,6 @@ proc genEcho(p: BProc, n: PNode) = linefmt(p, cpsStmts, "printf($1$2);$n", makeCString(repeatStr(n.len-1, "%s") & tnl), args) -include ccgcalls - proc genStrConcat(p: BProc, e: PNode, d: var TLoc) = # <Nimrod code> # s = 'Hello ' & name & ', how do you feel?' & 'z' @@ -1790,11 +1741,10 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = else: putIntoDest(p, d, t, tmp) -proc genBlock(p: BProc, t: PNode, d: var TLoc) -proc expr(p: BProc, e: PNode, d: var TLoc) = - case e.kind +proc expr(p: BProc, n: PNode, d: var TLoc) = + case n.kind of nkSym: - var sym = e.sym + var sym = n.sym case sym.Kind of skMethod: if sym.getBody.kind == nkEmpty or sfDispatcher in sym.flags: @@ -1807,22 +1757,22 @@ proc expr(p: BProc, e: PNode, d: var TLoc) = of skProc, skConverter, skIterator: genProc(p.module, sym) if sym.loc.r == nil or sym.loc.t == nil: - InternalError(e.info, "expr: proc not init " & sym.name.s) + InternalError(n.info, "expr: proc not init " & sym.name.s) putLocIntoDest(p, d, sym.loc) of skConst: if sfFakeConst in sym.flags: if sfGlobal in sym.flags: genVarPrototype(p.module, sym) putLocIntoDest(p, d, sym.loc) elif isSimpleConst(sym.typ): - putIntoDest(p, d, e.typ, genLiteral(p, sym.ast, sym.typ)) + putIntoDest(p, d, n.typ, genLiteral(p, sym.ast, sym.typ)) else: genComplexConst(p, sym, d) of skEnumField: - putIntoDest(p, d, e.typ, toRope(sym.position)) + putIntoDest(p, d, n.typ, toRope(sym.position)) of skVar, skForVar, skResult, skLet: if sfGlobal in sym.flags: genVarPrototype(p.module, sym) if sym.loc.r == nil or sym.loc.t == nil: - InternalError(e.info, "expr: var not init " & sym.name.s) + InternalError(n.info, "expr: var not init " & sym.name.s) if sfThread in sym.flags: AccessThreadLocalVar(p, sym) if emulatedThreadVars(): @@ -1833,75 +1783,129 @@ proc expr(p: BProc, e: PNode, d: var TLoc) = putLocIntoDest(p, d, sym.loc) of skTemp: if sym.loc.r == nil or sym.loc.t == nil: - InternalError(e.info, "expr: temp not init " & sym.name.s) + InternalError(n.info, "expr: temp not init " & sym.name.s) putLocIntoDest(p, d, sym.loc) of skParam: if sym.loc.r == nil or sym.loc.t == nil: - InternalError(e.info, "expr: param not init " & sym.name.s) + InternalError(n.info, "expr: param not init " & sym.name.s) putLocIntoDest(p, d, sym.loc) - else: InternalError(e.info, "expr(" & $sym.kind & "); unknown symbol") + else: InternalError(n.info, "expr(" & $sym.kind & "); unknown symbol") + of nkNilLit: + if not isEmptyType(n.typ): + putIntoDest(p, d, n.typ, genLiteral(p, n)) of nkStrLit..nkTripleStrLit, nkIntLit..nkUInt64Lit, - nkFloatLit..nkFloat128Lit, nkNilLit, nkCharLit: - putIntoDest(p, d, e.typ, genLiteral(p, e)) + nkFloatLit..nkFloat128Lit, nkCharLit: + putIntoDest(p, d, n.typ, genLiteral(p, n)) of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: - if e.sons[0].kind == nkSym and e.sons[0].sym.magic != mNone: - genMagicExpr(p, e, d, e.sons[0].sym.magic) + genLineDir(p, n) + if n.sons[0].kind == nkSym and n.sons[0].sym.magic != mNone: + genMagicExpr(p, n, d, n.sons[0].sym.magic) else: - genCall(p, e, d) + genCall(p, n, d) of nkCurly: - if isDeepConstExpr(e) and e.len != 0: - putIntoDest(p, d, e.typ, genSetNode(p, e)) + if isDeepConstExpr(n) and n.len != 0: + putIntoDest(p, d, n.typ, genSetNode(p, n)) else: - genSetConstr(p, e, d) + genSetConstr(p, n, d) of nkBracket: - if isDeepConstExpr(e) and e.len != 0: - exprComplexConst(p, e, d) - elif skipTypes(e.typ, abstractVarRange).kind == tySequence: - genSeqConstr(p, e, d) + if isDeepConstExpr(n) and n.len != 0: + exprComplexConst(p, n, d) + elif skipTypes(n.typ, abstractVarRange).kind == tySequence: + genSeqConstr(p, n, d) else: - genArrayConstr(p, e, d) + genArrayConstr(p, n, d) of nkPar: - if isDeepConstExpr(e) and e.len != 0: - exprComplexConst(p, e, d) + if isDeepConstExpr(n) and n.len != 0: + exprComplexConst(p, n, d) else: - genTupleConstr(p, e, d) - of nkObjConstr: genObjConstr(p, e, d) - of nkCast: genCast(p, e, d) - of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, e, d) - of nkHiddenAddr, nkAddr: genAddr(p, e, d) + genTupleConstr(p, n, d) + of nkObjConstr: genObjConstr(p, n, d) + of nkCast: genCast(p, n, d) + of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, d) + of nkHiddenAddr, nkAddr: genAddr(p, n, d) of nkBracketExpr: - var ty = skipTypes(e.sons[0].typ, abstractVarRange) + var ty = skipTypes(n.sons[0].typ, abstractVarRange) if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.sons[0], abstractVarRange) case ty.kind - of tyArray, tyArrayConstr: genArrayElem(p, e, d) - of tyOpenArray, tyVarargs: genOpenArrayElem(p, e, d) - of tySequence, tyString: genSeqElem(p, e, d) - of tyCString: genCStringElem(p, e, d) - of tyTuple: genTupleElem(p, e, d) - else: InternalError(e.info, "expr(nkBracketExpr, " & $ty.kind & ')') - of nkDerefExpr, nkHiddenDeref: genDeref(p, e, d) - of nkDotExpr: genRecordField(p, e, d) - of nkCheckedFieldExpr: genCheckedRecordField(p, e, d) - of nkBlockExpr: genBlock(p, e, d) - of nkStmtListExpr: genStmtListExpr(p, e, d) - of nkIfExpr: genIfExpr(p, e, d) - of nkObjDownConv: downConv(p, e, d) - of nkObjUpConv: upConv(p, e, d) - of nkChckRangeF: genRangeChck(p, e, d, "chckRangeF") - of nkChckRange64: genRangeChck(p, e, d, "chckRange64") - of nkChckRange: genRangeChck(p, e, d, "chckRange") - of nkStringToCString: convStrToCStr(p, e, d) - of nkCStringToString: convCStrToStr(p, e, d) + of tyArray, tyArrayConstr: genArrayElem(p, n, d) + of tyOpenArray, tyVarargs: genOpenArrayElem(p, n, d) + of tySequence, tyString: genSeqElem(p, n, d) + of tyCString: genCStringElem(p, n, d) + of tyTuple: genTupleElem(p, n, d) + else: InternalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')') + of nkDerefExpr, nkHiddenDeref: genDeref(p, n, d) + of nkDotExpr: genRecordField(p, n, d) + of nkCheckedFieldExpr: genCheckedRecordField(p, n, d) + of nkBlockExpr, nkBlockStmt: genBlock(p, n, d) + of nkStmtListExpr: genStmtListExpr(p, n, d) + of nkStmtList: + for i in countup(0, sonsLen(n) - 1): genStmts(p, n.sons[i]) + of nkIfExpr, nkIfStmt: genIf(p, n, d) + of nkObjDownConv: downConv(p, n, d) + of nkObjUpConv: upConv(p, n, d) + of nkChckRangeF: genRangeChck(p, n, d, "chckRangeF") + of nkChckRange64: genRangeChck(p, n, d, "chckRange64") + of nkChckRange: genRangeChck(p, n, d, "chckRange") + of nkStringToCString: convStrToCStr(p, n, d) + of nkCStringToString: convCStrToStr(p, n, d) of nkLambdaKinds: - var sym = e.sons[namePos].sym + var sym = n.sons[namePos].sym genProc(p.module, sym) if sym.loc.r == nil or sym.loc.t == nil: - InternalError(e.info, "expr: proc not init " & sym.name.s) + InternalError(n.info, "expr: proc not init " & sym.name.s) putLocIntoDest(p, d, sym.loc) - of nkClosure: genClosure(p, e, d) - of nkMetaNode: expr(p, e.sons[0], d) - else: InternalError(e.info, "expr(" & $e.kind & "); unknown node kind") + of nkClosure: genClosure(p, n, d) + of nkMetaNode: expr(p, n.sons[0], d) + + of nkEmpty: nil + of nkWhileStmt: genWhileStmt(p, n) + of nkVarSection, nkLetSection: genVarStmt(p, n) + of nkConstSection: genConstStmt(p, n) + of nkForStmt: internalError(n.info, "for statement not eliminated") + of nkCaseStmt: genCase(p, n, d) + of nkReturnStmt: genReturnStmt(p, n) + of nkBreakStmt: genBreakStmt(p, n) + of nkAsgn: genAsgn(p, n, fastAsgn=false) + of nkFastAsgn: + # transf is overly aggressive with 'nkFastAsgn', so we work around here. + # See tests/run/tcnstseq3 for an example that would fail otherwise. + genAsgn(p, n, fastAsgn=p.prc != nil) + of nkDiscardStmt: + if n.sons[0].kind != nkEmpty: + var a: TLoc + genLineDir(p, n) + initLocExpr(p, n.sons[0], a) + of nkAsmStmt: genAsmStmt(p, n) + of nkTryStmt: + if gCmd == cmdCompileToCpp: genTryCpp(p, n, d) + else: genTry(p, n, d) + of nkRaiseStmt: genRaiseStmt(p, n) + of nkTypeSection: + # we have to emit the type information for object types here to support + # separate compilation: + genTypeSection(p.module, n) + of nkCommentStmt, nkIteratorDef, nkIncludeStmt, + nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, + nkFromStmt, nkTemplateDef, nkMacroDef: + nil + of nkPragma: genPragma(p, n) + of nkProcDef, nkMethodDef, nkConverterDef: + if (n.sons[genericParamsPos].kind == nkEmpty): + var prc = n.sons[namePos].sym + if (optDeadCodeElim notin gGlobalOptions and + sfDeadCodeElim notin getModule(prc).flags) or + ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or + (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or + (prc.kind == skMethod): + # we have not only the header: + if prc.getBody.kind != nkEmpty or lfDynamicLib in prc.loc.flags: + genProc(p.module, prc) + of nkParForStmt: genParForStmt(p, n) + of nkState: genState(p, n) + of nkGotoState: genGotoState(p, n) + of nkBreakState: genBreakState(p, n) + else: InternalError(n.info, "expr(" & $n.kind & "); unknown node kind") proc genNamedConstExpr(p: BProc, n: PNode): PRope = if n.kind == nkExprColonExpr: result = genConstExpr(p, n.sons[1]) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 59cdbbef4..a8ef8b31a 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -102,6 +102,11 @@ proc genSimpleBlock(p: BProc, stmts: PNode) {.inline.} = genStmts(p, stmts) endBlock(p) +proc exprBlock(p: BProc, n: PNode, d: var TLoc) = + startBlock(p) + expr(p, n, d) + endBlock(p) + template preserveBreakIdx(body: stmt): stmt {.immediate.} = var oldBreakIdx = p.breakIdx body @@ -212,7 +217,7 @@ proc genConstStmt(p: BProc, t: PNode) = appf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n", [getTypeDesc(p.module, c.typ), c.loc.r, genConstExpr(p, c.ast)]) -proc genIfStmt(p: BProc, n: PNode) = +proc genIf(p: BProc, n: PNode, d: var TLoc) = # # { if (!expr1) goto L1; # thenPart } @@ -224,32 +229,36 @@ proc genIfStmt(p: BProc, n: PNode) = # L2: # { elsePart } # Lend: - var + var a: TLoc Lelse: TLabel + if not isEmptyType(n.typ) and d.k == locNone: + getTemp(p, n.typ, d) genLineDir(p, n) - var Lend = getLabel(p) + let Lend = getLabel(p) for i in countup(0, sonsLen(n) - 1): - var it = n.sons[i] + let it = n.sons[i] if it.len == 2: when newScopeForIf: startBlock(p) initLocExpr(p, it.sons[0], a) Lelse = getLabel(p) inc(p.labels) lineFF(p, cpsStmts, "if (!$1) goto $2;$n", - "br i1 $1, label %LOC$3, label %$2$n" & "LOC$3: $n", + "br i1 $1, label %LOC$3, label %$2$nLOC$3: $n", [rdLoc(a), Lelse, toRope(p.labels)]) when not newScopeForIf: startBlock(p) - genStmts(p, it.sons[1]) + expr(p, it.sons[1], d) endBlock(p) - if sonsLen(n) > 1: + if sonsLen(n) > 1: lineFF(p, cpsStmts, "goto $1;$n", "br label %$1$n", [Lend]) fixLabel(p, Lelse) elif it.len == 1: - genSimpleBlock(p, it.sons[0]) - else: internalError(n.info, "genIfStmt()") + startBlock(p) + expr(p, it.sons[0], d) + endBlock(p) + else: internalError(n.info, "genIf()") if sonsLen(n) > 1: fixLabel(p, Lend) - + proc blockLeaveActions(p: BProc, howMany: int) = var L = p.nestedTryStmts.len # danger of endless recursion! we workaround this here by a temp stack @@ -312,16 +321,15 @@ proc genWhileStmt(p: BProc, t: PNode) = proc genBlock(p: BProc, t: PNode, d: var TLoc) = preserveBreakIdx: p.breakIdx = startBlock(p) - if t.sons[0].kind != nkEmpty: + if t.sons[0].kind != nkEmpty: # named block? assert(t.sons[0].kind == nkSym) var sym = t.sons[0].sym sym.loc.k = locOther sym.loc.a = p.breakIdx - if t.kind == nkBlockExpr: genStmtListExpr(p, t.sons[1], d) - else: genStmts(p, t.sons[1]) + expr(p, t.sons[1], d) endBlock(p) - + proc genParForStmt(p: BProc, t: PNode) = assert(sonsLen(t) == 3) inc(p.withinLoop) @@ -411,42 +419,45 @@ proc genCaseGenericBranch(p: BProc, b: PNode, e: TLoc, initLocExpr(p, b.sons[i], x) lineCg(p, cpsStmts, eqFormat, [rdCharLoc(e), rdCharLoc(x), labl]) -proc genCaseSecondPass(p: BProc, t: PNode, labId, until: int): TLabel = +proc genCaseSecondPass(p: BProc, t: PNode, d: var TLoc, + labId, until: int): TLabel = var Lend = getLabel(p) - for i in 1..until: + for i in 1..until: lineF(p, cpsStmts, "LA$1: ;$n", [toRope(labId + i)]) if t.sons[i].kind == nkOfBranch: var length = sonsLen(t.sons[i]) - genSimpleBlock(p, t.sons[i].sons[length - 1]) + exprBlock(p, t.sons[i].sons[length - 1], d) lineF(p, cpsStmts, "goto $1;$n", [Lend]) - else: - genSimpleBlock(p, t.sons[i].sons[0]) + else: + exprBlock(p, t.sons[i].sons[0], d) result = Lend -proc genIfForCaseUntil(p: BProc, t: PNode, rangeFormat, eqFormat: TFormatStr, - until: int, a: TLoc): TLabel = +proc genIfForCaseUntil(p: BProc, t: PNode, d: var TLoc, + rangeFormat, eqFormat: TFormatStr, + until: int, a: TLoc): TLabel = # generate a C-if statement for a Nimrod case statement var labId = p.labels - for i in 1..until: + for i in 1..until: inc(p.labels) if t.sons[i].kind == nkOfBranch: # else statement - genCaseGenericBranch(p, t.sons[i], a, rangeFormat, eqFormat, + genCaseGenericBranch(p, t.sons[i], a, rangeFormat, eqFormat, con("LA", toRope(p.labels))) - else: + else: lineF(p, cpsStmts, "goto LA$1;$n", [toRope(p.labels)]) - if until < t.len-1: + if until < t.len-1: inc(p.labels) var gotoTarget = p.labels lineF(p, cpsStmts, "goto LA$1;$n", [toRope(gotoTarget)]) - result = genCaseSecondPass(p, t, labId, until) + result = genCaseSecondPass(p, t, d, labId, until) lineF(p, cpsStmts, "LA$1: ;$n", [toRope(gotoTarget)]) else: - result = genCaseSecondPass(p, t, labId, until) + result = genCaseSecondPass(p, t, d, labId, until) -proc genCaseGeneric(p: BProc, t: PNode, rangeFormat, eqFormat: TFormatStr) = +proc genCaseGeneric(p: BProc, t: PNode, d: var TLoc, + rangeFormat, eqFormat: TFormatStr) = var a: TLoc initLocExpr(p, t.sons[0], a) - var Lend = genIfForCaseUntil(p, t, rangeFormat, eqFormat, sonsLen(t)-1, a) + var Lend = genIfForCaseUntil(p, t, d, rangeFormat, eqFormat, sonsLen(t)-1, a) fixLabel(p, Lend) proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel, @@ -461,12 +472,12 @@ proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel, appcg(p.module, branches[j], "if (#eqStrings($1, $2)) goto $3;$n", [rdLoc(e), rdLoc(x), labl]) -proc genStringCase(p: BProc, t: PNode) = +proc genStringCase(p: BProc, t: PNode, d: var TLoc) = # count how many constant strings there are in the case: var strings = 0 - for i in countup(1, sonsLen(t) - 1): + for i in countup(1, sonsLen(t) - 1): if t.sons[i].kind == nkOfBranch: inc(strings, sonsLen(t.sons[i]) - 1) - if strings > stringCaseThreshold: + if strings > stringCaseThreshold: var bitMask = math.nextPowerOfTwo(strings) - 1 var branches: seq[PRope] newSeq(branches, bitMask + 1) @@ -484,23 +495,17 @@ proc genStringCase(p: BProc, t: PNode) = linefmt(p, cpsStmts, "switch (#hashString($1) & $2) {$n", rdLoc(a), toRope(bitMask)) for j in countup(0, high(branches)): - when false: - let interior = cast[int](interiorAllocatedPtr(addr(branches[0])))+ - 2*sizeof(pointer) - let brn = cast[int](cast[pointer](branches)) - if interior != brn: - echo "BUG! ", interior, "-", brn if branches[j] != nil: lineF(p, cpsStmts, "case $1: $n$2break;$n", [intLiteral(j), branches[j]]) lineF(p, cpsStmts, "}$n") # else statement: - if t.sons[sonsLen(t) - 1].kind != nkOfBranch: + if t.sons[sonsLen(t)-1].kind != nkOfBranch: lineF(p, cpsStmts, "goto LA$1;$n", [toRope(p.labels)]) # third pass: generate statements - var Lend = genCaseSecondPass(p, t, labId, sonsLen(t)-1) + var Lend = genCaseSecondPass(p, t, d, labId, sonsLen(t)-1) fixLabel(p, Lend) - else: - genCaseGeneric(p, t, "", "if (#eqStrings($1, $2)) goto $3;$n") + else: + genCaseGeneric(p, t, d, "", "if (#eqStrings($1, $2)) goto $3;$n") proc branchHasTooBigRange(b: PNode): bool = for i in countup(0, sonsLen(b)-2): @@ -535,14 +540,14 @@ proc genCaseRange(p: BProc, branch: PNode) = else: lineF(p, cpsStmts, "case $1:$n", [genLiteral(p, branch[j])]) -proc genOrdinalCase(p: BProc, n: PNode) = +proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) = # analyse 'case' statement: var splitPoint = IfSwitchSplitPoint(p, n) # generate if part (might be empty): var a: TLoc initLocExpr(p, n.sons[0], a) - var Lend = if splitPoint > 0: genIfForCaseUntil(p, n, + var Lend = if splitPoint > 0: genIfForCaseUntil(p, n, d, rangeFormat = "if ($1 >= $2 && $1 <= $3) goto $4;$n", eqFormat = "if ($1 == $2) goto $3;$n", splitPoint, a) else: nil @@ -555,28 +560,29 @@ proc genOrdinalCase(p: BProc, n: PNode) = var branch = n[i] if branch.kind == nkOfBranch: genCaseRange(p, branch) - genSimpleBlock(p, branch.lastSon) else: # else part of case statement: lineF(p, cpsStmts, "default:$n") - genSimpleBlock(p, branch[0]) hasDefault = true + exprBlock(p, branch.lastSon, d) lineF(p, cpsStmts, "break;$n") if (hasAssume in CC[ccompiler].props) and not hasDefault: lineF(p, cpsStmts, "default: __assume(0);$n") lineF(p, cpsStmts, "}$n") if Lend != nil: fixLabel(p, Lend) -proc genCaseStmt(p: BProc, t: PNode) = +proc genCase(p: BProc, t: PNode, d: var TLoc) = genLineDir(p, t) + if not isEmptyType(t.typ) and d.k == locNone: + getTemp(p, t.typ, d) case skipTypes(t.sons[0].typ, abstractVarRange).kind - of tyString: - genStringCase(p, t) + of tyString: + genStringCase(p, t, d) of tyFloat..tyFloat128: - genCaseGeneric(p, t, "if ($1 >= $2 && $1 <= $3) goto $4;$n", - "if ($1 == $2) goto $3;$n") - else: - genOrdinalCase(p, t) + genCaseGeneric(p, t, d, "if ($1 >= $2 && $1 <= $3) goto $4;$n", + "if ($1 == $2) goto $3;$n") + else: + genOrdinalCase(p, t, d) proc hasGeneralExceptSection(t: PNode): bool = var length = sonsLen(t) @@ -588,7 +594,7 @@ proc hasGeneralExceptSection(t: PNode): bool = inc(i) result = false -proc genTryStmtCpp(p: BProc, t: PNode) = +proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = # code to generate: # # XXX: There should be a standard dispatch algorithm @@ -609,6 +615,8 @@ proc genTryStmtCpp(p: BProc, t: PNode) = # } # } # finallyPart(); + if not isEmptyType(t.typ) and d.k == locNone: + getTemp(p, t.typ, d) var exc: PRope i, length, blen: int @@ -617,7 +625,7 @@ proc genTryStmtCpp(p: BProc, t: PNode) = discard cgsym(p.module, "E_Base") add(p.nestedTryStmts, t) startBlock(p, "try {$n") - genStmts(p, t.sons[0]) + expr(p, t.sons[0], d) length = sonsLen(t) endBlock(p, ropecg(p.module, "} catch (NimException& $1) {$n", [exc])) if optStackTrace in p.Options: @@ -631,7 +639,7 @@ proc genTryStmtCpp(p: BProc, t: PNode) = if blen == 1: # general except section: catchAllPresent = true - genSimpleBlock(p, t.sons[i].sons[0]) + exprBlock(p, t.sons[i].sons[0], d) else: var orExpr: PRope = nil for j in countup(0, blen - 2): @@ -641,7 +649,7 @@ proc genTryStmtCpp(p: BProc, t: PNode) = "#isObj($1.exp->m_type, $2)", [exc, genTypeInfo(p.module, t.sons[i].sons[j].typ)]) lineF(p, cpsStmts, "if ($1) ", [orExpr]) - genSimpleBlock(p, t.sons[i].sons[blen-1]) + exprBlock(p, t.sons[i].sons[blen-1], d) inc(i) # reraise the exception if there was no catch all @@ -651,7 +659,7 @@ proc genTryStmtCpp(p: BProc, t: PNode) = startBlock(p) var finallyBlock = t.lastSon if finallyBlock.kind == nkFinally: - genStmts(p, finallyBlock.sons[0]) + expr(p, finallyBlock.sons[0], d) line(p, cpsStmts, ~"throw;$n") endBlock(p) @@ -660,9 +668,9 @@ proc genTryStmtCpp(p: BProc, t: PNode) = discard pop(p.nestedTryStmts) if (i < length) and (t.sons[i].kind == nkFinally): - genSimpleBlock(p, t.sons[i].sons[0]) + exprBlock(p, t.sons[i].sons[0], d) -proc genTryStmt(p: BProc, t: PNode) = +proc genTry(p: BProc, t: PNode, d: var TLoc) = # code to generate: # # XXX: There should be a standard dispatch algorithm @@ -691,6 +699,8 @@ proc genTryStmt(p: BProc, t: PNode) = # if (exception not cleared) # propagateCurrentException(); # + if not isEmptyType(t.typ) and d.k == locNone: + getTemp(p, t.typ, d) genLineDir(p, t) var safePoint = getTempName() discard cgsym(p.module, "E_Base") @@ -700,7 +710,7 @@ proc genTryStmt(p: BProc, t: PNode) = startBlock(p, "if ($1.status == 0) {$n", [safePoint]) var length = sonsLen(t) add(p.nestedTryStmts, t) - genStmts(p, t.sons[0]) + expr(p, t.sons[0], d) linefmt(p, cpsStmts, "#popSafePoint();$n") endBlock(p) startBlock(p, "else {$n") @@ -716,7 +726,7 @@ proc genTryStmt(p: BProc, t: PNode) = if i > 1: lineF(p, cpsStmts, "else") startBlock(p) linefmt(p, cpsStmts, "$1.status = 0;$n", safePoint) - genStmts(p, t.sons[i].sons[0]) + expr(p, t.sons[i].sons[0], d) linefmt(p, cpsStmts, "#popCurrentException();$n") endBlock(p) else: @@ -730,7 +740,7 @@ proc genTryStmt(p: BProc, t: PNode) = if i > 1: line(p, cpsStmts, "else ") startBlock(p, "if ($1) {$n", [orExpr]) linefmt(p, cpsStmts, "$1.status = 0;$n", safePoint) - genStmts(p, t.sons[i].sons[blen-1]) + expr(p, t.sons[i].sons[blen-1], d) linefmt(p, cpsStmts, "#popCurrentException();$n") endBlock(p) inc(i) @@ -738,7 +748,7 @@ proc genTryStmt(p: BProc, t: PNode) = discard pop(p.nestedTryStmts) endBlock(p) # end of else block if i < length and t.sons[i].kind == nkFinally: - genSimpleBlock(p, t.sons[i].sons[0]) + exprBlock(p, t.sons[i].sons[0], d) linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", safePoint) proc genAsmOrEmitStmt(p: BProc, t: PNode): PRope = @@ -862,61 +872,5 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) = proc genStmts(p: BProc, t: PNode) = var a: TLoc - case t.kind - of nkEmpty: - nil - of nkStmtList: - for i in countup(0, sonsLen(t) - 1): genStmts(p, t.sons[i]) - of nkBlockStmt: genBlock(p, t, a) - of nkIfStmt: genIfStmt(p, t) - of nkWhileStmt: genWhileStmt(p, t) - of nkVarSection, nkLetSection: genVarStmt(p, t) - of nkConstSection: genConstStmt(p, t) - of nkForStmt: internalError(t.info, "for statement not eliminated") - of nkCaseStmt: genCaseStmt(p, t) - of nkReturnStmt: genReturnStmt(p, t) - of nkBreakStmt: genBreakStmt(p, t) - of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand, - nkCallStrLit, nkClosure: - genLineDir(p, t) - initLocExpr(p, t, a) - of nkAsgn: genAsgn(p, t, fastAsgn=false) - of nkFastAsgn: - # transf is overly aggressive with 'nkFastAsgn', so we work around here. - # See tests/run/tcnstseq3 for an example that would fail otherwise. - genAsgn(p, t, fastAsgn=p.prc != nil) - of nkDiscardStmt: - if t.sons[0].kind != nkEmpty: - genLineDir(p, t) - initLocExpr(p, t.sons[0], a) - of nkAsmStmt: genAsmStmt(p, t) - of nkTryStmt: - if gCmd == cmdCompileToCpp: genTryStmtCpp(p, t) - else: genTryStmt(p, t) - of nkRaiseStmt: genRaiseStmt(p, t) - of nkTypeSection: - # we have to emit the type information for object types here to support - # separate compilation: - genTypeSection(p.module, t) - of nkCommentStmt, nkNilLit, nkIteratorDef, nkIncludeStmt, - nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, - nkFromStmt, nkTemplateDef, nkMacroDef: - nil - of nkPragma: genPragma(p, t) - of nkProcDef, nkMethodDef, nkConverterDef: - if (t.sons[genericParamsPos].kind == nkEmpty): - var prc = t.sons[namePos].sym - if (optDeadCodeElim notin gGlobalOptions and - sfDeadCodeElim notin getModule(prc).flags) or - ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or - (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or - (prc.kind == skMethod): - # we have not only the header: - if prc.getBody.kind != nkEmpty or lfDynamicLib in prc.loc.flags: - genProc(p.module, prc) - of nkParForStmt: genParForStmt(p, t) - of nkState: genState(p, t) - of nkGotoState: genGotoState(p, t) - of nkBreakState: genBreakState(p, t) - else: internalError(t.info, "genStmts(" & $t.kind & ')') - + expr(p, t, a) + InternalAssert a.k notin {locNone, locTemp} diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 753576aa0..cb796e456 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -552,9 +552,21 @@ proc genVarPrototype(m: BModule, sym: PSym) proc requestConstImpl(p: BProc, sym: PSym) proc genProc(m: BModule, prc: PSym) proc genStmts(p: BProc, t: PNode) +proc expr(p: BProc, n: PNode, d: var TLoc) proc genProcPrototype(m: BModule, sym: PSym) +proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc) +proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) +proc intLiteral(i: biggestInt): PRope +proc genLiteral(p: BProc, n: PNode): PRope -include "ccgexprs.nim", "ccgstmts.nim" +proc initLocExpr(p: BProc, e: PNode, result: var TLoc) = + initLoc(result, locNone, e.typ, OnUnknown) + expr(p, e, result) + +proc lenField: PRope {.inline.} = + result = toRope(if gCmd != cmdCompileToCpp: "Sup.len" else: "len") + +include ccgcalls, "ccgstmts.nim", "ccgexprs.nim" # ----------------------------- dynamic library handling ----------------- # We don't finalize dynamic libs as this does the OS for us. diff --git a/compiler/parser.nim b/compiler/parser.nim index 4a3377681..e2167f460 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -913,15 +913,15 @@ proc parseTypeDescKAux(p: var TParser, kind: TNodeKind, proc parseExpr(p: var TParser): PNode = #| expr = (ifExpr #| | whenExpr - #| | caseExpr) + #| | caseExpr + #| | tryStmt) #| / simpleExpr case p.tok.tokType: of tkIf: result = parseIfExpr(p, nkIfExpr) of tkWhen: result = parseIfExpr(p, nkWhenExpr) of tkCase: result = parseCase(p) + of tkTry: result = parseTry(p) else: result = simpleExpr(p) - # XXX needs proper support: - #of tkTry: result = parseTry(p) proc parseObject(p: var TParser): PNode proc parseDistinct(p: var TParser): PNode diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 1333e40c4..d68d2e549 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -939,10 +939,9 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = gsub(g, n.sons[0]) put(g, tkDotDot, "..") gsub(g, n.sons[1]) - of nkDerefExpr: + of nkDerefExpr: gsub(g, n.sons[0]) - putWithSpace(g, tkOpr, "^") - # unfortunately this requires a space, because ^. would be only one opr + put(g, tkOpr, "[]") of nkAccQuoted: put(g, tkAccent, "`") if n.len > 0: gsub(g, n.sons[0]) diff --git a/compiler/sem.nim b/compiler/sem.nim index 791ca2793..cd332ad90 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -68,8 +68,10 @@ proc commonType*(x, y: PType): PType = var a = skipTypes(x, {tyGenericInst}) var b = skipTypes(y, {tyGenericInst}) result = x - if a.kind in {tyExpr, tyNil}: return y - elif b.kind in {tyExpr, tyNil}: return x + if a.kind in {tyExpr, tyNil}: result = y + elif b.kind in {tyExpr, tyNil}: result = x + elif a.kind == tyStmt: result = a + elif b.kind == tyStmt: result = b elif b.kind in {tyArray, tyArrayConstr, tySet, tySequence} and a.kind == b.kind: # check for seq[empty] vs. seq[int] diff --git a/compiler/semdestruct.nim b/compiler/semdestruct.nim new file mode 100644 index 000000000..2383ac649 --- /dev/null +++ b/compiler/semdestruct.nim @@ -0,0 +1,213 @@ +# +# +# The Nimrod Compiler +# (c) Copyright 2013 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements destructors. + + +# special marker values that indicates that we are +# 1) AnalyzingDestructor: currently analyzing the type for destructor +# generation (needed for recursive types) +# 2) DestructorIsTrivial: completed the analysis before and determined +# that the type has a trivial destructor +var AnalyzingDestructor, DestructorIsTrivial: PSym +new(AnalyzingDestructor) +new(DestructorIsTrivial) + +var + destructorName = getIdent"destroy_" + destructorParam = getIdent"this_" + destructorPragma = newIdentNode(getIdent"destructor", UnknownLineInfo()) + rangeDestructorProc*: PSym + +proc instantiateDestructor(c: PContext, typ: PType): bool + +proc doDestructorStuff(c: PContext, s: PSym, n: PNode) = + let t = s.typ.sons[1].skipTypes({tyVar}) + t.destructor = s + # automatically insert calls to base classes' destructors + if n.sons[bodyPos].kind != nkEmpty: + for i in countup(0, t.sonsLen - 1): + # when inheriting directly from object + # there will be a single nil son + if t.sons[i] == nil: continue + if instantiateDestructor(c, t.sons[i]): + n.sons[bodyPos].addSon(newNode(nkCall, t.sym.info, @[ + useSym(t.sons[i].destructor), + n.sons[paramsPos][1][0]])) + +proc destroyField(c: PContext, field: PSym, holder: PNode): PNode = + if instantiateDestructor(c, field.typ): + result = newNode(nkCall, field.info, @[ + useSym(field.typ.destructor), + newNode(nkDotExpr, field.info, @[holder, useSym(field)])]) + +proc destroyCase(c: PContext, n: PNode, holder: PNode): PNode = + var nonTrivialFields = 0 + result = newNode(nkCaseStmt, n.info, @[]) + # case x.kind + result.addSon(newNode(nkDotExpr, n.info, @[holder, n.sons[0]])) + for i in countup(1, n.len - 1): + # of A, B: + var caseBranch = newNode(n[i].kind, n[i].info, n[i].sons[0 .. -2]) + let recList = n[i].lastSon + var destroyRecList = newNode(nkStmtList, n[i].info, @[]) + template addField(f: expr): stmt = + let stmt = destroyField(c, f, holder) + if stmt != nil: + destroyRecList.addSon(stmt) + inc nonTrivialFields + + case recList.kind + of nkSym: + addField(recList.sym) + of nkRecList: + for j in countup(0, recList.len - 1): + addField(recList[j].sym) + else: + internalAssert false + + caseBranch.addSon(destroyRecList) + result.addSon(caseBranch) + # maybe no fields were destroyed? + if nonTrivialFields == 0: + result = nil + +proc generateDestructor(c: PContext, t: PType): PNode = + ## generate a destructor for a user-defined object or tuple type + ## returns nil if the destructor turns out to be trivial + + template addLine(e: expr): stmt = + if result == nil: result = newNode(nkStmtList) + result.addSon(e) + + # XXX: This may be true for some C-imported types such as + # Tposix_spawnattr + if t.n == nil or t.n.sons == nil: return + internalAssert t.n.kind == nkRecList + let destructedObj = newIdentNode(destructorParam, UnknownLineInfo()) + # call the destructods of all fields + for s in countup(0, t.n.sons.len - 1): + case t.n.sons[s].kind + of nkRecCase: + let stmt = destroyCase(c, t.n.sons[s], destructedObj) + if stmt != nil: addLine(stmt) + of nkSym: + let stmt = destroyField(c, t.n.sons[s].sym, destructedObj) + if stmt != nil: addLine(stmt) + else: + internalAssert false + # base classes' destructors will be automatically called by + # semProcAux for both auto-generated and user-defined destructors + +proc instantiateDestructor(c: PContext, typ: PType): bool = + # returns true if the type already had a user-defined + # destructor or if the compiler generated a default + # member-wise one + var t = skipTypes(typ, {tyConst, tyMutable}) + + if t.destructor != nil: + # XXX: This is not entirely correct for recursive types, but we need + # it temporarily to hide the "destroy is already defined" problem + return t.destructor notin [AnalyzingDestructor, DestructorIsTrivial] + + case t.kind + of tySequence, tyArray, tyArrayConstr, tyOpenArray, tyVarargs: + if instantiateDestructor(c, t.sons[0]): + if rangeDestructorProc == nil: + rangeDestructorProc = SymtabGet(c.tab, getIdent"nimDestroyRange") + t.destructor = rangeDestructorProc + return true + else: + return false + of tyTuple, tyObject: + t.destructor = AnalyzingDestructor + let generated = generateDestructor(c, t) + if generated != nil: + internalAssert t.sym != nil + var i = t.sym.info + let fullDef = newNode(nkProcDef, i, @[ + newIdentNode(destructorName, i), + emptyNode, + emptyNode, + newNode(nkFormalParams, i, @[ + emptyNode, + newNode(nkIdentDefs, i, @[ + newIdentNode(destructorParam, i), + useSym(t.sym), + emptyNode]), + ]), + newNode(nkPragma, i, @[destructorPragma]), + emptyNode, + generated + ]) + discard semProc(c, fullDef) + internalAssert t.destructor != nil + return true + else: + t.destructor = DestructorIsTrivial + return false + else: + return false + +proc insertDestructors(c: PContext, + varSection: PNode): tuple[outer, inner: PNode] = + # Accepts a var or let section. + # + # When a var section has variables with destructors + # the var section is split up and finally blocks are inserted + # immediately after all "destructable" vars + # + # In case there were no destrucable variables, the proc returns + # (nil, nil) and the enclosing stmt-list requires no modifications. + # + # Otherwise, after the try blocks are created, the rest of the enclosing + # stmt-list should be inserted in the most `inner` such block (corresponding + # to the last variable). + # + # `outer` is a statement list that should replace the original var section. + # It will include the new truncated var section followed by the outermost + # try block. + let totalVars = varSection.sonsLen + for j in countup(0, totalVars - 1): + let + varId = varSection[j][0] + varTyp = varId.sym.typ + info = varId.info + + if varTyp != nil and instantiateDestructor(c, varTyp) and + sfGlobal notin varId.sym.flags: + var tryStmt = newNodeI(nkTryStmt, info) + + if j < totalVars - 1: + var remainingVars = newNodeI(varSection.kind, info) + remainingVars.sons = varSection.sons[(j+1)..(-1)] + let (outer, inner) = insertDestructors(c, remainingVars) + if outer != nil: + tryStmt.addSon(outer) + result.inner = inner + else: + result.inner = newNodeI(nkStmtList, info) + result.inner.addSon(remainingVars) + tryStmt.addSon(result.inner) + else: + result.inner = newNodeI(nkStmtList, info) + tryStmt.addSon(result.inner) + + tryStmt.addSon( + newNode(nkFinally, info, @[ + semStmt(c, newNode(nkCall, info, @[ + useSym(varTyp.destructor), + useSym(varId.sym)]))])) + + result.outer = newNodeI(nkStmtList, info) + varSection.sons.setLen(j+1) + result.outer.addSon(varSection) + result.outer.addSon(tryStmt) + + return diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index e2a3c0978..cba4ed081 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -19,24 +19,6 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym, semCheck = true): PNode = proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode -proc performProcvarCheck(c: PContext, n: PNode, s: PSym) = - var smoduleId = getModule(s).id - if sfProcVar notin s.flags and s.typ.callConv == ccDefault and - smoduleId != c.module.id and smoduleId != c.friendModule.id: - LocalError(n.info, errXCannotBePassedToProcVar, s.name.s) - -proc semProcvarCheck(c: PContext, n: PNode) = - let n = n.skipConv - if n.kind == nkSym and n.sym.kind in {skProc, skMethod, skIterator, - skConverter}: - performProcvarCheck(c, n, n.sym) - -proc semDestructorCheck(c: PContext, n: PNode, flags: TExprFlags) {.inline.} = - if efAllowDestructor notin flags and n.kind in nkCallKinds+{nkObjConstr}: - if instantiateDestructor(c, n.typ): - LocalError(n.info, errGenerated, - "usage of a type with a destructor in a non destructible context") - proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # same as 'semExprWithType' but doesn't check for proc vars result = semExpr(c, n, flags) @@ -781,16 +763,6 @@ proc buildEchoStmt(c: PContext, n: PNode): PNode = # and check 'arg' for semantics again: addSon(result, semExpr(c, arg)) -proc discardCheck(result: PNode) = - if result.typ != nil and result.typ.kind notin {tyStmt, tyEmpty}: - if result.kind == nkNilLit: - # XXX too much work and fixing would break bootstrapping: - #Message(n.info, warnNilStatement) - result.typ = nil - elif not ImplicitlyDiscardable(result) and result.typ.kind != tyError and - gCmd != cmdInteractive: - localError(result.info, errDiscardValue) - proc semExprNoType(c: PContext, n: PNode): PNode = result = semExpr(c, n, {efWantStmt}) discardCheck(result) @@ -1125,14 +1097,16 @@ proc semAsgn(c: PContext, n: PNode): PNode = var rhs = semExprWithType(c, n.sons[1], if lhsIsResult: {efAllowDestructor} else: {}) - if lhsIsResult and lhs.sym.typ.kind == tyGenericParam: - if matchTypeClass(lhs.typ, rhs.typ): - InternalAssert c.p.resultSym != nil - lhs.typ = rhs.typ - c.p.resultSym.typ = rhs.typ - c.p.owner.typ.sons[0] = rhs.typ - else: - typeMismatch(n, lhs.typ, rhs.typ) + if lhsIsResult: + n.typ = EnforceVoidContext + if lhs.sym.typ.kind == tyGenericParam: + if matchTypeClass(lhs.typ, rhs.typ): + InternalAssert c.p.resultSym != nil + lhs.typ = rhs.typ + c.p.resultSym.typ = rhs.typ + c.p.owner.typ.sons[0] = rhs.typ + else: + typeMismatch(n, lhs.typ, rhs.typ) n.sons[1] = fitNode(c, le, rhs) fixAbstractType(c, n) @@ -1481,52 +1455,13 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = if result == nil: setResult(it.sons[0]) else: illFormedAst(n) - if result == nil: - result = newNodeI(nkNilLit, n.info) + if result == nil: + result = newNodeI(nkEmpty, n.info) # The ``when`` statement implements the mechanism for platform dependent # code. Thus we try to ensure here consistent ID allocation after the # ``when`` statement. IDsynchronizationPoint(200) - -proc semExprBranch(c: PContext, n: PNode): PNode = - result = semExpr(c, n) - if result.typ != nil: - # XXX tyGenericInst here? - semProcvarCheck(c, result) - if result.typ.kind == tyVar: result = newDeref(result) - semDestructorCheck(c, result, {}) - -proc semIf(c: PContext, n: PNode): PNode = - result = n - var typ = CommonTypeBegin - var hasElse = false - for i in countup(0, sonsLen(n) - 1): - var it = n.sons[i] - if it.len == 2: - when newScopeForIf: openScope(c.tab) - it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0])) - when not newScopeForIf: openScope(c.tab) - it.sons[1] = semExprBranch(c, it.sons[1]) - typ = commonType(typ, it.sons[1].typ) - closeScope(c.tab) - elif it.len == 1: - hasElse = true - openScope(c.tab) - it.sons[0] = semExprBranch(c, it.sons[0]) - typ = commonType(typ, it.sons[0].typ) - closeScope(c.tab) - else: illFormedAst(it) - if isEmptyType(typ) or not hasElse: - for it in n: discardCheck(it.lastSon) - result.kind = nkIfStmt - else: - for it in n: - let j = it.len-1 - it.sons[j] = fitNode(c, typ, it.sons[j]) - result.kind = nkIfExpr - result.typ = typ - proc semSetConstr(c: PContext, n: PNode): PNode = result = newNodeI(nkCurly, n.info) result.typ = newTypeS(tySet, c) @@ -1692,26 +1627,21 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = it.sons[1] = e # XXX object field name check for 'case objects' if the kind is static? -proc semStmtListExpr(c: PContext, n: PNode): PNode = - result = n - checkMinSonsLen(n, 1) - var length = sonsLen(n) - for i in countup(0, length - 2): - n.sons[i] = semStmt(c, n.sons[i]) - if length > 0: - n.sons[length - 1] = semExprWithType(c, n.sons[length - 1]) - n.typ = n.sons[length - 1].typ - -proc semBlockExpr(c: PContext, n: PNode): PNode = +proc semBlock(c: PContext, n: PNode): PNode = result = n Inc(c.p.nestedBlockCounter) checkSonsLen(n, 2) openScope(c.tab) # BUGFIX: label is in the scope of block! - if n.sons[0].kind notin {nkEmpty, nkSym}: - # nkSym for gensym'ed labels: - addDecl(c, newSymS(skLabel, n.sons[0], c)) - n.sons[1] = semStmtListExpr(c, n.sons[1]) + if n.sons[0].kind != nkEmpty: + var labl = newSymG(skLabel, n.sons[0], c) + if sfGenSym notin labl.flags: + addDecl(c, labl) + n.sons[0] = newSymNode(labl, n.sons[0].info) + suggestSym(n.sons[0], labl) + n.sons[1] = semExpr(c, n.sons[1]) n.typ = n.sons[1].typ + if isEmptyType(n.typ): n.kind = nkBlockStmt + else: n.kind = nkBlockExpr closeScope(c.tab) Dec(c.p.nestedBlockCounter) @@ -1731,57 +1661,6 @@ proc buildCall(n: PNode): PNode = else: result = n -proc semCaseExpr(c: PContext, caseStmt: PNode): PNode = - # The case expression is simply rewritten to a StmtListExpr: - # var res {.noInit, genSym.}: type(values) - # - # case E - # of X: res = value1 - # of Y: res = value2 - # - # res - var - info = caseStmt.info - resVar = newSym(skVar, idAnon, getCurrOwner(), info) - resNode = newSymNode(resVar, info) - resType: PType - - resVar.flags = { sfGenSym, sfNoInit } - - for i in countup(1, caseStmt.len - 1): - var cs = caseStmt[i] - case cs.kind - of nkOfBranch, nkElifBranch, nkElse: - # the value is always the last son regardless of the branch kind - cs.checkMinSonsLen 1 - var value = cs{-1} - if value.kind == nkStmtList: value.kind = nkStmtListExpr - - value = semExprWithType(c, value) - if resType == nil: - resType = value.typ - elif not sameType(resType, value.typ): - # XXX: semeType is a bit too harsh. - # work on finding a common base type. - # this will be useful for arrays/seq too: - # [ref DerivedA, ref DerivedB, ref Base] - typeMismatch(cs, resType, value.typ) - - cs{-1} = newNode(nkAsgn, cs.info, @[resNode, value]) - else: - IllFormedAst(caseStmt) - - result = newNode(nkStmtListExpr, info, @[ - newNode(nkVarSection, info, @[ - newNode(nkIdentDefs, info, @[ - resNode, - symNodeFromType(c, resType, info), - emptyNode])]), - caseStmt, - resNode]) - - result = semStmtListExpr(c, result) - proc fixImmediateParams(n: PNode): PNode = # XXX: Temporary work-around until we carry out # the planned overload resolution reforms @@ -1946,7 +1825,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkObjConstr: result = semObjConstr(c, n, flags) of nkLambdaKinds: result = semLambda(c, n, flags) of nkDerefExpr: result = semDeref(c, n) - of nkAddr: + of nkAddr: result = n checkSonsLen(n, 1) n.sons[0] = semExprWithType(c, n.sons[0]) @@ -1958,8 +1837,6 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = n.sons[0] = semExpr(c, n.sons[0], flags) of nkCast: result = semCast(c, n) of nkIfExpr, nkIfStmt: result = semIf(c, n) - of nkStmtListExpr: result = semStmtListExpr(c, n) - of nkBlockExpr: result = semBlockExpr(c, n) of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkHiddenCallConv: checkSonsLen(n, 2) of nkStringToCString, nkCStringToString, nkObjDownConv, nkObjUpConv: @@ -1976,8 +1853,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkStaticExpr: result = semStaticExpr(c, n) of nkAsgn: result = semAsgn(c, n) - of nkBlockStmt: result = semBlock(c, n) - of nkStmtList: result = semStmtList(c, n) + of nkBlockStmt, nkBlockExpr: result = semBlock(c, n) + of nkStmtList, nkStmtListExpr: result = semStmtList(c, n) of nkRaiseStmt: result = semRaise(c, n) of nkVarSection: result = semVarOrLet(c, n, skVar) of nkLetSection: result = semVarOrLet(c, n, skLet) @@ -1988,9 +1865,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkTryStmt: result = semTry(c, n) of nkBreakStmt, nkContinueStmt: result = semBreakOrContinue(c, n) of nkForStmt, nkParForStmt: result = semFor(c, n) - of nkCaseStmt: - if efWantStmt in flags: result = semCase(c, n) - else: result = semCaseExpr(c, n) + of nkCaseStmt: result = semCase(c, n) of nkReturnStmt: result = semReturn(c, n) of nkAsmStmt: result = semAsm(c, n) of nkYieldStmt: result = semYield(c, n) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index e1d8ed5a8..2811c107a 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -40,21 +40,6 @@ proc semBreakOrContinue(c: PContext, n: PNode): PNode = elif (c.p.nestedLoopCounter <= 0) and (c.p.nestedBlockCounter <= 0): localError(n.info, errInvalidControlFlowX, renderTree(n, {renderNoComments})) - -proc semBlock(c: PContext, n: PNode): PNode = - result = n - Inc(c.p.nestedBlockCounter) - checkSonsLen(n, 2) - openScope(c.tab) # BUGFIX: label is in the scope of block! - if n.sons[0].kind != nkEmpty: - var labl = newSymG(skLabel, n.sons[0], c) - if sfGenSym notin labl.flags: - addDecl(c, labl) - n.sons[0] = newSymNode(labl, n.sons[0].info) - suggestSym(n.sons[0], labl) - n.sons[1] = semStmt(c, n.sons[1]) - closeScope(c.tab) - Dec(c.p.nestedBlockCounter) proc semAsm(con: PContext, n: PNode): PNode = checkSonsLen(n, 2) @@ -79,20 +64,113 @@ proc toCover(t: PType): biggestInt = else: result = lengthOrd(skipTypes(t, abstractVar-{tyTypeDesc})) -proc semCase(c: PContext, n: PNode): PNode = - # check selector: +proc performProcvarCheck(c: PContext, n: PNode, s: PSym) = + var smoduleId = getModule(s).id + if sfProcVar notin s.flags and s.typ.callConv == ccDefault and + smoduleId != c.module.id and smoduleId != c.friendModule.id: + LocalError(n.info, errXCannotBePassedToProcVar, s.name.s) + +proc semProcvarCheck(c: PContext, n: PNode) = + let n = n.skipConv + if n.kind == nkSym and n.sym.kind in {skProc, skMethod, skIterator, + skConverter}: + performProcvarCheck(c, n, n.sym) + +proc semProc(c: PContext, n: PNode): PNode + +include semdestruct + +proc semDestructorCheck(c: PContext, n: PNode, flags: TExprFlags) {.inline.} = + if efAllowDestructor notin flags and n.kind in nkCallKinds+{nkObjConstr}: + if instantiateDestructor(c, n.typ): + LocalError(n.info, errGenerated, + "usage of a type with a destructor in a non destructible context") + +proc newDeref(n: PNode): PNode {.inline.} = + result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0]) + addSon(result, n) + +proc semExprBranch(c: PContext, n: PNode): PNode = + result = semExpr(c, n) + if result.typ != nil: + # XXX tyGenericInst here? + semProcvarCheck(c, result) + if result.typ.kind == tyVar: result = newDeref(result) + semDestructorCheck(c, result, {}) + +proc semExprBranchScope(c: PContext, n: PNode): PNode = + openScope(c.tab) + result = semExprBranch(c, n) + closeScope(c.tab) + +proc ImplicitlyDiscardable(n: PNode): bool = + result = isCallExpr(n) and n.sons[0].kind == nkSym and + sfDiscardable in n.sons[0].sym.flags + +proc fixNilType(n: PNode) = + if isAtom(n): + if n.kind != nkNilLit and n.typ != nil: + localError(n.info, errDiscardValue) + else: + for it in n: fixNilType(it) + n.typ = nil + +proc discardCheck(result: PNode) = + if result.typ != nil and result.typ.kind notin {tyStmt, tyEmpty}: + if result.kind == nkNilLit: + # XXX too much work and fixing would break bootstrapping: + #Message(n.info, warnNilStatement) + result.typ = nil + elif not ImplicitlyDiscardable(result) and result.typ.kind != tyError and + gCmd != cmdInteractive: + if result.typ.kind == tyNil: + fixNilType(result) + else: + localError(result.info, errDiscardValue) + +proc semIf(c: PContext, n: PNode): PNode = + result = n + var typ = CommonTypeBegin + var hasElse = false + for i in countup(0, sonsLen(n) - 1): + var it = n.sons[i] + if it.len == 2: + when newScopeForIf: openScope(c.tab) + it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0])) + when not newScopeForIf: openScope(c.tab) + it.sons[1] = semExprBranch(c, it.sons[1]) + typ = commonType(typ, it.sons[1].typ) + closeScope(c.tab) + elif it.len == 1: + hasElse = true + it.sons[0] = semExprBranchScope(c, it.sons[0]) + typ = commonType(typ, it.sons[0].typ) + else: illFormedAst(it) + if isEmptyType(typ) or not hasElse: + for it in n: discardCheck(it.lastSon) + result.kind = nkIfStmt + else: + for it in n: + let j = it.len-1 + it.sons[j] = fitNode(c, typ, it.sons[j]) + result.kind = nkIfExpr + result.typ = typ + +proc semCase(c: PContext, n: PNode): PNode = result = n checkMinSonsLen(n, 2) openScope(c.tab) n.sons[0] = semExprWithType(c, n.sons[0]) var chckCovered = false var covered: biggestint = 0 + var typ = CommonTypeBegin + var hasElse = false case skipTypes(n.sons[0].Typ, abstractVarRange-{tyTypeDesc}).Kind - of tyInt..tyInt64, tyChar, tyEnum: + of tyInt..tyInt64, tyChar, tyEnum: chckCovered = true - of tyFloat..tyFloat128, tyString, tyError: + of tyFloat..tyFloat128, tyString, tyError: nil - else: + else: LocalError(n.info, errSelectorMustBeOfCertainTypes) return for i in countup(1, sonsLen(n) - 1): @@ -101,21 +179,81 @@ proc semCase(c: PContext, n: PNode): PNode = of nkOfBranch: checkMinSonsLen(x, 2) semCaseBranch(c, n, x, i, covered) - var length = sonsLen(x) - x.sons[length - 1] = semStmtScope(c, x.sons[length - 1]) - of nkElifBranch: + var last = sonsLen(x)-1 + x.sons[last] = semExprBranchScope(c, x.sons[last]) + typ = commonType(typ, x.sons[last].typ) + of nkElifBranch: chckCovered = false checkSonsLen(x, 2) + when newScopeForIf: openScope(c.tab) x.sons[0] = forceBool(c, semExprWithType(c, x.sons[0])) - x.sons[1] = semStmtScope(c, x.sons[1]) - of nkElse: + when not newScopeForIf: openScope(c.tab) + x.sons[1] = semExprBranch(c, x.sons[1]) + typ = commonType(typ, x.sons[1].typ) + closeScope(c.tab) + of nkElse: chckCovered = false checkSonsLen(x, 1) - x.sons[0] = semStmtScope(c, x.sons[0]) - else: illFormedAst(x) + x.sons[0] = semExprBranchScope(c, x.sons[0]) + typ = commonType(typ, x.sons[0].typ) + hasElse = true + else: + illFormedAst(x) if chckCovered and (covered != toCover(n.sons[0].typ)): localError(n.info, errNotAllCasesCovered) closeScope(c.tab) + if isEmptyType(typ) or not hasElse: + for i in 1..n.len-1: discardCheck(n.sons[i].lastSon) + else: + for i in 1..n.len-1: + var it = n.sons[i] + let j = it.len-1 + it.sons[j] = fitNode(c, typ, it.sons[j]) + result.typ = typ + +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() + for i in countup(1, sonsLen(n) - 1): + var a = n.sons[i] + checkMinSonsLen(a, 1) + var length = sonsLen(a) + if a.kind == nkExceptBranch: + # XXX what does this do? 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 + + for j in countup(0, length-2): + var typ = semTypeNode(c, a.sons[j], nil) + if typ.kind == tyRef: typ = typ.sons[0] + if typ.kind != tyObject: + LocalError(a.sons[j].info, errExprCannotBeRaised) + a.sons[j] = newNodeI(nkType, a.sons[j].info) + a.sons[j].typ = typ + 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]) + typ = commonType(typ, a.sons[length-1].typ) + dec c.p.inTryStmt + if isEmptyType(typ): + discardCheck(n.sons[0]) + for i in 1..n.len-1: discardCheck(n.sons[i].lastSon) + else: + n.sons[0] = fitNode(c, typ, n.sons[0]) + for i in 1..n.len-1: + var it = n.sons[i] + let j = it.len-1 + it.sons[j] = fitNode(c, typ, it.sons[j]) + result.typ = typ proc fitRemoveHiddenConv(c: PContext, typ: Ptype, n: PNode): PNode = result = fitNode(c, typ, n) @@ -438,10 +576,6 @@ proc semForVars(c: PContext, n: PNode): PNode = n.sons[length-1] = SemStmt(c, n.sons[length-1]) Dec(c.p.nestedLoopCounter) -proc newDeref(n: PNode): PNode {.inline.} = - result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0]) - addSon(result, n) - proc implicitIterator(c: PContext, it: string, arg: PNode): PNode = result = newNodeI(nkCall, arg.info) result.add(newIdentNode(it.getIdent, arg.info)) @@ -489,36 +623,6 @@ proc semRaise(c: PContext, n: PNode): PNode = if typ.kind != tyRef or typ.sons[0].kind != tyObject: localError(n.info, errExprCannotBeRaised) -proc semTry(c: PContext, n: PNode): PNode = - result = n - inc c.p.inTryStmt - checkMinSonsLen(n, 2) - n.sons[0] = semStmtScope(c, n.sons[0]) - var check = initIntSet() - for i in countup(1, sonsLen(n) - 1): - var a = n.sons[i] - checkMinSonsLen(a, 1) - var length = sonsLen(a) - if a.kind == nkExceptBranch: - if length == 2 and a.sons[0].kind == nkBracket: - a.sons[0..0] = a.sons[0].sons - length = a.sonsLen - - for j in countup(0, length - 2): - var typ = semTypeNode(c, a.sons[j], nil) - if typ.kind == tyRef: typ = typ.sons[0] - if typ.kind != tyObject: - LocalError(a.sons[j].info, errExprCannotBeRaised) - a.sons[j] = newNodeI(nkType, a.sons[j].info) - a.sons[j].typ = typ - 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] = semStmtScope(c, a.sons[length - 1]) - dec c.p.inTryStmt - proc addGenericParamListToScope(c: PContext, n: PNode) = if n.kind != nkGenericParams: illFormedAst(n) for i in countup(0, sonsLen(n)-1): @@ -746,22 +850,6 @@ proc activate(c: PContext, n: PNode) = else: nil -proc instantiateDestructor*(c: PContext, typ: PType): bool - -proc doDestructorStuff(c: PContext, s: PSym, n: PNode) = - let t = s.typ.sons[1].skipTypes({tyVar}) - t.destructor = s - # automatically insert calls to base classes' destructors - if n.sons[bodyPos].kind != nkEmpty: - for i in countup(0, t.sonsLen - 1): - # when inheriting directly from object - # there will be a single nil son - if t.sons[i] == nil: continue - if instantiateDestructor(c, t.sons[i]): - n.sons[bodyPos].addSon(newNode(nkCall, t.sym.info, @[ - useSym(t.sons[i].destructor), - n.sons[paramsPos][1][0]])) - proc maybeAddResult(c: PContext, s: PSym, n: PNode) = if s.typ.sons[0] != nil and (s.kind != skIterator or s.typ.callConv == ccClosure): @@ -972,196 +1060,7 @@ proc semStaticStmt(c: PContext, n: PNode): PNode = result = newNodeI(nkDiscardStmt, n.info, 1) result.sons[0] = emptyNode -# special marker values that indicates that we are -# 1) AnalyzingDestructor: currently analyzing the type for destructor -# generation (needed for recursive types) -# 2) DestructorIsTrivial: completed the analysis before and determined -# that the type has a trivial destructor -var AnalyzingDestructor, DestructorIsTrivial: PSym -new(AnalyzingDestructor) -new(DestructorIsTrivial) - -var - destructorName = getIdent"destroy_" - destructorParam = getIdent"this_" - destructorPragma = newIdentNode(getIdent"destructor", UnknownLineInfo()) - rangeDestructorProc*: PSym - -proc destroyField(c: PContext, field: PSym, holder: PNode): PNode = - if instantiateDestructor(c, field.typ): - result = newNode(nkCall, field.info, @[ - useSym(field.typ.destructor), - newNode(nkDotExpr, field.info, @[holder, useSym(field)])]) - -proc destroyCase(c: PContext, n: PNode, holder: PNode): PNode = - var nonTrivialFields = 0 - result = newNode(nkCaseStmt, n.info, @[]) - # case x.kind - result.addSon(newNode(nkDotExpr, n.info, @[holder, n.sons[0]])) - for i in countup(1, n.len - 1): - # of A, B: - var caseBranch = newNode(n[i].kind, n[i].info, n[i].sons[0 .. -2]) - let recList = n[i].lastSon - var destroyRecList = newNode(nkStmtList, n[i].info, @[]) - template addField(f: expr): stmt = - let stmt = destroyField(c, f, holder) - if stmt != nil: - destroyRecList.addSon(stmt) - inc nonTrivialFields - - case recList.kind - of nkSym: - addField(recList.sym) - of nkRecList: - for j in countup(0, recList.len - 1): - addField(recList[j].sym) - else: - internalAssert false - - caseBranch.addSon(destroyRecList) - result.addSon(caseBranch) - # maybe no fields were destroyed? - if nonTrivialFields == 0: - result = nil - -proc generateDestructor(c: PContext, t: PType): PNode = - ## generate a destructor for a user-defined object or tuple type - ## returns nil if the destructor turns out to be trivial - - template addLine(e: expr): stmt = - if result == nil: result = newNode(nkStmtList) - result.addSon(e) - - # XXX: This may be true for some C-imported types such as - # Tposix_spawnattr - if t.n == nil or t.n.sons == nil: return - internalAssert t.n.kind == nkRecList - let destructedObj = newIdentNode(destructorParam, UnknownLineInfo()) - # call the destructods of all fields - for s in countup(0, t.n.sons.len - 1): - case t.n.sons[s].kind - of nkRecCase: - let stmt = destroyCase(c, t.n.sons[s], destructedObj) - if stmt != nil: addLine(stmt) - of nkSym: - let stmt = destroyField(c, t.n.sons[s].sym, destructedObj) - if stmt != nil: addLine(stmt) - else: - internalAssert false - - # base classes' destructors will be automatically called by - # semProcAux for both auto-generated and user-defined destructors - -proc instantiateDestructor*(c: PContext, typ: PType): bool = - # returns true if the type already had a user-defined - # destructor or if the compiler generated a default - # member-wise one - var t = skipTypes(typ, {tyConst, tyMutable}) - - if t.destructor != nil: - # XXX: This is not entirely correct for recursive types, but we need - # it temporarily to hide the "destroy is already defined" problem - return t.destructor notin [AnalyzingDestructor, DestructorIsTrivial] - - case t.kind - of tySequence, tyArray, tyArrayConstr, tyOpenArray, tyVarargs: - if instantiateDestructor(c, t.sons[0]): - if rangeDestructorProc == nil: - rangeDestructorProc = SymtabGet(c.tab, getIdent"nimDestroyRange") - t.destructor = rangeDestructorProc - return true - else: - return false - of tyTuple, tyObject: - t.destructor = AnalyzingDestructor - let generated = generateDestructor(c, t) - if generated != nil: - internalAssert t.sym != nil - var i = t.sym.info - let fullDef = newNode(nkProcDef, i, @[ - newIdentNode(destructorName, i), - emptyNode, - emptyNode, - newNode(nkFormalParams, i, @[ - emptyNode, - newNode(nkIdentDefs, i, @[ - newIdentNode(destructorParam, i), - useSym(t.sym), - emptyNode]), - ]), - newNode(nkPragma, i, @[destructorPragma]), - emptyNode, - generated - ]) - discard semProc(c, fullDef) - internalAssert t.destructor != nil - return true - else: - t.destructor = DestructorIsTrivial - return false - else: - return false - -proc insertDestructors(c: PContext, - varSection: PNode): tuple[outer, inner: PNode] = - # Accepts a var or let section. - # - # When a var section has variables with destructors - # the var section is split up and finally blocks are inserted - # immediately after all "destructable" vars - # - # In case there were no destrucable variables, the proc returns - # (nil, nil) and the enclosing stmt-list requires no modifications. - # - # Otherwise, after the try blocks are created, the rest of the enclosing - # stmt-list should be inserted in the most `inner` such block (corresponding - # to the last variable). - # - # `outer` is a statement list that should replace the original var section. - # It will include the new truncated var section followed by the outermost - # try block. - let totalVars = varSection.sonsLen - for j in countup(0, totalVars - 1): - let - varId = varSection[j][0] - varTyp = varId.sym.typ - info = varId.info - - if varTyp != nil and instantiateDestructor(c, varTyp) and - sfGlobal notin varId.sym.flags: - var tryStmt = newNodeI(nkTryStmt, info) - - if j < totalVars - 1: - var remainingVars = newNodeI(varSection.kind, info) - remainingVars.sons = varSection.sons[(j+1)..(-1)] - let (outer, inner) = insertDestructors(c, remainingVars) - if outer != nil: - tryStmt.addSon(outer) - result.inner = inner - else: - result.inner = newNodeI(nkStmtList, info) - result.inner.addSon(remainingVars) - tryStmt.addSon(result.inner) - else: - result.inner = newNodeI(nkStmtList, info) - tryStmt.addSon(result.inner) - - tryStmt.addSon( - newNode(nkFinally, info, @[ - semStmt(c, newNode(nkCall, info, @[ - useSym(varTyp.destructor), - useSym(varId.sym)]))])) - - result.outer = newNodeI(nkStmtList, info) - varSection.sons.setLen(j+1) - result.outer.addSon(varSection) - result.outer.addSon(tryStmt) - - return - -proc ImplicitlyDiscardable(n: PNode): bool = - result = isCallExpr(n) and n.sons[0].kind == nkSym and - sfDiscardable in n.sons[0].sym.flags +var EnforceVoidContext = PType(kind: tyStmt) proc semStmtList(c: PContext, n: PNode): PNode = # these must be last statements in a block: @@ -1169,6 +1068,11 @@ proc semStmtList(c: PContext, n: PNode): PNode = LastBlockStmts = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt} result = n var length = sonsLen(n) + var voidContext = false + var last = length-1 + while last > 0 and n.sons[last].kind in {nkPragma, nkCommentStmt, + nkNilLit, nkEmpty}: + dec last for i in countup(0, length - 1): case n.sons[i].kind of nkFinally, nkExceptBranch: @@ -1190,7 +1094,16 @@ proc semStmtList(c: PContext, n: PNode): PNode = n.sons.setLen(i+1) return else: - n.sons[i] = semStmt(c, n.sons[i]) + n.sons[i] = semExpr(c, n.sons[i]) + if n.sons[i].typ == EnforceVoidContext: + voidContext = true + n.typ = EnforceVoidContext + elif i != last or voidContext: + discardCheck(n.sons[i]) + else: + n.typ = n.sons[i].typ + if not isEmptyType(n.typ): + n.kind = nkStmtListExpr case n.sons[i].kind of nkVarSection, nkLetSection: let (outer, inner) = insertDestructors(c, n.sons[i]) @@ -1206,15 +1119,17 @@ proc semStmtList(c: PContext, n: PNode): PNode = of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: nil else: localError(n.sons[j].info, errStmtInvalidAfterReturn) else: nil - - # a statement list (s; e) has the type 'e': - if result.kind == nkStmtList and result.len > 0: - var lastStmt = lastSon(result) - if lastStmt.kind != nkNilLit and not ImplicitlyDiscardable(lastStmt): - result.typ = lastStmt.typ - #localError(lastStmt.info, errGenerated, - # "Last expression must be explicitly returned if it " & - # "is discardable or discarded") + if result.len == 1: + result = result.sons[0] + when false: + # a statement list (s; e) has the type 'e': + if result.kind == nkStmtList and result.len > 0: + var lastStmt = lastSon(result) + if lastStmt.kind != nkNilLit and not ImplicitlyDiscardable(lastStmt): + result.typ = lastStmt.typ + #localError(lastStmt.info, errGenerated, + # "Last expression must be explicitly returned if it " & + # "is discardable or discarded") proc SemStmt(c: PContext, n: PNode): PNode = # now: simply an alias: diff --git a/todo.txt b/todo.txt index 8d9e8e5a8..4aa0527a3 100644 --- a/todo.txt +++ b/todo.txt @@ -8,11 +8,6 @@ version 0.9.2 - CGEN: ``restrict`` pragma + backend support; computed goto support - document NimMain and check whether it works for threading - make use of commonType relation in expressions -- further expr/stmt unification: - - merge nkStmtListExpr and nkStmtList - - merge nkBlockExpr and nkBlockStmt - - rewrite nkCaseExpr handling - - try except as an expression Bugs ==== |