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. /// Root a concrete identifier using the [`Self::rooting_oi`] atop of
fn defines(&mut self, name: SPair) -> Result<ObjectIndex<Ident>, AsgError> { /// 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 let oi_root = self
.rooting_oi() .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(), self.asg_mut(),
oi_root, oi_root,
None, 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 /// Attempt to retrieve an identifier and its scope information from the
/// graph by name relative to the environment `env`. /// graph by name relative to the environment `env`.
/// ///

View File

@ -123,7 +123,7 @@ impl ParseState for AirExprAggregate {
} }
(BuildingExpr(es, oi), AirBind(BindIdent(id))) => { (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) oi_ident.bind_definition(ctx.asg_mut(), id, oi)
}); });
@ -139,53 +139,15 @@ impl ParseState for AirExprAggregate {
} }
(BuildingExpr(es, oi), AirBind(BindIdentAbstract(meta_name))) => { (BuildingExpr(es, oi), AirBind(BindIdentAbstract(meta_name))) => {
// To help mitigate potentially cryptic errors down the line, // Note that we are still dangling,
// let's try to be helpful and notify the user when // since the identifier is abstract and is therefore not
// they're trying to do something that almost certainly // yet reachable via its yet-to-be-determined identifier.
// will not succeed. ctx.defines_abstract(meta_name)
match ctx.dangling_expr_oi() { .and_then(|oi_ident| {
// The container does not support dangling expressions oi_ident.bind_definition(ctx.asg_mut(), meta_name, oi)
// and so there is no chance that this expression will })
// be expanded in the future. .map(|_| ())
None => { .transition(BuildingExpr(es, oi))
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))
}
}
} }
(BuildingExpr(es, oi), AirBind(RefIdent(name))) => { (BuildingExpr(es, oi), AirBind(RefIdent(name))) => {

View File

@ -81,7 +81,7 @@ impl ParseState for AirMetaAggregate {
.incomplete(), .incomplete(),
(TplMeta(oi_meta), AirBind(BindIdent(name))) => ctx (TplMeta(oi_meta), AirBind(BindIdent(name))) => ctx
.defines(name) .defines_concrete(name)
.and_then(|oi_ident| { .and_then(|oi_ident| {
oi_ident.bind_definition(ctx.asg_mut(), name, oi_meta) 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 (Toplevel(tpl), AirBind(BindIdent(id))) => ctx
.defines(id) .defines_concrete(id)
.and_then(|oi_ident| { .and_then(|oi_ident| {
oi_ident.bind_definition(ctx.asg_mut(), id, tpl.oi()) oi_ident.bind_definition(ctx.asg_mut(), id, tpl.oi())
}) })