mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 17:04:41 +00:00
129 lines
4.2 KiB
Nim
129 lines
4.2 KiB
Nim
|
|
import ast
|
|
|
|
import std / assertions
|
|
|
|
const
|
|
PathKinds0* = {nkDotExpr, nkCheckedFieldExpr,
|
|
nkBracketExpr, nkDerefExpr, nkHiddenDeref,
|
|
nkAddr, nkHiddenAddr,
|
|
nkObjDownConv, nkObjUpConv}
|
|
PathKinds1* = {nkHiddenStdConv, nkHiddenSubConv}
|
|
|
|
proc skipConvDfa*(n: PNode): PNode =
|
|
result = n
|
|
while true:
|
|
case result.kind
|
|
of nkObjDownConv, nkObjUpConv:
|
|
result = result[0]
|
|
of PathKinds1:
|
|
result = result[1]
|
|
else: break
|
|
|
|
proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool =
|
|
var n = orig
|
|
while true:
|
|
case n.kind
|
|
of PathKinds0 - {nkHiddenDeref, nkDerefExpr}:
|
|
n = n[0]
|
|
of PathKinds1:
|
|
n = n[1]
|
|
of nkHiddenDeref, nkDerefExpr:
|
|
# We "own" sinkparam[].loc but not ourVar[].location as it is a nasty
|
|
# pointer indirection.
|
|
# bug #14159, we cannot reason about sinkParam[].location as it can
|
|
# still be shared for tyRef.
|
|
n = n[0]
|
|
return n.kind == nkSym and n.sym.owner == owner and
|
|
(n.sym.typ.skipTypes(abstractInst-{tyOwned}).kind in {tyOwned})
|
|
else: break
|
|
# XXX Allow closure deref operations here if we know
|
|
# the owner controlled the closure allocation?
|
|
result = n.kind == nkSym and n.sym.owner == owner and
|
|
{sfGlobal, sfThread, sfCursor} * n.sym.flags == {} and
|
|
(n.sym.kind != skParam or isSinkParam(n.sym)) # or n.sym.typ.kind == tyVar)
|
|
# Note: There is a different move analyzer possible that checks for
|
|
# consume(param.key); param.key = newValue for all paths. Then code like
|
|
#
|
|
# let splited = split(move self.root, x)
|
|
# self.root = merge(splited.lower, splited.greater)
|
|
#
|
|
# could be written without the ``move self.root``. However, this would be
|
|
# wrong! Then the write barrier for the ``self.root`` assignment would
|
|
# free the old data and all is lost! Lesson: Don't be too smart, trust the
|
|
# lower level C++ optimizer to specialize this code.
|
|
|
|
type AliasKind* = enum
|
|
yes, no, maybe
|
|
|
|
proc aliases*(obj, field: PNode): AliasKind =
|
|
# obj -> field:
|
|
# x -> x: true
|
|
# x -> x.f: true
|
|
# x.f -> x: false
|
|
# x.f -> x.f: true
|
|
# x.f -> x.v: false
|
|
# x -> x[]: true
|
|
# x[] -> x: false
|
|
# x -> x[0]: true
|
|
# x[0] -> x: false
|
|
# x[0] -> x[0]: true
|
|
# x[0] -> x[1]: false
|
|
# x -> x[i]: true
|
|
# x[i] -> x: false
|
|
# x[i] -> x[i]: maybe; Further analysis could make this return true when i is a runtime-constant
|
|
# x[i] -> x[j]: maybe; also returns maybe if only one of i or j is a compiletime-constant
|
|
template collectImportantNodes(result, n) =
|
|
var result: seq[PNode]
|
|
var n = n
|
|
while true:
|
|
case n.kind
|
|
of PathKinds0 - {nkDotExpr, nkBracketExpr, nkDerefExpr, nkHiddenDeref}:
|
|
n = n[0]
|
|
of PathKinds1:
|
|
n = n[1]
|
|
of nkDotExpr, nkBracketExpr, nkDerefExpr, nkHiddenDeref:
|
|
result.add n
|
|
n = n[0]
|
|
of nkSym:
|
|
result.add n
|
|
break
|
|
else: return no
|
|
|
|
collectImportantNodes(objImportantNodes, obj)
|
|
collectImportantNodes(fieldImportantNodes, field)
|
|
|
|
# If field is less nested than obj, then it cannot be part of/aliased by obj
|
|
if fieldImportantNodes.len < objImportantNodes.len: return no
|
|
|
|
result = yes
|
|
for i in 1..objImportantNodes.len:
|
|
# We compare the nodes leading to the location of obj and field
|
|
# with each other.
|
|
# We continue until they diverge, in which case we return no, or
|
|
# until we reach the location of obj, in which case we do not need
|
|
# to look further, since field must be part of/aliased by obj now.
|
|
# If we encounter an element access using an index which is a runtime value,
|
|
# we simply return maybe instead of yes; should further nodes not diverge.
|
|
let currFieldPath = fieldImportantNodes[^i]
|
|
let currObjPath = objImportantNodes[^i]
|
|
|
|
if currFieldPath.kind != currObjPath.kind:
|
|
return no
|
|
|
|
case currFieldPath.kind
|
|
of nkSym:
|
|
if currFieldPath.sym != currObjPath.sym: return no
|
|
of nkDotExpr:
|
|
if currFieldPath[1].sym != currObjPath[1].sym: return no
|
|
of nkDerefExpr, nkHiddenDeref:
|
|
discard
|
|
of nkBracketExpr:
|
|
if currFieldPath[1].kind in nkLiterals and currObjPath[1].kind in nkLiterals:
|
|
if currFieldPath[1].intVal != currObjPath[1].intVal:
|
|
return no
|
|
else:
|
|
result = maybe
|
|
else: assert false # unreachable
|
|
|