Rollup merge of #110207 - compiler-errors:new-solver-unpin, r=lcnr
Assemble `Unpin` candidates specially for generators in new solver Fixes compiler-errors/next-solver-hir-issues#16 r? ``@lcnr``
This commit is contained in:
commit
69d7172b8e
6 changed files with 130 additions and 65 deletions
|
@ -348,6 +348,14 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
) {
|
) {
|
||||||
let lang_items = self.tcx().lang_items();
|
let lang_items = self.tcx().lang_items();
|
||||||
let trait_def_id = goal.predicate.trait_def_id(self.tcx());
|
let trait_def_id = goal.predicate.trait_def_id(self.tcx());
|
||||||
|
|
||||||
|
// N.B. When assembling built-in candidates for lang items that are also
|
||||||
|
// `auto` traits, then the auto trait candidate that is assembled in
|
||||||
|
// `consider_auto_trait_candidate` MUST be disqualified to remain sound.
|
||||||
|
//
|
||||||
|
// Instead of adding the logic here, it's a better idea to add it in
|
||||||
|
// `EvalCtxt::disqualify_auto_trait_candidate_due_to_possible_impl` in
|
||||||
|
// `solve::trait_goals` instead.
|
||||||
let result = if self.tcx().trait_is_auto(trait_def_id) {
|
let result = if self.tcx().trait_is_auto(trait_def_id) {
|
||||||
G::consider_auto_trait_candidate(self, goal)
|
G::consider_auto_trait_candidate(self, goal)
|
||||||
} else if self.tcx().trait_is_alias(trait_def_id) {
|
} else if self.tcx().trait_is_alias(trait_def_id) {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use super::assembly::{self, structural_traits};
|
use super::assembly::{self, structural_traits};
|
||||||
use super::{EvalCtxt, SolverMode};
|
use super::{EvalCtxt, SolverMode};
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::LangItem;
|
use rustc_hir::{LangItem, Movability};
|
||||||
use rustc_infer::traits::query::NoSolution;
|
use rustc_infer::traits::query::NoSolution;
|
||||||
use rustc_infer::traits::util::supertraits;
|
use rustc_infer::traits::util::supertraits;
|
||||||
use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult};
|
use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult};
|
||||||
|
@ -147,66 +147,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||||
goal: Goal<'tcx, Self>,
|
goal: Goal<'tcx, Self>,
|
||||||
) -> QueryResult<'tcx> {
|
) -> QueryResult<'tcx> {
|
||||||
let self_ty = goal.predicate.self_ty();
|
if let Some(result) = ecx.disqualify_auto_trait_candidate_due_to_possible_impl(goal) {
|
||||||
match *self_ty.kind() {
|
return result;
|
||||||
// Stall int and float vars until they are resolved to a concrete
|
|
||||||
// numerical type. That's because the check for impls below treats
|
|
||||||
// int vars as matching any impl. Even if we filtered such impls,
|
|
||||||
// we probably don't want to treat an `impl !AutoTrait for i32` as
|
|
||||||
// disqualifying the built-in auto impl for `i64: AutoTrait` either.
|
|
||||||
ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) => {
|
|
||||||
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// These types cannot be structurally decomposed into constitutent
|
|
||||||
// types, and therefore have no builtin impl.
|
|
||||||
ty::Dynamic(..)
|
|
||||||
| ty::Param(..)
|
|
||||||
| ty::Foreign(..)
|
|
||||||
| ty::Alias(ty::Projection, ..)
|
|
||||||
| ty::Placeholder(..) => return Err(NoSolution),
|
|
||||||
|
|
||||||
ty::Infer(_) | ty::Bound(_, _) => bug!("unexpected type `{self_ty}`"),
|
|
||||||
|
|
||||||
// For rigid types, we only register a builtin auto implementation
|
|
||||||
// if there is no implementation that could ever apply to the self
|
|
||||||
// type.
|
|
||||||
//
|
|
||||||
// This differs from the current stable behavior and fixes #84857.
|
|
||||||
// Due to breakage found via crater, we currently instead lint
|
|
||||||
// patterns which can be used to exploit this unsoundness on stable,
|
|
||||||
// see #93367 for more details.
|
|
||||||
ty::Bool
|
|
||||||
| ty::Char
|
|
||||||
| ty::Int(_)
|
|
||||||
| ty::Uint(_)
|
|
||||||
| ty::Float(_)
|
|
||||||
| ty::Str
|
|
||||||
| ty::Array(_, _)
|
|
||||||
| ty::Slice(_)
|
|
||||||
| ty::RawPtr(_)
|
|
||||||
| ty::Ref(_, _, _)
|
|
||||||
| ty::FnDef(_, _)
|
|
||||||
| ty::FnPtr(_)
|
|
||||||
| ty::Closure(_, _)
|
|
||||||
| ty::Generator(_, _, _)
|
|
||||||
| ty::GeneratorWitness(_)
|
|
||||||
| ty::GeneratorWitnessMIR(_, _)
|
|
||||||
| ty::Never
|
|
||||||
| ty::Tuple(_)
|
|
||||||
| ty::Error(_)
|
|
||||||
| ty::Adt(_, _)
|
|
||||||
| ty::Alias(ty::Opaque, _) => {
|
|
||||||
if let Some(def_id) = ecx.tcx().find_map_relevant_impl(
|
|
||||||
goal.predicate.def_id(),
|
|
||||||
goal.predicate.self_ty(),
|
|
||||||
TreatProjections::NextSolverLookup,
|
|
||||||
Some,
|
|
||||||
) {
|
|
||||||
debug!(?def_id, ?goal, "disqualified auto-trait implementation");
|
|
||||||
return Err(NoSolution);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ecx.probe_and_evaluate_goal_for_constituent_tys(
|
ecx.probe_and_evaluate_goal_for_constituent_tys(
|
||||||
|
@ -630,6 +572,97 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> EvalCtxt<'_, 'tcx> {
|
impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
|
// Return `Some` if there is an impl (built-in or user provided) that may
|
||||||
|
// hold for the self type of the goal, which for coherence and soundness
|
||||||
|
// purposes must disqualify the built-in auto impl assembled by considering
|
||||||
|
// the type's constituent types.
|
||||||
|
fn disqualify_auto_trait_candidate_due_to_possible_impl(
|
||||||
|
&mut self,
|
||||||
|
goal: Goal<'tcx, TraitPredicate<'tcx>>,
|
||||||
|
) -> Option<QueryResult<'tcx>> {
|
||||||
|
let self_ty = goal.predicate.self_ty();
|
||||||
|
match *self_ty.kind() {
|
||||||
|
// Stall int and float vars until they are resolved to a concrete
|
||||||
|
// numerical type. That's because the check for impls below treats
|
||||||
|
// int vars as matching any impl. Even if we filtered such impls,
|
||||||
|
// we probably don't want to treat an `impl !AutoTrait for i32` as
|
||||||
|
// disqualifying the built-in auto impl for `i64: AutoTrait` either.
|
||||||
|
ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) => {
|
||||||
|
Some(self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS))
|
||||||
|
}
|
||||||
|
|
||||||
|
// These types cannot be structurally decomposed into constitutent
|
||||||
|
// types, and therefore have no built-in auto impl.
|
||||||
|
ty::Dynamic(..)
|
||||||
|
| ty::Param(..)
|
||||||
|
| ty::Foreign(..)
|
||||||
|
| ty::Alias(ty::Projection, ..)
|
||||||
|
| ty::Placeholder(..) => Some(Err(NoSolution)),
|
||||||
|
|
||||||
|
ty::Infer(_) | ty::Bound(_, _) => bug!("unexpected type `{self_ty}`"),
|
||||||
|
|
||||||
|
// Generators have one special built-in candidate, `Unpin`, which
|
||||||
|
// takes precedence over the structural auto trait candidate being
|
||||||
|
// assembled.
|
||||||
|
ty::Generator(_, _, movability)
|
||||||
|
if Some(goal.predicate.def_id()) == self.tcx().lang_items().unpin_trait() =>
|
||||||
|
{
|
||||||
|
match movability {
|
||||||
|
Movability::Static => Some(Err(NoSolution)),
|
||||||
|
Movability::Movable => {
|
||||||
|
Some(self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For rigid types, any possible implementation that could apply to
|
||||||
|
// the type (even if after unification and processing nested goals
|
||||||
|
// it does not hold) will disqualify the built-in auto impl.
|
||||||
|
//
|
||||||
|
// This differs from the current stable behavior and fixes #84857.
|
||||||
|
// Due to breakage found via crater, we currently instead lint
|
||||||
|
// patterns which can be used to exploit this unsoundness on stable,
|
||||||
|
// see #93367 for more details.
|
||||||
|
ty::Bool
|
||||||
|
| ty::Char
|
||||||
|
| ty::Int(_)
|
||||||
|
| ty::Uint(_)
|
||||||
|
| ty::Float(_)
|
||||||
|
| ty::Str
|
||||||
|
| ty::Array(_, _)
|
||||||
|
| ty::Slice(_)
|
||||||
|
| ty::RawPtr(_)
|
||||||
|
| ty::Ref(_, _, _)
|
||||||
|
| ty::FnDef(_, _)
|
||||||
|
| ty::FnPtr(_)
|
||||||
|
| ty::Closure(_, _)
|
||||||
|
| ty::Generator(_, _, _)
|
||||||
|
| ty::GeneratorWitness(_)
|
||||||
|
| ty::GeneratorWitnessMIR(_, _)
|
||||||
|
| ty::Never
|
||||||
|
| ty::Tuple(_)
|
||||||
|
| ty::Adt(_, _)
|
||||||
|
// FIXME: Handling opaques here is kinda sus. Especially because we
|
||||||
|
// simplify them to PlaceholderSimplifiedType.
|
||||||
|
| ty::Alias(ty::Opaque, _) => {
|
||||||
|
if let Some(def_id) = self.tcx().find_map_relevant_impl(
|
||||||
|
goal.predicate.def_id(),
|
||||||
|
goal.predicate.self_ty(),
|
||||||
|
TreatProjections::NextSolverLookup,
|
||||||
|
Some,
|
||||||
|
) {
|
||||||
|
debug!(?def_id, ?goal, "disqualified auto-trait implementation");
|
||||||
|
// No need to actually consider the candidate here,
|
||||||
|
// since we do that in `consider_impl_candidate`.
|
||||||
|
return Some(Err(NoSolution));
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ty::Error(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Convenience function for traits that are structural, i.e. that only
|
/// Convenience function for traits that are structural, i.e. that only
|
||||||
/// have nested subgoals that only change the self type. Unlike other
|
/// have nested subgoals that only change the self type. Unlike other
|
||||||
/// evaluate-like helpers, this does a probe, so it doesn't need to be
|
/// evaluate-like helpers, this does a probe, so it doesn't need to be
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// revisions: current next
|
||||||
|
//[next] compile-flags: -Ztrait-solver=next
|
||||||
// run-pass
|
// run-pass
|
||||||
|
|
||||||
#![feature(generators, generator_trait)]
|
#![feature(generators, generator_trait)]
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
error[E0277]: `[static generator@$DIR/static-not-unpin.rs:11:25: 11:34]` cannot be unpinned
|
error[E0277]: `[static generator@$DIR/static-not-unpin.rs:14:25: 14:34]` cannot be unpinned
|
||||||
--> $DIR/static-not-unpin.rs:14:18
|
--> $DIR/static-not-unpin.rs:17:18
|
||||||
|
|
|
|
||||||
LL | assert_unpin(generator);
|
LL | assert_unpin(generator);
|
||||||
| ------------ ^^^^^^^^^ the trait `Unpin` is not implemented for `[static generator@$DIR/static-not-unpin.rs:11:25: 11:34]`
|
| ------------ ^^^^^^^^^ the trait `Unpin` is not implemented for `[static generator@$DIR/static-not-unpin.rs:14:25: 14:34]`
|
||||||
| |
|
| |
|
||||||
| required by a bound introduced by this call
|
| required by a bound introduced by this call
|
||||||
|
|
|
|
||||||
= note: consider using the `pin!` macro
|
= note: consider using the `pin!` macro
|
||||||
consider using `Box::pin` if you need to access the pinned value outside of the current scope
|
consider using `Box::pin` if you need to access the pinned value outside of the current scope
|
||||||
note: required by a bound in `assert_unpin`
|
note: required by a bound in `assert_unpin`
|
||||||
--> $DIR/static-not-unpin.rs:7:20
|
--> $DIR/static-not-unpin.rs:10:20
|
||||||
|
|
|
|
||||||
LL | fn assert_unpin<T: Unpin>(_: T) {
|
LL | fn assert_unpin<T: Unpin>(_: T) {
|
||||||
| ^^^^^ required by this bound in `assert_unpin`
|
| ^^^^^ required by this bound in `assert_unpin`
|
19
tests/ui/generator/static-not-unpin.next.stderr
Normal file
19
tests/ui/generator/static-not-unpin.next.stderr
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
error[E0277]: `[static generator@$DIR/static-not-unpin.rs:14:25: 14:34]` cannot be unpinned
|
||||||
|
--> $DIR/static-not-unpin.rs:17:18
|
||||||
|
|
|
||||||
|
LL | assert_unpin(generator);
|
||||||
|
| ------------ ^^^^^^^^^ the trait `Unpin` is not implemented for `[static generator@$DIR/static-not-unpin.rs:14:25: 14:34]`
|
||||||
|
| |
|
||||||
|
| required by a bound introduced by this call
|
||||||
|
|
|
||||||
|
= note: consider using the `pin!` macro
|
||||||
|
consider using `Box::pin` if you need to access the pinned value outside of the current scope
|
||||||
|
note: required by a bound in `assert_unpin`
|
||||||
|
--> $DIR/static-not-unpin.rs:10:20
|
||||||
|
|
|
||||||
|
LL | fn assert_unpin<T: Unpin>(_: T) {
|
||||||
|
| ^^^^^ required by this bound in `assert_unpin`
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0277`.
|
|
@ -1,3 +1,6 @@
|
||||||
|
// revisions: current next
|
||||||
|
//[next] compile-flags: -Ztrait-solver=next
|
||||||
|
|
||||||
#![feature(generators)]
|
#![feature(generators)]
|
||||||
|
|
||||||
// normalize-stderr-test "std::pin::Unpin" -> "std::marker::Unpin"
|
// normalize-stderr-test "std::pin::Unpin" -> "std::marker::Unpin"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue