Skip to content

Commit 7c9448e

Browse files
committed
Add helper trait that restricts lifetimes and extra predicates to ensure soundness
1 parent 4319262 commit 7c9448e

46 files changed

Lines changed: 830 additions & 76 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
@@ -875,6 +875,7 @@ bidirectional_lang_item_map! {
875875
Sized,
876876
TransmuteTrait,
877877
TrivialClone,
878+
TryAsDyn,
878879
Tuple,
879880
Unpin,
880881
Unsize,

compiler/rustc_middle/src/ty/mod.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -299,8 +299,9 @@ pub struct ImplTraitHeader<'tcx> {
299299
impl<'tcx> ImplTraitHeader<'tcx> {
300300
/// For trait impls, checks whether
301301
/// * the type and trait only use generic lifetime arguments (and no concrete ones like `'static`), and
302-
/// * uses each generic param (lifetime or type) only once.
303-
/// Pessimistic analysis, so it will reject alias types
302+
/// * uses any generic param (lifetime or type) only once.
303+
///
304+
/// This is a pessimistic analysis, so it will reject alias types
304305
/// and other types that may be actually ok. We can allow more in the future.
305306
///
306307
/// Constants (associated or generic) are irrelevant for this analysis, as their value is neither
@@ -322,14 +323,12 @@ impl<'tcx> ImplTraitHeader<'tcx> {
322323
ControlFlow::Break(())
323324
}
324325
}
325-
RegionKind::ReBound(..) | RegionKind::ReLateParam(_) => {
326-
ControlFlow::Continue(())
327-
}
328-
RegionKind::ReStatic
329-
| RegionKind::ReVar(_)
326+
RegionKind::ReBound(..) => ControlFlow::Continue(()),
327+
RegionKind::ReStatic | RegionKind::ReError(_) => ControlFlow::Break(()),
328+
RegionKind::ReVar(_)
330329
| RegionKind::RePlaceholder(_)
331330
| RegionKind::ReErased
332-
| RegionKind::ReError(_) => ControlFlow::Break(()),
331+
| RegionKind::ReLateParam(_) => bug!("unexpected lifetime in impl: {r:?}"),
333332
}
334333
}
335334

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

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

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

581+
// Builtin impls regularly are not `is_fully_generic_for_reflection`, so instead
582+
// of trying to handle these manually, we just reject all builtin impls in reflection
583+
// mode. We can probably lift this restriction for specific cases, but this is safer.
584+
// See `try_as_dyn_builtin_impl` for how just allowing all builtin impls is unsound.
585+
if self.typing_mode().is_reflection() {
586+
return Ok(());
587+
}
588+
576589
// N.B. When assembling built-in candidates for lang items that are also
577590
// `auto` traits, then the auto trait candidate that is assembled in
578591
// `consider_auto_trait_candidate` MUST be disqualified to remain sound.
@@ -665,6 +678,9 @@ where
665678
Some(SolverTraitLangItem::BikeshedGuaranteedNoDrop) => {
666679
G::consider_builtin_bikeshed_guaranteed_no_drop_candidate(self, goal)
667680
}
681+
Some(SolverTraitLangItem::TryAsDyn) => {
682+
G::consider_builtin_try_as_dyn_candidate(self, goal)
683+
}
668684
Some(SolverTraitLangItem::Field) => G::consider_builtin_field_candidate(self, goal),
669685
_ => Err(NoSolution.into()),
670686
}
@@ -869,6 +885,14 @@ where
869885
return;
870886
}
871887

888+
// Builtin impls regularly are not `is_fully_generic_for_reflection`, so instead
889+
// of trying to handle these manually, we just reject all builtin impls in reflection
890+
// mode. We can probably lift this restriction for specific cases, but this is safer.
891+
// See `try_as_dyn_builtin_impl` for how just allowing all builtin impls is unsound.
892+
if self.typing_mode().is_reflection() {
893+
return;
894+
}
895+
872896
let self_ty = goal.predicate.self_ty();
873897
let bounds = match self_ty.kind() {
874898
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
@@ -451,6 +451,13 @@ where
451451
unreachable!("BikeshedGuaranteedNoDrop is not const");
452452
}
453453

454+
fn consider_builtin_try_as_dyn_candidate(
455+
_ecx: &mut EvalCtxt<'_, D>,
456+
goal: Goal<I, Self>,
457+
) -> Result<Candidate<I>, NoSolutionOrRerunNonErased> {
458+
unreachable!("`TryAsDynCompat` is not const: {:?}", goal)
459+
}
460+
454461
fn consider_structural_builtin_unsize_candidates(
455462
_ecx: &mut EvalCtxt<'_, D>,
456463
_goal: Goal<I, Self>,

compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,6 +1076,13 @@ where
10761076
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
10771077
})
10781078
}
1079+
1080+
fn consider_builtin_try_as_dyn_candidate(
1081+
_ecx: &mut EvalCtxt<'_, D>,
1082+
_goal: Goal<I, Self>,
1083+
) -> Result<Candidate<I>, NoSolutionOrRerunNonErased> {
1084+
unreachable!("try_as_dyn helper trait doesn't have assoc types")
1085+
}
10791086
}
10801087

10811088
impl<D, I> EvalCtxt<'_, D>

compiler/rustc_next_trait_solver/src/solve/trait_goals.rs

Lines changed: 46 additions & 2 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

@@ -874,6 +875,49 @@ where
874875
}
875876
}
876877

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

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2122,6 +2122,7 @@ symbols! {
21222122
truncf32,
21232123
truncf64,
21242124
truncf128,
2125+
try_as_dyn,
21252126
try_blocks,
21262127
try_blocks_heterogeneous,
21272128
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)