Skip to content

Commit

Permalink
The compiler checks for exhaustive match arms in match expressions. (#…
Browse files Browse the repository at this point in the history
…701)

* Test case.

* Remove unused expression.

* Introduce the MatchExp as a wrapper for IfExp.

* Add generated names to this PR.

* Fix uncompleted change.

* Edit parsing mechanism to allow expressions the possibility of declaring variables prefixing the expression itself.

* Fix license information.

* Clippy suggestions.

* Use intermediate variable in match expressions.

* Update description of desugaring process.

* Exhaustivity checking works on integers

* Exhaustivity check works for tuples.

* Code review suggestions.

* Rebrand to usefulness.

* Compute specialized matrix algorithm.

* WIP

* Exhaustivity works in basic cases.

* WIP

* WIP

* Add error messages.

* Add some documentation.

* Misc cleanup items.

* Finding unimplemented patterns works for basic integers.

* Fix small bug.

* Display max and min instead of big numbers.

* WIP working on debugging

* Working for tuples.

* More stuff for tuples.

* Structs work.

* Nested structures work.

* Better display for Structs.

* Fix merge conflicts.

* Fix broken tests.

* Some docstrings stuff.

* More docstring stuff.

* More docstring stuff.

* More docstring stuff.

* WIP

* Move usefulness to its own dir.

* Fix clippy warning.

* Last of documentation.

* Fix test case pointing to too advanced std.

* Condense internal compiler error messages to one error type.

* Remove vscode from gitignore.

* Feedback.

* Improve test cases.

* other test case.

* Clippy issues.

* Update sway-core/Cargo.toml

Co-authored-by: John Adler <adlerjohn@users.noreply.github.com>

Co-authored-by: John Adler <adlerjohn@users.noreply.github.com>
  • Loading branch information
emilyaherbert and adlerjohn committed Mar 22, 2022
1 parent 6ea3181 commit c1ef577
Show file tree
Hide file tree
Showing 37 changed files with 4,318 additions and 579 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions sway-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ fuel-asm = "0.2"
fuel-crypto = "0.4.0"
fuel-vm = "0.5"
hex = { version = "0.4", optional = true }
itertools = "0.10"
lazy_static = "1.4"
nanoid = "0.4"
pest = { version = "3.0.4", package = "fuel-pest" }
Expand Down
2 changes: 1 addition & 1 deletion sway-core/src/asm_generation/from_ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2148,7 +2148,7 @@ mod tests {
print_intermediate_asm: false,
print_finalized_asm: false,
print_ir: false,
generated_names: std::sync::Arc::new(std::sync::Mutex::new(vec![])),
generated_names: Default::default(),
},
);

Expand Down
31 changes: 28 additions & 3 deletions sway-core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::{
parser::Rule,
style::{to_screaming_snake_case, to_snake_case, to_upper_camel_case},
type_engine::*,
VariableDeclaration,
};
use sway_types::{ident::Ident, span::Span};

Expand Down Expand Up @@ -78,6 +79,25 @@ pub(crate) fn ok<T>(
}
}

/// Acts as the result of parsing `Declaration`s, `Expression`s, etc.
/// Some `Expression`s need to be able to create `VariableDeclaration`s,
/// so this struct is used to "bubble up" those declarations to a viable
/// place in the AST.
#[derive(Debug, Clone)]
pub struct ParserLifter<T> {
pub var_decls: Vec<VariableDeclaration>,
pub value: T,
}

impl<T> ParserLifter<T> {
pub(crate) fn empty(value: T) -> Self {
ParserLifter {
var_decls: vec![],
value,
}
}
}

