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.
|
|
|
|
|
2022-12-16 15:02:57 -05:00
|
|
|
use std::iter::once;
|
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
use super::section::{SectionsError, XmleSections};
|
|
|
|
use crate::{
|
2022-12-22 14:24:40 -05:00
|
|
|
asg::{Asg, Ident, IdentKind, Object, ObjectIndex},
|
2022-12-16 15:02:57 -05:00
|
|
|
diagnose::{Annotate, Diagnostic},
|
tamer: Initial concept for AIR/ASG Expr
This begins to place expressions on the graph---something that I've been
thinking about for a couple of years now, so it's interesting to finally be
doing it.
This is going to evolve; I want to get some things committed so that it's
clear how I'm moving forward. The ASG makes things a bit awkward for a
number of reasons:
1. I'm dealing with older code where I had a different model of doing
things;
2. It's mutable, rather than the mostly-functional lowering pipeline;
3. We're dealing with an aggregate ever-evolving blob of data (the graph)
rather than a stream of tokens; and
4. We don't have as many type guarantees.
I've shown with the lowering pipeline that I'm able to take a mutable
reference and convert it into something that's both functional and
performant, where I remove it from its container (an `Option`), create a new
version of it, and place it back. Rust is able to optimize away the memcpys
and such and just directly manipulate the underlying value, which is often a
register with all of the inlining.
_But_ this is a different scenario now. The lowering pipeline has a narrow
context. The graph has to keep hitting memory. So we'll see how this
goes. But it's most important to get this working and measure how it
performs; I'm not trying to prematurely optimize. My attempts right now are
for the way that I wish to develop.
Speaking to #4 above, it also sucks that I'm not able to type the
relationships between nodes on the graph. Rather, it's not that I _can't_,
but a project to created a typed graph library is beyond the scope of this
work and would take far too much time. I'll leave that to a personal,
non-work project. Instead, I'm going to have to narrow the type any time
the graph is accessed. And while that sucks, I'm going to do my best to
encapsulate those details to make it as seamless as possible API-wise. The
performance hit of performing the narrowing I'm hoping will be very small
relative to all the business logic going on (a single cache miss is bound to
be far more expensive than many narrowings which are just integer
comparisons and branching)...but we'll see. Introducing branching sucks,
but branch prediction is pretty damn good in modern CPUs.
DEV-13160
2022-12-21 16:47:04 -05:00
|
|
|
diagnostic_unreachable,
|
2022-12-16 15:02:57 -05:00
|
|
|
fmt::{
|
|
|
|
AndConjList, DisplayWrapper, JoinListWrap, ListDisplayWrapper, Raw,
|
|
|
|
TtQuote,
|
|
|
|
},
|
|
|
|
parse::util::SPair,
|
2022-12-22 14:24:40 -05:00
|
|
|
span::UNKNOWN_SPAN,
|
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) {
|
2022-12-22 14:24:40 -05:00
|
|
|
// TODO: `new` really ought to be private to the `asg` module,
|
|
|
|
// so let's work on fully encapsulating Petgraph.
|
|
|
|
let oi = ObjectIndex::<Object>::new(index, UNKNOWN_SPAN);
|
|
|
|
|
|
|
|
match asg.get(oi).expect("missing object") {
|
2022-05-19 12:48:43 -04:00
|
|
|
Object::Root => (),
|
|
|
|
Object::Ident(ident) => dest.push(ident)?,
|
tamer: Initial concept for AIR/ASG Expr
This begins to place expressions on the graph---something that I've been
thinking about for a couple of years now, so it's interesting to finally be
doing it.
This is going to evolve; I want to get some things committed so that it's
clear how I'm moving forward. The ASG makes things a bit awkward for a
number of reasons:
1. I'm dealing with older code where I had a different model of doing
things;
2. It's mutable, rather than the mostly-functional lowering pipeline;
3. We're dealing with an aggregate ever-evolving blob of data (the graph)
rather than a stream of tokens; and
4. We don't have as many type guarantees.
I've shown with the lowering pipeline that I'm able to take a mutable
reference and convert it into something that's both functional and
performant, where I remove it from its container (an `Option`), create a new
version of it, and place it back. Rust is able to optimize away the memcpys
and such and just directly manipulate the underlying value, which is often a
register with all of the inlining.
_But_ this is a different scenario now. The lowering pipeline has a narrow
context. The graph has to keep hitting memory. So we'll see how this
goes. But it's most important to get this working and measure how it
performs; I'm not trying to prematurely optimize. My attempts right now are
for the way that I wish to develop.
Speaking to #4 above, it also sucks that I'm not able to type the
relationships between nodes on the graph. Rather, it's not that I _can't_,
but a project to created a typed graph library is beyond the scope of this
work and would take far too much time. I'll leave that to a personal,
non-work project. Instead, I'm going to have to narrow the type any time
the graph is accessed. And while that sucks, I'm going to do my best to
encapsulate those details to make it as seamless as possible API-wise. The
performance hit of performing the narrowing I'm hoping will be very small
relative to all the business logic going on (a single cache miss is bound to
be far more expensive than many narrowings which are just integer
comparisons and branching)...but we'll see. Introducing branching sucks,
but branch prediction is pretty damn good in modern CPUs.
DEV-13160
2022-12-21 16:47:04 -05:00
|
|
|
|
|
|
|
Object::Expr(expr) => diagnostic_unreachable!(
|
|
|
|
expr.internal_error(
|
|
|
|
"this expression should not be present on the graph"
|
|
|
|
)
|
|
|
|
.into(),
|
|
|
|
"linker graph should not contain expressions",
|
|
|
|
),
|
2022-05-19 12:48:43 -04:00
|
|
|
}
|
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-19 11:17:04 -04:00
|
|
|
fn get_ident<'a, S>(depgraph: &'a Asg, name: S) -> &'a Ident
|
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);
|
|
|
|
|
2022-12-16 15:02:57 -05:00
|
|
|
let cycles: Vec<Vec<SPair>> = sccs
|
2021-10-12 09:42:09 -04:00
|
|
|
.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;
|
|
|
|
}
|
|
|
|
|
2022-12-22 14:24:40 -05:00
|
|
|
let is_all_funcs = scc.iter().all(|ni| {
|
|
|
|
let oi = ObjectIndex::<Object>::new(*ni, UNKNOWN_SPAN);
|
|
|
|
let ident = asg.get(oi).expect("missing node");
|
2022-05-19 12:31:37 -04:00
|
|
|
matches!(
|
|
|
|
ident.as_ident_ref().and_then(Ident::kind),
|
|
|
|
Some(IdentKind::Func(..))
|
|
|
|
)
|
2021-10-12 09:42:09 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
if is_all_funcs {
|
|
|
|
None
|
|
|
|
} else {
|
2022-12-16 15:02:57 -05:00
|
|
|
// TODO: ...these aren't references, they're the actual
|
|
|
|
// identifiers,
|
|
|
|
// so the diagnostic message isn't that great of a guide
|
|
|
|
// yet!
|
|
|
|
// Use reference spans once they're available.
|
|
|
|
let cycles = scc
|
|
|
|
.iter()
|
2022-12-22 14:24:40 -05:00
|
|
|
.map(|ni| ObjectIndex::<Object>::new(*ni, UNKNOWN_SPAN))
|
|
|
|
.filter_map(|oi| {
|
|
|
|
asg.get(oi).unwrap().as_ident_ref().map(Ident::name)
|
2022-12-16 15:02:57 -05:00
|
|
|
})
|
|
|
|
.collect();
|
2021-10-12 09:42:09 -04:00
|
|
|
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-12-16 15:02:57 -05:00
|
|
|
Cycles(Vec<Vec<SPair>>),
|
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 {
|
2022-12-16 15:02:57 -05:00
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
2021-10-12 09:42:09 -04:00
|
|
|
match self {
|
2022-12-16 15:02:57 -05:00
|
|
|
Self::SectionsError(err) => err.fmt(f),
|
|
|
|
Self::Cycles(cycles) => {
|
|
|
|
let cycles_fmt = cycles
|
|
|
|
.iter()
|
|
|
|
.map(|cycle| {
|
|
|
|
let cycle_fmt = cycle
|
|
|
|
.iter()
|
|
|
|
.rev()
|
|
|
|
.chain(once(cycle.last().unwrap()))
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
// TODO: Wrappers ought to support nested lists.
|
|
|
|
JoinListWrap::<" -> ", Raw>::wrap(&cycle_fmt)
|
|
|
|
.to_string()
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"circular dependencies: {}",
|
|
|
|
AndConjList::<TtQuote>::wrap(&cycles_fmt),
|
|
|
|
)
|
|
|
|
}
|
2021-10-12 09:42:09 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-15 12:07:58 -05:00
|
|
|
impl Diagnostic for SortError {
|
|
|
|
fn describe(&self) -> Vec<crate::diagnose::AnnotatedSpan> {
|
|
|
|
use SortError::*;
|
|
|
|
|
|
|
|
match self {
|
|
|
|
SectionsError(e) => e.describe(),
|
2022-12-16 15:02:57 -05:00
|
|
|
|
|
|
|
// TODO: In future it'd be far less confusing to have a single
|
|
|
|
// error per cycle.
|
|
|
|
Cycles(cycles) => cycles
|
|
|
|
.iter()
|
|
|
|
.map(|cycle| {
|
|
|
|
let n = cycle.len();
|
|
|
|
let ident = cycle.last().unwrap();
|
|
|
|
|
|
|
|
cycle.iter().rev().enumerate().map(|(i, spair)| {
|
|
|
|
spair.note(match i {
|
|
|
|
0 => format!(
|
|
|
|
"[0/{n}] the cycle begins here, depending on..."
|
|
|
|
),
|
|
|
|
_ => format!(
|
|
|
|
"[{i}/{n}] ...this identifier, which depends on..."
|
|
|
|
)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.chain(once(ident.error(format!(
|
|
|
|
"[{n}/{n}] ...the first identifier once again, \
|
|
|
|
creating the cycle"
|
|
|
|
))))
|
|
|
|
.chain(vec![
|
|
|
|
ident.help(format!(
|
|
|
|
"the value of {} cannot be computed because its",
|
|
|
|
TtQuote::wrap(ident),
|
|
|
|
)),
|
|
|
|
ident.help(
|
|
|
|
" definition requires first computing itself.",
|
|
|
|
),
|
|
|
|
ident.help(
|
|
|
|
"in the future the above output will emphasize the "
|
|
|
|
),
|
|
|
|
ident.help(
|
|
|
|
" references to the identifiers rather than their "
|
|
|
|
),
|
|
|
|
ident.help(
|
|
|
|
" definition sites."
|
|
|
|
)
|
|
|
|
])
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
})
|
|
|
|
.flatten()
|
|
|
|
.collect(),
|
2022-12-15 12:07:58 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-12 09:42:09 -04:00
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::*;
|
|
|
|
use crate::{
|
2022-05-19 11:17:04 -04:00
|
|
|
asg::{FragmentText, Ident, Source},
|
2021-10-14 12:37:32 -04:00
|
|
|
ld::xmle::{section::PushResult, Sections},
|
2022-05-19 10:09:49 -04:00
|
|
|
num::{Dim, Dtype},
|
2022-12-15 12:07:58 -05:00
|
|
|
parse::util::SPair,
|
|
|
|
span::dummy::*,
|
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
|
|
|
{
|
2022-12-15 12:07:58 -05:00
|
|
|
let sym = SPair(st::L_MAP_UUUHEAD.into(), S1);
|
2022-05-16 10:53:07 -04:00
|
|
|
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
|
|
|
{
|
2022-12-15 12:07:58 -05:00
|
|
|
let sym = SPair(st::L_MAP_UUUTAIL.into(), S2);
|
2022-05-16 10:53:07 -04:00
|
|
|
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
|
|
|
{
|
2022-12-15 12:07:58 -05:00
|
|
|
let sym = SPair(st::L_RETMAP_UUUHEAD.into(), S3);
|
2022-05-16 10:53:07 -04:00
|
|
|
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
|
|
|
{
|
2022-12-15 12:07:58 -05:00
|
|
|
let sym = SPair(st::L_RETMAP_UUUTAIL.into(), S4);
|
2022-05-16 10:53:07 -04:00
|
|
|
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> {
|
2022-05-19 11:17:04 -04:00
|
|
|
pushed: Vec<&'a Ident>,
|
2021-10-14 12:37:32 -04:00
|
|
|
}
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
impl<'a> XmleSections<'a> for StubSections<'a> {
|
2022-05-19 11:17:04 -04:00
|
|
|
fn push(&mut self, ident: &'a Ident) -> PushResult {
|
2021-10-14 12:37:32 -04:00
|
|
|
self.pushed.push(ident);
|
|
|
|
Ok(())
|
|
|
|
}
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2022-05-19 11:17:04 -04:00
|
|
|
fn take_deps(&mut self) -> Vec<&'a Ident> {
|
2021-10-14 12:37:32 -04:00
|
|
|
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
|
2022-12-15 12:07:58 -05:00
|
|
|
.declare(
|
|
|
|
SPair("adep".into(), S1),
|
|
|
|
IdentKind::Meta,
|
|
|
|
Default::default(),
|
|
|
|
)
|
2021-10-14 12:37:32 -04:00
|
|
|
.unwrap();
|
|
|
|
let a = asg
|
2022-12-15 12:07:58 -05:00
|
|
|
.declare(SPair("a".into(), S2), IdentKind::Meta, Default::default())
|
2021-10-14 12:37:32 -04:00
|
|
|
.unwrap();
|
|
|
|
let adepdep = asg
|
2022-12-15 12:07:58 -05:00
|
|
|
.declare(
|
|
|
|
SPair("adepdep".into(), S3),
|
|
|
|
IdentKind::Meta,
|
|
|
|
Default::default(),
|
|
|
|
)
|
2021-10-14 12:37:32 -04:00
|
|
|
.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())
|
2022-05-19 12:31:37 -04:00
|
|
|
.and_then(|id| asg.get_ident(id)),
|
2021-10-14 12:37:32 -04:00
|
|
|
asg.lookup(st::L_RETMAP_UUUHEAD.into())
|
2022-05-19 12:31:37 -04:00
|
|
|
.and_then(|id| asg.get_ident(id)),
|
2021-10-14 12:37:32 -04:00
|
|
|
// Post-order
|
2022-05-19 12:31:37 -04:00
|
|
|
asg.get_ident(adepdep),
|
|
|
|
asg.get_ident(adep),
|
|
|
|
asg.get_ident(a),
|
2021-10-14 12:37:32 -04:00
|
|
|
// Static tail
|
|
|
|
asg.lookup(st::L_MAP_UUUTAIL.into())
|
2022-05-19 12:31:37 -04:00
|
|
|
.and_then(|id| asg.get_ident(id)),
|
2021-10-14 12:37:32 -04:00
|
|
|
asg.lookup(st::L_RETMAP_UUUTAIL.into())
|
2022-05-19 12:31:37 -04:00
|
|
|
.and_then(|id| asg.get_ident(id)),
|
2021-10-14 12:37:32 -04:00
|
|
|
]
|
|
|
|
.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
|
|
|
|
2022-12-15 12:07:58 -05:00
|
|
|
let sym = SPair("sym".into(), S1);
|
|
|
|
let dep = SPair("dep".into(), S2);
|
2021-10-12 09:42:09 -04:00
|
|
|
|
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
|
|
|
|
2022-12-15 12:07:58 -05:00
|
|
|
let sym = SPair("sym".into(), S1);
|
|
|
|
let dep = SPair("dep".into(), S2);
|
2021-10-12 09:42:09 -04:00
|
|
|
|
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
|
|
|
|
2022-12-15 12:07:58 -05:00
|
|
|
let sym = SPair("sym".into(), S1);
|
|
|
|
let dep = SPair("dep".into(), S2);
|
2021-10-12 09:42:09 -04:00
|
|
|
|
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-12-16 15:02:57 -05:00
|
|
|
let expected = vec![[dep_node, sym_node]
|
|
|
|
.into_iter()
|
2022-12-22 16:32:21 -05:00
|
|
|
.map(|o| asg.get(o).unwrap().name())
|
2022-12-16 15:02:57 -05:00
|
|
|
.collect::<Vec<_>>()];
|
|
|
|
|
2021-10-12 09:42:09 -04:00
|
|
|
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
|
|
|
|
2022-12-15 12:07:58 -05:00
|
|
|
let sym = SPair("sym".into(), S1);
|
|
|
|
let sym2 = SPair("sym2".into(), S2);
|
|
|
|
let dep = SPair("dep".into(), S3);
|
|
|
|
let dep2 = SPair("dep2".into(), S4);
|
2021-10-12 09:42:09 -04:00
|
|
|
|
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-12-16 15:02:57 -05:00
|
|
|
let expected = [[dep_node, sym_node], [dep2_node, sym2_node]]
|
|
|
|
.into_iter()
|
|
|
|
.map(|cycle| {
|
|
|
|
cycle
|
|
|
|
.into_iter()
|
2022-12-22 16:32:21 -05:00
|
|
|
.map(|o| asg.get(o).unwrap().name())
|
2022-12-16 15:02:57 -05:00
|
|
|
.collect::<Vec<_>>()
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
2021-10-12 09:42:09 -04:00
|
|
|
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
|
|
|
|
2022-12-15 12:07:58 -05:00
|
|
|
let sym = SPair("sym".into(), S1);
|
|
|
|
let dep = SPair("dep".into(), S2);
|
2021-10-12 09:42:09 -04:00
|
|
|
|
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
|
|
|
|
2022-12-15 12:07:58 -05:00
|
|
|
let sym1 = SPair("sym1".into(), S1);
|
|
|
|
let sym2 = SPair("sym2".into(), S2);
|
|
|
|
let sym3 = SPair("sym3".into(), S3);
|
2021-10-12 09:42:09 -04:00
|
|
|
|
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-12-16 15:02:57 -05:00
|
|
|
let expected = vec![[sym3_node, sym2_node, sym1_node]
|
|
|
|
.into_iter()
|
2022-12-22 16:32:21 -05:00
|
|
|
.map(|o| asg.get(o).unwrap().name())
|
2022-12-16 15:02:57 -05:00
|
|
|
.collect::<Vec<_>>()];
|
|
|
|
|
2021-10-12 09:42:09 -04:00
|
|
|
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
|
|
|
|
2022-12-15 12:07:58 -05:00
|
|
|
let sym1 = SPair("sym1".into(), S1);
|
|
|
|
let sym2 = SPair("sym2".into(), S2);
|
|
|
|
let sym3 = SPair("sym3".into(), S3);
|
2021-10-12 09:42:09 -04:00
|
|
|
|
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,
|
2022-05-19 10:09:49 -04:00
|
|
|
IdentKind::Func(Dim::Scalar, Dtype::Empty),
|
2021-10-12 09:42:09 -04:00
|
|
|
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-12-16 15:02:57 -05:00
|
|
|
let expected = vec![[sym3_node, sym2_node, sym1_node]
|
|
|
|
.into_iter()
|
2022-12-22 16:32:21 -05:00
|
|
|
.map(|o| asg.get(o).unwrap().name())
|
2022-12-16 15:02:57 -05:00
|
|
|
.collect::<Vec<_>>()];
|
|
|
|
|
2021-10-12 09:42:09 -04:00
|
|
|
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
|
|
|
|
2022-12-15 12:07:58 -05:00
|
|
|
let sym1 = SPair("sym1".into(), S1);
|
|
|
|
let sym2 = SPair("sym2".into(), S2);
|
|
|
|
let sym3 = SPair("sym3".into(), S3);
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let sym1_node = asg
|
2021-10-12 09:42:09 -04:00
|
|
|
.declare(
|
|
|
|
sym1,
|
2022-05-19 10:09:49 -04:00
|
|
|
IdentKind::Func(Dim::Scalar, Dtype::Empty),
|
2021-10-12 09:42:09 -04:00
|
|
|
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,
|
2022-05-19 10:09:49 -04:00
|
|
|
IdentKind::Func(Dim::Scalar, Dtype::Empty),
|
2021-10-12 09:42:09 -04:00
|
|
|
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-12-16 15:02:57 -05:00
|
|
|
let expected = vec![[sym3_node, sym2_node, sym1_node]
|
|
|
|
.into_iter()
|
2022-12-22 16:32:21 -05:00
|
|
|
.map(|o| asg.get(o).unwrap().name())
|
2022-12-16 15:02:57 -05:00
|
|
|
.collect::<Vec<_>>()];
|
|
|
|
|
2021-10-12 09:42:09 -04:00
|
|
|
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
|
|
|
|
2022-12-15 12:07:58 -05:00
|
|
|
let sym = SPair("sym".into(), S1);
|
|
|
|
let dep = SPair("dep".into(), S2);
|
2021-10-12 09:42:09 -04:00
|
|
|
|
2021-10-14 12:37:32 -04:00
|
|
|
let sym_node = asg
|
2021-10-12 09:42:09 -04:00
|
|
|
.declare(
|
|
|
|
sym,
|
2022-05-19 10:09:49 -04:00
|
|
|
IdentKind::Func(Dim::Scalar, Dtype::Empty),
|
2021-10-12 09:42:09 -04:00
|
|
|
Source {
|
|
|
|
virtual_: true,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2022-05-16 10:53:07 -04:00
|
|
|
asg.declare(
|
|
|
|
dep,
|
2022-05-19 10:09:49 -04:00
|
|
|
IdentKind::Func(Dim::Scalar, Dtype::Empty),
|
2022-05-16 10:53:07 -04:00
|
|
|
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
|
|
|
|
2022-12-15 12:07:58 -05:00
|
|
|
let sym1 = SPair("sym1".into(), S1);
|
|
|
|
let sym2 = SPair("sym2".into(), S2);
|
|
|
|
let sym3 = SPair("sym3".into(), S3);
|
2021-10-12 09:42:09 -04:00
|
|
|
|
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,
|
2022-05-19 10:09:49 -04:00
|
|
|
IdentKind::Func(Dim::Scalar, Dtype::Empty),
|
2021-10-12 09:42:09 -04:00
|
|
|
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-12-16 15:02:57 -05:00
|
|
|
let expected = vec![[sym3_node, sym2_node, sym1_node]
|
|
|
|
.into_iter()
|
2022-12-22 16:32:21 -05:00
|
|
|
.map(|o| asg.get(o).unwrap().name())
|
2022-12-16 15:02:57 -05:00
|
|
|
.collect::<Vec<_>>()];
|
|
|
|
|
2021-10-12 09:42:09 -04:00
|
|
|
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
|
|
|
|
2022-12-15 12:07:58 -05:00
|
|
|
let sym = SPair("sym".into(), S1);
|
|
|
|
let dep = SPair("dep".into(), S2);
|
|
|
|
let ignored = SPair("ignored".into(), S3);
|
2021-10-12 09:42:09 -04:00
|
|
|
|
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(())
|
|
|
|
}
|
|
|
|
}
|