1 use crate::check
::method
::MethodCallee
;
2 use crate::check
::{FnCtxt, PlaceOp}
;
4 use rustc_infer
::infer
::type_variable
::{TypeVariableOrigin, TypeVariableOriginKind}
;
5 use rustc_infer
::infer
::InferOk
;
6 use rustc_middle
::ty
::adjustment
::{Adjust, Adjustment, OverloadedDeref, PointerCast}
;
7 use rustc_middle
::ty
::adjustment
::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability}
;
8 use rustc_middle
::ty
::{self, Ty}
;
9 use rustc_span
::symbol
::{sym, Ident}
;
11 use rustc_trait_selection
::autoderef
::Autoderef
;
14 impl<'a
, 'tcx
> FnCtxt
<'a
, 'tcx
> {
15 /// Type-check `*oprnd_expr` with `oprnd_expr` type-checked already.
16 pub(super) fn lookup_derefing(
19 oprnd_expr
: &'tcx hir
::Expr
<'tcx
>,
21 ) -> Option
<Ty
<'tcx
>> {
22 if let Some(mt
) = oprnd_ty
.builtin_deref(true) {
26 let ok
= self.try_overloaded_deref(expr
.span
, oprnd_ty
)?
;
27 let method
= self.register_infer_ok_obligations(ok
);
28 if let ty
::Ref(region
, _
, hir
::Mutability
::Not
) = method
.sig
.inputs()[0].kind() {
29 self.apply_adjustments(
32 kind
: Adjust
::Borrow(AutoBorrow
::Ref(region
, AutoBorrowMutability
::Not
)),
33 target
: method
.sig
.inputs()[0],
37 span_bug
!(expr
.span
, "input to deref is not a ref?");
39 let ty
= self.make_overloaded_place_return_type(method
).ty
;
40 self.write_method_call(expr
.hir_id
, method
);
44 /// Type-check `*base_expr[index_expr]` with `base_expr` and `index_expr` type-checked already.
45 pub(super) fn lookup_indexing(
48 base_expr
: &'tcx hir
::Expr
<'tcx
>,
51 ) -> Option
<(/*index type*/ Ty
<'tcx
>, /*element type*/ Ty
<'tcx
>)> {
52 // FIXME(#18741) -- this is almost but not quite the same as the
53 // autoderef that normal method probing does. They could likely be
56 let mut autoderef
= self.autoderef(base_expr
.span
, base_ty
);
57 let mut result
= None
;
58 while result
.is_none() && autoderef
.next().is_some() {
59 result
= self.try_index_step(expr
, base_expr
, &autoderef
, idx_ty
);
61 self.register_predicates(autoderef
.into_obligations());
65 /// To type-check `base_expr[index_expr]`, we progressively autoderef
66 /// (and otherwise adjust) `base_expr`, looking for a type which either
67 /// supports builtin indexing or overloaded indexing.
68 /// This loop implements one step in that search; the autoderef loop
69 /// is implemented by `lookup_indexing`.
73 base_expr
: &hir
::Expr
<'_
>,
74 autoderef
: &Autoderef
<'a
, 'tcx
>,
76 ) -> Option
<(/*index type*/ Ty
<'tcx
>, /*element type*/ Ty
<'tcx
>)> {
78 self.structurally_resolved_type(autoderef
.span(), autoderef
.final_ty(false));
80 "try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \
82 expr
, base_expr
, adjusted_ty
, index_ty
85 for &unsize
in &[false, true] {
86 let mut self_ty
= adjusted_ty
;
88 // We only unsize arrays here.
89 if let ty
::Array(element_ty
, _
) = adjusted_ty
.kind() {
90 self_ty
= self.tcx
.mk_slice(element_ty
);
96 // If some lookup succeeds, write callee into table and extract index/element
97 // type from the method signature.
98 // If some lookup succeeded, install method in table
99 let input_ty
= self.next_ty_var(TypeVariableOrigin
{
100 kind
: TypeVariableOriginKind
::AutoDeref
,
101 span
: base_expr
.span
,
104 self.try_overloaded_place_op(expr
.span
, self_ty
, &[input_ty
], PlaceOp
::Index
);
106 if let Some(result
) = method
{
107 debug
!("try_index_step: success, using overloaded indexing");
108 let method
= self.register_infer_ok_obligations(result
);
110 let mut adjustments
= self.adjust_steps(autoderef
);
111 if let ty
::Ref(region
, _
, hir
::Mutability
::Not
) = method
.sig
.inputs()[0].kind() {
112 adjustments
.push(Adjustment
{
113 kind
: Adjust
::Borrow(AutoBorrow
::Ref(region
, AutoBorrowMutability
::Not
)),
114 target
: self.tcx
.mk_ref(
116 ty
::TypeAndMut { mutbl: hir::Mutability::Not, ty: adjusted_ty }
,
120 span_bug
!(expr
.span
, "input to index is not a ref?");
123 adjustments
.push(Adjustment
{
124 kind
: Adjust
::Pointer(PointerCast
::Unsize
),
125 target
: method
.sig
.inputs()[0],
128 self.apply_adjustments(base_expr
, adjustments
);
130 self.write_method_call(expr
.hir_id
, method
);
132 return Some((input_ty
, self.make_overloaded_place_return_type(method
).ty
));
139 /// Try to resolve an overloaded place op. We only deal with the immutable
140 /// variant here (Deref/Index). In some contexts we would need the mutable
141 /// variant (DerefMut/IndexMut); those would be later converted by
142 /// `convert_place_derefs_to_mutable`.
143 pub(super) fn try_overloaded_place_op(
147 arg_tys
: &[Ty
<'tcx
>],
149 ) -> Option
<InferOk
<'tcx
, MethodCallee
<'tcx
>>> {
150 debug
!("try_overloaded_place_op({:?},{:?},{:?})", span
, base_ty
, op
);
152 let (imm_tr
, imm_op
) = match op
{
153 PlaceOp
::Deref
=> (self.tcx
.lang_items().deref_trait(), sym
::deref
),
154 PlaceOp
::Index
=> (self.tcx
.lang_items().index_trait(), sym
::index
),
156 imm_tr
.and_then(|trait_did
| {
157 self.lookup_method_in_trait(
159 Ident
::with_dummy_span(imm_op
),
167 fn try_mutable_overloaded_place_op(
171 arg_tys
: &[Ty
<'tcx
>],
173 ) -> Option
<InferOk
<'tcx
, MethodCallee
<'tcx
>>> {
174 debug
!("try_mutable_overloaded_place_op({:?},{:?},{:?})", span
, base_ty
, op
);
176 let (mut_tr
, mut_op
) = match op
{
177 PlaceOp
::Deref
=> (self.tcx
.lang_items().deref_mut_trait(), sym
::deref_mut
),
178 PlaceOp
::Index
=> (self.tcx
.lang_items().index_mut_trait(), sym
::index_mut
),
180 mut_tr
.and_then(|trait_did
| {
181 self.lookup_method_in_trait(
183 Ident
::with_dummy_span(mut_op
),
191 /// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index`
192 /// into `DerefMut` and `IndexMut` respectively.
194 /// This is a second pass of typechecking derefs/indices. We need this because we do not
195 /// always know whether a place needs to be mutable or not in the first pass.
196 /// This happens whether there is an implicit mutable reborrow, e.g. when the type
197 /// is used as the receiver of a method call.
198 pub fn convert_place_derefs_to_mutable(&self, expr
: &hir
::Expr
<'_
>) {
199 // Gather up expressions we want to munge.
200 let mut exprs
= vec
![expr
];
202 while let hir
::ExprKind
::Field(ref expr
, _
)
203 | hir
::ExprKind
::Index(ref expr
, _
)
204 | hir
::ExprKind
::Unary(hir
::UnOp
::Deref
, ref expr
) = exprs
.last().unwrap().kind
209 debug
!("convert_place_derefs_to_mutable: exprs={:?}", exprs
);
211 // Fix up autoderefs and derefs.
212 let mut inside_union
= false;
213 for (i
, &expr
) in exprs
.iter().rev().enumerate() {
214 debug
!("convert_place_derefs_to_mutable: i={} expr={:?}", i
, expr
);
216 let mut source
= self.node_ty(expr
.hir_id
);
217 if matches
!(expr
.kind
, hir
::ExprKind
::Unary(hir
::UnOp
::Deref
, _
)) {
218 // Clear previous flag; after a pointer indirection it does not apply any more.
219 inside_union
= false;
221 if source
.ty_adt_def().map_or(false, |adt
| adt
.is_union()) {
224 // Fix up the autoderefs. Autorefs can only occur immediately preceding
225 // overloaded place ops, and will be fixed by them in order to get
226 // the correct region.
227 // Do not mutate adjustments in place, but rather take them,
228 // and replace them after mutating them, to avoid having the
229 // typeck results borrowed during (`deref_mut`) method resolution.
230 let previous_adjustments
=
231 self.typeck_results
.borrow_mut().adjustments_mut().remove(expr
.hir_id
);
232 if let Some(mut adjustments
) = previous_adjustments
{
233 for adjustment
in &mut adjustments
{
234 if let Adjust
::Deref(Some(ref mut deref
)) = adjustment
.kind
{
235 if let Some(ok
) = self.try_mutable_overloaded_place_op(
241 let method
= self.register_infer_ok_obligations(ok
);
242 if let ty
::Ref(region
, _
, mutbl
) = *method
.sig
.output().kind() {
243 *deref
= OverloadedDeref { region, mutbl, span: deref.span }
;
245 // If this is a union field, also throw an error for `DerefMut` of `ManuallyDrop` (see RFC 2514).
246 // This helps avoid accidental drops.
248 && source
.ty_adt_def().map_or(false, |adt
| adt
.is_manually_drop())
250 let mut err
= self.tcx
.sess
.struct_span_err(
252 "not automatically applying `DerefMut` on `ManuallyDrop` union field",
255 "writing to this reference calls the destructor for the old value",
257 err
.help("add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor");
262 source
= adjustment
.target
;
264 self.typeck_results
.borrow_mut().adjustments_mut().insert(expr
.hir_id
, adjustments
);
268 hir
::ExprKind
::Index(ref base_expr
, ..) => {
269 self.convert_place_op_to_mutable(PlaceOp
::Index
, expr
, base_expr
);
271 hir
::ExprKind
::Unary(hir
::UnOp
::Deref
, ref base_expr
) => {
272 self.convert_place_op_to_mutable(PlaceOp
::Deref
, expr
, base_expr
);
279 fn convert_place_op_to_mutable(
282 expr
: &hir
::Expr
<'_
>,
283 base_expr
: &hir
::Expr
<'_
>,
285 debug
!("convert_place_op_to_mutable({:?}, {:?}, {:?})", op
, expr
, base_expr
);
286 if !self.typeck_results
.borrow().is_method_call(expr
) {
287 debug
!("convert_place_op_to_mutable - builtin, nothing to do");
291 // Need to deref because overloaded place ops take self by-reference.
295 .expr_ty_adjusted(base_expr
)
296 .builtin_deref(false)
297 .expect("place op takes something that is not a ref")
300 let arg_ty
= match op
{
301 PlaceOp
::Deref
=> None
,
303 // We would need to recover the `T` used when we resolve `<_ as Index<T>>::index`
304 // in try_index_step. This is the subst at index 1.
306 // Note: we should *not* use `expr_ty` of index_expr here because autoderef
307 // during coercions can cause type of index_expr to differ from `T` (#72002).
308 // We also could not use `expr_ty_adjusted` of index_expr because reborrowing
309 // during coercions can also cause type of index_expr to differ from `T`,
310 // which can potentially cause regionck failure (#74933).
311 Some(self.typeck_results
.borrow().node_substs(expr
.hir_id
).type_at(1))
314 let arg_tys
= match arg_ty
{
316 Some(ref ty
) => slice
::from_ref(ty
),
319 let method
= self.try_mutable_overloaded_place_op(expr
.span
, base_ty
, arg_tys
, op
);
320 let method
= match method
{
321 Some(ok
) => self.register_infer_ok_obligations(ok
),
322 // Couldn't find the mutable variant of the place op, keep the
323 // current, immutable version.
326 debug
!("convert_place_op_to_mutable: method={:?}", method
);
327 self.write_method_call(expr
.hir_id
, method
);
329 let region
= if let ty
::Ref(r
, _
, hir
::Mutability
::Mut
) = method
.sig
.inputs()[0].kind() {
332 span_bug
!(expr
.span
, "input to mutable place op is not a mut ref?");
335 // Convert the autoref in the base expr to mutable with the correct
336 // region and mutability.
337 let base_expr_ty
= self.node_ty(base_expr
.hir_id
);
338 if let Some(adjustments
) =
339 self.typeck_results
.borrow_mut().adjustments_mut().get_mut(base_expr
.hir_id
)
341 let mut source
= base_expr_ty
;
342 for adjustment
in &mut adjustments
[..] {
343 if let Adjust
::Borrow(AutoBorrow
::Ref(..)) = adjustment
.kind
{
344 debug
!("convert_place_op_to_mutable: converting autoref {:?}", adjustment
);
345 let mutbl
= AutoBorrowMutability
::Mut
{
346 // Deref/indexing can be desugared to a method call,
347 // so maybe we could use two-phase here.
348 // See the documentation of AllowTwoPhase for why that's
349 // not the case today.
350 allow_two_phase_borrow
: AllowTwoPhase
::No
,
352 adjustment
.kind
= Adjust
::Borrow(AutoBorrow
::Ref(region
, mutbl
));
354 self.tcx
.mk_ref(region
, ty
::TypeAndMut { ty: source, mutbl: mutbl.into() }
);
356 source
= adjustment
.target
;
359 // If we have an autoref followed by unsizing at the end, fix the unsize target.
360 if let [.., Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }
, Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), ref mut target }
] =
363 *target
= method
.sig
.inputs()[0];