From f07c0e75be357c83666cca407148ff9daf16f1c2 Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Mon, 11 Apr 2022 15:15:04 -0400 Subject: [PATCH] tamer: tameld (TameldError): Error sum type This aggregates all non-panic errors that can occur during link time, making `Box` unnecessary. I've been wanting to do this for a long time, so it's nice seeing this come together. This is a powerful tool, in that we know, at compile time, all errors that can occur, and properly report on them and compose them. This method of error composition ensures that all errors have a chance to be handled within their context, though it'll take time to do so in a decent way. This just maintains compatibility with the dynamic dispatch that was previous occurring. This work is being done to introduce the initial diagnostic system, which was really difficult/confusing to do without proper errors types at the top level, considering the toplevel is responsible for triggering the diagnostic reporting. The cycle error is in particular going to be interesting once the system is in place, especially once it provides spans in the future, since it will guide the user through the code to understand how the cycle formed. More to come. DEV-10935 --- tamer/src/bin/tameld.rs | 4 +- tamer/src/ld/poc.rs | 167 +++++++++++++++++++++++++++++--------- tamer/src/obj/xmlo/mod.rs | 2 +- 3 files changed, 130 insertions(+), 43 deletions(-) diff --git a/tamer/src/bin/tameld.rs b/tamer/src/bin/tameld.rs index 98f0e0a8..c10ce86e 100644 --- a/tamer/src/bin/tameld.rs +++ b/tamer/src/bin/tameld.rs @@ -29,7 +29,7 @@ extern crate tamer; use getopts::{Fail, Options}; use std::env; use std::error::Error; -use tamer::ld::poc; +use tamer::ld::poc::{self, TameldError}; /// Types of commands enum Command { @@ -50,7 +50,7 @@ enum Emit { } /// Entrypoint for the linker -pub fn main() -> Result<(), Box> { +pub fn main() -> Result<(), TameldError> { let args: Vec = env::args().collect(); let program = &args[0]; let opts = get_opts(); diff --git a/tamer/src/ld/poc.rs b/tamer/src/ld/poc.rs index 97ab26ae..e7d699d5 100644 --- a/tamer/src/ld/poc.rs +++ b/tamer/src/ld/poc.rs @@ -25,36 +25,37 @@ use super::xmle::{ xir::lower_iter, XmleSections, }; -use crate::sym::SymbolId; -use crate::sym::{GlobalSymbolIntern, GlobalSymbolResolve}; -use crate::xir::writer::XmlWriter; use crate::{ asg::{Asg, DefaultAsg, IdentObject}, - xir::DefaultEscaper, -}; -use crate::{ fs::{ Filesystem, FsCanonicalizer, PathFile, VisitOnceFile, VisitOnceFilesystem, }, ld::xmle::Sections, -}; -use crate::{ - obj::xmlo::{AsgBuilder, AsgBuilderState, XmloReader}, - xir::Escaper, + obj::xmlo::{ + AsgBuilder, AsgBuilderError, AsgBuilderState, XmloError, XmloReader, + }, + parse::ParseError, + sym::{GlobalSymbolIntern, GlobalSymbolResolve, SymbolId}, + xir::{ + flat::{self, Object as XirfToken, StateError as XirfError}, + writer::{Error as XirWriterError, XmlWriter}, + DefaultEscaper, Error as XirError, Escaper, Token as XirToken, + }, }; use fxhash::FxBuildHasher; use petgraph_graphml::GraphMl; -use std::error::Error; -use std::fs; -use std::io::Write; -use std::io::{BufReader, BufWriter}; -use std::path::{Path, PathBuf}; +use std::{ + fmt::Display, + fs, + io::{self, BufReader, BufWriter, Write}, + path::{Path, PathBuf}, +}; type LinkerAsg = DefaultAsg; type LinkerAsgBuilderState = AsgBuilderState; -pub fn xmle(package_path: &str, output: &str) -> Result<(), Box> { +pub fn xmle(package_path: &str, output: &str) -> Result<(), TameldError> { let mut fs = VisitOnceFilesystem::new(); let mut depgraph = LinkerAsg::with_capacity(65536, 65536); let escaper = DefaultEscaper::default(); @@ -84,23 +85,21 @@ pub fn xmle(package_path: &str, output: &str) -> Result<(), Box> { let sorted = match sort(&depgraph, &roots, Sections::new()) { Ok(sections) => sections, Err(SortError::Cycles(cycles)) => { - let msg: Vec = cycles - .into_iter() - .map(|cycle| { - let mut path = cycle - .into_iter() - .map(|obj| { - depgraph.get(obj).unwrap().name().lookup_str() - }) - .collect::>(); + return Err(TameldError::CycleError( + cycles + .into_iter() + .map(|cycle| { + let mut path: Vec = cycle + .into_iter() + .map(|obj| depgraph.get(obj).unwrap().name()) + .collect(); - path.reverse(); - path.push(path[0].clone()); - format!("cycle: {}", path.join(" -> ")) - }) - .collect(); - - return Err(msg.join("\n").into()); + path.reverse(); + path.push(path[0].clone()); + path + }) + .collect(), + )) } Err(e) => return Err(e.into()), }; @@ -116,7 +115,7 @@ pub fn xmle(package_path: &str, output: &str) -> Result<(), Box> { Ok(()) } -pub fn graphml(package_path: &str, output: &str) -> Result<(), Box> { +pub fn graphml(package_path: &str, output: &str) -> Result<(), TameldError> { let mut fs = VisitOnceFilesystem::new(); let mut depgraph = LinkerAsg::with_capacity(65536, 65536); let escaper = DefaultEscaper::default(); @@ -175,7 +174,7 @@ fn load_xmlo<'a, P: AsRef, S: Escaper>( depgraph: &mut LinkerAsg, escaper: &S, state: LinkerAsgBuilderState, -) -> Result> { +) -> Result { let PathFile(path, file, ctx): PathFile> = match fs.open(path_str)? { VisitOnceFile::FirstVisit(file) => file, @@ -195,7 +194,7 @@ fn load_xmlo<'a, P: AsRef, S: Escaper>( { use crate::iter::into_iter_while_ok; use crate::parse::{ParseState, Parsed}; - use crate::xir::{flat, reader::XmlXirReader}; + use crate::xir::reader::XmlXirReader; // TODO: This entire block is a WIP and will be incrementally // abstracted away. @@ -247,7 +246,7 @@ fn output_xmle<'a, X: XmleSections<'a>, S: Escaper>( relroot: SymbolId, output: &str, escaper: &S, -) -> Result<(), Box> { +) -> Result<(), TameldError> { let file = fs::File::create(output)?; let mut buf = BufWriter::new(file); @@ -261,8 +260,96 @@ fn output_xmle<'a, X: XmleSections<'a>, S: Escaper>( Ok(()) } -#[cfg(test)] -mod test { - #[test] - fn placeholder() {} +// TODO: This, like everything else here, needs a home. +// TODO: Better encapsulation for `*ParseError` types. +/// Linker (`tameld`) error. +/// +/// This represents the aggregation of all possible errors that can occur +/// during link-time. +/// This cannot include panics, +/// but efforts have been made to reduce panics to situations that +/// represent the equivalent of assertions. +#[derive(Debug)] +pub enum TameldError { + Io(io::Error), + SortError(SortError), + XirError(XirError), + XirfParseError(ParseError), + XmloParseError(ParseError), + AsgBuilderError(AsgBuilderError), + XirWriterError(XirWriterError), + + CycleError(Vec>), +} + +impl From for TameldError { + fn from(e: io::Error) -> Self { + Self::Io(e) + } +} + +impl From for TameldError { + fn from(e: SortError) -> Self { + Self::SortError(e) + } +} + +impl From for TameldError { + fn from(e: XirError) -> Self { + Self::XirError(e) + } +} + +impl From> for TameldError { + fn from(e: ParseError) -> Self { + Self::XmloParseError(e) + } +} + +impl From> for TameldError { + fn from(e: ParseError) -> Self { + Self::XirfParseError(e) + } +} + +impl From for TameldError { + fn from(e: AsgBuilderError) -> Self { + Self::AsgBuilderError(e) + } +} + +impl From for TameldError { + fn from(e: XirWriterError) -> Self { + Self::XirWriterError(e) + } +} + +impl Display for TameldError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Io(e) => Display::fmt(e, f), + Self::SortError(e) => Display::fmt(e, f), + Self::XirError(e) => Display::fmt(e, f), + Self::XirfParseError(e) => Display::fmt(e, f), + Self::XmloParseError(e) => Display::fmt(e, f), + Self::AsgBuilderError(e) => Display::fmt(e, f), + Self::XirWriterError(e) => Display::fmt(e, f), + + TameldError::CycleError(cycles) => { + for cycle in cycles { + writeln!( + f, + "cycle: {}", + cycle + .iter() + .map(|sym| sym.lookup_str()) + .collect::>() + .join(" -> ") + )?; + } + + Ok(()) + } + } + } } diff --git a/tamer/src/obj/xmlo/mod.rs b/tamer/src/obj/xmlo/mod.rs index ec489137..27a84805 100644 --- a/tamer/src/obj/xmlo/mod.rs +++ b/tamer/src/obj/xmlo/mod.rs @@ -79,7 +79,7 @@ mod error; mod ir; mod reader; -pub use asg_builder::{AsgBuilder, AsgBuilderState}; +pub use asg_builder::{AsgBuilder, AsgBuilderError, AsgBuilderState}; pub use error::XmloError; pub use ir::{Dim, SymAttrs, SymDtype, SymType}; pub use reader::{XmloEvent, XmloReader};