Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

reserve impl<T> From<!> for T #62661

Merged
merged 13 commits into from
Sep 26, 2019
12 changes: 12 additions & 0 deletions src/libcore/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,18 @@ impl<T> From<T> for T {
fn from(t: T) -> T { t }
}

/// **Stability note:** This impl does not yet exist, but we are
/// "reserving space" to add it in the future. See
/// [rust-lang/rust#64715][#64715] for details.
///
/// [#64715]: https://github.com/rust-lang/rust/issues/64715
#[stable(feature = "convert_infallible", since = "1.34.0")]
#[cfg(not(bootstrap))]
#[rustc_reservation_impl="permitting this impl would forbid us from adding \
`impl<T> From<!> for T` later; see rust-lang/rust#64715 for details"]
impl<T> From<!> for T {
fn from(t: !) -> T { t }
}

// TryFrom implies TryInto
#[stable(feature = "try_from", since = "1.34.0")]
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ rustc_queries! {
query associated_item(_: DefId) -> ty::AssocItem {}

query impl_trait_ref(_: DefId) -> Option<ty::TraitRef<'tcx>> {}
query impl_polarity(_: DefId) -> hir::ImplPolarity {}
query impl_polarity(_: DefId) -> ty::ImplPolarity {}

query issue33140_self_ty(_: DefId) -> Option<ty::Ty<'tcx>> {}
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/traits/auto_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ impl AutoTraitFinder<'tcx> {
match vtable {
Vtable::VtableImpl(VtableImplData { impl_def_id, .. }) => {
// Blame tidy for the weird bracket placement
if infcx.tcx.impl_polarity(*impl_def_id) == hir::ImplPolarity::Negative
if infcx.tcx.impl_polarity(*impl_def_id) == ty::ImplPolarity::Negative
{
debug!("evaluate_nested_obligations: Found explicit negative impl\
{:?}, bailing out", impl_def_id);
Expand Down
58 changes: 48 additions & 10 deletions src/librustc/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ use crate::hir;
use rustc_data_structures::bit_set::GrowableBitSet;
use rustc_data_structures::sync::Lock;
use rustc_target::spec::abi::Abi;
use syntax::attr;
use syntax::symbol::sym;
use std::cell::{Cell, RefCell};
use std::cmp;
use std::fmt::{self, Display};
Expand Down Expand Up @@ -99,6 +101,9 @@ pub enum IntercrateAmbiguityCause {
trait_desc: String,
self_desc: Option<String>,
},
ReservationImpl {
message: String
},
}

impl IntercrateAmbiguityCause {
Expand Down Expand Up @@ -139,6 +144,11 @@ impl IntercrateAmbiguityCause {
trait_desc, self_desc
)
}
&IntercrateAmbiguityCause::ReservationImpl {
ref message
} => {
message.clone()
}
}
}
}
Expand Down Expand Up @@ -1326,17 +1336,38 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
(result, dep_node)
}

// Treat negative impls as unimplemented
fn filter_negative_impls(
&self,
// Treat negative impls as unimplemented, and reservation impls as ambiguity.
fn filter_negative_and_reservation_impls(
&mut self,
candidate: SelectionCandidate<'tcx>,
) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
if let ImplCandidate(def_id) = candidate {
if !self.allow_negative_impls
&& self.tcx().impl_polarity(def_id) == hir::ImplPolarity::Negative
{
return Err(Unimplemented);
}
let tcx = self.tcx();
match tcx.impl_polarity(def_id) {
ty::ImplPolarity::Negative if !self.allow_negative_impls => {
return Err(Unimplemented);
}
ty::ImplPolarity::Reservation => {
if let Some(intercrate_ambiguity_clauses)
= &mut self.intercrate_ambiguity_causes
{
let attrs = tcx.get_attrs(def_id);
let attr = attr::find_by_name(&attrs, sym::rustc_reservation_impl);
let value = attr.and_then(|a| a.value_str());
if let Some(value) = value {
debug!("filter_negative_and_reservation_impls: \
reservation impl ambiguity on {:?}", def_id);
intercrate_ambiguity_clauses.push(
IntercrateAmbiguityCause::ReservationImpl {
message: value.to_string()
}
);
}
}
return Ok(None);
}
_ => {}
};
}
Ok(Some(candidate))
}
Expand Down Expand Up @@ -1453,7 +1484,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// Instead, we select the right impl now but report `Bar does
// not implement Clone`.
if candidates.len() == 1 {
return self.filter_negative_impls(candidates.pop().unwrap());
return self.filter_negative_and_reservation_impls(candidates.pop().unwrap());
}

// Winnow, but record the exact outcome of evaluation, which
Expand Down Expand Up @@ -1528,7 +1559,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}

// Just one candidate left.
self.filter_negative_impls(candidates.pop().unwrap().candidate)
self.filter_negative_and_reservation_impls(candidates.pop().unwrap().candidate)
}

fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Option<Conflict> {
Expand Down Expand Up @@ -3728,6 +3759,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
return Err(());
}

