mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-26 11:59:49 +00:00
Auto merge of #14664 - HKalbasi:mir, r=HKalbasi
MIR episode 4 In lowering, it now supports overloaded and arith assignment binary operators, statics. and constants in patterns. There is now 252 functions that we fail to emit mir for them, and the majority of them are due type mismatches or other deep and unrelated issues (but it isn't done yet, for example slice patterns and destructing assignment is not implemented yet). In evaluating, it now can evaluate associated constants in traits (so now typenum's `U5::ToConst` should work), allocator functions, atomic intrinsics, and some more things. It also provides a (hacky) basis for making progress in #14275. I also added a `Interpret` code lens to `Run` and `Debug` when the experimental `interpret tests` is enabled (previously it showed result in hover, which was unusable even for debugging) Changes in unrelated files are: * Changes substitutions of closures, now it includes parent substs ~~before~~ after `sig_ty`. * ~~A salsa input for retrieving the path of a file id, used in emitting stack trace for interpret result.~~ * Normalizing associated types in layout computing
This commit is contained in:
commit
2fdd1ac510
47 changed files with 2557 additions and 805 deletions
|
@ -34,7 +34,7 @@ impl fmt::Debug for Change {
|
|||
}
|
||||
|
||||
impl Change {
|
||||
pub fn new() -> Change {
|
||||
pub fn new() -> Self {
|
||||
Change::default()
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ use rustc_hash::FxHashMap;
|
|||
use smallvec::SmallVec;
|
||||
use syntax::{
|
||||
ast::{
|
||||
self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasLoopBody, HasName,
|
||||
self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasAttrs, HasLoopBody, HasName,
|
||||
SlicePatComponents,
|
||||
},
|
||||
AstNode, AstPtr, SyntaxNodePtr,
|
||||
|
@ -302,16 +302,29 @@ impl ExprCollector<'_> {
|
|||
self.alloc_expr(Expr::For { iterable, pat, body, label }, syntax_ptr)
|
||||
}
|
||||
ast::Expr::CallExpr(e) => {
|
||||
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()
|
||||
let is_rustc_box = {
|
||||
let attrs = e.attrs();
|
||||
attrs.filter_map(|x| x.as_simple_atom()).any(|x| x == "rustc_box")
|
||||
};
|
||||
self.alloc_expr(
|
||||
Expr::Call { callee, args, is_assignee_expr: self.is_lowering_assignee_expr },
|
||||
syntax_ptr,
|
||||
)
|
||||
if is_rustc_box {
|
||||
let expr = self.collect_expr_opt(e.arg_list().and_then(|x| x.args().next()));
|
||||
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) => {
|
||||
let receiver = self.collect_expr_opt(e.receiver());
|
||||
|
|
|
@ -232,6 +232,25 @@ impl TyBuilder<()> {
|
|||
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 {
|
||||
let ((), subst) = self.build_internal();
|
||||
subst
|
||||
|
|
|
@ -24,7 +24,7 @@ use crate::{
|
|||
method_resolution::{TraitImpls, TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS},
|
||||
to_assoc_type_id, to_chalk_trait_id,
|
||||
traits::ChalkContext,
|
||||
utils::generics,
|
||||
utils::{generics, ClosureSubst},
|
||||
wrap_empty_binders, AliasEq, AliasTy, BoundVar, CallableDefId, DebruijnIndex, FnDefId,
|
||||
Interner, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Substitution, TraitRef,
|
||||
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>,
|
||||
substs: &chalk_ir::Substitution<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 io = rust_ir::FnDefInputsAndOutputDatum {
|
||||
argument_types: sig.params().to_vec(),
|
||||
|
|
|
@ -1,20 +1,22 @@
|
|||
//! 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::{
|
||||
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinType, BuiltinUint},
|
||||
generics::TypeOrConstParamData,
|
||||
lang_item::LangItem,
|
||||
type_ref::Rawness,
|
||||
FunctionId, GenericDefId, HasModule, ItemContainerId, Lookup, TraitId,
|
||||
DefWithBodyId, FunctionId, GenericDefId, HasModule, ItemContainerId, Lookup, TraitId,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
|
||||
from_placeholder_idx, to_chalk_trait_id, utils::generics, AdtId, AliasEq, AliasTy, Binders,
|
||||
CallableDefId, CallableSig, ClosureId, DynTy, FnPointer, ImplTraitId, Interner, Lifetime,
|
||||
ProjectionTy, QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags,
|
||||
WhereClause,
|
||||
db::HirDatabase,
|
||||
from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx,
|
||||
to_chalk_trait_id,
|
||||
utils::{generics, ClosureSubst},
|
||||
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 {
|
||||
|
@ -46,6 +48,7 @@ pub trait TyExt {
|
|||
|
||||
fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>>;
|
||||
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
|
||||
fn equals_ctor(&self, other: &Ty) -> bool;
|
||||
|
@ -185,10 +188,7 @@ impl TyExt for Ty {
|
|||
let sig = db.callable_item_signature(callable_def);
|
||||
Some(sig.substitute(Interner, parameters))
|
||||
}
|
||||
TyKind::Closure(.., substs) => {
|
||||
let sig_param = substs.at(Interner, 0).assert_ty_ref(Interner);
|
||||
sig_param.callable_sig(db)
|
||||
}
|
||||
TyKind::Closure(.., substs) => ClosureSubst(substs).sig_ty().callable_sig(db),
|
||||
_ => 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 {
|
||||
match (self.kind(Interner), other.kind(Interner)) {
|
||||
(TyKind::Adt(adt, ..), TyKind::Adt(adt2, ..)) => adt == adt2,
|
||||
|
|
|
@ -7,7 +7,7 @@ use hir_def::{
|
|||
path::Path,
|
||||
resolver::{Resolver, ValueNs},
|
||||
type_ref::ConstRef,
|
||||
ConstId, EnumVariantId,
|
||||
DefWithBodyId, EnumVariantId,
|
||||
};
|
||||
use la_arena::{Idx, RawIdx};
|
||||
use stdx::never;
|
||||
|
@ -57,7 +57,7 @@ pub enum ConstEvalError {
|
|||
impl From<MirLowerError> for ConstEvalError {
|
||||
fn from(value: MirLowerError) -> Self {
|
||||
match value {
|
||||
MirLowerError::ConstEvalError(e) => *e,
|
||||
MirLowerError::ConstEvalError(_, e) => *e,
|
||||
_ => ConstEvalError::MirLowerError(value),
|
||||
}
|
||||
}
|
||||
|
@ -168,7 +168,7 @@ pub fn try_const_usize(c: &Const) -> Option<u128> {
|
|||
pub(crate) fn const_eval_recover(
|
||||
_: &dyn HirDatabase,
|
||||
_: &[String],
|
||||
_: &ConstId,
|
||||
_: &DefWithBodyId,
|
||||
_: &Substitution,
|
||||
) -> Result<Const, ConstEvalError> {
|
||||
Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
|
||||
|
@ -184,10 +184,9 @@ pub(crate) fn const_eval_discriminant_recover(
|
|||
|
||||
pub(crate) fn const_eval_query(
|
||||
db: &dyn HirDatabase,
|
||||
const_id: ConstId,
|
||||
def: DefWithBodyId,
|
||||
subst: Substitution,
|
||||
) -> Result<Const, ConstEvalError> {
|
||||
let def = const_id.into();
|
||||
let body = db.mir_body(def)?;
|
||||
let c = interpret_mir(db, &body, subst, false)?;
|
||||
Ok(c)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use base_db::fixture::WithFixture;
|
||||
use base_db::{fixture::WithFixture, FileId};
|
||||
use chalk_ir::Substitution;
|
||||
use hir_def::db::DefDatabase;
|
||||
|
||||
|
@ -16,7 +16,7 @@ mod intrinsics;
|
|||
|
||||
fn simplify(e: ConstEvalError) -> ConstEvalError {
|
||||
match e {
|
||||
ConstEvalError::MirEvalError(MirEvalError::InFunction(_, e)) => {
|
||||
ConstEvalError::MirEvalError(MirEvalError::InFunction(_, e, _, _)) => {
|
||||
simplify(ConstEvalError::MirEvalError(*e))
|
||||
}
|
||||
_ => e,
|
||||
|
@ -24,13 +24,30 @@ fn simplify(e: ConstEvalError) -> ConstEvalError {
|
|||
}
|
||||
|
||||
#[track_caller]
|
||||
fn check_fail(ra_fixture: &str, error: ConstEvalError) {
|
||||
assert_eq!(eval_goal(ra_fixture).map_err(simplify), Err(error));
|
||||
fn check_fail(ra_fixture: &str, error: impl FnOnce(ConstEvalError) -> bool) {
|
||||
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]
|
||||
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 {
|
||||
chalk_ir::ConstValue::Concrete(c) => match &c.interned {
|
||||
ConstScalar::Bytes(b, _) => {
|
||||
|
@ -47,10 +64,9 @@ fn check_number(ra_fixture: &str, answer: i128) {
|
|||
}
|
||||
}
|
||||
|
||||
fn eval_goal(ra_fixture: &str) -> Result<Const, ConstEvalError> {
|
||||
let (db, file_id) = TestDB::with_single_file(ra_fixture);
|
||||
fn eval_goal(db: &TestDB, file_id: FileId) -> Result<Const, ConstEvalError> {
|
||||
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 const_id = scope
|
||||
.declarations()
|
||||
|
@ -65,7 +81,7 @@ fn eval_goal(ra_fixture: &str) -> Result<Const, ConstEvalError> {
|
|||
_ => None,
|
||||
})
|
||||
.unwrap();
|
||||
db.const_eval(const_id, Substitution::empty(Interner))
|
||||
db.const_eval(const_id.into(), Substitution::empty(Interner))
|
||||
}
|
||||
|
||||
#[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]
|
||||
fn function_call() {
|
||||
check_number(
|
||||
|
@ -426,6 +517,16 @@ fn generic_fn() {
|
|||
"#,
|
||||
12,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
const fn y<T>(b: T) -> (T, ) {
|
||||
let alloc = b;
|
||||
(alloc, )
|
||||
}
|
||||
const GOAL: u8 = y(2).0;
|
||||
"#,
|
||||
2,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: coerce_unsized, index, slice
|
||||
|
@ -590,6 +691,30 @@ fn loops() {
|
|||
"#,
|
||||
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]
|
||||
|
@ -790,10 +915,12 @@ fn path_pattern_matching() {
|
|||
|
||||
use Season::*;
|
||||
|
||||
const MY_SEASON: Season = Summer;
|
||||
|
||||
const fn f(x: Season) -> i32 {
|
||||
match x {
|
||||
Spring => 1,
|
||||
Summer => 2,
|
||||
MY_SEASON => 2,
|
||||
Fall => 3,
|
||||
Winter => 4,
|
||||
}
|
||||
|
@ -944,19 +1071,10 @@ fn function_param_patterns() {
|
|||
fn match_guards() {
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: option, eq
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
//- minicore: option
|
||||
fn f(x: Option<i32>) -> i32 {
|
||||
match x {
|
||||
y if y == Some(42) => 42000,
|
||||
y if let Some(42) = y => 42000,
|
||||
Some(y) => y,
|
||||
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]
|
||||
fn options() {
|
||||
check_number(
|
||||
|
@ -1147,6 +1318,16 @@ fn closures() {
|
|||
check_number(
|
||||
r#"
|
||||
//- 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);
|
||||
impl X {
|
||||
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]
|
||||
fn or_pattern() {
|
||||
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]
|
||||
fn function_pointer() {
|
||||
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]
|
||||
fn array_and_index() {
|
||||
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]
|
||||
fn enums() {
|
||||
check_number(
|
||||
|
@ -1531,13 +1835,13 @@ fn enums() {
|
|||
"#,
|
||||
0,
|
||||
);
|
||||
let r = eval_goal(
|
||||
let (db, file_id) = TestDB::with_single_file(
|
||||
r#"
|
||||
enum E { A = 1, B }
|
||||
const GOAL: E = E::A;
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
);
|
||||
let r = eval_goal(&db, file_id).unwrap();
|
||||
assert_eq!(try_const_usize(&r), Some(1));
|
||||
}
|
||||
|
||||
|
@ -1550,7 +1854,7 @@ fn const_loop() {
|
|||
const F2: i32 = 2 * F1;
|
||||
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]
|
||||
fn const_trait_assoc() {
|
||||
// FIXME: this should evaluate to 0
|
||||
check_fail(
|
||||
check_number(
|
||||
r#"
|
||||
struct U0;
|
||||
trait ToConst {
|
||||
|
@ -1619,9 +1922,35 @@ fn const_trait_assoc() {
|
|||
impl ToConst for U0 {
|
||||
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#"
|
||||
const GOAL: usize = loop {};
|
||||
"#,
|
||||
ConstEvalError::MirEvalError(MirEvalError::ExecutionLimitExceeded),
|
||||
|e| e == ConstEvalError::MirEvalError(MirEvalError::ExecutionLimitExceeded),
|
||||
);
|
||||
check_fail(
|
||||
r#"
|
||||
|
@ -1640,7 +1969,7 @@ fn exec_limits() {
|
|||
}
|
||||
const GOAL: i32 = f(0);
|
||||
"#,
|
||||
ConstEvalError::MirEvalError(MirEvalError::StackOverflow),
|
||||
|e| e == ConstEvalError::MirEvalError(MirEvalError::StackOverflow),
|
||||
);
|
||||
// Reasonable code should still work
|
||||
check_number(
|
||||
|
@ -1665,7 +1994,7 @@ fn exec_limits() {
|
|||
|
||||
#[test]
|
||||
fn type_error() {
|
||||
let e = eval_goal(
|
||||
check_fail(
|
||||
r#"
|
||||
const GOAL: u8 = {
|
||||
let x: u16 = 2;
|
||||
|
@ -1673,6 +2002,6 @@ fn type_error() {
|
|||
y.0
|
||||
};
|
||||
"#,
|
||||
|e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::TypeMismatch(_))),
|
||||
);
|
||||
assert!(matches!(e, Err(ConstEvalError::MirLowerError(MirLowerError::TypeMismatch(_)))));
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
fn offset() {
|
||||
check_number(
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::sync::Arc;
|
|||
|
||||
use base_db::{impl_intern_key, salsa, CrateId, Upcast};
|
||||
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,
|
||||
TypeOrConstParamId, VariantId,
|
||||
};
|
||||
|
@ -59,7 +59,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
|||
|
||||
#[salsa::invoke(crate::consteval::const_eval_query)]
|
||||
#[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::cycle(crate::consteval::const_eval_discriminant_recover)]
|
||||
|
|
|
@ -772,7 +772,7 @@ impl<'p> Fields<'p> {
|
|||
|
||||
(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 = 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(..))
|
||||
|| visibility[fid].is_visible_from(cx.db.upcast(), cx.module);
|
||||
let is_uninhabited = cx.is_uninhabited(&ty);
|
||||
|
|
|
@ -32,7 +32,7 @@ use crate::{
|
|||
mapping::from_chalk,
|
||||
mir::pad16,
|
||||
primitive, to_assoc_type_id,
|
||||
utils::{self, generics},
|
||||
utils::{self, generics, ClosureSubst},
|
||||
AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Const, ConstScalar, ConstValue,
|
||||
DomainGoal, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, LifetimeOutlives,
|
||||
MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Scalar,
|
||||
|
@ -419,6 +419,16 @@ impl HirDisplay for Const {
|
|||
}
|
||||
ConstValue::Concrete(c) => match &c.interned {
|
||||
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('_'),
|
||||
},
|
||||
}
|
||||
|
@ -485,7 +495,7 @@ fn render_const_scalar(
|
|||
chalk_ir::TyKind::Ref(_, _, t) => match t.kind(Interner) {
|
||||
chalk_ir::TyKind::Str => {
|
||||
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>");
|
||||
write!(f, "{s:?}")
|
||||
}
|
||||
|
@ -574,6 +584,11 @@ fn render_const_scalar(
|
|||
hir_def::AdtId::EnumId(_) => f.write_str("<enum-not-supported>"),
|
||||
},
|
||||
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>"),
|
||||
}
|
||||
}
|
||||
|
@ -794,82 +809,9 @@ impl HirDisplay for Ty {
|
|||
}
|
||||
f.end_location_link();
|
||||
|
||||
if parameters.len(Interner) > 0 {
|
||||
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, ¶meters);
|
||||
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;
|
||||
}
|
||||
}
|
||||
¶meters.as_slice(Interner)[0..default_from]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parameters.as_slice(Interner)
|
||||
};
|
||||
if !parameters_to_write.is_empty() {
|
||||
write!(f, "<")?;
|
||||
let generic_def = self.as_generic_def(db);
|
||||
|
||||
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, ">")?;
|
||||
}
|
||||
}
|
||||
hir_fmt_generics(f, parameters, generic_def)?;
|
||||
}
|
||||
TyKind::AssociatedType(assoc_type_id, parameters) => {
|
||||
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 {
|
||||
let (def, _) = db.lookup_intern_closure((*id).into());
|
||||
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, ¶meters);
|
||||
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;
|
||||
}
|
||||
}
|
||||
¶meters.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 {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
|
||||
write!(f, "fn(")?;
|
||||
|
|
|
@ -44,7 +44,7 @@ use crate::{
|
|||
db::HirDatabase, fold_tys, infer::coerce::CoerceMany, lower::ImplTraitLoweringMode,
|
||||
static_lifetime, to_assoc_type_id, traits::FnTrait, AliasEq, AliasTy, ClosureId, DomainGoal,
|
||||
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.
|
||||
|
@ -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
|
||||
/// 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) {
|
||||
return ty;
|
||||
}
|
||||
let trait_env = db.trait_environment_for_body(owner);
|
||||
let mut table = unify::InferenceTable::new(db, trait_env);
|
||||
|
||||
let ty_with_vars = table.normalize_associated_types_in(ty);
|
||||
|
|
|
@ -2,7 +2,11 @@
|
|||
|
||||
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::{
|
||||
data::adt::VariantData,
|
||||
hir::{
|
||||
|
@ -11,7 +15,7 @@ use hir_def::{
|
|||
},
|
||||
lang_item::LangItem,
|
||||
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
|
||||
FieldId, HasModule, VariantId,
|
||||
DefWithBodyId, FieldId, HasModule, VariantId,
|
||||
};
|
||||
use hir_expand::name;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
@ -20,12 +24,13 @@ use stdx::never;
|
|||
|
||||
use crate::{
|
||||
db::HirDatabase,
|
||||
from_placeholder_idx, make_binders,
|
||||
mir::{BorrowKind, MirSpan, ProjectionElem},
|
||||
static_lifetime, to_chalk_trait_id,
|
||||
traits::FnTrait,
|
||||
utils::{self, pattern_matching_dereference_count},
|
||||
Adjust, Adjustment, Canonical, CanonicalVarKinds, ChalkTraitId, ClosureId, DynTy, FnPointer,
|
||||
FnSig, InEnvironment, Interner, Substitution, Ty, TyBuilder, TyExt,
|
||||
utils::{self, generics, pattern_matching_dereference_count, Generics},
|
||||
Adjust, Adjustment, Binders, ChalkTraitId, ClosureId, DynTy, FnPointer, FnSig, Interner,
|
||||
Substitution, Ty, TyExt,
|
||||
};
|
||||
|
||||
use super::{Expectation, InferenceContext};
|
||||
|
@ -117,7 +122,7 @@ impl HirPlace {
|
|||
fn ty(&self, ctx: &mut InferenceContext<'_>) -> Ty {
|
||||
let mut ty = ctx.table.resolve_completely(ctx.result[self.local].clone());
|
||||
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");
|
||||
});
|
||||
}
|
||||
|
@ -152,7 +157,7 @@ pub struct CapturedItem {
|
|||
pub(crate) place: HirPlace,
|
||||
pub(crate) kind: CaptureKind,
|
||||
pub(crate) span: MirSpan,
|
||||
pub(crate) ty: Ty,
|
||||
pub(crate) ty: Binders<Ty>,
|
||||
}
|
||||
|
||||
impl CapturedItem {
|
||||
|
@ -232,6 +237,52 @@ pub(crate) struct CapturedItemWithoutTy {
|
|||
|
||||
impl CapturedItemWithoutTy {
|
||||
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 = match &self.kind {
|
||||
CaptureKind::ByValue => ty,
|
||||
|
@ -243,7 +294,12 @@ impl CapturedItemWithoutTy {
|
|||
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.
|
||||
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();
|
||||
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()
|
||||
ty.is_copy(self.db, self.owner)
|
||||
}
|
||||
|
||||
fn select_from_expr(&mut self, expr: ExprId) {
|
||||
|
|
|
@ -21,8 +21,10 @@ use crate::{
|
|||
Adjust, Adjustment, AutoBorrow, InferOk, InferenceContext, OverloadedDeref, PointerCast,
|
||||
TypeError, TypeMismatch,
|
||||
},
|
||||
static_lifetime, Canonical, DomainGoal, FnPointer, FnSig, Guidance, InEnvironment, Interner,
|
||||
Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
|
||||
static_lifetime,
|
||||
utils::ClosureSubst,
|
||||
Canonical, DomainGoal, FnPointer, FnSig, Guidance, InEnvironment, Interner, Solution,
|
||||
Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
|
||||
};
|
||||
|
||||
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 {
|
||||
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) {
|
||||
TyKind::Function(fn_ty) => TyKind::Function(FnPointer {
|
||||
num_binders: fn_ty.num_binders,
|
||||
|
|
|
@ -282,7 +282,7 @@ impl<'a> InferenceContext<'a> {
|
|||
let closure_id = self.db.intern_closure((self.owner, tgt_expr)).into();
|
||||
let closure_ty = TyKind::Closure(
|
||||
closure_id,
|
||||
Substitution::from1(Interner, sig_ty.clone()),
|
||||
TyBuilder::subst_for_closure(self.db, self.owner, sig_ty.clone()),
|
||||
)
|
||||
.intern(Interner);
|
||||
self.deferred_closures.entry(closure_id).or_default();
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
//! Compute the binary representation of a type
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use base_db::CrateId;
|
||||
use chalk_ir::{AdtId, TyKind};
|
||||
use hir_def::{
|
||||
|
@ -13,8 +15,8 @@ use la_arena::{Idx, RawIdx};
|
|||
use stdx::never;
|
||||
|
||||
use crate::{
|
||||
consteval::try_const_usize, db::HirDatabase, layout::adt::struct_variant_idx, Interner,
|
||||
Substitution, Ty,
|
||||
consteval::try_const_usize, db::HirDatabase, infer::normalize, layout::adt::struct_variant_idx,
|
||||
utils::ClosureSubst, Interner, Substitution, TraitEnvironment, Ty,
|
||||
};
|
||||
|
||||
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 cx = LayoutCx { krate, target: &target };
|
||||
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) {
|
||||
TyKind::Adt(AdtId(def), subst) => db.layout_of_adt(*def, subst.clone())?,
|
||||
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) => {
|
||||
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;
|
||||
let element = layout_of_ty(db, element, krate)?;
|
||||
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 infer = db.infer(def);
|
||||
let (captures, _) = infer.closure_info(c);
|
||||
let fields = captures
|
||||
.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<_>, _>>()?;
|
||||
let fields = fields.iter().collect::<Vec<_>>();
|
||||
let fields = fields.iter().collect::<Vec<_>>();
|
||||
|
|
|
@ -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]
|
||||
fn return_position_impl_trait() {
|
||||
size_and_align_expr! {
|
||||
|
|
|
@ -44,10 +44,10 @@ use chalk_ir::{
|
|||
NoSolution, TyData,
|
||||
};
|
||||
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 la_arena::{Arena, Idx};
|
||||
use mir::MirEvalError;
|
||||
use mir::{MirEvalError, VTableMap};
|
||||
use rustc_hash::FxHashSet;
|
||||
use traits::FnTrait;
|
||||
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
|
||||
/// meaningful.
|
||||
#[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 {
|
||||
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
|
||||
|
@ -165,7 +168,7 @@ impl MemoryMap {
|
|||
&self,
|
||||
mut f: impl FnMut(&[u8]) -> Result<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)]
|
||||
pub enum ConstScalar {
|
||||
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
|
||||
// FIXME: this is a hack to get around chalk not being able to represent unevaluatable
|
||||
// constants
|
||||
|
|
|
@ -3,13 +3,14 @@
|
|||
use std::{fmt::Display, iter};
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase, display::HirDisplay, infer::PointerCast, lang_items::is_box, ClosureId, Const,
|
||||
ConstScalar, InferenceResult, Interner, MemoryMap, Substitution, Ty, TyKind,
|
||||
db::HirDatabase, display::HirDisplay, infer::PointerCast, lang_items::is_box, mapping::ToChalk,
|
||||
CallableDefId, ClosureId, Const, ConstScalar, InferenceResult, Interner, MemoryMap,
|
||||
Substitution, Ty, TyKind,
|
||||
};
|
||||
use chalk_ir::Mutability;
|
||||
use hir_def::{
|
||||
hir::{BindingId, Expr, ExprId, Ordering, PatId},
|
||||
DefWithBodyId, FieldId, UnionId, VariantId,
|
||||
DefWithBodyId, FieldId, StaticId, UnionId, VariantId,
|
||||
};
|
||||
use la_arena::{Arena, ArenaMap, Idx, RawIdx};
|
||||
|
||||
|
@ -19,7 +20,7 @@ mod borrowck;
|
|||
mod pretty;
|
||||
|
||||
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::{
|
||||
lower_to_mir, mir_body_for_closure_query, mir_body_query, mir_body_recover, MirLowerError,
|
||||
};
|
||||
|
@ -76,6 +77,9 @@ pub enum Operand {
|
|||
Move(Place),
|
||||
/// Constants are already semantically values, and remain unchanged.
|
||||
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 {
|
||||
|
@ -90,6 +94,17 @@ impl Operand {
|
|||
fn const_zst(ty: Ty) -> Operand {
|
||||
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)]
|
||||
|
@ -110,7 +125,7 @@ impl<V, T> ProjectionElem<V, T> {
|
|||
&self,
|
||||
base: Ty,
|
||||
db: &dyn HirDatabase,
|
||||
closure_field: impl FnOnce(ClosureId, usize) -> Ty,
|
||||
closure_field: impl FnOnce(ClosureId, &Substitution, usize) -> Ty,
|
||||
) -> Ty {
|
||||
match self {
|
||||
ProjectionElem::Deref => match &base.data(Interner).kind {
|
||||
|
@ -142,7 +157,7 @@ impl<V, T> ProjectionElem<V, T> {
|
|||
never!("Out of bound tuple field");
|
||||
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");
|
||||
return TyKind::Error.intern(Interner);
|
||||
|
@ -261,7 +276,13 @@ impl SwitchTargets {
|
|||
}
|
||||
|
||||
#[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.
|
||||
Goto { target: BasicBlockId },
|
||||
|
||||
|
@ -836,6 +857,9 @@ pub enum Rvalue {
|
|||
/// affects alias analysis.
|
||||
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
|
||||
/// 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)
|
||||
|
@ -918,7 +942,7 @@ impl MirBody {
|
|||
Operand::Copy(p) | Operand::Move(p) => {
|
||||
f(p);
|
||||
}
|
||||
Operand::Constant(_) => (),
|
||||
Operand::Constant(_) | Operand::Static(_) => (),
|
||||
}
|
||||
}
|
||||
for (_, block) in self.basic_blocks.iter_mut() {
|
||||
|
@ -927,6 +951,7 @@ impl MirBody {
|
|||
StatementKind::Assign(p, r) => {
|
||||
f(p);
|
||||
match r {
|
||||
Rvalue::ShallowInitBoxWithAlloc(_) => (),
|
||||
Rvalue::ShallowInitBox(o, _)
|
||||
| Rvalue::UnaryOp(_, o)
|
||||
| Rvalue::Cast(_, o, _)
|
||||
|
@ -954,32 +979,32 @@ impl MirBody {
|
|||
}
|
||||
}
|
||||
match &mut block.terminator {
|
||||
Some(x) => match x {
|
||||
Terminator::SwitchInt { discr, .. } => for_operand(discr, &mut f),
|
||||
Terminator::FalseEdge { .. }
|
||||
| Terminator::FalseUnwind { .. }
|
||||
| Terminator::Goto { .. }
|
||||
| Terminator::Resume
|
||||
| Terminator::GeneratorDrop
|
||||
| Terminator::Abort
|
||||
| Terminator::Return
|
||||
| Terminator::Unreachable => (),
|
||||
Terminator::Drop { place, .. } => {
|
||||
Some(x) => match &mut x.kind {
|
||||
TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, &mut f),
|
||||
TerminatorKind::FalseEdge { .. }
|
||||
| TerminatorKind::FalseUnwind { .. }
|
||||
| TerminatorKind::Goto { .. }
|
||||
| TerminatorKind::Resume
|
||||
| TerminatorKind::GeneratorDrop
|
||||
| TerminatorKind::Abort
|
||||
| TerminatorKind::Return
|
||||
| TerminatorKind::Unreachable => (),
|
||||
TerminatorKind::Drop { place, .. } => {
|
||||
f(place);
|
||||
}
|
||||
Terminator::DropAndReplace { place, value, .. } => {
|
||||
TerminatorKind::DropAndReplace { place, value, .. } => {
|
||||
f(place);
|
||||
for_operand(value, &mut f);
|
||||
}
|
||||
Terminator::Call { func, args, destination, .. } => {
|
||||
TerminatorKind::Call { func, args, destination, .. } => {
|
||||
for_operand(func, &mut f);
|
||||
args.iter_mut().for_each(|x| for_operand(x, &mut f));
|
||||
f(destination);
|
||||
}
|
||||
Terminator::Assert { cond, .. } => {
|
||||
TerminatorKind::Assert { cond, .. } => {
|
||||
for_operand(cond, &mut f);
|
||||
}
|
||||
Terminator::Yield { value, resume_arg, .. } => {
|
||||
TerminatorKind::Yield { value, resume_arg, .. } => {
|
||||
for_operand(value, &mut f);
|
||||
f(resume_arg);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ use crate::{db::HirDatabase, ClosureId};
|
|||
|
||||
use super::{
|
||||
BasicBlockId, BorrowKind, LocalId, MirBody, MirLowerError, MirSpan, Place, ProjectionElem,
|
||||
Rvalue, StatementKind, Terminator,
|
||||
Rvalue, StatementKind, TerminatorKind,
|
||||
};
|
||||
|
||||
#[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");
|
||||
return;
|
||||
};
|
||||
let targets = match terminator {
|
||||
Terminator::Goto { target } => vec![*target],
|
||||
Terminator::SwitchInt { targets, .. } => targets.all_targets().to_vec(),
|
||||
Terminator::Resume
|
||||
| Terminator::Abort
|
||||
| Terminator::Return
|
||||
| Terminator::Unreachable => vec![],
|
||||
Terminator::Call { target, cleanup, destination, .. } => {
|
||||
let targets = match &terminator.kind {
|
||||
TerminatorKind::Goto { target } => vec![*target],
|
||||
TerminatorKind::SwitchInt { targets, .. } => targets.all_targets().to_vec(),
|
||||
TerminatorKind::Resume
|
||||
| TerminatorKind::Abort
|
||||
| TerminatorKind::Return
|
||||
| TerminatorKind::Unreachable => vec![],
|
||||
TerminatorKind::Call { target, cleanup, destination, .. } => {
|
||||
if destination.projection.len() == 0 && destination.local == l {
|
||||
is_ever_initialized = true;
|
||||
}
|
||||
target.into_iter().chain(cleanup.into_iter()).copied().collect()
|
||||
}
|
||||
Terminator::Drop { .. }
|
||||
| Terminator::DropAndReplace { .. }
|
||||
| Terminator::Assert { .. }
|
||||
| Terminator::Yield { .. }
|
||||
| Terminator::GeneratorDrop
|
||||
| Terminator::FalseEdge { .. }
|
||||
| Terminator::FalseUnwind { .. } => {
|
||||
TerminatorKind::Drop { .. }
|
||||
| TerminatorKind::DropAndReplace { .. }
|
||||
| TerminatorKind::Assert { .. }
|
||||
| TerminatorKind::Yield { .. }
|
||||
| TerminatorKind::GeneratorDrop
|
||||
| TerminatorKind::FalseEdge { .. }
|
||||
| TerminatorKind::FalseUnwind { .. } => {
|
||||
never!("We don't emit these MIR terminators yet");
|
||||
vec![]
|
||||
}
|
||||
|
@ -228,21 +228,21 @@ fn mutability_of_locals(body: &MirBody) -> ArenaMap<LocalId, MutabilityReason> {
|
|||
never!("Terminator should be none only in construction");
|
||||
continue;
|
||||
};
|
||||
match terminator {
|
||||
Terminator::Goto { .. }
|
||||
| Terminator::Resume
|
||||
| Terminator::Abort
|
||||
| Terminator::Return
|
||||
| Terminator::Unreachable
|
||||
| Terminator::FalseEdge { .. }
|
||||
| Terminator::FalseUnwind { .. }
|
||||
| Terminator::GeneratorDrop
|
||||
| Terminator::SwitchInt { .. }
|
||||
| Terminator::Drop { .. }
|
||||
| Terminator::DropAndReplace { .. }
|
||||
| Terminator::Assert { .. }
|
||||
| Terminator::Yield { .. } => (),
|
||||
Terminator::Call { destination, .. } => {
|
||||
match &terminator.kind {
|
||||
TerminatorKind::Goto { .. }
|
||||
| TerminatorKind::Resume
|
||||
| TerminatorKind::Abort
|
||||
| TerminatorKind::Return
|
||||
| TerminatorKind::Unreachable
|
||||
| TerminatorKind::FalseEdge { .. }
|
||||
| TerminatorKind::FalseUnwind { .. }
|
||||
| TerminatorKind::GeneratorDrop
|
||||
| TerminatorKind::SwitchInt { .. }
|
||||
| TerminatorKind::Drop { .. }
|
||||
| TerminatorKind::DropAndReplace { .. }
|
||||
| TerminatorKind::Assert { .. }
|
||||
| TerminatorKind::Yield { .. } => (),
|
||||
TerminatorKind::Call { destination, .. } => {
|
||||
if destination.projection.len() == 0 {
|
||||
if ever_init_map.get(destination.local).copied().unwrap_or_default() {
|
||||
push_mut_span(destination.local, MirSpan::Unknown);
|
||||
|
|
File diff suppressed because it is too large
Load diff
396
crates/hir-ty/src/mir/eval/shim.rs
Normal file
396
crates/hir-ty/src/mir/eval/shim.rs
Normal 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}"),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +1,15 @@
|
|||
//! 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 hir_def::{
|
||||
body::Body,
|
||||
data::adt::{StructKind, VariantData},
|
||||
hir::{
|
||||
Array, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, Pat, PatId,
|
||||
RecordFieldPat, RecordLitField,
|
||||
ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm,
|
||||
Pat, PatId, RecordFieldPat, RecordLitField,
|
||||
},
|
||||
lang_item::{LangItem, LangItemTarget},
|
||||
path::Path,
|
||||
|
@ -18,6 +19,7 @@ use hir_def::{
|
|||
use hir_expand::name::Name;
|
||||
use la_arena::ArenaMap;
|
||||
use rustc_hash::FxHashMap;
|
||||
use syntax::TextRange;
|
||||
|
||||
use crate::{
|
||||
consteval::ConstEvalError,
|
||||
|
@ -27,8 +29,9 @@ use crate::{
|
|||
inhabitedness::is_ty_uninhabited_from,
|
||||
layout::{layout_of_ty, LayoutError},
|
||||
mapping::ToChalk,
|
||||
method_resolution::lookup_impl_const,
|
||||
static_lifetime,
|
||||
utils::generics,
|
||||
utils::{generics, ClosureSubst},
|
||||
Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt,
|
||||
};
|
||||
|
||||
|
@ -62,14 +65,14 @@ struct MirLowerCtx<'a> {
|
|||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum MirLowerError {
|
||||
ConstEvalError(Box<ConstEvalError>),
|
||||
ConstEvalError(String, Box<ConstEvalError>),
|
||||
LayoutError(LayoutError),
|
||||
IncompleteExpr,
|
||||
/// Trying to lower a trait function, instead of an implementation
|
||||
TraitFunctionDefinition(TraitId, Name),
|
||||
UnresolvedName(String),
|
||||
RecordLiteralWithoutPath,
|
||||
UnresolvedMethod,
|
||||
UnresolvedMethod(String),
|
||||
UnresolvedField,
|
||||
MissingFunctionDefinition,
|
||||
TypeMismatch(TypeMismatch),
|
||||
|
@ -88,6 +91,46 @@ pub enum MirLowerError {
|
|||
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 {
|
||||
($x: expr) => {
|
||||
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 {
|
||||
fn from(value: LayoutError) -> Self {
|
||||
MirLowerError::LayoutError(value)
|
||||
|
@ -177,7 +211,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
if !self.has_adjustments(expr_id) {
|
||||
match &self.body.exprs[expr_id] {
|
||||
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)));
|
||||
}
|
||||
_ => (),
|
||||
|
@ -282,7 +316,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
{
|
||||
match assoc {
|
||||
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))
|
||||
},
|
||||
hir_def::AssocItemId::FunctionId(_) => {
|
||||
|
@ -309,17 +343,20 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
}
|
||||
};
|
||||
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(
|
||||
current,
|
||||
place,
|
||||
Operand::Copy(self.binding_local(pat_id)?.into()).into(),
|
||||
Operand::Copy(temp).into(),
|
||||
expr_id.into(),
|
||||
);
|
||||
Ok(Some(current))
|
||||
}
|
||||
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))
|
||||
}
|
||||
ValueNs::EnumVariantId(variant_id) => {
|
||||
|
@ -343,7 +380,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
not_supported!("owner without generic def id");
|
||||
};
|
||||
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(
|
||||
current,
|
||||
place,
|
||||
|
@ -388,12 +425,13 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
};
|
||||
self.set_terminator(
|
||||
current,
|
||||
Terminator::SwitchInt {
|
||||
TerminatorKind::SwitchInt {
|
||||
discr,
|
||||
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 } => {
|
||||
let Some((cond_place, current)) = self.lower_expr_as_place(current, *expr, true)? else {
|
||||
|
@ -423,32 +461,32 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
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 } => {
|
||||
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 } => {
|
||||
if let Some(label) = label {
|
||||
self.lower_loop(current, place.clone(), Some(*label), |this, begin| {
|
||||
if let Some(block) = this.lower_block_to_place(statements, begin, *tail, place)? {
|
||||
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, expr_id.into())? {
|
||||
let end = this.current_loop_end()?;
|
||||
this.set_goto(block, end);
|
||||
this.set_goto(block, end, expr_id.into());
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
} 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)? {
|
||||
this.set_goto(block, begin);
|
||||
this.set_goto(block, begin, expr_id.into());
|
||||
}
|
||||
Ok(())
|
||||
}),
|
||||
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 {
|
||||
return Ok(());
|
||||
};
|
||||
|
@ -456,13 +494,14 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
let after_cond = this.new_basic_block();
|
||||
this.set_terminator(
|
||||
to_switch,
|
||||
Terminator::SwitchInt {
|
||||
TerminatorKind::SwitchInt {
|
||||
discr,
|
||||
targets: SwitchTargets::static_if(1, after_cond, end),
|
||||
},
|
||||
expr_id.into(),
|
||||
);
|
||||
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(())
|
||||
})
|
||||
|
@ -478,12 +517,12 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
let into_iter_fn_op = Operand::const_zst(
|
||||
TyKind::FnDef(
|
||||
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));
|
||||
let iter_next_fn_op = Operand::const_zst(
|
||||
TyKind::FnDef(
|
||||
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));
|
||||
let &Some(iterator_ty) = &self.infer.type_of_for_iterator.get(&expr_id) else {
|
||||
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 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 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 {
|
||||
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.lower_loop(current, place, label, |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)?
|
||||
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, expr_id.into())?
|
||||
else {
|
||||
return Ok(());
|
||||
};
|
||||
|
@ -516,7 +555,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
AdtPatternShape::Tuple { args: &[pat], ellipsis: None },
|
||||
)?;
|
||||
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(())
|
||||
})
|
||||
|
@ -536,39 +575,36 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
place,
|
||||
current,
|
||||
self.is_uninhabited(expr_id),
|
||||
expr_id.into(),
|
||||
);
|
||||
}
|
||||
let callee_ty = self.expr_ty_after_adjustments(*callee);
|
||||
match &callee_ty.data(Interner).kind {
|
||||
chalk_ir::TyKind::FnDef(..) => {
|
||||
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(_) => {
|
||||
let Some((func, current)) = self.lower_expr_to_some_operand(*callee, current)? else {
|
||||
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),
|
||||
_ => return Err(MirLowerError::TypeError("function call on bad type")),
|
||||
}
|
||||
}
|
||||
Expr::MethodCall { receiver, args, .. } => {
|
||||
Expr::MethodCall { receiver, args, method_name, .. } => {
|
||||
let (func_id, generic_args) =
|
||||
self.infer.method_resolution(expr_id).ok_or(MirLowerError::UnresolvedMethod)?;
|
||||
let ty = chalk_ir::TyKind::FnDef(
|
||||
CallableDefId::FunctionId(func_id).to_chalk(self.db),
|
||||
generic_args,
|
||||
)
|
||||
.intern(Interner);
|
||||
let func = Operand::from_bytes(vec![], ty);
|
||||
self.infer.method_resolution(expr_id).ok_or_else(|| MirLowerError::UnresolvedMethod(format!("{}", method_name)))?;
|
||||
let func = Operand::from_fn(self.db, func_id, generic_args);
|
||||
self.lower_call_and_args(
|
||||
func,
|
||||
iter::once(*receiver).chain(args.iter().copied()),
|
||||
place,
|
||||
current,
|
||||
self.is_uninhabited(expr_id),
|
||||
expr_id.into(),
|
||||
)
|
||||
}
|
||||
Expr::Match { expr, arms } => {
|
||||
|
@ -591,7 +627,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
let next = 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)? {
|
||||
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
|
||||
} else {
|
||||
|
@ -599,7 +635,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
};
|
||||
if let Some(block) = self.lower_expr_to_place(*expr, place.clone(), then)? {
|
||||
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 {
|
||||
Some(o) => current = o,
|
||||
|
@ -611,18 +647,17 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
}
|
||||
}
|
||||
if self.is_unterminated(current) {
|
||||
self.set_terminator(current, Terminator::Unreachable);
|
||||
self.set_terminator(current, TerminatorKind::Unreachable, expr_id.into());
|
||||
}
|
||||
Ok(end)
|
||||
}
|
||||
Expr::Continue { label } => match label {
|
||||
Some(_) => not_supported!("continue with label"),
|
||||
None => {
|
||||
let loop_data =
|
||||
self.current_loop_blocks.as_ref().ok_or(MirLowerError::ContinueWithoutLoop)?;
|
||||
self.set_goto(current, loop_data.begin);
|
||||
Ok(None)
|
||||
}
|
||||
Expr::Continue { label } => {
|
||||
let loop_data = match label {
|
||||
Some(l) => self.labeled_loop_blocks.get(l).ok_or(MirLowerError::UnresolvedLabel)?,
|
||||
None => self.current_loop_blocks.as_ref().ok_or(MirLowerError::ContinueWithoutLoop)?,
|
||||
};
|
||||
self.set_goto(current, loop_data.begin, expr_id.into());
|
||||
Ok(None)
|
||||
},
|
||||
&Expr::Break { expr, label } => {
|
||||
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"),
|
||||
None => self.current_loop_end()?,
|
||||
};
|
||||
self.set_goto(current, end);
|
||||
self.set_goto(current, end, expr_id.into());
|
||||
Ok(None)
|
||||
}
|
||||
Expr::Return { expr } => {
|
||||
|
@ -650,7 +685,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
return Ok(None);
|
||||
}
|
||||
}
|
||||
self.set_terminator(current, Terminator::Return);
|
||||
self.set_terminator(current, TerminatorKind::Return, expr_id.into());
|
||||
Ok(None)
|
||||
}
|
||||
Expr::Yield { .. } => not_supported!("yield"),
|
||||
|
@ -672,7 +707,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
Some(p) => MirLowerError::UnresolvedName(p.display(self.db).to_string()),
|
||||
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(),
|
||||
_ => 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());
|
||||
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, .. } => {
|
||||
let Some((p, current)) = self.lower_expr_as_place_without_adjust(current, expr_id, true)? else {
|
||||
return Ok(None);
|
||||
|
@ -784,20 +829,63 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
},
|
||||
Expr::BinaryOp { lhs, rhs, op } => {
|
||||
let op = op.ok_or(MirLowerError::IncompleteExpr)?;
|
||||
if let hir_def::hir::BinaryOp::Assignment { op } = op {
|
||||
if op.is_some() {
|
||||
not_supported!("assignment with arith op (like +=)");
|
||||
let is_builtin = {
|
||||
// Without adjust here is a hack. We assume that we know every possible adjustment
|
||||
// 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)?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
self.push_assignment(current, lhs_place, rhs_op.into(), expr_id.into());
|
||||
return Ok(Some(current));
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
self.push_assignment(current, lhs_place, rhs_op.into(), expr_id.into());
|
||||
return Ok(Some(current));
|
||||
}
|
||||
}
|
||||
let Some((lhs_op, current)) = self.lower_expr_to_some_operand(*lhs, current)? else {
|
||||
return Ok(None);
|
||||
|
@ -826,7 +914,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
Ok(Some(current))
|
||||
}
|
||||
&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 {
|
||||
return Err(MirLowerError::TypeError("Range type is not adt"));
|
||||
};
|
||||
|
@ -869,7 +957,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
Ok(Some(current))
|
||||
},
|
||||
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 {
|
||||
not_supported!("closure with non closure type");
|
||||
};
|
||||
|
@ -893,7 +981,12 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
};
|
||||
match &capture.kind {
|
||||
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(
|
||||
current,
|
||||
tmp.clone(),
|
||||
|
@ -928,7 +1021,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
return Ok(None);
|
||||
};
|
||||
let r = Rvalue::Aggregate(
|
||||
AggregateKind::Tuple(self.expr_ty(expr_id)),
|
||||
AggregateKind::Tuple(self.expr_ty_without_adjust(expr_id)),
|
||||
values,
|
||||
);
|
||||
self.push_assignment(current, place, r, expr_id.into());
|
||||
|
@ -936,7 +1029,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
}
|
||||
Expr::Array(l) => match l {
|
||||
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(),
|
||||
_ => {
|
||||
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 {
|
||||
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(),
|
||||
_ => {
|
||||
return Err(MirLowerError::TypeError(
|
||||
|
@ -982,7 +1075,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
},
|
||||
},
|
||||
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)?;
|
||||
self.push_assignment(current, place, op.into(), expr_id.into());
|
||||
Ok(Some(current))
|
||||
|
@ -1057,8 +1150,30 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
place: Place,
|
||||
subst: Substitution,
|
||||
span: MirSpan,
|
||||
ty: Ty,
|
||||
) -> 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)
|
||||
}
|
||||
|
||||
|
@ -1114,6 +1229,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
place: Place,
|
||||
mut current: BasicBlockId,
|
||||
is_uninhabited: bool,
|
||||
span: MirSpan,
|
||||
) -> Result<Option<BasicBlockId>> {
|
||||
let Some(args) = args
|
||||
.map(|arg| {
|
||||
|
@ -1128,7 +1244,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
else {
|
||||
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(
|
||||
|
@ -1138,11 +1254,12 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
place: Place,
|
||||
current: BasicBlockId,
|
||||
is_uninhabited: bool,
|
||||
span: MirSpan,
|
||||
) -> Result<Option<BasicBlockId>> {
|
||||
let b = if is_uninhabited { None } else { Some(self.new_basic_block()) };
|
||||
self.set_terminator(
|
||||
current,
|
||||
Terminator::Call {
|
||||
TerminatorKind::Call {
|
||||
func,
|
||||
args,
|
||||
destination: place,
|
||||
|
@ -1150,6 +1267,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
cleanup: None,
|
||||
from_hir_call: true,
|
||||
},
|
||||
span,
|
||||
);
|
||||
Ok(b)
|
||||
}
|
||||
|
@ -1158,15 +1276,15 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
self.result.basic_blocks[source].terminator.is_none()
|
||||
}
|
||||
|
||||
fn set_terminator(&mut self, source: BasicBlockId, terminator: Terminator) {
|
||||
self.result.basic_blocks[source].terminator = Some(terminator);
|
||||
fn set_terminator(&mut self, source: BasicBlockId, terminator: TerminatorKind, span: MirSpan) {
|
||||
self.result.basic_blocks[source].terminator = Some(Terminator { span, kind: terminator });
|
||||
}
|
||||
|
||||
fn set_goto(&mut self, source: BasicBlockId, target: BasicBlockId) {
|
||||
self.set_terminator(source, Terminator::Goto { target });
|
||||
fn set_goto(&mut self, source: BasicBlockId, target: BasicBlockId, span: MirSpan) {
|
||||
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()
|
||||
}
|
||||
|
||||
|
@ -1177,7 +1295,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
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) {
|
||||
|
@ -1211,6 +1329,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
prev_block: BasicBlockId,
|
||||
place: Place,
|
||||
label: Option<LabelId>,
|
||||
span: MirSpan,
|
||||
f: impl FnOnce(&mut MirLowerCtx<'_>, BasicBlockId) -> Result<()>,
|
||||
) -> Result<Option<BasicBlockId>> {
|
||||
let begin = self.new_basic_block();
|
||||
|
@ -1228,7 +1347,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
} else {
|
||||
None
|
||||
};
|
||||
self.set_goto(prev_block, begin);
|
||||
self.set_goto(prev_block, begin, span);
|
||||
f(self, begin)?;
|
||||
let my = mem::replace(&mut self.current_loop_blocks, prev).ok_or(
|
||||
MirLowerError::ImplementationError("current_loop_blocks is corrupt".to_string()),
|
||||
|
@ -1247,14 +1366,15 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
&mut self,
|
||||
b1: Option<BasicBlockId>,
|
||||
b2: Option<BasicBlockId>,
|
||||
span: MirSpan,
|
||||
) -> Option<BasicBlockId> {
|
||||
match (b1, b2) {
|
||||
(None, None) => None,
|
||||
(None, Some(b)) | (Some(b), None) => Some(b),
|
||||
(Some(b1), Some(b2)) => {
|
||||
let bm = self.new_basic_block();
|
||||
self.set_goto(b1, bm);
|
||||
self.set_goto(b2, bm);
|
||||
self.set_goto(b1, bm, span);
|
||||
self.set_goto(b2, bm, span);
|
||||
Some(bm)
|
||||
}
|
||||
}
|
||||
|
@ -1332,6 +1452,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
mut current: BasicBlockId,
|
||||
tail: Option<ExprId>,
|
||||
place: Place,
|
||||
span: MirSpan,
|
||||
) -> Result<Option<Idx<BasicBlock>>> {
|
||||
for statement in statements.iter() {
|
||||
match statement {
|
||||
|
@ -1355,13 +1476,13 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||
match (else_block, else_branch) {
|
||||
(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)) => {
|
||||
if let Some((_, b)) =
|
||||
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,
|
||||
)?;
|
||||
if let Some(b) = r.1 {
|
||||
self.set_terminator(b, Terminator::Unreachable);
|
||||
self.set_terminator(b, TerminatorKind::Unreachable, param.into());
|
||||
}
|
||||
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> {
|
||||
|
@ -1498,7 +1631,7 @@ pub fn mir_body_for_closure_query(
|
|||
// 0 is return local
|
||||
ctx.result.locals.alloc(Local { ty: infer[*root].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");
|
||||
};
|
||||
let current = ctx.lower_params_and_bindings(
|
||||
|
@ -1506,7 +1639,7 @@ pub fn mir_body_for_closure_query(
|
|||
|_| true,
|
||||
)?;
|
||||
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();
|
||||
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)?
|
||||
};
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! MIR lowering for places
|
||||
|
||||
use super::*;
|
||||
use hir_def::FunctionId;
|
||||
use hir_def::{lang_item::lang_attr, FunctionId};
|
||||
use hir_expand::name;
|
||||
|
||||
macro_rules! not_supported {
|
||||
|
@ -16,7 +16,7 @@ impl MirLowerCtx<'_> {
|
|||
expr_id: ExprId,
|
||||
prev_block: 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 Some(current) = self.lower_expr_to_place_without_adjust(expr_id, place.into(), prev_block)? else {
|
||||
return Ok(None);
|
||||
|
@ -30,8 +30,10 @@ impl MirLowerCtx<'_> {
|
|||
prev_block: BasicBlockId,
|
||||
adjustments: &[Adjustment],
|
||||
) -> Result<Option<(Place, BasicBlockId)>> {
|
||||
let ty =
|
||||
adjustments.last().map(|x| x.target.clone()).unwrap_or_else(|| self.expr_ty(expr_id));
|
||||
let ty = adjustments
|
||||
.last()
|
||||
.map(|x| x.target.clone())
|
||||
.unwrap_or_else(|| self.expr_ty_without_adjust(expr_id));
|
||||
let place = self.temp(ty)?;
|
||||
let Some(current) = self.lower_expr_to_place_with_adjust(expr_id, place.into(), prev_block, adjustments)? else {
|
||||
return Ok(None);
|
||||
|
@ -80,7 +82,7 @@ impl MirLowerCtx<'_> {
|
|||
r,
|
||||
rest.last()
|
||||
.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(),
|
||||
expr_id.into(),
|
||||
match od.0 {
|
||||
|
@ -135,17 +137,39 @@ impl MirLowerCtx<'_> {
|
|||
};
|
||||
match pr {
|
||||
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),
|
||||
}
|
||||
}
|
||||
Expr::UnaryOp { expr, op } => match op {
|
||||
hir_def::hir::UnaryOp::Deref => {
|
||||
if !matches!(
|
||||
self.expr_ty(*expr).kind(Interner),
|
||||
TyKind::Ref(..) | TyKind::Raw(..)
|
||||
) {
|
||||
let is_builtin = match self.expr_ty_without_adjust(*expr).kind(Interner) {
|
||||
TyKind::Ref(..) | TyKind::Raw(..) => true,
|
||||
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 {
|
||||
return Ok(None);
|
||||
};
|
||||
|
@ -153,7 +177,7 @@ impl MirLowerCtx<'_> {
|
|||
current,
|
||||
p,
|
||||
self.expr_ty_after_adjustments(*expr),
|
||||
self.expr_ty(expr_id),
|
||||
self.expr_ty_without_adjust(expr_id),
|
||||
expr_id.into(),
|
||||
'b: {
|
||||
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 {
|
||||
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 {
|
||||
return Ok(None);
|
||||
|
@ -210,7 +234,7 @@ impl MirLowerCtx<'_> {
|
|||
current,
|
||||
base_place,
|
||||
base_ty,
|
||||
self.expr_ty(expr_id),
|
||||
self.expr_ty_without_adjust(expr_id),
|
||||
index_operand,
|
||||
expr_id.into(),
|
||||
index_fn,
|
||||
|
@ -266,7 +290,7 @@ impl MirLowerCtx<'_> {
|
|||
)
|
||||
.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);
|
||||
};
|
||||
result.projection.push(ProjectionElem::Deref);
|
||||
|
@ -313,7 +337,7 @@ impl MirLowerCtx<'_> {
|
|||
.intern(Interner),
|
||||
);
|
||||
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);
|
||||
};
|
||||
result.projection.push(ProjectionElem::Deref);
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
//! MIR lowering for patterns
|
||||
|
||||
use hir_def::resolver::HasResolver;
|
||||
|
||||
use crate::utils::pattern_matching_dereference_count;
|
||||
|
||||
use super::*;
|
||||
|
@ -72,7 +74,7 @@ impl MirLowerCtx<'_> {
|
|||
*pat,
|
||||
binding_mode,
|
||||
)?;
|
||||
self.set_goto(next, then_target);
|
||||
self.set_goto(next, then_target, pattern.into());
|
||||
match next_else {
|
||||
Some(t) => {
|
||||
current = t;
|
||||
|
@ -85,13 +87,13 @@ impl MirLowerCtx<'_> {
|
|||
}
|
||||
if !finished {
|
||||
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)
|
||||
}
|
||||
Pat::Record { args, .. } => {
|
||||
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(
|
||||
cond_ty,
|
||||
|
@ -106,11 +108,8 @@ impl MirLowerCtx<'_> {
|
|||
}
|
||||
Pat::Range { .. } => not_supported!("range pattern"),
|
||||
Pat::Slice { .. } => not_supported!("slice pattern"),
|
||||
Pat::Path(_) => {
|
||||
let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
|
||||
not_supported!("unresolved variant");
|
||||
};
|
||||
self.pattern_matching_variant(
|
||||
Pat::Path(p) => match self.infer.variant_resolution_for_pat(pattern) {
|
||||
Some(variant) => self.pattern_matching_variant(
|
||||
cond_ty,
|
||||
binding_mode,
|
||||
cond_place,
|
||||
|
@ -119,8 +118,60 @@ impl MirLowerCtx<'_> {
|
|||
pattern.into(),
|
||||
current_else,
|
||||
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] {
|
||||
Expr::Literal(l) => {
|
||||
let c = self.lower_literal_to_operand(cond_ty, l)?;
|
||||
|
@ -218,10 +269,11 @@ impl MirLowerCtx<'_> {
|
|||
let discr = Operand::Copy(discr);
|
||||
self.set_terminator(
|
||||
current,
|
||||
Terminator::SwitchInt {
|
||||
TerminatorKind::SwitchInt {
|
||||
discr,
|
||||
targets: SwitchTargets::static_if(1, then_target, else_target),
|
||||
},
|
||||
pattern.into(),
|
||||
);
|
||||
Ok((then_target, Some(else_target)))
|
||||
}
|
||||
|
@ -244,8 +296,7 @@ impl MirLowerCtx<'_> {
|
|||
};
|
||||
Ok(match variant {
|
||||
VariantId::EnumVariantId(v) => {
|
||||
let e = self.db.const_eval_discriminant(v)? as u128;
|
||||
let next = self.new_basic_block();
|
||||
let e = self.const_eval_discriminant(v)? as u128;
|
||||
let tmp = self.discr_temp_place();
|
||||
self.push_assignment(
|
||||
current,
|
||||
|
@ -253,13 +304,15 @@ impl MirLowerCtx<'_> {
|
|||
Rvalue::Discriminant(cond_place.clone()),
|
||||
span,
|
||||
);
|
||||
let next = self.new_basic_block();
|
||||
let else_target = current_else.unwrap_or_else(|| self.new_basic_block());
|
||||
self.set_terminator(
|
||||
current,
|
||||
Terminator::SwitchInt {
|
||||
TerminatorKind::SwitchInt {
|
||||
discr: Operand::Copy(tmp),
|
||||
targets: SwitchTargets::static_if(e, next, else_target),
|
||||
},
|
||||
span,
|
||||
);
|
||||
let enum_data = self.db.enum_data(v.parent);
|
||||
self.pattern_matching_variant_fields(
|
||||
|
|
|
@ -11,19 +11,52 @@ use la_arena::ArenaMap;
|
|||
|
||||
use crate::{
|
||||
db::HirDatabase,
|
||||
display::HirDisplay,
|
||||
mir::{PlaceElem, ProjectionElem, StatementKind, Terminator},
|
||||
display::{ClosureStyle, HirDisplay},
|
||||
mir::{PlaceElem, ProjectionElem, StatementKind, TerminatorKind},
|
||||
ClosureId,
|
||||
};
|
||||
|
||||
use super::{
|
||||
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 {
|
||||
pub fn pretty_print(&self, db: &dyn HirDatabase) -> String {
|
||||
let hir_body = db.body(self.owner);
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -49,21 +82,6 @@ struct MirPrettyCtx<'a> {
|
|||
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<'_> {
|
||||
fn write_str(&mut self, s: &str) -> std::fmt::Result {
|
||||
let mut it = s.split('\n'); // note: `.lines()` is wrong here
|
||||
|
@ -91,36 +109,40 @@ impl Display for LocalName {
|
|||
}
|
||||
|
||||
impl<'a> MirPrettyCtx<'a> {
|
||||
fn for_body(&mut self, name: impl Debug) {
|
||||
wln!(self, "// {:?}", name);
|
||||
fn for_body(&mut self, name: impl FnOnce(&mut MirPrettyCtx<'_>)) {
|
||||
name(self);
|
||||
self.with_block(|this| {
|
||||
this.locals();
|
||||
wln!(this);
|
||||
this.blocks();
|
||||
});
|
||||
for &closure in &self.body.closures {
|
||||
let body = match self.db.mir_body_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;
|
||||
self.for_closure(closure);
|
||||
}
|
||||
}
|
||||
|
||||
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<'_>)) {
|
||||
self.indent += " ";
|
||||
wln!(self, "{{");
|
||||
|
@ -155,7 +177,7 @@ impl<'a> MirPrettyCtx<'a> {
|
|||
|
||||
fn locals(&mut self) {
|
||||
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 {
|
||||
Some(terminator) => match terminator {
|
||||
Terminator::Goto { target } => {
|
||||
Some(terminator) => match &terminator.kind {
|
||||
TerminatorKind::Goto { target } => {
|
||||
wln!(this, "goto 'bb{};", u32::from(target.into_raw()))
|
||||
}
|
||||
Terminator::SwitchInt { discr, targets } => {
|
||||
TerminatorKind::SwitchInt { discr, targets } => {
|
||||
w!(this, "switch ");
|
||||
this.operand(discr);
|
||||
w!(this, " ");
|
||||
|
@ -213,7 +235,7 @@ impl<'a> MirPrettyCtx<'a> {
|
|||
wln!(this, "_ => {},", this.basic_block_id(targets.otherwise()));
|
||||
});
|
||||
}
|
||||
Terminator::Call { func, args, destination, target, .. } => {
|
||||
TerminatorKind::Call { func, args, destination, target, .. } => {
|
||||
w!(this, "Call ");
|
||||
this.with_block(|this| {
|
||||
w!(this, "func: ");
|
||||
|
@ -295,7 +317,8 @@ impl<'a> MirPrettyCtx<'a> {
|
|||
// equally. Feel free to change it.
|
||||
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) => {
|
||||
w!(self, "Cast({ck:?}, ");
|
||||
self.operand(op);
|
||||
w!(self, ", {})", ty.display(self.db));
|
||||
w!(self, ", {})", self.hir_display(ty));
|
||||
}
|
||||
Rvalue::CheckedBinaryOp(b, o1, o2) => {
|
||||
self.operand(o1);
|
||||
|
@ -369,6 +392,7 @@ impl<'a> MirPrettyCtx<'a> {
|
|||
self.place(p);
|
||||
w!(self, ")");
|
||||
}
|
||||
Rvalue::ShallowInitBoxWithAlloc(_) => w!(self, "ShallowInitBoxWithAlloc"),
|
||||
Rvalue::ShallowInitBox(op, _) => {
|
||||
w!(self, "ShallowInitBox(");
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,10 +23,11 @@ use hir_expand::name::Name;
|
|||
use intern::Interned;
|
||||
use rustc_hash::FxHashSet;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use stdx::never;
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase, ChalkTraitId, Interner, Substitution, TraitRef, TraitRefExt, Ty, TyExt,
|
||||
WhereClause,
|
||||
db::HirDatabase, ChalkTraitId, GenericArg, Interner, Substitution, TraitRef, TraitRefExt, Ty,
|
||||
TyExt, WhereClause,
|
||||
};
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
/// 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)]
|
||||
pub(crate) struct Generics {
|
||||
def: GenericDefId,
|
||||
|
|
|
@ -1856,11 +1856,21 @@ impl Function {
|
|||
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
|
||||
.mir_body(self.id.into())
|
||||
.map_err(|e| MirEvalError::MirLowerError(self.id.into(), e))?;
|
||||
interpret_mir(db, &body, Substitution::empty(Interner), false)?;
|
||||
.map_err(|e| MirEvalError::MirLowerError(self.id.into(), e))
|
||||
.map_err(converter)?;
|
||||
interpret_mir(db, &body, Substitution::empty(Interner), false).map_err(converter)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -2006,7 +2016,7 @@ impl Const {
|
|||
}
|
||||
|
||||
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));
|
||||
// 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
|
||||
|
|
|
@ -30,7 +30,6 @@ pub struct HoverConfig {
|
|||
pub documentation: bool,
|
||||
pub keywords: bool,
|
||||
pub format: HoverDocFormat,
|
||||
pub interpret_tests: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
|
|
|
@ -3,8 +3,7 @@ use std::fmt::Display;
|
|||
|
||||
use either::Either;
|
||||
use hir::{
|
||||
db::DefDatabase, Adt, AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay,
|
||||
MirEvalError, Semantics, TypeInfo,
|
||||
Adt, AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo,
|
||||
};
|
||||
use ide_db::{
|
||||
base_db::SourceDatabase,
|
||||
|
@ -435,19 +434,7 @@ pub(super) fn definition(
|
|||
))
|
||||
}),
|
||||
Definition::Module(it) => label_and_docs(db, it),
|
||||
Definition::Function(it) => label_and_layout_info_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::Function(it) => label_and_docs(db, it),
|
||||
Definition::Adt(it) => label_and_layout_info_and_docs(db, it, |&it| {
|
||||
let layout = it.layout(db).ok()?;
|
||||
Some(format!("size = {}, align = {}", layout.size.bytes(), layout.align.abi.bytes()))
|
||||
|
|
|
@ -9,7 +9,6 @@ const HOVER_BASE_CONFIG: HoverConfig = HoverConfig {
|
|||
documentation: true,
|
||||
format: HoverDocFormat::Markdown,
|
||||
keywords: true,
|
||||
interpret_tests: false,
|
||||
};
|
||||
|
||||
fn check_hover_no_result(ra_fixture: &str) {
|
||||
|
|
|
@ -3,7 +3,10 @@
|
|||
//! let _: u32 = /* <never-to-any> */ loop {};
|
||||
//! 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 stdx::never;
|
||||
|
@ -88,7 +91,13 @@ pub(super) fn hints(
|
|||
Adjust::NeverToAny if config.adjustment_hints == AdjustmentHints::Always => {
|
||||
("<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::Mut)) => ("&mut ", "unique borrow"),
|
||||
Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Shared)) => {
|
||||
|
|
|
@ -444,7 +444,7 @@ fn main() {
|
|||
file_id: FileId(
|
||||
1,
|
||||
),
|
||||
range: 5768..5776,
|
||||
range: 5769..5777,
|
||||
},
|
||||
),
|
||||
tooltip: "",
|
||||
|
@ -457,7 +457,7 @@ fn main() {
|
|||
file_id: FileId(
|
||||
1,
|
||||
),
|
||||
range: 5800..5804,
|
||||
range: 5801..5805,
|
||||
},
|
||||
),
|
||||
tooltip: "",
|
||||
|
@ -478,7 +478,7 @@ fn main() {
|
|||
file_id: FileId(
|
||||
1,
|
||||
),
|
||||
range: 5768..5776,
|
||||
range: 5769..5777,
|
||||
},
|
||||
),
|
||||
tooltip: "",
|
||||
|
@ -491,7 +491,7 @@ fn main() {
|
|||
file_id: FileId(
|
||||
1,
|
||||
),
|
||||
range: 5800..5804,
|
||||
range: 5801..5805,
|
||||
},
|
||||
),
|
||||
tooltip: "",
|
||||
|
@ -512,7 +512,7 @@ fn main() {
|
|||
file_id: FileId(
|
||||
1,
|
||||
),
|
||||
range: 5768..5776,
|
||||
range: 5769..5777,
|
||||
},
|
||||
),
|
||||
tooltip: "",
|
||||
|
@ -525,7 +525,7 @@ fn main() {
|
|||
file_id: FileId(
|
||||
1,
|
||||
),
|
||||
range: 5800..5804,
|
||||
range: 5801..5805,
|
||||
},
|
||||
),
|
||||
tooltip: "",
|
||||
|
|
49
crates/ide/src/interpret_function.rs
Normal file
49
crates/ide/src/interpret_function.rs
Normal 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),
|
||||
}
|
||||
}
|
|
@ -56,6 +56,7 @@ mod typing;
|
|||
mod view_crate_graph;
|
||||
mod view_hir;
|
||||
mod view_mir;
|
||||
mod interpret_function;
|
||||
mod view_item_tree;
|
||||
mod shuffle_crate_graph;
|
||||
|
||||
|
@ -317,6 +318,10 @@ impl Analysis {
|
|||
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> {
|
||||
self.with_db(|db| view_item_tree::view_item_tree(db, file_id))
|
||||
}
|
||||
|
|
|
@ -140,7 +140,6 @@ impl StaticIndex<'_> {
|
|||
documentation: true,
|
||||
keywords: true,
|
||||
format: crate::HoverDocFormat::Markdown,
|
||||
interpret_tests: false,
|
||||
};
|
||||
let tokens = tokens.filter(|token| {
|
||||
matches!(
|
||||
|
|
|
@ -574,6 +574,7 @@ pub struct LensConfig {
|
|||
// runnables
|
||||
pub run: bool,
|
||||
pub debug: bool,
|
||||
pub interpret: bool,
|
||||
|
||||
// implementations
|
||||
pub implementations: bool,
|
||||
|
@ -1423,6 +1424,9 @@ impl Config {
|
|||
LensConfig {
|
||||
run: self.data.lens_enable && self.data.lens_run_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,
|
||||
method_refs: self.data.lens_enable && self.data.lens_references_method_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,
|
||||
interpret_tests: self.data.interpret_tests,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -163,6 +163,16 @@ pub(crate) fn handle_view_mir(
|
|||
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(
|
||||
snap: GlobalStateSnapshot,
|
||||
params: lsp_types::TextDocumentIdentifier,
|
||||
|
|
|
@ -90,6 +90,14 @@ impl Request for 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 {}
|
||||
|
||||
impl Request for ViewFileText {
|
||||
|
|
|
@ -665,6 +665,7 @@ impl GlobalState {
|
|||
.on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree)
|
||||
.on::<lsp_ext::ViewHir>(handlers::handle_view_hir)
|
||||
.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::ViewCrateGraph>(handlers::handle_view_crate_graph)
|
||||
.on::<lsp_ext::ViewItemTree>(handlers::handle_view_item_tree)
|
||||
|
|
|
@ -1215,6 +1215,14 @@ pub(crate) fn code_lens(
|
|||
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 } => {
|
||||
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(
|
||||
snap: &GlobalStateSnapshot,
|
||||
nav: &NavigationTarget,
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
//! add:
|
||||
//! as_ref: sized
|
||||
//! bool_impl: option, fn
|
||||
//! cell: copy, drop
|
||||
//! clone: sized
|
||||
//! coerce_unsized: unsize
|
||||
//! copy: clone
|
||||
|
@ -139,6 +140,52 @@ pub mod 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
|
||||
pub mod clone {
|
||||
#[lang = "clone"]
|
||||
|
@ -220,6 +267,13 @@ pub mod mem {
|
|||
// endregion:manually_drop
|
||||
|
||||
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
|
||||
|
||||
|
@ -710,6 +764,14 @@ pub mod option {
|
|||
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
|
||||
pub fn and_then<U, F>(self, f: F) -> Option<U>
|
||||
where
|
||||
|
|
|
@ -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
|
||||
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.
|
||||
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
|
||||
|
||||
**Method:** `rust-analyzer/viewFileText`
|
||||
|
|
|
@ -120,6 +120,11 @@
|
|||
"title": "View Mir",
|
||||
"category": "rust-analyzer (debug command)"
|
||||
},
|
||||
{
|
||||
"command": "rust-analyzer.interpretFunction",
|
||||
"title": "Interpret Function",
|
||||
"category": "rust-analyzer (debug command)"
|
||||
},
|
||||
{
|
||||
"command": "rust-analyzer.viewFileText",
|
||||
"title": "View File Text (as seen by the server)",
|
||||
|
|
|
@ -422,8 +422,20 @@ export function syntaxTree(ctx: CtxInit): Cmd {
|
|||
function viewHirOrMir(ctx: CtxInit, xir: "hir" | "mir"): Cmd {
|
||||
const viewXir = xir === "hir" ? "viewHir" : "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 {
|
||||
readonly uri = vscode.Uri.parse(`rust-analyzer-${xir}://${viewXir}/${xir}.rs`);
|
||||
readonly uri = vscode.Uri.parse(uri);
|
||||
readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
|
||||
constructor() {
|
||||
vscode.workspace.onDidChangeTextDocument(
|
||||
|
@ -439,14 +451,14 @@ function viewHirOrMir(ctx: CtxInit, xir: "hir" | "mir"): Cmd {
|
|||
}
|
||||
|
||||
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.
|
||||
// Hence, good old sleep().
|
||||
void sleep(10).then(() => this.eventEmitter.fire(this.uri));
|
||||
}
|
||||
}
|
||||
private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) {
|
||||
if (editor && isRustEditor(editor)) {
|
||||
if (editor && isRustEditor(editor) && shouldUpdate) {
|
||||
this.eventEmitter.fire(this.uri);
|
||||
}
|
||||
}
|
||||
|
@ -473,9 +485,7 @@ function viewHirOrMir(ctx: CtxInit, xir: "hir" | "mir"): Cmd {
|
|||
}
|
||||
})();
|
||||
|
||||
ctx.pushExtCleanup(
|
||||
vscode.workspace.registerTextDocumentContentProvider(`rust-analyzer-${xir}`, tdcp)
|
||||
);
|
||||
ctx.pushExtCleanup(vscode.workspace.registerTextDocumentContentProvider(scheme, tdcp));
|
||||
|
||||
return async () => {
|
||||
const document = await vscode.workspace.openTextDocument(tdcp.uri);
|
||||
|
@ -501,6 +511,20 @@ export function viewMir(ctx: CtxInit): Cmd {
|
|||
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 {
|
||||
const tdcp = new (class implements vscode.TextDocumentContentProvider {
|
||||
readonly uri = vscode.Uri.parse("rust-analyzer-file-text://viewFileText/file.rs");
|
||||
|
|
|
@ -64,6 +64,9 @@ export const viewHir = new lc.RequestType<lc.TextDocumentPositionParams, string,
|
|||
export const viewMir = new lc.RequestType<lc.TextDocumentPositionParams, string, void>(
|
||||
"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>(
|
||||
"rust-analyzer/viewItemTree"
|
||||
);
|
||||
|
|
|
@ -159,6 +159,7 @@ function createCommands(): Record<string, CommandFactory> {
|
|||
syntaxTree: { enabled: commands.syntaxTree },
|
||||
viewHir: { enabled: commands.viewHir },
|
||||
viewMir: { enabled: commands.viewMir },
|
||||
interpretFunction: { enabled: commands.interpretFunction },
|
||||
viewFileText: { enabled: commands.viewFileText },
|
||||
viewItemTree: { enabled: commands.viewItemTree },
|
||||
viewCrateGraph: { enabled: commands.viewCrateGraph },
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue