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<Span>`, 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-13163main
parent
c0ba827d90
commit
9f74c0fc92
|
@ -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<ObjectIndexTo<Expr>> {
|
||||
fn dangling_expr_oi(&self) -> Option<ObjectIndexToTree<Expr>> {
|
||||
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<ObjectIndexTo<Tpl>> {
|
||||
fn expansion_oi(&self) -> Option<ObjectIndexToTree<Tpl>> {
|
||||
use AirAggregate::*;
|
||||
|
||||
self.stack.iter().rev().find_map(|st| match st {
|
||||
|
|
|
@ -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<ObjectIndexTo<Expr>>,
|
||||
oi_root: Option<ObjectIndexToTree<Expr>>,
|
||||
oi_expr: ObjectIndex<Expr>,
|
||||
) -> Result<(), AsgError> {
|
||||
let oi_container = oi_root
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
|
|
|
@ -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<OA: ObjectIndexRelTo<OB>, OB: ObjectKind + ObjectRelatable>(
|
||||
fn add_tree_edge<
|
||||
OA: ObjectIndexTreeRelTo<OB>,
|
||||
OB: ObjectKind + ObjectRelatable,
|
||||
>(
|
||||
&mut self,
|
||||
from_oi: OA,
|
||||
to_oi: ObjectIndex<OB>,
|
||||
ref_span: Option<Span>,
|
||||
) -> 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>,
|
||||
OB: ObjectKind + ObjectRelatable,
|
||||
>(
|
||||
&mut self,
|
||||
from_oi: OA,
|
||||
to_oi: ObjectIndex<OB>,
|
||||
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<O: ObjectKind>(
|
|||
///
|
||||
/// 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 [`<O as AsgRelMut>::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
|
||||
/// [`<O as AsgRelMut>::pre_add_edge`](AsgRelMut::pre_add_edge).
|
||||
///
|
||||
/// At the time of writing,
|
||||
/// there are two implementors of [`ObjectIndexRelTo`]:
|
||||
|
@ -469,7 +513,8 @@ fn diagnostic_node_missing_desc<O: ObjectKind>(
|
|||
/// and uses the appropriate specialization.
|
||||
///
|
||||
/// Because of other trait bounds leading up to this point,
|
||||
/// including those on [`Asg::add_edge`] and [`ObjectIndexRelTo`],
|
||||
/// 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_,
|
||||
|
@ -499,14 +544,15 @@ pub trait AsgRelMut<OB: ObjectRelatable>: 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<OA: ObjectRelatable, OB: ObjectRelatable> AsgRelMut<OB> for OA {
|
|||
}
|
||||
}
|
||||
|
||||
/// The relationship proposed by [`Asg::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<OA: ObjectKind, OB: ObjectKind> {
|
||||
from_oi: ObjectIndex<OA>,
|
||||
to_oi: ObjectIndex<OB>,
|
||||
|
||||
/// 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<Span>,
|
||||
}
|
||||
|
||||
|
|
|
@ -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<O: ObjectKind> ObjectIndex<O> {
|
|||
}
|
||||
}
|
||||
|
||||
/// 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<OB: ObjectRelatable>(
|
||||
fn add_tree_edge_to<OB: ObjectRelatable>(
|
||||
self,
|
||||
asg: &mut Asg,
|
||||
to_oi: ObjectIndex<OB>,
|
||||
ctx_span: Option<Span>,
|
||||
) -> Result<Self, AsgError>
|
||||
where
|
||||
Self: ObjectIndexRelTo<OB>,
|
||||
Self: ObjectIndexTreeRelTo<OB>,
|
||||
{
|
||||
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<OA: ObjectIndexRelTo<O>>(
|
||||
fn add_tree_edge_from<OA: ObjectIndexTreeRelTo<O>>(
|
||||
self,
|
||||
asg: &mut Asg,
|
||||
from_oi: OA,
|
||||
ctx_span: Option<Span>,
|
||||
) -> Result<Self, AsgError>
|
||||
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<OB: ObjectRelatable>(
|
||||
self,
|
||||
asg: &mut Asg,
|
||||
to_oi: ObjectIndex<OB>,
|
||||
ref_span: Span,
|
||||
) -> Result<Self, AsgError>
|
||||
where
|
||||
Self: ObjectIndexCrossRelTo<OB>,
|
||||
{
|
||||
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<OA: ObjectIndexCrossRelTo<O>>(
|
||||
self,
|
||||
asg: &mut Asg,
|
||||
from_oi: OA,
|
||||
ref_span: Span,
|
||||
) -> Result<Self, AsgError>
|
||||
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<O: ObjectKind> ObjectIndex<O> {
|
|||
.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<Self, AsgError>
|
||||
where
|
||||
Root: ObjectTreeRelTo<O>,
|
||||
{
|
||||
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<O: ObjectKind> ObjectIndex<O> {
|
|||
/// 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<Self, AsgError>
|
||||
///
|
||||
/// See also [`Self::root_tree`].
|
||||
pub fn root_cross(self, asg: &mut Asg) -> Result<Self, AsgError>
|
||||
where
|
||||
Root: ObjectRelTo<O>,
|
||||
Root: ObjectCrossRelTo<O>,
|
||||
{
|
||||
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<O>,
|
||||
|
@ -910,7 +961,7 @@ impl<O: ObjectKind> ObjectIndex<O> {
|
|||
oi: ObjectIndex<Ident>,
|
||||
) -> Result<Self, AsgError>
|
||||
where
|
||||
Self: ObjectIndexRelTo<Ident>,
|
||||
Self: ObjectIndexTreeRelTo<Ident>,
|
||||
{
|
||||
oi.defined_by(asg, self).map(|_| self)
|
||||
}
|
||||
|
@ -927,10 +978,10 @@ impl<O: ObjectKind> ObjectIndex<O> {
|
|||
clause: SPair,
|
||||
) -> Result<Self, AsgError>
|
||||
where
|
||||
O: ObjectRelTo<Doc>,
|
||||
O: ObjectTreeRelTo<Doc>,
|
||||
{
|
||||
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
|
||||
|
|
|
@ -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: Expr,
|
||||
) -> Result<ObjectIndex<Expr>, 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<Expr> {
|
|||
asg: &mut Asg,
|
||||
oi_ident: ObjectIndex<Ident>,
|
||||
) -> Result<Self, AsgError> {
|
||||
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<Expr> {
|
|||
pub fn held_by(
|
||||
&self,
|
||||
asg: &mut Asg,
|
||||
oi_container: ObjectIndexTo<Expr>,
|
||||
oi_container: ObjectIndexToTree<Expr>,
|
||||
) -> Result<Self, AsgError> {
|
||||
self.add_edge_from(asg, oi_container, None)
|
||||
self.add_tree_edge_from(asg, oi_container)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1221,7 +1221,7 @@ impl ObjectIndex<Ident> {
|
|||
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<Ident> {
|
|||
definition: ObjectIndex<O>,
|
||||
) -> Result<ObjectIndex<Ident>, AsgError>
|
||||
where
|
||||
Ident: ObjectRelTo<O>,
|
||||
Ident: ObjectTreeRelTo<O>,
|
||||
ObjectIndex<O>: Into<IdentDefinition>,
|
||||
{
|
||||
let my_span = self.into();
|
||||
|
@ -1314,7 +1314,7 @@ impl ObjectIndex<Ident> {
|
|||
// 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<Ident> {
|
|||
pub fn defined_by(
|
||||
&self,
|
||||
asg: &mut Asg,
|
||||
oi_root: impl ObjectIndexRelTo<Ident>,
|
||||
oi_root: impl ObjectIndexTreeRelTo<Ident>,
|
||||
) -> Result<Self, AsgError> {
|
||||
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<Ident> {
|
|||
asg: &mut Asg,
|
||||
oi_dep: ObjectIndex<Ident>,
|
||||
) -> Result<Self, AsgError> {
|
||||
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<Ident> {
|
|||
at: Span,
|
||||
) -> Result<ObjectIndex<Ident>, AsgError> {
|
||||
asg.create(Ident::new_abstract(at))
|
||||
.add_edge_to(asg, self, Some(at))
|
||||
.add_cross_edge_to(asg, self, at)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -243,7 +243,7 @@ impl ObjectIndex<Meta> {
|
|||
|
||||
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<Meta> {
|
|||
// 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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -134,7 +134,7 @@ impl ObjectIndex<Pkg> {
|
|||
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<Pkg> {
|
|||
text: SPair,
|
||||
) -> Result<Self, AsgError> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<OA: ObjectKind + ObjectRelatable> =
|
|||
/// 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<OB: ObjectKind + ObjectRelatable>:
|
||||
ObjectRelTo<OB>
|
||||
{
|
||||
}
|
||||
|
||||
/// 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<OB: ObjectKind + ObjectRelatable>:
|
||||
ObjectRelTo<OB>
|
||||
{
|
||||
}
|
||||
|
||||
/// 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<OB: ObjectRelatable> ObjectIndexRelTo<OB> for ObjectIndexToTree<OB> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<OB: ObjectRelatable> ObjectIndexRelTo<OB> for ObjectIndexToCross<OB> {
|
||||
fn src_rel_ty(&self) -> ObjectRelTy {
|
||||
match self {
|
||||
Self(oito) => oito.src_rel_ty(),
|
||||
}
|
||||
}
|
||||
|
||||
fn widen(&self) -> ObjectIndex<Object> {
|
||||
match self {
|
||||
Self(oito) => oito.widen(),
|
||||
}
|
||||
}
|
||||
|
||||
fn pre_add_edge(
|
||||
&self,
|
||||
asg: &mut Asg,
|
||||
to_oi: ObjectIndex<OB>,
|
||||
ref_span: Option<Span>,
|
||||
commit: impl FnOnce(&mut Asg),
|
||||
) -> Result<(), AsgError> {
|
||||
match self {
|
||||
Self(oito) => oito.pre_add_edge(asg, to_oi, ref_span, commit),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<OB: ObjectRelatable> From<ObjectIndexTo<OB>> for ObjectIndex<Object> {
|
||||
fn from(oi_rel: ObjectIndexTo<OB>) -> 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<OB: ObjectRelatable>:
|
||||
ObjectIndexRelTo<OB> + Into<ObjectIndexToCross<OB>>
|
||||
{
|
||||
}
|
||||
|
||||
impl<OB: ObjectRelatable> ObjectIndexCrossRelTo<OB> for ObjectIndexToCross<OB> {}
|
||||
|
||||
impl<O: ObjectRelatable, OB: ObjectRelatable> ObjectIndexCrossRelTo<OB>
|
||||
for ObjectIndex<O>
|
||||
where
|
||||
O: ObjectCrossRelTo<OB>,
|
||||
{
|
||||
}
|
||||
|
||||
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<OB: ObjectRelatable> From<ObjectIndexToCross<OB>> for ObjectIndexTo<OB> {
|
||||
fn from(value: ObjectIndexToCross<OB>) -> 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<OB: ObjectRelatable>(ObjectIndexTo<OB>);
|
||||
|
||||
impl<OB: ObjectRelatable, O: ObjectRelatable> From<ObjectIndex<O>>
|
||||
for ObjectIndexToCross<OB>
|
||||
where
|
||||
O: ObjectCrossRelTo<OB>,
|
||||
{
|
||||
fn from(oi: ObjectIndex<O>) -> Self {
|
||||
Self(oi.into())
|
||||
}
|
||||
}
|
||||
|
||||
// Deriving any of the below were introducing trait bounds on `OB`.
|
||||
|
||||
impl<OB: ObjectRelatable> PartialEq for ObjectIndexToCross<OB> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(Self(oi), Self(oi_other)) => oi == oi_other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<OB: ObjectRelatable> Eq for ObjectIndexToCross<OB> {}
|
||||
|
||||
impl<OB: ObjectRelatable> Hash for ObjectIndexToCross<OB> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
match self {
|
||||
Self(oi) => oi.hash(state),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<OB: ObjectRelatable> Clone for ObjectIndexToCross<OB> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<OB: ObjectRelatable> Copy for ObjectIndexToCross<OB> {}
|
||||
|
||||
impl<OB: ObjectRelatable> From<ObjectIndexToCross<OB>> for Span {
|
||||
fn from(oi: ObjectIndexToCross<OB>) -> Self {
|
||||
match oi {
|
||||
ObjectIndexToCross(inner) => inner.span(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ impl ObjectIndex<Root> {
|
|||
asg: &mut Asg,
|
||||
oi: ObjectIndex<Ident>,
|
||||
) -> Result<ObjectIndex<Ident>, AsgError> {
|
||||
oi.add_edge_from(asg, *self, None)
|
||||
// TODO: Is this a valid span?
|
||||
oi.add_cross_edge_from(asg, *self, oi.span())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Tpl> {
|
|||
oi_apply: ObjectIndex<Ident>,
|
||||
ref_span: Span,
|
||||
) -> Result<Self, AsgError> {
|
||||
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<Tpl> {
|
|||
/// template.
|
||||
/// If this template is _not_ closed,
|
||||
/// it will result in an error during evaluation.
|
||||
pub fn expand_into<OP: ObjectIndexRelTo<Tpl>>(
|
||||
pub fn expand_into<OP: ObjectIndexTreeRelTo<Tpl>>(
|
||||
self,
|
||||
asg: &mut Asg,
|
||||
oi_target_parent: OP,
|
||||
) -> Result<Self, AsgError> {
|
||||
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<Tpl> {
|
|||
text: SPair,
|
||||
) -> Result<Self, AsgError> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue