Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit bbe4ab0

Browse files
committed
Port derefs.nim in Nimony to Nim 2 #25183; Borrow / Mutability checks
1 parent ceaa7fb commit bbe4ab0

File tree

2 files changed

+219
-1
lines changed

2 files changed

+219
-1
lines changed

compiler/sem.nim

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import
1818
evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity,
1919
lowerings, plugins/active, lineinfos, int128,
2020
isolation_check, typeallowed, modulegraphs, enumtostr, concepts, astmsgs,
21-
extccomp, layeredtable
21+
extccomp, layeredtable, semderefs
2222

2323
import vtables
2424
import std/[strtabs, math, tables, intsets, strutils, packedsets]
@@ -821,6 +821,7 @@ proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
821821
else:
822822
result = n
823823
result = semStmt(c, result, {})
824+
result = injectDerefs(c.config, result)
824825
when false:
825826
# Code generators are lazy now and can deal with undeclared procs, so these
826827
# steps are not required anymore and actually harmful for the upcoming

compiler/semderefs.nim

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
import ast, lineinfos, msgs, options, renderer
2+
import std/[strutils]
3+
4+
type
5+
Expects = enum
6+
WantT
7+
WantTButSkipDeref
8+
WantVarT
9+
WantVarTResult
10+
WantMutableT # `var openArray` does need derefs but mutable locations regardless
11+
WantForwarding
12+
13+
CurrentRoutine = object
14+
returnExpects: Expects
15+
firstParamKind: TTypeKind
16+
firstParam: PSym
17+
resultSym: PSym
18+
routineSym: PSym
19+
20+
Context = object
21+
config: ConfigRef
22+
r: CurrentRoutine
23+
24+
proc tr(c: var Context; n: PNode; e: Expects): PNode
25+
26+
proc trSons(c: var Context; n: PNode; e: Expects): PNode =
27+
for i in 0 ..< n.safeLen:
28+
n[i] = tr(c, n[i], e)
29+
30+
result = n
31+
32+
proc validBorrowsFrom(c: var Context; n: PNode): bool =
33+
# --------------------------------------------------------------------------
34+
# We need to borrow from a location that is derived from the proc's
35+
# first parameter.
36+
# See RFC #7373
37+
# --------------------------------------------------------------------------
38+
result = false
39+
var n = n
40+
var someIndirection = false
41+
while true:
42+
case n.kind
43+
of nkDotExpr, nkCheckedFieldExpr, nkBracketExpr, nkObjUpConv, nkObjDownConv:
44+
n = n[0]
45+
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
46+
n = n[1]
47+
of nkHiddenDeref, nkHiddenAddr, nkDerefExpr, nkAddr:
48+
n = n[0]
49+
someIndirection = true
50+
of nkStmtList, nkStmtListExpr:
51+
if n.len > 0 and n.typ != nil: n = n.lastSon
52+
else: break
53+
of nkCallKinds:
54+
if n.len > 1:
55+
let fnType = n[0].typ
56+
n = n[1]
57+
if fnType != nil and fnType.len > 1:
58+
let firstParamType = fnType[1]
59+
let mightForward = firstParamType.kind in {tyVar, tyLent}
60+
if not mightForward:
61+
someIndirection = true
62+
else:
63+
break
64+
else:
65+
break
66+
67+
if n.kind == nkSym:
68+
if n.sym.kind == skParam and n.sym == c.r.firstParam:
69+
result = c.r.firstParamKind in {tyVar, tyLent} or someIndirection
70+
else:
71+
# Allow borrow from global or captured variables for backwards compatibility.
72+
result = n.sym.owner != c.r.routineSym
73+
else:
74+
result = false
75+
76+
proc skipToRoot(n: PNode): PNode =
77+
var n = n
78+
while true:
79+
case n.kind
80+
of nkDotExpr, nkCheckedFieldExpr, nkBracketExpr, nkObjUpConv, nkObjDownConv:
81+
n = n[0]
82+
of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkCast:
83+
n = n[1]
84+
of nkCallKinds:
85+
if n.len > 1:
86+
n = n[1]
87+
else:
88+
break
89+
else:
90+
break
91+
result = n
92+
93+
proc borrowsFromReadonly(c: var Context; n: PNode; allowLet = false): bool =
94+
let n = skipToRoot(n)
95+
if n.kind == nkSym:
96+
let tk = n.sym.typ.kind
97+
case n.sym.kind:
98+
of skConst:
99+
result = true
100+
of skLet:
101+
if allowLet:
102+
result = tk == tyLent
103+
else:
104+
result = tk notin {tyVar, tyLent}
105+
of skVar:
106+
result = tk == tyLent
107+
of skParam:
108+
result = tk notin {tyVar, tyLent, tySink}
109+
else:
110+
result = false
111+
elif n.kind in nkLiterals + {nkObjConstr}:
112+
result = true
113+
else:
114+
result = false
115+
116+
proc wantMutable(e: Expects): bool {.inline.} =
117+
e in {WantVarT, WantVarTResult, WantMutableT}
118+
119+
proc trProcDecl(c: var Context; n: PNode): PNode =
120+
if isGenericRoutine(n):
121+
result = n
122+
else:
123+
var r = CurrentRoutine(returnExpects: WantT)
124+
if n[namePos].kind == nkSym:
125+
r.routineSym = n[namePos].sym
126+
if n[namePos].sym.typ != nil:
127+
let fnType = n[namePos].sym.typ
128+
if fnType.len > 1:
129+
r.firstParamKind = fnType[1].kind
130+
if n[paramsPos].safeLen > 1 and n[paramsPos][1].kind == nkIdentDefs:
131+
let firstParam = n[paramsPos][1]
132+
if firstParam[0].kind == nkSym:
133+
r.firstParam = firstParam[0].sym
134+
if n.len > resultPos and n[resultPos].kind == nkSym:
135+
r.resultSym = n[resultPos].sym
136+
if n[resultPos].sym.typ != nil and n[resultPos].sym.typ.kind in {tyVar, tyLent}:
137+
r.returnExpects = WantVarTResult
138+
139+
swap c.r, r
140+
result = n
141+
result[bodyPos] = tr(c, n[bodyPos], c.r.returnExpects)
142+
swap c.r, r
143+
144+
proc cannotPassToVar(c: var Context; info: TLineInfo; arg: PNode) =
145+
localError(c.config, info, "cannot pass '$1' to var/out T parameter" % [renderTree(arg, {renderNoComments})])
146+
147+
type
148+
LvalueStatus = enum
149+
Valid
150+
InvalidBorrow
151+
LocationIsConst
152+
153+
proc trAsgn(c: var Context; n: PNode): PNode =
154+
var e = WantT
155+
var err = Valid
156+
if n[0].kind == nkSym and n[0].sym.kind == skResult and n[0].sym == c.r.resultSym:
157+
if c.r.returnExpects == WantVarTResult:
158+
if not validBorrowsFrom(c, n[1]):
159+
err = InvalidBorrow
160+
discard
161+
case err:
162+
of InvalidBorrow:
163+
localError c.config, n.info, "cannot borrow from " & renderTree(n[1], {renderNoComments})
164+
else:
165+
result = tr(c, n[1], e)
166+
result = n
167+
168+
proc trLocation(c: var Context; n: PNode; e: Expects): PNode =
169+
let typ = n.typ
170+
if typ != nil:
171+
let k = typ.kind
172+
if k in {tyVar, tyLent}:
173+
if e.wantMutable:
174+
# Consider `fvar(returnsVar(someLet))`: We must not allow this.
175+
if borrowsFromReadonly(c, n):
176+
cannotPassToVar c, n.info, n
177+
result = n
178+
else:
179+
result = trSons(c, n, WantT)
180+
else:
181+
result = trSons(c, n, WantT)
182+
elif e.wantMutable:
183+
if e == WantVarTResult:
184+
if n.kind == nkSym:
185+
result = n
186+
else:
187+
result = trSons(c, n, WantT)
188+
elif borrowsFromReadonly(c, n):
189+
cannotPassToVar c, n.info, n
190+
result = n
191+
else:
192+
result = trSons(c, n, WantT)
193+
else:
194+
result = trSons(c, n, WantT)
195+
else:
196+
result = n
197+
198+
proc tr(c: var Context; n: PNode; e: Expects): PNode =
199+
case n.kind
200+
of nkSym:
201+
result = trLocation(c, n, e)
202+
of nkLiterals:
203+
if e.wantMutable:
204+
cannotPassToVar c, n.info, n
205+
result = n
206+
of nkDotExpr, nkCheckedFieldExpr, nkBracketExpr:
207+
result = trLocation(c, n, e)
208+
of nkAsgn:
209+
result = trAsgn(c, n)
210+
of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef, nkIteratorDef:
211+
result = trProcDecl(c, n)
212+
else:
213+
result = trSons(c, n, e)
214+
215+
proc injectDerefs*(config: ConfigRef; n: PNode): PNode =
216+
var c = Context(config: config, r: CurrentRoutine())
217+
result = tr(c, n, WantT)

0 commit comments

Comments
 (0)