tamer: asg::graph::object::ident::definition{=>_narrow} and new `definition`

This renames the previous operation to `definition_narrow` and creates a new
`definition` that does not attempt to narrow.  This is important for
distinguishing between a missing defintion and a definition of a given type,
and will be important now that we're beginning to reason about identifiers
that were resolved during parsing.

DEV-13163
main
Mike Gerwitz 2023-07-30 23:15:03 -04:00
parent 1c06605188
commit 087ef45153
6 changed files with 42 additions and 13 deletions

View File

@ -1105,7 +1105,7 @@ impl AirAggregateCtx {
Ident: ObjectRelTo<O>, Ident: ObjectRelTo<O>,
{ {
self.env_scope_lookup::<Ident>(env, name) self.env_scope_lookup::<Ident>(env, name)
.and_then(|oi| oi.definition(self.asg_ref())) .and_then(|oi| oi.definition_narrow(self.asg_ref()))
} }
/// Attempt to retrieve an identifier from the graph by name relative to /// Attempt to retrieve an identifier from the graph by name relative to

View File

@ -298,7 +298,7 @@ fn assert_concat_list<'a, IT, IE: 'a>(
.edges(asg) .edges(asg)
.filter_map(|rel| match rel { .filter_map(|rel| match rel {
MetaRel::Meta(oi) => Some(oi), MetaRel::Meta(oi) => Some(oi),
MetaRel::Ident(oi) => oi.definition::<Meta>(asg), MetaRel::Ident(oi) => oi.definition_narrow::<Meta>(asg),
MetaRel::Doc(_) => None, MetaRel::Doc(_) => None,
}) })
.map(ObjectIndex::cresolve(asg)) .map(ObjectIndex::cresolve(asg))
@ -362,7 +362,7 @@ where
"could not locate stub template (did you call \ "could not locate stub template (did you call \
air_ctx_from_tpl_body_toks without parse_as_tpl_body?)", air_ctx_from_tpl_body_toks without parse_as_tpl_body?)",
) )
.definition(ctx.asg_ref()) .definition_narrow(ctx.asg_ref())
.expect("missing stub template definition (test setup bug?)"); .expect("missing stub template definition (test setup bug?)");
(ctx, oi_tpl) (ctx, oi_tpl)

View File

