diff --git a/fine/src/compiler.rs b/fine/src/compiler.rs index 6d66ecda..8600fd0b 100644 --- a/fine/src/compiler.rs +++ b/fine/src/compiler.rs @@ -3,7 +3,7 @@ use std::rc::Rc; use crate::{ parser::{Child, SyntaxTree, Tree, TreeKind, TreeRef}, - semantics::{string_constant_to_string, Declaration, Environment, Location, Semantics, Type}, + semantics::{string_constant_to_string, Declaration, Location, Semantics, Type}, tokens::TokenKind, }; @@ -366,7 +366,7 @@ fn compile_literal(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { let index = c.add_string(result); c.push(Instruction::PushString(index)) } - Type::Error => c.push_panic(format!("compiling literal {:?}", tr)), + Type::Error(e) => c.push_panic(format!("compiling literal {:?}: {e}", tr)), _ => ice!(c, t, "unsupported literal type: {t:?}"), }; OK @@ -556,22 +556,30 @@ fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { let ltree = &c.syntax[lvalue]; #[allow(unused_assignments)] - let mut environment = Environment::error("??"); + let mut environment = None; let declaration = match ltree.kind { // TODO: Assign to list access TreeKind::Identifier => { let id = ltree.nth_token(0).ok_or("no id")?.as_str(&c.source); - environment = c.semantics.environment_of(lvalue); - environment.bind(id).ok_or("cannot bind destination")? + environment = Some(c.semantics.environment_of(lvalue)); + environment + .as_ref() + .unwrap() + .bind(id) + .ok_or("cannot bind destination")? } TreeKind::MemberAccess => { let id = ltree.nth_token(2).ok_or("no member")?.as_str(&c.source); let t = ltree.nth_tree(0).ok_or("no lhs exp")?; let typ = c.semantics.type_of(t); - environment = c.semantics.member_environment(t, &typ); - environment.bind(id).ok_or("cannot bind field")? + environment = Some(c.semantics.member_environment(t, &typ)); + environment + .as_ref() + .unwrap() + .bind(id) + .ok_or("cannot bind field")? } _ => return Err("unsupported lval expression"), }; diff --git a/fine/src/lib.rs b/fine/src/lib.rs index c1235117..2a4fc39d 100644 --- a/fine/src/lib.rs +++ b/fine/src/lib.rs @@ -86,7 +86,10 @@ impl Runtime { } } - pub fn load_module(&mut self, name: &str) -> Result<(Vec, Rc), ModuleLoadError> { + pub fn load_module( + &mut self, + name: &str, + ) -> Result<(Vec>, Rc), ModuleLoadError> { let mut init_pending = HashMap::new(); let mut names = Vec::new(); let name = self.loader.normalize_module_name("", name.to_string()); diff --git a/fine/src/parser.rs b/fine/src/parser.rs index df006d3c..5a22dabb 100644 --- a/fine/src/parser.rs +++ b/fine/src/parser.rs @@ -26,6 +26,7 @@ impl SyntaxTree { assert!(t.parent.is_none()); let tr = TreeRef::from_index(self.trees.len()); + t.self_ref = tr; t.start_pos = t .children .first() @@ -172,6 +173,7 @@ pub enum TreeKind { } pub struct Tree { + pub self_ref: TreeRef, pub kind: TreeKind, pub parent: Option, // TODO: Do we actually need this? pub start_pos: usize, @@ -538,6 +540,7 @@ impl<'a> CParser<'a> { match event { ParseEvent::Start { kind } => stack.push(Tree { kind, + self_ref: TreeRef::from_index(0), parent: None, start_pos: 0, end_pos: 0, diff --git a/fine/src/semantics.rs b/fine/src/semantics.rs index dee3f1de..b93790b6 100644 --- a/fine/src/semantics.rs +++ b/fine/src/semantics.rs @@ -26,11 +26,12 @@ pub struct Error { pub file: Rc, pub start: (usize, usize), pub end: (usize, usize), + pub span: (usize, usize), pub message: String, } impl Error { - pub fn new(file: Rc, line: usize, column: usize, message: T) -> Self + pub fn new(file: Rc, line: usize, column: usize, pos: usize, message: T) -> Self where T: ToString, { @@ -38,6 +39,7 @@ impl Error { file, start: (line, column), end: (line, column), + span: (pos, pos), message: message.to_string(), } } @@ -46,6 +48,7 @@ impl Error { file: Rc, start: (usize, usize), end: (usize, usize), + span: (usize, usize), message: T, ) -> Self where @@ -55,6 +58,7 @@ impl Error { file, start, end, + span, message: message.to_string(), } } @@ -137,7 +141,7 @@ pub enum Type { // Signals a type error. If you receive this then you know that an error // has already been reported; if you produce this be sure to also note // the error in the errors collection. - Error, + Error(Rc), // Signals that the expression has a control-flow side-effect and that no // value will ever result from this expression. Usually this means @@ -190,14 +194,14 @@ pub enum Type { impl Type { pub fn is_error(&self) -> bool { match self { - Type::Error => true, + Type::Error(..) => true, _ => false, } } fn discriminant_number(&self) -> i8 { match self { - Type::Error => 0, + Type::Error(..) => 0, Type::Unreachable => 1, Type::Assignment(..) => 2, Type::TypeVariable(..) => 3, @@ -227,7 +231,7 @@ impl fmt::Display for Type { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use Type::*; match self { - Error => write!(f, "<< INTERNAL ERROR >>"), + Error(e) => write!(f, "<< INTERNAL ERROR ({e}) >>"), Unreachable => write!(f, "<< UNREACHABLE >>"), Assignment(_) => write!(f, "assignment"), Nothing => write!(f, "nothing"), @@ -431,7 +435,7 @@ pub struct Environment { pub location: Location, pub next_index: usize, pub declarations: HashMap, Declaration>, - pub error: Option<&'static str>, + pub error: Option>, } impl Environment { @@ -465,7 +469,7 @@ impl Environment { self.error.is_some() } - pub fn error(why: &'static str) -> EnvironmentRef { + pub fn error(why: Rc) -> EnvironmentRef { // TODO: Exactly once? EnvironmentRef::new(Environment { parent: None, @@ -709,7 +713,7 @@ pub struct Semantics { logical_parents: Vec>, // TODO: State should be externalized instead of this refcell nonsense. - errors: RefCell>, + errors: RefCell>>, types: RefCell>>, environments: RefCell>>, root_environment: EnvironmentRef, @@ -791,12 +795,9 @@ impl Semantics { .unwrap_or(Vec::new()) } - pub fn snapshot_errors(&self) -> Vec { + pub fn snapshot_errors(&self) -> Vec> { let mut result = (*self.errors.borrow()).clone(); - result.sort_by(|a, b| match a.start.0.cmp(&b.start.0) { - std::cmp::Ordering::Equal => a.start.1.cmp(&b.start.1), - o => o, - }); + result.sort_by_key(|a| a.span.0); result } @@ -808,28 +809,31 @@ impl Semantics { } } - fn report_error_span(&self, start: usize, end: usize, error: T) + fn report_error_span(&self, start_pos: usize, end_pos: usize, error: T) -> Rc where T: ToString, { - let start = self.lines.position(start); - let end = self.lines.position(end); - self.errors.borrow_mut().push(Error::new_spanned( + let start = self.lines.position(start_pos); + let end = self.lines.position(end_pos); + let error = Rc::new(Error::new_spanned( self.file.clone(), start, end, + (start_pos, end_pos), error.to_string(), )); + self.errors.borrow_mut().push(error.clone()); + error } - fn report_error_tree(&self, tree: &Tree, error: T) + fn report_error_tree(&self, tree: &Tree, error: T) -> Rc where T: ToString, { self.report_error_span(tree.start_pos, tree.end_pos, error) } - fn report_error_tree_ref(&self, tree: TreeRef, error: T) + fn report_error_tree_ref(&self, tree: TreeRef, error: T) -> Rc where T: ToString, { @@ -854,6 +858,52 @@ impl Semantics { } } + // TODO: Here we're just looking for *an* error, not the most specific + // error. + fn find_error(&self, tree: &Tree) -> Option> { + let mut result = (*self.errors.borrow()).clone(); + result.sort_by_key(|a| a.span.0); + + let mut error = None; + for candidate in result.into_iter() { + let (candiate_start, candidate_end) = candidate.span; + if candidate_end < tree.start_pos { + continue; + } + if candiate_start > tree.end_pos { + break; + } + + // End is after our point, Start is before our point, we are + // inside. This error at least affects us somehow. + error = Some(candidate); + } + + error + } + + fn type_error_for(&self, tree: &Tree) -> Type { + let Some(error) = self.find_error(&tree) else { + self.internal_compiler_error( + Some(tree.self_ref), + "Unable to find a diagnostic that encompasses the tree generating an error type", + ); + }; + + Type::Error(error) + } + + fn environment_error_for(&self, tree: &Tree) -> EnvironmentRef { + let Some(error) = self.find_error(&tree) else { + self.internal_compiler_error( + Some(tree.self_ref), + "Unable to find a diagnostic that encompasses the tree generating an error environment", + ); + }; + + Environment::error(error) + } + pub fn environment_of(&self, t: TreeRef) -> EnvironmentRef { { // I want to make sure that this borrow is dropped after this block. @@ -1141,12 +1191,12 @@ impl Semantics { 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("no rhs (pattern)"); + return self.environment_error_for(tree); }; // The left hand side of the `is` expression is used for wildcard types. let Some(lhs) = tree.nth_tree(0) else { - return Environment::error("no lhs (value)"); + return self.environment_error_for(tree); }; self.environment_of_pattern(parent, pattern, lhs) } @@ -1163,7 +1213,7 @@ impl Semantics { 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("arm: no lhs (pattern)"); + return self.environment_error_for(tree); }; // The expression in the match expression is the binding for the wildcard pattern. @@ -1185,7 +1235,7 @@ impl Semantics { // The expression is the first tree child of match expression. let Some(lhs) = tree.nth_tree(2) else { - return Environment::error("arm: no rhs (expression)"); + return self.environment_error_for(tree); }; self.environment_of_pattern(parent, pattern, lhs) } @@ -1205,7 +1255,7 @@ impl Semantics { return parent; }; let Some(variable) = binding.nth_token(0) else { - return Environment::error("no variable"); + return self.environment_error_for(binding); }; let is_wildcard = tree @@ -1222,7 +1272,7 @@ impl Semantics { // match for the variable to have a value. let Some(type_expr) = tree.child_of_kind(&self.syntax_tree, TreeKind::TypeExpression) else { - return Environment::error("no type expression"); + return self.environment_error_for(tree); }; type_expr }; @@ -1283,7 +1333,7 @@ impl Semantics { let field_type = f .nth_tree(2) .map(|t| self.type_of(t)) - .unwrap_or(Type::Error); + .unwrap_or_else(|| self.type_error_for(f)); fields.push(FieldDecl { name: field_name.as_str(&self.source).into(), declaration: field, @@ -1430,8 +1480,8 @@ impl Semantics { } // Avoid introducing more errors - (Type::Error, _) => true, - (_, Type::Error) => true, + (Type::Error(_), _) => true, + (_, Type::Error(_)) => true, // Can... I... convert unreachable always? Is this sound? (Type::Unreachable, _) => true, @@ -1468,9 +1518,11 @@ impl Semantics { Incremental::Complete(existing) => return existing.clone(), Incremental::InProgress => { // eprintln!("type_of circular => {t:?}"); - self.report_error_tree_ref(t, "The type of this expression depends on itself"); - *state = Incremental::Complete(Type::Error); - return Type::Error; + let error = self + .report_error_tree_ref(t, "The type of this expression depends on itself"); + let e_type = Type::Error(error); + *state = Incremental::Complete(e_type.clone()); + return e_type; } } *state = Incremental::InProgress; @@ -1480,7 +1532,7 @@ impl Semantics { // eprintln!(">>> type_of => {tree:?}"); let result = match tree.kind { - TreeKind::Error => Some(Type::Error), + TreeKind::Error => Some(self.type_error_for(tree)), TreeKind::AlternateType => self.type_of_alternate_type(tree), TreeKind::Argument => self.type_of_argument(tree), @@ -1525,7 +1577,7 @@ impl Semantics { }; // NOTE: These return `None` if they encounter some problem. - let result = result.unwrap_or(Type::Error); + let result = result.unwrap_or_else(|| self.type_error_for(tree)); self.types.borrow_mut()[t.index()] = Incremental::Complete(result.clone()); // eprintln!("<<< type_of => {tree:?}"); @@ -1546,19 +1598,19 @@ impl Semantics { // This is dumb and should be punished, probably. (_, Type::Unreachable) => { - self.report_error_span( + let err = self.report_error_span( op.start(), op.end(), "cannot apply a unary operator to something that doesn't yield a value", ); - Some(Type::Error) + Some(Type::Error(err)) } // Propagate existing errors without additional complaint. - (_, Type::Error) => Some(Type::Error), + (_, Type::Error(e)) => Some(Type::Error(e)), (_, arg_type) => { - self.report_error_span( + let err = self.report_error_span( op.start(), op.end(), format!( @@ -1567,7 +1619,7 @@ impl Semantics { arg_type ), ); - Some(Type::Error) + Some(Type::Error(err)) } } } @@ -1607,7 +1659,7 @@ impl Semantics { // This is dumb and should be punished, probably. (_, _, Type::Unreachable) => { - self.report_error_span( + let err = self.report_error_span( op.start(), op.end(), format!( @@ -1615,10 +1667,10 @@ impl Semantics { op.as_str(&self.source) ), ); - Some(Type::Error) + Some(Type::Error(err)) } (_, Type::Unreachable, _) => { - self.report_error_span( + let err = self.report_error_span( op.start(), op.end(), format!( @@ -1626,19 +1678,19 @@ impl Semantics { op.as_str(&self.source) ), ); - Some(Type::Error) + Some(Type::Error(err)) } // Propagate existing errors without additional complaint. - (_, Type::Error, _) => Some(Type::Error), - (_, _, Type::Error) => Some(Type::Error), + (_, Type::Error(e), _) => Some(Type::Error(e)), + (_, _, Type::Error(e)) => Some(Type::Error(e)), // Assignments are fun. (TokenKind::Equal, a, b) => self.type_of_assignment(left_tree, a, b, op), // Missed the whole table, it must be an error. (_, left_type, right_type) => { - self.report_error_span( + let err =self.report_error_span( op.start(), op.end(), format!( @@ -1646,7 +1698,7 @@ impl Semantics { op.as_str(&self.source) ), ); - Some(Type::Error) + Some(Type::Error(err)) } } } @@ -1662,75 +1714,79 @@ impl Semantics { let tree = &self.syntax_tree[left_tree]; #[allow(unused_assignments)] - let mut environment = Environment::error("?"); + let mut environment = None; let declaration = match tree.kind { // TODO: Assign to list access TreeKind::Identifier => { let id = tree.nth_token(0)?.as_str(&self.source); - environment = self.environment_of(left_tree); - match environment.bind(id) { + environment = Some(self.environment_of(left_tree)); + match environment.as_ref().unwrap().bind(id) { Some(decl) => decl, None => { - if !environment.is_error() { - self.report_error_tree(tree, format!("cannot find value {id} here")); - } - return Some(Type::Error); + let error = if let Some(e) = &environment.as_ref().unwrap().error { + e.clone() + } else { + self.report_error_tree(tree, format!("cannot find value {id} here")) + }; + return Some(Type::Error(error)); } } } TreeKind::MemberAccess => { let id = tree.nth_token(2)?.as_str(&self.source); let typ = self.type_of(tree.nth_tree(0)?); - environment = self.member_environment(left_tree, &typ); - match environment.bind(id) { + environment = Some(self.member_environment(left_tree, &typ)); + match environment.as_ref().unwrap().bind(id) { Some(decl) => decl, None => { - if !environment.is_error() { - self.report_error_tree(tree, format!("'{typ}' has no member {id}")); - } - return Some(Type::Error); + let error = if let Some(e) = &environment.as_ref().unwrap().error { + e.clone() + } else { + self.report_error_tree(tree, format!("'{typ}' has no member {id}")) + }; + return Some(Type::Error(error)); } } } _ => { - self.report_error_tree_ref( + let error = self.report_error_tree_ref( left_tree, "cannot assign a value to this expression, it is not a place you can store things", ); - return Some(Type::Error); + return Some(Type::Error(error)); } }; match declaration { Declaration::Variable { .. } => (), Declaration::ExternFunction { .. } | Declaration::Function { .. } => { - self.report_error_tree_ref( + let error = self.report_error_tree_ref( left_tree, "cannot assign a new value to a function declaration", ); - return Some(Type::Error); + return Some(Type::Error(error)); } Declaration::Class { .. } => { - self.report_error_tree_ref( + let error = self.report_error_tree_ref( left_tree, "cannot assign a new value to a class declaration", ); - return Some(Type::Error); + return Some(Type::Error(error)); } Declaration::ImportedModule { .. } => { - self.report_error_tree_ref( + let error = self.report_error_tree_ref( left_tree, "cannot assign a new value to an imported module", ); - return Some(Type::Error); + return Some(Type::Error(error)); } Declaration::ImportedDeclaration { .. } => { - self.report_error_tree_ref( + let error = self.report_error_tree_ref( left_tree, "cannot assign a new value to a member of an imported module", ); - return Some(Type::Error); + return Some(Type::Error(error)); } } @@ -1745,17 +1801,19 @@ impl Semantics { t => t, }; - if left_type.is_error() || right_type.is_error() { - Some(Type::Error) + if let Type::Error(e) = left_type { + Some(Type::Error(e)) + } else if let Type::Error(e) = right_type { + Some(Type::Error(e)) } else if self.can_convert(&right_type, &left_type) { Some(Type::Assignment(Box::new(left_type))) } else { - self.report_error_span( + let error = self.report_error_span( op.start(), op.end(), format!("cannot assign a value of type '{right_type}' to type '{left_type}'"), ); - Some(Type::Error) + Some(Type::Error(error)) } } @@ -1780,8 +1838,8 @@ impl Semantics { let mut arg_types: Vec<_> = args.child_trees().map(|t| self.type_of(t)).collect(); if arg_types.len() != 1 { - self.report_error_tree(tree, "list takes a single type argument"); - Some(Type::Error) + let error = self.report_error_tree(tree, "list takes a single type argument"); + Some(Type::Error(error)) } else { Some(Type::List(Box::new(arg_types.pop().unwrap()))) } @@ -1793,25 +1851,25 @@ impl Semantics { Some(self.type_of(*declaration)) } Some(Declaration::Variable { .. }) => { - self.report_error_tree( + let error = self.report_error_tree( tree, format!("'{token}' is a variable and cannot be used as a type"), ); - Some(Type::Error) + Some(Type::Error(error)) } Some(Declaration::Function { .. } | Declaration::ExternFunction { .. }) => { - self.report_error_tree( + let error = self.report_error_tree( tree, format!("'{token}' is a function and cannot be used as a type"), ); - Some(Type::Error) + Some(Type::Error(error)) } Some(Declaration::ImportedModule { .. }) => { - self.report_error_tree( + let error = self.report_error_tree( tree, format!("'{token}' is an imported module and cannot be used as a type"), ); - Some(Type::Error) + Some(Type::Error(error)) } Some(Declaration::ImportedDeclaration { semantics, @@ -1824,10 +1882,12 @@ impl Semantics { .type_of_declaration(*tree, declaration), ), None => { - if !environment.is_error() { - self.report_error_tree(tree, format!("Unrecognized type: '{token}'")); - } - Some(Type::Error) + let error = if let Some(e) = &environment.error { + e.clone() + } else { + self.report_error_tree(tree, format!("Unrecognized type: '{token}'")) + }; + Some(Type::Error(error)) } } } @@ -1918,8 +1978,8 @@ impl Semantics { }; match (then_type, else_type) { - (Type::Error, _) => Some(Type::Error), - (_, Some(Type::Error)) => Some(Type::Error), + (Type::Error(e), _) => Some(Type::Error(e)), + (_, Some(Type::Error(e))) => Some(Type::Error(e)), (Type::Unreachable, None) => Some(Type::Nothing), (Type::Unreachable, Some(t)) => Some(t), @@ -1956,76 +2016,88 @@ impl Semantics { }) .collect(); - if f.is_error() || arg_types.iter().any(|(_, t)| t.is_error()) { - return Some(Type::Error); + // Propagate type errors if there are any. + let type_error = if let Type::Error(e) = &f { + Some(e.clone()) + } else { + arg_types.iter().find_map(|(_, t)| match t { + Type::Error(e) => Some(e.clone()), + _ => None, + }) + }; + if let Some(error) = type_error { + return Some(Type::Error(error)); } match f { Type::Function(params, ret) => { - let mut any_errors = false; + let mut param_error = None; if params.len() != arg_types.len() { // TODO: Augment with function name if known - self.report_error_tree(tree, format!("expected {} parameters", params.len())); - any_errors = true; + let err = self + .report_error_tree(tree, format!("expected {} parameters", params.len())); + param_error = Some(err); } for (i, ((t, a), p)) in arg_types.iter().zip(params.iter()).enumerate() { // a here is the type of the argument expression; p is // the declared type of the parameter. if !self.can_convert(&a, p) { - self.report_error_tree_ref( + let err = self.report_error_tree_ref( *t, format!( "parameter {i} has an incompatible type: expected {} but got {}", p, a ), ); - any_errors = true; + param_error = Some(err); } } - if any_errors { - return Some(Type::Error); + if let Some(param_error) = param_error { + return Some(Type::Error(param_error)); } Some(*ret.clone()) } Type::Method(_, params, ret) => { - let mut any_errors = false; + let mut param_error = None; // For the purposes of type checking ignore the self type. if params.len() != arg_types.len() { // TODO: Augment with function name if known - self.report_error_tree(tree, format!("expected {} parameters", params.len())); - any_errors = true; + let err = self + .report_error_tree(tree, format!("expected {} parameters", params.len())); + param_error = Some(err); } for (i, ((t, a), p)) in arg_types.iter().zip(params.iter()).enumerate() { // a here is the type of the argument expression; p is // the declared type of the parameter. if !self.can_convert(&a, p) { - self.report_error_tree_ref( + let err = self.report_error_tree_ref( *t, format!( "parameter {i} has an incompatible type: expected {} but got {}", p, a ), ); - any_errors = true; + param_error = Some(err); } } - if any_errors { - return Some(Type::Error); + if let Some(param_error) = param_error { + return Some(Type::Error(param_error)); } Some(*ret.clone()) } _ => { - self.report_error_tree_ref(f_ref, format!("expected a function type, got: {f}")); - Some(Type::Error) + let err = self + .report_error_tree_ref(f_ref, format!("expected a function type, got: {f}")); + Some(Type::Error(err)) } } } @@ -2045,19 +2117,21 @@ impl Semantics { let env = self.member_environment(lhs, &typ); let id = tree.nth_token(2)?; if id.kind != TokenKind::Identifier { - return Some(Type::Error); + return Some(self.type_error_for(tree)); } let id_str = id.as_str(&self.source); let Some(declaration) = env.bind(id_str) else { - if !env.is_error() { + let error = if let Some(e) = &env.error { + e.clone() + } else { self.report_error_span( id.start(), id.end(), format!("'{typ}' has no member {id_str}"), - ); - } - return Some(Type::Error); + ) + }; + return Some(Type::Error(error)); }; Some(self.type_of_declaration(Some(t), declaration)) @@ -2104,10 +2178,11 @@ impl Semantics { } EnvironmentRef::new(result) } - Type::Error => return Environment::error("error type has no members"), + Type::Error(e) => return Environment::error(e.clone()), _ => { - self.report_error_tree_ref(t, format!("cannot access members of '{typ}'")); - return Environment::error("type has no members"); + let error = + self.report_error_tree_ref(t, format!("cannot access members of '{typ}'")); + return Environment::error(error); } } } @@ -2119,20 +2194,20 @@ impl Semantics { .map(|t| t.kind == TokenKind::Semicolon) .unwrap_or(false); - let expression_type = self.type_of(tree.nth_tree(0)?); - Some(match expression_type { - Type::Unreachable => Type::Unreachable, + let expression_type = tree.nth_tree(0).map(|t| self.type_of(t)); + match expression_type { + Some(Type::Unreachable) => Some(Type::Unreachable), _ => { // A semicolon at the end of an expression statement discards // the value, leaving us with nothing. (Even if the // expression otherwise generated a type error!) if last_is_semicolon { - Type::Nothing + Some(Type::Nothing) } else { expression_type } } - }) + } } fn type_of_identifier(&self, t: TreeRef, tree: &Tree) -> Option { @@ -2144,10 +2219,12 @@ impl Semantics { return Some(self.type_of_declaration(Some(t), declaration)); } - if !environment.is_error() { - self.report_error_tree(tree, format!("cannot find value {id} here")); - } - Some(Type::Error) + let error = if let Some(e) = &environment.error { + e.clone() + } else { + self.report_error_tree(tree, format!("cannot find value {id} here")) + }; + Some(Type::Error(error)) } fn type_of_declaration(&self, t: Option, declaration: &Declaration) -> Type { @@ -2184,8 +2261,8 @@ impl Semantics { let class_decl = &self.syntax_tree[cd]; if class_decl.kind != TreeKind::ClassDecl { - self.report_error_tree(tree, "self parameter only allowed in methods"); - Some(Type::Error) + let error = self.report_error_tree(tree, "self parameter only allowed in methods"); + Some(Type::Error(error)) } else { Some(self.type_of(cd)) } @@ -2206,10 +2283,12 @@ impl Semantics { }); } - if !environment.is_error() { - self.report_error_tree(tree, "`self` is only valid in methods"); - } - Some(Type::Error) + let error = if let Some(e) = &environment.error { + e.clone() + } else { + self.report_error_tree(tree, "`self` is only valid in methods") + }; + Some(Type::Error(error)) } fn type_of_function_decl(&self, tree: &Tree) -> Option { @@ -2249,8 +2328,9 @@ impl Semantics { match tree.child_of_kind(&self.syntax_tree, TreeKind::TypeExpression) { Some(t) => Some(self.type_of(t)), None => { - self.report_error_tree(tree, format!("the parameter is missing a type")); - Some(Type::Error) + let error = + self.report_error_tree(tree, format!("the parameter is missing a type")); + Some(Type::Error(error)) } } } @@ -2262,11 +2342,12 @@ impl Semantics { let enumerable = parent.nth_tree(3)?; let item_type = match self.type_of(enumerable) { - Type::Error => Type::Error, + Type::Error(e) => Type::Error(e), Type::List(x) => (&*x).clone(), _ => { - self.report_error_tree_ref(enumerable, "this expression is not enumerable"); - Type::Error + let error = + self.report_error_tree_ref(enumerable, "this expression is not enumerable"); + Type::Error(error) } }; @@ -2344,10 +2425,12 @@ impl Semantics { let declaration = match environment.bind(id) { Some(d) => d, None => { - if !environment.is_error() { - self.report_error_tree(tree, format!("cannot find value {id} here")); - } - return Some(Type::Error); + let error = if let Some(e) = &environment.error { + e.clone() + } else { + self.report_error_tree(tree, format!("cannot find value {id} here")) + }; + return Some(Type::Error(error)); } }; match declaration { @@ -2359,18 +2442,18 @@ impl Semantics { } => Some(declaration_type.clone()), Declaration::Class { .. } => { - self.report_error_tree( + let error = self.report_error_tree( tree, format!("'{id}' is a class, and cannot be the value of a field"), ); - Some(Type::Error) + Some(Type::Error(error)) } Declaration::ImportedModule { .. } => { - self.report_error_tree( + let error = self.report_error_tree( tree, format!("'{id}' is an imported module, and cannot be the value of a field"), ); - Some(Type::Error) + Some(Type::Error(error)) } Declaration::ImportedDeclaration { semantics, @@ -2416,8 +2499,9 @@ impl Semantics { .collect(); if arms.len() == 0 { - self.report_error_tree(tree, "a match expression must have at least one arm"); - Some(Type::Error) + let error = + self.report_error_tree(tree, "a match expression must have at least one arm"); + Some(Type::Error(error)) } else { let mut actual_type = self.type_of(arms[0]); for arm in &arms[1..] { @@ -2456,7 +2540,7 @@ impl Semantics { fn type_of_import(&self, tree: &Tree) -> Option { let tok = tree.nth_token(1)?; if tok.kind != TokenKind::String { - return Some(Type::Error); // Already reported as syntax error + return Some(self.type_error_for(tree)); } // do we bind it here? it's not normalized.... @@ -2468,8 +2552,9 @@ impl Semantics { match import_map.get(&name) { Some(import) => Some(Type::Module(name.into(), import.clone())), None => { - self.report_error_tree(tree, format!("unable to resolve module import {name}")); - Some(Type::Error) + let error = + self.report_error_tree(tree, format!("unable to resolve module import {name}")); + Some(Type::Error(error)) } } } @@ -2651,7 +2736,7 @@ impl Semantics { eprintln!("\nThe environment of the tree was:"); let mut environment = Some(self.environment_of(tr)); while let Some(env) = environment { - if let Some(error) = env.error { + if let Some(error) = &env.error { eprint!(" *** ERROR: {error}"); } for (k, v) in env.declarations.iter() { @@ -2865,14 +2950,14 @@ fn check_return_statement(s: &Semantics, tree: &Tree) { let actual_type = if let Some(expr) = tree.nth_tree(1) { s.type_of(expr) } else { - Type::Error + Type::Nothing }; if !s.can_convert(&actual_type, &expected_type) { s.report_error_tree(tree, format!("callers of this function expect a value of type '{expected_type}' but this statement returns a value of type '{actual_type}'")); } } - Type::Error => (), + Type::Error(_) => (), _ => s.internal_compiler_error( Some(enclosing_function), "a return statement in here expected this to yield a function type", @@ -2942,11 +3027,13 @@ fn check_new_object_expression(s: &Semantics, tree: &Tree) { } } } - Type::Error => (), - ct => s.report_error_tree_ref( - type_expression, - format!("expected this to be a class type, but it is {ct}"), - ), + Type::Error(_) => (), + ct => { + s.report_error_tree_ref( + type_expression, + format!("expected this to be a class type, but it is {ct}"), + ); + } } } @@ -2981,7 +3068,7 @@ fn check_pattern(s: &Semantics, tree: &Tree) { s.report_error_tree_ref( pred, format!("this predicate produces '{predicate_type}', but must produce bool"), - ) + ); } } } diff --git a/fine/tests/example_tests.rs b/fine/tests/example_tests.rs index 88f2f790..fd829613 100644 --- a/fine/tests/example_tests.rs +++ b/fine/tests/example_tests.rs @@ -198,7 +198,7 @@ fn assert_type_at(module: Rc, pos: usize, expected: &str, _source_ fn assert_type_error_at( module: Rc, - errors: &[Error], + errors: &[Rc], pos: usize, expected: &str, _source_path: &str, @@ -219,7 +219,7 @@ fn assert_type_error_at( semantic_assert!( &semantics, Some(tree_ref), - matches!(tree_type, Type::Error), + matches!(tree_type, Type::Error(_)), "The type of the {:?} tree at position {pos} was '{tree_type:?}', not an error", tree[tree_ref].kind ); @@ -286,10 +286,10 @@ fn assert_compiles_to(module: Rc, expected: &str, source_path: &st } } -fn assert_no_errors(module: Rc, errors: &[Error]) { +fn assert_no_errors(module: Rc, errors: &[Rc]) { let semantics = module.semantics(); - let expected_errors: &[Error] = &[]; + let expected_errors: &[Rc] = &[]; semantic_assert_eq!( &semantics, None, @@ -341,7 +341,7 @@ fn assert_eval_ok(module: Rc, expected: &str) { } } -fn assert_errors(module: Rc, errors: &[Error], expected_errors: Vec<&str>) { +fn assert_errors(module: Rc, errors: &[Rc], expected_errors: Vec<&str>) { let semantics = module.semantics(); let errors: Vec = errors.iter().map(|e| format!("{}", e)).collect(); @@ -355,7 +355,7 @@ fn assert_errors(module: Rc, errors: &[Error], expected_errors: Ve ); } -fn assert_check_error(module: Rc, errors: &[Error], expected: &str) { +fn assert_check_error(module: Rc, errors: &[Rc], expected: &str) { let semantics = module.semantics(); semantic_assert!( diff --git a/fine/tests/expression/errors/return_statement_mismatch.fine b/fine/tests/expression/errors/return_statement_mismatch.fine index 5cbf9a67..1d62ecc9 100644 --- a/fine/tests/expression/errors/return_statement_mismatch.fine +++ b/fine/tests/expression/errors/return_statement_mismatch.fine @@ -1,3 +1,7 @@ +fun something() -> f64 { + return +} + fun test() -> f64 { if false { return "no way!"; @@ -6,4 +10,5 @@ fun test() -> f64 { } // @expect-errors: -// | __test__:3:4: callers of this function expect a value of type 'f64' but this statement returns a value of type 'string' \ No newline at end of file +// | __test__:2:2: callers of this function expect a value of type 'f64' but this statement returns a value of type 'nothing' +// | __test__:7:4: callers of this function expect a value of type 'f64' but this statement returns a value of type 'string' \ No newline at end of file