Partially support "overloaded deref" MIR lowering
This commit is contained in:
parent
bf0f99f15d
commit
af90ec8096
@ -422,13 +422,6 @@ pub fn ident_patterns_for<'slf>(&'slf self, pat: &'slf PatId) -> &'slf [PatId] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn walk_child_bindings(&self, pat: PatId, f: &mut impl FnMut(BindingId)) {
|
|
||||||
if let Pat::Bind { id, .. } = self[pat] {
|
|
||||||
f(id)
|
|
||||||
}
|
|
||||||
self[pat].walk_child_pats(|p| self.walk_child_bindings(p, f));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pretty_print(&self, db: &dyn DefDatabase, owner: DefWithBodyId) -> String {
|
pub fn pretty_print(&self, db: &dyn DefDatabase, owner: DefWithBodyId) -> String {
|
||||||
pretty::print_body_hir(db, self, owner)
|
pretty::print_body_hir(db, self, owner)
|
||||||
}
|
}
|
||||||
|
@ -148,6 +148,60 @@ fn reference_autoderef() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn overloaded_deref() {
|
||||||
|
// FIXME: We should support this.
|
||||||
|
check_fail(
|
||||||
|
r#"
|
||||||
|
//- minicore: deref_mut
|
||||||
|
struct Foo;
|
||||||
|
|
||||||
|
impl core::ops::Deref for Foo {
|
||||||
|
type Target = i32;
|
||||||
|
fn deref(&self) -> &i32 {
|
||||||
|
&5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const GOAL: i32 = {
|
||||||
|
let x = Foo;
|
||||||
|
let y = &*x;
|
||||||
|
*y + *x
|
||||||
|
};
|
||||||
|
"#,
|
||||||
|
ConstEvalError::MirLowerError(MirLowerError::NotSupported(
|
||||||
|
"explicit overloaded deref".into(),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn overloaded_deref_autoref() {
|
||||||
|
check_number(
|
||||||
|
r#"
|
||||||
|
//- minicore: deref_mut
|
||||||
|
struct Foo;
|
||||||
|
struct Bar;
|
||||||
|
|
||||||
|
impl core::ops::Deref for Foo {
|
||||||
|
type Target = Bar;
|
||||||
|
fn deref(&self) -> &Bar {
|
||||||
|
&Bar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bar {
|
||||||
|
fn method(&self) -> i32 {
|
||||||
|
5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const GOAL: i32 = Foo.method();
|
||||||
|
"#,
|
||||||
|
5,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn function_call() {
|
fn function_call() {
|
||||||
check_number(
|
check_number(
|
||||||
|
@ -291,8 +291,10 @@ pub enum Adjust {
|
|||||||
/// call, with the signature `&'a T -> &'a U` or `&'a mut T -> &'a mut U`.
|
/// call, with the signature `&'a T -> &'a U` or `&'a mut T -> &'a mut U`.
|
||||||
/// The target type is `U` in both cases, with the region and mutability
|
/// The target type is `U` in both cases, with the region and mutability
|
||||||
/// being those shared by both the receiver and the returned reference.
|
/// being those shared by both the receiver and the returned reference.
|
||||||
|
///
|
||||||
|
/// Mutability is `None` when we are not sure.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct OverloadedDeref(pub Mutability);
|
pub struct OverloadedDeref(pub Option<Mutability>);
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum AutoBorrow {
|
pub enum AutoBorrow {
|
||||||
@ -355,7 +357,7 @@ pub struct InferenceResult {
|
|||||||
pub type_of_binding: ArenaMap<BindingId, Ty>,
|
pub type_of_binding: ArenaMap<BindingId, Ty>,
|
||||||
pub type_of_rpit: ArenaMap<RpitId, Ty>,
|
pub type_of_rpit: ArenaMap<RpitId, Ty>,
|
||||||
/// Type of the result of `.into_iter()` on the for. `ExprId` is the one of the whole for loop.
|
/// Type of the result of `.into_iter()` on the for. `ExprId` is the one of the whole for loop.
|
||||||
pub type_of_for_iterator: ArenaMap<ExprId, Ty>,
|
pub type_of_for_iterator: FxHashMap<ExprId, Ty>,
|
||||||
type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>,
|
type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>,
|
||||||
/// Interned common types to return references to.
|
/// Interned common types to return references to.
|
||||||
standard_types: InternedStandardTypes,
|
standard_types: InternedStandardTypes,
|
||||||
|
@ -693,7 +693,7 @@ pub(super) fn auto_deref_adjust_steps(autoderef: &Autoderef<'_, '_>) -> Vec<Adju
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|(kind, _source)| match kind {
|
.map(|(kind, _source)| match kind {
|
||||||
// We do not know what kind of deref we require at this point yet
|
// We do not know what kind of deref we require at this point yet
|
||||||
AutoderefKind::Overloaded => Some(OverloadedDeref(Mutability::Not)),
|
AutoderefKind::Overloaded => Some(OverloadedDeref(None)),
|
||||||
AutoderefKind::Builtin => None,
|
AutoderefKind::Builtin => None,
|
||||||
})
|
})
|
||||||
.zip(targets)
|
.zip(targets)
|
||||||
|
@ -5,7 +5,10 @@
|
|||||||
use chalk_ir::Mutability;
|
use chalk_ir::Mutability;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
body::Body,
|
body::Body,
|
||||||
expr::{Binding, BindingAnnotation, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId, RecordFieldPat, BindingId},
|
expr::{
|
||||||
|
Binding, BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId,
|
||||||
|
RecordFieldPat,
|
||||||
|
},
|
||||||
path::Path,
|
path::Path,
|
||||||
};
|
};
|
||||||
use hir_expand::name::Name;
|
use hir_expand::name::Name;
|
||||||
|
@ -579,8 +579,8 @@ pub(crate) fn apply(&self, table: &mut InferenceTable<'_>, ty: Ty) -> (Ty, Vec<A
|
|||||||
ty = new_ty.clone();
|
ty = new_ty.clone();
|
||||||
adjust.push(Adjustment {
|
adjust.push(Adjustment {
|
||||||
kind: Adjust::Deref(match kind {
|
kind: Adjust::Deref(match kind {
|
||||||
// FIXME should we know the mutability here?
|
// FIXME should we know the mutability here, when autoref is `None`?
|
||||||
AutoderefKind::Overloaded => Some(OverloadedDeref(Mutability::Not)),
|
AutoderefKind::Overloaded => Some(OverloadedDeref(self.autoref)),
|
||||||
AutoderefKind::Builtin => None,
|
AutoderefKind::Builtin => None,
|
||||||
}),
|
}),
|
||||||
target: new_ty,
|
target: new_ty,
|
||||||
|
@ -11,19 +11,23 @@
|
|||||||
},
|
},
|
||||||
lang_item::{LangItem, LangItemTarget},
|
lang_item::{LangItem, LangItemTarget},
|
||||||
layout::LayoutError,
|
layout::LayoutError,
|
||||||
|
path::Path,
|
||||||
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
|
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
|
||||||
DefWithBodyId, EnumVariantId, HasModule,
|
DefWithBodyId, EnumVariantId, HasModule,
|
||||||
};
|
};
|
||||||
|
use hir_expand::name;
|
||||||
use la_arena::ArenaMap;
|
use la_arena::ArenaMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
consteval::ConstEvalError, db::HirDatabase, display::HirDisplay, infer::TypeMismatch,
|
consteval::ConstEvalError, db::HirDatabase, display::HirDisplay, infer::TypeMismatch,
|
||||||
inhabitedness::is_ty_uninhabited_from, layout::layout_of_ty, mapping::ToChalk, static_lifetime,
|
inhabitedness::is_ty_uninhabited_from, layout::layout_of_ty, mapping::ToChalk, static_lifetime,
|
||||||
utils::generics, Adjust, AutoBorrow, CallableDefId, TyBuilder, TyExt,
|
utils::generics, Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
mod as_place;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
struct LoopBlocks {
|
struct LoopBlocks {
|
||||||
begin: BasicBlockId,
|
begin: BasicBlockId,
|
||||||
@ -61,6 +65,7 @@ pub enum MirLowerError {
|
|||||||
/// Something that should never happen and is definitely a bug, but we don't want to panic if it happened
|
/// Something that should never happen and is definitely a bug, but we don't want to panic if it happened
|
||||||
ImplementationError(&'static str),
|
ImplementationError(&'static str),
|
||||||
LangItemNotFound(LangItem),
|
LangItemNotFound(LangItem),
|
||||||
|
MutatingRvalue,
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! not_supported {
|
macro_rules! not_supported {
|
||||||
@ -69,6 +74,13 @@ macro_rules! not_supported {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! implementation_error {
|
||||||
|
($x: expr) => {{
|
||||||
|
::stdx::never!("MIR lower implementation bug: {}", $x);
|
||||||
|
return Err(MirLowerError::ImplementationError($x));
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
impl From<ConstEvalError> for MirLowerError {
|
impl From<ConstEvalError> for MirLowerError {
|
||||||
fn from(value: ConstEvalError) -> Self {
|
fn from(value: ConstEvalError) -> Self {
|
||||||
match value {
|
match value {
|
||||||
@ -84,117 +96,89 @@ fn from(value: LayoutError) -> Self {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl MirLowerError {
|
||||||
|
fn unresolved_path(db: &dyn HirDatabase, p: &Path) -> Self {
|
||||||
|
Self::UnresolvedName(p.display(db).to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, MirLowerError>;
|
type Result<T> = std::result::Result<T, MirLowerError>;
|
||||||
|
|
||||||
impl MirLowerCtx<'_> {
|
impl MirLowerCtx<'_> {
|
||||||
fn temp(&mut self, ty: Ty) -> Result<LocalId> {
|
fn temp(&mut self, ty: Ty) -> Result<LocalId> {
|
||||||
if matches!(ty.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) {
|
if matches!(ty.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) {
|
||||||
not_supported!("unsized temporaries");
|
implementation_error!("unsized temporaries");
|
||||||
}
|
}
|
||||||
Ok(self.result.locals.alloc(Local { ty }))
|
Ok(self.result.locals.alloc(Local { ty }))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lower_expr_as_place(&self, expr_id: ExprId) -> Option<Place> {
|
|
||||||
let adjustments = self.infer.expr_adjustments.get(&expr_id);
|
|
||||||
let mut r = self.lower_expr_as_place_without_adjust(expr_id)?;
|
|
||||||
for adjustment in adjustments.iter().flat_map(|x| x.iter()) {
|
|
||||||
match adjustment.kind {
|
|
||||||
Adjust::NeverToAny => return Some(r),
|
|
||||||
Adjust::Deref(None) => {
|
|
||||||
r.projection.push(ProjectionElem::Deref);
|
|
||||||
}
|
|
||||||
Adjust::Deref(Some(_)) => return None,
|
|
||||||
Adjust::Borrow(_) => return None,
|
|
||||||
Adjust::Pointer(_) => return None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lower_expr_as_place_without_adjust(&self, expr_id: ExprId) -> Option<Place> {
|
|
||||||
match &self.body.exprs[expr_id] {
|
|
||||||
Expr::Path(p) => {
|
|
||||||
let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
|
|
||||||
let pr = resolver.resolve_path_in_value_ns(self.db.upcast(), p.mod_path())?;
|
|
||||||
let pr = match pr {
|
|
||||||
ResolveValueResult::ValueNs(v) => v,
|
|
||||||
ResolveValueResult::Partial(..) => return None,
|
|
||||||
};
|
|
||||||
match pr {
|
|
||||||
ValueNs::LocalBinding(pat_id) => {
|
|
||||||
Some(self.result.binding_locals[pat_id].into())
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Expr::UnaryOp { expr, op } => match op {
|
|
||||||
hir_def::expr::UnaryOp::Deref => {
|
|
||||||
if !matches!(
|
|
||||||
self.expr_ty(*expr).kind(Interner),
|
|
||||||
TyKind::Ref(..) | TyKind::Raw(..)
|
|
||||||
) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let mut r = self.lower_expr_as_place(*expr)?;
|
|
||||||
r.projection.push(ProjectionElem::Deref);
|
|
||||||
Some(r)
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
},
|
|
||||||
Expr::Field { expr, .. } => {
|
|
||||||
let mut r = self.lower_expr_as_place(*expr)?;
|
|
||||||
self.push_field_projection(&mut r, expr_id).ok()?;
|
|
||||||
Some(r)
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lower_expr_to_some_operand(
|
fn lower_expr_to_some_operand(
|
||||||
&mut self,
|
&mut self,
|
||||||
expr_id: ExprId,
|
expr_id: ExprId,
|
||||||
current: BasicBlockId,
|
current: BasicBlockId,
|
||||||
) -> Result<(Operand, Option<BasicBlockId>)> {
|
) -> Result<Option<(Operand, BasicBlockId)>> {
|
||||||
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(expr_id);
|
||||||
return Ok((self.lower_literal_to_operand(ty, l)?, Some(current)));
|
return Ok(Some((self.lower_literal_to_operand(ty, l)?, current)));
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let (p, current) = self.lower_expr_to_some_place(expr_id, current)?;
|
let Some((p, current)) = self.lower_expr_as_place(current, expr_id, true)? else {
|
||||||
Ok((Operand::Copy(p), current))
|
return Ok(None);
|
||||||
|
};
|
||||||
|
Ok(Some((Operand::Copy(p), current)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lower_expr_to_some_place(
|
fn lower_expr_to_place_with_adjust(
|
||||||
&mut self,
|
&mut self,
|
||||||
expr_id: ExprId,
|
expr_id: ExprId,
|
||||||
prev_block: BasicBlockId,
|
place: Place,
|
||||||
) -> Result<(Place, Option<BasicBlockId>)> {
|
current: BasicBlockId,
|
||||||
if let Some(p) = self.lower_expr_as_place(expr_id) {
|
adjustments: &[Adjustment],
|
||||||
return Ok((p, Some(prev_block)));
|
) -> Result<Option<BasicBlockId>> {
|
||||||
|
match adjustments.split_last() {
|
||||||
|
Some((last, rest)) => match &last.kind {
|
||||||
|
Adjust::NeverToAny => {
|
||||||
|
let temp = self.temp(TyKind::Never.intern(Interner))?;
|
||||||
|
self.lower_expr_to_place_with_adjust(expr_id, temp.into(), current, rest)
|
||||||
|
}
|
||||||
|
Adjust::Deref(_) => {
|
||||||
|
let Some((p, current)) = self.lower_expr_as_place_with_adjust(current, expr_id, true, adjustments)? else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
self.push_assignment(current, place, Operand::Copy(p).into(), expr_id.into());
|
||||||
|
Ok(Some(current))
|
||||||
|
}
|
||||||
|
Adjust::Borrow(AutoBorrow::Ref(m) | AutoBorrow::RawPtr(m)) => {
|
||||||
|
let Some((p, current)) = self.lower_expr_as_place_with_adjust(current, expr_id, true, rest)? else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
let bk = BorrowKind::from_chalk(*m);
|
||||||
|
self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into());
|
||||||
|
Ok(Some(current))
|
||||||
|
}
|
||||||
|
Adjust::Pointer(cast) => {
|
||||||
|
let Some((p, current)) = self.lower_expr_as_place_with_adjust(current, expr_id, true, rest)? else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
self.push_assignment(
|
||||||
|
current,
|
||||||
|
place,
|
||||||
|
Rvalue::Cast(
|
||||||
|
CastKind::Pointer(cast.clone()),
|
||||||
|
Operand::Copy(p).into(),
|
||||||
|
last.target.clone(),
|
||||||
|
),
|
||||||
|
expr_id.into(),
|
||||||
|
);
|
||||||
|
Ok(Some(current))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => self.lower_expr_to_place_without_adjust(expr_id, place, current),
|
||||||
}
|
}
|
||||||
let ty = self.expr_ty_after_adjustments(expr_id);
|
|
||||||
let place = self.temp(ty)?;
|
|
||||||
Ok((place.into(), self.lower_expr_to_place(expr_id, place.into(), prev_block)?))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lower_expr_to_some_place_without_adjust(
|
|
||||||
&mut self,
|
|
||||||
expr_id: ExprId,
|
|
||||||
prev_block: BasicBlockId,
|
|
||||||
) -> Result<(Place, Option<BasicBlockId>)> {
|
|
||||||
if let Some(p) = self.lower_expr_as_place_without_adjust(expr_id) {
|
|
||||||
return Ok((p, Some(prev_block)));
|
|
||||||
}
|
|
||||||
let ty = self.expr_ty(expr_id);
|
|
||||||
let place = self.temp(ty)?;
|
|
||||||
Ok((
|
|
||||||
place.into(),
|
|
||||||
self.lower_expr_to_place_without_adjust(expr_id, place.into(), prev_block)?,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lower_expr_to_place(
|
fn lower_expr_to_place(
|
||||||
@ -203,50 +187,8 @@ fn lower_expr_to_place(
|
|||||||
place: Place,
|
place: Place,
|
||||||
prev_block: BasicBlockId,
|
prev_block: BasicBlockId,
|
||||||
) -> Result<Option<BasicBlockId>> {
|
) -> Result<Option<BasicBlockId>> {
|
||||||
if let Some(x) = self.infer.expr_adjustments.get(&expr_id) {
|
if let Some(adjustments) = self.infer.expr_adjustments.get(&expr_id) {
|
||||||
if x.len() > 0 {
|
return self.lower_expr_to_place_with_adjust(expr_id, place, prev_block, adjustments);
|
||||||
let (mut r, Some(current)) =
|
|
||||||
self.lower_expr_to_some_place_without_adjust(expr_id, prev_block)?
|
|
||||||
else {
|
|
||||||
return Ok(None);
|
|
||||||
};
|
|
||||||
for adjustment in x {
|
|
||||||
match &adjustment.kind {
|
|
||||||
Adjust::NeverToAny => (),
|
|
||||||
Adjust::Deref(None) => {
|
|
||||||
r.projection.push(ProjectionElem::Deref);
|
|
||||||
}
|
|
||||||
Adjust::Deref(Some(_)) => not_supported!("implicit overloaded dereference"),
|
|
||||||
Adjust::Borrow(AutoBorrow::Ref(m) | AutoBorrow::RawPtr(m)) => {
|
|
||||||
let tmp = self.temp(adjustment.target.clone())?;
|
|
||||||
self.push_assignment(
|
|
||||||
current,
|
|
||||||
tmp.into(),
|
|
||||||
Rvalue::Ref(BorrowKind::from_chalk(*m), r),
|
|
||||||
expr_id.into(),
|
|
||||||
);
|
|
||||||
r = tmp.into();
|
|
||||||
}
|
|
||||||
Adjust::Pointer(cast) => {
|
|
||||||
let target = &adjustment.target;
|
|
||||||
let tmp = self.temp(target.clone())?;
|
|
||||||
self.push_assignment(
|
|
||||||
current,
|
|
||||||
tmp.into(),
|
|
||||||
Rvalue::Cast(
|
|
||||||
CastKind::Pointer(cast.clone()),
|
|
||||||
Operand::Copy(r).into(),
|
|
||||||
target.clone(),
|
|
||||||
),
|
|
||||||
expr_id.into(),
|
|
||||||
);
|
|
||||||
r = tmp.into();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.push_assignment(current, place, Operand::Copy(r).into(), expr_id.into());
|
|
||||||
return Ok(Some(current));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self.lower_expr_to_place_without_adjust(expr_id, place, prev_block)
|
self.lower_expr_to_place_without_adjust(expr_id, place, prev_block)
|
||||||
}
|
}
|
||||||
@ -260,7 +202,7 @@ fn lower_expr_to_place_without_adjust(
|
|||||||
match &self.body.exprs[expr_id] {
|
match &self.body.exprs[expr_id] {
|
||||||
Expr::Missing => Err(MirLowerError::IncompleteExpr),
|
Expr::Missing => Err(MirLowerError::IncompleteExpr),
|
||||||
Expr::Path(p) => {
|
Expr::Path(p) => {
|
||||||
let unresolved_name = || MirLowerError::UnresolvedName(p.display(self.db).to_string());
|
let unresolved_name = || MirLowerError::unresolved_path(self.db, p);
|
||||||
let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
|
let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
|
||||||
let pr = resolver
|
let pr = resolver
|
||||||
.resolve_path_in_value_ns(self.db.upcast(), p.mod_path())
|
.resolve_path_in_value_ns(self.db.upcast(), p.mod_path())
|
||||||
@ -286,7 +228,7 @@ fn lower_expr_to_place_without_adjust(
|
|||||||
match variant {
|
match variant {
|
||||||
VariantId::EnumVariantId(e) => ValueNs::EnumVariantId(e),
|
VariantId::EnumVariantId(e) => ValueNs::EnumVariantId(e),
|
||||||
VariantId::StructId(s) => ValueNs::StructId(s),
|
VariantId::StructId(s) => ValueNs::StructId(s),
|
||||||
VariantId::UnionId(_) => return Err(MirLowerError::ImplementationError("Union variant as path")),
|
VariantId::UnionId(_) => implementation_error!("Union variant as path"),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(unresolved_name());
|
return Err(unresolved_name());
|
||||||
@ -355,7 +297,7 @@ fn lower_expr_to_place_without_adjust(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::If { condition, then_branch, else_branch } => {
|
Expr::If { condition, then_branch, else_branch } => {
|
||||||
let (discr, Some(current)) = self.lower_expr_to_some_operand(*condition, current)? else {
|
let Some((discr, current)) = self.lower_expr_to_some_operand(*condition, current)? else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
let start_of_then = self.new_basic_block();
|
let start_of_then = self.new_basic_block();
|
||||||
@ -377,8 +319,7 @@ fn lower_expr_to_place_without_adjust(
|
|||||||
Ok(self.merge_blocks(end_of_then, end_of_else))
|
Ok(self.merge_blocks(end_of_then, end_of_else))
|
||||||
}
|
}
|
||||||
Expr::Let { pat, expr } => {
|
Expr::Let { pat, expr } => {
|
||||||
self.push_storage_live(*pat, current)?;
|
let Some((cond_place, current)) = self.lower_expr_as_place(current, *expr, true)? else {
|
||||||
let (cond_place, Some(current)) = self.lower_expr_to_some_place(*expr, current)? else {
|
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
let (then_target, else_target) = self.pattern_match(
|
let (then_target, else_target) = self.pattern_match(
|
||||||
@ -411,70 +352,17 @@ fn lower_expr_to_place_without_adjust(
|
|||||||
self.lower_block_to_place(None, statements, current, *tail, place)
|
self.lower_block_to_place(None, statements, current, *tail, place)
|
||||||
}
|
}
|
||||||
Expr::Block { id: _, statements, tail, label } => {
|
Expr::Block { id: _, statements, tail, label } => {
|
||||||
if label.is_some() {
|
self.lower_block_to_place(*label, statements, current, *tail, place)
|
||||||
not_supported!("block with label");
|
|
||||||
}
|
|
||||||
for statement in statements.iter() {
|
|
||||||
match statement {
|
|
||||||
hir_def::expr::Statement::Let {
|
|
||||||
pat,
|
|
||||||
initializer,
|
|
||||||
else_branch,
|
|
||||||
type_ref: _,
|
|
||||||
} => {
|
|
||||||
self.push_storage_live(*pat, current)?;
|
|
||||||
if let Some(expr_id) = initializer {
|
|
||||||
let else_block;
|
|
||||||
let (init_place, Some(c)) =
|
|
||||||
self.lower_expr_to_some_place(*expr_id, current)?
|
|
||||||
else {
|
|
||||||
return Ok(None);
|
|
||||||
};
|
|
||||||
current = c;
|
|
||||||
(current, else_block) = self.pattern_match(
|
|
||||||
current,
|
|
||||||
None,
|
|
||||||
init_place,
|
|
||||||
self.expr_ty_after_adjustments(*expr_id),
|
|
||||||
*pat,
|
|
||||||
BindingAnnotation::Unannotated,
|
|
||||||
)?;
|
|
||||||
match (else_block, else_branch) {
|
|
||||||
(None, _) => (),
|
|
||||||
(Some(else_block), None) => {
|
|
||||||
self.set_terminator(else_block, Terminator::Unreachable);
|
|
||||||
}
|
|
||||||
(Some(else_block), Some(else_branch)) => {
|
|
||||||
let (_, b) = self
|
|
||||||
.lower_expr_to_some_place(*else_branch, else_block)?;
|
|
||||||
if let Some(b) = b {
|
|
||||||
self.set_terminator(b, Terminator::Unreachable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} },
|
|
||||||
hir_def::expr::Statement::Expr { expr, has_semi: _ } => {
|
|
||||||
let (_, Some(c)) = self.lower_expr_to_some_place(*expr, current)? else {
|
|
||||||
return Ok(None);
|
|
||||||
};
|
|
||||||
current = c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match tail {
|
|
||||||
Some(tail) => self.lower_expr_to_place(*tail, place, current),
|
|
||||||
None => Ok(Some(current)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Expr::Loop { body, label } => self.lower_loop(current, *label, |this, begin| {
|
Expr::Loop { body, label } => self.lower_loop(current, *label, |this, begin| {
|
||||||
if let (_, Some(block)) = this.lower_expr_to_some_place(*body, begin)? {
|
if let Some((_, block)) = this.lower_expr_as_place(begin, *body, true)? {
|
||||||
this.set_goto(block, begin);
|
this.set_goto(block, begin);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}),
|
}),
|
||||||
Expr::While { condition, body, label } => {
|
Expr::While { condition, body, label } => {
|
||||||
self.lower_loop(current, *label, |this, begin| {
|
self.lower_loop(current, *label, |this, begin| {
|
||||||
let (discr, Some(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(());
|
||||||
};
|
};
|
||||||
let end = this.current_loop_end()?;
|
let end = this.current_loop_end()?;
|
||||||
@ -486,7 +374,7 @@ fn lower_expr_to_place_without_adjust(
|
|||||||
targets: SwitchTargets::static_if(1, after_cond, end),
|
targets: SwitchTargets::static_if(1, after_cond, end),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if let (_, Some(block)) = this.lower_expr_to_some_place(*body, after_cond)? {
|
if let Some((_, block)) = this.lower_expr_as_place(after_cond, *body, true)? {
|
||||||
this.set_goto(block, begin);
|
this.set_goto(block, begin);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -510,7 +398,9 @@ fn lower_expr_to_place_without_adjust(
|
|||||||
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(iterable))
|
||||||
).intern(Interner));
|
).intern(Interner));
|
||||||
let iterator_ty = &self.infer.type_of_for_iterator[expr_id];
|
let &Some(iterator_ty) = &self.infer.type_of_for_iterator.get(&expr_id) else {
|
||||||
|
return Err(MirLowerError::TypeError("unknown for loop iterator type"));
|
||||||
|
};
|
||||||
let ref_mut_iterator_ty = TyKind::Ref(Mutability::Mut, static_lifetime(), iterator_ty.clone()).intern(Interner);
|
let ref_mut_iterator_ty = TyKind::Ref(Mutability::Mut, static_lifetime(), iterator_ty.clone()).intern(Interner);
|
||||||
let item_ty = &self.infer.type_of_pat[pat];
|
let item_ty = &self.infer.type_of_pat[pat];
|
||||||
let option_item_ty = TyKind::Adt(chalk_ir::AdtId(option.into()), Substitution::from1(Interner, item_ty.clone())).intern(Interner);
|
let option_item_ty = TyKind::Adt(chalk_ir::AdtId(option.into()), Substitution::from1(Interner, item_ty.clone())).intern(Interner);
|
||||||
@ -523,7 +413,6 @@ fn lower_expr_to_place_without_adjust(
|
|||||||
};
|
};
|
||||||
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, label, |this, begin| {
|
self.lower_loop(current, label, |this, begin| {
|
||||||
this.push_storage_live(pat, 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)?
|
||||||
else {
|
else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@ -538,7 +427,7 @@ fn lower_expr_to_place_without_adjust(
|
|||||||
pat.into(),
|
pat.into(),
|
||||||
Some(end),
|
Some(end),
|
||||||
&[pat], &None)?;
|
&[pat], &None)?;
|
||||||
if let (_, Some(block)) = this.lower_expr_to_some_place(body, current)? {
|
if let Some((_, block)) = this.lower_expr_as_place(current, body, true)? {
|
||||||
this.set_goto(block, begin);
|
this.set_goto(block, begin);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -595,7 +484,7 @@ fn lower_expr_to_place_without_adjust(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
Expr::Match { expr, arms } => {
|
Expr::Match { expr, arms } => {
|
||||||
let (cond_place, Some(mut current)) = self.lower_expr_to_some_place(*expr, current)?
|
let Some((cond_place, mut current)) = self.lower_expr_as_place(current, *expr, true)?
|
||||||
else {
|
else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
@ -605,7 +494,6 @@ fn lower_expr_to_place_without_adjust(
|
|||||||
if guard.is_some() {
|
if guard.is_some() {
|
||||||
not_supported!("pattern matching with guard");
|
not_supported!("pattern matching with guard");
|
||||||
}
|
}
|
||||||
self.push_storage_live(*pat, current)?;
|
|
||||||
let (then, otherwise) = self.pattern_match(
|
let (then, otherwise) = self.pattern_match(
|
||||||
current,
|
current,
|
||||||
None,
|
None,
|
||||||
@ -686,7 +574,7 @@ fn lower_expr_to_place_without_adjust(
|
|||||||
for RecordLitField { name, expr } in fields.iter() {
|
for RecordLitField { name, expr } in fields.iter() {
|
||||||
let field_id =
|
let field_id =
|
||||||
variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?;
|
variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?;
|
||||||
let (op, Some(c)) = self.lower_expr_to_some_operand(*expr, current)? else {
|
let Some((op, c)) = self.lower_expr_to_some_operand(*expr, current)? else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
current = c;
|
current = c;
|
||||||
@ -719,19 +607,6 @@ fn lower_expr_to_place_without_adjust(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Field { expr, .. } => {
|
|
||||||
let (mut current_place, Some(current)) = self.lower_expr_to_some_place(*expr, current)? else {
|
|
||||||
return Ok(None);
|
|
||||||
};
|
|
||||||
self.push_field_projection(&mut current_place, expr_id)?;
|
|
||||||
self.push_assignment(
|
|
||||||
current,
|
|
||||||
place,
|
|
||||||
Operand::Copy(current_place).into(),
|
|
||||||
expr_id.into(),
|
|
||||||
);
|
|
||||||
Ok(Some(current))
|
|
||||||
}
|
|
||||||
Expr::Await { .. } => not_supported!("await"),
|
Expr::Await { .. } => not_supported!("await"),
|
||||||
Expr::Try { .. } => not_supported!("? operator"),
|
Expr::Try { .. } => not_supported!("? operator"),
|
||||||
Expr::Yeet { .. } => not_supported!("yeet"),
|
Expr::Yeet { .. } => not_supported!("yeet"),
|
||||||
@ -739,7 +614,7 @@ fn lower_expr_to_place_without_adjust(
|
|||||||
Expr::Async { .. } => not_supported!("async block"),
|
Expr::Async { .. } => not_supported!("async block"),
|
||||||
Expr::Const { .. } => not_supported!("anonymous const block"),
|
Expr::Const { .. } => not_supported!("anonymous const block"),
|
||||||
Expr::Cast { expr, type_ref: _ } => {
|
Expr::Cast { expr, type_ref: _ } => {
|
||||||
let (x, Some(current)) = self.lower_expr_to_some_operand(*expr, current)? else {
|
let Some((x, current)) = self.lower_expr_to_some_operand(*expr, current)? else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
let source_ty = self.infer[*expr].clone();
|
let source_ty = self.infer[*expr].clone();
|
||||||
@ -753,7 +628,7 @@ fn lower_expr_to_place_without_adjust(
|
|||||||
Ok(Some(current))
|
Ok(Some(current))
|
||||||
}
|
}
|
||||||
Expr::Ref { expr, rawness: _, mutability } => {
|
Expr::Ref { expr, rawness: _, mutability } => {
|
||||||
let (p, Some(current)) = self.lower_expr_to_some_place(*expr, current)? else {
|
let Some((p, current)) = self.lower_expr_as_place(current, *expr, true)? else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
let bk = BorrowKind::from_hir(*mutability);
|
let bk = BorrowKind::from_hir(*mutability);
|
||||||
@ -761,35 +636,29 @@ fn lower_expr_to_place_without_adjust(
|
|||||||
Ok(Some(current))
|
Ok(Some(current))
|
||||||
}
|
}
|
||||||
Expr::Box { .. } => not_supported!("box expression"),
|
Expr::Box { .. } => not_supported!("box expression"),
|
||||||
Expr::UnaryOp { expr, op } => match op {
|
Expr::Field { .. } | Expr::Index { .. } | Expr::UnaryOp { op: hir_def::expr::UnaryOp::Deref, .. } => {
|
||||||
hir_def::expr::UnaryOp::Deref => {
|
let Some((p, current)) = self.lower_expr_as_place(current, expr_id, true)? else {
|
||||||
if !matches!(self.expr_ty(*expr).kind(Interner), TyKind::Ref(..) | TyKind::Raw(..)) {
|
return Ok(None);
|
||||||
not_supported!("explicit overloaded deref");
|
};
|
||||||
}
|
self.push_assignment(current, place, Operand::Copy(p).into(), expr_id.into());
|
||||||
let (mut tmp, Some(current)) = self.lower_expr_to_some_place(*expr, current)? else {
|
Ok(Some(current))
|
||||||
return Ok(None);
|
}
|
||||||
};
|
Expr::UnaryOp { expr, op: op @ (hir_def::expr::UnaryOp::Not | hir_def::expr::UnaryOp::Neg) } => {
|
||||||
tmp.projection.push(ProjectionElem::Deref);
|
let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)? else {
|
||||||
self.push_assignment(current, place, Operand::Copy(tmp).into(), expr_id.into());
|
return Ok(None);
|
||||||
Ok(Some(current))
|
};
|
||||||
}
|
let operation = match op {
|
||||||
hir_def::expr::UnaryOp::Not | hir_def::expr::UnaryOp::Neg => {
|
hir_def::expr::UnaryOp::Not => UnOp::Not,
|
||||||
let (operand, Some(current)) = self.lower_expr_to_some_operand(*expr, current)? else {
|
hir_def::expr::UnaryOp::Neg => UnOp::Neg,
|
||||||
return Ok(None);
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
let operation = match op {
|
self.push_assignment(
|
||||||
hir_def::expr::UnaryOp::Not => UnOp::Not,
|
current,
|
||||||
hir_def::expr::UnaryOp::Neg => UnOp::Neg,
|
place,
|
||||||
_ => unreachable!(),
|
Rvalue::UnaryOp(operation, operand),
|
||||||
};
|
expr_id.into(),
|
||||||
self.push_assignment(
|
);
|
||||||
current,
|
Ok(Some(current))
|
||||||
place,
|
|
||||||
Rvalue::UnaryOp(operation, operand),
|
|
||||||
expr_id.into(),
|
|
||||||
);
|
|
||||||
Ok(Some(current))
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
Expr::BinaryOp { lhs, rhs, op } => {
|
Expr::BinaryOp { lhs, rhs, op } => {
|
||||||
let op = op.ok_or(MirLowerError::IncompleteExpr)?;
|
let op = op.ok_or(MirLowerError::IncompleteExpr)?;
|
||||||
@ -797,19 +666,21 @@ fn lower_expr_to_place_without_adjust(
|
|||||||
if op.is_some() {
|
if op.is_some() {
|
||||||
not_supported!("assignment with arith op (like +=)");
|
not_supported!("assignment with arith op (like +=)");
|
||||||
}
|
}
|
||||||
let Some(lhs_place) = self.lower_expr_as_place(*lhs) else {
|
let Some((lhs_place, current)) =
|
||||||
not_supported!("assignment to complex place");
|
self.lower_expr_as_place(current, *lhs, false)?
|
||||||
|
else {
|
||||||
|
return Ok(None);
|
||||||
};
|
};
|
||||||
let (rhs_op, Some(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 (lhs_op, Some(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);
|
||||||
};
|
};
|
||||||
let (rhs_op, Some(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(
|
self.push_assignment(
|
||||||
@ -833,24 +704,12 @@ fn lower_expr_to_place_without_adjust(
|
|||||||
Ok(Some(current))
|
Ok(Some(current))
|
||||||
}
|
}
|
||||||
Expr::Range { .. } => not_supported!("range"),
|
Expr::Range { .. } => not_supported!("range"),
|
||||||
Expr::Index { base, index } => {
|
|
||||||
let (mut p_base, Some(current)) = self.lower_expr_to_some_place(*base, current)? else {
|
|
||||||
return Ok(None);
|
|
||||||
};
|
|
||||||
let l_index = self.temp(self.expr_ty_after_adjustments(*index))?;
|
|
||||||
let Some(current) = self.lower_expr_to_place(*index, l_index.into(), current)? else {
|
|
||||||
return Ok(None);
|
|
||||||
};
|
|
||||||
p_base.projection.push(ProjectionElem::Index(l_index));
|
|
||||||
self.push_assignment(current, place, Operand::Copy(p_base).into(), expr_id.into());
|
|
||||||
Ok(Some(current))
|
|
||||||
}
|
|
||||||
Expr::Closure { .. } => not_supported!("closure"),
|
Expr::Closure { .. } => not_supported!("closure"),
|
||||||
Expr::Tuple { exprs, is_assignee_expr: _ } => {
|
Expr::Tuple { exprs, is_assignee_expr: _ } => {
|
||||||
let Some(values) = exprs
|
let Some(values) = exprs
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| {
|
.map(|x| {
|
||||||
let (o, Some(c)) = self.lower_expr_to_some_operand(*x, current)? else {
|
let Some((o, c)) = self.lower_expr_to_some_operand(*x, current)? else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
current = c;
|
current = c;
|
||||||
@ -880,7 +739,7 @@ fn lower_expr_to_place_without_adjust(
|
|||||||
let Some(values) = elements
|
let Some(values) = elements
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| {
|
.map(|x| {
|
||||||
let (o, Some(c)) = self.lower_expr_to_some_operand(*x, current)? else {
|
let Some((o, c)) = self.lower_expr_to_some_operand(*x, current)? else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
current = c;
|
current = c;
|
||||||
@ -1034,7 +893,7 @@ fn lower_call_and_args(
|
|||||||
) -> Result<Option<BasicBlockId>> {
|
) -> Result<Option<BasicBlockId>> {
|
||||||
let Some(args) = args
|
let Some(args) = args
|
||||||
.map(|arg| {
|
.map(|arg| {
|
||||||
if let (temp, Some(c)) = self.lower_expr_to_some_operand(arg, current)? {
|
if let Some((temp, c)) = self.lower_expr_to_some_operand(arg, current)? {
|
||||||
current = c;
|
current = c;
|
||||||
Ok(Some(temp))
|
Ok(Some(temp))
|
||||||
} else {
|
} else {
|
||||||
@ -1250,6 +1109,7 @@ fn pattern_match(
|
|||||||
if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) {
|
if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) {
|
||||||
binding_mode = mode;
|
binding_mode = mode;
|
||||||
}
|
}
|
||||||
|
self.push_storage_live(*id, current)?;
|
||||||
self.push_assignment(
|
self.push_assignment(
|
||||||
current,
|
current,
|
||||||
target_place.into(),
|
target_place.into(),
|
||||||
@ -1464,7 +1324,7 @@ fn is_uninhabited(&self, expr_id: ExprId) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// This function push `StorageLive` statements for each binding in the pattern.
|
/// This function push `StorageLive` statements for each binding in the pattern.
|
||||||
fn push_storage_live(&mut self, pat: PatId, current: BasicBlockId) -> Result<()> {
|
fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) -> Result<()> {
|
||||||
// Current implementation is wrong. It adds no `StorageDead` at the end of scope, and before each break
|
// Current implementation is wrong. It adds no `StorageDead` at the end of scope, and before each break
|
||||||
// and continue. It just add a `StorageDead` before the `StorageLive`, which is not wrong, but unneeeded in
|
// and continue. It just add a `StorageDead` before the `StorageLive`, which is not wrong, but unneeeded in
|
||||||
// the proper implementation. Due this limitation, implementing a borrow checker on top of this mir will falsely
|
// the proper implementation. Due this limitation, implementing a borrow checker on top of this mir will falsely
|
||||||
@ -1483,12 +1343,15 @@ fn push_storage_live(&mut self, pat: PatId, current: BasicBlockId) -> Result<()>
|
|||||||
// ```
|
// ```
|
||||||
// But I think this approach work for mutability analysis, as user can't write code which mutates a binding
|
// But I think this approach work for mutability analysis, as user can't write code which mutates a binding
|
||||||
// after StorageDead, except loops, which are handled by this hack.
|
// after StorageDead, except loops, which are handled by this hack.
|
||||||
let span = pat.into();
|
let span = self.body.bindings[b]
|
||||||
self.body.walk_child_bindings(pat, &mut |b| {
|
.definitions
|
||||||
let l = self.result.binding_locals[b];
|
.first()
|
||||||
self.push_statement(current, StatementKind::StorageDead(l).with_span(span));
|
.copied()
|
||||||
self.push_statement(current, StatementKind::StorageLive(l).with_span(span));
|
.map(MirSpan::PatId)
|
||||||
});
|
.unwrap_or(MirSpan::Unknown);
|
||||||
|
let l = self.result.binding_locals[b];
|
||||||
|
self.push_statement(current, StatementKind::StorageDead(l).with_span(span));
|
||||||
|
self.push_statement(current, StatementKind::StorageLive(l).with_span(span));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1496,6 +1359,65 @@ fn resolve_lang_item(&self, item: LangItem) -> Result<LangItemTarget> {
|
|||||||
let crate_id = self.owner.module(self.db.upcast()).krate();
|
let crate_id = self.owner.module(self.db.upcast()).krate();
|
||||||
self.db.lang_item(crate_id, item).ok_or(MirLowerError::LangItemNotFound(item))
|
self.db.lang_item(crate_id, item).ok_or(MirLowerError::LangItemNotFound(item))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn lower_block_to_place(
|
||||||
|
&mut self,
|
||||||
|
label: Option<LabelId>,
|
||||||
|
statements: &[hir_def::expr::Statement],
|
||||||
|
mut current: BasicBlockId,
|
||||||
|
tail: Option<ExprId>,
|
||||||
|
place: Place,
|
||||||
|
) -> Result<Option<Idx<BasicBlock>>> {
|
||||||
|
if label.is_some() {
|
||||||
|
not_supported!("block with label");
|
||||||
|
}
|
||||||
|
for statement in statements.iter() {
|
||||||
|
match statement {
|
||||||
|
hir_def::expr::Statement::Let { pat, initializer, else_branch, type_ref: _ } => {
|
||||||
|
if let Some(expr_id) = initializer {
|
||||||
|
let else_block;
|
||||||
|
let Some((init_place, c)) =
|
||||||
|
self.lower_expr_as_place(current, *expr_id, true)?
|
||||||
|
else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
current = c;
|
||||||
|
(current, else_block) = self.pattern_match(
|
||||||
|
current,
|
||||||
|
None,
|
||||||
|
init_place,
|
||||||
|
self.expr_ty_after_adjustments(*expr_id),
|
||||||
|
*pat,
|
||||||
|
BindingAnnotation::Unannotated,
|
||||||
|
)?;
|
||||||
|
match (else_block, else_branch) {
|
||||||
|
(None, _) => (),
|
||||||
|
(Some(else_block), None) => {
|
||||||
|
self.set_terminator(else_block, Terminator::Unreachable);
|
||||||
|
}
|
||||||
|
(Some(else_block), Some(else_branch)) => {
|
||||||
|
if let Some((_, b)) =
|
||||||
|
self.lower_expr_as_place(else_block, *else_branch, true)?
|
||||||
|
{
|
||||||
|
self.set_terminator(b, Terminator::Unreachable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hir_def::expr::Statement::Expr { expr, has_semi: _ } => {
|
||||||
|
let Some((_, c)) = self.lower_expr_as_place(current, *expr, true)? else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
current = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match tail {
|
||||||
|
Some(tail) => self.lower_expr_to_place(tail, place, current),
|
||||||
|
None => Ok(Some(current)),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pattern_matching_dereference(
|
fn pattern_matching_dereference(
|
||||||
@ -1533,7 +1455,8 @@ fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
|
|||||||
pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result<Arc<MirBody>> {
|
pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result<Arc<MirBody>> {
|
||||||
let body = db.body(def);
|
let body = db.body(def);
|
||||||
let infer = db.infer(def);
|
let infer = db.infer(def);
|
||||||
Ok(Arc::new(lower_to_mir(db, def, &body, &infer, body.body_expr)?))
|
let result = lower_to_mir(db, def, &body, &infer, body.body_expr)?;
|
||||||
|
Ok(Arc::new(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mir_body_recover(
|
pub fn mir_body_recover(
|
||||||
@ -1553,9 +1476,7 @@ pub fn lower_to_mir(
|
|||||||
// need to take this input explicitly.
|
// need to take this input explicitly.
|
||||||
root_expr: ExprId,
|
root_expr: ExprId,
|
||||||
) -> Result<MirBody> {
|
) -> Result<MirBody> {
|
||||||
if let (Some((_, x)), _) | (_, Some((_, x))) =
|
if let Some((_, x)) = infer.type_mismatches().next() {
|
||||||
(infer.expr_type_mismatches().next(), infer.pat_type_mismatches().next())
|
|
||||||
{
|
|
||||||
return Err(MirLowerError::TypeMismatch(x.clone()));
|
return Err(MirLowerError::TypeMismatch(x.clone()));
|
||||||
}
|
}
|
||||||
let mut basic_blocks = Arena::new();
|
let mut basic_blocks = Arena::new();
|
||||||
|
236
crates/hir-ty/src/mir/lower/as_place.rs
Normal file
236
crates/hir-ty/src/mir/lower/as_place.rs
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
//! MIR lowering for places
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
macro_rules! not_supported {
|
||||||
|
($x: expr) => {
|
||||||
|
return Err(MirLowerError::NotSupported(format!($x)))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MirLowerCtx<'_> {
|
||||||
|
fn lower_expr_to_some_place_without_adjust(
|
||||||
|
&mut self,
|
||||||
|
expr_id: ExprId,
|
||||||
|
prev_block: BasicBlockId,
|
||||||
|
) -> Result<Option<(Place, BasicBlockId)>> {
|
||||||
|
let ty = self.expr_ty(expr_id);
|
||||||
|
let place = self.temp(ty)?;
|
||||||
|
let Some(current) = self.lower_expr_to_place_without_adjust(expr_id, place.into(), prev_block)? else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
Ok(Some((place.into(), current)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lower_expr_to_some_place_with_adjust(
|
||||||
|
&mut self,
|
||||||
|
expr_id: ExprId,
|
||||||
|
prev_block: BasicBlockId,
|
||||||
|
adjustments: &[Adjustment],
|
||||||
|
) -> Result<Option<(Place, BasicBlockId)>> {
|
||||||
|
let ty =
|
||||||
|
adjustments.last().map(|x| x.target.clone()).unwrap_or_else(|| self.expr_ty(expr_id));
|
||||||
|
let place = self.temp(ty)?;
|
||||||
|
let Some(current) = self.lower_expr_to_place_with_adjust(expr_id, place.into(), prev_block, adjustments)? else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
Ok(Some((place.into(), current)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn lower_expr_as_place_with_adjust(
|
||||||
|
&mut self,
|
||||||
|
current: BasicBlockId,
|
||||||
|
expr_id: ExprId,
|
||||||
|
upgrade_rvalue: bool,
|
||||||
|
adjustments: &[Adjustment],
|
||||||
|
) -> Result<Option<(Place, BasicBlockId)>> {
|
||||||
|
let try_rvalue = |this: &mut MirLowerCtx<'_>| {
|
||||||
|
if !upgrade_rvalue {
|
||||||
|
return Err(MirLowerError::MutatingRvalue);
|
||||||
|
}
|
||||||
|
this.lower_expr_to_some_place_with_adjust(expr_id, current, adjustments)
|
||||||
|
};
|
||||||
|
if let Some((last, rest)) = adjustments.split_last() {
|
||||||
|
match last.kind {
|
||||||
|
Adjust::Deref(None) => {
|
||||||
|
let Some(mut x) = self.lower_expr_as_place_with_adjust(
|
||||||
|
current,
|
||||||
|
expr_id,
|
||||||
|
upgrade_rvalue,
|
||||||
|
rest,
|
||||||
|
)? else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
x.0.projection.push(ProjectionElem::Deref);
|
||||||
|
Ok(Some(x))
|
||||||
|
}
|
||||||
|
Adjust::Deref(Some(od)) => {
|
||||||
|
let Some((r, current)) = self.lower_expr_as_place_with_adjust(
|
||||||
|
current,
|
||||||
|
expr_id,
|
||||||
|
upgrade_rvalue,
|
||||||
|
rest,
|
||||||
|
)? else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
self.lower_overloaded_deref(
|
||||||
|
current,
|
||||||
|
r,
|
||||||
|
rest.last()
|
||||||
|
.map(|x| x.target.clone())
|
||||||
|
.unwrap_or_else(|| self.expr_ty(expr_id)),
|
||||||
|
last.target.clone(),
|
||||||
|
expr_id.into(),
|
||||||
|
match od.0 {
|
||||||
|
Some(Mutability::Mut) => true,
|
||||||
|
Some(Mutability::Not) => false,
|
||||||
|
None => {
|
||||||
|
not_supported!("implicit overloaded deref with unknown mutability")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Adjust::NeverToAny | Adjust::Borrow(_) | Adjust::Pointer(_) => try_rvalue(self),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.lower_expr_as_place_without_adjust(current, expr_id, upgrade_rvalue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn lower_expr_as_place(
|
||||||
|
&mut self,
|
||||||
|
current: BasicBlockId,
|
||||||
|
expr_id: ExprId,
|
||||||
|
upgrade_rvalue: bool,
|
||||||
|
) -> Result<Option<(Place, BasicBlockId)>> {
|
||||||
|
match self.infer.expr_adjustments.get(&expr_id) {
|
||||||
|
Some(a) => self.lower_expr_as_place_with_adjust(current, expr_id, upgrade_rvalue, a),
|
||||||
|
None => self.lower_expr_as_place_without_adjust(current, expr_id, upgrade_rvalue),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lower_expr_as_place_without_adjust(
|
||||||
|
&mut self,
|
||||||
|
current: BasicBlockId,
|
||||||
|
expr_id: ExprId,
|
||||||
|
upgrade_rvalue: bool,
|
||||||
|
) -> Result<Option<(Place, BasicBlockId)>> {
|
||||||
|
let try_rvalue = |this: &mut MirLowerCtx<'_>| {
|
||||||
|
if !upgrade_rvalue {
|
||||||
|
return Err(MirLowerError::MutatingRvalue);
|
||||||
|
}
|
||||||
|
this.lower_expr_to_some_place_without_adjust(expr_id, current)
|
||||||
|
};
|
||||||
|
match &self.body.exprs[expr_id] {
|
||||||
|
Expr::Path(p) => {
|
||||||
|
let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
|
||||||
|
let Some(pr) = resolver.resolve_path_in_value_ns(self.db.upcast(), p.mod_path()) else {
|
||||||
|
return Err(MirLowerError::unresolved_path(self.db, p));
|
||||||
|
};
|
||||||
|
let pr = match pr {
|
||||||
|
ResolveValueResult::ValueNs(v) => v,
|
||||||
|
ResolveValueResult::Partial(..) => return try_rvalue(self),
|
||||||
|
};
|
||||||
|
match pr {
|
||||||
|
ValueNs::LocalBinding(pat_id) => {
|
||||||
|
Ok(Some((self.result.binding_locals[pat_id].into(), current)))
|
||||||
|
}
|
||||||
|
_ => try_rvalue(self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::UnaryOp { expr, op } => match op {
|
||||||
|
hir_def::expr::UnaryOp::Deref => {
|
||||||
|
if !matches!(
|
||||||
|
self.expr_ty(*expr).kind(Interner),
|
||||||
|
TyKind::Ref(..) | TyKind::Raw(..)
|
||||||
|
) {
|
||||||
|
let Some(_) = self.lower_expr_as_place(current, *expr, true)? else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
not_supported!("explicit overloaded deref");
|
||||||
|
}
|
||||||
|
let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
r.projection.push(ProjectionElem::Deref);
|
||||||
|
Ok(Some((r, current)))
|
||||||
|
}
|
||||||
|
_ => try_rvalue(self),
|
||||||
|
},
|
||||||
|
Expr::Field { expr, .. } => {
|
||||||
|
let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
self.push_field_projection(&mut r, expr_id)?;
|
||||||
|
Ok(Some((r, current)))
|
||||||
|
}
|
||||||
|
Expr::Index { base, index } => {
|
||||||
|
let base_ty = self.expr_ty_after_adjustments(*base);
|
||||||
|
let index_ty = self.expr_ty_after_adjustments(*index);
|
||||||
|
if index_ty != TyBuilder::usize()
|
||||||
|
|| !matches!(base_ty.kind(Interner), TyKind::Array(..) | TyKind::Slice(..))
|
||||||
|
{
|
||||||
|
not_supported!("overloaded index");
|
||||||
|
}
|
||||||
|
let Some((mut p_base, current)) =
|
||||||
|
self.lower_expr_as_place(current, *base, true)? else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
let l_index = self.temp(self.expr_ty_after_adjustments(*index))?;
|
||||||
|
let Some(current) = self.lower_expr_to_place(*index, l_index.into(), current)? else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
p_base.projection.push(ProjectionElem::Index(l_index));
|
||||||
|
Ok(Some((p_base, current)))
|
||||||
|
}
|
||||||
|
_ => try_rvalue(self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lower_overloaded_deref(
|
||||||
|
&mut self,
|
||||||
|
current: BasicBlockId,
|
||||||
|
place: Place,
|
||||||
|
source_ty: Ty,
|
||||||
|
target_ty: Ty,
|
||||||
|
span: MirSpan,
|
||||||
|
mutability: bool,
|
||||||
|
) -> Result<Option<(Place, BasicBlockId)>> {
|
||||||
|
let (chalk_mut, trait_lang_item, trait_method_name, borrow_kind) = if !mutability {
|
||||||
|
(Mutability::Not, LangItem::Deref, name![deref], BorrowKind::Shared)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
Mutability::Mut,
|
||||||
|
LangItem::DerefMut,
|
||||||
|
name![deref_mut],
|
||||||
|
BorrowKind::Mut { allow_two_phase_borrow: false },
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let ty_ref = TyKind::Ref(chalk_mut, static_lifetime(), source_ty.clone()).intern(Interner);
|
||||||
|
let target_ty_ref = TyKind::Ref(chalk_mut, static_lifetime(), target_ty).intern(Interner);
|
||||||
|
let ref_place: Place = self.temp(ty_ref)?.into();
|
||||||
|
self.push_assignment(current, ref_place.clone(), Rvalue::Ref(borrow_kind, place), span);
|
||||||
|
let deref_trait = self
|
||||||
|
.resolve_lang_item(trait_lang_item)?
|
||||||
|
.as_trait()
|
||||||
|
.ok_or(MirLowerError::LangItemNotFound(trait_lang_item))?;
|
||||||
|
let deref_fn = self
|
||||||
|
.db
|
||||||
|
.trait_data(deref_trait)
|
||||||
|
.method_by_name(&trait_method_name)
|
||||||
|
.ok_or(MirLowerError::LangItemNotFound(trait_lang_item))?;
|
||||||
|
let deref_fn_op = Operand::const_zst(
|
||||||
|
TyKind::FnDef(
|
||||||
|
self.db.intern_callable_def(CallableDefId::FunctionId(deref_fn)).into(),
|
||||||
|
Substitution::from1(Interner, source_ty),
|
||||||
|
)
|
||||||
|
.intern(Interner),
|
||||||
|
);
|
||||||
|
let mut result: Place = self.temp(target_ty_ref)?.into();
|
||||||
|
let Some(current) = self.lower_call(deref_fn_op, vec![Operand::Copy(ref_place)], result.clone(), current, false)? else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
result.projection.push(ProjectionElem::Deref);
|
||||||
|
Ok(Some((result, current)))
|
||||||
|
}
|
||||||
|
}
|
@ -258,6 +258,7 @@ fn test() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn coerce_autoderef_block() {
|
fn coerce_autoderef_block() {
|
||||||
|
// FIXME: We should know mutability in overloaded deref
|
||||||
check_no_mismatches(
|
check_no_mismatches(
|
||||||
r#"
|
r#"
|
||||||
//- minicore: deref
|
//- minicore: deref
|
||||||
@ -267,7 +268,7 @@ fn takes_ref_str(x: &str) {}
|
|||||||
fn returns_string() -> String { loop {} }
|
fn returns_string() -> String { loop {} }
|
||||||
fn test() {
|
fn test() {
|
||||||
takes_ref_str(&{ returns_string() });
|
takes_ref_str(&{ returns_string() });
|
||||||
// ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(Not))), Borrow(Ref(Not))
|
// ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(None))), Borrow(Ref(Not))
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
@ -1252,6 +1252,7 @@ fn foo<T: Trait>(a: &T) {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn autoderef_visibility_field() {
|
fn autoderef_visibility_field() {
|
||||||
|
// FIXME: We should know mutability in overloaded deref
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
//- minicore: deref
|
//- minicore: deref
|
||||||
@ -1273,7 +1274,7 @@ fn deref(&self) -> &Foo {
|
|||||||
mod b {
|
mod b {
|
||||||
fn foo() {
|
fn foo() {
|
||||||
let x = super::a::Bar::new().0;
|
let x = super::a::Bar::new().0;
|
||||||
// ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Not)))
|
// ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(None)))
|
||||||
// ^^^^^^^^^^^^^^^^^^^^^^ type: char
|
// ^^^^^^^^^^^^^^^^^^^^^^ type: char
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,8 @@
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase, Adt, Const, Enum, Field, FieldSource, Function, Impl, LifetimeParam,
|
db::HirDatabase, Adt, Const, Enum, Field, FieldSource, Function, Impl, LifetimeParam,
|
||||||
LocalSource, Macro, Module, Static, Struct, Trait, TypeAlias, TraitAlias, TypeOrConstParam, Union, Variant,
|
LocalSource, Macro, Module, Static, Struct, Trait, TraitAlias, TypeAlias, TypeOrConstParam,
|
||||||
|
Union, Variant,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait HasSource {
|
pub trait HasSource {
|
||||||
|
@ -85,12 +85,12 @@
|
|||||||
pub use crate::{
|
pub use crate::{
|
||||||
attrs::{HasAttrs, Namespace},
|
attrs::{HasAttrs, Namespace},
|
||||||
diagnostics::{
|
diagnostics::{
|
||||||
AnyDiagnostic, BreakOutsideOfLoop, InactiveCode, IncorrectCase, InvalidDeriveTarget,
|
AnyDiagnostic, BreakOutsideOfLoop, ExpectedFunction, InactiveCode, IncorrectCase,
|
||||||
MacroError, MalformedDerive, MismatchedArgCount, MissingFields, MissingMatchArms,
|
InvalidDeriveTarget, MacroError, MalformedDerive, MismatchedArgCount, MissingFields,
|
||||||
MissingUnsafe, NeedMut, NoSuchField, PrivateAssocItem, PrivateField,
|
MissingMatchArms, MissingUnsafe, NeedMut, NoSuchField, PrivateAssocItem, PrivateField,
|
||||||
ReplaceFilterMapNextWithFindMap, TypeMismatch, UnimplementedBuiltinMacro,
|
ReplaceFilterMapNextWithFindMap, TypeMismatch, UnimplementedBuiltinMacro,
|
||||||
UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall, UnresolvedModule,
|
UnresolvedExternCrate, UnresolvedField, UnresolvedImport, UnresolvedMacroCall,
|
||||||
UnresolvedProcMacro, UnusedMut,
|
UnresolvedMethodCall, UnresolvedModule, UnresolvedProcMacro, UnusedMut,
|
||||||
},
|
},
|
||||||
has_source::HasSource,
|
has_source::HasSource,
|
||||||
semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits},
|
semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits},
|
||||||
|
@ -1103,7 +1103,10 @@ fn expr_adjustments(&self, expr: &ast::Expr) -> Option<Vec<Adjustment>> {
|
|||||||
let kind = match adjust.kind {
|
let kind = match adjust.kind {
|
||||||
hir_ty::Adjust::NeverToAny => Adjust::NeverToAny,
|
hir_ty::Adjust::NeverToAny => Adjust::NeverToAny,
|
||||||
hir_ty::Adjust::Deref(Some(hir_ty::OverloadedDeref(m))) => {
|
hir_ty::Adjust::Deref(Some(hir_ty::OverloadedDeref(m))) => {
|
||||||
Adjust::Deref(Some(OverloadedDeref(mutability(m))))
|
// FIXME: Should we handle unknown mutability better?
|
||||||
|
Adjust::Deref(Some(OverloadedDeref(
|
||||||
|
m.map(mutability).unwrap_or(Mutability::Shared),
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
hir_ty::Adjust::Deref(None) => Adjust::Deref(None),
|
hir_ty::Adjust::Deref(None) => Adjust::Deref(None),
|
||||||
hir_ty::Adjust::Borrow(hir_ty::AutoBorrow::RawPtr(m)) => {
|
hir_ty::Adjust::Borrow(hir_ty::AutoBorrow::RawPtr(m)) => {
|
||||||
|
@ -529,6 +529,7 @@ fn for_loop() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn overloaded_deref() {
|
fn overloaded_deref() {
|
||||||
|
// FIXME: check for false negative
|
||||||
check_diagnostics(
|
check_diagnostics(
|
||||||
r#"
|
r#"
|
||||||
//- minicore: deref_mut
|
//- minicore: deref_mut
|
||||||
@ -547,9 +548,42 @@ fn deref_mut(&mut self) -> &mut i32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn f() {
|
fn f() {
|
||||||
// FIXME: remove this mut and detect error
|
let x = Foo;
|
||||||
|
let y = &*x;
|
||||||
|
let x = Foo;
|
||||||
let mut x = Foo;
|
let mut x = Foo;
|
||||||
let y = &mut *x;
|
let y: &mut i32 = &mut x;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn or_pattern() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
//- minicore: option
|
||||||
|
fn f(_: i32) {}
|
||||||
|
fn main() {
|
||||||
|
let ((Some(mut x), None) | (_, Some(mut x))) = (None, Some(7));
|
||||||
|
//^^^^^ 💡 weak: remove this `mut`
|
||||||
|
f(x);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn respect_allow_unused_mut() {
|
||||||
|
// FIXME: respect
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
fn f(_: i32) {}
|
||||||
|
fn main() {
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
let mut x = 2;
|
||||||
|
//^^^^^ 💡 weak: remove this `mut`
|
||||||
|
f(x);
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user