# # # The Nim Compiler # (c) Copyright 2015 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # # # included from cgen.nim proc canRaiseDisp(p: BProc; n: PNode): bool = # we assume things like sysFatal cannot raise themselves if n.kind == nkSym and {sfNeverRaises, sfImportc, sfCompilerProc} * n.sym.flags != {}: result = false elif optPanics in p.config.globalOptions or (n.kind == nkSym and sfSystemModule in getModule(n.sym).flags and sfSystemRaisesDefect notin n.sym.flags): # we know we can be strict: result = canRaise(n) else: # we have to be *very* conservative: result = canRaiseConservative(n) proc preventNrvo(p: BProc; dest, le, ri: PNode): bool = proc locationEscapes(p: BProc; le: PNode; inTryStmt: bool): bool = result = false var n = le while true: # do NOT follow nkHiddenDeref here! case n.kind of nkSym: # we don't own the location so it escapes: if n.sym.owner != p.prc: return true elif inTryStmt and sfUsedInFinallyOrExcept in n.sym.flags: # it is also an observable store if the location is used # in 'except' or 'finally' return true return false of nkDotExpr, nkBracketExpr, nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: n = n[0] of nkHiddenStdConv, nkHiddenSubConv, nkConv: n = n[1] else: # cannot analyse the location; assume the worst return true result = false if le != nil: for i in 1.. 0): message(p.config, le.info, warnObservableStores, $le) # bug #19613 prevent dangerous aliasing too: if dest != nil and dest != le: for i in 1.. 0: skipped = true q = q.lastSon if getMagic(q) == mSlice: # magic: pass slice to openArray: if skipped: q = skipConv(n) while q.kind == nkStmtListExpr and q.len > 0: for i in 0.. and otherwise . but this only depends on the argument and not on how the 'this' was declared however how the 'this' was declared affects whether we end up with wrong 'addr' and '[]' ops... Since I'm tired I'll enumerate all the cases here: var x: ptr T y: T proc t(x: T) x[].t() --> (*x).t() is correct. y.t() --> y.t() is correct proc u(x: ptr T) x.u() --> needs to become x->u() (addr y).u() --> needs to become y.u() proc v(x: var T) --> first skip the implicit 'nkAddr' node x[].v() --> (*x).v() is correct, but might have been eliminated due to the nkAddr node! So for this case we need to generate '->' y.v() --> y.v() is correct """ proc skipAddrDeref(node: PNode): PNode = var n = node var isAddr = false case n.kind of nkAddr, nkHiddenAddr: n = n[0] isAddr = true of nkDerefExpr, nkHiddenDeref: n = n[0] else: return n if n.kind == nkObjDownConv: n = n[0] if isAddr and n.kind in {nkDerefExpr, nkHiddenDeref}: result = n[0] elif n.kind in {nkAddr, nkHiddenAddr}: result = n[0] else: result = node proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Builder) = # for better or worse c2nim translates the 'this' argument to a 'var T'. # However manual wrappers may also use 'ptr T'. In any case we support both # for convenience. internalAssert p.config, i < typ.n.len assert(typ.n[i].kind == nkSym) # if the parameter is lying (tyVar) and thus we required an additional deref, # skip the deref: var ri = ri[i] while ri.kind == nkObjDownConv: ri = ri[0] let t = typ[i].skipTypes({tyGenericInst, tyAlias, tySink}) if t.kind in {tyVar}: let x = if ri.kind == nkHiddenAddr: ri[0] else: ri if x.typ.kind == tyPtr: genArgNoParam(p, x, result) result.add("->") elif x.kind in {nkHiddenDeref, nkDerefExpr} and x[0].typ.kind == tyPtr: genArgNoParam(p, x[0], result) result.add("->") else: genArgNoParam(p, x, result) result.add(".") elif t.kind == tyPtr: if ri.kind in {nkAddr, nkHiddenAddr}: genArgNoParam(p, ri[0], result) result.add(".") else: genArgNoParam(p, ri, result) result.add("->") else: ri = skipAddrDeref(ri) if ri.kind in {nkAddr, nkHiddenAddr}: ri = ri[0] genArgNoParam(p, ri, result) #, typ.n[i].sym) result.add(".") proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Builder) = var i = 0 var j = 1 while i < pat.len: case pat[i] of '@': var callBuilder = default(CallBuilder) # not init call builder for k in j..= start: result.add(substr(pat, start, i - 1)) proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) = var op = initLocExpr(p, ri[0]) # getUniqueType() is too expensive here: var typ = skipTypes(ri[0].typ, abstractInst) assert(typ.kind == tyProc) # don't call '$' here for efficiency: let pat = $ri[0].sym.loc.snippet internalAssert p.config, pat.len > 0 if pat.contains({'#', '(', '@', '\''}): var pl = newBuilder("") genPatternCall(p, ri, pat, typ, pl) # simpler version of 'fixupCall' that works with the pl+params combination: var typ = skipTypes(ri[0].typ, abstractInst) if typ.returnType != nil: if p.module.compileToCpp and lfSingleUse in d.flags: # do not generate spurious temporaries for C++! For C we're better off # with them to prevent undefined behaviour and because the codegen # is free to emit expressions multiple times! d.k = locCall d.snippet = extract(pl) excl d.flags, lfSingleUse else: if d.k == locNone: d = getTemp(p, typ.returnType) assert(d.t != nil) # generate an assignment to d: var list: TLoc = initLoc(locCall, d.lode, OnUnknown) list.snippet = extract(pl) genAssignment(p, d, list, {}) # no need for deep copying else: p.s(cpsStmts).addStmt(): p.s(cpsStmts).add(extract(pl)) else: var pl = newBuilder("") if 1 < ri.len: genThisArg(p, ri, 1, typ, pl) pl.add(op.snippet) var res = newBuilder("") var call = initCallBuilder(res, extract(pl)) for i in 2.. 0 var start = 3 if ' ' in pat: start = 1 pl.add(op.snippet) if ri.len > 1: pl.add(": ") genArg(p, ri[1], typ.n[1].sym, ri, pl) start = 2 else: if ri.len > 1: genArg(p, ri[1], typ.n[1].sym, ri, pl) pl.add(" ") pl.add(op.snippet) if ri.len > 2: pl.add(": ") genArg(p, ri[2], typ.n[2].sym, ri, pl) for i in start..= typ.n.len: internalError(p.config, ri.info, "varargs for objective C method?") assert(typ.n[i].kind == nkSym) var param = typ.n[i].sym pl.add(" ") pl.add(param.name.s) pl.add(": ") genArg(p, ri[i], param, ri, pl) if typ.returnType != nil: if isInvalidReturnType(p.config, typ): if ri.len > 1: pl.add(" ") # beware of 'result = p(result)'. We always allocate a temporary: if d.k in {locTemp, locNone}: # We already got a temp. Great, special case it: if d.k == locNone: d = getTemp(p, typ.returnType, needsInit=true) pl.add("Result: ") pl.add(addrLoc(p.config, d)) pl.add("]") p.s(cpsStmts).addStmt(): p.s(cpsStmts).add(extract(pl)) else: var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true) pl.add(addrLoc(p.config, tmp)) pl.add("]") p.s(cpsStmts).addStmt(): p.s(cpsStmts).add(extract(pl)) genAssignment(p, d, tmp, {}) # no need for deep copying else: pl.add("]") if d.k == locNone: d = getTemp(p, typ.returnType) assert(d.t != nil) # generate an assignment to d: var list: TLoc = initLoc(locCall, ri, OnUnknown) list.snippet = extract(pl) genAssignment(p, d, list, {}) # no need for deep copying else: pl.add("]") p.s(cpsStmts).addStmt(): p.s(cpsStmts).add(extract(pl)) proc notYetAlive(n: PNode): bool {.inline.} = let r = getRoot(n) result = r != nil and r.loc.lode == nil proc isInactiveDestructorCall(p: BProc, e: PNode): bool = #[ Consider this example. var :tmpD_3281815 try: if true: return let args_3280013 = wasMoved_3281816(:tmpD_3281815) `=_3280036`(:tmpD_3281815, [1]) :tmpD_3281815 finally: `=destroy_3280027`(args_3280013) We want to return early but the 'finally' section is traversed before the 'let args = ...' statement. We exploit this to generate better code for 'return'. ]# result = e.len == 2 and e[0].kind == nkSym and e[0].sym.name.s == "=destroy" and notYetAlive(e[1].skipAddr) proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) = if p.withinBlockLeaveActions > 0 and isInactiveDestructorCall(p, ri): return if ri[0].typ.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned}).callConv == ccClosure: genClosureCall(p, le, ri, d) elif ri[0].kind == nkSym and sfInfixCall in ri[0].sym.flags: genInfixCall(p, le, ri, d) elif ri[0].kind == nkSym and sfNamedParamCall in ri[0].sym.flags: genNamedParamCall(p, ri, d) else: genPrefixCall(p, le, ri, d) proc genCall(p: BProc, e: PNode, d: var TLoc) = genAsgnCall(p, nil, e, d)