mirror of
https://github.com/rust-lang/rust.git
synced 2025-06-05 11:48:30 +00:00
Record autoderef adjustments
This commit is contained in:
parent
9272942b92
commit
69e6511820
@ -13,12 +13,83 @@ use log::{info, warn};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase, static_lifetime, AliasEq, AliasTy, BoundVar, Canonical, CanonicalVarKinds,
|
db::HirDatabase, static_lifetime, AliasEq, AliasTy, BoundVar, Canonical, CanonicalVarKinds,
|
||||||
DebruijnIndex, InEnvironment, Interner, ProjectionTyExt, Solution, Substitution, Ty, TyBuilder,
|
DebruijnIndex, Environment, InEnvironment, Interner, ProjectionTyExt, Solution, Substitution,
|
||||||
TyKind,
|
Ty, TyBuilder, TyKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub(crate) enum AutoderefKind {
|
||||||
|
Builtin,
|
||||||
|
Overloaded,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct Autoderef<'db> {
|
||||||
|
db: &'db dyn HirDatabase,
|
||||||
|
ty: Canonical<Ty>,
|
||||||
|
at_start: bool,
|
||||||
|
krate: Option<CrateId>,
|
||||||
|
environment: Environment,
|
||||||
|
steps: Vec<(AutoderefKind, Ty)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'db> Autoderef<'db> {
|
||||||
|
pub(crate) fn new(
|
||||||
|
db: &'db dyn HirDatabase,
|
||||||
|
krate: Option<CrateId>,
|
||||||
|
ty: InEnvironment<Canonical<Ty>>,
|
||||||
|
) -> Self {
|
||||||
|
let InEnvironment { goal: ty, environment } = ty;
|
||||||
|
Autoderef { db, ty, at_start: true, environment, krate, steps: Vec::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn step_count(&self) -> usize {
|
||||||
|
self.steps.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn steps(&self) -> &[(AutoderefKind, chalk_ir::Ty<Interner>)] {
|
||||||
|
&self.steps
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn final_ty(&self) -> Ty {
|
||||||
|
self.ty.value.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for Autoderef<'_> {
|
||||||
|
type Item = (Canonical<Ty>, usize);
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.at_start {
|
||||||
|
self.at_start = false;
|
||||||
|
return Some((self.ty.clone(), 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.steps.len() >= AUTODEREF_RECURSION_LIMIT {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (kind, new_ty) = if let Some(derefed) = builtin_deref(&self.ty.value) {
|
||||||
|
(AutoderefKind::Builtin, Canonical { value: derefed, binders: self.ty.binders.clone() })
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
AutoderefKind::Overloaded,
|
||||||
|
deref_by_trait(
|
||||||
|
self.db,
|
||||||
|
self.krate?,
|
||||||
|
InEnvironment { goal: &self.ty, environment: self.environment.clone() },
|
||||||
|
)?,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
self.steps.push((kind, self.ty.value.clone()));
|
||||||
|
self.ty = new_ty;
|
||||||
|
|
||||||
|
Some((self.ty.clone(), self.step_count()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const AUTODEREF_RECURSION_LIMIT: usize = 10;
|
const AUTODEREF_RECURSION_LIMIT: usize = 10;
|
||||||
|
|
||||||
|
// FIXME: replace uses of this with Autoderef above
|
||||||
pub fn autoderef<'a>(
|
pub fn autoderef<'a>(
|
||||||
db: &'a dyn HirDatabase,
|
db: &'a dyn HirDatabase,
|
||||||
krate: Option<CrateId>,
|
krate: Option<CrateId>,
|
||||||
|
@ -5,14 +5,17 @@
|
|||||||
//! See <https://doc.rust-lang.org/nomicon/coercions.html> and
|
//! See <https://doc.rust-lang.org/nomicon/coercions.html> and
|
||||||
//! `librustc_typeck/check/coercion.rs`.
|
//! `librustc_typeck/check/coercion.rs`.
|
||||||
|
|
||||||
|
use std::iter;
|
||||||
|
|
||||||
use chalk_ir::{cast::Cast, Goal, Mutability, TyVariableKind};
|
use chalk_ir::{cast::Cast, Goal, Mutability, TyVariableKind};
|
||||||
use hir_def::{expr::ExprId, lang_item::LangItemTarget};
|
use hir_def::{expr::ExprId, lang_item::LangItemTarget};
|
||||||
|
use stdx::always;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
autoderef,
|
autoderef::{Autoderef, AutoderefKind},
|
||||||
infer::{
|
infer::{
|
||||||
Adjust, Adjustment, AutoBorrow, InferOk, InferResult, InferenceContext, PointerCast,
|
Adjust, Adjustment, AutoBorrow, InferOk, InferResult, InferenceContext, OverloadedDeref,
|
||||||
TypeError, TypeMismatch,
|
PointerCast, TypeError, TypeMismatch,
|
||||||
},
|
},
|
||||||
static_lifetime, Canonical, DomainGoal, FnPointer, FnSig, InEnvironment, Interner, Solution,
|
static_lifetime, Canonical, DomainGoal, FnPointer, FnSig, InEnvironment, Interner, Solution,
|
||||||
Substitution, Ty, TyBuilder, TyExt, TyKind,
|
Substitution, Ty, TyBuilder, TyExt, TyKind,
|
||||||
@ -241,9 +244,10 @@ impl<'a> InferenceContext<'a> {
|
|||||||
/// To match `A` with `B`, autoderef will be performed,
|
/// To match `A` with `B`, autoderef will be performed,
|
||||||
/// calling `deref`/`deref_mut` where necessary.
|
/// calling `deref`/`deref_mut` where necessary.
|
||||||
fn coerce_ref(&mut self, from_ty: Ty, to_ty: &Ty, to_mt: Mutability) -> CoerceResult {
|
fn coerce_ref(&mut self, from_ty: Ty, to_ty: &Ty, to_mt: Mutability) -> CoerceResult {
|
||||||
match from_ty.kind(&Interner) {
|
let from_mt = match from_ty.kind(&Interner) {
|
||||||
TyKind::Ref(mt, _, _) => {
|
&TyKind::Ref(mt, _, _) => {
|
||||||
coerce_mutabilities(*mt, to_mt)?;
|
coerce_mutabilities(mt, to_mt)?;
|
||||||
|
mt
|
||||||
}
|
}
|
||||||
_ => return self.unify_and(&from_ty, to_ty, identity),
|
_ => return self.unify_and(&from_ty, to_ty, identity),
|
||||||
};
|
};
|
||||||
@ -254,8 +258,8 @@ impl<'a> InferenceContext<'a> {
|
|||||||
// details of coercion errors though, so I think it's useful to leave
|
// details of coercion errors though, so I think it's useful to leave
|
||||||
// the structure like it is.
|
// the structure like it is.
|
||||||
|
|
||||||
let canonicalized = self.canonicalize(from_ty);
|
let canonicalized = self.canonicalize(from_ty.clone());
|
||||||
let autoderef = autoderef::autoderef(
|
let mut autoderef = Autoderef::new(
|
||||||
self.db,
|
self.db,
|
||||||
self.resolver.krate(),
|
self.resolver.krate(),
|
||||||
InEnvironment {
|
InEnvironment {
|
||||||
@ -266,7 +270,7 @@ impl<'a> InferenceContext<'a> {
|
|||||||
let mut first_error = None;
|
let mut first_error = None;
|
||||||
let mut found = None;
|
let mut found = None;
|
||||||
|
|
||||||
for (autoderefs, referent_ty) in autoderef.enumerate() {
|
for (referent_ty, autoderefs) in autoderef.by_ref() {
|
||||||
if autoderefs == 0 {
|
if autoderefs == 0 {
|
||||||
// Don't let this pass, otherwise it would cause
|
// Don't let this pass, otherwise it would cause
|
||||||
// &T to autoref to &&T.
|
// &T to autoref to &&T.
|
||||||
@ -324,12 +328,43 @@ impl<'a> InferenceContext<'a> {
|
|||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// FIXME: record overloaded deref adjustments
|
if ty == from_ty && from_mt == Mutability::Not && autoderef.step_count() == 1 {
|
||||||
success(
|
// As a special case, if we would produce `&'a *x`, that's
|
||||||
vec![Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(to_mt)), target: ty.clone() }],
|
// a total no-op. We end up with the type `&'a T` just as
|
||||||
ty,
|
// we started with. In that case, just skip it
|
||||||
goals,
|
// altogether. This is just an optimization.
|
||||||
)
|
//
|
||||||
|
// Note that for `&mut`, we DO want to reborrow --
|
||||||
|
// otherwise, this would be a move, which might be an
|
||||||
|
// error. For example `foo(self.x)` where `self` and
|
||||||
|
// `self.x` both have `&mut `type would be a move of
|
||||||
|
// `self.x`, but we auto-coerce it to `foo(&mut *self.x)`,
|
||||||
|
// which is a borrow.
|
||||||
|
always!(to_mt == Mutability::Not); // can only coerce &T -> &U
|
||||||
|
return success(vec![], ty, goals);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut adjustments = self.auto_deref_adjust_steps(&autoderef);
|
||||||
|
adjustments
|
||||||
|
.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(to_mt)), target: ty.clone() });
|
||||||
|
|
||||||
|
success(adjustments, ty, goals)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn auto_deref_adjust_steps(&self, autoderef: &Autoderef<'_>) -> Vec<Adjustment> {
|
||||||
|
let steps = autoderef.steps();
|
||||||
|
let targets =
|
||||||
|
steps.iter().skip(1).map(|(_, ty)| ty.clone()).chain(iter::once(autoderef.final_ty()));
|
||||||
|
steps
|
||||||
|
.iter()
|
||||||
|
.map(|(kind, _source)| match kind {
|
||||||
|
// We do not know what kind of deref we require at this point yet
|
||||||
|
AutoderefKind::Overloaded => Some(OverloadedDeref(Mutability::Not)),
|
||||||
|
AutoderefKind::Builtin => None,
|
||||||
|
})
|
||||||
|
.zip(targets)
|
||||||
|
.map(|(autoderef, target)| Adjustment { kind: Adjust::Deref(autoderef), target })
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to coerce from the type of a Rust function item into a function pointer.
|
/// Attempts to coerce from the type of a Rust function item into a function pointer.
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
//! Type inference for expressions.
|
//! Type inference for expressions.
|
||||||
|
|
||||||
use std::iter::{repeat, repeat_with};
|
use std::{
|
||||||
use std::{mem, sync::Arc};
|
iter::{repeat, repeat_with},
|
||||||
|
mem,
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
use chalk_ir::{cast::Cast, fold::Shift, Mutability, TyVariableKind};
|
use chalk_ir::{cast::Cast, fold::Shift, Mutability, TyVariableKind};
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
@ -15,7 +18,8 @@ use stdx::always;
|
|||||||
use syntax::ast::RangeOp;
|
use syntax::ast::RangeOp;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
autoderef, consteval,
|
autoderef::{self, Autoderef},
|
||||||
|
consteval,
|
||||||
infer::coerce::CoerceMany,
|
infer::coerce::CoerceMany,
|
||||||
lower::lower_to_chalk_mutability,
|
lower::lower_to_chalk_mutability,
|
||||||
mapping::from_chalk,
|
mapping::from_chalk,
|
||||||
@ -314,7 +318,7 @@ impl<'a> InferenceContext<'a> {
|
|||||||
Expr::Call { callee, args } => {
|
Expr::Call { callee, args } => {
|
||||||
let callee_ty = self.infer_expr(*callee, &Expectation::none());
|
let callee_ty = self.infer_expr(*callee, &Expectation::none());
|
||||||
let canonicalized = self.canonicalize(callee_ty.clone());
|
let canonicalized = self.canonicalize(callee_ty.clone());
|
||||||
let mut derefs = autoderef(
|
let mut derefs = Autoderef::new(
|
||||||
self.db,
|
self.db,
|
||||||
self.resolver.krate(),
|
self.resolver.krate(),
|
||||||
InEnvironment {
|
InEnvironment {
|
||||||
@ -322,14 +326,19 @@ impl<'a> InferenceContext<'a> {
|
|||||||
environment: self.table.trait_env.env.clone(),
|
environment: self.table.trait_env.env.clone(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
let (param_tys, ret_ty): (Vec<Ty>, Ty) = derefs
|
let res = derefs.by_ref().find_map(|(callee_deref_ty, _)| {
|
||||||
.find_map(|callee_deref_ty| {
|
|
||||||
self.callable_sig(
|
self.callable_sig(
|
||||||
&canonicalized.decanonicalize_ty(callee_deref_ty.value),
|
&canonicalized.decanonicalize_ty(callee_deref_ty.value),
|
||||||
args.len(),
|
args.len(),
|
||||||
)
|
)
|
||||||
})
|
});
|
||||||
.unwrap_or((Vec::new(), self.err_ty()));
|
let (param_tys, ret_ty): (Vec<Ty>, Ty) = match res {
|
||||||
|
Some(res) => {
|
||||||
|
self.write_expr_adj(*callee, self.auto_deref_adjust_steps(&derefs));
|
||||||
|
res
|
||||||
|
}
|
||||||
|
None => (Vec::new(), self.err_ty()),
|
||||||
|
};
|
||||||
self.register_obligations_for_call(&callee_ty);
|
self.register_obligations_for_call(&callee_ty);
|
||||||
self.check_call_arguments(args, ¶m_tys);
|
self.check_call_arguments(args, ¶m_tys);
|
||||||
self.normalize_associated_types_in(ret_ty)
|
self.normalize_associated_types_in(ret_ty)
|
||||||
@ -467,15 +476,16 @@ impl<'a> InferenceContext<'a> {
|
|||||||
Expr::Field { expr, name } => {
|
Expr::Field { expr, name } => {
|
||||||
let receiver_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
let receiver_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
||||||
let canonicalized = self.canonicalize(receiver_ty);
|
let canonicalized = self.canonicalize(receiver_ty);
|
||||||
let ty = autoderef::autoderef(
|
|
||||||
|
let mut autoderef = Autoderef::new(
|
||||||
self.db,
|
self.db,
|
||||||
self.resolver.krate(),
|
self.resolver.krate(),
|
||||||
InEnvironment {
|
InEnvironment {
|
||||||
goal: canonicalized.value.clone(),
|
goal: canonicalized.value.clone(),
|
||||||
environment: self.trait_env.env.clone(),
|
environment: self.trait_env.env.clone(),
|
||||||
},
|
},
|
||||||
)
|
);
|
||||||
.find_map(|derefed_ty| {
|
let ty = autoderef.by_ref().find_map(|(derefed_ty, _)| {
|
||||||
let def_db = self.db.upcast();
|
let def_db = self.db.upcast();
|
||||||
let module = self.resolver.module();
|
let module = self.resolver.module();
|
||||||
let is_visible = |field_id: &FieldId| {
|
let is_visible = |field_id: &FieldId| {
|
||||||
@ -524,8 +534,14 @@ impl<'a> InferenceContext<'a> {
|
|||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
.unwrap_or_else(|| self.err_ty());
|
let ty = match ty {
|
||||||
|
Some(ty) => {
|
||||||
|
self.write_expr_adj(*expr, self.auto_deref_adjust_steps(&autoderef));
|
||||||
|
ty
|
||||||
|
}
|
||||||
|
None => self.err_ty(),
|
||||||
|
};
|
||||||
let ty = self.insert_type_vars(ty);
|
let ty = self.insert_type_vars(ty);
|
||||||
self.normalize_associated_types_in(ty)
|
self.normalize_associated_types_in(ty)
|
||||||
}
|
}
|
||||||
|
@ -112,6 +112,7 @@ pub type Canonical<T> = chalk_ir::Canonical<T>;
|
|||||||
pub type FnSig = chalk_ir::FnSig<Interner>;
|
pub type FnSig = chalk_ir::FnSig<Interner>;
|
||||||
|
|
||||||
pub type InEnvironment<T> = chalk_ir::InEnvironment<T>;
|
pub type InEnvironment<T> = chalk_ir::InEnvironment<T>;
|
||||||
|
pub type Environment = chalk_ir::Environment<Interner>;
|
||||||
pub type DomainGoal = chalk_ir::DomainGoal<Interner>;
|
pub type DomainGoal = chalk_ir::DomainGoal<Interner>;
|
||||||
pub type Goal = chalk_ir::Goal<Interner>;
|
pub type Goal = chalk_ir::Goal<Interner>;
|
||||||
pub type AliasEq = chalk_ir::AliasEq<Interner>;
|
pub type AliasEq = chalk_ir::AliasEq<Interner>;
|
||||||
|
@ -236,6 +236,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))
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use expect_test::expect;
|
use expect_test::expect;
|
||||||
|
|
||||||
|
use crate::tests::check;
|
||||||
|
|
||||||
use super::{check_infer, check_types};
|
use super::{check_infer, check_types};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1137,7 +1139,7 @@ fn main() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn autoderef_visibility_field() {
|
fn autoderef_visibility_field() {
|
||||||
check_infer(
|
check(
|
||||||
r#"
|
r#"
|
||||||
//- minicore: deref
|
//- minicore: deref
|
||||||
mod a {
|
mod a {
|
||||||
@ -1158,33 +1160,18 @@ mod a {
|
|||||||
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)))
|
||||||
|
// ^^^^^^^^^^^^^^^^^^^^^^ type: char
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
|
||||||
107..138 '{ ... }': Bar
|
|
||||||
121..125 'Self': Bar(i32) -> Bar
|
|
||||||
121..128 'Self(0)': Bar
|
|
||||||
126..127 '0': i32
|
|
||||||
226..230 'self': &Bar
|
|
||||||
240..273 '{ ... }': &Foo
|
|
||||||
254..263 '&Foo('z')': &Foo
|
|
||||||
255..258 'Foo': Foo(char) -> Foo
|
|
||||||
255..263 'Foo('z')': Foo
|
|
||||||
259..262 ''z'': char
|
|
||||||
303..350 '{ ... }': ()
|
|
||||||
317..318 'x': char
|
|
||||||
321..339 'super:...r::new': fn new() -> Bar
|
|
||||||
321..341 'super:...:new()': Bar
|
|
||||||
321..343 'super:...ew().0': char
|
|
||||||
"#]],
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn autoderef_visibility_method() {
|
fn autoderef_visibility_method() {
|
||||||
cov_mark::check!(autoderef_candidate_not_visible);
|
cov_mark::check!(autoderef_candidate_not_visible);
|
||||||
check_infer(
|
check(
|
||||||
r#"
|
r#"
|
||||||
//- minicore: deref
|
//- minicore: deref
|
||||||
mod a {
|
mod a {
|
||||||
@ -1213,34 +1200,10 @@ mod a {
|
|||||||
mod b {
|
mod b {
|
||||||
fn foo() {
|
fn foo() {
|
||||||
let x = super::a::Bar::new().mango();
|
let x = super::a::Bar::new().mango();
|
||||||
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type: char
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
|
||||||
75..79 'self': &Foo
|
|
||||||
89..119 '{ ... }': char
|
|
||||||
103..107 'self': &Foo
|
|
||||||
103..109 'self.0': char
|
|
||||||
195..226 '{ ... }': Bar
|
|
||||||
209..213 'Self': Bar(i32) -> Bar
|
|
||||||
209..216 'Self(0)': Bar
|
|
||||||
214..215 '0': i32
|
|
||||||
245..249 'self': &Bar
|
|
||||||
258..288 '{ ... }': i32
|
|
||||||
272..276 'self': &Bar
|
|
||||||
272..278 'self.0': i32
|
|
||||||
376..380 'self': &Bar
|
|
||||||
390..423 '{ ... }': &Foo
|
|
||||||
404..413 '&Foo('z')': &Foo
|
|
||||||
405..408 'Foo': Foo(char) -> Foo
|
|
||||||
405..413 'Foo('z')': Foo
|
|
||||||
409..412 ''z'': char
|
|
||||||
453..506 '{ ... }': ()
|
|
||||||
467..468 'x': char
|
|
||||||
471..489 'super:...r::new': fn new() -> Bar
|
|
||||||
471..491 'super:...:new()': Bar
|
|
||||||
471..499 'super:...ango()': char
|
|
||||||
"#]],
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user