Skip to content

Commit 69e6511

Browse files
committed
Record autoderef adjustments
1 parent 9272942 commit 69e6511

File tree

6 files changed

+167
-80
lines changed

6 files changed

+167
-80
lines changed

crates/hir_ty/src/autoderef.rs

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,83 @@ use log::{info, warn};
1313

1414
use crate::{
1515
db::HirDatabase, static_lifetime, AliasEq, AliasTy, BoundVar, Canonical, CanonicalVarKinds,
16-
DebruijnIndex, InEnvironment, Interner, ProjectionTyExt, Solution, Substitution, Ty, TyBuilder,
17-
TyKind,
16+
DebruijnIndex, Environment, InEnvironment, Interner, ProjectionTyExt, Solution, Substitution,
17+
Ty, TyBuilder, TyKind,
1818
};
1919

20+
pub(crate) enum AutoderefKind {
21+
Builtin,
22+
Overloaded,
23+
}
24+
25+
pub(crate) struct Autoderef<'db> {
26+
db: &'db dyn HirDatabase,
27+
ty: Canonical<Ty>,
28+
at_start: bool,
29+
krate: Option<CrateId>,
30+
environment: Environment,
31+
steps: Vec<(AutoderefKind, Ty)>,
32+
}
33+
34+
impl<'db> Autoderef<'db> {
35+
pub(crate) fn new(
36+
db: &'db dyn HirDatabase,
37+
krate: Option<CrateId>,
38+
ty: InEnvironment<Canonical<Ty>>,
39+
) -> Self {
40+
let InEnvironment { goal: ty, environment } = ty;
41+
Autoderef { db, ty, at_start: true, environment, krate, steps: Vec::new() }
42+
}
43+
44+
pub(crate) fn step_count(&self) -> usize {
45+
self.steps.len()
46+
}
47+
48+
pub(crate) fn steps(&self) -> &[(AutoderefKind, chalk_ir::Ty<Interner>)] {
49+
&self.steps
50+
}
51+
52+
pub(crate) fn final_ty(&self) -> Ty {
53+
self.ty.value.clone()
54+
}
55+
}
56+
57+
impl Iterator for Autoderef<'_> {
58+
type Item = (Canonical<Ty>, usize);
59+
60+
fn next(&mut self) -> Option<Self::Item> {
61+
if self.at_start {
62+
self.at_start = false;
63+
return Some((self.ty.clone(), 0));
64+
}
65+
66+
if self.steps.len() >= AUTODEREF_RECURSION_LIMIT {
67+
return None;
68+
}
69+
70+
let (kind, new_ty) = if let Some(derefed) = builtin_deref(&self.ty.value) {
71+
(AutoderefKind::Builtin, Canonical { value: derefed, binders: self.ty.binders.clone() })
72+
} else {
73+
(
74+
AutoderefKind::Overloaded,
75+
deref_by_trait(
76+
self.db,
77+
self.krate?,
78+
InEnvironment { goal: &self.ty, environment: self.environment.clone() },
79+
)?,
80+
)
81+
};
82+
83+
self.steps.push((kind, self.ty.value.clone()));
84+
self.ty = new_ty;
85+
86+
Some((self.ty.clone(), self.step_count()))
87+
}
88+
}
89+
2090
const AUTODEREF_RECURSION_LIMIT: usize = 10;
2191

92+
// FIXME: replace uses of this with Autoderef above
2293
pub fn autoderef<'a>(
2394
db: &'a dyn HirDatabase,
2495
krate: Option<CrateId>,

crates/hir_ty/src/infer/coerce.rs

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,17 @@
55
//! See <https://doc.rust-lang.org/nomicon/coercions.html> and
66
//! `librustc_typeck/check/coercion.rs`.
77
8+
use std::iter;
9+
810
use chalk_ir::{cast::Cast, Goal, Mutability, TyVariableKind};
911
use hir_def::{expr::ExprId, lang_item::LangItemTarget};
12+
use stdx::always;
1013

