2021-10-12 09:42:09 -04:00
|
|
|
// ASG lowering into xmle sections
|
|
|
|
//
|
2022-05-03 14:14:29 -04:00
|
|
|
// Copyright (C) 2014-2022 Ryan Specialty Group, LLC.
|
2021-10-12 09:42:09 -04:00
|
|
|
//
|
|
|
|
// This file is part of TAME.
|
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
2021-11-04 16:12:15 -04:00
|
|
|
//! Lowering of the [ASG](crate::asg) into `xmle` [`XmleSections`].
|
2021-10-12 09:42:09 -04:00
|
|
|
//!
|
|
|
|
//! See the [parent module](super) for more information.
|
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
use super::section::{SectionsError, XmleSections};
|
|
|
|
use crate::{
|
2022-05-11 16:38:59 -04:00
|
|
|
asg::{Asg, IdentKind, IdentObject, ObjectRef},
|
2021-10-14 12:37:32 -04:00
|
|
|
sym::{st, GlobalSymbolResolve, SymbolId},
|
2021-10-12 09:42:09 -04:00
|
|
|
};
|
|
|
|
use petgraph::visit::DfsPostOrder;
|
|
|
|
|
|
|
|
// Result of [`sort`].
|
2022-01-14 10:21:49 -05:00
|
|
|
pub type SortResult<T> = Result<T, SortError>;
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
/// Lower ASG into [`XmleSections`] by ordering relocatable text fragments.
|
2021-10-12 09:42:09 -04:00
|
|
|
///
|
|
|
|
/// This performs the equivalent of a topological sort,
|
|
|
|
/// although function cycles are permitted.
|
|
|
|
/// The actual operation performed is a post-order depth-first traversal.
|
tamer: asg: Track roots on graph
Previously, since the graph contained only identifiers, discovered roots
were stored in a separate vector and exposed to the caller. This not only
leaked details, but added complexity; this was left over from the
refactoring of the proof-of-concept linker some time ago.
This moves the root management into the ASG itself, mostly, with one item
being left over for now in the asg_builder (eligibility classifications).
There are two roots that were added automatically:
- __yield
- __worksheet
The former has been removed and is now expected to be explicitly mapped in
the return map, which is now enforced with an extern in `core/base`. This
is still special, in the sense that it is explicitly referenced by the
generated code, but there's nothing inherently special about it and I'll
continue to generalize it into oblivion in the future, such that the final
yield is just a convention.
`__worksheet` is the only symbol of type `IdentKind::Worksheet`, and so that
was generalized just as the meta and map entries were.
The goal in the future will be to have this more under the control of the
source language, and to consolodate individual roots under packages, so that
the _actual_ roots are few.
As far as the actual ASG goes: this introduces a single root node that is
used as the sole reference for reachability analysis and topological
sorting. The edges of that root node replace the vector that was removed.
DEV-11864
2022-05-17 10:42:05 -04:00
|
|
|
pub fn sort<'a, S: XmleSections<'a>>(asg: &'a Asg, mut dest: S) -> SortResult<S>
|
2021-10-12 09:42:09 -04:00
|
|
|
where
|
2021-10-14 12:37:32 -04:00
|
|
|
S: XmleSections<'a>,
|
2021-10-12 09:42:09 -04:00
|
|
|
{
|
|
|
|
// TODO: we should check for cycles as we sort (as the POC did).
|
|
|
|
check_cycles(asg)?;
|
|
|
|
|
tamer: asg: Track roots on graph
Previously, since the graph contained only identifiers, discovered roots
were stored in a separate vector and exposed to the caller. This not only
leaked details, but added complexity; this was left over from the
refactoring of the proof-of-concept linker some time ago.
This moves the root management into the ASG itself, mostly, with one item
being left over for now in the asg_builder (eligibility classifications).
There are two roots that were added automatically:
- __yield
- __worksheet
The former has been removed and is now expected to be explicitly mapped in
the return map, which is now enforced with an extern in `core/base`. This
is still special, in the sense that it is explicitly referenced by the
generated code, but there's nothing inherently special about it and I'll
continue to generalize it into oblivion in the future, such that the final
yield is just a convention.
`__worksheet` is the only symbol of type `IdentKind::Worksheet`, and so that
was generalized just as the meta and map entries were.
The goal in the future will be to have this more under the control of the
source language, and to consolodate individual roots under packages, so that
the _actual_ roots are few.
As far as the actual ASG goes: this introduces a single root node that is
used as the sole reference for reachability analysis and topological
sorting. The edges of that root node replace the vector that was removed.
DEV-11864
2022-05-17 10:42:05 -04:00
|
|
|
// TODO: Encapsulate petgraph.
|
2021-10-12 09:42:09 -04:00
|
|
|
// This is technically a topological sort, but functions have cycles.
|
tamer: asg: Track roots on graph
Previously, since the graph contained only identifiers, discovered roots
were stored in a separate vector and exposed to the caller. This not only
leaked details, but added complexity; this was left over from the
refactoring of the proof-of-concept linker some time ago.
This moves the root management into the ASG itself, mostly, with one item
being left over for now in the asg_builder (eligibility classifications).
There are two roots that were added automatically:
- __yield
- __worksheet
The former has been removed and is now expected to be explicitly mapped in
the return map, which is now enforced with an extern in `core/base`. This
is still special, in the sense that it is explicitly referenced by the
generated code, but there's nothing inherently special about it and I'll
continue to generalize it into oblivion in the future, such that the final
yield is just a convention.
`__worksheet` is the only symbol of type `IdentKind::Worksheet`, and so that
was generalized just as the meta and map entries were.
The goal in the future will be to have this more under the control of the
source language, and to consolodate individual roots under packages, so that
the _actual_ roots are few.
As far as the actual ASG goes: this introduces a single root node that is
used as the sole reference for reachability analysis and topological
sorting. The edges of that root node replace the vector that was removed.
DEV-11864
2022-05-17 10:42:05 -04:00
|
|
|
let mut dfs = DfsPostOrder::new(&asg.graph, asg.root());
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
// These are always generated by the map compiler,
|
|
|
|
// but do not have edges that would allow them to be properly ordered
|
|
|
|
// (adding an edge to every map object would be wasteful).
|
|
|
|
dest.push(get_ident(asg, st::L_MAP_UUUHEAD))?;
|
|
|
|
dest.push(get_ident(asg, st::L_RETMAP_UUUHEAD))?;
|
|
|
|
|
2021-10-12 09:42:09 -04:00
|
|
|
while let Some(index) = dfs.next(&asg.graph) {
|
2021-10-12 12:14:24 -04:00
|
|
|
let ident = asg.get(index).expect("missing node");
|
2021-10-14 12:37:32 -04:00
|
|
|
dest.push(ident)?;
|
2021-10-12 09:42:09 -04:00
|
|
|
}
|
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
dest.push(get_ident(asg, st::L_MAP_UUUTAIL))?;
|
|
|
|
dest.push(get_ident(asg, st::L_RETMAP_UUUTAIL))?;
|
|
|
|
|
|
|
|
Ok(dest)
|
|
|
|
}
|
|
|
|
|
2022-05-12 15:44:32 -04:00
|
|
|
fn get_ident<'a, S>(depgraph: &'a Asg, name: S) -> &'a IdentObject
|
2021-10-14 12:37:32 -04:00
|
|
|
where
|
|
|
|
S: Into<SymbolId>,
|
|
|
|
{
|
|
|
|
let sym = name.into();
|
|
|
|
|
|
|
|
depgraph
|
|
|
|
.lookup(sym)
|
|
|
|
.and_then(|id| depgraph.get(id))
|
|
|
|
.expect(&format!(
|
|
|
|
"missing internal identifier: {}",
|
|
|
|
sym.lookup_str()
|
|
|
|
))
|
2021-10-12 09:42:09 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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.
|
2022-05-12 15:44:32 -04:00
|
|
|
fn check_cycles(asg: &Asg) -> SortResult<()> {
|
2021-10-12 09:42:09 -04:00
|
|
|
// 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| {
|
2022-05-11 16:38:59 -04:00
|
|
|
let ident = asg.get(*nx).expect("missing node");
|
tamer: Use `..` for tuple unimportant variant matches
Tbh, I was unaware that this was supported by tuple variants until reading
over the Rustc source code for something. (Which I had previously read, but
I must have missed it.)
This is more proper, in the sense that in a lot of cases we not only care
about how many values a tuple has, but if we explicitly match on them using
`_`, then any time we modify the number of values, it would _break_ any code
doing so. Using this method, we improve maintainability by not causing
breakages under those circumstances.
But, consequently, it's important that we use this only when we _really_
don't care and don't want to be notified by the compiler.
I did not use `..` as a prefix, even where supported, because the intent is
to append additional information to tuples. Consequently, I also used `..`
in places where no additional fields currently exist, since they may in the
future (e.g. introducing `Span` for `IdentObject`).
2021-10-15 12:28:59 -04:00
|
|
|
matches!(ident.kind(), Some(IdentKind::Func(..)))
|
2021-10-12 09:42:09 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
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(SortError::Cycles(cycles))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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)]
|
2022-01-14 10:21:49 -05:00
|
|
|
pub enum SortError {
|
2021-10-14 12:37:32 -04:00
|
|
|
/// Error while lowering into [`XmleSections`].
|
2021-10-12 12:14:24 -04:00
|
|
|
SectionsError(SectionsError),
|
2021-10-12 09:42:09 -04:00
|
|
|
|
|
|
|
/// The graph has a cyclic dependency.
|
2022-01-14 10:21:49 -05:00
|
|
|
Cycles(Vec<Vec<ObjectRef>>),
|
2021-10-12 09:42:09 -04:00
|
|
|
}
|
|
|
|
|
2022-01-14 10:21:49 -05:00
|
|
|
impl From<SectionsError> for SortError {
|
2021-10-12 12:14:24 -04:00
|
|
|
fn from(err: SectionsError) -> Self {
|
|
|
|
Self::SectionsError(err)
|
2021-10-12 09:42:09 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-14 10:21:49 -05:00
|
|
|
impl std::fmt::Display for SortError {
|
2021-10-12 09:42:09 -04:00
|
|
|
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
|
|
match self {
|
2021-10-12 12:14:24 -04:00
|
|
|
Self::SectionsError(err) => err.fmt(fmt),
|
2021-10-12 09:42:09 -04:00
|
|
|
Self::Cycles(_) => write!(fmt, "cyclic dependencies"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-14 10:21:49 -05:00
|
|
|
impl std::error::Error for SortError {
|
2021-10-12 09:42:09 -04:00
|
|
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
2021-10-12 12:14:24 -04:00
|
|
|
None
|
2021-10-12 09:42:09 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::*;
|
|
|
|
use crate::{
|
2021-11-04 16:12:15 -04:00
|
|
|
asg::{Dim, FragmentText, IdentObject, Source},
|
2021-10-14 12:37:32 -04:00
|
|
|
ld::xmle::{section::PushResult, Sections},
|
2021-11-04 13:20:38 -04:00
|
|
|
obj::xmlo::SymDtype,
|
2021-10-12 10:31:01 -04:00
|
|
|
sym::GlobalSymbolIntern,
|
2021-10-12 09:42:09 -04:00
|
|
|
};
|
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
/// Create a graph with the expected {ret,}map head/tail identifiers.
|
2022-05-12 15:44:32 -04:00
|
|
|
fn make_asg() -> Asg {
|
|
|
|
let mut asg = Asg::new();
|
2021-10-14 12:37:32 -04:00
|
|
|
|
|
|
|
let text = "dummy fragment".intern();
|
|
|
|
|
2022-05-16 10:53:07 -04:00
|
|
|
{
|
|
|
|
let sym = st::L_MAP_UUUHEAD.into();
|
|
|
|
asg.declare(sym, IdentKind::MapHead, Default::default())
|
|
|
|
.unwrap();
|
|
|
|
asg.set_fragment(sym, text).unwrap();
|
|
|
|
}
|
2021-10-14 12:37:32 -04:00
|
|
|
|
2022-05-16 10:53:07 -04:00
|
|
|
{
|
|
|
|
let sym = st::L_MAP_UUUTAIL.into();
|
|
|
|
asg.declare(sym, IdentKind::MapTail, Default::default())
|
|
|
|
.unwrap();
|
|
|
|
asg.set_fragment(sym, text).unwrap();
|
|
|
|
}
|
2021-10-14 12:37:32 -04:00
|
|
|
|
2022-05-16 10:53:07 -04:00
|
|
|
{
|
|
|
|
let sym = st::L_RETMAP_UUUHEAD.into();
|
|
|
|
asg.declare(sym, IdentKind::RetMapHead, Default::default())
|
|
|
|
.unwrap();
|
|
|
|
asg.set_fragment(sym, text).unwrap();
|
|
|
|
}
|
2021-10-14 12:37:32 -04:00
|
|
|
|
2022-05-16 10:53:07 -04:00
|
|
|
{
|
|
|
|
let sym = st::L_RETMAP_UUUTAIL.into();
|
|
|
|
asg.declare(sym, IdentKind::RetMapTail, Default::default())
|
|
|
|
.unwrap();
|
|
|
|
asg.set_fragment(sym, text).unwrap();
|
|
|
|
}
|
2021-10-14 12:37:32 -04:00
|
|
|
|
|
|
|
asg
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-01-14 10:21:49 -05:00
|
|
|
fn graph_sort() -> SortResult<()> {
|
2021-10-14 12:37:32 -04:00
|
|
|
// We care only about the order of pushes, not the sections they end
|
|
|
|
// up in.
|
|
|
|
struct StubSections<'a> {
|
|
|
|
pushed: Vec<&'a IdentObject>,
|
|
|
|
}
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
impl<'a> XmleSections<'a> for StubSections<'a> {
|
|
|
|
fn push(&mut self, ident: &'a IdentObject) -> PushResult {
|
|
|
|
self.pushed.push(ident);
|
|
|
|
Ok(())
|
|
|
|
}
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
fn take_deps(&mut self) -> Vec<&'a IdentObject> {
|
|
|
|
unimplemented!()
|
|
|
|
}
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
fn take_static(&mut self) -> Vec<SymbolId> {
|
|
|
|
unimplemented!()
|
2021-10-12 09:42:09 -04:00
|
|
|
}
|
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
fn take_map(&mut self) -> Vec<SymbolId> {
|
|
|
|
unimplemented!()
|
|
|
|
}
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
fn take_map_froms(&mut self) -> fxhash::FxHashSet<SymbolId> {
|
|
|
|
unimplemented!()
|
|
|
|
}
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
fn take_retmap(&mut self) -> Vec<SymbolId> {
|
|
|
|
unimplemented!()
|
|
|
|
}
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
fn take_exec(&mut self) -> Vec<SymbolId> {
|
|
|
|
unimplemented!()
|
|
|
|
}
|
|
|
|
}
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let mut asg = make_asg();
|
|
|
|
|
|
|
|
// Add them in an unsorted order.
|
|
|
|
let adep = asg
|
|
|
|
.declare("adep".into(), IdentKind::Meta, Default::default())
|
|
|
|
.unwrap();
|
|
|
|
let a = asg
|
|
|
|
.declare("a".into(), IdentKind::Meta, Default::default())
|
|
|
|
.unwrap();
|
|
|
|
let adepdep = asg
|
|
|
|
.declare("adepdep".into(), IdentKind::Meta, Default::default())
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
asg.add_dep(a, adep);
|
|
|
|
asg.add_dep(adep, adepdep);
|
|
|
|
|
tamer: asg: Track roots on graph
Previously, since the graph contained only identifiers, discovered roots
were stored in a separate vector and exposed to the caller. This not only
leaked details, but added complexity; this was left over from the
refactoring of the proof-of-concept linker some time ago.
This moves the root management into the ASG itself, mostly, with one item
being left over for now in the asg_builder (eligibility classifications).
There are two roots that were added automatically:
- __yield
- __worksheet
The former has been removed and is now expected to be explicitly mapped in
the return map, which is now enforced with an extern in `core/base`. This
is still special, in the sense that it is explicitly referenced by the
generated code, but there's nothing inherently special about it and I'll
continue to generalize it into oblivion in the future, such that the final
yield is just a convention.
`__worksheet` is the only symbol of type `IdentKind::Worksheet`, and so that
was generalized just as the meta and map entries were.
The goal in the future will be to have this more under the control of the
source language, and to consolodate individual roots under packages, so that
the _actual_ roots are few.
As far as the actual ASG goes: this introduces a single root node that is
used as the sole reference for reachability analysis and topological
sorting. The edges of that root node replace the vector that was removed.
DEV-11864
2022-05-17 10:42:05 -04:00
|
|
|
asg.add_root(a);
|
|
|
|
|
|
|
|
let sections = sort(&asg, StubSections { pushed: Vec::new() })?;
|
2021-10-14 12:37:32 -04:00
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
sections.pushed,
|
|
|
|
vec![
|
|
|
|
// Static head
|
|
|
|
asg.lookup(st::L_MAP_UUUHEAD.into())
|
|
|
|
.and_then(|id| asg.get(id)),
|
|
|
|
asg.lookup(st::L_RETMAP_UUUHEAD.into())
|
|
|
|
.and_then(|id| asg.get(id)),
|
|
|
|
// Post-order
|
|
|
|
asg.get(adepdep),
|
|
|
|
asg.get(adep),
|
|
|
|
asg.get(a),
|
tamer: asg: Track roots on graph
Previously, since the graph contained only identifiers, discovered roots
were stored in a separate vector and exposed to the caller. This not only
leaked details, but added complexity; this was left over from the
refactoring of the proof-of-concept linker some time ago.
This moves the root management into the ASG itself, mostly, with one item
being left over for now in the asg_builder (eligibility classifications).
There are two roots that were added automatically:
- __yield
- __worksheet
The former has been removed and is now expected to be explicitly mapped in
the return map, which is now enforced with an extern in `core/base`. This
is still special, in the sense that it is explicitly referenced by the
generated code, but there's nothing inherently special about it and I'll
continue to generalize it into oblivion in the future, such that the final
yield is just a convention.
`__worksheet` is the only symbol of type `IdentKind::Worksheet`, and so that
was generalized just as the meta and map entries were.
The goal in the future will be to have this more under the control of the
source language, and to consolodate individual roots under packages, so that
the _actual_ roots are few.
As far as the actual ASG goes: this introduces a single root node that is
used as the sole reference for reachability analysis and topological
sorting. The edges of that root node replace the vector that was removed.
DEV-11864
2022-05-17 10:42:05 -04:00
|
|
|
// TODO: This will go away once Root is no longer considered
|
|
|
|
// an identifier.
|
|
|
|
// The real Sections filters this out.
|
|
|
|
Some(&IdentObject::Root),
|
2021-10-14 12:37:32 -04:00
|
|
|
// Static tail
|
|
|
|
asg.lookup(st::L_MAP_UUUTAIL.into())
|
|
|
|
.and_then(|id| asg.get(id)),
|
|
|
|
asg.lookup(st::L_RETMAP_UUUTAIL.into())
|
|
|
|
.and_then(|id| asg.get(id)),
|
|
|
|
]
|
|
|
|
.into_iter()
|
|
|
|
.collect::<Option<Vec<_>>>()
|
|
|
|
.unwrap()
|
|
|
|
);
|
2021-10-12 09:42:09 -04:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-01-14 10:21:49 -05:00
|
|
|
fn graph_sort_missing_node() -> SortResult<()> {
|
2021-10-14 12:37:32 -04:00
|
|
|
let mut asg = make_asg();
|
2021-10-12 09:42:09 -04:00
|
|
|
|
|
|
|
let sym = "sym".intern();
|
|
|
|
let dep = "dep".intern();
|
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let sym_node = asg
|
2021-10-12 09:42:09 -04:00
|
|
|
.declare(
|
|
|
|
sym,
|
|
|
|
IdentKind::Tpl,
|
|
|
|
Source {
|
|
|
|
virtual_: true,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2022-05-16 10:53:07 -04:00
|
|
|
asg.set_fragment(sym, FragmentText::from("foo")).unwrap();
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let (_, _) = asg.add_dep_lookup(sym, dep);
|
2021-10-12 09:42:09 -04:00
|
|
|
|
tamer: asg: Track roots on graph
Previously, since the graph contained only identifiers, discovered roots
were stored in a separate vector and exposed to the caller. This not only
leaked details, but added complexity; this was left over from the
refactoring of the proof-of-concept linker some time ago.
This moves the root management into the ASG itself, mostly, with one item
being left over for now in the asg_builder (eligibility classifications).
There are two roots that were added automatically:
- __yield
- __worksheet
The former has been removed and is now expected to be explicitly mapped in
the return map, which is now enforced with an extern in `core/base`. This
is still special, in the sense that it is explicitly referenced by the
generated code, but there's nothing inherently special about it and I'll
continue to generalize it into oblivion in the future, such that the final
yield is just a convention.
`__worksheet` is the only symbol of type `IdentKind::Worksheet`, and so that
was generalized just as the meta and map entries were.
The goal in the future will be to have this more under the control of the
source language, and to consolodate individual roots under packages, so that
the _actual_ roots are few.
As far as the actual ASG goes: this introduces a single root node that is
used as the sole reference for reachability analysis and topological
sorting. The edges of that root node replace the vector that was removed.
DEV-11864
2022-05-17 10:42:05 -04:00
|
|
|
asg.add_root(sym_node);
|
|
|
|
|
|
|
|
match sort(&asg, Sections::new()) {
|
2021-10-12 09:42:09 -04:00
|
|
|
Ok(_) => panic!("Unexpected success - dependency is not in graph"),
|
2021-10-12 12:14:24 -04:00
|
|
|
Err(SortError::SectionsError(SectionsError::UnresolvedObject(
|
|
|
|
_,
|
|
|
|
))) => (),
|
2021-10-12 10:31:01 -04:00
|
|
|
bad => {
|
|
|
|
panic!("Incorrect error result when dependency is not in graph: {:?}", bad)
|
2021-10-12 09:42:09 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-01-14 10:21:49 -05:00
|
|
|
fn graph_sort_no_roots_same_as_empty_graph() -> SortResult<()> {
|
2021-10-14 12:37:32 -04:00
|
|
|
let mut asg_nonempty_no_roots = make_asg();
|
|
|
|
|
|
|
|
// "empty" (it has the head/tail {ret,}map objects)
|
|
|
|
let asg_empty = make_asg();
|
2021-10-12 09:42:09 -04:00
|
|
|
|
|
|
|
let sym = "sym".intern();
|
|
|
|
let dep = "dep".intern();
|
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
asg_nonempty_no_roots.add_dep_lookup(sym, dep);
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
assert_eq!(
|
tamer: asg: Track roots on graph
Previously, since the graph contained only identifiers, discovered roots
were stored in a separate vector and exposed to the caller. This not only
leaked details, but added complexity; this was left over from the
refactoring of the proof-of-concept linker some time ago.
This moves the root management into the ASG itself, mostly, with one item
being left over for now in the asg_builder (eligibility classifications).
There are two roots that were added automatically:
- __yield
- __worksheet
The former has been removed and is now expected to be explicitly mapped in
the return map, which is now enforced with an extern in `core/base`. This
is still special, in the sense that it is explicitly referenced by the
generated code, but there's nothing inherently special about it and I'll
continue to generalize it into oblivion in the future, such that the final
yield is just a convention.
`__worksheet` is the only symbol of type `IdentKind::Worksheet`, and so that
was generalized just as the meta and map entries were.
The goal in the future will be to have this more under the control of the
source language, and to consolodate individual roots under packages, so that
the _actual_ roots are few.
As far as the actual ASG goes: this introduces a single root node that is
used as the sole reference for reachability analysis and topological
sorting. The edges of that root node replace the vector that was removed.
DEV-11864
2022-05-17 10:42:05 -04:00
|
|
|
sort(&asg_nonempty_no_roots, Sections::new()),
|
|
|
|
sort(&asg_empty, Sections::new())
|
2021-10-14 12:37:32 -04:00
|
|
|
);
|
2021-10-12 09:42:09 -04:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-01-14 10:21:49 -05:00
|
|
|
fn graph_sort_simple_cycle() -> SortResult<()> {
|
2021-10-14 12:37:32 -04:00
|
|
|
let mut asg = make_asg();
|
2021-10-12 09:42:09 -04:00
|
|
|
|
|
|
|
let sym = "sym".intern();
|
|
|
|
let dep = "dep".intern();
|
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let sym_node = asg
|
2021-10-12 09:42:09 -04:00
|
|
|
.declare(
|
|
|
|
sym,
|
|
|
|
IdentKind::Tpl,
|
|
|
|
Source {
|
|
|
|
virtual_: true,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let dep_node = asg
|
2021-10-12 09:42:09 -04:00
|
|
|
.declare(
|
|
|
|
dep,
|
|
|
|
IdentKind::Tpl,
|
|
|
|
Source {
|
|
|
|
virtual_: true,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2022-05-16 10:53:07 -04:00
|
|
|
asg.set_fragment(sym, FragmentText::from("foo")).unwrap();
|
|
|
|
asg.set_fragment(dep, FragmentText::from("bar")).unwrap();
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let (_, _) = asg.add_dep_lookup(sym, dep);
|
|
|
|
let (_, _) = asg.add_dep_lookup(dep, sym);
|
2021-10-12 09:42:09 -04:00
|
|
|
|
tamer: asg: Track roots on graph
Previously, since the graph contained only identifiers, discovered roots
were stored in a separate vector and exposed to the caller. This not only
leaked details, but added complexity; this was left over from the
refactoring of the proof-of-concept linker some time ago.
This moves the root management into the ASG itself, mostly, with one item
being left over for now in the asg_builder (eligibility classifications).
There are two roots that were added automatically:
- __yield
- __worksheet
The former has been removed and is now expected to be explicitly mapped in
the return map, which is now enforced with an extern in `core/base`. This
is still special, in the sense that it is explicitly referenced by the
generated code, but there's nothing inherently special about it and I'll
continue to generalize it into oblivion in the future, such that the final
yield is just a convention.
`__worksheet` is the only symbol of type `IdentKind::Worksheet`, and so that
was generalized just as the meta and map entries were.
The goal in the future will be to have this more under the control of the
source language, and to consolodate individual roots under packages, so that
the _actual_ roots are few.
As far as the actual ASG goes: this introduces a single root node that is
used as the sole reference for reachability analysis and topological
sorting. The edges of that root node replace the vector that was removed.
DEV-11864
2022-05-17 10:42:05 -04:00
|
|
|
asg.add_root(sym_node);
|
|
|
|
|
|
|
|
let result = sort(&asg, Sections::new());
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2022-01-14 10:21:49 -05:00
|
|
|
let expected: Vec<Vec<ObjectRef>> =
|
2021-10-12 09:42:09 -04:00
|
|
|
vec![vec![dep_node.into(), sym_node.into()]];
|
|
|
|
match result {
|
|
|
|
Ok(_) => panic!("sort did not detect cycle"),
|
|
|
|
Err(SortError::Cycles(scc)) => assert_eq!(expected, scc),
|
|
|
|
Err(e) => panic!("unexpected error: {}", e),
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-01-14 10:21:49 -05:00
|
|
|
fn graph_sort_two_simple_cycles() -> SortResult<()> {
|
2021-10-14 12:37:32 -04:00
|
|
|
let mut asg = make_asg();
|
2021-10-12 09:42:09 -04:00
|
|
|
|
|
|
|
let sym = "sym".intern();
|
|
|
|
let sym2 = "sym2".intern();
|
|
|
|
let dep = "dep".intern();
|
|
|
|
let dep2 = "dep2".intern();
|
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let sym_node = asg
|
2021-10-12 09:42:09 -04:00
|
|
|
.declare(
|
|
|
|
sym,
|
|
|
|
IdentKind::Tpl,
|
|
|
|
Source {
|
|
|
|
virtual_: true,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let sym2_node = asg
|
2021-10-12 09:42:09 -04:00
|
|
|
.declare(
|
|
|
|
sym2,
|
|
|
|
IdentKind::Tpl,
|
|
|
|
Source {
|
|
|
|
virtual_: true,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let dep_node = asg
|
2021-10-12 09:42:09 -04:00
|
|
|
.declare(
|
|
|
|
dep,
|
|
|
|
IdentKind::Tpl,
|
|
|
|
Source {
|
|
|
|
virtual_: true,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let dep2_node = asg
|
2021-10-12 09:42:09 -04:00
|
|
|
.declare(
|
|
|
|
dep2,
|
|
|
|
IdentKind::Tpl,
|
|
|
|
Source {
|
|
|
|
virtual_: true,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2022-05-16 10:53:07 -04:00
|
|
|
asg.set_fragment(sym, FragmentText::from("foo")).unwrap();
|
|
|
|
asg.set_fragment(sym2, FragmentText::from("bar")).unwrap();
|
|
|
|
asg.set_fragment(dep, FragmentText::from("baz")).unwrap();
|
|
|
|
asg.set_fragment(dep2, FragmentText::from("huh")).unwrap();
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let (_, _) = asg.add_dep_lookup(sym, dep);
|
|
|
|
let (_, _) = asg.add_dep_lookup(dep, sym);
|
|
|
|
let (_, _) = asg.add_dep_lookup(sym2, dep2);
|
|
|
|
let (_, _) = asg.add_dep_lookup(dep2, sym2);
|
2021-10-12 09:42:09 -04:00
|
|
|
|
tamer: asg: Track roots on graph
Previously, since the graph contained only identifiers, discovered roots
were stored in a separate vector and exposed to the caller. This not only
leaked details, but added complexity; this was left over from the
refactoring of the proof-of-concept linker some time ago.
This moves the root management into the ASG itself, mostly, with one item
being left over for now in the asg_builder (eligibility classifications).
There are two roots that were added automatically:
- __yield
- __worksheet
The former has been removed and is now expected to be explicitly mapped in
the return map, which is now enforced with an extern in `core/base`. This
is still special, in the sense that it is explicitly referenced by the
generated code, but there's nothing inherently special about it and I'll
continue to generalize it into oblivion in the future, such that the final
yield is just a convention.
`__worksheet` is the only symbol of type `IdentKind::Worksheet`, and so that
was generalized just as the meta and map entries were.
The goal in the future will be to have this more under the control of the
source language, and to consolodate individual roots under packages, so that
the _actual_ roots are few.
As far as the actual ASG goes: this introduces a single root node that is
used as the sole reference for reachability analysis and topological
sorting. The edges of that root node replace the vector that was removed.
DEV-11864
2022-05-17 10:42:05 -04:00
|
|
|
asg.add_root(sym_node);
|
|
|
|
|
|
|
|
let result = sort(&asg, Sections::new());
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2022-01-14 10:21:49 -05:00
|
|
|
let expected: Vec<Vec<ObjectRef>> = vec![
|
2021-10-12 09:42:09 -04:00
|
|
|
vec![dep_node.into(), sym_node.into()],
|
|
|
|
vec![dep2_node.into(), sym2_node.into()],
|
|
|
|
];
|
|
|
|
match result {
|
|
|
|
Ok(_) => panic!("sort did not detect cycle"),
|
|
|
|
Err(SortError::Cycles(scc)) => assert_eq!(expected, scc),
|
|
|
|
Err(e) => panic!("unexpected error: {}", e),
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-01-14 10:21:49 -05:00
|
|
|
fn graph_sort_no_cycle_with_edge_to_same_node() -> SortResult<()> {
|
2021-10-14 12:37:32 -04:00
|
|
|
let mut asg = make_asg();
|
2021-10-12 09:42:09 -04:00
|
|
|
|
|
|
|
let sym = "sym".intern();
|
|
|
|
let dep = "dep".intern();
|
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let sym_node = asg
|
2021-10-12 09:42:09 -04:00
|
|
|
.declare(
|
|
|
|
sym,
|
|
|
|
IdentKind::Tpl,
|
|
|
|
Source {
|
|
|
|
virtual_: true,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2022-05-16 10:53:07 -04:00
|
|
|
asg.declare(
|
|
|
|
dep,
|
|
|
|
IdentKind::Tpl,
|
|
|
|
Source {
|
|
|
|
virtual_: true,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.unwrap();
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2022-05-16 10:53:07 -04:00
|
|
|
asg.set_fragment(sym, FragmentText::from("foo")).unwrap();
|
|
|
|
asg.set_fragment(dep, FragmentText::from("bar")).unwrap();
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let (_, _) = asg.add_dep_lookup(sym, dep);
|
|
|
|
let (_, _) = asg.add_dep_lookup(sym, dep);
|
2021-10-12 09:42:09 -04:00
|
|
|
|
tamer: asg: Track roots on graph
Previously, since the graph contained only identifiers, discovered roots
were stored in a separate vector and exposed to the caller. This not only
leaked details, but added complexity; this was left over from the
refactoring of the proof-of-concept linker some time ago.
This moves the root management into the ASG itself, mostly, with one item
being left over for now in the asg_builder (eligibility classifications).
There are two roots that were added automatically:
- __yield
- __worksheet
The former has been removed and is now expected to be explicitly mapped in
the return map, which is now enforced with an extern in `core/base`. This
is still special, in the sense that it is explicitly referenced by the
generated code, but there's nothing inherently special about it and I'll
continue to generalize it into oblivion in the future, such that the final
yield is just a convention.
`__worksheet` is the only symbol of type `IdentKind::Worksheet`, and so that
was generalized just as the meta and map entries were.
The goal in the future will be to have this more under the control of the
source language, and to consolodate individual roots under packages, so that
the _actual_ roots are few.
As far as the actual ASG goes: this introduces a single root node that is
used as the sole reference for reachability analysis and topological
sorting. The edges of that root node replace the vector that was removed.
DEV-11864
2022-05-17 10:42:05 -04:00
|
|
|
asg.add_root(sym_node);
|
|
|
|
|
|
|
|
let result = sort(&asg, Sections::new());
|
2021-10-12 09:42:09 -04:00
|
|
|
|
|
|
|
match result {
|
|
|
|
Ok(_) => (),
|
|
|
|
Err(e) => panic!("unexpected error: {}", e),
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-01-14 10:21:49 -05:00
|
|
|
fn graph_sort_cycle_with_a_few_steps() -> SortResult<()> {
|
2021-10-14 12:37:32 -04:00
|
|
|
let mut asg = make_asg();
|
2021-10-12 09:42:09 -04:00
|
|
|
|
|
|
|
let sym1 = "sym1".intern();
|
|
|
|
let sym2 = "sym2".intern();
|
|
|
|
let sym3 = "sym3".intern();
|
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let sym1_node = asg
|
2021-10-12 09:42:09 -04:00
|
|
|
.declare(
|
|
|
|
sym1,
|
|
|
|
IdentKind::Tpl,
|
|
|
|
Source {
|
|
|
|
virtual_: true,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let sym2_node = asg
|
2021-10-12 09:42:09 -04:00
|
|
|
.declare(
|
|
|
|
sym2,
|
|
|
|
IdentKind::Tpl,
|
|
|
|
Source {
|
|
|
|
virtual_: true,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let sym3_node = asg
|
2021-10-12 09:42:09 -04:00
|
|
|
.declare(
|
|
|
|
sym3,
|
|
|
|
IdentKind::Tpl,
|
|
|
|
Source {
|
|
|
|
virtual_: true,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2022-05-16 10:53:07 -04:00
|
|
|
asg.set_fragment(sym1, FragmentText::from("foo")).unwrap();
|
|
|
|
asg.set_fragment(sym2, FragmentText::from("bar")).unwrap();
|
|
|
|
asg.set_fragment(sym3, FragmentText::from("baz")).unwrap();
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let (_, _) = asg.add_dep_lookup(sym1, sym2);
|
|
|
|
let (_, _) = asg.add_dep_lookup(sym2, sym3);
|
|
|
|
let (_, _) = asg.add_dep_lookup(sym3, sym1);
|
2021-10-12 09:42:09 -04:00
|
|
|
|
tamer: asg: Track roots on graph
Previously, since the graph contained only identifiers, discovered roots
were stored in a separate vector and exposed to the caller. This not only
leaked details, but added complexity; this was left over from the
refactoring of the proof-of-concept linker some time ago.
This moves the root management into the ASG itself, mostly, with one item
being left over for now in the asg_builder (eligibility classifications).
There are two roots that were added automatically:
- __yield
- __worksheet
The former has been removed and is now expected to be explicitly mapped in
the return map, which is now enforced with an extern in `core/base`. This
is still special, in the sense that it is explicitly referenced by the
generated code, but there's nothing inherently special about it and I'll
continue to generalize it into oblivion in the future, such that the final
yield is just a convention.
`__worksheet` is the only symbol of type `IdentKind::Worksheet`, and so that
was generalized just as the meta and map entries were.
The goal in the future will be to have this more under the control of the
source language, and to consolodate individual roots under packages, so that
the _actual_ roots are few.
As far as the actual ASG goes: this introduces a single root node that is
used as the sole reference for reachability analysis and topological
sorting. The edges of that root node replace the vector that was removed.
DEV-11864
2022-05-17 10:42:05 -04:00
|
|
|
asg.add_root(sym1_node);
|
|
|
|
|
|
|
|
let result = sort(&asg, Sections::new());
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2022-01-14 10:21:49 -05:00
|
|
|
let expected: Vec<Vec<ObjectRef>> =
|
2021-10-12 09:42:09 -04:00
|
|
|
vec![vec![sym3_node.into(), sym2_node.into(), sym1_node.into()]];
|
|
|
|
match result {
|
|
|
|
Ok(_) => panic!("sort did not detect cycle"),
|
|
|
|
Err(SortError::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(
|
2022-01-14 10:21:49 -05:00
|
|
|
) -> SortResult<()> {
|
2021-10-14 12:37:32 -04:00
|
|
|
let mut asg = make_asg();
|
2021-10-12 09:42:09 -04:00
|
|
|
|
|
|
|
let sym1 = "sym1".intern();
|
|
|
|
let sym2 = "sym2".intern();
|
|
|
|
let sym3 = "sym3".intern();
|
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let sym1_node = asg
|
2021-10-12 09:42:09 -04:00
|
|
|
.declare(
|
|
|
|
sym1,
|
|
|
|
IdentKind::Tpl,
|
|
|
|
Source {
|
|
|
|
virtual_: true,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let sym2_node = asg
|
2021-10-12 09:42:09 -04:00
|
|
|
.declare(
|
|
|
|
sym2,
|
|
|
|
IdentKind::Tpl,
|
|
|
|
Source {
|
|
|
|
virtual_: true,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let sym3_node = asg
|
2021-10-12 09:42:09 -04:00
|
|
|
.declare(
|
|
|
|
sym3,
|
|
|
|
IdentKind::Func(Dim::default(), SymDtype::Empty),
|
|
|
|
Source {
|
|
|
|
virtual_: true,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2022-05-16 10:53:07 -04:00
|
|
|
asg.set_fragment(sym1, FragmentText::from("foo")).unwrap();
|
|
|
|
asg.set_fragment(sym2, FragmentText::from("bar")).unwrap();
|
|
|
|
asg.set_fragment(sym3, FragmentText::from("baz")).unwrap();
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let (_, _) = asg.add_dep_lookup(sym1, sym2);
|
|
|
|
let (_, _) = asg.add_dep_lookup(sym2, sym3);
|
|
|
|
let (_, _) = asg.add_dep_lookup(sym3, sym1);
|
2021-10-12 09:42:09 -04:00
|
|
|
|
tamer: asg: Track roots on graph
Previously, since the graph contained only identifiers, discovered roots
were stored in a separate vector and exposed to the caller. This not only
leaked details, but added complexity; this was left over from the
refactoring of the proof-of-concept linker some time ago.
This moves the root management into the ASG itself, mostly, with one item
being left over for now in the asg_builder (eligibility classifications).
There are two roots that were added automatically:
- __yield
- __worksheet
The former has been removed and is now expected to be explicitly mapped in
the return map, which is now enforced with an extern in `core/base`. This
is still special, in the sense that it is explicitly referenced by the
generated code, but there's nothing inherently special about it and I'll
continue to generalize it into oblivion in the future, such that the final
yield is just a convention.
`__worksheet` is the only symbol of type `IdentKind::Worksheet`, and so that
was generalized just as the meta and map entries were.
The goal in the future will be to have this more under the control of the
source language, and to consolodate individual roots under packages, so that
the _actual_ roots are few.
As far as the actual ASG goes: this introduces a single root node that is
used as the sole reference for reachability analysis and topological
sorting. The edges of that root node replace the vector that was removed.
DEV-11864
2022-05-17 10:42:05 -04:00
|
|
|
asg.add_root(sym1_node);
|
|
|
|
|
|
|
|
let result = sort(&asg, Sections::new());
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2022-01-14 10:21:49 -05:00
|
|
|
let expected: Vec<Vec<ObjectRef>> =
|
2021-10-12 09:42:09 -04:00
|
|
|
vec![vec![sym3_node.into(), sym2_node.into(), sym1_node.into()]];
|
|
|
|
match result {
|
|
|
|
Ok(_) => panic!("sort did not detect cycle"),
|
|
|
|
Err(SortError::Cycles(scc)) => assert_eq!(expected, scc),
|
|
|
|
Err(e) => panic!("unexpected error: {}", e),
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-01-14 10:21:49 -05:00
|
|
|
fn graph_sort_cyclic_bookended_by_functions() -> SortResult<()> {
|
2021-10-14 12:37:32 -04:00
|
|
|
let mut asg = make_asg();
|
2021-10-12 09:42:09 -04:00
|
|
|
|
|
|
|
let sym1 = "sym1".intern();
|
|
|
|
let sym2 = "sym2".intern();
|
|
|
|
let sym3 = "sym3".intern();
|
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let sym1_node = asg
|
2021-10-12 09:42:09 -04:00
|
|
|
.declare(
|
|
|
|
sym1,
|
|
|
|
IdentKind::Func(Dim::default(), SymDtype::Empty),
|
|
|
|
Source {
|
|
|
|
virtual_: true,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let sym2_node = asg
|
2021-10-12 09:42:09 -04:00
|
|
|
.declare(
|
|
|
|
sym2,
|
|
|
|
IdentKind::Tpl,
|
|
|
|
Source {
|
|
|
|
virtual_: true,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let sym3_node = asg
|
2021-10-12 09:42:09 -04:00
|
|
|
.declare(
|
|
|
|
sym3,
|
|
|
|
IdentKind::Func(Dim::default(), SymDtype::Empty),
|
|
|
|
Source {
|
|
|
|
virtual_: true,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2022-05-16 10:53:07 -04:00
|
|
|
asg.set_fragment(sym1, FragmentText::from("foo")).unwrap();
|
|
|
|
asg.set_fragment(sym2, FragmentText::from("bar")).unwrap();
|
|
|
|
asg.set_fragment(sym3, FragmentText::from("baz")).unwrap();
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let (_, _) = asg.add_dep_lookup(sym1, sym2);
|
|
|
|
let (_, _) = asg.add_dep_lookup(sym2, sym3);
|
|
|
|
let (_, _) = asg.add_dep_lookup(sym3, sym1);
|
2021-10-12 09:42:09 -04:00
|
|
|
|
tamer: asg: Track roots on graph
Previously, since the graph contained only identifiers, discovered roots
were stored in a separate vector and exposed to the caller. This not only
leaked details, but added complexity; this was left over from the
refactoring of the proof-of-concept linker some time ago.
This moves the root management into the ASG itself, mostly, with one item
being left over for now in the asg_builder (eligibility classifications).
There are two roots that were added automatically:
- __yield
- __worksheet
The former has been removed and is now expected to be explicitly mapped in
the return map, which is now enforced with an extern in `core/base`. This
is still special, in the sense that it is explicitly referenced by the
generated code, but there's nothing inherently special about it and I'll
continue to generalize it into oblivion in the future, such that the final
yield is just a convention.
`__worksheet` is the only symbol of type `IdentKind::Worksheet`, and so that
was generalized just as the meta and map entries were.
The goal in the future will be to have this more under the control of the
source language, and to consolodate individual roots under packages, so that
the _actual_ roots are few.
As far as the actual ASG goes: this introduces a single root node that is
used as the sole reference for reachability analysis and topological
sorting. The edges of that root node replace the vector that was removed.
DEV-11864
2022-05-17 10:42:05 -04:00
|
|
|
asg.add_root(sym1_node);
|
|
|
|
|
|
|
|
let result = sort(&asg, Sections::new());
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2022-01-14 10:21:49 -05:00
|
|
|
let expected: Vec<Vec<ObjectRef>> =
|
2021-10-12 09:42:09 -04:00
|
|
|
vec![vec![sym3_node.into(), sym2_node.into(), sym1_node.into()]];
|
|
|
|
match result {
|
|
|
|
Ok(_) => panic!("sort did not detect cycle"),
|
|
|
|
Err(SortError::Cycles(scc)) => assert_eq!(expected, scc),
|
|
|
|
Err(e) => panic!("unexpected error: {}", e),
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-01-14 10:21:49 -05:00
|
|
|
fn graph_sort_cyclic_function_ignored() -> SortResult<()> {
|
2021-10-14 12:37:32 -04:00
|
|
|
let mut asg = make_asg();
|
2021-10-12 09:42:09 -04:00
|
|
|
|
|
|
|
let sym = "sym".intern();
|
|
|
|
let dep = "dep".intern();
|
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let sym_node = asg
|
2021-10-12 09:42:09 -04:00
|
|
|
.declare(
|
|
|
|
sym,
|
|
|
|
IdentKind::Func(Dim::default(), SymDtype::Empty),
|
|
|
|
Source {
|
|
|
|
virtual_: true,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2022-05-16 10:53:07 -04:00
|
|
|
asg.declare(
|
|
|
|
dep,
|
|
|
|
IdentKind::Func(Dim::default(), SymDtype::Empty),
|
|
|
|
Source {
|
|
|
|
virtual_: true,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.unwrap();
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2022-05-16 10:53:07 -04:00
|
|
|
asg.set_fragment(sym, FragmentText::from("foo")).unwrap();
|
|
|
|
asg.set_fragment(dep, FragmentText::from("bar")).unwrap();
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let (_, _) = asg.add_dep_lookup(sym, dep);
|
|
|
|
let (_, _) = asg.add_dep_lookup(dep, sym);
|
2021-10-12 09:42:09 -04:00
|
|
|
|
tamer: asg: Track roots on graph
Previously, since the graph contained only identifiers, discovered roots
were stored in a separate vector and exposed to the caller. This not only
leaked details, but added complexity; this was left over from the
refactoring of the proof-of-concept linker some time ago.
This moves the root management into the ASG itself, mostly, with one item
being left over for now in the asg_builder (eligibility classifications).
There are two roots that were added automatically:
- __yield
- __worksheet
The former has been removed and is now expected to be explicitly mapped in
the return map, which is now enforced with an extern in `core/base`. This
is still special, in the sense that it is explicitly referenced by the
generated code, but there's nothing inherently special about it and I'll
continue to generalize it into oblivion in the future, such that the final
yield is just a convention.
`__worksheet` is the only symbol of type `IdentKind::Worksheet`, and so that
was generalized just as the meta and map entries were.
The goal in the future will be to have this more under the control of the
source language, and to consolodate individual roots under packages, so that
the _actual_ roots are few.
As far as the actual ASG goes: this introduces a single root node that is
used as the sole reference for reachability analysis and topological
sorting. The edges of that root node replace the vector that was removed.
DEV-11864
2022-05-17 10:42:05 -04:00
|
|
|
asg.add_root(sym_node);
|
|
|
|
|
|
|
|
let result = sort(&asg, Sections::new());
|
2021-10-12 09:42:09 -04:00
|
|
|
|
|
|
|
match result {
|
|
|
|
Ok(_) => (),
|
|
|
|
Err(e) => panic!("unexpected error: {}", e),
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-01-14 10:21:49 -05:00
|
|
|
fn graph_sort_cyclic_function_is_bookended() -> SortResult<()> {
|
2021-10-14 12:37:32 -04:00
|
|
|
let mut asg = make_asg();
|
2021-10-12 09:42:09 -04:00
|
|
|
|
|
|
|
let sym1 = "sym1".intern();
|
|
|
|
let sym2 = "sym2".intern();
|
|
|
|
let sym3 = "sym3".intern();
|
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let sym1_node = asg
|
2021-10-12 09:42:09 -04:00
|
|
|
.declare(
|
|
|
|
sym1,
|
|
|
|
IdentKind::Tpl,
|
|
|
|
Source {
|
|
|
|
virtual_: true,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let sym2_node = asg
|
2021-10-12 09:42:09 -04:00
|
|
|
.declare(
|
|
|
|
sym2,
|
|
|
|
IdentKind::Func(Dim::default(), SymDtype::Empty),
|
|
|
|
Source {
|
|
|
|
virtual_: true,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let sym3_node = asg
|
2021-10-12 09:42:09 -04:00
|
|
|
.declare(
|
|
|
|
sym3,
|
|
|
|
IdentKind::Tpl,
|
|
|
|
Source {
|
|
|
|
virtual_: true,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2022-05-16 10:53:07 -04:00
|
|
|
asg.set_fragment(sym1, FragmentText::from("foo")).unwrap();
|
|
|
|
asg.set_fragment(sym2, FragmentText::from("bar")).unwrap();
|
|
|
|
asg.set_fragment(sym3, FragmentText::from("baz")).unwrap();
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let (_, _) = asg.add_dep_lookup(sym1, sym2);
|
|
|
|
let (_, _) = asg.add_dep_lookup(sym2, sym3);
|
|
|
|
let (_, _) = asg.add_dep_lookup(sym3, sym1);
|
2021-10-12 09:42:09 -04:00
|
|
|
|
tamer: asg: Track roots on graph
Previously, since the graph contained only identifiers, discovered roots
were stored in a separate vector and exposed to the caller. This not only
leaked details, but added complexity; this was left over from the
refactoring of the proof-of-concept linker some time ago.
This moves the root management into the ASG itself, mostly, with one item
being left over for now in the asg_builder (eligibility classifications).
There are two roots that were added automatically:
- __yield
- __worksheet
The former has been removed and is now expected to be explicitly mapped in
the return map, which is now enforced with an extern in `core/base`. This
is still special, in the sense that it is explicitly referenced by the
generated code, but there's nothing inherently special about it and I'll
continue to generalize it into oblivion in the future, such that the final
yield is just a convention.
`__worksheet` is the only symbol of type `IdentKind::Worksheet`, and so that
was generalized just as the meta and map entries were.
The goal in the future will be to have this more under the control of the
source language, and to consolodate individual roots under packages, so that
the _actual_ roots are few.
As far as the actual ASG goes: this introduces a single root node that is
used as the sole reference for reachability analysis and topological
sorting. The edges of that root node replace the vector that was removed.
DEV-11864
2022-05-17 10:42:05 -04:00
|
|
|
asg.add_root(sym1_node);
|
|
|
|
|
|
|
|
let result = sort(&asg, Sections::new());
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2022-01-14 10:21:49 -05:00
|
|
|
let expected: Vec<Vec<ObjectRef>> =
|
2021-10-12 09:42:09 -04:00
|
|
|
vec![vec![sym3_node.into(), sym2_node.into(), sym1_node.into()]];
|
|
|
|
match result {
|
|
|
|
Ok(_) => panic!("sort did not detect cycle"),
|
|
|
|
Err(SortError::Cycles(scc)) => assert_eq!(expected, scc),
|
|
|
|
Err(e) => panic!("unexpected error: {}", e),
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-01-14 10:21:49 -05:00
|
|
|
fn graph_sort_ignore_non_linked() -> SortResult<()> {
|
2021-10-14 12:37:32 -04:00
|
|
|
let mut asg = make_asg();
|
2021-10-12 09:42:09 -04:00
|
|
|
|
|
|
|
let sym = "sym".intern();
|
|
|
|
let dep = "dep".intern();
|
|
|
|
let ignored = "ignored".intern();
|
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let sym_node = asg
|
2021-10-12 09:42:09 -04:00
|
|
|
.declare(
|
|
|
|
sym,
|
|
|
|
IdentKind::Tpl,
|
|
|
|
Source {
|
|
|
|
virtual_: true,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2022-05-16 10:53:07 -04:00
|
|
|
asg.declare(
|
|
|
|
dep,
|
|
|
|
IdentKind::Tpl,
|
|
|
|
Source {
|
|
|
|
virtual_: true,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.unwrap();
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2022-05-16 10:53:07 -04:00
|
|
|
asg.declare(
|
|
|
|
ignored,
|
|
|
|
IdentKind::Tpl,
|
|
|
|
Source {
|
|
|
|
virtual_: true,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.unwrap();
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2022-05-16 10:53:07 -04:00
|
|
|
asg.set_fragment(sym, FragmentText::from("foo")).unwrap();
|
|
|
|
asg.set_fragment(dep, FragmentText::from("bar")).unwrap();
|
|
|
|
asg.set_fragment(ignored, FragmentText::from("baz"))
|
2021-10-12 09:42:09 -04:00
|
|
|
.unwrap();
|
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let (_, _) = asg.add_dep_lookup(sym, dep);
|
|
|
|
let (_, _) = asg.add_dep_lookup(ignored, sym);
|
2021-10-12 09:42:09 -04:00
|
|
|
|
tamer: asg: Track roots on graph
Previously, since the graph contained only identifiers, discovered roots
were stored in a separate vector and exposed to the caller. This not only
leaked details, but added complexity; this was left over from the
refactoring of the proof-of-concept linker some time ago.
This moves the root management into the ASG itself, mostly, with one item
being left over for now in the asg_builder (eligibility classifications).
There are two roots that were added automatically:
- __yield
- __worksheet
The former has been removed and is now expected to be explicitly mapped in
the return map, which is now enforced with an extern in `core/base`. This
is still special, in the sense that it is explicitly referenced by the
generated code, but there's nothing inherently special about it and I'll
continue to generalize it into oblivion in the future, such that the final
yield is just a convention.
`__worksheet` is the only symbol of type `IdentKind::Worksheet`, and so that
was generalized just as the meta and map entries were.
The goal in the future will be to have this more under the control of the
source language, and to consolodate individual roots under packages, so that
the _actual_ roots are few.
As far as the actual ASG goes: this introduces a single root node that is
used as the sole reference for reachability analysis and topological
sorting. The edges of that root node replace the vector that was removed.
DEV-11864
2022-05-17 10:42:05 -04:00
|
|
|
asg.add_root(sym_node);
|
|
|
|
|
|
|
|
let result = sort(&asg, Sections::new());
|
2021-10-12 09:42:09 -04:00
|
|
|
|
|
|
|
match result {
|
|
|
|
Ok(_) => (),
|
|
|
|
Err(e) => panic!("unexpected error: {}", e),
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|