Skip to main content

hax_rust_engine/
resugarings.rs

1//! The "resugaring" phases used by printers.
2
3//! This module defines resugarings instances (see
4//! [`hax_rust_engine::ast::Resugaring`] for the definition of a
5//! resugaring). Each backend defines its own set of resugaring phases.
6
7use crate::ast::identifiers::GlobalId;
8use crate::ast::resugared::*;
9use crate::ast::visitors::*;
10use crate::ast::*;
11use crate::printer::*;
12
13/// Transforms [`ItemKind::Fn`] of arity zero into [`ResugaredItemKind::Constant`].
14/// Rust `const` items are encoded by the `ImportThir` phase of the hax engine as function of arity zero.
15/// Functions of arity zero themselves are encoded as functions operating on one argument of type `()`.
16#[derive(Copy, Clone, Default)]
17pub struct FunctionsToConstants;
18
19impl AstVisitorMut for FunctionsToConstants {
20    fn enter_item_kind(&mut self, item_kind: &mut ItemKind) {
21        let ItemKind::Fn {
22            name,
23            generics,
24            body,
25            params,
26            safety: SafetyKind::Safe,
27        } = item_kind
28        else {
29            return;
30        };
31        if !params.is_empty() {
32            return;
33        }
34        *item_kind = ItemKind::Resugared(ResugaredItemKind::Constant {
35            name: *name,
36            body: body.clone(),
37            generics: generics.clone(),
38        });
39    }
40    fn enter_impl_item_kind(&mut self, item_kind: &mut ImplItemKind) {
41        if let ImplItemKind::Fn { body, params } = item_kind
42            && params.is_empty()
43        {
44            *item_kind =
45                ImplItemKind::Resugared(ResugaredImplItemKind::Constant { body: body.clone() })
46        }
47    }
48}
49
50impl Resugaring for FunctionsToConstants {
51    fn name(&self) -> String {
52        "functions-to-constants".to_string()
53    }
54}
55
56/// Tuples resugaring. Resugars tuple constructors to the dedicated expression variant [`ResugaredExprKind::Tuple`],
57/// and tuple types to the dedicated type variant [`ResugaredTyKind::Tuple`].
58pub struct Tuples;
59
60impl AstVisitorMut for Tuples {
61    fn enter_expr_kind(&mut self, x: &mut ExprKind) {
62        let (constructor, fields) = match x {
63            ExprKind::Construct {
64                constructor,
65                is_record: false,
66                is_struct: true,
67                base: None,
68                fields,
69            } => (constructor, &fields[..]),
70            ExprKind::GlobalId(constructor) => (constructor, &[][..]),
71            _ => return,
72        };
73        if constructor.expect_tuple().is_some() {
74            let args = fields.iter().map(|(_, e)| e).cloned().collect();
75            *x = ExprKind::Resugared(ResugaredExprKind::Tuple(args))
76        }
77    }
78    fn enter_ty_kind(&mut self, x: &mut TyKind) {
79        let TyKind::App { head, args } = x else {
80            return;
81        };
82        if head.expect_tuple().is_some() {
83            let Some(args) = args
84                .iter()
85                .map(GenericValue::expect_ty)
86                .collect::<Option<Vec<_>>>()
87            else {
88                return;
89            };
90            *x = TyKind::Resugared(ResugaredTyKind::Tuple(args.into_iter().cloned().collect()))
91        }
92    }
93}
94
95impl Resugaring for Tuples {
96    fn name(&self) -> String {
97        "tuples".to_string()
98    }
99}
100
101/// Let-pure resugaring. Use to identify expressions of the form `let x ← pure ..`, where the arrow
102/// can be turned into a normal assignment `:=`
103pub struct LetPure;
104
105impl AstVisitorMut for LetPure {
106    fn enter_expr_kind(&mut self, expr: &mut ExprKind) {
107        const PURE: GlobalId = crate::names::rust_primitives::hax::explicit_monadic::pure;
108        if let ExprKind::Let { lhs, rhs, body } = expr
109            && let ExprKind::App {
110                head,
111                args,
112                generic_args,
113                bounds_impls,
114                trait_: None,
115            } = rhs.kind()
116            && *head.kind() == ExprKind::GlobalId(PURE)
117            && let ([pure_rhs], [], []) = (&args[..], &generic_args[..], &bounds_impls[..])
118        {
119            *expr = ExprKind::Resugared(ResugaredExprKind::LetPure {
120                lhs: lhs.clone(),
121                rhs: pure_rhs.clone(),
122                body: body.clone(),
123            })
124        }
125    }
126}
127
128impl Resugaring for LetPure {
129    fn name(&self) -> String {
130        "let_pure".to_string()
131    }
132}
133
134/// Recursive function detection. Identifies functions whose body contains a
135/// reference to their own name and resugars them to [`ResugaredItemKind::RecursiveFn`].
136#[derive(Copy, Clone, Default)]
137pub struct RecursiveFunctions;
138
139/// Helper visitor that checks whether an expression tree contains a reference
140/// to a specific [`GlobalId`].
141struct SelfReferenceChecker {
142    target: GlobalId,
143    found: bool,
144}
145
146impl AstVisitor for SelfReferenceChecker {
147    fn enter_expr_kind(&mut self, kind: &ExprKind) {
148        if let ExprKind::GlobalId(id) = kind
149            && *id == self.target
150        {
151            self.found = true;
152        }
153    }
154}
155
156impl AstVisitorMut for RecursiveFunctions {
157    fn visit_item_kind(&mut self, item_kind: &mut ItemKind) {
158        if let ItemKind::Fn {
159            name,
160            generics,
161            body,
162            params,
163            safety,
164        } = &*item_kind
165        {
166            let mut checker = SelfReferenceChecker {
167                target: *name,
168                found: false,
169            };
170            checker.visit_expr(body);
171            if checker.found {
172                *item_kind = ItemKind::Resugared(ResugaredItemKind::RecursiveFn {
173                    name: *name,
174                    generics: generics.clone(),
175                    body: body.clone(),
176                    params: params.clone(),
177                    safety: safety.clone(),
178                });
179            }
180        }
181    }
182}
183
184impl Resugaring for RecursiveFunctions {
185    fn name(&self) -> String {
186        "recursive-functions".to_string()
187    }
188}
189
190/// Record ellipsis resugaring. Identifies record-like `Construct` patterns where
191/// some fields are wildcards and resugars them into `ConstructWithEllipsis`,
192/// dropping the wildcard fields so the printer can emit `..`.
193pub struct RecordEllipsis;
194
195impl AstVisitorMut for RecordEllipsis {
196    fn enter_pat_kind(&mut self, x: &mut PatKind) {
197        let PatKind::Construct {
198            constructor,
199            is_record: true,
200            is_struct,
201            fields,
202        } = x
203        else {
204            return;
205        };
206        let non_wild: Vec<_> = fields
207            .iter()
208            .filter(|(_, pat)| !matches!(&*pat.kind, PatKind::Wild))
209            .cloned()
210            .collect();
211        if non_wild.len() < fields.len() {
212            *x = ResugaredPatKind::ConstructWithEllipsis {
213                constructor: *constructor,
214                is_struct: *is_struct,
215                fields: non_wild,
216            }
217            .into();
218        }
219    }
220}
221
222impl Resugaring for RecordEllipsis {
223    fn name(&self) -> String {
224        "record-ellipsis".to_string()
225    }
226}