From 6f68292df5b6318f97e00c427e32930bedf7ab5b Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Thu, 20 Apr 2023 14:55:20 -0400 Subject: [PATCH] tamer: asg::graph::{index_identifier=>index}: Generalize This may now index _any_ type of object, in preparation for indexing package import paths. In practice, this only makes sense (at least currently) for `Pkg` and `Ident`. This generalization also applies to `Asg::lookup_or_missing`. DEV-13162 --- tamer/src/asg/air.rs | 2 +- tamer/src/asg/graph.rs | 116 ++++++++++++++-------------- tamer/src/asg/graph/object.rs | 17 +++- tamer/src/asg/graph/object/ident.rs | 8 +- tamer/src/asg/graph/object/pkg.rs | 8 +- tamer/src/asg/graph/test.rs | 1 + 6 files changed, 91 insertions(+), 61 deletions(-) diff --git a/tamer/src/asg/air.rs b/tamer/src/asg/air.rs index f79d8507..3e24f366 100644 --- a/tamer/src/asg/air.rs +++ b/tamer/src/asg/air.rs @@ -571,7 +571,7 @@ impl AirAggregateCtx { // TODO: This currently only indexes for the top of the stack, // but we'll want no-shadow records for the rest of the env. if let Some(oi) = self.rooting_oi() { - self.asg_mut().index_identifier(oi, name, oi_ident); + self.asg_mut().index(oi, name, oi_ident); } oi_ident diff --git a/tamer/src/asg/graph.rs b/tamer/src/asg/graph.rs index 8f0d224b..6850ded7 100644 --- a/tamer/src/asg/graph.rs +++ b/tamer/src/asg/graph.rs @@ -22,11 +22,11 @@ //! ![Visualization of ASG ontology](../ontviz.svg) use self::object::{ - DynObjectRel, ObjectIndexRelTo, ObjectIndexToTree, ObjectIndexTreeRelTo, - ObjectRelFrom, ObjectRelTy, ObjectRelatable, Root, + DynObjectRel, NameableMissingObject, ObjectIndexRelTo, + ObjectIndexTreeRelTo, ObjectRelFrom, ObjectRelTy, ObjectRelatable, Root, }; -use super::{AsgError, Ident, Object, ObjectIndex, ObjectKind}; +use super::{AsgError, Object, ObjectIndex, ObjectKind}; use crate::{ diagnose::{panic::DiagnosticPanic, Annotate, AnnotatedSpan}, f::Functor, @@ -81,15 +81,6 @@ type Ix = global::ProgSymSize; /// /// This implementation is currently based on [`petgraph`]. /// -/// Identifiers are cached by name for `O(1)` lookup. -/// Since [`SymbolId`][crate::sym::SymbolId] is used for this purpose, -/// the index may contain more entries than nodes and may contain gaps. -/// -/// This IR focuses on the definition and manipulation of objects and their -/// dependencies. -/// See [`Ident`]for a summary of valid identifier object state -/// transitions. -/// /// Objects are never deleted from the graph, /// so [`ObjectIndex`]s will remain valid for the lifetime of the ASG. /// @@ -100,27 +91,26 @@ pub struct Asg { /// Directed graph on which objects are stored. pub graph: DiGraph, - /// Scoped map of [`SymbolId`][crate::sym::SymbolId] to node indexes. + /// Edge cache of [`SymbolId`][crate::sym::SymbolId] to + /// [`ObjectIndex`]es. /// - /// This maps a `(SymbolId, NodeIndex)` pair to a node on the graph, - /// if any, - /// such that the provided [`SymbolId`] is part of the environment at - /// the paired [`NodeIndex`]. - /// That is: - /// an identifier must be indexed for _each container_ in its - /// environment, - /// which in turn determines the scope of that identifier. + /// This maps a `(SymbolId, NodeIndex)` pair to a node on the graph for + /// a given [`ObjectRelTy`]. + /// _This indexing is not automatic_; + /// it must be explicitly performed using [`Self::index`]. /// - /// _An index does not imply the existence of an edge._ /// This index serves as a shortcut for finding nodes on a graph, /// _but makes no claims about the structure of the graph_. /// /// This allows for `O(1)` lookup of identifiers in the graph relative - /// to the environment of a given node. + /// to a given node. /// Note that, /// while we store [`NodeIndex`] internally, /// the public API encapsulates it within an [`ObjectIndex`]. - index: FxHashMap<(SymbolId, ObjectIndexToTree), ObjectIndex>, + index: FxHashMap< + (ObjectRelTy, SymbolId, ObjectIndex), + ObjectIndex, + >, /// The root node used for reachability analysis and topological /// sorting. @@ -192,28 +182,35 @@ impl Asg { self.graph } - /// Index the provided symbol `name` as representing the identifier - /// `node` in the immediate environment `imm_env`. + /// Index the provided symbol `name` as representing the + /// [`ObjectIndex`] in the immediate environment `imm_env`. /// - /// This index permits `O(1)` identifier lookups. + /// An index does not require the existence of an edge, + /// but an index may only be created if an edge `imm_env->oi` _could_ + /// be constructed. + /// + /// This index permits `O(1)` object lookups. /// The term "immediate environment" is meant to convey that this index /// applies only to the provided `imm_env` node and does not - /// propagate to any other nodes that share this environment. + /// propagate to any other objects that share this environment. /// - /// After an identifier is indexed it is not expected to be reassigned + /// After an object is indexed it is not expected to be re-indexed /// to another node. /// Debug builds contain an assertion that will panic in this instance. - pub(super) fn index_identifier< - OS: ObjectIndexTreeRelTo, + pub(super) fn index< + O: ObjectRelatable, + OS: ObjectIndexTreeRelTo, S: Into, >( &mut self, imm_env: OS, name: S, - oi: ObjectIndex, + oi: ObjectIndex, ) { let sym = name.into(); - let prev = self.index.insert((sym, imm_env.into()), oi); + let prev = self + .index + .insert((O::rel_ty(), sym, imm_env.widen()), oi.widen()); // We should never overwrite indexes #[allow(unused_variables)] // used only for debug @@ -246,35 +243,34 @@ impl Asg { } } - /// Lookup `ident` or add a missing identifier to the graph relative to + /// Lookup `name or add a missing object to the graph relative to /// the immediate environment `imm_env` and return a reference to it. /// - /// The provided span is necessary to seed the missing identifier with - /// some sort of context to aid in debugging why a missing identifier + /// The provided span is necessary to seed the missing object with + /// some sort of context to aid in debugging why a missing object /// was introduced to the graph. - /// The provided span will be used even if an identifier exists on the - /// graph, + /// The provided span will be used by the returned [`ObjectIndex`] even + /// if an object exists on the graph, /// which can be used for retaining information on the location that - /// requested the identifier. - /// To retrieve the span of a previously declared identifier, - /// you must resolve the [`Ident`] object and inspect it. + /// requested the object. + /// To retrieve the span of a previously declared object, + /// you must resolve the [`ObjectIndex`] and inspect it. /// - /// See [`Ident::declare`] for more information. - pub(super) fn lookup_or_missing>( + /// See [`Self::index`] for more information. + pub(super) fn lookup_or_missing< + O: ObjectRelatable, + OS: ObjectIndexTreeRelTo, + >( &mut self, imm_env: OS, name: SPair, - ) -> ObjectIndex { + ) -> ObjectIndex + where + O: NameableMissingObject, + { self.lookup(imm_env, name).unwrap_or_else(|| { - let index = self.graph.add_node(Ident::declare(name).into()); - let oi = ObjectIndex::new(index, name.span()); - - self.index_identifier( - ObjectIndex::::new(self.root_node, name.span()), - name.symbol(), - oi, - ); - + let oi = self.create(O::missing(name)); + self.index(imm_env, name.symbol(), oi); oi }) } @@ -500,14 +496,20 @@ impl Asg { /// compilation unit, /// which is a package. #[inline] - pub fn lookup>( + pub fn lookup>( &self, imm_env: OS, id: SPair, - ) -> Option> { + ) -> Option> { + // The type `O` is encoded into the index on [`Self::index`] and so + // should always be able to be narrowed into the expected type. + // If this invariant somehow does not hold, + // then the system will panic when the object is resolved. + // Maybe future Rust will have dependent types that allow for better + // static assurances. self.index - .get(&(id.symbol(), imm_env.into())) - .map(|&ni| ni.overwrite(id.span())) + .get(&(O::rel_ty(), id.symbol(), imm_env.widen())) + .map(|&ni| ni.overwrite(id.span()).must_narrow_into::()) } } diff --git a/tamer/src/asg/graph/object.rs b/tamer/src/asg/graph/object.rs index e0e8d1e6..76f7eadd 100644 --- a/tamer/src/asg/graph/object.rs +++ b/tamer/src/asg/graph/object.rs @@ -205,7 +205,7 @@ macro_rules! object_gen { /// /// TODO: Encapsulate within `crate::asg` when the graph can be better /// encapsulated. - #[derive(Debug, PartialEq, Eq, Clone, Copy)] + #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] pub enum ObjectTy { $($kind,)+ } @@ -963,3 +963,18 @@ fn container_oops() -> Vec> { ), ] } + +/// An [`ObjectKind`] with a representation of a missing object associated +/// with a [`SymbolId`](crate::sym::SymbolId). +/// +/// A _missing_ object has a node on the graph to which edges may be +/// drawn for when the object is eventually defined. +/// Missing objects are identified by a [`SymbolId`](crate::sym::SymbolId), +/// allowing them to be later retrieved for definition. +pub trait NameableMissingObject: ObjectKind { + /// Represent an object as missing on the [`Asg`]. + /// + /// The provided `name` will be used to later look up the object to + /// provide a definition. + fn missing(name: SPair) -> Self; +} diff --git a/tamer/src/asg/graph/object/ident.rs b/tamer/src/asg/graph/object/ident.rs index 8fa62aec..e742e494 100644 --- a/tamer/src/asg/graph/object/ident.rs +++ b/tamer/src/asg/graph/object/ident.rs @@ -19,7 +19,7 @@ //! Identifiers (a type of [object](super)). -use super::{prelude::*, Expr, Meta, Pkg, Tpl}; +use super::{prelude::*, Expr, Meta, NameableMissingObject, Pkg, Tpl}; use crate::{ diagnose::{Annotate, Diagnostic}, diagnostic_todo, @@ -169,6 +169,12 @@ impl Display for Ident { } } +impl NameableMissingObject for Ident { + fn missing(name: SPair) -> Self { + Self::Missing(name) + } +} + impl Ident { /// Identifier name. pub fn name(&self) -> SPair { diff --git a/tamer/src/asg/graph/object/pkg.rs b/tamer/src/asg/graph/object/pkg.rs index d1569e10..53b336c8 100644 --- a/tamer/src/asg/graph/object/pkg.rs +++ b/tamer/src/asg/graph/object/pkg.rs @@ -19,7 +19,7 @@ //! Package object on the ASG. -use super::{prelude::*, Doc, Ident, Tpl}; +use super::{prelude::*, Doc, Ident, NameableMissingObject, Tpl}; use crate::{ f::Functor, parse::{util::SPair, Token}, @@ -72,6 +72,12 @@ impl Display for Pkg { } } +impl NameableMissingObject for Pkg { + fn missing(pathspec: SPair) -> Self { + Self::new_imported(pathspec) + } +} + impl Functor for Pkg { fn map(self, f: impl FnOnce(Span) -> Span) -> Self::Target { match self { diff --git a/tamer/src/asg/graph/test.rs b/tamer/src/asg/graph/test.rs index 0caebf21..661ba6ae 100644 --- a/tamer/src/asg/graph/test.rs +++ b/tamer/src/asg/graph/test.rs @@ -19,6 +19,7 @@ use super::*; use crate::span::dummy::*; +use object::Ident; use std::convert::Infallible; type Sut = Asg;