mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-01 09:33:26 +00:00
Record autoderef adjustments
This commit is contained in:
parent
9272942b92
commit
69e6511820
@ -13,12 +13,83 @@ use log::{info, warn};
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase, static_lifetime, AliasEq, AliasTy, BoundVar, Canonical, CanonicalVarKinds,
|
||||
DebruijnIndex, InEnvironment, Interner, ProjectionTyExt, Solution, Substitution, Ty, TyBuilder,
|
||||
TyKind,
|
||||
DebruijnIndex, Environment, InEnvironment, Interner, ProjectionTyExt, Solution, Substitution,
|
||||
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;
|
||||
|
||||
// FIXME: replace uses of this with Autoderef above
|
||||
pub fn autoderef<'a>(
|
||||
db: &'a dyn HirDatabase,
|
||||
krate: Option<CrateId>,
|
||||
|
@ -5,14 +5,17 @@
|
||||
//! See <https://doc.rust-lang.org/nomicon/coercions.html> and
|
||||
//! `librustc_typeck/check/coercion.rs`.
|
||||
|
||||
use std::iter;
|
||||
|
||||
use chalk_ir::{cast::Cast, Goal, Mutability, TyVariableKind};
|
||||
use hir_def::{expr::ExprId, lang_item::LangItemTarget};
|
||||
use stdx::always;
|
||||
|
||||
use crate::{
|
||||
autoderef,
|
||||
autoderef::{Autoderef, AutoderefKind},
|
||||
infer::{
|
||||
Adjust, Adjustment, AutoBorrow, InferOk, InferResult, InferenceContext, PointerCast,
|
||||
TypeError, TypeMismatch,
|
||||
Adjust, Adjustment, AutoBorrow, InferOk, InferResult, InferenceContext, OverloadedDeref,
|
||||
PointerCast, TypeError, TypeMismatch,
|
||||
},
|
||||
static_lifetime, Canonical, DomainGoal, FnPointer, FnSig, InEnvironment, Interner, Solution,
|
||||
Substitution, Ty, TyBuilder, TyExt, TyKind,
|
||||
@ -241,9 +244,10 @@ impl<'a> InferenceContext<'a> {
|
||||
/// To match `A` with `B`, autoderef will be performed,
|
||||
/// calling `deref`/`deref_mut` where necessary.
|
||||
fn coerce_ref(&mut self, from_ty: Ty, to_ty: &Ty, to_mt: Mutability) -> CoerceResult {
|
||||
match from_ty.kind(&Interner) {
|
||||
TyKind::Ref(mt, _, _) => {
|
||||
coerce_mutabilities(*mt, to_mt)?;
|
||||
let from_mt = match from_ty.kind(&Interner) {
|
||||
&TyKind::Ref(mt, _, _) => {
|
||||
coerce_mutabilities(mt, to_mt)?;
|
||||
mt
|
||||
}
|
||||
_ => 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
|
||||
// the structure like it is.
|
||||
|
||||
let canonicalized = self.canonicalize(from_ty);
|
||||
let autoderef = autoderef::autoderef(
|
||||
let canonicalized = self.canonicalize(from_ty.clone());
|
||||
let mut autoderef = Autoderef::new(
|
||||
self.db,
|
||||
self.resolver.krate(),
|
||||
InEnvironment {
|
||||
@ -266,7 +270,7 @@ impl<'a> InferenceContext<'a> {
|
||||
let mut first_error = None;
|
||||
let mut found = None;
|
||||
|
||||
for (autoderefs, referent_ty) in autoderef.enumerate() {
|
||||
for (referent_ty, autoderefs) in autoderef.by_ref() {
|
||||
if autoderefs == 0 {
|
||||
// Don't let this pass, otherwise it would cause
|
||||
// &T to autoref to &&T.
|
||||
@ -324,12 +328,43 @@ impl<'a> InferenceContext<'a> {
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
// FIXME: record overloaded deref adjustments
|
||||
success(
|
||||
vec![Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(to_mt)), target: ty.clone() }],
|
||||
ty,
|
||||
goals,
|
||||
)
|
||||
if ty == from_ty && from_mt == Mutability::Not && autoderef.step_count() == 1 {
|
||||
// As a special case, if we would produce `&'a *x`, that's
|
||||
// a total no-op. We end up with the type `&'a T` just as
|
||||
// we started with. In that case, just skip it
|
||||
// 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.
|
||||
|
@ -1,7 +1,10 @@
|
||||
//! Type inference for expressions.
|
||||
|
||||
use std::iter::{repeat, repeat_with};
|
||||
use std::{mem, sync::Arc};
|
||||
use std::{
|
||||
iter::{repeat, repeat_with},
|
||||
mem,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use chalk_ir::{cast::Cast, fold::Shift, Mutability, TyVariableKind};
|
||||
use hir_def::{
|
||||
@ -15,7 +18,8 @@ use stdx::always;
|
||||
use syntax::ast::RangeOp;
|
||||
|
||||
use crate::{
|
||||
autoderef, consteval,
|
||||
autoderef::{self, Autoderef},
|
||||
consteval,
|
||||
infer::coerce::CoerceMany,
|
||||
lower::lower_to_chalk_mutability,
|
||||
mapping::from_chalk,
|
||||
@ -314,7 +318,7 @@ impl<'a> InferenceContext<'a> {
|
||||
Expr::Call { callee, args } => {
|
||||
let callee_ty = self.infer_expr(*callee, &Expectation::none());
|
||||
let canonicalized = self.canonicalize(callee_ty.clone());
|
||||
let mut derefs = autoderef(
|
||||
let mut derefs = Autoderef::new(
|
||||
self.db,
|
||||
self.resolver.krate(),
|
||||
InEnvironment {
|
||||
@ -322,14 +326,19 @@ impl<'a> InferenceContext<'a> {
|
||||
environment: self.table.trait_env.env.clone(),
|
||||
},
|
||||
);
|
||||
let (param_tys, ret_ty): (Vec<Ty>, Ty) = derefs
|
||||
.find_map(|callee_deref_ty| {
|
||||
self.callable_sig(
|
||||
&canonicalized.decanonicalize_ty(callee_deref_ty.value),
|
||||
args.len(),
|
||||
)
|
||||
})
|
||||
.unwrap_or((Vec::new(), self.err_ty()));
|
||||
let res = derefs.by_ref().find_map(|(callee_deref_ty, _)| {
|
||||
self.callable_sig(
|
||||
&canonicalized.decanonicalize_ty(callee_deref_ty.value),
|
||||
args.len(),
|
||||
)
|
||||
});
|
||||
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.check_call_arguments(args, ¶m_tys);
|
||||
self.normalize_associated_types_in(ret_ty)
|
||||
@ -467,15 +476,16 @@ impl<'a> InferenceContext<'a> {
|
||||
Expr::Field { expr, name } => {
|
||||
let receiver_ty = self.infer_expr_inner(*expr, &Expectation::none());
|
||||
let canonicalized = self.canonicalize(receiver_ty);
|
||||
let ty = autoderef::autoderef(
|
||||
|
||||
let mut autoderef = Autoderef::new(
|
||||
self.db,
|
||||
self.resolver.krate(),
|
||||
InEnvironment {
|
||||
goal: canonicalized.value.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 module = self.resolver.module();
|
||||
let is_visible = |field_id: &FieldId| {
|
||||
@ -524,8 +534,14 @@ impl<'a> InferenceContext<'a> {
|
||||
}
|
||||
_ => 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);
|
||||
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 InEnvironment<T> = chalk_ir::InEnvironment<T>;
|
||||
pub type Environment = chalk_ir::Environment<Interner>;
|
||||
pub type DomainGoal = chalk_ir::DomainGoal<Interner>;
|
||||
pub type Goal = chalk_ir::Goal<Interner>;
|
||||
pub type AliasEq = chalk_ir::AliasEq<Interner>;
|
||||
|
@ -236,6 +236,7 @@ fn takes_ref_str(x: &str) {}
|
||||
fn returns_string() -> String { loop {} }
|
||||
fn test() {
|
||||
takes_ref_str(&{ returns_string() });
|
||||
// ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(Not))), Borrow(Ref(Not))
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
@ -1,5 +1,7 @@
|
||||
use expect_test::expect;
|
||||
|
||||
use crate::tests::check;
|
||||
|
||||
use super::{check_infer, check_types};
|
||||
|
||||
#[test]
|
||||
@ -1137,7 +1139,7 @@ fn main() {
|
||||
|
||||
#[test]
|
||||
fn autoderef_visibility_field() {
|
||||
check_infer(
|
||||
check(
|
||||
r#"
|
||||
//- minicore: deref
|
||||
mod a {
|
||||
@ -1158,33 +1160,18 @@ mod a {
|
||||
mod b {
|
||||
fn foo() {
|
||||
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]
|
||||
fn autoderef_visibility_method() {
|
||||
cov_mark::check!(autoderef_candidate_not_visible);
|
||||
check_infer(
|
||||
check(
|
||||
r#"
|
||||
//- minicore: deref
|
||||
mod a {
|
||||
@ -1213,34 +1200,10 @@ mod a {
|
||||
mod b {
|
||||
fn foo() {
|
||||
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