Skip to content

Commit 0d0eec0

Browse files
committed
Add helper trait that restricts lifetimes and extra predicates to ensure soundness
1 parent f383ebe commit 0d0eec0

46 files changed

Lines changed: 853 additions & 80 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

compiler/rustc_hir/src/lang_items.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ language_item_table! {
189189
CoerceUnsized, sym::coerce_unsized, coerce_unsized_trait, Target::Trait, GenericRequirement::Minimum(1);
190190
DispatchFromDyn, sym::dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1);
191191

192+
TryAsDyn, sym::try_as_dyn, try_as_dyn, Target::Trait, GenericRequirement::Exact(1);
193+
192194
// lang items relating to transmutability
193195
TransmuteOpts, sym::transmute_opts, transmute_opts, Target::Struct, GenericRequirement::Exact(0);
194196
TransmuteTrait, sym::transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(2);

compiler/rustc_middle/src/traits/select.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ pub enum SelectionCandidate<'tcx> {
180180
BuiltinUnsizeCandidate,
181181

182182
BikeshedGuaranteedNoDropCandidate,
183+
184+
TryAsDynCandidate,
183185
}
184186

185187
/// The result of trait evaluation. The order is important

compiler/rustc_middle/src/ty/context/impl_interner.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -866,6 +866,7 @@ bidirectional_lang_item_map! {
866866
Sized,
867867
TransmuteTrait,
868868
TrivialClone,
869+
TryAsDyn,
869870
Tuple,
870871
Unpin,
871872
Unsize,

compiler/rustc_middle/src/ty/mod.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -283,8 +283,9 @@ pub struct ImplTraitHeader<'tcx> {
283283
impl<'tcx> ImplTraitHeader<'tcx> {
284284
/// For trait impls, checks whether
285285
/// * the type and trait only use generic lifetime arguments (and no concrete ones like `'static`), and
286-
/// * uses each generic param (lifetime or type) only once.
287-
/// Pessimistic analysis, so it will reject alias types
286+
/// * uses any generic param (lifetime or type) only once.
287+
///
288+
/// This is a pessimistic analysis, so it will reject alias types
288289
/// and other types that may be actually ok. We can allow more in the future.
289290
///
290291
/// Constants (associated or generic) are irrelevant for this analysis, as their value is neither
@@ -306,14 +307,12 @@ impl<'tcx> ImplTraitHeader<'tcx> {
306307
ControlFlow::Break(())
307308
}
308309
}
309-
RegionKind::ReBound(..) | RegionKind::ReLateParam(_) => {
310-
ControlFlow::Continue(())
311-
}
312-
RegionKind::ReStatic
313-
| RegionKind::ReVar(_)
310+
RegionKind::ReBound(..) => ControlFlow::Continue(()),
311+
RegionKind::ReStatic | RegionKind::ReError(_) => ControlFlow::Break(()),
312+
RegionKind::ReVar(_)
314313
| RegionKind::RePlaceholder(_)
315314
| RegionKind::ReErased
316-
| RegionKind::ReError(_) => ControlFlow::Break(()),
315+
| RegionKind::ReLateParam(_) => bug!("unexpected lifetime in impl: {r:?}"),
317316
}
318317
}
319318

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,11 @@ where
364364
goal: Goal<I, Self>,
365365
) -> Result<Candidate<I>, NoSolutionOrRerunNonErased>;
366366

367+
fn consider_builtin_try_as_dyn_candidate(
368+
ecx: &mut EvalCtxt<'_, D>,
369+
goal: Goal<I, Self>,
370+
) -> Result<Candidate<I>, NoSolutionOrRerunNonErased>;
371+
367372
/// Consider (possibly several) candidates to upcast or unsize a type to another
368373
/// type, excluding the coercion of a sized type into a `dyn Trait`.
369374
///
@@ -571,6 +576,20 @@ where
571576
let cx = self.cx();
572577
let trait_def_id = goal.predicate.trait_def_id(cx);
573578

579+
// Builtin impls regularly are not `is_fully_generic_for_reflection`, so instead
580+
// of trying to handle these manually, we just reject all builtin impls in reflection
581+
// mode. We can probably lift this restriction for specific cases, but this is safer.
582+
// See `try_as_dyn_builtin_impl` for how just allowing all builtin impls is unsound.
583+
match self.typing_mode() {
584+
TypingMode::Reflection => return Ok(()),
585+
TypingMode::Coherence
586+
| TypingMode::ErasedNotCoherence(_)
587+
| TypingMode::Analysis { .. }
588+
| TypingMode::Borrowck { .. }
589+
| TypingMode::PostBorrowckAnalysis { .. }
590+
| TypingMode::PostAnalysis => {}
591+
}
592+
574593
// N.B. When assembling built-in candidates for lang items that are also
575594
// `auto` traits, then the auto trait candidate that is assembled in
576595
// `consider_auto_trait_candidate` MUST be disqualified to remain sound.
@@ -663,6 +682,9 @@ where
663682
Some(SolverTraitLangItem::BikeshedGuaranteedNoDrop) => {
664683
G::consider_builtin_bikeshed_guaranteed_no_drop_candidate(self, goal)
665684
}
685+
Some(SolverTraitLangItem::TryAsDyn) => {
686+
G::consider_builtin_try_as_dyn_candidate(self, goal)
687+
}
666688
Some(SolverTraitLangItem::Field) => G::consider_builtin_field_candidate(self, goal),
667689
_ => Err(NoSolution.into()),
668690
}
@@ -864,6 +886,20 @@ where
864886
return;
865887
}
866888

