Skip to main content

ScopeTree

Struct ScopeTree 

Source
pub struct ScopeTree {
    pub nodes: Vec<ScopeNode>,
    pub root: ScopeNodeIdx,
}
Expand description

A workload’s runtime scope hierarchy. Built once per session. Stable indices into nodes; parent / child pointers are ScopeNodeIdx. Use the helpers on this struct for traversal — direct nodes access is fine for read-only inspection but the navigation helpers are easier to read.

Fields§

§nodes: Vec<ScopeNode>§root: ScopeNodeIdx

Implementations§

Source§

impl ScopeTree

Source

pub fn build(scenario_name: &str, nodes: &[ScenarioNode]) -> Self

Build a scope tree from the resolved scenario children. scenario_name becomes the named ScopeKind::Scenario that wraps nodes — the user’s authored grouping is preserved as a real ancestor, restoring “phase P in scenario default” as a path query.

Source

pub fn workload_root_idx(&self) -> ScopeNodeIdx

The workload-root node — the single child of the session root (node 0). One per execution; owns the outer workload kernel. Falls back to the root if (degenerately) there is no workload layer.

Source

pub fn scenario_root_idx(&self) -> ScopeNodeIdx

The scenario-layer node — the single child of the workload root (node 0). The walker seeds its scope cursor here so the top-level scenario nodes resolve positionally against this node’s children (one scope-tree child per scenario node, in order — see Self::append_subtree). Falls back to the root if (degenerately) there is no scenario layer.

Source

pub fn extend_with_op_templates( &mut self, phases: &HashMap<String, WorkloadPhase>, )

SRD-13d Phase 6 — extend every Phase scope node with OpTemplate children (one per op declared in the phase). Two-step build: ScopeTree::build produces the scenario-shaped skeleton (phases as leaves); this method adds the op-template tier on top by consulting the workload’s per-phase WorkloadPhase records.

Idempotent: a phase whose OpTemplate children are already present is left alone (the post-build pre-walk can run before or after this without double-adding). Run before mark_scope_elision so the per-op classification gets the chance to elide / materialise each op-template tier.

Source

pub fn mark_scope_elision<F>(&mut self, is_materialising: F)

SRD-13d §3.3 — pre-walk every scope-tree node and mark it materialised (own kernel) or elided (descendants bind through parent). Also assigns the SRD-13d §5.3 logical kernel name, which is the fully-qualified scope-tree path. Run once at workload-load; premap and runtime read the marks afterward.

is_materialising is the predicate the pre-walk applies per node — typically a closure that consults the AST node’s HasPolydatMatter classification (None / Readonly ⇒ elide; Definitions ⇒ check program-hash equivalence with the parent and decide). The walker is agnostic to the exact predicate; SRD-13d §3.3 fixes the order.

The workload root is always materialised (see SRD-13d §5.1) so the walk terminates at a materialised ancestor regardless of how aggressively descendants elide.

Source

pub fn nearest_materialised(&self, idx: ScopeNodeIdx) -> Option<ScopeNodeIdx>

SRD-13d §5.1 — walk past elided scope tiers to the nearest materialised ancestor (or self, when this node is itself materialised). Every consumer that needs a kernel handle (cache lookups, bind-outer- scope, diagnostics) routes through this — it’s the single point that knows about elision; nothing else does.

The workload root is always materialised, so this always terminates with Some(idx). Returns None only if [mark_scope_elision] hasn’t been run.

Source

pub fn iter_dfs(&self) -> DfsIter<'_> ⓘ

Iterate every scope node in depth-first pre-order. The scheduler’s default walk and the canonical display linearisation both consume this.

Source

pub fn ancestors(&self, idx: ScopeNodeIdx) -> AncestorsIter<'_> ⓘ

Walk from idx up through its ancestors to the root, inclusive of idx itself. Use this to compute effective pragmas or to render a path label.

Source

pub fn phase_node_by_name(&self, name: &str) -> Option<ScopeNodeIdx>

First scope-tree node whose kind is Phase { name } matching the given name. Returns None if the scenario doesn’t reference this phase. When a single phase is invoked from multiple scenario sites (rare; most workloads reference a phase exactly once), this returns the first occurrence in depth-first order — sufficient for current callers, who use the result to fetch the chain-walked PragmaSet.

Source

pub fn op_template_programs_for_phase( &self, phase_idx: ScopeNodeIdx, ) -> HashMap<String, Arc<PolydatProgram>>

Op-template kernel programs for every materialised op-template that’s a child of phase_idx. Keyed by the op’s name. Used by the executor to thread per-op-template programs into the activity so each MetricsDispenser builds its ScopeFixture against the correct scope (SRD-13d Phase 9 §“per-dispenser kernel instancing”). Flattened op-templates (materialised != Some(true)) are omitted from the map; their dispensers reach the parent kernel through the standard nearest_materialised fall-through.

Rule 2 write-through bindings ride on the program itself (baked in by the SRD-67 builder’s finalize step). Any kernel built from the program inherits them automatically when bound (ScopeKernel::bind_under) — no side channel.

Source

pub fn op_template_modules_for_phase( &self, phase_idx: ScopeNodeIdx, ) -> Vec<(String, Arc<OpTemplateModule>)>

The op-template scope modules of phase_idx’s materialised op-template children, keyed by op name: what each fiber instantiates its per-op kernels from on the fiber engine (crate::fiber_engine).

Source

pub fn phase_leaves(&self) -> Vec<ScopeNodeIdx> ⓘ

All phase-leaf indices in depth-first order. Equivalent to filtering iter_dfs() to ScopeKind::Phase — the helper exists because it’s the most common consumer query (TUI tree pre-mapping, dryrun=phase).

