Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: incorrect code generation for parameter borrowing #1460

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 75 additions & 3 deletions compiler/ast/types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ type
bvcSingle ## single-location view
bvcSequence ## view of contiguous locations

ViewTypeKind* = enum
noView, immutableView, mutableView

proc base*(t: PType): PType =
result = t[0]

Expand Down Expand Up @@ -1558,6 +1561,74 @@ proc classifyBackendView*(t: PType): BackendViewKind =
tyAnd, tyOr, tyNot, tyAnything, tyFromExpr:
unreachable()

proc combine(dest: var ViewTypeKind, b: ViewTypeKind) {.inline.} =
case dest
of noView, mutableView:
dest = b
of immutableView:
if b == mutableView: dest = b

proc classifyViewTypeAux(marker: var IntSet, t: PType): ViewTypeKind

proc classifyViewTypeNode(marker: var IntSet, n: PNode): ViewTypeKind =
case n.kind
of nkSym:
result = classifyViewTypeAux(marker, n.typ)
of nkOfBranch:
result = classifyViewTypeNode(marker, n.lastSon)
else:
result = noView
for child in n:
result.combine classifyViewTypeNode(marker, child)
if result == mutableView: break

proc classifyViewTypeAux(marker: var IntSet, t: PType): ViewTypeKind =
if containsOrIncl(marker, t.id): return noView
case t.kind
of tyVar:
result = mutableView
of tyLent, tyOpenArray, tyVarargs:
result = immutableView
of tyGenericInst, tyDistinct, tyAlias, tyInferred, tySink,
tyUncheckedArray, tySequence, tyArray, tyRef, tyStatic:
result = classifyViewTypeAux(marker, lastSon(t))
of tyFromExpr:
if t.len > 0:
result = classifyViewTypeAux(marker, lastSon(t))
else:
result = noView
of tyTuple:
result = noView
for i in 0..<t.len:
result.combine classifyViewTypeAux(marker, t[i])
if result == mutableView: break
of tyObject:
result = noView
if t.n != nil:
result = classifyViewTypeNode(marker, t.n)
if t[0] != nil:
result.combine classifyViewTypeAux(marker, t[0])
else:
# it doesn't matter what these types contain, 'ptr openArray' is not a
# view type!
result = noView

proc classifyViewType*(t: PType): ViewTypeKind =
var marker = initIntSet()
result = classifyViewTypeAux(marker, t)

proc directViewType*(t: PType): ViewTypeKind =
# does classify 't' without looking recursively into 't'.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# does classify 't' without looking recursively into 't'.
## classifies 't' without looking recursively into 't'.

A tad clearer and shorter, also made it a doc comment as it's relevant to what to expect from the procedure.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, but I figured to not modify the moved-over code, so as to allow git tooling to better detect the moved code.

case t.kind
of tyVar:
result = mutableView
of tyLent, tyOpenArray:
result = immutableView
of abstractInst-{tyTypeDesc}:
result = directViewType(t.lastSon)
else:
result = noView

proc isPassByRef*(conf: ConfigRef; s: PSym, retType: PType): bool =
var pt = skipTypes(s.typ, typedescInst)
assert skResult != s.kind
Expand All @@ -1581,8 +1652,9 @@ proc isPassByRef*(conf: ConfigRef; s: PSym, retType: PType): bool =
else:
result = false

