1 use crate::check
::method
::MethodCallee
;
2 use crate::check
::{has_expected_num_generic_args, FnCtxt, PlaceOp}
;
4 use rustc_errors
::Applicability
;
6 use rustc_infer
::infer
::type_variable
::{TypeVariableOrigin, TypeVariableOriginKind}
;
7 use rustc_infer
::infer
::InferOk
;
8 use rustc_middle
::ty
::adjustment
::{Adjust, Adjustment, OverloadedDeref, PointerCast}
;
9 use rustc_middle
::ty
::adjustment
::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability}
;
10 use rustc_middle
::ty
::{self, Ty}
;
11 use rustc_span
::symbol
::{sym, Ident}
;
13 use rustc_trait_selection
::autoderef
::Autoderef
;
16 impl<'a
, 'tcx
> FnCtxt
<'a
, 'tcx
> {
17 /// Type-check `*oprnd_expr` with `oprnd_expr` type-checked already.
18 pub(super) fn lookup_derefing(
21 oprnd_expr
: &'tcx hir
::Expr
<'tcx
>,
23 ) -> Option
<Ty
<'tcx
>> {
24 if let Some(mt
) = oprnd_ty
.builtin_deref(true) {
28 let ok
= self.try_overloaded_deref(expr
.span
, oprnd_ty
)?
;
29 let method
= self.register_infer_ok_obligations(ok
);
30 if let ty
::Ref(region
, _
, hir
::Mutability
::Not
) = method
.sig
.inputs()[0].kind() {
31 self.apply_adjustments(
34 kind
: Adjust
::Borrow(AutoBorrow
::Ref(*region
, AutoBorrowMutability
::Not
)),
35 target
: method
.sig
.inputs()[0],
39 span_bug
!(expr
.span
, "input to deref is not a ref?");
41 let ty
= self.make_overloaded_place_return_type(method
).ty
;
42 self.write_method_call(expr
.hir_id
, method
);
46 /// Type-check `*base_expr[index_expr]` with `base_expr` and `index_expr` type-checked already.
47 pub(super) fn lookup_indexing(
50 base_expr
: &'tcx hir
::Expr
<'tcx
>,
52 index_expr
: &'tcx hir
::Expr
<'tcx
>,
54 ) -> Option
<(/*index type*/ Ty
<'tcx
>, /*element type*/ Ty
<'tcx
>)> {
55 // FIXME(#18741) -- this is almost but not quite the same as the
56 // autoderef that normal method probing does. They could likely be
59 let mut autoderef
= self.autoderef(base_expr
.span
, base_ty
);
60 let mut result
= None
;
61 while result
.is_none() && autoderef
.next().is_some() {
62 result
= self.try_index_step(expr
, base_expr
, &autoderef
, idx_ty
, index_expr
);
64 self.register_predicates(autoderef
.into_obligations());
72 base_expr
: &hir
::Expr
<'_
>,
73 ) -> Option
<(Ty
<'tcx
>, Ty
<'tcx
>)> {
74 let ty
= self.resolve_vars_if_possible(ty
);
75 let mut err
= self.tcx
.sess
.struct_span_err(
77 &format
!("negative integers cannot be used to index on a `{ty}`"),
79 err
.span_label(span
, &format
!("cannot use a negative integer for indexing on `{ty}`"));
80 if let (hir
::ExprKind
::Path(..), Ok(snippet
)) =
81 (&base_expr
.kind
, self.tcx
.sess
.source_map().span_to_snippet(base_expr
.span
))
83 // `foo[-1]` to `foo[foo.len() - 1]`
84 err
.span_suggestion_verbose(
87 "to access an element starting from the end of the `{ty}`, compute the index",
89 format
!("{snippet}.len() "),
90 Applicability
::MachineApplicable
,
94 Some((self.tcx
.ty_error(), self.tcx
.ty_error()))
97 /// To type-check `base_expr[index_expr]`, we progressively autoderef
98 /// (and otherwise adjust) `base_expr`, looking for a type which either
99 /// supports builtin indexing or overloaded indexing.
100 /// This loop implements one step in that search; the autoderef loop
101 /// is implemented by `lookup_indexing`.
104 expr
: &hir
::Expr
<'_
>,
105 base_expr
: &hir
::Expr
<'_
>,
106 autoderef
: &Autoderef
<'a
, 'tcx
>,
108 index_expr
: &hir
::Expr
<'_
>,
109 ) -> Option
<(/*index type*/ Ty
<'tcx
>, /*element type*/ Ty
<'tcx
>)> {
111 self.structurally_resolved_type(autoderef
.span(), autoderef
.final_ty(false));
113 "try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \
115 expr
, base_expr
, adjusted_ty
, index_ty
118 if let hir
::ExprKind
::Unary(
121 kind
: hir
::ExprKind
::Lit(hir
::Lit { node: ast::LitKind::Int(..), .. }
),
126 match adjusted_ty
.kind() {
127 ty
::Adt(def
, _
) if self.tcx
.is_diagnostic_item(sym
::Vec
, def
.did()) => {
128 return self.negative_index(adjusted_ty
, index_expr
.span
, base_expr
);
130 ty
::Slice(_
) | ty
::Array(_
, _
) => {
131 return self.negative_index(adjusted_ty
, index_expr
.span
, base_expr
);
137 for unsize
in [false, true] {
138 let mut self_ty
= adjusted_ty
;
140 // We only unsize arrays here.
141 if let ty
::Array(element_ty
, _
) = adjusted_ty
.kind() {
142 self_ty
= self.tcx
.mk_slice(*element_ty
);
148 // If some lookup succeeds, write callee into table and extract index/element
149 // type from the method signature.
150 // If some lookup succeeded, install method in table
151 let input_ty
= self.next_ty_var(TypeVariableOrigin
{
152 kind
: TypeVariableOriginKind
::AutoDeref
,
153 span
: base_expr
.span
,
156 self.try_overloaded_place_op(expr
.span
, self_ty
, &[input_ty
], PlaceOp
::Index
);
158 if let Some(result
) = method
{
159 debug
!("try_index_step: success, using overloaded indexing");
160 let method
= self.register_infer_ok_obligations(result
);
162 let mut adjustments
= self.adjust_steps(autoderef
);
163 if let ty
::Ref(region
, _
, hir
::Mutability
::Not
) = method
.sig
.inputs()[0].kind() {
164 adjustments
.push(Adjustment
{
165 kind
: Adjust
::Borrow(AutoBorrow
::Ref(*region
, AutoBorrowMutability
::Not
)),
166 target
: self.tcx
.mk_ref(
168 ty
::TypeAndMut { mutbl: hir::Mutability::Not, ty: adjusted_ty }
,
172 span_bug
!(expr
.span
, "input to index is not a ref?");
175 adjustments
.push(Adjustment
{
176 kind
: Adjust
::Pointer(PointerCast
::Unsize
),
177 target
: method
.sig
.inputs()[0],
180 self.apply_adjustments(base_expr
, adjustments
);
182 self.write_method_call(expr
.hir_id
, method
);
184 return Some((input_ty
, self.make_overloaded_place_return_type(method
).ty
));
191 /// Try to resolve an overloaded place op. We only deal with the immutable
192 /// variant here (Deref/Index). In some contexts we would need the mutable
193 /// variant (DerefMut/IndexMut); those would be later converted by
194 /// `convert_place_derefs_to_mutable`.
195 pub(super) fn try_overloaded_place_op(
199 arg_tys
: &[Ty
<'tcx
>],
201 ) -> Option
<InferOk
<'tcx
, MethodCallee
<'tcx
>>> {
202 debug
!("try_overloaded_place_op({:?},{:?},{:?})", span
, base_ty
, op
);
204 let (imm_tr
, imm_op
) = match op
{
205 PlaceOp
::Deref
=> (self.tcx
.lang_items().deref_trait(), sym
::deref
),
206 PlaceOp
::Index
=> (self.tcx
.lang_items().index_trait(), sym
::index
),
209 // If the lang item was declared incorrectly, stop here so that we don't
210 // run into an ICE (#83893). The error is reported where the lang item is
212 if !has_expected_num_generic_args(
223 imm_tr
.and_then(|trait_did
| {
224 self.lookup_method_in_trait(
226 Ident
::with_dummy_span(imm_op
),
234 fn try_mutable_overloaded_place_op(
238 arg_tys
: &[Ty
<'tcx
>],
240 ) -> Option
<InferOk
<'tcx
, MethodCallee
<'tcx
>>> {
241 debug
!("try_mutable_overloaded_place_op({:?},{:?},{:?})", span
, base_ty
, op
);
243 let (mut_tr
, mut_op
) = match op
{
244 PlaceOp
::Deref
=> (self.tcx
.lang_items().deref_mut_trait(), sym
::deref_mut
),
245 PlaceOp
::Index
=> (self.tcx
.lang_items().index_mut_trait(), sym
::index_mut
),
248 // If the lang item was declared incorrectly, stop here so that we don't
249 // run into an ICE (#83893). The error is reported where the lang item is
251 if !has_expected_num_generic_args(
262 mut_tr
.and_then(|trait_did
| {
263 self.lookup_method_in_trait(
265 Ident
::with_dummy_span(mut_op
),
273 /// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index`
274 /// into `DerefMut` and `IndexMut` respectively.
276 /// This is a second pass of typechecking derefs/indices. We need this because we do not
277 /// always know whether a place needs to be mutable or not in the first pass.
278 /// This happens whether there is an implicit mutable reborrow, e.g. when the type
279 /// is used as the receiver of a method call.
280 pub fn convert_place_derefs_to_mutable(&self, expr
: &hir
::Expr
<'_
>) {
281 // Gather up expressions we want to munge.
282 let mut exprs
= vec
![expr
];
284 while let hir
::ExprKind
::Field(ref expr
, _
)
285 | hir
::ExprKind
::Index(ref expr
, _
)
286 | hir
::ExprKind
::Unary(hir
::UnOp
::Deref
, ref expr
) = exprs
.last().unwrap().kind
291 debug
!("convert_place_derefs_to_mutable: exprs={:?}", exprs
);
293 // Fix up autoderefs and derefs.
294 let mut inside_union
= false;
295 for (i
, &expr
) in exprs
.iter().rev().enumerate() {
296 debug
!("convert_place_derefs_to_mutable: i={} expr={:?}", i
, expr
);
298 let mut source
= self.node_ty(expr
.hir_id
);
299 if matches
!(expr
.kind
, hir
::ExprKind
::Unary(hir
::UnOp
::Deref
, _
)) {
300 // Clear previous flag; after a pointer indirection it does not apply any more.
301 inside_union
= false;
303 if source
.is_union() {
306 // Fix up the autoderefs. Autorefs can only occur immediately preceding
307 // overloaded place ops, and will be fixed by them in order to get
308 // the correct region.
309 // Do not mutate adjustments in place, but rather take them,
310 // and replace them after mutating them, to avoid having the
311 // typeck results borrowed during (`deref_mut`) method resolution.
312 let previous_adjustments
=
313 self.typeck_results
.borrow_mut().adjustments_mut().remove(expr
.hir_id
);
314 if let Some(mut adjustments
) = previous_adjustments
{
315 for adjustment
in &mut adjustments
{
316 if let Adjust
::Deref(Some(ref mut deref
)) = adjustment
.kind
317 && let Some(ok
) = self.try_mutable_overloaded_place_op(
324 let method
= self.register_infer_ok_obligations(ok
);
325 if let ty
::Ref(region
, _
, mutbl
) = *method
.sig
.output().kind() {
326 *deref
= OverloadedDeref { region, mutbl, span: deref.span }
;
328 // If this is a union field, also throw an error for `DerefMut` of `ManuallyDrop` (see RFC 2514).
329 // This helps avoid accidental drops.
331 && source
.ty_adt_def().map_or(false, |adt
| adt
.is_manually_drop())
333 let mut err
= self.tcx
.sess
.struct_span_err(
335 "not automatically applying `DerefMut` on `ManuallyDrop` union field",
338 "writing to this reference calls the destructor for the old value",
340 err
.help("add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor");
344 source
= adjustment
.target
;
346 self.typeck_results
.borrow_mut().adjustments_mut().insert(expr
.hir_id
, adjustments
);
350 hir
::ExprKind
::Index(base_expr
, ..) => {
351 self.convert_place_op_to_mutable(PlaceOp
::Index
, expr
, base_expr
);
353 hir
::ExprKind
::Unary(hir
::UnOp
::Deref
, base_expr
) => {
354 self.convert_place_op_to_mutable(PlaceOp
::Deref
, expr
, base_expr
);
361 fn convert_place_op_to_mutable(
364 expr
: &hir
::Expr
<'_
>,
365 base_expr
: &hir
::Expr
<'_
>,
367 debug
!("convert_place_op_to_mutable({:?}, {:?}, {:?})", op
, expr
, base_expr
);
368 if !self.typeck_results
.borrow().is_method_call(expr
) {
369 debug
!("convert_place_op_to_mutable - builtin, nothing to do");
373 // Need to deref because overloaded place ops take self by-reference.
377 .expr_ty_adjusted(base_expr
)
378 .builtin_deref(false)
379 .expect("place op takes something that is not a ref")
382 let arg_ty
= match op
{
383 PlaceOp
::Deref
=> None
,
385 // We would need to recover the `T` used when we resolve `<_ as Index<T>>::index`
386 // in try_index_step. This is the subst at index 1.
388 // Note: we should *not* use `expr_ty` of index_expr here because autoderef
389 // during coercions can cause type of index_expr to differ from `T` (#72002).
390 // We also could not use `expr_ty_adjusted` of index_expr because reborrowing
391 // during coercions can also cause type of index_expr to differ from `T`,
392 // which can potentially cause regionck failure (#74933).
393 Some(self.typeck_results
.borrow().node_substs(expr
.hir_id
).type_at(1))
396 let arg_tys
= match arg_ty
{
398 Some(ref ty
) => slice
::from_ref(ty
),
401 let method
= self.try_mutable_overloaded_place_op(expr
.span
, base_ty
, arg_tys
, op
);
402 let method
= match method
{
403 Some(ok
) => self.register_infer_ok_obligations(ok
),
404 // Couldn't find the mutable variant of the place op, keep the
405 // current, immutable version.
408 debug
!("convert_place_op_to_mutable: method={:?}", method
);
409 self.write_method_call(expr
.hir_id
, method
);
411 let ty
::Ref(region
, _
, hir
::Mutability
::Mut
) = method
.sig
.inputs()[0].kind() else {
412 span_bug
!(expr
.span
, "input to mutable place op is not a mut ref?");
415 // Convert the autoref in the base expr to mutable with the correct
416 // region and mutability.
417 let base_expr_ty
= self.node_ty(base_expr
.hir_id
);
418 if let Some(adjustments
) =
419 self.typeck_results
.borrow_mut().adjustments_mut().get_mut(base_expr
.hir_id
)
421 let mut source
= base_expr_ty
;
422 for adjustment
in &mut adjustments
[..] {
423 if let Adjust
::Borrow(AutoBorrow
::Ref(..)) = adjustment
.kind
{
424 debug
!("convert_place_op_to_mutable: converting autoref {:?}", adjustment
);
425 let mutbl
= AutoBorrowMutability
::Mut
{
426 // Deref/indexing can be desugared to a method call,
427 // so maybe we could use two-phase here.
428 // See the documentation of AllowTwoPhase for why that's
429 // not the case today.
430 allow_two_phase_borrow
: AllowTwoPhase
::No
,
432 adjustment
.kind
= Adjust
::Borrow(AutoBorrow
::Ref(*region
, mutbl
));
433 adjustment
.target
= self
435 .mk_ref(*region
, ty
::TypeAndMut { ty: source, mutbl: mutbl.into() }
);
437 source
= adjustment
.target
;
440 // If we have an autoref followed by unsizing at the end, fix the unsize target.
443 Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }
,
444 Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), ref mut target }
,
447 *target
= method
.sig
.inputs()[0];