sway_core/language/ty/expression/
reassignment.rs

1use crate::{
2    decl_engine::*,
3    engine_threading::*,
4    has_changes,
5    language::ty::*,
6    semantic_analysis::{
7        TypeCheckAnalysis, TypeCheckAnalysisContext, TypeCheckContext, TypeCheckFinalization,
8        TypeCheckFinalizationContext,
9    },
10    type_system::*,
11};
12use serde::{Deserialize, Serialize};
13use std::{
14    borrow::Cow,
15    hash::{Hash, Hasher},
16};
17use sway_error::handler::{ErrorEmitted, Handler};
18use sway_types::{Ident, Span, Spanned};
19
20#[derive(Clone, Debug, Serialize, Deserialize)]
21pub struct TyReassignment {
22    pub lhs: TyReassignmentTarget,
23    pub rhs: TyExpression,
24}
25
26#[derive(Clone, Debug, Serialize, Deserialize)]
27pub enum TyReassignmentTarget {
28    /// An [TyExpression] representing a single variable or a path
29    /// to a part of an aggregate.
30    /// E.g.:
31    ///  - `my_variable`
32    ///  - `array[0].field.x.1`
33    ElementAccess {
34        /// [Ident] of the single variable, or the starting variable
35        /// of the path to a part of an aggregate.
36        base_name: Ident,
37        /// [TypeId] of the variable behind the `base_name`.
38        base_type: TypeId,
39        /// Indices representing the path from the `base_name` to the
40        /// final part of an aggregate.
41        /// Empty if the LHS of the reassignment is a single variable.
42        indices: Vec<ProjectionKind>,
43    },
44    /// An dereferencing [TyExpression] representing dereferencing
45    /// of an arbitrary reference expression with optional indices.
46    /// E.g.:
47    ///  - *my_ref
48    ///  - **if x > 0 { &mut &mut a } else { &mut &mut b }
49    ///  - (*my_ref)\[0\]
50    ///  - (**my_ref)\[0\]
51    ///
52    /// The [TyExpression] is guaranteed to be of [TyExpressionVariant::Deref].
53    DerefAccess {
54        /// [TyExpression] of one or multiple nested [TyExpressionVariant::Deref].
55        exp: Box<TyExpression>,
56        /// Indices representing the path from the `base_name` to the
57        /// final part of an aggregate.
58        /// Empty if the LHS of the reassignment is a single variable.
59        indices: Vec<ProjectionKind>,
60    },
61}
62
63impl EqWithEngines for TyReassignmentTarget {}
64impl PartialEqWithEngines for TyReassignmentTarget {
65    fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
66        let type_engine = ctx.engines().te();
67        match (self, other) {
68            (
69                TyReassignmentTarget::DerefAccess {
70                    exp: l,
71                    indices: l_indices,
72                },
73                TyReassignmentTarget::DerefAccess {
74                    exp: r,
75                    indices: r_indices,
76                },
77            ) => (*l).eq(r, ctx) && l_indices.eq(r_indices, ctx),
78            (
79                TyReassignmentTarget::ElementAccess {
80                    base_name: l_name,
81                    base_type: l_type,
82                    indices: l_indices,
83                },
84                TyReassignmentTarget::ElementAccess {
85                    base_name: r_name,
86                    base_type: r_type,
87                    indices: r_indices,
88                },
89            ) => {
90                l_name == r_name
91                    && (l_type == r_type
92                        || type_engine.get(*l_type).eq(&type_engine.get(*r_type), ctx))
93                    && l_indices.eq(r_indices, ctx)
94            }
95            _ => false,
96        }
97    }
98}
99
100impl EqWithEngines for TyReassignment {}
101impl PartialEqWithEngines for TyReassignment {
102    fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
103        self.lhs.eq(&other.lhs, ctx) && self.rhs.eq(&other.rhs, ctx)
104    }
105}
106
107impl HashWithEngines for TyReassignmentTarget {
108    fn hash<H: Hasher>(&self, state: &mut H, engines: &Engines) {
109        let type_engine = engines.te();
110        match self {
111            TyReassignmentTarget::DerefAccess { exp, indices } => {
112                exp.hash(state, engines);
113                indices.hash(state, engines);
114            }
115            TyReassignmentTarget::ElementAccess {
116                base_name,
117                base_type,
118                indices,
119            } => {
120                base_name.hash(state);
121                type_engine.get(*base_type).hash(state, engines);
122                indices.hash(state, engines);
123            }
124        };
125    }
126}
127
128impl HashWithEngines for TyReassignment {
129    fn hash<H: Hasher>(&self, state: &mut H, engines: &Engines) {
130        let TyReassignment { lhs, rhs } = self;
131
132        lhs.hash(state, engines);
133        rhs.hash(state, engines);
134    }
135}
136
137impl SubstTypes for TyReassignmentTarget {
138    fn subst_inner(&mut self, ctx: &SubstTypesContext) -> HasChanges {
139        has_changes! {
140            match self {
141                TyReassignmentTarget::DerefAccess{exp, indices} => {
142                    has_changes! {
143                        exp.subst(ctx);
144                        indices.subst(ctx);
145                    }
146                },
147                TyReassignmentTarget::ElementAccess { base_type, indices, .. } => {
148                    has_changes! {
149                        base_type.subst(ctx);
150                        indices.subst(ctx);
151                    }
152                }
153            };
154        }
155    }
156}
157
158impl SubstTypes for TyReassignment {
159    fn subst_inner(&mut self, ctx: &SubstTypesContext) -> HasChanges {
160        has_changes! {
161            self.lhs.subst(ctx);
162            self.rhs.subst(ctx);
163        }
164    }
165}
166
167impl ReplaceDecls for TyReassignmentTarget {
168    fn replace_decls_inner(
169        &mut self,
170        decl_mapping: &DeclMapping,
171        handler: &Handler,
172        ctx: &mut TypeCheckContext,
173    ) -> Result<bool, ErrorEmitted> {
174        Ok(match self {
175            TyReassignmentTarget::DerefAccess { exp, indices } => {
176                let mut changed = exp.replace_decls(decl_mapping, handler, ctx)?;
177                changed |= indices
178                    .iter_mut()
179                    .map(|i| i.replace_decls(decl_mapping, handler, ctx))
180                    .collect::<Result<Vec<bool>, _>>()?
181                    .iter()
182                    .any(|is_changed| *is_changed);
183                changed
184            }
185            TyReassignmentTarget::ElementAccess { indices, .. } => indices
186                .iter_mut()
187                .map(|i| i.replace_decls(decl_mapping, handler, ctx))
188                .collect::<Result<Vec<bool>, _>>()?
189                .iter()
190                .any(|is_changed| *is_changed),
191        })
192    }
193}
194
195impl ReplaceDecls for TyReassignment {
196    fn replace_decls_inner(
197        &mut self,
198        decl_mapping: &DeclMapping,
199        handler: &Handler,
200        ctx: &mut TypeCheckContext,
201    ) -> Result<bool, ErrorEmitted> {
202        let lhs_changed = self.lhs.replace_decls(decl_mapping, handler, ctx)?;
203        let rhs_changed = self.rhs.replace_decls(decl_mapping, handler, ctx)?;
204
205        Ok(lhs_changed || rhs_changed)
206    }
207}
208
209impl TypeCheckAnalysis for TyReassignmentTarget {
210    fn type_check_analyze(
211        &self,
212        handler: &Handler,
213        ctx: &mut TypeCheckAnalysisContext,
214    ) -> Result<(), ErrorEmitted> {
215        match self {
216            TyReassignmentTarget::DerefAccess { exp, indices } => {
217                exp.type_check_analyze(handler, ctx)?;
218                indices
219                    .iter()
220                    .map(|i| i.type_check_analyze(handler, ctx))
221                    .collect::<Result<Vec<()>, _>>()
222                    .map(|_| ())?
223            }
224            TyReassignmentTarget::ElementAccess { indices, .. } => indices
225                .iter()
226                .map(|i| i.type_check_analyze(handler, ctx))
227                .collect::<Result<Vec<()>, _>>()
228                .map(|_| ())?,
229        };
230        Ok(())
231    }
232}
233
234impl TypeCheckAnalysis for TyReassignment {
235    fn type_check_analyze(
236        &self,
237        handler: &Handler,
238        ctx: &mut TypeCheckAnalysisContext,
239    ) -> Result<(), ErrorEmitted> {
240        self.lhs.type_check_analyze(handler, ctx)?;
241        self.rhs.type_check_analyze(handler, ctx)?;
242
243        Ok(())
244    }
245}
246
247impl TypeCheckFinalization for TyReassignmentTarget {
248    fn type_check_finalize(
249        &mut self,
250        handler: &Handler,
251        ctx: &mut TypeCheckFinalizationContext,
252    ) -> Result<(), ErrorEmitted> {
253        match self {
254            TyReassignmentTarget::DerefAccess { exp, indices } => {
255                exp.type_check_finalize(handler, ctx)?;
256                indices
257                    .iter_mut()
258                    .map(|i| i.type_check_finalize(handler, ctx))
259                    .collect::<Result<Vec<()>, _>>()
260                    .map(|_| ())?;
261            }
262            TyReassignmentTarget::ElementAccess { indices, .. } => indices
263                .iter_mut()
264                .map(|i| i.type_check_finalize(handler, ctx))
265                .collect::<Result<Vec<()>, _>>()
266                .map(|_| ())?,
267        };
268        Ok(())
269    }
270}
271
272impl TypeCheckFinalization for TyReassignment {
273    fn type_check_finalize(
274        &mut self,
275        handler: &Handler,
276        ctx: &mut TypeCheckFinalizationContext,
277    ) -> Result<(), ErrorEmitted> {
278        self.lhs.type_check_finalize(handler, ctx)?;
279        self.rhs.type_check_finalize(handler, ctx)?;
280
281        Ok(())
282    }
283}
284
285impl UpdateConstantExpression for TyReassignmentTarget {
286    fn update_constant_expression(&mut self, engines: &Engines, implementing_type: &TyDecl) {
287        match self {
288            TyReassignmentTarget::DerefAccess { exp, indices } => {
289                exp.update_constant_expression(engines, implementing_type);
290                indices
291                    .iter_mut()
292                    .for_each(|i| i.update_constant_expression(engines, implementing_type));
293            }
294            TyReassignmentTarget::ElementAccess { indices, .. } => {
295                indices
296                    .iter_mut()
297                    .for_each(|i| i.update_constant_expression(engines, implementing_type));
298            }
299        };
300    }
301}
302
303impl UpdateConstantExpression for TyReassignment {
304    fn update_constant_expression(&mut self, engines: &Engines, implementing_type: &TyDecl) {
305        self.lhs
306            .update_constant_expression(engines, implementing_type);
307        self.rhs
308            .update_constant_expression(engines, implementing_type);
309    }
310}
311
312#[derive(Clone, Debug, Serialize, Deserialize)]
313pub enum ProjectionKind {
314    StructField {
315        name: Ident,
316        field_to_access: Option<Box<TyStructField>>,
317    },
318    TupleField {
319        index: usize,
320        index_span: Span,
321    },
322    ArrayIndex {
323        index: Box<TyExpression>,
324        index_span: Span,
325    },
326}
327
328impl EqWithEngines for ProjectionKind {}
329impl PartialEqWithEngines for ProjectionKind {
330    fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
331        match (self, other) {
332            (
333                ProjectionKind::StructField {
334                    name: l_name,
335                    field_to_access: _,
336                },
337                ProjectionKind::StructField {
338                    name: r_name,
339                    field_to_access: _,
340                },
341            ) => l_name == r_name,
342            (
343                ProjectionKind::TupleField {
344                    index: l_index,
345                    index_span: l_index_span,
346                },
347                ProjectionKind::TupleField {
348                    index: r_index,
349                    index_span: r_index_span,
350                },
351            ) => l_index == r_index && l_index_span == r_index_span,
352            (
353                ProjectionKind::ArrayIndex {
354                    index: l_index,
355                    index_span: l_index_span,
356                },
357                ProjectionKind::ArrayIndex {
358                    index: r_index,
359                    index_span: r_index_span,
360                },
361            ) => l_index.eq(r_index, ctx) && l_index_span == r_index_span,
362            _ => false,
363        }
364    }
365}
366
367impl HashWithEngines for ProjectionKind {
368    fn hash<H: Hasher>(&self, state: &mut H, engines: &Engines) {
369        use ProjectionKind::*;
370        std::mem::discriminant(self).hash(state);
371        match self {
372            StructField {
373                name,
374                field_to_access: _,
375            } => name.hash(state),
376            TupleField {
377                index,
378                // these fields are not hashed because they aren't relevant/a
379                // reliable source of obj v. obj distinction
380                index_span: _,
381            } => index.hash(state),
382            ArrayIndex {
383                index,
384                // these fields are not hashed because they aren't relevant/a
385                // reliable source of obj v. obj distinction
386                index_span: _,
387            } => {
388                index.hash(state, engines);
389            }
390        }
391    }
392}
393
394impl SubstTypes for ProjectionKind {
395    fn subst_inner(&mut self, ctx: &SubstTypesContext) -> HasChanges {
396        use ProjectionKind::*;
397        match self {
398            ArrayIndex { index, .. } => index.subst(ctx),
399            _ => HasChanges::No,
400        }
401    }
402}
403
404impl ReplaceDecls for ProjectionKind {
405    fn replace_decls_inner(
406        &mut self,
407        decl_mapping: &DeclMapping,
408        handler: &Handler,
409        ctx: &mut TypeCheckContext,
410    ) -> Result<bool, ErrorEmitted> {
411        use ProjectionKind::*;
412        match self {
413            ArrayIndex { index, .. } => index.replace_decls(decl_mapping, handler, ctx),
414            _ => Ok(false),
415        }
416    }
417}
418
419impl TypeCheckAnalysis for ProjectionKind {
420    fn type_check_analyze(
421        &self,
422        handler: &Handler,
423        ctx: &mut TypeCheckAnalysisContext,
424    ) -> Result<(), ErrorEmitted> {
425        use ProjectionKind::*;
426        match self {
427            ArrayIndex { index, .. } => index.type_check_analyze(handler, ctx),
428            _ => Ok(()),
429        }
430    }
431}
432
433impl TypeCheckFinalization for ProjectionKind {
434    fn type_check_finalize(
435        &mut self,
436        handler: &Handler,
437        ctx: &mut TypeCheckFinalizationContext,
438    ) -> Result<(), ErrorEmitted> {
439        use ProjectionKind::*;
440        match self {
441            ArrayIndex { index, .. } => index.type_check_finalize(handler, ctx),
442            _ => Ok(()),
443        }
444    }
445}
446
447impl UpdateConstantExpression for ProjectionKind {
448    fn update_constant_expression(&mut self, engines: &Engines, implementing_type: &TyDecl) {
449        use ProjectionKind::*;
450        #[allow(clippy::single_match)]
451        // To keep it consistent and same looking as the above implementations.
452        match self {
453            ArrayIndex { index, .. } => {
454                index.update_constant_expression(engines, implementing_type)
455            }
456            _ => (),
457        }
458    }
459}
460
461impl Spanned for ProjectionKind {
462    fn span(&self) -> Span {
463        match self {
464            ProjectionKind::StructField {
465                name,
466                field_to_access: _,
467            } => name.span(),
468            ProjectionKind::TupleField { index_span, .. } => index_span.clone(),
469            ProjectionKind::ArrayIndex { index_span, .. } => index_span.clone(),
470        }
471    }
472}
473
474impl ProjectionKind {
475    pub(crate) fn pretty_print(&self) -> Cow<str> {
476        match self {
477            ProjectionKind::StructField {
478                name,
479                field_to_access: _,
480            } => Cow::Borrowed(name.as_str()),
481            ProjectionKind::TupleField { index, .. } => Cow::Owned(index.to_string()),
482            ProjectionKind::ArrayIndex { index, .. } => Cow::Owned(format!("{index:#?}")),
483        }
484    }
485}