Auto merge of #127172 - compiler-errors:full-can_eq-everywhere, r=lcnr

Make `can_eq` process obligations (almost) everywhere

Move `can_eq` to an extension trait on `InferCtxt` in `rustc_trait_selection`, and change it so that it processes obligations. This should strengthen it to be more accurate in some cases, but is most important for the new trait solver which delays relating aliases to `AliasRelate` goals. Without this, we always basically just return true when passing aliases to `can_eq`, which can lead to weird errors, for example #127149.

I'm not actually certain if we should *have* `can_eq` be called on the good path. In cases where we need `can_eq`, we probably should just be using a regular probe.

Fixes #127149

r? lcnr
This commit is contained in:
bors 2024-07-07 23:03:48 +00:00
commit 89aefb9c53
30 changed files with 133 additions and 154 deletions

View File

@ -21,6 +21,7 @@ use rustc_middle::ty::{
use rustc_middle::ty::{GenericParamDefKind, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_span::Span;
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::regions::InferCtxtRegionExt;
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;

View File

@ -39,6 +39,7 @@ use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _
use rustc_trait_selection::traits::{
self, FulfillmentError, ObligationCause, ObligationCauseCode, ObligationCtxt, WellFormedLoc,
};
use rustc_type_ir::solve::NoSolution;
use rustc_type_ir::TypeFlags;
use std::cell::LazyCell;
@ -1712,13 +1713,12 @@ fn receiver_is_valid<'tcx>(
let cause =
ObligationCause::new(span, wfcx.body_def_id, traits::ObligationCauseCode::MethodReceiver);
let can_eq_self = |ty| infcx.can_eq(wfcx.param_env, self_ty, ty);
// `self: Self` is always valid.
if can_eq_self(receiver_ty) {
if let Err(err) = wfcx.eq(&cause, wfcx.param_env, self_ty, receiver_ty) {
infcx.err_ctxt().report_mismatched_types(&cause, self_ty, receiver_ty, err).emit();
}
// Special case `receiver == self_ty`, which doesn't necessarily require the `Receiver` lang item.
if let Ok(()) = wfcx.infcx.commit_if_ok(|_| {
let ocx = ObligationCtxt::new(wfcx.infcx);
ocx.eq(&cause, wfcx.param_env, self_ty, receiver_ty)?;
if ocx.select_all_or_error().is_empty() { Ok(()) } else { Err(NoSolution) }
}) {
return true;
}
@ -1729,58 +1729,51 @@ fn receiver_is_valid<'tcx>(
autoderef = autoderef.include_raw_pointers();
}
// The first type is `receiver_ty`, which we know its not equal to `self_ty`; skip it.
autoderef.next();
let receiver_trait_def_id = tcx.require_lang_item(LangItem::Receiver, Some(span));
// Keep dereferencing `receiver_ty` until we get to `self_ty`.
loop {
if let Some((potential_self_ty, _)) = autoderef.next() {
debug!(
"receiver_is_valid: potential self type `{:?}` to match `{:?}`",
potential_self_ty, self_ty
);
while let Some((potential_self_ty, _)) = autoderef.next() {
debug!(
"receiver_is_valid: potential self type `{:?}` to match `{:?}`",
potential_self_ty, self_ty
);
if can_eq_self(potential_self_ty) {
wfcx.register_obligations(autoderef.into_obligations());
if let Err(err) = wfcx.eq(&cause, wfcx.param_env, self_ty, potential_self_ty) {
infcx
.err_ctxt()
.report_mismatched_types(&cause, self_ty, potential_self_ty, err)
.emit();
}
// Check if the self type unifies. If it does, then commit the result
// since it may have region side-effects.
if let Ok(()) = wfcx.infcx.commit_if_ok(|_| {
let ocx = ObligationCtxt::new(wfcx.infcx);
ocx.eq(&cause, wfcx.param_env, self_ty, potential_self_ty)?;
if ocx.select_all_or_error().is_empty() { Ok(()) } else { Err(NoSolution) }
}) {
wfcx.register_obligations(autoderef.into_obligations());
return true;
}
// Without `feature(arbitrary_self_types)`, we require that each step in the
// deref chain implement `receiver`.
if !arbitrary_self_types_enabled {
if !receiver_is_implemented(
wfcx,
receiver_trait_def_id,
cause.clone(),
potential_self_ty,
) {
// We cannot proceed.
break;
} else {
// Without `feature(arbitrary_self_types)`, we require that each step in the
// deref chain implement `receiver`
if !arbitrary_self_types_enabled
&& !receiver_is_implemented(
wfcx,
receiver_trait_def_id,
cause.clone(),
potential_self_ty,
)
{
return false;
}
}
} else {
debug!("receiver_is_valid: type `{:?}` does not deref to `{:?}`", receiver_ty, self_ty);
return false;
// Register the bound, in case it has any region side-effects.
wfcx.register_bound(
cause.clone(),
wfcx.param_env,
potential_self_ty,
receiver_trait_def_id,
);
}
}
// Without `feature(arbitrary_self_types)`, we require that `receiver_ty` implements `Receiver`.
if !arbitrary_self_types_enabled
&& !receiver_is_implemented(wfcx, receiver_trait_def_id, cause.clone(), receiver_ty)
{
return false;
}
true
debug!("receiver_is_valid: type `{:?}` does not deref to `{:?}`", receiver_ty, self_ty);
false
}
fn receiver_is_implemented<'tcx>(

View File

@ -49,6 +49,7 @@ use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::symbol::{kw, Ident, Symbol};
use rustc_span::{sym, Span, DUMMY_SP};
use rustc_target::spec::abi;
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::wf::object_region_bounds;
use rustc_trait_selection::traits::{self, ObligationCtxt};

View File

@ -13,6 +13,7 @@ use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, AssocItem, Ty, TypeFoldable, TypeVisitableExt};
use rustc_span::symbol::sym;
use rustc_span::{Span, DUMMY_SP};
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::ObligationCause;
use super::method::probe;

View File

@ -40,6 +40,7 @@ use rustc_middle::{bug, span_bug};
use rustc_session::Session;
use rustc_span::symbol::{kw, Ident};
use rustc_span::{sym, BytePos, Span, DUMMY_SP};
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext};
use std::iter;

View File

@ -2582,7 +2582,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
(hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr), _, &ty::Ref(_, checked, _))
if self.can_sub(self.param_env, checked, expected) =>
if self.can_eq(self.param_env, checked, expected) =>
{
let make_sugg = |start: Span, end: BytePos| {
// skip `(` for tuples such as `(c) = (&123)`.

View File

@ -33,6 +33,7 @@ use rustc_span::edit_distance::{
};
use rustc_span::symbol::sym;
use rustc_span::{symbol::Ident, Span, Symbol, DUMMY_SP};
use rustc_trait_selection::infer::InferCtxtExt as _;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
use rustc_trait_selection::traits::query::method_autoderef::MethodAutoderefBadTy;
use rustc_trait_selection::traits::query::method_autoderef::{
@ -857,7 +858,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
let args = self.fresh_args_for_item(self.span, method.def_id);
let fty = self.tcx.fn_sig(method.def_id).instantiate(self.tcx, args);
let fty = self.instantiate_binder_with_fresh_vars(self.span, infer::FnCall, fty);
self.can_sub(self.param_env, fty.output(), expected)
self.can_eq(self.param_env, fty.output(), expected)
}),
_ => false,
}

View File

@ -19,6 +19,7 @@ use rustc_span::source_map::Spanned;
use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::{BytePos, Span, DUMMY_SP};
use rustc_target::abi::FieldIdx;
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode};
use ty::VariantDef;

