Auto merge of #141731 - compiler-errors:tweak-fast-path-trait, r=lcnr
Tweak fast path trait handling (1.) Make it more sound by considering polarity (lol) (2.) Make it more general, by considering higher-ranked size/copy/clone (2.) Make it less observable, by only doing copy/clone fast path if there are no regions involved r? lcnr
This commit is contained in:
@@ -1882,10 +1882,8 @@ impl<'tcx> Ty<'tcx> {
|
|||||||
// Needs normalization or revealing to determine, so no is the safe answer.
|
// Needs normalization or revealing to determine, so no is the safe answer.
|
||||||
ty::Alias(..) => false,
|
ty::Alias(..) => false,
|
||||||
|
|
||||||
ty::Param(..) | ty::Placeholder(..) | ty::Infer(..) | ty::Error(..) => false,
|
ty::Param(..) | ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(..) => {
|
||||||
|
false
|
||||||
ty::Bound(..) => {
|
|
||||||
bug!("`is_trivially_pure_clone_copy` applied to unexpected type: {:?}", self);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ use std::ops::Deref;
|
|||||||
use rustc_type_ir::solve::{Certainty, Goal, NoSolution};
|
use rustc_type_ir::solve::{Certainty, Goal, NoSolution};
|
||||||
use rustc_type_ir::{self as ty, InferCtxtLike, Interner, TypeFoldable};
|
use rustc_type_ir::{self as ty, InferCtxtLike, Interner, TypeFoldable};
|
||||||
|
|
||||||
use crate::solve::HasChanged;
|
|
||||||
|
|
||||||
pub trait SolverDelegate: Deref<Target = Self::Infcx> + Sized {
|
pub trait SolverDelegate: Deref<Target = Self::Infcx> + Sized {
|
||||||
type Infcx: InferCtxtLike<Interner = Self::Interner>;
|
type Infcx: InferCtxtLike<Interner = Self::Interner>;
|
||||||
type Interner: Interner;
|
type Interner: Interner;
|
||||||
@@ -23,7 +21,7 @@ pub trait SolverDelegate: Deref<Target = Self::Infcx> + Sized {
|
|||||||
&self,
|
&self,
|
||||||
goal: Goal<Self::Interner, <Self::Interner as Interner>::Predicate>,
|
goal: Goal<Self::Interner, <Self::Interner as Interner>::Predicate>,
|
||||||
span: <Self::Interner as Interner>::Span,
|
span: <Self::Interner as Interner>::Span,
|
||||||
) -> Option<HasChanged>;
|
) -> Option<Certainty>;
|
||||||
|
|
||||||
fn fresh_var_for_kind_with_span(
|
fn fresh_var_for_kind_with_span(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -671,10 +671,13 @@ where
|
|||||||
// If this loop did not result in any progress, what's our final certainty.
|
// If this loop did not result in any progress, what's our final certainty.
|
||||||
let mut unchanged_certainty = Some(Certainty::Yes);
|
let mut unchanged_certainty = Some(Certainty::Yes);
|
||||||
for (source, goal, stalled_on) in mem::take(&mut self.nested_goals) {
|
for (source, goal, stalled_on) in mem::take(&mut self.nested_goals) {
|
||||||
if let Some(has_changed) = self.delegate.compute_goal_fast_path(goal, self.origin_span)
|
if let Some(certainty) = self.delegate.compute_goal_fast_path(goal, self.origin_span) {
|
||||||
{
|
match certainty {
|
||||||
if matches!(has_changed, HasChanged::Yes) {
|
Certainty::Yes => {}
|
||||||
unchanged_certainty = None;
|
Certainty::Maybe(_) => {
|
||||||
|
self.nested_goals.push((source, goal, None));
|
||||||
|
unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ use rustc_infer::infer::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TyCtx
|
|||||||
use rustc_infer::traits::solve::Goal;
|
use rustc_infer::traits::solve::Goal;
|
||||||
use rustc_middle::traits::query::NoSolution;
|
use rustc_middle::traits::query::NoSolution;
|
||||||
use rustc_middle::traits::solve::Certainty;
|
use rustc_middle::traits::solve::Certainty;
|
||||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitableExt as _, TypingMode};
|
use rustc_middle::ty::{
|
||||||
use rustc_next_trait_solver::solve::HasChanged;
|
self, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeVisitableExt as _, TypingMode,
|
||||||
|
};
|
||||||
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
|
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
|
||||||
|
|
||||||
use crate::traits::{EvaluateConstErr, ObligationCause, specialization_graph};
|
use crate::traits::{EvaluateConstErr, ObligationCause, specialization_graph};
|
||||||
@@ -61,11 +62,41 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
|
|||||||
&self,
|
&self,
|
||||||
goal: Goal<'tcx, ty::Predicate<'tcx>>,
|
goal: Goal<'tcx, ty::Predicate<'tcx>>,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> Option<HasChanged> {
|
) -> Option<Certainty> {
|
||||||
|
if let Some(trait_pred) = goal.predicate.as_trait_clause() {
|
||||||
|
if trait_pred.polarity() == ty::PredicatePolarity::Positive {
|
||||||
|
match self.0.tcx.as_lang_item(trait_pred.def_id()) {
|
||||||
|
Some(LangItem::Sized)
|
||||||
|
if self
|
||||||
|
.resolve_vars_if_possible(trait_pred.self_ty().skip_binder())
|
||||||
|
.is_trivially_sized(self.0.tcx) =>
|
||||||
|
{
|
||||||
|
return Some(Certainty::Yes);
|
||||||
|
}
|
||||||
|
Some(LangItem::Copy | LangItem::Clone) => {
|
||||||
|
let self_ty =
|
||||||
|
self.resolve_vars_if_possible(trait_pred.self_ty().skip_binder());
|
||||||
|
// Unlike `Sized` traits, which always prefer the built-in impl,
|
||||||
|
// `Copy`/`Clone` may be shadowed by a param-env candidate which
|
||||||
|
// could force a lifetime error or guide inference. While that's
|
||||||
|
// not generally desirable, it is observable, so for now let's
|
||||||
|
// ignore this fast path for types that have regions or infer.
|
||||||
|
if !self_ty
|
||||||
|
.has_type_flags(TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_INFER)
|
||||||
|
&& self_ty.is_trivially_pure_clone_copy()
|
||||||
|
{
|
||||||
|
return Some(Certainty::Yes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let pred = goal.predicate.kind();
|
let pred = goal.predicate.kind();
|
||||||
match pred.no_bound_vars()? {
|
match pred.no_bound_vars()? {
|
||||||
ty::PredicateKind::DynCompatible(def_id) if self.0.tcx.is_dyn_compatible(def_id) => {
|
ty::PredicateKind::DynCompatible(def_id) if self.0.tcx.is_dyn_compatible(def_id) => {
|
||||||
Some(HasChanged::No)
|
Some(Certainty::Yes)
|
||||||
}
|
}
|
||||||
ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(outlives)) => {
|
ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(outlives)) => {
|
||||||
self.0.sub_regions(
|
self.0.sub_regions(
|
||||||
@@ -73,7 +104,7 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
|
|||||||
outlives.1,
|
outlives.1,
|
||||||
outlives.0,
|
outlives.0,
|
||||||
);
|
);
|
||||||
Some(HasChanged::No)
|
Some(Certainty::Yes)
|
||||||
}
|
}
|
||||||
ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(outlives)) => {
|
ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(outlives)) => {
|
||||||
self.0.register_type_outlives_constraint(
|
self.0.register_type_outlives_constraint(
|
||||||
@@ -82,22 +113,7 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
|
|||||||
&ObligationCause::dummy_with_span(span),
|
&ObligationCause::dummy_with_span(span),
|
||||||
);
|
);
|
||||||
|
|
||||||
Some(HasChanged::No)
|
Some(Certainty::Yes)
|
||||||
}
|
|
||||||
ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) => {
|
|
||||||
match self.0.tcx.as_lang_item(trait_pred.def_id()) {
|
|
||||||
Some(LangItem::Sized)
|
|
||||||
if trait_pred.self_ty().is_trivially_sized(self.0.tcx) =>
|
|
||||||
{
|
|
||||||
Some(HasChanged::No)
|
|
||||||
}
|
|
||||||
Some(LangItem::Copy | LangItem::Clone)
|
|
||||||
if trait_pred.self_ty().is_trivially_pure_clone_copy() =>
|
|
||||||
{
|
|
||||||
Some(HasChanged::No)
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -195,10 +195,15 @@ where
|
|||||||
|
|
||||||
let goal = obligation.as_goal();
|
let goal = obligation.as_goal();
|
||||||
let delegate = <&SolverDelegate<'tcx>>::from(infcx);
|
let delegate = <&SolverDelegate<'tcx>>::from(infcx);
|
||||||
if let Some(fast_path_has_changed) =
|
if let Some(certainty) =
|
||||||
delegate.compute_goal_fast_path(goal, obligation.cause.span)
|
delegate.compute_goal_fast_path(goal, obligation.cause.span)
|
||||||
{
|
{
|
||||||
any_changed |= matches!(fast_path_has_changed, HasChanged::Yes);
|
match certainty {
|
||||||
|
Certainty::Yes => {}
|
||||||
|
Certainty::Maybe(_) => {
|
||||||
|
self.obligations.register(obligation, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user