[fine] Conditionals produce alternates (yikes!)

This commit is contained in:
John Doty 2024-02-07 07:58:36 -08:00
parent 8b9a69b898
commit b43947b6f1
6 changed files with 35 additions and 44 deletions

View file

@ -1633,8 +1633,6 @@ impl<'a> Semantics<'a> {
fn type_of_conditional(&self, tree: &Tree) -> Option<Type> {
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"),
);
}
}

View file

@ -127,5 +127,5 @@ fun test() -> f64 {
// like the above.
}
// @ignore WIP
/// @ignore WIP
// @no-errors

View file

@ -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')

View file

@ -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

View file

@ -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

View file

@ -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