Skip to content

Commit 42ab448

Browse files
authored
Rollup merge of #95585 - compiler-errors:ref-clone, r=estebank
Explain why `&T` is cloned when `T` is not `Clone` Fixes #95535
2 parents e597d06 + d12689c commit 42ab448

File tree

4 files changed

+86
-6
lines changed

4 files changed

+86
-6
lines changed

compiler/rustc_typeck/src/check/demand.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
4040
self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty);
4141
self.suggest_missing_parentheses(err, expr);
4242
self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected);
43+
self.note_type_is_not_clone(err, expected, expr_ty, expr);
4344
self.note_need_for_fn_pointer(err, expected, expr_ty);
4445
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
4546
self.report_closure_inferred_return_type(err, expected);
@@ -630,7 +631,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
630631
Applicability::MachineApplicable,
631632
true,
632633
));
633-
634634
}
635635
}
636636
_ => {}

compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs

+54-5
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ use super::FnCtxt;
22
use crate::astconv::AstConv;
33

44
use rustc_ast::util::parser::ExprPrecedence;
5-
use rustc_span::{self, Span};
6-
75
use rustc_errors::{Applicability, Diagnostic, MultiSpan};
86
use rustc_hir as hir;
97
use rustc_hir::def::{CtorOf, DefKind};
@@ -13,12 +11,14 @@ use rustc_hir::{
1311
WherePredicate,
1412
};
1513
use rustc_infer::infer::{self, TyCtxtInferExt};
16-
14+
use rustc_infer::traits;
1715
use rustc_middle::lint::in_external_macro;
18-
use rustc_middle::ty::{self, Binder, Ty};
16+
use rustc_middle::ty::subst::GenericArgKind;
17+
use rustc_middle::ty::{self, Binder, ToPredicate, Ty};
1918
use rustc_span::symbol::{kw, sym};
19+
use rustc_span::Span;
20+
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
2021

21-
use rustc_middle::ty::subst::GenericArgKind;
2222
use std::iter;
2323

2424
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -846,4 +846,53 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
846846
let node = self.tcx.hir().get(id);
847847
matches!(node, Node::Stmt(Stmt { kind: StmtKind::Local(..), .. }))
848848
}
849+
850+
/// Suggest that `&T` was cloned instead of `T` because `T` does not implement `Clone`,
851+
/// which is a side-effect of autoref.
852+
pub(crate) fn note_type_is_not_clone(
853+
&self,
854+
diag: &mut Diagnostic,
855+
expected_ty: Ty<'tcx>,
856+
found_ty: Ty<'tcx>,
857+
expr: &hir::Expr<'_>,
858+
) {
859+
let hir::ExprKind::MethodCall(segment, &[ref callee_expr], _) = expr.kind else { return; };
860+
let Some(clone_trait_did) = self.tcx.lang_items().clone_trait() else { return; };
861+
let ty::Ref(_, pointee_ty, _) = found_ty.kind() else { return };
862+
let results = self.typeck_results.borrow();
863+
// First, look for a `Clone::clone` call
864+
if segment.ident.name == sym::clone
865+
&& results.type_dependent_def_id(expr.hir_id).map_or(
866+
false,
867+
|did| {
868+
self.tcx.associated_item(did).container
869+
== ty::AssocItemContainer::TraitContainer(clone_trait_did)
870+
},
871+
)
872+
// If that clone call hasn't already dereferenced the self type (i.e. don't give this
873+
// diagnostic in cases where we have `(&&T).clone()` and we expect `T`).
874+
&& !results.expr_adjustments(callee_expr).iter().any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(..)))
875+
// Check that we're in fact trying to clone into the expected type
876+
&& self.can_coerce(*pointee_ty, expected_ty)
877+
// And the expected type doesn't implement `Clone`
878+
&& !self.predicate_must_hold_considering_regions(&traits::Obligation {
879+
cause: traits::ObligationCause::dummy(),
880+
param_env: self.param_env,
881+
recursion_depth: 0,
882+
predicate: ty::Binder::dummy(ty::TraitRef {
883+
def_id: clone_trait_did,
884+
substs: self.tcx.mk_substs([expected_ty.into()].iter()),
885+
})
886+
.without_const()
887+
.to_predicate(self.tcx),
888+
})
889+
{
890+
diag.span_note(
891+
callee_expr.span,
892+
&format!(
893+
"`{expected_ty}` does not implement `Clone`, so `{found_ty}` was cloned instead"
894+
),
895+
);
896+
}
897+
}
849898
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
struct NotClone;
2+
3+
fn main() {
4+
clone_thing(&NotClone);
5+
}
6+
7+
fn clone_thing(nc: &NotClone) -> NotClone {
8+
//~^ NOTE expected `NotClone` because of return type
9+
nc.clone()
10+
//~^ ERROR mismatched type
11+
//~| NOTE `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead
12+
//~| NOTE expected struct `NotClone`, found `&NotClone`
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/explain_clone_autoref.rs:9:5
3+
|
4+
LL | fn clone_thing(nc: &NotClone) -> NotClone {
5+
| -------- expected `NotClone` because of return type
6+
LL |
7+
LL | nc.clone()
8+
| ^^^^^^^^^^ expected struct `NotClone`, found `&NotClone`
9+
|
10+
note: `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead
11+
--> $DIR/explain_clone_autoref.rs:9:5
12+
|
13+
LL | nc.clone()
14+
| ^^
15+
16+
error: aborting due to previous error
17+
18+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)