tamer: ld::xmle::sections: Remove generic object type

xmle sections will only ever contain an object of one type, so there is no
use in making this generic.

I think the original plan was to have this represent, generically, sections
of some object file (like ELF), but doing so would require a significant
redesign anyway, so it makes no sense.  This is easier to reason about.

DEV-10859
main
Mike Gerwitz 2021-10-12 10:31:01 -04:00
parent 31144d0c9a
commit 08d92ca663
5 changed files with 80 additions and 193 deletions

View File

@ -212,7 +212,7 @@ fn get_ident<'a>(
fn output_xmle<'a>(
depgraph: &'a LinkerAsg,
sorted: &mut Sections<'a, IdentObject>,
sorted: &mut Sections<'a>,
name: SymbolId,
relroot: SymbolId,
output: &str,

View File

@ -24,8 +24,8 @@
use super::Sections;
use crate::{
ir::asg::{
Asg, BaseAsg, IdentKind, IdentObjectData, IdentObjectState, IndexType,
ObjectRef, UnresolvedError,
Asg, BaseAsg, IdentKind, IdentObject, IdentObjectData,
IdentObjectState, IndexType, ObjectRef, UnresolvedError,
},
sym::GlobalSymbolResolve,
};
@ -39,13 +39,12 @@ pub type SortResult<T, Ix> = Result<T, SortError<Ix>>;
/// This performs the equivalent of a topological sort,
/// although function cycles are permitted.
/// The actual operation performed is a post-order depth-first traversal.
pub fn sort<'a, O, Ix>(
asg: &'a BaseAsg<O, Ix>,
pub fn sort<'a, Ix>(
asg: &'a BaseAsg<IdentObject, Ix>,
roots: &[ObjectRef<Ix>],
) -> SortResult<Sections<'a, O>, Ix>
) -> SortResult<Sections<'a>, Ix>
where
Ix: IndexType,
O: IdentObjectData + IdentObjectState<O>,
{
let mut deps = Sections::new();
@ -101,10 +100,9 @@ where
///
/// 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>) -> SortResult<(), Ix>
fn check_cycles<Ix>(asg: &BaseAsg<IdentObject, Ix>) -> SortResult<(), 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
@ -209,95 +207,16 @@ impl<Ix: IndexType> std::error::Error for SortError<Ix> {
#[cfg(test)]
mod test {
use std::cell::RefCell;
use super::*;
use crate::{
ir::{
asg::{
DataType, Dim, FragmentText, IdentObject, Source,
TransitionResult,
},
asg::{DataType, Dim, FragmentText, IdentObject, Source},
legacyir::SymDtype,
},
sym::{GlobalSymbolIntern, SymbolId},
sym::GlobalSymbolIntern,
};
#[derive(Debug, Default, PartialEq)]
struct StubIdentObject {
given_declare: Option<SymbolId>,
given_extern: Option<(IdentKind, Source)>,
given_resolve: Option<(IdentKind, Source)>,
given_set_fragment: Option<FragmentText>,
fail_resolved: RefCell<Option<UnresolvedError>>,
}
impl<'i> IdentObjectData for StubIdentObject {
fn name(&self) -> Option<SymbolId> {
self.given_declare
}
fn kind(&self) -> Option<&IdentKind> {
self.given_resolve.as_ref().map(|args| &args.0)
}
fn src(&self) -> Option<&Source> {
None
}
fn fragment(&self) -> Option<&FragmentText> {
None
}
fn as_ident(&self) -> Option<&IdentObject> {
None
}
}
impl<'i> IdentObjectState<StubIdentObject> for StubIdentObject {
fn declare(ident: SymbolId) -> Self {
Self {
given_declare: Some(ident),
..Default::default()
}
}
fn resolve(
mut self,
kind: IdentKind,
src: Source,
) -> TransitionResult<StubIdentObject> {
self.given_resolve = Some((kind, src));
Ok(self)
}
fn resolved(&self) -> Result<&StubIdentObject, UnresolvedError> {
if self.fail_resolved.borrow().is_some() {
return Err(self.fail_resolved.replace(None).unwrap());
}
Ok(self)
}
fn extern_(
mut self,
kind: IdentKind,
src: Source,
) -> TransitionResult<StubIdentObject> {
self.given_extern = Some((kind, src));
Ok(self)
}
fn set_fragment(
mut self,
text: FragmentText,
) -> TransitionResult<StubIdentObject> {
self.given_set_fragment.replace(text);
Ok(self)
}
}
type Sut = BaseAsg<StubIdentObject, u16>;
type Sut = BaseAsg<IdentObject, u16>;
macro_rules! assert_section_sym {
( $iterable:expr, $s:ident ) => {{
@ -397,9 +316,9 @@ mod test {
match sort(&sut, &vec![sym_node]) {
Ok(_) => panic!("Unexpected success - dependency is not in graph"),
Err(SortError::MissingObjectKind(_)) => (),
_ => {
panic!("Incorrect error result when dependency is not in graph")
Err(SortError::UnresolvedObject(_)) => (),
bad => {
panic!("Incorrect error result when dependency is not in graph: {:?}", bad)
}
}
@ -969,33 +888,4 @@ mod test {
Ok(())
}
/// A graph containing unresolved objects cannot be sorted.
#[test]
fn graph_sort_fail_unresolved() -> SortResult<(), 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 = sort(&sut, &vec![node])
.expect_err("expected sort failure due to unresolved identifier");
match result {
SortError::UnresolvedObject(err) => {
assert_eq!(expected, err);
}
_ => panic!("expected SortError::Unresolved: {:?}", result),
}
Ok(())
}
}

View File

@ -23,7 +23,7 @@
//! which places the relocatable object code fragments in the order
//! necessary for execution.
use crate::ir::asg::IdentObjectData;
use crate::ir::asg::{IdentObject, IdentObjectData};
use crate::sym::SymbolId;
use fxhash::FxHashSet;
use std::collections::hash_set;
@ -37,13 +37,13 @@ use std::slice::Iter;
/// information. Rather than dealing with those differently, each `Section`
/// will have a `head` and `tail` that are empty by default.
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Section<'a, T> {
head: Option<&'a T>,
body: Vec<&'a T>,
tail: Option<&'a T>,
pub struct Section<'a> {
head: Option<&'a IdentObject>,
body: Vec<&'a IdentObject>,
tail: Option<&'a IdentObject>,
}
impl<'a, T> Section<'a, T> {
impl<'a> Section<'a> {
/// New empty section.
pub fn new() -> Self {
Self {
@ -61,26 +61,26 @@ impl<'a, T> Section<'a, T> {
/// Push an `IdentObject` into a `Section`'s head
#[inline]
pub fn set_head(&mut self, obj: &'a T) {
pub fn set_head(&mut self, obj: &'a IdentObject) {
self.head.replace(obj);
}
/// Push an `IdentObject` into a `Section`'s body
#[inline]
pub fn push_body(&mut self, obj: &'a T) {
pub fn push_body(&mut self, obj: &'a IdentObject) {
self.body.push(obj)
}
/// Push an `IdentObject` into a `Section`'s tail
#[inline]
pub fn set_tail(&mut self, obj: &'a T) {
pub fn set_tail(&mut self, obj: &'a IdentObject) {
self.tail.replace(obj);
}
/// Construct a new iterator visiting each head, body, and tail object
/// in order.
#[inline]
pub fn iter(&self) -> SectionIter<T> {
pub fn iter(&self) -> SectionIter {
SectionIter(
self.head
.iter()
@ -95,15 +95,15 @@ impl<'a, T> Section<'a, T> {
/// This iterator should be created with [`Section::iter`].
///
/// This hides the complex iterator type from callers.
pub struct SectionIter<'a, T>(
pub struct SectionIter<'a>(
Chain<
Chain<option::Iter<'a, &'a T>, Iter<'a, &'a T>>,
option::Iter<'a, &'a T>,
Chain<option::Iter<'a, &'a IdentObject>, Iter<'a, &'a IdentObject>>,
option::Iter<'a, &'a IdentObject>,
>,
);
impl<'a, T> Iterator for SectionIter<'a, T> {
type Item = &'a T;
impl<'a> Iterator for SectionIter<'a> {
type Item = &'a IdentObject;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
@ -122,14 +122,14 @@ impl<'a, T> Iterator for SectionIter<'a, T> {
/// [object file](crate::obj).
// TODO: Remove pub
#[derive(Debug, Default, PartialEq)]
pub struct Sections<'a, T> {
pub map: Section<'a, T>,
pub retmap: Section<'a, T>,
pub st: Section<'a, T>,
pub rater: Section<'a, T>,
pub struct Sections<'a> {
pub map: Section<'a>,
pub retmap: Section<'a>,
pub st: Section<'a>,
pub rater: Section<'a>,
}
impl<'a, T: IdentObjectData> Sections<'a, T> {
impl<'a> Sections<'a> {
/// New collection of empty sections.
#[inline]
pub fn new() -> Self {
@ -152,7 +152,7 @@ impl<'a, T: IdentObjectData> Sections<'a, T> {
/// they are chained in the same order in which they are defined
/// on the [`Sections`] struct.
#[inline]
pub fn iter_all(&self) -> SectionsIter<T> {
pub fn iter_all(&self) -> SectionsIter {
SectionsIter(SectionsIterType::All(
self.map
.iter()
@ -173,13 +173,13 @@ impl<'a, T: IdentObjectData> Sections<'a, T> {
/// appear in;
/// they may change or be combined in the future.
#[inline]
pub fn iter_static(&self) -> SectionsIter<T> {
pub fn iter_static(&self) -> SectionsIter {
SectionsIter(SectionsIterType::Single(self.st.iter()))
}
/// Construct an iterator over the map section.
#[inline]
pub fn iter_map(&self) -> SectionsIter<T> {
pub fn iter_map(&self) -> SectionsIter {
SectionsIter(SectionsIterType::Single(self.map.iter()))
}
@ -199,13 +199,13 @@ impl<'a, T: IdentObjectData> Sections<'a, T> {
/// Construct an iterator over the return map section.
#[inline]
pub fn iter_retmap(&self) -> SectionsIter<T> {
pub fn iter_retmap(&self) -> SectionsIter {
SectionsIter(SectionsIterType::Single(self.retmap.iter()))
}
/// Construct an iterator over the executable `rater` section.
#[inline]
pub fn iter_exec(&self) -> SectionsIter<T> {
pub fn iter_exec(&self) -> SectionsIter {
SectionsIter(SectionsIterType::Single(self.rater.iter()))
}
}
@ -213,15 +213,15 @@ impl<'a, T: IdentObjectData> Sections<'a, T> {
// Compose the chained iterator type for [`SectionsIter`].
// This could be further abstracted away,
// but it's likely that `Sections` will be simplified in the future.
type SIter<'a, T> = SectionIter<'a, T>;
type CSIter1<'a, T, L> = Chain<L, SIter<'a, T>>;
type CSIter2<'a, T, L> = CSIter1<'a, T, CSIter1<'a, T, L>>;
type SIter4<'a, T> = CSIter2<'a, T, CSIter1<'a, T, SIter<'a, T>>>;
type SIter<'a> = SectionIter<'a>;
type CSIter1<'a, L> = Chain<L, SIter<'a>>;
type CSIter2<'a, L> = CSIter1<'a, CSIter1<'a, L>>;
type SIter4<'a> = CSIter2<'a, CSIter1<'a, SIter<'a>>>;
/// Types of iterators encapsulated by [`SectionsIter`].
enum SectionsIterType<'a, T> {
All(SIter4<'a, T>),
Single(SIter<'a, T>),
enum SectionsIterType<'a> {
All(SIter4<'a>),
Single(SIter<'a>),
}
/// Iterator over each of the sections.
@ -229,10 +229,10 @@ enum SectionsIterType<'a, T> {
/// This iterator should be created with [`Sections::iter_all`].
///
/// This hides the complex iterator type from callers.
pub struct SectionsIter<'a, T>(SectionsIterType<'a, T>);
pub struct SectionsIter<'a>(SectionsIterType<'a>);
impl<'a, T> Iterator for SectionsIter<'a, T> {
type Item = &'a T;
impl<'a> Iterator for SectionsIter<'a> {
type Item = &'a IdentObject;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
@ -257,7 +257,7 @@ mod test {
use crate::ir::asg::{IdentKind, IdentObject, Source};
use crate::sym::GlobalSymbolIntern;
type Sut<'a> = Section<'a, IdentObject>;
type Sut<'a> = Section<'a>;
#[test]
fn section_empty() {

View File

@ -29,16 +29,15 @@
//! which can then use [`XmlWriter`](crate::ir::xir::writer::XmlWriter)
//! for writing.
use super::{Sections, SectionsIter};
use super::{super::LSPAN, Sections, SectionsIter};
use crate::{
ir::{
asg::{IdentKind, IdentObject, IdentObjectData},
asg::{IdentKind, IdentObject},
xir::{
iter::{elem_wrap, ElemWrapIter},
AttrValue, QName, Text, Token,
},
},
ld::LSPAN,
sym::{st::*, SymbolId},
};
use arrayvec::ArrayVec;
@ -117,18 +116,18 @@ const DEP_TOK_SIZE: usize = DEP_MAX_ATTRS_KEY_VAL + DEP_CLOSE;
/// another object is requested and the buffer populated with the
/// appropriate token stream.
/// This repeats until no more section object data is available.
struct DepListIter<'a, T: IdentObjectData> {
struct DepListIter<'a> {
/// Source data to lower into `l:deps`.
iter: SectionsIter<'a, T>,
iter: SectionsIter<'a>,
/// Constant-size [`Token`] buffer used as a stack.
toks: ArrayVec<Token, DEP_TOK_SIZE>,
/// Relative path to project root.
relroot: AttrValue,
}
impl<'a, T: IdentObjectData> DepListIter<'a, T> {
impl<'a> DepListIter<'a> {
#[inline]
fn new(sections: &'a Sections<T>, relroot: SymbolId) -> Self {
fn new(sections: &'a Sections, relroot: SymbolId) -> Self {
Self {
iter: sections.iter_all(),
toks: ArrayVec::new(),
@ -151,14 +150,12 @@ impl<'a, T: IdentObjectData> DepListIter<'a, T> {
// that we can diff the two;
// TODO: re-order sensibly once we're done.
self.iter.next().map(|obj| {
let ident = obj.as_ident().expect("unexpected non-identifier object");
match ident {
match obj {
IdentObject::Ident(sym, kind, src)
| IdentObject::IdentFragment(sym, kind, src, _) => (*sym, kind, src),
_ => unreachable!(
"identifier should have been filtered out during sorting: {:?}",
ident,
obj,
),
}
}).and_then(|(sym, kind, src)| {
@ -241,7 +238,7 @@ impl<'a, T: IdentObjectData> DepListIter<'a, T> {
}
}
impl<'a, T: IdentObjectData> Iterator for DepListIter<'a, T> {
impl<'a> Iterator for DepListIter<'a> {
type Item = Token;
#[inline]
@ -265,7 +262,7 @@ struct MapFromsIter {
impl MapFromsIter {
#[inline]
fn new<'a, T: IdentObjectData>(sections: &'a Sections<T>) -> Self {
fn new<'a>(sections: &'a Sections) -> Self {
let iter = Self {
iter: sections.iter_map_froms_uniq(),
// Most of the time we have a single `from` (4 tokens).
@ -301,18 +298,18 @@ impl Iterator for MapFromsIter {
/// Produce text fragments associated with objects.
///
/// Here, "text" refers to the compiled program text.
struct FragmentIter<'a, T> {
iter: SectionsIter<'a, T>,
struct FragmentIter<'a> {
iter: SectionsIter<'a>,
}
impl<'a, T: IdentObjectData> FragmentIter<'a, T> {
impl<'a> FragmentIter<'a> {
#[inline]
fn new(iter: SectionsIter<'a, T>) -> Self {
fn new(iter: SectionsIter<'a>) -> Self {
Self { iter }
}
}
impl<'a, T: IdentObjectData> Iterator for FragmentIter<'a, T> {
impl<'a> Iterator for FragmentIter<'a> {
type Item = Token;
#[inline]
@ -320,8 +317,7 @@ impl<'a, T: IdentObjectData> Iterator for FragmentIter<'a, T> {
self.iter
.by_ref()
.filter_map(|obj| {
match obj.as_ident().expect("encountered non-identifier object")
{
match obj {
IdentObject::IdentFragment(_, _, _, frag) => Some(*frag),
// These will never have fragments.
@ -345,28 +341,28 @@ impl<'a, T: IdentObjectData> Iterator for FragmentIter<'a, T> {
/// having to resort to dynamic dispatch,
/// since this iterator will receive over a million calls on larger
/// programs (and hundreds of thousands on smaller).
pub struct LowerIter<'a, T: IdentObjectData>(
pub struct LowerIter<'a>(
ElemWrapIter<
Chain<
Chain<
Chain<
Chain<
Chain<
Chain<HeaderIter, ElemWrapIter<DepListIter<'a, T>>>,
Chain<HeaderIter, ElemWrapIter<DepListIter<'a>>>,
ElemWrapIter<MapFromsIter>,
>,
ElemWrapIter<FragmentIter<'a, T>>,
ElemWrapIter<FragmentIter<'a>>,
>,
ElemWrapIter<FragmentIter<'a, T>>,
ElemWrapIter<FragmentIter<'a>>,
>,
ElemWrapIter<FragmentIter<'a, T>>,
ElemWrapIter<FragmentIter<'a>>,
>,
ElemWrapIter<FragmentIter<'a, T>>,
ElemWrapIter<FragmentIter<'a>>,
>,
>,
);
impl<'a, T: IdentObjectData> Iterator for LowerIter<'a, T> {
impl<'a> Iterator for LowerIter<'a> {
type Item = Token;
/// Produce the next XIR [`Token`] representing the lowering of
@ -388,11 +384,11 @@ impl<'a, T: IdentObjectData> Iterator for LowerIter<'a, T> {
/// which can be written using
/// [`XmlWriter`](crate::ir::xir::writer::XmlWriter).
#[inline]
pub fn lower_iter<'a, T: IdentObjectData>(
sections: &'a Sections<T>,
pub fn lower_iter<'a>(
sections: &'a Sections,
pkg_name: SymbolId,
relroot: SymbolId,
) -> LowerIter<'a, T> {
) -> LowerIter<'a> {
LowerIter(elem_wrap(
QN_PACKAGE,
LSPAN,

View File

@ -19,6 +19,7 @@
use super::*;
use crate::convert::ExpectInto;
use crate::ir::asg::IdentObjectData;
use crate::ir::legacyir::SymDtype;
use crate::ir::{
asg::{Dim, IdentKind, Source},
@ -44,7 +45,7 @@ macro_rules! assert_attr{
#[test]
fn test_produces_header() -> TestResult {
let empty = Sections::<IdentObject>::new();
let empty = Sections::new();
let name = "test-pkg".intern();
let relroot = "rel/root/".intern();
@ -63,7 +64,7 @@ fn test_produces_header() -> TestResult {
#[test]
fn test_closes_package() -> TestResult {
let empty = Sections::<IdentObject>::new();
let empty = Sections::new();
let result = lower_iter(&empty, "foo".intern(), "relroot".intern()).last();
@ -212,7 +213,7 @@ fn test_writes_deps() -> TestResult {
});
p_syms.enumerate().for_each(|(i, ele)| {
let ident = objs[i].as_ident().unwrap();
let ident = &objs[i];
let attrs = ele.attrs();
assert_eq!(