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-10935main
parent
a1a4ad3e8e
commit
f07c0e75be
|
@ -29,7 +29,7 @@ extern crate tamer;
|
||||||
use getopts::{Fail, Options};
|
use getopts::{Fail, Options};
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use tamer::ld::poc;
|
use tamer::ld::poc::{self, TameldError};
|
||||||
|
|
||||||
/// Types of commands
|
/// Types of commands
|
||||||
enum Command {
|
enum Command {
|
||||||
|
@ -50,7 +50,7 @@ enum Emit {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Entrypoint for the linker
|
/// Entrypoint for the linker
|
||||||
pub fn main() -> Result<(), Box<dyn Error>> {
|
pub fn main() -> Result<(), TameldError> {
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
let program = &args[0];
|
let program = &args[0];
|
||||||
let opts = get_opts();
|
let opts = get_opts();
|
||||||
|
|
|
@ -25,36 +25,37 @@ use super::xmle::{
|
||||||
xir::lower_iter,
|
xir::lower_iter,
|
||||||
XmleSections,
|
XmleSections,
|
||||||
};
|
};
|
||||||
use crate::sym::SymbolId;
|
|
||||||
use crate::sym::{GlobalSymbolIntern, GlobalSymbolResolve};
|
|
||||||
use crate::xir::writer::XmlWriter;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
asg::{Asg, DefaultAsg, IdentObject},
|
asg::{Asg, DefaultAsg, IdentObject},
|
||||||
xir::DefaultEscaper,
|
|
||||||
};
|
|
||||||
use crate::{
|
|
||||||
fs::{
|
fs::{
|
||||||
Filesystem, FsCanonicalizer, PathFile, VisitOnceFile,
|
Filesystem, FsCanonicalizer, PathFile, VisitOnceFile,
|
||||||
VisitOnceFilesystem,
|
VisitOnceFilesystem,
|
||||||
},
|
},
|
||||||
ld::xmle::Sections,
|
ld::xmle::Sections,
|
||||||
};
|
obj::xmlo::{
|
||||||
use crate::{
|
AsgBuilder, AsgBuilderError, AsgBuilderState, XmloError, XmloReader,
|
||||||
obj::xmlo::{AsgBuilder, AsgBuilderState, XmloReader},
|
},
|
||||||
xir::Escaper,
|
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 fxhash::FxBuildHasher;
|
||||||
use petgraph_graphml::GraphMl;
|
use petgraph_graphml::GraphMl;
|
||||||
use std::error::Error;
|
use std::{
|
||||||
use std::fs;
|
fmt::Display,
|
||||||
use std::io::Write;
|
fs,
|
||||||
use std::io::{BufReader, BufWriter};
|
io::{self, BufReader, BufWriter, Write},
|
||||||
use std::path::{Path, PathBuf};
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
type LinkerAsg = DefaultAsg<IdentObject>;
|
type LinkerAsg = DefaultAsg<IdentObject>;
|
||||||
type LinkerAsgBuilderState = AsgBuilderState<FxBuildHasher>;
|
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 fs = VisitOnceFilesystem::new();
|
||||||
let mut depgraph = LinkerAsg::with_capacity(65536, 65536);
|
let mut depgraph = LinkerAsg::with_capacity(65536, 65536);
|
||||||
let escaper = DefaultEscaper::default();
|
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()) {
|
let sorted = match sort(&depgraph, &roots, Sections::new()) {
|
||||||
Ok(sections) => sections,
|
Ok(sections) => sections,
|
||||||
Err(SortError::Cycles(cycles)) => {
|
Err(SortError::Cycles(cycles)) => {
|
||||||
let msg: Vec<String> = cycles
|
return Err(TameldError::CycleError(
|
||||||
.into_iter()
|
cycles
|
||||||
.map(|cycle| {
|
.into_iter()
|
||||||
let mut path = cycle
|
.map(|cycle| {
|
||||||
.into_iter()
|
let mut path: Vec<SymbolId> = cycle
|
||||||
.map(|obj| {
|
.into_iter()
|
||||||
depgraph.get(obj).unwrap().name().lookup_str()
|
.map(|obj| depgraph.get(obj).unwrap().name())
|
||||||
})
|
.collect();
|
||||||
.collect::<Vec<&str>>();
|
|
||||||
|
|
||||||
path.reverse();
|
path.reverse();
|
||||||
path.push(path[0].clone());
|
path.push(path[0].clone());
|
||||||
format!("cycle: {}", path.join(" -> "))
|
path
|
||||||
})
|
})
|
||||||
.collect();
|
.collect(),
|
||||||
|
))
|
||||||
return Err(msg.join("\n").into());
|
|
||||||
}
|
}
|
||||||
Err(e) => return Err(e.into()),
|
Err(e) => return Err(e.into()),
|
||||||
};
|
};
|
||||||
|
@ -116,7 +115,7 @@ pub fn xmle(package_path: &str, output: &str) -> Result<(), Box<dyn Error>> {
|
||||||
Ok(())
|
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 fs = VisitOnceFilesystem::new();
|
||||||
let mut depgraph = LinkerAsg::with_capacity(65536, 65536);
|
let mut depgraph = LinkerAsg::with_capacity(65536, 65536);
|
||||||
let escaper = DefaultEscaper::default();
|
let escaper = DefaultEscaper::default();
|
||||||
|
@ -175,7 +174,7 @@ fn load_xmlo<'a, P: AsRef<Path>, S: Escaper>(
|
||||||
depgraph: &mut LinkerAsg,
|
depgraph: &mut LinkerAsg,
|
||||||
escaper: &S,
|
escaper: &S,
|
||||||
state: LinkerAsgBuilderState,
|
state: LinkerAsgBuilderState,
|
||||||
) -> Result<LinkerAsgBuilderState, Box<dyn Error>> {
|
) -> Result<LinkerAsgBuilderState, TameldError> {
|
||||||
let PathFile(path, file, ctx): PathFile<BufReader<fs::File>> =
|
let PathFile(path, file, ctx): PathFile<BufReader<fs::File>> =
|
||||||
match fs.open(path_str)? {
|
match fs.open(path_str)? {
|
||||||
VisitOnceFile::FirstVisit(file) => file,
|
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::iter::into_iter_while_ok;
|
||||||
use crate::parse::{ParseState, Parsed};
|
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
|
// TODO: This entire block is a WIP and will be incrementally
|
||||||
// abstracted away.
|
// abstracted away.
|
||||||
|
@ -247,7 +246,7 @@ fn output_xmle<'a, X: XmleSections<'a>, S: Escaper>(
|
||||||
relroot: SymbolId,
|
relroot: SymbolId,
|
||||||
output: &str,
|
output: &str,
|
||||||
escaper: &S,
|
escaper: &S,
|
||||||
) -> Result<(), Box<dyn Error>> {
|
) -> Result<(), TameldError> {
|
||||||
let file = fs::File::create(output)?;
|
let file = fs::File::create(output)?;
|
||||||
let mut buf = BufWriter::new(file);
|
let mut buf = BufWriter::new(file);
|
||||||
|
|
||||||
|
@ -261,8 +260,96 @@ fn output_xmle<'a, X: XmleSections<'a>, S: Escaper>(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
// TODO: This, like everything else here, needs a home.
|
||||||
mod test {
|
// TODO: Better encapsulation for `*ParseError` types.
|
||||||
#[test]
|
/// Linker (`tameld`) error.
|
||||||
fn placeholder() {}
|
///
|
||||||
|
/// 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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,7 @@ mod error;
|
||||||
mod ir;
|
mod ir;
|
||||||
mod reader;
|
mod reader;
|
||||||
|
|
||||||
pub use asg_builder::{AsgBuilder, AsgBuilderState};
|
pub use asg_builder::{AsgBuilder, AsgBuilderError, AsgBuilderState};
|
||||||
pub use error::XmloError;
|
pub use error::XmloError;
|
||||||
pub use ir::{Dim, SymAttrs, SymDtype, SymType};
|
pub use ir::{Dim, SymAttrs, SymDtype, SymType};
|
||||||
pub use reader::{XmloEvent, XmloReader};
|
pub use reader::{XmloEvent, XmloReader};
|
||||||
|
|
Loading…
Reference in New Issue