From 17589939dd4903573d1750b99fe1688395096c4b Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Mon, 31 Jul 2023 14:27:36 -0400 Subject: [PATCH] tamer: asg::graph::object::ident::IdentDefinition: New sum type This allows removing the `diagnostic_panic!` from the `Ident` edge handling for `Tpl`. I do need to create a generic sum type macro at some point, considering how extensively these are used. DEV-13163 --- tamer/src/asg/graph/object/ident.rs | 76 ++++++++++++++++++++++++++--- tamer/src/asg/graph/object/tpl.rs | 23 ++------- 2 files changed, 73 insertions(+), 26 deletions(-) diff --git a/tamer/src/asg/graph/object/ident.rs b/tamer/src/asg/graph/object/ident.rs index 4fcc1a10..770f9f83 100644 --- a/tamer/src/asg/graph/object/ident.rs +++ b/tamer/src/asg/graph/object/ident.rs @@ -1121,12 +1121,63 @@ object_rel! { // metavariable reference. dyn Ident, + // Be sure to update `IdentDefinition` below if necessary + // (the compiler will remind you if you try to use such an object + // in a definition without doing so). tree Expr, tree Tpl, tree Meta, } can_recurse(ident) if matches!(ident.kind(), Some(IdentKind::Func(..))) } +/// An [`ObjectKind`] that may serve as a definition of an [`Ident`]. +/// +/// This is a more refined [`IdentRel`], +/// which is defined by the [`object_rel!`] macro. +/// +/// See [`ObjectIndex::definition`]. +#[derive(Debug, PartialEq, Eq)] +pub enum IdentDefinition { + Expr(ObjectIndex), + Tpl(ObjectIndex), + Meta(ObjectIndex), +} + +impl IdentDefinition { + /// Attempt to narrow the definition into the requested type `O`, + /// returning [`None`] if the types do not match. + fn narrow(self) -> Option> + where + O: ObjectRelFrom, + { + use IdentDefinition::*; + + match self { + Expr(oi) => oi.filter_rel(), + Tpl(oi) => oi.filter_rel(), + Meta(oi) => oi.filter_rel(), + } + } +} + +impl From> for IdentDefinition { + fn from(oi: ObjectIndex) -> Self { + Self::Expr(oi) + } +} + +impl From> for IdentDefinition { + fn from(oi: ObjectIndex) -> Self { + Self::Tpl(oi) + } +} + +impl From> for IdentDefinition { + fn from(oi: ObjectIndex) -> Self { + Self::Meta(oi) + } +} + impl ObjectIndex { /// Declare a concrete identifier. /// @@ -1207,6 +1258,7 @@ impl ObjectIndex { ) -> Result, AsgError> where Ident: ObjectRelTo, + ObjectIndex: Into, { let my_span = self.into(); @@ -1287,13 +1339,21 @@ impl ObjectIndex { /// then the definition that is returned is undefined. /// /// See also [`Self::bind_definition`] and [`Self::definition_narrow`]. - pub fn definition( - &self, - asg: &Asg, - ) -> Option<::Rel> { - // XXX: This could return an abstract binding metavar reference - // depending on undefined edge ordering! - self.edges(asg).next() + pub fn definition(&self, asg: &Asg) -> Option { + // `find_map` is still important because, + // even though we should have only one definition, + // we _may_ have multiple edges, + // e.g. in the case of an abstract binding. + self.edges(asg).find_map(|rel| match rel { + IdentRel::Expr(oi) => Some(IdentDefinition::Expr(oi)), + IdentRel::Tpl(oi) => Some(IdentDefinition::Tpl(oi)), + IdentRel::Meta(oi) => Some(IdentDefinition::Meta(oi)), + + // Could be an abstract binding or an opaque dependency. + // In either case, + // it's no definition. + IdentRel::Ident(_) => None, + }) } /// Look up the definition that this identifier binds to, @@ -1316,7 +1376,7 @@ impl ObjectIndex { &self, asg: &Asg, ) -> Option> { - self.edges(asg).find_map(ObjectRel::narrow) + self.definition(asg).and_then(IdentDefinition::narrow) } /// Whether this identifier is bound to the object represented by `oi`. diff --git a/tamer/src/asg/graph/object/tpl.rs b/tamer/src/asg/graph/object/tpl.rs index 340b42cf..559aa9b6 100644 --- a/tamer/src/asg/graph/object/tpl.rs +++ b/tamer/src/asg/graph/object/tpl.rs @@ -21,13 +21,8 @@ use std::fmt::Display; -use super::{ident::IdentRel, prelude::*, Doc, Expr, Ident}; -use crate::{ - asg::graph::ProposedRel, - f::Map, - parse::{prelude::Annotate, util::SPair}, - span::Span, -}; +use super::{ident::IdentDefinition, prelude::*, Doc, Expr, Ident}; +use crate::{asg::graph::ProposedRel, f::Map, parse::util::SPair, span::Span}; /// Template with associated name. #[derive(Debug, PartialEq, Eq)] @@ -285,7 +280,7 @@ object_rel! { // TAME is referentally transparent, // so a reference to an Expr is no different than // inlining that Expr. - (Some(ref_span), Some(IdentRel::Expr(_))) => { + (Some(ref_span), Some(IdentDefinition::Expr(_))) => { rel.from_oi.try_map_obj_inner( asg, try_adapt_to(TplShape::Expr(ref_span), tpl_name), @@ -294,7 +289,7 @@ object_rel! { // This is the same as the `Tpl` tree edge below, // but a named template instead of an anonymous one. - (Some(ref_span), Some(IdentRel::Tpl(to_oi))) => { + (Some(ref_span), Some(IdentDefinition::Tpl(to_oi))) => { // TODO: Factor common logic between this and the // `Tpl->Tpl` edge below. let tpl_name = to_oi.name(asg); @@ -309,17 +304,9 @@ object_rel! { )?; } - // TODO: Filter this out (Ident -> Ident) - (Some(span), Some(IdentRel::Ident(_))) => { - diagnostic_todo!( - vec![span.internal_error("while parsing this reference")], - "opaque identifier or abstract binding" - ) - } - // The mere _existence_ of metavariables (template // params) do not influence the expansion shape. - (Some(_), Some(IdentRel::Meta(_))) => (), + (Some(_), Some(IdentDefinition::Meta(_))) => (), // Lack of span means that this is not a cross edge, // and so not a reference;