mirror of
https://github.com/rust-lang/rust.git
synced 2025-06-04 19:29:07 +00:00
MIR episode 4
This commit is contained in:
parent
001607fdb4
commit
6312fbf521
@ -34,7 +34,7 @@ impl fmt::Debug for Change {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Change {
|
impl Change {
|
||||||
pub fn new() -> Change {
|
pub fn new() -> Self {
|
||||||
Change::default()
|
Change::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ use rustc_hash::FxHashMap;
|
|||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{
|
ast::{
|
||||||
self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasLoopBody, HasName,
|
self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasAttrs, HasLoopBody, HasName,
|
||||||
SlicePatComponents,
|
SlicePatComponents,
|
||||||
},
|
},
|
||||||
AstNode, AstPtr, SyntaxNodePtr,
|
AstNode, AstPtr, SyntaxNodePtr,
|
||||||
@ -302,16 +302,29 @@ impl ExprCollector<'_> {
|
|||||||
self.alloc_expr(Expr::For { iterable, pat, body, label }, syntax_ptr)
|
self.alloc_expr(Expr::For { iterable, pat, body, label }, syntax_ptr)
|
||||||
}
|
}
|
||||||
ast::Expr::CallExpr(e) => {
|
ast::Expr::CallExpr(e) => {
|
||||||
let callee = self.collect_expr_opt(e.expr());
|
let is_rustc_box = {
|
||||||
let args = if let Some(arg_list) = e.arg_list() {
|
let attrs = e.attrs();
|
||||||
arg_list.args().filter_map(|e| self.maybe_collect_expr(e)).collect()
|
attrs.filter_map(|x| x.as_simple_atom()).any(|x| x == "rustc_box")
|
||||||
} else {
|
|
||||||
Box::default()
|
|
||||||
};
|
};
|
||||||
self.alloc_expr(
|
if is_rustc_box {
|
||||||
Expr::Call { callee, args, is_assignee_expr: self.is_lowering_assignee_expr },
|
let expr = self.collect_expr_opt(e.arg_list().and_then(|x| x.args().next()));
|
||||||
syntax_ptr,
|
self.alloc_expr(Expr::Box { expr }, syntax_ptr)
|
||||||
)
|
} else {
|
||||||
|
let callee = self.collect_expr_opt(e.expr());
|
||||||
|
let args = if let Some(arg_list) = e.arg_list() {
|
||||||
|
arg_list.args().filter_map(|e| self.maybe_collect_expr(e)).collect()
|
||||||
|
} else {
|
||||||
|
Box::default()
|
||||||
|
};
|
||||||
|
self.alloc_expr(
|
||||||
|
Expr::Call {
|
||||||
|
callee,
|
||||||
|
args,
|
||||||
|
is_assignee_expr: self.is_lowering_assignee_expr,
|
||||||
|
},
|
||||||
|
syntax_ptr,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ast::Expr::MethodCallExpr(e) => {
|
ast::Expr::MethodCallExpr(e) => {
|
||||||
let receiver = self.collect_expr_opt(e.receiver());
|
let receiver = self.collect_expr_opt(e.receiver());
|
||||||
|
@ -232,6 +232,25 @@ impl TyBuilder<()> {
|
|||||||
TyBuilder::new((), params, parent_subst)
|
TyBuilder::new((), params, parent_subst)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn subst_for_closure(
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
parent: DefWithBodyId,
|
||||||
|
sig_ty: Ty,
|
||||||
|
) -> Substitution {
|
||||||
|
let sig_ty = sig_ty.cast(Interner);
|
||||||
|
let self_subst = iter::once(&sig_ty);
|
||||||
|
let Some(parent) = parent.as_generic_def_id() else {
|
||||||
|
return Substitution::from_iter(Interner, self_subst);
|
||||||
|
};
|
||||||
|
Substitution::from_iter(
|
||||||
|
Interner,
|
||||||
|
self_subst
|
||||||
|
.chain(generics(db.upcast(), parent).placeholder_subst(db).iter(Interner))
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build(self) -> Substitution {
|
pub fn build(self) -> Substitution {
|
||||||
let ((), subst) = self.build_internal();
|
let ((), subst) = self.build_internal();
|
||||||
subst
|
subst
|
||||||
|
@ -24,7 +24,7 @@ use crate::{
|
|||||||
method_resolution::{TraitImpls, TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS},
|
method_resolution::{TraitImpls, TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS},
|
||||||
to_assoc_type_id, to_chalk_trait_id,
|
to_assoc_type_id, to_chalk_trait_id,
|
||||||
traits::ChalkContext,
|
traits::ChalkContext,
|
||||||
utils::generics,
|
utils::{generics, ClosureSubst},
|
||||||
wrap_empty_binders, AliasEq, AliasTy, BoundVar, CallableDefId, DebruijnIndex, FnDefId,
|
wrap_empty_binders, AliasEq, AliasTy, BoundVar, CallableDefId, DebruijnIndex, FnDefId,
|
||||||
Interner, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Substitution, TraitRef,
|
Interner, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Substitution, TraitRef,
|
||||||
TraitRefExt, Ty, TyBuilder, TyExt, TyKind, WhereClause,
|
TraitRefExt, Ty, TyBuilder, TyExt, TyKind, WhereClause,
|
||||||
@ -337,7 +337,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
|
|||||||
_closure_id: chalk_ir::ClosureId<Interner>,
|
_closure_id: chalk_ir::ClosureId<Interner>,
|
||||||
substs: &chalk_ir::Substitution<Interner>,
|
substs: &chalk_ir::Substitution<Interner>,
|
||||||
) -> chalk_ir::Binders<rust_ir::FnDefInputsAndOutputDatum<Interner>> {
|
) -> chalk_ir::Binders<rust_ir::FnDefInputsAndOutputDatum<Interner>> {
|
||||||
let sig_ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone();
|
let sig_ty = ClosureSubst(substs).sig_ty();
|
||||||
let sig = &sig_ty.callable_sig(self.db).expect("first closure param should be fn ptr");
|
let sig = &sig_ty.callable_sig(self.db).expect("first closure param should be fn ptr");
|
||||||
let io = rust_ir::FnDefInputsAndOutputDatum {
|
let io = rust_ir::FnDefInputsAndOutputDatum {
|
||||||
argument_types: sig.params().to_vec(),
|
argument_types: sig.params().to_vec(),
|
||||||
|
@ -1,20 +1,22 @@
|
|||||||
//! Various extensions traits for Chalk types.
|
//! Various extensions traits for Chalk types.
|
||||||
|
|
||||||
use chalk_ir::{FloatTy, IntTy, Mutability, Scalar, TyVariableKind, UintTy};
|
use chalk_ir::{cast::Cast, FloatTy, IntTy, Mutability, Scalar, TyVariableKind, UintTy};
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinType, BuiltinUint},
|
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinType, BuiltinUint},
|
||||||
generics::TypeOrConstParamData,
|
generics::TypeOrConstParamData,
|
||||||
lang_item::LangItem,
|
lang_item::LangItem,
|
||||||
type_ref::Rawness,
|
type_ref::Rawness,
|
||||||
FunctionId, GenericDefId, HasModule, ItemContainerId, Lookup, TraitId,
|
DefWithBodyId, FunctionId, GenericDefId, HasModule, ItemContainerId, Lookup, TraitId,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
|
db::HirDatabase,
|
||||||
from_placeholder_idx, to_chalk_trait_id, utils::generics, AdtId, AliasEq, AliasTy, Binders,
|
from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx,
|
||||||
CallableDefId, CallableSig, ClosureId, DynTy, FnPointer, ImplTraitId, Interner, Lifetime,
|
to_chalk_trait_id,
|
||||||
ProjectionTy, QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags,
|
utils::{generics, ClosureSubst},
|
||||||
WhereClause,
|
AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Canonical, CanonicalVarKinds,
|
||||||
|
ClosureId, DynTy, FnPointer, ImplTraitId, InEnvironment, Interner, Lifetime, ProjectionTy,
|
||||||
|
QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags, WhereClause,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait TyExt {
|
pub trait TyExt {
|
||||||
@ -46,6 +48,7 @@ pub trait TyExt {
|
|||||||
|
|
||||||
fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>>;
|
fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>>;
|
||||||
fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId>;
|
fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId>;
|
||||||
|
fn is_copy(self, db: &dyn HirDatabase, owner: DefWithBodyId) -> bool;
|
||||||
|
|
||||||
/// FIXME: Get rid of this, it's not a good abstraction
|
/// FIXME: Get rid of this, it's not a good abstraction
|
||||||
fn equals_ctor(&self, other: &Ty) -> bool;
|
fn equals_ctor(&self, other: &Ty) -> bool;
|
||||||
@ -185,10 +188,7 @@ impl TyExt for Ty {
|
|||||||
let sig = db.callable_item_signature(callable_def);
|
let sig = db.callable_item_signature(callable_def);
|
||||||
Some(sig.substitute(Interner, parameters))
|
Some(sig.substitute(Interner, parameters))
|
||||||
}
|
}
|
||||||
TyKind::Closure(.., substs) => {
|
TyKind::Closure(.., substs) => ClosureSubst(substs).sig_ty().callable_sig(db),
|
||||||
let sig_param = substs.at(Interner, 0).assert_ty_ref(Interner);
|
|
||||||
sig_param.callable_sig(db)
|
|
||||||
}
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -327,6 +327,20 @@ impl TyExt for Ty {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_copy(self, db: &dyn HirDatabase, owner: DefWithBodyId) -> bool {
|
||||||
|
let crate_id = owner.module(db.upcast()).krate();
|
||||||
|
let Some(copy_trait) = db.lang_item(crate_id, LangItem::Copy).and_then(|x| x.as_trait()) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let trait_ref = TyBuilder::trait_ref(db, copy_trait).push(self).build();
|
||||||
|
let env = db.trait_environment_for_body(owner);
|
||||||
|
let goal = Canonical {
|
||||||
|
value: InEnvironment::new(&env.env, trait_ref.cast(Interner)),
|
||||||
|
binders: CanonicalVarKinds::empty(Interner),
|
||||||
|
};
|
||||||
|
db.trait_solve(crate_id, None, goal).is_some()
|
||||||
|
}
|
||||||
|
|
||||||
fn equals_ctor(&self, other: &Ty) -> bool {
|
fn equals_ctor(&self, other: &Ty) -> bool {
|
||||||
match (self.kind(Interner), other.kind(Interner)) {
|
match (self.kind(Interner), other.kind(Interner)) {
|
||||||
(TyKind::Adt(adt, ..), TyKind::Adt(adt2, ..)) => adt == adt2,
|
(TyKind::Adt(adt, ..), TyKind::Adt(adt2, ..)) => adt == adt2,
|
||||||
|
@ -7,7 +7,7 @@ use hir_def::{
|
|||||||
path::Path,
|
path::Path,
|
||||||
resolver::{Resolver, ValueNs},
|
resolver::{Resolver, ValueNs},
|
||||||
type_ref::ConstRef,
|
type_ref::ConstRef,
|
||||||
ConstId, EnumVariantId,
|
DefWithBodyId, EnumVariantId,
|
||||||
};
|
};
|
||||||
use la_arena::{Idx, RawIdx};
|
use la_arena::{Idx, RawIdx};
|
||||||
use stdx::never;
|
use stdx::never;
|
||||||
@ -57,7 +57,7 @@ pub enum ConstEvalError {
|
|||||||
impl From<MirLowerError> for ConstEvalError {
|
impl From<MirLowerError> for ConstEvalError {
|
||||||
fn from(value: MirLowerError) -> Self {
|
fn from(value: MirLowerError) -> Self {
|
||||||
match value {
|
match value {
|
||||||
MirLowerError::ConstEvalError(e) => *e,
|
MirLowerError::ConstEvalError(_, e) => *e,
|
||||||
_ => ConstEvalError::MirLowerError(value),
|
_ => ConstEvalError::MirLowerError(value),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -168,7 +168,7 @@ pub fn try_const_usize(c: &Const) -> Option<u128> {
|
|||||||
pub(crate) fn const_eval_recover(
|
pub(crate) fn const_eval_recover(
|
||||||
_: &dyn HirDatabase,
|
_: &dyn HirDatabase,
|
||||||
_: &[String],
|
_: &[String],
|
||||||
_: &ConstId,
|
_: &DefWithBodyId,
|
||||||
_: &Substitution,
|
_: &Substitution,
|
||||||
) -> Result<Const, ConstEvalError> {
|
) -> Result<Const, ConstEvalError> {
|
||||||
Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
|
Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
|
||||||
@ -184,10 +184,9 @@ pub(crate) fn const_eval_discriminant_recover(
|
|||||||
|
|
||||||
pub(crate) fn const_eval_query(
|
pub(crate) fn const_eval_query(
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
const_id: ConstId,
|
def: DefWithBodyId,
|
||||||
subst: Substitution,
|
subst: Substitution,
|
||||||
) -> Result<Const, ConstEvalError> {
|
) -> Result<Const, ConstEvalError> {
|
||||||
let def = const_id.into();
|
|
||||||
let body = db.mir_body(def)?;
|
let body = db.mir_body(def)?;
|
||||||
let c = interpret_mir(db, &body, subst, false)?;
|
let c = interpret_mir(db, &body, subst, false)?;
|
||||||
Ok(c)
|
Ok(c)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use base_db::fixture::WithFixture;
|
use base_db::{fixture::WithFixture, FileId};
|
||||||
use chalk_ir::Substitution;
|
use chalk_ir::Substitution;
|
||||||
use hir_def::db::DefDatabase;
|
use hir_def::db::DefDatabase;
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ mod intrinsics;
|
|||||||
|
|
||||||
fn simplify(e: ConstEvalError) -> ConstEvalError {
|
fn simplify(e: ConstEvalError) -> ConstEvalError {
|
||||||
match e {
|
match e {
|
||||||
ConstEvalError::MirEvalError(MirEvalError::InFunction(_, e)) => {
|
ConstEvalError::MirEvalError(MirEvalError::InFunction(_, e, _, _)) => {
|
||||||
simplify(ConstEvalError::MirEvalError(*e))
|
simplify(ConstEvalError::MirEvalError(*e))
|
||||||
}
|
}
|
||||||
_ => e,
|
_ => e,
|
||||||
@ -24,13 +24,30 @@ fn simplify(e: ConstEvalError) -> ConstEvalError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn check_fail(ra_fixture: &str, error: ConstEvalError) {
|
fn check_fail(ra_fixture: &str, error: impl FnOnce(ConstEvalError) -> bool) {
|
||||||
assert_eq!(eval_goal(ra_fixture).map_err(simplify), Err(error));
|
let (db, file_id) = TestDB::with_single_file(ra_fixture);
|
||||||
|
match eval_goal(&db, file_id).map_err(simplify) {
|
||||||
|
Ok(_) => panic!("Expected fail, but it succeeded"),
|
||||||
|
Err(e) => assert!(error(e)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn check_number(ra_fixture: &str, answer: i128) {
|
fn check_number(ra_fixture: &str, answer: i128) {
|
||||||
let r = eval_goal(ra_fixture).unwrap();
|
let (db, file_id) = TestDB::with_single_file(ra_fixture);
|
||||||
|
let r = match eval_goal(&db, file_id) {
|
||||||
|
Ok(t) => t,
|
||||||
|
Err(e) => {
|
||||||
|
let mut err = String::new();
|
||||||
|
let span_formatter = |file, range| format!("{:?} {:?}", file, range);
|
||||||
|
match e {
|
||||||
|
ConstEvalError::MirLowerError(e) => e.pretty_print(&mut err, &db, span_formatter),
|
||||||
|
ConstEvalError::MirEvalError(e) => e.pretty_print(&mut err, &db, span_formatter),
|
||||||
|
}
|
||||||
|
.unwrap();
|
||||||
|
panic!("Error in evaluating goal: {}", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
match &r.data(Interner).value {
|
match &r.data(Interner).value {
|
||||||
chalk_ir::ConstValue::Concrete(c) => match &c.interned {
|
chalk_ir::ConstValue::Concrete(c) => match &c.interned {
|
||||||
ConstScalar::Bytes(b, _) => {
|
ConstScalar::Bytes(b, _) => {
|
||||||
@ -47,10 +64,9 @@ fn check_number(ra_fixture: &str, answer: i128) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_goal(ra_fixture: &str) -> Result<Const, ConstEvalError> {
|
fn eval_goal(db: &TestDB, file_id: FileId) -> Result<Const, ConstEvalError> {
|
||||||
let (db, file_id) = TestDB::with_single_file(ra_fixture);
|
|
||||||
let module_id = db.module_for_file(file_id);
|
let module_id = db.module_for_file(file_id);
|
||||||
let def_map = module_id.def_map(&db);
|
let def_map = module_id.def_map(db);
|
||||||
let scope = &def_map[module_id.local_id].scope;
|
let scope = &def_map[module_id.local_id].scope;
|
||||||
let const_id = scope
|
let const_id = scope
|
||||||
.declarations()
|
.declarations()
|
||||||
@ -65,7 +81,7 @@ fn eval_goal(ra_fixture: &str) -> Result<Const, ConstEvalError> {
|
|||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
db.const_eval(const_id, Substitution::empty(Interner))
|
db.const_eval(const_id.into(), Substitution::empty(Interner))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -303,6 +319,81 @@ fn overloaded_index() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn overloaded_binop() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: add
|
||||||
|
enum Color {
|
||||||
|
Red,
|
||||||
|
Green,
|
||||||
|
Yellow,
|
||||||
|
}
|
||||||
|
|
||||||
|
use Color::*;
|
||||||
|
|
||||||
|
impl core::ops::Add for Color {
|
||||||
|
type Output = Color;
|
||||||
|
fn add(self, rhs: Color) -> Self::Output {
|
||||||
|
Yellow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::ops::AddAssign for Color {
|
||||||
|
fn add_assign(&mut self, rhs: Color) {
|
||||||
|
*self = Red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const GOAL: bool = {
|
||||||
|
let x = Red + Green;
|
||||||
|
let mut y = Green;
|
||||||
|
y += x;
|
||||||
|
x == Yellow && y == Red && Red + Green == Yellow && Red + Red == Yellow && Yellow + Green == Yellow
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: add
|
||||||
|
impl core::ops::Add for usize {
|
||||||
|
type Output = usize;
|
||||||
|
fn add(self, rhs: usize) -> Self::Output {
|
||||||
|
self + rhs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::ops::AddAssign for usize {
|
||||||
|
fn add_assign(&mut self, rhs: usize) {
|
||||||
|
*self += rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[lang = "shl"]
|
||||||
|
pub trait Shl<Rhs = Self> {
|
||||||
|
type Output;
|
||||||
|
|
||||||
|
fn shl(self, rhs: Rhs) -> Self::Output;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Shl<u8> for usize {
|
||||||
|
type Output = usize;
|
||||||
|
|
||||||
|
fn shl(self, rhs: u8) -> Self::Output {
|
||||||
|
self << rhs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const GOAL: usize = {
|
||||||
|
let mut x = 10;
|
||||||
|
x += 20;
|
||||||
|
2 + 2 + (x << 1u8)
|
||||||
|
};"#,
|
||||||
|
64,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn function_call() {
|
fn function_call() {
|
||||||
check_number(
|
check_number(
|
||||||
@ -426,6 +517,16 @@ fn generic_fn() {
|
|||||||
"#,
|
"#,
|
||||||
12,
|
12,
|
||||||
);
|
);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
const fn y<T>(b: T) -> (T, ) {
|
||||||
|
let alloc = b;
|
||||||
|
(alloc, )
|
||||||
|
}
|
||||||
|
const GOAL: u8 = y(2).0;
|
||||||
|
"#,
|
||||||
|
2,
|
||||||
|
);
|
||||||
check_number(
|
check_number(
|
||||||
r#"
|
r#"
|
||||||
//- minicore: coerce_unsized, index, slice
|
//- minicore: coerce_unsized, index, slice
|
||||||
@ -590,6 +691,30 @@ fn loops() {
|
|||||||
"#,
|
"#,
|
||||||
8,
|
8,
|
||||||
);
|
);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: add
|
||||||
|
const GOAL: u8 = {
|
||||||
|
let mut x = 0;
|
||||||
|
'a: loop {
|
||||||
|
'b: loop {
|
||||||
|
'c: while x < 20 {
|
||||||
|
'd: while x < 5 {
|
||||||
|
'e: loop {
|
||||||
|
x += 1;
|
||||||
|
continue 'c;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
x += 1;
|
||||||
|
};
|
||||||
|
break 'a;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
x
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
20,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -790,10 +915,12 @@ fn path_pattern_matching() {
|
|||||||
|
|
||||||
use Season::*;
|
use Season::*;
|
||||||
|
|
||||||
|
const MY_SEASON: Season = Summer;
|
||||||
|
|
||||||
const fn f(x: Season) -> i32 {
|
const fn f(x: Season) -> i32 {
|
||||||
match x {
|
match x {
|
||||||
Spring => 1,
|
Spring => 1,
|
||||||
Summer => 2,
|
MY_SEASON => 2,
|
||||||
Fall => 3,
|
Fall => 3,
|
||||||
Winter => 4,
|
Winter => 4,
|
||||||
}
|
}
|
||||||
@ -944,19 +1071,10 @@ fn function_param_patterns() {
|
|||||||
fn match_guards() {
|
fn match_guards() {
|
||||||
check_number(
|
check_number(
|
||||||
r#"
|
r#"
|
||||||
//- minicore: option, eq
|
//- minicore: option
|
||||||
impl<T: PartialEq> PartialEq for Option<T> {
|
|
||||||
fn eq(&self, other: &Rhs) -> bool {
|
|
||||||
match (self, other) {
|
|
||||||
(Some(x), Some(y)) => x == y,
|
|
||||||
(None, None) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn f(x: Option<i32>) -> i32 {
|
fn f(x: Option<i32>) -> i32 {
|
||||||
match x {
|
match x {
|
||||||
y if y == Some(42) => 42000,
|
y if let Some(42) = y => 42000,
|
||||||
Some(y) => y,
|
Some(y) => y,
|
||||||
None => 10
|
None => 10
|
||||||
}
|
}
|
||||||
@ -967,6 +1085,59 @@ fn match_guards() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn result_layout_niche_optimization() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: option, result
|
||||||
|
const GOAL: i32 = match Some(2).ok_or(Some(2)) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(_) => 1000,
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
2,
|
||||||
|
);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
pub enum AlignmentEnum64 {
|
||||||
|
_Align1Shl0 = 1 << 0,
|
||||||
|
_Align1Shl1 = 1 << 1,
|
||||||
|
_Align1Shl2 = 1 << 2,
|
||||||
|
_Align1Shl3 = 1 << 3,
|
||||||
|
_Align1Shl4 = 1 << 4,
|
||||||
|
_Align1Shl5 = 1 << 5,
|
||||||
|
}
|
||||||
|
const GOAL: Result<AlignmentEnum64, ()> = {
|
||||||
|
let align = Err(());
|
||||||
|
align
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
0, // It is 0 since result is niche encoded and 1 is valid for `AlignmentEnum64`
|
||||||
|
);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
pub enum AlignmentEnum64 {
|
||||||
|
_Align1Shl0 = 1 << 0,
|
||||||
|
_Align1Shl1 = 1 << 1,
|
||||||
|
_Align1Shl2 = 1 << 2,
|
||||||
|
_Align1Shl3 = 1 << 3,
|
||||||
|
_Align1Shl4 = 1 << 4,
|
||||||
|
_Align1Shl5 = 1 << 5,
|
||||||
|
}
|
||||||
|
const GOAL: i32 = {
|
||||||
|
let align = Ok::<_, ()>(AlignmentEnum64::_Align1Shl0);
|
||||||
|
match align {
|
||||||
|
Ok(_) => 2,
|
||||||
|
Err(_) => 1,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
2,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn options() {
|
fn options() {
|
||||||
check_number(
|
check_number(
|
||||||
@ -1147,6 +1318,16 @@ fn closures() {
|
|||||||
check_number(
|
check_number(
|
||||||
r#"
|
r#"
|
||||||
//- minicore: fn, copy
|
//- minicore: fn, copy
|
||||||
|
const GOAL: i32 = {
|
||||||
|
let c: fn(i32) -> i32 = |x| 2 * x;
|
||||||
|
c(2) + c(10)
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
24,
|
||||||
|
);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: fn, copy
|
||||||
struct X(i32);
|
struct X(i32);
|
||||||
impl X {
|
impl X {
|
||||||
fn mult(&mut self, n: i32) {
|
fn mult(&mut self, n: i32) {
|
||||||
@ -1180,6 +1361,36 @@ fn closures() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn closure_and_impl_fn() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: fn, copy
|
||||||
|
fn closure_wrapper<F: FnOnce() -> i32>(c: F) -> impl FnOnce() -> F {
|
||||||
|
|| c
|
||||||
|
}
|
||||||
|
|
||||||
|
const GOAL: i32 = {
|
||||||
|
let y = 5;
|
||||||
|
let c = closure_wrapper(|| y);
|
||||||
|
c()()
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
5,
|
||||||
|
);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: fn, copy
|
||||||
|
fn f<T, F: Fn() -> T>(t: F) -> impl Fn() -> T {
|
||||||
|
move || t()
|
||||||
|
}
|
||||||
|
|
||||||
|
const GOAL: i32 = f(|| 2)();
|
||||||
|
"#,
|
||||||
|
2,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn or_pattern() {
|
fn or_pattern() {
|
||||||
check_number(
|
check_number(
|
||||||
@ -1218,6 +1429,23 @@ fn or_pattern() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn function_pointer_in_constants() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
struct Foo {
|
||||||
|
f: fn(u8) -> u8,
|
||||||
|
}
|
||||||
|
const FOO: Foo = Foo { f: add2 };
|
||||||
|
fn add2(x: u8) -> u8 {
|
||||||
|
x + 2
|
||||||
|
}
|
||||||
|
const GOAL: u8 = (FOO.f)(3);
|
||||||
|
"#,
|
||||||
|
5,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn function_pointer() {
|
fn function_pointer() {
|
||||||
check_number(
|
check_number(
|
||||||
@ -1432,6 +1660,51 @@ fn dyn_trait() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn boxes() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: coerce_unsized, deref_mut, slice
|
||||||
|
use core::ops::{Deref, DerefMut};
|
||||||
|
use core::{marker::Unsize, ops::CoerceUnsized};
|
||||||
|
|
||||||
|
#[lang = "owned_box"]
|
||||||
|
pub struct Box<T: ?Sized> {
|
||||||
|
inner: *mut T,
|
||||||
|
}
|
||||||
|
impl<T> Box<T> {
|
||||||
|
fn new(t: T) -> Self {
|
||||||
|
#[rustc_box]
|
||||||
|
Box::new(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> Deref for Box<T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &T {
|
||||||
|
&**self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> DerefMut for Box<T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut T {
|
||||||
|
&mut **self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Box<U>> for Box<T> {}
|
||||||
|
|
||||||
|
const GOAL: usize = {
|
||||||
|
let x = Box::new(5);
|
||||||
|
let y: Box<[i32]> = Box::new([1, 2, 3]);
|
||||||
|
*x + y.len()
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
8,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn array_and_index() {
|
fn array_and_index() {
|
||||||
check_number(
|
check_number(
|
||||||
@ -1504,6 +1777,37 @@ fn consts() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn statics() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: cell
|
||||||
|
use core::cell::Cell;
|
||||||
|
fn f() -> i32 {
|
||||||
|
static S: Cell<i32> = Cell::new(10);
|
||||||
|
S.set(S.get() + 1);
|
||||||
|
S.get()
|
||||||
|
}
|
||||||
|
const GOAL: i32 = f() + f() + f();
|
||||||
|
"#,
|
||||||
|
36,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn extern_weak_statics() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
extern "C" {
|
||||||
|
#[linkage = "extern_weak"]
|
||||||
|
static __dso_handle: *mut u8;
|
||||||
|
}
|
||||||
|
const GOAL: usize = __dso_handle as usize;
|
||||||
|
"#,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn enums() {
|
fn enums() {
|
||||||
check_number(
|
check_number(
|
||||||
@ -1531,13 +1835,13 @@ fn enums() {
|
|||||||
"#,
|
"#,
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
let r = eval_goal(
|
let (db, file_id) = TestDB::with_single_file(
|
||||||
r#"
|
r#"
|
||||||
enum E { A = 1, B }
|
enum E { A = 1, B }
|
||||||
const GOAL: E = E::A;
|
const GOAL: E = E::A;
|
||||||
"#,
|
"#,
|
||||||
)
|
);
|
||||||
.unwrap();
|
let r = eval_goal(&db, file_id).unwrap();
|
||||||
assert_eq!(try_const_usize(&r), Some(1));
|
assert_eq!(try_const_usize(&r), Some(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1550,7 +1854,7 @@ fn const_loop() {
|
|||||||
const F2: i32 = 2 * F1;
|
const F2: i32 = 2 * F1;
|
||||||
const GOAL: i32 = F3;
|
const GOAL: i32 = F3;
|
||||||
"#,
|
"#,
|
||||||
ConstEvalError::MirLowerError(MirLowerError::Loop),
|
|e| e == ConstEvalError::MirLowerError(MirLowerError::Loop),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1609,8 +1913,7 @@ fn const_generic_subst_assoc_const_impl() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn const_trait_assoc() {
|
fn const_trait_assoc() {
|
||||||
// FIXME: this should evaluate to 0
|
check_number(
|
||||||
check_fail(
|
|
||||||
r#"
|
r#"
|
||||||
struct U0;
|
struct U0;
|
||||||
trait ToConst {
|
trait ToConst {
|
||||||
@ -1619,9 +1922,35 @@ fn const_trait_assoc() {
|
|||||||
impl ToConst for U0 {
|
impl ToConst for U0 {
|
||||||
const VAL: usize = 0;
|
const VAL: usize = 0;
|
||||||
}
|
}
|
||||||
const GOAL: usize = U0::VAL;
|
impl ToConst for i32 {
|
||||||
|
const VAL: usize = 32;
|
||||||
|
}
|
||||||
|
const GOAL: usize = U0::VAL + i32::VAL;
|
||||||
"#,
|
"#,
|
||||||
ConstEvalError::MirLowerError(MirLowerError::IncompleteExpr),
|
32,
|
||||||
|
);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
struct S<T>(*mut T);
|
||||||
|
|
||||||
|
trait MySized: Sized {
|
||||||
|
const SIZE: S<Self> = S(1 as *mut Self);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MySized for i32 {
|
||||||
|
const SIZE: S<i32> = S(10 as *mut i32);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MySized for i64 {
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn f<T: MySized>() -> usize {
|
||||||
|
T::SIZE.0 as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
const GOAL: usize = f::<i32>() + f::<i64>() * 2;
|
||||||
|
"#,
|
||||||
|
12,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1631,7 +1960,7 @@ fn exec_limits() {
|
|||||||
r#"
|
r#"
|
||||||
const GOAL: usize = loop {};
|
const GOAL: usize = loop {};
|
||||||
"#,
|
"#,
|
||||||
ConstEvalError::MirEvalError(MirEvalError::ExecutionLimitExceeded),
|
|e| e == ConstEvalError::MirEvalError(MirEvalError::ExecutionLimitExceeded),
|
||||||
);
|
);
|
||||||
check_fail(
|
check_fail(
|
||||||
r#"
|
r#"
|
||||||
@ -1640,7 +1969,7 @@ fn exec_limits() {
|
|||||||
}
|
}
|
||||||
const GOAL: i32 = f(0);
|
const GOAL: i32 = f(0);
|
||||||
"#,
|
"#,
|
||||||
ConstEvalError::MirEvalError(MirEvalError::StackOverflow),
|
|e| e == ConstEvalError::MirEvalError(MirEvalError::StackOverflow),
|
||||||
);
|
);
|
||||||
// Reasonable code should still work
|
// Reasonable code should still work
|
||||||
check_number(
|
check_number(
|
||||||
@ -1665,7 +1994,7 @@ fn exec_limits() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn type_error() {
|
fn type_error() {
|
||||||
let e = eval_goal(
|
check_fail(
|
||||||
r#"
|
r#"
|
||||||
const GOAL: u8 = {
|
const GOAL: u8 = {
|
||||||
let x: u16 = 2;
|
let x: u16 = 2;
|
||||||
@ -1673,6 +2002,6 @@ fn type_error() {
|
|||||||
y.0
|
y.0
|
||||||
};
|
};
|
||||||
"#,
|
"#,
|
||||||
|
|e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::TypeMismatch(_))),
|
||||||
);
|
);
|
||||||
assert!(matches!(e, Err(ConstEvalError::MirLowerError(MirLowerError::TypeMismatch(_)))));
|
|
||||||
}
|
}
|
||||||
|
@ -67,6 +67,135 @@ fn wrapping_add() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn allocator() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
extern "Rust" {
|
||||||
|
#[rustc_allocator]
|
||||||
|
fn __rust_alloc(size: usize, align: usize) -> *mut u8;
|
||||||
|
#[rustc_deallocator]
|
||||||
|
fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize);
|
||||||
|
#[rustc_reallocator]
|
||||||
|
fn __rust_realloc(ptr: *mut u8, old_size: usize, align: usize, new_size: usize) -> *mut u8;
|
||||||
|
#[rustc_allocator_zeroed]
|
||||||
|
fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GOAL: u8 = unsafe {
|
||||||
|
let ptr = __rust_alloc(4, 1);
|
||||||
|
let ptr2 = ((ptr as usize) + 1) as *mut u8;
|
||||||
|
*ptr = 23;
|
||||||
|
*ptr2 = 32;
|
||||||
|
let ptr = __rust_realloc(ptr, 4, 1, 8);
|
||||||
|
let ptr2 = ((ptr as usize) + 1) as *mut u8;
|
||||||
|
*ptr + *ptr2
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
55,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn overflowing_add() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
extern "rust-intrinsic" {
|
||||||
|
pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool);
|
||||||
|
}
|
||||||
|
|
||||||
|
const GOAL: u8 = add_with_overflow(1, 2).0;
|
||||||
|
"#,
|
||||||
|
3,
|
||||||
|
);
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
extern "rust-intrinsic" {
|
||||||
|
pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool);
|
||||||
|
}
|
||||||
|
|
||||||
|
const GOAL: u8 = add_with_overflow(1, 2).1 as u8;
|
||||||
|
"#,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn needs_drop() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: copy, sized
|
||||||
|
extern "rust-intrinsic" {
|
||||||
|
pub fn needs_drop<T: ?Sized>() -> bool;
|
||||||
|
}
|
||||||
|
struct X;
|
||||||
|
const GOAL: bool = !needs_drop::<i32>() && needs_drop::<X>();
|
||||||
|
"#,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn likely() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
extern "rust-intrinsic" {
|
||||||
|
pub fn likely(b: bool) -> bool;
|
||||||
|
pub fn unlikely(b: bool) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GOAL: bool = likely(true) && unlikely(true) && !likely(false) && !unlikely(false);
|
||||||
|
"#,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn atomic() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: copy
|
||||||
|
extern "rust-intrinsic" {
|
||||||
|
pub fn atomic_load_seqcst<T: Copy>(src: *const T) -> T;
|
||||||
|
pub fn atomic_xchg_acquire<T: Copy>(dst: *mut T, src: T) -> T;
|
||||||
|
pub fn atomic_cxchg_release_seqcst<T: Copy>(dst: *mut T, old: T, src: T) -> (T, bool);
|
||||||
|
pub fn atomic_cxchgweak_acquire_acquire<T: Copy>(dst: *mut T, old: T, src: T) -> (T, bool);
|
||||||
|
pub fn atomic_store_release<T: Copy>(dst: *mut T, val: T);
|
||||||
|
pub fn atomic_xadd_acqrel<T: Copy>(dst: *mut T, src: T) -> T;
|
||||||
|
pub fn atomic_xsub_seqcst<T: Copy>(dst: *mut T, src: T) -> T;
|
||||||
|
pub fn atomic_and_acquire<T: Copy>(dst: *mut T, src: T) -> T;
|
||||||
|
pub fn atomic_nand_seqcst<T: Copy>(dst: *mut T, src: T) -> T;
|
||||||
|
pub fn atomic_or_release<T: Copy>(dst: *mut T, src: T) -> T;
|
||||||
|
pub fn atomic_xor_seqcst<T: Copy>(dst: *mut T, src: T) -> T;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_not_reach() {
|
||||||
|
_ // fails the test if executed
|
||||||
|
}
|
||||||
|
|
||||||
|
const GOAL: i32 = {
|
||||||
|
let mut x = 5;
|
||||||
|
atomic_store_release(&mut x, 10);
|
||||||
|
let mut y = atomic_xchg_acquire(&mut x, 100);
|
||||||
|
atomic_xadd_acqrel(&mut y, 20);
|
||||||
|
if (30, true) != atomic_cxchg_release_seqcst(&mut y, 30, 40) {
|
||||||
|
should_not_reach();
|
||||||
|
}
|
||||||
|
if (40, false) != atomic_cxchg_release_seqcst(&mut y, 30, 50) {
|
||||||
|
should_not_reach();
|
||||||
|
}
|
||||||
|
if (40, true) != atomic_cxchgweak_acquire_acquire(&mut y, 40, 30) {
|
||||||
|
should_not_reach();
|
||||||
|
}
|
||||||
|
let mut z = atomic_xsub_seqcst(&mut x, -200);
|
||||||
|
atomic_xor_seqcst(&mut x, 1024);
|
||||||
|
atomic_load_seqcst(&x) + z * 3 + atomic_load_seqcst(&y) * 2
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
660 + 1024,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn offset() {
|
fn offset() {
|
||||||
check_number(
|
check_number(
|
||||||
|
@ -5,7 +5,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use base_db::{impl_intern_key, salsa, CrateId, Upcast};
|
use base_db::{impl_intern_key, salsa, CrateId, Upcast};
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
db::DefDatabase, hir::ExprId, layout::TargetDataLayout, AdtId, BlockId, ConstId, ConstParamId,
|
db::DefDatabase, hir::ExprId, layout::TargetDataLayout, AdtId, BlockId, ConstParamId,
|
||||||
DefWithBodyId, EnumVariantId, FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId,
|
DefWithBodyId, EnumVariantId, FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId,
|
||||||
TypeOrConstParamId, VariantId,
|
TypeOrConstParamId, VariantId,
|
||||||
};
|
};
|
||||||
@ -59,7 +59,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
|||||||
|
|
||||||
#[salsa::invoke(crate::consteval::const_eval_query)]
|
#[salsa::invoke(crate::consteval::const_eval_query)]
|
||||||
#[salsa::cycle(crate::consteval::const_eval_recover)]
|
#[salsa::cycle(crate::consteval::const_eval_recover)]
|
||||||
fn const_eval(&self, def: ConstId, subst: Substitution) -> Result<Const, ConstEvalError>;
|
fn const_eval(&self, def: DefWithBodyId, subst: Substitution) -> Result<Const, ConstEvalError>;
|
||||||
|
|
||||||
#[salsa::invoke(crate::consteval::const_eval_discriminant_variant)]
|
#[salsa::invoke(crate::consteval::const_eval_discriminant_variant)]
|
||||||
#[salsa::cycle(crate::consteval::const_eval_discriminant_recover)]
|
#[salsa::cycle(crate::consteval::const_eval_discriminant_recover)]
|
||||||
|
@ -772,7 +772,7 @@ impl<'p> Fields<'p> {
|
|||||||
|
|
||||||
(0..fields_len).map(|idx| LocalFieldId::from_raw(idx.into())).filter_map(move |fid| {
|
(0..fields_len).map(|idx| LocalFieldId::from_raw(idx.into())).filter_map(move |fid| {
|
||||||
let ty = field_ty[fid].clone().substitute(Interner, substs);
|
let ty = field_ty[fid].clone().substitute(Interner, substs);
|
||||||
let ty = normalize(cx.db, cx.body, ty);
|
let ty = normalize(cx.db, cx.db.trait_environment_for_body(cx.body), ty);
|
||||||
let is_visible = matches!(adt, hir_def::AdtId::EnumId(..))
|
let is_visible = matches!(adt, hir_def::AdtId::EnumId(..))
|
||||||
|| visibility[fid].is_visible_from(cx.db.upcast(), cx.module);
|
|| visibility[fid].is_visible_from(cx.db.upcast(), cx.module);
|
||||||
let is_uninhabited = cx.is_uninhabited(&ty);
|
let is_uninhabited = cx.is_uninhabited(&ty);
|
||||||
|
@ -32,7 +32,7 @@ use crate::{
|
|||||||
mapping::from_chalk,
|
mapping::from_chalk,
|
||||||
mir::pad16,
|
mir::pad16,
|
||||||
primitive, to_assoc_type_id,
|
primitive, to_assoc_type_id,
|
||||||
utils::{self, generics},
|
utils::{self, generics, ClosureSubst},
|
||||||
AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Const, ConstScalar, ConstValue,
|
AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Const, ConstScalar, ConstValue,
|
||||||
DomainGoal, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, LifetimeOutlives,
|
DomainGoal, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, LifetimeOutlives,
|
||||||
MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Scalar,
|
MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Scalar,
|
||||||
@ -419,6 +419,16 @@ impl HirDisplay for Const {
|
|||||||
}
|
}
|
||||||
ConstValue::Concrete(c) => match &c.interned {
|
ConstValue::Concrete(c) => match &c.interned {
|
||||||
ConstScalar::Bytes(b, m) => render_const_scalar(f, &b, m, &data.ty),
|
ConstScalar::Bytes(b, m) => render_const_scalar(f, &b, m, &data.ty),
|
||||||
|
ConstScalar::UnevaluatedConst(c, parameters) => {
|
||||||
|
let const_data = f.db.const_data(*c);
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
const_data.name.as_ref().and_then(|x| x.as_str()).unwrap_or("_")
|
||||||
|
)?;
|
||||||
|
hir_fmt_generics(f, parameters, Some((*c).into()))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
ConstScalar::Unknown => f.write_char('_'),
|
ConstScalar::Unknown => f.write_char('_'),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -485,7 +495,7 @@ fn render_const_scalar(
|
|||||||
chalk_ir::TyKind::Ref(_, _, t) => match t.kind(Interner) {
|
chalk_ir::TyKind::Ref(_, _, t) => match t.kind(Interner) {
|
||||||
chalk_ir::TyKind::Str => {
|
chalk_ir::TyKind::Str => {
|
||||||
let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap());
|
let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap());
|
||||||
let bytes = memory_map.0.get(&addr).map(|x| &**x).unwrap_or(&[]);
|
let bytes = memory_map.memory.get(&addr).map(|x| &**x).unwrap_or(&[]);
|
||||||
let s = std::str::from_utf8(bytes).unwrap_or("<utf8-error>");
|
let s = std::str::from_utf8(bytes).unwrap_or("<utf8-error>");
|
||||||
write!(f, "{s:?}")
|
write!(f, "{s:?}")
|
||||||
}
|
}
|
||||||
@ -574,6 +584,11 @@ fn render_const_scalar(
|
|||||||
hir_def::AdtId::EnumId(_) => f.write_str("<enum-not-supported>"),
|
hir_def::AdtId::EnumId(_) => f.write_str("<enum-not-supported>"),
|
||||||
},
|
},
|
||||||
chalk_ir::TyKind::FnDef(..) => ty.hir_fmt(f),
|
chalk_ir::TyKind::FnDef(..) => ty.hir_fmt(f),
|
||||||
|
chalk_ir::TyKind::Raw(_, _) => {
|
||||||
|
let x = u128::from_le_bytes(pad16(b, false));
|
||||||
|
write!(f, "{:#X} as ", x)?;
|
||||||
|
ty.hir_fmt(f)
|
||||||
|
}
|
||||||
_ => f.write_str("<not-supported>"),
|
_ => f.write_str("<not-supported>"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -794,82 +809,9 @@ impl HirDisplay for Ty {
|
|||||||
}
|
}
|
||||||
f.end_location_link();
|
f.end_location_link();
|
||||||
|
|
||||||
if parameters.len(Interner) > 0 {
|
let generic_def = self.as_generic_def(db);
|
||||||
let parameters_to_write = if f.display_target.is_source_code()
|
|
||||||
|| f.omit_verbose_types()
|
|
||||||
{
|
|
||||||
match self
|
|
||||||
.as_generic_def(db)
|
|
||||||
.map(|generic_def_id| db.generic_defaults(generic_def_id))
|
|
||||||
.filter(|defaults| !defaults.is_empty())
|
|
||||||
{
|
|
||||||
None => parameters.as_slice(Interner),
|
|
||||||
Some(default_parameters) => {
|
|
||||||
fn should_show(
|
|
||||||
parameter: &GenericArg,
|
|
||||||
default_parameters: &[Binders<GenericArg>],
|
|
||||||
i: usize,
|
|
||||||
parameters: &Substitution,
|
|
||||||
) -> bool {
|
|
||||||
if parameter.ty(Interner).map(|x| x.kind(Interner))
|
|
||||||
== Some(&TyKind::Error)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if let Some(ConstValue::Concrete(c)) = parameter
|
|
||||||
.constant(Interner)
|
|
||||||
.map(|x| &x.data(Interner).value)
|
|
||||||
{
|
|
||||||
if c.interned == ConstScalar::Unknown {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let default_parameter = match default_parameters.get(i) {
|
|
||||||
Some(x) => x,
|
|
||||||
None => return true,
|
|
||||||
};
|
|
||||||
let actual_default =
|
|
||||||
default_parameter.clone().substitute(Interner, ¶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() {
|
hir_fmt_generics(f, parameters, generic_def)?;
|
||||||
let mut first = true;
|
|
||||||
for generic_arg in parameters_to_write {
|
|
||||||
if !first {
|
|
||||||
write!(f, ", ")?;
|
|
||||||
}
|
|
||||||
first = false;
|
|
||||||
|
|
||||||
if generic_arg.ty(Interner).map(|ty| ty.kind(Interner))
|
|
||||||
== Some(&TyKind::Error)
|
|
||||||
{
|
|
||||||
write!(f, "_")?;
|
|
||||||
} else {
|
|
||||||
generic_arg.hir_fmt(f)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
f.write_joined(parameters_to_write, ", ")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, ">")?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
TyKind::AssociatedType(assoc_type_id, parameters) => {
|
TyKind::AssociatedType(assoc_type_id, parameters) => {
|
||||||
let type_alias = from_assoc_type_id(*assoc_type_id);
|
let type_alias = from_assoc_type_id(*assoc_type_id);
|
||||||
@ -983,7 +925,7 @@ impl HirDisplay for Ty {
|
|||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
let sig = substs.at(Interner, 0).assert_ty_ref(Interner).callable_sig(db);
|
let sig = ClosureSubst(substs).sig_ty().callable_sig(db);
|
||||||
if let Some(sig) = sig {
|
if let Some(sig) = sig {
|
||||||
let (def, _) = db.lookup_intern_closure((*id).into());
|
let (def, _) = db.lookup_intern_closure((*id).into());
|
||||||
let infer = db.infer(def);
|
let infer = db.infer(def);
|
||||||
@ -1141,6 +1083,85 @@ impl HirDisplay for Ty {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn hir_fmt_generics(
|
||||||
|
f: &mut HirFormatter<'_>,
|
||||||
|
parameters: &Substitution,
|
||||||
|
generic_def: Option<hir_def::GenericDefId>,
|
||||||
|
) -> Result<(), HirDisplayError> {
|
||||||
|
let db = f.db;
|
||||||
|
if parameters.len(Interner) > 0 {
|
||||||
|
let parameters_to_write = if f.display_target.is_source_code() || f.omit_verbose_types() {
|
||||||
|
match generic_def
|
||||||
|
.map(|generic_def_id| db.generic_defaults(generic_def_id))
|
||||||
|
.filter(|defaults| !defaults.is_empty())
|
||||||
|
{
|
||||||
|
None => parameters.as_slice(Interner),
|
||||||
|
Some(default_parameters) => {
|
||||||
|
fn should_show(
|
||||||
|
parameter: &GenericArg,
|
||||||
|
default_parameters: &[Binders<GenericArg>],
|
||||||
|
i: usize,
|
||||||
|
parameters: &Substitution,
|
||||||
|
) -> bool {
|
||||||
|
if parameter.ty(Interner).map(|x| x.kind(Interner)) == Some(&TyKind::Error)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if let Some(ConstValue::Concrete(c)) =
|
||||||
|
parameter.constant(Interner).map(|x| &x.data(Interner).value)
|
||||||
|
{
|
||||||
|
if c.interned == ConstScalar::Unknown {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let default_parameter = match default_parameters.get(i) {
|
||||||
|
Some(x) => x,
|
||||||
|
None => return true,
|
||||||
|
};
|
||||||
|
let actual_default =
|
||||||
|
default_parameter.clone().substitute(Interner, ¶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 {
|
impl HirDisplay for CallableSig {
|
||||||
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
|
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
|
||||||
write!(f, "fn(")?;
|
write!(f, "fn(")?;
|
||||||
|
@ -44,7 +44,7 @@ use crate::{
|
|||||||
db::HirDatabase, fold_tys, infer::coerce::CoerceMany, lower::ImplTraitLoweringMode,
|
db::HirDatabase, fold_tys, infer::coerce::CoerceMany, lower::ImplTraitLoweringMode,
|
||||||
static_lifetime, to_assoc_type_id, traits::FnTrait, AliasEq, AliasTy, ClosureId, DomainGoal,
|
static_lifetime, to_assoc_type_id, traits::FnTrait, AliasEq, AliasTy, ClosureId, DomainGoal,
|
||||||
GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId, Substitution,
|
GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId, Substitution,
|
||||||
TraitRef, Ty, TyBuilder, TyExt, TyKind,
|
TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
// This lint has a false positive here. See the link below for details.
|
// This lint has a false positive here. See the link below for details.
|
||||||
@ -117,11 +117,10 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
|
|||||||
///
|
///
|
||||||
/// This is appropriate to use only after type-check: it assumes
|
/// This is appropriate to use only after type-check: it assumes
|
||||||
/// that normalization will succeed, for example.
|
/// that normalization will succeed, for example.
|
||||||
pub(crate) fn normalize(db: &dyn HirDatabase, owner: DefWithBodyId, ty: Ty) -> Ty {
|
pub(crate) fn normalize(db: &dyn HirDatabase, trait_env: Arc<TraitEnvironment>, ty: Ty) -> Ty {
|
||||||
if !ty.data(Interner).flags.intersects(TypeFlags::HAS_PROJECTION) {
|
if !ty.data(Interner).flags.intersects(TypeFlags::HAS_PROJECTION) {
|
||||||
return ty;
|
return ty;
|
||||||
}
|
}
|
||||||
let trait_env = db.trait_environment_for_body(owner);
|
|
||||||
let mut table = unify::InferenceTable::new(db, trait_env);
|
let mut table = unify::InferenceTable::new(db, trait_env);
|
||||||
|
|
||||||
let ty_with_vars = table.normalize_associated_types_in(ty);
|
let ty_with_vars = table.normalize_associated_types_in(ty);
|
||||||
|
@ -2,7 +2,11 @@
|
|||||||
|
|
||||||
use std::{cmp, collections::HashMap, convert::Infallible, mem};
|
use std::{cmp, collections::HashMap, convert::Infallible, mem};
|
||||||
|
|
||||||
use chalk_ir::{cast::Cast, AliasEq, AliasTy, FnSubst, Mutability, TyKind, WhereClause};
|
use chalk_ir::{
|
||||||
|
cast::Cast,
|
||||||
|
fold::{FallibleTypeFolder, TypeFoldable},
|
||||||
|
AliasEq, AliasTy, BoundVar, DebruijnIndex, FnSubst, Mutability, TyKind, WhereClause,
|
||||||
|
};
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
data::adt::VariantData,
|
data::adt::VariantData,
|
||||||
hir::{
|
hir::{
|
||||||
@ -11,7 +15,7 @@ use hir_def::{
|
|||||||
},
|
},
|
||||||
lang_item::LangItem,
|
lang_item::LangItem,
|
||||||
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
|
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
|
||||||
FieldId, HasModule, VariantId,
|
DefWithBodyId, FieldId, HasModule, VariantId,
|
||||||
};
|
};
|
||||||
use hir_expand::name;
|
use hir_expand::name;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
@ -20,12 +24,13 @@ use stdx::never;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase,
|
db::HirDatabase,
|
||||||
|
from_placeholder_idx, make_binders,
|
||||||
mir::{BorrowKind, MirSpan, ProjectionElem},
|
mir::{BorrowKind, MirSpan, ProjectionElem},
|
||||||
static_lifetime, to_chalk_trait_id,
|
static_lifetime, to_chalk_trait_id,
|
||||||
traits::FnTrait,
|
traits::FnTrait,
|
||||||
utils::{self, pattern_matching_dereference_count},
|
utils::{self, generics, pattern_matching_dereference_count, Generics},
|
||||||
Adjust, Adjustment, Canonical, CanonicalVarKinds, ChalkTraitId, ClosureId, DynTy, FnPointer,
|
Adjust, Adjustment, Binders, ChalkTraitId, ClosureId, DynTy, FnPointer, FnSig, Interner,
|
||||||
FnSig, InEnvironment, Interner, Substitution, Ty, TyBuilder, TyExt,
|
Substitution, Ty, TyExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{Expectation, InferenceContext};
|
use super::{Expectation, InferenceContext};
|
||||||
@ -117,7 +122,7 @@ impl HirPlace {
|
|||||||
fn ty(&self, ctx: &mut InferenceContext<'_>) -> Ty {
|
fn ty(&self, ctx: &mut InferenceContext<'_>) -> Ty {
|
||||||
let mut ty = ctx.table.resolve_completely(ctx.result[self.local].clone());
|
let mut ty = ctx.table.resolve_completely(ctx.result[self.local].clone());
|
||||||
for p in &self.projections {
|
for p in &self.projections {
|
||||||
ty = p.projected_ty(ty, ctx.db, |_, _| {
|
ty = p.projected_ty(ty, ctx.db, |_, _, _| {
|
||||||
unreachable!("Closure field only happens in MIR");
|
unreachable!("Closure field only happens in MIR");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -152,7 +157,7 @@ pub struct CapturedItem {
|
|||||||
pub(crate) place: HirPlace,
|
pub(crate) place: HirPlace,
|
||||||
pub(crate) kind: CaptureKind,
|
pub(crate) kind: CaptureKind,
|
||||||
pub(crate) span: MirSpan,
|
pub(crate) span: MirSpan,
|
||||||
pub(crate) ty: Ty,
|
pub(crate) ty: Binders<Ty>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CapturedItem {
|
impl CapturedItem {
|
||||||
@ -232,6 +237,52 @@ pub(crate) struct CapturedItemWithoutTy {
|
|||||||
|
|
||||||
impl CapturedItemWithoutTy {
|
impl CapturedItemWithoutTy {
|
||||||
fn with_ty(self, ctx: &mut InferenceContext<'_>) -> CapturedItem {
|
fn with_ty(self, ctx: &mut InferenceContext<'_>) -> CapturedItem {
|
||||||
|
fn replace_placeholder_with_binder(
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
owner: DefWithBodyId,
|
||||||
|
ty: Ty,
|
||||||
|
) -> Binders<Ty> {
|
||||||
|
struct Filler<'a> {
|
||||||
|
db: &'a dyn HirDatabase,
|
||||||
|
generics: Generics,
|
||||||
|
}
|
||||||
|
impl FallibleTypeFolder<Interner> for Filler<'_> {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder<Interner, Error = Self::Error> {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn interner(&self) -> Interner {
|
||||||
|
Interner
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_fold_free_placeholder_ty(
|
||||||
|
&mut self,
|
||||||
|
idx: chalk_ir::PlaceholderIndex,
|
||||||
|
_outer_binder: DebruijnIndex,
|
||||||
|
) -> std::result::Result<Ty, Self::Error> {
|
||||||
|
let x = from_placeholder_idx(self.db, idx);
|
||||||
|
let Some(idx) = self.generics.param_idx(x) else {
|
||||||
|
return Err(());
|
||||||
|
};
|
||||||
|
Ok(TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, idx))
|
||||||
|
.intern(Interner))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let g_def = match owner {
|
||||||
|
DefWithBodyId::FunctionId(f) => Some(f.into()),
|
||||||
|
DefWithBodyId::StaticId(_) => None,
|
||||||
|
DefWithBodyId::ConstId(f) => Some(f.into()),
|
||||||
|
DefWithBodyId::VariantId(f) => Some(f.into()),
|
||||||
|
};
|
||||||
|
let Some(generics) = g_def.map(|g_def| generics(db.upcast(), g_def)) else {
|
||||||
|
return Binders::empty(Interner, ty);
|
||||||
|
};
|
||||||
|
let filler = &mut Filler { db, generics };
|
||||||
|
let result = ty.clone().try_fold_with(filler, DebruijnIndex::INNERMOST).unwrap_or(ty);
|
||||||
|
make_binders(db, &filler.generics, result)
|
||||||
|
}
|
||||||
let ty = self.place.ty(ctx).clone();
|
let ty = self.place.ty(ctx).clone();
|
||||||
let ty = match &self.kind {
|
let ty = match &self.kind {
|
||||||
CaptureKind::ByValue => ty,
|
CaptureKind::ByValue => ty,
|
||||||
@ -243,7 +294,12 @@ impl CapturedItemWithoutTy {
|
|||||||
TyKind::Ref(m, static_lifetime(), ty).intern(Interner)
|
TyKind::Ref(m, static_lifetime(), ty).intern(Interner)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
CapturedItem { place: self.place, kind: self.kind, span: self.span, ty }
|
CapturedItem {
|
||||||
|
place: self.place,
|
||||||
|
kind: self.kind,
|
||||||
|
span: self.span,
|
||||||
|
ty: replace_placeholder_with_binder(ctx.db, ctx.owner, ty),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -590,17 +646,7 @@ impl InferenceContext<'_> {
|
|||||||
// without creating query cycles.
|
// without creating query cycles.
|
||||||
return self.result.closure_info.get(id).map(|x| x.1 == FnTrait::Fn).unwrap_or(true);
|
return self.result.closure_info.get(id).map(|x| x.1 == FnTrait::Fn).unwrap_or(true);
|
||||||
}
|
}
|
||||||
let crate_id = self.owner.module(self.db.upcast()).krate();
|
ty.is_copy(self.db, self.owner)
|
||||||
let Some(copy_trait) = self.db.lang_item(crate_id, LangItem::Copy).and_then(|x| x.as_trait()) else {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
let trait_ref = TyBuilder::trait_ref(self.db, copy_trait).push(ty).build();
|
|
||||||
let env = self.db.trait_environment_for_body(self.owner);
|
|
||||||
let goal = Canonical {
|
|
||||||
value: InEnvironment::new(&env.env, trait_ref.cast(Interner)),
|
|
||||||
binders: CanonicalVarKinds::empty(Interner),
|
|
||||||
};
|
|
||||||
self.db.trait_solve(crate_id, None, goal).is_some()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_from_expr(&mut self, expr: ExprId) {
|
fn select_from_expr(&mut self, expr: ExprId) {
|
||||||
|
@ -21,8 +21,10 @@ use crate::{
|
|||||||
Adjust, Adjustment, AutoBorrow, InferOk, InferenceContext, OverloadedDeref, PointerCast,
|
Adjust, Adjustment, AutoBorrow, InferOk, InferenceContext, OverloadedDeref, PointerCast,
|
||||||
TypeError, TypeMismatch,
|
TypeError, TypeMismatch,
|
||||||
},
|
},
|
||||||
static_lifetime, Canonical, DomainGoal, FnPointer, FnSig, Guidance, InEnvironment, Interner,
|
static_lifetime,
|
||||||
Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
|
utils::ClosureSubst,
|
||||||
|
Canonical, DomainGoal, FnPointer, FnSig, Guidance, InEnvironment, Interner, Solution,
|
||||||
|
Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::unify::InferenceTable;
|
use super::unify::InferenceTable;
|
||||||
@ -670,7 +672,7 @@ impl<'a> InferenceTable<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn coerce_closure_fn_ty(closure_substs: &Substitution, safety: chalk_ir::Safety) -> Ty {
|
fn coerce_closure_fn_ty(closure_substs: &Substitution, safety: chalk_ir::Safety) -> Ty {
|
||||||
let closure_sig = closure_substs.at(Interner, 0).assert_ty_ref(Interner).clone();
|
let closure_sig = ClosureSubst(closure_substs).sig_ty().clone();
|
||||||
match closure_sig.kind(Interner) {
|
match closure_sig.kind(Interner) {
|
||||||
TyKind::Function(fn_ty) => TyKind::Function(FnPointer {
|
TyKind::Function(fn_ty) => TyKind::Function(FnPointer {
|
||||||
num_binders: fn_ty.num_binders,
|
num_binders: fn_ty.num_binders,
|
||||||
|
@ -282,7 +282,7 @@ impl<'a> InferenceContext<'a> {
|
|||||||
let closure_id = self.db.intern_closure((self.owner, tgt_expr)).into();
|
let closure_id = self.db.intern_closure((self.owner, tgt_expr)).into();
|
||||||
let closure_ty = TyKind::Closure(
|
let closure_ty = TyKind::Closure(
|
||||||
closure_id,
|
closure_id,
|
||||||
Substitution::from1(Interner, sig_ty.clone()),
|
TyBuilder::subst_for_closure(self.db, self.owner, sig_ty.clone()),
|
||||||
)
|
)
|
||||||
.intern(Interner);
|
.intern(Interner);
|
||||||
self.deferred_closures.entry(closure_id).or_default();
|
self.deferred_closures.entry(closure_id).or_default();
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
//! Compute the binary representation of a type
|
//! Compute the binary representation of a type
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use base_db::CrateId;
|
use base_db::CrateId;
|
||||||
use chalk_ir::{AdtId, TyKind};
|
use chalk_ir::{AdtId, TyKind};
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
@ -13,8 +15,8 @@ use la_arena::{Idx, RawIdx};
|
|||||||
use stdx::never;
|
use stdx::never;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
consteval::try_const_usize, db::HirDatabase, layout::adt::struct_variant_idx, Interner,
|
consteval::try_const_usize, db::HirDatabase, infer::normalize, layout::adt::struct_variant_idx,
|
||||||
Substitution, Ty,
|
utils::ClosureSubst, Interner, Substitution, TraitEnvironment, Ty,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
@ -80,6 +82,8 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
|
|||||||
let Some(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable) };
|
let Some(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable) };
|
||||||
let cx = LayoutCx { krate, target: &target };
|
let cx = LayoutCx { krate, target: &target };
|
||||||
let dl = &*cx.current_data_layout();
|
let dl = &*cx.current_data_layout();
|
||||||
|
let trait_env = Arc::new(TraitEnvironment::empty(krate));
|
||||||
|
let ty = normalize(db, trait_env, ty.clone());
|
||||||
Ok(match ty.kind(Interner) {
|
Ok(match ty.kind(Interner) {
|
||||||
TyKind::Adt(AdtId(def), subst) => db.layout_of_adt(*def, subst.clone())?,
|
TyKind::Adt(AdtId(def), subst) => db.layout_of_adt(*def, subst.clone())?,
|
||||||
TyKind::Scalar(s) => match s {
|
TyKind::Scalar(s) => match s {
|
||||||
@ -146,7 +150,7 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
|
|||||||
}
|
}
|
||||||
TyKind::Array(element, count) => {
|
TyKind::Array(element, count) => {
|
||||||
let count = try_const_usize(&count).ok_or(LayoutError::UserError(
|
let count = try_const_usize(&count).ok_or(LayoutError::UserError(
|
||||||
"mismatched type of const generic parameter".to_string(),
|
"unevaluated or mistyped const generic parameter".to_string(),
|
||||||
))? as u64;
|
))? as u64;
|
||||||
let element = layout_of_ty(db, element, krate)?;
|
let element = layout_of_ty(db, element, krate)?;
|
||||||
let size = element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow)?;
|
let size = element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow)?;
|
||||||
@ -252,13 +256,19 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TyKind::Closure(c, _) => {
|
TyKind::Closure(c, subst) => {
|
||||||
let (def, _) = db.lookup_intern_closure((*c).into());
|
let (def, _) = db.lookup_intern_closure((*c).into());
|
||||||
let infer = db.infer(def);
|
let infer = db.infer(def);
|
||||||
let (captures, _) = infer.closure_info(c);
|
let (captures, _) = infer.closure_info(c);
|
||||||
let fields = captures
|
let fields = captures
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| layout_of_ty(db, &x.ty, krate))
|
.map(|x| {
|
||||||
|
layout_of_ty(
|
||||||
|
db,
|
||||||
|
&x.ty.clone().substitute(Interner, ClosureSubst(subst).parent_subst()),
|
||||||
|
krate,
|
||||||
|
)
|
||||||
|
})
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
let fields = fields.iter().collect::<Vec<_>>();
|
let fields = fields.iter().collect::<Vec<_>>();
|
||||||
let fields = fields.iter().collect::<Vec<_>>();
|
let fields = fields.iter().collect::<Vec<_>>();
|
||||||
|
@ -218,6 +218,22 @@ fn generic() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn associated_types() {
|
||||||
|
size_and_align! {
|
||||||
|
trait Tr {
|
||||||
|
type Ty;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tr for i32 {
|
||||||
|
type Ty = i64;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Foo<A: Tr>(<A as Tr>::Ty);
|
||||||
|
struct Goal(Foo<i32>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn return_position_impl_trait() {
|
fn return_position_impl_trait() {
|
||||||
size_and_align_expr! {
|
size_and_align_expr! {
|
||||||
|
@ -44,10 +44,10 @@ use chalk_ir::{
|
|||||||
NoSolution, TyData,
|
NoSolution, TyData,
|
||||||
};
|
};
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_def::{hir::ExprId, type_ref::Rawness, TypeOrConstParamId};
|
use hir_def::{hir::ExprId, type_ref::Rawness, ConstId, TypeOrConstParamId};
|
||||||
use hir_expand::name;
|
use hir_expand::name;
|
||||||
use la_arena::{Arena, Idx};
|
use la_arena::{Arena, Idx};
|
||||||
use mir::MirEvalError;
|
use mir::{MirEvalError, VTableMap};
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
use traits::FnTrait;
|
use traits::FnTrait;
|
||||||
use utils::Generics;
|
use utils::Generics;
|
||||||
@ -151,11 +151,14 @@ pub type WhereClause = chalk_ir::WhereClause<Interner>;
|
|||||||
/// the necessary bits of memory of the const eval session to keep the constant
|
/// the necessary bits of memory of the const eval session to keep the constant
|
||||||
/// meaningful.
|
/// meaningful.
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||||
pub struct MemoryMap(pub HashMap<usize, Vec<u8>>);
|
pub struct MemoryMap {
|
||||||
|
pub memory: HashMap<usize, Vec<u8>>,
|
||||||
|
pub vtable: VTableMap,
|
||||||
|
}
|
||||||
|
|
||||||
impl MemoryMap {
|
impl MemoryMap {
|
||||||
fn insert(&mut self, addr: usize, x: Vec<u8>) {
|
fn insert(&mut self, addr: usize, x: Vec<u8>) {
|
||||||
self.0.insert(addr, x);
|
self.memory.insert(addr, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This functions convert each address by a function `f` which gets the byte intervals and assign an address
|
/// This functions convert each address by a function `f` which gets the byte intervals and assign an address
|
||||||
@ -165,7 +168,7 @@ impl MemoryMap {
|
|||||||
&self,
|
&self,
|
||||||
mut f: impl FnMut(&[u8]) -> Result<usize, MirEvalError>,
|
mut f: impl FnMut(&[u8]) -> Result<usize, MirEvalError>,
|
||||||
) -> Result<HashMap<usize, usize>, MirEvalError> {
|
) -> Result<HashMap<usize, usize>, MirEvalError> {
|
||||||
self.0.iter().map(|x| Ok((*x.0, f(x.1)?))).collect()
|
self.memory.iter().map(|x| Ok((*x.0, f(x.1)?))).collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,6 +176,9 @@ impl MemoryMap {
|
|||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum ConstScalar {
|
pub enum ConstScalar {
|
||||||
Bytes(Vec<u8>, MemoryMap),
|
Bytes(Vec<u8>, MemoryMap),
|
||||||
|
// FIXME: this is a hack to get around chalk not being able to represent unevaluatable
|
||||||
|
// constants
|
||||||
|
UnevaluatedConst(ConstId, Substitution),
|
||||||
/// Case of an unknown value that rustc might know but we don't
|
/// Case of an unknown value that rustc might know but we don't
|
||||||
// FIXME: this is a hack to get around chalk not being able to represent unevaluatable
|
// FIXME: this is a hack to get around chalk not being able to represent unevaluatable
|
||||||
// constants
|
// constants
|
||||||
|
@ -3,13 +3,14 @@
|
|||||||
use std::{fmt::Display, iter};
|
use std::{fmt::Display, iter};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase, display::HirDisplay, infer::PointerCast, lang_items::is_box, ClosureId, Const,
|
db::HirDatabase, display::HirDisplay, infer::PointerCast, lang_items::is_box, mapping::ToChalk,
|
||||||
ConstScalar, InferenceResult, Interner, MemoryMap, Substitution, Ty, TyKind,
|
CallableDefId, ClosureId, Const, ConstScalar, InferenceResult, Interner, MemoryMap,
|
||||||
|
Substitution, Ty, TyKind,
|
||||||
};
|
};
|
||||||
use chalk_ir::Mutability;
|
use chalk_ir::Mutability;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
hir::{BindingId, Expr, ExprId, Ordering, PatId},
|
hir::{BindingId, Expr, ExprId, Ordering, PatId},
|
||||||
DefWithBodyId, FieldId, UnionId, VariantId,
|
DefWithBodyId, FieldId, StaticId, UnionId, VariantId,
|
||||||
};
|
};
|
||||||
use la_arena::{Arena, ArenaMap, Idx, RawIdx};
|
use la_arena::{Arena, ArenaMap, Idx, RawIdx};
|
||||||
|
|
||||||
@ -19,7 +20,7 @@ mod borrowck;
|
|||||||
mod pretty;
|
mod pretty;
|
||||||
|
|
||||||
pub use borrowck::{borrowck_query, BorrowckResult, MutabilityReason};
|
pub use borrowck::{borrowck_query, BorrowckResult, MutabilityReason};
|
||||||
pub use eval::{interpret_mir, pad16, Evaluator, MirEvalError};
|
pub use eval::{interpret_mir, pad16, Evaluator, MirEvalError, VTableMap};
|
||||||
pub use lower::{
|
pub use lower::{
|
||||||
lower_to_mir, mir_body_for_closure_query, mir_body_query, mir_body_recover, MirLowerError,
|
lower_to_mir, mir_body_for_closure_query, mir_body_query, mir_body_recover, MirLowerError,
|
||||||
};
|
};
|
||||||
@ -76,6 +77,9 @@ pub enum Operand {
|
|||||||
Move(Place),
|
Move(Place),
|
||||||
/// Constants are already semantically values, and remain unchanged.
|
/// Constants are already semantically values, and remain unchanged.
|
||||||
Constant(Const),
|
Constant(Const),
|
||||||
|
/// NON STANDARD: This kind of operand returns an immutable reference to that static memory. Rustc
|
||||||
|
/// handles it with the `Constant` variant somehow.
|
||||||
|
Static(StaticId),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Operand {
|
impl Operand {
|
||||||
@ -90,6 +94,17 @@ impl Operand {
|
|||||||
fn const_zst(ty: Ty) -> Operand {
|
fn const_zst(ty: Ty) -> Operand {
|
||||||
Self::from_bytes(vec![], ty)
|
Self::from_bytes(vec![], ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn from_fn(
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
func_id: hir_def::FunctionId,
|
||||||
|
generic_args: Substitution,
|
||||||
|
) -> Operand {
|
||||||
|
let ty =
|
||||||
|
chalk_ir::TyKind::FnDef(CallableDefId::FunctionId(func_id).to_chalk(db), generic_args)
|
||||||
|
.intern(Interner);
|
||||||
|
Operand::from_bytes(vec![], ty)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
@ -110,7 +125,7 @@ impl<V, T> ProjectionElem<V, T> {
|
|||||||
&self,
|
&self,
|
||||||
base: Ty,
|
base: Ty,
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
closure_field: impl FnOnce(ClosureId, usize) -> Ty,
|
closure_field: impl FnOnce(ClosureId, &Substitution, usize) -> Ty,
|
||||||
) -> Ty {
|
) -> Ty {
|
||||||
match self {
|
match self {
|
||||||
ProjectionElem::Deref => match &base.data(Interner).kind {
|
ProjectionElem::Deref => match &base.data(Interner).kind {
|
||||||
@ -142,7 +157,7 @@ impl<V, T> ProjectionElem<V, T> {
|
|||||||
never!("Out of bound tuple field");
|
never!("Out of bound tuple field");
|
||||||
TyKind::Error.intern(Interner)
|
TyKind::Error.intern(Interner)
|
||||||
}),
|
}),
|
||||||
TyKind::Closure(id, _) => closure_field(*id, *f),
|
TyKind::Closure(id, subst) => closure_field(*id, subst, *f),
|
||||||
_ => {
|
_ => {
|
||||||
never!("Only tuple or closure has tuple or closure field");
|
never!("Only tuple or closure has tuple or closure field");
|
||||||
return TyKind::Error.intern(Interner);
|
return TyKind::Error.intern(Interner);
|
||||||
@ -261,7 +276,13 @@ impl SwitchTargets {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum Terminator {
|
pub struct Terminator {
|
||||||
|
span: MirSpan,
|
||||||
|
kind: TerminatorKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
|
pub enum TerminatorKind {
|
||||||
/// Block has one successor; we continue execution there.
|
/// Block has one successor; we continue execution there.
|
||||||
Goto { target: BasicBlockId },
|
Goto { target: BasicBlockId },
|
||||||
|
|
||||||
@ -836,6 +857,9 @@ pub enum Rvalue {
|
|||||||
/// affects alias analysis.
|
/// affects alias analysis.
|
||||||
ShallowInitBox(Operand, Ty),
|
ShallowInitBox(Operand, Ty),
|
||||||
|
|
||||||
|
/// NON STANDARD: allocates memory with the type's layout, and shallow init the box with the resulting pointer.
|
||||||
|
ShallowInitBoxWithAlloc(Ty),
|
||||||
|
|
||||||
/// A CopyForDeref is equivalent to a read from a place at the
|
/// A CopyForDeref is equivalent to a read from a place at the
|
||||||
/// codegen level, but is treated specially by drop elaboration. When such a read happens, it
|
/// codegen level, but is treated specially by drop elaboration. When such a read happens, it
|
||||||
/// is guaranteed (via nature of the mir_opt `Derefer` in rustc_mir_transform/src/deref_separator)
|
/// is guaranteed (via nature of the mir_opt `Derefer` in rustc_mir_transform/src/deref_separator)
|
||||||
@ -918,7 +942,7 @@ impl MirBody {
|
|||||||
Operand::Copy(p) | Operand::Move(p) => {
|
Operand::Copy(p) | Operand::Move(p) => {
|
||||||
f(p);
|
f(p);
|
||||||
}
|
}
|
||||||
Operand::Constant(_) => (),
|
Operand::Constant(_) | Operand::Static(_) => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (_, block) in self.basic_blocks.iter_mut() {
|
for (_, block) in self.basic_blocks.iter_mut() {
|
||||||
@ -927,6 +951,7 @@ impl MirBody {
|
|||||||
StatementKind::Assign(p, r) => {
|
StatementKind::Assign(p, r) => {
|
||||||
f(p);
|
f(p);
|
||||||
match r {
|
match r {
|
||||||
|
Rvalue::ShallowInitBoxWithAlloc(_) => (),
|
||||||
Rvalue::ShallowInitBox(o, _)
|
Rvalue::ShallowInitBox(o, _)
|
||||||
| Rvalue::UnaryOp(_, o)
|
| Rvalue::UnaryOp(_, o)
|
||||||
| Rvalue::Cast(_, o, _)
|
| Rvalue::Cast(_, o, _)
|
||||||
@ -954,32 +979,32 @@ impl MirBody {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
match &mut block.terminator {
|
match &mut block.terminator {
|
||||||
Some(x) => match x {
|
Some(x) => match &mut x.kind {
|
||||||
Terminator::SwitchInt { discr, .. } => for_operand(discr, &mut f),
|
TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, &mut f),
|
||||||
Terminator::FalseEdge { .. }
|
TerminatorKind::FalseEdge { .. }
|
||||||
| Terminator::FalseUnwind { .. }
|
| TerminatorKind::FalseUnwind { .. }
|
||||||
| Terminator::Goto { .. }
|
| TerminatorKind::Goto { .. }
|
||||||
| Terminator::Resume
|
| TerminatorKind::Resume
|
||||||
| Terminator::GeneratorDrop
|
| TerminatorKind::GeneratorDrop
|
||||||
| Terminator::Abort
|
| TerminatorKind::Abort
|
||||||
| Terminator::Return
|
| TerminatorKind::Return
|
||||||
| Terminator::Unreachable => (),
|
| TerminatorKind::Unreachable => (),
|
||||||
Terminator::Drop { place, .. } => {
|
TerminatorKind::Drop { place, .. } => {
|
||||||
f(place);
|
f(place);
|
||||||
}
|
}
|
||||||
Terminator::DropAndReplace { place, value, .. } => {
|
TerminatorKind::DropAndReplace { place, value, .. } => {
|
||||||
f(place);
|
f(place);
|
||||||
for_operand(value, &mut f);
|
for_operand(value, &mut f);
|
||||||
}
|
}
|
||||||
Terminator::Call { func, args, destination, .. } => {
|
TerminatorKind::Call { func, args, destination, .. } => {
|
||||||
for_operand(func, &mut f);
|
for_operand(func, &mut f);
|
||||||
args.iter_mut().for_each(|x| for_operand(x, &mut f));
|
args.iter_mut().for_each(|x| for_operand(x, &mut f));
|
||||||
f(destination);
|
f(destination);
|
||||||
}
|
}
|
||||||
Terminator::Assert { cond, .. } => {
|
TerminatorKind::Assert { cond, .. } => {
|
||||||
for_operand(cond, &mut f);
|
for_operand(cond, &mut f);
|
||||||
}
|
}
|
||||||
Terminator::Yield { value, resume_arg, .. } => {
|
TerminatorKind::Yield { value, resume_arg, .. } => {
|
||||||
for_operand(value, &mut f);
|
for_operand(value, &mut f);
|
||||||
f(resume_arg);
|
f(resume_arg);
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ use crate::{db::HirDatabase, ClosureId};
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
BasicBlockId, BorrowKind, LocalId, MirBody, MirLowerError, MirSpan, Place, ProjectionElem,
|
BasicBlockId, BorrowKind, LocalId, MirBody, MirLowerError, MirSpan, Place, ProjectionElem,
|
||||||
Rvalue, StatementKind, Terminator,
|
Rvalue, StatementKind, TerminatorKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
@ -141,26 +141,26 @@ fn ever_initialized_map(body: &MirBody) -> ArenaMap<BasicBlockId, ArenaMap<Local
|
|||||||
never!("Terminator should be none only in construction");
|
never!("Terminator should be none only in construction");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let targets = match terminator {
|
let targets = match &terminator.kind {
|
||||||
Terminator::Goto { target } => vec![*target],
|
TerminatorKind::Goto { target } => vec![*target],
|
||||||
Terminator::SwitchInt { targets, .. } => targets.all_targets().to_vec(),
|
TerminatorKind::SwitchInt { targets, .. } => targets.all_targets().to_vec(),
|
||||||
Terminator::Resume
|
TerminatorKind::Resume
|
||||||
| Terminator::Abort
|
| TerminatorKind::Abort
|
||||||
| Terminator::Return
|
| TerminatorKind::Return
|
||||||
| Terminator::Unreachable => vec![],
|
| TerminatorKind::Unreachable => vec![],
|
||||||
Terminator::Call { target, cleanup, destination, .. } => {
|
TerminatorKind::Call { target, cleanup, destination, .. } => {
|
||||||
if destination.projection.len() == 0 && destination.local == l {
|
if destination.projection.len() == 0 && destination.local == l {
|
||||||
is_ever_initialized = true;
|
is_ever_initialized = true;
|
||||||
}
|
}
|
||||||
target.into_iter().chain(cleanup.into_iter()).copied().collect()
|
target.into_iter().chain(cleanup.into_iter()).copied().collect()
|
||||||
}
|
}
|
||||||
Terminator::Drop { .. }
|
TerminatorKind::Drop { .. }
|
||||||
| Terminator::DropAndReplace { .. }
|
| TerminatorKind::DropAndReplace { .. }
|
||||||
| Terminator::Assert { .. }
|
| TerminatorKind::Assert { .. }
|
||||||
| Terminator::Yield { .. }
|
| TerminatorKind::Yield { .. }
|
||||||
| Terminator::GeneratorDrop
|
| TerminatorKind::GeneratorDrop
|
||||||
| Terminator::FalseEdge { .. }
|
| TerminatorKind::FalseEdge { .. }
|
||||||
| Terminator::FalseUnwind { .. } => {
|
| TerminatorKind::FalseUnwind { .. } => {
|
||||||
never!("We don't emit these MIR terminators yet");
|
never!("We don't emit these MIR terminators yet");
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
@ -228,21 +228,21 @@ fn mutability_of_locals(body: &MirBody) -> ArenaMap<LocalId, MutabilityReason> {
|
|||||||
never!("Terminator should be none only in construction");
|
never!("Terminator should be none only in construction");
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
match terminator {
|
match &terminator.kind {
|
||||||
Terminator::Goto { .. }
|
TerminatorKind::Goto { .. }
|
||||||
| Terminator::Resume
|
| TerminatorKind::Resume
|
||||||
| Terminator::Abort
|
| TerminatorKind::Abort
|
||||||
| Terminator::Return
|
| TerminatorKind::Return
|
||||||
| Terminator::Unreachable
|
| TerminatorKind::Unreachable
|
||||||
| Terminator::FalseEdge { .. }
|
| TerminatorKind::FalseEdge { .. }
|
||||||
| Terminator::FalseUnwind { .. }
|
| TerminatorKind::FalseUnwind { .. }
|
||||||
| Terminator::GeneratorDrop
|
| TerminatorKind::GeneratorDrop
|
||||||
| Terminator::SwitchInt { .. }
|
| TerminatorKind::SwitchInt { .. }
|
||||||
| Terminator::Drop { .. }
|
| TerminatorKind::Drop { .. }
|
||||||
| Terminator::DropAndReplace { .. }
|
| TerminatorKind::DropAndReplace { .. }
|
||||||
| Terminator::Assert { .. }
|
| TerminatorKind::Assert { .. }
|
||||||
| Terminator::Yield { .. } => (),
|
| TerminatorKind::Yield { .. } => (),
|
||||||
Terminator::Call { destination, .. } => {
|
TerminatorKind::Call { destination, .. } => {
|
||||||
if destination.projection.len() == 0 {
|
if destination.projection.len() == 0 {
|
||||||
if ever_init_map.get(destination.local).copied().unwrap_or_default() {
|
if ever_init_map.get(destination.local).copied().unwrap_or_default() {
|
||||||
push_mut_span(destination.local, MirSpan::Unknown);
|
push_mut_span(destination.local, MirSpan::Unknown);
|
||||||
|
File diff suppressed because it is too large
Load Diff
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
|
//! This module generates a polymorphic MIR from a hir body
|
||||||
|
|
||||||
use std::{iter, mem, sync::Arc};
|
use std::{fmt::Write, iter, mem, sync::Arc};
|
||||||
|
|
||||||
|
use base_db::FileId;
|
||||||
use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
|
use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
body::Body,
|
body::Body,
|
||||||
data::adt::{StructKind, VariantData},
|
data::adt::{StructKind, VariantData},
|
||||||
hir::{
|
hir::{
|
||||||
Array, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, Pat, PatId,
|
ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm,
|
||||||
RecordFieldPat, RecordLitField,
|
Pat, PatId, RecordFieldPat, RecordLitField,
|
||||||
},
|
},
|
||||||
lang_item::{LangItem, LangItemTarget},
|
lang_item::{LangItem, LangItemTarget},
|
||||||
path::Path,
|
path::Path,
|
||||||
@ -18,6 +19,7 @@ use hir_def::{
|
|||||||
use hir_expand::name::Name;
|
use hir_expand::name::Name;
|
||||||
use la_arena::ArenaMap;
|
use la_arena::ArenaMap;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
use syntax::TextRange;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
consteval::ConstEvalError,
|
consteval::ConstEvalError,
|
||||||
@ -27,8 +29,9 @@ use crate::{
|
|||||||
inhabitedness::is_ty_uninhabited_from,
|
inhabitedness::is_ty_uninhabited_from,
|
||||||
layout::{layout_of_ty, LayoutError},
|
layout::{layout_of_ty, LayoutError},
|
||||||
mapping::ToChalk,
|
mapping::ToChalk,
|
||||||
|
method_resolution::lookup_impl_const,
|
||||||
static_lifetime,
|
static_lifetime,
|
||||||
utils::generics,
|
utils::{generics, ClosureSubst},
|
||||||
Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt,
|
Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -62,14 +65,14 @@ struct MirLowerCtx<'a> {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum MirLowerError {
|
pub enum MirLowerError {
|
||||||
ConstEvalError(Box<ConstEvalError>),
|
ConstEvalError(String, Box<ConstEvalError>),
|
||||||
LayoutError(LayoutError),
|
LayoutError(LayoutError),
|
||||||
IncompleteExpr,
|
IncompleteExpr,
|
||||||
/// Trying to lower a trait function, instead of an implementation
|
/// Trying to lower a trait function, instead of an implementation
|
||||||
TraitFunctionDefinition(TraitId, Name),
|
TraitFunctionDefinition(TraitId, Name),
|
||||||
UnresolvedName(String),
|
UnresolvedName(String),
|
||||||
RecordLiteralWithoutPath,
|
RecordLiteralWithoutPath,
|
||||||
UnresolvedMethod,
|
UnresolvedMethod(String),
|
||||||
UnresolvedField,
|
UnresolvedField,
|
||||||
MissingFunctionDefinition,
|
MissingFunctionDefinition,
|
||||||
TypeMismatch(TypeMismatch),
|
TypeMismatch(TypeMismatch),
|
||||||
@ -88,6 +91,46 @@ pub enum MirLowerError {
|
|||||||
UnaccessableLocal,
|
UnaccessableLocal,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl MirLowerError {
|
||||||
|
pub fn pretty_print(
|
||||||
|
&self,
|
||||||
|
f: &mut String,
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
span_formatter: impl Fn(FileId, TextRange) -> String,
|
||||||
|
) -> std::result::Result<(), std::fmt::Error> {
|
||||||
|
match self {
|
||||||
|
MirLowerError::ConstEvalError(name, e) => {
|
||||||
|
writeln!(f, "In evaluating constant {name}")?;
|
||||||
|
match &**e {
|
||||||
|
ConstEvalError::MirLowerError(e) => e.pretty_print(f, db, span_formatter)?,
|
||||||
|
ConstEvalError::MirEvalError(e) => e.pretty_print(f, db, span_formatter)?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MirLowerError::LayoutError(_)
|
||||||
|
| MirLowerError::IncompleteExpr
|
||||||
|
| MirLowerError::UnaccessableLocal
|
||||||
|
| MirLowerError::TraitFunctionDefinition(_, _)
|
||||||
|
| MirLowerError::UnresolvedName(_)
|
||||||
|
| MirLowerError::RecordLiteralWithoutPath
|
||||||
|
| MirLowerError::UnresolvedMethod(_)
|
||||||
|
| MirLowerError::UnresolvedField
|
||||||
|
| MirLowerError::MissingFunctionDefinition
|
||||||
|
| MirLowerError::TypeMismatch(_)
|
||||||
|
| MirLowerError::TypeError(_)
|
||||||
|
| MirLowerError::NotSupported(_)
|
||||||
|
| MirLowerError::ContinueWithoutLoop
|
||||||
|
| MirLowerError::BreakWithoutLoop
|
||||||
|
| MirLowerError::Loop
|
||||||
|
| MirLowerError::ImplementationError(_)
|
||||||
|
| MirLowerError::LangItemNotFound(_)
|
||||||
|
| MirLowerError::MutatingRvalue
|
||||||
|
| MirLowerError::UnresolvedLabel
|
||||||
|
| MirLowerError::UnresolvedUpvar(_) => writeln!(f, "{:?}", self)?,
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! not_supported {
|
macro_rules! not_supported {
|
||||||
($x: expr) => {
|
($x: expr) => {
|
||||||
return Err(MirLowerError::NotSupported(format!($x)))
|
return Err(MirLowerError::NotSupported(format!($x)))
|
||||||
@ -101,15 +144,6 @@ macro_rules! implementation_error {
|
|||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ConstEvalError> for MirLowerError {
|
|
||||||
fn from(value: ConstEvalError) -> Self {
|
|
||||||
match value {
|
|
||||||
ConstEvalError::MirLowerError(e) => e,
|
|
||||||
_ => MirLowerError::ConstEvalError(Box::new(value)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<LayoutError> for MirLowerError {
|
impl From<LayoutError> for MirLowerError {
|
||||||
fn from(value: LayoutError) -> Self {
|
fn from(value: LayoutError) -> Self {
|
||||||
MirLowerError::LayoutError(value)
|
MirLowerError::LayoutError(value)
|
||||||
@ -177,7 +211,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
if !self.has_adjustments(expr_id) {
|
if !self.has_adjustments(expr_id) {
|
||||||
match &self.body.exprs[expr_id] {
|
match &self.body.exprs[expr_id] {
|
||||||
Expr::Literal(l) => {
|
Expr::Literal(l) => {
|
||||||
let ty = self.expr_ty(expr_id);
|
let ty = self.expr_ty_without_adjust(expr_id);
|
||||||
return Ok(Some((self.lower_literal_to_operand(ty, l)?, current)));
|
return Ok(Some((self.lower_literal_to_operand(ty, l)?, current)));
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
@ -282,7 +316,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
{
|
{
|
||||||
match assoc {
|
match assoc {
|
||||||
hir_def::AssocItemId::ConstId(c) => {
|
hir_def::AssocItemId::ConstId(c) => {
|
||||||
self.lower_const(c, current, place, subst, expr_id.into())?;
|
self.lower_const(c, current, place, subst, expr_id.into(), self.expr_ty_without_adjust(expr_id))?;
|
||||||
return Ok(Some(current))
|
return Ok(Some(current))
|
||||||
},
|
},
|
||||||
hir_def::AssocItemId::FunctionId(_) => {
|
hir_def::AssocItemId::FunctionId(_) => {
|
||||||
@ -309,17 +343,20 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
match pr {
|
match pr {
|
||||||
ValueNs::LocalBinding(pat_id) => {
|
ValueNs::LocalBinding(_) | ValueNs::StaticId(_) => {
|
||||||
|
let Some((temp, current)) = self.lower_expr_as_place_without_adjust(current, expr_id, false)? else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
self.push_assignment(
|
self.push_assignment(
|
||||||
current,
|
current,
|
||||||
place,
|
place,
|
||||||
Operand::Copy(self.binding_local(pat_id)?.into()).into(),
|
Operand::Copy(temp).into(),
|
||||||
expr_id.into(),
|
expr_id.into(),
|
||||||
);
|
);
|
||||||
Ok(Some(current))
|
Ok(Some(current))
|
||||||
}
|
}
|
||||||
ValueNs::ConstId(const_id) => {
|
ValueNs::ConstId(const_id) => {
|
||||||
self.lower_const(const_id, current, place, Substitution::empty(Interner), expr_id.into())?;
|
self.lower_const(const_id, current, place, Substitution::empty(Interner), expr_id.into(), self.expr_ty_without_adjust(expr_id))?;
|
||||||
Ok(Some(current))
|
Ok(Some(current))
|
||||||
}
|
}
|
||||||
ValueNs::EnumVariantId(variant_id) => {
|
ValueNs::EnumVariantId(variant_id) => {
|
||||||
@ -343,7 +380,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
not_supported!("owner without generic def id");
|
not_supported!("owner without generic def id");
|
||||||
};
|
};
|
||||||
let gen = generics(self.db.upcast(), def);
|
let gen = generics(self.db.upcast(), def);
|
||||||
let ty = self.expr_ty(expr_id);
|
let ty = self.expr_ty_without_adjust(expr_id);
|
||||||
self.push_assignment(
|
self.push_assignment(
|
||||||
current,
|
current,
|
||||||
place,
|
place,
|
||||||
@ -388,12 +425,13 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
};
|
};
|
||||||
self.set_terminator(
|
self.set_terminator(
|
||||||
current,
|
current,
|
||||||
Terminator::SwitchInt {
|
TerminatorKind::SwitchInt {
|
||||||
discr,
|
discr,
|
||||||
targets: SwitchTargets::static_if(1, start_of_then, start_of_else),
|
targets: SwitchTargets::static_if(1, start_of_then, start_of_else),
|
||||||
},
|
},
|
||||||
|
expr_id.into(),
|
||||||
);
|
);
|
||||||
Ok(self.merge_blocks(end_of_then, end_of_else))
|
Ok(self.merge_blocks(end_of_then, end_of_else, expr_id.into()))
|
||||||
}
|
}
|
||||||
Expr::Let { pat, expr } => {
|
Expr::Let { pat, expr } => {
|
||||||
let Some((cond_place, current)) = self.lower_expr_as_place(current, *expr, true)? else {
|
let Some((cond_place, current)) = self.lower_expr_as_place(current, *expr, true)? else {
|
||||||
@ -423,32 +461,32 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
MirSpan::Unknown,
|
MirSpan::Unknown,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
Ok(self.merge_blocks(Some(then_target), else_target))
|
Ok(self.merge_blocks(Some(then_target), else_target, expr_id.into()))
|
||||||
}
|
}
|
||||||
Expr::Unsafe { id: _, statements, tail } => {
|
Expr::Unsafe { id: _, statements, tail } => {
|
||||||
self.lower_block_to_place(statements, current, *tail, place)
|
self.lower_block_to_place(statements, current, *tail, place, expr_id.into())
|
||||||
}
|
}
|
||||||
Expr::Block { id: _, statements, tail, label } => {
|
Expr::Block { id: _, statements, tail, label } => {
|
||||||
if let Some(label) = label {
|
if let Some(label) = label {
|
||||||
self.lower_loop(current, place.clone(), Some(*label), |this, begin| {
|
self.lower_loop(current, place.clone(), Some(*label), expr_id.into(), |this, begin| {
|
||||||
if let Some(block) = this.lower_block_to_place(statements, begin, *tail, place)? {
|
if let Some(block) = this.lower_block_to_place(statements, begin, *tail, place, expr_id.into())? {
|
||||||
let end = this.current_loop_end()?;
|
let end = this.current_loop_end()?;
|
||||||
this.set_goto(block, end);
|
this.set_goto(block, end, expr_id.into());
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
self.lower_block_to_place(statements, current, *tail, place)
|
self.lower_block_to_place(statements, current, *tail, place, expr_id.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Loop { body, label } => self.lower_loop(current, place, *label, |this, begin| {
|
Expr::Loop { body, label } => self.lower_loop(current, place, *label, expr_id.into(), |this, begin| {
|
||||||
if let Some((_, block)) = this.lower_expr_as_place(begin, *body, true)? {
|
if let Some((_, block)) = this.lower_expr_as_place(begin, *body, true)? {
|
||||||
this.set_goto(block, begin);
|
this.set_goto(block, begin, expr_id.into());
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}),
|
}),
|
||||||
Expr::While { condition, body, label } => {
|
Expr::While { condition, body, label } => {
|
||||||
self.lower_loop(current, place, *label, |this, begin| {
|
self.lower_loop(current, place, *label, expr_id.into(),|this, begin| {
|
||||||
let Some((discr, to_switch)) = this.lower_expr_to_some_operand(*condition, begin)? else {
|
let Some((discr, to_switch)) = this.lower_expr_to_some_operand(*condition, begin)? else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
@ -456,13 +494,14 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
let after_cond = this.new_basic_block();
|
let after_cond = this.new_basic_block();
|
||||||
this.set_terminator(
|
this.set_terminator(
|
||||||
to_switch,
|
to_switch,
|
||||||
Terminator::SwitchInt {
|
TerminatorKind::SwitchInt {
|
||||||
discr,
|
discr,
|
||||||
targets: SwitchTargets::static_if(1, after_cond, end),
|
targets: SwitchTargets::static_if(1, after_cond, end),
|
||||||
},
|
},
|
||||||
|
expr_id.into(),
|
||||||
);
|
);
|
||||||
if let Some((_, block)) = this.lower_expr_as_place(after_cond, *body, true)? {
|
if let Some((_, block)) = this.lower_expr_as_place(after_cond, *body, true)? {
|
||||||
this.set_goto(block, begin);
|
this.set_goto(block, begin, expr_id.into());
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
@ -478,12 +517,12 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
let into_iter_fn_op = Operand::const_zst(
|
let into_iter_fn_op = Operand::const_zst(
|
||||||
TyKind::FnDef(
|
TyKind::FnDef(
|
||||||
self.db.intern_callable_def(CallableDefId::FunctionId(into_iter_fn)).into(),
|
self.db.intern_callable_def(CallableDefId::FunctionId(into_iter_fn)).into(),
|
||||||
Substitution::from1(Interner, self.expr_ty(iterable))
|
Substitution::from1(Interner, self.expr_ty_without_adjust(iterable))
|
||||||
).intern(Interner));
|
).intern(Interner));
|
||||||
let iter_next_fn_op = Operand::const_zst(
|
let iter_next_fn_op = Operand::const_zst(
|
||||||
TyKind::FnDef(
|
TyKind::FnDef(
|
||||||
self.db.intern_callable_def(CallableDefId::FunctionId(iter_next_fn)).into(),
|
self.db.intern_callable_def(CallableDefId::FunctionId(iter_next_fn)).into(),
|
||||||
Substitution::from1(Interner, self.expr_ty(iterable))
|
Substitution::from1(Interner, self.expr_ty_without_adjust(iterable))
|
||||||
).intern(Interner));
|
).intern(Interner));
|
||||||
let &Some(iterator_ty) = &self.infer.type_of_for_iterator.get(&expr_id) else {
|
let &Some(iterator_ty) = &self.infer.type_of_for_iterator.get(&expr_id) else {
|
||||||
return Err(MirLowerError::TypeError("unknown for loop iterator type"));
|
return Err(MirLowerError::TypeError("unknown for loop iterator type"));
|
||||||
@ -494,13 +533,13 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
let iterator_place: Place = self.temp(iterator_ty.clone())?.into();
|
let iterator_place: Place = self.temp(iterator_ty.clone())?.into();
|
||||||
let option_item_place: Place = self.temp(option_item_ty.clone())?.into();
|
let option_item_place: Place = self.temp(option_item_ty.clone())?.into();
|
||||||
let ref_mut_iterator_place: Place = self.temp(ref_mut_iterator_ty)?.into();
|
let ref_mut_iterator_place: Place = self.temp(ref_mut_iterator_ty)?.into();
|
||||||
let Some(current) = self.lower_call_and_args(into_iter_fn_op, Some(iterable).into_iter(), iterator_place.clone(), current, false)?
|
let Some(current) = self.lower_call_and_args(into_iter_fn_op, Some(iterable).into_iter(), iterator_place.clone(), current, false, expr_id.into())?
|
||||||
else {
|
else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
self.push_assignment(current, ref_mut_iterator_place.clone(), Rvalue::Ref(BorrowKind::Mut { allow_two_phase_borrow: false }, iterator_place), expr_id.into());
|
self.push_assignment(current, ref_mut_iterator_place.clone(), Rvalue::Ref(BorrowKind::Mut { allow_two_phase_borrow: false }, iterator_place), expr_id.into());
|
||||||
self.lower_loop(current, place, label, |this, begin| {
|
self.lower_loop(current, place, label, expr_id.into(), |this, begin| {
|
||||||
let Some(current) = this.lower_call(iter_next_fn_op, vec![Operand::Copy(ref_mut_iterator_place)], option_item_place.clone(), begin, false)?
|
let Some(current) = this.lower_call(iter_next_fn_op, vec![Operand::Copy(ref_mut_iterator_place)], option_item_place.clone(), begin, false, expr_id.into())?
|
||||||
else {
|
else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
@ -516,7 +555,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
AdtPatternShape::Tuple { args: &[pat], ellipsis: None },
|
AdtPatternShape::Tuple { args: &[pat], ellipsis: None },
|
||||||
)?;
|
)?;
|
||||||
if let Some((_, block)) = this.lower_expr_as_place(current, body, true)? {
|
if let Some((_, block)) = this.lower_expr_as_place(current, body, true)? {
|
||||||
this.set_goto(block, begin);
|
this.set_goto(block, begin, expr_id.into());
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
@ -536,39 +575,36 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
place,
|
place,
|
||||||
current,
|
current,
|
||||||
self.is_uninhabited(expr_id),
|
self.is_uninhabited(expr_id),
|
||||||
|
expr_id.into(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let callee_ty = self.expr_ty_after_adjustments(*callee);
|
let callee_ty = self.expr_ty_after_adjustments(*callee);
|
||||||
match &callee_ty.data(Interner).kind {
|
match &callee_ty.data(Interner).kind {
|
||||||
chalk_ir::TyKind::FnDef(..) => {
|
chalk_ir::TyKind::FnDef(..) => {
|
||||||
let func = Operand::from_bytes(vec![], callee_ty.clone());
|
let func = Operand::from_bytes(vec![], callee_ty.clone());
|
||||||
self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id))
|
self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id), expr_id.into())
|
||||||
}
|
}
|
||||||
chalk_ir::TyKind::Function(_) => {
|
chalk_ir::TyKind::Function(_) => {
|
||||||
let Some((func, current)) = self.lower_expr_to_some_operand(*callee, current)? else {
|
let Some((func, current)) = self.lower_expr_to_some_operand(*callee, current)? else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id))
|
self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id), expr_id.into())
|
||||||
}
|
}
|
||||||
TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition),
|
TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition),
|
||||||
_ => return Err(MirLowerError::TypeError("function call on bad type")),
|
_ => return Err(MirLowerError::TypeError("function call on bad type")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::MethodCall { receiver, args, .. } => {
|
Expr::MethodCall { receiver, args, method_name, .. } => {
|
||||||
let (func_id, generic_args) =
|
let (func_id, generic_args) =
|
||||||
self.infer.method_resolution(expr_id).ok_or(MirLowerError::UnresolvedMethod)?;
|
self.infer.method_resolution(expr_id).ok_or_else(|| MirLowerError::UnresolvedMethod(format!("{}", method_name)))?;
|
||||||
let ty = chalk_ir::TyKind::FnDef(
|
let func = Operand::from_fn(self.db, func_id, generic_args);
|
||||||
CallableDefId::FunctionId(func_id).to_chalk(self.db),
|
|
||||||
generic_args,
|
|
||||||
)
|
|
||||||
.intern(Interner);
|
|
||||||
let func = Operand::from_bytes(vec![], ty);
|
|
||||||
self.lower_call_and_args(
|
self.lower_call_and_args(
|
||||||
func,
|
func,
|
||||||
iter::once(*receiver).chain(args.iter().copied()),
|
iter::once(*receiver).chain(args.iter().copied()),
|
||||||
place,
|
place,
|
||||||
current,
|
current,
|
||||||
self.is_uninhabited(expr_id),
|
self.is_uninhabited(expr_id),
|
||||||
|
expr_id.into(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Expr::Match { expr, arms } => {
|
Expr::Match { expr, arms } => {
|
||||||
@ -591,7 +627,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
let next = self.new_basic_block();
|
let next = self.new_basic_block();
|
||||||
let o = otherwise.get_or_insert_with(|| self.new_basic_block());
|
let o = otherwise.get_or_insert_with(|| self.new_basic_block());
|
||||||
if let Some((discr, c)) = self.lower_expr_to_some_operand(guard, then)? {
|
if let Some((discr, c)) = self.lower_expr_to_some_operand(guard, then)? {
|
||||||
self.set_terminator(c, Terminator::SwitchInt { discr, targets: SwitchTargets::static_if(1, next, *o) });
|
self.set_terminator(c, TerminatorKind::SwitchInt { discr, targets: SwitchTargets::static_if(1, next, *o) }, expr_id.into());
|
||||||
}
|
}
|
||||||
next
|
next
|
||||||
} else {
|
} else {
|
||||||
@ -599,7 +635,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
};
|
};
|
||||||
if let Some(block) = self.lower_expr_to_place(*expr, place.clone(), then)? {
|
if let Some(block) = self.lower_expr_to_place(*expr, place.clone(), then)? {
|
||||||
let r = end.get_or_insert_with(|| self.new_basic_block());
|
let r = end.get_or_insert_with(|| self.new_basic_block());
|
||||||
self.set_goto(block, *r);
|
self.set_goto(block, *r, expr_id.into());
|
||||||
}
|
}
|
||||||
match otherwise {
|
match otherwise {
|
||||||
Some(o) => current = o,
|
Some(o) => current = o,
|
||||||
@ -611,18 +647,17 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.is_unterminated(current) {
|
if self.is_unterminated(current) {
|
||||||
self.set_terminator(current, Terminator::Unreachable);
|
self.set_terminator(current, TerminatorKind::Unreachable, expr_id.into());
|
||||||
}
|
}
|
||||||
Ok(end)
|
Ok(end)
|
||||||
}
|
}
|
||||||
Expr::Continue { label } => match label {
|
Expr::Continue { label } => {
|
||||||
Some(_) => not_supported!("continue with label"),
|
let loop_data = match label {
|
||||||
None => {
|
Some(l) => self.labeled_loop_blocks.get(l).ok_or(MirLowerError::UnresolvedLabel)?,
|
||||||
let loop_data =
|
None => self.current_loop_blocks.as_ref().ok_or(MirLowerError::ContinueWithoutLoop)?,
|
||||||
self.current_loop_blocks.as_ref().ok_or(MirLowerError::ContinueWithoutLoop)?;
|
};
|
||||||
self.set_goto(current, loop_data.begin);
|
self.set_goto(current, loop_data.begin, expr_id.into());
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
|
||||||
},
|
},
|
||||||
&Expr::Break { expr, label } => {
|
&Expr::Break { expr, label } => {
|
||||||
if let Some(expr) = expr {
|
if let Some(expr) = expr {
|
||||||
@ -639,7 +674,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
Some(l) => self.labeled_loop_blocks.get(&l).ok_or(MirLowerError::UnresolvedLabel)?.end.expect("We always generate end for labeled loops"),
|
Some(l) => self.labeled_loop_blocks.get(&l).ok_or(MirLowerError::UnresolvedLabel)?.end.expect("We always generate end for labeled loops"),
|
||||||
None => self.current_loop_end()?,
|
None => self.current_loop_end()?,
|
||||||
};
|
};
|
||||||
self.set_goto(current, end);
|
self.set_goto(current, end, expr_id.into());
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
Expr::Return { expr } => {
|
Expr::Return { expr } => {
|
||||||
@ -650,7 +685,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.set_terminator(current, Terminator::Return);
|
self.set_terminator(current, TerminatorKind::Return, expr_id.into());
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
Expr::Yield { .. } => not_supported!("yield"),
|
Expr::Yield { .. } => not_supported!("yield"),
|
||||||
@ -672,7 +707,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
Some(p) => MirLowerError::UnresolvedName(p.display(self.db).to_string()),
|
Some(p) => MirLowerError::UnresolvedName(p.display(self.db).to_string()),
|
||||||
None => MirLowerError::RecordLiteralWithoutPath,
|
None => MirLowerError::RecordLiteralWithoutPath,
|
||||||
})?;
|
})?;
|
||||||
let subst = match self.expr_ty(expr_id).kind(Interner) {
|
let subst = match self.expr_ty_without_adjust(expr_id).kind(Interner) {
|
||||||
TyKind::Adt(_, s) => s.clone(),
|
TyKind::Adt(_, s) => s.clone(),
|
||||||
_ => not_supported!("Non ADT record literal"),
|
_ => not_supported!("Non ADT record literal"),
|
||||||
};
|
};
|
||||||
@ -757,7 +792,17 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into());
|
self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into());
|
||||||
Ok(Some(current))
|
Ok(Some(current))
|
||||||
}
|
}
|
||||||
Expr::Box { .. } => not_supported!("box expression"),
|
Expr::Box { expr } => {
|
||||||
|
let ty = self.expr_ty_after_adjustments(*expr);
|
||||||
|
self.push_assignment(current, place.clone(), Rvalue::ShallowInitBoxWithAlloc(ty), expr_id.into());
|
||||||
|
let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)? else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
let mut p = place;
|
||||||
|
p.projection.push(ProjectionElem::Deref);
|
||||||
|
self.push_assignment(current, p, operand.into(), expr_id.into());
|
||||||
|
Ok(Some(current))
|
||||||
|
},
|
||||||
Expr::Field { .. } | Expr::Index { .. } | Expr::UnaryOp { op: hir_def::hir::UnaryOp::Deref, .. } => {
|
Expr::Field { .. } | Expr::Index { .. } | Expr::UnaryOp { op: hir_def::hir::UnaryOp::Deref, .. } => {
|
||||||
let Some((p, current)) = self.lower_expr_as_place_without_adjust(current, expr_id, true)? else {
|
let Some((p, current)) = self.lower_expr_as_place_without_adjust(current, expr_id, true)? else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
@ -784,20 +829,63 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
},
|
},
|
||||||
Expr::BinaryOp { lhs, rhs, op } => {
|
Expr::BinaryOp { lhs, rhs, op } => {
|
||||||
let op = op.ok_or(MirLowerError::IncompleteExpr)?;
|
let op = op.ok_or(MirLowerError::IncompleteExpr)?;
|
||||||
if let hir_def::hir::BinaryOp::Assignment { op } = op {
|
let is_builtin = {
|
||||||
if op.is_some() {
|
// Without adjust here is a hack. We assume that we know every possible adjustment
|
||||||
not_supported!("assignment with arith op (like +=)");
|
// for binary operator, and use without adjust to simplify our conditions.
|
||||||
|
let lhs_ty = self.expr_ty_without_adjust(*lhs);
|
||||||
|
let rhs_ty = self.expr_ty_without_adjust(*rhs);
|
||||||
|
let builtin_inequal_impls = matches!(
|
||||||
|
op,
|
||||||
|
BinaryOp::ArithOp(ArithOp::Shl | ArithOp::Shr) | BinaryOp::Assignment { op: Some(ArithOp::Shl | ArithOp::Shr) }
|
||||||
|
);
|
||||||
|
lhs_ty.as_builtin().is_some() && rhs_ty.as_builtin().is_some() && (lhs_ty == rhs_ty || builtin_inequal_impls)
|
||||||
|
};
|
||||||
|
if !is_builtin {
|
||||||
|
if let Some((func_id, generic_args)) = self.infer.method_resolution(expr_id) {
|
||||||
|
let func = Operand::from_fn(self.db, func_id, generic_args);
|
||||||
|
return self.lower_call_and_args(
|
||||||
|
func,
|
||||||
|
[*lhs, *rhs].into_iter(),
|
||||||
|
place,
|
||||||
|
current,
|
||||||
|
self.is_uninhabited(expr_id),
|
||||||
|
expr_id.into(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
let Some((lhs_place, current)) =
|
}
|
||||||
|
if let hir_def::hir::BinaryOp::Assignment { op } = op {
|
||||||
|
if let Some(op) = op {
|
||||||
|
// last adjustment is `&mut` which we don't want it.
|
||||||
|
let adjusts = self
|
||||||
|
.infer
|
||||||
|
.expr_adjustments
|
||||||
|
.get(lhs)
|
||||||
|
.and_then(|x| x.split_last())
|
||||||
|
.map(|x| x.1)
|
||||||
|
.ok_or(MirLowerError::TypeError("adjustment of binary op was missing"))?;
|
||||||
|
let Some((lhs_place, current)) =
|
||||||
|
self.lower_expr_as_place_with_adjust(current, *lhs, false, adjusts)?
|
||||||
|
else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
let r_value = Rvalue::CheckedBinaryOp(op.into(), Operand::Copy(lhs_place.clone()), rhs_op);
|
||||||
|
self.push_assignment(current, lhs_place, r_value, expr_id.into());
|
||||||
|
return Ok(Some(current));
|
||||||
|
} else {
|
||||||
|
let Some((lhs_place, current)) =
|
||||||
self.lower_expr_as_place(current, *lhs, false)?
|
self.lower_expr_as_place(current, *lhs, false)?
|
||||||
else {
|
else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else {
|
let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
self.push_assignment(current, lhs_place, rhs_op.into(), expr_id.into());
|
self.push_assignment(current, lhs_place, rhs_op.into(), expr_id.into());
|
||||||
return Ok(Some(current));
|
return Ok(Some(current));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let Some((lhs_op, current)) = self.lower_expr_to_some_operand(*lhs, current)? else {
|
let Some((lhs_op, current)) = self.lower_expr_to_some_operand(*lhs, current)? else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
@ -826,7 +914,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
Ok(Some(current))
|
Ok(Some(current))
|
||||||
}
|
}
|
||||||
&Expr::Range { lhs, rhs, range_type: _ } => {
|
&Expr::Range { lhs, rhs, range_type: _ } => {
|
||||||
let ty = self.expr_ty(expr_id);
|
let ty = self.expr_ty_without_adjust(expr_id);
|
||||||
let Some((adt, subst)) = ty.as_adt() else {
|
let Some((adt, subst)) = ty.as_adt() else {
|
||||||
return Err(MirLowerError::TypeError("Range type is not adt"));
|
return Err(MirLowerError::TypeError("Range type is not adt"));
|
||||||
};
|
};
|
||||||
@ -869,7 +957,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
Ok(Some(current))
|
Ok(Some(current))
|
||||||
},
|
},
|
||||||
Expr::Closure { .. } => {
|
Expr::Closure { .. } => {
|
||||||
let ty = self.expr_ty(expr_id);
|
let ty = self.expr_ty_without_adjust(expr_id);
|
||||||
let TyKind::Closure(id, _) = ty.kind(Interner) else {
|
let TyKind::Closure(id, _) = ty.kind(Interner) else {
|
||||||
not_supported!("closure with non closure type");
|
not_supported!("closure with non closure type");
|
||||||
};
|
};
|
||||||
@ -893,7 +981,12 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
};
|
};
|
||||||
match &capture.kind {
|
match &capture.kind {
|
||||||
CaptureKind::ByRef(bk) => {
|
CaptureKind::ByRef(bk) => {
|
||||||
let tmp: Place = self.temp(capture.ty.clone())?.into();
|
let placeholder_subst = match self.owner.as_generic_def_id() {
|
||||||
|
Some(x) => TyBuilder::placeholder_subst(self.db, x),
|
||||||
|
None => Substitution::empty(Interner),
|
||||||
|
};
|
||||||
|
let tmp_ty = capture.ty.clone().substitute(Interner, &placeholder_subst);
|
||||||
|
let tmp: Place = self.temp(tmp_ty)?.into();
|
||||||
self.push_assignment(
|
self.push_assignment(
|
||||||
current,
|
current,
|
||||||
tmp.clone(),
|
tmp.clone(),
|
||||||
@ -928,7 +1021,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
let r = Rvalue::Aggregate(
|
let r = Rvalue::Aggregate(
|
||||||
AggregateKind::Tuple(self.expr_ty(expr_id)),
|
AggregateKind::Tuple(self.expr_ty_without_adjust(expr_id)),
|
||||||
values,
|
values,
|
||||||
);
|
);
|
||||||
self.push_assignment(current, place, r, expr_id.into());
|
self.push_assignment(current, place, r, expr_id.into());
|
||||||
@ -936,7 +1029,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
}
|
}
|
||||||
Expr::Array(l) => match l {
|
Expr::Array(l) => match l {
|
||||||
Array::ElementList { elements, .. } => {
|
Array::ElementList { elements, .. } => {
|
||||||
let elem_ty = match &self.expr_ty(expr_id).data(Interner).kind {
|
let elem_ty = match &self.expr_ty_without_adjust(expr_id).data(Interner).kind {
|
||||||
TyKind::Array(ty, _) => ty.clone(),
|
TyKind::Array(ty, _) => ty.clone(),
|
||||||
_ => {
|
_ => {
|
||||||
return Err(MirLowerError::TypeError(
|
return Err(MirLowerError::TypeError(
|
||||||
@ -968,7 +1061,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
let Some((init, current)) = self.lower_expr_to_some_operand(*initializer, current)? else {
|
let Some((init, current)) = self.lower_expr_to_some_operand(*initializer, current)? else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
let len = match &self.expr_ty(expr_id).data(Interner).kind {
|
let len = match &self.expr_ty_without_adjust(expr_id).data(Interner).kind {
|
||||||
TyKind::Array(_, len) => len.clone(),
|
TyKind::Array(_, len) => len.clone(),
|
||||||
_ => {
|
_ => {
|
||||||
return Err(MirLowerError::TypeError(
|
return Err(MirLowerError::TypeError(
|
||||||
@ -982,7 +1075,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Expr::Literal(l) => {
|
Expr::Literal(l) => {
|
||||||
let ty = self.expr_ty(expr_id);
|
let ty = self.expr_ty_without_adjust(expr_id);
|
||||||
let op = self.lower_literal_to_operand(ty, l)?;
|
let op = self.lower_literal_to_operand(ty, l)?;
|
||||||
self.push_assignment(current, place, op.into(), expr_id.into());
|
self.push_assignment(current, place, op.into(), expr_id.into());
|
||||||
Ok(Some(current))
|
Ok(Some(current))
|
||||||
@ -1057,8 +1150,30 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
place: Place,
|
place: Place,
|
||||||
subst: Substitution,
|
subst: Substitution,
|
||||||
span: MirSpan,
|
span: MirSpan,
|
||||||
|
ty: Ty,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let c = self.db.const_eval(const_id, subst)?;
|
let c = if subst.len(Interner) != 0 {
|
||||||
|
// We can't evaluate constant with substitution now, as generics are not monomorphized in lowering.
|
||||||
|
intern_const_scalar(ConstScalar::UnevaluatedConst(const_id, subst), ty)
|
||||||
|
} else {
|
||||||
|
let (const_id, subst) = lookup_impl_const(
|
||||||
|
self.db,
|
||||||
|
self.db.trait_environment_for_body(self.owner),
|
||||||
|
const_id,
|
||||||
|
subst,
|
||||||
|
);
|
||||||
|
let name = self
|
||||||
|
.db
|
||||||
|
.const_data(const_id)
|
||||||
|
.name
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|x| x.as_str())
|
||||||
|
.unwrap_or("_")
|
||||||
|
.to_owned();
|
||||||
|
self.db
|
||||||
|
.const_eval(const_id.into(), subst)
|
||||||
|
.map_err(|e| MirLowerError::ConstEvalError(name, Box::new(e)))?
|
||||||
|
};
|
||||||
self.write_const_to_place(c, prev_block, place, span)
|
self.write_const_to_place(c, prev_block, place, span)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1114,6 +1229,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
place: Place,
|
place: Place,
|
||||||
mut current: BasicBlockId,
|
mut current: BasicBlockId,
|
||||||
is_uninhabited: bool,
|
is_uninhabited: bool,
|
||||||
|
span: MirSpan,
|
||||||
) -> Result<Option<BasicBlockId>> {
|
) -> Result<Option<BasicBlockId>> {
|
||||||
let Some(args) = args
|
let Some(args) = args
|
||||||
.map(|arg| {
|
.map(|arg| {
|
||||||
@ -1128,7 +1244,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
else {
|
else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
self.lower_call(func, args, place, current, is_uninhabited)
|
self.lower_call(func, args, place, current, is_uninhabited, span)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lower_call(
|
fn lower_call(
|
||||||
@ -1138,11 +1254,12 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
place: Place,
|
place: Place,
|
||||||
current: BasicBlockId,
|
current: BasicBlockId,
|
||||||
is_uninhabited: bool,
|
is_uninhabited: bool,
|
||||||
|
span: MirSpan,
|
||||||
) -> Result<Option<BasicBlockId>> {
|
) -> Result<Option<BasicBlockId>> {
|
||||||
let b = if is_uninhabited { None } else { Some(self.new_basic_block()) };
|
let b = if is_uninhabited { None } else { Some(self.new_basic_block()) };
|
||||||
self.set_terminator(
|
self.set_terminator(
|
||||||
current,
|
current,
|
||||||
Terminator::Call {
|
TerminatorKind::Call {
|
||||||
func,
|
func,
|
||||||
args,
|
args,
|
||||||
destination: place,
|
destination: place,
|
||||||
@ -1150,6 +1267,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
cleanup: None,
|
cleanup: None,
|
||||||
from_hir_call: true,
|
from_hir_call: true,
|
||||||
},
|
},
|
||||||
|
span,
|
||||||
);
|
);
|
||||||
Ok(b)
|
Ok(b)
|
||||||
}
|
}
|
||||||
@ -1158,15 +1276,15 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
self.result.basic_blocks[source].terminator.is_none()
|
self.result.basic_blocks[source].terminator.is_none()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_terminator(&mut self, source: BasicBlockId, terminator: Terminator) {
|
fn set_terminator(&mut self, source: BasicBlockId, terminator: TerminatorKind, span: MirSpan) {
|
||||||
self.result.basic_blocks[source].terminator = Some(terminator);
|
self.result.basic_blocks[source].terminator = Some(Terminator { span, kind: terminator });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_goto(&mut self, source: BasicBlockId, target: BasicBlockId) {
|
fn set_goto(&mut self, source: BasicBlockId, target: BasicBlockId, span: MirSpan) {
|
||||||
self.set_terminator(source, Terminator::Goto { target });
|
self.set_terminator(source, TerminatorKind::Goto { target }, span);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expr_ty(&self, e: ExprId) -> Ty {
|
fn expr_ty_without_adjust(&self, e: ExprId) -> Ty {
|
||||||
self.infer[e].clone()
|
self.infer[e].clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1177,7 +1295,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
ty = Some(x.target.clone());
|
ty = Some(x.target.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ty.unwrap_or_else(|| self.expr_ty(e))
|
ty.unwrap_or_else(|| self.expr_ty_without_adjust(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_statement(&mut self, block: BasicBlockId, statement: Statement) {
|
fn push_statement(&mut self, block: BasicBlockId, statement: Statement) {
|
||||||
@ -1211,6 +1329,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
prev_block: BasicBlockId,
|
prev_block: BasicBlockId,
|
||||||
place: Place,
|
place: Place,
|
||||||
label: Option<LabelId>,
|
label: Option<LabelId>,
|
||||||
|
span: MirSpan,
|
||||||
f: impl FnOnce(&mut MirLowerCtx<'_>, BasicBlockId) -> Result<()>,
|
f: impl FnOnce(&mut MirLowerCtx<'_>, BasicBlockId) -> Result<()>,
|
||||||
) -> Result<Option<BasicBlockId>> {
|
) -> Result<Option<BasicBlockId>> {
|
||||||
let begin = self.new_basic_block();
|
let begin = self.new_basic_block();
|
||||||
@ -1228,7 +1347,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
self.set_goto(prev_block, begin);
|
self.set_goto(prev_block, begin, span);
|
||||||
f(self, begin)?;
|
f(self, begin)?;
|
||||||
let my = mem::replace(&mut self.current_loop_blocks, prev).ok_or(
|
let my = mem::replace(&mut self.current_loop_blocks, prev).ok_or(
|
||||||
MirLowerError::ImplementationError("current_loop_blocks is corrupt".to_string()),
|
MirLowerError::ImplementationError("current_loop_blocks is corrupt".to_string()),
|
||||||
@ -1247,14 +1366,15 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
b1: Option<BasicBlockId>,
|
b1: Option<BasicBlockId>,
|
||||||
b2: Option<BasicBlockId>,
|
b2: Option<BasicBlockId>,
|
||||||
|
span: MirSpan,
|
||||||
) -> Option<BasicBlockId> {
|
) -> Option<BasicBlockId> {
|
||||||
match (b1, b2) {
|
match (b1, b2) {
|
||||||
(None, None) => None,
|
(None, None) => None,
|
||||||
(None, Some(b)) | (Some(b), None) => Some(b),
|
(None, Some(b)) | (Some(b), None) => Some(b),
|
||||||
(Some(b1), Some(b2)) => {
|
(Some(b1), Some(b2)) => {
|
||||||
let bm = self.new_basic_block();
|
let bm = self.new_basic_block();
|
||||||
self.set_goto(b1, bm);
|
self.set_goto(b1, bm, span);
|
||||||
self.set_goto(b2, bm);
|
self.set_goto(b2, bm, span);
|
||||||
Some(bm)
|
Some(bm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1332,6 +1452,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
mut current: BasicBlockId,
|
mut current: BasicBlockId,
|
||||||
tail: Option<ExprId>,
|
tail: Option<ExprId>,
|
||||||
place: Place,
|
place: Place,
|
||||||
|
span: MirSpan,
|
||||||
) -> Result<Option<Idx<BasicBlock>>> {
|
) -> Result<Option<Idx<BasicBlock>>> {
|
||||||
for statement in statements.iter() {
|
for statement in statements.iter() {
|
||||||
match statement {
|
match statement {
|
||||||
@ -1355,13 +1476,13 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
match (else_block, else_branch) {
|
match (else_block, else_branch) {
|
||||||
(None, _) => (),
|
(None, _) => (),
|
||||||
(Some(else_block), None) => {
|
(Some(else_block), None) => {
|
||||||
self.set_terminator(else_block, Terminator::Unreachable);
|
self.set_terminator(else_block, TerminatorKind::Unreachable, span);
|
||||||
}
|
}
|
||||||
(Some(else_block), Some(else_branch)) => {
|
(Some(else_block), Some(else_branch)) => {
|
||||||
if let Some((_, b)) =
|
if let Some((_, b)) =
|
||||||
self.lower_expr_as_place(else_block, *else_branch, true)?
|
self.lower_expr_as_place(else_block, *else_branch, true)?
|
||||||
{
|
{
|
||||||
self.set_terminator(b, Terminator::Unreachable);
|
self.set_terminator(b, TerminatorKind::Unreachable, span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1438,7 +1559,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
BindingAnnotation::Unannotated,
|
BindingAnnotation::Unannotated,
|
||||||
)?;
|
)?;
|
||||||
if let Some(b) = r.1 {
|
if let Some(b) = r.1 {
|
||||||
self.set_terminator(b, Terminator::Unreachable);
|
self.set_terminator(b, TerminatorKind::Unreachable, param.into());
|
||||||
}
|
}
|
||||||
current = r.0;
|
current = r.0;
|
||||||
}
|
}
|
||||||
@ -1456,6 +1577,18 @@ impl<'ctx> MirLowerCtx<'ctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn const_eval_discriminant(&self, variant: EnumVariantId) -> Result<i128> {
|
||||||
|
let r = self.db.const_eval_discriminant(variant);
|
||||||
|
match r {
|
||||||
|
Ok(r) => Ok(r),
|
||||||
|
Err(e) => {
|
||||||
|
let data = self.db.enum_data(variant.parent);
|
||||||
|
let name = format!("{}::{}", data.name, data.variants[variant.local_id].name);
|
||||||
|
Err(MirLowerError::ConstEvalError(name, Box::new(e)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
|
fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
|
||||||
@ -1498,7 +1631,7 @@ pub fn mir_body_for_closure_query(
|
|||||||
// 0 is return local
|
// 0 is return local
|
||||||
ctx.result.locals.alloc(Local { ty: infer[*root].clone() });
|
ctx.result.locals.alloc(Local { ty: infer[*root].clone() });
|
||||||
ctx.result.locals.alloc(Local { ty: infer[expr].clone() });
|
ctx.result.locals.alloc(Local { ty: infer[expr].clone() });
|
||||||
let Some(sig) = substs.at(Interner, 0).assert_ty_ref(Interner).callable_sig(db) else {
|
let Some(sig) = ClosureSubst(substs).sig_ty().callable_sig(db) else {
|
||||||
implementation_error!("closure has not callable sig");
|
implementation_error!("closure has not callable sig");
|
||||||
};
|
};
|
||||||
let current = ctx.lower_params_and_bindings(
|
let current = ctx.lower_params_and_bindings(
|
||||||
@ -1506,7 +1639,7 @@ pub fn mir_body_for_closure_query(
|
|||||||
|_| true,
|
|_| true,
|
||||||
)?;
|
)?;
|
||||||
if let Some(b) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? {
|
if let Some(b) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? {
|
||||||
ctx.set_terminator(b, Terminator::Return);
|
ctx.set_terminator(b, TerminatorKind::Return, (*root).into());
|
||||||
}
|
}
|
||||||
let mut upvar_map: FxHashMap<LocalId, Vec<(&CapturedItem, usize)>> = FxHashMap::default();
|
let mut upvar_map: FxHashMap<LocalId, Vec<(&CapturedItem, usize)>> = FxHashMap::default();
|
||||||
for (i, capture) in captures.iter().enumerate() {
|
for (i, capture) in captures.iter().enumerate() {
|
||||||
@ -1628,7 +1761,7 @@ pub fn lower_to_mir(
|
|||||||
ctx.lower_params_and_bindings([].into_iter(), binding_picker)?
|
ctx.lower_params_and_bindings([].into_iter(), binding_picker)?
|
||||||
};
|
};
|
||||||
if let Some(b) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? {
|
if let Some(b) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? {
|
||||||
ctx.set_terminator(b, Terminator::Return);
|
ctx.set_terminator(b, TerminatorKind::Return, root_expr.into());
|
||||||
}
|
}
|
||||||
Ok(ctx.result)
|
Ok(ctx.result)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! MIR lowering for places
|
//! MIR lowering for places
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use hir_def::FunctionId;
|
use hir_def::{lang_item::lang_attr, FunctionId};
|
||||||
use hir_expand::name;
|
use hir_expand::name;
|
||||||
|
|
||||||
macro_rules! not_supported {
|
macro_rules! not_supported {
|
||||||
@ -16,7 +16,7 @@ impl MirLowerCtx<'_> {
|
|||||||
expr_id: ExprId,
|
expr_id: ExprId,
|
||||||
prev_block: BasicBlockId,
|
prev_block: BasicBlockId,
|
||||||
) -> Result<Option<(Place, BasicBlockId)>> {
|
) -> Result<Option<(Place, BasicBlockId)>> {
|
||||||
let ty = self.expr_ty(expr_id);
|
let ty = self.expr_ty_without_adjust(expr_id);
|
||||||
let place = self.temp(ty)?;
|
let place = self.temp(ty)?;
|
||||||
let Some(current) = self.lower_expr_to_place_without_adjust(expr_id, place.into(), prev_block)? else {
|
let Some(current) = self.lower_expr_to_place_without_adjust(expr_id, place.into(), prev_block)? else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
@ -30,8 +30,10 @@ impl MirLowerCtx<'_> {
|
|||||||
prev_block: BasicBlockId,
|
prev_block: BasicBlockId,
|
||||||
adjustments: &[Adjustment],
|
adjustments: &[Adjustment],
|
||||||
) -> Result<Option<(Place, BasicBlockId)>> {
|
) -> Result<Option<(Place, BasicBlockId)>> {
|
||||||
let ty =
|
let ty = adjustments
|
||||||
adjustments.last().map(|x| x.target.clone()).unwrap_or_else(|| self.expr_ty(expr_id));
|
.last()
|
||||||
|
.map(|x| x.target.clone())
|
||||||
|
.unwrap_or_else(|| self.expr_ty_without_adjust(expr_id));
|
||||||
let place = self.temp(ty)?;
|
let place = self.temp(ty)?;
|
||||||
let Some(current) = self.lower_expr_to_place_with_adjust(expr_id, place.into(), prev_block, adjustments)? else {
|
let Some(current) = self.lower_expr_to_place_with_adjust(expr_id, place.into(), prev_block, adjustments)? else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
@ -80,7 +82,7 @@ impl MirLowerCtx<'_> {
|
|||||||
r,
|
r,
|
||||||
rest.last()
|
rest.last()
|
||||||
.map(|x| x.target.clone())
|
.map(|x| x.target.clone())
|
||||||
.unwrap_or_else(|| self.expr_ty(expr_id)),
|
.unwrap_or_else(|| self.expr_ty_without_adjust(expr_id)),
|
||||||
last.target.clone(),
|
last.target.clone(),
|
||||||
expr_id.into(),
|
expr_id.into(),
|
||||||
match od.0 {
|
match od.0 {
|
||||||
@ -135,17 +137,39 @@ impl MirLowerCtx<'_> {
|
|||||||
};
|
};
|
||||||
match pr {
|
match pr {
|
||||||
ValueNs::LocalBinding(pat_id) => {
|
ValueNs::LocalBinding(pat_id) => {
|
||||||
Ok(Some((self.result.binding_locals[pat_id].into(), current)))
|
Ok(Some((self.binding_local(pat_id)?.into(), current)))
|
||||||
|
}
|
||||||
|
ValueNs::StaticId(s) => {
|
||||||
|
let ty = self.expr_ty_without_adjust(expr_id);
|
||||||
|
let ref_ty =
|
||||||
|
TyKind::Ref(Mutability::Not, static_lifetime(), ty).intern(Interner);
|
||||||
|
let mut temp: Place = self.temp(ref_ty)?.into();
|
||||||
|
self.push_assignment(
|
||||||
|
current,
|
||||||
|
temp.clone(),
|
||||||
|
Operand::Static(s).into(),
|
||||||
|
expr_id.into(),
|
||||||
|
);
|
||||||
|
temp.projection.push(ProjectionElem::Deref);
|
||||||
|
Ok(Some((temp, current)))
|
||||||
}
|
}
|
||||||
_ => try_rvalue(self),
|
_ => try_rvalue(self),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::UnaryOp { expr, op } => match op {
|
Expr::UnaryOp { expr, op } => match op {
|
||||||
hir_def::hir::UnaryOp::Deref => {
|
hir_def::hir::UnaryOp::Deref => {
|
||||||
if !matches!(
|
let is_builtin = match self.expr_ty_without_adjust(*expr).kind(Interner) {
|
||||||
self.expr_ty(*expr).kind(Interner),
|
TyKind::Ref(..) | TyKind::Raw(..) => true,
|
||||||
TyKind::Ref(..) | TyKind::Raw(..)
|
TyKind::Adt(id, _) => {
|
||||||
) {
|
if let Some(lang_item) = lang_attr(self.db.upcast(), id.0) {
|
||||||
|
lang_item == LangItem::OwnedBox
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
if !is_builtin {
|
||||||
let Some((p, current)) = self.lower_expr_as_place(current, *expr, true)? else {
|
let Some((p, current)) = self.lower_expr_as_place(current, *expr, true)? else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
@ -153,7 +177,7 @@ impl MirLowerCtx<'_> {
|
|||||||
current,
|
current,
|
||||||
p,
|
p,
|
||||||
self.expr_ty_after_adjustments(*expr),
|
self.expr_ty_after_adjustments(*expr),
|
||||||
self.expr_ty(expr_id),
|
self.expr_ty_without_adjust(expr_id),
|
||||||
expr_id.into(),
|
expr_id.into(),
|
||||||
'b: {
|
'b: {
|
||||||
if let Some((f, _)) = self.infer.method_resolution(expr_id) {
|
if let Some((f, _)) = self.infer.method_resolution(expr_id) {
|
||||||
@ -198,7 +222,7 @@ impl MirLowerCtx<'_> {
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
let Some(index_fn) = self.infer.method_resolution(expr_id) else {
|
let Some(index_fn) = self.infer.method_resolution(expr_id) else {
|
||||||
return Err(MirLowerError::UnresolvedMethod);
|
return Err(MirLowerError::UnresolvedMethod("[overloaded index]".to_string()));
|
||||||
};
|
};
|
||||||
let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else {
|
let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
@ -210,7 +234,7 @@ impl MirLowerCtx<'_> {
|
|||||||
current,
|
current,
|
||||||
base_place,
|
base_place,
|
||||||
base_ty,
|
base_ty,
|
||||||
self.expr_ty(expr_id),
|
self.expr_ty_without_adjust(expr_id),
|
||||||
index_operand,
|
index_operand,
|
||||||
expr_id.into(),
|
expr_id.into(),
|
||||||
index_fn,
|
index_fn,
|
||||||
@ -266,7 +290,7 @@ impl MirLowerCtx<'_> {
|
|||||||
)
|
)
|
||||||
.intern(Interner),
|
.intern(Interner),
|
||||||
);
|
);
|
||||||
let Some(current) = self.lower_call(index_fn_op, vec![Operand::Copy(ref_place), index_operand], result.clone(), current, false)? else {
|
let Some(current) = self.lower_call(index_fn_op, vec![Operand::Copy(ref_place), index_operand], result.clone(), current, false, span)? else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
result.projection.push(ProjectionElem::Deref);
|
result.projection.push(ProjectionElem::Deref);
|
||||||
@ -313,7 +337,7 @@ impl MirLowerCtx<'_> {
|
|||||||
.intern(Interner),
|
.intern(Interner),
|
||||||
);
|
);
|
||||||
let mut result: Place = self.temp(target_ty_ref)?.into();
|
let mut result: Place = self.temp(target_ty_ref)?.into();
|
||||||
let Some(current) = self.lower_call(deref_fn_op, vec![Operand::Copy(ref_place)], result.clone(), current, false)? else {
|
let Some(current) = self.lower_call(deref_fn_op, vec![Operand::Copy(ref_place)], result.clone(), current, false, span)? else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
result.projection.push(ProjectionElem::Deref);
|
result.projection.push(ProjectionElem::Deref);
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
//! MIR lowering for patterns
|
//! MIR lowering for patterns
|
||||||
|
|
||||||
|
use hir_def::resolver::HasResolver;
|
||||||
|
|
||||||
use crate::utils::pattern_matching_dereference_count;
|
use crate::utils::pattern_matching_dereference_count;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -72,7 +74,7 @@ impl MirLowerCtx<'_> {
|
|||||||
*pat,
|
*pat,
|
||||||
binding_mode,
|
binding_mode,
|
||||||
)?;
|
)?;
|
||||||
self.set_goto(next, then_target);
|
self.set_goto(next, then_target, pattern.into());
|
||||||
match next_else {
|
match next_else {
|
||||||
Some(t) => {
|
Some(t) => {
|
||||||
current = t;
|
current = t;
|
||||||
@ -85,13 +87,13 @@ impl MirLowerCtx<'_> {
|
|||||||
}
|
}
|
||||||
if !finished {
|
if !finished {
|
||||||
let ce = *current_else.get_or_insert_with(|| self.new_basic_block());
|
let ce = *current_else.get_or_insert_with(|| self.new_basic_block());
|
||||||
self.set_goto(current, ce);
|
self.set_goto(current, ce, pattern.into());
|
||||||
}
|
}
|
||||||
(then_target, current_else)
|
(then_target, current_else)
|
||||||
}
|
}
|
||||||
Pat::Record { args, .. } => {
|
Pat::Record { args, .. } => {
|
||||||
let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
|
let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
|
||||||
not_supported!("unresolved variant");
|
not_supported!("unresolved variant for record");
|
||||||
};
|
};
|
||||||
self.pattern_matching_variant(
|
self.pattern_matching_variant(
|
||||||
cond_ty,
|
cond_ty,
|
||||||
@ -106,11 +108,8 @@ impl MirLowerCtx<'_> {
|
|||||||
}
|
}
|
||||||
Pat::Range { .. } => not_supported!("range pattern"),
|
Pat::Range { .. } => not_supported!("range pattern"),
|
||||||
Pat::Slice { .. } => not_supported!("slice pattern"),
|
Pat::Slice { .. } => not_supported!("slice pattern"),
|
||||||
Pat::Path(_) => {
|
Pat::Path(p) => match self.infer.variant_resolution_for_pat(pattern) {
|
||||||
let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
|
Some(variant) => self.pattern_matching_variant(
|
||||||
not_supported!("unresolved variant");
|
|
||||||
};
|
|
||||||
self.pattern_matching_variant(
|
|
||||||
cond_ty,
|
cond_ty,
|
||||||
binding_mode,
|
binding_mode,
|
||||||
cond_place,
|
cond_place,
|
||||||
@ -119,8 +118,60 @@ impl MirLowerCtx<'_> {
|
|||||||
pattern.into(),
|
pattern.into(),
|
||||||
current_else,
|
current_else,
|
||||||
AdtPatternShape::Unit,
|
AdtPatternShape::Unit,
|
||||||
)?
|
)?,
|
||||||
}
|
None => {
|
||||||
|
let unresolved_name = || MirLowerError::unresolved_path(self.db, p);
|
||||||
|
let resolver = self.owner.resolver(self.db.upcast());
|
||||||
|
let pr = resolver
|
||||||
|
.resolve_path_in_value_ns(self.db.upcast(), p)
|
||||||
|
.ok_or_else(unresolved_name)?;
|
||||||
|
match pr {
|
||||||
|
ResolveValueResult::ValueNs(v) => match v {
|
||||||
|
ValueNs::ConstId(c) => {
|
||||||
|
let tmp: Place = self.temp(cond_ty.clone())?.into();
|
||||||
|
let span = pattern.into();
|
||||||
|
self.lower_const(
|
||||||
|
c,
|
||||||
|
current,
|
||||||
|
tmp.clone(),
|
||||||
|
Substitution::empty(Interner),
|
||||||
|
span,
|
||||||
|
cond_ty.clone(),
|
||||||
|
)?;
|
||||||
|
let tmp2: Place = self.temp(TyBuilder::bool())?.into();
|
||||||
|
self.push_assignment(
|
||||||
|
current,
|
||||||
|
tmp2.clone(),
|
||||||
|
Rvalue::CheckedBinaryOp(
|
||||||
|
BinOp::Eq,
|
||||||
|
Operand::Copy(tmp),
|
||||||
|
Operand::Copy(cond_place),
|
||||||
|
),
|
||||||
|
span,
|
||||||
|
);
|
||||||
|
let next = self.new_basic_block();
|
||||||
|
let else_target =
|
||||||
|
current_else.unwrap_or_else(|| self.new_basic_block());
|
||||||
|
self.set_terminator(
|
||||||
|
current,
|
||||||
|
TerminatorKind::SwitchInt {
|
||||||
|
discr: Operand::Copy(tmp2),
|
||||||
|
targets: SwitchTargets::static_if(1, next, else_target),
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
);
|
||||||
|
(next, Some(else_target))
|
||||||
|
}
|
||||||
|
_ => not_supported!(
|
||||||
|
"path in pattern position that is not const or variant"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
ResolveValueResult::Partial(_, _) => {
|
||||||
|
not_supported!("assoc const in patterns")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
Pat::Lit(l) => match &self.body.exprs[*l] {
|
Pat::Lit(l) => match &self.body.exprs[*l] {
|
||||||
Expr::Literal(l) => {
|
Expr::Literal(l) => {
|
||||||
let c = self.lower_literal_to_operand(cond_ty, l)?;
|
let c = self.lower_literal_to_operand(cond_ty, l)?;
|
||||||
@ -218,10 +269,11 @@ impl MirLowerCtx<'_> {
|
|||||||
let discr = Operand::Copy(discr);
|
let discr = Operand::Copy(discr);
|
||||||
self.set_terminator(
|
self.set_terminator(
|
||||||
current,
|
current,
|
||||||
Terminator::SwitchInt {
|
TerminatorKind::SwitchInt {
|
||||||
discr,
|
discr,
|
||||||
targets: SwitchTargets::static_if(1, then_target, else_target),
|
targets: SwitchTargets::static_if(1, then_target, else_target),
|
||||||
},
|
},
|
||||||
|
pattern.into(),
|
||||||
);
|
);
|
||||||
Ok((then_target, Some(else_target)))
|
Ok((then_target, Some(else_target)))
|
||||||
}
|
}
|
||||||
@ -244,8 +296,7 @@ impl MirLowerCtx<'_> {
|
|||||||
};
|
};
|
||||||
Ok(match variant {
|
Ok(match variant {
|
||||||
VariantId::EnumVariantId(v) => {
|
VariantId::EnumVariantId(v) => {
|
||||||
let e = self.db.const_eval_discriminant(v)? as u128;
|
let e = self.const_eval_discriminant(v)? as u128;
|
||||||
let next = self.new_basic_block();
|
|
||||||
let tmp = self.discr_temp_place();
|
let tmp = self.discr_temp_place();
|
||||||
self.push_assignment(
|
self.push_assignment(
|
||||||
current,
|
current,
|
||||||
@ -253,13 +304,15 @@ impl MirLowerCtx<'_> {
|
|||||||
Rvalue::Discriminant(cond_place.clone()),
|
Rvalue::Discriminant(cond_place.clone()),
|
||||||
span,
|
span,
|
||||||
);
|
);
|
||||||
|
let next = self.new_basic_block();
|
||||||
let else_target = current_else.unwrap_or_else(|| self.new_basic_block());
|
let else_target = current_else.unwrap_or_else(|| self.new_basic_block());
|
||||||
self.set_terminator(
|
self.set_terminator(
|
||||||
current,
|
current,
|
||||||
Terminator::SwitchInt {
|
TerminatorKind::SwitchInt {
|
||||||
discr: Operand::Copy(tmp),
|
discr: Operand::Copy(tmp),
|
||||||
targets: SwitchTargets::static_if(e, next, else_target),
|
targets: SwitchTargets::static_if(e, next, else_target),
|
||||||
},
|
},
|
||||||
|
span,
|
||||||
);
|
);
|
||||||
let enum_data = self.db.enum_data(v.parent);
|
let enum_data = self.db.enum_data(v.parent);
|
||||||
self.pattern_matching_variant_fields(
|
self.pattern_matching_variant_fields(
|
||||||
|
@ -11,19 +11,52 @@ use la_arena::ArenaMap;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase,
|
db::HirDatabase,
|
||||||
display::HirDisplay,
|
display::{ClosureStyle, HirDisplay},
|
||||||
mir::{PlaceElem, ProjectionElem, StatementKind, Terminator},
|
mir::{PlaceElem, ProjectionElem, StatementKind, TerminatorKind},
|
||||||
|
ClosureId,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
AggregateKind, BasicBlockId, BorrowKind, LocalId, MirBody, Operand, Place, Rvalue, UnOp,
|
AggregateKind, BasicBlockId, BorrowKind, LocalId, MirBody, Operand, Place, Rvalue, UnOp,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
macro_rules! w {
|
||||||
|
($dst:expr, $($arg:tt)*) => {
|
||||||
|
{ let _ = write!($dst, $($arg)*); }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! wln {
|
||||||
|
($dst:expr) => {
|
||||||
|
{ let _ = writeln!($dst); }
|
||||||
|
};
|
||||||
|
($dst:expr, $($arg:tt)*) => {
|
||||||
|
{ let _ = writeln!($dst, $($arg)*); }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
impl MirBody {
|
impl MirBody {
|
||||||
pub fn pretty_print(&self, db: &dyn HirDatabase) -> String {
|
pub fn pretty_print(&self, db: &dyn HirDatabase) -> String {
|
||||||
let hir_body = db.body(self.owner);
|
let hir_body = db.body(self.owner);
|
||||||
let mut ctx = MirPrettyCtx::new(self, &hir_body, db);
|
let mut ctx = MirPrettyCtx::new(self, &hir_body, db);
|
||||||
ctx.for_body(ctx.body.owner);
|
ctx.for_body(|this| match ctx.body.owner {
|
||||||
|
hir_def::DefWithBodyId::FunctionId(id) => {
|
||||||
|
let data = db.function_data(id);
|
||||||
|
w!(this, "fn {}() ", data.name);
|
||||||
|
}
|
||||||
|
hir_def::DefWithBodyId::StaticId(id) => {
|
||||||
|
let data = db.static_data(id);
|
||||||
|
w!(this, "static {}: _ = ", data.name);
|
||||||
|
}
|
||||||
|
hir_def::DefWithBodyId::ConstId(id) => {
|
||||||
|
let data = db.const_data(id);
|
||||||
|
w!(this, "const {}: _ = ", data.name.as_ref().unwrap_or(&Name::missing()));
|
||||||
|
}
|
||||||
|
hir_def::DefWithBodyId::VariantId(id) => {
|
||||||
|
let data = db.enum_data(id.parent);
|
||||||
|
w!(this, "enum {} = ", data.name);
|
||||||
|
}
|
||||||
|
});
|
||||||
ctx.result
|
ctx.result
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,21 +82,6 @@ struct MirPrettyCtx<'a> {
|
|||||||
local_to_binding: ArenaMap<LocalId, BindingId>,
|
local_to_binding: ArenaMap<LocalId, BindingId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! w {
|
|
||||||
($dst:expr, $($arg:tt)*) => {
|
|
||||||
{ let _ = write!($dst, $($arg)*); }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! wln {
|
|
||||||
($dst:expr) => {
|
|
||||||
{ let _ = writeln!($dst); }
|
|
||||||
};
|
|
||||||
($dst:expr, $($arg:tt)*) => {
|
|
||||||
{ let _ = writeln!($dst, $($arg)*); }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Write for MirPrettyCtx<'_> {
|
impl Write for MirPrettyCtx<'_> {
|
||||||
fn write_str(&mut self, s: &str) -> std::fmt::Result {
|
fn write_str(&mut self, s: &str) -> std::fmt::Result {
|
||||||
let mut it = s.split('\n'); // note: `.lines()` is wrong here
|
let mut it = s.split('\n'); // note: `.lines()` is wrong here
|
||||||
@ -91,36 +109,40 @@ impl Display for LocalName {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> MirPrettyCtx<'a> {
|
impl<'a> MirPrettyCtx<'a> {
|
||||||
fn for_body(&mut self, name: impl Debug) {
|
fn for_body(&mut self, name: impl FnOnce(&mut MirPrettyCtx<'_>)) {
|
||||||
wln!(self, "// {:?}", name);
|
name(self);
|
||||||
self.with_block(|this| {
|
self.with_block(|this| {
|
||||||
this.locals();
|
this.locals();
|
||||||
wln!(this);
|
wln!(this);
|
||||||
this.blocks();
|
this.blocks();
|
||||||
});
|
});
|
||||||
for &closure in &self.body.closures {
|
for &closure in &self.body.closures {
|
||||||
let body = match self.db.mir_body_for_closure(closure) {
|
self.for_closure(closure);
|
||||||
Ok(x) => x,
|
|
||||||
Err(e) => {
|
|
||||||
wln!(self, "// error in {closure:?}: {e:?}");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let result = mem::take(&mut self.result);
|
|
||||||
let indent = mem::take(&mut self.indent);
|
|
||||||
let mut ctx = MirPrettyCtx {
|
|
||||||
body: &body,
|
|
||||||
local_to_binding: body.binding_locals.iter().map(|(x, y)| (*y, x)).collect(),
|
|
||||||
result,
|
|
||||||
indent,
|
|
||||||
..*self
|
|
||||||
};
|
|
||||||
ctx.for_body(closure);
|
|
||||||
self.result = ctx.result;
|
|
||||||
self.indent = ctx.indent;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn for_closure(&mut self, closure: ClosureId) {
|
||||||
|
let body = match self.db.mir_body_for_closure(closure) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => {
|
||||||
|
wln!(self, "// error in {closure:?}: {e:?}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let result = mem::take(&mut self.result);
|
||||||
|
let indent = mem::take(&mut self.indent);
|
||||||
|
let mut ctx = MirPrettyCtx {
|
||||||
|
body: &body,
|
||||||
|
local_to_binding: body.binding_locals.iter().map(|(x, y)| (*y, x)).collect(),
|
||||||
|
result,
|
||||||
|
indent,
|
||||||
|
..*self
|
||||||
|
};
|
||||||
|
ctx.for_body(|this| wln!(this, "// Closure: {:?}", closure));
|
||||||
|
self.result = ctx.result;
|
||||||
|
self.indent = ctx.indent;
|
||||||
|
}
|
||||||
|
|
||||||
fn with_block(&mut self, f: impl FnOnce(&mut MirPrettyCtx<'_>)) {
|
fn with_block(&mut self, f: impl FnOnce(&mut MirPrettyCtx<'_>)) {
|
||||||
self.indent += " ";
|
self.indent += " ";
|
||||||
wln!(self, "{{");
|
wln!(self, "{{");
|
||||||
@ -155,7 +177,7 @@ impl<'a> MirPrettyCtx<'a> {
|
|||||||
|
|
||||||
fn locals(&mut self) {
|
fn locals(&mut self) {
|
||||||
for (id, local) in self.body.locals.iter() {
|
for (id, local) in self.body.locals.iter() {
|
||||||
wln!(self, "let {}: {};", self.local_name(id), local.ty.display(self.db));
|
wln!(self, "let {}: {};", self.local_name(id), self.hir_display(&local.ty));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,11 +220,11 @@ impl<'a> MirPrettyCtx<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
match &block.terminator {
|
match &block.terminator {
|
||||||
Some(terminator) => match terminator {
|
Some(terminator) => match &terminator.kind {
|
||||||
Terminator::Goto { target } => {
|
TerminatorKind::Goto { target } => {
|
||||||
wln!(this, "goto 'bb{};", u32::from(target.into_raw()))
|
wln!(this, "goto 'bb{};", u32::from(target.into_raw()))
|
||||||
}
|
}
|
||||||
Terminator::SwitchInt { discr, targets } => {
|
TerminatorKind::SwitchInt { discr, targets } => {
|
||||||
w!(this, "switch ");
|
w!(this, "switch ");
|
||||||
this.operand(discr);
|
this.operand(discr);
|
||||||
w!(this, " ");
|
w!(this, " ");
|
||||||
@ -213,7 +235,7 @@ impl<'a> MirPrettyCtx<'a> {
|
|||||||
wln!(this, "_ => {},", this.basic_block_id(targets.otherwise()));
|
wln!(this, "_ => {},", this.basic_block_id(targets.otherwise()));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Terminator::Call { func, args, destination, target, .. } => {
|
TerminatorKind::Call { func, args, destination, target, .. } => {
|
||||||
w!(this, "Call ");
|
w!(this, "Call ");
|
||||||
this.with_block(|this| {
|
this.with_block(|this| {
|
||||||
w!(this, "func: ");
|
w!(this, "func: ");
|
||||||
@ -295,7 +317,8 @@ impl<'a> MirPrettyCtx<'a> {
|
|||||||
// equally. Feel free to change it.
|
// equally. Feel free to change it.
|
||||||
self.place(p);
|
self.place(p);
|
||||||
}
|
}
|
||||||
Operand::Constant(c) => w!(self, "Const({})", c.display(self.db)),
|
Operand::Constant(c) => w!(self, "Const({})", self.hir_display(c)),
|
||||||
|
Operand::Static(s) => w!(self, "Static({:?})", s),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,7 +372,7 @@ impl<'a> MirPrettyCtx<'a> {
|
|||||||
Rvalue::Cast(ck, op, ty) => {
|
Rvalue::Cast(ck, op, ty) => {
|
||||||
w!(self, "Cast({ck:?}, ");
|
w!(self, "Cast({ck:?}, ");
|
||||||
self.operand(op);
|
self.operand(op);
|
||||||
w!(self, ", {})", ty.display(self.db));
|
w!(self, ", {})", self.hir_display(ty));
|
||||||
}
|
}
|
||||||
Rvalue::CheckedBinaryOp(b, o1, o2) => {
|
Rvalue::CheckedBinaryOp(b, o1, o2) => {
|
||||||
self.operand(o1);
|
self.operand(o1);
|
||||||
@ -369,6 +392,7 @@ impl<'a> MirPrettyCtx<'a> {
|
|||||||
self.place(p);
|
self.place(p);
|
||||||
w!(self, ")");
|
w!(self, ")");
|
||||||
}
|
}
|
||||||
|
Rvalue::ShallowInitBoxWithAlloc(_) => w!(self, "ShallowInitBoxWithAlloc"),
|
||||||
Rvalue::ShallowInitBox(op, _) => {
|
Rvalue::ShallowInitBox(op, _) => {
|
||||||
w!(self, "ShallowInitBox(");
|
w!(self, "ShallowInitBox(");
|
||||||
self.operand(op);
|
self.operand(op);
|
||||||
@ -392,4 +416,8 @@ impl<'a> MirPrettyCtx<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn hir_display<T: HirDisplay>(&self, ty: &'a T) -> impl Display + 'a {
|
||||||
|
ty.display(self.db).with_closure_style(ClosureStyle::ClosureWithId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,10 +23,11 @@ use hir_expand::name::Name;
|
|||||||
use intern::Interned;
|
use intern::Interned;
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
|
use stdx::never;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase, ChalkTraitId, Interner, Substitution, TraitRef, TraitRefExt, Ty, TyExt,
|
db::HirDatabase, ChalkTraitId, GenericArg, Interner, Substitution, TraitRef, TraitRefExt, Ty,
|
||||||
WhereClause,
|
TyExt, WhereClause,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn fn_traits(
|
pub(crate) fn fn_traits(
|
||||||
@ -176,6 +177,37 @@ pub(crate) fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics {
|
|||||||
Generics { def, params: db.generic_params(def), parent_generics }
|
Generics { def, params: db.generic_params(def), parent_generics }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// It is a bit different from the rustc equivalent. Currently it stores:
|
||||||
|
/// - 0: the function signature, encoded as a function pointer type
|
||||||
|
/// - 1..n: generics of the parent
|
||||||
|
///
|
||||||
|
/// and it doesn't store the closure types and fields.
|
||||||
|
///
|
||||||
|
/// Codes should not assume this ordering, and should always use methods available
|
||||||
|
/// on this struct for retriving, and `TyBuilder::substs_for_closure` for creating.
|
||||||
|
pub(crate) struct ClosureSubst<'a>(pub(crate) &'a Substitution);
|
||||||
|
|
||||||
|
impl<'a> ClosureSubst<'a> {
|
||||||
|
pub(crate) fn parent_subst(&self) -> &'a [GenericArg] {
|
||||||
|
match self.0.as_slice(Interner) {
|
||||||
|
[_, x @ ..] => x,
|
||||||
|
_ => {
|
||||||
|
never!("Closure missing parameter");
|
||||||
|
&[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn sig_ty(&self) -> &'a Ty {
|
||||||
|
match self.0.as_slice(Interner) {
|
||||||
|
[x, ..] => x.assert_ty_ref(Interner),
|
||||||
|
_ => {
|
||||||
|
unreachable!("Closure missing sig_ty parameter");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Generics {
|
pub(crate) struct Generics {
|
||||||
def: GenericDefId,
|
def: GenericDefId,
|
||||||
|
@ -1856,11 +1856,21 @@ impl Function {
|
|||||||
def_map.fn_as_proc_macro(self.id).map(|id| Macro { id: id.into() })
|
def_map.fn_as_proc_macro(self.id).map(|id| Macro { id: id.into() })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval(self, db: &dyn HirDatabase) -> Result<(), MirEvalError> {
|
pub fn eval(
|
||||||
|
self,
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
span_formatter: impl Fn(FileId, TextRange) -> String,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let converter = |e: MirEvalError| {
|
||||||
|
let mut r = String::new();
|
||||||
|
_ = e.pretty_print(&mut r, db, &span_formatter);
|
||||||
|
r
|
||||||
|
};
|
||||||
let body = db
|
let body = db
|
||||||
.mir_body(self.id.into())
|
.mir_body(self.id.into())
|
||||||
.map_err(|e| MirEvalError::MirLowerError(self.id.into(), e))?;
|
.map_err(|e| MirEvalError::MirLowerError(self.id.into(), e))
|
||||||
interpret_mir(db, &body, Substitution::empty(Interner), false)?;
|
.map_err(converter)?;
|
||||||
|
interpret_mir(db, &body, Substitution::empty(Interner), false).map_err(converter)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2006,7 +2016,7 @@ impl Const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_eval(self, db: &dyn HirDatabase) -> Result<String, ConstEvalError> {
|
pub fn render_eval(self, db: &dyn HirDatabase) -> Result<String, ConstEvalError> {
|
||||||
let c = db.const_eval(self.id, Substitution::empty(Interner))?;
|
let c = db.const_eval(self.id.into(), Substitution::empty(Interner))?;
|
||||||
let r = format!("{}", HexifiedConst(c).display(db));
|
let r = format!("{}", HexifiedConst(c).display(db));
|
||||||
// We want to see things like `<utf8-error>` and `<layout-error>` as they are probably bug in our
|
// We want to see things like `<utf8-error>` and `<layout-error>` as they are probably bug in our
|
||||||
// implementation, but there is no need to show things like `<enum-not-supported>` or `<ref-not-supported>` to
|
// implementation, but there is no need to show things like `<enum-not-supported>` or `<ref-not-supported>` to
|
||||||
|
@ -30,7 +30,6 @@ pub struct HoverConfig {
|
|||||||
pub documentation: bool,
|
pub documentation: bool,
|
||||||
pub keywords: bool,
|
pub keywords: bool,
|
||||||
pub format: HoverDocFormat,
|
pub format: HoverDocFormat,
|
||||||
pub interpret_tests: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
@ -3,8 +3,7 @@ use std::fmt::Display;
|
|||||||
|
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir::{
|
use hir::{
|
||||||
db::DefDatabase, Adt, AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay,
|
Adt, AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo,
|
||||||
MirEvalError, Semantics, TypeInfo,
|
|
||||||
};
|
};
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
base_db::SourceDatabase,
|
base_db::SourceDatabase,
|
||||||
@ -435,19 +434,7 @@ pub(super) fn definition(
|
|||||||
))
|
))
|
||||||
}),
|
}),
|
||||||
Definition::Module(it) => label_and_docs(db, it),
|
Definition::Module(it) => label_and_docs(db, it),
|
||||||
Definition::Function(it) => label_and_layout_info_and_docs(db, it, |_| {
|
Definition::Function(it) => label_and_docs(db, it),
|
||||||
if !config.interpret_tests {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
match it.eval(db) {
|
|
||||||
Ok(()) => Some("pass".into()),
|
|
||||||
Err(MirEvalError::MirLowerError(f, e)) => {
|
|
||||||
let name = &db.function_data(f).name;
|
|
||||||
Some(format!("error: fail to lower {name} due {e:?}"))
|
|
||||||
}
|
|
||||||
Err(e) => Some(format!("error: {e:?}")),
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
Definition::Adt(it) => label_and_layout_info_and_docs(db, it, |&it| {
|
Definition::Adt(it) => label_and_layout_info_and_docs(db, it, |&it| {
|
||||||
let layout = it.layout(db).ok()?;
|
let layout = it.layout(db).ok()?;
|
||||||
Some(format!("size = {}, align = {}", layout.size.bytes(), layout.align.abi.bytes()))
|
Some(format!("size = {}, align = {}", layout.size.bytes(), layout.align.abi.bytes()))
|
||||||
|
@ -9,7 +9,6 @@ const HOVER_BASE_CONFIG: HoverConfig = HoverConfig {
|
|||||||
documentation: true,
|
documentation: true,
|
||||||
format: HoverDocFormat::Markdown,
|
format: HoverDocFormat::Markdown,
|
||||||
keywords: true,
|
keywords: true,
|
||||||
interpret_tests: false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fn check_hover_no_result(ra_fixture: &str) {
|
fn check_hover_no_result(ra_fixture: &str) {
|
||||||
|
@ -3,7 +3,10 @@
|
|||||||
//! let _: u32 = /* <never-to-any> */ loop {};
|
//! let _: u32 = /* <never-to-any> */ loop {};
|
||||||
//! let _: &u32 = /* &* */ &mut 0;
|
//! let _: &u32 = /* &* */ &mut 0;
|
||||||
//! ```
|
//! ```
|
||||||
use hir::{Adjust, Adjustment, AutoBorrow, HirDisplay, Mutability, PointerCast, Safety, Semantics};
|
use hir::{
|
||||||
|
Adjust, Adjustment, AutoBorrow, HirDisplay, Mutability, OverloadedDeref, PointerCast, Safety,
|
||||||
|
Semantics,
|
||||||
|
};
|
||||||
use ide_db::RootDatabase;
|
use ide_db::RootDatabase;
|
||||||
|
|
||||||
use stdx::never;
|
use stdx::never;
|
||||||
@ -88,7 +91,13 @@ pub(super) fn hints(
|
|||||||
Adjust::NeverToAny if config.adjustment_hints == AdjustmentHints::Always => {
|
Adjust::NeverToAny if config.adjustment_hints == AdjustmentHints::Always => {
|
||||||
("<never-to-any>", "never to any")
|
("<never-to-any>", "never to any")
|
||||||
}
|
}
|
||||||
Adjust::Deref(_) => ("*", "dereference"),
|
Adjust::Deref(None) => ("*", "dereference"),
|
||||||
|
Adjust::Deref(Some(OverloadedDeref(Mutability::Shared))) => {
|
||||||
|
("*", "`Deref` dereference")
|
||||||
|
}
|
||||||
|
Adjust::Deref(Some(OverloadedDeref(Mutability::Mut))) => {
|
||||||
|
("*", "`DerefMut` dereference")
|
||||||
|
}
|
||||||
Adjust::Borrow(AutoBorrow::Ref(Mutability::Shared)) => ("&", "borrow"),
|
Adjust::Borrow(AutoBorrow::Ref(Mutability::Shared)) => ("&", "borrow"),
|
||||||
Adjust::Borrow(AutoBorrow::Ref(Mutability::Mut)) => ("&mut ", "unique borrow"),
|
Adjust::Borrow(AutoBorrow::Ref(Mutability::Mut)) => ("&mut ", "unique borrow"),
|
||||||
Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Shared)) => {
|
Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Shared)) => {
|
||||||
|
@ -444,7 +444,7 @@ fn main() {
|
|||||||
file_id: FileId(
|
file_id: FileId(
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
range: 5768..5776,
|
range: 5769..5777,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
tooltip: "",
|
tooltip: "",
|
||||||
@ -457,7 +457,7 @@ fn main() {
|
|||||||
file_id: FileId(
|
file_id: FileId(
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
range: 5800..5804,
|
range: 5801..5805,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
tooltip: "",
|
tooltip: "",
|
||||||
@ -478,7 +478,7 @@ fn main() {
|
|||||||
file_id: FileId(
|
file_id: FileId(
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
range: 5768..5776,
|
range: 5769..5777,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
tooltip: "",
|
tooltip: "",
|
||||||
@ -491,7 +491,7 @@ fn main() {
|
|||||||
file_id: FileId(
|
file_id: FileId(
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
range: 5800..5804,
|
range: 5801..5805,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
tooltip: "",
|
tooltip: "",
|
||||||
@ -512,7 +512,7 @@ fn main() {
|
|||||||
file_id: FileId(
|
file_id: FileId(
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
range: 5768..5776,
|
range: 5769..5777,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
tooltip: "",
|
tooltip: "",
|
||||||
@ -525,7 +525,7 @@ fn main() {
|
|||||||
file_id: FileId(
|
file_id: FileId(
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
range: 5800..5804,
|
range: 5801..5805,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
tooltip: "",
|
tooltip: "",
|
||||||
|
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_crate_graph;
|
||||||
mod view_hir;
|
mod view_hir;
|
||||||
mod view_mir;
|
mod view_mir;
|
||||||
|
mod interpret_function;
|
||||||
mod view_item_tree;
|
mod view_item_tree;
|
||||||
mod shuffle_crate_graph;
|
mod shuffle_crate_graph;
|
||||||
|
|
||||||
@ -317,6 +318,10 @@ impl Analysis {
|
|||||||
self.with_db(|db| view_mir::view_mir(db, position))
|
self.with_db(|db| view_mir::view_mir(db, position))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn interpret_function(&self, position: FilePosition) -> Cancellable<String> {
|
||||||
|
self.with_db(|db| interpret_function::interpret_function(db, position))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn view_item_tree(&self, file_id: FileId) -> Cancellable<String> {
|
pub fn view_item_tree(&self, file_id: FileId) -> Cancellable<String> {
|
||||||
self.with_db(|db| view_item_tree::view_item_tree(db, file_id))
|
self.with_db(|db| view_item_tree::view_item_tree(db, file_id))
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,6 @@ impl StaticIndex<'_> {
|
|||||||
documentation: true,
|
documentation: true,
|
||||||
keywords: true,
|
keywords: true,
|
||||||
format: crate::HoverDocFormat::Markdown,
|
format: crate::HoverDocFormat::Markdown,
|
||||||
interpret_tests: false,
|
|
||||||
};
|
};
|
||||||
let tokens = tokens.filter(|token| {
|
let tokens = tokens.filter(|token| {
|
||||||
matches!(
|
matches!(
|
||||||
|
@ -574,6 +574,7 @@ pub struct LensConfig {
|
|||||||
// runnables
|
// runnables
|
||||||
pub run: bool,
|
pub run: bool,
|
||||||
pub debug: bool,
|
pub debug: bool,
|
||||||
|
pub interpret: bool,
|
||||||
|
|
||||||
// implementations
|
// implementations
|
||||||
pub implementations: bool,
|
pub implementations: bool,
|
||||||
@ -1423,6 +1424,9 @@ impl Config {
|
|||||||
LensConfig {
|
LensConfig {
|
||||||
run: self.data.lens_enable && self.data.lens_run_enable,
|
run: self.data.lens_enable && self.data.lens_run_enable,
|
||||||
debug: self.data.lens_enable && self.data.lens_debug_enable,
|
debug: self.data.lens_enable && self.data.lens_debug_enable,
|
||||||
|
interpret: self.data.lens_enable
|
||||||
|
&& self.data.lens_run_enable
|
||||||
|
&& self.data.interpret_tests,
|
||||||
implementations: self.data.lens_enable && self.data.lens_implementations_enable,
|
implementations: self.data.lens_enable && self.data.lens_implementations_enable,
|
||||||
method_refs: self.data.lens_enable && self.data.lens_references_method_enable,
|
method_refs: self.data.lens_enable && self.data.lens_references_method_enable,
|
||||||
refs_adt: self.data.lens_enable && self.data.lens_references_adt_enable,
|
refs_adt: self.data.lens_enable && self.data.lens_references_adt_enable,
|
||||||
@ -1481,7 +1485,6 @@ impl Config {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
keywords: self.data.hover_documentation_keywords_enable,
|
keywords: self.data.hover_documentation_keywords_enable,
|
||||||
interpret_tests: self.data.interpret_tests,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,6 +163,16 @@ pub(crate) fn handle_view_mir(
|
|||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn handle_interpret_function(
|
||||||
|
snap: GlobalStateSnapshot,
|
||||||
|
params: lsp_types::TextDocumentPositionParams,
|
||||||
|
) -> Result<String> {
|
||||||
|
let _p = profile::span("handle_interpret_function");
|
||||||
|
let position = from_proto::file_position(&snap, params)?;
|
||||||
|
let res = snap.analysis.interpret_function(position)?;
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn handle_view_file_text(
|
pub(crate) fn handle_view_file_text(
|
||||||
snap: GlobalStateSnapshot,
|
snap: GlobalStateSnapshot,
|
||||||
params: lsp_types::TextDocumentIdentifier,
|
params: lsp_types::TextDocumentIdentifier,
|
||||||
|
@ -90,6 +90,14 @@ impl Request for ViewMir {
|
|||||||
const METHOD: &'static str = "rust-analyzer/viewMir";
|
const METHOD: &'static str = "rust-analyzer/viewMir";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum InterpretFunction {}
|
||||||
|
|
||||||
|
impl Request for InterpretFunction {
|
||||||
|
type Params = lsp_types::TextDocumentPositionParams;
|
||||||
|
type Result = String;
|
||||||
|
const METHOD: &'static str = "rust-analyzer/interpretFunction";
|
||||||
|
}
|
||||||
|
|
||||||
pub enum ViewFileText {}
|
pub enum ViewFileText {}
|
||||||
|
|
||||||
impl Request for ViewFileText {
|
impl Request for ViewFileText {
|
||||||
|
@ -665,6 +665,7 @@ impl GlobalState {
|
|||||||
.on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree)
|
.on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree)
|
||||||
.on::<lsp_ext::ViewHir>(handlers::handle_view_hir)
|
.on::<lsp_ext::ViewHir>(handlers::handle_view_hir)
|
||||||
.on::<lsp_ext::ViewMir>(handlers::handle_view_mir)
|
.on::<lsp_ext::ViewMir>(handlers::handle_view_mir)
|
||||||
|
.on::<lsp_ext::InterpretFunction>(handlers::handle_interpret_function)
|
||||||
.on::<lsp_ext::ViewFileText>(handlers::handle_view_file_text)
|
.on::<lsp_ext::ViewFileText>(handlers::handle_view_file_text)
|
||||||
.on::<lsp_ext::ViewCrateGraph>(handlers::handle_view_crate_graph)
|
.on::<lsp_ext::ViewCrateGraph>(handlers::handle_view_crate_graph)
|
||||||
.on::<lsp_ext::ViewItemTree>(handlers::handle_view_item_tree)
|
.on::<lsp_ext::ViewItemTree>(handlers::handle_view_item_tree)
|
||||||
|
@ -1215,6 +1215,14 @@ pub(crate) fn code_lens(
|
|||||||
data: None,
|
data: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if lens_config.interpret {
|
||||||
|
let command = command::interpret_single(&r);
|
||||||
|
acc.push(lsp_types::CodeLens {
|
||||||
|
range: annotation_range,
|
||||||
|
command: Some(command),
|
||||||
|
data: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
AnnotationKind::HasImpls { pos: file_range, data } => {
|
AnnotationKind::HasImpls { pos: file_range, data } => {
|
||||||
if !client_commands_config.show_reference {
|
if !client_commands_config.show_reference {
|
||||||
@ -1359,6 +1367,15 @@ pub(crate) mod command {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn interpret_single(_runnable: &lsp_ext::Runnable) -> lsp_types::Command {
|
||||||
|
lsp_types::Command {
|
||||||
|
title: "Interpret".into(),
|
||||||
|
command: "rust-analyzer.interpretFunction".into(),
|
||||||
|
// FIXME: use the `_runnable` here.
|
||||||
|
arguments: Some(vec![]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn goto_location(
|
pub(crate) fn goto_location(
|
||||||
snap: &GlobalStateSnapshot,
|
snap: &GlobalStateSnapshot,
|
||||||
nav: &NavigationTarget,
|
nav: &NavigationTarget,
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
//! add:
|
//! add:
|
||||||
//! as_ref: sized
|
//! as_ref: sized
|
||||||
//! bool_impl: option, fn
|
//! bool_impl: option, fn
|
||||||
|
//! cell: copy, drop
|
||||||
//! clone: sized
|
//! clone: sized
|
||||||
//! coerce_unsized: unsize
|
//! coerce_unsized: unsize
|
||||||
//! copy: clone
|
//! copy: clone
|
||||||
@ -139,6 +140,52 @@ pub mod hash {
|
|||||||
}
|
}
|
||||||
// endregion:hash
|
// endregion:hash
|
||||||
|
|
||||||
|
// region:cell
|
||||||
|
pub mod cell {
|
||||||
|
use crate::mem;
|
||||||
|
|
||||||
|
#[lang = "unsafe_cell"]
|
||||||
|
pub struct UnsafeCell<T: ?Sized> {
|
||||||
|
value: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> UnsafeCell<T> {
|
||||||
|
pub const fn new(value: T) -> UnsafeCell<T> {
|
||||||
|
UnsafeCell { value }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn get(&self) -> *mut T {
|
||||||
|
self as *const UnsafeCell<T> as *const T as *mut T
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cell<T: ?Sized> {
|
||||||
|
value: UnsafeCell<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Cell<T> {
|
||||||
|
pub const fn new(value: T) -> Cell<T> {
|
||||||
|
Cell { value: UnsafeCell::new(value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(&self, val: T) {
|
||||||
|
let old = self.replace(val);
|
||||||
|
mem::drop(old);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn replace(&self, val: T) -> T {
|
||||||
|
mem::replace(unsafe { &mut *self.value.get() }, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Copy> Cell<T> {
|
||||||
|
pub fn get(&self) -> T {
|
||||||
|
unsafe { *self.value.get() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// endregion:cell
|
||||||
|
|
||||||
// region:clone
|
// region:clone
|
||||||
pub mod clone {
|
pub mod clone {
|
||||||
#[lang = "clone"]
|
#[lang = "clone"]
|
||||||
@ -220,6 +267,13 @@ pub mod mem {
|
|||||||
// endregion:manually_drop
|
// endregion:manually_drop
|
||||||
|
|
||||||
pub fn drop<T>(_x: T) {}
|
pub fn drop<T>(_x: T) {}
|
||||||
|
pub const fn replace<T>(dest: &mut T, src: T) -> T {
|
||||||
|
unsafe {
|
||||||
|
let result = *dest;
|
||||||
|
*dest = src;
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// endregion:drop
|
// endregion:drop
|
||||||
|
|
||||||
@ -710,6 +764,14 @@ pub mod option {
|
|||||||
None => default,
|
None => default,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// region:result
|
||||||
|
pub const fn ok_or<E>(self, err: E) -> Result<T, E> {
|
||||||
|
match self {
|
||||||
|
Some(v) => Ok(v),
|
||||||
|
None => Err(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// endregion:result
|
||||||
// region:fn
|
// region:fn
|
||||||
pub fn and_then<U, F>(self, f: F) -> Option<U>
|
pub fn and_then<U, F>(self, f: F) -> Option<U>
|
||||||
where
|
where
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<!---
|
<!---
|
||||||
lsp_ext.rs hash: be2f663a78beb7bd
|
lsp_ext.rs hash: 37ac44a0f507e05a
|
||||||
|
|
||||||
If you need to change the above hash to make the test pass, please check if you
|
If you need to change the above hash to make the test pass, please check if you
|
||||||
need to adjust this doc as well and ping this issue:
|
need to adjust this doc as well and ping this issue:
|
||||||
@ -548,6 +548,18 @@ For debugging or when working on rust-analyzer itself.
|
|||||||
Returns a textual representation of the MIR of the function containing the cursor.
|
Returns a textual representation of the MIR of the function containing the cursor.
|
||||||
For debugging or when working on rust-analyzer itself.
|
For debugging or when working on rust-analyzer itself.
|
||||||
|
|
||||||
|
## Interpret Function
|
||||||
|
|
||||||
|
**Method:** `rust-analyzer/interpretFunction`
|
||||||
|
|
||||||
|
**Request:** `TextDocumentPositionParams`
|
||||||
|
|
||||||
|
**Response:** `string`
|
||||||
|
|
||||||
|
Tries to evaluate the function using internal rust analyzer knowledge, without compiling
|
||||||
|
the code. Currently evaluates the function under cursor, but will give a runnable in
|
||||||
|
future. Highly experimental.
|
||||||
|
|
||||||
## View File Text
|
## View File Text
|
||||||
|
|
||||||
**Method:** `rust-analyzer/viewFileText`
|
**Method:** `rust-analyzer/viewFileText`
|
||||||
|
@ -120,6 +120,11 @@
|
|||||||
"title": "View Mir",
|
"title": "View Mir",
|
||||||
"category": "rust-analyzer (debug command)"
|
"category": "rust-analyzer (debug command)"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"command": "rust-analyzer.interpretFunction",
|
||||||
|
"title": "Interpret Function",
|
||||||
|
"category": "rust-analyzer (debug command)"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"command": "rust-analyzer.viewFileText",
|
"command": "rust-analyzer.viewFileText",
|
||||||
"title": "View File Text (as seen by the server)",
|
"title": "View File Text (as seen by the server)",
|
||||||
|
@ -422,8 +422,20 @@ export function syntaxTree(ctx: CtxInit): Cmd {
|
|||||||
function viewHirOrMir(ctx: CtxInit, xir: "hir" | "mir"): Cmd {
|
function viewHirOrMir(ctx: CtxInit, xir: "hir" | "mir"): Cmd {
|
||||||
const viewXir = xir === "hir" ? "viewHir" : "viewMir";
|
const viewXir = xir === "hir" ? "viewHir" : "viewMir";
|
||||||
const requestType = xir === "hir" ? ra.viewHir : ra.viewMir;
|
const requestType = xir === "hir" ? ra.viewHir : ra.viewMir;
|
||||||
|
const uri = `rust-analyzer-${xir}://${viewXir}/${xir}.rs`;
|
||||||
|
const scheme = `rust-analyzer-${xir}`;
|
||||||
|
return viewFileUsingTextDocumentContentProvider(ctx, requestType, uri, scheme, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function viewFileUsingTextDocumentContentProvider(
|
||||||
|
ctx: CtxInit,
|
||||||
|
requestType: lc.RequestType<lc.TextDocumentPositionParams, string, void>,
|
||||||
|
uri: string,
|
||||||
|
scheme: string,
|
||||||
|
shouldUpdate: boolean
|
||||||
|
): Cmd {
|
||||||
const tdcp = new (class implements vscode.TextDocumentContentProvider {
|
const tdcp = new (class implements vscode.TextDocumentContentProvider {
|
||||||
readonly uri = vscode.Uri.parse(`rust-analyzer-${xir}://${viewXir}/${xir}.rs`);
|
readonly uri = vscode.Uri.parse(uri);
|
||||||
readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
|
readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
|
||||||
constructor() {
|
constructor() {
|
||||||
vscode.workspace.onDidChangeTextDocument(
|
vscode.workspace.onDidChangeTextDocument(
|
||||||
@ -439,14 +451,14 @@ function viewHirOrMir(ctx: CtxInit, xir: "hir" | "mir"): Cmd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
|
private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
|
||||||
if (isRustDocument(event.document)) {
|
if (isRustDocument(event.document) && shouldUpdate) {
|
||||||
// We need to order this after language server updates, but there's no API for that.
|
// We need to order this after language server updates, but there's no API for that.
|
||||||
// Hence, good old sleep().
|
// Hence, good old sleep().
|
||||||
void sleep(10).then(() => this.eventEmitter.fire(this.uri));
|
void sleep(10).then(() => this.eventEmitter.fire(this.uri));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) {
|
private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) {
|
||||||
if (editor && isRustEditor(editor)) {
|
if (editor && isRustEditor(editor) && shouldUpdate) {
|
||||||
this.eventEmitter.fire(this.uri);
|
this.eventEmitter.fire(this.uri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -473,9 +485,7 @@ function viewHirOrMir(ctx: CtxInit, xir: "hir" | "mir"): Cmd {
|
|||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
ctx.pushExtCleanup(
|
ctx.pushExtCleanup(vscode.workspace.registerTextDocumentContentProvider(scheme, tdcp));
|
||||||
vscode.workspace.registerTextDocumentContentProvider(`rust-analyzer-${xir}`, tdcp)
|
|
||||||
);
|
|
||||||
|
|
||||||
return async () => {
|
return async () => {
|
||||||
const document = await vscode.workspace.openTextDocument(tdcp.uri);
|
const document = await vscode.workspace.openTextDocument(tdcp.uri);
|
||||||
@ -501,6 +511,20 @@ export function viewMir(ctx: CtxInit): Cmd {
|
|||||||
return viewHirOrMir(ctx, "mir");
|
return viewHirOrMir(ctx, "mir");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Opens the virtual file that will show the MIR of the function containing the cursor position
|
||||||
|
//
|
||||||
|
// The contents of the file come from the `TextDocumentContentProvider`
|
||||||
|
export function interpretFunction(ctx: CtxInit): Cmd {
|
||||||
|
const uri = `rust-analyzer-interpret-function://interpretFunction/result.log`;
|
||||||
|
return viewFileUsingTextDocumentContentProvider(
|
||||||
|
ctx,
|
||||||
|
ra.interpretFunction,
|
||||||
|
uri,
|
||||||
|
`rust-analyzer-interpret-function`,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function viewFileText(ctx: CtxInit): Cmd {
|
export function viewFileText(ctx: CtxInit): Cmd {
|
||||||
const tdcp = new (class implements vscode.TextDocumentContentProvider {
|
const tdcp = new (class implements vscode.TextDocumentContentProvider {
|
||||||
readonly uri = vscode.Uri.parse("rust-analyzer-file-text://viewFileText/file.rs");
|
readonly uri = vscode.Uri.parse("rust-analyzer-file-text://viewFileText/file.rs");
|
||||||
|
@ -64,6 +64,9 @@ export const viewHir = new lc.RequestType<lc.TextDocumentPositionParams, string,
|
|||||||
export const viewMir = new lc.RequestType<lc.TextDocumentPositionParams, string, void>(
|
export const viewMir = new lc.RequestType<lc.TextDocumentPositionParams, string, void>(
|
||||||
"rust-analyzer/viewMir"
|
"rust-analyzer/viewMir"
|
||||||
);
|
);
|
||||||
|
export const interpretFunction = new lc.RequestType<lc.TextDocumentPositionParams, string, void>(
|
||||||
|
"rust-analyzer/interpretFunction"
|
||||||
|
);
|
||||||
export const viewItemTree = new lc.RequestType<ViewItemTreeParams, string, void>(
|
export const viewItemTree = new lc.RequestType<ViewItemTreeParams, string, void>(
|
||||||
"rust-analyzer/viewItemTree"
|
"rust-analyzer/viewItemTree"
|
||||||
);
|
);
|
||||||
|
@ -159,6 +159,7 @@ function createCommands(): Record<string, CommandFactory> {
|
|||||||
syntaxTree: { enabled: commands.syntaxTree },
|
syntaxTree: { enabled: commands.syntaxTree },
|
||||||
viewHir: { enabled: commands.viewHir },
|
viewHir: { enabled: commands.viewHir },
|
||||||
viewMir: { enabled: commands.viewMir },
|
viewMir: { enabled: commands.viewMir },
|
||||||
|
interpretFunction: { enabled: commands.interpretFunction },
|
||||||
viewFileText: { enabled: commands.viewFileText },
|
viewFileText: { enabled: commands.viewFileText },
|
||||||
viewItemTree: { enabled: commands.viewItemTree },
|
viewItemTree: { enabled: commands.viewItemTree },
|
||||||
viewCrateGraph: { enabled: commands.viewCrateGraph },
|
viewCrateGraph: { enabled: commands.viewCrateGraph },
|
||||||
|
Loading…
Reference in New Issue
Block a user