about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2023-02-28 02:03:26 +0000
committerMichael Goulet <michael@errs.io>2023-02-28 02:03:43 +0000
commitecac8fd5afbb4ca8a98f8b0d3b547608e04cfc44 (patch)
tree43e93cb3e088c8affc4a5763a4168fe31556317f
parent6290ae92b2df2bfff09abdcb80a3aa483692bab6 (diff)
downloadrust-ecac8fd5afbb4ca8a98f8b0d3b547608e04cfc44.tar.gz
rust-ecac8fd5afbb4ca8a98f8b0d3b547608e04cfc44.zip
Descriptive error when users try to combine RPITIT/AFIT with specialization
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs40
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs30
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs28
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs24
-rw-r--r--tests/ui/async-await/in-trait/dont-project-to-specializable-projection.stderr15
-rw-r--r--tests/ui/impl-trait/in-trait/specialization-broken.rs1
-rw-r--r--tests/ui/impl-trait/in-trait/specialization-broken.stderr10
7 files changed, 83 insertions, 65 deletions
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 4ea471f8f05..848828175e2 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -792,8 +792,10 @@ fn check_impl_items_against_trait<'tcx>(
             trait_def.must_implement_one_of.as_deref();
 
         for &trait_item_id in tcx.associated_item_def_ids(impl_trait_ref.def_id) {
-            let is_implemented = ancestors
-                .leaf_def(tcx, trait_item_id)
+            let leaf_def = ancestors.leaf_def(tcx, trait_item_id);
+
+            let is_implemented = leaf_def
+                .as_ref()
                 .map_or(false, |node_item| node_item.item.defaultness(tcx).has_value());
 
             if !is_implemented && tcx.impl_defaultness(impl_id).is_final() {
@@ -801,8 +803,8 @@ fn check_impl_items_against_trait<'tcx>(
             }
 
             // true if this item is specifically implemented in this impl
-            let is_implemented_here = ancestors
-                .leaf_def(tcx, trait_item_id)
+            let is_implemented_here = leaf_def
+                .as_ref()
                 .map_or(false, |node_item| !node_item.defining_node.is_from_trait());
 
             if !is_implemented_here {
@@ -831,6 +833,36 @@ fn check_impl_items_against_trait<'tcx>(
                     }
                 }
             }
+
+            if let Some(leaf_def) = &leaf_def
+                && !leaf_def.is_final()
+                && let def_id = leaf_def.item.def_id
+                && tcx.impl_method_has_trait_impl_trait_tys(def_id)
+            {
+                let def_kind = tcx.def_kind(def_id);
+                let descr = tcx.def_kind_descr(def_kind, def_id);
+                let (msg, feature) = if tcx.asyncness(def_id).is_async() {
+                    (
+                        format!("async {descr} in trait cannot be specialized"),
+                        sym::async_fn_in_trait,
+                    )
+                } else {
+                    (
+                        format!(
+                            "{descr} with return-position `impl Trait` in trait cannot be specialized"
+                        ),
+                        sym::return_position_impl_trait_in_trait,
+                    )
+                };
+                tcx.sess
+                    .struct_span_err(tcx.def_span(def_id), msg)
+                    .note(format!(
+                        "specialization behaves in inconsistent and \
+                        surprising ways with `#![feature({feature})]`, \
+                        and for now is disallowed"
+                    ))
+                    .emit();
+            }
         }
 
         if !missing_items.is_empty() {
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index f0dafe73c00..27490a09a36 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -1101,34 +1101,6 @@ fn should_encode_const(def_kind: DefKind) -> bool {
     }
 }
 
