MIR episode 4

This commit is contained in:
hkalbasi 2023-04-28 20:44:30 +03:30
parent 001607fdb4
commit 6312fbf521
47 changed files with 2557 additions and 805 deletions

View File

@ -34,7 +34,7 @@ impl fmt::Debug for Change {
} }
impl Change { impl Change {
pub fn new() -> Change { pub fn new() -> Self {
Change::default() Change::default()
} }

View File

@ -17,7 +17,7 @@ use rustc_hash::FxHashMap;
use smallvec::SmallVec; use smallvec::SmallVec;
use syntax::{ use syntax::{
ast::{ ast::{
self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasLoopBody, HasName, self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasAttrs, HasLoopBody, HasName,
SlicePatComponents, SlicePatComponents,
}, },
AstNode, AstPtr, SyntaxNodePtr, AstNode, AstPtr, SyntaxNodePtr,
@ -302,16 +302,29 @@ impl ExprCollector<'_> {
self.alloc_expr(Expr::For { iterable, pat, body, label }, syntax_ptr) self.alloc_expr(Expr::For { iterable, pat, body, label }, syntax_ptr)
} }
ast::Expr::CallExpr(e) => { ast::Expr::CallExpr(e) => {
let callee = self.collect_expr_opt(e.expr()); let is_rustc_box = {
let args = if let Some(arg_list) = e.arg_list() { let attrs = e.attrs();
arg_list.args().filter_map(|e| self.maybe_collect_expr(e)).collect() attrs.filter_map(|x| x.as_simple_atom()).any(|x| x == "rustc_box")
} else {
Box::default()
}; };
self.alloc_expr( if is_rustc_box {
Expr::Call { callee, args, is_assignee_expr: self.is_lowering_assignee_expr }, let expr = self.collect_expr_opt(e.arg_list().and_then(|x| x.args().next()));
syntax_ptr, self.alloc_expr(Expr::Box { expr }, syntax_ptr)
) } else {
let callee = self.collect_expr_opt(e.expr());
let args = if let Some(arg_list) = e.arg_list() {
arg_list.args().filter_map(|e| self.maybe_collect_expr(e)).collect()
} else {
Box::default()
};
self.alloc_expr(
Expr::Call {
callee,
args,
is_assignee_expr: self.is_lowering_assignee_expr,
},
syntax_ptr,
)
}
} }
ast::Expr::MethodCallExpr(e) => { ast::Expr::MethodCallExpr(e) => {
let receiver = self.collect_expr_opt(e.receiver()); let receiver = self.collect_expr_opt(e.receiver());

View File

@ -232,6 +232,25 @@ impl TyBuilder<()> {
TyBuilder::new((), params, parent_subst) TyBuilder::new((), params, parent_subst)
} }
pub fn subst_for_closure(
db: &dyn HirDatabase,
parent: DefWithBodyId,
sig_ty: Ty,
) -> Substitution {
let sig_ty = sig_ty.cast(Interner);
let self_subst = iter::once(&sig_ty);
let Some(parent) = parent.as_generic_def_id() else {
return Substitution::from_iter(Interner, self_subst);
};
Substitution::from_iter(
Interner,
self_subst
.chain(generics(db.upcast(), parent).placeholder_subst(db).iter(Interner))
.cloned()
.collect::<Vec<_>>(),
)
}
pub fn build(self) -> Substitution { pub fn build(self) -> Substitution {
let ((), subst) = self.build_internal(); let ((), subst) = self.build_internal();
subst subst

View File

@ -24,7 +24,7 @@ use crate::{
method_resolution::{TraitImpls, TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS}, method_resolution::{TraitImpls, TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS},
to_assoc_type_id, to_chalk_trait_id, to_assoc_type_id, to_chalk_trait_id,
traits::ChalkContext, traits::ChalkContext,
utils::generics, utils::{generics, ClosureSubst},
wrap_empty_binders, AliasEq, AliasTy, BoundVar, CallableDefId, DebruijnIndex, FnDefId, wrap_empty_binders, AliasEq, AliasTy, BoundVar, CallableDefId, DebruijnIndex, FnDefId,
Interner, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Substitution, TraitRef, Interner, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Substitution, TraitRef,
TraitRefExt, Ty, TyBuilder, TyExt, TyKind, WhereClause, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, WhereClause,
@ -337,7 +337,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
_closure_id: chalk_ir::ClosureId<Interner>, _closure_id: chalk_ir::ClosureId<Interner>,
substs: &chalk_ir::Substitution<Interner>, substs: &chalk_ir::Substitution<Interner>,
) -> chalk_ir::Binders<rust_ir::FnDefInputsAndOutputDatum<Interner>> { ) -> chalk_ir::Binders<rust_ir::FnDefInputsAndOutputDatum<Interner>> {
let sig_ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone(); let sig_ty = ClosureSubst(substs).sig_ty();
let sig = &sig_ty.callable_sig(self.db).expect("first closure param should be fn ptr"); let sig = &sig_ty.callable_sig(self.db).expect("first closure param should be fn ptr");
let io = rust_ir::FnDefInputsAndOutputDatum { let io = rust_ir::FnDefInputsAndOutputDatum {
argument_types: sig.params().to_vec(), argument_types: sig.params().to_vec(),

View File

@ -1,20 +1,22 @@
//! Various extensions traits for Chalk types. //! Various extensions traits for Chalk types.
use chalk_ir::{FloatTy, IntTy, Mutability, Scalar, TyVariableKind, UintTy}; use chalk_ir::{cast::Cast, FloatTy, IntTy, Mutability, Scalar, TyVariableKind, UintTy};
use hir_def::{ use hir_def::{
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinType, BuiltinUint}, builtin_type::{BuiltinFloat, BuiltinInt, BuiltinType, BuiltinUint},
generics::TypeOrConstParamData, generics::TypeOrConstParamData,
lang_item::LangItem, lang_item::LangItem,
type_ref::Rawness, type_ref::Rawness,
FunctionId, GenericDefId, HasModule, ItemContainerId, Lookup, TraitId, DefWithBodyId, FunctionId, GenericDefId, HasModule, ItemContainerId, Lookup, TraitId,
}; };
use crate::{ use crate::{
db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, db::HirDatabase,
from_placeholder_idx, to_chalk_trait_id, utils::generics, AdtId, AliasEq, AliasTy, Binders, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx,
CallableDefId, CallableSig, ClosureId, DynTy, FnPointer, ImplTraitId, Interner, Lifetime, to_chalk_trait_id,
ProjectionTy, QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags, utils::{generics, ClosureSubst},
WhereClause, AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Canonical, CanonicalVarKinds,
ClosureId, DynTy, FnPointer, ImplTraitId, InEnvironment, Interner, Lifetime, ProjectionTy,
QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags, WhereClause,
}; };
pub trait TyExt { pub trait TyExt {
@ -46,6 +48,7 @@ pub trait TyExt {
fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>>; fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>>;
fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId>; fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId>;
fn is_copy(self, db: &dyn HirDatabase, owner: DefWithBodyId) -> bool;
/// FIXME: Get rid of this, it's not a good abstraction /// FIXME: Get rid of this, it's not a good abstraction
fn equals_ctor(&self, other: &Ty) -> bool; fn equals_ctor(&self, other: &Ty) -> bool;
@ -185,10 +188,7 @@ impl TyExt for Ty {
let sig = db.callable_item_signature(callable_def); let sig = db.callable_item_signature(callable_def);
Some(sig.substitute(Interner, parameters)) Some(sig.substitute(Interner, parameters))
} }
TyKind::Closure(.., substs) => { TyKind::Closure(.., substs) => ClosureSubst(substs).sig_ty().callable_sig(db),
let sig_param = substs.at(Interner, 0).assert_ty_ref(Interner);
sig_param.callable_sig(db)
}
_ => None, _ => None,
} }
} }
@ -327,6 +327,20 @@ impl TyExt for Ty {
} }
} }
fn is_copy(self, db: &dyn HirDatabase, owner: DefWithBodyId) -> bool {
let crate_id = owner.module(db.upcast()).krate();
let Some(copy_trait) = db.lang_item(crate_id, LangItem::Copy).and_then(|x| x.as_trait()) else {
return false;
};
let trait_ref = TyBuilder::trait_ref(db, copy_trait).push(self).build();
let env = db.trait_environment_for_body(owner);
let goal = Canonical {
value: InEnvironment::new(&env.env, trait_ref.cast(Interner)),
binders: CanonicalVarKinds::empty(Interner),
};
db.trait_solve(crate_id, None, goal).is_some()
}
fn equals_ctor(&self, other: &Ty) -> bool { fn equals_ctor(&self, other: &Ty) -> bool {
match (self.kind(Interner), other.kind(Interner)) { match (self.kind(Interner), other.kind(Interner)) {
(TyKind::Adt(adt, ..), TyKind::Adt(adt2, ..)) => adt == adt2, (TyKind::Adt(adt, ..), TyKind::Adt(adt2, ..)) => adt == adt2,

View File

@ -7,7 +7,7 @@ use hir_def::{
path::Path, path::Path,
resolver::{Resolver, ValueNs}, resolver::{Resolver, ValueNs},
type_ref::ConstRef, type_ref::ConstRef,
ConstId, EnumVariantId, DefWithBodyId, EnumVariantId,
}; };
use la_arena::{Idx, RawIdx}; use la_arena::{Idx, RawIdx};
use stdx::never; use stdx::never;
@ -57,7 +57,7 @@ pub enum ConstEvalError {
impl From<MirLowerError> for ConstEvalError { impl From<MirLowerError> for ConstEvalError {
fn from(value: MirLowerError) -> Self { fn from(value: MirLowerError) -> Self {
match value { match value {
MirLowerError::ConstEvalError(e) => *e, MirLowerError::ConstEvalError(_, e) => *e,
_ => ConstEvalError::MirLowerError(value), _ => ConstEvalError::MirLowerError(value),
} }
} }
@ -168,7 +168,7 @@ pub fn try_const_usize(c: &Const) -> Option<u128> {
pub(crate) fn const_eval_recover( pub(crate) fn const_eval_recover(
_: &dyn HirDatabase, _: &dyn HirDatabase,
_: &[String], _: &[String],
_: &ConstId, _: &DefWithBodyId,
_: &Substitution, _: &Substitution,
) -> Result<Const, ConstEvalError> { ) -> Result<Const, ConstEvalError> {
Err(ConstEvalError::MirLowerError(MirLowerError::Loop)) Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
@ -184,10 +184,9 @@ pub(crate) fn const_eval_discriminant_recover(
pub(crate) fn const_eval_query( pub(crate) fn const_eval_query(
db: &dyn HirDatabase, db: &dyn HirDatabase,
const_id: ConstId, def: DefWithBodyId,
subst: Substitution, subst: Substitution,
) -> Result<Const, ConstEvalError> { ) -> Result<Const, ConstEvalError> {
let def = const_id.into();
let body = db.mir_body(def)?; let body = db.mir_body(def)?;
let c = interpret_mir(db, &body, subst, false)?; let c = interpret_mir(db, &body, subst, false)?;
Ok(c) Ok(c)

View File

@ -1,4 +1,4 @@
use base_db::fixture::WithFixture; use base_db::{fixture::WithFixture, FileId};
use chalk_ir::Substitution; use chalk_ir::Substitution;
use hir_def::db::DefDatabase; use hir_def::db::DefDatabase;
@ -16,7 +16,7 @@ mod intrinsics;
fn simplify(e: ConstEvalError) -> ConstEvalError { fn simplify(e: ConstEvalError) -> ConstEvalError {
match e { match e {
ConstEvalError::MirEvalError(MirEvalError::InFunction(_, e)) => { ConstEvalError::MirEvalError(MirEvalError::InFunction(_, e, _, _)) => {
simplify(ConstEvalError::MirEvalError(*e)) simplify(ConstEvalError::MirEvalError(*e))
} }
_ => e, _ => e,
@ -24,13 +24,30 @@ fn simplify(e: ConstEvalError) -> ConstEvalError {
} }
#[track_caller] #[track_caller]
fn check_fail(ra_fixture: &str, error: ConstEvalError) { fn check_fail(ra_fixture: &str, error: impl FnOnce(ConstEvalError) -> bool) {
assert_eq!(eval_goal(ra_fixture).map_err(simplify), Err(error)); let (db, file_id) = TestDB::with_single_file(ra_fixture);
match eval_goal(&db, file_id).map_err(simplify) {
Ok(_) => panic!("Expected fail, but it succeeded"),
Err(e) => assert!(error(e)),
}
} }
#[track_caller] #[track_caller]
fn check_number(ra_fixture: &str, answer: i128) { fn check_number(ra_fixture: &str, answer: i128) {
let r = eval_goal(ra_fixture).unwrap(); let (db, file_id) = TestDB::with_single_file(ra_fixture);
let r = match eval_goal(&db, file_id) {
Ok(t) => t,
Err(e) => {
let mut err = String::new();
let span_formatter = |file, range| format!("{:?} {:?}", file, range);
match e {
ConstEvalError::MirLowerError(e) => e.pretty_print(&mut err, &db, span_formatter),
ConstEvalError::MirEvalError(e) => e.pretty_print(&mut err, &db, span_formatter),
}
.unwrap();
panic!("Error in evaluating goal: {}", err);
}
};
match &r.data(Interner).value { match &r.data(Interner).value {
chalk_ir::ConstValue::Concrete(c) => match &c.interned { chalk_ir::ConstValue::Concrete(c) => match &c.interned {
ConstScalar::Bytes(b, _) => { ConstScalar::Bytes(b, _) => {
@ -47,10 +64,9 @@ fn check_number(ra_fixture: &str, answer: i128) {
} }
} }
fn eval_goal(ra_fixture: &str) -> Result<Const, ConstEvalError> { fn eval_goal(db: &TestDB, file_id: FileId) -> Result<Const, ConstEvalError> {
let (db, file_id) = TestDB::with_single_file(ra_fixture);
let module_id = db.module_for_file(file_id); let module_id = db.module_for_file(file_id);
let def_map = module_id.def_map(&db); let def_map = module_id.def_map(db);
let scope = &def_map[module_id.local_id].scope; let scope = &def_map[module_id.local_id].scope;
let const_id = scope let const_id = scope
.declarations() .declarations()
@ -65,7 +81,7 @@ fn eval_goal(ra_fixture: &str) -> Result<Const, ConstEvalError> {
_ => None, _ => None,
}) })
.unwrap(); .unwrap();
db.const_eval(const_id, Substitution::empty(Interner)) db.const_eval(const_id.into(), Substitution::empty(Interner))
} }
#[test] #[test]
@ -303,6 +319,81 @@ fn overloaded_index() {
); );
} }
#[test]
fn overloaded_binop() {
check_number(
r#"
//- minicore: add
enum Color {
Red,
Green,
Yellow,
}
use Color::*;
impl core::ops::Add for Color {
type Output = Color;
fn add(self, rhs: Color) -> Self::Output {
Yellow
}
}
impl core::ops::AddAssign for Color {
fn add_assign(&mut self, rhs: Color) {
*self = Red;
}
}
const GOAL: bool = {
let x = Red + Green;
let mut y = Green;
y += x;
x == Yellow && y == Red && Red + Green == Yellow && Red + Red == Yellow && Yellow + Green == Yellow
};
"#,
1,
);
check_number(
r#"
//- minicore: add
impl core::ops::Add for usize {
type Output = usize;
fn add(self, rhs: usize) -> Self::Output {
self + rhs
}
}
impl core::ops::AddAssign for usize {
fn add_assign(&mut self, rhs: usize) {
*self += rhs;
}
}
#[lang = "shl"]
pub trait Shl<Rhs = Self> {
type Output;
fn shl(self, rhs: Rhs) -> Self::Output;
}
impl Shl<u8> for usize {
type Output = usize;
fn shl(self, rhs: u8) -> Self::Output {
self << rhs
}
}
const GOAL: usize = {
let mut x = 10;
x += 20;
2 + 2 + (x << 1u8)
};"#,
64,
);
}
#[test] #[test]
fn function_call() { fn function_call() {
check_number( check_number(
@ -426,6 +517,16 @@ fn generic_fn() {
"#, "#,
12, 12,
); );
check_number(
r#"
const fn y<T>(b: T) -> (T, ) {
let alloc = b;
(alloc, )
}
const GOAL: u8 = y(2).0;
"#,
2,
);
check_number( check_number(
r#" r#"
//- minicore: coerce_unsized, index, slice //- minicore: coerce_unsized, index, slice
@ -590,6 +691,30 @@ fn loops() {
"#, "#,
8, 8,
); );
check_number(
r#"
//- minicore: add
const GOAL: u8 = {
let mut x = 0;
'a: loop {
'b: loop {
'c: while x < 20 {
'd: while x < 5 {
'e: loop {
x += 1;
continue 'c;
};
};
x += 1;
};
break 'a;
};
}
x
};
"#,
20,
);
} }
#[test] #[test]
@ -790,10 +915,12 @@ fn path_pattern_matching() {
use Season::*; use Season::*;
const MY_SEASON: Season = Summer;
const fn f(x: Season) -> i32 { const fn f(x: Season) -> i32 {
match x { match x {
Spring => 1, Spring => 1,
Summer => 2, MY_SEASON => 2,
Fall => 3, Fall => 3,
Winter => 4, Winter => 4,
} }
@ -944,19 +1071,10 @@ fn function_param_patterns() {
fn match_guards() { fn match_guards() {
check_number( check_number(
r#" r#"
//- minicore: option, eq //- minicore: option
impl<T: PartialEq> PartialEq for Option<T> {
fn eq(&self, other: &Rhs) -> bool {
match (self, other) {
(Some(x), Some(y)) => x == y,
(None, None) => true,
_ => false,
}
}
}
fn f(x: Option<i32>) -> i32 { fn f(x: Option<i32>) -> i32 {
match x { match x {
y if y == Some(42) => 42000, y if let Some(42) = y => 42000,
Some(y) => y, Some(y) => y,
None => 10 None => 10
} }
@ -967,6 +1085,59 @@ fn match_guards() {
); );
} }
#[test]
fn result_layout_niche_optimization() {
check_number(
r#"
//- minicore: option, result
const GOAL: i32 = match Some(2).ok_or(Some(2)) {
Ok(x) => x,
Err(_) => 1000,
};
"#,
2,
);
check_number(
r#"
//- minicore: result
pub enum AlignmentEnum64 {
_Align1Shl0 = 1 << 0,
_Align1Shl1 = 1 << 1,
_Align1Shl2 = 1 << 2,
_Align1Shl3 = 1 << 3,
_Align1Shl4 = 1 << 4,
_Align1Shl5 = 1 << 5,
}
const GOAL: Result<AlignmentEnum64, ()> = {
let align = Err(());
align
};
"#,
0, // It is 0 since result is niche encoded and 1 is valid for `AlignmentEnum64`
);
check_number(
r#"
//- minicore: result
pub enum AlignmentEnum64 {
_Align1Shl0 = 1 << 0,
_Align1Shl1 = 1 << 1,
_Align1Shl2 = 1 << 2,
_Align1Shl3 = 1 << 3,
_Align1Shl4 = 1 << 4,
_Align1Shl5 = 1 << 5,
}
const GOAL: i32 = {
let align = Ok::<_, ()>(AlignmentEnum64::_Align1Shl0);
match align {
Ok(_) => 2,
Err(_) => 1,
}
};
"#,
2,
);
}
#[test] #[test]
fn options() { fn options() {
check_number( check_number(
@ -1147,6 +1318,16 @@ fn closures() {
check_number( check_number(
r#" r#"
//- minicore: fn, copy //- minicore: fn, copy
const GOAL: i32 = {
let c: fn(i32) -> i32 = |x| 2 * x;
c(2) + c(10)
};
"#,
24,
);
check_number(
r#"
//- minicore: fn, copy
struct X(i32); struct X(i32);
impl X { impl X {
fn mult(&mut self, n: i32) { fn mult(&mut self, n: i32) {
@ -1180,6 +1361,36 @@ fn closures() {
); );
} }
#[test]
fn closure_and_impl_fn() {
check_number(
r#"
//- minicore: fn, copy
fn closure_wrapper<F: FnOnce() -> i32>(c: F) -> impl FnOnce() -> F {
|| c
}
const GOAL: i32 = {
let y = 5;
let c = closure_wrapper(|| y);
c()()
};
"#,
5,
);
check_number(
r#"
//- minicore: fn, copy
fn f<T, F: Fn() -> T>(t: F) -> impl Fn() -> T {
move || t()
}
const GOAL: i32 = f(|| 2)();
"#,
2,
);
}
#[test] #[test]
fn or_pattern() { fn or_pattern() {
check_number( check_number(
@ -1218,6 +1429,23 @@ fn or_pattern() {
); );
} }
#[test]
fn function_pointer_in_constants() {
check_number(
r#"
struct Foo {
f: fn(u8) -> u8,
}
const FOO: Foo = Foo { f: add2 };
fn add2(x: u8) -> u8 {
x + 2
}
const GOAL: u8 = (FOO.f)(3);
"#,
5,
);
}
#[test] #[test]
fn function_pointer() { fn function_pointer() {
check_number( check_number(
@ -1432,6 +1660,51 @@ fn dyn_trait() {
); );
} }
#[test]
fn boxes() {
check_number(
r#"
//- minicore: coerce_unsized, deref_mut, slice
use core::ops::{Deref, DerefMut};
use core::{marker::Unsize, ops::CoerceUnsized};
#[lang = "owned_box"]
pub struct Box<T: ?Sized> {
inner: *mut T,
}
impl<T> Box<T> {
fn new(t: T) -> Self {
#[rustc_box]
Box::new(t)
}
}
impl<T: ?Sized> Deref for Box<T> {
type Target = T;
fn deref(&self) -> &T {
&**self
}
}
impl<T: ?Sized> DerefMut for Box<T> {
fn deref_mut(&mut self) -> &mut T {
&mut **self
}
}
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Box<U>> for Box<T> {}
const GOAL: usize = {
let x = Box::new(5);
let y: Box<[i32]> = Box::new([1, 2, 3]);
*x + y.len()
};
"#,
8,
);
}
#[test] #[test]
fn array_and_index() { fn array_and_index() {
check_number( check_number(
@ -1504,6 +1777,37 @@ fn consts() {
); );
} }
#[test]
fn statics() {
check_number(
r#"
//- minicore: cell
use core::cell::Cell;
fn f() -> i32 {
static S: Cell<i32> = Cell::new(10);
S.set(S.get() + 1);
S.get()
}
const GOAL: i32 = f() + f() + f();
"#,
36,
);
}
#[test]
fn extern_weak_statics() {
check_number(
r#"
extern "C" {
#[linkage = "extern_weak"]
static __dso_handle: *mut u8;
}
const GOAL: usize = __dso_handle as usize;
"#,
0,
);
}
#[test] #[test]
fn enums() { fn enums() {
check_number( check_number(
@ -1531,13 +1835,13 @@ fn enums() {
"#, "#,
0, 0,
); );
let r = eval_goal( let (db, file_id) = TestDB::with_single_file(
r#" r#"
enum E { A = 1, B } enum E { A = 1, B }
const GOAL: E = E::A; const GOAL: E = E::A;
"#, "#,
) );
.unwrap(); let r = eval_goal(&db, file_id).unwrap();
assert_eq!(try_const_usize(&r), Some(1)); assert_eq!(try_const_usize(&r), Some(1));
} }
@ -1550,7 +1854,7 @@ fn const_loop() {
const F2: i32 = 2 * F1; const F2: i32 = 2 * F1;
const GOAL: i32 = F3; const GOAL: i32 = F3;
"#, "#,
ConstEvalError::MirLowerError(MirLowerError::Loop), |e| e == ConstEvalError::MirLowerError(MirLowerError::Loop),
); );
} }
@ -1609,8 +1913,7 @@ fn const_generic_subst_assoc_const_impl() {
#[test] #[test]
fn const_trait_assoc() { fn const_trait_assoc() {
// FIXME: this should evaluate to 0 check_number(
check_fail(
r#" r#"
struct U0; struct U0;
trait ToConst { trait ToConst {
@ -1619,9 +1922,35 @@ fn const_trait_assoc() {
impl ToConst for U0 { impl ToConst for U0 {
const VAL: usize = 0; const VAL: usize = 0;
} }
const GOAL: usize = U0::VAL; impl ToConst for i32 {
const VAL: usize = 32;
}
const GOAL: usize = U0::VAL + i32::VAL;
"#, "#,
ConstEvalError::MirLowerError(MirLowerError::IncompleteExpr), 32,
);
check_number(
r#"
struct S<T>(*mut T);
trait MySized: Sized {
const SIZE: S<Self> = S(1 as *mut Self);
}
impl MySized for i32 {
const SIZE: S<i32> = S(10 as *mut i32);
}
impl MySized for i64 {
}
const fn f<T: MySized>() -> usize {
T::SIZE.0 as usize
}
const GOAL: usize = f::<i32>() + f::<i64>() * 2;
"#,
12,
); );
} }
@ -1631,7 +1960,7 @@ fn exec_limits() {
r#" r#"
const GOAL: usize = loop {}; const GOAL: usize = loop {};
"#, "#,
ConstEvalError::MirEvalError(MirEvalError::ExecutionLimitExceeded), |e| e == ConstEvalError::MirEvalError(MirEvalError::ExecutionLimitExceeded),
); );
check_fail( check_fail(
r#" r#"
@ -1640,7 +1969,7 @@ fn exec_limits() {
} }
const GOAL: i32 = f(0); const GOAL: i32 = f(0);
"#, "#,
ConstEvalError::MirEvalError(MirEvalError::StackOverflow), |e| e == ConstEvalError::MirEvalError(MirEvalError::StackOverflow),
); );
// Reasonable code should still work // Reasonable code should still work
check_number( check_number(
@ -1665,7 +1994,7 @@ fn exec_limits() {
#[test] #[test]
fn type_error() { fn type_error() {
let e = eval_goal( check_fail(
r#" r#"
const GOAL: u8 = { const GOAL: u8 = {
let x: u16 = 2; let x: u16 = 2;
@ -1673,6 +2002,6 @@ fn type_error() {
y.0 y.0
}; };
"#, "#,
|e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::TypeMismatch(_))),
); );
assert!(matches!(e, Err(ConstEvalError::MirLowerError(MirLowerError::TypeMismatch(_)))));
} }

View File

@ -67,6 +67,135 @@ fn wrapping_add() {
); );
} }
#[test]
fn allocator() {
check_number(
r#"
extern "Rust" {
#[rustc_allocator]
fn __rust_alloc(size: usize, align: usize) -> *mut u8;
#[rustc_deallocator]
fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize);
#[rustc_reallocator]
fn __rust_realloc(ptr: *mut u8, old_size: usize, align: usize, new_size: usize) -> *mut u8;
#[rustc_allocator_zeroed]
fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8;
}
const GOAL: u8 = unsafe {
let ptr = __rust_alloc(4, 1);
let ptr2 = ((ptr as usize) + 1) as *mut u8;
*ptr = 23;
*ptr2 = 32;
let ptr = __rust_realloc(ptr, 4, 1, 8);
let ptr2 = ((ptr as usize) + 1) as *mut u8;
*ptr + *ptr2
};
"#,
55,
);
}
#[test]
fn overflowing_add() {
check_number(
r#"
extern "rust-intrinsic" {
pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool);
}
const GOAL: u8 = add_with_overflow(1, 2).0;
"#,
3,
);
check_number(
r#"
extern "rust-intrinsic" {
pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool);
}
const GOAL: u8 = add_with_overflow(1, 2).1 as u8;
"#,
0,
);
}
#[test]
fn needs_drop() {
check_number(
r#"
//- minicore: copy, sized
extern "rust-intrinsic" {
pub fn needs_drop<T: ?Sized>() -> bool;
}
struct X;
const GOAL: bool = !needs_drop::<i32>() && needs_drop::<X>();
"#,
1,
);
}
#[test]
fn likely() {
check_number(
r#"
extern "rust-intrinsic" {
pub fn likely(b: bool) -> bool;
pub fn unlikely(b: bool) -> bool;
}
const GOAL: bool = likely(true) && unlikely(true) && !likely(false) && !unlikely(false);
"#,
1,
);
}
#[test]
fn atomic() {
check_number(
r#"
//- minicore: copy
extern "rust-intrinsic" {
pub fn atomic_load_seqcst<T: Copy>(src: *const T) -> T;
pub fn atomic_xchg_acquire<T: Copy>(dst: *mut T, src: T) -> T;
pub fn atomic_cxchg_release_seqcst<T: Copy>(dst: *mut T, old: T, src: T) -> (T, bool);
pub fn atomic_cxchgweak_acquire_acquire<T: Copy>(dst: *mut T, old: T, src: T) -> (T, bool);
pub fn atomic_store_release<T: Copy>(dst: *mut T, val: T);
pub fn atomic_xadd_acqrel<T: Copy>(dst: *mut T, src: T) -> T;
pub fn atomic_xsub_seqcst<T: Copy>(dst: *mut T, src: T) -> T;
pub fn atomic_and_acquire<T: Copy>(dst: *mut T, src: T) -> T;
pub fn atomic_nand_seqcst<T: Copy>(dst: *mut T, src: T) -> T;
pub fn atomic_or_release<T: Copy>(dst: *mut T, src: T) -> T;
pub fn atomic_xor_seqcst<T: Copy>(dst: *mut T, src: T) -> T;
}
fn should_not_reach() {
_ // fails the test if executed
}
const GOAL: i32 = {
let mut x = 5;
atomic_store_release(&mut x, 10);
let mut y = atomic_xchg_acquire(&mut x, 100);
atomic_xadd_acqrel(&mut y, 20);
if (30, true) != atomic_cxchg_release_seqcst(&mut y, 30, 40) {
should_not_reach();
}
if (40, false) != atomic_cxchg_release_seqcst(&mut y, 30, 50) {
should_not_reach();
}
if (40, true) != atomic_cxchgweak_acquire_acquire(&mut y, 40, 30) {
should_not_reach();
}
let mut z = atomic_xsub_seqcst(&mut x, -200);
atomic_xor_seqcst(&mut x, 1024);
atomic_load_seqcst(&x) + z * 3 + atomic_load_seqcst(&y) * 2
};
"#,
660 + 1024,
);
}
#[test] #[test]
fn offset() { fn offset() {
check_number( check_number(

View File

@ -5,7 +5,7 @@ use std::sync::Arc;
use base_db::{impl_intern_key, salsa, CrateId, Upcast}; use base_db::{impl_intern_key, salsa, CrateId, Upcast};
use hir_def::{ use hir_def::{
db::DefDatabase, hir::ExprId, layout::TargetDataLayout, AdtId, BlockId, ConstId, ConstParamId, db::DefDatabase, hir::ExprId, layout::TargetDataLayout, AdtId, BlockId, ConstParamId,
DefWithBodyId, EnumVariantId, FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, DefWithBodyId, EnumVariantId, FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId,
TypeOrConstParamId, VariantId, TypeOrConstParamId, VariantId,
}; };
@ -59,7 +59,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[salsa::invoke(crate::consteval::const_eval_query)] #[salsa::invoke(crate::consteval::const_eval_query)]
#[salsa::cycle(crate::consteval::const_eval_recover)] #[salsa::cycle(crate::consteval::const_eval_recover)]
fn const_eval(&self, def: ConstId, subst: Substitution) -> Result<Const, ConstEvalError>; fn const_eval(&self, def: DefWithBodyId, subst: Substitution) -> Result<Const, ConstEvalError>;
#[salsa::invoke(crate::consteval::const_eval_discriminant_variant)] #[salsa::invoke(crate::consteval::const_eval_discriminant_variant)]
#[salsa::cycle(crate::consteval::const_eval_discriminant_recover)] #[salsa::cycle(crate::consteval::const_eval_discriminant_recover)]

View File

@ -772,7 +772,7 @@ impl<'p> Fields<'p> {
(0..fields_len).map(|idx| LocalFieldId::from_raw(idx.into())).filter_map(move |fid| { (0..fields_len).map(|idx| LocalFieldId::from_raw(idx.into())).filter_map(move |fid| {
let ty = field_ty[fid].clone().substitute(Interner, substs); let ty = field_ty[fid].clone().substitute(Interner, substs);
let ty = normalize(cx.db, cx.body, ty); let ty = normalize(cx.db, cx.db.trait_environment_for_body(cx.body), ty);
let is_visible = matches!(adt, hir_def::AdtId::EnumId(..)) let is_visible = matches!(adt, hir_def::AdtId::EnumId(..))
|| visibility[fid].is_visible_from(cx.db.upcast(), cx.module); || visibility[fid].is_visible_from(cx.db.upcast(), cx.module);
let is_uninhabited = cx.is_uninhabited(&ty); let is_uninhabited = cx.is_uninhabited(&ty);

View File

@ -32,7 +32,7 @@ use crate::{
mapping::from_chalk, mapping::from_chalk,
mir::pad16, mir::pad16,
primitive, to_assoc_type_id, primitive, to_assoc_type_id,
utils::{self, generics}, utils::{self, generics, ClosureSubst},
AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Const, ConstScalar, ConstValue, AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Const, ConstScalar, ConstValue,
DomainGoal, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, LifetimeOutlives, DomainGoal, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, LifetimeOutlives,
MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Scalar, MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Scalar,
@ -419,6 +419,16 @@ impl HirDisplay for Const {
} }
ConstValue::Concrete(c) => match &c.interned { ConstValue::Concrete(c) => match &c.interned {
ConstScalar::Bytes(b, m) => render_const_scalar(f, &b, m, &data.ty), ConstScalar::Bytes(b, m) => render_const_scalar(f, &b, m, &data.ty),
ConstScalar::UnevaluatedConst(c, parameters) => {
let const_data = f.db.const_data(*c);
write!(
f,
"{}",
const_data.name.as_ref().and_then(|x| x.as_str()).unwrap_or("_")
)?;
hir_fmt_generics(f, parameters, Some((*c).into()))?;
Ok(())
}
ConstScalar::Unknown => f.write_char('_'), ConstScalar::Unknown => f.write_char('_'),
}, },
} }
@ -485,7 +495,7 @@ fn render_const_scalar(
chalk_ir::TyKind::Ref(_, _, t) => match t.kind(Interner) { chalk_ir::TyKind::Ref(_, _, t) => match t.kind(Interner) {
chalk_ir::TyKind::Str => { chalk_ir::TyKind::Str => {
let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap()); let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap());
let bytes = memory_map.0.get(&addr).map(|x| &**x).unwrap_or(&[]); let bytes = memory_map.memory.get(&addr).map(|x| &**x).unwrap_or(&[]);
let s = std::str::from_utf8(bytes).unwrap_or("<utf8-error>"); let s = std::str::from_utf8(bytes).unwrap_or("<utf8-error>");
write!(f, "{s:?}") write!(f, "{s:?}")
} }
@ -574,6 +584,11 @@ fn render_const_scalar(
hir_def::AdtId::EnumId(_) => f.write_str("<enum-not-supported>"), hir_def::AdtId::EnumId(_) => f.write_str("<enum-not-supported>"),
}, },
chalk_ir::TyKind::FnDef(..) => ty.hir_fmt(f), chalk_ir::TyKind::FnDef(..) => ty.hir_fmt(f),
chalk_ir::TyKind::Raw(_, _) => {
let x = u128::from_le_bytes(pad16(b, false));
write!(f, "{:#X} as ", x)?;
ty.hir_fmt(f)
}
_ => f.write_str("<not-supported>"), _ => f.write_str("<not-supported>"),
} }
} }
@ -794,82 +809,9 @@ impl HirDisplay for Ty {
} }
f.end_location_link(); f.end_location_link();
if parameters.len(Interner) > 0 { let generic_def = self.as_generic_def(db);
let parameters_to_write = if f.display_target.is_source_code()
|| f.omit_verbose_types()
{
match self
.as_generic_def(db)
.map(|generic_def_id| db.generic_defaults(generic_def_id))
.filter(|defaults| !defaults.is_empty())
{
None => parameters.as_slice(Interner),
Some(default_parameters) => {
fn should_show(
parameter: &GenericArg,
default_parameters: &[Binders<GenericArg>],
i: usize,
parameters: &Substitution,
) -> bool {
if parameter.ty(Interner).map(|x| x.kind(Interner))
== Some(&TyKind::Error)
{
return true;
}
if let Some(ConstValue::Concrete(c)) = parameter
.constant(Interner)
.map(|x| &x.data(Interner).value)
{
if c.interned == ConstScalar::Unknown {
return true;
}
}
let default_parameter = match default_parameters.get(i) {
Some(x) => x,
None => return true,
};
let actual_default =
default_parameter.clone().substitute(Interner, &parameters);
parameter != &actual_default
}
let mut default_from = 0;
for (i, parameter) in parameters.iter(Interner).enumerate() {
if should_show(parameter, &default_parameters, i, parameters) {
default_from = i + 1;
}
}
&parameters.as_slice(Interner)[0..default_from]
}
}
} else {
parameters.as_slice(Interner)
};
if !parameters_to_write.is_empty() {
write!(f, "<")?;
if f.display_target.is_source_code() { hir_fmt_generics(f, parameters, generic_def)?;
let mut first = true;
for generic_arg in parameters_to_write {
if !first {
write!(f, ", ")?;
}
first = false;
if generic_arg.ty(Interner).map(|ty| ty.kind(Interner))
== Some(&TyKind::Error)
{
write!(f, "_")?;
} else {
generic_arg.hir_fmt(f)?;
}
}
} else {
f.write_joined(parameters_to_write, ", ")?;
}
write!(f, ">")?;
}
}
} }
TyKind::AssociatedType(assoc_type_id, parameters) => { TyKind::AssociatedType(assoc_type_id, parameters) => {
let type_alias = from_assoc_type_id(*assoc_type_id); let type_alias = from_assoc_type_id(*assoc_type_id);
@ -983,7 +925,7 @@ impl HirDisplay for Ty {
} }
_ => (), _ => (),
} }
let sig = substs.at(Interner, 0).assert_ty_ref(Interner).callable_sig(db); let sig = ClosureSubst(substs).sig_ty().callable_sig(db);
if let Some(sig) = sig { if let Some(sig) = sig {
let (def, _) = db.lookup_intern_closure((*id).into()); let (def, _) = db.lookup_intern_closure((*id).into());
let infer = db.infer(def); let infer = db.infer(def);
@ -1141,6 +1083,85 @@ impl HirDisplay for Ty {
} }
} }
fn hir_fmt_generics(
f: &mut HirFormatter<'_>,
parameters: &Substitution,
generic_def: Option<hir_def::GenericDefId>,
) -> Result<(), HirDisplayError> {
let db = f.db;
if parameters.len(Interner) > 0 {
let parameters_to_write = if f.display_target.is_source_code() || f.omit_verbose_types() {
match generic_def
.map(|generic_def_id| db.generic_defaults(generic_def_id))
.filter(|defaults| !defaults.is_empty())
{
None => parameters.as_slice(Interner),
Some(default_parameters) => {
fn should_show(
parameter: &GenericArg,
default_parameters: &[Binders<GenericArg>],
i: usize,
parameters: &Substitution,
) -> bool {
if parameter.ty(Interner).map(|x| x.kind(Interner)) == Some(&TyKind::Error)
{
return true;
}
if let Some(ConstValue::Concrete(c)) =
parameter.constant(Interner).map(|x| &x.data(Interner).value)
{
if c.interned == ConstScalar::Unknown {
return true;
}
}
let default_parameter = match default_parameters.get(i) {
Some(x) => x,
None => return true,
};
let actual_default =
default_parameter.clone().substitute(Interner, &parameters);
parameter != &actual_default
}
let mut default_from = 0;
for (i, parameter) in parameters.iter(Interner).enumerate() {
if should_show(parameter, &default_parameters, i, parameters) {
default_from = i + 1;
}
}
&parameters.as_slice(Interner)[0..default_from]
}
}
} else {
parameters.as_slice(Interner)
};
if !parameters_to_write.is_empty() {
write!(f, "<")?;
if f.display_target.is_source_code() {
let mut first = true;
for generic_arg in parameters_to_write {
if !first {
write!(f, ", ")?;
}
first = false;
if generic_arg.ty(Interner).map(|ty| ty.kind(Interner)) == Some(&TyKind::Error)
{
write!(f, "_")?;
} else {
generic_arg.hir_fmt(f)?;
}
}
} else {
f.write_joined(parameters_to_write, ", ")?;
}
write!(f, ">")?;
}
}
Ok(())
}
impl HirDisplay for CallableSig { impl HirDisplay for CallableSig {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
write!(f, "fn(")?; write!(f, "fn(")?;

View File

@ -44,7 +44,7 @@ use crate::{
db::HirDatabase, fold_tys, infer::coerce::CoerceMany, lower::ImplTraitLoweringMode, db::HirDatabase, fold_tys, infer::coerce::CoerceMany, lower::ImplTraitLoweringMode,
static_lifetime, to_assoc_type_id, traits::FnTrait, AliasEq, AliasTy, ClosureId, DomainGoal, static_lifetime, to_assoc_type_id, traits::FnTrait, AliasEq, AliasTy, ClosureId, DomainGoal,
GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId, Substitution, GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId, Substitution,
TraitRef, Ty, TyBuilder, TyExt, TyKind, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
}; };
// This lint has a false positive here. See the link below for details. // This lint has a false positive here. See the link below for details.
@ -117,11 +117,10 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
/// ///
/// This is appropriate to use only after type-check: it assumes /// This is appropriate to use only after type-check: it assumes
/// that normalization will succeed, for example. /// that normalization will succeed, for example.
pub(crate) fn normalize(db: &dyn HirDatabase, owner: DefWithBodyId, ty: Ty) -> Ty { pub(crate) fn normalize(db: &dyn HirDatabase, trait_env: Arc<TraitEnvironment>, ty: Ty) -> Ty {
if !ty.data(Interner).flags.intersects(TypeFlags::HAS_PROJECTION) { if !ty.data(Interner).flags.intersects(TypeFlags::HAS_PROJECTION) {
return ty; return ty;
} }
let trait_env = db.trait_environment_for_body(owner);
let mut table = unify::InferenceTable::new(db, trait_env); let mut table = unify::InferenceTable::new(db, trait_env);
let ty_with_vars = table.normalize_associated_types_in(ty); let ty_with_vars = table.normalize_associated_types_in(ty);

View File

@ -2,7 +2,11 @@
use std::{cmp, collections::HashMap, convert::Infallible, mem}; use std::{cmp, collections::HashMap, convert::Infallible, mem};
use chalk_ir::{cast::Cast, AliasEq, AliasTy, FnSubst, Mutability, TyKind, WhereClause}; use chalk_ir::{
cast::Cast,
fold::{FallibleTypeFolder, TypeFoldable},
AliasEq, AliasTy, BoundVar, DebruijnIndex, FnSubst, Mutability, TyKind, WhereClause,
};
use hir_def::{ use hir_def::{
data::adt::VariantData, data::adt::VariantData,
hir::{ hir::{
@ -11,7 +15,7 @@ use hir_def::{
}, },
lang_item::LangItem, lang_item::LangItem,
resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
FieldId, HasModule, VariantId, DefWithBodyId, FieldId, HasModule, VariantId,
}; };
use hir_expand::name; use hir_expand::name;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
@ -20,12 +24,13 @@ use stdx::never;
use crate::{ use crate::{
db::HirDatabase, db::HirDatabase,
from_placeholder_idx, make_binders,
mir::{BorrowKind, MirSpan, ProjectionElem}, mir::{BorrowKind, MirSpan, ProjectionElem},
static_lifetime, to_chalk_trait_id, static_lifetime, to_chalk_trait_id,
traits::FnTrait, traits::FnTrait,
utils::{self, pattern_matching_dereference_count}, utils::{self, generics, pattern_matching_dereference_count, Generics},
Adjust, Adjustment, Canonical, CanonicalVarKinds, ChalkTraitId, ClosureId, DynTy, FnPointer, Adjust, Adjustment, Binders, ChalkTraitId, ClosureId, DynTy, FnPointer, FnSig, Interner,
FnSig, InEnvironment, Interner, Substitution, Ty, TyBuilder, TyExt, Substitution, Ty, TyExt,
}; };
use super::{Expectation, InferenceContext}; use super::{Expectation, InferenceContext};
@ -117,7 +122,7 @@ impl HirPlace {
fn ty(&self, ctx: &mut InferenceContext<'_>) -> Ty { fn ty(&self, ctx: &mut InferenceContext<'_>) -> Ty {
let mut ty = ctx.table.resolve_completely(ctx.result[self.local].clone()); let mut ty = ctx.table.resolve_completely(ctx.result[self.local].clone());
for p in &self.projections { for p in &self.projections {
ty = p.projected_ty(ty, ctx.db, |_, _| { ty = p.projected_ty(ty, ctx.db, |_, _, _| {
unreachable!("Closure field only happens in MIR"); unreachable!("Closure field only happens in MIR");
}); });
} }
@ -152,7 +157,7 @@ pub struct CapturedItem {
pub(crate) place: HirPlace, pub(crate) place: HirPlace,
pub(crate) kind: CaptureKind, pub(crate) kind: CaptureKind,
pub(crate) span: MirSpan, pub(crate) span: MirSpan,
pub(crate) ty: Ty, pub(crate) ty: Binders<Ty>,
} }
impl CapturedItem { impl CapturedItem {
@ -232,6 +237,52 @@ pub(crate) struct CapturedItemWithoutTy {
impl CapturedItemWithoutTy { impl CapturedItemWithoutTy {
fn with_ty(self, ctx: &mut InferenceContext<'_>) -> CapturedItem { fn with_ty(self, ctx: &mut InferenceContext<'_>) -> CapturedItem {
fn replace_placeholder_with_binder(
db: &dyn HirDatabase,
owner: DefWithBodyId,
ty: Ty,
) -> Binders<Ty> {
struct Filler<'a> {
db: &'a dyn HirDatabase,
generics: Generics,
}
impl FallibleTypeFolder<Interner> for Filler<'_> {
type Error = ();
fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder<Interner, Error = Self::Error> {
self
}
fn interner(&self) -> Interner {
Interner
}
fn try_fold_free_placeholder_ty(
&mut self,
idx: chalk_ir::PlaceholderIndex,
_outer_binder: DebruijnIndex,
) -> std::result::Result<Ty, Self::Error> {
let x = from_placeholder_idx(self.db, idx);
let Some(idx) = self.generics.param_idx(x) else {
return Err(());
};
Ok(TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, idx))
.intern(Interner))
}
}
let g_def = match owner {
DefWithBodyId::FunctionId(f) => Some(f.into()),
DefWithBodyId::StaticId(_) => None,
DefWithBodyId::ConstId(f) => Some(f.into()),
DefWithBodyId::VariantId(f) => Some(f.into()),
};
let Some(generics) = g_def.map(|g_def| generics(db.upcast(), g_def)) else {
return Binders::empty(Interner, ty);
};
let filler = &mut Filler { db, generics };
let result = ty.clone().try_fold_with(filler, DebruijnIndex::INNERMOST).unwrap_or(ty);
make_binders(db, &filler.generics, result)
}
let ty = self.place.ty(ctx).clone(); let ty = self.place.ty(ctx).clone();
let ty = match &self.kind { let ty = match &self.kind {
CaptureKind::ByValue => ty, CaptureKind::ByValue => ty,
@ -243,7 +294,12 @@ impl CapturedItemWithoutTy {
TyKind::Ref(m, static_lifetime(), ty).intern(Interner) TyKind::Ref(m, static_lifetime(), ty).intern(Interner)
} }
}; };
CapturedItem { place: self.place, kind: self.kind, span: self.span, ty } CapturedItem {
place: self.place,
kind: self.kind,
span: self.span,
ty: replace_placeholder_with_binder(ctx.db, ctx.owner, ty),
}
} }
} }
@ -590,17 +646,7 @@ impl InferenceContext<'_> {
// without creating query cycles. // without creating query cycles.
return self.result.closure_info.get(id).map(|x| x.1 == FnTrait::Fn).unwrap_or(true); return self.result.closure_info.get(id).map(|x| x.1 == FnTrait::Fn).unwrap_or(true);
} }
let crate_id = self.owner.module(self.db.upcast()).krate(); ty.is_copy(self.db, self.owner)
let Some(copy_trait) = self.db.lang_item(crate_id, LangItem::Copy).and_then(|x| x.as_trait()) else {
return false;
};
let trait_ref = TyBuilder::trait_ref(self.db, copy_trait).push(ty).build();
let env = self.db.trait_environment_for_body(self.owner);
let goal = Canonical {
value: InEnvironment::new(&env.env, trait_ref.cast(Interner)),
binders: CanonicalVarKinds::empty(Interner),
};
self.db.trait_solve(crate_id, None, goal).is_some()
} }
fn select_from_expr(&mut self, expr: ExprId) { fn select_from_expr(&mut self, expr: ExprId) {

View File

@ -21,8 +21,10 @@ use crate::{
Adjust, Adjustment, AutoBorrow, InferOk, InferenceContext, OverloadedDeref, PointerCast, Adjust, Adjustment, AutoBorrow, InferOk, InferenceContext, OverloadedDeref, PointerCast,
TypeError, TypeMismatch, TypeError, TypeMismatch,
}, },
static_lifetime, Canonical, DomainGoal, FnPointer, FnSig, Guidance, InEnvironment, Interner, static_lifetime,
Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, utils::ClosureSubst,
Canonical, DomainGoal, FnPointer, FnSig, Guidance, InEnvironment, Interner, Solution,
Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
}; };
use super::unify::InferenceTable; use super::unify::InferenceTable;
@ -670,7 +672,7 @@ impl<'a> InferenceTable<'a> {
} }
fn coerce_closure_fn_ty(closure_substs: &Substitution, safety: chalk_ir::Safety) -> Ty { fn coerce_closure_fn_ty(closure_substs: &Substitution, safety: chalk_ir::Safety) -> Ty {
let closure_sig = closure_substs.at(Interner, 0).assert_ty_ref(Interner).clone(); let closure_sig = ClosureSubst(closure_substs).sig_ty().clone();
match closure_sig.kind(Interner) { match closure_sig.kind(Interner) {
TyKind::Function(fn_ty) => TyKind::Function(FnPointer { TyKind::Function(fn_ty) => TyKind::Function(FnPointer {
num_binders: fn_ty.num_binders, num_binders: fn_ty.num_binders,

View File

@ -282,7 +282,7 @@ impl<'a> InferenceContext<'a> {
let closure_id = self.db.intern_closure((self.owner, tgt_expr)).into(); let closure_id = self.db.intern_closure((self.owner, tgt_expr)).into();
let closure_ty = TyKind::Closure( let closure_ty = TyKind::Closure(
closure_id, closure_id,
Substitution::from1(Interner, sig_ty.clone()), TyBuilder::subst_for_closure(self.db, self.owner, sig_ty.clone()),
) )
.intern(Interner); .intern(Interner);
self.deferred_closures.entry(closure_id).or_default(); self.deferred_closures.entry(closure_id).or_default();

View File

@ -1,5 +1,7 @@
//! Compute the binary representation of a type //! Compute the binary representation of a type
use std::sync::Arc;
use base_db::CrateId; use base_db::CrateId;
use chalk_ir::{AdtId, TyKind}; use chalk_ir::{AdtId, TyKind};
use hir_def::{ use hir_def::{
@ -13,8 +15,8 @@ use la_arena::{Idx, RawIdx};
use stdx::never; use stdx::never;
use crate::{ use crate::{
consteval::try_const_usize, db::HirDatabase, layout::adt::struct_variant_idx, Interner, consteval::try_const_usize, db::HirDatabase, infer::normalize, layout::adt::struct_variant_idx,
Substitution, Ty, utils::ClosureSubst, Interner, Substitution, TraitEnvironment, Ty,
}; };
pub use self::{ pub use self::{
@ -80,6 +82,8 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
let Some(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable) }; let Some(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable) };
let cx = LayoutCx { krate, target: &target }; let cx = LayoutCx { krate, target: &target };
let dl = &*cx.current_data_layout(); let dl = &*cx.current_data_layout();
let trait_env = Arc::new(TraitEnvironment::empty(krate));
let ty = normalize(db, trait_env, ty.clone());
Ok(match ty.kind(Interner) { Ok(match ty.kind(Interner) {
TyKind::Adt(AdtId(def), subst) => db.layout_of_adt(*def, subst.clone())?, TyKind::Adt(AdtId(def), subst) => db.layout_of_adt(*def, subst.clone())?,
TyKind::Scalar(s) => match s { TyKind::Scalar(s) => match s {
@ -146,7 +150,7 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
} }
TyKind::Array(element, count) => { TyKind::Array(element, count) => {
let count = try_const_usize(&count).ok_or(LayoutError::UserError( let count = try_const_usize(&count).ok_or(LayoutError::UserError(
"mismatched type of const generic parameter".to_string(), "unevaluated or mistyped const generic parameter".to_string(),
))? as u64; ))? as u64;
let element = layout_of_ty(db, element, krate)?; let element = layout_of_ty(db, element, krate)?;
let size = element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow)?; let size = element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow)?;
@ -252,13 +256,19 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
} }
} }
} }
TyKind::Closure(c, _) => { TyKind::Closure(c, subst) => {
let (def, _) = db.lookup_intern_closure((*c).into()); let (def, _) = db.lookup_intern_closure((*c).into());
let infer = db.infer(def); let infer = db.infer(def);
let (captures, _) = infer.closure_info(c); let (captures, _) = infer.closure_info(c);
let fields = captures let fields = captures
.iter() .iter()
.map(|x| layout_of_ty(db, &x.ty, krate)) .map(|x| {
layout_of_ty(
db,
&x.ty.clone().substitute(Interner, ClosureSubst(subst).parent_subst()),
krate,
)
})
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
let fields = fields.iter().collect::<Vec<_>>(); let fields = fields.iter().collect::<Vec<_>>();
let fields = fields.iter().collect::<Vec<_>>(); let fields = fields.iter().collect::<Vec<_>>();

View File

@ -218,6 +218,22 @@ fn generic() {
} }
} }
#[test]
fn associated_types() {
size_and_align! {
trait Tr {
type Ty;
}
impl Tr for i32 {
type Ty = i64;
}
struct Foo<A: Tr>(<A as Tr>::Ty);
struct Goal(Foo<i32>);
}
}
#[test] #[test]
fn return_position_impl_trait() { fn return_position_impl_trait() {
size_and_align_expr! { size_and_align_expr! {

View File

@ -44,10 +44,10 @@ use chalk_ir::{
NoSolution, TyData, NoSolution, TyData,
}; };
use either::Either; use either::Either;
use hir_def::{hir::ExprId, type_ref::Rawness, TypeOrConstParamId}; use hir_def::{hir::ExprId, type_ref::Rawness, ConstId, TypeOrConstParamId};
use hir_expand::name; use hir_expand::name;
use la_arena::{Arena, Idx}; use la_arena::{Arena, Idx};
use mir::MirEvalError; use mir::{MirEvalError, VTableMap};
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use traits::FnTrait; use traits::FnTrait;
use utils::Generics; use utils::Generics;
@ -151,11 +151,14 @@ pub type WhereClause = chalk_ir::WhereClause<Interner>;
/// the necessary bits of memory of the const eval session to keep the constant /// the necessary bits of memory of the const eval session to keep the constant
/// meaningful. /// meaningful.
#[derive(Debug, Default, Clone, PartialEq, Eq)] #[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct MemoryMap(pub HashMap<usize, Vec<u8>>); pub struct MemoryMap {
pub memory: HashMap<usize, Vec<u8>>,
pub vtable: VTableMap,
}
impl MemoryMap { impl MemoryMap {
fn insert(&mut self, addr: usize, x: Vec<u8>) { fn insert(&mut self, addr: usize, x: Vec<u8>) {
self.0.insert(addr, x); self.memory.insert(addr, x);
} }
/// This functions convert each address by a function `f` which gets the byte intervals and assign an address /// This functions convert each address by a function `f` which gets the byte intervals and assign an address
@ -165,7 +168,7 @@ impl MemoryMap {
&self, &self,
mut f: impl FnMut(&[u8]) -> Result<usize, MirEvalError>, mut f: impl FnMut(&[u8]) -> Result<usize, MirEvalError>,
) -> Result<HashMap<usize, usize>, MirEvalError> { ) -> Result<HashMap<usize, usize>, MirEvalError> {
self.0.iter().map(|x| Ok((*x.0, f(x.1)?))).collect() self.memory.iter().map(|x| Ok((*x.0, f(x.1)?))).collect()
} }
} }
@ -173,6 +176,9 @@ impl MemoryMap {
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum ConstScalar { pub enum ConstScalar {
Bytes(Vec<u8>, MemoryMap), Bytes(Vec<u8>, MemoryMap),
// FIXME: this is a hack to get around chalk not being able to represent unevaluatable
// constants
UnevaluatedConst(ConstId, Substitution),
/// Case of an unknown value that rustc might know but we don't /// Case of an unknown value that rustc might know but we don't
// FIXME: this is a hack to get around chalk not being able to represent unevaluatable // FIXME: this is a hack to get around chalk not being able to represent unevaluatable
// constants // constants

View File

@ -3,13 +3,14 @@
use std::{fmt::Display, iter}; use std::{fmt::Display, iter};
use crate::{ use crate::{
db::HirDatabase, display::HirDisplay, infer::PointerCast, lang_items::is_box, ClosureId, Const, db::HirDatabase, display::HirDisplay, infer::PointerCast, lang_items::is_box, mapping::ToChalk,
ConstScalar, InferenceResult, Interner, MemoryMap, Substitution, Ty, TyKind, CallableDefId, ClosureId, Const, ConstScalar, InferenceResult, Interner, MemoryMap,
Substitution, Ty, TyKind,
}; };
use chalk_ir::Mutability; use chalk_ir::Mutability;
use hir_def::{ use hir_def::{
hir::{BindingId, Expr, ExprId, Ordering, PatId}, hir::{BindingId, Expr, ExprId, Ordering, PatId},
DefWithBodyId, FieldId, UnionId, VariantId, DefWithBodyId, FieldId, StaticId, UnionId, VariantId,
}; };
use la_arena::{Arena, ArenaMap, Idx, RawIdx}; use la_arena::{Arena, ArenaMap, Idx, RawIdx};
@ -19,7 +20,7 @@ mod borrowck;
mod pretty; mod pretty;
pub use borrowck::{borrowck_query, BorrowckResult, MutabilityReason}; pub use borrowck::{borrowck_query, BorrowckResult, MutabilityReason};
pub use eval::{interpret_mir, pad16, Evaluator, MirEvalError}; pub use eval::{interpret_mir, pad16, Evaluator, MirEvalError, VTableMap};
pub use lower::{ pub use lower::{
lower_to_mir, mir_body_for_closure_query, mir_body_query, mir_body_recover, MirLowerError, lower_to_mir, mir_body_for_closure_query, mir_body_query, mir_body_recover, MirLowerError,
}; };
@ -76,6 +77,9 @@ pub enum Operand {
Move(Place), Move(Place),
/// Constants are already semantically values, and remain unchanged. /// Constants are already semantically values, and remain unchanged.
Constant(Const), Constant(Const),
/// NON STANDARD: This kind of operand returns an immutable reference to that static memory. Rustc
/// handles it with the `Constant` variant somehow.
Static(StaticId),
} }
impl Operand { impl Operand {
@ -90,6 +94,17 @@ impl Operand {
fn const_zst(ty: Ty) -> Operand { fn const_zst(ty: Ty) -> Operand {
Self::from_bytes(vec![], ty) Self::from_bytes(vec![], ty)
} }
fn from_fn(
db: &dyn HirDatabase,
func_id: hir_def::FunctionId,
generic_args: Substitution,
) -> Operand {
let ty =
chalk_ir::TyKind::FnDef(CallableDefId::FunctionId(func_id).to_chalk(db), generic_args)
.intern(Interner);
Operand::from_bytes(vec![], ty)
}
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -110,7 +125,7 @@ impl<V, T> ProjectionElem<V, T> {
&self, &self,
base: Ty, base: Ty,
db: &dyn HirDatabase, db: &dyn HirDatabase,
closure_field: impl FnOnce(ClosureId, usize) -> Ty, closure_field: impl FnOnce(ClosureId, &Substitution, usize) -> Ty,
) -> Ty { ) -> Ty {
match self { match self {
ProjectionElem::Deref => match &base.data(Interner).kind { ProjectionElem::Deref => match &base.data(Interner).kind {
@ -142,7 +157,7 @@ impl<V, T> ProjectionElem<V, T> {
never!("Out of bound tuple field"); never!("Out of bound tuple field");
TyKind::Error.intern(Interner) TyKind::Error.intern(Interner)
}), }),
TyKind::Closure(id, _) => closure_field(*id, *f), TyKind::Closure(id, subst) => closure_field(*id, subst, *f),
_ => { _ => {
never!("Only tuple or closure has tuple or closure field"); never!("Only tuple or closure has tuple or closure field");
return TyKind::Error.intern(Interner); return TyKind::Error.intern(Interner);
@ -261,7 +276,13 @@ impl SwitchTargets {
} }
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]
pub enum Terminator { pub struct Terminator {
span: MirSpan,
kind: TerminatorKind,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum TerminatorKind {
/// Block has one successor; we continue execution there. /// Block has one successor; we continue execution there.
Goto { target: BasicBlockId }, Goto { target: BasicBlockId },
@ -836,6 +857,9 @@ pub enum Rvalue {
/// affects alias analysis. /// affects alias analysis.
ShallowInitBox(Operand, Ty), ShallowInitBox(Operand, Ty),
/// NON STANDARD: allocates memory with the type's layout, and shallow init the box with the resulting pointer.
ShallowInitBoxWithAlloc(Ty),
/// A CopyForDeref is equivalent to a read from a place at the /// A CopyForDeref is equivalent to a read from a place at the
/// codegen level, but is treated specially by drop elaboration. When such a read happens, it /// codegen level, but is treated specially by drop elaboration. When such a read happens, it
/// is guaranteed (via nature of the mir_opt `Derefer` in rustc_mir_transform/src/deref_separator) /// is guaranteed (via nature of the mir_opt `Derefer` in rustc_mir_transform/src/deref_separator)
@ -918,7 +942,7 @@ impl MirBody {
Operand::Copy(p) | Operand::Move(p) => { Operand::Copy(p) | Operand::Move(p) => {
f(p); f(p);
} }
Operand::Constant(_) => (), Operand::Constant(_) | Operand::Static(_) => (),
} }
} }
for (_, block) in self.basic_blocks.iter_mut() { for (_, block) in self.basic_blocks.iter_mut() {
@ -927,6 +951,7 @@ impl MirBody {
StatementKind::Assign(p, r) => { StatementKind::Assign(p, r) => {
f(p); f(p);
match r { match r {
Rvalue::ShallowInitBoxWithAlloc(_) => (),
Rvalue::ShallowInitBox(o, _) Rvalue::ShallowInitBox(o, _)
| Rvalue::UnaryOp(_, o) | Rvalue::UnaryOp(_, o)
| Rvalue::Cast(_, o, _) | Rvalue::Cast(_, o, _)
@ -954,32 +979,32 @@ impl MirBody {
} }
} }
match &mut block.terminator { match &mut block.terminator {
Some(x) => match x { Some(x) => match &mut x.kind {
Terminator::SwitchInt { discr, .. } => for_operand(discr, &mut f), TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, &mut f),
Terminator::FalseEdge { .. } TerminatorKind::FalseEdge { .. }
| Terminator::FalseUnwind { .. } | TerminatorKind::FalseUnwind { .. }
| Terminator::Goto { .. } | TerminatorKind::Goto { .. }
| Terminator::Resume | TerminatorKind::Resume
| Terminator::GeneratorDrop | TerminatorKind::GeneratorDrop
| Terminator::Abort | TerminatorKind::Abort
| Terminator::Return | TerminatorKind::Return
| Terminator::Unreachable => (), | TerminatorKind::Unreachable => (),
Terminator::Drop { place, .. } => { TerminatorKind::Drop { place, .. } => {
f(place); f(place);
} }
Terminator::DropAndReplace { place, value, .. } => { TerminatorKind::DropAndReplace { place, value, .. } => {
f(place); f(place);
for_operand(value, &mut f); for_operand(value, &mut f);
} }
Terminator::Call { func, args, destination, .. } => { TerminatorKind::Call { func, args, destination, .. } => {
for_operand(func, &mut f); for_operand(func, &mut f);
args.iter_mut().for_each(|x| for_operand(x, &mut f)); args.iter_mut().for_each(|x| for_operand(x, &mut f));
f(destination); f(destination);
} }
Terminator::Assert { cond, .. } => { TerminatorKind::Assert { cond, .. } => {
for_operand(cond, &mut f); for_operand(cond, &mut f);
} }
Terminator::Yield { value, resume_arg, .. } => { TerminatorKind::Yield { value, resume_arg, .. } => {
for_operand(value, &mut f); for_operand(value, &mut f);
f(resume_arg); f(resume_arg);
} }

View File

@ -13,7 +13,7 @@ use crate::{db::HirDatabase, ClosureId};
use super::{ use super::{
BasicBlockId, BorrowKind, LocalId, MirBody, MirLowerError, MirSpan, Place, ProjectionElem, BasicBlockId, BorrowKind, LocalId, MirBody, MirLowerError, MirSpan, Place, ProjectionElem,
Rvalue, StatementKind, Terminator, Rvalue, StatementKind, TerminatorKind,
}; };
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@ -141,26 +141,26 @@ fn ever_initialized_map(body: &MirBody) -> ArenaMap<BasicBlockId, ArenaMap<Local
never!("Terminator should be none only in construction"); never!("Terminator should be none only in construction");
return; return;
}; };
let targets = match terminator { let targets = match &terminator.kind {
Terminator::Goto { target } => vec![*target], TerminatorKind::Goto { target } => vec![*target],
Terminator::SwitchInt { targets, .. } => targets.all_targets().to_vec(), TerminatorKind::SwitchInt { targets, .. } => targets.all_targets().to_vec(),
Terminator::Resume TerminatorKind::Resume
| Terminator::Abort | TerminatorKind::Abort
| Terminator::Return | TerminatorKind::Return
| Terminator::Unreachable => vec![], | TerminatorKind::Unreachable => vec![],
Terminator::Call { target, cleanup, destination, .. } => { TerminatorKind::Call { target, cleanup, destination, .. } => {
if destination.projection.len() == 0 && destination.local == l { if destination.projection.len() == 0 && destination.local == l {
is_ever_initialized = true; is_ever_initialized = true;
} }
target.into_iter().chain(cleanup.into_iter()).copied().collect() target.into_iter().chain(cleanup.into_iter()).copied().collect()
} }
Terminator::Drop { .. } TerminatorKind::Drop { .. }
| Terminator::DropAndReplace { .. } | TerminatorKind::DropAndReplace { .. }
| Terminator::Assert { .. } | TerminatorKind::Assert { .. }
| Terminator::Yield { .. } | TerminatorKind::Yield { .. }
| Terminator::GeneratorDrop | TerminatorKind::GeneratorDrop
| Terminator::FalseEdge { .. } | TerminatorKind::FalseEdge { .. }
| Terminator::FalseUnwind { .. } => { | TerminatorKind::FalseUnwind { .. } => {
never!("We don't emit these MIR terminators yet"); never!("We don't emit these MIR terminators yet");
vec![] vec![]
} }
@ -228,21 +228,21 @@ fn mutability_of_locals(body: &MirBody) -> ArenaMap<LocalId, MutabilityReason> {
never!("Terminator should be none only in construction"); never!("Terminator should be none only in construction");
continue; continue;
}; };
match terminator { match &terminator.kind {
Terminator::Goto { .. } TerminatorKind::Goto { .. }
| Terminator::Resume | TerminatorKind::Resume
| Terminator::Abort | TerminatorKind::Abort
| Terminator::Return | TerminatorKind::Return
| Terminator::Unreachable | TerminatorKind::Unreachable
| Terminator::FalseEdge { .. } | TerminatorKind::FalseEdge { .. }
| Terminator::FalseUnwind { .. } | TerminatorKind::FalseUnwind { .. }
| Terminator::GeneratorDrop | TerminatorKind::GeneratorDrop
| Terminator::SwitchInt { .. } | TerminatorKind::SwitchInt { .. }
| Terminator::Drop { .. } | TerminatorKind::Drop { .. }
| Terminator::DropAndReplace { .. } | TerminatorKind::DropAndReplace { .. }
| Terminator::Assert { .. } | TerminatorKind::Assert { .. }
| Terminator::Yield { .. } => (), | TerminatorKind::Yield { .. } => (),
Terminator::Call { destination, .. } => { TerminatorKind::Call { destination, .. } => {
if destination.projection.len() == 0 { if destination.projection.len() == 0 {
if ever_init_map.get(destination.local).copied().unwrap_or_default() { if ever_init_map.get(destination.local).copied().unwrap_or_default() {
push_mut_span(destination.local, MirSpan::Unknown); push_mut_span(destination.local, MirSpan::Unknown);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,396 @@
//! Interpret intrinsics, lang items and `extern "C"` wellknown functions which their implementation
//! is not available.
use super::*;
macro_rules! from_bytes {
($ty:tt, $value:expr) => {
($ty::from_le_bytes(match ($value).try_into() {
Ok(x) => x,
Err(_) => return Err(MirEvalError::TypeError("mismatched size")),
}))
};
}
macro_rules! not_supported {
($x: expr) => {
return Err(MirEvalError::NotSupported(format!($x)))
};
}
impl Evaluator<'_> {
pub(super) fn detect_and_exec_special_function(
&mut self,
def: FunctionId,
args: &[IntervalAndTy],
generic_args: &Substitution,
locals: &Locals<'_>,
destination: Interval,
span: MirSpan,
) -> Result<bool> {
let function_data = self.db.function_data(def);
let is_intrinsic = match &function_data.abi {
Some(abi) => *abi == Interned::new_str("rust-intrinsic"),
None => match def.lookup(self.db.upcast()).container {
hir_def::ItemContainerId::ExternBlockId(block) => {
let id = block.lookup(self.db.upcast()).id;
id.item_tree(self.db.upcast())[id.value].abi.as_deref()
== Some("rust-intrinsic")
}
_ => false,
},
};
if is_intrinsic {
self.exec_intrinsic(
function_data.name.as_text().unwrap_or_default().as_str(),
args,
generic_args,
destination,
&locals,
span,
)?;
return Ok(true);
}
let alloc_fn = function_data
.attrs
.iter()
.filter_map(|x| x.path().as_ident())
.filter_map(|x| x.as_str())
.find(|x| {
[
"rustc_allocator",
"rustc_deallocator",
"rustc_reallocator",
"rustc_allocator_zeroed",
]
.contains(x)
});
if let Some(alloc_fn) = alloc_fn {
self.exec_alloc_fn(alloc_fn, args, destination)?;
return Ok(true);
}
if let Some(x) = self.detect_lang_function(def) {
let arg_bytes =
args.iter().map(|x| Ok(x.get(&self)?.to_owned())).collect::<Result<Vec<_>>>()?;
let result = self.exec_lang_item(x, &arg_bytes)?;
destination.write_from_bytes(self, &result)?;
return Ok(true);
}
Ok(false)
}
fn exec_alloc_fn(
&mut self,
alloc_fn: &str,
args: &[IntervalAndTy],
destination: Interval,
) -> Result<()> {
match alloc_fn {
"rustc_allocator_zeroed" | "rustc_allocator" => {
let [size, align] = args else {
return Err(MirEvalError::TypeError("rustc_allocator args are not provided"));
};
let size = from_bytes!(usize, size.get(self)?);
let align = from_bytes!(usize, align.get(self)?);
let result = self.heap_allocate(size, align);
destination.write_from_bytes(self, &result.to_bytes())?;
}
"rustc_deallocator" => { /* no-op for now */ }
"rustc_reallocator" => {
let [ptr, old_size, align, new_size] = args else {
return Err(MirEvalError::TypeError("rustc_allocator args are not provided"));
};
let ptr = Address::from_bytes(ptr.get(self)?)?;
let old_size = from_bytes!(usize, old_size.get(self)?);
let new_size = from_bytes!(usize, new_size.get(self)?);
let align = from_bytes!(usize, align.get(self)?);
let result = self.heap_allocate(new_size, align);
Interval { addr: result, size: old_size }
.write_from_interval(self, Interval { addr: ptr, size: old_size })?;
destination.write_from_bytes(self, &result.to_bytes())?;
}
_ => not_supported!("unknown alloc function"),
}
Ok(())
}
fn detect_lang_function(&self, def: FunctionId) -> Option<LangItem> {
use LangItem::*;
let candidate = lang_attr(self.db.upcast(), def)?;
// We want to execute these functions with special logic
if [PanicFmt, BeginPanic, SliceLen].contains(&candidate) {
return Some(candidate);
}
None
}
fn exec_lang_item(&self, x: LangItem, args: &[Vec<u8>]) -> Result<Vec<u8>> {
use LangItem::*;
let mut args = args.iter();
match x {
// FIXME: we want to find the panic message from arguments, but it wouldn't work
// currently even if we do that, since macro expansion of panic related macros
// is dummy.
PanicFmt | BeginPanic => Err(MirEvalError::Panic("<format-args>".to_string())),
SliceLen => {
let arg = args
.next()
.ok_or(MirEvalError::TypeError("argument of <[T]>::len() is not provided"))?;
let ptr_size = arg.len() / 2;
Ok(arg[ptr_size..].into())
}
x => not_supported!("Executing lang item {x:?}"),
}
}
fn exec_intrinsic(
&mut self,
as_str: &str,
args: &[IntervalAndTy],
generic_args: &Substitution,
destination: Interval,
locals: &Locals<'_>,
span: MirSpan,
) -> Result<()> {
// We are a single threaded runtime with no UB checking and no optimization, so
// we can implement these as normal functions.
if let Some(name) = as_str.strip_prefix("atomic_") {
let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
return Err(MirEvalError::TypeError("atomic intrinsic generic arg is not provided"));
};
let Some(arg0) = args.get(0) else {
return Err(MirEvalError::TypeError("atomic intrinsic arg0 is not provided"));
};
let arg0_addr = Address::from_bytes(arg0.get(self)?)?;
let arg0_interval = Interval::new(
arg0_addr,
self.size_of_sized(ty, locals, "atomic intrinsic type arg")?,
);
if name.starts_with("load_") {
return destination.write_from_interval(self, arg0_interval);
}
let Some(arg1) = args.get(1) else {
return Err(MirEvalError::TypeError("atomic intrinsic arg1 is not provided"));
};
if name.starts_with("store_") {
return arg0_interval.write_from_interval(self, arg1.interval);
}
if name.starts_with("xchg_") {
destination.write_from_interval(self, arg0_interval)?;
return arg0_interval.write_from_interval(self, arg1.interval);
}
if name.starts_with("xadd_") {
destination.write_from_interval(self, arg0_interval)?;
let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false));
let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false));
let ans = lhs.wrapping_add(rhs);
return arg0_interval
.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]);
}
if name.starts_with("xsub_") {
destination.write_from_interval(self, arg0_interval)?;
let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false));
let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false));
let ans = lhs.wrapping_sub(rhs);
return arg0_interval
.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]);
}
if name.starts_with("and_") {
destination.write_from_interval(self, arg0_interval)?;
let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false));
let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false));
let ans = lhs & rhs;
return arg0_interval
.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]);
}
if name.starts_with("or_") {
destination.write_from_interval(self, arg0_interval)?;
let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false));
let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false));
let ans = lhs | rhs;
return arg0_interval
.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]);
}
if name.starts_with("xor_") {
destination.write_from_interval(self, arg0_interval)?;
let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false));
let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false));
let ans = lhs ^ rhs;
return arg0_interval
.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]);
}
if name.starts_with("nand_") {
destination.write_from_interval(self, arg0_interval)?;
let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false));
let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false));
let ans = !(lhs & rhs);
return arg0_interval
.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]);
}
let Some(arg2) = args.get(2) else {
return Err(MirEvalError::TypeError("atomic intrinsic arg2 is not provided"));
};
if name.starts_with("cxchg_") || name.starts_with("cxchgweak_") {
let dest = if arg1.get(self)? == arg0_interval.get(self)? {
arg0_interval.write_from_interval(self, arg2.interval)?;
(arg1.interval, true)
} else {
(arg0_interval, false)
};
let result_ty = TyKind::Tuple(
2,
Substitution::from_iter(Interner, [ty.clone(), TyBuilder::bool()]),
)
.intern(Interner);
let layout = self.layout(&result_ty)?;
let result = self.make_by_layout(
layout.size.bytes_usize(),
&layout,
None,
[
IntervalOrOwned::Borrowed(dest.0),
IntervalOrOwned::Owned(vec![u8::from(dest.1)]),
]
.into_iter(),
)?;
return destination.write_from_bytes(self, &result);
}
not_supported!("unknown atomic intrinsic {name}");
}
match as_str {
"size_of" => {
let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
return Err(MirEvalError::TypeError("size_of generic arg is not provided"));
};
let size = self.size_of_sized(ty, locals, "size_of arg")?;
destination.write_from_bytes(self, &size.to_le_bytes()[0..destination.size])
}
"min_align_of" | "pref_align_of" => {
let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
return Err(MirEvalError::TypeError("align_of generic arg is not provided"));
};
let align = self.layout_filled(ty, locals)?.align.abi.bytes();
destination.write_from_bytes(self, &align.to_le_bytes()[0..destination.size])
}
"needs_drop" => {
let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
return Err(MirEvalError::TypeError("size_of generic arg is not provided"));
};
let result = !ty.clone().is_copy(self.db, locals.body.owner);
destination.write_from_bytes(self, &[u8::from(result)])
}
"ptr_guaranteed_cmp" => {
// FIXME: this is wrong for const eval, it should return 2 in some
// cases.
let [lhs, rhs] = args else {
return Err(MirEvalError::TypeError("wrapping_add args are not provided"));
};
let ans = lhs.get(self)? == rhs.get(self)?;
destination.write_from_bytes(self, &[u8::from(ans)])
}
"wrapping_add" => {
let [lhs, rhs] = args else {
return Err(MirEvalError::TypeError("wrapping_add args are not provided"));
};
let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
let ans = lhs.wrapping_add(rhs);
destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
}
"add_with_overflow" => {
let [lhs, rhs] = args else {
return Err(MirEvalError::TypeError("const_eval_select args are not provided"));
};
let result_ty = TyKind::Tuple(
2,
Substitution::from_iter(Interner, [lhs.ty.clone(), TyBuilder::bool()]),
)
.intern(Interner);
let op_size =
self.size_of_sized(&lhs.ty, locals, "operand of add_with_overflow")?;
let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
let ans = lhs.wrapping_add(rhs);
let is_overflow = false;
let is_overflow = vec![u8::from(is_overflow)];
let layout = self.layout(&result_ty)?;
let result = self.make_by_layout(
layout.size.bytes_usize(),
&layout,
None,
[ans.to_le_bytes()[0..op_size].to_vec(), is_overflow]
.into_iter()
.map(IntervalOrOwned::Owned),
)?;
destination.write_from_bytes(self, &result)
}
"copy" | "copy_nonoverlapping" => {
let [src, dst, offset] = args else {
return Err(MirEvalError::TypeError("copy_nonoverlapping args are not provided"));
};
let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
return Err(MirEvalError::TypeError("copy_nonoverlapping generic arg is not provided"));
};
let src = Address::from_bytes(src.get(self)?)?;
let dst = Address::from_bytes(dst.get(self)?)?;
let offset = from_bytes!(usize, offset.get(self)?);
let size = self.size_of_sized(ty, locals, "copy_nonoverlapping ptr type")?;
let size = offset * size;
let src = Interval { addr: src, size };
let dst = Interval { addr: dst, size };
dst.write_from_interval(self, src)
}
"offset" | "arith_offset" => {
let [ptr, offset] = args else {
return Err(MirEvalError::TypeError("offset args are not provided"));
};
let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
return Err(MirEvalError::TypeError("offset generic arg is not provided"));
};
let ptr = u128::from_le_bytes(pad16(ptr.get(self)?, false));
let offset = u128::from_le_bytes(pad16(offset.get(self)?, false));
let size = self.size_of_sized(ty, locals, "offset ptr type")? as u128;
let ans = ptr + offset * size;
destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
}
"assert_inhabited" | "assert_zero_valid" | "assert_uninit_valid" | "assume" => {
// FIXME: We should actually implement these checks
Ok(())
}
"forget" => {
// We don't call any drop glue yet, so there is nothing here
Ok(())
}
"transmute" => {
let [arg] = args else {
return Err(MirEvalError::TypeError("trasmute arg is not provided"));
};
destination.write_from_interval(self, arg.interval)
}
"likely" | "unlikely" => {
let [arg] = args else {
return Err(MirEvalError::TypeError("likely arg is not provided"));
};
destination.write_from_interval(self, arg.interval)
}
"const_eval_select" => {
let [tuple, const_fn, _] = args else {
return Err(MirEvalError::TypeError("const_eval_select args are not provided"));
};
let mut args = vec![const_fn.clone()];
let TyKind::Tuple(_, fields) = tuple.ty.kind(Interner) else {
return Err(MirEvalError::TypeError("const_eval_select arg[0] is not a tuple"));
};
let layout = self.layout(&tuple.ty)?;
for (i, field) in fields.iter(Interner).enumerate() {
let field = field.assert_ty_ref(Interner).clone();
let offset = layout.fields.offset(i).bytes_usize();
let addr = tuple.interval.addr.offset(offset);
args.push(IntervalAndTy::new(addr, field, self, locals)?);
}
self.exec_fn_trait(&args, destination, locals, span)
}
_ => not_supported!("unknown intrinsic {as_str}"),
}
}
}

View File

@ -1,14 +1,15 @@
//! This module generates a polymorphic MIR from a hir body //! This module generates a polymorphic MIR from a hir body
use std::{iter, mem, sync::Arc}; use std::{fmt::Write, iter, mem, sync::Arc};
use base_db::FileId;
use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind}; use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
use hir_def::{ use hir_def::{
body::Body, body::Body,
data::adt::{StructKind, VariantData}, data::adt::{StructKind, VariantData},
hir::{ hir::{
Array, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, Pat, PatId, ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm,
RecordFieldPat, RecordLitField, Pat, PatId, RecordFieldPat, RecordLitField,
}, },
lang_item::{LangItem, LangItemTarget}, lang_item::{LangItem, LangItemTarget},
path::Path, path::Path,
@ -18,6 +19,7 @@ use hir_def::{
use hir_expand::name::Name; use hir_expand::name::Name;
use la_arena::ArenaMap; use la_arena::ArenaMap;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use syntax::TextRange;
use crate::{ use crate::{
consteval::ConstEvalError, consteval::ConstEvalError,
@ -27,8 +29,9 @@ use crate::{
inhabitedness::is_ty_uninhabited_from, inhabitedness::is_ty_uninhabited_from,
layout::{layout_of_ty, LayoutError}, layout::{layout_of_ty, LayoutError},
mapping::ToChalk, mapping::ToChalk,
method_resolution::lookup_impl_const,
static_lifetime, static_lifetime,
utils::generics, utils::{generics, ClosureSubst},
Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt, Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt,
}; };
@ -62,14 +65,14 @@ struct MirLowerCtx<'a> {
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum MirLowerError { pub enum MirLowerError {
ConstEvalError(Box<ConstEvalError>), ConstEvalError(String, Box<ConstEvalError>),
LayoutError(LayoutError), LayoutError(LayoutError),
IncompleteExpr, IncompleteExpr,
/// Trying to lower a trait function, instead of an implementation /// Trying to lower a trait function, instead of an implementation
TraitFunctionDefinition(TraitId, Name), TraitFunctionDefinition(TraitId, Name),
UnresolvedName(String), UnresolvedName(String),
RecordLiteralWithoutPath, RecordLiteralWithoutPath,
UnresolvedMethod, UnresolvedMethod(String),
UnresolvedField, UnresolvedField,
MissingFunctionDefinition, MissingFunctionDefinition,
TypeMismatch(TypeMismatch), TypeMismatch(TypeMismatch),
@ -88,6 +91,46 @@ pub enum MirLowerError {
UnaccessableLocal, UnaccessableLocal,
} }
impl MirLowerError {
pub fn pretty_print(
&self,
f: &mut String,
db: &dyn HirDatabase,
span_formatter: impl Fn(FileId, TextRange) -> String,
) -> std::result::Result<(), std::fmt::Error> {
match self {
MirLowerError::ConstEvalError(name, e) => {
writeln!(f, "In evaluating constant {name}")?;
match &**e {
ConstEvalError::MirLowerError(e) => e.pretty_print(f, db, span_formatter)?,
ConstEvalError::MirEvalError(e) => e.pretty_print(f, db, span_formatter)?,
}
}
MirLowerError::LayoutError(_)
| MirLowerError::IncompleteExpr
| MirLowerError::UnaccessableLocal
| MirLowerError::TraitFunctionDefinition(_, _)
| MirLowerError::UnresolvedName(_)
| MirLowerError::RecordLiteralWithoutPath
| MirLowerError::UnresolvedMethod(_)
| MirLowerError::UnresolvedField
| MirLowerError::MissingFunctionDefinition
| MirLowerError::TypeMismatch(_)
| MirLowerError::TypeError(_)
| MirLowerError::NotSupported(_)
| MirLowerError::ContinueWithoutLoop
| MirLowerError::BreakWithoutLoop
| MirLowerError::Loop
| MirLowerError::ImplementationError(_)
| MirLowerError::LangItemNotFound(_)
| MirLowerError::MutatingRvalue
| MirLowerError::UnresolvedLabel
| MirLowerError::UnresolvedUpvar(_) => writeln!(f, "{:?}", self)?,
}
Ok(())
}
}
macro_rules! not_supported { macro_rules! not_supported {
($x: expr) => { ($x: expr) => {
return Err(MirLowerError::NotSupported(format!($x))) return Err(MirLowerError::NotSupported(format!($x)))
@ -101,15 +144,6 @@ macro_rules! implementation_error {
}}; }};
} }
impl From<ConstEvalError> for MirLowerError {
fn from(value: ConstEvalError) -> Self {
match value {
ConstEvalError::MirLowerError(e) => e,
_ => MirLowerError::ConstEvalError(Box::new(value)),
}
}
}
impl From<LayoutError> for MirLowerError { impl From<LayoutError> for MirLowerError {
fn from(value: LayoutError) -> Self { fn from(value: LayoutError) -> Self {
MirLowerError::LayoutError(value) MirLowerError::LayoutError(value)
@ -177,7 +211,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
if !self.has_adjustments(expr_id) { if !self.has_adjustments(expr_id) {
match &self.body.exprs[expr_id] { match &self.body.exprs[expr_id] {
Expr::Literal(l) => { Expr::Literal(l) => {
let ty = self.expr_ty(expr_id); let ty = self.expr_ty_without_adjust(expr_id);
return Ok(Some((self.lower_literal_to_operand(ty, l)?, current))); return Ok(Some((self.lower_literal_to_operand(ty, l)?, current)));
} }
_ => (), _ => (),
@ -282,7 +316,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
{ {
match assoc { match assoc {
hir_def::AssocItemId::ConstId(c) => { hir_def::AssocItemId::ConstId(c) => {
self.lower_const(c, current, place, subst, expr_id.into())?; self.lower_const(c, current, place, subst, expr_id.into(), self.expr_ty_without_adjust(expr_id))?;
return Ok(Some(current)) return Ok(Some(current))
}, },
hir_def::AssocItemId::FunctionId(_) => { hir_def::AssocItemId::FunctionId(_) => {
@ -309,17 +343,20 @@ impl<'ctx> MirLowerCtx<'ctx> {
} }
}; };
match pr { match pr {
ValueNs::LocalBinding(pat_id) => { ValueNs::LocalBinding(_) | ValueNs::StaticId(_) => {
let Some((temp, current)) = self.lower_expr_as_place_without_adjust(current, expr_id, false)? else {
return Ok(None);
};
self.push_assignment( self.push_assignment(
current, current,
place, place,
Operand::Copy(self.binding_local(pat_id)?.into()).into(), Operand::Copy(temp).into(),
expr_id.into(), expr_id.into(),
); );
Ok(Some(current)) Ok(Some(current))
} }
ValueNs::ConstId(const_id) => { ValueNs::ConstId(const_id) => {
self.lower_const(const_id, current, place, Substitution::empty(Interner), expr_id.into())?; self.lower_const(const_id, current, place, Substitution::empty(Interner), expr_id.into(), self.expr_ty_without_adjust(expr_id))?;
Ok(Some(current)) Ok(Some(current))
} }
ValueNs::EnumVariantId(variant_id) => { ValueNs::EnumVariantId(variant_id) => {
@ -343,7 +380,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
not_supported!("owner without generic def id"); not_supported!("owner without generic def id");
}; };
let gen = generics(self.db.upcast(), def); let gen = generics(self.db.upcast(), def);
let ty = self.expr_ty(expr_id); let ty = self.expr_ty_without_adjust(expr_id);
self.push_assignment( self.push_assignment(
current, current,
place, place,
@ -388,12 +425,13 @@ impl<'ctx> MirLowerCtx<'ctx> {
}; };
self.set_terminator( self.set_terminator(
current, current,
Terminator::SwitchInt { TerminatorKind::SwitchInt {
discr, discr,
targets: SwitchTargets::static_if(1, start_of_then, start_of_else), targets: SwitchTargets::static_if(1, start_of_then, start_of_else),
}, },
expr_id.into(),
); );
Ok(self.merge_blocks(end_of_then, end_of_else)) Ok(self.merge_blocks(end_of_then, end_of_else, expr_id.into()))
} }
Expr::Let { pat, expr } => { Expr::Let { pat, expr } => {
let Some((cond_place, current)) = self.lower_expr_as_place(current, *expr, true)? else { let Some((cond_place, current)) = self.lower_expr_as_place(current, *expr, true)? else {
@ -423,32 +461,32 @@ impl<'ctx> MirLowerCtx<'ctx> {
MirSpan::Unknown, MirSpan::Unknown,
)?; )?;
} }
Ok(self.merge_blocks(Some(then_target), else_target)) Ok(self.merge_blocks(Some(then_target), else_target, expr_id.into()))
} }
Expr::Unsafe { id: _, statements, tail } => { Expr::Unsafe { id: _, statements, tail } => {
self.lower_block_to_place(statements, current, *tail, place) self.lower_block_to_place(statements, current, *tail, place, expr_id.into())
} }
Expr::Block { id: _, statements, tail, label } => { Expr::Block { id: _, statements, tail, label } => {
if let Some(label) = label { if let Some(label) = label {
self.lower_loop(current, place.clone(), Some(*label), |this, begin| { self.lower_loop(current, place.clone(), Some(*label), expr_id.into(), |this, begin| {
if let Some(block) = this.lower_block_to_place(statements, begin, *tail, place)? { if let Some(block) = this.lower_block_to_place(statements, begin, *tail, place, expr_id.into())? {
let end = this.current_loop_end()?; let end = this.current_loop_end()?;
this.set_goto(block, end); this.set_goto(block, end, expr_id.into());
} }
Ok(()) Ok(())
}) })
} else { } else {
self.lower_block_to_place(statements, current, *tail, place) self.lower_block_to_place(statements, current, *tail, place, expr_id.into())
} }
} }
Expr::Loop { body, label } => self.lower_loop(current, place, *label, |this, begin| { Expr::Loop { body, label } => self.lower_loop(current, place, *label, expr_id.into(), |this, begin| {
if let Some((_, block)) = this.lower_expr_as_place(begin, *body, true)? { if let Some((_, block)) = this.lower_expr_as_place(begin, *body, true)? {
this.set_goto(block, begin); this.set_goto(block, begin, expr_id.into());
} }
Ok(()) Ok(())
}), }),
Expr::While { condition, body, label } => { Expr::While { condition, body, label } => {
self.lower_loop(current, place, *label, |this, begin| { self.lower_loop(current, place, *label, expr_id.into(),|this, begin| {
let Some((discr, to_switch)) = this.lower_expr_to_some_operand(*condition, begin)? else { let Some((discr, to_switch)) = this.lower_expr_to_some_operand(*condition, begin)? else {
return Ok(()); return Ok(());
}; };
@ -456,13 +494,14 @@ impl<'ctx> MirLowerCtx<'ctx> {
let after_cond = this.new_basic_block(); let after_cond = this.new_basic_block();
this.set_terminator( this.set_terminator(
to_switch, to_switch,
Terminator::SwitchInt { TerminatorKind::SwitchInt {
discr, discr,
targets: SwitchTargets::static_if(1, after_cond, end), targets: SwitchTargets::static_if(1, after_cond, end),
}, },
expr_id.into(),
); );
if let Some((_, block)) = this.lower_expr_as_place(after_cond, *body, true)? { if let Some((_, block)) = this.lower_expr_as_place(after_cond, *body, true)? {
this.set_goto(block, begin); this.set_goto(block, begin, expr_id.into());
} }
Ok(()) Ok(())
}) })
@ -478,12 +517,12 @@ impl<'ctx> MirLowerCtx<'ctx> {
let into_iter_fn_op = Operand::const_zst( let into_iter_fn_op = Operand::const_zst(
TyKind::FnDef( TyKind::FnDef(
self.db.intern_callable_def(CallableDefId::FunctionId(into_iter_fn)).into(), self.db.intern_callable_def(CallableDefId::FunctionId(into_iter_fn)).into(),
Substitution::from1(Interner, self.expr_ty(iterable)) Substitution::from1(Interner, self.expr_ty_without_adjust(iterable))
).intern(Interner)); ).intern(Interner));
let iter_next_fn_op = Operand::const_zst( let iter_next_fn_op = Operand::const_zst(
TyKind::FnDef( TyKind::FnDef(
self.db.intern_callable_def(CallableDefId::FunctionId(iter_next_fn)).into(), self.db.intern_callable_def(CallableDefId::FunctionId(iter_next_fn)).into(),
Substitution::from1(Interner, self.expr_ty(iterable)) Substitution::from1(Interner, self.expr_ty_without_adjust(iterable))
).intern(Interner)); ).intern(Interner));
let &Some(iterator_ty) = &self.infer.type_of_for_iterator.get(&expr_id) else { let &Some(iterator_ty) = &self.infer.type_of_for_iterator.get(&expr_id) else {
return Err(MirLowerError::TypeError("unknown for loop iterator type")); return Err(MirLowerError::TypeError("unknown for loop iterator type"));
@ -494,13 +533,13 @@ impl<'ctx> MirLowerCtx<'ctx> {
let iterator_place: Place = self.temp(iterator_ty.clone())?.into(); let iterator_place: Place = self.temp(iterator_ty.clone())?.into();
let option_item_place: Place = self.temp(option_item_ty.clone())?.into(); let option_item_place: Place = self.temp(option_item_ty.clone())?.into();
let ref_mut_iterator_place: Place = self.temp(ref_mut_iterator_ty)?.into(); let ref_mut_iterator_place: Place = self.temp(ref_mut_iterator_ty)?.into();
let Some(current) = self.lower_call_and_args(into_iter_fn_op, Some(iterable).into_iter(), iterator_place.clone(), current, false)? let Some(current) = self.lower_call_and_args(into_iter_fn_op, Some(iterable).into_iter(), iterator_place.clone(), current, false, expr_id.into())?
else { else {
return Ok(None); return Ok(None);
}; };
self.push_assignment(current, ref_mut_iterator_place.clone(), Rvalue::Ref(BorrowKind::Mut { allow_two_phase_borrow: false }, iterator_place), expr_id.into()); self.push_assignment(current, ref_mut_iterator_place.clone(), Rvalue::Ref(BorrowKind::Mut { allow_two_phase_borrow: false }, iterator_place), expr_id.into());
self.lower_loop(current, place, label, |this, begin| { self.lower_loop(current, place, label, expr_id.into(), |this, begin| {
let Some(current) = this.lower_call(iter_next_fn_op, vec![Operand::Copy(ref_mut_iterator_place)], option_item_place.clone(), begin, false)? let Some(current) = this.lower_call(iter_next_fn_op, vec![Operand::Copy(ref_mut_iterator_place)], option_item_place.clone(), begin, false, expr_id.into())?
else { else {
return Ok(()); return Ok(());
}; };
@ -516,7 +555,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
AdtPatternShape::Tuple { args: &[pat], ellipsis: None }, AdtPatternShape::Tuple { args: &[pat], ellipsis: None },
)?; )?;
if let Some((_, block)) = this.lower_expr_as_place(current, body, true)? { if let Some((_, block)) = this.lower_expr_as_place(current, body, true)? {
this.set_goto(block, begin); this.set_goto(block, begin, expr_id.into());
} }
Ok(()) Ok(())
}) })
@ -536,39 +575,36 @@ impl<'ctx> MirLowerCtx<'ctx> {
place, place,
current, current,
self.is_uninhabited(expr_id), self.is_uninhabited(expr_id),
expr_id.into(),
); );
} }
let callee_ty = self.expr_ty_after_adjustments(*callee); let callee_ty = self.expr_ty_after_adjustments(*callee);
match &callee_ty.data(Interner).kind { match &callee_ty.data(Interner).kind {
chalk_ir::TyKind::FnDef(..) => { chalk_ir::TyKind::FnDef(..) => {
let func = Operand::from_bytes(vec![], callee_ty.clone()); let func = Operand::from_bytes(vec![], callee_ty.clone());
self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id)) self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id), expr_id.into())
} }
chalk_ir::TyKind::Function(_) => { chalk_ir::TyKind::Function(_) => {
let Some((func, current)) = self.lower_expr_to_some_operand(*callee, current)? else { let Some((func, current)) = self.lower_expr_to_some_operand(*callee, current)? else {
return Ok(None); return Ok(None);
}; };
self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id)) self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id), expr_id.into())
} }
TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition), TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition),
_ => return Err(MirLowerError::TypeError("function call on bad type")), _ => return Err(MirLowerError::TypeError("function call on bad type")),
} }
} }
Expr::MethodCall { receiver, args, .. } => { Expr::MethodCall { receiver, args, method_name, .. } => {
let (func_id, generic_args) = let (func_id, generic_args) =
self.infer.method_resolution(expr_id).ok_or(MirLowerError::UnresolvedMethod)?; self.infer.method_resolution(expr_id).ok_or_else(|| MirLowerError::UnresolvedMethod(format!("{}", method_name)))?;
let ty = chalk_ir::TyKind::FnDef( let func = Operand::from_fn(self.db, func_id, generic_args);
CallableDefId::FunctionId(func_id).to_chalk(self.db),
generic_args,
)
.intern(Interner);
let func = Operand::from_bytes(vec![], ty);
self.lower_call_and_args( self.lower_call_and_args(
func, func,
iter::once(*receiver).chain(args.iter().copied()), iter::once(*receiver).chain(args.iter().copied()),
place, place,
current, current,
self.is_uninhabited(expr_id), self.is_uninhabited(expr_id),
expr_id.into(),
) )
} }
Expr::Match { expr, arms } => { Expr::Match { expr, arms } => {
@ -591,7 +627,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
let next = self.new_basic_block(); let next = self.new_basic_block();
let o = otherwise.get_or_insert_with(|| self.new_basic_block()); let o = otherwise.get_or_insert_with(|| self.new_basic_block());
if let Some((discr, c)) = self.lower_expr_to_some_operand(guard, then)? { if let Some((discr, c)) = self.lower_expr_to_some_operand(guard, then)? {
self.set_terminator(c, Terminator::SwitchInt { discr, targets: SwitchTargets::static_if(1, next, *o) }); self.set_terminator(c, TerminatorKind::SwitchInt { discr, targets: SwitchTargets::static_if(1, next, *o) }, expr_id.into());
} }
next next
} else { } else {
@ -599,7 +635,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
}; };
if let Some(block) = self.lower_expr_to_place(*expr, place.clone(), then)? { if let Some(block) = self.lower_expr_to_place(*expr, place.clone(), then)? {
let r = end.get_or_insert_with(|| self.new_basic_block()); let r = end.get_or_insert_with(|| self.new_basic_block());
self.set_goto(block, *r); self.set_goto(block, *r, expr_id.into());
} }
match otherwise { match otherwise {
Some(o) => current = o, Some(o) => current = o,
@ -611,18 +647,17 @@ impl<'ctx> MirLowerCtx<'ctx> {
} }
} }
if self.is_unterminated(current) { if self.is_unterminated(current) {
self.set_terminator(current, Terminator::Unreachable); self.set_terminator(current, TerminatorKind::Unreachable, expr_id.into());
} }
Ok(end) Ok(end)
} }
Expr::Continue { label } => match label { Expr::Continue { label } => {
Some(_) => not_supported!("continue with label"), let loop_data = match label {
None => { Some(l) => self.labeled_loop_blocks.get(l).ok_or(MirLowerError::UnresolvedLabel)?,
let loop_data = None => self.current_loop_blocks.as_ref().ok_or(MirLowerError::ContinueWithoutLoop)?,
self.current_loop_blocks.as_ref().ok_or(MirLowerError::ContinueWithoutLoop)?; };
self.set_goto(current, loop_data.begin); self.set_goto(current, loop_data.begin, expr_id.into());
Ok(None) Ok(None)
}
}, },
&Expr::Break { expr, label } => { &Expr::Break { expr, label } => {
if let Some(expr) = expr { if let Some(expr) = expr {
@ -639,7 +674,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
Some(l) => self.labeled_loop_blocks.get(&l).ok_or(MirLowerError::UnresolvedLabel)?.end.expect("We always generate end for labeled loops"), Some(l) => self.labeled_loop_blocks.get(&l).ok_or(MirLowerError::UnresolvedLabel)?.end.expect("We always generate end for labeled loops"),
None => self.current_loop_end()?, None => self.current_loop_end()?,
}; };
self.set_goto(current, end); self.set_goto(current, end, expr_id.into());
Ok(None) Ok(None)
} }
Expr::Return { expr } => { Expr::Return { expr } => {
@ -650,7 +685,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
return Ok(None); return Ok(None);
} }
} }
self.set_terminator(current, Terminator::Return); self.set_terminator(current, TerminatorKind::Return, expr_id.into());
Ok(None) Ok(None)
} }
Expr::Yield { .. } => not_supported!("yield"), Expr::Yield { .. } => not_supported!("yield"),
@ -672,7 +707,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
Some(p) => MirLowerError::UnresolvedName(p.display(self.db).to_string()), Some(p) => MirLowerError::UnresolvedName(p.display(self.db).to_string()),
None => MirLowerError::RecordLiteralWithoutPath, None => MirLowerError::RecordLiteralWithoutPath,
})?; })?;
let subst = match self.expr_ty(expr_id).kind(Interner) { let subst = match self.expr_ty_without_adjust(expr_id).kind(Interner) {
TyKind::Adt(_, s) => s.clone(), TyKind::Adt(_, s) => s.clone(),
_ => not_supported!("Non ADT record literal"), _ => not_supported!("Non ADT record literal"),
}; };
@ -757,7 +792,17 @@ impl<'ctx> MirLowerCtx<'ctx> {
self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into()); self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into());
Ok(Some(current)) Ok(Some(current))
} }
Expr::Box { .. } => not_supported!("box expression"), Expr::Box { expr } => {
let ty = self.expr_ty_after_adjustments(*expr);
self.push_assignment(current, place.clone(), Rvalue::ShallowInitBoxWithAlloc(ty), expr_id.into());
let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)? else {
return Ok(None);
};
let mut p = place;
p.projection.push(ProjectionElem::Deref);
self.push_assignment(current, p, operand.into(), expr_id.into());
Ok(Some(current))
},
Expr::Field { .. } | Expr::Index { .. } | Expr::UnaryOp { op: hir_def::hir::UnaryOp::Deref, .. } => { Expr::Field { .. } | Expr::Index { .. } | Expr::UnaryOp { op: hir_def::hir::UnaryOp::Deref, .. } => {
let Some((p, current)) = self.lower_expr_as_place_without_adjust(current, expr_id, true)? else { let Some((p, current)) = self.lower_expr_as_place_without_adjust(current, expr_id, true)? else {
return Ok(None); return Ok(None);
@ -784,20 +829,63 @@ impl<'ctx> MirLowerCtx<'ctx> {
}, },
Expr::BinaryOp { lhs, rhs, op } => { Expr::BinaryOp { lhs, rhs, op } => {
let op = op.ok_or(MirLowerError::IncompleteExpr)?; let op = op.ok_or(MirLowerError::IncompleteExpr)?;
if let hir_def::hir::BinaryOp::Assignment { op } = op { let is_builtin = {
if op.is_some() { // Without adjust here is a hack. We assume that we know every possible adjustment
not_supported!("assignment with arith op (like +=)"); // for binary operator, and use without adjust to simplify our conditions.
let lhs_ty = self.expr_ty_without_adjust(*lhs);
let rhs_ty = self.expr_ty_without_adjust(*rhs);
let builtin_inequal_impls = matches!(
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)
};
if !is_builtin {
if let Some((func_id, generic_args)) = self.infer.method_resolution(expr_id) {
let func = Operand::from_fn(self.db, func_id, generic_args);
return self.lower_call_and_args(
func,
[*lhs, *rhs].into_iter(),
place,
current,
self.is_uninhabited(expr_id),
expr_id.into(),
);
} }
let Some((lhs_place, current)) = }
if let hir_def::hir::BinaryOp::Assignment { op } = op {
if let Some(op) = op {
// last adjustment is `&mut` which we don't want it.
let adjusts = self
.infer
.expr_adjustments
.get(lhs)
.and_then(|x| x.split_last())
.map(|x| x.1)
.ok_or(MirLowerError::TypeError("adjustment of binary op was missing"))?;
let Some((lhs_place, current)) =
self.lower_expr_as_place_with_adjust(current, *lhs, false, adjusts)?
else {
return Ok(None);
};
let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else {
return Ok(None);
};
let r_value = Rvalue::CheckedBinaryOp(op.into(), Operand::Copy(lhs_place.clone()), rhs_op);
self.push_assignment(current, lhs_place, r_value, expr_id.into());
return Ok(Some(current));
} else {
let Some((lhs_place, current)) =
self.lower_expr_as_place(current, *lhs, false)? self.lower_expr_as_place(current, *lhs, false)?
else { else {
return Ok(None); return Ok(None);
}; };
let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else { let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else {
return Ok(None); return Ok(None);
}; };
self.push_assignment(current, lhs_place, rhs_op.into(), expr_id.into()); self.push_assignment(current, lhs_place, rhs_op.into(), expr_id.into());
return Ok(Some(current)); return Ok(Some(current));
}
} }
let Some((lhs_op, current)) = self.lower_expr_to_some_operand(*lhs, current)? else { let Some((lhs_op, current)) = self.lower_expr_to_some_operand(*lhs, current)? else {
return Ok(None); return Ok(None);
@ -826,7 +914,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
Ok(Some(current)) Ok(Some(current))
} }
&Expr::Range { lhs, rhs, range_type: _ } => { &Expr::Range { lhs, rhs, range_type: _ } => {
let ty = self.expr_ty(expr_id); let ty = self.expr_ty_without_adjust(expr_id);
let Some((adt, subst)) = ty.as_adt() else { let Some((adt, subst)) = ty.as_adt() else {
return Err(MirLowerError::TypeError("Range type is not adt")); return Err(MirLowerError::TypeError("Range type is not adt"));
}; };
@ -869,7 +957,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
Ok(Some(current)) Ok(Some(current))
}, },
Expr::Closure { .. } => { Expr::Closure { .. } => {
let ty = self.expr_ty(expr_id); let ty = self.expr_ty_without_adjust(expr_id);
let TyKind::Closure(id, _) = ty.kind(Interner) else { let TyKind::Closure(id, _) = ty.kind(Interner) else {
not_supported!("closure with non closure type"); not_supported!("closure with non closure type");
}; };
@ -893,7 +981,12 @@ impl<'ctx> MirLowerCtx<'ctx> {
}; };
match &capture.kind { match &capture.kind {
CaptureKind::ByRef(bk) => { CaptureKind::ByRef(bk) => {
let tmp: Place = self.temp(capture.ty.clone())?.into(); let placeholder_subst = match self.owner.as_generic_def_id() {
Some(x) => TyBuilder::placeholder_subst(self.db, x),
None => Substitution::empty(Interner),
};
let tmp_ty = capture.ty.clone().substitute(Interner, &placeholder_subst);
let tmp: Place = self.temp(tmp_ty)?.into();
self.push_assignment( self.push_assignment(
current, current,
tmp.clone(), tmp.clone(),
@ -928,7 +1021,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
return Ok(None); return Ok(None);
}; };
let r = Rvalue::Aggregate( let r = Rvalue::Aggregate(
AggregateKind::Tuple(self.expr_ty(expr_id)), AggregateKind::Tuple(self.expr_ty_without_adjust(expr_id)),
values, values,
); );
self.push_assignment(current, place, r, expr_id.into()); self.push_assignment(current, place, r, expr_id.into());
@ -936,7 +1029,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
} }
Expr::Array(l) => match l { Expr::Array(l) => match l {
Array::ElementList { elements, .. } => { Array::ElementList { elements, .. } => {
let elem_ty = match &self.expr_ty(expr_id).data(Interner).kind { let elem_ty = match &self.expr_ty_without_adjust(expr_id).data(Interner).kind {
TyKind::Array(ty, _) => ty.clone(), TyKind::Array(ty, _) => ty.clone(),
_ => { _ => {
return Err(MirLowerError::TypeError( return Err(MirLowerError::TypeError(
@ -968,7 +1061,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
let Some((init, current)) = self.lower_expr_to_some_operand(*initializer, current)? else { let Some((init, current)) = self.lower_expr_to_some_operand(*initializer, current)? else {
return Ok(None); return Ok(None);
}; };
let len = match &self.expr_ty(expr_id).data(Interner).kind { let len = match &self.expr_ty_without_adjust(expr_id).data(Interner).kind {
TyKind::Array(_, len) => len.clone(), TyKind::Array(_, len) => len.clone(),
_ => { _ => {
return Err(MirLowerError::TypeError( return Err(MirLowerError::TypeError(
@ -982,7 +1075,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
}, },
}, },
Expr::Literal(l) => { Expr::Literal(l) => {
let ty = self.expr_ty(expr_id); let ty = self.expr_ty_without_adjust(expr_id);
let op = self.lower_literal_to_operand(ty, l)?; let op = self.lower_literal_to_operand(ty, l)?;
self.push_assignment(current, place, op.into(), expr_id.into()); self.push_assignment(current, place, op.into(), expr_id.into());
Ok(Some(current)) Ok(Some(current))
@ -1057,8 +1150,30 @@ impl<'ctx> MirLowerCtx<'ctx> {
place: Place, place: Place,
subst: Substitution, subst: Substitution,
span: MirSpan, span: MirSpan,
ty: Ty,
) -> Result<()> { ) -> Result<()> {
let c = self.db.const_eval(const_id, subst)?; let c = if subst.len(Interner) != 0 {
// We can't evaluate constant with substitution now, as generics are not monomorphized in lowering.
intern_const_scalar(ConstScalar::UnevaluatedConst(const_id, subst), ty)
} else {
let (const_id, subst) = lookup_impl_const(
self.db,
self.db.trait_environment_for_body(self.owner),
const_id,
subst,
);
let name = self
.db
.const_data(const_id)
.name
.as_ref()
.and_then(|x| x.as_str())
.unwrap_or("_")
.to_owned();
self.db
.const_eval(const_id.into(), subst)
.map_err(|e| MirLowerError::ConstEvalError(name, Box::new(e)))?
};
self.write_const_to_place(c, prev_block, place, span) self.write_const_to_place(c, prev_block, place, span)
} }
@ -1114,6 +1229,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
place: Place, place: Place,
mut current: BasicBlockId, mut current: BasicBlockId,
is_uninhabited: bool, is_uninhabited: bool,
span: MirSpan,
) -> Result<Option<BasicBlockId>> { ) -> Result<Option<BasicBlockId>> {
let Some(args) = args let Some(args) = args
.map(|arg| { .map(|arg| {
@ -1128,7 +1244,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
else { else {
return Ok(None); return Ok(None);
}; };
self.lower_call(func, args, place, current, is_uninhabited) self.lower_call(func, args, place, current, is_uninhabited, span)
} }
fn lower_call( fn lower_call(
@ -1138,11 +1254,12 @@ impl<'ctx> MirLowerCtx<'ctx> {
place: Place, place: Place,
current: BasicBlockId, current: BasicBlockId,
is_uninhabited: bool, is_uninhabited: bool,
span: MirSpan,
) -> Result<Option<BasicBlockId>> { ) -> Result<Option<BasicBlockId>> {
let b = if is_uninhabited { None } else { Some(self.new_basic_block()) }; let b = if is_uninhabited { None } else { Some(self.new_basic_block()) };
self.set_terminator( self.set_terminator(
current, current,
Terminator::Call { TerminatorKind::Call {
func, func,
args, args,
destination: place, destination: place,
@ -1150,6 +1267,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
cleanup: None, cleanup: None,
from_hir_call: true, from_hir_call: true,
}, },
span,
); );
Ok(b) Ok(b)
} }
@ -1158,15 +1276,15 @@ impl<'ctx> MirLowerCtx<'ctx> {
self.result.basic_blocks[source].terminator.is_none() self.result.basic_blocks[source].terminator.is_none()
} }
fn set_terminator(&mut self, source: BasicBlockId, terminator: Terminator) { fn set_terminator(&mut self, source: BasicBlockId, terminator: TerminatorKind, span: MirSpan) {
self.result.basic_blocks[source].terminator = Some(terminator); self.result.basic_blocks[source].terminator = Some(Terminator { span, kind: terminator });
} }
fn set_goto(&mut self, source: BasicBlockId, target: BasicBlockId) { fn set_goto(&mut self, source: BasicBlockId, target: BasicBlockId, span: MirSpan) {
self.set_terminator(source, Terminator::Goto { target }); self.set_terminator(source, TerminatorKind::Goto { target }, span);
} }
fn expr_ty(&self, e: ExprId) -> Ty { fn expr_ty_without_adjust(&self, e: ExprId) -> Ty {
self.infer[e].clone() self.infer[e].clone()
} }
@ -1177,7 +1295,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
ty = Some(x.target.clone()); ty = Some(x.target.clone());
} }
} }
ty.unwrap_or_else(|| self.expr_ty(e)) ty.unwrap_or_else(|| self.expr_ty_without_adjust(e))
} }
fn push_statement(&mut self, block: BasicBlockId, statement: Statement) { fn push_statement(&mut self, block: BasicBlockId, statement: Statement) {
@ -1211,6 +1329,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
prev_block: BasicBlockId, prev_block: BasicBlockId,
place: Place, place: Place,
label: Option<LabelId>, label: Option<LabelId>,
span: MirSpan,
f: impl FnOnce(&mut MirLowerCtx<'_>, BasicBlockId) -> Result<()>, f: impl FnOnce(&mut MirLowerCtx<'_>, BasicBlockId) -> Result<()>,
) -> Result<Option<BasicBlockId>> { ) -> Result<Option<BasicBlockId>> {
let begin = self.new_basic_block(); let begin = self.new_basic_block();
@ -1228,7 +1347,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
} else { } else {
None None
}; };
self.set_goto(prev_block, begin); self.set_goto(prev_block, begin, span);
f(self, begin)?; f(self, begin)?;
let my = mem::replace(&mut self.current_loop_blocks, prev).ok_or( let my = mem::replace(&mut self.current_loop_blocks, prev).ok_or(
MirLowerError::ImplementationError("current_loop_blocks is corrupt".to_string()), MirLowerError::ImplementationError("current_loop_blocks is corrupt".to_string()),
@ -1247,14 +1366,15 @@ impl<'ctx> MirLowerCtx<'ctx> {
&mut self, &mut self,
b1: Option<BasicBlockId>, b1: Option<BasicBlockId>,
b2: Option<BasicBlockId>, b2: Option<BasicBlockId>,
span: MirSpan,
) -> Option<BasicBlockId> { ) -> Option<BasicBlockId> {
match (b1, b2) { match (b1, b2) {
(None, None) => None, (None, None) => None,
(None, Some(b)) | (Some(b), None) => Some(b), (None, Some(b)) | (Some(b), None) => Some(b),
(Some(b1), Some(b2)) => { (Some(b1), Some(b2)) => {
let bm = self.new_basic_block(); let bm = self.new_basic_block();
self.set_goto(b1, bm); self.set_goto(b1, bm, span);
self.set_goto(b2, bm); self.set_goto(b2, bm, span);
Some(bm) Some(bm)
} }
} }
@ -1332,6 +1452,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
mut current: BasicBlockId, mut current: BasicBlockId,
tail: Option<ExprId>, tail: Option<ExprId>,
place: Place, place: Place,
span: MirSpan,
) -> Result<Option<Idx<BasicBlock>>> { ) -> Result<Option<Idx<BasicBlock>>> {
for statement in statements.iter() { for statement in statements.iter() {
match statement { match statement {
@ -1355,13 +1476,13 @@ impl<'ctx> MirLowerCtx<'ctx> {
match (else_block, else_branch) { match (else_block, else_branch) {
(None, _) => (), (None, _) => (),
(Some(else_block), None) => { (Some(else_block), None) => {
self.set_terminator(else_block, Terminator::Unreachable); self.set_terminator(else_block, TerminatorKind::Unreachable, span);
} }
(Some(else_block), Some(else_branch)) => { (Some(else_block), Some(else_branch)) => {
if let Some((_, b)) = if let Some((_, b)) =
self.lower_expr_as_place(else_block, *else_branch, true)? self.lower_expr_as_place(else_block, *else_branch, true)?
{ {
self.set_terminator(b, Terminator::Unreachable); self.set_terminator(b, TerminatorKind::Unreachable, span);
} }
} }
} }
@ -1438,7 +1559,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
BindingAnnotation::Unannotated, BindingAnnotation::Unannotated,
)?; )?;
if let Some(b) = r.1 { if let Some(b) = r.1 {
self.set_terminator(b, Terminator::Unreachable); self.set_terminator(b, TerminatorKind::Unreachable, param.into());
} }
current = r.0; current = r.0;
} }
@ -1456,6 +1577,18 @@ impl<'ctx> MirLowerCtx<'ctx> {
} }
} }
} }
fn const_eval_discriminant(&self, variant: EnumVariantId) -> Result<i128> {
let r = self.db.const_eval_discriminant(variant);
match r {
Ok(r) => Ok(r),
Err(e) => {
let data = self.db.enum_data(variant.parent);
let name = format!("{}::{}", data.name, data.variants[variant.local_id].name);
Err(MirLowerError::ConstEvalError(name, Box::new(e)))
}
}
}
} }
fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> { fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
@ -1498,7 +1631,7 @@ pub fn mir_body_for_closure_query(
// 0 is return local // 0 is return local
ctx.result.locals.alloc(Local { ty: infer[*root].clone() }); ctx.result.locals.alloc(Local { ty: infer[*root].clone() });
ctx.result.locals.alloc(Local { ty: infer[expr].clone() }); ctx.result.locals.alloc(Local { ty: infer[expr].clone() });
let Some(sig) = substs.at(Interner, 0).assert_ty_ref(Interner).callable_sig(db) else { let Some(sig) = ClosureSubst(substs).sig_ty().callable_sig(db) else {
implementation_error!("closure has not callable sig"); implementation_error!("closure has not callable sig");
}; };
let current = ctx.lower_params_and_bindings( let current = ctx.lower_params_and_bindings(
@ -1506,7 +1639,7 @@ pub fn mir_body_for_closure_query(
|_| true, |_| true,
)?; )?;
if let Some(b) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? { if let Some(b) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? {
ctx.set_terminator(b, Terminator::Return); ctx.set_terminator(b, TerminatorKind::Return, (*root).into());
} }
let mut upvar_map: FxHashMap<LocalId, Vec<(&CapturedItem, usize)>> = FxHashMap::default(); let mut upvar_map: FxHashMap<LocalId, Vec<(&CapturedItem, usize)>> = FxHashMap::default();
for (i, capture) in captures.iter().enumerate() { for (i, capture) in captures.iter().enumerate() {
@ -1628,7 +1761,7 @@ pub fn lower_to_mir(
ctx.lower_params_and_bindings([].into_iter(), binding_picker)? ctx.lower_params_and_bindings([].into_iter(), binding_picker)?
}; };
if let Some(b) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? { if let Some(b) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? {
ctx.set_terminator(b, Terminator::Return); ctx.set_terminator(b, TerminatorKind::Return, root_expr.into());
} }
Ok(ctx.result) Ok(ctx.result)
} }

View File

@ -1,7 +1,7 @@
//! MIR lowering for places //! MIR lowering for places
use super::*; use super::*;
use hir_def::FunctionId; use hir_def::{lang_item::lang_attr, FunctionId};
use hir_expand::name; use hir_expand::name;
macro_rules! not_supported { macro_rules! not_supported {
@ -16,7 +16,7 @@ impl MirLowerCtx<'_> {
expr_id: ExprId, expr_id: ExprId,
prev_block: BasicBlockId, prev_block: BasicBlockId,
) -> Result<Option<(Place, BasicBlockId)>> { ) -> Result<Option<(Place, BasicBlockId)>> {
let ty = self.expr_ty(expr_id); let ty = self.expr_ty_without_adjust(expr_id);
let place = self.temp(ty)?; let place = self.temp(ty)?;
let Some(current) = self.lower_expr_to_place_without_adjust(expr_id, place.into(), prev_block)? else { let Some(current) = self.lower_expr_to_place_without_adjust(expr_id, place.into(), prev_block)? else {
return Ok(None); return Ok(None);
@ -30,8 +30,10 @@ impl MirLowerCtx<'_> {
prev_block: BasicBlockId, prev_block: BasicBlockId,
adjustments: &[Adjustment], adjustments: &[Adjustment],
) -> Result<Option<(Place, BasicBlockId)>> { ) -> Result<Option<(Place, BasicBlockId)>> {
let ty = let ty = adjustments
adjustments.last().map(|x| x.target.clone()).unwrap_or_else(|| self.expr_ty(expr_id)); .last()
.map(|x| x.target.clone())
.unwrap_or_else(|| self.expr_ty_without_adjust(expr_id));
let place = self.temp(ty)?; let place = self.temp(ty)?;
let Some(current) = self.lower_expr_to_place_with_adjust(expr_id, place.into(), prev_block, adjustments)? else { let Some(current) = self.lower_expr_to_place_with_adjust(expr_id, place.into(), prev_block, adjustments)? else {
return Ok(None); return Ok(None);
@ -80,7 +82,7 @@ impl MirLowerCtx<'_> {
r, r,
rest.last() rest.last()
.map(|x| x.target.clone()) .map(|x| x.target.clone())
.unwrap_or_else(|| self.expr_ty(expr_id)), .unwrap_or_else(|| self.expr_ty_without_adjust(expr_id)),
last.target.clone(), last.target.clone(),
expr_id.into(), expr_id.into(),
match od.0 { match od.0 {
@ -135,17 +137,39 @@ impl MirLowerCtx<'_> {
}; };
match pr { match pr {
ValueNs::LocalBinding(pat_id) => { ValueNs::LocalBinding(pat_id) => {
Ok(Some((self.result.binding_locals[pat_id].into(), current))) Ok(Some((self.binding_local(pat_id)?.into(), current)))
}
ValueNs::StaticId(s) => {
let ty = self.expr_ty_without_adjust(expr_id);
let ref_ty =
TyKind::Ref(Mutability::Not, static_lifetime(), ty).intern(Interner);
let mut temp: Place = self.temp(ref_ty)?.into();
self.push_assignment(
current,
temp.clone(),
Operand::Static(s).into(),
expr_id.into(),
);
temp.projection.push(ProjectionElem::Deref);
Ok(Some((temp, current)))
} }
_ => try_rvalue(self), _ => try_rvalue(self),
} }
} }
Expr::UnaryOp { expr, op } => match op { Expr::UnaryOp { expr, op } => match op {
hir_def::hir::UnaryOp::Deref => { hir_def::hir::UnaryOp::Deref => {
if !matches!( let is_builtin = match self.expr_ty_without_adjust(*expr).kind(Interner) {
self.expr_ty(*expr).kind(Interner), TyKind::Ref(..) | TyKind::Raw(..) => true,
TyKind::Ref(..) | TyKind::Raw(..) TyKind::Adt(id, _) => {
) { if let Some(lang_item) = lang_attr(self.db.upcast(), id.0) {
lang_item == LangItem::OwnedBox
} else {
false
}
}
_ => false,
};
if !is_builtin {
let Some((p, current)) = self.lower_expr_as_place(current, *expr, true)? else { let Some((p, current)) = self.lower_expr_as_place(current, *expr, true)? else {
return Ok(None); return Ok(None);
}; };
@ -153,7 +177,7 @@ impl MirLowerCtx<'_> {
current, current,
p, p,
self.expr_ty_after_adjustments(*expr), self.expr_ty_after_adjustments(*expr),
self.expr_ty(expr_id), self.expr_ty_without_adjust(expr_id),
expr_id.into(), expr_id.into(),
'b: { 'b: {
if let Some((f, _)) = self.infer.method_resolution(expr_id) { if let Some((f, _)) = self.infer.method_resolution(expr_id) {
@ -198,7 +222,7 @@ impl MirLowerCtx<'_> {
) )
{ {
let Some(index_fn) = self.infer.method_resolution(expr_id) else { let Some(index_fn) = self.infer.method_resolution(expr_id) else {
return Err(MirLowerError::UnresolvedMethod); return Err(MirLowerError::UnresolvedMethod("[overloaded index]".to_string()));
}; };
let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else { let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else {
return Ok(None); return Ok(None);
@ -210,7 +234,7 @@ impl MirLowerCtx<'_> {
current, current,
base_place, base_place,
base_ty, base_ty,
self.expr_ty(expr_id), self.expr_ty_without_adjust(expr_id),
index_operand, index_operand,
expr_id.into(), expr_id.into(),
index_fn, index_fn,
@ -266,7 +290,7 @@ impl MirLowerCtx<'_> {
) )
.intern(Interner), .intern(Interner),
); );
let Some(current) = self.lower_call(index_fn_op, vec![Operand::Copy(ref_place), index_operand], result.clone(), current, false)? else { let Some(current) = self.lower_call(index_fn_op, vec![Operand::Copy(ref_place), index_operand], result.clone(), current, false, span)? else {
return Ok(None); return Ok(None);
}; };
result.projection.push(ProjectionElem::Deref); result.projection.push(ProjectionElem::Deref);
@ -313,7 +337,7 @@ impl MirLowerCtx<'_> {
.intern(Interner), .intern(Interner),
); );
let mut result: Place = self.temp(target_ty_ref)?.into(); let mut result: Place = self.temp(target_ty_ref)?.into();
let Some(current) = self.lower_call(deref_fn_op, vec![Operand::Copy(ref_place)], result.clone(), current, false)? else { let Some(current) = self.lower_call(deref_fn_op, vec![Operand::Copy(ref_place)], result.clone(), current, false, span)? else {
return Ok(None); return Ok(None);
}; };
result.projection.push(ProjectionElem::Deref); result.projection.push(ProjectionElem::Deref);

View File

@ -1,5 +1,7 @@
//! MIR lowering for patterns //! MIR lowering for patterns
use hir_def::resolver::HasResolver;
use crate::utils::pattern_matching_dereference_count; use crate::utils::pattern_matching_dereference_count;
use super::*; use super::*;
@ -72,7 +74,7 @@ impl MirLowerCtx<'_> {
*pat, *pat,
binding_mode, binding_mode,
)?; )?;
self.set_goto(next, then_target); self.set_goto(next, then_target, pattern.into());
match next_else { match next_else {
Some(t) => { Some(t) => {
current = t; current = t;
@ -85,13 +87,13 @@ impl MirLowerCtx<'_> {
} }
if !finished { if !finished {
let ce = *current_else.get_or_insert_with(|| self.new_basic_block()); let ce = *current_else.get_or_insert_with(|| self.new_basic_block());
self.set_goto(current, ce); self.set_goto(current, ce, pattern.into());
} }
(then_target, current_else) (then_target, current_else)
} }
Pat::Record { args, .. } => { Pat::Record { args, .. } => {
let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else { let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
not_supported!("unresolved variant"); not_supported!("unresolved variant for record");
}; };
self.pattern_matching_variant( self.pattern_matching_variant(
cond_ty, cond_ty,
@ -106,11 +108,8 @@ impl MirLowerCtx<'_> {
} }
Pat::Range { .. } => not_supported!("range pattern"), Pat::Range { .. } => not_supported!("range pattern"),
Pat::Slice { .. } => not_supported!("slice pattern"), Pat::Slice { .. } => not_supported!("slice pattern"),
Pat::Path(_) => { Pat::Path(p) => match self.infer.variant_resolution_for_pat(pattern) {
let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else { Some(variant) => self.pattern_matching_variant(
not_supported!("unresolved variant");
};
self.pattern_matching_variant(
cond_ty, cond_ty,
binding_mode, binding_mode,
cond_place, cond_place,
@ -119,8 +118,60 @@ impl MirLowerCtx<'_> {
pattern.into(), pattern.into(),
current_else, current_else,
AdtPatternShape::Unit, AdtPatternShape::Unit,
)? )?,
} None => {
let unresolved_name = || MirLowerError::unresolved_path(self.db, p);
let resolver = self.owner.resolver(self.db.upcast());
let pr = resolver
.resolve_path_in_value_ns(self.db.upcast(), p)
.ok_or_else(unresolved_name)?;
match pr {
ResolveValueResult::ValueNs(v) => match v {
ValueNs::ConstId(c) => {
let tmp: Place = self.temp(cond_ty.clone())?.into();
let span = pattern.into();
self.lower_const(
c,
current,
tmp.clone(),
Substitution::empty(Interner),
span,
cond_ty.clone(),
)?;
let tmp2: Place = self.temp(TyBuilder::bool())?.into();
self.push_assignment(
current,
tmp2.clone(),
Rvalue::CheckedBinaryOp(
BinOp::Eq,
Operand::Copy(tmp),
Operand::Copy(cond_place),
),
span,
);
let next = self.new_basic_block();
let else_target =
current_else.unwrap_or_else(|| self.new_basic_block());
self.set_terminator(
current,
TerminatorKind::SwitchInt {
discr: Operand::Copy(tmp2),
targets: SwitchTargets::static_if(1, next, else_target),
},
span,
);
(next, Some(else_target))
}
_ => not_supported!(
"path in pattern position that is not const or variant"
),
},
ResolveValueResult::Partial(_, _) => {
not_supported!("assoc const in patterns")
}
}
}
},
Pat::Lit(l) => match &self.body.exprs[*l] { Pat::Lit(l) => match &self.body.exprs[*l] {
Expr::Literal(l) => { Expr::Literal(l) => {
let c = self.lower_literal_to_operand(cond_ty, l)?; let c = self.lower_literal_to_operand(cond_ty, l)?;
@ -218,10 +269,11 @@ impl MirLowerCtx<'_> {
let discr = Operand::Copy(discr); let discr = Operand::Copy(discr);
self.set_terminator( self.set_terminator(
current, current,
Terminator::SwitchInt { TerminatorKind::SwitchInt {
discr, discr,
targets: SwitchTargets::static_if(1, then_target, else_target), targets: SwitchTargets::static_if(1, then_target, else_target),
}, },
pattern.into(),
); );
Ok((then_target, Some(else_target))) Ok((then_target, Some(else_target)))
} }
@ -244,8 +296,7 @@ impl MirLowerCtx<'_> {
}; };
Ok(match variant { Ok(match variant {
VariantId::EnumVariantId(v) => { VariantId::EnumVariantId(v) => {
let e = self.db.const_eval_discriminant(v)? as u128; let e = self.const_eval_discriminant(v)? as u128;
let next = self.new_basic_block();
let tmp = self.discr_temp_place(); let tmp = self.discr_temp_place();
self.push_assignment( self.push_assignment(
current, current,
@ -253,13 +304,15 @@ impl MirLowerCtx<'_> {
Rvalue::Discriminant(cond_place.clone()), Rvalue::Discriminant(cond_place.clone()),
span, span,
); );
let next = self.new_basic_block();
let else_target = current_else.unwrap_or_else(|| self.new_basic_block()); let else_target = current_else.unwrap_or_else(|| self.new_basic_block());
self.set_terminator( self.set_terminator(
current, current,
Terminator::SwitchInt { TerminatorKind::SwitchInt {
discr: Operand::Copy(tmp), discr: Operand::Copy(tmp),
targets: SwitchTargets::static_if(e, next, else_target), targets: SwitchTargets::static_if(e, next, else_target),
}, },
span,
); );
let enum_data = self.db.enum_data(v.parent); let enum_data = self.db.enum_data(v.parent);
self.pattern_matching_variant_fields( self.pattern_matching_variant_fields(

View File

@ -11,19 +11,52 @@ use la_arena::ArenaMap;
use crate::{ use crate::{
db::HirDatabase, db::HirDatabase,
display::HirDisplay, display::{ClosureStyle, HirDisplay},
mir::{PlaceElem, ProjectionElem, StatementKind, Terminator}, mir::{PlaceElem, ProjectionElem, StatementKind, TerminatorKind},
ClosureId,
}; };
use super::{ use super::{
AggregateKind, BasicBlockId, BorrowKind, LocalId, MirBody, Operand, Place, Rvalue, UnOp, AggregateKind, BasicBlockId, BorrowKind, LocalId, MirBody, Operand, Place, Rvalue, UnOp,
}; };
macro_rules! w {
($dst:expr, $($arg:tt)*) => {
{ let _ = write!($dst, $($arg)*); }
};
}
macro_rules! wln {
($dst:expr) => {
{ let _ = writeln!($dst); }
};
($dst:expr, $($arg:tt)*) => {
{ let _ = writeln!($dst, $($arg)*); }
};
}
impl MirBody { impl MirBody {
pub fn pretty_print(&self, db: &dyn HirDatabase) -> String { pub fn pretty_print(&self, db: &dyn HirDatabase) -> String {
let hir_body = db.body(self.owner); let hir_body = db.body(self.owner);
let mut ctx = MirPrettyCtx::new(self, &hir_body, db); let mut ctx = MirPrettyCtx::new(self, &hir_body, db);
ctx.for_body(ctx.body.owner); ctx.for_body(|this| match ctx.body.owner {
hir_def::DefWithBodyId::FunctionId(id) => {
let data = db.function_data(id);
w!(this, "fn {}() ", data.name);
}
hir_def::DefWithBodyId::StaticId(id) => {
let data = db.static_data(id);
w!(this, "static {}: _ = ", data.name);
}
hir_def::DefWithBodyId::ConstId(id) => {
let data = db.const_data(id);
w!(this, "const {}: _ = ", data.name.as_ref().unwrap_or(&Name::missing()));
}
hir_def::DefWithBodyId::VariantId(id) => {
let data = db.enum_data(id.parent);
w!(this, "enum {} = ", data.name);
}
});
ctx.result ctx.result
} }
@ -49,21 +82,6 @@ struct MirPrettyCtx<'a> {
local_to_binding: ArenaMap<LocalId, BindingId>, local_to_binding: ArenaMap<LocalId, BindingId>,
} }
macro_rules! w {
($dst:expr, $($arg:tt)*) => {
{ let _ = write!($dst, $($arg)*); }
};
}
macro_rules! wln {
($dst:expr) => {
{ let _ = writeln!($dst); }
};
($dst:expr, $($arg:tt)*) => {
{ let _ = writeln!($dst, $($arg)*); }
};
}
impl Write for MirPrettyCtx<'_> { impl Write for MirPrettyCtx<'_> {
fn write_str(&mut self, s: &str) -> std::fmt::Result { fn write_str(&mut self, s: &str) -> std::fmt::Result {
let mut it = s.split('\n'); // note: `.lines()` is wrong here let mut it = s.split('\n'); // note: `.lines()` is wrong here
@ -91,36 +109,40 @@ impl Display for LocalName {
} }
impl<'a> MirPrettyCtx<'a> { impl<'a> MirPrettyCtx<'a> {
fn for_body(&mut self, name: impl Debug) { fn for_body(&mut self, name: impl FnOnce(&mut MirPrettyCtx<'_>)) {
wln!(self, "// {:?}", name); name(self);
self.with_block(|this| { self.with_block(|this| {
this.locals(); this.locals();
wln!(this); wln!(this);
this.blocks(); this.blocks();
}); });
for &closure in &self.body.closures { for &closure in &self.body.closures {
let body = match self.db.mir_body_for_closure(closure) { self.for_closure(closure);
Ok(x) => x,
Err(e) => {
wln!(self, "// error in {closure:?}: {e:?}");
continue;
}
};
let result = mem::take(&mut self.result);
let indent = mem::take(&mut self.indent);
let mut ctx = MirPrettyCtx {
body: &body,
local_to_binding: body.binding_locals.iter().map(|(x, y)| (*y, x)).collect(),
result,
indent,
..*self
};
ctx.for_body(closure);
self.result = ctx.result;
self.indent = ctx.indent;
} }
} }
fn for_closure(&mut self, closure: ClosureId) {
let body = match self.db.mir_body_for_closure(closure) {
Ok(x) => x,
Err(e) => {
wln!(self, "// error in {closure:?}: {e:?}");
return;
}
};
let result = mem::take(&mut self.result);
let indent = mem::take(&mut self.indent);
let mut ctx = MirPrettyCtx {
body: &body,
local_to_binding: body.binding_locals.iter().map(|(x, y)| (*y, x)).collect(),
result,
indent,
..*self
};
ctx.for_body(|this| wln!(this, "// Closure: {:?}", closure));
self.result = ctx.result;
self.indent = ctx.indent;
}
fn with_block(&mut self, f: impl FnOnce(&mut MirPrettyCtx<'_>)) { fn with_block(&mut self, f: impl FnOnce(&mut MirPrettyCtx<'_>)) {
self.indent += " "; self.indent += " ";
wln!(self, "{{"); wln!(self, "{{");
@ -155,7 +177,7 @@ impl<'a> MirPrettyCtx<'a> {
fn locals(&mut self) { fn locals(&mut self) {
for (id, local) in self.body.locals.iter() { for (id, local) in self.body.locals.iter() {
wln!(self, "let {}: {};", self.local_name(id), local.ty.display(self.db)); wln!(self, "let {}: {};", self.local_name(id), self.hir_display(&local.ty));
} }
} }
@ -198,11 +220,11 @@ impl<'a> MirPrettyCtx<'a> {
} }
} }
match &block.terminator { match &block.terminator {
Some(terminator) => match terminator { Some(terminator) => match &terminator.kind {
Terminator::Goto { target } => { TerminatorKind::Goto { target } => {
wln!(this, "goto 'bb{};", u32::from(target.into_raw())) wln!(this, "goto 'bb{};", u32::from(target.into_raw()))
} }
Terminator::SwitchInt { discr, targets } => { TerminatorKind::SwitchInt { discr, targets } => {
w!(this, "switch "); w!(this, "switch ");
this.operand(discr); this.operand(discr);
w!(this, " "); w!(this, " ");
@ -213,7 +235,7 @@ impl<'a> MirPrettyCtx<'a> {
wln!(this, "_ => {},", this.basic_block_id(targets.otherwise())); wln!(this, "_ => {},", this.basic_block_id(targets.otherwise()));
}); });
} }
Terminator::Call { func, args, destination, target, .. } => { TerminatorKind::Call { func, args, destination, target, .. } => {
w!(this, "Call "); w!(this, "Call ");
this.with_block(|this| { this.with_block(|this| {
w!(this, "func: "); w!(this, "func: ");
@ -295,7 +317,8 @@ impl<'a> MirPrettyCtx<'a> {
// equally. Feel free to change it. // equally. Feel free to change it.
self.place(p); self.place(p);
} }
Operand::Constant(c) => w!(self, "Const({})", c.display(self.db)), Operand::Constant(c) => w!(self, "Const({})", self.hir_display(c)),
Operand::Static(s) => w!(self, "Static({:?})", s),
} }
} }
@ -349,7 +372,7 @@ impl<'a> MirPrettyCtx<'a> {
Rvalue::Cast(ck, op, ty) => { Rvalue::Cast(ck, op, ty) => {
w!(self, "Cast({ck:?}, "); w!(self, "Cast({ck:?}, ");
self.operand(op); self.operand(op);
w!(self, ", {})", ty.display(self.db)); w!(self, ", {})", self.hir_display(ty));
} }
Rvalue::CheckedBinaryOp(b, o1, o2) => { Rvalue::CheckedBinaryOp(b, o1, o2) => {
self.operand(o1); self.operand(o1);
@ -369,6 +392,7 @@ impl<'a> MirPrettyCtx<'a> {
self.place(p); self.place(p);
w!(self, ")"); w!(self, ")");
} }
Rvalue::ShallowInitBoxWithAlloc(_) => w!(self, "ShallowInitBoxWithAlloc"),
Rvalue::ShallowInitBox(op, _) => { Rvalue::ShallowInitBox(op, _) => {
w!(self, "ShallowInitBox("); w!(self, "ShallowInitBox(");
self.operand(op); self.operand(op);
@ -392,4 +416,8 @@ impl<'a> MirPrettyCtx<'a> {
} }
} }
} }
fn hir_display<T: HirDisplay>(&self, ty: &'a T) -> impl Display + 'a {
ty.display(self.db).with_closure_style(ClosureStyle::ClosureWithId)
}
} }

View File

@ -23,10 +23,11 @@ use hir_expand::name::Name;
use intern::Interned; use intern::Interned;
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use stdx::never;
use crate::{ use crate::{
db::HirDatabase, ChalkTraitId, Interner, Substitution, TraitRef, TraitRefExt, Ty, TyExt, db::HirDatabase, ChalkTraitId, GenericArg, Interner, Substitution, TraitRef, TraitRefExt, Ty,
WhereClause, TyExt, WhereClause,
}; };
pub(crate) fn fn_traits( pub(crate) fn fn_traits(
@ -176,6 +177,37 @@ pub(crate) fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics {
Generics { def, params: db.generic_params(def), parent_generics } Generics { def, params: db.generic_params(def), parent_generics }
} }
/// It is a bit different from the rustc equivalent. Currently it stores:
/// - 0: the function signature, encoded as a function pointer type
/// - 1..n: generics of the parent
///
/// and it doesn't store the closure types and fields.
///
/// Codes should not assume this ordering, and should always use methods available
/// on this struct for retriving, and `TyBuilder::substs_for_closure` for creating.
pub(crate) struct ClosureSubst<'a>(pub(crate) &'a Substitution);
impl<'a> ClosureSubst<'a> {
pub(crate) fn parent_subst(&self) -> &'a [GenericArg] {
match self.0.as_slice(Interner) {
[_, x @ ..] => x,
_ => {
never!("Closure missing parameter");
&[]
}
}
}
pub(crate) fn sig_ty(&self) -> &'a Ty {
match self.0.as_slice(Interner) {
[x, ..] => x.assert_ty_ref(Interner),
_ => {
unreachable!("Closure missing sig_ty parameter");
}
}
}
}
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct Generics { pub(crate) struct Generics {
def: GenericDefId, def: GenericDefId,

View File

@ -1856,11 +1856,21 @@ impl Function {
def_map.fn_as_proc_macro(self.id).map(|id| Macro { id: id.into() }) def_map.fn_as_proc_macro(self.id).map(|id| Macro { id: id.into() })
} }
pub fn eval(self, db: &dyn HirDatabase) -> Result<(), MirEvalError> { pub fn eval(
self,
db: &dyn HirDatabase,
span_formatter: impl Fn(FileId, TextRange) -> String,
) -> Result<(), String> {
let converter = |e: MirEvalError| {
let mut r = String::new();
_ = e.pretty_print(&mut r, db, &span_formatter);
r
};
let body = db let body = db
.mir_body(self.id.into()) .mir_body(self.id.into())
.map_err(|e| MirEvalError::MirLowerError(self.id.into(), e))?; .map_err(|e| MirEvalError::MirLowerError(self.id.into(), e))
interpret_mir(db, &body, Substitution::empty(Interner), false)?; .map_err(converter)?;
interpret_mir(db, &body, Substitution::empty(Interner), false).map_err(converter)?;
Ok(()) Ok(())
} }
} }
@ -2006,7 +2016,7 @@ impl Const {
} }
pub fn render_eval(self, db: &dyn HirDatabase) -> Result<String, ConstEvalError> { pub fn render_eval(self, db: &dyn HirDatabase) -> Result<String, ConstEvalError> {
let c = db.const_eval(self.id, Substitution::empty(Interner))?; let c = db.const_eval(self.id.into(), Substitution::empty(Interner))?;
let r = format!("{}", HexifiedConst(c).display(db)); let r = format!("{}", HexifiedConst(c).display(db));
// We want to see things like `<utf8-error>` and `<layout-error>` as they are probably bug in our // We want to see things like `<utf8-error>` and `<layout-error>` as they are probably bug in our
// implementation, but there is no need to show things like `<enum-not-supported>` or `<ref-not-supported>` to // implementation, but there is no need to show things like `<enum-not-supported>` or `<ref-not-supported>` to

View File

@ -30,7 +30,6 @@ pub struct HoverConfig {
pub documentation: bool, pub documentation: bool,
pub keywords: bool, pub keywords: bool,
pub format: HoverDocFormat, pub format: HoverDocFormat,
pub interpret_tests: bool,
} }
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]

View File

@ -3,8 +3,7 @@ use std::fmt::Display;
use either::Either; use either::Either;
use hir::{ use hir::{
db::DefDatabase, Adt, AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, Adt, AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo,
MirEvalError, Semantics, TypeInfo,
}; };
use ide_db::{ use ide_db::{
base_db::SourceDatabase, base_db::SourceDatabase,
@ -435,19 +434,7 @@ pub(super) fn definition(
)) ))
}), }),
Definition::Module(it) => label_and_docs(db, it), Definition::Module(it) => label_and_docs(db, it),
Definition::Function(it) => label_and_layout_info_and_docs(db, it, |_| { Definition::Function(it) => label_and_docs(db, it),
if !config.interpret_tests {
return None;
}
match it.eval(db) {
Ok(()) => Some("pass".into()),
Err(MirEvalError::MirLowerError(f, e)) => {
let name = &db.function_data(f).name;
Some(format!("error: fail to lower {name} due {e:?}"))
}
Err(e) => Some(format!("error: {e:?}")),
}
}),
Definition::Adt(it) => label_and_layout_info_and_docs(db, it, |&it| { Definition::Adt(it) => label_and_layout_info_and_docs(db, it, |&it| {
let layout = it.layout(db).ok()?; let layout = it.layout(db).ok()?;
Some(format!("size = {}, align = {}", layout.size.bytes(), layout.align.abi.bytes())) Some(format!("size = {}, align = {}", layout.size.bytes(), layout.align.abi.bytes()))

View File

@ -9,7 +9,6 @@ const HOVER_BASE_CONFIG: HoverConfig = HoverConfig {
documentation: true, documentation: true,
format: HoverDocFormat::Markdown, format: HoverDocFormat::Markdown,
keywords: true, keywords: true,
interpret_tests: false,
}; };
fn check_hover_no_result(ra_fixture: &str) { fn check_hover_no_result(ra_fixture: &str) {

View File

@ -3,7 +3,10 @@
//! let _: u32 = /* <never-to-any> */ loop {}; //! let _: u32 = /* <never-to-any> */ loop {};
//! let _: &u32 = /* &* */ &mut 0; //! let _: &u32 = /* &* */ &mut 0;
//! ``` //! ```
use hir::{Adjust, Adjustment, AutoBorrow, HirDisplay, Mutability, PointerCast, Safety, Semantics}; use hir::{
Adjust, Adjustment, AutoBorrow, HirDisplay, Mutability, OverloadedDeref, PointerCast, Safety,
Semantics,
};
use ide_db::RootDatabase; use ide_db::RootDatabase;
use stdx::never; use stdx::never;
@ -88,7 +91,13 @@ pub(super) fn hints(
Adjust::NeverToAny if config.adjustment_hints == AdjustmentHints::Always => { Adjust::NeverToAny if config.adjustment_hints == AdjustmentHints::Always => {
("<never-to-any>", "never to any") ("<never-to-any>", "never to any")
} }
Adjust::Deref(_) => ("*", "dereference"), Adjust::Deref(None) => ("*", "dereference"),
Adjust::Deref(Some(OverloadedDeref(Mutability::Shared))) => {
("*", "`Deref` dereference")
}
Adjust::Deref(Some(OverloadedDeref(Mutability::Mut))) => {
("*", "`DerefMut` dereference")
}
Adjust::Borrow(AutoBorrow::Ref(Mutability::Shared)) => ("&", "borrow"), Adjust::Borrow(AutoBorrow::Ref(Mutability::Shared)) => ("&", "borrow"),
Adjust::Borrow(AutoBorrow::Ref(Mutability::Mut)) => ("&mut ", "unique borrow"), Adjust::Borrow(AutoBorrow::Ref(Mutability::Mut)) => ("&mut ", "unique borrow"),
Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Shared)) => { Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Shared)) => {

View File

@ -444,7 +444,7 @@ fn main() {
file_id: FileId( file_id: FileId(
1, 1,
), ),
range: 5768..5776, range: 5769..5777,
}, },
), ),
tooltip: "", tooltip: "",
@ -457,7 +457,7 @@ fn main() {
file_id: FileId( file_id: FileId(
1, 1,
), ),
range: 5800..5804, range: 5801..5805,
}, },
), ),
tooltip: "", tooltip: "",
@ -478,7 +478,7 @@ fn main() {
file_id: FileId( file_id: FileId(
1, 1,
), ),
range: 5768..5776, range: 5769..5777,
}, },
), ),
tooltip: "", tooltip: "",
@ -491,7 +491,7 @@ fn main() {
file_id: FileId( file_id: FileId(
1, 1,
), ),
range: 5800..5804, range: 5801..5805,
}, },
), ),
tooltip: "", tooltip: "",
@ -512,7 +512,7 @@ fn main() {
file_id: FileId( file_id: FileId(
1, 1,
), ),
range: 5768..5776, range: 5769..5777,
}, },
), ),
tooltip: "", tooltip: "",
@ -525,7 +525,7 @@ fn main() {
file_id: FileId( file_id: FileId(
1, 1,
), ),
range: 5800..5804, range: 5801..5805,
}, },
), ),
tooltip: "", tooltip: "",

View File

@ -0,0 +1,49 @@
use hir::Semantics;
use ide_db::base_db::SourceDatabaseExt;
use ide_db::RootDatabase;
use ide_db::{base_db::FilePosition, LineIndexDatabase};
use std::{fmt::Write, time::Instant};
use syntax::TextRange;
use syntax::{algo::find_node_at_offset, ast, AstNode};
// Feature: Interpret Function
//
// |===
// | Editor | Action Name
//
// | VS Code | **rust-analyzer: Interpret Function**
// |===
pub(crate) fn interpret_function(db: &RootDatabase, position: FilePosition) -> String {
let start_time = Instant::now();
let mut result = find_and_interpret(db, position)
.unwrap_or_else(|| "Not inside a function body".to_string());
let duration = Instant::now() - start_time;
writeln!(result, "").unwrap();
writeln!(result, "----------------------").unwrap();
writeln!(result, " Finished in {}s", duration.as_secs_f32()).unwrap();
result
}
fn find_and_interpret(db: &RootDatabase, position: FilePosition) -> Option<String> {
let sema = Semantics::new(db);
let source_file = sema.parse(position.file_id);
let item = find_node_at_offset::<ast::Item>(source_file.syntax(), position.offset)?;
let def = match item {
ast::Item::Fn(it) => sema.to_def(&it)?,
_ => return None,
};
let span_formatter = |file_id, text_range: TextRange| {
let line_col = db.line_index(file_id).line_col(text_range.start());
let path = &db
.source_root(db.file_source_root(file_id))
.path_for_file(&file_id)
.map(|x| x.to_string());
let path = path.as_deref().unwrap_or("<unknown file>");
format!("file://{path}#{}:{}", line_col.line + 1, line_col.col)
};
match def.eval(db, span_formatter) {
Ok(_) => Some("pass".to_string()),
Err(e) => Some(e),
}
}

View File

@ -56,6 +56,7 @@ mod typing;
mod view_crate_graph; mod view_crate_graph;
mod view_hir; mod view_hir;
mod view_mir; mod view_mir;
mod interpret_function;
mod view_item_tree; mod view_item_tree;
mod shuffle_crate_graph; mod shuffle_crate_graph;
@ -317,6 +318,10 @@ impl Analysis {
self.with_db(|db| view_mir::view_mir(db, position)) self.with_db(|db| view_mir::view_mir(db, position))
} }
pub fn interpret_function(&self, position: FilePosition) -> Cancellable<String> {
self.with_db(|db| interpret_function::interpret_function(db, position))
}
pub fn view_item_tree(&self, file_id: FileId) -> Cancellable<String> { pub fn view_item_tree(&self, file_id: FileId) -> Cancellable<String> {
self.with_db(|db| view_item_tree::view_item_tree(db, file_id)) self.with_db(|db| view_item_tree::view_item_tree(db, file_id))
} }

View File

@ -140,7 +140,6 @@ impl StaticIndex<'_> {
documentation: true, documentation: true,
keywords: true, keywords: true,
format: crate::HoverDocFormat::Markdown, format: crate::HoverDocFormat::Markdown,
interpret_tests: false,
}; };
let tokens = tokens.filter(|token| { let tokens = tokens.filter(|token| {
matches!( matches!(

View File

@ -574,6 +574,7 @@ pub struct LensConfig {
// runnables // runnables
pub run: bool, pub run: bool,
pub debug: bool, pub debug: bool,
pub interpret: bool,
// implementations // implementations
pub implementations: bool, pub implementations: bool,
@ -1423,6 +1424,9 @@ impl Config {
LensConfig { LensConfig {
run: self.data.lens_enable && self.data.lens_run_enable, run: self.data.lens_enable && self.data.lens_run_enable,
debug: self.data.lens_enable && self.data.lens_debug_enable, debug: self.data.lens_enable && self.data.lens_debug_enable,
interpret: self.data.lens_enable
&& self.data.lens_run_enable
&& self.data.interpret_tests,
implementations: self.data.lens_enable && self.data.lens_implementations_enable, implementations: self.data.lens_enable && self.data.lens_implementations_enable,
method_refs: self.data.lens_enable && self.data.lens_references_method_enable, method_refs: self.data.lens_enable && self.data.lens_references_method_enable,
refs_adt: self.data.lens_enable && self.data.lens_references_adt_enable, refs_adt: self.data.lens_enable && self.data.lens_references_adt_enable,
@ -1481,7 +1485,6 @@ impl Config {
} }
}, },
keywords: self.data.hover_documentation_keywords_enable, keywords: self.data.hover_documentation_keywords_enable,
interpret_tests: self.data.interpret_tests,
} }
} }

View File

@ -163,6 +163,16 @@ pub(crate) fn handle_view_mir(
Ok(res) Ok(res)
} }
pub(crate) fn handle_interpret_function(
snap: GlobalStateSnapshot,
params: lsp_types::TextDocumentPositionParams,
) -> Result<String> {
let _p = profile::span("handle_interpret_function");
let position = from_proto::file_position(&snap, params)?;
let res = snap.analysis.interpret_function(position)?;
Ok(res)
}
pub(crate) fn handle_view_file_text( pub(crate) fn handle_view_file_text(
snap: GlobalStateSnapshot, snap: GlobalStateSnapshot,
params: lsp_types::TextDocumentIdentifier, params: lsp_types::TextDocumentIdentifier,

View File

@ -90,6 +90,14 @@ impl Request for ViewMir {
const METHOD: &'static str = "rust-analyzer/viewMir"; const METHOD: &'static str = "rust-analyzer/viewMir";
} }
pub enum InterpretFunction {}
impl Request for InterpretFunction {
type Params = lsp_types::TextDocumentPositionParams;
type Result = String;
const METHOD: &'static str = "rust-analyzer/interpretFunction";
}
pub enum ViewFileText {} pub enum ViewFileText {}
impl Request for ViewFileText { impl Request for ViewFileText {

View File

@ -665,6 +665,7 @@ impl GlobalState {
.on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree) .on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree)
.on::<lsp_ext::ViewHir>(handlers::handle_view_hir) .on::<lsp_ext::ViewHir>(handlers::handle_view_hir)
.on::<lsp_ext::ViewMir>(handlers::handle_view_mir) .on::<lsp_ext::ViewMir>(handlers::handle_view_mir)
.on::<lsp_ext::InterpretFunction>(handlers::handle_interpret_function)
.on::<lsp_ext::ViewFileText>(handlers::handle_view_file_text) .on::<lsp_ext::ViewFileText>(handlers::handle_view_file_text)
.on::<lsp_ext::ViewCrateGraph>(handlers::handle_view_crate_graph) .on::<lsp_ext::ViewCrateGraph>(handlers::handle_view_crate_graph)
.on::<lsp_ext::ViewItemTree>(handlers::handle_view_item_tree) .on::<lsp_ext::ViewItemTree>(handlers::handle_view_item_tree)

View File

@ -1215,6 +1215,14 @@ pub(crate) fn code_lens(
data: None, data: None,
}) })
} }
if lens_config.interpret {
let command = command::interpret_single(&r);
acc.push(lsp_types::CodeLens {
range: annotation_range,
command: Some(command),
data: None,
})
}
} }
AnnotationKind::HasImpls { pos: file_range, data } => { AnnotationKind::HasImpls { pos: file_range, data } => {
if !client_commands_config.show_reference { if !client_commands_config.show_reference {
@ -1359,6 +1367,15 @@ pub(crate) mod command {
} }
} }
pub(crate) fn interpret_single(_runnable: &lsp_ext::Runnable) -> lsp_types::Command {
lsp_types::Command {
title: "Interpret".into(),
command: "rust-analyzer.interpretFunction".into(),
// FIXME: use the `_runnable` here.
arguments: Some(vec![]),
}
}
pub(crate) fn goto_location( pub(crate) fn goto_location(
snap: &GlobalStateSnapshot, snap: &GlobalStateSnapshot,
nav: &NavigationTarget, nav: &NavigationTarget,

View File

@ -11,6 +11,7 @@
//! add: //! add:
//! as_ref: sized //! as_ref: sized
//! bool_impl: option, fn //! bool_impl: option, fn
//! cell: copy, drop
//! clone: sized //! clone: sized
//! coerce_unsized: unsize //! coerce_unsized: unsize
//! copy: clone //! copy: clone
@ -139,6 +140,52 @@ pub mod hash {
} }
// endregion:hash // endregion:hash
// region:cell
pub mod cell {
use crate::mem;
#[lang = "unsafe_cell"]
pub struct UnsafeCell<T: ?Sized> {
value: T,
}
impl<T> UnsafeCell<T> {
pub const fn new(value: T) -> UnsafeCell<T> {
UnsafeCell { value }
}
pub const fn get(&self) -> *mut T {
self as *const UnsafeCell<T> as *const T as *mut T
}
}
pub struct Cell<T: ?Sized> {
value: UnsafeCell<T>,
}
impl<T> Cell<T> {
pub const fn new(value: T) -> Cell<T> {
Cell { value: UnsafeCell::new(value) }
}
pub fn set(&self, val: T) {
let old = self.replace(val);
mem::drop(old);
}
pub fn replace(&self, val: T) -> T {
mem::replace(unsafe { &mut *self.value.get() }, val)
}
}
impl<T: Copy> Cell<T> {
pub fn get(&self) -> T {
unsafe { *self.value.get() }
}
}
}
// endregion:cell
// region:clone // region:clone
pub mod clone { pub mod clone {
#[lang = "clone"] #[lang = "clone"]
@ -220,6 +267,13 @@ pub mod mem {
// endregion:manually_drop // endregion:manually_drop
pub fn drop<T>(_x: T) {} pub fn drop<T>(_x: T) {}
pub const fn replace<T>(dest: &mut T, src: T) -> T {
unsafe {
let result = *dest;
*dest = src;
result
}
}
} }
// endregion:drop // endregion:drop
@ -710,6 +764,14 @@ pub mod option {
None => default, None => default,
} }
} }
// region:result
pub const fn ok_or<E>(self, err: E) -> Result<T, E> {
match self {
Some(v) => Ok(v),
None => Err(err),
}
}
// endregion:result
// region:fn // region:fn
pub fn and_then<U, F>(self, f: F) -> Option<U> pub fn and_then<U, F>(self, f: F) -> Option<U>
where where

View File

@ -1,5 +1,5 @@
<!--- <!---
lsp_ext.rs hash: be2f663a78beb7bd lsp_ext.rs hash: 37ac44a0f507e05a
If you need to change the above hash to make the test pass, please check if you If you need to change the above hash to make the test pass, please check if you
need to adjust this doc as well and ping this issue: need to adjust this doc as well and ping this issue:
@ -548,6 +548,18 @@ For debugging or when working on rust-analyzer itself.
Returns a textual representation of the MIR of the function containing the cursor. Returns a textual representation of the MIR of the function containing the cursor.
For debugging or when working on rust-analyzer itself. For debugging or when working on rust-analyzer itself.
## Interpret Function
**Method:** `rust-analyzer/interpretFunction`
**Request:** `TextDocumentPositionParams`
**Response:** `string`
Tries to evaluate the function using internal rust analyzer knowledge, without compiling
the code. Currently evaluates the function under cursor, but will give a runnable in
future. Highly experimental.
## View File Text ## View File Text
**Method:** `rust-analyzer/viewFileText` **Method:** `rust-analyzer/viewFileText`

View File

@ -120,6 +120,11 @@
"title": "View Mir", "title": "View Mir",
"category": "rust-analyzer (debug command)" "category": "rust-analyzer (debug command)"
}, },
{
"command": "rust-analyzer.interpretFunction",
"title": "Interpret Function",
"category": "rust-analyzer (debug command)"
},
{ {
"command": "rust-analyzer.viewFileText", "command": "rust-analyzer.viewFileText",
"title": "View File Text (as seen by the server)", "title": "View File Text (as seen by the server)",

View File

@ -422,8 +422,20 @@ export function syntaxTree(ctx: CtxInit): Cmd {
function viewHirOrMir(ctx: CtxInit, xir: "hir" | "mir"): Cmd { function viewHirOrMir(ctx: CtxInit, xir: "hir" | "mir"): Cmd {
const viewXir = xir === "hir" ? "viewHir" : "viewMir"; const viewXir = xir === "hir" ? "viewHir" : "viewMir";
const requestType = xir === "hir" ? ra.viewHir : ra.viewMir; const requestType = xir === "hir" ? ra.viewHir : ra.viewMir;
const uri = `rust-analyzer-${xir}://${viewXir}/${xir}.rs`;
const scheme = `rust-analyzer-${xir}`;
return viewFileUsingTextDocumentContentProvider(ctx, requestType, uri, scheme, true);
}
function viewFileUsingTextDocumentContentProvider(
ctx: CtxInit,
requestType: lc.RequestType<lc.TextDocumentPositionParams, string, void>,
uri: string,
scheme: string,
shouldUpdate: boolean
): Cmd {
const tdcp = new (class implements vscode.TextDocumentContentProvider { const tdcp = new (class implements vscode.TextDocumentContentProvider {
readonly uri = vscode.Uri.parse(`rust-analyzer-${xir}://${viewXir}/${xir}.rs`); readonly uri = vscode.Uri.parse(uri);
readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>(); readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
constructor() { constructor() {
vscode.workspace.onDidChangeTextDocument( vscode.workspace.onDidChangeTextDocument(
@ -439,14 +451,14 @@ function viewHirOrMir(ctx: CtxInit, xir: "hir" | "mir"): Cmd {
} }
private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) { private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
if (isRustDocument(event.document)) { if (isRustDocument(event.document) && shouldUpdate) {
// We need to order this after language server updates, but there's no API for that. // We need to order this after language server updates, but there's no API for that.
// Hence, good old sleep(). // Hence, good old sleep().
void sleep(10).then(() => this.eventEmitter.fire(this.uri)); void sleep(10).then(() => this.eventEmitter.fire(this.uri));
} }
} }
private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) { private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) {
if (editor && isRustEditor(editor)) { if (editor && isRustEditor(editor) && shouldUpdate) {
this.eventEmitter.fire(this.uri); this.eventEmitter.fire(this.uri);
} }
} }
@ -473,9 +485,7 @@ function viewHirOrMir(ctx: CtxInit, xir: "hir" | "mir"): Cmd {
} }
})(); })();
ctx.pushExtCleanup( ctx.pushExtCleanup(vscode.workspace.registerTextDocumentContentProvider(scheme, tdcp));
vscode.workspace.registerTextDocumentContentProvider(`rust-analyzer-${xir}`, tdcp)
);
return async () => { return async () => {
const document = await vscode.workspace.openTextDocument(tdcp.uri); const document = await vscode.workspace.openTextDocument(tdcp.uri);
@ -501,6 +511,20 @@ export function viewMir(ctx: CtxInit): Cmd {
return viewHirOrMir(ctx, "mir"); return viewHirOrMir(ctx, "mir");
} }
// Opens the virtual file that will show the MIR of the function containing the cursor position
//
// The contents of the file come from the `TextDocumentContentProvider`
export function interpretFunction(ctx: CtxInit): Cmd {
const uri = `rust-analyzer-interpret-function://interpretFunction/result.log`;
return viewFileUsingTextDocumentContentProvider(
ctx,
ra.interpretFunction,
uri,
`rust-analyzer-interpret-function`,
false
);
}
export function viewFileText(ctx: CtxInit): Cmd { export function viewFileText(ctx: CtxInit): Cmd {
const tdcp = new (class implements vscode.TextDocumentContentProvider { const tdcp = new (class implements vscode.TextDocumentContentProvider {
readonly uri = vscode.Uri.parse("rust-analyzer-file-text://viewFileText/file.rs"); readonly uri = vscode.Uri.parse("rust-analyzer-file-text://viewFileText/file.rs");

View File

@ -64,6 +64,9 @@ export const viewHir = new lc.RequestType<lc.TextDocumentPositionParams, string,
export const viewMir = new lc.RequestType<lc.TextDocumentPositionParams, string, void>( export const viewMir = new lc.RequestType<lc.TextDocumentPositionParams, string, void>(
"rust-analyzer/viewMir" "rust-analyzer/viewMir"
); );
export const interpretFunction = new lc.RequestType<lc.TextDocumentPositionParams, string, void>(
"rust-analyzer/interpretFunction"
);
export const viewItemTree = new lc.RequestType<ViewItemTreeParams, string, void>( export const viewItemTree = new lc.RequestType<ViewItemTreeParams, string, void>(
"rust-analyzer/viewItemTree" "rust-analyzer/viewItemTree"
); );

View File

@ -159,6 +159,7 @@ function createCommands(): Record<string, CommandFactory> {
syntaxTree: { enabled: commands.syntaxTree }, syntaxTree: { enabled: commands.syntaxTree },
viewHir: { enabled: commands.viewHir }, viewHir: { enabled: commands.viewHir },
viewMir: { enabled: commands.viewMir }, viewMir: { enabled: commands.viewMir },
interpretFunction: { enabled: commands.interpretFunction },
viewFileText: { enabled: commands.viewFileText }, viewFileText: { enabled: commands.viewFileText },
viewItemTree: { enabled: commands.viewItemTree }, viewItemTree: { enabled: commands.viewItemTree },
viewCrateGraph: { enabled: commands.viewCrateGraph }, viewCrateGraph: { enabled: commands.viewCrateGraph },