Skip to content

Commit 285d976

Browse files
Expand match expressions to encompass enums (#1642)
* Refactor namespace and enums. * clippy * clippy * Improvements to namespace API and declaration ABI. * Introduce the trait and the type. * More instances. * Introduce the CreateTypeId trait. * Introduce the MonomorphizeHelper trait. * Improvements to monomorphization. * Fix bug with node dependencies. * Combine Unit Scruntinee and Tuple Scrutinee. * Improve the MatcherResult type. * Add recursive catchall pattern. * Move match expression desugaring from parsing to type checking. * Use some of the new methods. * clippy * Rename function. * Add purity check back in. * update * Support nested enums. * Allow empty match arms. * Fix case with malformed enum name. * Confirm there are not duplicated errors. * Fix bad test. * Fix bad tests. * Remove unusable tests. * docstrings * reviewer comments
1 parent 09e2ed4 commit 285d976

File tree

54 files changed

+1524
-1431
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+1524
-1431
lines changed

sway-core/src/asm_generation/expression/mod.rs

Lines changed: 1 addition & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@ use crate::{
33
asm_lang::*,
44
parse_tree::{BuiltinProperty, CallPath, Literal},
55
semantic_analysis::{
6-
ast_node::{
7-
TypedAsmRegisterDeclaration, TypedCodeBlock, TypedEnumVariant, TypedExpressionVariant,
8-
},
6+
ast_node::{TypedAsmRegisterDeclaration, TypedCodeBlock, TypedExpressionVariant},
97
TypedExpression,
108
},
119
type_engine::*,
@@ -321,24 +319,6 @@ pub(crate) fn convert_expression_to_asm(
321319
}
322320
// ABI casts are purely compile-time constructs and generate no corresponding bytecode
323321
TypedExpressionVariant::AbiCast { .. } => ok(vec![], warnings, errors),
324-
TypedExpressionVariant::IfLet {
325-
enum_type,
326-
variant,
327-
then,
328-
r#else,
329-
variable_to_assign,
330-
expr,
331-
} => convert_if_let_to_asm(
332-
expr,
333-
*enum_type,
334-
variant,
335-
then,
336-
r#else,
337-
variable_to_assign,
338-
return_register,
339-
namespace,
340-
register_sequencer,
341-
),
342322
TypedExpressionVariant::TypeProperty {
343323
property, type_id, ..
344324
} => match property {
@@ -628,130 +608,3 @@ pub(crate) fn convert_abi_fn_to_asm(
628608
// is done
629609
ok(asm_buf, warnings, errors)
630610
}
631-
632-
#[allow(clippy::too_many_arguments)]
633-
fn convert_if_let_to_asm(
634-
expr: &TypedExpression,
635-
_enum_type: TypeId,
636-
variant: &TypedEnumVariant,
637-
then: &TypedCodeBlock,
638-
r#else: &Option<Box<TypedExpression>>,
639-
variable_to_assign: &Ident,
640-
return_register: &VirtualRegister,
641-
namespace: &mut AsmNamespace,
642-
register_sequencer: &mut RegisterSequencer,
643-
) -> CompileResult<Vec<Op>> {
644-
// 1. evaluate the expression
645-
// 2. load the expected tag into a register ($rA)
646-
// 3. compare the tag to the first word of the expression's returned value
647-
// 4. grab a register for `variable_to_assign`, insert it into the asm namespace
648-
// 5. if the tags are equal, load the returned value from byte 1..end into `variable_to_assign`
649-
// 5.5 if they are not equal, jump to the label in 7
650-
// 6. evaluate the then branch with that variable in scope
651-
// 7. insert a jump label for the else branch
652-
// 8. evaluate the else branch, if any
653-
let mut warnings = vec![];
654-
let mut errors = vec![];
655-
let mut buf = vec![];
656-
// 1.
657-
let expr_return_register = register_sequencer.next();
658-
let mut expr_buf = check!(
659-
convert_expression_to_asm(&*expr, namespace, &expr_return_register, register_sequencer),
660-
vec![],
661-
warnings,
662-
errors
663-
);
664-
buf.append(&mut expr_buf);
665-
// load the tag from the evaluated value
666-
// as this is an enum we know the value in the register is a pointer
667-
// we can therefore read a word from the register and move it into another register
668-
let received_tag_register = register_sequencer.next();
669-
buf.push(Op {
670-
opcode: Either::Left(VirtualOp::LW(
671-
received_tag_register.clone(),
672-
expr_return_register.clone(),
673-
VirtualImmediate12::new_unchecked(0, "infallible"),
674-
)),
675-
comment: "load received enum tag".into(),
676-
owning_span: Some(expr.span.clone()),
677-
});
678-
// 2.
679-
let expected_tag_register = register_sequencer.next();
680-
let expected_tag_label = namespace.insert_data_value(&Literal::U64(variant.tag as u64));
681-
buf.push(Op {
682-
opcode: either::Either::Left(VirtualOp::LWDataId(
683-
expected_tag_register.clone(),
684-
expected_tag_label,
685-
)),
686-
comment: "load enum tag for if let".into(),
687-
owning_span: Some(expr.span.clone()),
688-
});
689-
let label_for_else_branch = register_sequencer.get_label();
690-
// 3 - 5
691-
buf.push(Op {
692-
opcode: Either::Right(OrganizationalOp::JumpIfNotEq(
693-
expected_tag_register,
694-
received_tag_register,
695-
label_for_else_branch.clone(),
696-
)),
697-
comment: "jump to if let's else branch".into(),
698-
owning_span: Some(expr.span.clone()),
699-
});
700-
// 6.
701-
// put the destructured variable into the namespace for the then branch, but not otherwise
702-
let mut then_branch_asm_namespace = namespace.clone();
703-
let variable_to_assign_register = register_sequencer.next();
704-
then_branch_asm_namespace.insert_variable(
705-
variable_to_assign.clone(),
706-
variable_to_assign_register.clone(),
707-
);
708-
// load the word that is at the expr return register + 1 word
709-
// + 1 word is to account for the enum tag
710-
buf.push(Op {
711-
opcode: Either::Left(VirtualOp::LW(
712-
variable_to_assign_register,
713-
expr_return_register,
714-
VirtualImmediate12::new_unchecked(1, "infallible"),
715-
)),
716-
owning_span: Some(then.span().clone()),
717-
comment: "Load destructured value into register".into(),
718-
});
719-
720-
// 6
721-
buf.append(&mut check!(
722-
convert_code_block_to_asm(
723-
then,
724-
&mut then_branch_asm_namespace,
725-
register_sequencer,
726-
Some(return_register)
727-
),
728-
return err(warnings, errors),
729-
warnings,
730-
errors
731-
));
732-
733-
// add the data section from the then branch back to the main one
734-
735-
namespace.overwrite_data_section(then_branch_asm_namespace);
736-
737-
let label_for_after_else_branch = register_sequencer.get_label();
738-
if let Some(r#else) = r#else {
739-
buf.push(Op::jump_to_label_comment(
740-
label_for_after_else_branch.clone(),
741-
"jump to after the else branch",
742-
));
743-
744-
buf.push(Op::unowned_jump_label(label_for_else_branch));
745-
746-
buf.append(&mut check!(
747-
convert_expression_to_asm(r#else, namespace, return_register, register_sequencer),
748-
return err(warnings, errors),
749-
warnings,
750-
errors
751-
));
752-
753-
buf.push(Op::unowned_jump_label(label_for_after_else_branch));
754-
}
755-
756-
ok(buf, warnings, errors)
757-
}

sway-core/src/asm_generation/mod.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -612,11 +612,6 @@ impl AsmNamespace {
612612
),
613613
}
614614
}
615-
616-
/// In the
617-
pub(crate) fn overwrite_data_section(&mut self, other: Self) {
618-
self.data_section.value_pairs = other.data_section.value_pairs;
619-
}
620615
}
621616

622617
pub(crate) fn compile_ast_to_asm(

sway-core/src/control_flow_analysis/dead_code_analysis.rs

Lines changed: 18 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -963,35 +963,6 @@ fn connect_expression(
963963
)?;
964964
Ok([prefix_idx, index_idx].concat())
965965
}
966-
IfLet {
967-
expr, then, r#else, ..
968-
} => {
969-
let leaves = connect_expression(
970-
&expr.expression,
971-
graph,
972-
leaves,
973-
exit_node,
974-
"if let expr",
975-
tree_type,
976-
expr.span.clone(),
977-
)?;
978-
let then_expr = connect_code_block(then, graph, &leaves, exit_node, tree_type)?;
979-
980-
let else_expr = if let Some(else_expr) = r#else {
981-
connect_expression(
982-
&else_expr.expression,
983-
graph,
984-
&leaves,
985-
exit_node,
986-
"if let: else branch",
987-
tree_type,
988-
(**else_expr).span.clone(),
989-
)?
990-
} else {
991-
vec![]
992-
};
993-
Ok([then_expr, else_expr].concat())
994-
}
995966
TupleElemAccess { prefix, .. } => {
996967
let prefix_idx = connect_expression(
997968
&prefix.expression,
@@ -1050,6 +1021,24 @@ fn connect_expression(
10501021
Ok(leaves.to_vec())
10511022
}
10521023
FunctionParameter => Ok(leaves.to_vec()),
1024+
EnumTag { exp } => connect_expression(
1025+
&exp.expression,
1026+
graph,
1027+
leaves,
1028+
exit_node,
1029+
"enum tag exp",
1030+
tree_type,
1031+
exp.span.clone(),
1032+
),
1033+
UnsafeDowncast { exp, .. } => connect_expression(
1034+
&exp.expression,
1035+
graph,
1036+
leaves,
1037+
exit_node,
1038+
"unsafe downcast exp",
1039+
tree_type,
1040+
exp.span.clone(),
1041+
),
10531042
}
10541043
}
10551044

