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-13162
main
Mike Gerwitz 2023-04-17 15:17:49 -04:00
parent c367666d8e
commit 778e90c81d
7 changed files with 125 additions and 205 deletions

View File

@ -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
}

View File

@ -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))) => {

View File

@ -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));

View File

@ -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)
}

View File

@ -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));

View File

@ -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;

View File

@ -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()))
}
}