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
25pub(super) type MatchReq = (ty::TyExpression, ty::TyExpression);
28
29pub(super) type MatchVarDecl = (Ident, ty::TyExpression);
32
33#[allow(clippy::large_enum_variant)]
38pub(super) enum ReqOrVarDecl {
39 Neither,
42 Req(MatchReq),
43 VarDecl(MatchVarDecl),
44}
45
46pub(super) struct ReqDeclTree {
56 root: ReqDeclNode,
57}
58
59impl ReqDeclTree {
60 fn req(req: MatchReq) -> Self {
63 Self {
64 root: ReqDeclNode::ReqOrVarDecl(ReqOrVarDecl::Req(req)),
65 }
66 }
67
68 fn decl(decl: MatchVarDecl) -> Self {
71 Self {
72 root: ReqDeclNode::ReqOrVarDecl(ReqOrVarDecl::VarDecl(decl)),
73 }
74 }
75
76 fn none() -> Self {
79 Self {
80 root: ReqDeclNode::ReqOrVarDecl(ReqOrVarDecl::Neither),
81 }
82 }
83
84 fn and(nodes: Vec<ReqDeclNode>) -> Self {
87 Self {
88 root: ReqDeclNode::And(nodes),
89 }
90 }
91
92 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#[allow(clippy::large_enum_variant)]
107pub(super) enum ReqDeclNode {
108 ReqOrVarDecl(ReqOrVarDecl),
115 And(Vec<ReqDeclNode>),
122 Or(Vec<ReqDeclNode>),
129}
130
131impl ReqDeclNode {
132 fn req(req: MatchReq) -> Self {
134 ReqDeclNode::ReqOrVarDecl(ReqOrVarDecl::Req(req))
135 }
136
137 fn decl(decl: MatchVarDecl) -> Self {
139 ReqDeclNode::ReqOrVarDecl(ReqOrVarDecl::VarDecl(decl))
140 }
141}
142
143pub(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 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![]; for alternative in alternatives {
272 let alternative_span = alternative.span.clone();
273
274 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 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 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 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 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 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 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 None => {
455 nodes.push(ReqDeclNode::decl((field, subfield)));
456 }
457 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 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 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}