[fine] Environment cycles are internal compiler errors
Re-work the failure to add official ICE support, and extract the reporting from the tests into the semantics, so everybody benefits.
This commit is contained in:
parent
3877c6e547
commit
2f71946d21
2 changed files with 74 additions and 53 deletions
|
|
@ -344,18 +344,16 @@ impl<'a> Semantics<'a> {
|
||||||
|
|
||||||
pub fn environment_of(&self, t: TreeRef) -> EnvironmentRef {
|
pub fn environment_of(&self, t: TreeRef) -> EnvironmentRef {
|
||||||
{
|
{
|
||||||
let state = &mut self.environments.borrow_mut()[t.index()];
|
// I want to make sure that this borrow is dropped after this block.
|
||||||
|
let mut borrow = self.environments.borrow_mut();
|
||||||
|
let state = &mut borrow[t.index()];
|
||||||
match state {
|
match state {
|
||||||
Incremental::None => (),
|
Incremental::None => (),
|
||||||
Incremental::Complete(e) => return e.clone(),
|
Incremental::Complete(e) => return e.clone(),
|
||||||
Incremental::InProgress => {
|
Incremental::InProgress => {
|
||||||
// eprintln!("environment_of circular => {t:?}");
|
// eprintln!("environment_of circular => {t:?}");
|
||||||
self.report_error_tree_ref(
|
drop(borrow);
|
||||||
t,
|
self.internal_compiler_error(Some(t), "circular environment dependency");
|
||||||
"INTERNAL COMPILER ERROR: Circular dependency detected: environment",
|
|
||||||
);
|
|
||||||
*state = Incremental::Complete(self.empty_environment.clone());
|
|
||||||
return self.empty_environment.clone();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*state = Incremental::InProgress;
|
*state = Incremental::InProgress;
|
||||||
|
|
@ -759,4 +757,66 @@ impl<'a> Semantics<'a> {
|
||||||
self.report_error_tree(tree, format!("cannot find value {id} here"));
|
self.report_error_tree(tree, format!("cannot find value {id} here"));
|
||||||
Some(Type::Error)
|
Some(Type::Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn dump_compiler_state(&self, tr: Option<TreeRef>) {
|
||||||
|
eprintln!("Parsed the tree as:");
|
||||||
|
eprintln!("\n{}", self.syntax_tree.dump(true));
|
||||||
|
|
||||||
|
{
|
||||||
|
let errors = self.errors.borrow();
|
||||||
|
if errors.len() == 0 {
|
||||||
|
eprintln!("There were no errors reported during checking.\n");
|
||||||
|
} else {
|
||||||
|
eprintln!(
|
||||||
|
"{} error{} reported during checking:",
|
||||||
|
errors.len(),
|
||||||
|
if errors.len() == 1 { "" } else { "s" }
|
||||||
|
);
|
||||||
|
for error in errors.iter() {
|
||||||
|
eprintln!(" Error: {error}");
|
||||||
|
}
|
||||||
|
eprintln!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(tr) = tr {
|
||||||
|
eprintln!("This is about the tree: {:?}", &self.syntax_tree[tr]);
|
||||||
|
eprintln!("The logical parent chain of the tree was:\n");
|
||||||
|
let mut current = Some(tr);
|
||||||
|
while let Some(c) = current {
|
||||||
|
let t = &self.syntax_tree[c];
|
||||||
|
eprintln!(" {:?} [{}-{})", t.kind, t.start_pos, t.end_pos);
|
||||||
|
current = self.logical_parents[c.index()];
|
||||||
|
}
|
||||||
|
eprintln!("\nThe environment of the tree was:");
|
||||||
|
let mut environment = Some(self.environment_of(tr));
|
||||||
|
while let Some(env) = environment {
|
||||||
|
for (k, v) in env.declarations.iter() {
|
||||||
|
eprintln!(" {k}: {:?}", v.declaration_type);
|
||||||
|
}
|
||||||
|
environment = env.parent.clone();
|
||||||
|
}
|
||||||
|
eprintln!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn internal_compiler_error(&self, tr: Option<TreeRef>, message: &str) -> ! {
|
||||||
|
eprintln!("Internan compiler error: {message}!");
|
||||||
|
self.dump_compiler_state(tr);
|
||||||
|
panic!("INTERNAL COMPILER ERROR: {message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::parser::parse;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "INTERNAL COMPILER ERROR: oh no")]
|
||||||
|
pub fn ice() {
|
||||||
|
let (tree, lines) = parse("1 + 1");
|
||||||
|
let semantics = Semantics::new(&tree, &lines);
|
||||||
|
semantics.internal_compiler_error(tree.root(), "oh no");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use fine::parser::{SyntaxTree, TreeRef};
|
use fine::parser::SyntaxTree;
|
||||||
use fine::semantics::{Semantics, Type};
|
use fine::semantics::{Semantics, Type};
|
||||||
use fine::tokens::Lines;
|
use fine::tokens::Lines;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|
@ -85,52 +85,11 @@ fn assert_concrete(tree: &SyntaxTree, expected: &str, source_path: &str) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report_semantic_error(semantics: &Semantics, tr: Option<TreeRef>, message: &str) {
|
|
||||||
let tree = semantics.tree();
|
|
||||||
|
|
||||||
println!("{message}! Parsed the tree as:");
|
|
||||||
println!("\n{}", tree.dump(true));
|
|
||||||
|
|
||||||
let errors = semantics.snapshot_errors();
|
|
||||||
if errors.len() == 0 {
|
|
||||||
println!("There were no errors reported during checking.\n");
|
|
||||||
} else {
|
|
||||||
println!(
|
|
||||||
"{} error{} reported during checking:",
|
|
||||||
errors.len(),
|
|
||||||
if errors.len() == 1 { "" } else { "s" }
|
|
||||||
);
|
|
||||||
for error in &errors {
|
|
||||||
println!(" Error: {error}");
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(tr) = tr {
|
|
||||||
println!("About the tree: {:?}", &tree[tr]);
|
|
||||||
println!("The logical parent chain of the tree was:\n");
|
|
||||||
let mut current = Some(tr);
|
|
||||||
while let Some(c) = current {
|
|
||||||
let t = &tree[c];
|
|
||||||
println!(" {:?} [{}-{})", t.kind, t.start_pos, t.end_pos);
|
|
||||||
current = semantics.logical_parent(c);
|
|
||||||
}
|
|
||||||
println!("\nThe environment of the tree was:");
|
|
||||||
let mut environment = Some(semantics.environment_of(tr));
|
|
||||||
while let Some(env) = environment {
|
|
||||||
for (k, v) in env.declarations.iter() {
|
|
||||||
println!(" {k}: {:?}", v.declaration_type);
|
|
||||||
}
|
|
||||||
environment = env.parent.clone();
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! semantic_panic {
|
macro_rules! semantic_panic {
|
||||||
($semantics:expr, $tr:expr, $($t:tt)*) => {{
|
($semantics:expr, $tr:expr, $($t:tt)*) => {{
|
||||||
let message = format!($($t)*);
|
let message = format!($($t)*);
|
||||||
report_semantic_error($semantics, $tr, &message);
|
eprintln!("{message}!");
|
||||||
|
$semantics.dump_compiler_state($tr);
|
||||||
panic!("{message}");
|
panic!("{message}");
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
@ -139,7 +98,8 @@ macro_rules! semantic_assert {
|
||||||
($semantics:expr, $tr:expr, $pred:expr, $($t:tt)*) => {{
|
($semantics:expr, $tr:expr, $pred:expr, $($t:tt)*) => {{
|
||||||
if !$pred {
|
if !$pred {
|
||||||
let message = format!($($t)*);
|
let message = format!($($t)*);
|
||||||
report_semantic_error($semantics, $tr, &message);
|
eprintln!("{message}!");
|
||||||
|
$semantics.dump_compiler_state($tr);
|
||||||
panic!("{message}");
|
panic!("{message}");
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
|
|
@ -151,7 +111,8 @@ macro_rules! semantic_assert_eq {
|
||||||
let rr = $right;
|
let rr = $right;
|
||||||
if ll != rr {
|
if ll != rr {
|
||||||
let message = format!($($t)*);
|
let message = format!($($t)*);
|
||||||
report_semantic_error($semantics, $tr, &message);
|
eprintln!("{message}!");
|
||||||
|
$semantics.dump_compiler_state($tr);
|
||||||
assert_eq!(ll, rr, "{}", message);
|
assert_eq!(ll, rr, "{}", message);
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue