tamer: asg::graph::object: ObjectIndexTo and ObjectIndexRelTo
The graph's ontology is defined in the direction of the edge: from OA to OB. This is enforced by the type system to ensure that no code path is able to generate an invalid graph. But that also makes it very difficult to work with a generic source to a specific target. This introduces a `ObjectIndexRelTo` trait that says whether `Self` is able to be related to some `ObjectKind` `OB`, implements it for `ObjectIndex where ObjectRelTo<OB>`, and introduces a new semi-opaque type `ObjectIndexTo` that allows for the source `ObjectIndex` to be generic. This then redefines some existing graph primitives in terms of `ObjectIndexRelTo`, in particular creating edges, so that `ObjectIndex` can be used as today, and the new `ObjectIndexTo` can be used in the same way with the same API, without violating the graph ontology. This will be used by `AirAggregate` to create dynamic targets for rooting and splicing/expansion. DEV-13708main
parent
eebacb52cc
commit
2ae33a1dfa
|
@ -30,7 +30,10 @@ use super::{
|
|||
AirAggregateCtx,
|
||||
};
|
||||
use crate::{
|
||||
asg::{graph::object::ObjectRelTo, Ident, ObjectKind},
|
||||
asg::{
|
||||
graph::object::{ObjectIndexRelTo, ObjectRelTo},
|
||||
Ident, ObjectKind,
|
||||
},
|
||||
f::Functor,
|
||||
parse::prelude::*,
|
||||
};
|
||||
|
|
|
@ -29,7 +29,7 @@ use super::{
|
|||
};
|
||||
use crate::{
|
||||
asg::{
|
||||
graph::object::{Meta, ObjectRelTo},
|
||||
graph::object::{Meta, ObjectIndexRelTo, ObjectRelTo},
|
||||
Ident,
|
||||
},
|
||||
diagnose::Annotate,
|
||||
|
|
|
@ -22,7 +22,8 @@
|
|||
//! ![Visualization of ASG ontology](../ontviz.svg)
|
||||
|
||||
use self::object::{
|
||||
DynObjectRel, ObjectRelFrom, ObjectRelTy, ObjectRelatable, Root,
|
||||
DynObjectRel, ObjectIndexRelTo, ObjectRelFrom, ObjectRelTy,
|
||||
ObjectRelatable, Root,
|
||||
};
|
||||
|
||||
use super::{
|
||||
|
@ -441,18 +442,16 @@ impl Asg {
|
|||
///
|
||||
/// For more information on how the ASG's ontology is enforced statically,
|
||||
/// see [`ObjectRelTo`].
|
||||
fn add_edge<OA: ObjectKind, OB: ObjectKind>(
|
||||
fn add_edge<OB: ObjectKind + ObjectRelatable>(
|
||||
&mut self,
|
||||
from_oi: ObjectIndex<OA>,
|
||||
from_oi: impl ObjectIndexRelTo<OB>,
|
||||
to_oi: ObjectIndex<OB>,
|
||||
ctx_span: Option<Span>,
|
||||
) where
|
||||
OA: ObjectRelTo<OB>,
|
||||
{
|
||||
) {
|
||||
self.graph.add_edge(
|
||||
from_oi.into(),
|
||||
from_oi.widen().into(),
|
||||
to_oi.into(),
|
||||
(OA::rel_ty(), OB::rel_ty(), ctx_span),
|
||||
(from_oi.src_rel_ty(), OB::rel_ty(), ctx_span),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -143,8 +143,8 @@ pub use ident::Ident;
|
|||
pub use meta::Meta;
|
||||
pub use pkg::Pkg;
|
||||
pub use rel::{
|
||||
DynObjectRel, ObjectRel, ObjectRelFrom, ObjectRelTo, ObjectRelTy,
|
||||
ObjectRelatable,
|
||||
DynObjectRel, ObjectIndexRelTo, ObjectIndexTo, ObjectRel, ObjectRelFrom,
|
||||
ObjectRelTo, ObjectRelTy, ObjectRelatable,
|
||||
};
|
||||
pub use root::Root;
|
||||
pub use tpl::Tpl;
|
||||
|
@ -517,48 +517,22 @@ impl<O: ObjectKind> ObjectIndex<O> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Add an edge from `self` to `to_oi` on the provided [`Asg`].
|
||||
///
|
||||
/// An edge can only be added if ontologically valid;
|
||||
/// see [`ObjectRelTo`] for more information.
|
||||
///
|
||||
/// Edges may contain _contextual [`Span`]s_ if there is an important
|
||||
/// distinction to be made between a the span of a _reference_ to the
|
||||
/// target and the span of the target itself.
|
||||
/// This is of particular benefit to cross edges
|
||||
/// (see [`ObjectRel::is_cross_edge`]),
|
||||
/// which reference nodes from other trees in different contexts.
|
||||
///
|
||||
/// See also [`Self::add_edge_to`].
|
||||
pub fn add_edge_to<OB: ObjectKind>(
|
||||
self,
|
||||
asg: &mut Asg,
|
||||
to_oi: ObjectIndex<OB>,
|
||||
ctx_span: Option<Span>,
|
||||
) -> Self
|
||||
where
|
||||
O: ObjectRelTo<OB>,
|
||||
{
|
||||
asg.add_edge(self, to_oi, ctx_span);
|
||||
self
|
||||
}
|
||||
|
||||
/// Add an 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`].
|
||||
pub fn add_edge_from<OB: ObjectKind>(
|
||||
pub fn add_edge_from<OF: ObjectIndexRelTo<O>>(
|
||||
self,
|
||||
asg: &mut Asg,
|
||||
from_oi: ObjectIndex<OB>,
|
||||
from_oi: OF,
|
||||
ctx_span: Option<Span>,
|
||||
) -> Self
|
||||
where
|
||||
OB: ObjectRelTo<O>,
|
||||
O: ObjectRelatable,
|
||||
{
|
||||
from_oi.add_edge_to(asg, self, ctx_span);
|
||||
asg.add_edge(from_oi, self, ctx_span);
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -709,18 +683,20 @@ impl<O: ObjectKind> ObjectIndex<O> {
|
|||
/// This generalization is useful in dynamic contexts,
|
||||
/// but it discards type information that must be later re-checked and
|
||||
/// verified.
|
||||
/// See [`Self::widen_dyn_ty`] to retain that type information.
|
||||
pub fn widen(self) -> ObjectIndex<Object> {
|
||||
match self {
|
||||
Self(index, span, _pd) => ObjectIndex::new(index, span),
|
||||
}
|
||||
}
|
||||
|
||||
/// Indicate that the given identifier `oi` is defined by this object.
|
||||
pub fn defines(self, asg: &mut Asg, oi: ObjectIndex<Ident>) -> Self
|
||||
/// Widen an [`ObjectKind`] `O` into [`Object`] like [`Self::widen`],
|
||||
/// but retain type information for later narrowing at runtime.
|
||||
pub fn widen_dyn_ty(self) -> (ObjectIndex<Object>, ObjectRelTy)
|
||||
where
|
||||
O: ObjectRelTo<Ident>,
|
||||
O: ObjectRelatable,
|
||||
{
|
||||
self.add_edge_to(asg, oi, None)
|
||||
(self.widen(), O::rel_ty())
|
||||
}
|
||||
|
||||
/// Attempt to look up a locally bound [`Ident`] via a linear search of
|
||||
|
@ -760,11 +736,12 @@ impl<O: ObjectKind> ObjectIndex<O> {
|
|||
|
||||
/// Declare a local identifier.
|
||||
///
|
||||
/// A local identifier is lexically scoped to `self`.
|
||||
/// This operation is valid only for [`ObjectKind`]s that can contain
|
||||
/// edges to [`Ident`]s.
|
||||
///
|
||||
/// TODO: This allows for duplicate local identifiers!
|
||||
/// See [`ObjectIndexRelTo::declare_local`].
|
||||
//
|
||||
// TODO: This method exists here only as a fallback when Rust is unable
|
||||
// to infer the proper type for [`ObjectIndexRelTo`].
|
||||
// It can be removed once that is resolved;
|
||||
// delete this method and compile to see.
|
||||
pub fn declare_local(
|
||||
&self,
|
||||
asg: &mut Asg,
|
||||
|
@ -773,8 +750,7 @@ impl<O: ObjectKind> ObjectIndex<O> {
|
|||
where
|
||||
O: ObjectRelTo<Ident>,
|
||||
{
|
||||
asg.create(Ident::declare(name))
|
||||
.add_edge_from(asg, *self, None)
|
||||
ObjectIndexRelTo::declare_local(self, asg, name)
|
||||
}
|
||||
|
||||
/// Retrieve the identifier for this object,
|
||||
|
|
|
@ -22,8 +22,8 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
use super::{
|
||||
Asg, Ident, Object, ObjectIndex, ObjectRel, ObjectRelFrom, ObjectRelTy,
|
||||
ObjectRelatable,
|
||||
Asg, Ident, Object, ObjectIndex, ObjectIndexRelTo, ObjectRel,
|
||||
ObjectRelFrom, ObjectRelTy, ObjectRelatable,
|
||||
};
|
||||
use crate::{
|
||||
f::Functor,
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
|
||||
use super::{
|
||||
super::{Asg, AsgError, ObjectIndex, ObjectKind},
|
||||
Expr, Meta, Object, ObjectRel, ObjectRelFrom, ObjectRelTo, ObjectRelTy,
|
||||
ObjectRelatable, Pkg, Tpl,
|
||||
Expr, Meta, Object, ObjectIndexRelTo, ObjectRel, ObjectRelFrom,
|
||||
ObjectRelTo, ObjectRelTy, ObjectRelatable, Pkg, Tpl,
|
||||
};
|
||||
use crate::{
|
||||
diagnose::{Annotate, Diagnostic},
|
||||
|
|
|
@ -25,8 +25,8 @@
|
|||
//! The canonical metavariable is the template parameter.
|
||||
|
||||
use super::{
|
||||
Ident, Object, ObjectIndex, ObjectRel, ObjectRelFrom, ObjectRelTy,
|
||||
ObjectRelatable, Tpl,
|
||||
Ident, Object, ObjectIndex, ObjectIndexRelTo, ObjectRel, ObjectRelFrom,
|
||||
ObjectRelTy, ObjectRelatable, Tpl,
|
||||
};
|
||||
use crate::{
|
||||
asg::Asg,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// Relationship between objects represented on ASG //
|
||||
// Relationship between objects represented on ASG
|
||||
//
|
||||
// Copyright (C) 2014-2023 Ryan Specialty, LLC.
|
||||
//
|
||||
// This file is part of TAME.
|
||||
|
@ -27,9 +28,10 @@ use super::{
|
|||
use crate::{
|
||||
asg::{graph::object::Tpl, Asg},
|
||||
f::Functor,
|
||||
parse::util::SPair,
|
||||
span::Span,
|
||||
};
|
||||
use std::fmt::Display;
|
||||
use std::{fmt::Display, marker::PhantomData};
|
||||
|
||||
pub use super::ObjectTy as ObjectRelTy;
|
||||
|
||||
|
@ -561,3 +563,165 @@ pub trait ObjectRel<OA: ObjectKind + ObjectRelatable>: Sized {
|
|||
/// once all use cases are clear.
|
||||
fn is_cross_edge<S, T>(&self, rel: &DynObjectRel<S, T>) -> bool;
|
||||
}
|
||||
|
||||
/// An [`ObjectIndex`]-like object that is able to relate to
|
||||
/// [`ObjectKind`] `OB`.
|
||||
///
|
||||
/// This serves primarily as an opaque [`ObjectIndex`] that we know can be
|
||||
/// related to some other type of [`ObjectKind`] `OB`.
|
||||
/// This allows for generic graph operations that operate on relationships
|
||||
/// without having to know the type of the source object (`Self`).
|
||||
pub trait ObjectIndexRelTo<OB: ObjectRelatable>: Sized + Clone + Copy {
|
||||
/// The [`ObjectRelTy`] of the inner [`ObjectIndex`] before widening.
|
||||
fn src_rel_ty(&self) -> ObjectRelTy;
|
||||
|
||||
/// Widen this type into a generic [`ObjectIndex`] with no
|
||||
/// [`ObjectKind`] information.
|
||||
///
|
||||
/// See [`ObjectIndex::widen`] for more information.
|
||||
fn widen(&self) -> ObjectIndex<Object>;
|
||||
|
||||
/// Add an 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.
|
||||
/// To create edges to other types of objects,
|
||||
/// and for more information about this operation
|
||||
/// (including `ctx_span`),
|
||||
/// see [`ObjectIndex::add_edge_to`].
|
||||
fn add_edge_to(
|
||||
self,
|
||||
asg: &mut Asg,
|
||||
to_oi: ObjectIndex<OB>,
|
||||
ctx_span: Option<Span>,
|
||||
) -> Self {
|
||||
asg.add_edge(self, to_oi, ctx_span);
|
||||
self
|
||||
}
|
||||
|
||||
/// Indicate that the given identifier `oi` is defined by this object.
|
||||
fn defines(self, asg: &mut Asg, oi: ObjectIndex<Ident>) -> Self
|
||||
where
|
||||
Self: ObjectIndexRelTo<Ident>,
|
||||
{
|
||||
self.add_edge_to(asg, oi, None)
|
||||
}
|
||||
|
||||
/// Declare a local identifier.
|
||||
///
|
||||
/// A local identifier is lexically scoped to `self`.
|
||||
/// This operation is valid only for [`ObjectKind`]s that can contain
|
||||
/// edges to [`Ident`]s.
|
||||
///
|
||||
/// TODO: This allows for duplicate local identifiers!
|
||||
fn declare_local(&self, asg: &mut Asg, name: SPair) -> ObjectIndex<Ident>
|
||||
where
|
||||
Self: ObjectIndexRelTo<Ident>,
|
||||
{
|
||||
asg.create(Ident::declare(name))
|
||||
.add_edge_from(asg, *self, None)
|
||||
}
|
||||
}
|
||||
|
||||
impl<O: ObjectRelatable, OB: ObjectRelatable> ObjectIndexRelTo<OB>
|
||||
for ObjectIndex<O>
|
||||
where
|
||||
O: ObjectRelTo<OB>,
|
||||
{
|
||||
fn src_rel_ty(&self) -> ObjectRelTy {
|
||||
O::rel_ty()
|
||||
}
|
||||
|
||||
fn widen(&self) -> ObjectIndex<Object> {
|
||||
ObjectIndex::<O>::widen(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<OB: ObjectRelatable> ObjectIndexRelTo<OB> for ObjectIndexTo<OB> {
|
||||
fn src_rel_ty(&self) -> ObjectRelTy {
|
||||
match self {
|
||||
Self((_, ty), _) => *ty,
|
||||
}
|
||||
}
|
||||
|
||||
fn widen(&self) -> ObjectIndex<Object> {
|
||||
*self.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<OB: ObjectRelatable> From<ObjectIndexTo<OB>> for ObjectIndex<Object> {
|
||||
fn from(oi_rel: ObjectIndexTo<OB>) -> Self {
|
||||
oi_rel.widen()
|
||||
}
|
||||
}
|
||||
|
||||
impl<OB: ObjectRelatable> AsRef<ObjectIndex<Object>> for ObjectIndexTo<OB> {
|
||||
fn as_ref(&self) -> &ObjectIndex<Object> {
|
||||
match self {
|
||||
Self((oi, _), _) => oi,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub use private::ObjectIndexTo;
|
||||
|
||||
/// Private inner module to ensure that nothing is able to bypass invariants
|
||||
/// by constructing [`ObjectIndexTo`] manually.
|
||||
mod private {
|
||||
use super::*;
|
||||
|
||||
/// Some [`ObjectIndex`] that is able to [`ObjectRelTo`] `OB`.
|
||||
///
|
||||
/// This type upholds the invariant that any [`ObjectIndex`] contained
|
||||
/// within is [`ObjectRelTo`] `OB`.
|
||||
/// This allows this object to serve in place of a concrete
|
||||
/// [`ObjectIndex`] for graph operations that need only know whether
|
||||
/// an object is relatable to another,
|
||||
/// such as when adding edges.
|
||||
///
|
||||
/// This object is only needed when relations need to be manipulated on
|
||||
/// a known target [`ObjectKind`] `OB`,
|
||||
/// but the source [`ObjectKind`] is dynamic.
|
||||
/// This is necessary because a generic [`Object`] must first be
|
||||
/// narrowed before being able to be used in any graph operation so
|
||||
/// that the ontology can be statically enforced.
|
||||
///
|
||||
/// See [`ObjectIndexRelTo`] for more information.
|
||||
///
|
||||
/// Constructing [`ObjectIndexTo`]
|
||||
/// ==============================
|
||||
/// This object is intended to be constructed using [`From`].
|
||||
/// _Never construct this object in any other way;_
|
||||
/// manually creating the struct will not uphold its invariants,
|
||||
/// which can lead to an invalid graph construction,
|
||||
/// which will in turn lead to internal system failures when trying
|
||||
/// to operate on the graph data down the line.
|
||||
/// There are no memory safety concerns.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct ObjectIndexTo<OB: ObjectRelatable>(
|
||||
(ObjectIndex<Object>, ObjectRelTy),
|
||||
PhantomData<OB>,
|
||||
);
|
||||
|
||||
impl<OB: ObjectRelatable, O: ObjectRelatable> From<ObjectIndex<O>>
|
||||
for ObjectIndexTo<OB>
|
||||
where
|
||||
O: ObjectRelTo<OB>,
|
||||
{
|
||||
fn from(oi: ObjectIndex<O>) -> Self {
|
||||
Self(oi.widen_dyn_ty(), PhantomData::default())
|
||||
}
|
||||
}
|
||||
|
||||
// Deriving `Clone`/`Copy` as of 2023-03 was introducing a
|
||||
// `Clone`/`Copy` bound on `OB`.
|
||||
impl<OB: ObjectRelatable> Clone for ObjectIndexTo<OB> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<OB: ObjectRelatable> Copy for ObjectIndexTo<OB> {}
|
||||
}
|
||||
|
|
|
@ -22,8 +22,8 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
use super::{
|
||||
Expr, Ident, Object, ObjectIndex, ObjectRel, ObjectRelFrom, ObjectRelTo,
|
||||
ObjectRelTy, ObjectRelatable,
|
||||
Expr, Ident, Object, ObjectIndex, ObjectIndexRelTo, ObjectRel,
|
||||
ObjectRelFrom, ObjectRelTo, ObjectRelTy, ObjectRelatable,
|
||||
};
|
||||
use crate::{
|
||||
asg::Asg,
|
||||
|
|
Loading…
Reference in New Issue