tamer: ir::asg::SortableAsg: Move into ld::xmle::lower

This has always been a lowering operation, but it was not phrased in terms
of it, which made the process a bit more confusing to understand.

The implementation hasn't changed, but this is an incremental refactoring
and so exposes BaseAsg and its `graph` field temporarily.

DEV-10859
main
Mike Gerwitz 2021-10-12 09:42:09 -04:00
parent 81ec65742a
commit df328da71f
8 changed files with 1024 additions and 998 deletions

View File

@ -30,9 +30,7 @@ use test::Bencher;
mod base {
use super::*;
use tamer::ir::asg::{
Asg, DataType, DefaultAsg, IdentKind, IdentObject, SortableAsg, Source,
};
use tamer::ir::asg::{Asg, DefaultAsg, IdentKind, IdentObject, Source};
use tamer::sym::{GlobalSymbolIntern, SymbolId};
type Sut = DefaultAsg<IdentObject>;
@ -307,71 +305,6 @@ mod base {
.for_each(drop);
});
}
#[bench]
fn sort_1_with_1_000_existing_supernode(bench: &mut Bencher) {
let mut sut = Sut::new();
let xs = interned_n(1_000);
let orefs = xs
.iter()
.map(|sym| {
sut.declare(
*sym,
IdentKind::Rate(DataType::Integer),
Source::default(),
)
.unwrap()
})
.collect::<Vec<_>>();
let root = orefs[0];
// All edges from a single node.
orefs.iter().skip(1).for_each(|to| {
sut.add_dep(root, *to);
});
bench.iter(|| {
drop(sut.sort(&[root]));
});
}
#[bench]
fn sort_1_with_1_000_existing_one_edge_per_node_one_path(
bench: &mut Bencher,
) {
let mut sut = Sut::new();
let xs = interned_n(1_000);
let orefs = xs
.iter()
.map(|sym| {
sut.declare(
*sym,
IdentKind::Rate(DataType::Integer),
Source::default(),
)
.unwrap()
})
.collect::<Vec<_>>();
// Note that there's no `cycle` call on the iterator, like the
// above tests, to make sure we don't create a cycle on the
// graph.
orefs
.iter()
.zip(orefs.iter().skip(1))
.for_each(|(from, to)| {
sut.add_dep(*from, *to);
});
let root = orefs[0];
bench.iter(|| {
drop(sut.sort(&[root]));
});
}
}
mod object {

View File

@ -19,18 +19,13 @@
//! Base concrete [`Asg`] implementation.
use super::graph::{
Asg, AsgEdge, AsgResult, IndexType, Node, ObjectRef, SortableAsg,
SortableAsgError, SortableAsgResult,
};
use super::graph::{Asg, AsgEdge, AsgResult, IndexType, Node, ObjectRef};
use super::ident::IdentKind;
use super::object::{
FragmentText, IdentObjectData, IdentObjectState, Source, TransitionResult,
};
use crate::ld::xmle::Sections;
use crate::sym::{GlobalSymbolResolve, SymbolId};
use crate::sym::SymbolId;
use petgraph::graph::{DiGraph, Graph, NodeIndex};
use petgraph::visit::DfsPostOrder;
/// Concrete ASG.
///
@ -46,8 +41,9 @@ pub struct BaseAsg<O, Ix>
where
Ix: IndexType,
{
// TODO: private; see `ld::xmle::lower`.
/// Directed graph on which objects are stored.
graph: DiGraph<Node<O>, AsgEdge, Ix>,
pub graph: DiGraph<Node<O>, AsgEdge, Ix>,
/// Map of [`SymbolId`][crate::sym::SymbolId] to node indexes.
///
@ -274,126 +270,14 @@ where
}
}
impl<O, Ix> SortableAsg<O, Ix> for BaseAsg<O, Ix>
where
Ix: IndexType,
O: IdentObjectData + IdentObjectState<O>,
{
fn sort<'i>(
&'i self,
roots: &[ObjectRef<Ix>],
) -> SortableAsgResult<Sections<'i, O>, Ix> {
let mut deps = Sections::new();
check_cycles(self)?;
// This is technically a topological sort, but functions have cycles.
let mut dfs = DfsPostOrder::empty(&self.graph);
for index in roots {
dfs.stack.push((*index).into());
}
while let Some(index) = dfs.next(&self.graph) {
let ident = self.get(index).expect("missing node").resolved()?;
match ident.kind() {
Some(kind) => match kind {
IdentKind::Meta
| IdentKind::Worksheet
| IdentKind::Param(_, _)
| IdentKind::Type(_)
| IdentKind::Func(_, _)
| IdentKind::Const(_, _) => deps.st.push_body(ident),
IdentKind::MapHead
| IdentKind::Map
| IdentKind::MapTail => deps.map.push_body(ident),
IdentKind::RetMapHead
| IdentKind::RetMap
| IdentKind::RetMapTail => deps.retmap.push_body(ident),
_ => deps.rater.push_body(ident),
},
None => {
return Err(SortableAsgError::MissingObjectKind(
ident
.name()
.map(|name| format!("{}", name.lookup_str()))
.unwrap_or("<unknown>".into())
.into(),
))
}
}
}
Ok(deps)
}
}
/// Check graph for cycles
///
/// We want to catch any cycles before we start using the graph.
/// Unfortunately, we need to allow cycles for our [`IdentKind::Func`]
/// so we cannot use the typical algorithms in a straightforward manner.
///
/// We loop through all SCCs and check that they are not all functions. If
/// they are, we ignore the cycle, otherwise we will return an error.
fn check_cycles<O, Ix>(asg: &BaseAsg<O, Ix>) -> SortableAsgResult<(), Ix>
where
Ix: IndexType,
O: IdentObjectData + IdentObjectState<O>,
{
// While `tarjan_scc` does do a topological sort, it does not suit our
// needs because we need to filter out some allowed cycles. It would
// still be possible to use this, but we also need to only check nodes
// that are attached to our "roots". We are doing our own sort and as of
// the initial writing, this does not have a significant performance
// impact.
let sccs = petgraph::algo::tarjan_scc(&asg.graph);
let cycles: Vec<_> = sccs
.into_iter()
.filter_map(|scc| {
// For single-node SCCs, we just need to make sure they are
// not neighbors with themselves.
if scc.len() == 1
&& !asg.graph.neighbors(scc[0]).any(|nx| nx == scc[0])
{
return None;
}
let is_all_funcs = scc.iter().all(|nx| {
let ident = Asg::get(asg, *nx).expect("missing node");
matches!(ident.kind(), Some(IdentKind::Func(_, _)))
});
if is_all_funcs {
None
} else {
let cycles =
scc.iter().map(|nx| ObjectRef::from(*nx)).collect();
Some(cycles)
}
})
.collect();
if cycles.is_empty() {
Ok(())
} else {
Err(SortableAsgError::Cycles(cycles))
}
}
#[cfg(test)]
mod test {
use super::super::graph::AsgError;
use super::*;
use crate::ir::asg::{
DataType, Dim, IdentObject, TransitionError, TransitionResult,
UnresolvedError,
Dim, IdentObject, TransitionError, TransitionResult, UnresolvedError,
};
use crate::ir::legacyir::SymDtype;
use crate::sym::GlobalSymbolIntern;
use crate::sym::SymbolId;
use crate::sym::{GlobalSymbolIntern, SymbolId};
use std::cell::RefCell;
#[derive(Debug, Default, PartialEq)]
@ -855,707 +739,4 @@ mod test {
Ok(())
}
macro_rules! assert_section_sym {
( $iterable:expr, $s:ident ) => {{
let mut pos = 0;
for obj in $iterable.iter() {
let sym = obj.name().expect("missing object");
assert_eq!($s.get(pos).map(|s| *s), Some(sym));
pos = pos + 1;
}
assert_eq!(pos, $s.len());
};};
}
macro_rules! add_syms {
($sut:ident, $base:expr, {$($dest:ident <- $name:ident: $kind:expr,)*}) => {
$(
let sym = stringify!($name).intern();
$sut.declare(sym, $kind, Source::default()).unwrap();
let (_, _) = $sut.add_dep_lookup($base, sym);
$dest.push(sym);
)*
};
}
#[test]
fn graph_sort() -> SortableAsgResult<(), u16> {
let mut sut = Sut::new();
let mut st = vec![];
let mut map = vec![];
let mut retmap = vec![];
let base = "sym1".intern();
let base_node = sut
.declare(base, IdentKind::Map, Source::default())
.unwrap();
add_syms!(sut, base, {
st <- meta1: IdentKind::Meta,
st <- work1: IdentKind::Worksheet,
st <- const1: IdentKind::Const(Dim::from_u8(0), DataType::Float),
map <- map1: IdentKind::MapHead,
map <- map2: IdentKind::Map,
map <- map3: IdentKind::MapTail,
retmap <- retmap1: IdentKind::RetMapHead,
retmap <- retmap2: IdentKind::RetMap,
retmap <- retmap3: IdentKind::RetMapTail,
retmap <- retmap4: IdentKind::RetMapTail,
retmap <- retmap5: IdentKind::RetMap,
map <- map4: IdentKind::MapHead,
map <- map5: IdentKind::Map,
st <- meta2: IdentKind::Meta,
st <- work2: IdentKind::Worksheet,
map <- map6: IdentKind::MapTail,
retmap <- retmap6: IdentKind::RetMapHead,
st <- const2: IdentKind::Const(Dim::from_u8(2), DataType::Float),
});
map.push(base);
let sections = sut.sort(&vec![base_node])?;
assert_section_sym!(sections.st, st);
assert_section_sym!(sections.map, map);
assert_section_sym!(sections.retmap, retmap);
Ok(())
}
#[test]
fn graph_sort_missing_node() -> SortableAsgResult<(), u16> {
let mut sut = Sut::new();
let sym = "sym".intern();
let dep = "dep".intern();
let sym_node = sut
.declare(
sym,
IdentKind::Tpl,
Source {
virtual_: true,
..Default::default()
},
)
.unwrap();
sut.set_fragment(sym_node, FragmentText::from("foo"))
.unwrap();
let (_, _) = sut.add_dep_lookup(sym, dep);
match sut.sort(&vec![sym_node]) {
Ok(_) => panic!("Unexpected success - dependency is not in graph"),
Err(SortableAsgError::MissingObjectKind(_)) => (),
_ => {
panic!("Incorrect error result when dependency is not in graph")
}
}
Ok(())
}
#[test]
fn graph_sort_no_roots() -> SortableAsgResult<(), u16> {
let mut sut = Sut::new();
let sym = "sym".intern();
let dep = "dep".intern();
let (_, _) = sut.add_dep_lookup(sym, dep);
let sections = sut.sort(&vec![])?;
assert_eq!(Sections::new(), sections);
Ok(())
}
#[test]
fn graph_sort_simple_cycle() -> SortableAsgResult<(), u16> {
let mut sut = Sut::new();
let sym = "sym".intern();
let dep = "dep".intern();
let sym_node = sut
.declare(
sym,
IdentKind::Tpl,
Source {
virtual_: true,
..Default::default()
},
)
.unwrap();
let dep_node = sut
.declare(
dep,
IdentKind::Tpl,
Source {
virtual_: true,
..Default::default()
},
)
.unwrap();
sut.set_fragment(sym_node, FragmentText::from("foo"))
.unwrap();
sut.set_fragment(dep_node, FragmentText::from("bar"))
.unwrap();
let (_, _) = sut.add_dep_lookup(sym, dep);
let (_, _) = sut.add_dep_lookup(dep, sym);
let result = sut.sort(&vec![sym_node]);
let expected: Vec<Vec<ObjectRef<u16>>> =
vec![vec![dep_node.into(), sym_node.into()]];
match result {
Ok(_) => panic!("sort did not detect cycle"),
Err(SortableAsgError::Cycles(scc)) => assert_eq!(expected, scc),
Err(e) => panic!("unexpected error: {}", e),
}
Ok(())
}
#[test]
fn graph_sort_two_simple_cycles() -> SortableAsgResult<(), u16> {
let mut sut = Sut::new();
let sym = "sym".intern();
let sym2 = "sym2".intern();
let dep = "dep".intern();
let dep2 = "dep2".intern();
let sym_node = sut
.declare(
sym,
IdentKind::Tpl,
Source {
virtual_: true,
..Default::default()
},
)
.unwrap();
let sym2_node = sut
.declare(
sym2,
IdentKind::Tpl,
Source {
virtual_: true,
..Default::default()
},
)
.unwrap();
let dep_node = sut
.declare(
dep,
IdentKind::Tpl,
Source {
virtual_: true,
..Default::default()
},
)
.unwrap();
let dep2_node = sut
.declare(
dep2,
IdentKind::Tpl,
Source {
virtual_: true,
..Default::default()
},
)
.unwrap();
sut.set_fragment(sym_node, FragmentText::from("foo"))
.unwrap();
sut.set_fragment(sym2_node, FragmentText::from("bar"))
.unwrap();
sut.set_fragment(dep_node, FragmentText::from("baz"))
.unwrap();
sut.set_fragment(dep2_node, FragmentText::from("huh"))
.unwrap();
let (_, _) = sut.add_dep_lookup(sym, dep);
let (_, _) = sut.add_dep_lookup(dep, sym);
let (_, _) = sut.add_dep_lookup(sym2, dep2);
let (_, _) = sut.add_dep_lookup(dep2, sym2);
let result = sut.sort(&vec![sym_node]);
let expected: Vec<Vec<ObjectRef<u16>>> = vec![
vec![dep_node.into(), sym_node.into()],
vec![dep2_node.into(), sym2_node.into()],
];
match result {
Ok(_) => panic!("sort did not detect cycle"),
Err(SortableAsgError::Cycles(scc)) => assert_eq!(expected, scc),
Err(e) => panic!("unexpected error: {}", e),
}
Ok(())
}
#[test]
fn graph_sort_no_cycle_with_edge_to_same_node() -> SortableAsgResult<(), u16>
{
let mut sut = Sut::new();
let sym = "sym".intern();
let dep = "dep".intern();
let sym_node = sut
.declare(
sym,
IdentKind::Tpl,
Source {
virtual_: true,
..Default::default()
},
)
.unwrap();
let dep_node = sut
.declare(
dep,
IdentKind::Tpl,
Source {
virtual_: true,
..Default::default()
},
)
.unwrap();
sut.set_fragment(sym_node, FragmentText::from("foo"))
.unwrap();
sut.set_fragment(dep_node, FragmentText::from("bar"))
.unwrap();
let (_, _) = sut.add_dep_lookup(sym, dep);
let (_, _) = sut.add_dep_lookup(sym, dep);
let result = sut.sort(&vec![sym_node]);
match result {
Ok(_) => (),
Err(e) => panic!("unexpected error: {}", e),
}
Ok(())
}
#[test]
fn graph_sort_cycle_with_a_few_steps() -> SortableAsgResult<(), u16> {
let mut sut = Sut::new();
let sym1 = "sym1".intern();
let sym2 = "sym2".intern();
let sym3 = "sym3".intern();
let sym1_node = sut
.declare(
sym1,
IdentKind::Tpl,
Source {
virtual_: true,
..Default::default()
},
)
.unwrap();
let sym2_node = sut
.declare(
sym2,
IdentKind::Tpl,
Source {
virtual_: true,
..Default::default()
},
)
.unwrap();
let sym3_node = sut
.declare(
sym3,
IdentKind::Tpl,
Source {
virtual_: true,
..Default::default()
},
)
.unwrap();
sut.set_fragment(sym1_node, FragmentText::from("foo"))
.unwrap();
sut.set_fragment(sym2_node, FragmentText::from("bar"))
.unwrap();
sut.set_fragment(sym3_node, FragmentText::from("baz"))
.unwrap();
let (_, _) = sut.add_dep_lookup(sym1, sym2);
let (_, _) = sut.add_dep_lookup(sym2, sym3);
let (_, _) = sut.add_dep_lookup(sym3, sym1);
let result = sut.sort(&vec![sym1_node]);
let expected: Vec<Vec<ObjectRef<u16>>> =
vec![vec![sym3_node.into(), sym2_node.into(), sym1_node.into()]];
match result {
Ok(_) => panic!("sort did not detect cycle"),
Err(SortableAsgError::Cycles(scc)) => assert_eq!(expected, scc),
Err(e) => panic!("unexpected error: {}", e),
}
Ok(())
}
#[test]
fn graph_sort_cyclic_function_with_non_function_with_a_few_steps(
) -> SortableAsgResult<(), u16> {
let mut sut = Sut::new();
let sym1 = "sym1".intern();
let sym2 = "sym2".intern();
let sym3 = "sym3".intern();
let sym1_node = sut
.declare(
sym1,
IdentKind::Tpl,
Source {
virtual_: true,
..Default::default()
},
)
.unwrap();
let sym2_node = sut
.declare(
sym2,
IdentKind::Tpl,
Source {
virtual_: true,
..Default::default()
},
)
.unwrap();
let sym3_node = sut
.declare(
sym3,
IdentKind::Func(Dim::default(), SymDtype::Empty),
Source {
virtual_: true,
..Default::default()
},
)
.unwrap();
sut.set_fragment(sym1_node, FragmentText::from("foo"))
.unwrap();
sut.set_fragment(sym2_node, FragmentText::from("bar"))
.unwrap();
sut.set_fragment(sym3_node, FragmentText::from("baz"))
.unwrap();
let (_, _) = sut.add_dep_lookup(sym1, sym2);
let (_, _) = sut.add_dep_lookup(sym2, sym3);
let (_, _) = sut.add_dep_lookup(sym3, sym1);
let result = sut.sort(&vec![sym1_node]);
let expected: Vec<Vec<ObjectRef<u16>>> =
vec![vec![sym3_node.into(), sym2_node.into(), sym1_node.into()]];
match result {
Ok(_) => panic!("sort did not detect cycle"),
Err(SortableAsgError::Cycles(scc)) => assert_eq!(expected, scc),
Err(e) => panic!("unexpected error: {}", e),
}
Ok(())
}
#[test]
fn graph_sort_cyclic_bookended_by_functions() -> SortableAsgResult<(), u16>
{
let mut sut = Sut::new();
let sym1 = "sym1".intern();
let sym2 = "sym2".intern();
let sym3 = "sym3".intern();
let sym1_node = sut
.declare(
sym1,
IdentKind::Func(Dim::default(), SymDtype::Empty),
Source {
virtual_: true,
..Default::default()
},
)
.unwrap();
let sym2_node = sut
.declare(
sym2,
IdentKind::Tpl,
Source {
virtual_: true,
..Default::default()
},
)
.unwrap();
let sym3_node = sut
.declare(
sym3,
IdentKind::Func(Dim::default(), SymDtype::Empty),
Source {
virtual_: true,
..Default::default()
},
)
.unwrap();
sut.set_fragment(sym1_node, FragmentText::from("foo"))
.unwrap();
sut.set_fragment(sym2_node, FragmentText::from("bar"))
.unwrap();
sut.set_fragment(sym3_node, FragmentText::from("baz"))
.unwrap();
let (_, _) = sut.add_dep_lookup(sym1, sym2);
let (_, _) = sut.add_dep_lookup(sym2, sym3);
let (_, _) = sut.add_dep_lookup(sym3, sym1);
let result = sut.sort(&vec![sym1_node]);
let expected: Vec<Vec<ObjectRef<u16>>> =
vec![vec![sym3_node.into(), sym2_node.into(), sym1_node.into()]];
match result {
Ok(_) => panic!("sort did not detect cycle"),
Err(SortableAsgError::Cycles(scc)) => assert_eq!(expected, scc),
Err(e) => panic!("unexpected error: {}", e),
}
Ok(())
}
#[test]
fn graph_sort_cyclic_function_ignored() -> SortableAsgResult<(), u16> {
let mut sut = Sut::new();
let sym = "sym".intern();
let dep = "dep".intern();
let sym_node = sut
.declare(
sym,
IdentKind::Func(Dim::default(), SymDtype::Empty),
Source {
virtual_: true,
..Default::default()
},
)
.unwrap();
let dep_node = sut
.declare(
dep,
IdentKind::Func(Dim::default(), SymDtype::Empty),
Source {
virtual_: true,
..Default::default()
},
)
.unwrap();
sut.set_fragment(sym_node, FragmentText::from("foo"))
.unwrap();
sut.set_fragment(dep_node, FragmentText::from("bar"))
.unwrap();
let (_, _) = sut.add_dep_lookup(sym, dep);
let (_, _) = sut.add_dep_lookup(dep, sym);
let result = sut.sort(&vec![sym_node]);
match result {
Ok(_) => (),
Err(e) => panic!("unexpected error: {}", e),
}
Ok(())
}
#[test]
fn graph_sort_cyclic_function_is_bookended() -> SortableAsgResult<(), u16> {
let mut sut = Sut::new();
let sym1 = "sym1".intern();
let sym2 = "sym2".intern();
let sym3 = "sym3".intern();
let sym1_node = sut
.declare(
sym1,
IdentKind::Tpl,
Source {
virtual_: true,
..Default::default()
},
)
.unwrap();
let sym2_node = sut
.declare(
sym2,
IdentKind::Func(Dim::default(), SymDtype::Empty),
Source {
virtual_: true,
..Default::default()
},
)
.unwrap();
let sym3_node = sut
.declare(
sym3,
IdentKind::Tpl,
Source {
virtual_: true,
..Default::default()
},
)
.unwrap();
sut.set_fragment(sym1_node, FragmentText::from("foo"))
.unwrap();
sut.set_fragment(sym2_node, FragmentText::from("bar"))
.unwrap();
sut.set_fragment(sym3_node, FragmentText::from("baz"))
.unwrap();
let (_, _) = sut.add_dep_lookup(sym1, sym2);
let (_, _) = sut.add_dep_lookup(sym2, sym3);
let (_, _) = sut.add_dep_lookup(sym3, sym1);
let result = sut.sort(&vec![sym1_node]);
let expected: Vec<Vec<ObjectRef<u16>>> =
vec![vec![sym3_node.into(), sym2_node.into(), sym1_node.into()]];
match result {
Ok(_) => panic!("sort did not detect cycle"),
Err(SortableAsgError::Cycles(scc)) => assert_eq!(expected, scc),
Err(e) => panic!("unexpected error: {}", e),
}
Ok(())
}
#[test]
fn graph_sort_ignore_non_linked() -> SortableAsgResult<(), u16> {
let mut sut = Sut::new();
let sym = "sym".intern();
let dep = "dep".intern();
let ignored = "ignored".intern();
let sym_node = sut
.declare(
sym,
IdentKind::Tpl,
Source {
virtual_: true,
..Default::default()
},
)
.unwrap();
let dep_node = sut
.declare(
dep,
IdentKind::Tpl,
Source {
virtual_: true,
..Default::default()
},
)
.unwrap();
let ignored_node = sut
.declare(
ignored,
IdentKind::Tpl,
Source {
virtual_: true,
..Default::default()
},
)
.unwrap();
sut.set_fragment(sym_node, FragmentText::from("foo"))
.unwrap();
sut.set_fragment(dep_node, FragmentText::from("bar"))
.unwrap();
sut.set_fragment(ignored_node, FragmentText::from("baz"))
.unwrap();
let (_, _) = sut.add_dep_lookup(sym, dep);
let (_, _) = sut.add_dep_lookup(ignored, sym);
let result = sut.sort(&vec![sym_node]);
match result {
Ok(_) => (),
Err(e) => panic!("unexpected error: {}", e),
}
Ok(())
}
/// A graph containing unresolved objects cannot be sorted.
#[test]
fn graph_sort_fail_unresolved() -> SortableAsgResult<(), u16> {
let mut sut = Sut::new();
let sym = "unresolved".intern();
let node = sut
.declare(sym, IdentKind::Meta, Default::default())
.unwrap();
let ident = sut.get(node).unwrap();
let expected = UnresolvedError::Missing { name: sym };
// Cause resolved() to fail.
ident.fail_resolved.replace(Some(expected.clone()));
let result = sut
.sort(&vec![node])
.expect_err("expected sort failure due to unresolved identifier");
match result {
SortableAsgError::UnresolvedObject(err) => {
assert_eq!(expected, err);
}
_ => panic!("expected SortableAsgError::Unresolved: {:?}", result),
}
Ok(())
}
}

