Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: error on if without else when type mismatch #7302

Merged
merged 1 commit into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 20 additions & 18 deletions compiler/noirc_frontend/src/elaborator/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,7 @@ impl<'context> Elaborator<'context> {
target_type: Option<&Type>,
) -> (HirExpression, Type) {
let expr_span = if_expr.condition.span;
let consequence_span = if_expr.consequence.span;
let (condition, cond_type) = self.elaborate_expression(if_expr.condition);
let (consequence, mut ret_type) =
self.elaborate_expression_with_target_type(if_expr.consequence, target_type);
Expand All @@ -924,29 +925,30 @@ impl<'context> Elaborator<'context> {
expr_span,
});

let alternative = if_expr.alternative.map(|alternative| {
let expr_span = alternative.span;
let (alternative, else_type, error_span) = if let Some(alternative) = if_expr.alternative {
let (else_, else_type) =
self.elaborate_expression_with_target_type(alternative, target_type);
(Some(else_), else_type, expr_span)
} else {
(None, Type::Unit, consequence_span)
};

self.unify(&ret_type, &else_type, || {
let err = TypeCheckError::TypeMismatch {
expected_typ: ret_type.to_string(),
expr_typ: else_type.to_string(),
expr_span,
};
self.unify(&ret_type, &else_type, || {
let err = TypeCheckError::TypeMismatch {
expected_typ: ret_type.to_string(),
expr_typ: else_type.to_string(),
expr_span: error_span,
};

let context = if ret_type == Type::Unit {
"Are you missing a semicolon at the end of your 'else' branch?"
} else if else_type == Type::Unit {
"Are you missing a semicolon at the end of the first block of this 'if'?"
} else {
"Expected the types of both if branches to be equal"
};
let context = if ret_type == Type::Unit {
"Are you missing a semicolon at the end of your 'else' branch?"
} else if else_type == Type::Unit {
"Are you missing a semicolon at the end of the first block of this 'if'?"
} else {
"Expected the types of both if branches to be equal"
};

err.add_context(context)
});
else_
err.add_context(context)
});

if alternative.is_none() {
Expand Down
18 changes: 18 additions & 0 deletions compiler/noirc_frontend/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3190,7 +3190,7 @@
}

#[test]
fn dont_infer_globals_to_u32_from_type_use() {

Check warning on line 3193 in compiler/noirc_frontend/src/tests.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (dont)
let src = r#"
global ARRAY_LEN = 3;
global STR_LEN: _ = 2;
Expand Down Expand Up @@ -3220,7 +3220,7 @@
}

#[test]
fn dont_infer_partial_global_types() {

Check warning on line 3223 in compiler/noirc_frontend/src/tests.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (dont)
let src = r#"
pub global ARRAY: [Field; _] = [0; 3];
pub global NESTED_ARRAY: [[Field; _]; 3] = [[]; 3];
Expand Down Expand Up @@ -3463,7 +3463,7 @@
// wouldn't panic due to infinite recursion, but the errors asserted here
// come from the compilation checks, which does static analysis to catch the
// problem before it even has a chance to cause a panic.
let srcs = vec![

Check warning on line 3466 in compiler/noirc_frontend/src/tests.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (srcs)
r#"
fn main() {
main()
Expand Down Expand Up @@ -3525,7 +3525,7 @@
"#,
];

for src in srcs {

Check warning on line 3528 in compiler/noirc_frontend/src/tests.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (srcs)
let errors = get_program_errors(src);
assert!(
!errors.is_empty(),
Expand All @@ -3544,7 +3544,7 @@

#[test]
fn unconditional_recursion_pass() {
let srcs = vec![

Check warning on line 3547 in compiler/noirc_frontend/src/tests.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (srcs)
r#"
fn main() {
if false { main(); }
Expand Down Expand Up @@ -3586,7 +3586,7 @@
"#,
];

for src in srcs {

Check warning on line 3589 in compiler/noirc_frontend/src/tests.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (srcs)
assert_no_errors(src);
}
}
Expand Down Expand Up @@ -4347,3 +4347,21 @@
"#;
assert_no_errors(src);
}

#[test]
fn errors_on_if_without_else_type_mismatch() {
let src = r#"
fn main() {
if true {
1
}
}
"#;
let errors = get_program_errors(src);
assert_eq!(errors.len(), 1);

let CompilationError::TypeError(TypeCheckError::Context { err, .. }) = &errors[0].0 else {
panic!("Expected a Context error");
};
assert!(matches!(**err, TypeCheckError::TypeMismatch { .. }));
}