# first parameter and return type is 'lent T'? --> use pass by pointer if
# not already a pointer-like type
if s.position == 0 and retType != nil and retType.kind == tyLent:
# first parameter and return type is immutable view? --> use pass by pointer
# if not already a pointer-like type
if s.position == 0 and retType != nil and
classifyViewType(retType) == immutableView:
result = pt.kind notin {tyVar, tyOpenArray, tyVarargs, tyRef, tyPtr,
tyPointer}
2 changes: 1 addition & 1 deletion compiler/mir/mirgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -766,7 +766,7 @@ proc genArgs(c: var TCtx, n: PNode) =
c.add empty(c, n[i])
elif i == 1 and not fntyp[0].isEmptyType() and
not isHandleLike(t) and
classifyBackendView(fntyp[0]) != bvcNone:
classifyViewType(fntyp[0]) == immutableView:
# the procedure returns a view, but the first parameter is not something
# that resembles a handle. We need to make sure that the first argument
# (which the view could be created from), is passed by reference
Expand Down
72 changes: 0 additions & 72 deletions compiler/sem/typeallowed.nim
Original file line number Diff line number Diff line change
Expand Up @@ -217,78 +217,6 @@ proc typeAllowedOrError*(t: PType, kind: TSymKind, c: PContext,
kind: kind,
allowedFlags: flags)))

type
ViewTypeKind* = enum
noView, immutableView, mutableView

proc combine(dest: var ViewTypeKind, b: ViewTypeKind) {.inline.} =
case dest
of noView, mutableView:
dest = b
of immutableView:
if b == mutableView: dest = b

proc classifyViewTypeAux(marker: var IntSet, t: PType): ViewTypeKind

proc classifyViewTypeNode(marker: var IntSet, n: PNode): ViewTypeKind =
case n.kind
of nkSym:
result = classifyViewTypeAux(marker, n.typ)
of nkOfBranch:
result = classifyViewTypeNode(marker, n.lastSon)
else:
result = noView
for child in n:
result.combine classifyViewTypeNode(marker, child)
if result == mutableView: break

proc classifyViewTypeAux(marker: var IntSet, t: PType): ViewTypeKind =
if containsOrIncl(marker, t.id): return noView
case t.kind
of tyVar:
result = mutableView
of tyLent, tyOpenArray, tyVarargs:
result = immutableView
of tyGenericInst, tyDistinct, tyAlias, tyInferred, tySink,
tyUncheckedArray, tySequence, tyArray, tyRef, tyStatic:
result = classifyViewTypeAux(marker, lastSon(t))
of tyFromExpr:
if t.len > 0:
result = classifyViewTypeAux(marker, lastSon(t))
else:
result = noView
of tyTuple:
result = noView
for i in 0..<t.len:
result.combine classifyViewTypeAux(marker, t[i])
if result == mutableView: break
of tyObject:
result = noView
if t.n != nil:
result = classifyViewTypeNode(marker, t.n)
if t[0] != nil:
result.combine classifyViewTypeAux(marker, t[0])
else:
# it doesn't matter what these types contain, 'ptr openArray' is not a
# view type!
result = noView

proc classifyViewType*(t: PType): ViewTypeKind =
var marker = initIntSet()
result = classifyViewTypeAux(marker, t)

proc directViewType*(t: PType): ViewTypeKind =
# does classify 't' without looking recursively into 't'.
case t.kind
of tyVar:
result = mutableView
of tyLent, tyOpenArray:
result = immutableView
of abstractInst-{tyTypeDesc}:
result = directViewType(t.lastSon)
else:
result = noView

proc requiresInit*(t: PType): bool =
(t.flags * {tfRequiresInit, tfNeedsFullInit, tfNotNil} != {}) or
classifyViewType(t) != noView
3 changes: 0 additions & 3 deletions compiler/sem/varpartitions.nim
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,6 @@ import
options,
msgs,
],
compiler/sem/[
typeallowed,
],
compiler/modules/[
modulegraphs,
]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
discard """
description: '''
Ensure a view borrowing from the first parameter can be safely returned
from a procedure.
'''
targets: c js vm
knownIssue.js vm: "The first parameter isn't passed by reference"
"""

block direct_lent_view_primitive:
# test borrowing from primitive-type parameter with a direct view
proc test(x: int): lent int =
x

var x = 0
doAssert addr(test(x)) == addr(x)

block object_lent_view_primitive:
# test borrowing from primitive-type parameter with an indirect view
type Object = object
x: lent int

proc test(x: int): Object =
Object(x: x)

var x = 0
doAssert addr(test(x).x) == addr(x)
Loading