1114
use crate::{
12-
autoderef,
15+
autoderef::{Autoderef, AutoderefKind},
1316
infer::{
14-
Adjust, Adjustment, AutoBorrow, InferOk, InferResult, InferenceContext, PointerCast,
15-
TypeError, TypeMismatch,
17+
Adjust, Adjustment, AutoBorrow, InferOk, InferResult, InferenceContext, OverloadedDeref,
18+
PointerCast, TypeError, TypeMismatch,
1619
},
1720
static_lifetime, Canonical, DomainGoal, FnPointer, FnSig, InEnvironment, Interner, Solution,
1821
Substitution, Ty, TyBuilder, TyExt, TyKind,
@@ -241,9 +244,10 @@ impl<'a> InferenceContext<'a> {
241244
/// To match `A` with `B`, autoderef will be performed,
242245
/// calling `deref`/`deref_mut` where necessary.
243246
fn coerce_ref(&mut self, from_ty: Ty, to_ty: &Ty, to_mt: Mutability) -> CoerceResult {
244-
match from_ty.kind(&Interner) {
245-
TyKind::Ref(mt, _, _) => {
246-
coerce_mutabilities(*mt, to_mt)?;
247+
let from_mt = match from_ty.kind(&Interner) {
248+
&TyKind::Ref(mt, _, _) => {
249+
coerce_mutabilities(mt, to_mt)?;
250+
mt
247251
}
248252
_ => return self.unify_and(&from_ty, to_ty, identity),
249253
};
@@ -254,8 +258,8 @@ impl<'a> InferenceContext<'a> {
254258
// details of coercion errors though, so I think it's useful to leave
255259
// the structure like it is.
256260

257-
let canonicalized = self.canonicalize(from_ty);
258-
let autoderef = autoderef::autoderef(
261+
let canonicalized = self.canonicalize(from_ty.clone());
262+
let mut autoderef = Autoderef::new(
259263
self.db,
260264
self.resolver.krate(),
261265
InEnvironment {
@@ -266,7 +270,7 @@ impl<'a> InferenceContext<'a> {
266270
let mut first_error = None;
267271
let mut found = None;
268272

269-
for (autoderefs, referent_ty) in autoderef.enumerate() {
273+
for (referent_ty, autoderefs) in autoderef.by_ref() {
270274
if autoderefs == 0 {
271275
// Don't let this pass, otherwise it would cause
272276
// &T to autoref to &&T.
@@ -324,12 +328,43 @@ impl<'a> InferenceContext<'a> {
324328
return Err(err);
325329
}
326330
};
327-
// FIXME: record overloaded deref adjustments
328-
success(
329-
vec![Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(to_mt)), target: ty.clone() }],
330-
ty,
331-
goals,
332-
)
331+
if ty == from_ty && from_mt == Mutability::Not && autoderef.step_count() == 1 {
332+
// As a special case, if we would produce `&'a *x`, that's
333+
// a total no-op. We end up with the type `&'a T` just as
334+
// we started with. In that case, just skip it
335+
// altogether. This is just an optimization.
336+
//
337+
// Note that for `&mut`, we DO want to reborrow --
338+
// otherwise, this would be a move, which might be an
339+
// error. For example `foo(self.x)` where `self` and
340+
// `self.x` both have `&mut `type would be a move of
341+
// `self.x`, but we auto-coerce it to `foo(&mut *self.x)`,
342+
// which is a borrow.
343+
always!(to_mt == Mutability::Not); // can only coerce &T -> &U
344+
return success(vec![], ty, goals);
345+
}
346+
347+
let mut adjustments = self.auto_deref_adjust_steps(&autoderef);
348+
adjustments
349+
.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(to_mt)), target: ty.clone() });
350+
351+
success(adjustments, ty, goals)
352+
}
353+
354+
pub(super) fn auto_deref_adjust_steps(&self, autoderef: &Autoderef<'_>) -> Vec<Adjustment> {
355+
let steps = autoderef.steps();
356+
let targets =
357+
steps.iter().skip(1).map(|(_, ty)| ty.clone()).chain(iter::once(autoderef.final_ty()));
358+
steps
359+
.iter()
360+
.map(|(kind, _source)| match kind {
361+
// We do not know what kind of deref we require at this point yet
362+
AutoderefKind::Overloaded => Some(OverloadedDeref(Mutability::Not)),
363+
AutoderefKind::Builtin => None,
364+
})
365+
.zip(targets)
366+
.map(|(autoderef, target)| Adjustment { kind: Adjust::Deref(autoderef), target })
367+
.collect()
333368
}
334369

335370
/// Attempts to coerce from the type of a Rust function item into a function pointer.

crates/hir_ty/src/infer/expr.rs

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
//! Type inference for expressions.
22
3-
use std::iter::{repeat, repeat_with};
4-
use std::{mem, sync::Arc};
3+
use std::{
4+
iter::{repeat, repeat_with},
5+
mem,
6+
sync::Arc,
7+
};
58

69
use chalk_ir::{cast::Cast, fold::Shift, Mutability, TyVariableKind};
710
use hir_def::{
@@ -15,7 +18,8 @@ use stdx::always;
1518
use syntax::ast::RangeOp;
1619

1720
use crate::{
18-
autoderef, consteval,
21+
autoderef::{self, Autoderef},
22+
consteval,
1923
infer::coerce::CoerceMany,
2024
lower::lower_to_chalk_mutability,
2125
mapping::from_chalk,
@@ -314,22 +318,27 @@ impl<'a> InferenceContext<'a> {
314318
Expr::Call { callee, args } => {
315319
let callee_ty = self.infer_expr(*callee, &Expectation::none());
316320
let canonicalized = self.canonicalize(callee_ty.clone());
317-
let mut derefs = autoderef(
321+
let mut derefs = Autoderef::new(
318322
self.db,
319323
self.resolver.krate(),
320324
InEnvironment {
321325
goal: canonicalized.value.clone(),
322326
environment: self.table.trait_env.env.clone(),
323327
},
324328
);
325-
let (param_tys, ret_ty): (Vec<Ty>, Ty) = derefs
326-
.find_map(|callee_deref_ty| {
327-
self.callable_sig(
328-
&canonicalized.decanonicalize_ty(callee_deref_ty.value),
329-
args.len(),
330-
)
331-
})
332-
.unwrap_or((Vec::new(), self.err_ty()));
329+
let res = derefs.by_ref().find_map(|(callee_deref_ty, _)| {
330+
self.callable_sig(
331+
&canonicalized.decanonicalize_ty(callee_deref_ty.value),
332+
args.len(),
333+
)
334+
});
335+
let (param_tys, ret_ty): (Vec<Ty>, Ty) = match res {
336+
Some(res) => {
337+
self.write_expr_adj(*callee, self.auto_deref_adjust_steps(&derefs));
338+
res
339+
}
340+
None => (Vec::new(), self.err_ty()),
341+
};
333342
self.register_obligations_for_call(&callee_ty);
334343
self.check_call_arguments(args, &param_tys);
335344
self.normalize_associated_types_in(ret_ty)
@@ -467,15 +476,16 @@ impl<'a> InferenceContext<'a> {
467476
Expr::Field { expr, name } => {
468477
let receiver_ty = self.infer_expr_inner(*expr, &Expectation::none());
469478
let canonicalized = self.canonicalize(receiver_ty);
470-
let ty = autoderef::autoderef(
479+
480+
let mut autoderef = Autoderef::new(
471481
self.db,
472482
self.resolver.krate(),
473483
InEnvironment {
474484
goal: canonicalized.value.clone(),
475485
environment: self.trait_env.env.clone(),
476486
},
477-
)
478-
.find_map(|derefed_ty| {
487+
);
488+
let ty = autoderef.by_ref().find_map(|(derefed_ty, _)| {
479489
let def_db = self.db.upcast();
480490
let module = self.resolver.module();
481491
let is_visible = |field_id: &FieldId| {
@@ -524,8 +534,14 @@ impl<'a> InferenceContext<'a> {
524534
}
525535
_ => None,
526536
}
527-
})
528-
.unwrap_or_else(|| self.err_ty());
537+
});
538+
let ty = match ty {
539+
Some(ty) => {
540+
self.write_expr_adj(*expr, self.auto_deref_adjust_steps(&autoderef));
541+
ty
542+
}
543+
None => self.err_ty(),
544+
};
529545
let ty = self.insert_type_vars(ty);
530546
self.normalize_associated_types_in(ty)
531547
}

crates/hir_ty/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ pub type Canonical<T> = chalk_ir::Canonical<T>;
112112
pub type FnSig = chalk_ir::FnSig<Interner>;
113113

114114
pub type InEnvironment<T> = chalk_ir::InEnvironment<T>;
115+
pub type Environment = chalk_ir::Environment<Interner>;
115116
pub type DomainGoal = chalk_ir::DomainGoal<Interner>;
116117
pub type Goal = chalk_ir::Goal<Interner>;
117118
pub type AliasEq = chalk_ir::AliasEq<Interner>;

crates/hir_ty/src/tests/coercion.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ fn takes_ref_str(x: &str) {}
236236
fn returns_string() -> String { loop {} }
237237
fn test() {
238238
takes_ref_str(&{ returns_string() });
239+
// ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(Not))), Borrow(Ref(Not))
239240
}
240241
"#,
241242
);

crates/hir_ty/src/tests/method_resolution.rs

Lines changed: 9 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use expect_test::expect;
22

3+
use crate::tests::check;
4+
35
use super::{check_infer, check_types};
46

57
#[test]
@@ -1137,7 +1139,7 @@ fn main() {
11371139

11381140
#[test]
11391141
fn autoderef_visibility_field() {
1140-
check_infer(
1142+
check(
11411143
r#"
11421144
//- minicore: deref
11431145
mod a {
@@ -1158,33 +1160,18 @@ mod a {
11581160
mod b {
11591161
fn foo() {
11601162
let x = super::a::Bar::new().0;
1163+
// ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Not)))
1164+
// ^^^^^^^^^^^^^^^^^^^^^^ type: char
11611165
}
11621166
}
1163-
"#,
1164-
expect![[r#"
1165-
107..138 '{ ... }': Bar
1166-
121..125 'Self': Bar(i32) -> Bar
1167-
121..128 'Self(0)': Bar
1168-
126..127 '0': i32
1169-
226..230 'self': &Bar
1170-
240..273 '{ ... }': &Foo
1171-
254..263 '&Foo('z')': &Foo
1172-
255..258 'Foo': Foo(char) -> Foo
1173-
255..263 'Foo('z')': Foo
1174-
259..262 ''z'': char
1175-
303..350 '{ ... }': ()
1176-
317..318 'x': char
1177-
321..339 'super:...r::new': fn new() -> Bar
1178-
321..341 'super:...:new()': Bar
1179-
321..343 'super:...ew().0': char
1180-
"#]],
1167+
"#,
11811168
)
11821169
}
11831170

11841171
#[test]
11851172
fn autoderef_visibility_method() {
11861173
cov_mark::check!(autoderef_candidate_not_visible);
1187-
check_infer(
1174+
check(
11881175
r#"
11891176
//- minicore: deref
11901177
mod a {
@@ -1213,34 +1200,10 @@ mod a {
12131200
mod b {
12141201
fn foo() {
12151202
let x = super::a::Bar::new().mango();
1203+
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type: char
12161204
}
12171205
}
1218-
"#,
1219-
expect![[r#"
1220-
75..79 'self': &Foo
1221-
89..119 '{ ... }': char
1222-
103..107 'self': &Foo
1223-
103..109 'self.0': char
1224-
195..226 '{ ... }': Bar
1225-
209..213 'Self': Bar(i32) -> Bar
1226-
209..216 'Self(0)': Bar
1227-
214..215 '0': i32
1228-
245..249 'self': &Bar
1229-
258..288 '{ ... }': i32
1230-
272..276 'self': &Bar
1231-
272..278 'self.0': i32
1232-
376..380 'self': &Bar
1233-
390..423 '{ ... }': &Foo
1234-
404..413 '&Foo('z')': &Foo
1235-
405..408 'Foo': Foo(char) -> Foo
1236-
405..413 'Foo('z')': Foo
1237-
409..412 ''z'': char
1238-
453..506 '{ ... }': ()
1239-
467..468 'x': char
1240-
471..489 'super:...r::new': fn new() -> Bar
1241-
471..491 'super:...:new()': Bar
1242-
471..499 'super:...ango()': char
1243-
"#]],
1206+
"#,
12441207
)
12451208
}
12461209

0 commit comments

Comments
 (0)