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<(), ErrorEmitted> {
256        match self {
257            TyReassignmentTarget::DerefAccess { exp, indices } => {
258                exp.type_check_finalize(handler, ctx)?;
259                indices
260                    .iter_mut()
261                    .map(|i| i.type_check_finalize(handler, ctx))
262                    .collect::<Result<Vec<()>, _>>()
263                    .map(|_| ())?;
264            }
265            TyReassignmentTarget::ElementAccess { indices, .. } => indices
266                .iter_mut()
267                .map(|i| i.type_check_finalize(handler, ctx))
268                .collect::<Result<Vec<()>, _>>()
269                .map(|_| ())?,
270        };
271        Ok(())
272    }
273}
274
275impl TypeCheckFinalization for TyReassignment {
276    fn type_check_finalize(
277        &mut self,
278        handler: &Handler,
279        ctx: &mut TypeCheckFinalizationContext,
280    ) -> Result<(), ErrorEmitted> {
281        self.lhs.type_check_finalize(handler, ctx)?;
282        self.rhs.type_check_finalize(handler, ctx)?;
283
284        Ok(())
285    }
286}
287
288impl UpdateConstantExpression for TyReassignmentTarget {
289    fn update_constant_expression(
290        &mut self,
291        engines: &Engines,
292        implementing_type: &TyDecl,
293    ) -> HasChanges {
294        match self {
295            TyReassignmentTarget::DerefAccess { exp, indices } => has_changes! {
296                exp.update_constant_expression(engines, implementing_type);
297                indices
298                    .iter_mut()
299                    .fold(HasChanges::No, |acc, i| {
300                        acc | i.update_constant_expression(engines, implementing_type)
301                    });
302            },
303            TyReassignmentTarget::ElementAccess { indices, .. } => {
304                indices.iter_mut().fold(HasChanges::No, |acc, i| {
305                    acc | i.update_constant_expression(engines, implementing_type)
306                })
307            }
308        }
309    }
310}
311
312impl UpdateConstantExpression for TyReassignment {
313    fn update_constant_expression(
314        &mut self,
315        engines: &Engines,
316        implementing_type: &TyDecl,
317    ) -> HasChanges {
318        has_changes! {
319            self.lhs.update_constant_expression(engines, implementing_type);
320            self.rhs.update_constant_expression(engines, implementing_type);
321        }
322    }
323}
324
325#[derive(Clone, Debug, Serialize, Deserialize)]
326pub enum ProjectionKind {
327    StructField {
328        name: Ident,
329        field_to_access: Option<Box<TyStructField>>,
330    },
331    TupleField {
332        index: usize,
333        index_span: Span,
334    },
335    ArrayIndex {
336        index: Box<TyExpression>,
337        index_span: Span,
338    },
339}
340
341impl EqWithEngines for ProjectionKind {}
342impl PartialEqWithEngines for ProjectionKind {
343    fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
344        match (self, other) {
345            (
346                ProjectionKind::StructField {
347                    name: l_name,
348                    field_to_access: _,
349                },
350                ProjectionKind::StructField {
351                    name: r_name,
352                    field_to_access: _,
353                },
354            ) => l_name == r_name,
355            (
356                ProjectionKind::TupleField {
357                    index: l_index,
358                    index_span: l_index_span,
359                },
360                ProjectionKind::TupleField {
361                    index: r_index,
362                    index_span: r_index_span,
363                },
364            ) => l_index == r_index && l_index_span == r_index_span,
365            (
366                ProjectionKind::ArrayIndex {
367                    index: l_index,
368                    index_span: l_index_span,
369                },
370                ProjectionKind::ArrayIndex {
371                    index: r_index,
372                    index_span: r_index_span,
373                },
374            ) => l_index.eq(r_index, ctx) && l_index_span == r_index_span,
375            _ => false,
376        }
377    }
378}
379
380impl HashWithEngines for ProjectionKind {
381    fn hash<H: Hasher>(&self, state: &mut H, engines: &Engines) {
382        use ProjectionKind::*;
383        std::mem::discriminant(self).hash(state);
384        match self {
385            StructField {
386                name,
387                field_to_access: _,
388            } => name.hash(state),
389            TupleField {
390                index,
391                // these fields are not hashed because they aren't relevant/a
392                // reliable source of obj v. obj distinction
393                index_span: _,
394            } => index.hash(state),
395            ArrayIndex {
396                index,
397                // these fields are not hashed because they aren't relevant/a
398                // reliable source of obj v. obj distinction
399                index_span: _,
400            } => {
401                index.hash(state, engines);
402            }
403        }
404    }
405}
406
407impl SubstTypes for ProjectionKind {
408    fn subst_inner(&mut self, ctx: &SubstTypesContext) -> HasChanges {
409        use ProjectionKind::*;
410        match self {
411            ArrayIndex { index, .. } => index.subst(ctx),
412            _ => HasChanges::No,
413        }
414    }
415}
416
417impl ReplaceDecls for ProjectionKind {
418    fn replace_decls_inner(
419        &mut self,
420        decl_mapping: &DeclMapping,
421        handler: &Handler,
422        ctx: &mut TypeCheckContext,
423    ) -> Result<HasChanges, ErrorEmitted> {
424        use ProjectionKind::*;
425        match self {
426            ArrayIndex { index, .. } => index.replace_decls(decl_mapping, handler, ctx),
427            _ => Ok(HasChanges::No),
428        }
429    }
430}
431
432impl TypeCheckAnalysis for ProjectionKind {
433    fn type_check_analyze(
434        &self,
435        handler: &Handler,
436        ctx: &mut TypeCheckAnalysisContext,
437    ) -> Result<(), ErrorEmitted> {
438        use ProjectionKind::*;
439        match self {
440            ArrayIndex { index, .. } => index.type_check_analyze(handler, ctx),
441            _ => Ok(()),
442        }
443    }
444}
445
446impl TypeCheckFinalization for ProjectionKind {
447    fn type_check_finalize(
448        &mut self,
449        handler: &Handler,
450        ctx: &mut TypeCheckFinalizationContext,
451    ) -> Result<(), ErrorEmitted> {
452        use ProjectionKind::*;
453        match self {
454            ArrayIndex { index, .. } => index.type_check_finalize(handler, ctx),
455            _ => Ok(()),
456        }
457    }
458}
459
460impl UpdateConstantExpression for ProjectionKind {
461    fn update_constant_expression(
462        &mut self,
463        engines: &Engines,
464        implementing_type: &TyDecl,
465    ) -> HasChanges {
466        use ProjectionKind::*;
467        match self {
468            ArrayIndex { index, .. } => {
469                index.update_constant_expression(engines, implementing_type)
470            }
471            _ => HasChanges::No,
472        }
473    }
474}
475
476impl Spanned for ProjectionKind {
477    fn span(&self) -> Span {
478        match self {
479            ProjectionKind::StructField {
480                name,
481                field_to_access: _,
482            } => name.span(),
483            ProjectionKind::TupleField { index_span, .. } => index_span.clone(),
484            ProjectionKind::ArrayIndex { index_span, .. } => index_span.clone(),
485        }
486    }
487}
488
489impl ProjectionKind {
490    pub(crate) fn pretty_print(&self) -> Cow<'_, str> {
491        match self {
492            ProjectionKind::StructField {
493                name,
494                field_to_access: _,
495            } => Cow::Borrowed(name.as_str()),
496            ProjectionKind::TupleField { index, .. } => Cow::Owned(index.to_string()),
497            ProjectionKind::ArrayIndex { index, .. } => Cow::Owned(format!("{index:#?}")),
498        }
499    }
500}