Skip to content

Commit 042929a

Browse files
committed
wip: trait_sel: add sizedness traits to fast path
There's an existing fast path for the `type_op_prove_predicate` predicate which can be extended to support the new sizedness traits and host effect predicates, avoiding lots of machinery. This fast path logic can also be reused as a fast path for within-query trait solving.
1 parent 82c8dfd commit 042929a

25 files changed

+219
-89
lines changed

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ use rustc_lint::builtin::SELF_CONSTRUCTOR_FROM_OUTER_ITEM;
2323
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
2424
use rustc_middle::ty::{
2525
self, AdtKind, CanonicalUserType, GenericArgKind, GenericArgsRef, GenericParamDefKind,
26-
IsIdentity, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitableExt, UserArgs, UserSelfTy,
26+
IsIdentity, SizedTraitKind, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitableExt,
27+
UserArgs, UserSelfTy,
2728
};
2829
use rustc_middle::{bug, span_bug};
2930
use rustc_session::lint;
@@ -439,7 +440,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
439440
|| {},
440441
);
441442
// Sized types have static alignment, and so do slices.
442-
if tail.is_trivially_sized(self.tcx) || matches!(tail.kind(), ty::Slice(..)) {
443+
if tail.has_trivial_sizedness(self.tcx, SizedTraitKind::Sized)
444+
|| matches!(tail.kind(), ty::Slice(..))
445+
{
443446
// Nothing else is required here.
444447
} else {
445448
// We can't be sure, let's required full `Sized`.

compiler/rustc_middle/src/ty/sty.rs

+33-16
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use rustc_hir::def_id::DefId;
1616
use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, extension};
1717
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
1818
use rustc_type_ir::TyKind::*;
19+
use rustc_type_ir::solve::SizedTraitKind;
1920
use rustc_type_ir::{self as ir, BoundVar, CollectAndApply, DynKind, TypeVisitableExt, elaborate};
2021
use tracing::instrument;
2122
use ty::util::{AsyncDropGlueMorphology, IntTypeExt};
@@ -1783,7 +1784,7 @@ impl<'tcx> Ty<'tcx> {
17831784
let Some(pointee_ty) = self.builtin_deref(true) else {
17841785
bug!("Type {self:?} is not a pointer or reference type")
17851786
};
1786-
if pointee_ty.is_trivially_sized(tcx) {
1787+
if pointee_ty.has_trivial_sizedness(tcx, SizedTraitKind::Sized) {
17871788
tcx.types.unit
17881789
} else {
17891790
match pointee_ty.ptr_metadata_ty_or_tail(tcx, |x| x) {
@@ -1886,17 +1887,17 @@ impl<'tcx> Ty<'tcx> {
18861887
}
18871888
}
18881889

1889-
/// Fast path helper for testing if a type is `Sized`.
1890+
/// Fast path helper for testing if a type is `Sized`, `MetaSized` or `PointeeSized`.
18901891
///
1891-
/// Returning true means the type is known to be sized. Returning
1892-
/// `false` means nothing -- could be sized, might not be.
1892+
/// Returning true means the type is known to implement the sizedness trait. Returning `false`
1893+
/// means nothing -- could be sized, might not be.
18931894
///
1894-
/// Note that we could never rely on the fact that a type such as `[_]` is
1895-
/// trivially `!Sized` because we could be in a type environment with a
1896-
/// bound such as `[_]: Copy`. A function with such a bound obviously never
1897-
/// can be called, but that doesn't mean it shouldn't typecheck. This is why
1898-
/// this method doesn't return `Option<bool>`.
1899-
pub fn is_trivially_sized(self, tcx: TyCtxt<'tcx>) -> bool {
1895+
/// Note that we could never rely on the fact that a type such as `[_]` is trivially `!Sized`
1896+
/// because we could be in a type environment with a bound such as `[_]: Copy`. A function with
1897+
/// such a bound obviously never can be called, but that doesn't mean it shouldn't typecheck.
1898+
/// This is why this method doesn't return `Option<bool>`.
1899+
#[instrument(skip(tcx), level = "debug")]
1900+
pub fn has_trivial_sizedness(self, tcx: TyCtxt<'tcx>, sizedness: SizedTraitKind) -> bool {
19001901
match self.kind() {
19011902
ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
19021903
| ty::Uint(_)
@@ -1919,20 +1920,36 @@ impl<'tcx> Ty<'tcx> {
19191920
| ty::Error(_)
19201921
| ty::Dynamic(_, _, ty::DynStar) => true,
19211922

1922-
ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => false,
1923+
ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) => match sizedness {
1924+
SizedTraitKind::Sized => false,
1925+
SizedTraitKind::MetaSized | SizedTraitKind::PointeeSized => true,
1926+
},
1927+
1928+
ty::Foreign(..) => match sizedness {
1929+
SizedTraitKind::Sized | SizedTraitKind::MetaSized => false,
1930+
SizedTraitKind::PointeeSized => true,
1931+
},
19231932

1924-
ty::Tuple(tys) => tys.last().is_none_or(|ty| ty.is_trivially_sized(tcx)),
1933+
ty::Tuple(tys) => tys.last().is_none_or(|ty| ty.has_trivial_sizedness(tcx, sizedness)),
19251934

1926-
ty::Adt(def, args) => def
1927-
.sized_constraint(tcx)
1928-
.is_none_or(|ty| ty.instantiate(tcx, args).is_trivially_sized(tcx)),
1935+
ty::Adt(def, args) => {
1936+
let constraint = match sizedness {
1937+
SizedTraitKind::Sized => def.sized_constraint(tcx),
1938+
SizedTraitKind::MetaSized => def.meta_sized_constraint(tcx),
1939+
SizedTraitKind::PointeeSized => def.pointee_sized_constraint(tcx),
1940+
};
1941+
1942+
constraint.is_none_or(|ty| {
1943+
ty.instantiate(tcx, args).has_trivial_sizedness(tcx, sizedness)
1944+
})
1945+
}
19291946

19301947
ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) | ty::Bound(..) => false,
19311948

19321949
ty::Infer(ty::TyVar(_)) => false,
19331950

19341951
ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
1935-
bug!("`is_trivially_sized` applied to unexpected type: {:?}", self)
1952+
bug!("`has_trivial_sizedness` applied to unexpected type: {:?}", self)
19361953
}
19371954
}
19381955
}

compiler/rustc_middle/src/ty/util.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use rustc_index::bit_set::GrowableBitSet;
1616
use rustc_macros::{HashStable, TyDecodable, TyEncodable, extension};
1717
use rustc_session::Limit;
1818
use rustc_span::sym;
19+
use rustc_type_ir::solve::SizedTraitKind;
1920
use smallvec::{SmallVec, smallvec};
2021
use tracing::{debug, instrument};
2122

@@ -1189,7 +1190,8 @@ impl<'tcx> Ty<'tcx> {
11891190
/// strange rules like `<T as Foo<'static>>::Bar: Sized` that
11901191
/// actually carry lifetime requirements.
11911192
pub fn is_sized(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> bool {
1192-
self.is_trivially_sized(tcx) || tcx.is_sized_raw(typing_env.as_query_input(self))
1193+
self.has_trivial_sizedness(tcx, SizedTraitKind::Sized)
1194+
|| tcx.is_sized_raw(typing_env.as_query_input(self))
11931195
}
11941196

11951197
/// Checks whether values of this type `T` implement the `Freeze`

compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,8 @@ where
196196
/// trait to be implemented (see the trait goal for that), as these are orthogonal. This means that
197197
/// the effect predicate can succeed while the trait predicate can fail - this is unintuitive but
198198
/// allows this function to be much simpler.
199-
// NOTE: Keep this in sync with `evaluate_host_effect_for_sizedness_goal` in the old solver.
199+
// NOTE: Keep this in sync with `evaluate_host_effect_for_sizedness_goal` in the old solver and
200+
// `ProvePredicate::try_fast_path`
200201
#[instrument(level = "trace", skip(cx), ret)]
201202
pub(in crate::solve) fn const_conditions_for_sizedness<I: Interner>(
202203
cx: I,

compiler/rustc_trait_selection/src/traits/effects.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,8 @@ fn evaluate_host_effect_from_builtin_impls<'tcx>(
253253
}
254254
}
255255

256-
// NOTE: Keep this in sync with `const_conditions_for_sizedness` in the new solver.
256+
// NOTE: Keep this in sync with `const_conditions_for_sizedness` in the new solver and
257+
// `ProvePredicate::try_fast_path`
257258
fn evaluate_host_effect_for_sizedness_goal<'tcx>(
258259
selcx: &mut SelectionContext<'_, 'tcx>,
259260
obligation: &HostEffectObligation<'tcx>,

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

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

330330
let infcx = self.selcx.infcx;
331331

332+
if sizedness_fast_path(infcx.tcx, obligation.predicate, obligation.param_env) {
333+
return ProcessResult::Changed(thin_vec::thin_vec![]);
334+
}
335+
332336
if obligation.predicate.has_aliases() {
333337
let mut obligations = PredicateObligations::new();
334338
let predicate = normalize_with_depth_to(

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;

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, key.param_env) {
2819
return Some(());
2920
}
3021

compiler/rustc_trait_selection/src/traits/select/mod.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ use crate::infer::{InferCtxt, InferOk, TypeFreshener};
5151
use crate::solve::InferCtxtSelectExt as _;
5252
use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to};
5353
use crate::traits::project::{ProjectAndUnifyResult, ProjectionCacheKeyExt};
54-
use crate::traits::{EvaluateConstErr, ProjectionCacheKey, Unimplemented, effects};
54+
use crate::traits::{
55+
EvaluateConstErr, ProjectionCacheKey, Unimplemented, effects, sizedness_fast_path,
56+
};
5557

5658
mod _match;
5759
mod candidate_assembly;
@@ -605,6 +607,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
605607
None => self.check_recursion_limit(&obligation, &obligation)?,
606608
}
607609

610+
if sizedness_fast_path(self.tcx(), obligation.predicate, obligation.param_env) {
611+
return Ok(EvaluatedToOk);
612+
}
613+
608614
ensure_sufficient_stack(|| {
609615
let bound_predicate = obligation.predicate.kind();
610616
match bound_predicate.skip_binder() {

compiler/rustc_trait_selection/src/traits/util.rs

+67-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
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::*;
78
use rustc_middle::bug;
89
use rustc_middle::ty::{
9-
self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
10+
self, SizedTraitKind, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
1011
};
1112
use rustc_span::Span;
1213
use smallvec::{SmallVec, smallvec};
@@ -504,3 +505,68 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> {
504505
}
505506
}
506507
}
508+
509+
pub fn sizedness_fast_path<'tcx>(
510+
tcx: TyCtxt<'tcx>,
511+
predicate: ty::Predicate<'tcx>,
512+
param_env: ty::ParamEnv<'tcx>,
513+
) -> bool {
514+
// Proving `Sized`/`MetaSized`/`PointeeSized`, very often on "obviously sized" types like
515+
// `&T`, accounts for about 60% percentage of the predicates we have to prove. No need to
516+
// canonicalize and all that for such cases.
517+
if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_ref)) =
518+
predicate.kind().skip_binder()
519+
{
520+
let sizedness = if tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized) {
521+
Some(SizedTraitKind::Sized)
522+
} else if tcx.is_lang_item(trait_ref.def_id(), LangItem::MetaSized) {
523+
Some(SizedTraitKind::MetaSized)
524+
} else if tcx.is_lang_item(trait_ref.def_id(), LangItem::PointeeSized) {
525+
Some(SizedTraitKind::PointeeSized)
526+
} else {
527+
None
528+
};
529+
530+
if let Some(sizedness) = sizedness
531+
&& trait_ref.self_ty().has_trivial_sizedness(tcx, sizedness)
532+
{
533+
debug!("fast path -- trivial sizedness");
534+
return true;
535+
}
536+
537+
if matches!(sizedness, Some(SizedTraitKind::MetaSized)) {
538+
let has_sized_in_param_env = param_env
539+
.caller_bounds()
540+
.iter()
541+
.rev() // sizedness predicates are normally at the end for diagnostics reasons
542+
.filter_map(|p| p.as_trait_clause())
543+
.any(|c| {
544+
trait_ref.self_ty() == c.skip_binder().self_ty()
545+
&& tcx.is_lang_item(c.def_id(), LangItem::Sized)
546+
});
547+
if has_sized_in_param_env {
548+
debug!("fast path -- metasized paramenv");
549+
return true;
550+
}
551+
}
552+
}
553+
554+
// Likewise, determining if a sizedness trait is implemented const-ly is a trivial
555+
// determination that can happen in the fast path.
556+
//
557+
// NOTE: Keep this in sync with `evaluate_host_effect_for_sizedness_goal` in the old solver,
558+
// `const_conditions_for_sizedness` in the new solver.
559+
if let ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(host_pred)) =
560+
predicate.kind().skip_binder()
561+
{
562+
let is_sizedness = tcx.is_lang_item(host_pred.def_id(), LangItem::Sized)
563+
|| tcx.is_lang_item(host_pred.def_id(), LangItem::MetaSized);
564+
565+
if is_sizedness && !host_pred.self_ty().has_non_const_sizedness() {
566+
debug!("fast path -- host effect");
567+
return true;
568+
}
569+
}
570+
571+
false
572+
}

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), param_env) {
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

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, param_env) {
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

tests/ui/associated-consts/assoc-const-eq-bound-var-in-ty-not-wf.rs

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ trait Trait<T> {
77
}
88

99
fn take(
10+
//~^ ERROR implementation of `Project` is not general enough
1011
_: impl Trait<
1112
<<for<'a> fn(&'a str) -> &'a str as Project>::Out as Discard>::Out,
1213
K = { () }

0 commit comments

Comments
 (0)