tamer: asg::air: Index package identifiers on `Pkg` rather than `Root`
I've been torturing myself trying to figure out how I want to generalize indexing, lookups, and value numbering in a way that is appropriate for this project (that is, not over-engineered relative to my needs). Before I can do much of anything, though, I need to stop having indexing only as a `Root` thing (previously it wasn't even tied to `Root`). This makes that change for tamec, but temporarily removes scoping concerns until I can add more specific types of indexing. Not only does this allow cleaning up some `Ident`-specific stuff from `Asg`, but the cleanup also helps to show that portions of the system aren't still using Root-based globals. The linker (`tameld`) still uses the old `global` methods for now; those will eventually go away, but this needs to change to unify both tamec and tameld once we get to imports as part of the compiler. DEV-13162main
parent
c367666d8e
commit
778e90c81d
|
@ -537,39 +537,25 @@ impl AirAggregateCtx {
|
|||
fn lookup_lexical_or_missing(&mut self, name: SPair) -> ObjectIndex<Ident> {
|
||||
let Self(asg, stack, _) = self;
|
||||
|
||||
let found = stack
|
||||
stack
|
||||
.iter()
|
||||
.skip(1)
|
||||
.rev()
|
||||
.filter_map(|st| st.active_rooting_oi())
|
||||
.find_map(|oi| asg.lookup(oi, name));
|
||||
|
||||
// Rust's borrow checker won't allow us to use unwrap_or_else above
|
||||
// as of 2023-04.
|
||||
if let Some(oi) = found {
|
||||
oi
|
||||
} else {
|
||||
// TODO: This special case can be removed once we generalize
|
||||
// indexing/scope.
|
||||
// TODO: This filtering is a quick kludge to get things working!
|
||||
match stack.iter().filter_map(|st| st.active_rooting_oi()).nth(1) {
|
||||
None => asg.lookup_global_or_missing(name),
|
||||
_ => self.create_env_indexed_ident(name),
|
||||
}
|
||||
}
|
||||
.find_map(|oi| asg.lookup(oi, name))
|
||||
.unwrap_or_else(|| self.create_env_indexed_ident(name))
|
||||
}
|
||||
|
||||
/// Index an identifier within its environment.
|
||||
///
|
||||
/// TODO: More information as this is formalized.
|
||||
fn create_env_indexed_ident(&mut self, name: SPair) -> ObjectIndex<Ident> {
|
||||
let Self(asg, stack, _) = self;
|
||||
let oi_ident = asg.create(Ident::declare(name));
|
||||
let oi_ident = self.asg_mut().create(Ident::declare(name));
|
||||
|
||||
stack
|
||||
.iter()
|
||||
.filter_map(|st| st.active_rooting_oi())
|
||||
.for_each(|oi| asg.index_identifier(oi, name, oi_ident));
|
||||
// TODO: This currently only indexes for the top of the stack,
|
||||
// but we'll want no-shadow records for the rest of the env.
|
||||
if let Some(oi) = self.rooting_oi() {
|
||||
self.asg_mut().index_identifier(oi, name, oi_ident);
|
||||
}
|
||||
|
||||
oi_ident
|
||||
}
|
||||
|
|
|
@ -138,9 +138,13 @@ impl ParseState for AirExprAggregate {
|
|||
}
|
||||
}
|
||||
|
||||
(BuildingExpr(es, oi), AirBind(RefIdent(ident))) => {
|
||||
Transition(BuildingExpr(es, oi.ref_expr(ctx.asg_mut(), ident)))
|
||||
.incomplete()
|
||||
(BuildingExpr(es, oi), AirBind(RefIdent(name))) => {
|
||||
let oi_ident = ctx.lookup_lexical_or_missing(name);
|
||||
Transition(BuildingExpr(
|
||||
es,
|
||||
oi.ref_expr(ctx.asg_mut(), oi_ident),
|
||||
))
|
||||
.incomplete()
|
||||
}
|
||||
|
||||
(BuildingExpr(es, oi), AirDoc(DocIndepClause(clause))) => {
|
||||
|
|
|
@ -20,7 +20,10 @@
|
|||
use super::*;
|
||||
use crate::asg::{
|
||||
air::{
|
||||
test::{asg_from_toks, parse_as_pkg_body},
|
||||
test::{
|
||||
asg_from_toks, parse_as_pkg_body, pkg_expect_ident_obj,
|
||||
pkg_expect_ident_oi, pkg_get_ident_obj, pkg_lookup,
|
||||
},
|
||||
Air, AirAggregate,
|
||||
},
|
||||
graph::object::{expr::ExprRel, Doc, ObjectRel},
|
||||
|
@ -59,7 +62,7 @@ fn expr_empty_ident() {
|
|||
|
||||
// The expression should have been bound to this identifier so that
|
||||
// we're able to retrieve it from the graph by name.
|
||||
let expr = asg.expect_ident_obj::<Expr>(id);
|
||||
let expr = pkg_expect_ident_obj::<Expr>(&asg, id);
|
||||
assert_eq!(expr.span(), S1.merge(S3).unwrap());
|
||||
}
|
||||
|
||||
|
@ -182,11 +185,11 @@ fn expr_non_empty_ident_root() {
|
|||
|
||||
let asg = sut.finalize().unwrap().into_context();
|
||||
|
||||
let expr_a = asg.expect_ident_obj::<Expr>(id_a);
|
||||
let expr_a = pkg_expect_ident_obj::<Expr>(&asg, id_a);
|
||||
assert_eq!(expr_a.span(), S1.merge(S6).unwrap());
|
||||
|
||||
// Identifiers should reference the same expression.
|
||||
let expr_b = asg.expect_ident_obj::<Expr>(id_b);
|
||||
let expr_b = pkg_expect_ident_obj::<Expr>(&asg, id_b);
|
||||
assert_eq!(expr_a, expr_b);
|
||||
}
|
||||
|
||||
|
@ -216,7 +219,7 @@ fn expr_non_empty_bind_only_after() {
|
|||
|
||||
let asg = sut.finalize().unwrap().into_context();
|
||||
|
||||
let expr = asg.expect_ident_obj::<Expr>(id);
|
||||
let expr = pkg_expect_ident_obj::<Expr>(&asg, id);
|
||||
assert_eq!(expr.span(), S1.merge(S5).unwrap());
|
||||
}
|
||||
|
||||
|
@ -402,7 +405,7 @@ fn recovery_expr_reachable_after_dangling() {
|
|||
|
||||
// Let's make sure that we _actually_ added it to the graph,
|
||||
// despite the previous error.
|
||||
let expr = asg.expect_ident_obj::<Expr>(id);
|
||||
let expr = pkg_expect_ident_obj::<Expr>(&asg, id);
|
||||
assert_eq!(expr.span(), S3.merge(S5).unwrap());
|
||||
|
||||
// The dangling expression may or may not be on the graph,
|
||||
|
@ -457,7 +460,7 @@ fn expr_close_unbalanced() {
|
|||
let asg = sut.finalize().unwrap().into_context();
|
||||
|
||||
// Just verify that the expression was successfully added after recovery.
|
||||
let expr = asg.expect_ident_obj::<Expr>(id);
|
||||
let expr = pkg_expect_ident_obj::<Expr>(&asg, id);
|
||||
assert_eq!(expr.span(), S2.merge(S4).unwrap());
|
||||
}
|
||||
|
||||
|
@ -529,11 +532,11 @@ fn expr_bind_to_empty() {
|
|||
|
||||
// Neither of the identifiers outside of expressions should exist on the
|
||||
// graph.
|
||||
assert_eq!(None, asg.get_ident_obj::<Expr>(id_noexpr_a));
|
||||
assert_eq!(None, asg.get_ident_obj::<Expr>(id_noexpr_b));
|
||||
assert_eq!(None, pkg_get_ident_obj::<Expr>(&asg, id_noexpr_a));
|
||||
assert_eq!(None, pkg_get_ident_obj::<Expr>(&asg, id_noexpr_b));
|
||||
|
||||
// Verify that the expression was successfully added after recovery.
|
||||
let expr = asg.expect_ident_obj::<Expr>(id_good);
|
||||
let expr = pkg_expect_ident_obj::<Expr>(&asg, id_good);
|
||||
assert_eq!(expr.span(), S5.merge(S7).unwrap());
|
||||
}
|
||||
|
||||
|
@ -573,7 +576,7 @@ fn sibling_subexprs_have_ordered_edges_to_parent() {
|
|||
// (the siblings above).
|
||||
// Note that we retrieve its _index_,
|
||||
// not the object itself.
|
||||
let oi_root = asg.expect_ident_oi::<Expr>(id_root);
|
||||
let oi_root = pkg_expect_ident_oi::<Expr>(&asg, id_root);
|
||||
|
||||
let siblings = oi_root
|
||||
.edges_filtered::<Expr>(&asg)
|
||||
|
@ -611,7 +614,7 @@ fn nested_subexprs_related_to_relative_parent() {
|
|||
|
||||
let asg = asg_from_toks(toks);
|
||||
|
||||
let oi_0 = asg.expect_ident_oi::<Expr>(id_root);
|
||||
let oi_0 = pkg_expect_ident_oi::<Expr>(&asg, id_root);
|
||||
let subexprs_0 = collect_subexprs(&asg, oi_0);
|
||||
|
||||
// Subexpr 1
|
||||
|
@ -669,7 +672,7 @@ fn expr_redefine_ident() {
|
|||
let asg = sut.finalize().unwrap().into_context();
|
||||
|
||||
// The identifier should continue to reference the first expression.
|
||||
let expr = asg.expect_ident_obj::<Expr>(id_first);
|
||||
let expr = pkg_expect_ident_obj::<Expr>(&asg, id_first);
|
||||
assert_eq!(expr.span(), S1.merge(S5).unwrap());
|
||||
}
|
||||
|
||||
|
@ -752,7 +755,7 @@ fn expr_still_dangling_on_redefine() {
|
|||
let asg = sut.finalize().unwrap().into_context();
|
||||
|
||||
// The identifier should continue to reference the first expression.
|
||||
let expr = asg.expect_ident_obj::<Expr>(id_first);
|
||||
let expr = pkg_expect_ident_obj::<Expr>(&asg, id_first);
|
||||
assert_eq!(expr.span(), S1.merge(S3).unwrap());
|
||||
|
||||
// There's nothing we can do using the ASG's public API at the time of
|
||||
|
@ -760,7 +763,7 @@ fn expr_still_dangling_on_redefine() {
|
|||
|
||||
// The second identifier should have been successfully bound despite the
|
||||
// failed initial attempt.
|
||||
let expr = asg.expect_ident_obj::<Expr>(id_second);
|
||||
let expr = pkg_expect_ident_obj::<Expr>(&asg, id_second);
|
||||
assert_eq!(expr.span(), S7.merge(S10).unwrap());
|
||||
}
|
||||
|
||||
|
@ -791,7 +794,7 @@ fn expr_ref_to_ident() {
|
|||
|
||||
let asg = asg_from_toks(toks);
|
||||
|
||||
let oi_foo = asg.expect_ident_oi::<Expr>(id_foo);
|
||||
let oi_foo = pkg_expect_ident_oi::<Expr>(&asg, id_foo);
|
||||
|
||||
let mut foo_rels = oi_foo
|
||||
.edges(&asg)
|
||||
|
@ -816,7 +819,7 @@ fn expr_ref_to_ident() {
|
|||
// added it as `Missing`.
|
||||
assert_eq!(ident_bar.span(), id_bar.span());
|
||||
|
||||
let oi_expr_bar = asg.expect_ident_oi::<Expr>(id_bar);
|
||||
let oi_expr_bar = pkg_expect_ident_oi::<Expr>(&asg, id_bar);
|
||||
assert!(oi_ident_bar.is_bound_to(&asg, oi_expr_bar));
|
||||
}
|
||||
|
||||
|
@ -845,14 +848,14 @@ fn idents_share_defining_pkg() {
|
|||
assert!(sut.all(|x| x.is_ok()));
|
||||
let asg = sut.finalize().unwrap().into_context();
|
||||
|
||||
let oi_foo = asg.lookup_global(id_foo).unwrap();
|
||||
let oi_bar = asg.lookup_global(id_bar).unwrap();
|
||||
let oi_foo = pkg_lookup(&asg, id_foo).unwrap();
|
||||
let oi_bar = pkg_lookup(&asg, id_bar).unwrap();
|
||||
|
||||
assert_eq!(oi_foo.src_pkg(&asg).unwrap(), oi_bar.src_pkg(&asg).unwrap());
|
||||
|
||||
// Missing identifiers should not have a source package,
|
||||
// since we don't know what defined it yet.
|
||||
let oi_baz = asg.lookup_global(id_baz).unwrap();
|
||||
let oi_baz = pkg_lookup(&asg, id_baz).unwrap();
|
||||
assert_eq!(None, oi_baz.src_pkg(&asg));
|
||||
|
||||
// The package span should encompass the entire definition.
|
||||
|
@ -877,7 +880,7 @@ fn expr_doc_short_desc() {
|
|||
|
||||
let asg = asg_from_toks(toks);
|
||||
|
||||
let oi_expr = asg.expect_ident_oi::<Expr>(id_expr);
|
||||
let oi_expr = pkg_expect_ident_oi::<Expr>(&asg, id_expr);
|
||||
let oi_docs = oi_expr
|
||||
.edges_filtered::<Doc>(&asg)
|
||||
.map(ObjectIndex::cresolve(&asg));
|
||||
|
|
|
@ -22,7 +22,10 @@
|
|||
|
||||
use super::{super::Ident, *};
|
||||
use crate::{
|
||||
asg::{graph::object::ObjectRel, IdentKind, Source},
|
||||
asg::{
|
||||
graph::object::{ObjectRel, ObjectRelFrom, ObjectRelatable},
|
||||
IdentKind, Source,
|
||||
},
|
||||
parse::{ParseError, Parsed, Parser},
|
||||
span::dummy::*,
|
||||
};
|
||||
|
@ -399,3 +402,52 @@ where
|
|||
assert!(sut.all(|x| x.is_ok()));
|
||||
sut.finalize().unwrap().into_context()
|
||||
}
|
||||
|
||||
pub fn pkg_lookup(asg: &Asg, name: SPair) -> Option<ObjectIndex<Ident>> {
|
||||
let oi_pkg = asg
|
||||
.root(S1)
|
||||
.edges_filtered::<Pkg>(&asg)
|
||||
.next()
|
||||
.expect("missing rooted package");
|
||||
|
||||
asg.lookup(oi_pkg, name)
|
||||
}
|
||||
|
||||
pub fn pkg_get_ident_oi<O: ObjectRelatable + ObjectRelFrom<Ident>>(
|
||||
asg: &Asg,
|
||||
name: SPair,
|
||||
) -> Option<ObjectIndex<O>> {
|
||||
pkg_lookup(asg, name)
|
||||
.and_then(|oi| oi.edges(asg).next())
|
||||
.and_then(|oi| oi.narrow())
|
||||
}
|
||||
|
||||
pub fn pkg_expect_ident_oi<O: ObjectRelatable + ObjectRelFrom<Ident>>(
|
||||
asg: &Asg,
|
||||
name: SPair,
|
||||
) -> ObjectIndex<O> {
|
||||
// Duplicates logic of `pkg_get_ident_oi`,
|
||||
// but in doing so,
|
||||
// provides better assertion messages.
|
||||
pkg_lookup(asg, name)
|
||||
.expect(&format!("missing ident: `{name}`"))
|
||||
.edges(asg)
|
||||
.next()
|
||||
.expect(&format!("missing definition for ident `{name}`"))
|
||||
.narrow()
|
||||
.expect(&format!("ident `{name}` was not of expected ObjectKind"))
|
||||
}
|
||||
|
||||
pub fn pkg_get_ident_obj<O: ObjectRelatable + ObjectRelFrom<Ident>>(
|
||||
asg: &Asg,
|
||||
name: SPair,
|
||||
) -> Option<&O> {
|
||||
pkg_get_ident_oi(asg, name).map(|oi| oi.resolve(asg))
|
||||
}
|
||||
|
||||
pub fn pkg_expect_ident_obj<O: ObjectRelatable + ObjectRelFrom<Ident>>(
|
||||
asg: &Asg,
|
||||
name: SPair,
|
||||
) -> &O {
|
||||
pkg_expect_ident_oi(asg, name).resolve(asg)
|
||||
}
|
||||
|
|
|
@ -21,7 +21,10 @@ use super::*;
|
|||
use crate::asg::{
|
||||
air::{
|
||||
expr::test::collect_subexprs,
|
||||
test::{asg_from_toks, parse_as_pkg_body},
|
||||
test::{
|
||||
asg_from_toks, parse_as_pkg_body, pkg_expect_ident_obj,
|
||||
pkg_expect_ident_oi, pkg_lookup,
|
||||
},
|
||||
Air, AirAggregate,
|
||||
},
|
||||
graph::object::{Doc, Meta, ObjectRel},
|
||||
|
@ -51,10 +54,10 @@ fn tpl_defining_pkg() {
|
|||
assert!(sut.all(|x| x.is_ok()));
|
||||
let asg = sut.finalize().unwrap().into_context();
|
||||
|
||||
let tpl = asg.expect_ident_obj::<Tpl>(id_tpl);
|
||||
let tpl = pkg_expect_ident_obj::<Tpl>(&asg, id_tpl);
|
||||
assert_eq!(S2.merge(S4).unwrap(), tpl.span());
|
||||
|
||||
let oi_id_tpl = asg.lookup_global(id_tpl).unwrap();
|
||||
let oi_id_tpl = pkg_lookup(&asg, id_tpl).unwrap();
|
||||
assert_eq!(
|
||||
S1.merge(S5),
|
||||
oi_id_tpl.src_pkg(&asg).map(|pkg| pkg.resolve(&asg).span()),
|
||||
|
@ -86,7 +89,7 @@ fn tpl_after_expr() {
|
|||
assert!(sut.all(|x| x.is_ok()));
|
||||
let asg = sut.finalize().unwrap().into_context();
|
||||
|
||||
let tpl = asg.expect_ident_obj::<Tpl>(id_tpl);
|
||||
let tpl = pkg_expect_ident_obj::<Tpl>(&asg, id_tpl);
|
||||
assert_eq!(S5.merge(S7).unwrap(), tpl.span());
|
||||
}
|
||||
|
||||
|
@ -135,13 +138,13 @@ fn tpl_within_expr() {
|
|||
let asg = sut.finalize().unwrap().into_context();
|
||||
|
||||
// The inner template.
|
||||
let tpl = asg.expect_ident_obj::<Tpl>(id_tpl);
|
||||
let tpl = pkg_expect_ident_obj::<Tpl>(&asg, id_tpl);
|
||||
assert_eq!(S6.merge(S8).unwrap(), tpl.span());
|
||||
|
||||
// The expression that was produced on the graph ought to be equivalent
|
||||
// to the expression without the template being present at all
|
||||
// (noting that the spans are of course not adjusted).
|
||||
let oi_expr = asg.expect_ident_oi::<Expr>(id_expr);
|
||||
let oi_expr = pkg_expect_ident_oi::<Expr>(&asg, id_expr);
|
||||
let expr = oi_expr.resolve(&asg);
|
||||
assert_eq!(S2.merge(S11).unwrap(), expr.span());
|
||||
assert_eq!(
|
||||
|
@ -187,13 +190,13 @@ fn tpl_apply_within_expr() {
|
|||
let asg = asg_from_toks(toks);
|
||||
|
||||
// The inner template.
|
||||
let tpl = asg.expect_ident_obj::<Tpl>(id_tpl);
|
||||
let tpl = pkg_expect_ident_obj::<Tpl>(&asg, id_tpl);
|
||||
assert_eq!(S4.merge(S6).unwrap(), tpl.span());
|
||||
|
||||
// The expression that was produced on the graph ought to be equivalent
|
||||
// to the expression without the template being present at all,
|
||||
// but retaining the _application_.
|
||||
let oi_expr = asg.expect_ident_oi::<Expr>(id_expr);
|
||||
let oi_expr = pkg_expect_ident_oi::<Expr>(&asg, id_expr);
|
||||
let expr = oi_expr.resolve(&asg);
|
||||
assert_eq!(S2.merge(S10).unwrap(), expr.span());
|
||||
assert_eq!(
|
||||
|
@ -261,7 +264,7 @@ fn tpl_with_reachable_expression() {
|
|||
|
||||
let asg = asg_from_toks(toks);
|
||||
|
||||
let oi_tpl = asg.expect_ident_oi::<Tpl>(id_tpl);
|
||||
let oi_tpl = pkg_expect_ident_oi::<Tpl>(&asg, id_tpl);
|
||||
let tpl = oi_tpl.resolve(&asg);
|
||||
assert_eq!(S1.merge(S9).unwrap(), tpl.span());
|
||||
|
||||
|
@ -290,19 +293,19 @@ fn tpl_with_reachable_expression() {
|
|||
);
|
||||
|
||||
// ...but not by the package containing the template.
|
||||
let oi_pkg = asg.lookup_global(id_tpl).unwrap().src_pkg(&asg).unwrap();
|
||||
let oi_pkg = pkg_lookup(&asg, id_tpl).unwrap().src_pkg(&asg).unwrap();
|
||||
assert_eq!(
|
||||
vec![
|
||||
// The only identifier on the package should be the template itself.
|
||||
asg.lookup_global(id_tpl).unwrap(),
|
||||
pkg_lookup(&asg, id_tpl).unwrap(),
|
||||
],
|
||||
oi_pkg.edges_filtered::<Ident>(&asg).collect::<Vec<_>>()
|
||||
);
|
||||
|
||||
// Verify the above claim that these are not cached in the global
|
||||
// environment.
|
||||
assert_eq!(None, asg.lookup_global(id_expr_a));
|
||||
assert_eq!(None, asg.lookup_global(id_expr_b));
|
||||
assert_eq!(None, pkg_lookup(&asg, id_expr_a));
|
||||
assert_eq!(None, pkg_lookup(&asg, id_expr_b));
|
||||
}
|
||||
|
||||
// Templates can expand into many contexts,
|
||||
|
@ -330,7 +333,7 @@ fn tpl_holds_dangling_expressions() {
|
|||
];
|
||||
|
||||
let asg = asg_from_toks(toks);
|
||||
let oi_tpl = asg.expect_ident_oi::<Tpl>(id_tpl);
|
||||
let oi_tpl = pkg_expect_ident_oi::<Tpl>(&asg, id_tpl);
|
||||
|
||||
assert_eq!(
|
||||
vec![S5.merge(S6).unwrap(), S3.merge(S4).unwrap(),],
|
||||
|
@ -430,7 +433,7 @@ fn unreachable_anonymous_tpl() {
|
|||
let asg = sut.finalize().unwrap().into_context();
|
||||
|
||||
// Let's make sure that the template created after recovery succeeded.
|
||||
asg.expect_ident_obj::<Tpl>(id_ok);
|
||||
pkg_expect_ident_obj::<Tpl>(&asg, id_ok);
|
||||
}
|
||||
|
||||
// Normally we cannot reference objects without an identifier using AIR
|
||||
|
@ -481,7 +484,7 @@ fn tpl_with_param() {
|
|||
];
|
||||
|
||||
let asg = asg_from_toks(toks);
|
||||
let oi_tpl = asg.expect_ident_oi::<Tpl>(id_tpl);
|
||||
let oi_tpl = pkg_expect_ident_oi::<Tpl>(&asg, id_tpl);
|
||||
|
||||
// The template should have an edge to each identifier for each
|
||||
// metavariable.
|
||||
|
@ -526,8 +529,8 @@ fn tpl_nested() {
|
|||
// The outer template should be defined globally,
|
||||
// but not the inner,
|
||||
// since it hasn't been expanded yet.
|
||||
let oi_tpl_outer = asg.expect_ident_oi::<Tpl>(id_tpl_outer);
|
||||
assert_eq!(None, asg.lookup_global(id_tpl_inner));
|
||||
let oi_tpl_outer = pkg_expect_ident_oi::<Tpl>(&asg, id_tpl_outer);
|
||||
assert_eq!(None, pkg_lookup(&asg, id_tpl_inner));
|
||||
assert_eq!(S1.merge(S6).unwrap(), oi_tpl_outer.resolve(&asg).span());
|
||||
|
||||
// The identifier for the inner template should be local to the outer
|
||||
|
@ -566,7 +569,7 @@ fn tpl_apply_nested() {
|
|||
|
||||
let asg = asg_from_toks(toks);
|
||||
|
||||
let oi_tpl_outer = asg.expect_ident_oi::<Tpl>(id_tpl_outer);
|
||||
let oi_tpl_outer = pkg_expect_ident_oi::<Tpl>(&asg, id_tpl_outer);
|
||||
assert_eq!(S1.merge(S5).unwrap(), oi_tpl_outer.resolve(&asg).span());
|
||||
|
||||
// The inner template,
|
||||
|
@ -615,12 +618,12 @@ fn tpl_apply_nested_missing() {
|
|||
|
||||
let asg = asg_from_toks(toks);
|
||||
|
||||
let oi_tpl_outer = asg.expect_ident_oi::<Tpl>(id_tpl_outer);
|
||||
let oi_tpl_outer = pkg_expect_ident_oi::<Tpl>(&asg, id_tpl_outer);
|
||||
assert_eq!(S1.merge(S12).unwrap(), oi_tpl_outer.resolve(&asg).span());
|
||||
|
||||
// The inner template should be contained within the outer and so not
|
||||
// globally resolvable.
|
||||
assert!(asg.lookup_global(id_tpl_inner).is_none());
|
||||
assert!(pkg_lookup(&asg, id_tpl_inner).is_none());
|
||||
|
||||
// But it is accessible as a local on the outer template.
|
||||
let oi_tpl_inner = oi_tpl_outer
|
||||
|
@ -667,7 +670,7 @@ fn tpl_doc_short_desc() {
|
|||
|
||||
let asg = asg_from_toks(toks);
|
||||
|
||||
let oi_expr = asg.expect_ident_oi::<Tpl>(id_tpl);
|
||||
let oi_expr = pkg_expect_ident_oi::<Tpl>(&asg, id_tpl);
|
||||
let oi_docs = oi_expr
|
||||
.edges_filtered::<Doc>(&asg)
|
||||
.map(ObjectIndex::cresolve(&asg));
|
||||
|
|
|
@ -33,7 +33,6 @@ use super::{
|
|||
use crate::{
|
||||
diagnose::{panic::DiagnosticPanic, Annotate, AnnotatedSpan},
|
||||
f::Functor,
|
||||
fmt::{DisplayWrapper, TtQuote},
|
||||
global,
|
||||
parse::{util::SPair, Token},
|
||||
span::Span,
|
||||
|
@ -221,7 +220,9 @@ impl Asg {
|
|||
|
||||
// We should never overwrite indexes
|
||||
#[allow(unused_variables)] // used only for debug
|
||||
#[allow(unused_imports)]
|
||||
if let Some(prev_oi) = prev {
|
||||
use crate::fmt::{DisplayWrapper, TtQuote};
|
||||
crate::debug_diagnostic_panic!(
|
||||
vec![
|
||||
imm_env.into().note("at this scope boundary"),
|
||||
|
@ -647,75 +648,6 @@ impl Asg {
|
|||
.map(move |edge| ObjectIndex::<OI>::new(edge.source(), oi))
|
||||
}
|
||||
|
||||
/// Retrieve the [`ObjectIndex`] to which the given `ident` is bound,
|
||||
/// if any.
|
||||
///
|
||||
/// The type parameter `O` indicates the _expected_ [`ObjectKind`] to be
|
||||
/// bound to the returned [`ObjectIndex`],
|
||||
/// which will be used for narrowing (downcasting) the object after
|
||||
/// lookup.
|
||||
/// An incorrect kind will not cause any failures until such a lookup
|
||||
/// occurs.
|
||||
///
|
||||
/// This will return [`None`] if the identifier is either opaque or does
|
||||
/// not exist.
|
||||
fn get_ident_oi<O: ObjectKind>(
|
||||
&self,
|
||||
ident: SPair,
|
||||
) -> Option<ObjectIndex<O>> {
|
||||
self.lookup_global(ident)
|
||||
.and_then(|identi| {
|
||||
self.graph
|
||||
.neighbors_directed(identi.into(), Direction::Outgoing)
|
||||
.next()
|
||||
})
|
||||
// Note that this use of `O` for `ObjectIndex` here means "I
|
||||
// _expect_ this to `O`";
|
||||
// the type will be verified during narrowing but will panic
|
||||
// if this expectation is not met.
|
||||
.map(|ni| ObjectIndex::<O>::new(ni, ident.span()))
|
||||
}
|
||||
|
||||
/// Retrieve the [`ObjectIndex`] to which the given `ident` is bound,
|
||||
/// panicing if the identifier is either opaque or does not exist.
|
||||
///
|
||||
/// Panics
|
||||
/// ======
|
||||
/// This method will panic if the identifier is opaque
|
||||
/// (has no edge to the object to which it is bound)
|
||||
/// or does not exist on the graph.
|
||||
pub fn expect_ident_oi<O: ObjectKind>(
|
||||
&self,
|
||||
ident: SPair,
|
||||
) -> ObjectIndex<O> {
|
||||
self.get_ident_oi(ident).diagnostic_expect(
|
||||
|| diagnostic_unknown_ident_desc(ident),
|
||||
|| format!("unknown identifier {}", TtQuote::wrap(ident),),
|
||||
)
|
||||
}
|
||||
|
||||
/// Attempt to retrieve the [`Object`] to which the given `ident` is bound.
|
||||
///
|
||||
/// If the identifier either does not exist on the graph or is opaque
|
||||
/// (is not bound to any expression),
|
||||
/// then [`None`] will be returned.
|
||||
///
|
||||
/// If the system expects that the identifier must exist and would
|
||||
/// otherwise represent a bug in the compiler,
|
||||
/// see [`Self::expect_ident_obj`].
|
||||
///
|
||||
/// Panics
|
||||
/// ======
|
||||
/// This method will panic if certain graph invariants are not met,
|
||||
/// representing an invalid system state that should not be able to
|
||||
/// occur through this API.
|
||||
/// Violations of these invariants represent either a bug in the API
|
||||
/// (that allows for the invariant to be violated)
|
||||
/// or direct manipulation of the underlying graph.
|
||||
pub fn get_ident_obj<O: ObjectKind>(&self, ident: SPair) -> Option<&O> {
|
||||
self.get_ident_oi::<O>(ident).map(|oi| self.expect_obj(oi))
|
||||
}
|
||||
|
||||
pub(super) fn expect_obj<O: ObjectKind>(&self, oi: ObjectIndex<O>) -> &O {
|
||||
let obj_container =
|
||||
self.graph.node_weight(oi.into()).diagnostic_expect(
|
||||
|
@ -726,35 +658,6 @@ impl Asg {
|
|||
obj_container.get()
|
||||
}
|
||||
|
||||
/// Attempt to retrieve the [`Object`] to which the given `ident` is bound,
|
||||
/// panicing if the identifier is opaque or does not exist.
|
||||
///
|
||||
/// This method represents a compiler invariant;
|
||||
/// it should _only_ be used when the identifier _must_ exist,
|
||||
/// otherwise there is a bug in the compiler.
|
||||
/// If this is _not_ the case,
|
||||
/// use [`Self::get_ident_obj`] to get [`None`] in place of a panic.
|
||||
///
|
||||
/// Panics
|
||||
/// ======
|
||||
/// This method will panic if
|
||||
///
|
||||
/// 1. The identifier does not exist on the graph; or
|
||||
/// 2. The identifier is opaque (has no edge to any object on the
|
||||
/// graph).
|
||||
pub fn expect_ident_obj<O: ObjectKind>(&self, ident: SPair) -> &O {
|
||||
self.get_ident_obj(ident).diagnostic_expect(
|
||||
|| diagnostic_opaque_ident_desc(ident),
|
||||
|| {
|
||||
format!(
|
||||
"identifier was not expected to be opaque: \
|
||||
{} has no object binding",
|
||||
TtQuote::wrap(ident),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Retrieve an identifier from the graph by [`ObjectIndex`].
|
||||
///
|
||||
/// If the object exists but is not an identifier,
|
||||
|
@ -879,26 +782,5 @@ fn diagnostic_node_missing_desc<O: ObjectKind>(
|
|||
]
|
||||
}
|
||||
|
||||
fn diagnostic_opaque_ident_desc(ident: SPair) -> Vec<AnnotatedSpan<'static>> {
|
||||
vec![
|
||||
ident.internal_error(
|
||||
"this identifier is not bound to any object on the ASG",
|
||||
),
|
||||
ident.help("the system expects to be able to reach the object that"),
|
||||
ident.help(" this identifies, but this identifier has no"),
|
||||
ident.help(" corresponding object present on the graph."),
|
||||
]
|
||||
}
|
||||
|
||||
fn diagnostic_unknown_ident_desc(ident: SPair) -> Vec<AnnotatedSpan<'static>> {
|
||||
vec![
|
||||
ident.internal_error("reference to an unknown identifier"),
|
||||
ident.help(
|
||||
"the system expects this identifier to be known, \
|
||||
but it could not be found.",
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
|
|
@ -20,12 +20,7 @@
|
|||
//! Expressions on the ASG.
|
||||
|
||||
use super::{prelude::*, Doc, Ident, Tpl};
|
||||
use crate::{
|
||||
f::Functor,
|
||||
num::Dim,
|
||||
parse::{util::SPair, Token},
|
||||
span::Span,
|
||||
};
|
||||
use crate::{f::Functor, num::Dim, span::Span};
|
||||
use std::fmt::Display;
|
||||
|
||||
#[cfg(doc)]
|
||||
|
@ -243,14 +238,9 @@ impl ObjectIndex<Expr> {
|
|||
oi_subexpr.add_edge_from(asg, self, None)
|
||||
}
|
||||
|
||||
/// Reference the value of the expression identified by `ident` as if it
|
||||
/// were a subexpression.
|
||||
///
|
||||
/// If `ident` does not yet exist on the graph,
|
||||
/// a missing identifier will take its place to be later resolved once
|
||||
/// it becomes available.
|
||||
pub fn ref_expr(self, asg: &mut Asg, ident: SPair) -> Self {
|
||||
let identi = asg.lookup_global_or_missing(ident);
|
||||
self.add_edge_to(asg, identi, Some(ident.span()))
|
||||
/// Reference the value of the expression identified by `oi_ident` as if
|
||||
/// it were a subexpression.
|
||||
pub fn ref_expr(self, asg: &mut Asg, oi_ident: ObjectIndex<Ident>) -> Self {
|
||||
self.add_edge_to(asg, oi_ident, Some(oi_ident.span()))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue