|
3 | 3 | //! Read type-judgment witnesses; these do not claim public compiler provenance. |
4 | 4 | use super::*; |
5 | 5 |
|
| 6 | +#[test] |
| 7 | +fn ordered_list_guards_accept_strict_word_comparisons() { |
| 8 | + let core = core(); |
| 9 | + let operation = |name: &str, args| { |
| 10 | + m([ |
| 11 | + ("kind", t("call")), |
| 12 | + ("callee", t(name)), |
| 13 | + ("typeArgs", Value::Array(vec![t("Numbers")])), |
| 14 | + ("args", Value::Array(args)), |
| 15 | + ]) |
| 16 | + }; |
| 17 | + let length = operation("core.list.length", vec![call("numbers", vec![])]); |
| 18 | + let access = operation("core.list.at", vec![call("numbers", vec![]), integer(0)]); |
| 19 | + let conditional = |op: &str, index: Value, reversed: bool, negated: bool| { |
| 20 | + let (left, right) = if reversed { |
| 21 | + (length.clone(), index) |
| 22 | + } else { |
| 23 | + (index, length.clone()) |
| 24 | + }; |
| 25 | + m([ |
| 26 | + ("kind", t("if")), |
| 27 | + ( |
| 28 | + "predicate", |
| 29 | + m([ |
| 30 | + ("kind", t("compare")), |
| 31 | + ("op", t(op)), |
| 32 | + ("left", left), |
| 33 | + ("right", right), |
| 34 | + ]), |
| 35 | + ), |
| 36 | + ("then", if negated { integer(0) } else { access.clone() }), |
| 37 | + ("else", if negated { access.clone() } else { integer(0) }), |
| 38 | + ]) |
| 39 | + }; |
| 40 | + let wrong_width = m([ |
| 41 | + ("kind", t("const")), |
| 42 | + ( |
| 43 | + "value", |
| 44 | + m([ |
| 45 | + ("kind", t("int")), |
| 46 | + ("width", t("U8")), |
| 47 | + ("value", Value::Integer(0)), |
| 48 | + ]), |
| 49 | + ), |
| 50 | + ]); |
| 51 | + let mut admitted = Vec::new(); |
| 52 | + for (op, reversed, negated) in [ |
| 53 | + ("<", false, false), |
| 54 | + ("<=", true, true), |
| 55 | + (">", true, false), |
| 56 | + (">=", false, true), |
| 57 | + ] { |
| 58 | + admitted.push( |
| 59 | + check( |
| 60 | + &core, |
| 61 | + &conditional(op, integer(0), reversed, negated), |
| 62 | + "U64", |
| 63 | + ) |
| 64 | + .is_ok(), |
| 65 | + ); |
| 66 | + assert_refusal(check( |
| 67 | + &core, |
| 68 | + &conditional(op, wrong_width.clone(), reversed, negated), |
| 69 | + "U64", |
| 70 | + )); |
| 71 | + } |
| 72 | + assert_eq!( |
| 73 | + admitted, |
| 74 | + vec![true, true, true, true], |
| 75 | + "all compiler-supported strict guard orientations" |
| 76 | + ); |
| 77 | +} |
| 78 | + |
| 79 | +#[test] |
| 80 | +fn ordered_list_operations_preserve_types_after_domain_validation() { |
| 81 | + let core = core(); |
| 82 | + let operation = |name: &str, types: &[&str], args| { |
| 83 | + m([ |
| 84 | + ("kind", t("call")), |
| 85 | + ("callee", t(name)), |
| 86 | + ( |
| 87 | + "typeArgs", |
| 88 | + Value::Array(types.iter().map(|s| t(s)).collect()), |
| 89 | + ), |
| 90 | + ("args", Value::Array(args)), |
| 91 | + ]) |
| 92 | + }; |
| 93 | + let numbers = || call("numbers", vec![]); |
| 94 | + // This type relation runs after the separate whole-intent domain audit. |
| 95 | + for (value, expected) in [ |
| 96 | + ( |
| 97 | + operation("core.list.length", &["Numbers"], vec![numbers()]), |
| 98 | + "U64", |
| 99 | + ), |
| 100 | + ( |
| 101 | + operation("core.list.at", &["Numbers"], vec![numbers(), integer(0)]), |
| 102 | + "U64", |
| 103 | + ), |
| 104 | + ( |
| 105 | + operation( |
| 106 | + "core.list.append", |
| 107 | + &["Numbers"], |
| 108 | + vec![numbers(), integer(7)], |
| 109 | + ), |
| 110 | + "Numbers", |
| 111 | + ), |
| 112 | + ( |
| 113 | + operation( |
| 114 | + "core.list.concat", |
| 115 | + &["Numbers", "Numbers"], |
| 116 | + vec![numbers(), numbers()], |
| 117 | + ), |
| 118 | + "List<U64,max=4>", |
| 119 | + ), |
| 120 | + ] { |
| 121 | + assert!(check(&core, &value, expected).is_ok(), "{expected}"); |
| 122 | + } |
| 123 | + for (value, expected) in [ |
| 124 | + ( |
| 125 | + operation( |
| 126 | + "core.list.append", |
| 127 | + &["List<U64,max=3>"], |
| 128 | + vec![numbers(), integer(7)], |
| 129 | + ), |
| 130 | + "List<U64,max=3>", |
| 131 | + ), |
| 132 | + ( |
| 133 | + operation( |
| 134 | + "core.list.concat", |
| 135 | + &["Numbers", "Numbers"], |
| 136 | + vec![numbers(), numbers()], |
| 137 | + ), |
| 138 | + "List<U64,max=3>", |
| 139 | + ), |
| 140 | + ( |
| 141 | + operation( |
| 142 | + "core.list.at", |
| 143 | + &["List<U8,max=2>"], |
| 144 | + vec![numbers(), integer(0)], |
| 145 | + ), |
| 146 | + "U8", |
| 147 | + ), |
| 148 | + ( |
| 149 | + operation( |
| 150 | + "core.list.concat", |
| 151 | + &["List<U64,max=18446744073709551615>", "Numbers"], |
| 152 | + vec![numbers(), numbers()], |
| 153 | + ), |
| 154 | + "Numbers", |
| 155 | + ), |
| 156 | + ] { |
| 157 | + assert_refusal(check(&core, &value, expected)); |
| 158 | + } |
| 159 | +} |
| 160 | + |
6 | 161 | fn t(value: &str) -> Value { |
7 | 162 | Value::Text(value.into()) |
8 | 163 | } |
@@ -47,6 +202,7 @@ fn row(value: Value) -> Value { |
47 | 202 | fn core() -> Value { |
48 | 203 | m([ |
49 | 204 | ("coordinate", t("example@1")), |
| 205 | + ("pureFunctions", Value::Array(vec![])), |
50 | 206 | ( |
51 | 207 | "types", |
52 | 208 | m([ |
@@ -255,3 +411,38 @@ fn assert_refusal(result: Result<(), ProviderRefusalV1>) { |
255 | 411 | "{result:?}" |
256 | 412 | ); |
257 | 413 | } |
| 414 | + |
| 415 | +#[test] |
| 416 | +fn list_inline_record_types_preserve_structural_and_nominal_meaning() { |
| 417 | + let core = core(); |
| 418 | + let judgment = scope(&core); |
| 419 | + assert!(judgment.ty("Row").is_ok()); |
| 420 | + assert!(judgment.ty("Record<payload:Bytes<max=4>>").is_ok()); |
| 421 | + assert_eq!( |
| 422 | + judgment.ty("Row"), |
| 423 | + judgment.ty("Record<payload:Bytes<max=4>>") |
| 424 | + ); |
| 425 | + assert!(judgment |
| 426 | + .ty("List<Record<a:A,nested:List<Record<x:U8>,max=2>>,max=3>") |
| 427 | + .is_ok()); |
| 428 | + assert!(judgment.ty("Record<a:A>").is_ok()); |
| 429 | + assert!(judgment.ty("Record<a:B>").is_ok()); |
| 430 | + assert_ne!(judgment.ty("Record<a:A>"), judgment.ty("Record<a:B>")); |
| 431 | + assert!(judgment.ty("Record<>").is_ok()); |
| 432 | + for invalid in [ |
| 433 | + "Record<x:U8,x:U8>", |
| 434 | + "Record<z:U8,a:U8>", |
| 435 | + "Record<x:U8,>", |
| 436 | + "Record<:U8>", |
| 437 | + "Record<0x:U8>", |
| 438 | + "Record<x:>", |
| 439 | + "Record<x:List<U8,max=1>>>", |
| 440 | + "Record<x:Unknown>", |
| 441 | + ] { |
| 442 | + assert!(judgment.ty(invalid).is_err(), "{invalid}"); |
| 443 | + } |
| 444 | + judgment.remaining_type_work.set(3); |
| 445 | + assert!(judgment.ty("Record<long:U8,z:U8>").is_err()); |
| 446 | + let deep = format!("{}U8{}", "Record<x:".repeat(65), ">".repeat(65)); |
| 447 | + assert!(scope(&core).ty(&deep).is_err()); |
| 448 | +} |
0 commit comments