Skip to content

Commit 2da29db

Browse files
committed
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
2 parents 990039e + 72d17bf commit 2da29db

File tree

12 files changed

+70
-31
lines changed

12 files changed

+70
-31
lines changed

Diff for: compiler/rustc_middle/src/ty/sty.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -1873,14 +1873,14 @@ impl<'tcx> Ty<'tcx> {
18731873

18741874
/// Fast path helper for testing if a type is `Sized`.
18751875
///
1876-
/// Returning true means the type is known to be sized. Returning
1877-
/// `false` means nothing -- could be sized, might not be.
1876+
/// Returning true means the type is known to implement `Sized`. Returning `false` means
1877+
/// nothing -- could be sized, might not be.
18781878
///
1879-
/// Note that we could never rely on the fact that a type such as `[_]` is
1880-
/// trivially `!Sized` because we could be in a type environment with a
1881-
/// bound such as `[_]: Copy`. A function with such a bound obviously never
1882-
/// can be called, but that doesn't mean it shouldn't typecheck. This is why
1883-
/// this method doesn't return `Option<bool>`.
1879+
/// Note that we could never rely on the fact that a type such as `[_]` is trivially `!Sized`
1880+
/// because we could be in a type environment with a bound such as `[_]: Copy`. A function with
1881+
/// such a bound obviously never can be called, but that doesn't mean it shouldn't typecheck.
1882+
/// This is why this method doesn't return `Option<bool>`.
1883+
#[instrument(skip(tcx), level = "debug")]
18841884
pub fn is_trivially_sized(self, tcx: TyCtxt<'tcx>) -> bool {
18851885
match self.kind() {
18861886
ty::Infer(ty::IntVar(_) | ty::FloatVar(_))

Diff for: compiler/rustc_trait_selection/src/traits/fulfill.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ use super::{
2424
};
2525
use crate::error_reporting::InferCtxtErrorExt;
2626
use crate::infer::{InferCtxt, TyOrConstInferVar};
27-
use crate::traits::EvaluateConstErr;
2827
use crate::traits::normalize::normalize_with_depth_to;
2928
use crate::traits::project::{PolyProjectionObligation, ProjectionCacheKeyExt as _};
3029
use crate::traits::query::evaluate_obligation::InferCtxtExt;
30+
use crate::traits::{EvaluateConstErr, sizedness_fast_path};
3131

3232
pub(crate) type PendingPredicateObligations<'tcx> = ThinVec<PendingPredicateObligation<'tcx>>;
3333

@@ -335,6 +335,10 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
335335

336336
let infcx = self.selcx.infcx;
337337

338+
if sizedness_fast_path(infcx.tcx, obligation.predicate) {
339+
return ProcessResult::Changed(thin_vec::thin_vec![]);
340+
}
341+
338342
if obligation.predicate.has_aliases() {
339343
let mut obligations = PredicateObligations::new();
340344
let predicate = normalize_with_depth_to(

Diff for: compiler/rustc_trait_selection/src/traits/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ pub use self::specialize::{
6565
pub use self::structural_normalize::StructurallyNormalizeExt;
6666
pub use self::util::{
6767
BoundVarReplacer, PlaceholderReplacer, elaborate, expand_trait_aliases, impl_item_is_final,
68-
supertrait_def_ids, supertraits, transitive_bounds_that_define_assoc_item, upcast_choices,
69-
with_replaced_escaping_bound_vars,
68+
sizedness_fast_path, supertrait_def_ids, supertraits, transitive_bounds_that_define_assoc_item,
69+
upcast_choices, with_replaced_escaping_bound_vars,
7070
};
7171
use crate::error_reporting::InferCtxtErrorExt;
7272
use crate::infer::outlives::env::OutlivesEnvironment;

Diff for: compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs

+2-11
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use rustc_hir::LangItem;
21
use rustc_infer::traits::Obligation;
32
use rustc_middle::traits::ObligationCause;
43
use rustc_middle::traits::query::NoSolution;
@@ -7,7 +6,7 @@ use rustc_middle::ty::{self, ParamEnvAnd, TyCtxt};
76
use rustc_span::Span;
87

98
use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse};
10-
use crate::traits::ObligationCtxt;
9+
use crate::traits::{ObligationCtxt, sizedness_fast_path};
1110

1211
impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> {
1312
type QueryResponse = ();
@@ -16,15 +15,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> {
1615
tcx: TyCtxt<'tcx>,
1716
key: &ParamEnvAnd<'tcx, Self>,
1817
) -> Option<Self::QueryResponse> {
19-
// Proving Sized, very often on "obviously sized" types like
20-
// `&T`, accounts for about 60% percentage of the predicates
21-
// we have to prove. No need to canonicalize and all that for
22-
// such cases.
23-
if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_ref)) =
24-
key.value.predicate.kind().skip_binder()
25-
&& tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized)
26-
&& trait_ref.self_ty().is_trivially_sized(tcx)
27-
{
18+
if sizedness_fast_path(tcx, key.value.predicate) {
2819
return Some(());
2920
}
3021

Diff for: compiler/rustc_trait_selection/src/traits/select/mod.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ use crate::infer::{InferCtxt, InferOk, TypeFreshener};
4949
use crate::solve::InferCtxtSelectExt as _;
5050
use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to};
5151
use crate::traits::project::{ProjectAndUnifyResult, ProjectionCacheKeyExt};
52-
use crate::traits::{EvaluateConstErr, ProjectionCacheKey, Unimplemented, effects};
52+
use crate::traits::{
53+
EvaluateConstErr, ProjectionCacheKey, Unimplemented, effects, sizedness_fast_path,
54+
};
5355

5456
mod _match;
5557
mod candidate_assembly;
@@ -603,6 +605,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
603605
None => self.check_recursion_limit(&obligation, &obligation)?,
604606
}
605607

608+
if sizedness_fast_path(self.tcx(), obligation.predicate) {
609+
return Ok(EvaluatedToOk);
610+
}
611+
606612
ensure_sufficient_stack(|| {
607613
let bound_predicate = obligation.predicate.kind();
608614
match bound_predicate.skip_binder() {

Diff for: compiler/rustc_trait_selection/src/traits/util.rs

+19
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::collections::{BTreeMap, VecDeque};
22

33
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
4+
use rustc_hir::LangItem;
45
use rustc_hir::def_id::DefId;
56
use rustc_infer::infer::InferCtxt;
67
pub use rustc_infer::traits::util::*;
@@ -504,3 +505,21 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> {
504505
}
505506
}
506507
}
508+
509+
pub fn sizedness_fast_path<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tcx>) -> bool {
510+
// Proving `Sized` very often on "obviously sized" types like `&T`, accounts for about 60%
511+
// percentage of the predicates we have to prove. No need to canonicalize and all that for
512+
// such cases.
513+
if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_ref)) =
514+
predicate.kind().skip_binder()
515+
{
516+
if tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized)
517+
&& trait_ref.self_ty().is_trivially_sized(tcx)
518+
{
519+
debug!("fast path -- trivial sizedness");
520+
return true;
521+
}
522+
}
523+
524+
false
525+
}

