tidy(aura-engine): single-own alias-validity + shared alias-count anchor
Collapse the two correctness-neutral debt items the 0021 fan-in pre-pass left behind (architect drift review at cycle close). Alias-validity: the `BadInteriorIndex` check was raised in two places — the structural pre-pass `check_composite_fan_in` and `inline_composite`. Since `compile_with_params` runs the pre-pass over every composite before any lowering, `inline_composite`'s copy was dead for that error path. The check now lives in one helper `check_alias_indices`, called solely by the pre-pass; `inline_composite` drops its copy (and the now-unused `param_aliases` binding — the overlay is a pure naming layer, unused in lowering, which is driven by the injected scalar vector). Alias-count: the per-node `a.node == node` predicate was re-derived in three spots (signature base, the unaliased-param test, the CLI render base-length). All three now route through one exported anchor `aliases_on`, so an alias-semantics change touches one site. Behaviour-preserving: the full workspace suite is the guard (15+6 cli, 25 core, 69 engine, 6+1+1 ingest/misc, 29 std — 0 failed), `out_of_range_param_alias_rejected` still green (now raised by the pre-pass), clippy -D warnings clean. No ledger change (the C9 refinement landed in 0021). closes #45
This commit is contained in:
@@ -16,7 +16,7 @@
|
||||
use ascii_dag::graph::{Graph, RenderMode};
|
||||
use ascii_dag::render::colors::Palette;
|
||||
use aura_core::{LeafFactory, Node, ScalarKind};
|
||||
use aura_engine::{signature_of, Blueprint, BlueprintNode, Composite, Edge, SourceSpec};
|
||||
use aura_engine::{aliases_on, signature_of, Blueprint, BlueprintNode, Composite, Edge, SourceSpec};
|
||||
|
||||
/// Edge colouring for a rendered graph. `Plain` is monochrome — golden-stable and
|
||||
/// pipe-safe (no escape codes when redirected to a file). `Ansi` emits per-edge
|
||||
@@ -252,7 +252,7 @@ fn slot_source(c: &Composite, index: usize, slot: usize) -> (String, usize, bool
|
||||
/// rendered identifier never goes below.
|
||||
fn signature_base_len(c: &Composite, node: usize) -> usize {
|
||||
match &c.nodes()[node] {
|
||||
BlueprintNode::Leaf(_) => 1 + c.params().iter().filter(|a| a.node == node).count(),
|
||||
BlueprintNode::Leaf(_) => 1 + aliases_on(c.params(), node).count(),
|
||||
BlueprintNode::Composite(_) => 1,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,7 +276,7 @@ pub fn signature_of(
|
||||
if let Some(ch) = f.label().chars().next() {
|
||||
s.push(ch);
|
||||
}
|
||||
for a in aliases.iter().filter(|a| a.node == node) {
|
||||
for a in aliases_on(aliases, node) {
|
||||
if let Some(ch) = a.name.chars().next() {
|
||||
s.push(ch);
|
||||
}
|
||||
@@ -309,6 +309,30 @@ pub fn signature_of(
|
||||
s
|
||||
}
|
||||
|
||||
/// The param aliases declared on interior node `node`, in declared order. The single
|
||||
/// anchor for the `a.node == node` predicate shared by the signature base, the
|
||||
/// unaliased-param test, and the CLI's render base-length (issue #45) — change the
|
||||
/// predicate here, not in three places.
|
||||
pub fn aliases_on(aliases: &[ParamAlias], node: usize) -> impl Iterator<Item = &ParamAlias> {
|
||||
aliases.iter().filter(move |a| a.node == node)
|
||||
}
|
||||
|
||||
/// Validate that every param alias names a real interior leaf param slot: a dangling
|
||||
/// `(node, slot)` is a `BadInteriorIndex` (C23 — names are cosmetic, but a dangling
|
||||
/// handle is an author error). Sole owner of the alias-index check: the structural
|
||||
/// pre-pass recurses every composite that lowering reaches, so `inline_composite`
|
||||
/// trusts this has already run rather than re-checking (issue #45).
|
||||
fn check_alias_indices(nodes: &[BlueprintNode], aliases: &[ParamAlias]) -> Result<(), CompileError> {
|
||||
for a in aliases {
|
||||
let ok = a.node < nodes.len()
|
||||
&& matches!(&nodes[a.node], BlueprintNode::Leaf(f) if a.slot < f.params().len());
|
||||
if !ok {
|
||||
return Err(CompileError::BadInteriorIndex);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Structural validation (param-value-independent): walk every composite in the
|
||||
/// blueprint and reject an indistinguishable fan-in. Recurses into nested
|
||||
/// composites so the check holds at every level. Runs before the arity gate (a
|
||||
@@ -334,16 +358,10 @@ fn check_composite_fan_in(
|
||||
input_roles: &[Role],
|
||||
param_aliases: &[ParamAlias],
|
||||
) -> Result<(), CompileError> {
|
||||
// alias-validity first (mirrors `inline_composite`): a bogus alias index is a
|
||||
// `BadInteriorIndex`, ordered ahead of the fan-in fault so a bad alias surfaces
|
||||
// as the index error, not as an incidental collision.
|
||||
for a in param_aliases {
|
||||
let ok = a.node < nodes.len()
|
||||
&& matches!(&nodes[a.node], BlueprintNode::Leaf(f) if a.slot < f.params().len());
|
||||
if !ok {
|
||||
return Err(CompileError::BadInteriorIndex);
|
||||
}
|
||||
}
|
||||
// alias-validity first: a bogus alias index is a `BadInteriorIndex`, ordered
|
||||
// ahead of the fan-in fault so a bad alias surfaces as the index error, not as an
|
||||
// incidental collision.
|
||||
check_alias_indices(nodes, param_aliases)?;
|
||||
|
||||
for node in 0..nodes.len() {
|
||||
let mut sources: Vec<(usize, String, bool)> = Vec::new(); // (slot, sig, has_unaliased_param)
|
||||
@@ -380,7 +398,7 @@ fn leaf_has_unaliased_param(nodes: &[BlueprintNode], aliases: &[ParamAlias], nod
|
||||
match &nodes[node] {
|
||||
BlueprintNode::Leaf(f) => {
|
||||
let n_params = f.params().len();
|
||||
let aliased = aliases.iter().filter(|a| a.node == node).count();
|
||||
let aliased = aliases_on(aliases, node).count();
|
||||
n_params > aliased
|
||||
}
|
||||
BlueprintNode::Composite(_) => false,
|
||||
@@ -492,21 +510,15 @@ fn inline_composite(
|
||||
) -> Result<ItemLowering, CompileError> {
|
||||
// `name` is the non-load-bearing render symbol (#13); it dissolves at inline
|
||||
// (C23 — the boundary does not reach the compilat), so it is not destructured.
|
||||
// `params` here are the composite's ParamAlias overlay (renamed to avoid
|
||||
// shadowing the injected scalar `params: &[Scalar]` arg consumed by
|
||||
// `lower_items` below).
|
||||
let Composite { name: _, nodes, edges, input_roles, params: param_aliases, output } = c;
|
||||
// `params` (the composite's ParamAlias overlay) is a pure naming layer validated
|
||||
// by the pre-pass and unused in lowering — the injected scalar `params: &[Scalar]`
|
||||
// arg drives `lower_items` below — so it is dropped here.
|
||||
let Composite { name: _, nodes, edges, input_roles, params: _, output } = c;
|
||||
let item_count = nodes.len();
|
||||
|
||||
// an alias must name a real interior leaf param slot (C23 — names are cosmetic
|
||||
// but a dangling handle is an author error). Mirrors the output range-check.
|
||||
for a in ¶m_aliases {
|
||||
let ok = a.node < item_count
|
||||
&& matches!(&nodes[a.node], BlueprintNode::Leaf(f) if a.slot < f.params().len());
|
||||
if !ok {
|
||||
return Err(CompileError::BadInteriorIndex);
|
||||
}
|
||||
}
|
||||
// alias-validity (a dangling `(node, slot)` is a `BadInteriorIndex`) is owned by
|
||||
// the structural pre-pass `check_alias_indices`, which `compile_with_params` runs
|
||||
// over every composite before any lowering — so it has already fired here (#45).
|
||||
|
||||
// recursively lower interior items, then rewrite interior edges through them
|
||||
let interior = lower_items(nodes, params, cursor, flat_nodes, flat_edges)?;
|
||||
|
||||
@@ -36,7 +36,8 @@ mod harness;
|
||||
mod report;
|
||||
|
||||
pub use blueprint::{
|
||||
signature_of, Blueprint, BlueprintNode, CompileError, Composite, OutField, ParamAlias, Role,
|
||||
aliases_on, signature_of, Blueprint, BlueprintNode, CompileError, Composite, OutField,
|
||||
ParamAlias, Role,
|
||||
};
|
||||
pub use harness::{BootstrapError, Edge, Harness, SourceSpec, Target};
|
||||
pub use report::{f64_field, summarize, RunManifest, RunMetrics, RunReport};
|
||||
|
||||
Reference in New Issue
Block a user