Skip to content

Commit

Permalink
Auto merge of #127172 - compiler-errors:full-can_eq-everywhere, r=lcnr
Browse files Browse the repository at this point in the history
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
  • Loading branch information
bors committed Jul 7, 2024
2 parents 20ae37c + 465e7d5 commit 89aefb9
Show file tree
Hide file tree
Showing 30 changed files with 133 additions and 154 deletions.
1 change: 1 addition & 0 deletions compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 _;
Expand Down
91 changes: 42 additions & 49 deletions compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand All @@ -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
);

if can_eq_self(potential_self_ty) {
wfcx.register_obligations(autoderef.into_obligations());
while let Some((potential_self_ty, _)) = autoderef.next() {
debug!(
"receiver_is_valid: potential self type `{:?}` to match `{:?}`",
potential_self_ty, self_ty
);

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;
}
}

// 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;
// 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,
);
}
}

true
debug!("receiver_is_valid: type `{:?}` does not deref to `{:?}`", receiver_ty, self_ty);
false
}

fn receiver_is_implemented<'tcx>(
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir_typeck/src/demand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)`.
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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,
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir_typeck/src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand All @@ -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,
Expand Down
16 changes: 3 additions & 13 deletions compiler/rustc_infer/src/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
{
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_trait_selection/src/infer.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand All @@ -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);

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_trait_selection/src/traits/select/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/impl-trait/nested_impl_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()> {
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/impl-trait/nested_impl_trait.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand All @@ -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 }
Expand Down
12 changes: 0 additions & 12 deletions tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.nn.stderr

This file was deleted.

12 changes: 0 additions & 12 deletions tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.ny.stderr

This file was deleted.

3 changes: 2 additions & 1 deletion tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.rs
Original file line number Diff line number Diff line change
@@ -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)]

Expand Down
12 changes: 0 additions & 12 deletions tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.yn.stderr

This file was deleted.

12 changes: 0 additions & 12 deletions tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.yy.stderr

This file was deleted.

Loading

0 comments on commit 89aefb9

Please sign in to comment.