tamer: asg::graph::object::ObjectIndexRefined: New narrowing type

The provided documentation provides rationale, and the use case is the
ontree change.  I was uncomfortable without the exhaustive match, and I was
further annoyed by the lack of easy `ObjectIndex` narrowing.

DEV-13163
main
Mike Gerwitz 2023-07-18 09:36:41 -04:00
parent 5a301c1548
commit 507669cb30
3 changed files with 69 additions and 16 deletions

View File

@ -243,6 +243,20 @@ macro_rules! object_gen {
}
}
/// Narrowed [`ObjectIndex`] types for each [`ObjectKind`].
///
/// This allows for converting a dynamic
/// [`ObjectIndex<Object>`](ObjectIndex) into a statically known
/// [`ObjectKind`],
/// while also providing the ability to exhaustively match
/// against all such possibilities.
#[derive(Debug, PartialEq, Eq)]
pub enum ObjectIndexRefined {
$(
$kind(ObjectIndex<$kind>),
)+
}
/// The collection of potential objects of [`Object`].
pub trait ObjectInner {
$(type $kind;)+

View File

@ -22,8 +22,8 @@
//! See (parent module)[super] for more information.
use super::{
Doc, Expr, Ident, Meta, Object, ObjectIndex, ObjectKind, OiPairObjectInner,
Pkg, Root,
Doc, Expr, Ident, Meta, Object, ObjectIndex, ObjectIndexRefined,
ObjectKind, OiPairObjectInner, Pkg, Root,
};
use crate::{
asg::{graph::object::Tpl, Asg},
@ -270,12 +270,47 @@ impl<S> DynObjectRel<S, ObjectIndex<Object>> {
/// Attempt to narrow the target into the [`ObjectRel`] of `O`.
///
/// See [`ObjectRelatable::new_rel_dyn`] for more information.
///
/// To exhaustively match against all possible [`ObjectKind`]s,
/// see [`Self::refine_target`].
pub fn narrow_target<O: ObjectKind + ObjectRelatable>(
&self,
) -> Option<O::Rel> {
O::new_rel_dyn(self.target_ty(), *self.target())
}
/// Refine the target [`ObjectIndex<Object>`](ObjectIndex) into
/// [`ObjectIndexRefined`] such that the returned variant has a
/// narrowed [`ObjectIndex<O>`] type.
///
/// This allows converting a dynamic [`ObjectIndex`] into a statically
/// known type where `O` is derived from [`Self::target_ty`].
/// This avoids having to manually match on [`Self::target_ty`] and then
/// use [`ObjectIndex::must_narrow_into`] on the matching
/// [`ObjectKind`],
/// since there is a risk of those getting out of sync.
///
/// In contrast to [`Self::narrow_target`],
/// where the caller must specify the expected [`ObjectKind`],
/// this allows for exhaustively matching against all possible objects.
pub fn refine_target(&self) -> ObjectIndexRefined {
macro_rules! narrow_each_rel_ty {
( $($var:ident),+ ) => {
match self.target_ty() {
$(
ObjectRelTy::$var => {
ObjectIndexRefined::$var(
self.target().must_narrow_into()
)
}
)+
}
}
}
narrow_each_rel_ty!(Root, Pkg, Ident, Expr, Tpl, Meta, Doc)
}
/// Attempt to convert [`Self`] into an [`ObjectIndex`] with an
/// [`ObjectKind`] of type `O`.
///

View File

@ -324,6 +324,8 @@ impl Token for TreeWalkRel {
impl parse::Object for TreeWalkRel {}
mod order {
use crate::asg::graph::object::ObjectIndexRefined;
use super::*;
/// Emit edges in the same order that they were added to the graph.
@ -437,14 +439,13 @@ mod order {
depth: Depth,
stack: &mut Vec<(DynObjectRel, Depth)>,
) {
use ObjectTy::*;
// We start by adding edges to the stack in natural order,
// remembering the original stack offset so that we can sort
// just the portion that we added.
let offset = stack.len();
NaturalTreeEdgeOrder::push_edges_of(asg, rel, depth, stack);
use ObjectTy::*;
match rel.target_ty() {
// Templates require partial ordering into a header and a body.
Tpl => {
@ -485,20 +486,23 @@ mod order {
// definitions,
// which are not all that common relative to
// everything else).
use ObjectIndexRefined::*;
part.sort_by_cached_key(|(child_rel, _)| {
if let Some(ident) =
child_rel.filter_into_target::<object::Ident>()
{
// This is the (comparatively) expensive lookup,
// requiring a small graph traversal.
match ident.definition::<object::Meta>(asg) {
Some(_) => TplOrder::Param,
None => TplOrder::Body,
match child_rel.refine_target() {
Ident(oi_ident) => {
// This is the (comparatively) expensive lookup,
// requiring a small graph traversal.
match oi_ident.definition::<object::Meta>(asg) {
Some(_) => TplOrder::Param,
None => TplOrder::Body,
}
}
Doc(_) => TplOrder::TplDesc,
Root(_) | Pkg(_) | Expr(_) | Tpl(_) | Meta(_) => {
TplOrder::Body
}
} else if child_rel.target_ty() == Doc {
TplOrder::TplDesc
} else {
TplOrder::Body
}
});
}