-fn should_encode_trait_impl_trait_tys(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
-    if tcx.def_kind(def_id) != DefKind::AssocFn {
-        return false;
-    }
-
-    let Some(item) = tcx.opt_associated_item(def_id) else { return false; };
-    if item.container != ty::AssocItemContainer::ImplContainer {
-        return false;
-    }
-
-    let Some(trait_item_def_id) = item.trait_item_def_id else { return false; };
-
-    // FIXME(RPITIT): This does a somewhat manual walk through the signature
-    // of the trait fn to look for any RPITITs, but that's kinda doing a lot
-    // of work. We can probably remove this when we refactor RPITITs to be
-    // associated types.
-    tcx.fn_sig(trait_item_def_id).subst_identity().skip_binder().output().walk().any(|arg| {
-        if let ty::GenericArgKind::Type(ty) = arg.unpack()
-            && let ty::Alias(ty::Projection, data) = ty.kind()
-            && tcx.def_kind(data.def_id) == DefKind::ImplTraitPlaceholder
-        {
-            true
-        } else {
-            false
-        }
-    })
-}
-
 // Return `false` to avoid encoding impl trait in trait, while we don't use the query.
 fn should_encode_fn_impl_trait_in_trait<'tcx>(_tcx: TyCtxt<'tcx>, _def_id: DefId) -> bool {
     false
@@ -1211,7 +1183,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             if let DefKind::Enum | DefKind::Struct | DefKind::Union = def_kind {
                 self.encode_info_for_adt(def_id);
             }
-            if should_encode_trait_impl_trait_tys(tcx, def_id)
+            if tcx.impl_method_has_trait_impl_trait_tys(def_id)
                 && let Ok(table) = self.tcx.collect_return_position_impl_trait_in_trait_tys(def_id)
             {
                 record!(self.tables.trait_impl_trait_tys[def_id] <- table);
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index fc5757564a7..5084bc9cec6 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -2541,6 +2541,34 @@ impl<'tcx> TyCtxt<'tcx> {
         }
         def_id
     }
+
+    pub fn impl_method_has_trait_impl_trait_tys(self, def_id: DefId) -> bool {
+        if self.def_kind(def_id) != DefKind::AssocFn {
+            return false;
+        }
+
+        let Some(item) = self.opt_associated_item(def_id) else { return false; };
+        if item.container != ty::AssocItemContainer::ImplContainer {
+            return false;
+        }
+
+        let Some(trait_item_def_id) = item.trait_item_def_id else { return false; };
+
+        // FIXME(RPITIT): This does a somewhat manual walk through the signature
+        // of the trait fn to look for any RPITITs, but that's kinda doing a lot
+        // of work. We can probably remove this when we refactor RPITITs to be
+        // associated types.
+        self.fn_sig(trait_item_def_id).subst_identity().skip_binder().output().walk().any(|arg| {
+            if let ty::GenericArgKind::Type(ty) = arg.unpack()
+                && let ty::Alias(ty::Projection, data) = ty.kind()
+                && self.def_kind(data.def_id) == DefKind::ImplTraitPlaceholder
+            {
+                true
+            } else {
+                false
+            }
+        })
+    }
 }
 
 /// Yields the parent function's `LocalDefId` if `def_id` is an `impl Trait` definition.
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index d542240be9b..013db2edb39 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -1307,25 +1307,8 @@ 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))) => {
-                    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(())
-                    }
+                    candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait(data));
+                    Ok(())
                 }
                 Ok(None) => {
                     candidate_set.mark_ambiguous();
@@ -2216,7 +2199,8 @@ fn confirm_impl_trait_in_trait_candidate<'tcx>(
         Ok(assoc_ty) => assoc_ty,
         Err(guar) => return Progress::error(tcx, guar),
     };
-    if !leaf_def.item.defaultness(tcx).has_value() {
+    // We don't support specialization for RPITITs anyways... yet.
+    if !leaf_def.is_final() {
         return Progress { term: tcx.ty_error_misc().into(), obligations };
     }
 
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
index 371122ea71e..f71fd9980a2 100644
--- 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
@@ -7,20 +7,13 @@ 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
+error: async associated function in trait cannot be specialized
+  --> $DIR/dont-project-to-specializable-projection.rs:14:5
    |
 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>`
+   = note: specialization behaves in inconsistent and surprising ways with `#![feature(async_fn_in_trait)]`, and for now is disallowed
 
 error: aborting due to previous error; 1 warning emitted
 
-For more information about this error, try `rustc --explain E0053`.
diff --git a/tests/ui/impl-trait/in-trait/specialization-broken.rs b/tests/ui/impl-trait/in-trait/specialization-broken.rs
index 9d27d3710a6..2fcffdf3f9a 100644
--- a/tests/ui/impl-trait/in-trait/specialization-broken.rs
+++ b/tests/ui/impl-trait/in-trait/specialization-broken.rs
@@ -15,6 +15,7 @@ where
 {
     fn bar(&self) -> U {
         //~^ ERROR method `bar` has an incompatible type for trait
+        //~| ERROR method with return-position `impl Trait` in trait cannot be specialized
         *self
     }
 }
diff --git a/tests/ui/impl-trait/in-trait/specialization-broken.stderr b/tests/ui/impl-trait/in-trait/specialization-broken.stderr
index 37cfd74498d..dc621d6b8a8 100644
--- a/tests/ui/impl-trait/in-trait/specialization-broken.stderr
+++ b/tests/ui/impl-trait/in-trait/specialization-broken.stderr
@@ -18,6 +18,14 @@ LL |     fn bar(&self) -> impl Sized;
    = note: expected signature `fn(&U) -> impl Sized`
               found signature `fn(&U) -> U`
 
-error: aborting due to previous error
+error: method with return-position `impl Trait` in trait cannot be specialized
+  --> $DIR/specialization-broken.rs:16:5
+   |
+LL |     fn bar(&self) -> U {
+   |     ^^^^^^^^^^^^^^^^^^
+   |
+   = note: specialization behaves in inconsistent and surprising ways with `#![feature(return_position_impl_trait_in_trait)]`, and for now is disallowed
+
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0053`.