Skip to content

Commit d2aef58

Browse files
Rollup merge of rust-lang#108203 - compiler-errors:rpitit-fix-defaults-2, r=jackh726
Fix RPITITs in default trait methods (by assuming projection predicates in param-env) Instead of having special projection logic that allows us to turn `ProjectionTy(RPITIT, [Self#0, ...])` into `OpaqueTy(RPITIT, [Self#0, ...])`, we can instead augment the param-env of default trait method bodies to assume these as projection predicates. This should allow us to only project where we're allowed to! In order to make this work without introducing a bunch of cycle errors, we additionally tweak the `OpaqueTypeExpander` used by `ParamEnv::with_reveal_all_normalized` to not normalize the right-hand side of projection predicates. This should be fine, because if we use the projection predicate to normalize some other projection type, we'll continue to normalize the opaque that it gets projected to. This also makes it possible to support default trait methods with RPITITs in an associated-type based RPITIT lowering strategy without too much extra effort. Fixes rust-lang#107002 Alternative to rust-lang#108142
2 parents 243dcd0 + 3e57b20 commit d2aef58

File tree

6 files changed

+161
-37
lines changed

6 files changed

+161
-37
lines changed

compiler/rustc_hir_analysis/src/check/wfcheck.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1599,7 +1599,7 @@ fn check_return_position_impl_trait_in_trait_bounds<'tcx>(
15991599
{
16001600
for arg in fn_output.walk() {
16011601
if let ty::GenericArgKind::Type(ty) = arg.unpack()
1602-
&& let ty::Alias(ty::Projection, proj) = ty.kind()
1602+
&& let ty::Alias(ty::Opaque, proj) = ty.kind()
16031603
&& tcx.def_kind(proj.def_id) == DefKind::ImplTraitPlaceholder
16041604
&& tcx.impl_trait_in_trait_parent(proj.def_id) == fn_def_id.to_def_id()
16051605
{

compiler/rustc_middle/src/ty/util.rs

+21-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
44
use crate::mir;
55
use crate::ty::layout::IntegerExt;
66
use crate::ty::{
7-
self, ir::TypeFolder, DefIdTree, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable,
7+
self, ir::TypeFolder, DefIdTree, FallibleTypeFolder, ToPredicate, Ty, TyCtxt, TypeFoldable,
88
TypeSuperFoldable,
99
};
1010
use crate::ty::{GenericArgKind, SubstsRef};
@@ -865,6 +865,26 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for OpaqueTypeExpander<'tcx> {
865865
}
866866
t
867867
}
868+
869+
fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
870+
if let ty::PredicateKind::Clause(clause) = p.kind().skip_binder()
871+
&& let ty::Clause::Projection(projection_pred) = clause
872+
{
873+
p.kind()
874+
.rebind(ty::ProjectionPredicate {
875+
projection_ty: projection_pred.projection_ty.fold_with(self),
876+
// Don't fold the term on the RHS of the projection predicate.
877+
// This is because for default trait methods with RPITITs, we
878+
// install a `NormalizesTo(Projection(RPITIT) -> Opaque(RPITIT))`
879+
// predicate, which would trivially cause a cycle when we do
880+
// anything that requires `ParamEnv::with_reveal_all_normalized`.
881+
term: projection_pred.term,
882+
})
883+
.to_predicate(self.tcx)
884+
} else {
885+
p.super_fold_with(self)
886+
}
887+
}
868888
}
869889