Diff for: compiler/rustc_traits/src/codegen.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
use rustc_infer::infer::TyCtxtInferExt;
77
use rustc_middle::bug;
88
use rustc_middle::traits::CodegenObligationError;
9-
use rustc_middle::ty::{self, PseudoCanonicalInput, TyCtxt, TypeVisitableExt};
9+
use rustc_middle::ty::{self, PseudoCanonicalInput, TyCtxt, TypeVisitableExt, Upcast};
1010
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
1111
use rustc_trait_selection::traits::{
1212
ImplSource, Obligation, ObligationCause, ObligationCtxt, ScrubbedTraitError, SelectionContext,
13-
Unimplemented,
13+
Unimplemented, sizedness_fast_path,
1414
};
1515
use tracing::debug;
1616

@@ -34,6 +34,13 @@ pub(crate) fn codegen_select_candidate<'tcx>(
3434
let (infcx, param_env) = tcx.infer_ctxt().ignoring_regions().build_with_typing_env(typing_env);
3535
let mut selcx = SelectionContext::new(&infcx);
3636

37+
if sizedness_fast_path(tcx, trait_ref.upcast(tcx)) {
38+
return Ok(&*tcx.arena.alloc(ImplSource::Builtin(
39+
ty::solve::BuiltinImplSource::Trivial,
40+
Default::default(),
41+
)));
42+
}
43+
3744
let obligation_cause = ObligationCause::dummy();
3845
let obligation = Obligation::new(tcx, obligation_cause, param_env, trait_ref);
3946

