diff --git a/src/v3/grounding_engine/Cargo.toml b/src/v3/grounding_engine/Cargo.toml index f4c93643ac7..92da56d4c6e 100644 --- a/src/v3/grounding_engine/Cargo.toml +++ b/src/v3/grounding_engine/Cargo.toml @@ -4,9 +4,8 @@ version.workspace = true edition.workspace = true license.workspace = true -# T-Ground-Engine Phase 1 validates the RustPrimitive type structure -# through v3-compiler's loader accessor. Pilot-list enumeration remains -# in v3-grounding-pilot until Phase 2 grows structured list bodies. +# T-Ground-Engine: Phase 1 type-structure validation + Phase 2 enumeration of +# `rust_pilot_primitives` list bodies via v3-compiler's Dag accessor. [dependencies] v3-compiler = { path = "../compiler" } diff --git a/src/v3/grounding_engine/src/lib.rs b/src/v3/grounding_engine/src/lib.rs index 9cfebd71cf5..e151ddc6f42 100644 --- a/src/v3/grounding_engine/src/lib.rs +++ b/src/v3/grounding_engine/src/lib.rs @@ -1,14 +1,19 @@ -//! T-Ground-Engine Phase 1: Rust target primitive type-structure validation. +//! T-Ground-Engine: Rust target primitive validation against the bootstrap Dag. //! -//! This crate consumes `v3_compiler::dag::Dag::rust_pilot_primitives()` and -//! walks the loaded `Declaration` graph for `RustPrimitive`. It deliberately -//! does not enumerate `rust_pilot_primitives.value_body`: Phase 1 keeps the -//! pilot crate's `RUST_PILOT_PRIMITIVES` mirror for routing until the substrate -//! grows structured top-level list bodies. +//! **Phase 1** walks the loaded `Declaration` graph for `RustPrimitive` +//! type structure (sum partition, field shapes, nested tag enums). +//! +//! **Phase 2 (sharpened-(b), enumeration slice)** walks +//! `rust_pilot_primitives.value_body` as [`ValueBody::List`] and checks that +//! the first pilot row matches the authority ordering in +//! `dsl/extdeps/languages/rust/primitives.dag` (asserted here via the pilot +//! crate's `RUST_PILOT_PRIMITIVES` mirror until mirror retirement completes). use std::collections::{BTreeMap, BTreeSet}; -use v3_compiler::dag::{Dag, Declaration, DeclarationId, Field, TypeConnective}; +use v3_compiler::dag::{ + Dag, Declaration, DeclarationId, Field, FieldValue, LiteralBits, TypeConnective, ValueBody, +}; use v3_grounding_pilot::{ IntegerAlgebra as PilotIntegerAlgebra, IntegerOverflow as PilotIntegerOverflow, NonIntegerAlgebra as PilotNonIntegerAlgebra, RustPrimitive as PilotRustPrimitive, @@ -158,7 +163,94 @@ pub fn validate_mirror_consistency() -> StructureResult<()> { actual: format!("{shape:?}"), }); } - validate_pilot_values_with_shape(&shape) + validate_pilot_values_with_shape(&shape)?; + validate_first_rust_pilot_row_matches_mirror() +} + +/// Phase 2: the first lowered `rust_pilot_primitives` list element must match +/// `RUST_PILOT_PRIMITIVES[0]` (authority ordering in `primitives.dag`). +pub fn validate_first_rust_pilot_row_matches_mirror() -> StructureResult<()> { + let dag = Dag::new(); + let pilot_list = dag + .rust_pilot_primitives() + .ok_or_else(|| StructureMismatch { + location: "Dag::rust_pilot_primitives".to_string(), + expected: "loaded rust_pilot_primitives declaration".to_string(), + actual: "missing".to_string(), + })?; + + let rust_primitive_id = rust_primitive_element_id(pilot_list)?; + let rust_primitive = dag.declaration(rust_primitive_id); + let disj_variants = expect_disj_variants( + rust_primitive, + "RustPrimitive", + &["IntegerPrimitive", "NonIntegerPrimitive"], + )?; + + let body = pilot_list + .value_body + .as_ref() + .ok_or_else(|| StructureMismatch { + location: "rust_pilot_primitives.value_body".to_string(), + expected: "data body present".to_string(), + actual: "None".to_string(), + })?; + let ValueBody::List(elements) = body else { + return Err(StructureMismatch { + location: "rust_pilot_primitives.value_body".to_string(), + expected: "ValueBody::List".to_string(), + actual: value_body_kind(body), + }); + }; + let first = elements.first().ok_or_else(|| StructureMismatch { + location: "rust_pilot_primitives.value_body".to_string(), + expected: "non-empty pilot list".to_string(), + actual: "empty list".to_string(), + })?; + let FieldValue::Variant { + constructor, + payload, + } = first + else { + return Err(StructureMismatch { + location: "rust_pilot_primitives[0]".to_string(), + expected: "FieldValue::Variant".to_string(), + actual: field_value_kind(first), + }); + }; + + let variant_label = rust_primitive_variant_label(&disj_variants, *constructor)?; + let mirror0 = RUST_PILOT_PRIMITIVES + .first() + .ok_or_else(|| StructureMismatch { + location: "RUST_PILOT_PRIMITIVES".to_string(), + expected: "non-empty mirror slice".to_string(), + actual: "empty".to_string(), + })?; + + let label = variant_label.as_str(); + match mirror0 { + PilotRustPrimitive::IntegerPrimitive { .. } => { + if label != "IntegerPrimitive" { + return Err(StructureMismatch { + location: "rust_pilot_primitives[0]".to_string(), + expected: "IntegerPrimitive (first authority row)".to_string(), + actual: label.to_string(), + }); + } + assert_integer_primitive_payload_matches(&dag, payload, mirror0) + } + PilotRustPrimitive::NonIntegerPrimitive { .. } => { + if label != "NonIntegerPrimitive" { + return Err(StructureMismatch { + location: "rust_pilot_primitives[0]".to_string(), + expected: "NonIntegerPrimitive (first authority row)".to_string(), + actual: label.to_string(), + }); + } + assert_non_integer_primitive_payload_matches(&dag, payload, mirror0) + } + } } pub fn require_field_absent(variant: &VariantShape, field: &str) -> StructureResult<()> { @@ -318,7 +410,7 @@ fn connective_name(connective: &TypeConnective) -> &'static str { TypeConnective::Conj { .. } => "Conj", TypeConnective::Disj { .. } => "Disj", TypeConnective::Arrow { .. } => "Arrow", - TypeConnective::Cardinality(_) => "Cardinality", + TypeConnective::Cardinality { .. } => "Cardinality", TypeConnective::Instantiation { .. } => "Instantiation", } } @@ -498,6 +590,271 @@ fn pilot_overflow_name(overflow: PilotIntegerOverflow) -> &'static str { } } +fn value_body_kind(body: &ValueBody) -> String { + match body { + ValueBody::Unparsed(_) => "ValueBody::Unparsed".to_string(), + ValueBody::Structural { .. } => "ValueBody::Structural".to_string(), + ValueBody::Scalar(_) => "ValueBody::Scalar".to_string(), + ValueBody::List(_) => "ValueBody::List".to_string(), + } +} + +fn field_value_kind(value: &FieldValue) -> String { + match value { + FieldValue::Literal(_) => "FieldValue::Literal".to_string(), + FieldValue::Reference(_) => "FieldValue::Reference".to_string(), + FieldValue::Record(_) => "FieldValue::Record".to_string(), + FieldValue::List(_) => "FieldValue::List".to_string(), + FieldValue::Variant { .. } => "FieldValue::Variant".to_string(), + } +} + +fn rust_primitive_variant_label( + disj_variants: &[Field], + constructor: DeclarationId, +) -> StructureResult { + disj_variants + .iter() + .find(|variant| variant.ty == constructor) + .map(|variant| variant.label.clone()) + .ok_or_else(|| StructureMismatch { + location: "rust_pilot_primitives[0].constructor".to_string(), + expected: "constructor id belonging to RustPrimitive".to_string(), + actual: "unknown constructor".to_string(), + }) +} + +fn expect_literal_string_at( + payload: &[FieldValue], + idx: usize, + ctx: &str, +) -> StructureResult { + let value = payload.get(idx).ok_or_else(|| StructureMismatch { + location: ctx.to_string(), + expected: format!("payload field index {idx}"), + actual: format!("len {}", payload.len()), + })?; + let FieldValue::Literal(LiteralBits::String(s)) = value else { + return Err(StructureMismatch { + location: format!("{ctx}[{idx}]"), + expected: "Literal String".to_string(), + actual: field_value_kind(value), + }); + }; + Ok(s.clone()) +} + +fn expect_literal_bool_at(payload: &[FieldValue], idx: usize, ctx: &str) -> StructureResult { + let value = payload.get(idx).ok_or_else(|| StructureMismatch { + location: ctx.to_string(), + expected: format!("payload field index {idx}"), + actual: format!("len {}", payload.len()), + })?; + let FieldValue::Literal(LiteralBits::Bool(b)) = value else { + return Err(StructureMismatch { + location: format!("{ctx}[{idx}]"), + expected: "Literal Bool".to_string(), + actual: field_value_kind(value), + }); + }; + Ok(*b) +} + +fn expect_nullary_variant_label( + dag: &Dag, + enum_name: &str, + expected_variants: &[&str], + value: &FieldValue, + ctx: &str, +) -> StructureResult { + let FieldValue::Variant { + constructor, + payload, + } = value + else { + return Err(StructureMismatch { + location: ctx.to_string(), + expected: "FieldValue::Variant".to_string(), + actual: field_value_kind(value), + }); + }; + if !payload.is_empty() { + return Err(StructureMismatch { + location: ctx.to_string(), + expected: "nullary enum constructor (empty payload)".to_string(), + actual: format!("payload len {}", payload.len()), + }); + } + let enum_decl = dag + .declaration_by_name(enum_name) + .ok_or_else(|| StructureMismatch { + location: ctx.to_string(), + expected: format!("declared enum `{enum_name}`"), + actual: "missing".to_string(), + })?; + let variants = expect_disj_variants(enum_decl, enum_name, expected_variants)?; + variants + .iter() + .find(|variant| variant.ty == *constructor) + .map(|variant| variant.label.clone()) + .ok_or_else(|| StructureMismatch { + location: ctx.to_string(), + expected: format!("constructor id belonging to {enum_name}"), + actual: "unknown constructor".to_string(), + }) +} + +fn require_string_eq(actual: &str, expected: &str, field: &str) -> StructureResult<()> { + if actual == expected { + Ok(()) + } else { + Err(StructureMismatch { + location: field.to_string(), + expected: expected.to_string(), + actual: actual.to_string(), + }) + } +} + +fn assert_integer_primitive_payload_matches( + dag: &Dag, + payload: &[FieldValue], + pilot: &PilotRustPrimitive, +) -> StructureResult<()> { + let PilotRustPrimitive::IntegerPrimitive { + target_name, + algebra, + carrier, + range_min_inclusive, + range_max_inclusive, + is_copy, + overflow, + } = pilot + else { + return Err(StructureMismatch { + location: "validate_first_rust_pilot_row_matches_mirror".to_string(), + expected: "IntegerPrimitive mirror row".to_string(), + actual: "NonIntegerPrimitive".to_string(), + }); + }; + let ctx = "RustPrimitive.IntegerPrimitive payload"; + if payload.len() != 7 { + return Err(StructureMismatch { + location: ctx.to_string(), + expected: "7 positional fields".to_string(), + actual: format!("{}", payload.len()), + }); + } + let tn = expect_literal_string_at(payload, 0, ctx)?; + require_string_eq(&tn, target_name, "target_name")?; + let alg = expect_nullary_variant_label( + dag, + "IntegerAlgebra", + &["OrderedRingAlgebra", "SemiringAlgebra"], + &payload[1], + &format!("{ctx}.algebra"), + )?; + require_string_eq(&alg, pilot_integer_algebra_name(*algebra), "algebra")?; + let car = expect_nullary_variant_label( + dag, + "TargetCarrier", + &[ + "BitCarrier", + "ByteCarrier", + "Word16Carrier", + "Word32Carrier", + "Word64Carrier", + "TerminalCarrier", + ], + &payload[2], + &format!("{ctx}.carrier"), + )?; + require_string_eq(&car, pilot_carrier_name(*carrier), "carrier")?; + let rmin = expect_literal_string_at(payload, 3, ctx)?; + require_string_eq(&rmin, range_min_inclusive, "range_min_inclusive")?; + let rmax = expect_literal_string_at(payload, 4, ctx)?; + require_string_eq(&rmax, range_max_inclusive, "range_max_inclusive")?; + let ic = expect_literal_bool_at(payload, 5, ctx)?; + if ic != *is_copy { + return Err(StructureMismatch { + location: format!("{ctx}.is_copy"), + expected: format!("{is_copy}"), + actual: format!("{ic}"), + }); + } + let ov = expect_nullary_variant_label( + dag, + "IntegerOverflow", + &["TwoComplementWrap", "Saturating", "Trap"], + &payload[6], + &format!("{ctx}.overflow"), + )?; + require_string_eq(&ov, pilot_overflow_name(*overflow), "overflow")?; + Ok(()) +} + +fn assert_non_integer_primitive_payload_matches( + dag: &Dag, + payload: &[FieldValue], + pilot: &PilotRustPrimitive, +) -> StructureResult<()> { + let PilotRustPrimitive::NonIntegerPrimitive { + target_name, + algebra, + carrier, + is_copy, + } = pilot + else { + return Err(StructureMismatch { + location: "validate_first_rust_pilot_row_matches_mirror".to_string(), + expected: "NonIntegerPrimitive mirror row".to_string(), + actual: "IntegerPrimitive".to_string(), + }); + }; + let ctx = "RustPrimitive.NonIntegerPrimitive payload"; + if payload.len() != 4 { + return Err(StructureMismatch { + location: ctx.to_string(), + expected: "4 positional fields".to_string(), + actual: format!("{}", payload.len()), + }); + } + let tn = expect_literal_string_at(payload, 0, ctx)?; + require_string_eq(&tn, target_name, "target_name")?; + let alg = expect_nullary_variant_label( + dag, + "NonIntegerAlgebra", + &["BooleanAlgebraAlgebra", "TerminalAlgebra"], + &payload[1], + &format!("{ctx}.algebra"), + )?; + require_string_eq(&alg, pilot_non_integer_algebra_name(*algebra), "algebra")?; + let car = expect_nullary_variant_label( + dag, + "TargetCarrier", + &[ + "BitCarrier", + "ByteCarrier", + "Word16Carrier", + "Word32Carrier", + "Word64Carrier", + "TerminalCarrier", + ], + &payload[2], + &format!("{ctx}.carrier"), + )?; + require_string_eq(&car, pilot_carrier_name(*carrier), "carrier")?; + let ic = expect_literal_bool_at(payload, 3, ctx)?; + if ic != *is_copy { + return Err(StructureMismatch { + location: format!("{ctx}.is_copy"), + expected: format!("{is_copy}"), + actual: format!("{ic}"), + }); + } + Ok(()) +} + #[cfg(test)] mod tests { use super::*; @@ -513,6 +870,12 @@ mod tests { validate_mirror_consistency().expect("pilot mirror matches loaded type shape"); } + #[test] + fn first_enumerated_pilot_row_matches_mirror_i8() { + validate_first_rust_pilot_row_matches_mirror() + .expect("first lowered list row matches RUST_PILOT_PRIMITIVES[0] (i8)"); + } + #[test] fn state_space_keeps_overflow_off_non_integer_variant() { let shape = validate_loaded_rust_primitive_type_structure().expect("shape validates");