Skip to content

Commit 3a6c542

Browse files
authored
Rollup merge of rust-lang#108319 - compiler-errors:dont-project-to-specializable-rpitits, r=lcnr
Don't project specializable RPITIT projection This effective rejects specialization + RPITIT/AFIT (usages of `impl Trait` in traits) because the implementation is significantly complicated over making regular "default" trait method bodies work. I have another PR that experimentally fixes all this, but the code may not be worth investing in.
2 parents 58136ff + 9bf32c4 commit 3a6c542

File tree

4 files changed

+126
-5
lines changed

4 files changed

+126
-5
lines changed

compiler/rustc_hir_analysis/src/check/compare_impl_item.rs

+7
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,13 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
648648
tcx.fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs),
649649
)
650650
.fold_with(&mut collector);
651+
652+
debug_assert_ne!(
653+
collector.types.len(),
654+
0,
655+
"expect >1 RPITITs in call to `collect_return_position_impl_trait_in_trait_tys`"
656+
);
657+
651658
let trait_sig = ocx.normalize(&norm_cause, param_env, unnormalized_trait_sig);
652659
trait_sig.error_reported()?;
653660
let trait_return_ty = trait_sig.output();

compiler/rustc_trait_selection/src/traits/project.rs

+22-5
Original file line numberDiff line numberDiff line change
@@ -1307,21 +1307,38 @@ fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>(
13071307
let _ = selcx.infcx.commit_if_ok(|_| {
13081308
match selcx.select(&obligation.with(tcx, trait_predicate)) {
13091309
Ok(Some(super::ImplSource::UserDefined(data))) => {
1310-
candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait(data));
1311-
Ok(())
1310+
let Ok(leaf_def) = specialization_graph::assoc_def(tcx, data.impl_def_id, trait_fn_def_id) else {
1311+
return Err(());
1312+
};
1313+
// Only reveal a specializable default if we're past type-checking
1314+
// and the obligation is monomorphic, otherwise passes such as
1315+
// transmute checking and polymorphic MIR optimizations could
1316+
// get a result which isn't correct for all monomorphizations.
1317+
if leaf_def.is_final()
1318+
|| (obligation.param_env.reveal() == Reveal::All
1319+
&& !selcx
1320+
.infcx
1321+
.resolve_vars_if_possible(obligation.predicate.trait_ref(tcx))
1322+
.still_further_specializable())
1323+
{
1324+
candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait(data));
1325+
Ok(())
1326+
} else {
1327+
Err(())
1328+
}
13121329
}
13131330
Ok(None) => {
13141331
candidate_set.mark_ambiguous();
1315-
return Err(());
1332+
Err(())
13161333
}
13171334
Ok(Some(_)) => {
13181335
// Don't know enough about the impl to provide a useful signature
1319-
return Err(());
1336+
Err(())
13201337
}
13211338
Err(e) => {
13221339
debug!(error = ?e, "selection error");
13231340
candidate_set.mark_error(e);
1324-
return Err(());
1341+
Err(())
13251342
}
13261343
}
13271344
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// edition: 2021
2+
// known-bug: #108309
3+
4+
#![feature(async_fn_in_trait)]
5+
#![feature(min_specialization)]
6+
7+
struct MyStruct;
8+
9+
trait MyTrait<T> {
10+
async fn foo(_: T) -> &'static str;
11+
}
12+
13+
impl<T> MyTrait<T> for MyStruct {
14+
default async fn foo(_: T) -> &'static str {
15+
"default"
16+
}
17+
}
18+
19+
impl MyTrait<i32> for MyStruct {
20+
async fn foo(_: i32) -> &'static str {
21+
"specialized"
22+
}
23+
}
24+
25+
async fn async_main() {
26+
assert_eq!(MyStruct::foo(42).await, "specialized");
27+
assert_eq!(indirection(42).await, "specialized");
28+
}
29+
30+
async fn indirection<T>(x: T) -> &'static str {
31+
//explicit type coercion is currently necessary
32+
// because of https://github.com/rust-lang/rust/issues/67918
33+
<MyStruct as MyTrait<T>>::foo(x).await
34+
}
35+
36+
// ------------------------------------------------------------------------- //
37+
// Implementation Details Below...
38+
39+
use std::future::Future;
40+
use std::pin::Pin;
41+
use std::task::*;
42+
43+
pub fn noop_waker() -> Waker {
44+
let raw = RawWaker::new(std::ptr::null(), &NOOP_WAKER_VTABLE);
45+
46+
// SAFETY: the contracts for RawWaker and RawWakerVTable are upheld
47+
unsafe { Waker::from_raw(raw) }
48+
}
49+
50+
const NOOP_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(noop_clone, noop, noop, noop);
51+
52+
unsafe fn noop_clone(_p: *const ()) -> RawWaker {
53+
RawWaker::new(std::ptr::null(), &NOOP_WAKER_VTABLE)
54+
}
55+
56+
unsafe fn noop(_p: *const ()) {}
57+
58+
fn main() {
59+
let mut fut = async_main();
60+
61+
// Poll loop, just to test the future...
62+
let waker = noop_waker();
63+
let ctx = &mut Context::from_waker(&waker);
64+
65+
loop {
66+
match unsafe { Pin::new_unchecked(&mut fut).poll(ctx) } {
67+
Poll::Pending => {}
68+
Poll::Ready(()) => break,
69+
}
70+
}
71+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
warning: the feature `async_fn_in_trait` is incomplete and may not be safe to use and/or cause compiler crashes
2+
--> $DIR/dont-project-to-specializable-projection.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+
error[E0053]: method `foo` has an incompatible type for trait
11+
--> $DIR/dont-project-to-specializable-projection.rs:14:35
12+
|
13+
LL | default async fn foo(_: T) -> &'static str {
14+
| ^^^^^^^^^^^^ expected associated type, found future
15+
|
16+
note: type in trait
17+
--> $DIR/dont-project-to-specializable-projection.rs:10:27
18+
|
19+
LL | async fn foo(_: T) -> &'static str;
20+
| ^^^^^^^^^^^^
21+
= note: expected signature `fn(_) -> impl Future<Output = &'static str>`
22+
found signature `fn(_) -> impl Future<Output = &'static str>`
23+
24+
error: aborting due to previous error; 1 warning emitted
25+
26+
For more information about this error, try `rustc --explain E0053`.

0 commit comments

Comments
 (0)