From b43947b6f15454b662080baca8ea0dbd0e7ab58a Mon Sep 17 00:00:00 2001 From: John Doty Date: Wed, 7 Feb 2024 07:58:36 -0800 Subject: [PATCH] [fine] Conditionals produce alternates (yikes!) --- fine/src/semantics.rs | 65 +++++++++---------- fine/tests/expression/alternates.fine | 2 +- .../expression/errors/if_mismatched_arms.fine | 4 -- fine/tests/expression/errors/if_not_bool.fine | 4 +- .../expression/errors/if_requires_else.fine | 2 +- .../expression/errors/while_not_bool.fine | 2 +- 6 files changed, 35 insertions(+), 44 deletions(-) delete mode 100644 fine/tests/expression/errors/if_mismatched_arms.fine diff --git a/fine/src/semantics.rs b/fine/src/semantics.rs index 71f0001d..4942a2f3 100644 --- a/fine/src/semantics.rs +++ b/fine/src/semantics.rs @@ -1633,8 +1633,6 @@ impl<'a> Semantics<'a> { fn type_of_conditional(&self, tree: &Tree) -> Option { assert_eq!(tree.kind, TreeKind::ConditionalExpression); - let cond_tree = tree.nth_tree(1)?; - let cond_type = self.type_of(cond_tree); let then_type = self.type_of(tree.nth_tree(2)?); let has_else = tree @@ -1647,38 +1645,18 @@ impl<'a> Semantics<'a> { None }; - if !self.can_convert(&cond_type, &Type::Bool) { - if !cond_type.is_error() { - self.report_error_tree_ref(cond_tree, "conditions must yield a boolean"); - } - Some(Type::Error) - } else { - match (then_type, else_type) { - (Type::Error, _) => Some(Type::Error), - (_, Some(Type::Error)) => Some(Type::Error), + match (then_type, else_type) { + (Type::Error, _) => Some(Type::Error), + (_, Some(Type::Error)) => Some(Type::Error), - (Type::Unreachable, None) => Some(Type::Nothing), - (Type::Unreachable, Some(t)) => Some(t), - (t, Some(Type::Unreachable)) => Some(t), + (Type::Unreachable, None) => Some(Type::Nothing), + (Type::Unreachable, Some(t)) => Some(t), + (t, Some(Type::Unreachable)) => Some(t), - (then_type, else_type) => { - let else_type = else_type.unwrap_or(Type::Nothing); - if self.can_convert(&then_type, &else_type) { - Some(else_type) - // I know, I know, as of this writing there is no real - // reason to ever check this, because this relationship - // is symmetrical. But someday it won't be, and you'll be - // glad I had the forethought to be thorough. - } else if self.can_convert(&else_type, &then_type) { - Some(then_type) - } else { - self.report_error_tree( - tree, - format!("the type of the 'then' branch ('{then_type}') must match the type of the 'else' branch ('{else_type}')"), - ); - Some(Type::Error) - } - } + (then_type, else_type) => { + let else_type = else_type.unwrap_or(Type::Nothing); + + Some(self.build_alternate(&then_type, &else_type)) } } } @@ -2097,7 +2075,7 @@ impl<'a> Semantics<'a> { // New lowest-common-denominator type actual_type = arm_type; } else { - self.report_error_tree_ref(*arm, format!("this arm produces a value of type {arm_type} which is incompatible with the general result type {actual_type}")); + self.report_error_tree_ref(*arm, format!("this arm produces a value of type '{arm_type}' which is incompatible with the general result type {actual_type}")); } } } @@ -2342,11 +2320,11 @@ pub fn check(s: &Semantics) { | TreeKind::LiteralExpression | TreeKind::GroupingExpression | TreeKind::UnaryExpression - | TreeKind::ConditionalExpression | TreeKind::BinaryExpression | TreeKind::MemberAccess => { let _ = s.type_of(t); } + TreeKind::ConditionalExpression => check_conditional(s, tree), TreeKind::CallExpression => { let _ = s.type_of(t); } @@ -2392,6 +2370,21 @@ pub fn check(s: &Semantics) { } } +fn check_conditional(s: &Semantics, tree: &Tree) { + let Some(cond_tree) = tree.nth_tree(1) else { + return; + }; + let cond_type = s.type_of(cond_tree); + if !s.can_convert(&cond_type, &Type::Bool) { + if !cond_type.is_error() { + s.report_error_tree_ref( + cond_tree, + format!("this condition produces '{cond_type}', but must produce bool"), + ); + } + } +} + fn check_function_decl(s: &Semantics, t: TreeRef, tree: &Tree) { assert_eq!(tree.kind, TreeKind::FunctionDecl); let _ = s.environment_of(t); @@ -2576,7 +2569,7 @@ fn check_pattern(s: &Semantics, tree: &Tree) { // TODO: TEST s.report_error_tree_ref( pred, - format!("this predicate produces {predicate_type}, but must produce a boolean"), + format!("this predicate produces '{predicate_type}', but must produce bool"), ) } } @@ -2609,7 +2602,7 @@ fn check_while_statement(s: &Semantics, tree: &Tree) { if !s.can_convert(&expr_type, &Type::Bool) { s.report_error_tree_ref( expr, - "the condition of the while loop must produce a boolean", + format!("this condition produces '{expr_type}', but must produce bool"), ); } } diff --git a/fine/tests/expression/alternates.fine b/fine/tests/expression/alternates.fine index 8b922480..963f1a8e 100644 --- a/fine/tests/expression/alternates.fine +++ b/fine/tests/expression/alternates.fine @@ -127,5 +127,5 @@ fun test() -> f64 { // like the above. } -// @ignore WIP +/// @ignore WIP // @no-errors \ No newline at end of file diff --git a/fine/tests/expression/errors/if_mismatched_arms.fine b/fine/tests/expression/errors/if_mismatched_arms.fine deleted file mode 100644 index 252d1798..00000000 --- a/fine/tests/expression/errors/if_mismatched_arms.fine +++ /dev/null @@ -1,4 +0,0 @@ -if true { "blarg" } else { 23 } - -// @expect-errors: -// | 1:0: the type of the 'then' branch ('string') must match the type of the 'else' branch ('f64') diff --git a/fine/tests/expression/errors/if_not_bool.fine b/fine/tests/expression/errors/if_not_bool.fine index 770b28e1..c72335be 100644 --- a/fine/tests/expression/errors/if_not_bool.fine +++ b/fine/tests/expression/errors/if_not_bool.fine @@ -1,2 +1,4 @@ if 23 { "what" } else { "the" } -// @type-error: 0 conditions must yield a boolean + +// @expect-errors: +// | 1:3: this condition produces 'f64', but must produce bool diff --git a/fine/tests/expression/errors/if_requires_else.fine b/fine/tests/expression/errors/if_requires_else.fine index 153134b8..8f60f676 100644 --- a/fine/tests/expression/errors/if_requires_else.fine +++ b/fine/tests/expression/errors/if_requires_else.fine @@ -1,4 +1,4 @@ if (if false { true }) { 32 } else { 23 } // @expect-errors: -// | 1:4: the type of the 'then' branch ('bool') must match the type of the 'else' branch ('nothing') +// | 1:3: this condition produces 'nothing or bool', but must produce bool diff --git a/fine/tests/expression/errors/while_not_bool.fine b/fine/tests/expression/errors/while_not_bool.fine index edd47dc8..f0d19504 100644 --- a/fine/tests/expression/errors/while_not_bool.fine +++ b/fine/tests/expression/errors/while_not_bool.fine @@ -3,4 +3,4 @@ fun test() { } // @expect-errors: -// | 2:8: the condition of the while loop must produce a boolean +// | 2:8: this condition produces 'f64', but must produce bool