Skip to content

Commit b2dc8c5

Browse files
authored
Rollup merge of #108551 - compiler-errors:rpitit-bad-spec, r=oli-obk
Descriptive error when users try to combine RPITIT/AFIT with specialization Previously we failed with some esoteric error like: ``` 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>` ``` Now we error like: ``` 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 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: specialization behaves in inconsistent and surprising ways with `#![feature(async_fn_in_trait)]`, and for now is disallowed ```
2 parents 78f9bb1 + ecac8fd commit b2dc8c5

File tree

7 files changed

+83
-65
lines changed

7 files changed

+83
-65
lines changed

compiler/rustc_hir_analysis/src/check/check.rs

+36-4
Original file line numberDiff line numberDiff line change
@@ -792,17 +792,19 @@ fn check_impl_items_against_trait<'tcx>(
792792
trait_def.must_implement_one_of.as_deref();
793793

794794
for &trait_item_id in tcx.associated_item_def_ids(impl_trait_ref.def_id) {
795-
let is_implemented = ancestors
796-
.leaf_def(tcx, trait_item_id)
795+
let leaf_def = ancestors.leaf_def(tcx, trait_item_id);
796+
797+
let is_implemented = leaf_def
798+
.as_ref()
797799
.map_or(false, |node_item| node_item.item.defaultness(tcx).has_value());
798800

799801
if !is_implemented && tcx.impl_defaultness(impl_id).is_final() {
800802
missing_items.push(tcx.associated_item(trait_item_id));
801803
}
802804

803805
// true if this item is specifically implemented in this impl
804-
let is_implemented_here = ancestors
805-
.leaf_def(tcx, trait_item_id)
806+
let is_implemented_here = leaf_def
807+
.as_ref()
806808
.map_or(false, |node_item| !node_item.defining_node.is_from_trait());
807809

808810
if !is_implemented_here {
@@ -831,6 +833,36 @@ fn check_impl_items_against_trait<'tcx>(
831833
}
832834
}
833835
}
836+
837+
if let Some(leaf_def) = &leaf_def
838+
&& !leaf_def.is_final()
839+
&& let def_id = leaf_def.item.def_id
840+
&& tcx.impl_method_has_trait_impl_trait_tys(def_id)
841+
{
842+
let def_kind = tcx.def_kind(def_id);
843+
let descr = tcx.def_kind_descr(def_kind, def_id);
844+
let (msg, feature) = if tcx.asyncness(def_id).is_async() {
845+
(
846+
format!("async {descr} in trait cannot be specialized"),
847+
sym::async_fn_in_trait,
848+
)
849+
} else {
850+
(
851+
format!(
852+
"{descr} with return-position `impl Trait` in trait cannot be specialized"
853+
),
854+
sym::return_position_impl_trait_in_trait,
855+
)
856+
};
857+
tcx.sess
858+
.struct_span_err(tcx.def_span(def_id), msg)
859+
.note(format!(
860+
"specialization behaves in inconsistent and \
861+
surprising ways with `#![feature({feature})]`, \
862+
and for now is disallowed"
863+
))
864+
.emit();
865+
}
834866
}
835867

836868
if !missing_items.is_empty() {

compiler/rustc_metadata/src/rmeta/encoder.rs

+1-29
Original file line numberDiff line numberDiff line change
@@ -1101,34 +1101,6 @@ fn should_encode_const(def_kind: DefKind) -> bool {
11011101
}
11021102
}
11031103

