diff --git a/layout/src/code.rs b/layout/src/code.rs index b9a285f4..446f0133 100644 --- a/layout/src/code.rs +++ b/layout/src/code.rs @@ -1240,6 +1240,13 @@ fn write_linear_inst( .get(r.as_str()) .map(String::as_str) .unwrap_or(r.as_str()); + // 未定型整数字面量适配(spec 03-类型系统/01 第 10 条):typed 无符号算子 + // (`uint64·add` 等)的字面量操作数按该 kind 落槽——runtime 的 typed op 只 + // 接受同符号性族的操作数,int64 字面量进 `uint64·add` 会失败。 + if let Some((wire, lt)) = adapted_literal_slot(rv, &opcode)? { + pairs.push((format!("{prefix}/[{n},-{}]", j + 1), lt, wire)); + continue; + } let ty = read_slot_type(r, param_langtype, declared, type_sets); let (wire, lt) = typed_slot_value(rv, &ty, target_char)?; pairs.push((format!("{prefix}/[{n},-{}]", j + 1), lt, wire)); @@ -1403,6 +1410,46 @@ fn opcode_value( } /// Preserve the target type. +/// 整数字面量文本(可选负号,无小数点/指数)。 +fn integer_literal_text(s: &str) -> bool { + let b = s.as_bytes(); + let start = usize::from(b.first() == Some(&b'-')); + let rest = &b[start..]; + !rest.is_empty() && rest.iter().all(u8::is_ascii_digit) +} + +/// typed 无符号算子的字面量操作数:按算子 kind 编码(范围复检,越界不改编码)。 +fn adapted_literal_slot( + text: &str, + opcode: &str, +) -> Result, String)>, String> { + let Some((kind, _)) = opcode.split_once(keytree::MEMBER_SEP) else { + return Ok(None); + }; + if !builtin::is_unsigned_kind(kind) || !integer_literal_text(text) { + return Ok(None); + } + let Ok(v) = text.parse::() else { + return Ok(None); + }; + if v < 0 { + return Ok(None); + } + let v = v as u128; + let (bytes, max): (Vec, u128) = match kind { + "uint8" => (vec![v as u8], u8::MAX as u128), + "uint16" => ((v as u16).to_le_bytes().to_vec(), u16::MAX as u128), + "uint32" => ((v as u32).to_le_bytes().to_vec(), u32::MAX as u128), + "uint64" => ((v as u64).to_le_bytes().to_vec(), u64::MAX as u128), + _ => return Ok(None), + }; + if v > max { + return Ok(None); + } + let wire = ffi::xvalue_to_wire(kind, &bytes, 1)?; + Ok(Some((wire, kind.to_string()))) +} + fn typed_slot_value(name: &str, ty: &str, target_char: &str) -> Result<(Vec, String), String> { if !ty.is_empty() && (name.starts_with('/') || !is_literal(name)) { return Ok(( diff --git a/layout/src/lower.rs b/layout/src/lower.rs index 302e1640..91fce041 100644 --- a/layout/src/lower.rs +++ b/layout/src/lower.rs @@ -1493,6 +1493,35 @@ fn infer_op_type(opcode: &str, reads: &[String], tm: &mut HashMap = Vec::new(); + let mut al: Vec> = Vec::new(); + for r in reads { + let t = slot_type(r, tm); + let members = type_members(&t); + if members.is_empty() { + return String::new(); + } + if matches!(literal_text_kind(r), Some("int64")) { + ak.push("int64".to_string()); + al.push(Some(r.as_str())); + } else { + for m in &members { + if !builtin::is_num_kind(m) { + return String::new(); + } + ak.push(m.to_string()); + al.push(None); + } + } + } + if let Some(target) = adapt_int_uint(&ak, &al) { + let rk = builtin::op_kind(&word, &target); + return if rk.is_empty() { target } else { rk }; + } + } // 结果 kind 必须与 runtime 一致:同族按更宽者合流(narrow_int/narrow_float); // 任一操作数类型未知(动态 any/空)→ 不声明,runtime 按实际 kind 产出。 // union 操作数按成员笛卡尔积求更宽者,结果仍是有限 union。 @@ -2157,6 +2186,7 @@ fn specialize_inst( /* 操作数类型必须全部可证明(exact_operand_kind);混合数值 kind 按五语言 * 提升规则取唯一结果 kind,证明不了(未知/字符串/bool/不可判混合)保留通用 opcode。 */ let mut kinds: Vec = Vec::new(); + let mut literals: Vec> = Vec::new(); let mut string_operands = 0usize; for arg in &e.args { let t = match exact_operand_kind(arg, declared, stable) { @@ -2166,6 +2196,7 @@ fn specialize_inst( if arg.is_leaf() && sets_all_string(type_sets, &arg.val) { string_operands += 1; kinds.push("char/utf32".to_string()); + literals.push(None); continue; } return; @@ -2176,6 +2207,7 @@ fn specialize_inst( } else if !builtin::is_num_kind(&t) { return; } + literals.push(untyped_int_literal(arg)); kinds.push(t); } if kinds.is_empty() { @@ -2206,7 +2238,7 @@ fn specialize_inst( let is_cmp = matches!(word.as_str(), "eq" | "neq" | "lt" | "le" | "gt" | "ge"); /* 混合 int×float 的比较在 runtime 报错(same_class=false);不特化以免发出 * 与解释器行为不符的 typed 名。 */ - let mut k = match promoted_numeric_kind(&kinds, !is_cmp) { + let mut k = match promoted_numeric_kind(&kinds, !is_cmp, &literals) { Some(k) => k, None => return, }; @@ -2270,7 +2302,98 @@ fn specialize_inst( /// 否则 float32; /// - int×uint 混合 runtime 直接报错(same_class=false),不特化。 /// `allow_mixed_classes=false`(比较类)时跨类混合一律不特化。 -fn promoted_numeric_kind(kinds: &[String], allow_mixed_classes: bool) -> Option { +/// 未定型整数字面量:叶且整数文本(含 lower 合成的文本叶),返回字面量文本。 +/// 浮点字面量不算:浮点走「任一侧浮点则提升为浮点」的既有路径。 +fn untyped_int_literal(e: &Expr) -> Option<&str> { + if !e.is_leaf() || e.quote != 0 { + return None; + } + match e.lit { + LitKind::LitInt => Some(e.val.as_str()), + LitKind::LitNone + if matches!(literal_text_kind(&e.val), Some("int64")) => + { + Some(e.val.as_str()) + } + _ => None, + } +} + +/// 整数字面量文本是否落在目标整数 kind 的取值范围内。 +fn literal_fits(text: &str, kind: &str) -> bool { + let Ok(v) = text.parse::() else { + return false; + }; + let (signed, width) = match kind { + "int8" => (true, 8), + "int16" => (true, 16), + "int32" => (true, 32), + "int64" => (true, 64), + "uint8" => (false, 8), + "uint16" => (false, 16), + "uint32" => (false, 32), + "uint64" => (false, 64), + _ => return false, + }; + if signed { + let max = (1i128 << (width - 1)) - 1; + v >= -max - 1 && v <= max + } else { + v >= 0 && (v as u128) <= (1u128 << width) - 1 + } +} + +/// int×uint 混合时,若冲突只来自「未定型整数字面量」,把字面量适配到确定类型那一侧。 +/// 规则(spec 01-种类与定宽类型 第 10 条):字面量按上下文适配;只有两侧都是**确定** +/// 且冲突的符号性才保持混合冲突(返回 None)。 +fn adapt_int_uint(kinds: &[String], literals: &[Option<&str>]) -> Option { + let (mut det_int, mut det_uint) = (None::, None::); + let mut has_lit = false; + for (i, k) in kinds.iter().enumerate() { + if literals.get(i).is_some_and(|l| l.is_some()) { + has_lit = true; + continue; + } + if builtin::is_unsigned_kind(k) { + det_uint = Some(match det_uint { + Some(t) => builtin::wider_int_kind(&t, k), + None => k.clone(), + }); + } else if builtin::is_int_kind(k) { + det_int = Some(match det_int { + Some(t) => builtin::wider_int_kind(&t, k), + None => k.clone(), + }); + } else { + return None; + } + } + if !has_lit { + return None; + } + let target = match (det_int, det_uint) { + (Some(_), Some(_)) => return None, // 真冲突:两侧都是确定类型 + (Some(t), None) | (None, Some(t)) => t, + (None, None) => return None, + }; + for (i, lit) in literals.iter().enumerate() { + if let Some(text) = lit { + if !literal_fits(text, &target) { + return None; + } + } + if kinds.get(i).is_some_and(|k| builtin::is_float_kind(k)) { + return None; + } + } + Some(target) +} + +fn promoted_numeric_kind( + kinds: &[String], + allow_mixed_classes: bool, + literals: &[Option<&str>], +) -> Option { let (mut has_int, mut has_uint, mut has_float) = (false, false, false); let mut has_float64 = false; for k in kinds { @@ -2286,7 +2409,12 @@ fn promoted_numeric_kind(kinds: &[String], allow_mixed_classes: bool) -> Option< } } if has_int && has_uint { - return None; + // 混合符号:字面量在场且能适配到确定类型一侧时不算冲突;浮点在场时保持原行为 + // (不在此特化,留给浮点提升/运行时判别,避免顺手改动浮点路径)。 + if has_float { + return None; + } + return adapt_int_uint(kinds, literals); } let mixed_class = has_float && (has_int || has_uint); if mixed_class { diff --git a/layout/tests/uint_literal_adapt.rs b/layout/tests/uint_literal_adapt.rs new file mode 100644 index 00000000..e28859db --- /dev/null +++ b/layout/tests/uint_literal_adapt.rs @@ -0,0 +1,181 @@ +//! 未定型整数字面量适配(spec 03-类型系统/01-种类与定宽类型 第 10 条): +//! `uint64(5) + 1` 的 `1` 按上下文适配到 uint64,不算混合确定性符号; +//! 两侧都是确定类型且符号冲突(`uint64_var + int64_var`)仍不特化(运行时 TypeError)。 + +use kvlanglayout::{compile, init_dirs, kvkind, Kv}; + +fn fresh_kv(tag: &str) -> Kv { + let dsn = format!( + "fs:///tmp/kvlanglayout_uintlit_{}_{}_{}", + tag, + std::process::id(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_nanos() + ); + let mut kv = Kv::conn(&dsn); + init_dirs(&mut kv).unwrap(); + kv +} + +fn slot_text(kv: &mut Kv, key: &str) -> String { + let v = kv.get_one(key); + let lt = kv.langtype_of(key); + let h = kvkind::head(&v); + let b = kvkind::body(&v, &h); + String::from_utf8_lossy(&b[5.min(b.len())..]).into_owned() +} + +/// 收集函数里所有 opcode 槽([i,0])的文本。 +fn ops(kv: &mut Kv, func: &str) -> Vec { + let prefix = format!("/lib/{func}/"); + let children = kv.list(&prefix, false, false); + let mut out = Vec::new(); + for c in children { + let t = c.trim_end_matches('/'); + if !t.starts_with('[') || !t.ends_with(",0]") { + continue; + } + out.push(slot_text(kv, &format!("{prefix}{c}"))); + } + out +} + +/// 写槽的声明类型(rwir 槽 body = `名字\0类型`)。 +fn write_kind(kv: &mut Kv, key: &str) -> String { + let text = slot_text(kv, key); + text.rsplit('\0').next().unwrap_or("").to_string() +} + +#[test] +fn cast_plus_literal_adapts_to_unsigned() { + for src in [ + "rwfunc f() -> () {\n uint64(5) + 1 -> r\n}\n", + "rwfunc f() -> () {\n 1 + uint64(5) -> r\n}\n", + "rwfunc f() -> () {\n u:uint64 = 5\n u + 1 -> r\n}\n", + ] { + let mut kv = fresh_kv("cast"); + compile(&mut kv, src).unwrap(); + let os = ops(&mut kv, "f"); + assert!( + os.iter().any(|o| o == "uint64·add"), + "应特化为 uint64·add:{os:?} src={src}" + ); + assert!( + !os.iter().any(|o| o == "int64·add"), + "不得特化为 int64·add:{os:?} src={src}" + ); + } +} + +#[test] +fn write_slot_kind_matches_specialized_op() { + let mut kv = fresh_kv("kind"); + compile( + &mut kv, + "rwfunc f() -> () {\n uint64(5) + 1 -> r\n}\n", + ) + .unwrap(); + // [2] 是 add;写槽 [2,1] 的 kind 必须是 uint64(不能算对却记 int64)。 + let k = write_kind(&mut kv, "/lib/f/[2,1]"); + assert_eq!(k, "uint64", "写槽 kind 应为 uint64,实际 {k}"); +} + +#[test] +fn uint8_literal_boundary_fits_and_overflows() { + let mut kv = fresh_kv("bound"); + compile( + &mut kv, + "rwfunc f() -> () {\n u:uint8 = 200\n u + 255 -> r\n}\n", + ) + .unwrap(); + let os = ops(&mut kv, "f"); + assert!(os.iter().any(|o| o == "uint8·add"), "255 应适配 uint8:{os:?}"); + + let mut kv = fresh_kv("overflow"); + compile( + &mut kv, + "rwfunc f() -> () {\n u:uint8 = 200\n u + 256 -> r\n}\n", + ) + .unwrap(); + let os = ops(&mut kv, "f"); + assert!( + !os.iter().any(|o| o == "uint8·add"), + "256 超出 uint8,不得适配:{os:?}" + ); + assert!(os.iter().any(|o| o == "+"), "应保持通用 +:{os:?}"); +} + +#[test] +fn float_still_wins_over_literal_adaptation() { + let mut kv = fresh_kv("float"); + compile( + &mut kv, + "rwfunc f() -> () {\n u:uint64 = 5\n u + 1.0 -> r\n}\n", + ) + .unwrap(); + let os = ops(&mut kv, "f"); + assert!( + os.iter().any(|o| o == "float64·add"), + "浮点在场应提升为 float64·add:{os:?}" + ); +} + +#[test] +fn two_unsigned_widths_still_widen() { + let mut kv = fresh_kv("widen"); + compile( + &mut kv, + "rwfunc f() -> () {\n a:uint64 = 5\n b:uint8 = 1\n a + b -> r\n}\n", + ) + .unwrap(); + let os = ops(&mut kv, "f"); + assert!( + os.iter().any(|o| o == "uint64·add"), + "两个无符号应按宽度加宽为 uint64·add:{os:?}" + ); +} + +#[test] +fn confirmed_mixed_signs_stay_untyped() { + let mut kv = fresh_kv("mixed"); + compile( + &mut kv, + "rwfunc f() -> () {\n a:uint64 = 5\n b:int64 = 1\n a + b -> r\n}\n", + ) + .unwrap(); + let os = ops(&mut kv, "f"); + assert!( + os.iter().any(|o| o == "+"), + "两侧都是确定类型且符号冲突,应保持通用 +:{os:?}" + ); + assert!( + !os.iter().any(|o| o.contains('·') && o.ends_with("add")), + "不得特化:{os:?}" + ); +} + +#[test] +fn comparison_with_literal_adapts_but_negative_does_not() { + let mut kv = fresh_kv("cmp"); + compile( + &mut kv, + "rwfunc f() -> () {\n u:uint64 = 5\n u == 1 -> r\n}\n", + ) + .unwrap(); + let os = ops(&mut kv, "f"); + assert!(os.iter().any(|o| o == "uint64·eq"), "u == 1 应适配:{os:?}"); + + let mut kv = fresh_kv("cmpneg"); + compile( + &mut kv, + "rwfunc f() -> () {\n u:uint64 = 5\n u == -1 -> r\n}\n", + ) + .unwrap(); + let os = ops(&mut kv, "f"); + assert!( + !os.iter().any(|o| o == "uint64·eq"), + "-1 不能适配 uint64,应保持通用比较:{os:?}" + ); +}