889+
// Builtin impls regularly are not `is_fully_generic_for_reflection`, so instead
890+
// of trying to handle these manually, we just reject all builtin impls in reflection
891+
// mode. We can probably lift this restriction for specific cases, but this is safer.
892+
// See `try_as_dyn_builtin_impl` for how just allowing all builtin impls is unsound.
893+
match self.typing_mode() {
894+
TypingMode::Reflection => return,
895+
TypingMode::Coherence
896+
| TypingMode::Analysis { .. }
897+
| TypingMode::ErasedNotCoherence(_)
898+
| TypingMode::Borrowck { .. }
899+
| TypingMode::PostBorrowckAnalysis { .. }
900+
| TypingMode::PostAnalysis => {}
901+
}
902+
867903
let self_ty = goal.predicate.self_ty();
868904
let bounds = match self_ty.kind() {
869905
ty::Bool

compiler/rustc_next_trait_solver/src/solve/effect_goals.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,13 @@ where
443443
unreachable!("BikeshedGuaranteedNoDrop is not const");
444444
}
445445

446+
fn consider_builtin_try_as_dyn_candidate(
447+
_ecx: &mut EvalCtxt<'_, D>,
448+
goal: Goal<I, Self>,
449+
) -> Result<Candidate<I>, NoSolutionOrRerunNonErased> {
450+
unreachable!("`TryAsDynCompat` is not const: {:?}", goal)
451+
}
452+
446453
fn consider_structural_builtin_unsize_candidates(
447454
_ecx: &mut EvalCtxt<'_, D>,
448455
_goal: Goal<I, Self>,

compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,6 +1011,13 @@ where
10111011
unreachable!("`BikeshedGuaranteedNoDrop` does not have an associated type: {:?}", goal)
10121012
}
10131013

1014+
fn consider_builtin_try_as_dyn_candidate(
1015+
_ecx: &mut EvalCtxt<'_, D>,
1016+
goal: Goal<I, Self>,
1017+
) -> Result<Candidate<I>, NoSolutionOrRerunNonErased> {
1018+
unreachable!("`TryAsDynCompat` does not have an associated type: {:?}", goal)
1019+
}
1020+
10141021
fn consider_builtin_field_candidate(
10151022
ecx: &mut EvalCtxt<'_, D>,
10161023
goal: Goal<I, Self>,

compiler/rustc_next_trait_solver/src/solve/trait_goals.rs

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ use rustc_type_ir::solve::{
1010
RerunReason, RerunResultExt, SizedTraitKind,
1111
};
1212
use rustc_type_ir::{
13-
self as ty, FieldInfo, Interner, MayBeErased, Movability, PredicatePolarity, TraitPredicate,
14-
TraitRef, TypeVisitableExt as _, TypingMode, Unnormalized, Upcast as _, elaborate,
13+
self as ty, ExistentialPredicate, FieldInfo, Interner, MayBeErased, Movability,
14+
PredicatePolarity, TraitPredicate, TraitRef, TypeVisitableExt as _, TypingMode, Unnormalized,
15+
Upcast as _, elaborate,
1516
};
1617
use tracing::{debug, instrument, trace, warn};
1718

@@ -87,12 +88,17 @@ where
8788
// Impl matches polarity
8889
(ty::ImplPolarity::Positive, ty::PredicatePolarity::Positive)
8990
| (ty::ImplPolarity::Negative, ty::PredicatePolarity::Negative) => {
90-
if let TypingMode::Reflection = ecx.typing_mode()
91-
&& !cx.is_fully_generic_for_reflection(impl_def_id)
92-
{
93-
return Err(NoSolution.into());
94-
} else {
95-
Certainty::Yes
91+
match ecx.typing_mode() {
92+
TypingMode::Reflection if !cx.is_fully_generic_for_reflection(impl_def_id) => {
93+
return Err(NoSolution.into());
94+
}
95+
TypingMode::Coherence
96+
| TypingMode::ErasedNotCoherence(_)
97+
| TypingMode::Analysis { .. }
98+
| TypingMode::Borrowck { .. }
99+
| TypingMode::PostBorrowckAnalysis { .. }
100+
| TypingMode::PostAnalysis
101+
| TypingMode::Reflection => Certainty::Yes,
96102
}
97103
}
98104

@@ -873,6 +879,49 @@ where
873879
}
874880
}
875881

