Skip to content

Commit

Permalink
Explain why &T is cloned when T is not Clone
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed Apr 5, 2022
1 parent 634770c commit d12689c
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 6 deletions.
2 changes: 1 addition & 1 deletion compiler/rustc_typeck/src/check/demand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty);
self.suggest_missing_parentheses(err, expr);
self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected);
self.note_type_is_not_clone(err, expected, expr_ty, expr);
self.note_need_for_fn_pointer(err, expected, expr_ty);
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
self.report_closure_inferred_return_type(err, expected);
Expand Down Expand Up @@ -630,7 +631,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Applicability::MachineApplicable,
true,
));

}
}
_ => {}
Expand Down
59 changes: 54 additions & 5 deletions compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ use super::FnCtxt;
use crate::astconv::AstConv;

use rustc_ast::util::parser::ExprPrecedence;
use rustc_span::{self, Span};

use rustc_errors::{Applicability, Diagnostic, MultiSpan};
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind};
Expand All @@ -13,12 +11,14 @@ use rustc_hir::{
WherePredicate,
};
use rustc_infer::infer::{self, TyCtxtInferExt};

use rustc_infer::traits;
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, Binder, Ty};
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{self, Binder, ToPredicate, Ty};
use rustc_span::symbol::{kw, sym};
use rustc_span::Span;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;

use rustc_middle::ty::subst::GenericArgKind;
use std::iter;

impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Expand Down Expand Up @@ -846,4 +846,53 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let node = self.tcx.hir().get(id);
matches!(node, Node::Stmt(Stmt { kind: StmtKind::Local(..), .. }))
}

/// Suggest that `&T` was cloned instead of `T` because `T` does not implement `Clone`,
/// which is a side-effect of autoref.
pub(crate) fn note_type_is_not_clone(
&self,
diag: &mut Diagnostic,
expected_ty: Ty<'tcx>,
found_ty: Ty<'tcx>,
expr: &hir::Expr<'_>,
) {
let hir::ExprKind::MethodCall(segment, &[ref callee_expr], _) = expr.kind else { return; };
let Some(clone_trait_did) = self.tcx.lang_items().clone_trait() else { return; };
let ty::Ref(_, pointee_ty, _) = found_ty.kind() else { return };
let results = self.typeck_results.borrow();
// First, look for a `Clone::clone` call
if segment.ident.name == sym::clone
&& results.type_dependent_def_id(expr.hir_id).map_or(
false,
|did| {
self.tcx.associated_item(did).container
== ty::AssocItemContainer::TraitContainer(clone_trait_did)
},
)
// If that clone call hasn't already dereferenced the self type (i.e. don't give this
// diagnostic in cases where we have `(&&T).clone()` and we expect `T`).
&& !results.expr_adjustments(callee_expr).iter().any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(..)))
// Check that we're in fact trying to clone into the expected type
&& self.can_coerce(*pointee_ty, expected_ty)
// And the expected type doesn't implement `Clone`
&& !self.predicate_must_hold_considering_regions(&traits::Obligation {
cause: traits::ObligationCause::dummy(),
param_env: self.param_env,
recursion_depth: 0,
predicate: ty::Binder::dummy(ty::TraitRef {
def_id: clone_trait_did,
substs: self.tcx.mk_substs([expected_ty.into()].iter()),
})
.without_const()
.to_predicate(self.tcx),
})
{
diag.span_note(
callee_expr.span,
&format!(
"`{expected_ty}` does not implement `Clone`, so `{found_ty}` was cloned instead"
),
);
}
}
}
13 changes: 13 additions & 0 deletions src/test/ui/typeck/explain_clone_autoref.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
struct NotClone;

fn main() {
clone_thing(&NotClone);
}

fn clone_thing(nc: &NotClone) -> NotClone {
//~^ NOTE expected `NotClone` because of return type
nc.clone()
//~^ ERROR mismatched type
//~| NOTE `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead
//~| NOTE expected struct `NotClone`, found `&NotClone`
}
18 changes: 18 additions & 0 deletions src/test/ui/typeck/explain_clone_autoref.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
error[E0308]: mismatched types
--> $DIR/explain_clone_autoref.rs:9:5
|
LL | fn clone_thing(nc: &NotClone) -> NotClone {
| -------- expected `NotClone` because of return type
LL |
LL | nc.clone()
| ^^^^^^^^^^ expected struct `NotClone`, found `&NotClone`
|
note: `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead
--> $DIR/explain_clone_autoref.rs:9:5
|
LL | nc.clone()
| ^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.

0 comments on commit d12689c

Please sign in to comment.