Skip to content

Commit 7cee7ee

Browse files
committed
Require that marker impls are empty, but allow them to overlap
1 parent 6149a83 commit 7cee7ee

File tree

8 files changed

+181
-15
lines changed

8 files changed

+181
-15
lines changed

src/librustc/ty/mod.rs

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2652,23 +2652,32 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
26522652
as Box<dyn Iterator<Item = AssociatedItem> + 'a>
26532653
}
26542654

2655-
/// Returns true if the impls are the same polarity and are implementing
2656-
/// a trait which contains no items
2655+
/// Returns true if the impls are the same polarity and the trait either
2656+
/// has no items or is annotated #[marker] and prevents item overrides.
26572657
pub fn impls_are_allowed_to_overlap(self, def_id1: DefId, def_id2: DefId) -> bool {
2658-
if !self.features().overlapping_marker_traits {
2659-
return false;
2658+
if self.features().overlapping_marker_traits {
2659+
let trait1_is_empty = self.impl_trait_ref(def_id1)
2660+
.map_or(false, |trait_ref| {
2661+
self.associated_item_def_ids(trait_ref.def_id).is_empty()
2662+
});
2663+
let trait2_is_empty = self.impl_trait_ref(def_id2)
2664+
.map_or(false, |trait_ref| {
2665+
self.associated_item_def_ids(trait_ref.def_id).is_empty()
2666+
});
2667+
self.impl_polarity(def_id1) == self.impl_polarity(def_id2)
2668+
&& trait1_is_empty
2669+
&& trait2_is_empty
2670+
} else if self.features().marker_trait_attr {
2671+
let is_marker_impl = |def_id: DefId| -> bool {
2672+
let trait_ref = self.impl_trait_ref(def_id);
2673+
trait_ref.map_or(false, |tr| self.trait_def(tr.def_id).is_marker)
2674+
};
2675+
self.impl_polarity(def_id1) == self.impl_polarity(def_id2)
2676+
&& is_marker_impl(def_id1)
2677+
&& is_marker_impl(def_id2)
2678+
} else {
2679+
false
26602680
}
2661-
let trait1_is_empty = self.impl_trait_ref(def_id1)
2662-
.map_or(false, |trait_ref| {
2663-
self.associated_item_def_ids(trait_ref.def_id).is_empty()
2664-
});
2665-
let trait2_is_empty = self.impl_trait_ref(def_id2)
2666-
.map_or(false, |trait_ref| {
2667-
self.associated_item_def_ids(trait_ref.def_id).is_empty()
2668-
});
2669-
self.impl_polarity(def_id1) == self.impl_polarity(def_id2)
2670-
&& trait1_is_empty
2671-
&& trait2_is_empty
26722681
}
26732682

26742683
// Returns `ty::VariantDef` if `def` refers to a struct,

src/librustc_typeck/coherence/mod.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ fn check_impl<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, node_id: ast::NodeId) {
4646
}
4747

4848
enforce_trait_manually_implementable(tcx, impl_def_id, trait_ref.def_id);
49+
enforce_empty_impls_for_marker_traits(tcx, impl_def_id, trait_ref.def_id);
4950
}
5051
}
5152

@@ -99,6 +100,25 @@ fn enforce_trait_manually_implementable(tcx: TyCtxt, impl_def_id: DefId, trait_d
99100
.emit();
100101
}
101102

