148 lines
4.7 KiB
Rust
148 lines
4.7 KiB
Rust
|
//@ run-pass
|
||
|
//! Test information regarding binary operations.
|
||
|
|
||
|
//@ ignore-stage1
|
||
|
//@ ignore-cross-compile
|
||
|
//@ ignore-remote
|
||
|
//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837
|
||
|
|
||
|
#![feature(rustc_private)]
|
||
|
|
||
|
extern crate rustc_hir;
|
||
|
#[macro_use]
|
||
|
extern crate rustc_smir;
|
||
|
extern crate rustc_driver;
|
||
|
extern crate rustc_interface;
|
||
|
extern crate stable_mir;
|
||
|
|
||
|
use rustc_smir::rustc_internal;
|
||
|
use stable_mir::mir::mono::Instance;
|
||
|
use stable_mir::mir::visit::{Location, MirVisitor};
|
||
|
use stable_mir::mir::{LocalDecl, Rvalue, Statement, StatementKind, Terminator, TerminatorKind};
|
||
|
use stable_mir::ty::{RigidTy, TyKind};
|
||
|
use std::collections::HashSet;
|
||
|
use std::convert::TryFrom;
|
||
|
use std::io::Write;
|
||
|
use std::ops::ControlFlow;
|
||
|
|
||
|
/// This function tests that we can correctly get type information from binary operations.
|
||
|
fn test_binops() -> ControlFlow<()> {
|
||
|
// Find items in the local crate.
|
||
|
let items = stable_mir::all_local_items();
|
||
|
let mut instances =
|
||
|
items.into_iter().map(|item| Instance::try_from(item).unwrap()).collect::<Vec<_>>();
|
||
|
while let Some(instance) = instances.pop() {
|
||
|
// The test below shouldn't have recursion in it.
|
||
|
let Some(body) = instance.body() else {
|
||
|
continue;
|
||
|
};
|
||
|
let mut visitor = Visitor { locals: body.locals(), calls: Default::default() };
|
||
|
visitor.visit_body(&body);
|
||
|
instances.extend(visitor.calls.into_iter());
|
||
|
}
|
||
|
ControlFlow::Continue(())
|
||
|
}
|
||
|
|
||
|
struct Visitor<'a> {
|
||
|
locals: &'a [LocalDecl],
|
||
|
calls: HashSet<Instance>,
|
||
|
}
|
||
|
|
||
|
impl<'a> MirVisitor for Visitor<'a> {
|
||
|
fn visit_statement(&mut self, stmt: &Statement, _loc: Location) {
|
||
|
match &stmt.kind {
|
||
|
StatementKind::Assign(place, Rvalue::BinaryOp(op, rhs, lhs)) => {
|
||
|
let ret_ty = place.ty(self.locals).unwrap();
|
||
|
let op_ty = op.ty(rhs.ty(self.locals).unwrap(), lhs.ty(self.locals).unwrap());
|
||
|
assert_eq!(ret_ty, op_ty, "Operation type should match the assigned place type");
|
||
|
}
|
||
|
_ => {}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn visit_terminator(&mut self, term: &Terminator, _loc: Location) {
|
||
|
match &term.kind {
|
||
|
TerminatorKind::Call { func, .. } => {
|
||
|
let TyKind::RigidTy(RigidTy::FnDef(def, args)) =
|
||
|
func.ty(self.locals).unwrap().kind()
|
||
|
else {
|
||
|
return;
|
||
|
};
|
||
|
self.calls.insert(Instance::resolve(def, &args).unwrap());
|
||
|
}
|
||
|
_ => {}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// This test will generate and analyze a dummy crate using the stable mir.
|
||
|
/// For that, it will first write the dummy crate into a file.
|
||
|
/// Then it will create a `StableMir` using custom arguments and then
|
||
|
/// it will run the compiler.
|
||
|
fn main() {
|
||
|
let path = "binop_input.rs";
|
||
|
generate_input(&path).unwrap();
|
||
|
let args = vec!["rustc".to_string(), "--crate-type=lib".to_string(), path.to_string()];
|
||
|
run!(args, test_binops).unwrap();
|
||
|
}
|
||
|
|
||
|
fn generate_input(path: &str) -> std::io::Result<()> {
|
||
|
let mut file = std::fs::File::create(path)?;
|
||
|
write!(
|
||
|
file,
|
||
|
r#"
|
||
|
macro_rules! binop_int {{
|
||
|
($fn:ident, $typ:ty) => {{
|
||
|
pub fn $fn(lhs: $typ, rhs: $typ) {{
|
||
|
let eq = lhs == rhs;
|
||
|
let lt = lhs < rhs;
|
||
|
let le = lhs <= rhs;
|
||
|
|
||
|
let sum = lhs + rhs;
|
||
|
let mult = lhs * sum;
|
||
|
let shift = mult << 2;
|
||
|
let bit_or = shift | rhs;
|
||
|
let cmp = lhs.cmp(&bit_or);
|
||
|
|
||
|
// Try to avoid the results above being pruned
|
||
|
std::hint::black_box(((eq, lt, le), cmp));
|
||
|
}}
|
||
|
}}
|
||
|
}}
|
||
|
|
||
|
binop_int!(binop_u8, u8);
|
||
|
binop_int!(binop_i64, i64);
|
||
|
|
||
|
pub fn binop_bool(lhs: bool, rhs: bool) {{
|
||
|
let eq = lhs == rhs;
|
||
|
let or = lhs | eq;
|
||
|
let lt = lhs < or;
|
||
|
let cmp = lhs.cmp(&rhs);
|
||
|
|
||
|
// Try to avoid the results above being pruned
|
||
|
std::hint::black_box((lt, cmp));
|
||
|
}}
|
||
|
|
||
|
pub fn binop_char(lhs: char, rhs: char) {{
|
||
|
let eq = lhs == rhs;
|
||
|
let lt = lhs < rhs;
|
||
|
let cmp = lhs.cmp(&rhs);
|
||
|
|
||
|
// Try to avoid the results above being pruned
|
||
|
std::hint::black_box(([eq, lt], cmp));
|
||
|
}}
|
||
|
|
||
|
pub fn binop_ptr(lhs: *const char, rhs: *const char) {{
|
||
|
let eq = lhs == rhs;
|
||
|
let lt = lhs < rhs;
|
||
|
let cmp = lhs.cmp(&rhs);
|
||
|
let off = unsafe {{ lhs.offset(2) }};
|
||
|
|
||
|
// Try to avoid the results above being pruned
|
||
|
std::hint::black_box(([eq, lt], cmp, off));
|
||
|
}}
|
||
|
"#
|
||
|
)?;
|
||
|
Ok(())
|
||
|
}
|