Skip to content

Commit 9e1c600

Browse files
committed
Disallow impl autotrait for trait object
1 parent a94b9fd commit 9e1c600

6 files changed

+267
-68
lines changed

compiler/rustc_data_structures/src/sync.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ cfg_if! {
3131
pub auto trait Send {}
3232
pub auto trait Sync {}
3333

34-
impl<T: ?Sized> Send for T {}
35-
impl<T: ?Sized> Sync for T {}
34+
impl<T> Send for T {}
35+
impl<T> Sync for T {}
3636

3737
#[macro_export]
3838
macro_rules! rustc_erase_owner {

compiler/rustc_hir_analysis/src/coherence/orphan.rs

+161-40
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rustc_hir as hir;
88
use rustc_middle::ty::subst::InternalSubsts;
99
use rustc_middle::ty::util::IgnoreRegions;
1010
use rustc_middle::ty::{
11-
self, ImplPolarity, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor,
11+
self, AliasKind, ImplPolarity, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor,
1212
};
1313
use rustc_session::lint;
1414
use rustc_span::def_id::{DefId, LocalDefId};
@@ -86,7 +86,7 @@ fn do_orphan_check_impl<'tcx>(
8686
// struct B { }
8787
// impl Foo for A { }
8888
// impl Foo for B { }
89-
// impl !Send for (A, B) { }
89+
// impl !Foo for (A, B) { }
9090
// ```
9191
//
9292
// This final impl is legal according to the orphan
@@ -99,50 +99,171 @@ fn do_orphan_check_impl<'tcx>(
9999
tcx.trait_is_auto(trait_def_id)
100100
);
101101

102-
if tcx.trait_is_auto(trait_def_id) && !trait_def_id.is_local() {
102+
if tcx.trait_is_auto(trait_def_id) {
103103
let self_ty = trait_ref.self_ty();
104-
let opt_self_def_id = match *self_ty.kind() {
105-
ty::Adt(self_def, _) => Some(self_def.did()),
106-
ty::Foreign(did) => Some(did),
107-
_ => None,
108-
};
109104

110-
let msg = match opt_self_def_id {
111-
// We only want to permit nominal types, but not *all* nominal types.
112-
// They must be local to the current crate, so that people
113-
// can't do `unsafe impl Send for Rc<SomethingLocal>` or
114-
// `impl !Send for Box<SomethingLocalAndSend>`.
115-
Some(self_def_id) => {
116-
if self_def_id.is_local() {
117-
None
105+
// If the impl is in the same crate as the auto-trait, almost anything
106+
// goes.
107+
//
108+
// impl MyAuto for Rc<Something> {} // okay
109+
// impl<T> !MyAuto for *const T {} // okay
110+
// impl<T> MyAuto for T {} // okay
111+
//
112+
// But there is one important exception: implementing for a trait object
113+
// is not allowed.
114+
//
115+
// impl MyAuto for dyn Trait {} // NOT OKAY
116+
// impl<T: ?Sized> MyAuto for T {} // NOT OKAY
117+
//
118+
enum LocalImpl {
119+
Allow,
120+
Disallow { problematic_kind: &'static str },
121+
}
122+
123+
// If the auto-trait is from a dependency, it must only be getting
124+
// implemented for a nominal type, and specifically one local to the
125+
// current crate.
126+
//
127+
// impl<T> Sync for MyStruct<T> {} // okay
128+
//
129+
// impl Sync for Rc<MyStruct> {} // NOT OKAY
130+
enum NonlocalImpl {
131+
Allow,
132+
DisallowBecauseNonlocal,
133+
DisallowOther,
134+
}
135+
136+
// Exhaustive match considering that this logic is essential for
137+
// soundness.
138+
let (local_impl, nonlocal_impl) = match self_ty.kind() {
139+
// struct Struct<T>;
140+
// impl AutoTrait for Struct<Foo> {}
141+
ty::Adt(self_def, _) => (
142+
LocalImpl::Allow,
143+
if self_def.did().is_local() {
144+
NonlocalImpl::Allow
145+
} else {
146+
NonlocalImpl::DisallowBecauseNonlocal
147+
},
148+
),
149+
150+
// extern { type OpaqueType; }
151+
// impl AutoTrait for OpaqueType {}
152+
ty::Foreign(did) => (
153+
LocalImpl::Allow,
154+
if did.is_local() {
155+
NonlocalImpl::Allow
118156
} else {
119-
Some((
120-
format!(
121-
"cross-crate traits with a default impl, like `{}`, \
122-
can only be implemented for a struct/enum type \
123-
defined in the current crate",
124-
tcx.def_path_str(trait_def_id)
125-
),
126-
"can't implement cross-crate trait for type in another crate",
127-
))
157+
NonlocalImpl::DisallowBecauseNonlocal
158+
},
159+
),
160+
161+
// impl AutoTrait for dyn Trait {}
162+
ty::Dynamic(..) => (
163+
LocalImpl::Disallow { problematic_kind: "trait object" },
164+
NonlocalImpl::DisallowOther,
165+
),
166+
167+
// impl<T> AutoTrait for T {}
168+
// impl<T: ?Sized> AutoTrait for T {}
169+
ty::Param(..) => (
170+
if self_ty.is_sized(tcx, tcx.param_env(def_id)) {
171+
LocalImpl::Allow
172+
} else {
173+
LocalImpl::Disallow { problematic_kind: "generic type" }
174+
},
175+
NonlocalImpl::DisallowOther,
176+
),
177+
178+
// trait Id { type This: ?Sized; }
179+
// impl<T: ?Sized> Id for T {
180+
// type This = T;
181+
// }
182+
// impl<T: ?Sized> AutoTrait for <T as Id>::This {}
183+
ty::Alias(AliasKind::Projection, _) => (
184+
LocalImpl::Disallow { problematic_kind: "associated type" },
185+
NonlocalImpl::DisallowOther,
186+
),
187+
188+
// type Opaque = impl Trait;
189+
// impl AutoTrait for Opaque {}
190+
ty::Alias(AliasKind::Opaque, _) => (
191+
LocalImpl::Disallow { problematic_kind: "opaque type" },
192+
NonlocalImpl::DisallowOther,
193+
),
194+
195+
ty::Bool
196+
| ty::Char
197+
| ty::Int(..)
198+
| ty::Uint(..)
199+
| ty::Float(..)
200+
| ty::Str
201+
| ty::Array(..)
202+
| ty::Slice(..)
203+
| ty::RawPtr(..)
204+
| ty::Ref(..)
205+
| ty::FnDef(..)
206+
| ty::FnPtr(..)
207+
| ty::Never
208+
| ty::Tuple(..) => (LocalImpl::Allow, NonlocalImpl::DisallowOther),
209+
210+
ty::Closure(..)
211+
| ty::Generator(..)
212+
| ty::GeneratorWitness(..)
213+
| ty::GeneratorWitnessMIR(..)
214+
| ty::Bound(..)
215+
| ty::Placeholder(..)
216+
| ty::Infer(..) => span_bug!(sp, "weird self type for autotrait impl"),
217+
218+
ty::Error(..) => (LocalImpl::Allow, NonlocalImpl::Allow),
219+
};
220+
221+
if trait_def_id.is_local() {
222+
match local_impl {
223+
LocalImpl::Allow => {}
224+
LocalImpl::Disallow { problematic_kind } => {
225+
let msg = format!(
226+
"traits with a default impl, like `{trait}`, \
227+
cannot be implemented for {problematic_kind} `{self_ty}`",
228+
trait = tcx.def_path_str(trait_def_id),
229+
);
230+
let label = format!(
231+
"a trait object implements `{trait}` if and only if `{trait}` \
232+
is one of the trait object's trait bounds",
233+
trait = tcx.def_path_str(trait_def_id),
234+
);
235+
let reported =
236+
struct_span_err!(tcx.sess, sp, E0321, "{}", msg).note(label).emit();
237+
return Err(reported);
128238
}
129239
}
130-
_ => Some((
131-
format!(
132-
"cross-crate traits with a default impl, like `{}`, can \
240+
} else {
241+
if let Some((msg, label)) = match nonlocal_impl {
242+
NonlocalImpl::Allow => None,
243+
NonlocalImpl::DisallowBecauseNonlocal => Some((
244+
format!(
245+
"cross-crate traits with a default impl, like `{}`, \
246+
can only be implemented for a struct/enum type \
247+
defined in the current crate",
248+
tcx.def_path_str(trait_def_id)
249+
),
250+
"can't implement cross-crate trait for type in another crate",
251+
)),
252+
NonlocalImpl::DisallowOther => Some((
253+
format!(
254+
"cross-crate traits with a default impl, like `{}`, can \
133255
only be implemented for a struct/enum type, not `{}`",
134-
tcx.def_path_str(trait_def_id),
135-
self_ty
136-
),
137-
"can't implement cross-crate trait with a default impl for \
138-
non-struct/enum type",
139-
)),
140-
};
141-
142-
if let Some((msg, label)) = msg {
143-
let reported =
144-
struct_span_err!(tcx.sess, sp, E0321, "{}", msg).span_label(sp, label).emit();
145-
return Err(reported);
256+
tcx.def_path_str(trait_def_id),
257+
self_ty
258+
),
259+
"can't implement cross-crate trait with a default impl for \
260+
non-struct/enum type",
261+
)),
262+
} {
263+
let reported =
264+
struct_span_err!(tcx.sess, sp, E0321, "{}", msg).span_label(sp, label).emit();
265+
return Err(reported);
266+
}
146267
}
147268
}
148269

tests/ui/coherence/coherence-impl-trait-for-marker-trait-negative.rs

+15-8
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,26 @@ auto trait Marker2 {}
1212
trait Object: Marker1 {}
1313

1414
// A supertrait marker is illegal...
15-
impl !Marker1 for dyn Object + Marker2 { } //~ ERROR E0371
15+
impl !Marker1 for dyn Object + Marker2 {} //~ ERROR E0371
16+
//~^ ERROR 0321
1617
// ...and also a direct component.
17-
impl !Marker2 for dyn Object + Marker2 { } //~ ERROR E0371
18-
19-
// But implementing a marker if it is not present is OK.
20-
impl !Marker2 for dyn Object {} // OK
18+
impl !Marker2 for dyn Object + Marker2 {} //~ ERROR E0371
19+
//~^ ERROR 0321
2120

2221
// A non-principal trait-object type is orphan even in its crate.
2322
impl !Send for dyn Marker2 {} //~ ERROR E0117
2423

25-
// And impl'ing a remote marker for a local trait object is forbidden
26-
// by one of these special orphan-like rules.
24+
// Implementing a marker for a local trait object is forbidden by a special
25+
// orphan-like rule.
26+
impl !Marker2 for dyn Object {} //~ ERROR E0321
2727
impl !Send for dyn Object {} //~ ERROR E0321
2828
impl !Send for dyn Object + Marker2 {} //~ ERROR E0321
2929

30-
fn main() { }
30+
// Blanket impl that applies to dyn Object is equally problematic.
31+
auto trait Marker3 {}
32+
impl<T: ?Sized> !Marker3 for T {} //~ ERROR E0321
33+
34+
auto trait Marker4 {}
35+
impl<T> !Marker4 for T {} // okay
36+
37+
fn main() {}

tests/ui/coherence/coherence-impl-trait-for-marker-trait-negative.stderr

+37-5
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,41 @@
11
error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker1`
22
--> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:15:1
33
|
4-
LL | impl !Marker1 for dyn Object + Marker2 { }
4+
LL | impl !Marker1 for dyn Object + Marker2 {}
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker1`
66

7+
error[E0321]: traits with a default impl, like `Marker1`, cannot be implemented for trait object `(dyn Object + Marker2 + 'static)`
8+
--> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:15:1
9+
|
10+
LL | impl !Marker1 for dyn Object + Marker2 {}
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
|
13+
= note: a trait object implements `Marker1` if and only if `Marker1` is one of the trait object's trait bounds
14+
715
error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker2`
8-
--> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:17:1
16+
--> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:18:1
917
|
10-
LL | impl !Marker2 for dyn Object + Marker2 { }
18+
LL | impl !Marker2 for dyn Object + Marker2 {}
1119
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker2`
1220

21+
error[E0321]: traits with a default impl, like `Marker2`, cannot be implemented for trait object `(dyn Object + Marker2 + 'static)`
22+
--> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:18:1
23+
|
24+
LL | impl !Marker2 for dyn Object + Marker2 {}
25+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
26+
|
27+
= note: a trait object implements `Marker2` if and only if `Marker2` is one of the trait object's trait bounds
28+
29+
error[E0321]: traits with a default impl, like `Marker2`, cannot be implemented for trait object `(dyn Object + 'static)`
30+
--> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:26:1
31+
|
32+
LL | impl !Marker2 for dyn Object {}
33+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
34+
|
35+
= note: a trait object implements `Marker2` if and only if `Marker2` is one of the trait object's trait bounds
36+
1337
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
14-
--> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:23:1
38+
--> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:22:1
1539
|
1640
LL | impl !Send for dyn Marker2 {}
1741
| ^^^^^^^^^^^^^^^-----------
@@ -33,7 +57,15 @@ error[E0321]: cross-crate traits with a default impl, like `Send`, can only be i
3357
LL | impl !Send for dyn Object + Marker2 {}
3458
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type
3559

36-
error: aborting due to 5 previous errors
60+
error[E0321]: traits with a default impl, like `Marker3`, cannot be implemented for generic type `T`
61+
--> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:32:1
62+
|
63+
LL | impl<T: ?Sized> !Marker3 for T {}
64+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
65+
|
66+
= note: a trait object implements `Marker3` if and only if `Marker3` is one of the trait object's trait bounds
67+
68+
error: aborting due to 9 previous errors
3769

3870
Some errors have detailed explanations: E0117, E0321, E0371.
3971
For more information about an error, try `rustc --explain E0117`.

tests/ui/coherence/coherence-impl-trait-for-marker-trait-positive.rs

+15-8
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,26 @@ auto trait Marker2 {}
1212
trait Object: Marker1 {}
1313

1414
// A supertrait marker is illegal...
15-
impl Marker1 for dyn Object + Marker2 { } //~ ERROR E0371
15+
impl Marker1 for dyn Object + Marker2 {} //~ ERROR E0371
16+
//~^ ERROR E0321
1617
// ...and also a direct component.
17-
impl Marker2 for dyn Object + Marker2 { } //~ ERROR E0371
18-
19-
// But implementing a marker if it is not present is OK.
20-
impl Marker2 for dyn Object {} // OK
18+
impl Marker2 for dyn Object + Marker2 {} //~ ERROR E0371
19+
//~^ ERROR E0321
2120

2221
// A non-principal trait-object type is orphan even in its crate.
2322
unsafe impl Send for dyn Marker2 {} //~ ERROR E0117
2423

25-
// And impl'ing a remote marker for a local trait object is forbidden
26-
// by one of these special orphan-like rules.
24+
// Implementing a marker for a local trait object is forbidden by a special
25+
// orphan-like rule.
26+
impl Marker2 for dyn Object {} //~ ERROR E0321
2727
unsafe impl Send for dyn Object {} //~ ERROR E0321
2828
unsafe impl Send for dyn Object + Marker2 {} //~ ERROR E0321
2929

30-
fn main() { }
30+
// Blanket impl that applies to dyn Object is equally problematic.
31+
auto trait Marker3 {}
32+
impl<T: ?Sized> Marker3 for T {} //~ ERROR E0321
33+
34+
auto trait Marker4 {}
35+
impl<T> Marker4 for T {} // okay
36+
37+
fn main() {}

0 commit comments

Comments
 (0)