870890
impl<'tcx> Ty<'tcx> {

compiler/rustc_trait_selection/src/traits/project.rs

+3-33
Original file line numberDiff line numberDiff line change
@@ -90,15 +90,7 @@ enum ProjectionCandidate<'tcx> {
9090
/// From an "impl" (or a "pseudo-impl" returned by select)
9191
Select(Selection<'tcx>),
9292

93-
ImplTraitInTrait(ImplTraitInTraitCandidate<'tcx>),
94-
}
95-
96-
#[derive(PartialEq, Eq, Debug)]
97-
enum ImplTraitInTraitCandidate<'tcx> {
98-
// The `impl Trait` from a trait function's default body
99-
Trait,
100-
// A concrete type provided from a trait's `impl Trait` from an impl
101-
Impl(ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>>),
93+
ImplTraitInTrait(ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>>),
10294
}
10395

10496
enum ProjectionCandidateSet<'tcx> {
@@ -1292,17 +1284,6 @@ fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>(
12921284
let tcx = selcx.tcx();
12931285
if tcx.def_kind(obligation.predicate.def_id) == DefKind::ImplTraitPlaceholder {
12941286
let trait_fn_def_id = tcx.impl_trait_in_trait_parent(obligation.predicate.def_id);
1295-
// If we are trying to project an RPITIT with trait's default `Self` parameter,
1296-
// then we must be within a default trait body.
1297-
if obligation.predicate.self_ty()
1298-
== ty::InternalSubsts::identity_for_item(tcx, obligation.predicate.def_id).type_at(0)
1299-
&& tcx.associated_item(trait_fn_def_id).defaultness(tcx).has_value()
1300-
{
1301-
candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait(
1302-
ImplTraitInTraitCandidate::Trait,
1303-
));
1304-
return;
1305-
}
13061287

13071288
let trait_def_id = tcx.parent(trait_fn_def_id);
13081289
let trait_substs =
@@ -1313,9 +1294,7 @@ fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>(
13131294
let _ = selcx.infcx.commit_if_ok(|_| {
13141295
match selcx.select(&obligation.with(tcx, trait_predicate)) {
13151296
Ok(Some(super::ImplSource::UserDefined(data))) => {
1316-
candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait(
1317-
ImplTraitInTraitCandidate::Impl(data),
1318-
));
1297+
candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait(data));
13191298
Ok(())
13201299
}
13211300
Ok(None) => {
@@ -1777,18 +1756,9 @@ fn confirm_candidate<'cx, 'tcx>(
17771756
ProjectionCandidate::Select(impl_source) => {
17781757
confirm_select_candidate(selcx, obligation, impl_source)
17791758
}
1780-
ProjectionCandidate::ImplTraitInTrait(ImplTraitInTraitCandidate::Impl(data)) => {
1759+
ProjectionCandidate::ImplTraitInTrait(data) => {
17811760
confirm_impl_trait_in_trait_candidate(selcx, obligation, data)
17821761
}
1783-
// If we're projecting an RPITIT for a default trait body, that's just
1784-
// the same def-id, but as an opaque type (with regular RPIT semantics).
1785-
ProjectionCandidate::ImplTraitInTrait(ImplTraitInTraitCandidate::Trait) => Progress {
1786-
term: selcx
1787-
.tcx()
1788-
.mk_opaque(obligation.predicate.def_id, obligation.predicate.substs)
1789-
.into(),
1790-
obligations: vec![],
1791-
},
17921762
};
17931763

17941764
// When checking for cycle during evaluation, we compare predicates with

compiler/rustc_ty_utils/src/ty.rs

+59-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
use rustc_data_structures::fx::FxIndexSet;
1+
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
22
use rustc_hir as hir;
3+
use rustc_hir::def::DefKind;
34
use rustc_index::bit_set::BitSet;
5+
#[cfg(not(bootstrap))]
6+
use rustc_middle::ty::ir::TypeVisitable;
47
use rustc_middle::ty::{
5-
self, Binder, EarlyBinder, Predicate, PredicateKind, ToPredicate, Ty, TyCtxt,
8+
self, ir::TypeVisitor, Binder, EarlyBinder, Predicate, PredicateKind, ToPredicate, Ty, TyCtxt,
9+
TypeSuperVisitable,
610
};
711
use rustc_session::config::TraitSolver;
812
use rustc_span::def_id::{DefId, CRATE_DEF_ID};
@@ -136,6 +140,19 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> {
136140
predicates.extend(environment);
137141
}
138142

143+
if tcx.def_kind(def_id) == DefKind::AssocFn
144+
&& tcx.associated_item(def_id).container == ty::AssocItemContainer::TraitContainer
145+
{
146+
let sig = tcx.fn_sig(def_id).subst_identity();
147+
sig.visit_with(&mut ImplTraitInTraitFinder {
148+
tcx,
149+
fn_def_id: def_id,
150+
bound_vars: sig.bound_vars(),
151+
predicates: &mut predicates,
152+
seen: FxHashSet::default(),
153+
});
154+
}
155+
139156
let local_did = def_id.as_local();
140157
let hir_id = local_did.map(|def_id| tcx.hir().local_def_id_to_hir_id(def_id));
141158

@@ -222,6 +239,46 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> {
222239
traits::normalize_param_env_or_error(tcx, unnormalized_env, cause)
223240
}
224241

242+
/// Walk through a function type, gathering all RPITITs and installing a
243+
/// `NormalizesTo(Projection(RPITIT) -> Opaque(RPITIT))` predicate into the
244+
/// predicates list. This allows us to observe that an RPITIT projects to
245+
/// its corresponding opaque within the body of a default-body trait method.
246+
struct ImplTraitInTraitFinder<'a, 'tcx> {
247+
tcx: TyCtxt<'tcx>,
248+
predicates: &'a mut Vec<Predicate<'tcx>>,
249+
fn_def_id: DefId,
250+
bound_vars: &'tcx ty::List<ty::BoundVariableKind>,
251+
seen: FxHashSet<DefId>,
252+
}
253+
254+
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitFinder<'_, 'tcx> {
255+
fn visit_ty(&mut self, ty: Ty<'tcx>) -> std::ops::ControlFlow<Self::BreakTy> {
256+
if let ty::Alias(ty::Projection, alias_ty) = *ty.kind()
257+
&& self.tcx.def_kind(alias_ty.def_id) == DefKind::ImplTraitPlaceholder
258+
&& self.tcx.impl_trait_in_trait_parent(alias_ty.def_id) == self.fn_def_id
259+
&& self.seen.insert(alias_ty.def_id)
260+
{
261+
self.predicates.push(
262+
ty::Binder::bind_with_vars(
263+
ty::ProjectionPredicate {
264+
projection_ty: alias_ty,
265+
term: self.tcx.mk_alias(ty::Opaque, alias_ty).into(),
266+
},
267+
self.bound_vars,
268+
)
269+
.to_predicate(self.tcx),
270+
);
271+
272+
for bound in self.tcx.item_bounds(alias_ty.def_id).subst_iter(self.tcx, alias_ty.substs)
273+
{
274+
bound.visit_with(self);
275+
}
276+
}
277+
278+
ty.super_visit_with(self)
279+
}
280+
}
281+
225282
/// Elaborate the environment.
226283
///
227284
/// Collect a list of `Predicate`'s used for building the `ParamEnv`. Adds `TypeWellFormedFromEnv`'s
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// run-pass
2+
// edition:2021
3+
4+
#![feature(async_fn_in_trait)]
5+
//~^ WARN the feature `async_fn_in_trait` is incomplete and may not be safe to use
6+
7+
use std::future::Future;
8+
9+
trait AsyncTrait {
10+
async fn default_impl() {
11+
assert!(false);
12+
}
13+
14+
async fn call_default_impl() {
15+
Self::default_impl().await
16+
}
17+
}
18+
19+
struct AsyncType;
20+
21+
impl AsyncTrait for AsyncType {
22+
async fn default_impl() {
23+
// :)
24+
}
25+
}
26+
27+
async fn async_main() {
28+
// Should not assert false
29+
AsyncType::call_default_impl().await;
30+
}
31+
32+
// ------------------------------------------------------------------------- //
33+
// Implementation Details Below...
34+
35+
use std::pin::Pin;
36+
use std::task::*;
37+
38+
pub fn noop_waker() -> Waker {
39+
let raw = RawWaker::new(std::ptr::null(), &NOOP_WAKER_VTABLE);
40+
41+
// SAFETY: the contracts for RawWaker and RawWakerVTable are upheld
42+
unsafe { Waker::from_raw(raw) }
43+
}
44+
45+
const NOOP_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(noop_clone, noop, noop, noop);
46+
47+
unsafe fn noop_clone(_p: *const ()) -> RawWaker {
48+
RawWaker::new(std::ptr::null(), &NOOP_WAKER_VTABLE)
49+
}
50+
51+
unsafe fn noop(_p: *const ()) {}
52+
53+
fn main() {
54+
let mut fut = async_main();
55+
56+
// Poll loop, just to test the future...
57+
let waker = noop_waker();
58+
let ctx = &mut Context::from_waker(&waker);
59+
60+
loop {
61+
match unsafe { Pin::new_unchecked(&mut fut).poll(ctx) } {
62+
Poll::Pending => {}
63+
Poll::Ready(()) => break,
64+
}
65+
}
66+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
warning: the feature `async_fn_in_trait` is incomplete and may not be safe to use and/or cause compiler crashes
2+
--> $DIR/async-default-fn-overridden.rs:4:12
3+
|
4+
LL | #![feature(async_fn_in_trait)]
5+
| ^^^^^^^^^^^^^^^^^
6+
|
7+
= note: see issue #91611 <https://github.com/rust-lang/rust/issues/91611> for more information
8+
= note: `#[warn(incomplete_features)]` on by default
9+
10+
warning: 1 warning emitted
11+

0 commit comments

Comments
 (0)