tamer: tameld (TameldError): Error sum type

This aggregates all non-panic errors that can occur during link time, making
`Box<dyn Error>` 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
main
Mike Gerwitz 2022-04-11 15:15:04 -04:00
parent a1a4ad3e8e
commit f07c0e75be
3 changed files with 130 additions and 43 deletions

View File

@ -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<dyn Error>> {
pub fn main() -> Result<(), TameldError> {
let args: Vec<String> = env::args().collect();
let program = &args[0];
let opts = get_opts();

View File

@ -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<IdentObject>;
type LinkerAsgBuilderState = AsgBuilderState<FxBuildHasher>;
pub fn xmle(package_path: &str, output: &str) -> Result<(), Box<dyn Error>> {
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<dyn Error>> {
let sorted = match sort(&depgraph, &roots, Sections::new()) {
Ok(sections) => sections,
Err(SortError::Cycles(cycles)) => {
let msg: Vec<String> = cycles
.into_iter()
.map(|cycle| {
let mut path = cycle
.into_iter()
.map(|obj| {
depgraph.get(obj).unwrap().name().lookup_str()
})
.collect::<Vec<&str>>();
return Err(TameldError::CycleError(
cycles
.into_iter()
.map(|cycle| {
let mut path: Vec<SymbolId> = 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<dyn Error>> {
Ok(())
}
pub fn graphml(package_path: &str, output: &str) -> Result<(), Box<dyn Error>> {
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<Path>, S: Escaper>(
depgraph: &mut LinkerAsg,
escaper: &S,
state: LinkerAsgBuilderState,
) -> Result<LinkerAsgBuilderState, Box<dyn Error>> {
) -> Result<LinkerAsgBuilderState, TameldError> {
let PathFile(path, file, ctx): PathFile<BufReader<fs::File>> =
match fs.open(path_str)? {
VisitOnceFile::FirstVisit(file) => file,
@ -195,7 +194,7 @@ fn load_xmlo<'a, P: AsRef<Path>, 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<dyn Error>> {
) -> 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<XirToken, XirfError>),
XmloParseError(ParseError<XirfToken, XmloError>),
AsgBuilderError(AsgBuilderError),
XirWriterError(XirWriterError),
CycleError(Vec<Vec<SymbolId>>),
}
impl From<io::Error> for TameldError {
fn from(e: io::Error) -> Self {
Self::Io(e)
}
}
impl From<SortError> for TameldError {
fn from(e: SortError) -> Self {
Self::SortError(e)
}
}
impl From<XirError> for TameldError {
fn from(e: XirError) -> Self {
Self::XirError(e)
}
}
impl From<ParseError<XirfToken, XmloError>> for TameldError {
fn from(e: ParseError<XirfToken, XmloError>) -> Self {
Self::XmloParseError(e)
}
}
impl From<ParseError<XirToken, XirfError>> for TameldError {
fn from(e: ParseError<XirToken, XirfError>) -> Self {
Self::XirfParseError(e)
}
}
impl From<AsgBuilderError> for TameldError {
fn from(e: AsgBuilderError) -> Self {
Self::AsgBuilderError(e)
}
}
impl From<XirWriterError> 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::<Vec<&str>>()
.join(" -> ")
)?;
}
Ok(())
}
}
}
}

View File

@ -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};