Skip to content

Commit 67f7c3d

Browse files
committed
Add helper trait that restricts lifetimes and extra predicates to ensure soundness
1 parent 13d22e8 commit 67f7c3d

44 files changed

Lines changed: 806 additions & 81 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
@@ -820,6 +820,7 @@ bidirectional_lang_item_map! {
820820
Sized,
821821
TransmuteTrait,
822822
TrivialClone,
823+
TryAsDyn,
823824
Tuple,
824825
Unpin,
825826
Unsize,

compiler/rustc_middle/src/ty/mod.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -260,9 +260,9 @@ pub struct ImplTraitHeader<'tcx> {
260260
}
261261

262262
impl<'tcx> ImplTraitHeader<'tcx> {
263-
/// For trait impls, checks whether the type and trait only have generic parameters in their
264-
/// arguments and only uses each generic param once, too.
265-
/// Pessimistic analysis, so it will reject alias types
263+
/// For trait impls, checks whether the type and trait only have generic lifetime parameters in their
264+
/// arguments and only use any generic param once.
265+
/// This is a pessimistic analysis, so it will reject alias types
266266
/// and other types that may be actually ok. We can allow more in the future.
267267
pub fn is_fully_generic_for_reflection(self) -> bool {
268268
#[derive(Default)]
@@ -281,14 +281,12 @@ impl<'tcx> ImplTraitHeader<'tcx> {
281281
ControlFlow::Break(())
282282
}
283283
}
284-
RegionKind::ReBound(..) | RegionKind::ReLateParam(_) => {
285-
ControlFlow::Continue(())
286-
}
287-
RegionKind::ReStatic
288-
| RegionKind::ReVar(_)
284+
RegionKind::ReBound(..) => ControlFlow::Continue(()),
285+
RegionKind::ReStatic | RegionKind::ReError(_) => ControlFlow::Break(()),
286+
RegionKind::ReVar(_)
289287
| RegionKind::RePlaceholder(_)
290288
| RegionKind::ReErased
291-
| RegionKind::ReError(_) => ControlFlow::Break(()),
289+
| RegionKind::ReLateParam(_) => bug!("unexpected lifetime in impl: {r:?}"),
292290
}
293291
}
294292

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,11 @@ where
355355
goal: Goal<I, Self>,
356356
) -> Result<Candidate<I>, NoSolution>;
357357

358+
fn consider_builtin_try_as_dyn_candidate(
359+
ecx: &mut EvalCtxt<'_, D>,
360+
goal: Goal<I, Self>,
361+
) -> Result<Candidate<I>, NoSolution>;
362+
358363
/// Consider (possibly several) candidates to upcast or unsize a type to another
359364
/// type, excluding the coercion of a sized type into a `dyn Trait`.
360365
///
@@ -554,6 +559,19 @@ where
554559
let cx = self.cx();
555560
let trait_def_id = goal.predicate.trait_def_id(cx);
556561

562+
// Builtin impls regularly are not `is_fully_generic_for_reflection`, so instead
563+
// of trying to handle these manually, we just reject all builtin impls in reflection
564+
// mode. We can probably lift this restriction for specific cases, but this is safer.
565+
// See `try_as_dyn_builtin_impl` for how just allowing all builtin impls is unsound.
566+
match self.typing_mode() {
567+
TypingMode::Reflection => return,
568+
TypingMode::Coherence
569+
| TypingMode::Analysis { .. }
570+
| TypingMode::Borrowck { .. }
571+
| TypingMode::PostBorrowckAnalysis { .. }
572+
| TypingMode::PostAnalysis => {}
573+
}
574+
557575
// N.B. When assembling built-in candidates for lang items that are also
558576
// `auto` traits, then the auto trait candidate that is assembled in
559577
// `consider_auto_trait_candidate` MUST be disqualified to remain sound.
@@ -646,6 +664,9 @@ where
646664
Some(SolverTraitLangItem::BikeshedGuaranteedNoDrop) => {
647665
G::consider_builtin_bikeshed_guaranteed_no_drop_candidate(self, goal)
648666
}
667+
Some(SolverTraitLangItem::TryAsDyn) => {
668+
G::consider_builtin_try_as_dyn_candidate(self, goal)
669+
}
649670
Some(SolverTraitLangItem::Field) => G::consider_builtin_field_candidate(self, goal),
650671
_ => Err(NoSolution),
651672
}
@@ -832,6 +853,19 @@ where
832853
return;
833854
}
834855

