From 2d3b27ac0183bcb15686b5178e52e7461436c536 Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Tue, 31 Jan 2023 22:00:51 -0500 Subject: [PATCH] tamer: asg: Root package definition This causes a package definition to be rooted (so that it can be easily accessed for a graph walk). This keeps consistent with the new `ObjectIndex`-based API by introducing a unit `Root` `ObjectKind` and the boilerplate that goes with it. This boilerplate, now glaringly obvious, will be refactored at some point, since its repetition is onerous and distracting. DEV-13159 --- tamer/src/asg/air.rs | 2 +- tamer/src/asg/air/test.rs | 22 ++++--- tamer/src/asg/graph.rs | 21 ++++--- tamer/src/asg/graph/object.rs | 65 +++++++++++++++++++- tamer/src/asg/graph/object/expr.rs | 2 +- tamer/src/asg/graph/object/ident.rs | 2 +- tamer/src/asg/graph/object/pkg.rs | 4 +- tamer/src/asg/graph/object/root.rs | 93 +++++++++++++++++++++++++++++ tamer/src/ld/xmle/lower.rs | 4 +- 9 files changed, 189 insertions(+), 26 deletions(-) create mode 100644 tamer/src/asg/graph/object/root.rs diff --git a/tamer/src/asg/air.rs b/tamer/src/asg/air.rs index 4a494284..e5138b43 100644 --- a/tamer/src/asg/air.rs +++ b/tamer/src/asg/air.rs @@ -488,7 +488,7 @@ impl ParseState for AirAggregate { (st, Todo) => Transition(st).incomplete(), (Empty(es), PkgOpen(span)) => { - let oi_pkg = asg.create(Pkg::new(span)); + let oi_pkg = asg.create(Pkg::new(span)).root(asg); Transition(PkgDfn(oi_pkg, es)).incomplete() } diff --git a/tamer/src/asg/air/test.rs b/tamer/src/asg/air/test.rs index 4bb24805..b4b78711 100644 --- a/tamer/src/asg/air/test.rs +++ b/tamer/src/asg/air/test.rs @@ -247,14 +247,22 @@ fn ident_root_existing() { } #[test] -fn empty_pkg() { +fn pkg_is_rooted() { let toks = vec![Air::PkgOpen(S1), Air::PkgClose(S2)]; let mut sut = Sut::parse(toks.into_iter()); assert!(sut.all(|x| x.is_ok())); - // TODO: Packages aren't named via AIR at the time of writing, - // so we can't look anything up. + let asg = sut.finalize().unwrap().into_context(); + + let oi_root = asg.root(S3); + let pkg = oi_root + .edges_filtered::(&asg) + .next() + .expect("missing rooted package") + .resolve(&asg); + + assert_eq!(pkg.span(), S1); } #[test] @@ -785,8 +793,7 @@ fn sibling_subexprs_have_ordered_edges_to_parent() { let oi_root = asg.expect_ident_oi::(id_root); let siblings = oi_root - .edges(&asg) - .filter_map(ObjectRel::narrow::) + .edges_filtered::(&asg) .map(ObjectIndex::cresolve(&asg)) .collect::>(); @@ -987,10 +994,7 @@ fn expr_ref_to_ident() { let oi_foo = asg.expect_ident_oi::(id_foo); - let foo_refs = oi_foo - .edges(&asg) - .filter_map(ObjectRel::narrow::) - .collect::>(); + let foo_refs = oi_foo.edges_filtered::(&asg).collect::>(); // We should have only a single reference (to `id_bar`). assert_eq!(foo_refs.len(), 1); diff --git a/tamer/src/asg/graph.rs b/tamer/src/asg/graph.rs index 356d768b..b086baf1 100644 --- a/tamer/src/asg/graph.rs +++ b/tamer/src/asg/graph.rs @@ -19,7 +19,7 @@ //! Abstract semantic graph. -use self::object::{ObjectRelFrom, ObjectRelTy, ObjectRelatable}; +use self::object::{ObjectRelFrom, ObjectRelTy, ObjectRelatable, Root}; use super::{ AsgError, FragmentText, Ident, IdentKind, Object, ObjectIndex, ObjectKind, @@ -31,6 +31,7 @@ use crate::{ fmt::{DisplayWrapper, TtQuote}, global, parse::{util::SPair, Token}, + span::Span, sym::SymbolId, }; use petgraph::{ @@ -155,13 +156,13 @@ impl Asg { // Exhaust the first index to be used as a placeholder // (its value does not matter). - let empty_node = graph.add_node(Object::Root.into()); + let empty_node = graph.add_node(Object::Root(Root).into()); index.push(empty_node); // Automatically add the root which will be used to determine what // identifiers ought to be retained by the final program. // This is not indexed and is not accessable by name. - let root_node = graph.add_node(Object::Root.into()); + let root_node = graph.add_node(Object::Root(Root).into()); Self { graph, @@ -276,10 +277,16 @@ impl Asg { .map_err(Into::into) } - // TODO: This is transitional; - // remove once [`crate::xmlo::asg_builder`] is removed. - pub fn root(&self) -> NodeIndex { - self.root_node + /// Root object. + /// + /// All [`Object`]s reachable from the root will be included in the + /// compilation unit or linked executable. + /// + /// The `witness` is used in the returned [`ObjectIndex`] and is + /// intended for diagnostic purposes to highlight the source entity that + /// triggered the request of the root. + pub fn root(&self, witness: Span) -> ObjectIndex { + ObjectIndex::new(self.root_node, witness) } /// Add an object as a root. diff --git a/tamer/src/asg/graph/object.rs b/tamer/src/asg/graph/object.rs index a60d0f23..b025d959 100644 --- a/tamer/src/asg/graph/object.rs +++ b/tamer/src/asg/graph/object.rs @@ -120,10 +120,12 @@ use std::{convert::Infallible, fmt::Display, marker::PhantomData}; pub mod expr; pub mod ident; pub mod pkg; +pub mod root; pub use expr::Expr; pub use ident::Ident; pub use pkg::Pkg; +pub use root::Root; /// An object on the ASG. /// @@ -136,7 +138,7 @@ pub enum Object { /// the final executable. /// /// There should be only one object of this kind. - Root, + Root(Root), /// A package of identifiers. Pkg(Pkg), @@ -169,7 +171,7 @@ pub enum ObjectRelTy { impl Display for Object { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { - Self::Root => write!(f, "root ASG node"), + Self::Root(_) => write!(f, "root ASG node"), Self::Pkg(pkg) => Display::fmt(pkg, f), Self::Ident(ident) => Display::fmt(ident, f), Self::Expr(expr) => Display::fmt(expr, f), @@ -180,7 +182,7 @@ impl Display for Object { impl Object { pub fn span(&self) -> Span { match self { - Self::Root => UNKNOWN_SPAN, + Self::Root(_) => UNKNOWN_SPAN, Self::Pkg(pkg) => pkg.span(), Self::Ident(ident) => ident.span(), Self::Expr(expr) => expr.span(), @@ -245,6 +247,12 @@ impl From<&Object> for Span { } } +impl From for Object { + fn from(root: Root) -> Self { + Self::Root(root) + } +} + impl From for Object { fn from(pkg: Pkg) -> Self { Self::Pkg(pkg) @@ -263,6 +271,17 @@ impl From for Object { } } +impl From for Root { + /// Narrow an object into an [`Root`], + /// panicing if the object is not of that type. + fn from(val: Object) -> Self { + match val { + Object::Root(root) => root, + _ => val.narrowing_panic("the root"), + } + } +} + impl From for Pkg { /// Narrow an object into an [`Ident`], /// panicing if the object is not of that type. @@ -302,6 +321,15 @@ impl AsRef for Object { } } +impl AsRef for Object { + fn as_ref(&self) -> &Root { + match self { + Object::Root(ref root) => root, + _ => self.narrowing_panic("the root"), + } + } +} + impl AsRef for Object { fn as_ref(&self) -> &Pkg { match self { @@ -449,6 +477,21 @@ impl ObjectIndex { asg.edges(self) } + /// Iterate over the [`ObjectIndex`]es of the outgoing edges of `self` + /// that match the [`ObjectKind`] `OB`. + /// + /// This is simply a shorthand for applying [`ObjectRel::narrow`] via + /// [`Iterator::filter_map`]. + pub fn edges_filtered<'a, OB: ObjectKind + ObjectRelatable + 'a>( + self, + asg: &'a Asg, + ) -> impl Iterator> + 'a + where + O: ObjectRelTo + 'a, + { + self.edges(asg).filter_map(ObjectRel::narrow::) + } + /// Incoming edges to self filtered by [`ObjectKind`] `OI`. /// /// For filtering rationale, @@ -542,6 +585,22 @@ impl ObjectIndex { Some(ObjectIndex::(index, span, PhantomData::default())) .filter(|_| O::rel_ty() == OB::rel_ty()) } + + /// Root this object in the ASG. + /// + /// A rooted object is forced to be reachable. + /// This should only be utilized when necessary for toplevel objects; + /// other objects should be reachable via their relations to other + /// 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) -> Self + where + Root: ObjectRelTo, + { + asg.root(self.span()).add_edge_to(asg, self); + self + } } impl ObjectIndex { diff --git a/tamer/src/asg/graph/object/expr.rs b/tamer/src/asg/graph/object/expr.rs index f986814c..82103076 100644 --- a/tamer/src/asg/graph/object/expr.rs +++ b/tamer/src/asg/graph/object/expr.rs @@ -219,9 +219,9 @@ impl ObjectRelatable for Expr { ) -> Option { match ty { ObjectRelTy::Root => None, + ObjectRelTy::Pkg => None, ObjectRelTy::Ident => Some(ExprRel::Ident(oi.must_narrow_into())), ObjectRelTy::Expr => Some(ExprRel::Expr(oi.must_narrow_into())), - ObjectRelTy::Pkg => None, } } } diff --git a/tamer/src/asg/graph/object/ident.rs b/tamer/src/asg/graph/object/ident.rs index 23fec91c..a8e40d19 100644 --- a/tamer/src/asg/graph/object/ident.rs +++ b/tamer/src/asg/graph/object/ident.rs @@ -1000,9 +1000,9 @@ impl ObjectRelatable for Ident { ) -> Option { match ty { ObjectRelTy::Root => None, + ObjectRelTy::Pkg => None, ObjectRelTy::Ident => Some(IdentRel::Ident(oi.must_narrow_into())), ObjectRelTy::Expr => Some(IdentRel::Expr(oi.must_narrow_into())), - ObjectRelTy::Pkg => None, } } } diff --git a/tamer/src/asg/graph/object/pkg.rs b/tamer/src/asg/graph/object/pkg.rs index 5c716e8f..207d27fb 100644 --- a/tamer/src/asg/graph/object/pkg.rs +++ b/tamer/src/asg/graph/object/pkg.rs @@ -78,9 +78,9 @@ impl ObjectRelatable for Pkg { fn new_rel_dyn(ty: ObjectRelTy, oi: ObjectIndex) -> Option { match ty { ObjectRelTy::Root => None, + ObjectRelTy::Pkg => None, ObjectRelTy::Ident => Some(PkgRel::Ident(oi.must_narrow_into())), ObjectRelTy::Expr => None, - ObjectRelTy::Pkg => None, } } } @@ -93,7 +93,7 @@ impl From> for PkgRel { impl ObjectIndex { /// Indicate that the given identifier `oi` is defined in this package. - pub fn defines(&self, asg: &mut Asg, oi: ObjectIndex) -> Self { + pub fn defines(self, asg: &mut Asg, oi: ObjectIndex) -> Self { self.add_edge_to(asg, oi) } } diff --git a/tamer/src/asg/graph/object/root.rs b/tamer/src/asg/graph/object/root.rs new file mode 100644 index 00000000..f332e2eb --- /dev/null +++ b/tamer/src/asg/graph/object/root.rs @@ -0,0 +1,93 @@ +// Root of the ASG. +// +// Copyright (C) 2014-2023 Ryan Specialty, LLC. +// +// This file is part of TAME. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Root node of the ASG. + +use std::fmt::Display; + +use super::{ + Ident, Object, ObjectIndex, ObjectKind, ObjectRel, ObjectRelTy, + ObjectRelatable, Pkg, +}; + +/// A unit [`Object`] type representing the root node. +/// +/// This exists for consistency with the rest of the object API, +/// and for use with [`ObjectIndex`]. +#[derive(Debug, PartialEq)] +pub struct Root; + +impl Display for Root { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "ASG root") + } +} + +/// Subset of [`ObjectKind`]s that are valid targets for edges from +/// [`Ident`]. +/// +/// See [`ObjectRel`] for more information. +#[derive(Debug, PartialEq, Eq)] +pub enum RootRel { + Pkg(ObjectIndex), + Ident(ObjectIndex), +} + +impl ObjectRel for RootRel { + fn narrow( + self, + ) -> Option> { + match self { + Self::Ident(oi) => oi.filter_rel(), + Self::Pkg(oi) => oi.filter_rel(), + } + } +} + +impl ObjectRelatable for Root { + type Rel = RootRel; + + fn rel_ty() -> ObjectRelTy { + ObjectRelTy::Root + } + + fn new_rel_dyn( + ty: ObjectRelTy, + oi: ObjectIndex, + ) -> Option { + match ty { + ObjectRelTy::Root => None, + ObjectRelTy::Pkg => Some(RootRel::Pkg(oi.must_narrow_into())), + ObjectRelTy::Ident => Some(RootRel::Ident(oi.must_narrow_into())), + ObjectRelTy::Expr => None, + } + } +} + +impl From> for RootRel { + fn from(value: ObjectIndex) -> Self { + Self::Pkg(value) + } +} + +impl From> for RootRel { + fn from(value: ObjectIndex) -> Self { + Self::Ident(value) + } +} diff --git a/tamer/src/ld/xmle/lower.rs b/tamer/src/ld/xmle/lower.rs index f7c03108..47f25660 100644 --- a/tamer/src/ld/xmle/lower.rs +++ b/tamer/src/ld/xmle/lower.rs @@ -55,7 +55,7 @@ where // TODO: Encapsulate petgraph. // This is technically a topological sort, but functions have cycles. - let mut dfs = DfsPostOrder::new(&asg.graph, asg.root()); + let mut dfs = DfsPostOrder::new(&asg.graph, asg.root(UNKNOWN_SPAN).into()); // These are always generated by the map compiler, // but do not have edges that would allow them to be properly ordered @@ -69,7 +69,7 @@ where let oi = ObjectIndex::::new(index, UNKNOWN_SPAN); match asg.get(oi).expect("missing object") { - Object::Root => (), + Object::Root(_) => (), Object::Ident(ident) => dest.push(ident)?, obj @ (Object::Pkg(_) | Object::Expr(_)) => {