1use std::{
2 collections::{HashMap, hash_map::Entry},
3 rc::Rc,
4};
5
6use crate::{
7 Resolver, SyntaxUtil,
8 idents::builtin_ident,
9 pass::{Imports, Module, UsedItems, Visit, imported_item_path},
10};
11use wesl_macros::query_mut;
12
13use wgsl_parse::{SyntaxNode, syntax::*};
14
15type BoxedIterator<'a, T> = Box<dyn Iterator<Item = T> + 'a>;
17
18#[allow(dead_code)]
19pub trait IteratorExt: Iterator {
20 fn boxed<'a>(self) -> BoxedIterator<'a, Self::Item>
21 where
22 Self: Sized + 'a;
23}
24
25impl<T: Iterator> IteratorExt for T {
26 fn boxed<'a>(self) -> BoxedIterator<'a, Self::Item>
27 where
28 Self: Sized + 'a,
29 {
30 Box::new(self)
31 }
32}
33
34struct ScopeInner {
35 local: HashMap<String, Ident>,
36 parent: Option<Rc<ScopeInner>>,
37}
38
39struct Scope(Rc<ScopeInner>);
40
41impl ScopeInner {
42 fn iter(&self) -> impl Iterator<Item = (&String, &Ident)> {
43 self.local
44 .iter()
45 .chain(self.parent.iter().flat_map(|parent| parent.iter().boxed()))
46 }
47}
48
49impl Scope {
50 fn new() -> Scope {
51 Scope(Rc::new(ScopeInner {
52 local: HashMap::new(),
53 parent: None,
54 }))
55 }
56
57 fn push(&self) -> Scope {
58 Scope(Rc::new(ScopeInner {
59 local: HashMap::new(),
60 parent: Some(self.0.clone()),
61 }))
62 }
63
64 fn iter(&self) -> impl Iterator<Item = (&String, &Ident)> {
65 self.0.iter()
66 }
67
68 fn insert(&mut self, ident: &mut Ident) {
70 let inner = Rc::get_mut(&mut self.0).expect("cannot insert: scope use-count > 1");
71 match inner.local.entry(ident.to_string()) {
72 Entry::Occupied(entry) => {
73 *ident = entry.get().clone();
74 }
75 Entry::Vacant(entry) => {
76 entry.insert(ident.clone());
77 }
78 }
79 }
80}
81
82pub fn retarget_idents(module: &mut TranslationUnit) {
91 fn flatten_imports(imports: &mut [ImportStatement]) -> impl Iterator<Item = &mut Ident> + '_ {
92 fn rec(content: &mut ImportContent) -> impl Iterator<Item = &mut Ident> + '_ {
93 match content {
94 ImportContent::Item(item) => {
95 std::iter::once(item.rename.as_mut().unwrap_or(&mut item.ident)).boxed()
96 }
97 ImportContent::Collection(coll) => coll
98 .iter_mut()
99 .flat_map(|import| rec(&mut import.content))
100 .boxed(),
101 }
102 }
103 imports
104 .iter_mut()
105 .flat_map(|import| rec(&mut import.content))
106 }
107
108 fn retarget_ty(ty: &mut TypeExpression, scope: &Scope) {
109 if ty.path.is_none() {
110 if let Some((_, id)) = scope
111 .iter()
112 .find(|(name, _)| name.as_str() == *ty.ident.name())
113 {
114 ty.ident = id.clone();
115 } else {
116 let builtin = builtin_ident(&ty.ident.name()).cloned();
117 if let Some(id) = builtin {
118 ty.ident = id;
119 }
120 }
121 }
122 query_mut!(ty.template_args.[].[].expression.(x => Visit::<TypeExpression>::visit_mut(&mut **x)))
123 .for_each(|ty| retarget_ty(ty, scope));
124 }
125
126 fn retarget_stats<'a>(
130 stats: impl IntoIterator<Item = &'a mut StatementNode>,
131 mut scope: Scope,
132 ) -> Scope {
133 stats.into_iter().for_each(|stmt| match stmt.node_mut() {
134 Statement::Void => (),
135 Statement::Compound(s) => {
136 query_mut!(s.attributes.[].(x => x.visit_mut()))
137 .for_each(|ty| retarget_ty(ty, &scope));
138 retarget_stats(&mut s.statements, scope.push());
139 }
140 Statement::Assignment(s) => {
141 query_mut!(s.{
142 attributes.[].(x => x.visit_mut()),
143 lhs.(x => Visit::<TypeExpression>::visit_mut(&mut **x)),
144 rhs.(x => Visit::<TypeExpression>::visit_mut(&mut **x)),
145 })
146 .for_each(|ty| retarget_ty(ty, &scope));
147 }
148 Statement::Increment(s) => {
149 query_mut!(s.{
150 attributes.[].(x => x.visit_mut()),
151 expression.(x => Visit::<TypeExpression>::visit_mut(&mut **x)),
152 })
153 .for_each(|ty| retarget_ty(ty, &scope));
154 }
155 Statement::Decrement(s) => {
156 query_mut!(s.{
157 attributes.[].(x => x.visit_mut()),
158 expression.(x => Visit::<TypeExpression>::visit_mut(&mut **x)),
159 })
160 .for_each(|ty| retarget_ty(ty, &scope));
161 }
162 Statement::If(s) => {
163 let s2 = &mut *s; query_mut!(s2.{
165 attributes.[].(x => x.visit_mut()),
166 if_clause.{
167 expression.(x => Visit::<TypeExpression>::visit_mut(&mut **x)),
168 body.{
169 attributes.[].(x => x.visit_mut()),
170 }
171 },
172 else_if_clauses.[].{
173 attributes.[].(x => x.visit_mut()),
174 expression.(x => Visit::<TypeExpression>::visit_mut(&mut **x)),
175 body.{
176 attributes.[].(x => x.visit_mut()),
177 }
178 },
179 else_clause.[].{
180 attributes.[].(x => x.visit_mut()),
181 body.{
182 attributes.[].(x => x.visit_mut()),
183 },
184 },
185 })
186 .for_each(|ty| retarget_ty(ty, &scope));
187 retarget_stats(&mut s.if_clause.body.statements, scope.push());
188 for clause in &mut s.else_if_clauses {
189 retarget_stats(&mut clause.body.statements, scope.push());
190 }
191 if let Some(clause) = &mut s.else_clause {
192 retarget_stats(&mut clause.body.statements, scope.push());
193 }
194 }
195 Statement::Switch(s) => {
196 let s2 = &mut *s; query_mut!(s2.{
198 attributes.[].(x => x.visit_mut()),
199 expression.(x => Visit::<TypeExpression>::visit_mut(&mut **x)),
200 body_attributes.[].(x => x.visit_mut()),
201 clauses.[].{
202 attributes.[].(x => x.visit_mut()),
203 case_selectors.[].CaseSelector::Expression.(x => Visit::<TypeExpression>::visit_mut(&mut **x)),
204 body.{
205 attributes.[].(x => x.visit_mut()),
206 }
207 },
208
209 })
210 .for_each(|ty| retarget_ty(ty, &scope));
211 for clause in &mut s.clauses {
212 retarget_stats(&mut clause.body.statements, scope.push());
213 }
214 }
215 Statement::Loop(s) => {
216 let s2 = &mut *s; query_mut!(s2.{
218 attributes.[].(x => x.visit_mut()),
219 body.attributes.[].(x => x.visit_mut()),
220 })
221 .for_each(|ty| retarget_ty(ty, &scope));
222 let scope = retarget_stats(&mut s.body.statements, scope.push());
223 if let Some(s) = &mut s.continuing {
226 let s2 = &mut *s; query_mut!(s2.{
228 attributes.[].(x => x.visit_mut()),
229 body.attributes.[].(x => x.visit_mut()),
230 })
231 .for_each(|ty| retarget_ty(ty, &scope));
232 let scope = retarget_stats(&mut s.body.statements, scope.push());
233 if let Some(s) = &mut s.break_if {
236 let s2 = &mut *s; query_mut!(s2.{
238 attributes.[].(x => x.visit_mut()),
239 expression.(x => Visit::<TypeExpression>::visit_mut(&mut **x)),
240 })
241 .for_each(|ty| retarget_ty(ty, &scope));
242 }
243 }
244 }
245 Statement::For(s) => {
246 query_mut!(s.attributes.[].(x => x.visit_mut()))
247 .for_each(|ty| retarget_ty(ty, &scope));
248 let scope = if let Some(init) = &mut s.initializer {
249 retarget_stats([init], scope.push())
250 } else {
251 scope.push()
252 };
253 query_mut!(s.condition.[].(x => Visit::<TypeExpression>::visit_mut(&mut **x)))
254 .for_each(|ty| retarget_ty(ty, &scope));
255 query_mut!(s.body.attributes.[].(x => x.visit_mut()))
256 .for_each(|ty| retarget_ty(ty, &scope));
257 if let Some(update) = &mut s.update {
258 retarget_stats([update], scope.push());
259 }
260 retarget_stats(&mut s.body.statements, scope);
261 }
262 Statement::While(s) => {
263 let s2 = &mut *s; query_mut!(s2.{
265 attributes.[].(x => x.visit_mut()),
266 condition.(x => Visit::<TypeExpression>::visit_mut(&mut **x)),
267 body.attributes.[].(x => x.visit_mut()),
268 })
269 .for_each(|ty| retarget_ty(ty, &scope));
270 retarget_stats(&mut s.body.statements, scope.push());
271 }
272 Statement::Break(s) => {
273 query_mut!(s.attributes.[].(x => x.visit_mut()))
274 .for_each(|ty| retarget_ty(ty, &scope));
275 }
276 Statement::Continue(s) => {
277 query_mut!(s.attributes.[].(x => x.visit_mut()))
278 .for_each(|ty| retarget_ty(ty, &scope));
279 }
280 Statement::Return(s) => {
281 query_mut!(s.expression.[].(x => Visit::<TypeExpression>::visit_mut(&mut **x)))
282 .for_each(|ty| retarget_ty(ty, &scope));
283 }
284 Statement::Discard(s) => {
285 query_mut!(s.attributes.[].(x => x.visit_mut()))
286 .for_each(|ty| retarget_ty(ty, &scope));
287 }
288 Statement::FunctionCall(s) => {
289 query_mut!(s.{
290 attributes.[].(x => x.visit_mut()),
291 call.{
292 ty,
293 arguments.[].(x => Visit::<TypeExpression>::visit_mut(&mut **x)),
294 }
295 })
296 .for_each(|ty| retarget_ty(ty, &scope));
297 }
298 Statement::ConstAssert(s) => {
299 query_mut!(s.{
300 expression.(x => Visit::<TypeExpression>::visit_mut(&mut **x))
301 })
302 .for_each(|ty| retarget_ty(ty, &scope));
303 }
304 Statement::Declaration(s) => {
305 let s2 = &mut *s; query_mut!(s2.{
307 attributes.[].(x => x.visit_mut()),
308 ty.[],
309 initializer.[].(x => Visit::<TypeExpression>::visit_mut(&mut **x)),
310 })
311 .for_each(|ty| retarget_ty(ty, &scope));
312 scope.insert(&mut s.ident);
313 }
314 });
315 scope
316 }
317
318 let mut scope = Scope::new();
319
320 for ident in flatten_imports(&mut module.imports) {
321 scope.insert(ident);
322 }
323
324 for decl in &mut module.global_declarations {
325 let ident = match decl.node_mut() {
326 GlobalDeclaration::Void => None,
327 GlobalDeclaration::Declaration(decl) => Some(&mut decl.ident),
328 GlobalDeclaration::TypeAlias(decl) => Some(&mut decl.ident),
329 GlobalDeclaration::Struct(decl) => Some(&mut decl.ident),
330 GlobalDeclaration::Function(decl) => Some(&mut decl.ident),
331 GlobalDeclaration::ConstAssert(_) => None,
332 GlobalDeclaration::Compound(_) => None,
333 };
334
335 if let Some(ident) = ident {
336 scope.insert(ident);
337 }
338 }
339
340 for decl in &mut module.global_declarations {
341 match decl.node_mut() {
342 GlobalDeclaration::Void => (),
343 GlobalDeclaration::Declaration(d) => {
344 Visit::<TypeExpression>::visit_mut(d).for_each(|ty| retarget_ty(ty, &scope))
345 }
346 GlobalDeclaration::TypeAlias(d) => {
347 Visit::<TypeExpression>::visit_mut(d).for_each(|ty| retarget_ty(ty, &scope))
348 }
349 GlobalDeclaration::Struct(d) => {
350 Visit::<TypeExpression>::visit_mut(d).for_each(|ty| retarget_ty(ty, &scope))
351 }
352 GlobalDeclaration::Function(d) => {
353 #[cfg(feature = "generics")]
354 let scope = {
355 let mut scope = scope.push();
356 d.attributes
357 .iter_mut()
358 .filter_map(|attr| match attr.node_mut() {
359 Attribute::Type(attr) => Some(&mut attr.ident),
360 _ => None,
361 })
362 .for_each(|ident| scope.insert(ident));
363 scope
364 };
365 let d2 = &mut *d; query_mut!(d2.{
367 attributes.[].(x => x.visit_mut()),
368 parameters.[].{
369 attributes.[].(x => x.visit_mut()),
370 ty,
371 },
372 return_attributes.[].(x => x.visit_mut()),
373 return_type.[],
374 body.{
375 attributes.[].(x => x.visit_mut()),
376 }
377 })
378 .for_each(|ty| retarget_ty(ty, &scope));
379 let mut scope = scope.push();
380 d.parameters
381 .iter_mut()
382 .for_each(|param| scope.insert(&mut param.ident));
383 retarget_stats(&mut d.body.statements, scope);
384 }
385 GlobalDeclaration::ConstAssert(d) => {
386 Visit::<TypeExpression>::visit_mut(d).for_each(|ty| retarget_ty(ty, &scope))
387 }
388 GlobalDeclaration::Compound(d) => {
389 query_mut!(d.{
390 attributes.[].(x => x.visit_mut()),
391 body.[].(x => Visit::<TypeExpression>::visit_mut(&mut **x)),
392 })
393 .for_each(|ty| retarget_ty(ty, &scope));
394 }
395 }
396 }
397}
398
399pub fn retarget_modules(modules: &mut [Module], used_items: &UsedItems, resolver: &impl Resolver) {
408 fn retarget_ty<'a>(
413 ty: &mut TypeExpression,
414 module_path: &ModulePath,
415 module_imports: &Imports,
416 module_idents: &HashMap<Ident, Visibility>,
417 other_modules: impl IntoIterator<Item = &'a Module> + Clone + 'a,
418 resolver: &impl Resolver,
419 ) {
420 for ty in Visit::<TypeExpression>::visit_mut(ty) {
422 retarget_ty(
423 ty,
424 module_path,
425 module_imports,
426 module_idents,
427 other_modules.clone(),
428 resolver,
429 );
430 }
431
432 if let Some((import_path, mut import_ident)) =
433 imported_item_path(ty, module_path, module_imports)
434 {
435 let mut import_path = resolver.canonical_path(&import_path);
436 loop {
439 if import_path == *module_path {
443 if let Some(ident) = module_idents
444 .keys()
445 .find(|ident| *ident.name() == *import_ident.name())
446 .cloned()
447 {
448 ty.path = None;
450 ty.ident = ident;
451 return;
452 } else if let Some((_, item)) = module_imports
453 .iter()
454 .find(|(ident, _)| *ident.name() == *ty.ident.name())
455 {
456 import_path = resolver.canonical_path(&item.path);
460 import_ident = item.ident.clone();
461 } else {
462 debug_assert!(false, "no declaration {import_ident} in {import_path}");
463 return;
464 }
465 } else {
466 let Some(import_module) = other_modules
467 .clone()
468 .into_iter()
469 .find(|m| m.path == import_path)
470 else {
471 debug_assert!(false, "no importable module {import_path}");
472 return;
473 };
474 if let Some(ident) = import_module.syntax.decl_ident(&import_ident.name()) {
475 ty.path = None;
477 ty.ident = ident;
478 return;
479 } else if let Some((_, item)) = import_module
480 .imports
481 .iter()
482 .find(|(ident, _)| *ident.name() == *import_ident.name())
483 {
484 import_path = resolver.canonical_path(&item.path);
488 import_ident = item.ident.clone();
489 } else {
490 debug_assert!(false, "no declaration {import_ident} in {import_path}");
491 return;
492 }
493 }
494 }
495 }
496 }
497
498 for i in 0..modules.len() {
499 let (left, right) = modules.split_at_mut(i);
502 let (module, right) =
503 right.split_first_mut().unwrap();
504 let other_modules = left.iter().chain(right.iter());
505
506 let Some(module_used_items) = used_items.get_module(&module.path) else {
507 debug_assert!(false, "missing module {} in retarget_idents", module.path);
508 continue;
509 };
510
511 for decl in &mut module.syntax.global_declarations {
512 if let Some(ident) = decl.ident()
514 && !module_used_items.contains_key(&ident)
515 {
516 continue;
517 }
518
519 for ty in Visit::<TypeExpression>::visit_mut(decl.node_mut()) {
520 retarget_ty(
521 ty,
522 &module.path,
523 &module.imports,
524 module_used_items,
525 other_modules.clone(),
526 resolver,
527 );
528 }
529 }
530 }
531}
532
533#[test]
539fn test_retarget_idents() {
540 use std::collections::HashSet;
541
542 let source = r#"
543 import i0;
544 import i::{i0, i0, j};
545 const_assert i0+c1+v2+a3+s4; // due to hoisting, all global decls idents should be visible
546 const c1: c1 = i0+c1+v2+a3+s4;
547 var<private> v2: v2 = i0+c1+v2+a3+s4;
548 alias a3 = a3<i0, c1, v2, a3>;
549 struct s4 { m: m5 }
550
551 fn f18(p: p6) {
552 let x7 = i0+c1+v2+a3+s4;
553 let x7 = x7;
554
555 // shadowing with local declarations
556 let i8 = i0+c1+v2+a3+s4;
557 let c9 = i8+c1+v2+a3+s4;
558 let v10 = i8+c9+v2+a3+s4;
559 let a11 = i8+c9+v10+a3+s4;
560 let s12 = i8+c9+v10+a11+s4;
561 let x7 = i8+c9+v10+a11+s12; // x is in the same scope as previous x: should have same ident
562
563 {
564 let x13 = i8+c9+v10+a11+s12 +x7; // different x
565 let x13 = x13;
566
567 for (let x14 = x13; x14; x14++) {
568 let x14: x14 = x14; // in for loops, the initializer cannot be shadowed
569 }
570
571 loop {
572 var x15 = x13;
573 const y17 = x15;
574 continuing {
575 let x16 = x15;
576 break if x16 + y17;
577 }
578 }
579 }
580
581 switch x7 {
582 case x7, f18, g19 { x7 = x7; }
583 }
584 }
585
586 fn f18() {} // f is in the same scope as previous f: should have same ident
587 fn g19() {}
588 "#;
589
590 let module: TranslationUnit = source.parse().expect("parse failure");
591 let source_stripped = source.replace(|c: char| c.is_ascii_digit(), "");
592 let mut module_stripped: TranslationUnit = source_stripped.parse().expect("parse failure");
593 retarget_idents(&mut module_stripped);
594
595 let mut idents = HashSet::new();
596 let mut ordered_idents = Vec::new();
597 for ty in Visit::<TypeExpression>::visit(&module_stripped) {
599 let inserted = idents.insert(ty.ident.clone());
600 if inserted {
601 ordered_idents.push(ty.ident.clone());
602 }
603 }
604
605 for (i, ident) in ordered_idents.iter().enumerate() {
606 println!("ident #{i}: {ident}, count: {}", ident.use_count() - 2);
607 ident.clone().rename(format!("{ident}{i}"));
608 }
609
610 println!("=== expectation ===\n{module}");
611 println!("=== test output ===\n{module_stripped}");
612
613 assert_eq!(module.to_string(), module_stripped.to_string())
614}
615
616#[test]
617fn test_qualified_reference() {
618 let mut module: TranslationUnit = "fn value() -> u32 { return package::helper::value(); }"
619 .parse()
620 .unwrap();
621 retarget_idents(&mut module);
622 assert!(crate::pass::validate_wesl(&module).is_ok());
623}