tamer: asg::air::ir::AirBind::RefIdent: New optional canonical name

This allows for a canonical package name to be optionally provided to
explicitly resolve a reference against, avoiding a lexical lookup.

This change doesn't actually utilize this new value yet; it just
retains BC.  The new argument will be used for the linker, since it already
knows the package that defined an identifier while reading the object file's
symbol table.  It will also be used by tamec for the same purposes while
processing package imports.

DEV-13162

-- squashed with --

tamer: asg::air::ir::RefIdent: CanonicalName=SPair

The use of CanonicalName created an asymmetry between RefIdent and
BindIdent.  The hope was to move CanonicalName instantiation outside of AIR
and into NIR, but doing so would be confusing and awkward without doing
something with BindIdent.

I don't have the time to deal with that for now, so let's observe how the
system continues to evolve and see whether hoisting it out makes sense in the
end.  For now, this works just fine and I need to move on with the actual
goal of finishing package imports so that I can expand templates.

DEV-13162
main
Mike Gerwitz 2023-05-05 15:24:42 -04:00
parent 00492ace01
commit 572337505c
11 changed files with 164 additions and 95 deletions

View File

@ -42,6 +42,7 @@ use super::{
use crate::{
diagnose::Annotate,
diagnostic_todo,
fmt::{DisplayWrapper, TtQuote},
parse::{prelude::*, StateStack},
span::{Span, UNKNOWN_SPAN},
sym::SymbolId,
@ -217,11 +218,39 @@ impl ParseState for AirAggregate {
}
// Package import
(Toplevel(oi_pkg), AirBind(RefIdent(pathspec))) => oi_pkg
.import(ctx.asg_mut(), pathspec)
(Toplevel(oi_pkg), AirBind(RefIdent(namespec, None))) => oi_pkg
.import(ctx.asg_mut(), namespec)
.map(|_| ())
.transition(Toplevel(oi_pkg)),
// Providing a canonical package name here makes a lot of sense,
// but we'd have to figure out what to expect for the namespec
// in this case.
// For example,
// we could use it to import specific identifiers,
// or '*',
// or anything really.
// The system should never produce this,
// thus the internal error.
(Toplevel(oi_pkg), AirBind(RefIdent(namespec, Some(in_pkg)))) => {
crate::diagnostic_panic!(
vec![
oi_pkg.note("in this package"),
namespec.note("for this namespec"),
in_pkg.internal_error(
"unexpected pre-resolved canonical package name"
),
in_pkg.help(
"TAMER expected to resolve the namespec itself; \
this could lead to a potential contradiction"
),
],
"unexpected pre-resolved canonical package name \
for namespec {}",
TtQuote::wrap(namespec),
)
}
// Note: We unfortunately can't match on `AirExpr | AirBind | ...`
// and delegate in the same block
// (without having to duplicate type checks and then handle

View File

@ -138,7 +138,7 @@ impl ParseState for AirExprAggregate {
}
}
(BuildingExpr(es, oi), AirBind(RefIdent(name))) => {
(BuildingExpr(es, oi), AirBind(RefIdent(name, None))) => {
let oi_ident = ctx.lookup_lexical_or_missing(name);
Transition(BuildingExpr(
es,
@ -147,6 +147,19 @@ impl ParseState for AirExprAggregate {
.incomplete()
}
(BuildingExpr(..), AirBind(RefIdent(name, Some(in_pkg)))) => {
use crate::diagnose::Annotate;
crate::diagnostic_todo!(
vec![
in_pkg.note("for an identifier in this package"),
name.internal_error(
"RefIdent with resolved Pkg not yet supported"
)
],
"RefIdent with resolved Pkg",
)
}
(BuildingExpr(es, oi), AirDoc(DocIndepClause(clause))) => {
oi.desc_short(ctx.asg_mut(), clause);
Transition(BuildingExpr(es, oi)).incomplete()

View File

@ -703,7 +703,7 @@ fn expr_ref_to_ident() {
// Reference to an as-of-yet-undefined id (okay),
// with a different span than `id_bar`.
Air::RefIdent(SPair("bar".into(), S3)),
Air::RefIdent(SPair("bar".into(), S3), None),
Air::ExprEnd(S4),
//
@ -762,7 +762,7 @@ fn idents_share_defining_pkg() {
Air::ExprStart(ExprOp::Sum, S4),
Air::BindIdent(id_bar),
Air::RefIdent(id_baz),
Air::RefIdent(id_baz, None),
Air::ExprEnd(S7),
Air::ExprEnd(S8),
Air::PkgEnd(S9),

View File

@ -537,7 +537,22 @@ sum_ir! {
/// However,
/// the identifier must eventually be bound to an object of
/// the appropriate type depending on context.
RefIdent(id: SPair) => {
///
/// A reference may optionally be accompanied by an explicit
/// package resolution.
/// If present,
/// then the identifier is explicitly bound to an identifier
/// in a package _canonically_ named by the provided
/// [`SPair`].
/// Otherwise,
/// a value of [`None`] indicates that the reference should be
/// resolved in the usual way using lexical scoping rules and
/// considering package imports.
///
/// The canonical name has potential attractive future use cases,
/// including the ability to reference identifiers from
/// packages that have not been explicitly imported.
RefIdent(id: SPair, pkg: Option<SPair>) => {
span: id,
display: |f| write!(
f,

View File

@ -566,7 +566,7 @@ fn pkg_import_canonicalized_against_current_pkg() {
let toks = vec![
PkgStart(S1),
BindIdent(pkg_name),
RefIdent(pkg_rel),
RefIdent(pkg_rel, None),
PkgEnd(S3),
];
@ -602,7 +602,7 @@ fn pkg_doc() {
// Some object to place in-between the two
// documentation blocks.
RefIdent(id_import),
RefIdent(id_import, None),
DocText(doc_b),
];

View File

@ -197,7 +197,7 @@ impl ParseState for AirTplAggregate {
.map(|_| ())
.transition(Toplevel(tpl.identify(id))),
(Toplevel(tpl), AirBind(RefIdent(name))) => {
(Toplevel(tpl), AirBind(RefIdent(name, None))) => {
let tpl_oi = tpl.oi();
let ref_oi = ctx.lookup_lexical_or_missing(name);
@ -206,6 +206,18 @@ impl ParseState for AirTplAggregate {
Transition(Toplevel(tpl)).incomplete()
}
(Toplevel(..), AirBind(RefIdent(name, Some(in_pkg)))) => {
crate::diagnostic_todo!(
vec![
in_pkg.note("for an identifier in this package"),
name.internal_error(
"RefIdent with resolved Pkg not yet supported"
)
],
"RefIdent with resolved Pkg",
)
}
(Toplevel(tpl), AirDoc(DocIndepClause(clause))) => {
tpl.oi().desc_short(ctx.asg_mut(), clause);
Transition(Toplevel(tpl)).incomplete()

View File

@ -182,7 +182,7 @@ fn tpl_apply_within_expr() {
// But the application will remain.
Air::TplStart(S7),
Air::RefIdent(ref_tpl),
Air::RefIdent(ref_tpl, None),
Air::TplEndRef(S9),
Air::ExprEnd(S10),
];
@ -600,7 +600,7 @@ fn tpl_apply_nested_missing() {
// Inner template application (Missing)
Air::TplStart(S3),
Air::RefIdent(ref_tpl_inner_pre),
Air::RefIdent(ref_tpl_inner_pre, None),
Air::TplEndRef(S5),
// Define the template above
@ -611,7 +611,7 @@ fn tpl_apply_nested_missing() {
// Apply again,
// this time _after_ having been defined.
Air::TplStart(S9),
Air::RefIdent(ref_tpl_inner_post),
Air::RefIdent(ref_tpl_inner_post, None),
Air::TplEndRef(S11),
Air::TplEnd(S12),
];

View File

@ -84,7 +84,7 @@ fn traverses_ontological_tree() {
ExprStart(ExprOp::Sum, S4),
ExprEnd(S5),
RefIdent(SPair(id_b.symbol(), S6)),
RefIdent(SPair(id_b.symbol(), S6), None),
ExprEnd(S7),
ExprStart(ExprOp::Sum, S8),
@ -212,7 +212,7 @@ fn traverses_ontological_tree_tpl_apply() {
// Apply the above template.
TplStart(S5),
RefIdent(ref_tpl),
RefIdent(ref_tpl, None),
TplMetaStart(S7),
BindIdent(id_param),
@ -273,18 +273,18 @@ fn traverses_ontological_tree_tpl_within_template() {
// which will begin as Missing and must be later resolved when
// the template is defined.
TplStart(S6),
RefIdent(ref_inner_before), // --.
TplEndRef(S8), // |
// |
// Named inner template. // |
TplStart(S9), // /
BindIdent(id_tpl_inner), //<-:
TplEnd(S11), // \
// |
// Apply above inner template, // |
// _after_ definition. // |
TplStart(S12), // |
RefIdent(ref_inner_after), // __/
RefIdent(ref_inner_before, None), // --.
TplEndRef(S8), // |
// |
// Named inner template. // |
TplStart(S9), // /
BindIdent(id_tpl_inner), //<-:
TplEnd(S11), // \
// |
// Apply above inner template, // |
// _after_ definition. // |
TplStart(S12), // |
RefIdent(ref_inner_after, None), // __/
TplEndRef(S14),
TplEnd(S15),
PkgEnd(S16),

View File

@ -89,26 +89,26 @@ fn sorts_objects_given_single_root() {
PkgStart(S1),
// Before this can be computed,
// its dependencies must be.
ExprStart(ExprOp::Sum, S2), // -.
BindIdent(id_a), // |
// |
// This is a dependency, // |
// but it is owned by this Expr // |
// and so would have been emitted // |
// first anyway. // |
ExprStart(ExprOp::Sum, S4), // |
ExprEnd(S5), // |
// v
ExprStart(ExprOp::Sum, S2), // -.
BindIdent(id_a), // |
// |
// This is a dependency, // |
// but it is owned by this Expr // |
// and so would have been emitted // |
// first anyway. // |
ExprStart(ExprOp::Sum, S4), // |
ExprEnd(S5), // |
// v
// But this is a reference to another
// Expr that appears later.
RefIdent(SPair(id_b.symbol(), S6)), // --.
ExprEnd(S7), // |
// |
// This will have to be emitted // |
// _before_ the above Expr that // |
// depends on its value having been // |
// computed. // /
ExprStart(ExprOp::Sum, S8), // <`
RefIdent(SPair(id_b.symbol(), S6), None), // --.
ExprEnd(S7), // |
// |
// This will have to be emitted // |
// _before_ the above Expr that // |
// depends on its value having been // |
// computed. // /
ExprStart(ExprOp::Sum, S8), // <`
BindIdent(id_b),
ExprEnd(S10),
@ -176,21 +176,21 @@ fn sorts_objects_given_single_root_more_complex() {
PkgStart(S1),
ExprStart(ExprOp::Sum, S2),
BindIdent(id_a),
RefIdent(SPair(id_b.symbol(), S4)), // ---.
ExprEnd(S5), // )
// /
ExprStart(ExprOp::Sum, S6), // /
BindIdent(id_b), // <'
RefIdent(SPair(id_d.symbol(), S8)), // -------.
ExprEnd(S9), // <. |
// \ |
ExprStart(ExprOp::Sum, S10), // \ |
BindIdent(id_c), // ) |
RefIdent(SPair(id_b.symbol(), S12)), // ---' /
ExprEnd(S13), // /
// /
ExprStart(ExprOp::Sum, S14), // /
BindIdent(id_d), // <--'
RefIdent(SPair(id_b.symbol(), S4), None), // ---.
ExprEnd(S5), // )
// /
ExprStart(ExprOp::Sum, S6), // /
BindIdent(id_b), // <'
RefIdent(SPair(id_d.symbol(), S8), None), // -------.
ExprEnd(S9), // <. |
// \ |
ExprStart(ExprOp::Sum, S10), // \ |
BindIdent(id_c), // ) |
RefIdent(SPair(id_b.symbol(), S12), None), // ---' /
ExprEnd(S13), // /
// /
ExprStart(ExprOp::Sum, S14), // /
BindIdent(id_d), // <--'
ExprEnd(S16),
PkgEnd(S17),
];
@ -236,21 +236,21 @@ fn omits_unreachable() {
PkgStart(S1),
ExprStart(ExprOp::Sum, S2),
BindIdent(id_a),
RefIdent(SPair(id_b.symbol(), S4)), // ---.
ExprEnd(S5), // )
// /
ExprStart(ExprOp::Sum, S6), // /
BindIdent(id_b), // <'
RefIdent(SPair(id_d.symbol(), S8)), // -------.
ExprEnd(S9), // <. |
// \ |
ExprStart(ExprOp::Sum, S10), // \ |
BindIdent(id_c), // ) |
RefIdent(SPair(id_b.symbol(), S12)), // ---' /
ExprEnd(S13), // /
// /
ExprStart(ExprOp::Sum, S14), // /
BindIdent(id_d), // <--'
RefIdent(SPair(id_b.symbol(), S4), None), // ---.
ExprEnd(S5), // )
// /
ExprStart(ExprOp::Sum, S6), // /
BindIdent(id_b), // <'
RefIdent(SPair(id_d.symbol(), S8), None), // -------.
ExprEnd(S9), // <. |
// \ |
ExprStart(ExprOp::Sum, S10), // \ |
BindIdent(id_c), // ) |
RefIdent(SPair(id_b.symbol(), S12), None), // ---' /
ExprEnd(S13), // /
// /
ExprStart(ExprOp::Sum, S14), // /
BindIdent(id_d), // <--'
ExprEnd(S16),
PkgEnd(S17),
];
@ -363,14 +363,14 @@ fn unsupported_cycles_with_recovery() {
let toks = vec![
PkgStart(S1),
ExprStart(ExprOp::Sum, S2),
BindIdent(id_a), // <----. self-cycle
RefIdent(SPair(id_a.symbol(), S4)), // ____/ \
RefIdent(SPair(id_b.symbol(), S5)), // ---. \ a->b->a
ExprEnd(S6), // ) ) cycle
// / /
ExprStart(ExprOp::Sum, S7), // / /
BindIdent(id_b), // <' /
RefIdent(SPair(id_a.symbol(), S9)), // ----'
BindIdent(id_a), // <----. self-cycle
RefIdent(SPair(id_a.symbol(), S4), None), // ____/ \
RefIdent(SPair(id_b.symbol(), S5), None), // ---. \ a->b->a
ExprEnd(S6), // ) ) cycle
// / /
ExprStart(ExprOp::Sum, S7), // / /
BindIdent(id_b), // <' /
RefIdent(SPair(id_a.symbol(), S9), None), // ----'
ExprEnd(S10),
PkgEnd(S11),
];

View File

@ -149,13 +149,13 @@ impl ParseState for NirToAir {
Transition(PredPartial(ospan, on)).incomplete()
}
(PredPartial(ospan, on), Ref(value)) => {
stack.push(Air::RefIdent(value));
stack.push(Air::RefIdent(on));
stack.push(Air::RefIdent(value, None));
stack.push(Air::RefIdent(on, None));
Transition(Ready).ok(Air::ExprStart(ExprOp::Eq, ospan))
}
(PredPartial(ospan, on), Close(Match, cspan)) => {
stack.push(Air::RefIdent(SPair(SYM_TRUE, ospan)));
stack.push(Air::RefIdent(on));
stack.push(Air::RefIdent(SPair(SYM_TRUE, ospan), None));
stack.push(Air::RefIdent(on, None));
Transition(Ready)
.ok(Air::ExprStart(ExprOp::Eq, ospan))
.with_lookahead(Close(Match, cspan))
@ -248,7 +248,7 @@ impl ParseState for NirToAir {
Transition(st).ok(Air::BindIdent(spair))
}
(Ready, Ref(spair) | RefSubject(spair)) => {
Transition(Ready).ok(Air::RefIdent(spair))
Transition(Ready).ok(Air::RefIdent(spair, None))
}
(Ready, Desc(clause)) => {

View File

@ -188,7 +188,7 @@ fn apply_template_long_form_nullary() {
#[rustfmt::skip]
Ok(vec![
O(Air::TplStart(S1)),
O(Air::RefIdent(name)),
O(Air::RefIdent(name, None)),
O(Air::TplEndRef(S3)),
]),
Sut::parse(toks.into_iter()).collect(),
@ -224,7 +224,7 @@ fn apply_template_long_form_args() {
#[rustfmt::skip]
Ok(vec![
O(Air::TplStart(S1)),
O(Air::RefIdent(name)),
O(Air::RefIdent(name, None)),
O(Air::TplMetaStart(S3)),
O(Air::BindIdent(p1)),
@ -267,8 +267,8 @@ fn match_short_no_value() {
// Once closing,
// we default to an equality check against `TRUE`.
O(Air::ExprStart(ExprOp::Eq, S1)),
O(Air::RefIdent(name)),
O(Air::RefIdent(SPair(SYM_TRUE, S1))),
O(Air::RefIdent(name, None)),
O(Air::RefIdent(SPair(SYM_TRUE, S1), None)),
O(Air::ExprEnd(S3)),
]),
Sut::parse(toks.into_iter()).collect(),
@ -297,10 +297,10 @@ fn match_short_with_value() {
Incomplete,
Incomplete,
O(Air::ExprStart(ExprOp::Eq, S1)),
O(Air::RefIdent(name)),
O(Air::RefIdent(name, None)),
// Rather than defaulting to `SYM_TRUE` as above,
// we use the _user-provided_ value.
O(Air::RefIdent(value)),
O(Air::RefIdent(value, None)),
O(Air::ExprEnd(S4)),
]),
Sut::parse(toks.into_iter()).collect(),
@ -338,8 +338,8 @@ fn match_short_value_before_subject_err() {
// because no value has been provided
// (rather the value was consumed in the error).
Ok(O(Air::ExprStart(ExprOp::Eq, S1))),
Ok(O(Air::RefIdent(name))),
Ok(O(Air::RefIdent(SPair(SYM_TRUE, S1)))),
Ok(O(Air::RefIdent(name, None))),
Ok(O(Air::RefIdent(SPair(SYM_TRUE, S1), None))),
Ok(O(Air::ExprEnd(S3))),
],
Sut::parse(toks.into_iter()).collect::<Vec<Result<_, _>>>(),