Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 32 additions & 9 deletions implementations/rust/sugar-lift-rust-tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3604,10 +3604,11 @@ fn closure_body_advances_iterator(body: &Expr) -> bool {
/// dissolvable/liftable pure point-wise iteration, return the NAMED `Effect` the
/// `ClosureAdaptorSugar` node's `desugar` `Hit`s (a mutation / iterator-advance /
/// opaque-runtime / TLS boundary). A THIN ADAPTER over the node, which lives in
/// `sugar::closure_adaptor`: it `build`s the node (`decompose_closure_adaptor`), `desugar`s
/// it once, and reads the verdict -- mapping the node's STRUCTURAL backstop `Hit` back to
/// `None` (the honest-unclassified fall-through, the old `None`). The caller renders
/// `effect.reason()` into `skip_reasons` -- the wire format is unchanged.
/// `sugar::closure_adaptor`: it `build`s the node through the factory
/// (`sugar::factory::build_closure_adaptor`, the single-recognizer walk), `desugar`s it once,
/// and reads the verdict -- mapping the node's STRUCTURAL backstop `Hit` back to `None` (the
/// honest-unclassified fall-through, the old `None`). The caller renders `effect.reason()`
/// into `skip_reasons` -- the wire format is unchanged.
///
/// Returns `None` for a PURE adaptor over a PURE body over a LITERAL-resolvable receiver
/// (the defoldable / for_each-liftable case -- honest UNCLASSIFIED work, never fake-refused)
Expand All @@ -3622,7 +3623,12 @@ fn closure_method_terminal_effect(
macro_depth: usize,
let_inits: &BTreeMap<String, &Expr>,
) -> Option<Effect> {
let node = sugar::closure_adaptor::decompose_closure_adaptor(expr, let_inits)?;
let fcx = sugar::factory::FactoryCtx {
scope,
options,
let_inits,
};
let node = sugar::factory::build_closure_adaptor(expr, &fcx);
let ctx = sugar_ctx(scope, options, reducer, float_widths, macro_depth);
match node.desugar(&ctx) {
// The STRUCTURAL backstop = the honest-unclassified fall-through (the old `None`).
Expand Down Expand Up @@ -3868,7 +3874,15 @@ fn statement_position_terminal_effect(
float_widths: &mut FloatWidthScope,
macro_depth: usize,
) -> Option<Effect> {
let node = sugar::statement_position::decompose_statement_position(expr)?;
// The statement-position recognizer's verdict is purely structural (it ignores the build
// env), so the in-scope `let` initializers are irrelevant -- an empty map suffices.
let let_inits: BTreeMap<String, &Expr> = BTreeMap::new();
let fcx = sugar::factory::FactoryCtx {
scope,
options,
let_inits: &let_inits,
};
let node = sugar::factory::build_statement_position(expr, &fcx);
let ctx = sugar_ctx(scope, options, reducer, float_widths, macro_depth);
match node.desugar(&ctx) {
// The STRUCTURAL backstop = the honest-unclassified fall-through (the old `None`).
Expand Down Expand Up @@ -3903,7 +3917,17 @@ fn impl_method_terminal_effect(
float_widths: &mut FloatWidthScope,
macro_depth: usize,
) -> Option<Effect> {
let node = sugar::impl_method::decompose_impl_method(imp)?;
// The impl-method recognizer's verdict is purely structural (it ignores the build env),
// so the in-scope `let` initializers are irrelevant -- an empty map suffices. `build_item`
// dispatches on a `syn::Item`, so wrap the `ItemImpl` back into an `Item::Impl`.
let let_inits: BTreeMap<String, &Expr> = BTreeMap::new();
let fcx = sugar::factory::FactoryCtx {
scope,
options,
let_inits: &let_inits,
};
let item = syn::Item::Impl(imp.clone());
let node = sugar::factory::build_item(&item, &fcx);
let ctx = sugar_ctx(scope, options, reducer, float_widths, macro_depth);
match node.desugar(&ctx) {
// The STRUCTURAL backstop = the honest-unclassified fall-through (the old `None`).
Expand Down Expand Up @@ -7915,8 +7939,7 @@ fn translate_bool_assertion(
/// scrutinee declines to RECOGNIZE (the build arm returns `None`), which this router maps to
/// `None` -- leaving everything else UNCLASSIFIED, never terminalized by position.
fn runtime_match_scrutinee_effect(expr: &Expr) -> Option<Effect> {
let node = sugar::match_scrutinee::decompose_match_scrutinee(expr)?;
match node.desugar_ctx_free() {
match sugar::factory::build_match_scrutinee(expr) {
// The STRUCTURAL backstop = the honest-unclassified fall-through (the old `None`).
Outcome::Hit(Effect::Unsupported { reason }) if reason == STRUCTURAL_BACKSTOP_REASON => {
None
Expand Down
85 changes: 79 additions & 6 deletions implementations/rust/sugar-lift-rust-tests/src/sugar/factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,18 @@

use std::collections::BTreeMap;

use syn::Expr;
use syn::{Expr, Item};

use crate::sugar::{
array_repeat, array_term, await_term, binop, block_term, call, cast_term, closure_adaptor,
closure_term, conditional, const_block, control_flow_term, field_term, fold, forall, index,
literal, macro_term, match_node, match_scrutinee, method_call_term, path, range_term,
raw_addr_term, reference_term, repeat_term, struct_term, term_literal, transparent_term,
tuple_term, unary,
closure_term, conditional, const_block, control_flow_term, field_term, fold, forall,
impl_method, index, literal, macro_term, match_node, match_scrutinee, method_call_term, path,
range_term, raw_addr_term, reference_term, repeat_term, statement_position, struct_term,
term_literal, transparent_term, tuple_term, unary,
};
use crate::{
Effect, LiftOptions, Outcome, Sugar, SugarCtx, TemporalScope, STRUCTURAL_BACKSTOP_REASON,
};
use crate::{LiftOptions, Outcome, Sugar, SugarCtx, TemporalScope};

/// What a recognizer needs from its environment to construct a node: the temporal
/// `scope` (binding / mutability oracle), the lift `options`, and the in-scope `let`
Expand Down Expand Up @@ -154,6 +156,77 @@ pub(crate) fn build_composite(expr: &Expr, fcx: &FactoryCtx) -> Box<dyn Sugar> {
unsupported()
}

/// The type of an ITEM-shape recognizer: an `&syn::Item`-position recognizer. `Some(node)`
/// if the shape matches, else `None`. The `Item` analogue of [`Recognizer`] — an `impl` block
/// is a `syn::Item`/`ItemImpl`, not an `Expr`, so it cannot go through `build_term`/
/// `build_composite`; it gets its own tiny registry walked by [`build_item`].
type ItemRecognizer = fn(&Item, &FactoryCtx) -> Option<Box<dyn Sugar>>;

/// The ITEM-position recognizers, in dispatch precedence order. Currently the single
/// statement-nested-`impl` REFUSE node; faithfully reproduces the former
/// `Stmt::Item(Item::Impl)` router arm's `decompose_impl_method` call.
const ITEM_RECOGNIZERS: &[ItemRecognizer] = &[
impl_method::recognize, // Item::Impl (statement-nested asserting impl method)
];

/// The recursive Sugar factory for ITEM-position constructs: the third dispatch the
/// collector's statement-nested-`impl` site consumes. DISTINCT from `build_term` /
/// `build_composite` because its input is a `syn::Item`, not an `Expr`. Same shape: walk the
/// item registry and return the first recognizer's node, else the structural backstop. Total:
/// a pure / assert-free `impl` becomes the [`UnsupportedSugar`] backstop, which the
/// verdict-reading router discards exactly as the old `decompose_impl_method` `None`.
pub(crate) fn build_item(item: &Item, fcx: &FactoryCtx) -> Box<dyn Sugar> {
for recognize in ITEM_RECOGNIZERS {
if let Some(node) = recognize(item, fcx) {
return node;
}
}
unsupported()
}

/// Build the closure-adaptor REFUSE node for a statement expr (the [`closure_adaptor`] node),
/// walking its single-recognizer registry. The verdict-reading routers
/// (`closure_method_terminal_effect`) `desugar` this and read the named order-loss `Effect`;
/// a non-recognized shape returns the [`UnsupportedSugar`] backstop, which those routers
/// discard exactly as the old `decompose_closure_adaptor` `None`. This is the SAME node the
/// composite registry's `closure_adaptor::recognize_composite` slot produces in the method-call
/// chain; this entry is the verdict-reader's dedicated single-shape walk (it never competes
/// with `fold`/`for_each` precedence — the router wants the closure-adaptor verdict alone).
pub(crate) fn build_closure_adaptor(expr: &Expr, fcx: &FactoryCtx) -> Box<dyn Sugar> {
closure_adaptor::recognize_composite(expr, fcx).unwrap_or_else(unsupported)
}

/// Build the statement-position REFUSE node for a bare statement expr (the
/// [`statement_position`] node), walking its single-recognizer registry. The verdict-reading
/// router (`statement_position_terminal_effect`) `desugar`s this and reads the named
/// value-NOT-in-scope `Effect`; a non-asserting statement returns the [`UnsupportedSugar`]
/// backstop, discarded exactly as the old `decompose_statement_position` `None`.
pub(crate) fn build_statement_position(expr: &Expr, fcx: &FactoryCtx) -> Box<dyn Sugar> {
statement_position::recognize(expr, fcx).unwrap_or_else(unsupported)
}

/// Build the match-scrutinee REFUSE node for an `Expr::Match` (the [`match_scrutinee`] node),
/// walking its single-recognizer registry. The verdict-reading router
/// (`runtime_match_scrutinee_effect`) `desugar`s this and reads the runtime-match-scrutinee
/// `Effect`; a non-`match` / constructed-scrutinee `match` returns the [`UnsupportedSugar`]
/// backstop, discarded exactly as the old `decompose_match_scrutinee` `None`. The verdict is
/// purely SYNTACTIC (the recognizer and node ignore the build/desugar env), so this entry is
/// ctx-FREE — it needs no `FactoryCtx` and no `SugarCtx`. It both BUILDS the node and reduces
/// it through the node's own ctx-free reduction, returning the `Outcome` directly: a
/// recognized `Expr::Match` over a runtime scrutinee `Hit`s `RuntimeMatchScrutinee`; a
/// non-recognized shape is the structural backstop (`from_opt(None)` =
/// `Hit(Unsupported{STRUCTURAL_BACKSTOP_REASON})`), discarded by the verdict-reading router
/// exactly as the old `decompose_match_scrutinee` `None`. This is what lets the ctx-less
/// `translate_bool_assertion` callsite route through the factory unchanged.
pub(crate) fn build_match_scrutinee(expr: &Expr) -> Outcome {
match match_scrutinee::recognize(expr) {
Some(node) => node.desugar_ctx_free(),
None => Outcome::Hit(Effect::Unsupported {
reason: STRUCTURAL_BACKSTOP_REASON.to_string(),
}),
}
}

/// Box a recognized concrete node, or fall to the structural backstop when a decomposer
/// declined (`None`). Used by the composite recognizers that wrap an `Option<S>`-
/// returning `decompose_*` whose `None` is a recognized-but-declined verdict (the `If`/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,24 @@
// unreachable tail kept to mirror the node shape -- a `Hit` the fall-through router would
// discard exactly as the old `None`, never a fake-refuse.

use syn::ItemImpl;
use syn::{Item, ItemImpl};

use crate::sugar::factory::FactoryCtx;
use crate::{impl_block_method_name, Effect, Outcome, Sugar, SugarCtx, STRUCTURAL_BACKSTOP_REASON};

/// ITEM-position recognizer ([`ImplMethodSugar`] via [`decompose_impl_method`]): `Some` only
/// for a statement-nested `impl` block whose first method body carries an assertion, else
/// `None`. The item factory entry (`build_item`) walks a one-recognizer registry over this;
/// the node owns the impl-method-reachability verdict in its own `desugar`. An `impl` operates
/// on a `syn::Item`/`ItemImpl`, not an `Expr`, so this is the item-registry analogue of the
/// term/composite `recognize` shape. Ctx-independent (the verdict is purely structural).
pub(crate) fn recognize(item: &Item, _fcx: &FactoryCtx) -> Option<Box<dyn Sugar>> {
let Item::Impl(imp) = item else {
return None;
};
decompose_impl_method(imp).map(|node| Box::new(node) as Box<dyn Sugar>)
}

/// The statement-nested `impl` block whose method body carries an assertion, composed as a
/// node whose `desugar` makes the impl-method-reachability verdict at its single LEAF (the
/// asserting method). See the module header.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,18 @@ pub(crate) fn recognize_composite(expr: &Expr, _fcx: &FactoryCtx) -> Option<Box<
}
}

/// MATCH-position recognizer ([`MatchScrutineeSugar`] via [`decompose_match_scrutinee`]):
/// `Some` only for an `Expr::Match` over a RUNTIME call-result scrutinee, else `None`. This
/// is the shape the verdict-reading factory entry (`build_match_scrutinee`) walks for the two
/// `Expr::Match` callsites (the statement-context residue and the `translate_bool_assertion`
/// half-refuse) — DISTINCT from `recognize_composite`, which owns the method-call-chain
/// placeholder slot. The verdict is purely SYNTACTIC, so this recognizer takes no
/// `FactoryCtx` and returns the CONCRETE node (the factory entry reduces it via the node's
/// ctx-free reduction, not a `Box<dyn Sugar>` registry walk).
pub(crate) fn recognize(expr: &Expr) -> Option<MatchScrutineeSugar> {
decompose_match_scrutinee(expr)
}

/// The `match <runtime call> { .. }` whose asserted value is the arm taken by a runtime
/// non-scalar result, composed as a node whose `desugar` makes the runtime-match-scrutinee
/// verdict at its single LEAF (the scrutinee). See the module header.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,22 @@

use syn::{BinOp, Expr};

use crate::sugar::factory::FactoryCtx;
use crate::{
closure_body_advances_iterator, count_asserts_in_expr, expr_contains_await,
is_free_fn_block_on_async, reflection_scrutinee, strip_const_block, token_key, Effect, Outcome,
Sugar, SugarCtx, STRUCTURAL_BACKSTOP_REASON,
};

/// REFUSE-side statement-position recognizer ([`StatementPositionSugar`] via
/// [`decompose_statement_position`]): `Some` only for a bare statement expr that carries an
/// assertion, else `None`. The verdict-reading factory entry (`build_statement_position`)
/// walks a one-recognizer registry over this; the node owns the runtime-continuation verdict
/// in its own `desugar`. Ctx-independent (the build env is unused).
pub(crate) fn recognize(expr: &Expr, _fcx: &FactoryCtx) -> Option<Box<dyn Sugar>> {
decompose_statement_position(expr).map(|node| Box::new(node) as Box<dyn Sugar>)
}

/// The bare expression-statement whose asserted value flows through an out-of-scope runtime
/// continuation, composed as a node whose `desugar` makes every statement-position terminal
/// verdict at the LEAVES (continuation / reflection / loop / aliased-read). See the module header.
Expand Down
Loading