tamer: asg::air: Extract abstract definition into context

This logic ought to live alongside other definition logic...which in turn
needs its own extraction, but that's a separate concern.

This makes the definition of abstract identifiers very similar to
concrete.  But, treating these as dangling, even if that's technically true,
has to change---we still want an edge drawn to the abstract identifier via
e.g. a template since we want the graph to mirror the structure of what it
will expand into concretely.  I didn't notice this problem until trying to
generate the xmli for it.

So, see the commit to follow.

DEV-13163
main
Mike Gerwitz 2023-07-13 11:16:10 -04:00
parent b4b85a5e85
commit 760223f0c9
4 changed files with 78 additions and 54 deletions

View File

@ -929,19 +929,81 @@ impl AirAggregateCtx {
})
}
/// Root an identifier using the [`Self::rooting_oi`] atop of the stack.
fn defines(&mut self, name: SPair) -> Result<ObjectIndex<Ident>, AsgError> {
/// Root a concrete identifier using the [`Self::rooting_oi`] atop of
/// the stack.
///
/// This definition will index the identifier into the proper
/// environments,
/// giving it scope.
/// If the identifier is abstract,
/// it is important to use [`Self::defines_abstract`] instead so that
/// the metavariable that the identifier references will not be
/// indexed as the binding `name`.
fn defines_concrete(
&mut self,
binding_name: SPair,
) -> Result<ObjectIndex<Ident>, AsgError> {
let oi_root = self
.rooting_oi()
.ok_or(AsgError::InvalidBindContext(name))?;
.ok_or(AsgError::InvalidBindContext(binding_name))?;
Ok(self.lookup_lexical_or_missing(name).add_edge_from(
Ok(self.lookup_lexical_or_missing(binding_name).add_edge_from(
self.asg_mut(),
oi_root,
None,
))
}
/// Define an abstract identifier within the context of a container that
/// is able to hold dangling objects.
///
/// If the identifier is concrete,
/// then it is important to use [`Self::defines_concrete`] instead to
/// ensure that the identifier has its scope computed and indexed.
///
/// TODO: This is about to evolve;
/// document further.
fn defines_abstract(
&mut self,
meta_name: SPair,
) -> Result<ObjectIndex<Ident>, AsgError> {
// To help mitigate potentially cryptic errors down the line,
// let's try to be helpful and notify the user when
// they're trying to do something that almost certainly
// will not succeed.
match self.dangling_expr_oi() {
// The container does not support dangling expressions
// and so there is no chance that this expression will
// be expanded in the future.
None => {
let rooting_span = self
.rooting_oi()
.map(|oi| oi.widen().resolve(self.asg_ref()).span());
// Note that we _discard_ the attempted bind token
// and so remain in a dangling state.
Err(AsgError::InvalidAbstractBindContext(
meta_name,
rooting_span,
))
}
// We don't care what our container is,
// only that the above check passed.
// That is:
// the above check is entirely optional and intended
// only as a debugging aid for users.
Some(_) => {
let oi_meta_ident = self.lookup_lexical_or_missing(meta_name);
let oi_abstract = oi_meta_ident
.new_abstract_ident(self.asg_mut(), meta_name.span());
Ok(oi_abstract)
}
}
}
/// Attempt to retrieve an identifier and its scope information from the
/// graph by name relative to the environment `env`.
///

View File

@ -123,7 +123,7 @@ impl ParseState for AirExprAggregate {
}
(BuildingExpr(es, oi), AirBind(BindIdent(id))) => {
let result = ctx.defines(id).and_then(|oi_ident| {
let result = ctx.defines_concrete(id).and_then(|oi_ident| {
oi_ident.bind_definition(ctx.asg_mut(), id, oi)
});
@ -139,53 +139,15 @@ impl ParseState for AirExprAggregate {
}
(BuildingExpr(es, oi), AirBind(BindIdentAbstract(meta_name))) => {
// To help mitigate potentially cryptic errors down the line,
// let's try to be helpful and notify the user when
// they're trying to do something that almost certainly
// will not succeed.
match ctx.dangling_expr_oi() {
// The container does not support dangling expressions
// and so there is no chance that this expression will
// be expanded in the future.
None => {
let rooting_span = ctx
.rooting_oi()
.map(|oi| oi.widen().resolve(ctx.asg_ref()).span());
// Note that we _discard_ the attempted bind token
// and so remain in a dangling state.
Transition(BuildingExpr(es, oi)).err(
AsgError::InvalidAbstractBindContext(
meta_name,
rooting_span,
),
)
}
// We don't care what our container is,
// only that the above check passed.
// That is:
// the above check is entirely optional and intended
// only as a debugging aid for users.
Some(_) => {
let oi_meta_ident =
ctx.lookup_lexical_or_missing(meta_name);
let oi_abstract = oi_meta_ident.new_abstract_ident(
ctx.asg_mut(),
meta_name.span(),
);
// Note that we are still dangling,
// since the identifier is abstract and is
// therefore not yet reachable via its
// yet-to-be-determined identifier.
oi_abstract
.bind_definition(ctx.asg_mut(), meta_name, oi)
.map(|_| ())
.transition(BuildingExpr(es, oi))
}
}
// Note that we are still dangling,
// since the identifier is abstract and is therefore not
// yet reachable via its yet-to-be-determined identifier.
ctx.defines_abstract(meta_name)
.and_then(|oi_ident| {
oi_ident.bind_definition(ctx.asg_mut(), meta_name, oi)
})
.map(|_| ())
.transition(BuildingExpr(es, oi))
}
(BuildingExpr(es, oi), AirBind(RefIdent(name))) => {

View File

@ -81,7 +81,7 @@ impl ParseState for AirMetaAggregate {
.incomplete(),
(TplMeta(oi_meta), AirBind(BindIdent(name))) => ctx
.defines(name)
.defines_concrete(name)
.and_then(|oi_ident| {
oi_ident.bind_definition(ctx.asg_mut(), name, oi_meta)
})

View File

@ -175,7 +175,7 @@ impl ParseState for AirTplAggregate {
}
(Toplevel(tpl), AirBind(BindIdent(id))) => ctx
.defines(id)
.defines_concrete(id)
.and_then(|oi_ident| {
oi_ident.bind_definition(ctx.asg_mut(), id, tpl.oi())
})