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
21 changes: 21 additions & 0 deletions .claude/board/entries/2026-10-31-polyfill-positional-lens.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# The polyfill lens `0xFFFF :: 0xF000` binds by position; R2IL, SQL and Quack names are aliases — 2026-10-31

**Status:** MEASURED (tests in `crates/lance-graph-r2il/tests/integration/polyfill.rs`). Implements D-DT64-7 §1a/§1b (plan on PR #1898) for the relational polyfill only.

## What changed
- `lance_graph_r2il::polyfill`: `POLYFILL_LENS = compose_classid(0xFFFF, 0xF000)` and `POLYFILL`, 17 positions in `binding::EXECUTABLE` order. Each position carries its opcode, arity and aliases: the R2IL name first, then a SQL name where one fits, then the Quack variant.
- `binding::prepare` accepts this lens beside the R2IL fold reading. Under it, word `p` is `POLYFILL[p]`; the view's label at `p` must be an alias of `p`, and a name belonging to another position is refused (`ExecRefusal::NotAnAlias`), never followed. The R2IL fold reading keeps its label binding.

## Measured
- R2IL, SQL and Quack views bind the same steps and the same plan id, and answer the scalar oracle.
- Methods registered by Quack names (`Cmp::LtI32`, `Cmp::EqI32`, `Filter::and`, `Filter::negate`, `Agg::{Count, Any, All, SumI32, MinI32, MaxI32, CountDistinctOrderedU32}`) answer exactly as Quack's own `lower` + mask-risc `execute` over the same lanes: 21 cases over a partial, a full and an empty selection, plus one compound filter.
- The table matches `R2ILVocabulary` (name, stack arity, one result) and the `EXECUTABLE` order; every alias names one position; the table's arity overrules a wrong runtime `declare_algebra`.
- Disable runs, each red: alias check removed (1 fail), positional path removed (4), lens not accepted (5), SUM/MIN opcodes swapped (3), a duplicate alias (1), table arity check removed (1, after adding the wrong-declaration test; it was green before because registration already ties arity).

## What "register SQL via Quack" turned out to mean
Quack has no SQL text parser. Its SQL surface is the typed builder (`bind::table(..).where_eq(..).count()`) lowering to `Query { Filter, Agg }`. So SQL registers through Quack's variant names, and those are now aliases with a parity test against Quack's own execution. The SQL keyword aliases (`<`, `AND`, `COUNT`, `EXISTS`, ...) are recorded, but nothing in the tree parses SQL text into them.

## Not covered (OPEN)
- Quack aggregates with no single executable word: `Rows`, `BlendI32`, `ScatterOrU32`, the `Group*` family (`GROUP_SUM` and `KEEP` write sinks and are refused by the binding). `Cmp::{Ne, Le, Gt, Ge}*`, the ternary matches, strided compares and `Range` are composable or absent, not aliased.
- SQL `NULL` over an empty selection: `SUM`/`MIN`/`MAX` answer the fold identity (`0`, `i64::MAX`, `i64::MIN`), as Quack's `Agg` does through this mapping; three-valued bindings are refused.
- The lens is still declared at runtime through `BindStore::declare_algebra`, and DT64 does not yet act as the authority for `0xFFFF` (D-DT64-7-4, O8). The opcodes are still `ogar-loco` `FnIndex` values.
3 changes: 2 additions & 1 deletion .claude/board/entries/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@ index row, (3) no duplicate entry id. Checks 1 and 2 are deliberately
opposite directions; the stranding this convention prevents shows up in
exactly one of them, never both.

533 entries, 2026-08-06 .. 2026-10-31.
534 entries, 2026-08-06 .. 2026-10-31.

| date | entry id | finding | file |
|---|---|---|---|
| 2026-10-31 | `x2-dt64-admission-policy` | | [2026-10-31-x2-dt64-admission-policy.md](2026-10-31-x2-dt64-admission-policy.md) |
| 2026-10-31 | `r1-r2il-binding-reading` | | [2026-10-31-r1-r2il-binding-reading.md](2026-10-31-r1-r2il-binding-reading.md) |
| 2026-10-31 | `r1-native-prepared-successor` | | [2026-10-31-r1-native-prepared-successor.md](2026-10-31-r1-native-prepared-successor.md) |
| 2026-10-31 | `polyfill-positional-lens` | | [2026-10-31-polyfill-positional-lens.md](2026-10-31-polyfill-positional-lens.md) |
| 2026-10-31 | `ogar-behavior-probe-p2-p3-p9` | | [2026-10-31-ogar-behavior-probe-p2-p3-p9.md](2026-10-31-ogar-behavior-probe-p2-p3-p9.md) |
| 2026-10-31 | `D-SAG-6` | | [2026-10-31-kythe-bridge-ra-facts.md](2026-10-31-kythe-bridge-ra-facts.md) |
| 2026-10-31 | `D-XLOG-NATIVE-6` | | [2026-10-31-d-xlog-native-6-ndarray-simd-execution.md](2026-10-31-d-xlog-native-6-ndarray-simd-execution.md) |
Expand Down
6 changes: 6 additions & 0 deletions crates/lance-graph-r2il/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,9 @@ ogar-r2il = { git = "https://github.com/AdaWorldAPI/OGAR", branch = "main" }
# this crate executes it. Both are zero-dep beyond the contract.
lance-graph-dt64 = { path = "../lance-graph-dt64" }
lance-graph-contract = { path = "../lance-graph-contract" }

[dev-dependencies]
# The polyfill alias test (`tests/integration/polyfill.rs`) registers methods
# by Quack's names and checks each answer against Quack's own lowering, run
# through mask-risc. Quack is zero-OGAR and does not depend on this crate.
lance-graph-quack = { path = "../lance-graph-quack" }
70 changes: 59 additions & 11 deletions crates/lance-graph-r2il/src/binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,28 @@
//! **never** read as an [`FnIndex`] because the two happen to be `u8`.
//! [`prepare`] checks each effect before binding it:
//!
//! 1. the lens is the one this seam executes, and its concept half is
//! OGAR's R2IL fold reading ([`ogar_r2il::CONCEPT_R2IL_FOLD`], provisional
//! in OGAR: a lab lens, never a persisted identity);
//! 1. the lens is the one this seam executes, and it is one of two readings:
//! - the hardcoded polyfill lens [`POLYFILL_LENS`] (`0xFFFF :: 0xF000`),
//! where the POSITION binds (see 3);
//! - a lens whose concept half is OGAR's R2IL fold reading
//! ([`ogar_r2il::CONCEPT_R2IL_FOLD`], provisional in OGAR: a lab lens,
//! never a persisted identity), where the LABEL binds;
//! 2. the lens is bound in the store and its basis matches its `ClassView`
//! at the stated schema revision ([`BindStore::resolve_algebra`]);
//! 3. the word's LABEL resolves to exactly one opcode by OGAR's own
//! vocabulary ([`R2ILVocabulary`]), so the position a lens gives a word is
//! free and only the name binds;
//! 3. the word becomes an opcode:
//! - under [`POLYFILL_LENS`], word `p` is [`POLYFILL`]`[p]`; the view's
//! label at `p` must be one of that position's aliases (an R2IL, SQL or
//! Quack name), and a label belonging to another position is refused,
//! never followed;
//! - under the R2IL fold reading, the label resolves to exactly one opcode
//! by OGAR's own vocabulary ([`R2ILVocabulary`]), so the position is
//! free and only the name binds;
//! 4. the opcode is in the subset this seam translates faithfully
//! ([`EXECUTABLE`]);
//! 5. OGAR's stack arity equals the effect's argument count (which DT64's
//! registration already ties to the lens's declared arity), and OGAR says
//! the opcode pushes one result.
//! 5. the opcode's stack arity equals the effect's argument count (which
//! DT64's registration already ties to the lens's declared arity) and it
//! pushes one result: by the polyfill table under [`POLYFILL_LENS`], by
//! OGAR's vocabulary under the R2IL fold reading.
//!
//! # What is checked when
//!
Expand Down Expand Up @@ -74,6 +83,7 @@ use lance_graph_mask_risc::{LaneKind, LaneRef, Planes};
use ogar_loco::{FnIndex, Vocabulary};
use ogar_r2il::{R2ILVocabulary, ALL, ANY, CONCEPT_R2IL_FOLD, KEY_RUNS, MAX, MIN, SUM};

use crate::polyfill::{POLYFILL, POLYFILL_LENS};
use crate::{
Addr, FoldDialect, FoldError, Val, INT_ADD, INT_AND, INT_EQUAL, INT_MULT, INT_NOT, INT_OR,
INT_SUB, INT_S_DIV, INT_S_LESS, INT_XOR, POP_COUNT,
Expand Down Expand Up @@ -199,8 +209,16 @@ pub enum ExecRefusal {
ThreeValuedNotExecutable,
/// An effect names a lens other than the R2IL lens.
WrongLens { effect: usize, lens: u32 },
/// The R2IL lens's concept half is not OGAR's R2IL fold reading.
/// The lens is neither [`POLYFILL_LENS`] nor a lens whose concept half
/// is OGAR's R2IL fold reading.
NotR2ilReading { lens: u32 },
/// Under [`POLYFILL_LENS`], the view names the word with a label that is
/// not an alias of its position.
NotAnAlias {
effect: usize,
word: AlgebraRef,
label: String,
},
/// The word's position is outside the lens's basis.
WordOutOfBasis { effect: usize, word: AlgebraRef },
/// The word's label is not an R2IL opcode name, or names more than one.
Expand Down Expand Up @@ -275,7 +293,7 @@ pub fn prepare<V: ClassView + ?Sized>(
if binding.truth == Truth::ThreeValued {
return Err(ExecRefusal::ThreeValuedNotExecutable);
}
if classid_concept(lens.lens) != CONCEPT_R2IL_FOLD {
if lens.lens != POLYFILL_LENS && classid_concept(lens.lens) != CONCEPT_R2IL_FOLD {
return Err(ExecRefusal::NotR2ilReading { lens: lens.lens });
}
let steps = binding
Expand Down Expand Up @@ -493,6 +511,9 @@ fn opcode<V: ClassView + ?Sized>(
})?
.label
.as_str();
if lens.lens == POLYFILL_LENS {
return frozen_opcode(effect, e, label);
}
let mut named = (0..=u8::MAX)
.map(FnIndex)
.filter(|&f| R2ILVocabulary.name(f) == Some(label));
Expand Down Expand Up @@ -527,6 +548,33 @@ fn opcode<V: ClassView + ?Sized>(
Ok(f)
}

/// The opcode at a polyfill position: the position binds, the view's label
/// must be one of its aliases, and the table's arity must match.
fn frozen_opcode(effect: usize, e: &Effect, label: &str) -> Result<FnIndex, ExecRefusal> {
let word = POLYFILL
.get(usize::from(e.algebra.word))
.ok_or(ExecRefusal::WordOutOfBasis {
effect,
word: e.algebra,
})?;
if !word.aliases.contains(&label) {
return Err(ExecRefusal::NotAnAlias {
effect,
word: e.algebra,
label: label.to_string(),
});
}
if usize::from(word.arity) != e.args.len() {
return Err(ExecRefusal::Arity {
effect,
ogar: None,
declared: Some(word.arity),
args: e.args.len(),
});
}
Ok(word.opcode)
}

/// The address of the resident lane field `p` is bound to.
pub(crate) fn lane(
planes: &Planes<'_>,
Expand Down
1 change: 1 addition & 0 deletions crates/lance-graph-r2il/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@

pub mod binding;
pub mod binding_native;
pub mod polyfill;
pub mod procs;

use std::cell::Cell;
Expand Down
102 changes: 102 additions & 0 deletions crates/lance-graph-r2il/src/polyfill.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//! The hardcoded relational polyfill as a positional lens (D-DT64-7 §1a, §1b).
//!
//! Hardcoded behaviour is addressed `0xFFFF :: 0xF###`: left side `0xFFFF`,
//! right side a behaviour lens in a concern range. [`POLYFILL_LENS`] is
//! `0xFFFF :: 0xF000`, the relational / mask-risc polyfill.
//!
//! **Position binds; labels are aliases.** Word `p` of this lens is
//! [`POLYFILL`]`[p]`, whatever any view calls it. A view over the lens
//! supplies its language's names for the positions (an R2IL opcode name, a
//! SQL keyword, a Quack variant), and each name is checked against the
//! aliases fixed at that position. A name that belongs to another position,
//! or to none, is refused; it never selects a different word.
//!
//! An alias states that the word computes the same thing as the named
//! operation over a non-empty selection. Over an empty selection SQL answers
//! `NULL` for `SUM`, `MIN` and `MAX`, while this reading answers the fold's
//! identity (`0`, `i64::MAX`, `i64::MIN`); a three-valued binding is refused
//! by [`crate::binding::prepare`], so `NULL` never reaches it.
//!
//! The positions are the [`crate::binding::EXECUTABLE`] opcodes in that
//! order. The opcode, arity and R2IL name of each entry are checked against
//! OGAR's `R2ILVocabulary` by test, not at run time.

use lance_graph_contract::ogar_codebook::compose_classid;
use ogar_loco::FnIndex;
use ogar_r2il::{ALL, ANY, KEY_RUNS, MAX, MIN, SUM};

use crate::{
INT_ADD, INT_AND, INT_EQUAL, INT_MULT, INT_NOT, INT_OR, INT_SUB, INT_S_DIV, INT_S_LESS,
INT_XOR, POP_COUNT,
};

/// The left side of every hardcoded behaviour classid.
pub const HARDCODED: u16 = 0xFFFF;

/// The relational / mask-risc polyfill lens, `0xFFFF :: 0xF000`.
pub const POLYFILL_LENS: u32 = compose_classid(HARDCODED, 0xF000);

/// One word of the polyfill lens.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FrozenWord {
/// The fold-dialect opcode the word runs as.
pub opcode: FnIndex,
/// The number of stack operands it takes; it always pushes one value.
pub arity: u8,
/// Every name a view may give this position. The first is the R2IL
/// opcode name.
pub aliases: &'static [&'static str],
}

const fn w(opcode: FnIndex, arity: u8, aliases: &'static [&'static str]) -> FrozenWord {
FrozenWord {
opcode,
arity,
aliases,
}
}

/// The polyfill lens, by position.
pub const POLYFILL: [FrozenWord; 17] = [
w(INT_ADD, 2, &["IntAdd", "+"]),
w(INT_SUB, 2, &["IntSub", "-"]),
w(INT_MULT, 2, &["IntMult", "*"]),
w(INT_S_DIV, 2, &["IntSDiv", "/"]),
w(INT_EQUAL, 2, &["IntEqual", "=", "Cmp::EqI32", "Cmp::EqU32"]),
w(INT_S_LESS, 2, &["IntSLess", "<", "Cmp::LtI32"]),
w(INT_AND, 2, &["IntAnd", "AND", "Filter::and"]),
w(INT_OR, 2, &["IntOr", "OR", "Filter::or"]),
w(INT_XOR, 2, &["IntXor", "XOR"]),
w(INT_NOT, 1, &["IntNot", "NOT", "Filter::negate"]),
w(POP_COUNT, 1, &["PopCount", "COUNT", "Agg::Count"]),
w(SUM, 2, &["SUM", "Agg::SumI32"]),
w(MIN, 2, &["MIN", "Agg::MinI32"]),
w(MAX, 2, &["MAX", "Agg::MaxI32"]),
w(ANY, 1, &["ANY", "EXISTS", "Agg::Any"]),
w(ALL, 1, &["ALL", "Agg::All"]),
w(
KEY_RUNS,
2,
&["KEY_RUNS", "COUNT DISTINCT", "Agg::CountDistinctOrderedU32"],
),
];

/// The lens's declared arities, in position order, for
/// `BindStore::declare_algebra`.
#[must_use]
pub fn polyfill_arities() -> Vec<u8> {
POLYFILL.iter().map(|w| w.arity).collect()
}

/// The position whose aliases include `label`, if exactly one does.
#[must_use]
pub fn position_of(label: &str) -> Option<u8> {
let mut hits = POLYFILL
.iter()
.enumerate()
.filter(|(_, w)| w.aliases.contains(&label));
match (hits.next(), hits.next()) {
(Some((p, _)), None) => u8::try_from(p).ok(),
_ => None,
}
}
1 change: 1 addition & 0 deletions crates/lance-graph-r2il/tests/integration/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@

pub mod binding;
pub mod binding_native;
pub mod polyfill;
Loading
Loading