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.
46    /// E.g.:
47    ///  - *my_ref
48    ///  - **if x > 0 { &mut &mut a } else { &mut &mut b }
49    ///
50    /// The [TyExpression] is guaranteed to be of [TyExpressionVariant::Deref].
51    Deref(Box<TyExpression>),
52}
53
54impl EqWithEngines for TyReassignmentTarget {}
55impl PartialEqWithEngines for TyReassignmentTarget {
56    fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
57        let type_engine = ctx.engines().te();
58        match (self, other) {
59            (TyReassignmentTarget::Deref(l), TyReassignmentTarget::Deref(r)) => (*l).eq(r, ctx),
60            (
61                TyReassignmentTarget::ElementAccess {
62                    base_name: l_name,
63                    base_type: l_type,
64                    indices: l_indices,
65                },
66                TyReassignmentTarget::ElementAccess {
67                    base_name: r_name,
68                    base_type: r_type,
69                    indices: r_indices,
70                },
71            ) => {
72                l_name == r_name
73                    && (l_type == r_type
74                        || type_engine.get(*l_type).eq(&type_engine.get(*r_type), ctx))
75                    && l_indices.eq(r_indices, ctx)
76            }
77            _ => false,
78        }
79    }
80}
81
82impl EqWithEngines for TyReassignment {}
83impl PartialEqWithEngines for TyReassignment {
84    fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
85        self.lhs.eq(&other.lhs, ctx) && self.rhs.eq(&other.rhs, ctx)
86    }
87}
88
89impl HashWithEngines for TyReassignmentTarget {
90    fn hash<H: Hasher>(&self, state: &mut H, engines: &Engines) {
91        let type_engine = engines.te();
92        match self {
93            TyReassignmentTarget::Deref(exp) => exp.hash(state, engines),
94            TyReassignmentTarget::ElementAccess {
95                base_name,
96                base_type,
97                indices,
98            } => {
99                base_name.hash(state);
100                type_engine.get(*base_type).hash(state, engines);
101                indices.hash(state, engines);
102            }
103        };
104    }
105}
106
107impl HashWithEngines for TyReassignment {
108    fn hash<H: Hasher>(&self, state: &mut H, engines: &Engines) {
109        let TyReassignment { lhs, rhs } = self;
110
111        lhs.hash(state, engines);
112        rhs.hash(state, engines);
113    }
114}
115
116impl SubstTypes for TyReassignmentTarget {
117    fn subst_inner(&mut self, ctx: &SubstTypesContext) -> HasChanges {
118        has_changes! {
119            match self {
120                TyReassignmentTarget::Deref(exp) => exp.subst(ctx),
121                TyReassignmentTarget::ElementAccess { base_type, indices, .. } => {
122                    has_changes! {
123                        base_type.subst(ctx);
124                        indices.subst(ctx);
125                    }
126                }
127            };
128        }
129    }
130}
131
132impl SubstTypes for TyReassignment {
133    fn subst_inner(&mut self, ctx: &SubstTypesContext) -> HasChanges {
134        has_changes! {
135            self.lhs.subst(ctx);
136            self.rhs.subst(ctx);
137        }
138    }
139}
140
141impl ReplaceDecls for TyReassignmentTarget {
142    fn replace_decls_inner(
143        &mut self,
144        decl_mapping: &DeclMapping,
145        handler: &Handler,
146        ctx: &mut TypeCheckContext,
147    ) -> Result<bool, ErrorEmitted> {
148        Ok(match self {
149            TyReassignmentTarget::Deref(exp) => exp.replace_decls(decl_mapping, handler, ctx)?,
150            TyReassignmentTarget::ElementAccess { indices, .. } => indices
151                .iter_mut()
152                .map(|i| i.replace_decls(decl_mapping, handler, ctx))
153                .collect::<Result<Vec<bool>, _>>()?
154                .iter()
155                .any(|is_changed| *is_changed),
156        })
157    }
158}
159
160impl ReplaceDecls for TyReassignment {
161    fn replace_decls_inner(
162        &mut self,
163        decl_mapping: &DeclMapping,
164        handler: &Handler,
165        ctx: &mut TypeCheckContext,
166    ) -> Result<bool, ErrorEmitted> {
167        let lhs_changed = self.lhs.replace_decls(decl_mapping, handler, ctx)?;
168        let rhs_changed = self.rhs.replace_decls(decl_mapping, handler, ctx)?;
169
170        Ok(lhs_changed || rhs_changed)
171    }
172}
173
174impl TypeCheckAnalysis for TyReassignmentTarget {
175    fn type_check_analyze(
176        &self,
177        handler: &Handler,
178        ctx: &mut TypeCheckAnalysisContext,
179    ) -> Result<(), ErrorEmitted> {
180        match self {
181            TyReassignmentTarget::Deref(exp) => exp.type_check_analyze(handler, ctx)?,
182            TyReassignmentTarget::ElementAccess { indices, .. } => indices
183                .iter()
184                .map(|i| i.type_check_analyze(handler, ctx))
185                .collect::<Result<Vec<()>, _>>()
186                .map(|_| ())?,
187        };
188        Ok(())
189    }
190}
191
192impl TypeCheckAnalysis for TyReassignment {
193    fn type_check_analyze(
194        &self,
195        handler: &Handler,
196        ctx: &mut TypeCheckAnalysisContext,
197    ) -> Result<(), ErrorEmitted> {
198        self.lhs.type_check_analyze(handler, ctx)?;
199        self.rhs.type_check_analyze(handler, ctx)?;
200
201        Ok(())
202    }
203}
204
205impl TypeCheckFinalization for TyReassignmentTarget {
206    fn type_check_finalize(
207        &mut self,
208        handler: &Handler,
209        ctx: &mut TypeCheckFinalizationContext,
210    ) -> Result<(), ErrorEmitted> {
211        match self {
212            TyReassignmentTarget::Deref(exp) => exp.type_check_finalize(handler, ctx)?,
213            TyReassignmentTarget::ElementAccess { indices, .. } => indices
214                .iter_mut()
215                .map(|i| i.type_check_finalize(handler, ctx))
216                .collect::<Result<Vec<()>, _>>()
217                .map(|_| ())?,
218        };
219        Ok(())
220    }
221}
222
223impl TypeCheckFinalization for TyReassignment {
224    fn type_check_finalize(
225        &mut self,
226        handler: &Handler,
227        ctx: &mut TypeCheckFinalizationContext,
228    ) -> Result<(), ErrorEmitted> {
229        self.lhs.type_check_finalize(handler, ctx)?;
230        self.rhs.type_check_finalize(handler, ctx)?;
231
232        Ok(())
233    }
234}
235
236impl UpdateConstantExpression for TyReassignmentTarget {
237    fn update_constant_expression(&mut self, engines: &Engines, implementing_type: &TyDecl) {
238        match self {
239            TyReassignmentTarget::Deref(exp) => {
240                exp.update_constant_expression(engines, implementing_type)
241            }
242            TyReassignmentTarget::ElementAccess { indices, .. } => {
243                indices
244                    .iter_mut()
245                    .for_each(|i| i.update_constant_expression(engines, implementing_type));
246            }
247        };
248    }
249}
250
251impl UpdateConstantExpression for TyReassignment {
252    fn update_constant_expression(&mut self, engines: &Engines, implementing_type: &TyDecl) {
253        self.lhs
254            .update_constant_expression(engines, implementing_type);
255        self.rhs
256            .update_constant_expression(engines, implementing_type);
257    }
258}
259
260#[derive(Clone, Debug, Serialize, Deserialize)]
261pub enum ProjectionKind {
262    StructField {
263        name: Ident,
264    },
265    TupleField {
266        index: usize,
267        index_span: Span,
268    },
269    ArrayIndex {
270        index: Box<TyExpression>,
271        index_span: Span,
272    },
273}
274
275impl EqWithEngines for ProjectionKind {}
276impl PartialEqWithEngines for ProjectionKind {
277    fn eq(&self, other: &Self, ctx: &PartialEqWithEnginesContext) -> bool {
278        match (self, other) {
279            (
280                ProjectionKind::StructField { name: l_name },
281                ProjectionKind::StructField { name: r_name },
282            ) => l_name == r_name,
283            (
284                ProjectionKind::TupleField {
285                    index: l_index,
286                    index_span: l_index_span,
287                },
288                ProjectionKind::TupleField {
289                    index: r_index,
290                    index_span: r_index_span,
291                },
292            ) => l_index == r_index && l_index_span == r_index_span,
293            (
294                ProjectionKind::ArrayIndex {
295                    index: l_index,
296                    index_span: l_index_span,
297                },
298                ProjectionKind::ArrayIndex {
299                    index: r_index,
300                    index_span: r_index_span,
301                },
302            ) => l_index.eq(r_index, ctx) && l_index_span == r_index_span,
303            _ => false,
304        }
305    }
306}
307
308impl HashWithEngines for ProjectionKind {
309    fn hash<H: Hasher>(&self, state: &mut H, engines: &Engines) {
310        use ProjectionKind::*;
311        std::mem::discriminant(self).hash(state);
312        match self {
313            StructField { name } => name.hash(state),
314            TupleField {
315                index,
316                // these fields are not hashed because they aren't relevant/a
317                // reliable source of obj v. obj distinction
318                index_span: _,
319            } => index.hash(state),
320            ArrayIndex {
321                index,
322                // these fields are not hashed because they aren't relevant/a
323                // reliable source of obj v. obj distinction
324                index_span: _,
325            } => {
326                index.hash(state, engines);
327            }
328        }
329    }
330}
331
332impl SubstTypes for ProjectionKind {
333    fn subst_inner(&mut self, ctx: &SubstTypesContext) -> HasChanges {
334        use ProjectionKind::*;
335        match self {
336            ArrayIndex { index, .. } => index.subst(ctx),
337            _ => HasChanges::No,
338        }
339    }
340}
341
342impl ReplaceDecls for ProjectionKind {
343    fn replace_decls_inner(
344        &mut self,
345        decl_mapping: &DeclMapping,
346        handler: &Handler,
347        ctx: &mut TypeCheckContext,
348    ) -> Result<bool, ErrorEmitted> {
349        use ProjectionKind::*;
350        match self {
351            ArrayIndex { index, .. } => index.replace_decls(decl_mapping, handler, ctx),
352            _ => Ok(false),
353        }
354    }
355}
356
357impl TypeCheckAnalysis for ProjectionKind {
358    fn type_check_analyze(
359        &self,
360        handler: &Handler,
361        ctx: &mut TypeCheckAnalysisContext,
362    ) -> Result<(), ErrorEmitted> {
363        use ProjectionKind::*;
364        match self {
365            ArrayIndex { index, .. } => index.type_check_analyze(handler, ctx),
366            _ => Ok(()),
367        }
368    }
369}
370
371impl TypeCheckFinalization for ProjectionKind {
372    fn type_check_finalize(
373        &mut self,
374        handler: &Handler,
375        ctx: &mut TypeCheckFinalizationContext,
376    ) -> Result<(), ErrorEmitted> {
377        use ProjectionKind::*;
378        match self {
379            ArrayIndex { index, .. } => index.type_check_finalize(handler, ctx),
380            _ => Ok(()),
381        }
382    }
383}
384
385impl UpdateConstantExpression for ProjectionKind {
386    fn update_constant_expression(&mut self, engines: &Engines, implementing_type: &TyDecl) {
387        use ProjectionKind::*;
388        #[allow(clippy::single_match)]
389        // To keep it consistent and same looking as the above implementations.
390        match self {
391            ArrayIndex { index, .. } => {
392                index.update_constant_expression(engines, implementing_type)
393            }
394            _ => (),
395        }
396    }
397}
398
399impl Spanned for ProjectionKind {
400    fn span(&self) -> Span {
401        match self {
402            ProjectionKind::StructField { name } => name.span(),
403            ProjectionKind::TupleField { index_span, .. } => index_span.clone(),
404            ProjectionKind::ArrayIndex { index_span, .. } => index_span.clone(),
405        }
406    }
407}
408
409impl ProjectionKind {
410    pub(crate) fn pretty_print(&self) -> Cow<str> {
411        match self {
412            ProjectionKind::StructField { name } => Cow::Borrowed(name.as_str()),
413            ProjectionKind::TupleField { index, .. } => Cow::Owned(index.to_string()),
414            ProjectionKind::ArrayIndex { index, .. } => Cow::Owned(format!("{index:#?}")),
415        }
416    }
417}