1104-
fn should_encode_trait_impl_trait_tys(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
1105-
if tcx.def_kind(def_id) != DefKind::AssocFn {
1106-
return false;
1107-
}
1108-
1109-
let Some(item) = tcx.opt_associated_item(def_id) else { return false; };
1110-
if item.container != ty::AssocItemContainer::ImplContainer {
1111-
return false;
1112-
}
1113-
1114-
let Some(trait_item_def_id) = item.trait_item_def_id else { return false; };
1115-
1116-
// FIXME(RPITIT): This does a somewhat manual walk through the signature
1117-
// of the trait fn to look for any RPITITs, but that's kinda doing a lot
1118-
// of work. We can probably remove this when we refactor RPITITs to be
1119-
// associated types.
1120-
tcx.fn_sig(trait_item_def_id).subst_identity().skip_binder().output().walk().any(|arg| {
1121-
if let ty::GenericArgKind::Type(ty) = arg.unpack()
1122-
&& let ty::Alias(ty::Projection, data) = ty.kind()
1123-
&& tcx.def_kind(data.def_id) == DefKind::ImplTraitPlaceholder
1124-
{
1125-
true
1126-
} else {
1127-
false
1128-
}
1129-
})
1130-
}
1131-
11321104
// Return `false` to avoid encoding impl trait in trait, while we don't use the query.
11331105
fn should_encode_fn_impl_trait_in_trait<'tcx>(_tcx: TyCtxt<'tcx>, _def_id: DefId) -> bool {
11341106
false
@@ -1211,7 +1183,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
12111183
if let DefKind::Enum | DefKind::Struct | DefKind::Union = def_kind {
12121184
self.encode_info_for_adt(def_id);
12131185
}
1214-
if should_encode_trait_impl_trait_tys(tcx, def_id)
1186+
if tcx.impl_method_has_trait_impl_trait_tys(def_id)
12151187
&& let Ok(table) = self.tcx.collect_return_position_impl_trait_in_trait_tys(def_id)
12161188
{
12171189
record!(self.tables.trait_impl_trait_tys[def_id] <- table);

compiler/rustc_middle/src/ty/mod.rs

+28
Original file line numberDiff line numberDiff line change
@@ -2541,6 +2541,34 @@ impl<'tcx> TyCtxt<'tcx> {
25412541
}
25422542
def_id
25432543
}
2544+
2545+
pub fn impl_method_has_trait_impl_trait_tys(self, def_id: DefId) -> bool {
2546+
if self.def_kind(def_id) != DefKind::AssocFn {
2547+
return false;
2548+
}
2549+
2550+
let Some(item) = self.opt_associated_item(def_id) else { return false; };
2551+
if item.container != ty::AssocItemContainer::ImplContainer {
2552+
return false;
2553+
}
2554+
2555+
let Some(trait_item_def_id) = item.trait_item_def_id else { return false; };
2556+
2557+
// FIXME(RPITIT): This does a somewhat manual walk through the signature
2558+
// of the trait fn to look for any RPITITs, but that's kinda doing a lot
2559+
// of work. We can probably remove this when we refactor RPITITs to be
2560+
// associated types.
2561+
self.fn_sig(trait_item_def_id).subst_identity().skip_binder().output().walk().any(|arg| {
2562+
if let ty::GenericArgKind::Type(ty) = arg.unpack()
2563+
&& let ty::Alias(ty::Projection, data) = ty.kind()
2564+
&& self.def_kind(data.def_id) == DefKind::ImplTraitPlaceholder
2565+
{
2566+
true
2567+
} else {
2568+
false
2569+
}
2570+
})
2571+
}
25442572
}
25452573

25462574
/// Yields the parent function's `LocalDefId` if `def_id` is an `impl Trait` definition.

compiler/rustc_trait_selection/src/traits/project.rs

+4-20
Original file line numberDiff line numberDiff line change
@@ -1307,25 +1307,8 @@ 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-
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-
}
1310+
candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait(data));
1311+
Ok(())
13291312
}
13301313
Ok(None) => {
13311314
candidate_set.mark_ambiguous();
@@ -2216,7 +2199,8 @@ fn confirm_impl_trait_in_trait_candidate<'tcx>(
22162199
Ok(assoc_ty) => assoc_ty,
22172200
Err(guar) => return Progress::error(tcx, guar),
22182201
};
2219-
if !leaf_def.item.defaultness(tcx).has_value() {
2202+
// We don't support specialization for RPITITs anyways... yet.
2203+
if !leaf_def.is_final() {
22202204
return Progress { term: tcx.ty_error_misc().into(), obligations };
22212205
}
22222206

tests/ui/async-await/in-trait/dont-project-to-specializable-projection.stderr

+4-11
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,13 @@ LL | #![feature(async_fn_in_trait)]
77
= note: see issue #91611 <https://github.com/rust-lang/rust/issues/91611> for more information
88
= note: `#[warn(incomplete_features)]` on by default
99

10-
error[E0053]: method `foo` has an incompatible type for trait
11-
--> $DIR/dont-project-to-specializable-projection.rs:14:35
10+
error: async associated function in trait cannot be specialized
11+
--> $DIR/dont-project-to-specializable-projection.rs:14:5
1212
|
1313
LL | default async fn foo(_: T) -> &'static str {
14-
| ^^^^^^^^^^^^ expected associated type, found future
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1515
|
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>`
16+
= note: specialization behaves in inconsistent and surprising ways with `#![feature(async_fn_in_trait)]`, and for now is disallowed
2317

2418
error: aborting due to previous error; 1 warning emitted
2519

26-
For more information about this error, try `rustc --explain E0053`.

tests/ui/impl-trait/in-trait/specialization-broken.rs

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ where
1515
{
1616
fn bar(&self) -> U {
1717
//~^ ERROR method `bar` has an incompatible type for trait
18+
//~| ERROR method with return-position `impl Trait` in trait cannot be specialized
1819
*self
1920
}
2021
}

tests/ui/impl-trait/in-trait/specialization-broken.stderr

+9-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ LL | fn bar(&self) -> impl Sized;
1818
= note: expected signature `fn(&U) -> impl Sized`
1919
found signature `fn(&U) -> U`
2020

21-
error: aborting due to previous error
21+
error: method with return-position `impl Trait` in trait cannot be specialized
22+
--> $DIR/specialization-broken.rs:16:5
23+
|
24+
LL | fn bar(&self) -> U {
25+
| ^^^^^^^^^^^^^^^^^^
26+
|
27+
= note: specialization behaves in inconsistent and surprising ways with `#![feature(return_position_impl_trait_in_trait)]`, and for now is disallowed
28+
29+
error: aborting due to 2 previous errors
2230

2331
For more information about this error, try `rustc --explain E0053`.

0 commit comments

Comments
 (0)