Auto merge of #139577 - davidtwco:sizedness-go-vroom, r=oli-obk
re-use `Sized` fast-path There's an existing fast path for the `type_op_prove_predicate` predicate, checking for trivially `Sized` types, which can be re-used when evaluating obligations within queries. This should improve performance and was found to be beneficial in #137944. r? types
This commit is contained in:
commit
2da29dbe8f
12 changed files with 70 additions and 31 deletions
|
@ -1873,14 +1873,14 @@ impl<'tcx> Ty<'tcx> {
|
||||||
|
|
||||||
/// Fast path helper for testing if a type is `Sized`.
|
/// Fast path helper for testing if a type is `Sized`.
|
||||||
///
|
///
|
||||||
/// Returning true means the type is known to be sized. Returning
|
/// Returning true means the type is known to implement `Sized`. Returning `false` means
|
||||||
/// `false` means nothing -- could be sized, might not be.
|
/// nothing -- could be sized, might not be.
|
||||||
///
|
///
|
||||||
/// Note that we could never rely on the fact that a type such as `[_]` is
|
/// Note that we could never rely on the fact that a type such as `[_]` is trivially `!Sized`
|
||||||
/// trivially `!Sized` because we could be in a type environment with a
|
/// because we could be in a type environment with a bound such as `[_]: Copy`. A function with
|
||||||
/// bound such as `[_]: Copy`. A function with such a bound obviously never
|
/// such a bound obviously never can be called, but that doesn't mean it shouldn't typecheck.
|
||||||
/// can be called, but that doesn't mean it shouldn't typecheck. This is why
|
/// This is why this method doesn't return `Option<bool>`.
|
||||||
/// this method doesn't return `Option<bool>`.
|
#[instrument(skip(tcx), level = "debug")]
|
||||||
pub fn is_trivially_sized(self, tcx: TyCtxt<'tcx>) -> bool {
|
pub fn is_trivially_sized(self, tcx: TyCtxt<'tcx>) -> bool {
|
||||||
match self.kind() {
|
match self.kind() {
|
||||||
ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
|
ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
|
||||||
|
|
|
@ -24,10 +24,10 @@ use super::{
|
||||||
};
|
};
|
||||||
use crate::error_reporting::InferCtxtErrorExt;
|
use crate::error_reporting::InferCtxtErrorExt;
|
||||||
use crate::infer::{InferCtxt, TyOrConstInferVar};
|
use crate::infer::{InferCtxt, TyOrConstInferVar};
|
||||||
use crate::traits::EvaluateConstErr;
|
|
||||||
use crate::traits::normalize::normalize_with_depth_to;
|
use crate::traits::normalize::normalize_with_depth_to;
|
||||||
use crate::traits::project::{PolyProjectionObligation, ProjectionCacheKeyExt as _};
|
use crate::traits::project::{PolyProjectionObligation, ProjectionCacheKeyExt as _};
|
||||||
use crate::traits::query::evaluate_obligation::InferCtxtExt;
|
use crate::traits::query::evaluate_obligation::InferCtxtExt;
|
||||||
|
use crate::traits::{EvaluateConstErr, sizedness_fast_path};
|
||||||
|
|
||||||
pub(crate) type PendingPredicateObligations<'tcx> = ThinVec<PendingPredicateObligation<'tcx>>;
|
pub(crate) type PendingPredicateObligations<'tcx> = ThinVec<PendingPredicateObligation<'tcx>>;
|
||||||
|
|
||||||
|
@ -335,6 +335,10 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
|
||||||
|
|
||||||
let infcx = self.selcx.infcx;
|
let infcx = self.selcx.infcx;
|
||||||
|
|
||||||
|
if sizedness_fast_path(infcx.tcx, obligation.predicate) {
|
||||||
|
return ProcessResult::Changed(thin_vec::thin_vec![]);
|
||||||
|
}
|
||||||
|
|
||||||
if obligation.predicate.has_aliases() {
|
if obligation.predicate.has_aliases() {
|
||||||
let mut obligations = PredicateObligations::new();
|
let mut obligations = PredicateObligations::new();
|
||||||
let predicate = normalize_with_depth_to(
|
let predicate = normalize_with_depth_to(
|
||||||
|
|
|
@ -65,8 +65,8 @@ pub use self::specialize::{
|
||||||
pub use self::structural_normalize::StructurallyNormalizeExt;
|
pub use self::structural_normalize::StructurallyNormalizeExt;
|
||||||
pub use self::util::{
|
pub use self::util::{
|
||||||
BoundVarReplacer, PlaceholderReplacer, elaborate, expand_trait_aliases, impl_item_is_final,
|
BoundVarReplacer, PlaceholderReplacer, elaborate, expand_trait_aliases, impl_item_is_final,
|
||||||
supertrait_def_ids, supertraits, transitive_bounds_that_define_assoc_item, upcast_choices,
|
sizedness_fast_path, supertrait_def_ids, supertraits, transitive_bounds_that_define_assoc_item,
|
||||||
with_replaced_escaping_bound_vars,
|
upcast_choices, with_replaced_escaping_bound_vars,
|
||||||
};
|
};
|
||||||
use crate::error_reporting::InferCtxtErrorExt;
|
use crate::error_reporting::InferCtxtErrorExt;
|
||||||
use crate::infer::outlives::env::OutlivesEnvironment;
|
use crate::infer::outlives::env::OutlivesEnvironment;
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use rustc_hir::LangItem;
|
|
||||||
use rustc_infer::traits::Obligation;
|
use rustc_infer::traits::Obligation;
|
||||||
use rustc_middle::traits::ObligationCause;
|
use rustc_middle::traits::ObligationCause;
|
||||||
use rustc_middle::traits::query::NoSolution;
|
use rustc_middle::traits::query::NoSolution;
|
||||||
|
@ -7,7 +6,7 @@ use rustc_middle::ty::{self, ParamEnvAnd, TyCtxt};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
|
||||||
use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse};
|
use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse};
|
||||||
use crate::traits::ObligationCtxt;
|
use crate::traits::{ObligationCtxt, sizedness_fast_path};
|
||||||
|
|
||||||
impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> {
|
impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> {
|
||||||
type QueryResponse = ();
|
type QueryResponse = ();
|
||||||
|
@ -16,15 +15,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> {
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
key: &ParamEnvAnd<'tcx, Self>,
|
key: &ParamEnvAnd<'tcx, Self>,
|
||||||
) -> Option<Self::QueryResponse> {
|
) -> Option<Self::QueryResponse> {
|
||||||
// Proving Sized, very often on "obviously sized" types like
|
if sizedness_fast_path(tcx, key.value.predicate) {
|
||||||
// `&T`, accounts for about 60% percentage of the predicates
|
|
||||||
// we have to prove. No need to canonicalize and all that for
|
|
||||||
// such cases.
|
|
||||||
if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_ref)) =
|
|
||||||
key.value.predicate.kind().skip_binder()
|
|
||||||
&& tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized)
|
|
||||||
&& trait_ref.self_ty().is_trivially_sized(tcx)
|
|
||||||
{
|
|
||||||
return Some(());
|
return Some(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,9 @@ use crate::infer::{InferCtxt, InferOk, TypeFreshener};
|
||||||
use crate::solve::InferCtxtSelectExt as _;
|
use crate::solve::InferCtxtSelectExt as _;
|
||||||
use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to};
|
use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to};
|
||||||
use crate::traits::project::{ProjectAndUnifyResult, ProjectionCacheKeyExt};
|
use crate::traits::project::{ProjectAndUnifyResult, ProjectionCacheKeyExt};
|
||||||
use crate::traits::{EvaluateConstErr, ProjectionCacheKey, Unimplemented, effects};
|
use crate::traits::{
|
||||||
|
EvaluateConstErr, ProjectionCacheKey, Unimplemented, effects, sizedness_fast_path,
|
||||||
|
};
|
||||||
|
|
||||||
mod _match;
|
mod _match;
|
||||||
mod candidate_assembly;
|
mod candidate_assembly;
|
||||||
|
@ -603,6 +605,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
None => self.check_recursion_limit(&obligation, &obligation)?,
|
None => self.check_recursion_limit(&obligation, &obligation)?,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if sizedness_fast_path(self.tcx(), obligation.predicate) {
|
||||||
|
return Ok(EvaluatedToOk);
|
||||||
|
}
|
||||||
|
|
||||||
ensure_sufficient_stack(|| {
|
ensure_sufficient_stack(|| {
|
||||||
let bound_predicate = obligation.predicate.kind();
|
let bound_predicate = obligation.predicate.kind();
|
||||||
match bound_predicate.skip_binder() {
|
match bound_predicate.skip_binder() {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::collections::{BTreeMap, VecDeque};
|
use std::collections::{BTreeMap, VecDeque};
|
||||||
|
|
||||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
||||||
|
use rustc_hir::LangItem;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_infer::infer::InferCtxt;
|
use rustc_infer::infer::InferCtxt;
|
||||||
pub use rustc_infer::traits::util::*;
|
pub use rustc_infer::traits::util::*;
|
||||||
|
@ -504,3 +505,21 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn sizedness_fast_path<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tcx>) -> bool {
|
||||||
|
// Proving `Sized` very often on "obviously sized" types like `&T`, accounts for about 60%
|
||||||
|
// percentage of the predicates we have to prove. No need to canonicalize and all that for
|
||||||
|
// such cases.
|
||||||
|
if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_ref)) =
|
||||||
|
predicate.kind().skip_binder()
|
||||||
|
{
|
||||||
|
if tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized)
|
||||||
|
&& trait_ref.self_ty().is_trivially_sized(tcx)
|
||||||
|
{
|
||||||
|
debug!("fast path -- trivial sizedness");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
|
@ -6,11 +6,11 @@
|
||||||
use rustc_infer::infer::TyCtxtInferExt;
|
use rustc_infer::infer::TyCtxtInferExt;
|
||||||
use rustc_middle::bug;
|
use rustc_middle::bug;
|
||||||
use rustc_middle::traits::CodegenObligationError;
|
use rustc_middle::traits::CodegenObligationError;
|
||||||
use rustc_middle::ty::{self, PseudoCanonicalInput, TyCtxt, TypeVisitableExt};
|
use rustc_middle::ty::{self, PseudoCanonicalInput, TyCtxt, TypeVisitableExt, Upcast};
|
||||||
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
|
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
|
||||||
use rustc_trait_selection::traits::{
|
use rustc_trait_selection::traits::{
|
||||||
ImplSource, Obligation, ObligationCause, ObligationCtxt, ScrubbedTraitError, SelectionContext,
|
ImplSource, Obligation, ObligationCause, ObligationCtxt, ScrubbedTraitError, SelectionContext,
|
||||||
Unimplemented,
|
Unimplemented, sizedness_fast_path,
|
||||||
};
|
};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
|
@ -34,6 +34,13 @@ pub(crate) fn codegen_select_candidate<'tcx>(
|
||||||
let (infcx, param_env) = tcx.infer_ctxt().ignoring_regions().build_with_typing_env(typing_env);
|
let (infcx, param_env) = tcx.infer_ctxt().ignoring_regions().build_with_typing_env(typing_env);
|
||||||
let mut selcx = SelectionContext::new(&infcx);
|
let mut selcx = SelectionContext::new(&infcx);
|
||||||
|
|
||||||
|
if sizedness_fast_path(tcx, trait_ref.upcast(tcx)) {
|
||||||
|
return Ok(&*tcx.arena.alloc(ImplSource::Builtin(
|
||||||
|
ty::solve::BuiltinImplSource::Trivial,
|
||||||
|
Default::default(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
let obligation_cause = ObligationCause::dummy();
|
let obligation_cause = ObligationCause::dummy();
|
||||||
let obligation = Obligation::new(tcx, obligation_cause, param_env, trait_ref);
|
let obligation = Obligation::new(tcx, obligation_cause, param_env, trait_ref);
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ use rustc_span::DUMMY_SP;
|
||||||
use rustc_trait_selection::traits::query::CanonicalPredicateGoal;
|
use rustc_trait_selection::traits::query::CanonicalPredicateGoal;
|
||||||
use rustc_trait_selection::traits::{
|
use rustc_trait_selection::traits::{
|
||||||
EvaluationResult, Obligation, ObligationCause, OverflowError, SelectionContext, TraitQueryMode,
|
EvaluationResult, Obligation, ObligationCause, OverflowError, SelectionContext, TraitQueryMode,
|
||||||
|
sizedness_fast_path,
|
||||||
};
|
};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
|
@ -23,6 +24,10 @@ fn evaluate_obligation<'tcx>(
|
||||||
debug!("evaluate_obligation: goal={:#?}", goal);
|
debug!("evaluate_obligation: goal={:#?}", goal);
|
||||||
let ParamEnvAnd { param_env, value: predicate } = goal;
|
let ParamEnvAnd { param_env, value: predicate } = goal;
|
||||||
|
|
||||||
|
if sizedness_fast_path(tcx, predicate) {
|
||||||
|
return Ok(EvaluationResult::EvaluatedToOk);
|
||||||
|
}
|
||||||
|
|
||||||
let mut selcx = SelectionContext::with_query_mode(infcx, TraitQueryMode::Canonical);
|
let mut selcx = SelectionContext::with_query_mode(infcx, TraitQueryMode::Canonical);
|
||||||
let obligation = Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate);
|
let obligation = Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
fn server() -> impl {
|
fn server() -> impl {
|
||||||
//~^ ERROR at least one trait must be specified
|
//~^ ERROR at least one trait must be specified
|
||||||
|
//~^^ ERROR type annotations needed
|
||||||
().map2(|| "")
|
().map2(|| "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ LL | fn server() -> impl {
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
||||||
error[E0412]: cannot find type `F` in this scope
|
error[E0412]: cannot find type `F` in this scope
|
||||||
--> $DIR/issue-78720.rs:13:12
|
--> $DIR/issue-78720.rs:14:12
|
||||||
|
|
|
|
||||||
LL | _func: F,
|
LL | _func: F,
|
||||||
| ^
|
| ^
|
||||||
|
@ -22,8 +22,14 @@ help: you might be missing a type parameter
|
||||||
LL | struct Map2<Segment2, F> {
|
LL | struct Map2<Segment2, F> {
|
||||||
| +++
|
| +++
|
||||||
|
|
||||||
|
error[E0282]: type annotations needed
|
||||||
|
--> $DIR/issue-78720.rs:1:16
|
||||||
|
|
|
||||||
|
LL | fn server() -> impl {
|
||||||
|
| ^^^^ cannot infer type
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/issue-78720.rs:7:39
|
--> $DIR/issue-78720.rs:8:39
|
||||||
|
|
|
|
||||||
LL | fn map2<F>(self, f: F) -> Map2<F> {}
|
LL | fn map2<F>(self, f: F) -> Map2<F> {}
|
||||||
| ^^ expected `Map2<F>`, found `()`
|
| ^^ expected `Map2<F>`, found `()`
|
||||||
|
@ -32,7 +38,7 @@ LL | fn map2<F>(self, f: F) -> Map2<F> {}
|
||||||
found unit type `()`
|
found unit type `()`
|
||||||
|
|
||||||
error[E0277]: the size for values of type `Self` cannot be known at compilation time
|
error[E0277]: the size for values of type `Self` cannot be known at compilation time
|
||||||
--> $DIR/issue-78720.rs:7:16
|
--> $DIR/issue-78720.rs:8:16
|
||||||
|
|
|
|
||||||
LL | fn map2<F>(self, f: F) -> Map2<F> {}
|
LL | fn map2<F>(self, f: F) -> Map2<F> {}
|
||||||
| ^^^^ doesn't have a size known at compile-time
|
| ^^^^ doesn't have a size known at compile-time
|
||||||
|
@ -47,7 +53,7 @@ help: function arguments must have a statically known size, borrowed types alway
|
||||||
LL | fn map2<F>(&self, f: F) -> Map2<F> {}
|
LL | fn map2<F>(&self, f: F) -> Map2<F> {}
|
||||||
| +
|
| +
|
||||||
|
|
||||||
error: aborting due to 4 previous errors
|
error: aborting due to 5 previous errors
|
||||||
|
|
||||||
Some errors have detailed explanations: E0277, E0308, E0412.
|
Some errors have detailed explanations: E0277, E0282, E0308, E0412.
|
||||||
For more information about an error, try `rustc --explain E0277`.
|
For more information about an error, try `rustc --explain E0277`.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
//~ ERROR overflow evaluating the requirement `{closure@$DIR/overflow-during-mono.rs:13:41: 13:44}: Sized`
|
//~ ERROR overflow evaluating the requirement `for<'a> {closure@$DIR/overflow-during-mono.rs:13:41: 13:44}: FnMut(&'a _)`
|
||||||
//@ build-fail
|
//@ build-fail
|
||||||
|
|
||||||
#![recursion_limit = "32"]
|
#![recursion_limit = "32"]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
error[E0275]: overflow evaluating the requirement `{closure@$DIR/overflow-during-mono.rs:13:41: 13:44}: Sized`
|
error[E0275]: overflow evaluating the requirement `for<'a> {closure@$DIR/overflow-during-mono.rs:13:41: 13:44}: FnMut(&'a _)`
|
||||||
|
|
|
|
||||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "64"]` attribute to your crate (`overflow_during_mono`)
|
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "64"]` attribute to your crate (`overflow_during_mono`)
|
||||||
= note: required for `Filter<std::array::IntoIter<i32, 11>, {closure@$DIR/overflow-during-mono.rs:13:41: 13:44}>` to implement `Iterator`
|
= note: required for `Filter<std::array::IntoIter<i32, 11>, {closure@$DIR/overflow-during-mono.rs:13:41: 13:44}>` to implement `Iterator`
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue