v4 / vlib / v / ast / scope.v
364 lines · 334 sloc · 7.88 KB · d0637eb51bc2153560c5bd3b4795b0b5a4f425ba
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module ast
5
6pub const empty_scope = &Scope{
7 parent: unsafe { nil }
8}
9
10@[heap]
11pub struct Scope {
12pub mut:
13 // mut:
14 objects map[string]ScopeObject
15 struct_fields map[string]ScopeStructField
16 parent &Scope = unsafe { nil }
17 detached_from_parent bool
18 children []&Scope
19 start_pos int
20 end_pos int
21}
22
23@[unsafe]
24pub fn (s &Scope) free() {
25 if s == unsafe { nil } {
26 return
27 }
28 unsafe {
29 s.objects.free()
30 s.struct_fields.free()
31 for child in s.children {
32 child.free()
33 }
34 s.children.free()
35 }
36}
37
38/*
39pub fn new_scope(parent &Scope, start_pos int) &Scope {
40 return &Scope{
41 parent: parent
42 start_pos: start_pos
43 }
44}
45*/
46
47@[inline]
48fn (s &Scope) dont_lookup_parent() bool {
49 return s.parent == unsafe { nil } || s.detached_from_parent
50}
51
52pub fn (s &Scope) find(name string) ?ScopeObject {
53 if _unlikely_(s == unsafe { nil }) {
54 return none
55 }
56 for sc := unsafe { s }; true; sc = sc.parent {
57 if name in sc.objects {
58 return unsafe { sc.objects[name] }
59 }
60 if sc.dont_lookup_parent() {
61 break
62 }
63 }
64 return none
65}
66
67pub fn (s &Scope) find_ptr(name string) &ScopeObject {
68 if _unlikely_(s == unsafe { nil }) {
69 return 0
70 }
71 for sc := unsafe { s }; true; sc = sc.parent {
72 pobj := unsafe { &sc.objects[name] or { nil } }
73 if pobj != unsafe { nil } {
74 return pobj
75 }
76 if sc.dont_lookup_parent() {
77 break
78 }
79 }
80 return 0
81}
82
83// selector_expr: name.field_name
84pub fn (s &Scope) find_struct_field(name string, struct_type Type, field_name string) &ScopeStructField {
85 if _unlikely_(s == unsafe { nil }) {
86 return unsafe { nil }
87 }
88 k := '${name}.${field_name}'
89 for sc := unsafe { s }; true; sc = sc.parent {
90 if field := sc.struct_fields[k] {
91 if field.struct_type == struct_type {
92 return &ScopeStructField{
93 ...field
94 }
95 }
96 }
97 if sc.dont_lookup_parent() {
98 break
99 }
100 }
101 return unsafe { nil }
102}
103
104pub fn (s &Scope) find_var(name string) ?&Var {
105 obj := s.find_ptr(name)
106 if _likely_(obj != unsafe { nil }) {
107 match obj {
108 Var { return &obj }
109 else {}
110 }
111 }
112 return none
113}
114
115// find_var_decl returns the nearest declaration variable named `name`, walking
116// the parent scope chain and skipping synthetic smartcast vars introduced by
117// `if x is T`/`match` branches. Use this when a promotion such as auto-heap must
118// be recorded on the variable that codegen emits as the declaration, not on a
119// smartcast copy living in an inner branch scope.
120//
121// Only true synthetic copies are skipped: those are registered as separate
122// scope objects carrying both a non-empty `smartcasts` list and a non-zero
123// `orig_type` (the pre-smartcast type). A real declaration that is smartcast in
124// place keeps `orig_type == 0` (e.g. an option var unwrapped by
125// `if x == none { continue }` via `update_smartcasts`), so it is returned
126// rather than skipped.
127pub fn (s &Scope) find_var_decl(name string) ?&Var {
128 if _unlikely_(s == unsafe { nil }) {
129 return none
130 }
131 for sc := unsafe { s }; true; sc = sc.parent {
132 pobj := unsafe { &sc.objects[name] or { nil } }
133 if pobj != unsafe { nil } {
134 match pobj {
135 Var {
136 if pobj.smartcasts.len == 0 || pobj.orig_type == 0 {
137 return &pobj
138 }
139 }
140 else {}
141 }
142 }
143 if sc.dont_lookup_parent() {
144 break
145 }
146 }
147 return none
148}
149
150pub fn (s &Scope) find_global(name string) ?&GlobalField {
151 obj := s.find_ptr(name)
152 if _likely_(obj != unsafe { nil }) {
153 match obj {
154 GlobalField { return &obj }
155 else {}
156 }
157 }
158 return none
159}
160
161pub fn (s &Scope) find_const(name string) ?&ConstField {
162 obj := s.find_ptr(name)
163 if _likely_(obj != unsafe { nil }) {
164 match obj {
165 ConstField { return &obj }
166 else {}
167 }
168 }
169 return none
170}
171
172pub fn (s &Scope) known_var(name string) bool {
173 if _unlikely_(s == unsafe { nil }) {
174 return false
175 }
176 for sc := unsafe { s }; true; sc = sc.parent {
177 if name in sc.objects {
178 obj := unsafe { sc.objects[name] or { empty_scope_object } }
179 if obj is Var {
180 return true
181 }
182 }
183 if sc.dont_lookup_parent() {
184 break
185 }
186 }
187 return false
188}
189
190pub fn (s &Scope) known_global(name string) bool {
191 s.find_global(name) or { return false }
192 return true
193}
194
195pub fn (s &Scope) known_const(name string) bool {
196 s.find_const(name) or { return false }
197 return true
198}
199
200pub fn (mut s Scope) update_var_type(name string, typ Type) {
201 mut obj := unsafe { s.objects[name] }
202 if mut obj is Var {
203 if obj.typ != typ {
204 obj.typ = typ
205 }
206 }
207}
208
209pub fn (mut s Scope) update_ct_var_kind(name string, kind ComptimeVarKind) {
210 mut obj := unsafe { s.objects[name] }
211 if mut obj is Var {
212 obj.ct_type_var = kind
213 }
214}
215
216pub fn (mut s Scope) update_smartcasts(name string, typ Type, is_unwrapped bool) {
217 mut obj := unsafe { s.objects[name] }
218 if mut obj is Var {
219 obj.smartcasts = [typ]
220 obj.is_unwrapped = is_unwrapped
221 }
222}
223
224pub fn (mut s Scope) reset_smartcasts(name string) {
225 mut obj := unsafe { s.objects[name] }
226 if mut obj is Var {
227 obj.smartcasts = []
228 obj.is_unwrapped = false
229 }
230}
231
232// selector_expr: name.field_name
233pub fn (mut s Scope) register_struct_field(name string, field ScopeStructField) {
234 k := '${name}.${field.name}'
235 s.struct_fields[k] = field
236}
237
238pub fn (mut s Scope) register(obj ScopeObject) {
239 if !(obj.name == '_' || obj.name in s.objects) {
240 s.objects[obj.name] = obj
241 }
242}
243
244// returns the innermost scope containing pos
245// pub fn (s &Scope) innermost(pos int) ?&Scope {
246@[direct_array_access]
247pub fn (s &Scope) innermost(pos int) &Scope {
248 if s.contains(pos) {
249 // binary search
250 mut first := 0
251 mut last := s.children.len - 1
252 mut middle := last / 2
253 for first <= last {
254 // println('FIRST: ${first}, LAST: ${last}, LEN: ${s.children.len-1}')
255 s1 := s.children[middle]
256 if s1.end_pos < pos {
257 first = middle + 1
258 } else if s1.contains(pos) {
259 return s1.innermost(pos)
260 } else {
261 last = middle - 1
262 }
263 middle = (first + last) / 2
264 if first > last {
265 break
266 }
267 }
268 return s
269 }
270 // return none
271 return s
272}
273
274// get_all_vars extracts all current scope vars
275pub fn (s &Scope) get_all_vars() []ScopeObject {
276 if s == unsafe { nil } {
277 return []
278 }
279 mut scope_vars := []ScopeObject{}
280 for sc := unsafe { s }; true; sc = sc.parent {
281 if sc.objects.len > 0 {
282 scope_vars << sc.objects.values().filter(|it| it is Var)
283 }
284 if sc.dont_lookup_parent() {
285 break
286 }
287 }
288 return scope_vars
289}
290
291@[inline]
292pub fn (s &Scope) contains(pos int) bool {
293 return pos >= s.start_pos && pos <= s.end_pos
294}
295
296@[inline]
297pub fn (s &Scope) == (o &Scope) bool {
298 return s.start_pos == o.start_pos && s.end_pos == o.end_pos
299}
300
301pub fn (s &Scope) has_inherited_vars() bool {
302 for _, obj in s.objects {
303 if obj is Var {
304 if obj.is_inherited {
305 return true
306 }
307 }
308 }
309 return false
310}
311
312pub fn (s &Scope) is_inherited_var(var_name string) bool {
313 for _, obj in s.objects {
314 if obj is Var {
315 if obj.is_inherited && obj.name == var_name {
316 return true
317 }
318 }
319 }
320 return false
321}
322
323pub fn (sc &Scope) show(depth int, max_depth int) string {
324 mut out := ''
325 mut indent := ''
326 for _ in 0 .. depth * 4 {
327 indent += ' '
328 }
329 out += '${indent}# ${sc.start_pos} - ${sc.end_pos}\n'
330 for _, obj in sc.objects {
331 match obj {
332 ConstField { out += '${indent} * const: ${obj.name} - ${obj.typ}\n' }
333 Var { out += '${indent} * var: ${obj.name} - ${obj.typ}\n' }
334 else {}
335 }
336 }
337 for _, field in sc.struct_fields {
338 out += '${indent} * struct_field: ${field.struct_type} ${field.name} - ${field.typ}\n'
339 }
340 if max_depth == 0 || depth < max_depth - 1 {
341 for i, _ in sc.children {
342 out += sc.children[i].show(depth + 1, max_depth)
343 }
344 }
345 return out
346}
347
348pub fn (mut sc Scope) mark_var_as_used(varname string) bool {
349 mut obj := sc.find_ptr(varname)
350 if obj == unsafe { nil } {
351 return false
352 }
353 if mut obj is Var {
354 obj.is_used = true
355 return true
356 } else if obj is GlobalField {
357 return true
358 }
359 return false
360}
361
362pub fn (sc &Scope) str() string {
363 return sc.show(0, 3)
364}
365