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-13708
main
Mike Gerwitz 2023-03-28 11:34:05 -04:00
parent eebacb52cc
commit 2ae33a1dfa
9 changed files with 205 additions and 63 deletions

View File

@ -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::*,
};

View File

@ -29,7 +29,7 @@ use super::{
};
use crate::{
asg::{
graph::object::{Meta, ObjectRelTo},
graph::object::{Meta, ObjectIndexRelTo, ObjectRelTo},
Ident,
},
diagnose::Annotate,

View File

@ -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),
);
}

View File

@ -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,

View File

@ -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,

View File

@ -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},

View File

@ -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,

View File

@ -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> {}
}

View File

@ -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,