882+
fn consider_builtin_try_as_dyn_candidate(
883+
ecx: &mut EvalCtxt<'_, D>,
884+
goal: Goal<I, Self>,
885+
) -> Result<Candidate<I>, NoSolutionOrRerunNonErased> {
886+
if goal.predicate.polarity != ty::PredicatePolarity::Positive {
887+
return Err(NoSolution.into());
888+
}
889+
let cx = ecx.cx();
890+
891+
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
892+
let self_ty = goal.predicate.self_ty();
893+
let ty_lifetime = goal.predicate.trait_ref.args.region_at(1);
894+
match self_ty.kind() {
895+
ty::Dynamic(bounds, lifetime) => {
896+
for bound in bounds.iter() {
897+
match bound.skip_binder() {
898+
ExistentialPredicate::Trait(_) => {}
899+
// FIXME(try_as_dyn): check what kind of projections we can allow
900+
ExistentialPredicate::Projection(_) => return Err(NoSolution.into()),
901+
// Auto traits do not affect lifetimes outside of specialization,
902+
// which is disabled in reflection.
903+
ExistentialPredicate::AutoTrait(_) => {}
904+
}
905+
}
906+
ecx.add_goal(
907+
GoalSource::Misc,
908+
goal.with(cx, ty::OutlivesPredicate(ty_lifetime, lifetime)),
909+
);
910+
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
911+
}
912+
913+
ty::Bound(..)
914+
| ty::Infer(
915+
ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_),
916+
) => {
917+
panic!("unexpected type `{self_ty:?}`")
918+
}
919+
920+
_ => Err(NoSolution.into()),
921+
}
922+
})
923+
}
924+
876925
fn consider_builtin_field_candidate(
877926
ecx: &mut EvalCtxt<'_, D>,
878927
goal: Goal<I, Self>,

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2078,6 +2078,7 @@ symbols! {
20782078
truncf32,
20792079
truncf64,
20802080
truncf128,
2081+
try_as_dyn,
20812082
try_blocks,
20822083
try_blocks_heterogeneous,
20832084
try_capture,

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

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ use rustc_hir::{self as hir, CoroutineDesugaring, CoroutineKind};
1515
use rustc_infer::traits::{Obligation, PolyTraitObligation, PredicateObligation, SelectionError};
1616
use rustc_middle::ty::fast_reject::DeepRejectCtxt;
1717
use rustc_middle::ty::{
18-
self, FieldInfo, SizedTraitKind, TraitRef, Ty, TypeVisitableExt, elaborate,
18+
self, ExistentialPredicate, FieldInfo, SizedTraitKind, TraitRef, Ty, TypeVisitableExt,
19+
elaborate,
1920
};
2021
use rustc_middle::{bug, span_bug};
2122
use rustc_span::DUMMY_SP;
@@ -132,6 +133,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
132133
&mut candidates,
133134
);
134135
}
136+
Some(LangItem::TryAsDyn) => {
137+
self.assemble_candidates_for_try_as_dyn(obligation, &mut candidates);
138+
}
135139
Some(LangItem::Field) => {
136140
self.assemble_candidates_for_field_trait(obligation, &mut candidates);
137141
}
@@ -1450,6 +1454,34 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
14501454
}
14511455
}
14521456

1457+
fn assemble_candidates_for_try_as_dyn(
1458+
&mut self,
1459+
obligation: &PolyTraitObligation<'tcx>,
1460+
candidates: &mut SelectionCandidateSet<'tcx>,
1461+
) {
1462+
match *obligation.predicate.self_ty().skip_binder().kind() {
1463+
ty::Dynamic(bounds, _lifetime) => {
1464+
for bound in bounds {
1465+
match bound.skip_binder() {
1466+
ExistentialPredicate::Trait(_) => {}
1467+
// FIXME(try_as_dyn): check what kind of projections we can allow
1468+
ExistentialPredicate::Projection(_) => return,
1469+
// Auto traits do not affect lifetimes outside of specialization,
1470+
// which is disabled in reflection.
1471+
ExistentialPredicate::AutoTrait(_) => {}
1472+
}
1473+
}
1474+
candidates.vec.push(TryAsDynCandidate);
1475+
}
1476+
1477+
ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
1478+
candidates.ambiguous = true;
1479+
}
1480+
1481+
_ => {}
1482+
}
1483+
}
1484+
14531485
fn assemble_candidates_for_field_trait(
14541486
&mut self,
14551487
obligation: &PolyTraitObligation<'tcx>,

0 commit comments

Comments
 (0)