Source

pub fn nearest_installed_ancestor_kernel( &self, idx: ScopeNodeIdx, ) -> Option<Arc<ScopeKernel>>

Walk ancestors of idx looking for the nearest scope node that has a kernel installed. Used at routing time to find the kernel a for_each scope’s materialize_wiring_from_outer should chain from. Workload root always has a kernel installed (per M3.1), so this never returns None for any descendant of the root.

Source

pub fn ancestor_kernels(&self, idx: ScopeNodeIdx) -> Vec<Arc<ScopeKernel>>

Collect every installed ancestor kernel of idx, innermost first (immediate parent → workload root). Skips ancestor levels whose cached_kernel is empty (intermediate nodes that don’t own their own kernel). Used by the checkpoint identity path to feed polydat::kernel::PolydatProgram::instance_hash (SRD-44 §“Identity matching at resume” + project memory program_vs_instance_hash).

Source

pub fn ancestor_kernels_split( &self, idx: ScopeNodeIdx, ) -> (Vec<Arc<ScopeKernel>>, Option<Arc<ScopeKernel>>)

Self::ancestor_kernels split at the session boundary (SRD-107): (below, session) where below is every installed ancestor kernel from the immediate parent up through the workload root, and session is the session-node kernel (the workload-params module) when one is installed. The provenance base hash covers below only; param values are covered per-phase by the consumed-params digest instead.

Source

pub fn find_comprehension_scope( &self, comprehension: &Comprehension, ) -> Option<ScopeNodeIdx>

Find a Comprehension scope by structural-equality match against its Comprehension AST. Returns the first DFS-pre-order match.

Source

pub fn find_bindings_scope(&self, source: &str) -> Option<ScopeNodeIdx>

First scope-tree node whose kind is ScopeKind::Bindings { source } matching exactly, searched globally from root in DFS pre-order.

Prefer Self::find_bindings_scope_under when the executor’s current scope position is known — see that method’s doc for why a global content-only lookup is currently unsafe.

Source

pub fn find_bindings_scope_under( &self, parent: ScopeNodeIdx, source: &str, ) -> Option<ScopeNodeIdx>

TRANSITIONAL WORKAROUND — see task #19 for the canonical end-state plan. Constrains the lookup of a Bindings scope to descendants of parent so that two Bindings nodes sharing source text at different scope- tree positions resolve to the right one based on the executor’s current position.

The deeper problem: today the AST/source we use as the lookup key is LOSSY — two scope-tree nodes that produce semantically-distinct installed kernels (different cascaded externs from different parent chains) can share AST/source. Per SRD-13d §“Op-template scope synthesis” + SRD-13f §“The read invariant”, installed kernels are determined by their PARENT chain, not by their own content alone. The current scope-aware lookup adds the missing context (parent subtree) at the call site to disambiguate.

Future direction: make the AST/source self- identifying so that semantically-distinct kernels never share matter (embed parent-chain signature, encode scenario-tree path, or some equivalent invariant). Once that lands, content-only find_bindings_scope is correct again and this _under variant can be retired.

Source

pub fn find_comprehension_scope_under( &self, parent: ScopeNodeIdx, comprehension: &Comprehension, ) -> Option<ScopeNodeIdx>

TRANSITIONAL WORKAROUND — see Self::find_bindings_scope_under for the underlying principle and task #19 for the canonical end-state plan. Retired once the AST becomes self-identifying.

Source

pub fn validate_iter_var_uniqueness( &self, workload_params: &HashSet<String>, ) -> Result<(), String>

Validate iteration-variable name uniqueness against the surrounding scope chain.

An iter-var name (for_each: "X in ...", for_combinations: "X in ..., Y in ...", for_each_union: ..., do-loop counters) must not shadow:

  • a workload param,
  • an iter var declared by an enclosing scope.

Aliasing creates a name that can’t unambiguously resolve at spec-evaluation time (the iter var is being defined from a value that uses the same name; the runtime can’t tell whether {X} means the iter var or the shadowed outer name). Rather than try to disambiguate, the build rejects it up-front with a clear error so the user renames the iter var.

Returns Ok(()) if every iter-var name is unique. Returns Err(...) with the offending name and which kind of collision (workload param vs ancestor iter var) the user has on the first violation found.

Source

pub fn install_kernel( &self, scope_idx: ScopeNodeIdx, kernel: Arc<ScopeKernel>, ) -> bool

Install the canonical compiled kernel for scope_idx.

Called at pre-map time after compiling the scope’s PolydatProgram. Once installed, the kernel is the single authoritative answer for “what is <name> at this scope?” — every name visible at this scope (own outputs plus parent-inherited values bound via [PolydatKernel::materialize_wiring_from_outer]) resolves through the standard Polydat API on this one kernel. Callers don’t walk the scope tree to do name resolution; Polydat’s auto-extern + outer-scope wiring already encapsulates the layering.

Idempotent only by virtue of OnceLock: a second install silently no-ops, returning false. Returns true on fresh install. Callers that need to detect a duplicate install should check the boolean.

Source

pub fn populate_pragmas(&mut self, phases: &HashMap<String, WorkloadPhase>)

Populate pragmas on every phase-leaf scope by scanning each phase’s BindingsDef::PolydatSource strings for pragma statements, then walk the tree so each scope’s set holds its ancestors’ pragmas followed by its own, as polydat nests a scope’s pragmas (PragmaSet::nested). After this call, node.pragmas.strict_values() answers for every pragma in force at the node.

SRD 18b §“Pragma chain along the scope tree”. Idempotent per call (replaces any prior pragmas content).

Trait Implementations§

Source§

impl Clone for ScopeTree

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ScopeTree

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more