diff --git a/fine/src/semantics.rs b/fine/src/semantics.rs index 5a72fada..2620f116 100644 --- a/fine/src/semantics.rs +++ b/fine/src/semantics.rs @@ -634,11 +634,12 @@ impl<'a> Semantics<'a> { }; let result = match tree.kind { - TreeKind::IsExpression => self.environment_of_is_expression(parent, tree), TreeKind::Block => self.environment_of_block(parent, tree), TreeKind::File => self.environment_of_file(parent, tree), TreeKind::ForStatement => self.environment_of_for(parent, tree), + TreeKind::IsExpression => self.environment_of_is_expression(parent, tree), TreeKind::LetStatement => self.environment_of_let(parent, tree), + TreeKind::MatchArm => self.environment_of_match_arm(parent, t, tree), TreeKind::MemberAccess => self.environment_of_member_access(tree), TreeKind::ParamList => self.environment_of_paramlist(parent, tree), @@ -865,6 +866,45 @@ impl<'a> Semantics<'a> { self.environment_of_pattern(parent, pattern, lhs) } + fn environment_of_match_arm( + &self, + parent: EnvironmentRef, + t: TreeRef, + tree: &Tree, + ) -> EnvironmentRef { + assert_eq!(tree.kind, TreeKind::MatchArm); + + // The environment of a `match arm` expression is the environment produced by the pattern. + let Some(pattern) = tree.child_tree_of_kind(self.syntax_tree, TreeKind::Pattern) else { + // Should really have a pattern in there; otherwise there was a + // parse error, don't make more trouble. + return Environment::error(); + }; + + // The expression in the match expression is the binding for the wildcard pattern. + // If we are somewhere weird then... uh.... + let Some(match_body) = tree.parent else { + self.internal_compiler_error(Some(t), "no parent on match arm"); + }; + let match_body = &self.syntax_tree[match_body]; + if match_body.kind != TreeKind::MatchBody { + self.internal_compiler_error(Some(t), "match arm parent not match body"); + } + let Some(match_expression) = match_body.parent else { + self.internal_compiler_error(Some(t), "no parent on match body"); + }; + let match_expression = &self.syntax_tree[match_expression]; + if match_expression.kind != TreeKind::MatchExpression { + self.internal_compiler_error(Some(t), "match body parent not match expression"); + } + + // The expression is the first tree child of match expression. + let Some(lhs) = tree.nth_tree(1) else { + return Environment::error(); + }; + self.environment_of_pattern(parent, pattern, lhs) + } + // NOTE: THIS IS CALLED DIRECTLY, NOT VIA `environment_of` TO AVOID CYCLES. fn environment_of_pattern( &self, @@ -1061,6 +1101,11 @@ impl<'a> Semantics<'a> { // Can... I... convert unreachable always? Is this sound? (Type::Unreachable, _) => true, + // Alternates convert if either side can convert. + (_, Type::Alternate(left, right)) => { + self.can_convert(from, left) || self.can_convert(from, right) + } + // TODO: Unification on type variables! :D (_, _) => false, } @@ -1120,6 +1165,10 @@ impl<'a> Semantics<'a> { TreeKind::TypeParameter => self.type_of_type_parameter(tree), TreeKind::UnaryExpression => self.type_of_unary(tree), + TreeKind::MatchExpression => self.type_of_match_expression(tree), + TreeKind::MatchBody => self.type_of_match_body(tree), + TreeKind::MatchArm => self.type_of_match_arm(tree), + _ => self.internal_compiler_error(Some(t), "asking for a nonsense type"), }; @@ -1823,6 +1872,41 @@ impl<'a> Semantics<'a> { Some(Type::Alternate(Box::new(left), Box::new(right))) } + fn type_of_match_expression(&self, tree: &Tree) -> Option { + Some(self.type_of(tree.child_of_kind(self.syntax_tree, TreeKind::MatchBody)?)) + } + + fn type_of_match_body(&self, tree: &Tree) -> Option { + let arms: Vec<_> = tree + .children_of_kind(self.syntax_tree, TreeKind::MatchArm) + .collect(); + + if arms.len() == 0 { + self.report_error_tree(tree, "a match expression must have at least one arm"); + Some(Type::Error) + } else { + let mut actual_type = self.type_of(arms[0]); + for arm in &arms[1..] { + let arm_type = self.type_of(*arm); + if !self.can_convert(&arm_type, &actual_type) { + if self.can_convert(&actual_type, &arm_type) { + // 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}")); + } + } + } + + Some(actual_type) + } + } + + fn type_of_match_arm(&self, tree: &Tree) -> Option { + // The type of a match arm is the type of the expression to the right. + Some(self.type_of(tree.nth_tree(2)?)) + } + pub fn dump_compiler_state(&self, tr: Option) { eprintln!("Parsed the tree as:"); eprintln!("\n{}", self.syntax_tree.dump(true)); @@ -1949,13 +2033,13 @@ pub fn check(s: &Semantics) { TreeKind::SelfParameter => {} TreeKind::SelfReference => {} - TreeKind::IsExpression => check_is_expression(s, tree), - TreeKind::VariableBinding => check_variable_binding(s, tree), + TreeKind::IsExpression => {} + TreeKind::VariableBinding => {} TreeKind::Pattern => check_pattern(s, tree), TreeKind::WildcardPattern => {} - TreeKind::MatchArm => {} - TreeKind::MatchBody => {} + TreeKind::MatchArm => check_match_arm(s, tree), + TreeKind::MatchBody => check_match_body(s, t, tree), TreeKind::MatchExpression => {} } } @@ -2132,10 +2216,6 @@ fn check_class_declaration(s: &Semantics, tree: &Tree) { } } -fn check_is_expression(_s: &Semantics, _tree: &Tree) { - // TODO -} - fn check_pattern(s: &Semantics, tree: &Tree) { // If there's an AND then it must produce a boolean. let and_index = tree.children.iter().position(|c| match c { @@ -2156,10 +2236,23 @@ fn check_pattern(s: &Semantics, tree: &Tree) { } } -fn check_variable_binding(_s: &Semantics, _tree: &Tree) { - // TODO +fn check_match_body(s: &Semantics, t: TreeRef, tree: &Tree) { + let _ = s.type_of(t); // Checks arm count and compatibility. + + let arms: Vec<_> = tree + .children_of_kind(s.syntax_tree, TreeKind::MatchArm) + .collect(); + + if arms.len() > 0 { + for arm in &arms[1..] { + // TODO: How do I know if it's complete? + // TODO: How do I know if it's redundant? + } + } } +fn check_match_arm(s: &Semantics, tree: &Tree) {} + #[cfg(test)] mod tests { use super::*;