diff --git a/compiler/ast.nim b/compiler/ast.nim index 6ca79aeeee410..1e619e51b0bbd 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1841,6 +1841,11 @@ proc isImportedException*(t: PType; conf: ConfigRef): bool = proc isInfixAs*(n: PNode): bool = return n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "as" +proc skipColon*(n: PNode): PNode = + result = n + if n.kind == nkExprColonExpr: + result = n[1] + proc findUnresolvedStatic*(n: PNode): PNode = # n.typ == nil: see issue #14802 if n.kind == nkSym and n.typ != nil and n.typ.kind == tyStatic and n.typ.n == nil: @@ -1860,6 +1865,10 @@ when false: if n[i].containsNil: return true template hasDestructor*(t: PType): bool = {tfHasAsgn, tfHasOwned} * t.flags != {} + +proc hasDisabledAsgn*(t: PType): bool = + t.attachedOps[attachedAsgn] != nil and sfError in t.attachedOps[attachedAsgn].flags + template incompleteType*(t: PType): bool = t.sym != nil and {sfForward, sfNoForward} * t.sym.flags == {sfForward} diff --git a/compiler/cursor_inference.nim b/compiler/cursor_inference.nim deleted file mode 100644 index 0402febec7f30..0000000000000 --- a/compiler/cursor_inference.nim +++ /dev/null @@ -1,300 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2020 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## Cursor inference: -## The basic idea was like this: Elide 'destroy(x)' calls if only -## special literals are assigned to 'x' and 'x' is not mutated or -## passed by 'var T' to something else. Special literals are string literals or -## arrays / tuples of string literals etc. -## -## However, there is a much more general rule here: Compute which variables -## can be annotated with `.cursor`. To see how and when we can do that, -## think about this question: In `dest = src` when do we really have to -## *materialize* the full copy? - Only if `dest` or `src` are mutated -## afterwards. `dest` is the potential cursor variable, so that is -## simple to analyse. And if `src` is a location derived from a -## formal parameter, we also know it is not mutated! In other words, we -## do a compile-time copy-on-write analysis. - -import - ast, types, renderer, idents, intsets, options - -type - Cursor = object - s: PSym - deps: IntSet - Con = object - cursors: seq[Cursor] - mayOwnData: IntSet - mutations: IntSet - reassigns: IntSet - config: ConfigRef - inAsgnSource: int - -proc locationRoot(e: PNode; followDotExpr = true): PSym = - var n = e - while true: - case n.kind - of nkSym: - if n.sym.kind in {skVar, skResult, skTemp, skLet, skForVar, skParam}: - return n.sym - else: - return nil - of nkDotExpr, nkDerefExpr, nkBracketExpr, nkHiddenDeref, - nkCheckedFieldExpr, nkAddr, nkHiddenAddr: - if followDotExpr: - n = n[0] - else: - return nil - of nkObjUpConv, nkObjDownConv: - n = n[0] - of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkCast: - n = n[1] - of nkStmtList, nkStmtListExpr: - if n.len > 0: - n = n[^1] - else: - return nil - else: - return nil - -proc addDep(c: var Con; dest: var Cursor; dependsOn: PSym) = - if dest.s != dependsOn: - dest.deps.incl dependsOn.id - -proc cursorId(c: Con; x: PSym): int = - for i in 0.. 0: - analyseAsgn(c, dest, n[^1]) - - of nkClosure: - for i in 1.. treat it like a sink parameter - c.mayOwnData.incl r.id - c.mutations.incl r.id - -proc analyse(c: var Con; n: PNode) = - case n.kind - of nkCallKinds: - let parameters = n[0].typ - let L = if parameters != nil: parameters.len else: 0 - - analyse(c, n[0]) - for i in 1..= 0: - analyseAsgn(c, c.cursors[idx], n[1]) - - c.reassigns.incl n[0].sym.id - else: - # assignments like 'x.field = value' mean that 'x' itself cannot - # be a cursor: - let r = locationRoot(n[0]) - if r != nil: - # however, an assignment like 'it.field = x' does not influence r's - # cursorness property: - if r.typ.skipTypes(abstractInst).kind notin {tyPtr, tyRef}: - c.mayOwnData.incl r.id - c.mutations.incl r.id - - if hasDestructor(n[1].typ): - rhsIsSink(c, n[1]) - - of nkAddr, nkHiddenAddr: - analyse(c, n[0]) - let r = locationRoot(n[0]) - if r != nil: - c.mayOwnData.incl r.id - c.mutations.incl r.id - - of nkTupleConstr, nkBracket, nkObjConstr: - for child in n: analyse(c, child) - if c.inAsgnSource > 0: - for i in ord(n.kind == nkObjConstr).. 0: - allRoots(n.lastSon, result) + allRoots(n.lastSon, result, followDotExpr) of nkCaseStmt, nkObjConstr: for i in 1.. 1: - allRoots(n[1], result) + allRoots(n[1], result, followDotExpr) else: let m = getMagic(n) case m @@ -201,77 +216,223 @@ proc allRoots(n: PNode; result: var seq[PSym]) = let paramType = typ.n[i].typ if not paramType.isCompileTimeOnly and not typ.sons[0].isEmptyType and canAlias(paramType, typ.sons[0]): - allRoots(it, result) + allRoots(it, result, followDotExpr) else: - allRoots(it, result) + allRoots(it, result, followDotExpr) of mSlice: - allRoots(n[1], result) + allRoots(n[1], result, followDotExpr) else: discard "harmless operation" else: discard "nothing to do" -proc deps(p: var Partitions; dest, src: PNode) = +proc analyseAsgn(c: var Partitions; dest: var VarIndex; n: PNode) = + case n.kind + of nkEmpty, nkCharLit..nkNilLit: + # primitive literals including the empty are harmless: + discard + + of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkCast, nkConv: + analyseAsgn(c, dest, n[1]) + + of nkIfStmt, nkIfExpr: + for i in 0.. 0: + analyseAsgn(c, dest, n[^1]) + + of nkClosure: + for i in 1..= 0: + c.s[vid].flags.incl preventCursor + +proc rhsIsSink(c: var Partitions, n: PNode) = + if n.kind == nkSym and n.typ.skipTypes(abstractInst-{tyOwned}).kind == tyRef: + discard "do no pessimize simple refs further, injectdestructors.nim will prevent moving from it" + else: + var roots: seq[PSym] + allRoots(n, roots, followDotExpr = false) + # let x = cursor? --> treat it like a sink parameter + for r in roots: + noCursor(c, r) + +proc deps(c: var Partitions; dest, src: PNode) = var targets, sources: seq[PSym] allRoots(dest, targets) allRoots(src, sources) for t in targets: if dest.kind != nkSym: - potentialMutation(p, t, dest.info) + potentialMutation(c, t, dest.info) proc wrap(t: PType): bool {.nimcall.} = t.kind in {tyRef, tyPtr} if types.searchTypeFor(t.typ, wrap): for s in sources: - connect(p, t, s, dest.info) + connect(c, t, s, dest.info) + + if c.performCursorInference and src.kind != nkEmpty: + if dest.kind == nkSym: + let vid = variableId(c, dest.sym) + if vid >= 0: + analyseAsgn(c, c.s[vid], src) + # do not borrow from a different local variable, this is easier + # than tracking reassignments, consider 'var cursor = local; local = newNode()' + if src.kind == nkSym and (src.sym.kind in {skVar, skResult, skTemp} or + (src.sym.kind in {skLet, skParam, skForVar} and hasDisabledAsgn(src.sym.typ))): + c.s[vid].flags.incl preventCursor + + if hasDestructor(src.typ): + rhsIsSink(c, src) -proc traverse(p: var Partitions; n: PNode) = +proc traverse(c: var Partitions; n: PNode) = case n.kind of nkLetSection, nkVarSection: for child in n: let last = lastSon(child) - traverse(p, last) + traverse(c, last) if child.kind == nkVarTuple and last.kind in {nkPar, nkTupleConstr}: if child.len-2 != last.len: return for i in 0.. 0: + for i in 0.. 0: + for i in 1..