sway-core/src/convert_parse_tree.rs

Lines changed: 57 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1964,8 +1964,11 @@ fn if_expr_to_expression(
19641964
..
19651965
} = if_expr;
19661966
let then_block_span = then_block.span();
1967-
let then_block = braced_code_block_contents_to_code_block(ec, then_block)?;
1968-
let else_opt = match else_opt {
1967+
let then_block = Expression::CodeBlock {
1968+
contents: braced_code_block_contents_to_code_block(ec, then_block)?,
1969+
span: then_block_span.clone(),
1970+
};
1971+
let else_block = match else_opt {
19691972
None => None,
19701973
Some((_else_token, tail)) => {
19711974
let expression = match tail {
@@ -1974,26 +1977,52 @@ fn if_expr_to_expression(
19741977
}
19751978
ControlFlow::Continue(if_expr) => if_expr_to_expression(ec, *if_expr)?,
19761979
};
1977-
Some(Box::new(expression))
1980+
Some(expression)
19781981
}
19791982
};
19801983
let expression = match condition {
19811984
IfCondition::Expr(condition) => Expression::IfExp {
19821985
condition: Box::new(expr_to_expression(ec, *condition)?),
1983-
then: Box::new(Expression::CodeBlock {
1984-
contents: then_block,
1985-
span: then_block_span,
1986-
}),
1987-
r#else: else_opt,
1988-
span,
1989-
},
1990-
IfCondition::Let { lhs, rhs, .. } => Expression::IfLet {
1991-
scrutinee: pattern_to_scrutinee(ec, *lhs)?,
1992-
expr: Box::new(expr_to_expression(ec, *rhs)?),
1993-
then: then_block,
1994-
r#else: else_opt,
1986+
then: Box::new(then_block),
1987+
r#else: else_block.map(Box::new),
19951988
span,
19961989
},
1990+
IfCondition::Let { lhs, rhs, .. } => {
1991+
let scrutinee = pattern_to_scrutinee(ec, *lhs)?;
1992+
let scrutinee_span = scrutinee.span();
1993+
let mut branches = vec![MatchBranch {
1994+
scrutinee,
1995+
result: then_block.clone(),
1996+
span: Span::join(scrutinee_span, then_block_span),
1997+
}];
1998+
branches.push(match else_block {
1999+
Some(else_block) => {
2000+
let else_block_span = else_block.span();
2001+
MatchBranch {
2002+
scrutinee: Scrutinee::CatchAll {
2003+
span: else_block_span.clone(),
2004+
},
2005+
result: else_block,
2006+
span: else_block_span,
2007+
}
2008+
}
2009+
None => {
2010+
let else_block_span = then_block.span();
2011+
MatchBranch {
2012+
scrutinee: Scrutinee::CatchAll {
2013+
span: else_block_span.clone(),
2014+
},
2015+
result: then_block,
2016+
span: else_block_span,
2017+
}
2018+
}
2019+
});
2020+
Expression::MatchExp {
2021+
value: Box::new(expr_to_expression(ec, *rhs)?),
2022+
branches,
2023+
span,
2024+
}
2025+
}
19972026
};
19982027
Ok(expression)
19992028
}
@@ -2515,35 +2544,28 @@ fn pattern_to_scrutinee(
25152544
value: literal_to_literal(ec, literal)?,
25162545
span,
25172546
},
2518-
Pattern::Constant(path_expr) => Scrutinee::EnumScrutinee {
2519-
call_path: path_expr_to_call_path(ec, path_expr)?,
2520-
variable_to_assign: Ident::new_no_span("_"),
2521-
span,
2522-
},
2547+
Pattern::Constant(path_expr) => {
2548+
let call_path = path_expr_to_call_path(ec, path_expr)?;
2549+
let call_path_span = call_path.span();
2550+
Scrutinee::EnumScrutinee {
2551+
call_path,
2552+
value: Box::new(Scrutinee::CatchAll {
2553+
span: call_path_span,
2554+
}),
2555+
span,
2556+
}
2557+
}
25232558
Pattern::Constructor { path, args } => {
2524-
let arg = match iter_to_array(args.into_inner()) {
2559+
let value = match iter_to_array(args.into_inner()) {
25252560
Some([arg]) => arg,
25262561
None => {
25272562
let error = ConvertParseTreeError::ConstructorPatternOneArg { span };
25282563
return Err(ec.error(error));
25292564
}
25302565
};
2531-
let variable_to_assign = match arg {
2532-
Pattern::Var { mutable, name } => {
2533-
if mutable.is_some() {
2534-
let error = ConvertParseTreeError::MutableBindingsNotSupportedHere { span };
2535-
return Err(ec.error(error));
2536-
}
2537-
name
2538-
}
2539-
_ => {
2540-
let error = ConvertParseTreeError::ConstructorPatternSubPatterns { span };
2541-
return Err(ec.error(error));
2542-
}
2543-
};
25442566
Scrutinee::EnumScrutinee {
25452567
call_path: path_expr_to_call_path(ec, path)?,
2546-
variable_to_assign,
2568+
value: Box::new(pattern_to_scrutinee(ec, value)?),
25472569
span,
25482570
}
25492571
}

0 commit comments

Comments
 (0)