From 7b6cc807af4e794d475b4e66ce68f5eff11277ef Mon Sep 17 00:00:00 2001 From: Yihong Zhang Date: Wed, 24 Jul 2024 01:00:22 -0700 Subject: [PATCH 1/9] add quote and location for a few errors --- src/ast/desugar.rs | 39 ++++++----- src/ast/mod.rs | 159 ++++++++++++++++++++++++++++++++++-------- src/ast/parse.lalrpop | 20 +++--- src/constraint.rs | 6 +- src/lib.rs | 65 +++++++++++------ src/typechecking.rs | 22 +++--- 6 files changed, 216 insertions(+), 95 deletions(-) diff --git a/src/ast/desugar.rs b/src/ast/desugar.rs index 4fe3fc1e..d723106c 100644 --- a/src/ast/desugar.rs +++ b/src/ast/desugar.rs @@ -155,8 +155,7 @@ fn add_semi_naive_rule(desugar: &mut Desugar, rule: Rule) -> Option { } } -fn desugar_simplify(desugar: &mut Desugar, expr: &Expr, schedule: &Schedule) -> Vec { - let span = expr.span(); +fn desugar_simplify(desugar: &mut Desugar, expr: &Expr, schedule: &Schedule, span: Span) -> Vec { let mut res = vec![NCommand::Push(1)]; let lhs = desugar.get_fresh(); res.push(NCommand::CoreAction(Action::Let( @@ -168,8 +167,9 @@ fn desugar_simplify(desugar: &mut Desugar, expr: &Expr, schedule: &Schedule) -> res.extend( desugar_command( Command::QueryExtract { + span: span.clone(), variants: 0, - expr: Expr::Var(span, lhs), + expr: Expr::Var(span.clone(), lhs), }, desugar, false, @@ -178,7 +178,7 @@ fn desugar_simplify(desugar: &mut Desugar, expr: &Expr, schedule: &Schedule) -> .unwrap(), ); - res.push(NCommand::Pop(1)); + res.push(NCommand::Pop(span, 1)); res } @@ -233,7 +233,7 @@ pub(crate) fn desugar_calc( vec![Fact::Eq(span.clone(), vec![expr1.clone(), expr2.clone()])], )); - res.push(Command::Pop(1)); + res.push(Command::Pop(span.clone(), 1)); } desugar_commands(res, desugar, false, seminaive_transform) @@ -269,9 +269,9 @@ pub(crate) fn desugar_command( Command::BiRewrite(ruleset, rewrite) => { desugar_birewrite(ruleset, rewrite_name(&rewrite).into(), &rewrite) } - Command::Include(file) => { + Command::Include(span, file) => { let s = std::fs::read_to_string(&file) - .unwrap_or_else(|_| panic!("Failed to read file {file}")); + .unwrap_or_else(|_| panic!("{} Failed to read file {file}", span.get_quote())); return desugar_commands( desugar.parse_program(Some(file), &s)?, desugar, @@ -312,7 +312,7 @@ pub(crate) fn desugar_command( vec![NCommand::UnstableCombinedRuleset(name, subrulesets)] } Command::Action(action) => vec![NCommand::CoreAction(action)], - Command::Simplify { expr, schedule } => desugar_simplify(desugar, &expr, &schedule), + Command::Simplify { span, expr, schedule } => desugar_simplify(desugar, &expr, &schedule, span), Command::Calc(span, idents, exprs) => { desugar_calc(desugar, span, idents, exprs, seminaive_transform)? } @@ -322,7 +322,7 @@ pub(crate) fn desugar_command( Command::PrintOverallStatistics => { vec![NCommand::PrintOverallStatistics] } - Command::QueryExtract { variants, expr } => { + Command::QueryExtract { span: _, variants, expr } => { let fresh = desugar.get_fresh(); let fresh_ruleset = desugar.get_fresh(); let desugaring = if let Expr::Var(_, v) = expr { @@ -339,6 +339,7 @@ pub(crate) fn desugar_command( }; desugar.desugar_program( + // TODO: all spans should be that of the original query desugar.parse_program(None, &desugaring).unwrap(), get_all_proofs, seminaive_transform, @@ -354,26 +355,26 @@ pub(crate) fn desugar_command( res } Command::CheckProof => vec![NCommand::CheckProof], - Command::PrintFunction(symbol, size) => { - vec![NCommand::PrintTable(symbol, size)] + Command::PrintFunction(span, symbol, size) => { + vec![NCommand::PrintTable(span, symbol, size)] } - Command::PrintSize(symbol) => vec![NCommand::PrintSize(symbol)], - Command::Output { file, exprs } => vec![NCommand::Output { file, exprs }], + Command::PrintSize(span, symbol) => vec![NCommand::PrintSize(span, symbol)], + Command::Output { span, file, exprs } => vec![NCommand::Output { span, file, exprs }], Command::Push(num) => { vec![NCommand::Push(num)] } - Command::Pop(num) => { - vec![NCommand::Pop(num)] + Command::Pop(span, num) => { + vec![NCommand::Pop(span, num)] } - Command::Fail(cmd) => { + Command::Fail(span, cmd) => { let mut desugared = desugar_command(*cmd, desugar, false, seminaive_transform)?; let last = desugared.pop().unwrap(); - desugared.push(NCommand::Fail(Box::new(last))); + desugared.push(NCommand::Fail(span, Box::new(last))); return Ok(desugared); } - Command::Input { name, file } => { - vec![NCommand::Input { name, file }] + Command::Input { span, name, file } => { + vec![NCommand::Input { span, name, file }] } }; diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 30e49bc6..0bcaa8d9 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -61,10 +61,82 @@ pub struct SrcFile { pub contents: Option, } +pub struct Location { + pub line: usize, + pub col: usize, +} + +pub enum Quote { + Quote { + quote: String, + start: Location, + end: Location, + }, + NotAvailable, +} + +impl Display for Quote { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Quote::Quote { + quote, + start, + end, + } => { + if start.line == end.line { + write!( + f, + "In {}:{}-{}: {}", + start.line, start.col, end.col, quote + ) + } else { + write!( + f, + "In {}:{}-{}:{}: {}", + start.line, start.col, end.line, end.col, quote + ) + } + } + Quote::NotAvailable => write!(f, "In "), + } + } +} + +impl SrcFile { + pub fn get_location(&self, offset: usize) -> Location { + let mut line = 1; + let mut col = 1; + for (i, c) in self.contents.as_ref().unwrap().chars().enumerate() { + if i == offset { + break; + } + if c == '\n' { + line += 1; + col = 1; + } else { + col += 1; + } + } + Location { line, col } + } +} + /// A [`Span`] contains the file name and a pair of offsets representing the start and the end. #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Span(pub Arc, pub usize, pub usize); +impl Span { + pub fn get_quote(&self) -> Quote { + let Some(contents) = self.0.contents.as_ref() else { + return Quote::NotAvailable; + }; + let start = self.0.get_location(self.1); + let end = self.0.get_location(self.2 - 1); + let quote = contents[self.1..self.2].to_string(); + Quote::Quote { quote, start, end } + } +} + lazy_static! { pub(crate) static ref DUMMY_FILE: Arc = Arc::new(SrcFile { name: DUMMY_FILENAME.to_string(), @@ -114,16 +186,18 @@ where PrintOverallStatistics, Check(Span, Vec>), CheckProof, - PrintTable(Symbol, usize), - PrintSize(Option), + PrintTable(Span, Symbol, usize), + PrintSize(Span, Option), Output { + span: Span, file: String, exprs: Vec>, }, Push(usize), - Pop(usize), - Fail(Box>), + Pop(Span, usize), + Fail(Span, Box>), Input { + span: Span, name: Symbol, file: String, }, @@ -162,16 +236,24 @@ where GenericCommand::Check(span.clone(), facts.clone()) } GenericNCommand::CheckProof => GenericCommand::CheckProof, - GenericNCommand::PrintTable(name, n) => GenericCommand::PrintFunction(*name, *n), - GenericNCommand::PrintSize(name) => GenericCommand::PrintSize(*name), - GenericNCommand::Output { file, exprs } => GenericCommand::Output { + GenericNCommand::PrintTable(span, name, n) => { + GenericCommand::PrintFunction(span.clone(), *name, *n) + } + GenericNCommand::PrintSize(span, name) => { + GenericCommand::PrintSize(span.clone(), *name) + } + GenericNCommand::Output { span, file, exprs } => GenericCommand::Output { + span: span.clone(), file: file.to_string(), exprs: exprs.clone(), }, GenericNCommand::Push(n) => GenericCommand::Push(*n), - GenericNCommand::Pop(n) => GenericCommand::Pop(*n), - GenericNCommand::Fail(cmd) => GenericCommand::Fail(Box::new(cmd.to_command())), - GenericNCommand::Input { name, file } => GenericCommand::Input { + GenericNCommand::Pop(span, n) => GenericCommand::Pop(span.clone(), *n), + GenericNCommand::Fail(span, cmd) => { + GenericCommand::Fail(span.clone(), Box::new(cmd.to_command())) + } + GenericNCommand::Input { span, name, file } => GenericCommand::Input { + span: span.clone(), name: *name, file: file.clone(), }, @@ -214,16 +296,23 @@ where facts.into_iter().map(|fact| fact.visit_exprs(f)).collect(), ), GenericNCommand::CheckProof => GenericNCommand::CheckProof, - GenericNCommand::PrintTable(name, n) => GenericNCommand::PrintTable(name, n), - GenericNCommand::PrintSize(name) => GenericNCommand::PrintSize(name), - GenericNCommand::Output { file, exprs } => GenericNCommand::Output { + GenericNCommand::PrintTable(span, name, n) => { + GenericNCommand::PrintTable(span, name, n) + } + GenericNCommand::PrintSize(span, name) => GenericNCommand::PrintSize(span, name), + GenericNCommand::Output { span, file, exprs } => GenericNCommand::Output { + span, file, exprs: exprs.into_iter().map(f).collect(), }, GenericNCommand::Push(n) => GenericNCommand::Push(n), - GenericNCommand::Pop(n) => GenericNCommand::Pop(n), - GenericNCommand::Fail(cmd) => GenericNCommand::Fail(Box::new(cmd.visit_exprs(f))), - GenericNCommand::Input { name, file } => GenericNCommand::Input { name, file }, + GenericNCommand::Pop(span, n) => GenericNCommand::Pop(span, n), + GenericNCommand::Fail(span, cmd) => { + GenericNCommand::Fail(span, Box::new(cmd.visit_exprs(f))) + } + GenericNCommand::Input { span, name, file } => { + GenericNCommand::Input { span, name, file } + } } } } @@ -618,6 +707,7 @@ where PrintOverallStatistics, // TODO provide simplify docs Simplify { + span: Span, expr: GenericExpr, schedule: GenericSchedule, }, @@ -649,6 +739,7 @@ where /// Under the hood, this command is implemented with the [`EGraph::extract`] /// function. QueryExtract { + span: Span, variants: usize, expr: GenericExpr, }, @@ -682,16 +773,18 @@ where /// ``` /// prints the first 20 rows of the `Add` function. /// - PrintFunction(Symbol, usize), + PrintFunction(Span, Symbol, usize), /// Print out the number of rows in a function or all functions. - PrintSize(Option), + PrintSize(Span, Option), /// Input a CSV file directly into a function. Input { + span: Span, name: Symbol, file: String, }, /// Extract and output a set of expressions to a file. Output { + span: Span, file: String, exprs: Vec>, }, @@ -700,11 +793,11 @@ where Push(usize), /// `pop` the current egraph, restoring the previous one. /// The argument specifies how many egraphs to pop. - Pop(usize), + Pop(Span, usize), /// Assert that a command fails with an error. - Fail(Box>), + Fail(Span, Box>), /// Include another egglog file directly as text and run it. - Include(String), + Include(Span, String), } impl ToSexp for GenericCommand @@ -747,22 +840,28 @@ where GenericCommand::RunSchedule(sched) => list!("run-schedule", sched), GenericCommand::PrintOverallStatistics => list!("print-stats"), GenericCommand::Calc(_ann, args, exprs) => list!("calc", list!(++ args), ++ exprs), - GenericCommand::QueryExtract { variants, expr } => { + GenericCommand::QueryExtract { + span: _, + variants, + expr, + } => { list!("query-extract", ":variants", variants, expr) } GenericCommand::Check(_ann, facts) => list!("check", ++ facts), GenericCommand::CheckProof => list!("check-proof"), GenericCommand::Push(n) => list!("push", n), - GenericCommand::Pop(n) => list!("pop", n), - GenericCommand::PrintFunction(name, n) => list!("print-function", name, n), - GenericCommand::PrintSize(name) => list!("print-size", ++ name), - GenericCommand::Input { name, file } => list!("input", name, format!("\"{}\"", file)), - GenericCommand::Output { file, exprs } => { + GenericCommand::Pop(_span, n) => list!("pop", n), + GenericCommand::PrintFunction(_span, name, n) => list!("print-function", name, n), + GenericCommand::PrintSize(_span, name) => list!("print-size", ++ name), + GenericCommand::Input { span: _, name, file } => { + list!("input", name, format!("\"{}\"", file)) + } + GenericCommand::Output { span: _, file, exprs } => { list!("output", format!("\"{}\"", file), ++ exprs) } - GenericCommand::Fail(cmd) => list!("fail", cmd), - GenericCommand::Include(file) => list!("include", format!("\"{}\"", file)), - GenericCommand::Simplify { expr, schedule } => list!("simplify", schedule, expr), + GenericCommand::Fail(_span, cmd) => list!("fail", cmd), + GenericCommand::Include(_span, file) => list!("include", format!("\"{}\"", file)), + GenericCommand::Simplify { span: _, expr, schedule } => list!("simplify", schedule, expr), } } } diff --git a/src/ast/parse.lalrpop b/src/ast/parse.lalrpop index 4d55408a..41075e31 100644 --- a/src/ast/parse.lalrpop +++ b/src/ast/parse.lalrpop @@ -80,23 +80,23 @@ Command: Command = { "run" )?> => Command::RunSchedule(Schedule::Repeat(Span(srcfile.clone(), lo, hi), limit, Box::new( Schedule::Run(Span(srcfile.clone(), lo, hi), RunConfig { ruleset, until })))), - LParen "simplify" RParen - => Command::Simplify { expr, schedule }, + "simplify" + => Command::Simplify { span: Span(srcfile.clone(), lo, hi), expr, schedule }, "calc" LParen RParen => Command::Calc(Span(srcfile.clone(), lo, hi), idents, exprs), - LParen "query-extract" )?> RParen => Command::QueryExtract { expr, variants: variants.unwrap_or(0) }, + "query-extract" )?> => Command::QueryExtract { span: Span(srcfile.clone(), lo, hi), expr, variants: variants.unwrap_or(0) }, "check" => Command::Check(Span(srcfile.clone(), lo, hi), facts), LParen "check-proof" RParen => Command::CheckProof, "run-schedule" => Command::RunSchedule(Schedule::Sequence(Span(srcfile.clone(), lo, hi), scheds)), LParen "print-stats" RParen => Command::PrintOverallStatistics, LParen "push" RParen => Command::Push(<>.unwrap_or(1)), - LParen "pop" RParen => Command::Pop(<>.unwrap_or(1)), - LParen "print-function" RParen => Command::PrintFunction(sym, n), - LParen "print-size" RParen => Command::PrintSize(sym), - LParen "input" RParen => Command::Input { name, file }, - LParen "output" RParen => Command::Output { file, exprs }, - LParen "fail" RParen => Command::Fail(Box::new(<>)), - LParen "include" RParen => Command::Include(file), + "pop" => Command::Pop(Span(srcfile.clone(), lo, hi), n.unwrap_or(1)), + "print-function" => Command::PrintFunction(Span(srcfile.clone(), lo, hi), sym, n), + "print-size" => Command::PrintSize(Span(srcfile.clone(), lo, hi), sym), + "input" => Command::Input { span: Span(srcfile.clone(), lo, hi), name, file }, + "output" => Command::Output { span: Span(srcfile.clone(), lo, hi), file, exprs }, + "fail" => Command::Fail(Span(srcfile.clone(), lo, hi), Box::new(c)), + "include" => Command::Include(Span(srcfile.clone(), lo, hi), file), } Schedule: Schedule = { diff --git a/src/constraint.rs b/src/constraint.rs index 8c94f888..0fedf34b 100644 --- a/src/constraint.rs +++ b/src/constraint.rs @@ -336,7 +336,7 @@ impl Assignment { .collect(); let resolved_call = ResolvedCall::from_resolution(head, &types, typeinfo); if !matches!(resolved_call, ResolvedCall::Func(_)) { - return Err(TypeError::UnboundFunction(*head)); + return Err(TypeError::UnboundFunction(*head, span.clone())); } Ok(ResolvedAction::Set( span.clone(), @@ -365,7 +365,7 @@ impl Assignment { .collect(); let resolved_call = ResolvedCall::from_resolution_func_types(head, &types, typeinfo) - .ok_or_else(|| TypeError::UnboundFunction(*head))?; + .ok_or_else(|| TypeError::UnboundFunction(*head, span.clone()))?; Ok(ResolvedAction::Change( span.clone(), *change, @@ -646,7 +646,7 @@ fn get_atom_application_constraints( // do literal and global variable constraints first // as they are the most "informative" match xor_constraints.len() { - 0 => Err(TypeError::UnboundFunction(*head)), + 0 => Err(TypeError::UnboundFunction(*head, span.clone())), 1 => Ok(xor_constraints.pop().unwrap()), _ => Ok(vec![Constraint::Xor( xor_constraints.into_iter().map(Constraint::And).collect(), diff --git a/src/lib.rs b/src/lib.rs index 4ffebec5..4b94a6d7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -516,7 +516,7 @@ impl EGraph { /// it with the previously pushed egraph. /// It preserves the run report and messages from the popped /// egraph. - pub fn pop(&mut self) -> Result<(), Error> { + pub fn pop(&mut self) -> Result<(), ()> { match self.egraphs.pop() { Some(e) => { // Copy the reports and messages from the popped egraph @@ -535,7 +535,7 @@ impl EGraph { self.msgs = messages; Ok(()) } - None => Err(Error::Pop), + None => Err(()), } } @@ -739,7 +739,10 @@ impl EGraph { sym: Symbol, n: usize, ) -> Result<(Vec<(Term, Term)>, TermDag), Error> { - let f = self.functions.get(&sym).ok_or(TypeError::Unbound(sym))?; + let f = self + .functions + .get(&sym) + .ok_or(TypeError::UnboundFunction(sym, DUMMY_SPAN.clone()))?; let schema = f.schema.clone(); let nodes = f .nodes @@ -782,7 +785,8 @@ impl EGraph { let f = self .functions .get(&sym) - .ok_or(TypeError::UnboundFunction(sym))?; + // function_to_dag should have checked this + .unwrap(); let out_is_unit = f.schema.output.name() == UNIT_SYM.into(); let mut buf = String::new(); @@ -812,7 +816,10 @@ impl EGraph { pub fn print_size(&mut self, sym: Option) -> Result<(), Error> { if let Some(sym) = sym { - let f = self.functions.get(&sym).ok_or(TypeError::Unbound(sym))?; + let f = self + .functions + .get(&sym) + .ok_or(TypeError::UnboundFunction(sym, DUMMY_SPAN.clone()))?; log::info!("Function {} has size {}", sym, f.nodes.len()); self.print_msg(f.nodes.len().to_string()); Ok(()) @@ -1308,30 +1315,42 @@ impl EGraph { (0..n).for_each(|_| self.push()); log::info!("Pushed {n} levels.") } - ResolvedNCommand::Pop(n) => { + ResolvedNCommand::Pop(span, n) => { for _ in 0..n { - self.pop()?; + self.pop().map_err(|_| Error::Pop(span.clone()))?; } log::info!("Popped {n} levels.") } - ResolvedNCommand::PrintTable(f, n) => { - self.print_function(f, n)?; + ResolvedNCommand::PrintTable(span, f, n) => { + self.print_function(f, n).map_err(|e| match e { + Error::TypeError(TypeError::UnboundFunction(f, _)) => { + Error::TypeError(TypeError::UnboundFunction(f, span.clone())) + } + // This case is currently impossible + _ => e, + })?; } - ResolvedNCommand::PrintSize(f) => { - self.print_size(f)?; + ResolvedNCommand::PrintSize(span, f) => { + self.print_size(f).map_err(|e| match e { + Error::TypeError(TypeError::UnboundFunction(f, _)) => { + Error::TypeError(TypeError::UnboundFunction(f, span.clone())) + } + // This case is currently impossible + _ => e, + })?; } - ResolvedNCommand::Fail(c) => { + ResolvedNCommand::Fail(span, c) => { let result = self.run_command(*c); if let Err(e) = result { log::info!("Command failed as expected: {e}"); } else { - return Err(Error::ExpectFail); + return Err(Error::ExpectFail(span)); } } - ResolvedNCommand::Input { name, file } => { + ResolvedNCommand::Input { span: _, name, file } => { self.input_file(name, file)?; } - ResolvedNCommand::Output { file, exprs } => { + ResolvedNCommand::Output { span, file, exprs } => { let mut filename = self.fact_directory.clone().unwrap_or_default(); filename.push(file.as_str()); // append to file @@ -1340,7 +1359,7 @@ impl EGraph { .append(true) .create(true) .open(&filename) - .map_err(|e| Error::IoError(filename.clone(), e))?; + .map_err(|e| Error::IoError(filename.clone(), e, span.clone()))?; let mut termdag = TermDag::default(); for expr in exprs { let value = self.eval_resolved_expr(&expr, true)?; @@ -1348,7 +1367,7 @@ impl EGraph { let term = self.extract(value, &mut termdag, &expr_type).1; use std::io::Write; writeln!(f, "{}", termdag.to_string(&term)) - .map_err(|e| Error::IoError(filename.clone(), e))?; + .map_err(|e| Error::IoError(filename.clone(), e, span.clone()))?; } log::info!("Output to '{filename:?}'.") @@ -1606,12 +1625,12 @@ pub enum Error { PrimitiveError(Primitive, Vec), #[error("Illegal merge attempted for function {0}, {1:?} != {2:?}")] MergeError(Symbol, Value, Value), - #[error("Tried to pop too much")] - Pop, - #[error("Command should have failed.")] - ExpectFail, - #[error("IO error: {0}: {1}")] - IoError(PathBuf, std::io::Error), + #[error("{}\nTried to pop too much", .0.get_quote())] + Pop(Span), + #[error("{}\nCommand should have failed.", .0.get_quote())] + ExpectFail(Span), + #[error("{}\nIO error: {0}: {1}", .2.get_quote())] + IoError(PathBuf, std::io::Error, Span), #[error("Cannot subsume function with merge: {0}")] SubsumeMergeError(Symbol), } diff --git a/src/typechecking.rs b/src/typechecking.rs index 56c2c0ad..365da06d 100644 --- a/src/typechecking.rs +++ b/src/typechecking.rs @@ -198,12 +198,12 @@ impl TypeInfo { NCommand::Check(span, facts) => { ResolvedNCommand::Check(span.clone(), self.typecheck_facts(facts)?) } - NCommand::Fail(cmd) => ResolvedNCommand::Fail(Box::new(self.typecheck_command(cmd)?)), + NCommand::Fail(span, cmd) => ResolvedNCommand::Fail(span.clone(), Box::new(self.typecheck_command(cmd)?)), NCommand::RunSchedule(schedule) => { ResolvedNCommand::RunSchedule(self.typecheck_schedule(schedule)?) } - NCommand::Pop(n) => ResolvedNCommand::Pop(*n), - NCommand::Push(n) => ResolvedNCommand::Push(*n), + NCommand::Pop(span, n) => ResolvedNCommand::Pop(span.clone(), *n), + NCommand::Push( n) => ResolvedNCommand::Push(*n), NCommand::SetOption { name, value } => { let value = self.typecheck_expr(value, &Default::default())?; ResolvedNCommand::SetOption { name: *name, value } @@ -214,22 +214,24 @@ impl TypeInfo { } NCommand::PrintOverallStatistics => ResolvedNCommand::PrintOverallStatistics, NCommand::CheckProof => ResolvedNCommand::CheckProof, - NCommand::PrintTable(table, size) => ResolvedNCommand::PrintTable(*table, *size), - NCommand::PrintSize(n) => { + NCommand::PrintTable(span, table, size) => ResolvedNCommand::PrintTable(span.clone(), *table, *size), + NCommand::PrintSize(span, n) => { // Should probably also resolve the function symbol here - ResolvedNCommand::PrintSize(*n) + ResolvedNCommand::PrintSize(span.clone(), *n) } - NCommand::Output { file, exprs } => { + NCommand::Output { span, file, exprs } => { let exprs = exprs .iter() .map(|expr| self.typecheck_expr(expr, &Default::default())) .collect::, _>>()?; ResolvedNCommand::Output { + span: span.clone(), file: file.clone(), exprs, } } - NCommand::Input { name, file } => ResolvedNCommand::Input { + NCommand::Input { span, name, file } => ResolvedNCommand::Input { + span: span.clone(), name: *name, file: file.clone(), }, @@ -470,8 +472,8 @@ pub enum TypeError { Unbound(Symbol), #[error("Undefined sort {0}")] UndefinedSort(Symbol), - #[error("Unbound function {0}")] - UnboundFunction(Symbol), + #[error("{}\nUnbound function {0}", .1.get_quote())] + UnboundFunction(Symbol, Span), #[error("Function already bound {0}")] FunctionAlreadyBound(Symbol), #[error("Function declarations are not allowed after a push.")] From aebba3e75c3b35e345ab24c18eaabf8ca30b78dd Mon Sep 17 00:00:00 2001 From: Yihong Zhang Date: Wed, 14 Aug 2024 23:44:30 -0700 Subject: [PATCH 2/9] add spans to TypeError --- src/ast/desugar.rs | 24 +++----- src/ast/mod.rs | 23 ++++--- src/ast/parse.lalrpop | 16 ++--- src/ast/remove_globals.rs | 1 + src/core.rs | 4 +- src/function/mod.rs | 4 +- src/lib.rs | 4 +- src/sort/fn.rs | 6 +- src/sort/map.rs | 21 +++++-- src/sort/set.rs | 8 ++- src/sort/vec.rs | 8 ++- src/typechecking.rs | 125 +++++++++++++++++++------------------- 12 files changed, 130 insertions(+), 114 deletions(-) diff --git a/src/ast/desugar.rs b/src/ast/desugar.rs index d723106c..e271f432 100644 --- a/src/ast/desugar.rs +++ b/src/ast/desugar.rs @@ -18,8 +18,8 @@ impl Default for Desugar { } } -fn desugar_datatype(name: Symbol, variants: Vec) -> Vec { - vec![NCommand::Sort(name, None)] +fn desugar_datatype(span: Span, name: Symbol, variants: Vec) -> Vec { + vec![NCommand::Sort(span.clone(), name, None)] .into_iter() .chain(variants.into_iter().map(|variant| { NCommand::Function(FunctionDecl { @@ -34,6 +34,7 @@ fn desugar_datatype(name: Symbol, variants: Vec) -> Vec { cost: variant.cost, unextractable: false, ignore_viz: false, + span: variant.span, }) })) .collect() @@ -258,11 +259,12 @@ pub(crate) fn desugar_command( } Command::Function(fdecl) => desugar.desugar_function(&fdecl), Command::Relation { + span, constructor, inputs, - } => desugar.desugar_function(&FunctionDecl::relation(constructor, inputs)), + } => desugar.desugar_function(&FunctionDecl::relation(span, constructor, inputs)), Command::Declare { span, name, sort } => desugar.declare(span, name, sort), - Command::Datatype { name, variants } => desugar_datatype(name, variants), + Command::Datatype { span, name, variants } => desugar_datatype(span, name, variants), Command::Rewrite(ruleset, rewrite, subsume) => { desugar_rewrite(ruleset, rewrite_name(&rewrite).into(), &rewrite, subsume) } @@ -306,7 +308,7 @@ pub(crate) fn desugar_command( result } - Command::Sort(sort, option) => vec![NCommand::Sort(sort, option)], + Command::Sort(span, sort, option) => vec![NCommand::Sort(span, sort, option)], Command::AddRuleset(name) => vec![NCommand::AddRuleset(name)], Command::UnstableCombinedRuleset(name, subrulesets) => { vec![NCommand::UnstableCombinedRuleset(name, subrulesets)] @@ -451,6 +453,7 @@ impl Desugar { cost: None, unextractable: false, ignore_viz: false, + span: span.clone() }), NCommand::CoreAction(Action::Let( span.clone(), @@ -461,16 +464,7 @@ impl Desugar { } pub fn desugar_function(&mut self, fdecl: &FunctionDecl) -> Vec { - vec![NCommand::Function(FunctionDecl { - name: fdecl.name, - schema: fdecl.schema.clone(), - default: fdecl.default.clone(), - merge: fdecl.merge.clone(), - merge_action: fdecl.merge_action.clone(), - cost: fdecl.cost, - unextractable: fdecl.unextractable, - ignore_viz: fdecl.ignore_viz, - })] + vec![NCommand::Function(fdecl.clone())] } pub fn parent_name(&mut self, eqsort_name: Symbol) -> Symbol { diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 0bcaa8d9..9522da6e 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -172,7 +172,7 @@ where name: Symbol, value: GenericExpr, }, - Sort(Symbol, Option<(Symbol, Vec>)>), + Sort(Span, Symbol, Option<(Symbol, Vec>)>), Function(GenericFunctionDecl), AddRuleset(Symbol), UnstableCombinedRuleset(Symbol, Vec), @@ -214,7 +214,7 @@ where name: *name, value: value.clone(), }, - GenericNCommand::Sort(name, params) => GenericCommand::Sort(*name, params.clone()), + GenericNCommand::Sort(span, name, params) => GenericCommand::Sort(span.clone(), *name, params.clone()), GenericNCommand::Function(f) => GenericCommand::Function(f.clone()), GenericNCommand::AddRuleset(name) => GenericCommand::AddRuleset(*name), GenericNCommand::UnstableCombinedRuleset(name, others) => { @@ -269,7 +269,7 @@ where name, value: f(value.clone()), }, - GenericNCommand::Sort(name, params) => GenericNCommand::Sort(name, params), + GenericNCommand::Sort(span, name, params) => GenericNCommand::Sort(span, name, params), GenericNCommand::Function(func) => GenericNCommand::Function(func.visit_exprs(f)), GenericNCommand::AddRuleset(name) => GenericNCommand::AddRuleset(name), GenericNCommand::UnstableCombinedRuleset(name, rulesets) => { @@ -463,6 +463,7 @@ where /// Datatypes are also known as algebraic data types, tagged unions and sum types. Datatype { + span: Span, name: Symbol, variants: Vec, }, @@ -501,7 +502,7 @@ where /// ``` /// /// Now `MathVec` can be used as an input or output sort. - Sort(Symbol, Option<(Symbol, Vec>)>), + Sort(Span, Symbol, Option<(Symbol, Vec>)>), /// Declare an egglog function, which is a database table with a /// a functional dependency (also called a primary key) on its inputs to one output. /// @@ -561,6 +562,7 @@ where /// (function edge (i64 i64) Unit :default ()) /// ``` Relation { + span: Span, constructor: Symbol, inputs: Vec, }, @@ -812,19 +814,20 @@ where rewrite.to_sexp(*name, false, *subsume) } GenericCommand::BiRewrite(name, rewrite) => rewrite.to_sexp(*name, true, false), - GenericCommand::Datatype { name, variants } => list!("datatype", name, ++ variants), + GenericCommand::Datatype { span: _, name, variants } => list!("datatype", name, ++ variants), GenericCommand::Declare { span: _, name, sort, } => list!("declare", name, sort), GenericCommand::Action(a) => a.to_sexp(), - GenericCommand::Sort(name, None) => list!("sort", name), - GenericCommand::Sort(name, Some((name2, args))) => { + GenericCommand::Sort(_span, name, None) => list!("sort", name), + GenericCommand::Sort(_span, name, Some((name2, args))) => { list!("sort", name, list!( name2, ++ args)) } GenericCommand::Function(f) => f.to_sexp(), GenericCommand::Relation { + span: _, constructor, inputs, } => list!("relation", constructor, list!(++ inputs)), @@ -981,10 +984,12 @@ where /// Globals are desugared to functions, with this flag set to true. /// This is used by visualization to handle globals differently. pub ignore_viz: bool, + pub span: Span, } #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Variant { + pub span: Span, pub name: Symbol, pub types: Vec, pub cost: Option, @@ -1023,7 +1028,7 @@ impl Schema { } impl FunctionDecl { - pub fn relation(name: Symbol, input: Vec) -> Self { + pub fn relation(span: Span, name: Symbol, input: Vec) -> Self { Self { name, schema: Schema { @@ -1036,6 +1041,7 @@ impl FunctionDecl { cost: None, unextractable: false, ignore_viz: false, + span, } } } @@ -1058,6 +1064,7 @@ where cost: self.cost, unextractable: self.unextractable, ignore_viz: self.ignore_viz, + span: self.span, } } } diff --git a/src/ast/parse.lalrpop b/src/ast/parse.lalrpop index 41075e31..1000ea34 100644 --- a/src/ast/parse.lalrpop +++ b/src/ast/parse.lalrpop @@ -46,17 +46,17 @@ Comma: Vec = { Command: Command = { LParen "set-option" RParen => Command::SetOption { name, value }, - LParen "datatype" RParen => Command::Datatype { <> }, - LParen "sort" LParen RParen RParen => Command::Sort (name, Some((head, tail))), - LParen "sort" RParen => Command::Sort (name, None), - LParen "function" + "datatype" => Command::Datatype { span: Span(srcfile.clone(), lo, hi), name, variants }, + "sort" LParen RParen => Command::Sort (Span(srcfile.clone(), lo, hi), name, Some((head, tail))), + "sort" => Command::Sort (Span(srcfile.clone(), lo, hi), name, None), + "function" >)?> - )?> )?> RParen => { - Command::Function(FunctionDecl { name, schema, merge, merge_action: Actions::new(merge_action.unwrap_or_default()), default, cost, unextractable: unextractable.is_some(), ignore_viz: false }) + )?> )?> => { + Command::Function(FunctionDecl { span: Span(srcfile.clone(), lo, hi), name, schema, merge, merge_action: Actions::new(merge_action.unwrap_or_default()), default, cost, unextractable: unextractable.is_some(), ignore_viz: false }) }, "declare" => Command::Declare{span: Span(srcfile.clone(), lo, hi), name, sort}, - LParen "relation" > RParen => Command::Relation{constructor, inputs}, + "relation" > => Command::Relation{span: Span(srcfile.clone(), lo, hi), constructor, inputs}, LParen "ruleset" RParen => Command::AddRuleset(name), LParen "unstable-combined-ruleset" RParen => Command::UnstableCombinedRuleset(name, subrulesets), "rule" > > @@ -177,7 +177,7 @@ CallExpr: Expr = { ExprList: Vec = { LParen RParen => sexps } Variant: Variant = { - LParen RParen => Variant { <> }, + => Variant { span: Span(srcfile.clone(), lo, hi), name, types, cost }, } Type: Symbol = ; diff --git a/src/ast/remove_globals.rs b/src/ast/remove_globals.rs index eca727df..7bcf5c84 100644 --- a/src/ast/remove_globals.rs +++ b/src/ast/remove_globals.rs @@ -113,6 +113,7 @@ impl<'a> GlobalRemover<'a> { cost: None, unextractable: true, ignore_viz: true, + span: span.clone() }; let resolved_call = ResolvedCall::Func(FuncType { name: name.name, diff --git a/src/core.rs b/src/core.rs index c64dc2b3..039a926e 100644 --- a/src/core.rs +++ b/src/core.rs @@ -445,7 +445,7 @@ where match action { GenericAction::Let(span, var, expr) => { if binding.contains(var) { - return Err(TypeError::AlreadyDefined(var.to_symbol())); + return Err(TypeError::AlreadyDefined(var.to_symbol(), span.clone())); } let (actions, mapped_expr) = expr.to_core_actions(typeinfo, binding, fresh_gen)?; @@ -637,7 +637,7 @@ where GenericExpr::Var(span.clone(), v.clone()), )) } else { - Err(TypeError::Unbound(sym)) + Err(TypeError::Unbound(sym, span.clone())) } } GenericExpr::Call(span, f, args) => { diff --git a/src/function/mod.rs b/src/function/mod.rs index 028b89b6..0abdde8f 100644 --- a/src/function/mod.rs +++ b/src/function/mod.rs @@ -88,13 +88,13 @@ impl Function { for s in &decl.schema.input { input.push(match egraph.type_info().sorts.get(s) { Some(sort) => sort.clone(), - None => return Err(Error::TypeError(TypeError::Unbound(*s))), + None => return Err(Error::TypeError(TypeError::UndefinedSort(*s, decl.span.clone()))), }) } let output = match egraph.type_info().sorts.get(&decl.schema.output) { Some(sort) => sort.clone(), - None => return Err(Error::TypeError(TypeError::Unbound(decl.schema.output))), + None => return Err(Error::TypeError(TypeError::UndefinedSort(decl.schema.output, decl.span.clone()))), }; let binding = IndexSet::from_iter([ diff --git a/src/lib.rs b/src/lib.rs index 4b94a6d7..f2175fc0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1264,7 +1264,7 @@ impl EGraph { log::info!("{}", str) } // Sorts are already declared during typechecking - ResolvedNCommand::Sort(name, _presort_and_args) => { + ResolvedNCommand::Sort(_span, name, _presort_and_args) => { log::info!("Declared sort {}.", name) } ResolvedNCommand::Function(fdecl) => { @@ -1546,7 +1546,7 @@ impl EGraph { /// Add a user-defined sort pub fn add_arcsort(&mut self, arcsort: ArcSort) -> Result<(), TypeError> { - self.type_info_mut().add_arcsort(arcsort) + self.type_info_mut().add_arcsort(arcsort, DUMMY_SPAN.clone()) } /// Add a user-defined primitive diff --git a/src/sort/fn.rs b/src/sort/fn.rs index fdd4efff..403dec79 100644 --- a/src/sort/fn.rs +++ b/src/sort/fn.rs @@ -62,11 +62,11 @@ impl FunctionSort { name: Symbol, args: &[Expr], ) -> Result { - if let [inputs, Expr::Var(_, output)] = args { + if let [inputs, Expr::Var(span, output)] = args { let output_sort = typeinfo .sorts .get(output) - .ok_or(TypeError::UndefinedSort(*output))?; + .ok_or(TypeError::UndefinedSort(*output, span.clone()))?; let input_sorts = match inputs { Expr::Call(_, first, rest_args) => { @@ -82,7 +82,7 @@ impl FunctionSort { typeinfo .sorts .get(arg) - .ok_or(TypeError::UndefinedSort(*arg)) + .ok_or(TypeError::UndefinedSort(*arg, span.clone())) .map(|s| s.clone()) }) .collect::, _>>()? diff --git a/src/sort/map.rs b/src/sort/map.rs index a0f9b416..fef29ca4 100644 --- a/src/sort/map.rs +++ b/src/sort/map.rs @@ -29,13 +29,24 @@ impl MapSort { name: Symbol, args: &[Expr], ) -> Result { - if let [Expr::Var(_, k), Expr::Var(_, v)] = args { - let k = typeinfo.sorts.get(k).ok_or(TypeError::UndefinedSort(*k))?; - let v = typeinfo.sorts.get(v).ok_or(TypeError::UndefinedSort(*v))?; + if let [Expr::Var(k_span, k), Expr::Var(v_span, v)] = args { + let k = typeinfo.sorts.get(k).ok_or(TypeError::UndefinedSort(*k, k_span.clone()))?; + let v = typeinfo.sorts.get(v).ok_or(TypeError::UndefinedSort(*v, v_span.clone()))?; + + // TODO: specialize the error message + if k.is_eq_container_sort() { + return Err(TypeError::DisallowedSort( + name, + "Maps nested with other EqSort containers are not allowed".into(), + k_span.clone() + )); + } - if k.is_eq_container_sort() || v.is_container_sort() { - return Err(TypeError::UndefinedSort( + if v.is_container_sort() { + return Err(TypeError::DisallowedSort( + name, "Maps nested with other EqSort containers are not allowed".into(), + v_span.clone() )); } diff --git a/src/sort/set.rs b/src/sort/set.rs index 99e238a5..430b37a6 100644 --- a/src/sort/set.rs +++ b/src/sort/set.rs @@ -28,12 +28,14 @@ impl SetSort { name: Symbol, args: &[Expr], ) -> Result { - if let [Expr::Var(_, e)] = args { - let e = typeinfo.sorts.get(e).ok_or(TypeError::UndefinedSort(*e))?; + if let [Expr::Var(span, e)] = args { + let e = typeinfo.sorts.get(e).ok_or(TypeError::UndefinedSort(*e, span.clone()))?; if e.is_eq_container_sort() { - return Err(TypeError::UndefinedSort( + return Err(TypeError::DisallowedSort( + name, "Sets nested with other EqSort containers are not allowed".into(), + span.clone(), )); } diff --git a/src/sort/vec.rs b/src/sort/vec.rs index b616889d..59cbf5d6 100644 --- a/src/sort/vec.rs +++ b/src/sort/vec.rs @@ -43,12 +43,14 @@ impl VecSort { name: Symbol, args: &[Expr], ) -> Result { - if let [Expr::Var(_, e)] = args { - let e = typeinfo.sorts.get(e).ok_or(TypeError::UndefinedSort(*e))?; + if let [Expr::Var(span, e)] = args { + let e = typeinfo.sorts.get(e).ok_or(TypeError::UndefinedSort(*e, span.clone()))?; if e.is_eq_container_sort() { - return Err(TypeError::UndefinedSort( + return Err(TypeError::DisallowedSort( + name, "Sets nested with other EqSort containers are not allowed".into(), + span.clone(), )); } diff --git a/src/typechecking.rs b/src/typechecking.rs index 365da06d..a48f9488 100644 --- a/src/typechecking.rs +++ b/src/typechecking.rs @@ -35,12 +35,12 @@ impl Default for TypeInfo { global_types: Default::default(), }; - res.add_sort(UnitSort::new(UNIT_SYM.into())); - res.add_sort(StringSort::new("String".into())); - res.add_sort(BoolSort::new("bool".into())); - res.add_sort(I64Sort::new("i64".into())); - res.add_sort(F64Sort::new("f64".into())); - res.add_sort(RationalSort::new("Rational".into())); + res.add_sort(UnitSort::new(UNIT_SYM.into()), DUMMY_SPAN.clone()); + res.add_sort(StringSort::new("String".into()), DUMMY_SPAN.clone()); + res.add_sort(BoolSort::new("bool".into()), DUMMY_SPAN.clone()); + res.add_sort(I64Sort::new("i64".into()), DUMMY_SPAN.clone()); + res.add_sort(F64Sort::new("f64".into()), DUMMY_SPAN.clone()); + res.add_sort(RationalSort::new("Rational".into()), DUMMY_SPAN.clone()); res.presort_names.extend(MapSort::presort_names()); res.presort_names.extend(SetSort::presort_names()); @@ -76,15 +76,15 @@ impl TypeInfo { .clone() } - pub fn add_sort(&mut self, sort: S) { - self.add_arcsort(Arc::new(sort)).unwrap() + pub fn add_sort(&mut self, sort: S, span: Span) { + self.add_arcsort(Arc::new(sort), span).unwrap() } - pub fn add_arcsort(&mut self, sort: ArcSort) -> Result<(), TypeError> { + pub fn add_arcsort(&mut self, sort: ArcSort, span: Span) -> Result<(), TypeError> { let name = sort.name(); match self.sorts.entry(name) { - Entry::Occupied(_) => Err(TypeError::SortAlreadyBound(name)), + Entry::Occupied(_) => Err(TypeError::SortAlreadyBound(name, span)), Entry::Vacant(e) => { e.insert(sort.clone()); sort.register_primitives(self); @@ -141,14 +141,17 @@ impl TypeInfo { if let Some(sort) = self.sorts.get(name) { Ok(sort.clone()) } else { - Err(TypeError::Unbound(*name)) + Err(TypeError::UndefinedSort(*name, func.span.clone())) } }) .collect::, _>>()?; let output = if let Some(sort) = self.sorts.get(&func.schema.output) { Ok(sort.clone()) } else { - Err(TypeError::Unbound(func.schema.output)) + Err(TypeError::UndefinedSort( + func.schema.output, + func.span.clone(), + )) }?; Ok(FuncType { @@ -174,11 +177,11 @@ impl TypeInfo { ruleset: *ruleset, name: *name, }, - NCommand::Sort(sort, presort_and_args) => { + NCommand::Sort(span, sort, presort_and_args) => { // Note this is bad since typechecking should be pure and idempotent // Otherwise typechecking the same program twice will fail - self.declare_sort(*sort, presort_and_args)?; - ResolvedNCommand::Sort(*sort, presort_and_args.clone()) + self.declare_sort(*sort, presort_and_args, span.clone())?; + ResolvedNCommand::Sort(span.clone(), *sort, presort_and_args.clone()) } NCommand::CoreAction(Action::Let(span, var, expr)) => { let expr = self.typecheck_expr(expr, &Default::default())?; @@ -198,12 +201,14 @@ impl TypeInfo { NCommand::Check(span, facts) => { ResolvedNCommand::Check(span.clone(), self.typecheck_facts(facts)?) } - NCommand::Fail(span, cmd) => ResolvedNCommand::Fail(span.clone(), Box::new(self.typecheck_command(cmd)?)), + NCommand::Fail(span, cmd) => { + ResolvedNCommand::Fail(span.clone(), Box::new(self.typecheck_command(cmd)?)) + } NCommand::RunSchedule(schedule) => { ResolvedNCommand::RunSchedule(self.typecheck_schedule(schedule)?) } NCommand::Pop(span, n) => ResolvedNCommand::Pop(span.clone(), *n), - NCommand::Push( n) => ResolvedNCommand::Push(*n), + NCommand::Push(n) => ResolvedNCommand::Push(*n), NCommand::SetOption { name, value } => { let value = self.typecheck_expr(value, &Default::default())?; ResolvedNCommand::SetOption { name: *name, value } @@ -214,7 +219,9 @@ impl TypeInfo { } NCommand::PrintOverallStatistics => ResolvedNCommand::PrintOverallStatistics, NCommand::CheckProof => ResolvedNCommand::CheckProof, - NCommand::PrintTable(span, table, size) => ResolvedNCommand::PrintTable(span.clone(), *table, *size), + NCommand::PrintTable(span, table, size) => { + ResolvedNCommand::PrintTable(span.clone(), *table, *size) + } NCommand::PrintSize(span, n) => { // Should probably also resolve the function symbol here ResolvedNCommand::PrintSize(span.clone(), *n) @@ -244,14 +251,20 @@ impl TypeInfo { fdecl: &FunctionDecl, ) -> Result { if self.sorts.contains_key(&fdecl.name) { - return Err(TypeError::SortAlreadyBound(fdecl.name)); + return Err(TypeError::SortAlreadyBound(fdecl.name, fdecl.span.clone())); } if self.is_primitive(fdecl.name) { - return Err(TypeError::PrimitiveAlreadyBound(fdecl.name)); + return Err(TypeError::PrimitiveAlreadyBound( + fdecl.name, + fdecl.span.clone(), + )); } let ftype = self.function_to_functype(fdecl)?; if self.func_types.insert(fdecl.name, ftype).is_some() { - return Err(TypeError::FunctionAlreadyBound(fdecl.name)); + return Err(TypeError::FunctionAlreadyBound( + fdecl.name, + fdecl.span.clone(), + )); } let mut bound_vars = IndexMap::default(); let output_type = self.sorts.get(&fdecl.schema.output).unwrap(); @@ -274,6 +287,7 @@ impl TypeInfo { cost: fdecl.cost, unextractable: fdecl.unextractable, ignore_viz: fdecl.ignore_viz, + span: fdecl.span.clone(), }) } @@ -317,23 +331,24 @@ impl TypeInfo { &mut self, name: impl Into, presort_and_args: &Option<(Symbol, Vec)>, + span: Span, ) -> Result<(), TypeError> { let name = name.into(); if self.func_types.contains_key(&name) { - return Err(TypeError::FunctionAlreadyBound(name)); + return Err(TypeError::FunctionAlreadyBound(name, span)); } let sort = match presort_and_args { Some((presort, args)) => { - let mksort = self - .presorts - .get(presort) - .ok_or(TypeError::PresortNotFound(*presort))?; - mksort(self, name, args)? + if let Some(mksort) = self.presorts.get(presort) { + mksort(self, name, args)? + } else { + return Err(TypeError::PresortNotFound(*presort, span)); + } } None => Arc::new(EqSort { name }), }; - self.add_arcsort(sort) + self.add_arcsort(sort, span) } fn typecheck_rule(&self, rule: &Rule) -> Result { @@ -454,11 +469,11 @@ impl TypeInfo { #[derive(Debug, Clone, Error)] pub enum TypeError { - #[error("Arity mismatch, expected {expected} args: {expr}")] + #[error("{}\nArity mismatch, expected {expected} args: {expr}", .expr.span().get_quote())] Arity { expr: Expr, expected: usize }, #[error( - "Type mismatch: expr = {expr}, expected = {}, actual = {}, reason: {reason}", - .expected.name(), .actual.name(), + "{}\nType mismatch: expr = {expr}, expected = {}, actual = {}, reason: {reason}", + .expr.span().get_quote(), .expected.name(), .actual.name(), )] Mismatch { expr: Expr, @@ -466,42 +481,26 @@ pub enum TypeError { actual: ArcSort, reason: String, }, - #[error("Tried to unify too many literals: {}", ListDisplay(.0, "\n"))] - TooManyLiterals(Vec), #[error("Unbound symbol {0}")] - Unbound(Symbol), - #[error("Undefined sort {0}")] - UndefinedSort(Symbol), + Unbound(Symbol, Span), + #[error("{}\nUndefined sort {0}", .1.get_quote())] + UndefinedSort(Symbol, Span), + #[error("{}\nSort {0} definition is disallowed: {1}", .2.get_quote())] + DisallowedSort(Symbol, String, Span), #[error("{}\nUnbound function {0}", .1.get_quote())] UnboundFunction(Symbol, Span), - #[error("Function already bound {0}")] - FunctionAlreadyBound(Symbol), - #[error("Function declarations are not allowed after a push.")] - FunctionAfterPush(Symbol), - #[error("Cannot set the datatype {} to a value. Did you mean to use union?", .0.name)] - SetDatatype(FuncType), - #[error("Sort declarations are not allowed after a push.")] - SortAfterPush(Symbol), - #[error("Global already bound {0}")] - GlobalAlreadyBound(Symbol), - #[error("Local already bound {0} with type {}. Got: {}", .1.name(), .2.name())] - LocalAlreadyBound(Symbol, ArcSort, ArcSort), - #[error("Sort {0} already declared.")] - SortAlreadyBound(Symbol), - #[error("Primitive {0} already declared.")] - PrimitiveAlreadyBound(Symbol), - #[error("Type mismatch: expected {}, actual {}", .0.name(), .1.name())] - TypeMismatch(ArcSort, ArcSort), - #[error("Presort {0} not found.")] - PresortNotFound(Symbol), - #[error("Cannot type a variable as unit: {0}")] - UnitVar(Symbol), - #[error("Failed to infer a type for: {0}")] + #[error("{}\nFunction already bound {0}", .1.get_quote())] + FunctionAlreadyBound(Symbol, Span), + #[error("{}\nSort {0} already declared.", .1.get_quote())] + SortAlreadyBound(Symbol, Span), + #[error("{}\nPrimitive {0} already declared.", .1.get_quote())] + PrimitiveAlreadyBound(Symbol, Span), + #[error("{}\nPresort {0} not found.", .1.get_quote())] + PresortNotFound(Symbol, Span), + #[error("{}\nFailed to infer a type for: {0}", .0.span().get_quote())] InferenceFailure(Expr), - #[error("No matching primitive for: ({op} {})", ListDisplay(.inputs, " "))] - NoMatchingPrimitive { op: Symbol, inputs: Vec }, - #[error("Variable {0} was already defined")] - AlreadyDefined(Symbol), + #[error("{}\nVariable {0} was already defined", .1.get_quote())] + AlreadyDefined(Symbol, Span), #[error("All alternative definitions considered failed\n{}", .0.iter().map(|e| format!(" {e}\n")).collect::>().join(""))] AllAlternativeFailed(Vec), } From 72c3e63363bdef8f6c2908d9c78ed0cffee99bf0 Mon Sep 17 00:00:00 2001 From: Yihong Zhang Date: Thu, 15 Aug 2024 00:00:05 -0700 Subject: [PATCH 3/9] nits --- src/ast/desugar.rs | 27 ++++++++++++++++---- src/ast/mod.rs | 52 ++++++++++++++++++++++++++------------- src/ast/remove_globals.rs | 2 +- src/function/mod.rs | 14 +++++++++-- src/lib.rs | 21 ++++++++++++---- src/sort/map.rs | 14 ++++++++--- src/sort/set.rs | 5 +++- src/sort/vec.rs | 5 +++- 8 files changed, 104 insertions(+), 36 deletions(-) diff --git a/src/ast/desugar.rs b/src/ast/desugar.rs index e271f432..e3851d68 100644 --- a/src/ast/desugar.rs +++ b/src/ast/desugar.rs @@ -156,7 +156,12 @@ fn add_semi_naive_rule(desugar: &mut Desugar, rule: Rule) -> Option { } } -fn desugar_simplify(desugar: &mut Desugar, expr: &Expr, schedule: &Schedule, span: Span) -> Vec { +fn desugar_simplify( + desugar: &mut Desugar, + expr: &Expr, + schedule: &Schedule, + span: Span, +) -> Vec { let mut res = vec![NCommand::Push(1)]; let lhs = desugar.get_fresh(); res.push(NCommand::CoreAction(Action::Let( @@ -264,7 +269,11 @@ pub(crate) fn desugar_command( inputs, } => desugar.desugar_function(&FunctionDecl::relation(span, constructor, inputs)), Command::Declare { span, name, sort } => desugar.declare(span, name, sort), - Command::Datatype { span, name, variants } => desugar_datatype(span, name, variants), + Command::Datatype { + span, + name, + variants, + } => desugar_datatype(span, name, variants), Command::Rewrite(ruleset, rewrite, subsume) => { desugar_rewrite(ruleset, rewrite_name(&rewrite).into(), &rewrite, subsume) } @@ -314,7 +323,11 @@ pub(crate) fn desugar_command( vec![NCommand::UnstableCombinedRuleset(name, subrulesets)] } Command::Action(action) => vec![NCommand::CoreAction(action)], - Command::Simplify { span, expr, schedule } => desugar_simplify(desugar, &expr, &schedule, span), + Command::Simplify { + span, + expr, + schedule, + } => desugar_simplify(desugar, &expr, &schedule, span), Command::Calc(span, idents, exprs) => { desugar_calc(desugar, span, idents, exprs, seminaive_transform)? } @@ -324,7 +337,11 @@ pub(crate) fn desugar_command( Command::PrintOverallStatistics => { vec![NCommand::PrintOverallStatistics] } - Command::QueryExtract { span: _, variants, expr } => { + Command::QueryExtract { + span: _, + variants, + expr, + } => { let fresh = desugar.get_fresh(); let fresh_ruleset = desugar.get_fresh(); let desugaring = if let Expr::Var(_, v) = expr { @@ -453,7 +470,7 @@ impl Desugar { cost: None, unextractable: false, ignore_viz: false, - span: span.clone() + span: span.clone(), }), NCommand::CoreAction(Action::Let( span.clone(), diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 9522da6e..f290e246 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -78,17 +78,9 @@ pub enum Quote { impl Display for Quote { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Quote::Quote { - quote, - start, - end, - } => { + Quote::Quote { quote, start, end } => { if start.line == end.line { - write!( - f, - "In {}:{}-{}: {}", - start.line, start.col, end.col, quote - ) + write!(f, "In {}:{}-{}: {}", start.line, start.col, end.col, quote) } else { write!( f, @@ -172,7 +164,11 @@ where name: Symbol, value: GenericExpr, }, - Sort(Span, Symbol, Option<(Symbol, Vec>)>), + Sort( + Span, + Symbol, + Option<(Symbol, Vec>)>, + ), Function(GenericFunctionDecl), AddRuleset(Symbol), UnstableCombinedRuleset(Symbol, Vec), @@ -214,7 +210,9 @@ where name: *name, value: value.clone(), }, - GenericNCommand::Sort(span, name, params) => GenericCommand::Sort(span.clone(), *name, params.clone()), + GenericNCommand::Sort(span, name, params) => { + GenericCommand::Sort(span.clone(), *name, params.clone()) + } GenericNCommand::Function(f) => GenericCommand::Function(f.clone()), GenericNCommand::AddRuleset(name) => GenericCommand::AddRuleset(*name), GenericNCommand::UnstableCombinedRuleset(name, others) => { @@ -502,7 +500,11 @@ where /// ``` /// /// Now `MathVec` can be used as an input or output sort. - Sort(Span, Symbol, Option<(Symbol, Vec>)>), + Sort( + Span, + Symbol, + Option<(Symbol, Vec>)>, + ), /// Declare an egglog function, which is a database table with a /// a functional dependency (also called a primary key) on its inputs to one output. /// @@ -814,7 +816,11 @@ where rewrite.to_sexp(*name, false, *subsume) } GenericCommand::BiRewrite(name, rewrite) => rewrite.to_sexp(*name, true, false), - GenericCommand::Datatype { span: _, name, variants } => list!("datatype", name, ++ variants), + GenericCommand::Datatype { + span: _, + name, + variants, + } => list!("datatype", name, ++ variants), GenericCommand::Declare { span: _, name, @@ -856,15 +862,27 @@ where GenericCommand::Pop(_span, n) => list!("pop", n), GenericCommand::PrintFunction(_span, name, n) => list!("print-function", name, n), GenericCommand::PrintSize(_span, name) => list!("print-size", ++ name), - GenericCommand::Input { span: _, name, file } => { + GenericCommand::Input { + span: _, + name, + file, + } => { list!("input", name, format!("\"{}\"", file)) } - GenericCommand::Output { span: _, file, exprs } => { + GenericCommand::Output { + span: _, + file, + exprs, + } => { list!("output", format!("\"{}\"", file), ++ exprs) } GenericCommand::Fail(_span, cmd) => list!("fail", cmd), GenericCommand::Include(_span, file) => list!("include", format!("\"{}\"", file)), - GenericCommand::Simplify { span: _, expr, schedule } => list!("simplify", schedule, expr), + GenericCommand::Simplify { + span: _, + expr, + schedule, + } => list!("simplify", schedule, expr), } } } diff --git a/src/ast/remove_globals.rs b/src/ast/remove_globals.rs index 7bcf5c84..9c60e21c 100644 --- a/src/ast/remove_globals.rs +++ b/src/ast/remove_globals.rs @@ -113,7 +113,7 @@ impl<'a> GlobalRemover<'a> { cost: None, unextractable: true, ignore_viz: true, - span: span.clone() + span: span.clone(), }; let resolved_call = ResolvedCall::Func(FuncType { name: name.name, diff --git a/src/function/mod.rs b/src/function/mod.rs index 0abdde8f..12d0774a 100644 --- a/src/function/mod.rs +++ b/src/function/mod.rs @@ -88,13 +88,23 @@ impl Function { for s in &decl.schema.input { input.push(match egraph.type_info().sorts.get(s) { Some(sort) => sort.clone(), - None => return Err(Error::TypeError(TypeError::UndefinedSort(*s, decl.span.clone()))), + None => { + return Err(Error::TypeError(TypeError::UndefinedSort( + *s, + decl.span.clone(), + ))) + } }) } let output = match egraph.type_info().sorts.get(&decl.schema.output) { Some(sort) => sort.clone(), - None => return Err(Error::TypeError(TypeError::UndefinedSort(decl.schema.output, decl.span.clone()))), + None => { + return Err(Error::TypeError(TypeError::UndefinedSort( + decl.schema.output, + decl.span.clone(), + ))) + } }; let binding = IndexSet::from_iter([ diff --git a/src/lib.rs b/src/lib.rs index f2175fc0..133e776c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -516,7 +516,7 @@ impl EGraph { /// it with the previously pushed egraph. /// It preserves the run report and messages from the popped /// egraph. - pub fn pop(&mut self) -> Result<(), ()> { + pub fn pop(&mut self) -> Result<(), Error> { match self.egraphs.pop() { Some(e) => { // Copy the reports and messages from the popped egraph @@ -535,7 +535,7 @@ impl EGraph { self.msgs = messages; Ok(()) } - None => Err(()), + None => Err(Error::Pop(DUMMY_SPAN.clone())), } } @@ -1317,7 +1317,13 @@ impl EGraph { } ResolvedNCommand::Pop(span, n) => { for _ in 0..n { - self.pop().map_err(|_| Error::Pop(span.clone()))?; + self.pop().map_err(|err| { + if let Error::Pop(_) = err { + Error::Pop(span.clone()) + } else { + err + } + })?; } log::info!("Popped {n} levels.") } @@ -1347,7 +1353,11 @@ impl EGraph { return Err(Error::ExpectFail(span)); } } - ResolvedNCommand::Input { span: _, name, file } => { + ResolvedNCommand::Input { + span: _, + name, + file, + } => { self.input_file(name, file)?; } ResolvedNCommand::Output { span, file, exprs } => { @@ -1546,7 +1556,8 @@ impl EGraph { /// Add a user-defined sort pub fn add_arcsort(&mut self, arcsort: ArcSort) -> Result<(), TypeError> { - self.type_info_mut().add_arcsort(arcsort, DUMMY_SPAN.clone()) + self.type_info_mut() + .add_arcsort(arcsort, DUMMY_SPAN.clone()) } /// Add a user-defined primitive diff --git a/src/sort/map.rs b/src/sort/map.rs index fef29ca4..dfdb7bb9 100644 --- a/src/sort/map.rs +++ b/src/sort/map.rs @@ -30,15 +30,21 @@ impl MapSort { args: &[Expr], ) -> Result { if let [Expr::Var(k_span, k), Expr::Var(v_span, v)] = args { - let k = typeinfo.sorts.get(k).ok_or(TypeError::UndefinedSort(*k, k_span.clone()))?; - let v = typeinfo.sorts.get(v).ok_or(TypeError::UndefinedSort(*v, v_span.clone()))?; + let k = typeinfo + .sorts + .get(k) + .ok_or(TypeError::UndefinedSort(*k, k_span.clone()))?; + let v = typeinfo + .sorts + .get(v) + .ok_or(TypeError::UndefinedSort(*v, v_span.clone()))?; // TODO: specialize the error message if k.is_eq_container_sort() { return Err(TypeError::DisallowedSort( name, "Maps nested with other EqSort containers are not allowed".into(), - k_span.clone() + k_span.clone(), )); } @@ -46,7 +52,7 @@ impl MapSort { return Err(TypeError::DisallowedSort( name, "Maps nested with other EqSort containers are not allowed".into(), - v_span.clone() + v_span.clone(), )); } diff --git a/src/sort/set.rs b/src/sort/set.rs index 430b37a6..fc9dc309 100644 --- a/src/sort/set.rs +++ b/src/sort/set.rs @@ -29,7 +29,10 @@ impl SetSort { args: &[Expr], ) -> Result { if let [Expr::Var(span, e)] = args { - let e = typeinfo.sorts.get(e).ok_or(TypeError::UndefinedSort(*e, span.clone()))?; + let e = typeinfo + .sorts + .get(e) + .ok_or(TypeError::UndefinedSort(*e, span.clone()))?; if e.is_eq_container_sort() { return Err(TypeError::DisallowedSort( diff --git a/src/sort/vec.rs b/src/sort/vec.rs index 59cbf5d6..42098fbc 100644 --- a/src/sort/vec.rs +++ b/src/sort/vec.rs @@ -44,7 +44,10 @@ impl VecSort { args: &[Expr], ) -> Result { if let [Expr::Var(span, e)] = args { - let e = typeinfo.sorts.get(e).ok_or(TypeError::UndefinedSort(*e, span.clone()))?; + let e = typeinfo + .sorts + .get(e) + .ok_or(TypeError::UndefinedSort(*e, span.clone()))?; if e.is_eq_container_sort() { return Err(TypeError::DisallowedSort( From 994d8c366b10905b8aa0011c63662696a3dc23ae Mon Sep 17 00:00:00 2001 From: Yihong Zhang Date: Thu, 15 Aug 2024 14:07:04 -0700 Subject: [PATCH 4/9] nits --- src/constraint.rs | 1 - src/typechecking.rs | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/constraint.rs b/src/constraint.rs index 15ee4d2b..7c3d7f87 100644 --- a/src/constraint.rs +++ b/src/constraint.rs @@ -57,7 +57,6 @@ impl ConstraintError { expr: x.to_expr(), expected: v1.clone(), actual: v2.clone(), - reason: "mismatch".into(), }, ConstraintError::UnconstrainedVar(v) => TypeError::InferenceFailure(v.to_expr()), ConstraintError::NoConstraintSatisfied(constraints) => TypeError::AllAlternativeFailed( diff --git a/src/typechecking.rs b/src/typechecking.rs index 88981c92..e64143da 100644 --- a/src/typechecking.rs +++ b/src/typechecking.rs @@ -472,16 +472,15 @@ pub enum TypeError { #[error("{}\nArity mismatch, expected {expected} args: {expr}", .expr.span().get_quote())] Arity { expr: Expr, expected: usize }, #[error( - "{}\nType mismatch: expr = {expr}, expected = {}, actual = {}, reason: {reason}", + "{}\n Expect expression {expr} to have type {}, but get type {}", .expr.span().get_quote(), .expected.name(), .actual.name(), )] Mismatch { expr: Expr, expected: ArcSort, actual: ArcSort, - reason: String, }, - #[error("Unbound symbol {0}")] + #[error("{}\nUnbound symbol {0}", .1.get_quote())] Unbound(Symbol, Span), #[error("{}\nUndefined sort {0}", .1.get_quote())] UndefinedSort(Symbol, Span), From 8df581d5642872db4e86833245c3dc01ae9bd7ae Mon Sep 17 00:00:00 2001 From: Yihong Zhang Date: Thu, 15 Aug 2024 14:39:56 -0700 Subject: [PATCH 5/9] improve doc for parse_and_run_program --- src/lib.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index aa1a762b..c5776717 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1525,8 +1525,12 @@ impl EGraph { self.desugar.parse_program(filename, input) } - /// Parse and run a program, returning a list of messages. - /// If filename is None, a default name will be provided + + /// Takes a source program `input`, parses it, runs it, and returns a list of messages. + /// + /// `filename` is an optional argument to indicate the source of + /// the program for error reporting. If `filename` is `None`, + /// a default name will be used. pub fn parse_and_run_program( &mut self, filename: Option, From 36bcdd5f0b60afa57d99812e5588f8dedd8aac92 Mon Sep 17 00:00:00 2001 From: Yihong Zhang Date: Thu, 15 Aug 2024 14:42:31 -0700 Subject: [PATCH 6/9] nits --- src/lib.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c5776717..553428d1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1525,10 +1525,9 @@ impl EGraph { self.desugar.parse_program(filename, input) } - /// Takes a source program `input`, parses it, runs it, and returns a list of messages. - /// - /// `filename` is an optional argument to indicate the source of + /// + /// `filename` is an optional argument to indicate the source of /// the program for error reporting. If `filename` is `None`, /// a default name will be used. pub fn parse_and_run_program( From 8f962beab690b17022e25302107cddf0d5aa5cc7 Mon Sep 17 00:00:00 2001 From: Yihong Zhang Date: Fri, 16 Aug 2024 16:01:37 -0700 Subject: [PATCH 7/9] add filename --- src/ast/mod.rs | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 26bda0fd..fb25e7e3 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -73,6 +73,7 @@ pub struct Location { pub enum Quote { Quote { + filename: String, quote: String, start: Location, end: Location, @@ -83,13 +84,22 @@ pub enum Quote { impl Display for Quote { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Quote::Quote { quote, start, end } => { + Quote::Quote { + filename, + quote, + start, + end, + } => { if start.line == end.line { - write!(f, "In {}:{}-{}: {}", start.line, start.col, end.col, quote) + write!( + f, + "In {}:{}-{} of {filename}: {}", + start.line, start.col, end.col, quote + ) } else { write!( f, - "In {}:{}-{}:{}: {}", + "In {}:{}-{}:{} of {filename}: {}", start.line, start.col, end.line, end.col, quote ) } @@ -127,10 +137,16 @@ impl Span { let Some(contents) = self.0.contents.as_ref() else { return Quote::NotAvailable; }; + let filename = self.0.name.clone(); let start = self.0.get_location(self.1); let end = self.0.get_location(self.2 - 1); let quote = contents[self.1..self.2].to_string(); - Quote::Quote { quote, start, end } + Quote::Quote { + filename, + quote, + start, + end, + } } } From 039a6725782bc106eb6a03fa7203c6b309c3b8c5 Mon Sep 17 00:00:00 2001 From: Yihong Zhang Date: Thu, 22 Aug 2024 14:30:36 -0700 Subject: [PATCH 8/9] incorporate feedbacks --- src/ast/desugar.rs | 80 ++++++++++++++++++++++++++++++---------------- src/ast/mod.rs | 16 ++++++---- 2 files changed, 62 insertions(+), 34 deletions(-) diff --git a/src/ast/desugar.rs b/src/ast/desugar.rs index e3851d68..b8120f94 100644 --- a/src/ast/desugar.rs +++ b/src/ast/desugar.rs @@ -262,12 +262,16 @@ pub(crate) fn desugar_command( Command::SetOption { name, value } => { vec![NCommand::SetOption { name, value }] } - Command::Function(fdecl) => desugar.desugar_function(&fdecl), + Command::Function(fdecl) => vec![NCommand::Function(fdecl)], Command::Relation { span, constructor, inputs, - } => desugar.desugar_function(&FunctionDecl::relation(span, constructor, inputs)), + } => vec![NCommand::Function(FunctionDecl::relation( + span, + constructor, + inputs, + ))], Command::Declare { span, name, sort } => desugar.declare(span, name, sort), Command::Datatype { span, @@ -338,31 +342,57 @@ pub(crate) fn desugar_command( vec![NCommand::PrintOverallStatistics] } Command::QueryExtract { - span: _, + span, variants, expr, } => { - let fresh = desugar.get_fresh(); - let fresh_ruleset = desugar.get_fresh(); - let desugaring = if let Expr::Var(_, v) = expr { - format!("(extract {v} {variants})") + let variants = Expr::Lit(span.clone(), Literal::Int(variants.try_into().unwrap())); + if let Expr::Var(..) = expr { + // (extract {v} {variants}) + vec![NCommand::CoreAction(Action::Extract( + span.clone(), + expr, + variants, + ))] } else { - format!( - "(check {expr}) - (ruleset {fresh_ruleset}) - (rule ((= {fresh} {expr})) - ((extract {fresh} {variants})) - :ruleset {fresh_ruleset}) - (run {fresh_ruleset} 1)" - ) - }; - - desugar.desugar_program( - // TODO: all spans should be that of the original query - desugar.parse_program(None, &desugaring).unwrap(), - get_all_proofs, - seminaive_transform, - )? + // (check {expr}) + // (ruleset {fresh_ruleset}) + // (rule ((= {fresh} {expr})) + // ((extract {fresh} {variants})) + // :ruleset {fresh_ruleset}) + // (run {fresh_ruleset} 1) + let fresh = desugar.get_fresh(); + let fresh_ruleset = desugar.get_fresh(); + let fresh_rulename = desugar.get_fresh(); + let rule = Rule { + span: span.clone(), + body: vec![Fact::Eq( + span.clone(), + vec![Expr::Var(span.clone(), fresh), expr.clone()], + )], + head: Actions::singleton(Action::Extract( + span.clone(), + Expr::Var(span.clone(), fresh), + variants, + )), + }; + vec![ + NCommand::Check(span.clone(), vec![Fact::Fact(expr.clone())]), + NCommand::AddRuleset(fresh_ruleset), + NCommand::NormRule { + name: fresh_rulename, + ruleset: fresh_ruleset, + rule, + }, + NCommand::RunSchedule(Schedule::Run( + span.clone(), + RunConfig { + ruleset: fresh_ruleset, + until: None, + }, + )), + ] + } } Command::Check(span, facts) => { let res = vec![NCommand::Check(span, facts)]; @@ -480,10 +510,6 @@ impl Desugar { ] } - pub fn desugar_function(&mut self, fdecl: &FunctionDecl) -> Vec { - vec![NCommand::Function(fdecl.clone())] - } - pub fn parent_name(&mut self, eqsort_name: Symbol) -> Symbol { self.fresh_gen .generate_special(&format!("{}Parent", eqsort_name).into()) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index fb25e7e3..0ffbe399 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -66,22 +66,24 @@ pub struct SrcFile { pub contents: Option, } +#[derive(Clone, Copy)] pub struct Location { pub line: usize, pub col: usize, } -pub enum Quote { +#[derive(Clone, Copy)] +pub enum Quote<'a> { Quote { - filename: String, - quote: String, + filename: &'a str, + quote: &'a str, start: Location, end: Location, }, NotAvailable, } -impl Display for Quote { +impl<'a> Display for Quote<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Quote::Quote { @@ -133,14 +135,14 @@ impl SrcFile { pub struct Span(pub Arc, pub usize, pub usize); impl Span { - pub fn get_quote(&self) -> Quote { + pub fn get_quote(&self) -> Quote<'_> { let Some(contents) = self.0.contents.as_ref() else { return Quote::NotAvailable; }; - let filename = self.0.name.clone(); + let filename = &self.0.name; let start = self.0.get_location(self.1); let end = self.0.get_location(self.2 - 1); - let quote = contents[self.1..self.2].to_string(); + let quote = &contents[self.1..self.2]; Quote::Quote { filename, quote, From a9ef260eb02d333ec34715604ea65b1701701971 Mon Sep 17 00:00:00 2001 From: Yihong Zhang Date: Thu, 22 Aug 2024 14:52:31 -0700 Subject: [PATCH 9/9] add source locations to CheckError, NoSuchRuleset, CombinedRulesetError --- src/lib.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 553428d1..a9c50dfb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1114,10 +1114,10 @@ impl EGraph { }; Ok(name) } - Ruleset::Combined(_, _) => Err(Error::CombinedRulesetError(ruleset)), + Ruleset::Combined(_, _) => Err(Error::CombinedRulesetError(ruleset, rule.span)), } } else { - Err(Error::NoSuchRuleset(ruleset)) + Err(Error::NoSuchRuleset(ruleset, rule.span)) } } @@ -1238,9 +1238,9 @@ impl EGraph { Err(()) }); if !matched { - // TODO add useful info here Err(Error::CheckError( facts.iter().map(|f| f.clone().make_unresolved()).collect(), + span.clone(), )) } else { Ok(()) @@ -1623,6 +1623,10 @@ impl EGraph { } } +// Currently, only the following errors can thrown without location information: +// * PrimitiveError +// * MergeError +// * SubsumeMergeError #[derive(Debug, Error)] pub enum Error { #[error(transparent)] @@ -1633,12 +1637,12 @@ pub enum Error { TypeError(#[from] TypeError), #[error("Errors:\n{}", ListDisplay(.0, "\n"))] TypeErrors(Vec), - #[error("Check failed: \n{}", ListDisplay(.0, "\n"))] - CheckError(Vec), - #[error("No such ruleset: {0}")] - NoSuchRuleset(Symbol), - #[error("Attempted to add a rule to combined ruleset {0}. Combined rulesets may only depend on other rulesets.")] - CombinedRulesetError(Symbol), + #[error("{}\nCheck failed: \n{}", .1.get_quote(), ListDisplay(.0, "\n"))] + CheckError(Vec, Span), + #[error("{}\nNo such ruleset: {0}", .1.get_quote())] + NoSuchRuleset(Symbol, Span), + #[error("{}\nAttempted to add a rule to combined ruleset {0}. Combined rulesets may only depend on other rulesets.", .1.get_quote())] + CombinedRulesetError(Symbol, Span), #[error("Evaluating primitive {0:?} failed. ({0:?} {:?})", ListDebug(.1, " "))] PrimitiveError(Primitive, Vec), #[error("Illegal merge attempted for function {0}, {1:?} != {2:?}")]