Skip to main content

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