Diff for: compiler/rustc_traits/src/evaluate_obligation.rs

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use rustc_span::DUMMY_SP;
55
use rustc_trait_selection::traits::query::CanonicalPredicateGoal;
66
use rustc_trait_selection::traits::{
77
EvaluationResult, Obligation, ObligationCause, OverflowError, SelectionContext, TraitQueryMode,
8+
sizedness_fast_path,
89
};
910
use tracing::debug;
1011

@@ -23,6 +24,10 @@ fn evaluate_obligation<'tcx>(
2324
debug!("evaluate_obligation: goal={:#?}", goal);
2425
let ParamEnvAnd { param_env, value: predicate } = goal;
2526

27+
if sizedness_fast_path(tcx, predicate) {
28+
return Ok(EvaluationResult::EvaluatedToOk);
29+
}
30+
2631
let mut selcx = SelectionContext::with_query_mode(infcx, TraitQueryMode::Canonical);
2732
let obligation = Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate);
2833

Diff for: tests/ui/closures/issue-78720.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
fn server() -> impl {
22
//~^ ERROR at least one trait must be specified
3+
//~^^ ERROR type annotations needed
34
().map2(|| "")
45
}
56

Diff for: tests/ui/closures/issue-78720.stderr

+11-5
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ LL | fn server() -> impl {
55
| ^^^^
66

77
error[E0412]: cannot find type `F` in this scope
8-
--> $DIR/issue-78720.rs:13:12
8+
--> $DIR/issue-78720.rs:14:12
99
|
1010
LL | _func: F,
1111
| ^
@@ -22,8 +22,14 @@ help: you might be missing a type parameter
2222
LL | struct Map2<Segment2, F> {
2323
| +++
2424

25+
error[E0282]: type annotations needed
26+
--> $DIR/issue-78720.rs:1:16
27+
|
28+
LL | fn server() -> impl {
29+
| ^^^^ cannot infer type
30+
2531
error[E0308]: mismatched types
26-
--> $DIR/issue-78720.rs:7:39
32+
--> $DIR/issue-78720.rs:8:39
2733
|
2834
LL | fn map2<F>(self, f: F) -> Map2<F> {}
2935
| ^^ expected `Map2<F>`, found `()`
@@ -32,7 +38,7 @@ LL | fn map2<F>(self, f: F) -> Map2<F> {}
3238
found unit type `()`
3339

3440
error[E0277]: the size for values of type `Self` cannot be known at compilation time
35-
--> $DIR/issue-78720.rs:7:16
41+
--> $DIR/issue-78720.rs:8:16
3642
|
3743
LL | fn map2<F>(self, f: F) -> Map2<F> {}
3844
| ^^^^ 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
4753
LL | fn map2<F>(&self, f: F) -> Map2<F> {}
4854
| +
4955

50-
error: aborting due to 4 previous errors
56+
error: aborting due to 5 previous errors
5157

52-
Some errors have detailed explanations: E0277, E0308, E0412.
58+
Some errors have detailed explanations: E0277, E0282, E0308, E0412.
5359
For more information about an error, try `rustc --explain E0277`.

Diff for: tests/ui/codegen/overflow-during-mono.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//~ ERROR overflow evaluating the requirement `{closure@$DIR/overflow-during-mono.rs:13:41: 13:44}: Sized`
1+
//~ ERROR overflow evaluating the requirement `for<'a> {closure@$DIR/overflow-during-mono.rs:13:41: 13:44}: FnMut(&'a _)`
22
//@ build-fail
33

44
#![recursion_limit = "32"]

Diff for: tests/ui/codegen/overflow-during-mono.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error[E0275]: overflow evaluating the requirement `{closure@$DIR/overflow-during-mono.rs:13:41: 13:44}: Sized`
1+
error[E0275]: overflow evaluating the requirement `for<'a> {closure@$DIR/overflow-during-mono.rs:13:41: 13:44}: FnMut(&'a _)`
22
|
33
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "64"]` attribute to your crate (`overflow_during_mono`)
44
= note: required for `Filter<std::array::IntoIter<i32, 11>, {closure@$DIR/overflow-during-mono.rs:13:41: 13:44}>` to implement `Iterator`

0 commit comments

Comments
 (0)