-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Make InferCtxtExt::could_impl_trait
more precise, less ICEy
#119934
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,14 @@ | ||
use crate::solve::FulfillmentCtxt; | ||
use crate::traits::query::evaluate_obligation::InferCtxtExt as _; | ||
use crate::traits::{self, DefiningAnchor, ObligationCtxt}; | ||
use crate::traits::{self, DefiningAnchor, ObligationCtxt, SelectionContext}; | ||
|
||
use crate::traits::TraitEngineExt as _; | ||
use rustc_hir::def_id::DefId; | ||
use rustc_hir::lang_items::LangItem; | ||
use rustc_infer::traits::{TraitEngine, TraitEngineExt}; | ||
use rustc_infer::traits::{Obligation, TraitEngine, TraitEngineExt as _}; | ||
use rustc_middle::arena::ArenaAllocatable; | ||
use rustc_middle::infer::canonical::{Canonical, CanonicalQueryResponse, QueryResponse}; | ||
use rustc_middle::traits::query::NoSolution; | ||
use rustc_middle::traits::ObligationCause; | ||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; | ||
use rustc_middle::ty::{GenericArg, ToPredicate}; | ||
use rustc_span::DUMMY_SP; | ||
|
@@ -21,24 +22,36 @@ pub trait InferCtxtExt<'tcx> { | |
|
||
fn type_is_sized_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool; | ||
|
||
/// Check whether a `ty` implements given trait(trait_def_id). | ||
/// Check whether a `ty` implements given trait(trait_def_id) without side-effects. | ||
/// | ||
/// The inputs are: | ||
/// | ||
/// - the def-id of the trait | ||
/// - the type parameters of the trait, including the self-type | ||
/// - the parameter environment | ||
/// | ||
/// Invokes `evaluate_obligation`, so in the event that evaluating | ||
/// `Ty: Trait` causes overflow, EvaluatedToErrStackDependent (or EvaluatedToAmbigStackDependent) | ||
/// will be returned. | ||
/// `Ty: Trait` causes overflow, EvaluatedToErrStackDependent | ||
/// (or EvaluatedToAmbigStackDependent) will be returned. | ||
fn type_implements_trait( | ||
&self, | ||
trait_def_id: DefId, | ||
params: impl IntoIterator<Item: Into<GenericArg<'tcx>>>, | ||
param_env: ty::ParamEnv<'tcx>, | ||
) -> traits::EvaluationResult; | ||
|
||
fn could_impl_trait( | ||
/// Returns `Some` if a type implements a trait shallowly, without side-effects, | ||
/// along with any errors that would have been reported upon further obligation | ||
/// processing. | ||
/// | ||
/// - If this returns `Some([])`, then the trait holds modulo regions. | ||
/// - If this returns `Some([errors..])`, then the trait has an impl for | ||
/// the self type, but some nested obligations do not hold. | ||
/// - If this returns `None`, no implementation that applies could be found. | ||
/// | ||
/// FIXME(-Znext-solver): Due to the recursive nature of the new solver, | ||
/// this will probably only ever return `Some([])` or `None`. | ||
fn type_implements_trait_shallow( | ||
&self, | ||
trait_def_id: DefId, | ||
ty: Ty<'tcx>, | ||
|
@@ -86,64 +99,26 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { | |
self.evaluate_obligation(&obligation).unwrap_or(traits::EvaluationResult::EvaluatedToErr) | ||
} | ||
|
||
fn could_impl_trait( | ||
fn type_implements_trait_shallow( | ||
&self, | ||
trait_def_id: DefId, | ||
ty: Ty<'tcx>, | ||
param_env: ty::ParamEnv<'tcx>, | ||
) -> Option<Vec<traits::FulfillmentError<'tcx>>> { | ||
self.probe(|_snapshot| { | ||
if let ty::Adt(def, args) = ty.kind() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this name "could_impl_trait" if it was specific to ADTs? No comment too! :( |
||
&& let Some((impl_def_id, _)) = self | ||
.tcx | ||
.all_impls(trait_def_id) | ||
.filter_map(|impl_def_id| { | ||
self.tcx.impl_trait_ref(impl_def_id).map(|r| (impl_def_id, r)) | ||
}) | ||
.map(|(impl_def_id, imp)| (impl_def_id, imp.skip_binder())) | ||
.find(|(_, imp)| match imp.self_ty().peel_refs().kind() { | ||
ty::Adt(i_def, _) if i_def.did() == def.did() => true, | ||
_ => false, | ||
}) | ||
{ | ||
let mut fulfill_cx = FulfillmentCtxt::new(self); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why were we using the new trait solver!? |
||
// We get all obligations from the impl to talk about specific | ||
// trait bounds. | ||
let obligations = self | ||
.tcx | ||
.predicates_of(impl_def_id) | ||
.instantiate(self.tcx, args) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here is the cause of the ICE -- we were trying to substitute an ADT's args into an impl's predicates. There's no guarantee that these generics line up. |
||
.into_iter() | ||
.map(|(clause, span)| { | ||
traits::Obligation::new( | ||
self.tcx, | ||
traits::ObligationCause::dummy_with_span(span), | ||
param_env, | ||
clause, | ||
) | ||
}) | ||
.collect::<Vec<_>>(); | ||
fulfill_cx.register_predicate_obligations(self, obligations); | ||
let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, [ty]); | ||
let obligation = traits::Obligation::new( | ||
self.tcx, | ||
traits::ObligationCause::dummy(), | ||
param_env, | ||
trait_ref, | ||
); | ||
fulfill_cx.register_predicate_obligation(self, obligation); | ||
let mut errors = fulfill_cx.select_all_or_error(self); | ||
// We remove the last predicate failure, which corresponds to | ||
// the top-level obligation, because most of the type we only | ||
// care about the other ones, *except* when it is the only one. | ||
// This seems to only be relevant for arbitrary self-types. | ||
// Look at `tests/ui/moves/move-fn-self-receiver.rs`. | ||
if errors.len() > 1 { | ||
errors.truncate(errors.len() - 1); | ||
let mut selcx = SelectionContext::new(self); | ||
match selcx.select(&Obligation::new( | ||
self.tcx, | ||
ObligationCause::dummy(), | ||
param_env, | ||
ty::TraitRef::new(self.tcx, trait_def_id, [ty]), | ||
)) { | ||
Ok(Some(selection)) => { | ||
let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(self); | ||
fulfill_cx.register_predicate_obligations(self, selection.nested_obligations()); | ||
Some(fulfill_cx.select_all_or_error(self)) | ||
} | ||
Some(errors) | ||
} else { | ||
None | ||
Ok(None) | Err(_) => None, | ||
} | ||
}) | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
use std::marker::PhantomData; | ||
|
||
struct Example<E, FakeParam>(PhantomData<(fn(E), fn(FakeParam))>); | ||
|
||
struct NoLifetime; | ||
struct Immutable<'a>(PhantomData<&'a ()>); | ||
|
||
impl<'a, E: 'a> Copy for Example<E, Immutable<'a>> {} | ||
impl<'a, E: 'a> Clone for Example<E, Immutable<'a>> { | ||
fn clone(&self) -> Self { | ||
*self | ||
} | ||
} | ||
|
||
impl<E, FakeParam> Example<E, FakeParam> { | ||
unsafe fn change<NewFakeParam>(self) -> Example<E, NewFakeParam> { | ||
Example(PhantomData) | ||
} | ||
} | ||
|
||
impl<E> Example<E, NoLifetime> { | ||
fn the_ice(&mut self) -> Example<E, Immutable<'_>> { | ||
unsafe { self.change() } | ||
//~^ ERROR cannot move out of `*self` which is behind a mutable reference | ||
} | ||
} | ||
|
||
fn main() {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
error[E0507]: cannot move out of `*self` which is behind a mutable reference | ||
--> $DIR/issue-119915-bad-clone-suggestion.rs:23:18 | ||
| | ||
LL | unsafe { self.change() } | ||
| ^^^^ -------- `*self` moved due to this method call | ||
| | | ||
| move occurs because `*self` has type `Example<E, NoLifetime>`, which does not implement the `Copy` trait | ||
| | ||
note: `Example::<E, FakeParam>::change` takes ownership of the receiver `self`, which moves `*self` | ||
--> $DIR/issue-119915-bad-clone-suggestion.rs:16:36 | ||
| | ||
LL | unsafe fn change<NewFakeParam>(self) -> Example<E, NewFakeParam> { | ||
| ^^^^ | ||
|
||
error: aborting due to 1 previous error | ||
|
||
For more information about this error, try `rustc --explain E0507`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
91206 is the only test that i consider to have regressed with this PR. the problem is that fixing this code makes it much harder to only tailor the suggestion for when it should apply. I don't think that it's worth it.