sway_core/semantic_analysis/ast_node/expression/match_expression/typed/
matcher.rs

1use indexmap::IndexMap;
2
3use crate::{
4    language::{
5        ty::{self, TyConstantDecl},
6        CallPath, Literal,
7    },
8    semantic_analysis::{
9        ast_node::expression::typed_expression::{
10            instantiate_enum_unsafe_downcast, instantiate_struct_field_access,
11            instantiate_tuple_index_access,
12        },
13        TypeCheckContext,
14    },
15    Ident, TypeId, UnifyCheck,
16};
17
18use sway_error::{
19    error::CompileError,
20    handler::{ErrorEmitted, Handler},
21};
22
23use sway_types::{span::Span, Named, Spanned};
24
25/// A single requirement in the form `<lhs> == <rhs>` that has to be
26/// fulfilled for the match arm to match.
27pub(super) type MatchReq = (ty::TyExpression, ty::TyExpression);
28
29/// A single variable in the form `let <ident> = <expression>`
30/// that has to be extracted from the match arm.
31pub(super) type MatchVarDecl = (Ident, ty::TyExpression);
32
33/// A leaf of a match pattern can be either a requirement on the scrutinee or a
34/// variable declaration but not both at the same time.
35/// In the case of the catch-all `_` we will have neither a requirement nor
36/// a variable declaration.
37#[allow(clippy::large_enum_variant)]
38pub(super) enum ReqOrVarDecl {
39    /// Neither a requirement, nor a variable declaration.
40    /// Means a catch-all pattern.
41    Neither,
42    Req(MatchReq),
43    VarDecl(MatchVarDecl),
44}
45
46/// A tree structure that describes:
47/// - the overall requirement that needs to be satisfied in order for the match arm to match
48/// - all variable declarations within the match arm
49///
50/// The tree represents a logical expression that consists of equality comparisons, and
51/// lazy AND and OR operators.
52///
53/// The leaves of the tree are either equality comparisons or eventual variable declarations
54/// or none of those in the case of catch-all `_` pattern or only a single rest `..` in structs.
55pub(super) struct ReqDeclTree {
56    root: ReqDeclNode,
57}
58
59impl ReqDeclTree {
60    /// Creates a new tree that contains only one leaf node with the
61    /// [MatchReq] of the form `<lhs> == <rhs>`.
62    fn req(req: MatchReq) -> Self {
63        Self {
64            root: ReqDeclNode::ReqOrVarDecl(ReqOrVarDecl::Req(req)),
65        }
66    }
67
68    /// Creates a new tree that contains only the leaf node with the
69    /// [MatchVarDecl] `decl`.
70    fn decl(decl: MatchVarDecl) -> Self {
71        Self {
72            root: ReqDeclNode::ReqOrVarDecl(ReqOrVarDecl::VarDecl(decl)),
73        }
74    }
75
76    /// Creates a new tree that contains only the leaf node with
77    /// neither a requirement nor a variable declaration.
78    fn none() -> Self {
79        Self {
80            root: ReqDeclNode::ReqOrVarDecl(ReqOrVarDecl::Neither),
81        }
82    }
83
84    /// Creates a new tree that contains only an AND node
85    /// made of `nodes`.
86    fn and(nodes: Vec<ReqDeclNode>) -> Self {
87        Self {
88            root: ReqDeclNode::And(nodes),
89        }
90    }
91
92    /// Creates a new tree that contains only an OR node
93    /// made of `nodes`.
94    fn or(nodes: Vec<ReqDeclNode>) -> Self {
95        Self {
96            root: ReqDeclNode::Or(nodes),
97        }
98    }
99
100    pub fn root(&self) -> &ReqDeclNode {
101        &self.root
102    }
103}
104
105/// A single node in the [ReqDeclTree].
106#[allow(clippy::large_enum_variant)]
107pub(super) enum ReqDeclNode {
108    /// The leaf node. Contains the information about a single requirement or
109    /// variable declaration.
110    /// E.g., a catch all `_` will have neither a requirement nor a variable declaration.
111    /// E.g., a match arm variable `x` cannot have a requirement (it acts as catch all)
112    /// but it will have the declaration of the variable `x`.
113    /// E.g., a literal `123` will have a requirement on the scrutinee e.g. `struct.x == 123`.
114    ReqOrVarDecl(ReqOrVarDecl),
115    /// Represent the requirements and declarations connected with the lazy AND operator,
116    /// if there are more than two of them.
117    /// Notice that the vector of contained nodes can be empty or have only one element.
118    /// AND semantics is applied if there are two or more elements.
119    /// E.g., requirements coming from the struct and tuple patterns
120    /// must all be fulfilled in order for the whole pattern to match.
121    And(Vec<ReqDeclNode>),
122    /// Represent the requirements and declarations connected with the lazy OR operator,
123    /// if there are more than two of them.
124    /// Notice that the vector of contained nodes can be empty or have only one element.
125    /// OR semantics is applied if there are two or more elements.
126    /// Only the requirements coming from the individual variants of an OR match arm
127    /// will be connected with the OR operator.
128    Or(Vec<ReqDeclNode>),
129}
130
131impl ReqDeclNode {
132    /// Creates a new leaf node with the [MatchReq] of the form `<lhs> == <rhs>`.
133    fn req(req: MatchReq) -> Self {
134        ReqDeclNode::ReqOrVarDecl(ReqOrVarDecl::Req(req))
135    }
136
137    /// Creates a new leaf node with the [MatchVarDecl] `decl`.
138    fn decl(decl: MatchVarDecl) -> Self {
139        ReqDeclNode::ReqOrVarDecl(ReqOrVarDecl::VarDecl(decl))
140    }
141}
142
143/// The [matcher] returns the [ReqDeclTree] for the given `scrutinee` that tries
144/// to match the given expression `exp`.
145///
146/// Given the following example:
147///
148/// ```ignore
149/// struct Point {
150///     x: u64,
151///     y: u64
152/// }
153///
154/// let p = Point {
155///     x: 42,
156///     y: 24
157/// };
158///
159/// match p {
160///     Point { x, y: 5 } => { x },                        // 1.
161///     Point { x, y: 5 } | Point { x, y: 10 } => { x },   // 2.
162///     Point { x: 10, y: 24 } => { 1 },                   // 3.
163///     Point { x: 22, .. } => { 2 },                      // 4.
164///     Point { .. } => { 3 },                             // 5.
165///     _ => 0                                             // 6.
166/// }
167/// ```
168///
169/// the returned [ReqDeclTree] for each match arm will have the following form
170/// (square brackets represent each individual leaf node [ReqDeclNode::ReqOrVarDecl]):
171///
172/// ```ignore
173/// 1.
174///               &&
175///              /  \
176/// [let x = p.x]    [p.y == 5]
177///
178/// 2.
179///                             ||
180///                 ___________/  \____________
181///               &&                           &&
182///              /  \                         /  \
183/// [let x = p.x]    [p.y == 5]  [let x = p.x]    [p.y == 10]
184///
185/// 3.
186///             &&
187///            /  \
188/// [p.x == 10]    [p.y == 24]
189///
190/// 4.
191///      &&      // Note that this AND node has only one childe node.
192///      |
193/// [p.x == 22]
194///
195/// 5.
196///   &&      // Note that this AND node has only one childe node.
197///   |
198/// [None]
199///
200/// 6.
201/// [None]
202/// ```
203pub(super) fn matcher(
204    handler: &Handler,
205    ctx: TypeCheckContext,
206    match_value: &ty::TyExpression,
207    exp: &ty::TyExpression,
208    scrutinee: ty::TyScrutinee,
209) -> Result<ReqDeclTree, ErrorEmitted> {
210    let ty::TyScrutinee {
211        variant,
212        type_id,
213        span,
214    } = scrutinee;
215
216    let type_engine = ctx.engines.te();
217
218    // unify the type of the scrutinee with the type of the expression
219    handler.scope(|h| {
220        type_engine.unify(h, ctx.engines, type_id, exp.return_type, &span, "", || None);
221        Ok(())
222    })?;
223
224    match variant {
225        ty::TyScrutineeVariant::Or(alternatives) => {
226            match_or(handler, ctx, match_value, exp, alternatives)
227        }
228        ty::TyScrutineeVariant::CatchAll => Ok(ReqDeclTree::none()),
229        ty::TyScrutineeVariant::Literal(value) => Ok(match_literal(exp, value, span)),
230        ty::TyScrutineeVariant::Variable(name) => Ok(match_variable(exp, name)),
231        ty::TyScrutineeVariant::Constant(_, _, const_decl) => {
232            Ok(match_constant(ctx, exp, const_decl, span))
233        }
234        ty::TyScrutineeVariant::StructScrutinee {
235            struct_ref: _,
236            fields,
237            ..
238        } => match_struct(handler, ctx, match_value, exp, fields),
239        ty::TyScrutineeVariant::EnumScrutinee {
240            variant,
241            call_path_decl,
242            value,
243            ..
244        } => match_enum(
245            handler,
246            ctx,
247            match_value,
248            exp,
249            *variant,
250            call_path_decl,
251            *value,
252            span,
253        ),
254        ty::TyScrutineeVariant::Tuple(elems) => {
255            match_tuple(handler, ctx, match_value, exp, elems, span)
256        }
257    }
258}
259
260fn match_or(
261    handler: &Handler,
262    mut ctx: TypeCheckContext,
263    match_value: &ty::TyExpression,
264    exp: &ty::TyExpression,
265    alternatives: Vec<ty::TyScrutinee>,
266) -> Result<ReqDeclTree, ErrorEmitted> {
267    return handler.scope(|handler| {
268        let mut nodes = vec![];
269        let mut variables_in_alternatives: Vec<(Span, Vec<(Ident, TypeId)>)> = vec![]; // Span is the span of the alternative.
270
271        for alternative in alternatives {
272            let alternative_span = alternative.span.clone();
273
274            // We want to collect as many errors as possible.
275            // If an alternative has any internal issues we will emit them, ignore that alternative,
276            // but still process the remaining alternatives.
277            let alternative_req_decl_tree =
278                match matcher(handler, ctx.by_ref(), match_value, exp, alternative) {
279                    Ok(req_decl_tree) => req_decl_tree,
280                    Err(_) => continue,
281                };
282
283            variables_in_alternatives.push((
284                alternative_span,
285                variable_declarations(&alternative_req_decl_tree),
286            ));
287
288            nodes.push(alternative_req_decl_tree.root);
289        }
290
291        // All the first occurrences of variables in order of appearance.
292        let mut variables: IndexMap<&Ident, TypeId> = IndexMap::new();
293        for (ident, type_id) in variables_in_alternatives.iter().flat_map(|(_, vars)| vars) {
294            variables.entry(ident).or_insert(*type_id);
295        }
296
297        // At this stage, in the matcher, we are not concerned about the duplicates
298        // in individual alternatives.
299
300        // Check that we have all variables in all alternatives.
301        for (variable, _) in variables.iter() {
302            let missing_in_alternatives: Vec<Span> = variables_in_alternatives
303                .iter()
304                .filter_map(|(span, vars)| {
305                    (!vars.iter().any(|(ident, _)| ident == *variable)).then_some(span.clone())
306                })
307                .collect();
308
309            if missing_in_alternatives.is_empty() {
310                continue;
311            }
312
313            handler.emit_err(CompileError::MatchArmVariableNotDefinedInAllAlternatives {
314                match_value: match_value.span.clone(),
315                match_type: ctx.engines.help_out(match_value.return_type).to_string(),
316                variable: (*variable).clone(),
317                missing_in_alternatives,
318            });
319        }
320
321        // Check that the variable types are the same in all alternatives
322        // (assuming that the variable exist in the alternative).
323
324        // To the equality, we accept type aliases and the types they encapsulate
325        // to be equal, otherwise, we are strict, e.g., no coercion between u8 and u16, etc.
326        let equality = UnifyCheck::non_dynamic_equality(ctx.engines);
327
328        for (variable, type_id) in variables {
329            let type_mismatched_vars = variables_in_alternatives.iter().flat_map(|(_, vars)| {
330                vars.iter().filter_map(|(ident, var_type_id)| {
331                    (ident == variable && !equality.check(type_id, *var_type_id))
332                        .then_some((ident.clone(), *var_type_id))
333                })
334            });
335
336            for type_mismatched_var in type_mismatched_vars {
337                handler.emit_err(CompileError::MatchArmVariableMismatchedType {
338                    match_value: match_value.span.clone(),
339                    match_type: ctx.engines.help_out(match_value.return_type).to_string(),
340                    variable: type_mismatched_var.0,
341                    first_definition: variable.span(),
342                    expected: ctx.engines.help_out(type_id).to_string(),
343                    received: ctx.engines.help_out(type_mismatched_var.1).to_string(),
344                });
345            }
346        }
347
348        Ok(ReqDeclTree::or(nodes))
349    });
350
351    /// Returns all [MatchVarDecl]s found in the match arm
352    /// in order of their appearance from left to right.
353    fn variable_declarations(req_decl_tree: &ReqDeclTree) -> Vec<(Ident, TypeId)> {
354        let mut result = vec![];
355
356        collect_variable_declarations(&req_decl_tree.root, &mut result);
357
358        return result;
359
360        fn collect_variable_declarations(
361            node: &ReqDeclNode,
362            declarations: &mut Vec<(Ident, TypeId)>,
363        ) {
364            // Traverse the tree depth-first, left to right.
365            match node {
366                ReqDeclNode::ReqOrVarDecl(ReqOrVarDecl::VarDecl((ident, exp))) => {
367                    declarations.push((ident.clone(), exp.return_type));
368                }
369                ReqDeclNode::ReqOrVarDecl(_) => (),
370                ReqDeclNode::And(nodes) | ReqDeclNode::Or(nodes) => {
371                    for node in nodes {
372                        collect_variable_declarations(node, declarations);
373                    }
374                }
375            }
376        }
377    }
378}
379
380fn match_literal(exp: &ty::TyExpression, scrutinee: Literal, span: Span) -> ReqDeclTree {
381    let req = (
382        exp.to_owned(),
383        ty::TyExpression {
384            expression: ty::TyExpressionVariant::Literal(scrutinee),
385            return_type: exp.return_type,
386            span,
387        },
388    );
389
390    ReqDeclTree::req(req)
391}
392
393fn match_variable(exp: &ty::TyExpression, scrutinee_name: Ident) -> ReqDeclTree {
394    let decl = (scrutinee_name, exp.to_owned());
395
396    ReqDeclTree::decl(decl)
397}
398
399fn match_constant(
400    ctx: TypeCheckContext,
401    exp: &ty::TyExpression,
402    const_decl: TyConstantDecl,
403    span: Span,
404) -> ReqDeclTree {
405    let name = const_decl.name().clone();
406    let return_type = const_decl.type_ascription.type_id;
407
408    let req = (
409        exp.to_owned(),
410        ty::TyExpression {
411            expression: ty::TyExpressionVariant::ConstantExpression {
412                span: span.clone(),
413                decl: Box::new(const_decl),
414                call_path: Some(CallPath::from(name).to_fullpath(ctx.engines(), ctx.namespace())),
415            },
416            return_type,
417            span,
418        },
419    );
420
421    ReqDeclTree::req(req)
422}
423
424fn match_struct(
425    handler: &Handler,
426    mut ctx: TypeCheckContext,
427    match_value: &ty::TyExpression,
428    exp: &ty::TyExpression,
429    fields: Vec<ty::TyStructScrutineeField>,
430) -> Result<ReqDeclTree, ErrorEmitted> {
431    let mut nodes = vec![];
432
433    for ty::TyStructScrutineeField {
434        field,
435        scrutinee,
436        span: field_span,
437        field_def_name: _,
438    } in fields.into_iter()
439    {
440        // Get the expression that access the struct field e.g., `my_struct.x`.
441        let subfield = instantiate_struct_field_access(
442            handler,
443            ctx.engines(),
444            ctx.namespace(),
445            exp.clone(),
446            field.clone(),
447            field_span,
448        )?;
449
450        match scrutinee {
451            // If there is no scrutinee, we simply have the struct field name.
452            // This means declaring a variable with the same name as the struct field,
453            // initialized to the values of the subfield expression.
454            None => {
455                nodes.push(ReqDeclNode::decl((field, subfield)));
456            }
457            // If the scrutinee exist, we have the form `<field>: <match_sub_pattern>`.
458            // We need to match the subfield against the sub pattern.
459            Some(match_sub_pattern) => {
460                let req_decl_tree = matcher(
461                    handler,
462                    ctx.by_ref(),
463                    match_value,
464                    &subfield,
465                    match_sub_pattern,
466                )?;
467                nodes.push(req_decl_tree.root);
468            }
469        }
470    }
471
472    Ok(ReqDeclTree::and(nodes))
473}
474
475#[allow(clippy::too_many_arguments)]
476fn match_enum(
477    handler: &Handler,
478    ctx: TypeCheckContext,
479    match_value: &ty::TyExpression,
480    exp: &ty::TyExpression,
481    variant: ty::TyEnumVariant,
482    call_path_decl: ty::TyDecl,
483    enum_value_scrutinee: ty::TyScrutinee,
484    span: Span,
485) -> Result<ReqDeclTree, ErrorEmitted> {
486    let type_engine = ctx.engines.te();
487
488    let mut nodes = vec![];
489
490    // The first requirement is that the enum variant behind the `exp` is
491    // of the kind `variant`. `exp is variant` is expressed as `EnumTag(<exp>) == <variant.tag>`.
492    let enum_variant_req = (
493        ty::TyExpression {
494            expression: ty::TyExpressionVariant::EnumTag {
495                exp: Box::new(exp.clone()),
496            },
497            return_type: type_engine.id_of_u64(),
498            span: exp.span.clone(),
499        },
500        ty::TyExpression {
501            expression: ty::TyExpressionVariant::Literal(Literal::U64(variant.tag as u64)),
502            return_type: type_engine.id_of_u64(),
503            span: exp.span.clone(),
504        },
505    );
506
507    nodes.push(ReqDeclNode::req(enum_variant_req));
508
509    // Afterwards, we need to collect the requirements for the enum variant underlying value.
510    // If the enum variant does not have a value the `enum_value_scrutinee` will be of the
511    // scrutinee variant `CatchAll` that will produce a ReqDeclTree without further requirements
512    // or variable declarations.
513    let unsafe_downcast = instantiate_enum_unsafe_downcast(exp, variant, call_path_decl, span);
514    let req_decl_tree = matcher(
515        handler,
516        ctx,
517        match_value,
518        &unsafe_downcast,
519        enum_value_scrutinee,
520    )?;
521
522    nodes.push(req_decl_tree.root);
523
524    Ok(ReqDeclTree::and(nodes))
525}
526
527fn match_tuple(
528    handler: &Handler,
529    mut ctx: TypeCheckContext,
530    match_value: &ty::TyExpression,
531    exp: &ty::TyExpression,
532    elems: Vec<ty::TyScrutinee>,
533    span: Span,
534) -> Result<ReqDeclTree, ErrorEmitted> {
535    let mut nodes = vec![];
536
537    for (pos, elem) in elems.into_iter().enumerate() {
538        let tuple_index_access = instantiate_tuple_index_access(
539            handler,
540            ctx.engines(),
541            exp.clone(),
542            pos,
543            span.clone(),
544            span.clone(),
545        )?;
546
547        let req_decl_tree = matcher(
548            handler,
549            ctx.by_ref(),
550            match_value,
551            &tuple_index_access,
552            elem,
553        )?;
554
555        nodes.push(req_decl_tree.root);
556    }
557
558    Ok(ReqDeclTree::and(nodes))
559}