@ -592,7 +592,7 @@ fn tpl_nested() {
let oi_tpl_inner_ident = let oi_tpl_inner_ident =
oi_tpl_outer.lookup_local_linear(&asg, id_tpl_inner); oi_tpl_outer.lookup_local_linear(&asg, id_tpl_inner);
let tpl_inner = oi_tpl_inner_ident let tpl_inner = oi_tpl_inner_ident
.and_then(|oi| oi.definition::<Tpl>(&asg)) .and_then(|oi| oi.definition_narrow::<Tpl>(&asg))
.map(ObjectIndex::cresolve(&asg)); .map(ObjectIndex::cresolve(&asg));
assert_eq!(S3.merge(S5), tpl_inner.map(Tpl::span)); assert_eq!(S3.merge(S5), tpl_inner.map(Tpl::span));
@ -699,7 +699,7 @@ fn metavars_within_exprs_hoisted_to_parent_tpl() {
let span_outer = ctx let span_outer = ctx
.env_scope_lookup::<Ident>(oi_outer, id_param_outer) .env_scope_lookup::<Ident>(oi_outer, id_param_outer)
.expect("missing id_param_outer Ident") .expect("missing id_param_outer Ident")
.definition::<Meta>(asg) .definition_narrow::<Meta>(asg)
.expect("missing id_param_outer definition") .expect("missing id_param_outer definition")
.resolve(asg) .resolve(asg)
.span(); .span();
@ -709,13 +709,13 @@ fn metavars_within_exprs_hoisted_to_parent_tpl() {
let oi_inner = ctx let oi_inner = ctx
.env_scope_lookup::<Ident>(oi_outer, id_tpl_inner) .env_scope_lookup::<Ident>(oi_outer, id_tpl_inner)
.expect("could not locate inner Tpl's Ident") .expect("could not locate inner Tpl's Ident")
.definition::<Tpl>(asg) .definition_narrow::<Tpl>(asg)
.expect("missing inner Tpl"); .expect("missing inner Tpl");
let span_inner = ctx let span_inner = ctx
.env_scope_lookup::<Ident>(oi_inner, id_param_inner) .env_scope_lookup::<Ident>(oi_inner, id_param_inner)
.expect("missing id_param_inner Ident") .expect("missing id_param_inner Ident")
.definition::<Meta>(asg) .definition_narrow::<Meta>(asg)
.expect("missing id_param_inner definition") .expect("missing id_param_inner definition")
.resolve(asg) .resolve(asg)
.span(); .span();
@ -780,7 +780,7 @@ fn expr_abstract_bind_produces_cross_edge_from_ident_to_meta() {
// The identifier should be bound to the expression. // The identifier should be bound to the expression.
let oi_expr = oi_ident let oi_expr = oi_ident
.definition::<Expr>(asg) .definition_narrow::<Expr>(asg)
.expect("abstract identifier did not bind to Expr"); .expect("abstract identifier did not bind to Expr");
assert_eq!(S3.merge(S5).unwrap(), oi_expr.resolve(asg).span()); assert_eq!(S3.merge(S5).unwrap(), oi_expr.resolve(asg).span());

View File

@ -185,7 +185,7 @@ fn tpl_apply_nested_missing() {
let oi_tpl_inner = oi_tpl_outer let oi_tpl_inner = oi_tpl_outer
.lookup_local_linear(&asg, id_tpl_inner) .lookup_local_linear(&asg, id_tpl_inner)
.expect("could not locate inner template as a local") .expect("could not locate inner template as a local")
.definition::<Tpl>(&asg) .definition_narrow::<Tpl>(&asg)
.expect("could not resolve inner template ref to Tpl"); .expect("could not resolve inner template ref to Tpl");
// We should have two inner template applications. // We should have two inner template applications.
@ -206,7 +206,7 @@ fn tpl_apply_nested_missing() {
inners inners
.iter() .iter()
.flat_map(|oi| oi.edges_filtered::<Ident>(&asg)) .flat_map(|oi| oi.edges_filtered::<Ident>(&asg))
.filter_map(|oi| oi.definition(&asg)) .filter_map(|oi| oi.definition_narrow(&asg))
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
); );
} }

View File

@ -1282,8 +1282,35 @@ impl ObjectIndex<Ident> {
/// Look up the definition that this identifier binds to, /// Look up the definition that this identifier binds to,
/// if any. /// if any.
/// ///
/// See [`Self::bind_definition`]. /// _It is assumed that an identifier has only a single definition_.
pub fn definition<O: ObjectRelFrom<Ident> + ObjectRelatable>( /// If this invariant is not upheld,
/// then the definition that is returned is undefined.
///
/// See also [`Self::bind_definition`] and [`Self::definition_narrow`].
pub fn definition(
&self,
asg: &Asg,
) -> Option<<Ident as ObjectRelatable>::Rel> {
self.edges(asg).next()
}
/// Look up the definition that this identifier binds to,
/// if any,
/// and attempt to narrow its type.
///
/// This can be read as "attempt to find a definition of this
/// [`ObjectKind`]".
/// It cannot distinguish between the absence of a definition,
/// and one that does not meet the provided criterion.
/// If this distinction is important,
/// see [`Self::definition`].
///
/// _It is assumed that an identifier has only a single definition_.
/// If this invariant is not upheld,
/// then the definition that is returned is undefined.
///
/// See also [`Self::bind_definition`].
pub fn definition_narrow<O: ObjectRelFrom<Ident> + ObjectRelatable>(
&self, &self,
asg: &Asg, asg: &Asg,
) -> Option<ObjectIndex<O>> { ) -> Option<ObjectIndex<O>> {

View File

@ -492,7 +492,9 @@ mod order {
Ident(oi_ident) => { Ident(oi_ident) => {
// This is the (comparatively) expensive lookup, // This is the (comparatively) expensive lookup,
// requiring a small graph traversal. // requiring a small graph traversal.
match oi_ident.definition::<object::Meta>(asg) { match oi_ident
.definition_narrow::<object::Meta>(asg)
{
Some(_) => TplOrder::Param, Some(_) => TplOrder::Param,
None => TplOrder::Body, None => TplOrder::Body,
} }