if self.intercrate.is_none()
&& self.tcx().impl_polarity(impl_def_id) == ty::ImplPolarity::Reservation
{
debug!("match_impl: reservation impls only apply in intercrate mode");
return Err(());
}

debug!("match_impl: success impl_substs={:?}", impl_substs);
Ok(Normalized {
value: impl_substs,
Expand Down
51 changes: 40 additions & 11 deletions src/librustc/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,19 @@ pub struct ImplHeader<'tcx> {
pub predicates: Vec<Predicate<'tcx>>,
}

#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, HashStable)]
pub enum ImplPolarity {
/// `impl Trait for Type`
Positive,
/// `impl !Trait for Type`
Negative,
/// `#[rustc_reservation_impl] impl Trait for Type`
///
/// This is a "stability hack", not a real Rust feature.
/// See #64631 for details.
Reservation,
nikomatsakis marked this conversation as resolved.
Show resolved Hide resolved
}

#[derive(Copy, Clone, Debug, PartialEq, HashStable)]
pub struct AssocItem {
pub def_id: DefId,
Expand Down Expand Up @@ -2911,7 +2924,26 @@ impl<'tcx> TyCtxt<'tcx> {
return Some(ImplOverlapKind::Permitted);
}

let is_legit = if self.features().overlapping_marker_traits {
match (self.impl_polarity(def_id1), self.impl_polarity(def_id2)) {
(ImplPolarity::Reservation, _) |
(_, ImplPolarity::Reservation) => {
// `#[rustc_reservation_impl]` impls don't overlap with anything
debug!("impls_are_allowed_to_overlap({:?}, {:?}) = Some(Permitted) (reservations)",
def_id1, def_id2);
return Some(ImplOverlapKind::Permitted);
}
(ImplPolarity::Positive, ImplPolarity::Negative) |
(ImplPolarity::Negative, ImplPolarity::Positive) => {
// `impl AutoTrait for Type` + `impl !AutoTrait for Type`
debug!("impls_are_allowed_to_overlap({:?}, {:?}) - None (differing polarities)",
def_id1, def_id2);
return None;
}
(ImplPolarity::Positive, ImplPolarity::Positive) |
(ImplPolarity::Negative, ImplPolarity::Negative) => {}
};

let is_marker_overlap = 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()
Expand All @@ -2920,22 +2952,19 @@ impl<'tcx> TyCtxt<'tcx> {
.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
trait1_is_empty && trait2_is_empty
} else {
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)
is_marker_impl(def_id1) && is_marker_impl(def_id2)
};

if is_legit {
debug!("impls_are_allowed_to_overlap({:?}, {:?}) = Some(Permitted)",
def_id1, def_id2);

if is_marker_overlap {
debug!("impls_are_allowed_to_overlap({:?}, {:?}) = Some(Permitted) (marker overlap)",
def_id1, def_id2);
Some(ImplOverlapKind::Permitted)
} else {
if let Some(self_ty1) = self.issue33140_self_ty(def_id1) {
Expand Down Expand Up @@ -3317,7 +3346,7 @@ fn issue33140_self_ty(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Ty<'_>> {
debug!("issue33140_self_ty({:?}), trait-ref={:?}", def_id, trait_ref);

let is_marker_like =
tcx.impl_polarity(def_id) == hir::ImplPolarity::Positive &&
tcx.impl_polarity(def_id) == ty::ImplPolarity::Positive &&
tcx.associated_item_def_ids(trait_ref.def_id).is_empty();

// Check whether these impls would be ok for a marker trait.
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_metadata/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -722,7 +722,7 @@ impl<'a, 'tcx> CrateMetadata {
self.get_impl_data(id).parent_impl
}

pub fn get_impl_polarity(&self, id: DefIndex) -> hir::ImplPolarity {
pub fn get_impl_polarity(&self, id: DefIndex) -> ty::ImplPolarity {
self.get_impl_data(id).polarity
}

Expand Down
3 changes: 2 additions & 1 deletion src/librustc_metadata/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1172,8 +1172,9 @@ impl EncodeContext<'tcx> {
ctor_sig: None,
}), repr_options)
}
hir::ItemKind::Impl(_, polarity, defaultness, ..) => {
hir::ItemKind::Impl(_, _, defaultness, ..) => {
let trait_ref = tcx.impl_trait_ref(def_id);
let polarity = tcx.impl_polarity(def_id);
let parent = if let Some(trait_ref) = trait_ref {
let trait_def = tcx.trait_def(trait_ref.def_id);
trait_def.ancestors(tcx, def_id).nth(1).and_then(|node| {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_metadata/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ pub struct TraitAliasData<'tcx> {

#[derive(RustcEncodable, RustcDecodable)]
pub struct ImplData<'tcx> {
pub polarity: hir::ImplPolarity,
pub polarity: ty::ImplPolarity,
pub defaultness: hir::Defaultness,
pub parent_impl: Option<DefId>,

Expand Down
4 changes: 2 additions & 2 deletions src/librustc_traits/lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use rustc::hir::def::DefKind;
use rustc::hir::def_id::DefId;
use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc::hir::map::definitions::DefPathData;
use rustc::hir::{self, ImplPolarity};
use rustc::hir;
use rustc::traits::{
Clause,
Clauses,
Expand Down Expand Up @@ -295,7 +295,7 @@ fn program_clauses_for_trait(tcx: TyCtxt<'_>, def_id: DefId) -> Clauses<'_> {
}

fn program_clauses_for_impl(tcx: TyCtxt<'tcx>, def_id: DefId) -> Clauses<'tcx> {
if let ImplPolarity::Negative = tcx.impl_polarity(def_id) {
if let ty::ImplPolarity::Negative = tcx.impl_polarity(def_id) {
return List::empty();
}

Expand Down
38 changes: 24 additions & 14 deletions src/librustc_typeck/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,20 +94,27 @@ pub fn check_item_well_formed(tcx: TyCtxt<'_>, def_id: DefId) {
//
// won't be allowed unless there's an *explicit* implementation of `Send`
// for `T`
hir::ItemKind::Impl(_, polarity, defaultness, _, ref trait_ref, ref self_ty, _) => {
hir::ItemKind::Impl(_, _, defaultness, _, ref trait_ref, ref self_ty, _) => {
let is_auto = tcx.impl_trait_ref(tcx.hir().local_def_id(item.hir_id))
.map_or(false, |trait_ref| tcx.trait_is_auto(trait_ref.def_id));
.map_or(false, |trait_ref| tcx.trait_is_auto(trait_ref.def_id));
let polarity = tcx.impl_polarity(def_id);
if let (hir::Defaultness::Default { .. }, true) = (defaultness, is_auto) {
tcx.sess.span_err(item.span, "impls of auto traits cannot be default");
}
if polarity == hir::ImplPolarity::Positive {
check_impl(tcx, item, self_ty, trait_ref);
} else {
// FIXME(#27579): what amount of WF checking do we need for neg impls?
if trait_ref.is_some() && !is_auto {
span_err!(tcx.sess, item.span, E0192,
"negative impls are only allowed for \
auto traits (e.g., `Send` and `Sync`)")
match polarity {
ty::ImplPolarity::Positive => {
check_impl(tcx, item, self_ty, trait_ref);
}
ty::ImplPolarity::Negative => {
// FIXME(#27579): what amount of WF checking do we need for neg impls?
if trait_ref.is_some() && !is_auto {
span_err!(tcx.sess, item.span, E0192,
"negative impls are only allowed for \
auto traits (e.g., `Send` and `Sync`)")
}
}
ty::ImplPolarity::Reservation => {
// FIXME: what amount of WF checking do we need for reservation impls?
}
}
}
Expand Down Expand Up @@ -398,16 +405,19 @@ fn check_impl<'tcx>(

match *ast_trait_ref {
Some(ref ast_trait_ref) => {
// `#[rustc_reservation_impl]` impls are not real impls and
// therefore don't need to be WF (the trait's `Self: Trait` predicate
// won't hold).
let trait_ref = fcx.tcx.impl_trait_ref(item_def_id).unwrap();
let trait_ref =
fcx.normalize_associated_types_in(
ast_trait_ref.path.span, &trait_ref);
let obligations =
ty::wf::trait_obligations(fcx,
fcx.param_env,
fcx.body_id,
&trait_ref,
ast_trait_ref.path.span);
fcx.param_env,
fcx.body_id,
&trait_ref,
ast_trait_ref.path.span);
for obligation in obligations {
fcx.register_predicate(obligation);
}
Expand Down
26 changes: 23 additions & 3 deletions src/librustc_typeck/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1866,10 +1866,30 @@ fn impl_trait_ref(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::TraitRef<'_>> {
}
}

fn impl_polarity(tcx: TyCtxt<'_>, def_id: DefId) -> hir::ImplPolarity {
fn impl_polarity(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ImplPolarity {
let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
match tcx.hir().expect_item(hir_id).node {
hir::ItemKind::Impl(_, polarity, ..) => polarity,
let is_rustc_reservation = tcx.has_attr(def_id, sym::rustc_reservation_impl);
let item = tcx.hir().expect_item(hir_id);
match &item.node {
hir::ItemKind::Impl(_, hir::ImplPolarity::Negative, ..) => {
if is_rustc_reservation {
tcx.sess.span_err(item.span, "reservation impls can't be negative");
}
ty::ImplPolarity::Negative
}
hir::ItemKind::Impl(_, hir::ImplPolarity::Positive, _, _, None, _, _) => {
if is_rustc_reservation {
tcx.sess.span_err(item.span, "reservation impls can't be inherent");
}
ty::ImplPolarity::Positive
}
hir::ItemKind::Impl(_, hir::ImplPolarity::Positive, _, _, Some(_tr), _, _) => {
if is_rustc_reservation {
ty::ImplPolarity::Reservation
} else {
ty::ImplPolarity::Positive
}
}
ref item => bug!("impl_polarity: {:?} not an impl", item),
}
}
Expand Down
Loading