From 9f74c0fc925f15b08b76a1245aa8919ce3f104bf Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Tue, 1 Aug 2023 01:32:28 -0400 Subject: [PATCH] tamer: asg::graph: Strict static enforcement of tree/cross edge spans We are now able to have confidence that the graph is properly constructed with or without a reference span depending on whether the edge is a tree or cross edge. Consequently, the span is now a reliable indicator of whether an edge is tree or cross in _all_ cases, not just in dynamic ones. In fact, this did catch a couple cases where the spans were _not_ properly applied. This will in turn give me confidence moving forward with static analysis based on the graph (and edge hooks). I could go further than this by introducing a new span type in place of `Option`, which would also allow me to get rid of having two methods on `Asg`, but I want to move on for now; this can be cleaned up more later on. It's also worth noting that this explicit method-based distinction between edge types also means that each caller will carefully consider how the operation affects the graph. Previously, that consideration was framed very differently: "do I need a contextual span or not?". That's not the right question to ask. DEV-13163 --- tamer/src/asg/air.rs | 6 +- tamer/src/asg/air/expr.rs | 4 +- tamer/src/asg/air/opaque.rs | 2 +- tamer/src/asg/graph.rs | 100 +++++++++++++++----- tamer/src/asg/graph/object.rs | 111 ++++++++++++++++------ tamer/src/asg/graph/object/expr.rs | 10 +- tamer/src/asg/graph/object/ident.rs | 14 +-- tamer/src/asg/graph/object/meta.rs | 6 +- tamer/src/asg/graph/object/pkg.rs | 4 +- tamer/src/asg/graph/object/rel.rs | 142 +++++++++++++++++++++++++++- tamer/src/asg/graph/object/root.rs | 3 +- tamer/src/asg/graph/object/tpl.rs | 14 +-- 12 files changed, 330 insertions(+), 86 deletions(-) diff --git a/tamer/src/asg/air.rs b/tamer/src/asg/air.rs index ee567b8e..7a670b93 100644 --- a/tamer/src/asg/air.rs +++ b/tamer/src/asg/air.rs @@ -863,7 +863,7 @@ impl AirAggregateCtx { }, )?; - oi_pkg.root(&mut self.asg)?; + oi_pkg.root_tree(&mut self.asg)?; self.ooi_pkg.replace(oi_pkg); Ok(oi_pkg) @@ -921,7 +921,7 @@ impl AirAggregateCtx { /// A value of [`None`] indicates that expressions are not permitted to /// dangle in the current context /// (and so must be identified). - fn dangling_expr_oi(&self) -> Option> { + fn dangling_expr_oi(&self) -> Option> { use AirAggregate::*; self.stack.iter().rev().find_map(|st| match st { @@ -963,7 +963,7 @@ impl AirAggregateCtx { /// /// A value of [`None`] indicates that template expansion is not /// permitted in this current context. - fn expansion_oi(&self) -> Option> { + fn expansion_oi(&self) -> Option> { use AirAggregate::*; self.stack.iter().rev().find_map(|st| match st { diff --git a/tamer/src/asg/air/expr.rs b/tamer/src/asg/air/expr.rs index 5759e281..ca87a9eb 100644 --- a/tamer/src/asg/air/expr.rs +++ b/tamer/src/asg/air/expr.rs @@ -30,7 +30,7 @@ use super::{ AirAggregate, AirAggregateCtx, }; use crate::{ - asg::{graph::object::ObjectIndexTo, ObjectKind}, + asg::{graph::object::ObjectIndexToTree, ObjectKind}, f::Map, parse::prelude::*, }; @@ -204,7 +204,7 @@ impl AirExprAggregate { /// then an [`AsgError::DanglingExpr`] will be returned. fn hold_dangling( asg: &mut Asg, - oi_root: Option>, + oi_root: Option>, oi_expr: ObjectIndex, ) -> Result<(), AsgError> { let oi_container = oi_root diff --git a/tamer/src/asg/air/opaque.rs b/tamer/src/asg/air/opaque.rs index 7a4b631f..643c8b4c 100644 --- a/tamer/src/asg/air/opaque.rs +++ b/tamer/src/asg/air/opaque.rs @@ -88,7 +88,7 @@ impl ParseState for AirOpaqueAggregate { (Ready, IdentRoot(name)) => ctx .lookup_lexical_or_missing(name) - .root(ctx.asg_mut()) + .root_cross(ctx.asg_mut()) .map(|_| ()) .transition(Ready), } diff --git a/tamer/src/asg/graph.rs b/tamer/src/asg/graph.rs index ed74b253..c6023f7b 100644 --- a/tamer/src/asg/graph.rs +++ b/tamer/src/asg/graph.rs @@ -22,8 +22,8 @@ //! ![Visualization of ASG ontology](../ontviz.svg) use self::object::{ - DynObjectRel, ObjectIndexRelTo, ObjectRelFrom, ObjectRelTy, - ObjectRelatable, Root, + DynObjectRel, ObjectIndexCrossRelTo, ObjectIndexRelTo, + ObjectIndexTreeRelTo, ObjectRelFrom, ObjectRelTy, ObjectRelatable, Root, }; use super::{AsgError, Object, ObjectIndex, ObjectKind}; @@ -190,32 +190,74 @@ impl Asg { ObjectIndex::new(node_id, span) } - /// Add an edge from the [`Object`] represented by the + /// Add a tree edge from the [`Object`] represented by the /// [`ObjectIndex`] `from_oi` to the object represented by `to_oi`. /// - /// The edge may optionally contain a _contextual [`Span`]_, - /// in cases where it is important to distinguish between the span - /// associated with the target and the span associated with the - /// _reference_ to the target. + /// A tree edge represents _ownership_ over the target object + /// (`from_oi` owns `to_oi`), + /// forming a tree parent/child relationships between objects. + /// To reference objects from other trees + /// (crossing trees), + /// see [`Self::add_cross_edge`]. /// /// For more information on how the ASG's ontology is enforced statically, - /// see [`ObjectRelTo`](object::ObjectRelTo). + /// see [`object`]. /// /// Callers external to this module should use [`ObjectIndex`] APIs to /// manipulate the graph; /// this allows those objects to uphold their own invariants /// relative to the state of the graph. - fn add_edge, OB: ObjectKind + ObjectRelatable>( + fn add_tree_edge< + OA: ObjectIndexTreeRelTo, + OB: ObjectKind + ObjectRelatable, + >( &mut self, from_oi: OA, to_oi: ObjectIndex, - ref_span: Option, ) -> Result<(), AsgError> { - from_oi.pre_add_edge(self, to_oi, ref_span, |asg| { + from_oi.pre_add_edge(self, to_oi, None, |asg| { asg.graph.add_edge( from_oi.widen().into(), to_oi.into(), - (from_oi.src_rel_ty(), OB::rel_ty(), ref_span), + (from_oi.src_rel_ty(), OB::rel_ty(), None), + ); + }) + } + + /// Add a cross edge from the [`Object`] represented by the + /// [`ObjectIndex`] `from_oi` to the object represented by `to_oi`. + /// + /// A cross edge represents a _reference_ to another object, + /// crossing into another tree. + /// To indicate ownership, + /// see [`Self::add_tree_edge`]. + /// + /// The edge must contain a _reference [`Span`]_, + /// which represents the location of the reference _to_ the + /// object `to_oi`, + /// rather than the object itself. + /// + /// For more information on how the ASG's ontology is enforced statically, + /// see [`object`]. + /// + /// Callers external to this module should use [`ObjectIndex`] APIs to + /// manipulate the graph; + /// this allows those objects to uphold their own invariants + /// relative to the state of the graph. + fn add_cross_edge< + OA: ObjectIndexCrossRelTo, + OB: ObjectKind + ObjectRelatable, + >( + &mut self, + from_oi: OA, + to_oi: ObjectIndex, + ref_span: Span, + ) -> Result<(), AsgError> { + from_oi.pre_add_edge(self, to_oi, Some(ref_span), |asg| { + asg.graph.add_edge( + from_oi.widen().into(), + to_oi.into(), + (from_oi.src_rel_ty(), OB::rel_ty(), Some(ref_span)), ); }) } @@ -409,9 +451,11 @@ fn diagnostic_node_missing_desc( /// /// How Does This Work With Trait Specialization? /// ============================================= -/// [`Asg::add_edge`] is provided a [`ObjectIndexRelTo`], -/// which needs narrowing to an appropriate source [`ObjectKind`] so that -/// we can invoke [`::pre_add_edge`](AsgRelMut::pre_add_edge). +/// [`Asg::add_tree_edge`]/[`Asg::add_cross_edge`] is provided a +/// [`ObjectIndexRelTo`], +/// which needs narrowing to an appropriate source [`ObjectKind`] so +/// that we can invoke +/// [`::pre_add_edge`](AsgRelMut::pre_add_edge). /// /// At the time of writing, /// there are two implementors of [`ObjectIndexRelTo`]: @@ -469,9 +513,10 @@ fn diagnostic_node_missing_desc( /// and uses the appropriate specialization. /// /// Because of other trait bounds leading up to this point, -/// including those on [`Asg::add_edge`] and [`ObjectIndexRelTo`], -/// this cannot be invoked for any `to_oi` that is not a valid target -/// for `Self`. +/// including those on [`Asg::add_tree_edge`]/[`Asg::add_cross_edge`] and +/// [`ObjectIndexRelTo`], +/// this cannot be invoked for any `to_oi` that is not a valid target +/// for `Self`. /// But we cannot be too strict on that bound _here_, /// because otherwise it's not general enough for /// [`ObjectIndexTo::pre_add_edge`]. @@ -499,14 +544,15 @@ pub trait AsgRelMut: ObjectRelatable { /// since the [`ObjectIndex`] APIs may not be utilized /// (e.g. in the case of [`ObjectIndexRelTo`]. /// - /// This is invoked by [`Asg::add_edge`]. + /// This is invoked by [`Asg::add_tree_edge`] and + /// [`Asg::add_cross_edge`]. /// The provided `commit` callback will complete the addition of the /// edge if provided [`Ok`], /// and the commit cannot fail. /// If [`Err`] is provided to `commit`, - /// then [`Asg::add_edge`] will fail with that error. + /// then the calling [`Asg`] method will fail with that error. /// - /// Unlike the type of [`Asg::add_edge`], + /// Unlike the type of [`Asg::add_tree_edge`]/[`Asg::add_cross_edge`], /// the source [`ObjectIndex`] has been narrowed to the appropriate /// type for you. fn pre_add_edge( @@ -537,11 +583,19 @@ impl AsgRelMut for OA { } } -/// The relationship proposed by [`Asg::add_edge`], -/// requiring approval from [`AsgRelMut::pre_add_edge`]. +/// The relationship proposed by [`Asg::add_tree_edge`] or +/// [`Asg::add_cross_edge`], +/// requiring approval from [`AsgRelMut::pre_add_edge`]. pub struct ProposedRel { from_oi: ObjectIndex, to_oi: ObjectIndex, + + /// Reference span. + /// + /// This will be [`Some`] by [`Asg::add_cross_edge`], + /// but will always be [`None`] via [`Asg::add_tree_edge`]. + /// This can therefore be used to determine whether an edge is a tree or + /// a cross edge. ref_span: Option, } diff --git a/tamer/src/asg/graph/object.rs b/tamer/src/asg/graph/object.rs index 79a99ab5..ee2e7be2 100644 --- a/tamer/src/asg/graph/object.rs +++ b/tamer/src/asg/graph/object.rs @@ -152,9 +152,10 @@ pub use ident::Ident; pub use meta::Meta; pub use pkg::Pkg; pub use rel::{ - DynObjectRel, ObjectIndexRelTo, ObjectIndexTo, ObjectIndexToTree, - ObjectIndexTreeRelTo, ObjectRel, ObjectRelFrom, ObjectRelTo, ObjectRelTy, - ObjectRelatable, ObjectTreeRelTo, + DynObjectRel, ObjectCrossRelTo, ObjectIndexCrossRelTo, ObjectIndexRelTo, + ObjectIndexTo, ObjectIndexToCross, ObjectIndexToTree, ObjectIndexTreeRelTo, + ObjectRel, ObjectRelFrom, ObjectRelTo, ObjectRelTy, ObjectRelatable, + ObjectTreeRelTo, }; pub use root::Root; pub use tpl::Tpl; @@ -163,9 +164,10 @@ pub use tpl::Tpl; pub mod prelude { pub use super::{ super::{super::error::AsgError, Asg, AsgRelMut}, - Object, ObjectIndex, ObjectIndexRelTo, ObjectKind, ObjectRel, - ObjectRelFrom, ObjectRelTo, ObjectRelTy, ObjectRelatable, - ObjectTreeRelTo, + Object, ObjectCrossRelTo, ObjectIndex, ObjectIndexCrossRelTo, + ObjectIndexRelTo, ObjectIndexToCross, ObjectIndexToTree, + ObjectIndexTreeRelTo, ObjectKind, ObjectRel, ObjectRelFrom, + ObjectRelTo, ObjectRelTy, ObjectRelatable, ObjectTreeRelTo, }; } @@ -593,52 +595,81 @@ impl ObjectIndex { } } - /// Add an edge from `self` to `to_oi` on the provided [`Asg`]. + /// Add a tree edge from `self` to `to_oi` on the provided [`Asg`]. /// - /// Since the only invariant asserted by [`ObjectIndexRelTo`] is that - /// it may be related to `OB`, - /// this method will only permit edges to `OB`; - /// nothing else about the inner object is statically known. - /// - /// See also [`Self::add_edge_from`]. + /// See also [`Self::add_tree_edge_from`]. /// /// _This method must remain private_, /// forcing callers to go through APIs for specific operations that /// allow objects to enforce their own invariants. /// This is also the reason why this method is defined here rather than /// on [`ObjectIndexRelTo`]. - fn add_edge_to( + fn add_tree_edge_to( self, asg: &mut Asg, to_oi: ObjectIndex, - ctx_span: Option, ) -> Result where - Self: ObjectIndexRelTo, + Self: ObjectIndexTreeRelTo, { - asg.add_edge(self, to_oi, ctx_span).map(|()| self) + asg.add_tree_edge(self, to_oi).map(|()| self) } - /// Add an edge from `from_oi` to `self` on the provided [`Asg`]. + /// Add a tree edge from `from_oi` to `self` on the provided [`Asg`]. /// /// An edge can only be added if ontologically valid; /// see [`ObjectRelTo`] for more information. /// - /// See also [`Self::add_edge_to`]. + /// See also [`Self::add_tree_edge_to`]. /// /// _This method must remain private_, /// forcing callers to go through APIs for specific operations that /// allow objects to enforce their own invariants. - fn add_edge_from>( + fn add_tree_edge_from>( self, asg: &mut Asg, from_oi: OA, - ctx_span: Option, ) -> Result where O: ObjectRelatable, { - asg.add_edge(from_oi, self, ctx_span).map(|()| self) + asg.add_tree_edge(from_oi, self).map(|()| self) + } + + /// Add a cross edge from `self` to `to_oi` on the provided [`Asg`]. + /// + /// For more information, + /// see [`Self::add_tree_edge_to`]. + /// + /// See also [`Self::add_cross_edge_from`]. + fn add_cross_edge_to( + self, + asg: &mut Asg, + to_oi: ObjectIndex, + ref_span: Span, + ) -> Result + where + Self: ObjectIndexCrossRelTo, + { + asg.add_cross_edge(self, to_oi, ref_span).map(|()| self) + } + + /// Add a cross edge from `from_oi` to `self` on the provided [`Asg`]. + /// + /// For more information, + /// see [`Self::add_tree_edge_from`]. + /// + /// See also [`Self::add_cross_edge_to`]. + fn add_cross_edge_from>( + self, + asg: &mut Asg, + from_oi: OA, + ref_span: Span, + ) -> Result + where + O: ObjectRelatable, + { + asg.add_cross_edge(from_oi, self, ref_span).map(|()| self) } /// Create an iterator over the [`ObjectIndex`]es of the outgoing edges @@ -813,7 +844,24 @@ impl ObjectIndex { .filter(|_| O::rel_ty() == OB::rel_ty()) } - /// Root this object in the ASG's [`Root`] object. + /// Root this object in the ASG's [`Root`] object, + /// acting as an owner of the object. + /// + /// This is intended for rooting toplevel objects that otherwise have no + /// owner for a given compilation unit, + /// allowing them to be reachable from the graph root. + /// + /// See also [`Self::root_cross`]. + pub fn root_tree(self, asg: &mut Asg) -> Result + where + Root: ObjectTreeRelTo, + { + asg.root(self.span()) + .add_tree_edge_to(asg, self) + .map(|_| self) + } + + /// Root a reference to this object in the ASG's [`Root`] object. /// /// A rooted object is forced to be reachable. /// This should only be utilized when necessary for toplevel objects; @@ -821,18 +869,21 @@ impl ObjectIndex { /// objects. /// Forcing objects to be reachable can prevent them from being /// optimized away if they are not used. - pub fn root(self, asg: &mut Asg) -> Result + /// + /// See also [`Self::root_tree`]. + pub fn root_cross(self, asg: &mut Asg) -> Result where - Root: ObjectRelTo, + Root: ObjectCrossRelTo, { asg.root(self.span()) - .add_edge_to(asg, self, None) + .add_cross_edge_to(asg, self, self.span()) .map(|_| self) } /// Whether this object has been rooted in the ASG's [`Root`] object. /// - /// See [`Self::root`] for more information. + /// See [`Self::root_tree`] and [`Self::root_cross`] for more + /// information. pub fn is_rooted(&self, asg: &Asg) -> bool where Root: ObjectRelTo, @@ -910,7 +961,7 @@ impl ObjectIndex { oi: ObjectIndex, ) -> Result where - Self: ObjectIndexRelTo, + Self: ObjectIndexTreeRelTo, { oi.defined_by(asg, self).map(|_| self) } @@ -927,10 +978,10 @@ impl ObjectIndex { clause: SPair, ) -> Result where - O: ObjectRelTo, + O: ObjectTreeRelTo, { let oi_doc = asg.create(Doc::new_indep_clause(clause)); - self.add_edge_to(asg, oi_doc, None) + self.add_tree_edge_to(asg, oi_doc) } /// Retrieve a description of this expression using a short independent diff --git a/tamer/src/asg/graph/object/expr.rs b/tamer/src/asg/graph/object/expr.rs index 58a9f6fe..33b12fd4 100644 --- a/tamer/src/asg/graph/object/expr.rs +++ b/tamer/src/asg/graph/object/expr.rs @@ -19,7 +19,7 @@ //! Expressions on the ASG. -use super::{prelude::*, Doc, Ident, ObjectIndexTo, Tpl}; +use super::{prelude::*, Doc, Ident, ObjectIndexToTree, Tpl}; use crate::{f::Map, num::Dim, span::Span}; use std::fmt::Display; @@ -235,7 +235,7 @@ impl ObjectIndex { expr: Expr, ) -> Result, AsgError> { let oi_subexpr = asg.create(expr); - oi_subexpr.add_edge_from(asg, self, None) + oi_subexpr.add_tree_edge_from(asg, self) } /// Reference the value of the expression identified by `oi_ident` as if @@ -245,7 +245,7 @@ impl ObjectIndex { asg: &mut Asg, oi_ident: ObjectIndex, ) -> Result { - self.add_edge_to(asg, oi_ident, Some(oi_ident.span())) + self.add_cross_edge_to(asg, oi_ident, oi_ident.span()) } /// The expression is held by the container `oi_container`. @@ -263,8 +263,8 @@ impl ObjectIndex { pub fn held_by( &self, asg: &mut Asg, - oi_container: ObjectIndexTo, + oi_container: ObjectIndexToTree, ) -> Result { - self.add_edge_from(asg, oi_container, None) + self.add_tree_edge_from(asg, oi_container) } } diff --git a/tamer/src/asg/graph/object/ident.rs b/tamer/src/asg/graph/object/ident.rs index 770f9f83..54b62ef0 100644 --- a/tamer/src/asg/graph/object/ident.rs +++ b/tamer/src/asg/graph/object/ident.rs @@ -1221,7 +1221,7 @@ impl ObjectIndex { self.try_map_obj(asg, |obj| obj.resolve(name.span(), kind, src)) .map_err(Into::into) .map(|ident| { - is_auto_root.then(|| self.root(asg)); + is_auto_root.then(|| self.root_cross(asg)); ident }) } @@ -1257,7 +1257,7 @@ impl ObjectIndex { definition: ObjectIndex, ) -> Result, AsgError> where - Ident: ObjectRelTo, + Ident: ObjectTreeRelTo, ObjectIndex: Into, { let my_span = self.into(); @@ -1314,7 +1314,7 @@ impl ObjectIndex { // and use the newly provided `id` and its span. Missing(_) => Ok(Transparent(id)), }) - .and_then(|ident_oi| ident_oi.add_edge_to(asg, definition, None)) + .and_then(|ident_oi| ident_oi.add_tree_edge_to(asg, definition)) } /// Set the fragment associated with a concrete identifier. @@ -1414,9 +1414,9 @@ impl ObjectIndex { pub fn defined_by( &self, asg: &mut Asg, - oi_root: impl ObjectIndexRelTo, + oi_root: impl ObjectIndexTreeRelTo, ) -> Result { - self.add_edge_from(asg, oi_root, None) + self.add_tree_edge_from(asg, oi_root) } /// Declare that `oi_dep` is an opaque dependency of `self`. @@ -1425,7 +1425,7 @@ impl ObjectIndex { asg: &mut Asg, oi_dep: ObjectIndex, ) -> Result { - self.add_edge_to(asg, oi_dep, None) + self.add_tree_edge_to(asg, oi_dep) } /// Retrieve either the concrete name of the identifier or the name of @@ -1491,7 +1491,7 @@ impl ObjectIndex { at: Span, ) -> Result, AsgError> { asg.create(Ident::new_abstract(at)) - .add_edge_to(asg, self, Some(at)) + .add_cross_edge_to(asg, self, at) } } diff --git a/tamer/src/asg/graph/object/meta.rs b/tamer/src/asg/graph/object/meta.rs index 5e56090c..5eb55718 100644 --- a/tamer/src/asg/graph/object/meta.rs +++ b/tamer/src/asg/graph/object/meta.rs @@ -243,7 +243,7 @@ impl ObjectIndex { for rel_lexeme in rels { let oi = asg.create(Meta::Lexeme(rel_lexeme.span(), rel_lexeme)); - self.add_edge_to(asg, oi, None)?; + self.add_tree_edge_to(asg, oi)?; } Ok(self) @@ -297,12 +297,12 @@ impl ObjectIndex { // we must add the edge before appending the ref since // concatenation will occur during expansion in edge order. if let Some(orig) = pre { - asg.create(orig).add_edge_from(asg, self, None)?; + asg.create(orig).add_tree_edge_from(asg, self)?; } // Having been guaranteed a `ConcatList` above, // we now only need to append an edge that references what to // concatenate. - self.add_edge_to(asg, oi_ref, Some(oi_ref.span())) + self.add_cross_edge_to(asg, oi_ref, oi_ref.span()) } } diff --git a/tamer/src/asg/graph/object/pkg.rs b/tamer/src/asg/graph/object/pkg.rs index f64291a6..d4fa6046 100644 --- a/tamer/src/asg/graph/object/pkg.rs +++ b/tamer/src/asg/graph/object/pkg.rs @@ -134,7 +134,7 @@ impl ObjectIndex { let parent = self.resolve(asg); let oi_import = asg.create(Pkg::new_imported(parent, namespec)?); - self.add_edge_to(asg, oi_import, Some(namespec.span())) + self.add_cross_edge_to(asg, oi_import, namespec.span()) } /// Arbitrary text serving as documentation in a literate style. @@ -144,6 +144,6 @@ impl ObjectIndex { text: SPair, ) -> Result { let oi_doc = asg.create(Doc::new_text(text)); - self.add_edge_to(asg, oi_doc, None) + self.add_tree_edge_to(asg, oi_doc) } } diff --git a/tamer/src/asg/graph/object/rel.rs b/tamer/src/asg/graph/object/rel.rs index 4c2390ed..ef722a0a 100644 --- a/tamer/src/asg/graph/object/rel.rs +++ b/tamer/src/asg/graph/object/rel.rs @@ -203,14 +203,17 @@ macro_rules! object_rel { // Similar to above but providing _static_ information to the type // system. // The above could be rolled into this at some point. - (@impl_rel_to $from:ident cross $kind:ident) => {}; + (@impl_rel_to $from:ident cross $kind:ident) => { + impl ObjectCrossRelTo<$kind> for $from {} + }; (@impl_rel_to $from:ident tree $kind:ident) => { impl ObjectTreeRelTo<$kind> for $from {} }; (@impl_rel_to $from:ident dyn $kind:ident) => { - // It _could_ be a tree edge; + // It _could_ be either a tree or cross edge; // we can't know statically. impl ObjectTreeRelTo<$kind> for $from {} + impl ObjectCrossRelTo<$kind> for $from {} }; } @@ -536,11 +539,31 @@ pub trait ObjectRelFrom = /// see that trait for more information. /// This trait is intended to be used in contexts where the distinction /// between reference and ownership is important. +/// +/// For cross edges, +/// see [`ObjectCrossRelTo`]; +/// dynamic edges will implement both this trait and that one. pub trait ObjectTreeRelTo: ObjectRelTo { } +/// Indicate that an [`ObjectKind`] `Self` _could possibly be a reference +/// to_ an [`ObjectKind`] `OB`. +/// +/// This is a stronger assertion than [`ObjectRelTo`]; +/// see that trait for more information. +/// This trait is intended to be used in contexts where the distinction +/// between reference and ownership is important. +/// +/// For tree edges, +/// see [`ObjectTreeRelTo`]; +/// dynamic edges will implement both this trait and that one. +pub trait ObjectCrossRelTo: + ObjectRelTo +{ +} + /// Identify [`Self::Rel`] as a sum type consisting of the subset of /// [`Object`] variants representing the valid _target_ edges of /// [`Self`]. @@ -1036,6 +1059,32 @@ impl ObjectIndexRelTo for ObjectIndexToTree { } } +impl ObjectIndexRelTo for ObjectIndexToCross { + fn src_rel_ty(&self) -> ObjectRelTy { + match self { + Self(oito) => oito.src_rel_ty(), + } + } + + fn widen(&self) -> ObjectIndex { + match self { + Self(oito) => oito.widen(), + } + } + + fn pre_add_edge( + &self, + asg: &mut Asg, + to_oi: ObjectIndex, + ref_span: Option, + commit: impl FnOnce(&mut Asg), + ) -> Result<(), AsgError> { + match self { + Self(oito) => oito.pre_add_edge(asg, to_oi, ref_span, commit), + } + } +} + impl From> for ObjectIndex { fn from(oi_rel: ObjectIndexTo) -> Self { oi_rel.widen() @@ -1072,7 +1121,29 @@ where { } -pub use private::{ObjectIndexTo, ObjectIndexToTree}; +/// An [`ObjectIndex`]-like object that is able to create a _cross_ edge to +/// [`ObjectKind`] `OB`. +/// +/// This allows for generic graph operations that operate on ownership +/// relationships without having to know the type of the source +/// object (`Self`). +/// +/// This is a specialization of [`ObjectIndexRelTo`]. +pub trait ObjectIndexCrossRelTo: + ObjectIndexRelTo + Into> +{ +} + +impl ObjectIndexCrossRelTo for ObjectIndexToCross {} + +impl ObjectIndexCrossRelTo + for ObjectIndex +where + O: ObjectCrossRelTo, +{ +} + +pub use private::{ObjectIndexTo, ObjectIndexToCross, ObjectIndexToTree}; /// Private inner module to ensure that nothing is able to bypass invariants /// by constructing [`ObjectIndexTo`] manually. @@ -1204,6 +1275,14 @@ mod private { } } + impl From> for ObjectIndexTo { + fn from(value: ObjectIndexToCross) -> Self { + match value { + ObjectIndexToCross(oit) => oit, + } + } + } + /// Some [`ObjectIndex`] that can create a _tree_ edge to `OB`. /// /// This is a specialization of @@ -1260,4 +1339,61 @@ mod private { } } } + + /// Some [`ObjectIndex`] that can create a _cross_ edge to `OB`. + /// + /// This is a specialization of + /// (and contains) + /// [`ObjectIndexTo`]; + /// see that for more information. + /// + /// See also [`ObjectIndexTreeRelTo`]. + #[derive(Debug)] + pub struct ObjectIndexToCross(ObjectIndexTo); + + impl From> + for ObjectIndexToCross + where + O: ObjectCrossRelTo, + { + fn from(oi: ObjectIndex) -> Self { + Self(oi.into()) + } + } + + // Deriving any of the below were introducing trait bounds on `OB`. + + impl PartialEq for ObjectIndexToCross { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self(oi), Self(oi_other)) => oi == oi_other, + } + } + } + + impl Eq for ObjectIndexToCross {} + + impl Hash for ObjectIndexToCross { + fn hash(&self, state: &mut H) { + match self { + Self(oi) => oi.hash(state), + } + } + } + + impl Clone for ObjectIndexToCross { + fn clone(&self) -> Self { + Self(self.0) + } + } + + impl Copy for ObjectIndexToCross {} + + impl From> for Span { + fn from(oi: ObjectIndexToCross) -> Self { + match oi { + ObjectIndexToCross(inner) => inner.span(), + } + } + } } diff --git a/tamer/src/asg/graph/object/root.rs b/tamer/src/asg/graph/object/root.rs index 32c90db6..f1d10f3c 100644 --- a/tamer/src/asg/graph/object/root.rs +++ b/tamer/src/asg/graph/object/root.rs @@ -67,6 +67,7 @@ impl ObjectIndex { asg: &mut Asg, oi: ObjectIndex, ) -> Result, AsgError> { - oi.add_edge_from(asg, *self, None) + // TODO: Is this a valid span? + oi.add_cross_edge_from(asg, *self, oi.span()) } } diff --git a/tamer/src/asg/graph/object/tpl.rs b/tamer/src/asg/graph/object/tpl.rs index f9eef9ed..45f669a9 100644 --- a/tamer/src/asg/graph/object/tpl.rs +++ b/tamer/src/asg/graph/object/tpl.rs @@ -232,6 +232,10 @@ impl Display for TplShape { } } +// TODO: Inlining code into this otherwise-declarative abstraction has +// tainted it. +// This can be refactored into a better abstraction over time as +// requirements are explored though implementation. object_rel! { /// Templates may expand into nearly any context, /// and must therefore be able to contain just about anything. @@ -303,8 +307,6 @@ object_rel! { // be hoisted into the rooting context of the // application site, // which does not impact template shape. - // TODO: Let's make that span assumption explicit in the - // `ProposeRel` abstraction. (None, _) => Ok(()), }?; @@ -393,7 +395,7 @@ impl ObjectIndex { oi_apply: ObjectIndex, ref_span: Span, ) -> Result { - self.add_edge_to(asg, oi_apply, Some(ref_span)) + self.add_cross_edge_to(asg, oi_apply, ref_span) } /// Directly reference this template from another object @@ -406,12 +408,12 @@ impl ObjectIndex { /// template. /// If this template is _not_ closed, /// it will result in an error during evaluation. - pub fn expand_into>( + pub fn expand_into>( self, asg: &mut Asg, oi_target_parent: OP, ) -> Result { - self.add_edge_from(asg, oi_target_parent, None) + self.add_tree_edge_from(asg, oi_target_parent) } /// Arbitrary text serving as documentation in a literate style, @@ -422,6 +424,6 @@ impl ObjectIndex { text: SPair, ) -> Result { let oi_doc = asg.create(Doc::new_text(text)); - self.add_edge_to(asg, oi_doc, None) + self.add_tree_edge_to(asg, oi_doc) } }