#[derive(Debug, Clone)]
pub struct CompileResult<T> {
pub value: Option<T>,
Expand Down Expand Up @@ -254,6 +274,7 @@ pub enum Warning {
ShadowingReservedRegister {
reg_name: Ident,
},
MatchExpressionUnreachableArm,
}

impl fmt::Display for Warning {
Expand Down Expand Up @@ -359,6 +380,7 @@ impl fmt::Display for Warning {
"This register declaration shadows the reserved register, \"{}\".",
reg_name
),
MatchExpressionUnreachableArm => write!(f, "This match arm is unreachable."),
}
}
}
Expand Down Expand Up @@ -390,8 +412,6 @@ pub enum CompileError {
},
#[error("Unimplemented feature: {0}")]
Unimplemented(&'static str, Span),
#[error("pattern matching algorithm failure on: {0}")]
PatternMatchingAlgorithmFailure(&'static str, Span),
#[error("{0}")]
TypeError(TypeError),
#[error("Error parsing input: expected {err:?}")]
Expand Down Expand Up @@ -820,6 +840,11 @@ pub enum CompileError {
"
)]
MatchWrongType { expected: TypeId, span: Span },
#[error("Non-exhaustive match expression. Missing patterns {missing_patterns}")]
MatchExpressionNonExhaustive {
missing_patterns: String,
span: Span,
},
#[error("Impure function called inside of pure function. Pure functions can only call other pure functions. Try making the surrounding function impure by prepending \"impure\" to the function declaration.")]
PureCalledImpure { span: Span },
#[error("Impure function inside of non-contract. Contract storage is only accessible from contracts.")]
Expand Down Expand Up @@ -1058,8 +1083,8 @@ impl CompileError {
ShadowsOtherSymbol { span, .. } => span,
StarImportShadowsOtherSymbol { span, .. } => span,
MatchWrongType { span, .. } => span,
MatchExpressionNonExhaustive { span, .. } => span,
NotAnEnum { span, .. } => span,
PatternMatchingAlgorithmFailure(_, span) => span,
PureCalledImpure { span, .. } => span,
ImpureInNonContract { span, .. } => span,
IntegerTooLarge { span, .. } => span,
Expand Down
19 changes: 11 additions & 8 deletions sway-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -590,19 +590,22 @@ fn parse_root_from_pairs(
for pair in input {
match pair.as_rule() {
Rule::non_var_decl => {
let decl = check!(
let span = span::Span {
span: pair.as_span(),
path: path.clone(),
};
let decls = check!(
Declaration::parse_non_var_from_pair(pair.clone(), config),
continue,
warnings,
errors
);
parse_tree.push(AstNode {
content: AstNodeContent::Declaration(decl),
span: span::Span {
span: pair.as_span(),
path: path.clone(),
},
});
for decl in decls.into_iter() {
parse_tree.push(AstNode {
content: AstNodeContent::Declaration(decl),
span: span.clone(),
});
}
}
Rule::use_statement => {
let stmt = check!(
Expand Down
2 changes: 1 addition & 1 deletion sway-core/src/optimize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2178,7 +2178,7 @@ mod tests {
print_intermediate_asm: false,
print_finalized_asm: false,
print_ir: false,
generated_names: std::sync::Arc::new(std::sync::Mutex::new(vec![])),
generated_names: Default::default(),
};

let mut warnings = vec![];
Expand Down
85 changes: 49 additions & 36 deletions sway-core/src/parse_tree/code_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,17 @@ use crate::{
error::*,
parse_tree::{Expression, ReturnStatement},
parser::Rule,
span::Span,
AstNode, AstNodeContent, Declaration,
AstNode, AstNodeContent, Declaration, VariableDeclaration,
};

use sway_types::span;
use sway_types::span::Span;

use pest::iterators::Pair;

#[derive(Debug, Clone)]
pub struct CodeBlock {
pub contents: Vec<AstNode>,
pub(crate) whole_block_span: span::Span,
pub(crate) whole_block_span: Span,
}

impl CodeBlock {
Expand All @@ -29,13 +28,17 @@ impl CodeBlock {
let path = config.map(|c| c.path());
let mut warnings = Vec::new();
let mut errors = Vec::new();
let whole_block_span = span::Span {
let whole_block_span = Span {
span: block.as_span(),
path: path.clone(),
};
let block_inner = block.into_inner();
let mut contents = Vec::new();
for pair in block_inner {
let span = Span {
span: pair.as_span(),
path: path.clone(),
};
let mut ast_nodes = match pair.as_rule() {
Rule::declaration => check!(
Declaration::parse_from_pair(pair.clone(), config),
Expand All @@ -46,14 +49,14 @@ impl CodeBlock {
.into_iter()
.map(|content| AstNode {
content: AstNodeContent::Declaration(content),
span: span::Span {
span: Span {
span: pair.as_span(),
path: path.clone(),
},
})
.collect::<Vec<_>>(),
Rule::expr_statement => {
let evaluated_node = check!(
let ParserLifter { value, var_decls } = check!(
Expression::parse_from_pair(
pair.clone().into_inner().next().unwrap().clone(),
config
Expand All @@ -62,61 +65,61 @@ impl CodeBlock {
warnings,
errors
);
vec![AstNode {
content: AstNodeContent::Expression(evaluated_node),
span: span::Span {
span: pair.as_span(),
path: path.clone(),
},
}]
let mut ast_node_contents = collect_var_decls(var_decls, span.clone());
ast_node_contents.push(AstNode {
content: AstNodeContent::Expression(value),
span,
});
ast_node_contents
}
Rule::return_statement => {
let evaluated_node = check!(
let ParserLifter { value, var_decls } = check!(
ReturnStatement::parse_from_pair(pair.clone(), config),
continue,
warnings,
errors
);
vec![AstNode {
content: AstNodeContent::ReturnStatement(evaluated_node),
span: span::Span {
span: pair.as_span(),
path: path.clone(),
},
}]
let mut ast_node_contents = collect_var_decls(var_decls, span.clone());
ast_node_contents.push(AstNode {
content: AstNodeContent::ReturnStatement(value),
span,
});
ast_node_contents
}
Rule::expr => {
let res = check!(
let ParserLifter { value, var_decls } = check!(
Expression::parse_from_pair(pair.clone(), config),
continue,
warnings,
errors
);
vec![AstNode {
content: AstNodeContent::ImplicitReturnExpression(res.clone()),
span: res.span(),
}]
let mut ast_node_contents = collect_var_decls(var_decls, span.clone());
let expr_span = value.span();
ast_node_contents.push(AstNode {
content: AstNodeContent::ImplicitReturnExpression(value),
span: expr_span,
});
ast_node_contents
}
Rule::while_loop => {
let res = check!(
let ParserLifter { value, var_decls } = check!(
WhileLoop::parse_from_pair(pair.clone(), config),
continue,
warnings,
errors
);
vec![AstNode {
content: AstNodeContent::WhileLoop(res),
span: span::Span {
span: pair.as_span(),
path: path.clone(),
},
}]
let mut ast_node_contents = collect_var_decls(var_decls, span.clone());
ast_node_contents.push(AstNode {
content: AstNodeContent::WhileLoop(value),
span,
});
ast_node_contents
}
a => {
println!("In code block parsing: {:?} {:?}", a, pair.as_str());
errors.push(CompileError::UnimplementedRule(
a,
span::Span {
Span {
span: pair.as_span(),
path: path.clone(),
},
Expand All @@ -137,3 +140,13 @@ impl CodeBlock {
)
}
}

fn collect_var_decls(var_decls: Vec<VariableDeclaration>, span: Span) -> Vec<AstNode> {
var_decls
.into_iter()
.map(|x| AstNode {
content: AstNodeContent::Declaration(Declaration::VariableDeclaration(x)),
span: span.clone(),
})
.collect::<Vec<_>>()
}

0 comments on commit c1ef577

Please sign in to comment.