View File

@ -20,11 +20,7 @@
//! Abstract graph as the basis for concrete ASGs.
use super::ident::IdentKind;
use super::object::{
FragmentText, IdentObjectData, IdentObjectState, Source, TransitionError,
UnresolvedError,
};
use crate::ld::xmle::Sections;
use super::object::{FragmentText, IdentObjectState, Source, TransitionError};
use crate::sym::SymbolId;
use petgraph::graph::NodeIndex;
use std::fmt::Debug;
@ -178,38 +174,12 @@ where
) -> (ObjectRef<Ix>, ObjectRef<Ix>);
}
/// Sort a graph into different [`Sections`]
///
/// Allow a graph to be partitioned into different [`Sections`] that can be
/// used as an `Intermediate Representation`.
pub trait SortableAsg<O, Ix>
where
O: IdentObjectData,
Ix: IndexType,
{
/// Sort graph into [`Sections`].
///
/// Sorting will fail if the graph contains unresolved objects,
/// or identifiers whose kind cannot be determined
/// (see [`UnresolvedError`]).
fn sort<'i>(
&'i self,
roots: &[ObjectRef<Ix>],
) -> SortableAsgResult<Sections<'i, O>, Ix>;
}
/// A [`Result`] with a hard-coded [`AsgError`] error type.
///
/// This is the result of every [`Asg`] operation that could potentially
/// fail in error.
pub type AsgResult<T> = Result<T, AsgError>;
/// A [`Result`] with a hard-coded [`SortableAsgError`] error type.
///
/// This is the result of every [`SortableAsg`] operation that could
/// potentially fail in error.
pub type SortableAsgResult<T, Ix> = Result<T, SortableAsgError<Ix>>;
/// Reference to an [object][super::object] stored within the [`Asg`].
///
/// IdentObject references are integer offsets,
@ -285,66 +255,6 @@ impl From<TransitionError> for AsgError {
}
}
/// Error during graph sorting.
///
/// These errors reflect barriers to meaningfully understanding the
/// properties of the data in the graph with respect to sorting.
/// It does not represent bad underlying data that does not affect the
/// sorting process.
#[derive(Debug, PartialEq)]
pub enum SortableAsgError<Ix: IndexType> {
/// An unresolved object was encountered during sorting.
///
/// An unresolved object means that the graph has an incomplete picture
/// of the program,
/// and so sorting cannot be reliably performed.
/// Since all objects are supposed to be resolved prior to sorting,
/// this represents either a problem with the program being compiled
/// or a bug in the compiler itself.
UnresolvedObject(UnresolvedError),
/// The kind of an object encountered during sorting could not be
/// determined.
///
/// Sorting uses the object kind to place objects into their appropriate
/// sections.
/// It should never be the case that a resolved object has no kind,
/// so this likely represents a compiler bug.
MissingObjectKind(String),
/// The graph has a cyclic dependency.
Cycles(Vec<Vec<ObjectRef<Ix>>>),
}
impl<Ix: IndexType> From<UnresolvedError> for SortableAsgError<Ix> {
fn from(err: UnresolvedError) -> Self {
Self::UnresolvedObject(err)
}
}
impl<Ix: IndexType> std::fmt::Display for SortableAsgError<Ix> {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::UnresolvedObject(err) => std::fmt::Display::fmt(err, fmt),
Self::MissingObjectKind(name) => write!(
fmt,
"internal error: missing object kind for object `{}` (this may be a compiler bug!)",
name,
),
Self::Cycles(_) => write!(fmt, "cyclic dependencies"),
}
}
}
impl<Ix: IndexType> std::error::Error for SortableAsgError<Ix> {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::UnresolvedObject(err) => Some(err),
_ => None,
}
}
}
#[cfg(test)]
mod test {
use super::*;

View File

@ -195,10 +195,10 @@ mod graph;
mod ident;
mod object;
pub use graph::{
Asg, AsgError, AsgResult, IndexType, ObjectRef, SortableAsg,
SortableAsgError,
};
// TODO: Stop exposing this. See `ld::xmle::lower`.
pub use base::BaseAsg;
pub use graph::{Asg, AsgError, AsgResult, IndexType, ObjectRef};
pub use ident::{DataType, Dim, IdentKind, IdentKindError};
pub use object::{
FragmentText, IdentObject, IdentObjectData, IdentObjectState, Source,

View File

@ -20,15 +20,16 @@
//! **This contains the remaining portions of the proof-of-concept linker.**
//! It is feature-complete and just needs final refactoring.
use super::xmle::{xir::lower_iter, Sections};
use super::xmle::{
lower::{sort, SortError},
xir::lower_iter,
Sections,
};
use crate::fs::{
Filesystem, FsCanonicalizer, PathFile, VisitOnceFile, VisitOnceFilesystem,
};
use crate::global;
use crate::ir::asg::{
Asg, DefaultAsg, IdentObject, IdentObjectData, SortableAsg,
SortableAsgError,
};
use crate::ir::asg::{Asg, DefaultAsg, IdentObject, IdentObjectData};
use crate::ir::xir::writer::XmlWriter;
use crate::obj::xmlo::{AsgBuilder, AsgBuilderState, XmloReader};
use crate::sym::SymbolId;
@ -71,9 +72,9 @@ pub fn xmle(package_path: &str, output: &str) -> Result<(), Box<dyn Error>> {
.filter_map(|sym| depgraph.lookup(sym)),
);
let mut sorted = match depgraph.sort(&roots) {
let mut sorted = match sort(&depgraph, &roots) {
Ok(sections) => sections,
Err(SortableAsgError::Cycles(cycles)) => {
Err(SortError::Cycles(cycles)) => {
let msg: Vec<String> = cycles
.into_iter()
.map(|cycle| {

File diff suppressed because it is too large Load Diff

View File

@ -70,6 +70,8 @@
//! ```
mod section;
pub mod lower;
pub mod xir;
pub use section::{Section, SectionIter, Sections, SectionsIter};

View File

@ -19,11 +19,9 @@
//! Sections of a linked [`xmle`](super) object file.
//!
//! These sections are the result of [`SortableAsg::sort`],
//! These sections are the result of [`sort`](super::lower::sort),
//! which places the relocatable object code fragments in the order
//! necessary for execution.
//!
//! [`SortableAsg::sort`]: crate::ir::asg::SortableAsg::sort
use crate::ir::asg::IdentObjectData;
use crate::sym::SymbolId;