103+
/// We allow impls of marker traits to overlap, so they can't override impls
104+
/// as that could make it ambiguous which associated item to use.
105+
fn enforce_empty_impls_for_marker_traits(tcx: TyCtxt, impl_def_id: DefId, trait_def_id: DefId) {
106+
if !tcx.trait_def(trait_def_id).is_marker {
107+
return;
108+
}
109+
110+
if tcx.associated_item_def_ids(trait_def_id).is_empty() {
111+
return;
112+
}
113+
114+
let span = tcx.sess.source_map().def_span(tcx.span_of_impl(impl_def_id).unwrap());
115+
struct_span_err!(tcx.sess,
116+
span,
117+
E0715,
118+
"impls for marker traits cannot contain items")
119+
.emit();
120+
}
121+
102122
pub fn provide(providers: &mut Providers) {
103123
use self::builtin::coerce_unsized_info;
104124
use self::inherent_impls::{crate_inherent_impls, inherent_impls};

src/librustc_typeck/diagnostics.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4833,4 +4833,5 @@ register_diagnostics! {
48334833
E0641, // cannot cast to/from a pointer with an unknown kind
48344834
E0645, // trait aliases not finished
48354835
E0698, // type inside generator must be known in this context
4836+
E0715, // impls for marker traits cannot contain items
48364837
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Tests for RFC 1268: we allow overlapping impls of marker traits,
12+
// that is, traits with #[marker]. In this case, a type `T` is
13+
// `MyMarker` if it is either `Debug` or `Display`.
14+
15+
#![feature(marker_trait_attr)]
16+
17+
use std::fmt::{Debug, Display};
18+
19+
#[marker] trait MyMarker {}
20+
21+
impl<T: Debug> MyMarker for T {}
22+
impl<T: Display> MyMarker for T {}
23+
24+
fn foo<T: MyMarker>(t: T) -> T {
25+
t
26+
}
27+
28+
fn main() {
29+
// Debug && Display:
30+
assert_eq!(1, foo(1));
31+
assert_eq!(2.0, foo(2.0));
32+
33+
// Debug && !Display:
34+
assert_eq!(vec![1], foo(vec![1]));
35+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test for RFC 1268: we allow overlapping impls of marker traits,
12+
// that is, traits with #[marker]. In this case, a type `T` is
13+
// `MyMarker` if it is either `Debug` or `Display`. This test just
14+
// checks that we don't consider **all** types to be `MyMarker`.
15+
16+
#![feature(marker_trait_attr)]
17+
18+
use std::fmt::{Debug, Display};
19+
20+
#[marker] trait Marker {}
21+
22+
impl<T: Debug> Marker for T {}
23+
impl<T: Display> Marker for T {}
24+
25+
fn is_marker<T: Marker>() { }
26+
27+
struct NotDebugOrDisplay;
28+
29+
fn main() {
30+
// Debug && Display:
31+
is_marker::<i32>();
32+
33+
// Debug && !Display:
34+
is_marker::<Vec<i32>>();
35+
36+
// !Debug && !Display
37+
is_marker::<NotDebugOrDisplay>(); //~ ERROR
38+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0277]: the trait bound `NotDebugOrDisplay: Marker` is not satisfied
2+
--> $DIR/overlap-marker-trait.rs:37:5
3+
|
4+
LL | is_marker::<NotDebugOrDisplay>(); //~ ERROR
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Marker` is not implemented for `NotDebugOrDisplay`
6+
|
7+
note: required by `is_marker`
8+
--> $DIR/overlap-marker-trait.rs:25:1
9+
|
10+
LL | fn is_marker<T: Marker>() { }
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
12+
13+
error: aborting due to previous error
14+
15+
For more information about this error, try `rustc --explain E0277`.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(marker_trait_attr)]
12+
13+
#[marker]
14+
trait Marker {
15+
const N: usize = 0;
16+
fn do_something() {}
17+
}
18+
19+
struct OverrideConst;
20+
impl Marker for OverrideConst {
21+
//~^ ERROR impls for marker traits cannot contain items
22+
const N: usize = 1;
23+
}
24+
25+
struct OverrideFn;
26+
impl Marker for OverrideFn {
27+
//~^ ERROR impls for marker traits cannot contain items
28+
fn do_something() {
29+
println!("Hello world!");
30+
}
31+
}
32+
33+
fn main() {}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0715]: impls for marker traits cannot contain items
2+
--> $DIR/override-item-on-marker-trait.rs:20:1
3+
|
4+
LL | impl Marker for OverrideConst {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
7+
error[E0715]: impls for marker traits cannot contain items
8+
--> $DIR/override-item-on-marker-trait.rs:26:1
9+
|
10+
LL | impl Marker for OverrideFn {
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
13+
error: aborting due to 2 previous errors
14+
15+
For more information about this error, try `rustc --explain E0715`.

0 commit comments

Comments
 (0)