Skip to content

Commit

Permalink
suggest_borrow_generic_arg: instantiate clauses properly
Browse files Browse the repository at this point in the history
Fixes issue 133118.
This also modifies `tests/ui/moves/moved-value-on-as-ref-arg.rs` to have more
useful bounds on the tests for suggestions to borrow `Borrow` and `BorrowMut`
arguments. With its old tautological `T: BorrowMut<T>` bound, this fix would
make it suggest a shared borrow for that argument.
  • Loading branch information
dianne committed Nov 18, 2024
1 parent 5afd5ad commit 546ba3d
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 20 deletions.
26 changes: 10 additions & 16 deletions compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use rustc_middle::mir::{
};
use rustc_middle::ty::print::PrintTraitRefExt as _;
use rustc_middle::ty::{
self, ClauseKind, PredicateKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor, Upcast,
self, PredicateKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor, Upcast,
suggest_constraining_type_params,
};
use rustc_middle::util::CallKind;
Expand Down Expand Up @@ -649,11 +649,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
) -> Option<ty::Mutability> {
let tcx = self.infcx.tcx;
let sig = tcx.fn_sig(callee_did).instantiate_identity().skip_binder();
let clauses = tcx.predicates_of(callee_did).instantiate_identity(self.infcx.tcx).predicates;
let clauses = tcx.predicates_of(callee_did);

// First, is there at least one method on one of `param`'s trait bounds?
// This keeps us from suggesting borrowing the argument to `mem::drop`, e.g.
if !clauses.iter().any(|clause| {
if !clauses.instantiate_identity(tcx).predicates.iter().any(|clause| {
clause.as_trait_clause().is_some_and(|tc| {
tc.self_ty().skip_binder().is_param(param.index)
&& tc.polarity() == ty::PredicatePolarity::Positive
Expand Down Expand Up @@ -700,23 +700,17 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
return false;
}

// Test the callee's predicates, substituting a reference in for the self ty
// in bounds on `param`.
clauses.iter().all(|&clause| {
let clause_for_ref = clause.kind().map_bound(|kind| match kind {
ClauseKind::Trait(c) if c.self_ty().is_param(param.index) => {
ClauseKind::Trait(c.with_self_ty(tcx, ref_ty))
}
ClauseKind::Projection(c) if c.self_ty().is_param(param.index) => {
ClauseKind::Projection(c.with_self_ty(tcx, ref_ty))
}
_ => kind,
});
// Test the callee's predicates, substituting in `ref_ty` for the moved argument type.
clauses.instantiate(tcx, new_args).predicates.iter().all(|&(mut clause)| {
// Normalize before testing to see through type aliases and projections.
if let Ok(normalized) = tcx.try_normalize_erasing_regions(self.param_env, clause) {
clause = normalized;
}
self.infcx.predicate_must_hold_modulo_regions(&Obligation::new(
tcx,
ObligationCause::dummy(),
self.param_env,
ty::EarlyBinder::bind(clause_for_ref).instantiate(tcx, generic_args),
clause,
))
})
}) {
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/moves/moved-value-on-as-ref-arg.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ impl AsMut<Bar> for Bar {

fn foo<T: AsRef<Bar>>(_: T) {}
fn qux<T: AsMut<Bar>>(_: T) {}
fn bat<T: Borrow<T>>(_: T) {}
fn baz<T: BorrowMut<T>>(_: T) {}
fn bat<T: Borrow<Bar>>(_: T) {}
fn baz<T: BorrowMut<Bar>>(_: T) {}

pub fn main() {
let bar = Bar;
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/moves/moved-value-on-as-ref-arg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ impl AsMut<Bar> for Bar {

fn foo<T: AsRef<Bar>>(_: T) {}
fn qux<T: AsMut<Bar>>(_: T) {}
fn bat<T: Borrow<T>>(_: T) {}
fn baz<T: BorrowMut<T>>(_: T) {}
fn bat<T: Borrow<Bar>>(_: T) {}
fn baz<T: BorrowMut<Bar>>(_: T) {}

pub fn main() {
let bar = Bar;
Expand Down
25 changes: 25 additions & 0 deletions tests/ui/moves/region-var-in-moved-ty-issue-133118.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//! regression test for #133118
pub trait Alpha {
fn y(self) -> usize;
}

pub trait Beta {
type Gamma;
fn gamma(&self) -> Self::Gamma;
}

pub fn a<T: Alpha>(_x: T) -> usize {
todo!();
}

pub fn x<B>(beta: &B) -> usize
where
for<'a> &'a B: Beta,
for<'a> <&'a B as Beta>::Gamma: Alpha,
{
let g1 = beta.gamma();
a(g1) + a(g1) //~ ERROR use of moved value: `g1` [E0382]
}

pub fn main() {}
21 changes: 21 additions & 0 deletions tests/ui/moves/region-var-in-moved-ty-issue-133118.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
error[E0382]: use of moved value: `g1`
--> $DIR/region-var-in-moved-ty-issue-133118.rs:22:15
|
LL | let g1 = beta.gamma();
| -- move occurs because `g1` has type `<&B as Beta>::Gamma`, which does not implement the `Copy` trait
LL | a(g1) + a(g1)
| -- ^^ value used here after move
| |
| value moved here
|
note: consider changing this parameter type in function `a` to borrow instead if owning the value isn't necessary
--> $DIR/region-var-in-moved-ty-issue-133118.rs:12:24
|
LL | pub fn a<T: Alpha>(_x: T) -> usize {
| - ^ this parameter takes ownership of the value
| |
| in this function

error: aborting due to 1 previous error

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

0 comments on commit 546ba3d

Please sign in to comment.