diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index d9e3bdaf266..4ecd504b31d 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -2652,23 +2652,32 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { as Box + 'a> } - /// Returns true if the impls are the same polarity and are implementing - /// a trait which contains no items + /// Returns true if the impls are the same polarity and the trait either + /// has no items or is annotated #[marker] and prevents item overrides. pub fn impls_are_allowed_to_overlap(self, def_id1: DefId, def_id2: DefId) -> bool { - if !self.features().overlapping_marker_traits { - return false; + if self.features().overlapping_marker_traits { + let trait1_is_empty = self.impl_trait_ref(def_id1) + .map_or(false, |trait_ref| { + self.associated_item_def_ids(trait_ref.def_id).is_empty() + }); + let trait2_is_empty = self.impl_trait_ref(def_id2) + .map_or(false, |trait_ref| { + self.associated_item_def_ids(trait_ref.def_id).is_empty() + }); + self.impl_polarity(def_id1) == self.impl_polarity(def_id2) + && trait1_is_empty + && trait2_is_empty + } else if self.features().marker_trait_attr { + let is_marker_impl = |def_id: DefId| -> bool { + let trait_ref = self.impl_trait_ref(def_id); + trait_ref.map_or(false, |tr| self.trait_def(tr.def_id).is_marker) + }; + self.impl_polarity(def_id1) == self.impl_polarity(def_id2) + && is_marker_impl(def_id1) + && is_marker_impl(def_id2) + } else { + false } - let trait1_is_empty = self.impl_trait_ref(def_id1) - .map_or(false, |trait_ref| { - self.associated_item_def_ids(trait_ref.def_id).is_empty() - }); - let trait2_is_empty = self.impl_trait_ref(def_id2) - .map_or(false, |trait_ref| { - self.associated_item_def_ids(trait_ref.def_id).is_empty() - }); - self.impl_polarity(def_id1) == self.impl_polarity(def_id2) - && trait1_is_empty - && trait2_is_empty } // Returns `ty::VariantDef` if `def` refers to a struct, diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs index 168cf47fb52..ecb7a8ea8b6 100644 --- a/src/librustc_typeck/coherence/mod.rs +++ b/src/librustc_typeck/coherence/mod.rs @@ -46,6 +46,7 @@ fn check_impl<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, node_id: ast::NodeId) { } enforce_trait_manually_implementable(tcx, impl_def_id, trait_ref.def_id); + enforce_empty_impls_for_marker_traits(tcx, impl_def_id, trait_ref.def_id); } } @@ -99,6 +100,25 @@ fn enforce_trait_manually_implementable(tcx: TyCtxt, impl_def_id: DefId, trait_d .emit(); } +/// We allow impls of marker traits to overlap, so they can't override impls +/// as that could make it ambiguous which associated item to use. +fn enforce_empty_impls_for_marker_traits(tcx: TyCtxt, impl_def_id: DefId, trait_def_id: DefId) { + if !tcx.trait_def(trait_def_id).is_marker { + return; + } + + if tcx.associated_item_def_ids(trait_def_id).is_empty() { + return; + } + + let span = tcx.sess.source_map().def_span(tcx.span_of_impl(impl_def_id).unwrap()); + struct_span_err!(tcx.sess, + span, + E0715, + "impls for marker traits cannot contain items") + .emit(); +} + pub fn provide(providers: &mut Providers) { use self::builtin::coerce_unsized_info; use self::inherent_impls::{crate_inherent_impls, inherent_impls}; diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 80c1ca944bb..f51d1b19f06 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -4833,4 +4833,5 @@ register_diagnostics! { E0641, // cannot cast to/from a pointer with an unknown kind E0645, // trait aliases not finished E0698, // type inside generator must be known in this context + E0715, // impls for marker traits cannot contain items } diff --git a/src/test/run-pass/overlap-permitted-for-annotated-marker-traits.rs b/src/test/run-pass/overlap-permitted-for-annotated-marker-traits.rs new file mode 100644 index 00000000000..e1081032c05 --- /dev/null +++ b/src/test/run-pass/overlap-permitted-for-annotated-marker-traits.rs @@ -0,0 +1,35 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Tests for RFC 1268: we allow overlapping impls of marker traits, +// that is, traits with #[marker]. In this case, a type `T` is +// `MyMarker` if it is either `Debug` or `Display`. + +#![feature(marker_trait_attr)] + +use std::fmt::{Debug, Display}; + +#[marker] trait MyMarker {} + +impl MyMarker for T {} +impl MyMarker for T {} + +fn foo(t: T) -> T { + t +} + +fn main() { + // Debug && Display: + assert_eq!(1, foo(1)); + assert_eq!(2.0, foo(2.0)); + + // Debug && !Display: + assert_eq!(vec![1], foo(vec![1])); +} diff --git a/src/test/ui/marker_trait_attr/overlap-marker-trait.rs b/src/test/ui/marker_trait_attr/overlap-marker-trait.rs new file mode 100644 index 00000000000..a3b4fd6aeba --- /dev/null +++ b/src/test/ui/marker_trait_attr/overlap-marker-trait.rs @@ -0,0 +1,38 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test for RFC 1268: we allow overlapping impls of marker traits, +// that is, traits with #[marker]. In this case, a type `T` is +// `MyMarker` if it is either `Debug` or `Display`. This test just +// checks that we don't consider **all** types to be `MyMarker`. + +#![feature(marker_trait_attr)] + +use std::fmt::{Debug, Display}; + +#[marker] trait Marker {} + +impl Marker for T {} +impl Marker for T {} + +fn is_marker() { } + +struct NotDebugOrDisplay; + +fn main() { + // Debug && Display: + is_marker::(); + + // Debug && !Display: + is_marker::>(); + + // !Debug && !Display + is_marker::(); //~ ERROR +} diff --git a/src/test/ui/marker_trait_attr/overlap-marker-trait.stderr b/src/test/ui/marker_trait_attr/overlap-marker-trait.stderr new file mode 100644 index 00000000000..a4c4f0629dd --- /dev/null +++ b/src/test/ui/marker_trait_attr/overlap-marker-trait.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `NotDebugOrDisplay: Marker` is not satisfied + --> $DIR/overlap-marker-trait.rs:37:5 + | +LL | is_marker::(); //~ ERROR + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Marker` is not implemented for `NotDebugOrDisplay` + | +note: required by `is_marker` + --> $DIR/overlap-marker-trait.rs:25:1 + | +LL | fn is_marker() { } + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/marker_trait_attr/override-item-on-marker-trait.rs b/src/test/ui/marker_trait_attr/override-item-on-marker-trait.rs new file mode 100644 index 00000000000..4a9252b38f2 --- /dev/null +++ b/src/test/ui/marker_trait_attr/override-item-on-marker-trait.rs @@ -0,0 +1,33 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(marker_trait_attr)] + +#[marker] +trait Marker { + const N: usize = 0; + fn do_something() {} +} + +struct OverrideConst; +impl Marker for OverrideConst { +//~^ ERROR impls for marker traits cannot contain items + const N: usize = 1; +} + +struct OverrideFn; +impl Marker for OverrideFn { +//~^ ERROR impls for marker traits cannot contain items + fn do_something() { + println!("Hello world!"); + } +} + +fn main() {} diff --git a/src/test/ui/marker_trait_attr/override-item-on-marker-trait.stderr b/src/test/ui/marker_trait_attr/override-item-on-marker-trait.stderr new file mode 100644 index 00000000000..43af997511e --- /dev/null +++ b/src/test/ui/marker_trait_attr/override-item-on-marker-trait.stderr @@ -0,0 +1,15 @@ +error[E0715]: impls for marker traits cannot contain items + --> $DIR/override-item-on-marker-trait.rs:20:1 + | +LL | impl Marker for OverrideConst { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0715]: impls for marker traits cannot contain items + --> $DIR/override-item-on-marker-trait.rs:26:1 + | +LL | impl Marker for OverrideFn { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0715`.