about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2023-02-21 18:27:17 +0000
committerMichael Goulet <michael@errs.io>2023-02-23 02:12:51 +0000
commit9bf32c40b4c1b7558aee3421ed3e8e73ef518dd9 (patch)
treec41aba5c5a5e475749366249a8f8820364411a3d
parent02b3664766f1053746a3068c6eed6c464d8a40dc (diff)
downloadrust-9bf32c40b4c1b7558aee3421ed3e8e73ef518dd9.tar.gz
rust-9bf32c40b4c1b7558aee3421ed3e8e73ef518dd9.zip
Don't project specializable RPITIT projection
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs27
-rw-r--r--tests/ui/async-await/in-trait/dont-project-to-specializable-projection.rs71
-rw-r--r--tests/ui/async-await/in-trait/dont-project-to-specializable-projection.stderr26
3 files changed, 119 insertions, 5 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 279725b16d8..c9e46ba41b0 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -1307,21 +1307,38 @@ fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>(
         let _ = selcx.infcx.commit_if_ok(|_| {
             match selcx.select(&obligation.with(tcx, trait_predicate)) {
                 Ok(Some(super::ImplSource::UserDefined(data))) => {
-                    candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait(data));
-                    Ok(())
+                    let Ok(leaf_def) = specialization_graph::assoc_def(tcx, data.impl_def_id, trait_fn_def_id) else {
+                        return Err(());
+                    };
+                    // Only reveal a specializable default if we're past type-checking
+                    // and the obligation is monomorphic, otherwise passes such as
+                    // transmute checking and polymorphic MIR optimizations could
+                    // get a result which isn't correct for all monomorphizations.
+                    if leaf_def.is_final()
+                        || (obligation.param_env.reveal() == Reveal::All
+                            && !selcx
+                                .infcx
+                                .resolve_vars_if_possible(obligation.predicate.trait_ref(tcx))
+                                .still_further_specializable())
+                    {
+                        candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait(data));
+                        Ok(())
+                    } else {
+                        Err(())
+                    }
                 }
                 Ok(None) => {
                     candidate_set.mark_ambiguous();
-                    return Err(());
+                    Err(())
                 }
                 Ok(Some(_)) => {
                     // Don't know enough about the impl to provide a useful signature
-                    return Err(());
+                    Err(())
                 }
                 Err(e) => {
                     debug!(error = ?e, "selection error");
                     candidate_set.mark_error(e);
-                    return Err(());
+                    Err(())
                 }
             }
         });
diff --git a/tests/ui/async-await/in-trait/dont-project-to-specializable-projection.rs b/tests/ui/async-await/in-trait/dont-project-to-specializable-projection.rs
new file mode 100644
index 00000000000..afd3db5e052
--- /dev/null
+++ b/tests/ui/async-await/in-trait/dont-project-to-specializable-projection.rs
@@ -0,0 +1,71 @@
+// edition: 2021
+// known-bug: #108309
+
+#![feature(async_fn_in_trait)]
+#![feature(min_specialization)]
+
+struct MyStruct;
+
+trait MyTrait<T> {
+    async fn foo(_: T) -> &'static str;
+}
+
+impl<T> MyTrait<T> for MyStruct {
+    default async fn foo(_: T) -> &'static str {
+        "default"
+    }
+}
+
+impl MyTrait<i32> for MyStruct {
+    async fn foo(_: i32) -> &'static str {
+        "specialized"
+    }
+}
+
+async fn async_main() {
+    assert_eq!(MyStruct::foo(42).await, "specialized");
+    assert_eq!(indirection(42).await, "specialized");
+}
+
+async fn indirection<T>(x: T) -> &'static str {
+    //explicit type coercion is currently necessary
+    // because of https://github.com/rust-lang/rust/issues/67918
+    <MyStruct as MyTrait<T>>::foo(x).await
+}
+
+// ------------------------------------------------------------------------- //
+// Implementation Details Below...
+
+use std::future::Future;
+use std::pin::Pin;
+use std::task::*;
+
+pub fn noop_waker() -> Waker {
+    let raw = RawWaker::new(std::ptr::null(), &NOOP_WAKER_VTABLE);
+
+    // SAFETY: the contracts for RawWaker and RawWakerVTable are upheld
+    unsafe { Waker::from_raw(raw) }
+}
+
+const NOOP_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(noop_clone, noop, noop, noop);
+
+unsafe fn noop_clone(_p: *const ()) -> RawWaker {
+    RawWaker::new(std::ptr::null(), &NOOP_WAKER_VTABLE)
+}
+
+unsafe fn noop(_p: *const ()) {}
+
+fn main() {
+    let mut fut = async_main();
+
+    // Poll loop, just to test the future...
+    let waker = noop_waker();
+    let ctx = &mut Context::from_waker(&waker);
+
+    loop {
+        match unsafe { Pin::new_unchecked(&mut fut).poll(ctx) } {
+            Poll::Pending => {}
+            Poll::Ready(()) => break,
+        }
+    }
+}
diff --git a/tests/ui/async-await/in-trait/dont-project-to-specializable-projection.stderr b/tests/ui/async-await/in-trait/dont-project-to-specializable-projection.stderr
new file mode 100644
index 00000000000..371122ea71e
--- /dev/null
+++ b/tests/ui/async-await/in-trait/dont-project-to-specializable-projection.stderr
@@ -0,0 +1,26 @@
+warning: the feature `async_fn_in_trait` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/dont-project-to-specializable-projection.rs:4:12
+   |
+LL | #![feature(async_fn_in_trait)]
+   |            ^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #91611 <https://github.com/rust-lang/rust/issues/91611> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+error[E0053]: method `foo` has an incompatible type for trait
+  --> $DIR/dont-project-to-specializable-projection.rs:14:35
+   |
+LL |     default async fn foo(_: T) -> &'static str {
+   |                                   ^^^^^^^^^^^^ expected associated type, found future
+   |
+note: type in trait
+  --> $DIR/dont-project-to-specializable-projection.rs:10:27
+   |
+LL |     async fn foo(_: T) -> &'static str;
+   |                           ^^^^^^^^^^^^
+   = note: expected signature `fn(_) -> impl Future<Output = &'static str>`
+              found signature `fn(_) -> impl Future<Output = &'static str>`
+
+error: aborting due to previous error; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0053`.