From 507669cb30a0d48802a45c668c2f7d5dd03c0083 Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Tue, 18 Jul 2023 09:36:41 -0400 Subject: [PATCH] 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 --- tamer/src/asg/graph/object.rs | 14 +++++++++++ tamer/src/asg/graph/object/rel.rs | 39 +++++++++++++++++++++++++++-- tamer/src/asg/graph/visit/ontree.rs | 32 ++++++++++++----------- 3 files changed, 69 insertions(+), 16 deletions(-) diff --git a/tamer/src/asg/graph/object.rs b/tamer/src/asg/graph/object.rs index 8c59073f..e64677ad 100644 --- a/tamer/src/asg/graph/object.rs +++ b/tamer/src/asg/graph/object.rs @@ -243,6 +243,20 @@ macro_rules! object_gen { } } + /// Narrowed [`ObjectIndex`] types for each [`ObjectKind`]. + /// + /// This allows for converting a dynamic + /// [`ObjectIndex`](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;)+ diff --git a/tamer/src/asg/graph/object/rel.rs b/tamer/src/asg/graph/object/rel.rs index b6b2062f..01495de5 100644 --- a/tamer/src/asg/graph/object/rel.rs +++ b/tamer/src/asg/graph/object/rel.rs @@ -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 DynObjectRel> { /// 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( &self, ) -> Option { O::new_rel_dyn(self.target_ty(), *self.target()) } + /// Refine the target [`ObjectIndex`](ObjectIndex) into + /// [`ObjectIndexRefined`] such that the returned variant has a + /// narrowed [`ObjectIndex`] 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`. /// diff --git a/tamer/src/asg/graph/visit/ontree.rs b/tamer/src/asg/graph/visit/ontree.rs index b5006ec3..e8eddba5 100644 --- a/tamer/src/asg/graph/visit/ontree.rs +++ b/tamer/src/asg/graph/visit/ontree.rs @@ -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::() - { - // This is the (comparatively) expensive lookup, - // requiring a small graph traversal. - match ident.definition::(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::(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 } }); }