Skip to content

Commit 943f454

Browse files
committed
Arbitrary self types v2: avoid dyn, generic ICE
Bug rust-lang#57276 (and several duplicates) is an ICE which occurs when a generic type is used as a receiver which implements both Receiver and DispatchFromDyn. This change proposes to detect this error condition and turn it into a regular error rather than an ICE. Future changes could liberalise things here. As this same code path currently produces an ICE, this seems to be strictly better, and it seems defensible to inform the user that their excessively generic type is not dyn-safe. This is somewhat related to the stabilization of arbitrary self types in PR rust-lang#135881, tracked in rust-lang#44874.
1 parent aa6f5ab commit 943f454

File tree

6 files changed

+61
-26
lines changed

6 files changed

+61
-26
lines changed

compiler/rustc_middle/src/traits/mod.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -786,6 +786,9 @@ pub enum DynCompatibilityViolation {
786786

787787
/// GAT
788788
GAT(Symbol, Span),
789+
790+
/// Type layout can't be determined
791+
TooGeneric(Span),
789792
}
790793

791794
impl DynCompatibilityViolation {
@@ -853,16 +856,18 @@ impl DynCompatibilityViolation {
853856
DynCompatibilityViolation::GAT(name, _) => {
854857
format!("it contains the generic associated type `{name}`").into()
855858
}
859+
DynCompatibilityViolation::TooGeneric(_span) => {
860+
format!("it is too generic to determine type layout").into()
861+
}
856862
}
857863
}
858864

859865
pub fn solution(&self) -> DynCompatibilityViolationSolution {
860866
match self {
861867
DynCompatibilityViolation::SizedSelf(_)
862868
| DynCompatibilityViolation::SupertraitSelf(_)
863-
| DynCompatibilityViolation::SupertraitNonLifetimeBinder(..) => {
864-
DynCompatibilityViolationSolution::None
865-
}
869+
| DynCompatibilityViolation::SupertraitNonLifetimeBinder(..)
870+
| DynCompatibilityViolation::TooGeneric(..) => DynCompatibilityViolationSolution::None,
866871
DynCompatibilityViolation::Method(
867872
name,
868873
MethodViolationCode::StaticMethod(Some((add_self_sugg, make_sized_sugg))),

compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs

+16-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ use super::elaborate;
2727
use crate::infer::TyCtxtInferExt;
2828
pub use crate::traits::DynCompatibilityViolation;
2929
use crate::traits::query::evaluate_obligation::InferCtxtExt;
30+
use crate::traits::ty::layout::LayoutError;
3031
use crate::traits::{MethodViolationCode, Obligation, ObligationCause, util};
3132

3233
/// Returns the dyn-compatibility violations that affect HIR ty lowering.
@@ -112,7 +113,7 @@ fn dyn_compatibility_violations_for_trait(
112113
if violations.is_empty() {
113114
for item in tcx.associated_items(trait_def_id).in_definition_order() {
114115
if let ty::AssocKind::Fn = item.kind {
115-
check_receiver_correct(tcx, trait_def_id, *item);
116+
check_receiver_correct(tcx, trait_def_id, *item, &mut violations);
116117
}
117118
}
118119
}
@@ -501,9 +502,16 @@ fn virtual_call_violations_for_method<'tcx>(
501502

502503
/// This code checks that `receiver_is_dispatchable` is correctly implemented.
503504
///
505+
/// Any errors are appended to `violations`.
506+
///
504507
/// This check is outlined from the dyn-compatibility check to avoid cycles with
505508
/// layout computation, which relies on knowing whether methods are dyn-compatible.
506-
fn check_receiver_correct<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, method: ty::AssocItem) {
509+
fn check_receiver_correct<'tcx>(
510+
tcx: TyCtxt<'tcx>,
511+
trait_def_id: DefId,
512+
method: ty::AssocItem,
513+
violations: &mut Vec<DynCompatibilityViolation>,
514+
) {
507515
if !is_vtable_safe_method(tcx, trait_def_id, method) {
508516
return;
509517
}
@@ -522,6 +530,9 @@ fn check_receiver_correct<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, method:
522530
let unit_receiver_ty = receiver_for_self_ty(tcx, receiver_ty, tcx.types.unit, method_def_id);
523531
match tcx.layout_of(typing_env.as_query_input(unit_receiver_ty)).map(|l| l.backend_repr) {
524532
Ok(BackendRepr::Scalar(..)) => (),
533+
Err(LayoutError::TooGeneric(..)) => {
534+
violations.push(DynCompatibilityViolation::TooGeneric(tcx.def_span(method_def_id)));
535+
}
525536
abi => {
526537
tcx.dcx().span_delayed_bug(
527538
tcx.def_span(method_def_id),
@@ -537,6 +548,9 @@ fn check_receiver_correct<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, method:
537548
receiver_for_self_ty(tcx, receiver_ty, trait_object_ty, method_def_id);
538549
match tcx.layout_of(typing_env.as_query_input(trait_object_receiver)).map(|l| l.backend_repr) {
539550
Ok(BackendRepr::ScalarPair(..)) => (),
551+
Err(LayoutError::TooGeneric(..)) => {
552+
violations.push(DynCompatibilityViolation::TooGeneric(tcx.def_span(method_def_id)));
553+
}
540554
abi => {
541555
tcx.dcx().span_delayed_bug(
542556
tcx.def_span(method_def_id),

tests/crashes/125810.rs

-10
This file was deleted.

tests/crashes/57276.rs

-11
This file was deleted.
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#![feature(arbitrary_self_types, dispatch_from_dyn)]
2+
3+
use std::ops::{Deref, DispatchFromDyn};
4+
5+
trait Trait<T: Deref<Target=Self> + DispatchFromDyn<T>> {
6+
fn foo(self: T) -> dyn Trait<T>;
7+
//~^ ERROR: associated item referring to unboxed trait object for its own trait
8+
//~| ERROR: the trait `Trait` is not dyn compatible
9+
}
10+
11+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
error: associated item referring to unboxed trait object for its own trait
2+
--> $DIR/dispatch-from-dyn-layout.rs:6:24
3+
|
4+
LL | trait Trait<T: Deref<Target=Self> + DispatchFromDyn<T>> {
5+
| ----- in this trait
6+
LL | fn foo(self: T) -> dyn Trait<T>;
7+
| ^^^^^^^^^^^^
8+
|
9+
help: you might have meant to use `Self` to refer to the implementing type
10+
|
11+
LL | fn foo(self: T) -> Self;
12+
| ~~~~
13+
14+
error[E0038]: the trait `Trait` is not dyn compatible
15+
--> $DIR/dispatch-from-dyn-layout.rs:6:24
16+
|
17+
LL | fn foo(self: T) -> dyn Trait<T>;
18+
| ^^^^^^^^^^^^ `Trait` is not dyn compatible
19+
|
20+
= note: the trait is not dyn compatible because it is too generic to determine type layout
21+
= note: for a trait to be dyn compatible it needs to allow building a vtable
22+
for more information, visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
23+
24+
error: aborting due to 2 previous errors
25+
26+
For more information about this error, try `rustc --explain E0038`.

0 commit comments

Comments
 (0)