856+
// Builtin impls regularly are not `is_fully_generic_for_reflection`, so instead
857+
// of trying to handle these manually, we just reject all builtin impls in reflection
858+
// mode. We can probably lift this restriction for specific cases, but this is safer.
859+
// See `try_as_dyn_builtin_impl` for how just allowing all builtin impls is unsound.
860+
match self.typing_mode() {
861+
TypingMode::Reflection => return,
862+
TypingMode::Coherence
863+
| TypingMode::Analysis { .. }
864+
| TypingMode::Borrowck { .. }
865+
| TypingMode::PostBorrowckAnalysis { .. }
866+
| TypingMode::PostAnalysis => {}
867+
}
868+
835869
let self_ty = goal.predicate.self_ty();
836870
let bounds = match self_ty.kind() {
837871
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
@@ -441,6 +441,13 @@ where
441441
unreachable!("BikeshedGuaranteedNoDrop is not const");
442442
}
443443

444+
fn consider_builtin_try_as_dyn_candidate(
445+
_ecx: &mut EvalCtxt<'_, D>,
446+
goal: Goal<I, Self>,
447+
) -> Result<Candidate<I>, NoSolution> {
448+
unreachable!("`TryAsDynCompat` is not const: {:?}", goal)
449+
}
450+
444451
fn consider_structural_builtin_unsize_candidates(
445452
_ecx: &mut EvalCtxt<'_, D>,
446453
_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
@@ -972,6 +972,13 @@ where
972972
unreachable!("`BikeshedGuaranteedNoDrop` does not have an associated type: {:?}", goal)
973973
}
974974

975+
fn consider_builtin_try_as_dyn_candidate(
976+
_ecx: &mut EvalCtxt<'_, D>,
977+
goal: Goal<I, Self>,
978+
) -> Result<Candidate<I>, NoSolution> {
979+
unreachable!("`TryAsDynCompat` does not have an associated type: {:?}", goal)
980+
}
981+
975982
fn consider_builtin_field_candidate(
976983
ecx: &mut EvalCtxt<'_, D>,
977984
goal: Goal<I, Self>,

compiler/rustc_next_trait_solver/src/solve/trait_goals.rs

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,16 @@ where
8686
// Impl matches polarity
8787
(ty::ImplPolarity::Positive, ty::PredicatePolarity::Positive)
8888
| (ty::ImplPolarity::Negative, ty::PredicatePolarity::Negative) => {
89-
if let TypingMode::Reflection = ecx.typing_mode()
90-
&& !cx.is_fully_generic_for_reflection(impl_def_id)
91-
{
92-
return Err(NoSolution);
93-
} else {
94-
Certainty::Yes
89+
match ecx.typing_mode() {
90+
TypingMode::Reflection if !cx.is_fully_generic_for_reflection(impl_def_id) => {
91+
return Err(NoSolution);
92+
}
93+
TypingMode::Coherence
94+
| TypingMode::Analysis { .. }
95+
| TypingMode::Borrowck { .. }
96+
| TypingMode::PostBorrowckAnalysis { .. }
97+
| TypingMode::PostAnalysis
98+
| TypingMode::Reflection => Certainty::Yes,
9599
}
96100
}
97101

@@ -865,6 +869,39 @@ where
865869
})
866870
}
867871

872+
fn consider_builtin_try_as_dyn_candidate(
873+
ecx: &mut EvalCtxt<'_, D>,
874+
goal: Goal<I, Self>,
875+
) -> Result<Candidate<I>, NoSolution> {
876+
if goal.predicate.polarity != ty::PredicatePolarity::Positive {
877+
return Err(NoSolution);
878+
}
879+
let cx = ecx.cx();
880+
881+
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
882+
let self_ty = goal.predicate.self_ty();
883+
let ty_lifetime = goal.predicate.trait_ref.args.region_at(1);
884+
match self_ty.kind() {
885+
ty::Dynamic(_bounds, lifetime) => {
886+
ecx.add_goal(
887+
GoalSource::Misc,
888+
goal.with(cx, ty::OutlivesPredicate(ty_lifetime, lifetime)),
889+
);
890+
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
891+
}
892+
893+
ty::Bound(..)
894+
| ty::Infer(
895+
ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_),
896+
) => {
897+
panic!("unexpected type `{self_ty:?}`")
898+
}
899+
900+
_ => Err(NoSolution),
901+
}
902+
})
903+
}
904+
868905
fn consider_builtin_field_candidate(
869906
ecx: &mut EvalCtxt<'_, D>,
870907
goal: Goal<I, Self>,

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2068,6 +2068,7 @@ symbols! {
20682068
truncf32,
20692069
truncf64,
20702070
truncf128,
2071+
try_as_dyn,
20712072
try_blocks,
20722073
try_blocks_heterogeneous,
20732074
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)