View File

@ -820,7 +820,7 @@ fn foo(&self) -> Self::T { String::new() }
tcx.defaultness(item.id.owner_id)
{
let assoc_ty = tcx.type_of(item.id.owner_id).instantiate_identity();
if self.infcx.can_eq(param_env, assoc_ty, found) {
if self.infcx.can_eq_shallow(param_env, assoc_ty, found) {
diag.span_label(
item.span,
"associated type defaults can't be assumed inside the \
@ -843,7 +843,7 @@ fn foo(&self) -> Self::T { String::new() }
let assoc_ty = tcx.type_of(item.id.owner_id).instantiate_identity();
if let hir::Defaultness::Default { has_value: true } =
tcx.defaultness(item.id.owner_id)
&& self.infcx.can_eq(param_env, assoc_ty, found)
&& self.infcx.can_eq_shallow(param_env, assoc_ty, found)
{
diag.span_label(
item.span,

View File

@ -768,19 +768,9 @@ impl<'tcx> InferCtxt<'tcx> {
.collect()
}
pub fn can_sub<T>(&self, param_env: ty::ParamEnv<'tcx>, expected: T, actual: T) -> bool
where
T: at::ToTrace<'tcx>,
{
let origin = &ObligationCause::dummy();
self.probe(|_| {
// We're only answering whether there could be a subtyping relation, and with
// opaque types, "there could be one", via registering a hidden type.
self.at(origin, param_env).sub(DefineOpaqueTypes::Yes, expected, actual).is_ok()
})
}
pub fn can_eq<T>(&self, param_env: ty::ParamEnv<'tcx>, a: T, b: T) -> bool
// FIXME(-Znext-solver): Get rid of this method, it's never correct. Either that,
// or we need to process the obligations.
pub fn can_eq_shallow<T>(&self, param_env: ty::ParamEnv<'tcx>, a: T, b: T) -> bool
where
T: at::ToTrace<'tcx>,
{

View File

@ -1,3 +1,4 @@
use crate::infer::at::ToTrace;
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
use crate::traits::{self, Obligation, ObligationCause, ObligationCtxt, SelectionContext};
@ -17,6 +18,16 @@ pub use rustc_infer::infer::*;
#[extension(pub trait InferCtxtExt<'tcx>)]
impl<'tcx> InferCtxt<'tcx> {
fn can_eq<T: ToTrace<'tcx>>(&self, param_env: ty::ParamEnv<'tcx>, a: T, b: T) -> bool {
self.probe(|_| {
let ocx = ObligationCtxt::new(self);
let Ok(()) = ocx.eq(&ObligationCause::dummy(), param_env, a, b) else {
return false;
};
ocx.select_where_possible().is_empty()
})
}
fn type_is_copy_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
let ty = self.resolve_vars_if_possible(ty);

View File

@ -1,5 +1,10 @@
use super::{ObligationCauseCode, PredicateObligation};
use crate::errors::{
EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented,
};
use crate::infer::error_reporting::TypeErrCtxt;
use crate::infer::InferCtxtExt;
use crate::traits::error_reporting::type_err_ctxt_ext::InferCtxtPrivExt;
use rustc_ast::AttrArgs;
use rustc_ast::AttrArgsEq;
use rustc_ast::AttrKind;
@ -21,12 +26,6 @@ use rustc_span::Span;
use std::iter;
use std::path::PathBuf;
use crate::errors::{
EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented,
};
use crate::traits::error_reporting::type_err_ctxt_ext::InferCtxtPrivExt;
/// The symbols which are always allowed in a format string
static ALLOWED_FORMAT_SYMBOLS: &[Symbol] = &[
kw::SelfUpper,

View File

@ -1073,7 +1073,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
// mismatched, then we have a totally different error to report.
if self.enter_forall(found_args, |found_args| {
self.enter_forall(expected_args, |expected_args| {
!self.can_sub(obligation.param_env, expected_args, found_args)
!self.can_eq(obligation.param_env, expected_args, found_args)
})
}) {
return None;

View File

@ -18,7 +18,7 @@ use super::{
TraitQueryMode,
};
use crate::infer::{InferCtxt, InferOk, TypeFreshener};
use crate::infer::{InferCtxt, InferCtxtExt, InferOk, TypeFreshener};
use crate::solve::InferCtxtSelectExt as _;
use crate::traits::error_reporting::TypeErrCtxtExt;
use crate::traits::normalize::normalize_with_depth;

View File

@ -1,4 +1,4 @@
error[E0277]: the trait bound `i32: Baz<Self>` is not satisfied
error[E0277]: the trait bound `<Self as Foo>::Bar<()>: Eq<i32>` is not satisfied
--> $DIR/assume-gat-normalization-for-nested-goals.rs:9:30
|
LL | type Bar<T>: Baz<Self> = i32;

View File

@ -5,7 +5,7 @@ fn fine(x: impl Into<u32>) -> impl Into<u32> { x }
fn bad_in_ret_position(x: impl Into<u32>) -> impl Into<impl Debug> { x }
//~^ ERROR nested `impl Trait` is not allowed
//~| ERROR the trait bound `impl Into<u32>: Into<impl Debug>` is not satisfied
//~| ERROR the trait bound `impl Debug: From<impl Into<u32>>` is not satisfied
fn bad_in_fn_syntax(x: fn() -> impl Into<impl Debug>) {}
//~^ ERROR nested `impl Trait` is not allowed
@ -18,7 +18,7 @@ struct X;
impl X {
fn bad(x: impl Into<u32>) -> impl Into<impl Debug> { x }
//~^ ERROR nested `impl Trait` is not allowed
//~| ERROR the trait bound `impl Into<u32>: Into<impl Debug>` is not satisfied
//~| ERROR the trait bound `impl Debug: From<impl Into<u32>>` is not satisfied
}
fn allowed_in_assoc_type() -> impl Iterator<Item=impl Fn()> {

View File

@ -42,7 +42,7 @@ LL | fn bad_in_fn_syntax(x: fn() -> impl Into<impl Debug>) {}
|
= note: `impl Trait` is only allowed in arguments and return types of functions and methods
error[E0277]: the trait bound `impl Into<u32>: Into<impl Debug>` is not satisfied
error[E0277]: the trait bound `impl Debug: From<impl Into<u32>>` is not satisfied
--> $DIR/nested_impl_trait.rs:6:46
|
LL | fn bad_in_ret_position(x: impl Into<u32>) -> impl Into<impl Debug> { x }
@ -51,7 +51,7 @@ LL | fn bad_in_ret_position(x: impl Into<u32>) -> impl Into<impl Debug> { x }
= help: the trait `Into<U>` is implemented for `T`
= note: required for `impl Into<u32>` to implement `Into<impl Debug>`
error[E0277]: the trait bound `impl Into<u32>: Into<impl Debug>` is not satisfied
error[E0277]: the trait bound `impl Debug: From<impl Into<u32>>` is not satisfied
--> $DIR/nested_impl_trait.rs:19:34
|
LL | fn bad(x: impl Into<u32>) -> impl Into<impl Debug> { x }

View File

@ -1,12 +0,0 @@
error[E0271]: type mismatch resolving `<() as Index>::Output == &mut <() as Index>::Output`
--> $DIR/issue-100222.rs:34:12
|
LL | fn foo(&mut self, x: <Self as Index>::Output) -> <Self as Index>::Output
| ^^^^^^^^^ expected `()`, found `&mut <() as Index>::Output`
|
= note: expected unit type `()`
found mutable reference `&mut <() as Index>::Output`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0271`.

View File

@ -1,12 +0,0 @@
error[E0271]: type mismatch resolving `<() as Index>::Output == &mut <() as Index>::Output`
--> $DIR/issue-100222.rs:25:12
|
LL | fn foo(&mut self, x: <Self as Index>::Output) -> <Self as Index>::Output
| ^^^^^^^^^ expected `()`, found `&mut <() as Index>::Output`
|
= note: expected unit type `()`
found mutable reference `&mut <() as Index>::Output`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0271`.

View File

@ -1,6 +1,7 @@
//@ revisions: nn ny yn yy
//@ known-bug: #110395
//@ compile-flags: -Znext-solver
//@ check-pass
#![allow(incomplete_features)]
#![feature(const_trait_impl, effects, associated_type_defaults, const_mut_refs)]

View File

@ -1,12 +0,0 @@
error[E0271]: type mismatch resolving `<() as Index>::Output == &mut <() as Index>::Output`
--> $DIR/issue-100222.rs:34:12
|
LL | fn foo(&mut self, x: <Self as Index>::Output) -> <Self as Index>::Output
| ^^^^^^^^^ expected `()`, found `&mut <() as Index>::Output`
|
= note: expected unit type `()`
found mutable reference `&mut <() as Index>::Output`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0271`.

View File

@ -1,12 +0,0 @@
error[E0271]: type mismatch resolving `<() as Index>::Output == &mut <() as Index>::Output`
--> $DIR/issue-100222.rs:25:12
|
LL | fn foo(&mut self, x: <Self as Index>::Output) -> <Self as Index>::Output
| ^^^^^^^^^ expected `()`, found `&mut <() as Index>::Output`
|
= note: expected unit type `()`
found mutable reference `&mut <() as Index>::Output`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0271`.

View File

@ -17,10 +17,6 @@ note: required by a bound in `is_send`
|
LL | fn is_send(_: impl Send) {}
| ^^^^ required by this bound in `is_send`
help: consider dereferencing here
|
LL | is_send(*foo());
| +
error: aborting due to 1 previous error

View File

@ -0,0 +1,23 @@
//@ compile-flags: -Znext-solver
//@ check-pass
// Fixes a regression in `receiver_is_valid` in wfcheck where we were using
// `InferCtxt::can_eq` instead of processing alias-relate goals, leading to false
// positives, not deref'ing enough steps to check the receiver is valid.
trait Mirror {
type Mirror: ?Sized;
}
impl<T: ?Sized> Mirror for T {
type Mirror = T;
}
trait Foo {
fn foo(&self) {}
}
impl Foo for <() as Mirror>::Mirror {
fn foo(&self) {}
}
fn main() {}

View File

@ -8,7 +8,7 @@ LL | fn bar(self: Bar<u32>) {
= help: consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one of the previous types except `Self`)
error[E0307]: invalid `self` parameter type: `&Bar<u32>`
--> $DIR/method_resolution3.rs:21:18
--> $DIR/method_resolution3.rs:20:18
|
LL | fn baz(self: &Bar<u32>) {
| ^^^^^^^^^

View File

@ -1,15 +1,21 @@
error[E0271]: type mismatch resolving `Foo == u32`
error[E0307]: invalid `self` parameter type: `Bar<u32>`
--> $DIR/method_resolution3.rs:16:18
|
LL | fn bar(self: Bar<u32>) {
| ^^^^^^^^ types differ
| ^^^^^^^^
|
= note: type of `self` must be `Self` or a type that dereferences to it
= help: consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one of the previous types except `Self`)
error[E0271]: type mismatch resolving `Foo == u32`
--> $DIR/method_resolution3.rs:21:18
error[E0307]: invalid `self` parameter type: `&Bar<u32>`
--> $DIR/method_resolution3.rs:20:18
|
LL | fn baz(self: &Bar<u32>) {
| ^^^^^^^^^ types differ
| ^^^^^^^^^
|
= note: type of `self` must be `Self` or a type that dereferences to it
= help: consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one of the previous types except `Self`)
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0271`.
For more information about this error, try `rustc --explain E0307`.

View File

@ -14,13 +14,11 @@ struct Bar<T>(T);
impl Bar<Foo> {
fn bar(self: Bar<u32>) {
//[current]~^ ERROR: invalid `self` parameter
//[next]~^^ ERROR: type mismatch resolving `Foo == u32`
//~^ ERROR: invalid `self` parameter
self.foo()
}
fn baz(self: &Bar<u32>) {
//[current]~^ ERROR: invalid `self` parameter
//[next]~^^ ERROR: type mismatch resolving `Foo == u32`
//~^ ERROR: invalid `self` parameter
self.foo()
}
}

View File

@ -8,7 +8,7 @@ LL | fn foo(self: Bar<Foo>) {
= help: consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one of the previous types except `Self`)
error[E0307]: invalid `self` parameter type: `&Bar<Foo>`
--> $DIR/method_resolution4.rs:32:20
--> $DIR/method_resolution4.rs:31:20
|
LL | fn foomp(self: &Bar<Foo>) {
| ^^^^^^^^^

View File

@ -1,15 +1,21 @@
error[E0271]: type mismatch resolving `u32 == Foo`
error[E0307]: invalid `self` parameter type: `Bar<Foo>`
--> $DIR/method_resolution4.rs:27:18
|
LL | fn foo(self: Bar<Foo>) {
| ^^^^^^^^ types differ
| ^^^^^^^^
|
= note: type of `self` must be `Self` or a type that dereferences to it
= help: consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one of the previous types except `Self`)
error[E0271]: type mismatch resolving `u32 == Foo`
--> $DIR/method_resolution4.rs:32:20
error[E0307]: invalid `self` parameter type: `&Bar<Foo>`
--> $DIR/method_resolution4.rs:31:20
|
LL | fn foomp(self: &Bar<Foo>) {
| ^^^^^^^^^ types differ
| ^^^^^^^^^
|
= note: type of `self` must be `Self` or a type that dereferences to it
= help: consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one of the previous types except `Self`)
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0271`.
For more information about this error, try `rustc --explain E0307`.

View File

@ -25,13 +25,11 @@ impl Bar<Foo> {
impl Bar<u32> {
fn foo(self: Bar<Foo>) {
//[current]~^ ERROR: invalid `self` parameter
//[next]~^^ ERROR: type mismatch resolving `u32 == Foo`
//~^ ERROR: invalid `self` parameter
self.bar()
}
fn foomp(self: &Bar<Foo>) {
//[current]~^ ERROR: invalid `self` parameter
//[next]~^^ ERROR: type mismatch resolving `u32 == Foo`
//~^ ERROR: invalid `self` parameter
self.bar()
}
}