Skip to content

Make can_eq process obligations (almost) everywhere #127172

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am very confused by the previous control flow here:

if auto-deref steps returns None and a type still implemented the receiver trait, it was fine? That feels undesirable. receiver_is_implemented could also be converted to a commit_if_ok 🤔 not sure how that impacts diagnostics though

r=me with or without that change

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If autoderef returns None then it went into the else branch of the if-let and returned false.

It shouldn't affect diagnostics, since we're still registering the receiver trait, though I don't think it does much since it's a single root obligation

{
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 @@ -767,19 +767,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
Loading