diff --git a/crates/hir-ty/src/chalk_ext.rs b/crates/hir-ty/src/chalk_ext.rs index 3fb3de5ba8d30..a8071591adac9 100644 --- a/crates/hir-ty/src/chalk_ext.rs +++ b/crates/hir-ty/src/chalk_ext.rs @@ -22,6 +22,7 @@ use crate::{ pub trait TyExt { fn is_unit(&self) -> bool; fn is_integral(&self) -> bool; + fn is_scalar(&self) -> bool; fn is_floating_point(&self) -> bool; fn is_never(&self) -> bool; fn is_unknown(&self) -> bool; @@ -68,6 +69,10 @@ impl TyExt for Ty { ) } + fn is_scalar(&self) -> bool { + matches!(self.kind(Interner), TyKind::Scalar(_)) + } + fn is_floating_point(&self) -> bool { matches!( self.kind(Interner), diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index 6a7d045648635..06fff08b7d35b 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -179,6 +179,7 @@ fn casts() { "#, 4, ); + check_number(r#"const GOAL: i32 = -12i8 as i32"#, -12); } #[test] @@ -1034,16 +1035,18 @@ fn pattern_matching_literal() { ); check_number( r#" - const fn f(x: &str) -> u8 { + const fn f(x: &str) -> i32 { match x { - "foo" => 1, - "bar" => 10, - _ => 100, + "f" => 1, + "foo" => 10, + "" => 100, + "bar" => 1000, + _ => 10000, } } - const GOAL: u8 = f("foo") + f("bar"); + const GOAL: i32 = f("f") + f("foo") * 2 + f("") * 3 + f("bar") * 4; "#, - 11, + 4321, ); } diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 6d7701c9e8d4b..6778808d529db 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -17,6 +17,7 @@ use hir_expand::InFile; use intern::Interned; use la_arena::ArenaMap; use rustc_hash::{FxHashMap, FxHashSet}; +use stdx::never; use syntax::{SyntaxNodePtr, TextRange}; use triomphe::Arc; @@ -896,7 +897,7 @@ impl Evaluator<'_> { Owned(c) } } - Rvalue::CheckedBinaryOp(op, lhs, rhs) => { + Rvalue::CheckedBinaryOp(op, lhs, rhs) => 'binary_op: { let lc = self.eval_operand(lhs, locals)?; let rc = self.eval_operand(rhs, locals)?; let mut lc = lc.get(&self)?; @@ -905,10 +906,17 @@ impl Evaluator<'_> { while let TyKind::Ref(_, _, z) = ty.kind(Interner) { ty = z.clone(); let size = if ty.kind(Interner) == &TyKind::Str { - let ns = from_bytes!(usize, &lc[self.ptr_size()..self.ptr_size() * 2]); + if *op != BinOp::Eq { + never!("Only eq is builtin for `str`"); + } + let ls = from_bytes!(usize, &lc[self.ptr_size()..self.ptr_size() * 2]); + let rs = from_bytes!(usize, &rc[self.ptr_size()..self.ptr_size() * 2]); + if ls != rs { + break 'binary_op Owned(vec![0]); + } lc = &lc[..self.ptr_size()]; rc = &rc[..self.ptr_size()]; - ns + ls } else { self.size_of_sized(&ty, locals, "operand of binary op")? }; @@ -1200,8 +1208,15 @@ impl Evaluator<'_> { CastKind::IntToInt | CastKind::PointerExposeAddress | CastKind::PointerFromExposedAddress => { - // FIXME: handle signed cast - let current = pad16(self.eval_operand(operand, locals)?.get(&self)?, false); + let current_ty = self.operand_ty(operand, locals)?; + let is_signed = match current_ty.kind(Interner) { + TyKind::Scalar(s) => match s { + chalk_ir::Scalar::Int(_) => true, + _ => false, + }, + _ => false, + }; + let current = pad16(self.eval_operand(operand, locals)?.get(&self)?, is_signed); let dest_size = self.size_of_sized(target_ty, locals, "destination of int to int cast")?; Owned(current[0..dest_size].to_vec()) diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs index 77ee7b6b6e2a8..3b9ef03c369f4 100644 --- a/crates/hir-ty/src/mir/eval/shim.rs +++ b/crates/hir-ty/src/mir/eval/shim.rs @@ -238,6 +238,22 @@ impl Evaluator<'_> { _span: MirSpan, ) -> Result<()> { match as_str { + "memcmp" => { + let [ptr1, ptr2, size] = args else { + return Err(MirEvalError::TypeError("memcmp args are not provided")); + }; + let addr1 = Address::from_bytes(ptr1.get(self)?)?; + let addr2 = Address::from_bytes(ptr2.get(self)?)?; + let size = from_bytes!(usize, size.get(self)?); + let slice1 = self.read_memory(addr1, size)?; + let slice2 = self.read_memory(addr2, size)?; + let r: i128 = match slice1.cmp(slice2) { + cmp::Ordering::Less => -1, + cmp::Ordering::Equal => 0, + cmp::Ordering::Greater => 1, + }; + destination.write_from_bytes(self, &r.to_le_bytes()[..destination.size]) + } "write" => { let [fd, ptr, len] = args else { return Err(MirEvalError::TypeError("libc::write args are not provided")); diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs index 453c93de8e765..8c097539eb5e6 100644 --- a/crates/hir-ty/src/mir/eval/tests.rs +++ b/crates/hir-ty/src/mir/eval/tests.rs @@ -228,6 +228,39 @@ fn main() { ); } +#[test] +fn memcmp() { + check_pass( + r#" +//- minicore: slice, coerce_unsized, index + +fn should_not_reach() -> bool { + _ // FIXME: replace this function with panic when that works +} + +extern "C" { + fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32; +} + +fn my_cmp(x: &[u8], y: &[u8]) -> i32 { + memcmp(x as *const u8, y as *const u8, x.len()) +} + +fn main() { + if my_cmp(&[1, 2, 3], &[1, 2, 3]) != 0 { + should_not_reach(); + } + if my_cmp(&[1, 20, 3], &[1, 2, 3]) <= 0 { + should_not_reach(); + } + if my_cmp(&[1, 2, 3], &[1, 20, 3]) >= 0 { + should_not_reach(); + } +} + "#, + ); +} + #[test] fn unix_write_stdout() { check_pass_and_stdio( diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 5ed95133356cd..ebd419983551c 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -829,7 +829,7 @@ impl<'ctx> MirLowerCtx<'ctx> { op, BinaryOp::ArithOp(ArithOp::Shl | ArithOp::Shr) | BinaryOp::Assignment { op: Some(ArithOp::Shl | ArithOp::Shr) } ); - lhs_ty.as_builtin().is_some() && rhs_ty.as_builtin().is_some() && (lhs_ty == rhs_ty || builtin_inequal_impls) + lhs_ty.is_scalar() && rhs_ty.is_scalar() && (lhs_ty == rhs_ty || builtin_inequal_impls) }; if !is_builtin { if let Some((func_id, generic_args)) = self.infer.method_resolution(expr_id) {