[fine] Report errors with environments, fix match bug

I'm worried that this will turn into something I always have to do
instead of something I just do sometimes: always provide the
provenance of some error type or another. Link error types to
diagnostics, etc.
This commit is contained in:
John Doty 2024-03-24 08:15:33 -07:00
parent 2ba34701ac
commit 8779aade24
3 changed files with 41 additions and 30 deletions

View file

@ -556,7 +556,7 @@ 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 = Environment::error("??");
let declaration = match ltree.kind {
// TODO: Assign to list access
@ -752,10 +752,11 @@ fn compile_pattern(c: &mut Compiler, t: TreeRef) -> CR {
// If you have a binding, dup and store now, it is in scope.
if let Some(binding) = tree.child_tree_of_kind(&c.syntax, TreeKind::VariableBinding) {
if let Some(variable) = binding.nth_token(0) {
let id = variable.as_str(&c.source);
let environment = c.semantics.environment_of(t);
let declaration = environment
.bind(variable.as_str(&c.source))
.ok_or("not bound")?;
let Some(declaration) = environment.bind(id) else {
ice!(c, t, "cannot bind pattern variable `{id}`");
};
let Declaration::Variable {
location: Location::Local,

View file

@ -431,7 +431,7 @@ pub struct Environment {
pub location: Location,
pub next_index: usize,
pub declarations: HashMap<Box<str>, Declaration>,
pub is_error: bool,
pub error: Option<&'static str>,
}
impl Environment {
@ -457,18 +457,22 @@ impl Environment {
location,
next_index,
declarations: HashMap::new(),
is_error: false,
error: None,
}
}
pub fn error() -> EnvironmentRef {
pub fn is_error(&self) -> bool {
self.error.is_some()
}
pub fn error(why: &'static str) -> EnvironmentRef {
// TODO: Exactly once?
EnvironmentRef::new(Environment {
parent: None,
location: Location::Local,
next_index: 0,
declarations: HashMap::new(),
is_error: true,
error: Some(why),
})
}
@ -1137,12 +1141,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();
return Environment::error("no rhs (pattern)");
};
// The left hand side of the `is` expression is used for wildcard types.
let Some(lhs) = tree.nth_tree(0) else {
return Environment::error();
return Environment::error("no lhs (value)");
};
self.environment_of_pattern(parent, pattern, lhs)
}
@ -1159,7 +1163,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();
return Environment::error("arm: no lhs (pattern)");
};
// The expression in the match expression is the binding for the wildcard pattern.
@ -1180,8 +1184,8 @@ impl Semantics {
}
// The expression is the first tree child of match expression.
let Some(lhs) = tree.nth_tree(1) else {
return Environment::error();
let Some(lhs) = tree.nth_tree(2) else {
return Environment::error("arm: no rhs (expression)");
};
self.environment_of_pattern(parent, pattern, lhs)
}
@ -1201,7 +1205,7 @@ impl Semantics {
return parent;
};
let Some(variable) = binding.nth_token(0) else {
return Environment::error();
return Environment::error("no variable");
};
let is_wildcard = tree
@ -1218,7 +1222,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();
return Environment::error("no type expression");
};
type_expr
};
@ -1658,7 +1662,7 @@ impl Semantics {
let tree = &self.syntax_tree[left_tree];
#[allow(unused_assignments)]
let mut environment = Environment::error();
let mut environment = Environment::error("?");
let declaration = match tree.kind {
// TODO: Assign to list access
@ -1668,7 +1672,7 @@ impl Semantics {
match environment.bind(id) {
Some(decl) => decl,
None => {
if !environment.is_error {
if !environment.is_error() {
self.report_error_tree(tree, format!("cannot find value {id} here"));
}
return Some(Type::Error);
@ -1682,7 +1686,7 @@ impl Semantics {
match environment.bind(id) {
Some(decl) => decl,
None => {
if !environment.is_error {
if !environment.is_error() {
self.report_error_tree(tree, format!("'{typ}' has no member {id}"));
}
return Some(Type::Error);
@ -1820,7 +1824,7 @@ impl Semantics {
.type_of_declaration(*tree, declaration),
),
None => {
if !environment.is_error {
if !environment.is_error() {
self.report_error_tree(tree, format!("Unrecognized type: '{token}'"));
}
Some(Type::Error)
@ -2046,7 +2050,7 @@ impl Semantics {
let id_str = id.as_str(&self.source);
let Some(declaration) = env.bind(id_str) else {
if !env.is_error {
if !env.is_error() {
self.report_error_span(
id.start(),
id.end(),
@ -2100,10 +2104,10 @@ impl Semantics {
}
EnvironmentRef::new(result)
}
Type::Error => return Environment::error(),
Type::Error => return Environment::error("error type has no members"),
_ => {
self.report_error_tree_ref(t, format!("cannot access members of '{typ}'"));
return Environment::error();
return Environment::error("type has no members");
}
}
}
@ -2140,7 +2144,7 @@ impl Semantics {
return Some(self.type_of_declaration(Some(t), declaration));
}
if !environment.is_error {
if !environment.is_error() {
self.report_error_tree(tree, format!("cannot find value {id} here"));
}
Some(Type::Error)
@ -2202,7 +2206,7 @@ impl Semantics {
});
}
if !environment.is_error {
if !environment.is_error() {
self.report_error_tree(tree, "`self` is only valid in methods");
}
Some(Type::Error)
@ -2340,7 +2344,7 @@ impl Semantics {
let declaration = match environment.bind(id) {
Some(d) => d,
None => {
if !environment.is_error {
if !environment.is_error() {
self.report_error_tree(tree, format!("cannot find value {id} here"));
}
return Some(Type::Error);
@ -2647,6 +2651,9 @@ 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 {
eprint!(" *** ERROR: {error}");
}
for (k, v) in env.declarations.iter() {
eprint!(" {k}: ");
match v {

View file

@ -46,13 +46,17 @@ class Monster {
fun print(x:string) {}
fun in_range(weapon: MeleeWeapon or RangedWeapon, distance: f64) {
fun in_range(weapon: MeleeWeapon or RangedWeapon, distance: f64) -> bool {
match weapon {
w:RangedWeapon -> distance >= w.minRange and distance <= w.maxRange,
_ -> distance == 1
}
}
fun roll_dice(x:f64) -> f64 {
0
}
fun attack(weapon: MeleeWeapon or RangedWeapon, monster: Monster, distance: f64) {
// This is worse than Bob's final version but but it works. `is` operator
// should be the same precedence as `and` and left-associative, so the
@ -79,10 +83,10 @@ fun attack(weapon: MeleeWeapon or RangedWeapon, monster: Monster, distance: f64)
if monster.health <= damage {
print("You kill the monster!");
monster.health = 0
monster.health = 0;
} else {
print("You wound the monster.");
monster.health = monster.health - damage
monster.health = monster.health - damage;
}
}
@ -147,6 +151,5 @@ fun test() -> f64 {
// like the above.
}
// @ignore not finished yet, still compiler bugs
// @no-errors
// @eval: Float(90.0)
// @eval: Float(190.0)