Skip to content

Commit

Permalink
introduce negative_impls feature gate and document
Browse files Browse the repository at this point in the history
They used to be covered by `optin_builtin_traits` but negative impls
are now applicable to all traits, not just auto traits.

This also adds docs in the unstable book for the current state of auto traits.
  • Loading branch information
nikomatsakis committed Mar 26, 2020
1 parent 6507170 commit fda3378
Show file tree
Hide file tree
Showing 102 changed files with 330 additions and 111 deletions.
57 changes: 57 additions & 0 deletions src/doc/unstable-book/src/language-features/negative-impls.md
@@ -0,0 +1,57 @@
# `negative_impls`

The tracking issue for this feature is [#13231]

[#13231]: https://github.com/rust-lang/rust/issues/13231

----

With the feature gate `negative_impls`, you can write negative impls as well as positive ones:

```rust
#![feature(negative_impls)]
trait DerefMut { }
impl<T: ?Sized> !DerefMut for &T { }
```

Negative impls indicate a semver guarantee that the given trait will not be implemented for the given types. Negative impls play an additional purpose for auto traits, described below.

Negative impls have the following characteristics:

* They do not have any items.
* They must obey the orphan rules as if they were a positive impl.
* They cannot "overlap" with any positive impls.

## Semver interaction

It is a breaking change to remove a negative impl. Negative impls are a commitment not to implement the given trait for the named types.

## Orphan and overlap rules

Negative impls must obey the same orphan rules as a positive impl. This implies you cannot add a negative impl for types defined in upstream crates and so forth.

Similarly, negative impls cannot overlap with positive impls, again using the same "overlap" check that we ordinarily use to determine if two impls overlap. (Note that positive impls typically cannot overlap with one another either, except as permitted by specialization.)

## Interaction with auto traits

Declaring a negative impl `impl !SomeAutoTrait for SomeType` for an
auto-trait serves two purposes:

* as with any trait, it declares that `SomeType` will never implement `SomeAutoTrait`;
* it disables the automatic `SomeType: SomeAutoTrait` impl that would otherwise have been generated.

Note that, at present, there is no way to indicate that a given type
does not implement an auto trait *but that it may do so in the
future*. For ordinary types, this is done by simply not declaring any
impl at all, but that is not an option for auto traits. A workaround
is that one could embed a marker type as one of the fields, where the
marker type is `!AutoTrait`.

## Immediate uses

Negative impls are used to declare that `&T: !DerefMut` and `&mut T: !Clone`, as required to fix the soundness of `Pin` described in [#66544](https://github.com/rust-lang/rust/issues/66544).

This serves two purposes:

* For proving the correctness of unsafe code, we can use that impl as evidence that no `DerefMut` or `Clone` impl exists.
* It prevents downstream crates from creating such impls.
Expand Up @@ -10,7 +10,8 @@ The `optin_builtin_traits` feature gate allows you to define auto traits.

Auto traits, like [`Send`] or [`Sync`] in the standard library, are marker traits
that are automatically implemented for every type, unless the type, or a type it contains,
has explicitly opted out via a negative impl.
has explicitly opted out via a negative impl. (Negative impls are separately controlled
by the `negative_impls` feature.)

[`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html
[`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html
Expand All @@ -22,6 +23,7 @@ impl !Trait for Type
Example:

```rust
#![feature(negative_impls)]
#![feature(optin_builtin_traits)]

auto trait Valid {}
Expand All @@ -43,3 +45,63 @@ fn main() {
// must_be_valid( MaybeValid(False) );
}
```

## Automatic trait implementations

When a type is declared as an `auto trait`, we will automatically
create impls for every struct/enum/union, unless an explicit impl is
provided. These automatic impls contain a where clause for each field
of the form `T: AutoTrait`, where `T` is the type of the field and
`AutoTrait` is the auto trait in question. As an example, consider the
struct `List` and the auto trait `Send`:

```rust
struct List<T> {
data: T,
next: Option<Box<List<T>>>,
}
```

Presuming that there is no explicit impl of `Send` for `List`, the
compiler will supply an automatic impl of the form:

```rust
struct List<T> {
data: T,
next: Option<Box<List<T>>>,
}

unsafe impl<T> Send for List<T>
where
T: Send, // from the field `data`
Option<Box<List<T>>>: Send, // from the field `next`
{ }
```

Explicit impls may be either positive or negative. They take the form:

```rust,ignore
impl<...> AutoTrait for StructName<..> { }
impl<...> !AutoTrait for StructName<..> { }
```

## Coinduction: Auto traits permit cyclic matching

Unlike ordinary trait matching, auto traits are **coinductive**. This
means, in short, that cycles which occur in trait matching are
considered ok. As an example, consider the recursive struct `List`
introduced in the previous section. In attempting to determine whether
`List: Send`, we would wind up in a cycle: to apply the impl, we must
show that `Option<Box<List>>: Send`, which will in turn require
`Box<List>: Send` and then finally `List: Send` again. Under ordinary
trait matching, this cycle would be an error, but for an auto trait it
is considered a successful match.

## Items

Auto traits cannot have any trait items, such as methods or associated types. This ensures that we can generate default implementations.

## Supertraits

Auto traits cannot have supertraits. This is for soundness reasons, as the interaction of coinduction with implied bounds is difficult to reconcile.

1 change: 1 addition & 0 deletions src/liballoc/lib.rs
Expand Up @@ -99,6 +99,7 @@
#![feature(internal_uninit_const)]
#![feature(lang_items)]
#![feature(libc)]
#![cfg_attr(not(bootstrap), feature(negative_impls))]
#![feature(nll)]
#![feature(optin_builtin_traits)]
#![feature(pattern)]
Expand Down
1 change: 1 addition & 0 deletions src/libcore/lib.rs
Expand Up @@ -98,6 +98,7 @@
#![feature(is_sorted)]
#![feature(lang_items)]
#![feature(link_llvm_intrinsics)]
#![cfg_attr(not(bootstrap), feature(negative_impls))]
#![feature(never_type)]
#![feature(nll)]
#![feature(exhaustive_patterns)]
Expand Down
1 change: 1 addition & 0 deletions src/libproc_macro/lib.rs
Expand Up @@ -24,6 +24,7 @@
#![feature(decl_macro)]
#![feature(extern_types)]
#![feature(in_band_lifetimes)]
#![cfg_attr(not(bootstrap), feature(negative_impls))]
#![feature(optin_builtin_traits)]
#![feature(rustc_attrs)]
#![cfg_attr(bootstrap, feature(specialization))]
Expand Down
10 changes: 5 additions & 5 deletions src/librustc_ast_passes/feature_gate.rs
Expand Up @@ -286,8 +286,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
start,
i.span,
"`#[start]` functions are experimental \
and their signature may change \
over time"
and their signature may change \
over time"
);
}
if attr::contains_name(&i.attrs[..], sym::main) {
Expand All @@ -296,8 +296,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
main,
i.span,
"declaration of a non-standard `#[main]` \
function may change over time, for now \
a top-level `fn main()` is required"
function may change over time, for now \
a top-level `fn main()` is required"
);
}
}
Expand Down Expand Up @@ -341,7 +341,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
if let ast::ImplPolarity::Negative(span) = polarity {
gate_feature_post!(
&self,
optin_builtin_traits,
negative_impls,
span.to(of_trait.as_ref().map(|t| t.path.span).unwrap_or(span)),
"negative trait bounds are not yet fully implemented; \
use marker types for now"
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_errors/lib.rs
Expand Up @@ -802,13 +802,13 @@ impl HandlerInner {
));
self.failure(&format!(
"For more information about an error, try \
`rustc --explain {}`.",
`rustc --explain {}`.",
&error_codes[0]
));
} else {
self.failure(&format!(
"For more information about this error, try \
`rustc --explain {}`.",
`rustc --explain {}`.",
&error_codes[0]
));
}
Expand Down
3 changes: 3 additions & 0 deletions src/librustc_feature/active.rs
Expand Up @@ -152,6 +152,9 @@ declare_features! (
/// Allows features specific to OIBIT (auto traits).
(active, optin_builtin_traits, "1.0.0", Some(13231), None),

/// Allow negative trait implementations.
(active, negative_impls, "1.0.0", Some(13231), None),

/// Allows using `box` in patterns (RFC 469).
(active, box_patterns, "1.0.0", Some(29641), None),

Expand Down
43 changes: 36 additions & 7 deletions src/librustc_span/lib.rs
Expand Up @@ -9,6 +9,7 @@
#![feature(const_if_match)]
#![feature(const_fn)]
#![feature(const_panic)]
#![cfg_attr(not(bootstrap), feature(negative_impls))]
#![feature(nll)]
#![feature(optin_builtin_traits)]
#![feature(specialization)]
Expand Down Expand Up @@ -305,7 +306,11 @@ impl Span {

/// Returns `self` if `self` is not the dummy span, and `other` otherwise.
pub fn substitute_dummy(self, other: Span) -> Span {
if self.is_dummy() { other } else { self }
if self.is_dummy() {
other
} else {
self
}
}

/// Returns `true` if `self` fully encloses `other`.
Expand Down Expand Up @@ -336,21 +341,33 @@ impl Span {
pub fn trim_start(self, other: Span) -> Option<Span> {
let span = self.data();
let other = other.data();
if span.hi > other.hi { Some(span.with_lo(cmp::max(span.lo, other.hi))) } else { None }
if span.hi > other.hi {
Some(span.with_lo(cmp::max(span.lo, other.hi)))
} else {
None
}
}

/// Returns the source span -- this is either the supplied span, or the span for
/// the macro callsite that expanded to it.
pub fn source_callsite(self) -> Span {
let expn_data = self.ctxt().outer_expn_data();
if !expn_data.is_root() { expn_data.call_site.source_callsite() } else { self }
if !expn_data.is_root() {
expn_data.call_site.source_callsite()
} else {
self
}
}

/// The `Span` for the tokens in the previous macro expansion from which `self` was generated,
/// if any.
pub fn parent(self) -> Option<Span> {
let expn_data = self.ctxt().outer_expn_data();
if !expn_data.is_root() { Some(expn_data.call_site) } else { None }
if !expn_data.is_root() {
Some(expn_data.call_site)
} else {
None
}
}

/// Edition of the crate from which this span came.
Expand All @@ -376,10 +393,18 @@ impl Span {
pub fn source_callee(self) -> Option<ExpnData> {
fn source_callee(expn_data: ExpnData) -> ExpnData {
let next_expn_data = expn_data.call_site.ctxt().outer_expn_data();
if !next_expn_data.is_root() { source_callee(next_expn_data) } else { expn_data }
if !next_expn_data.is_root() {
source_callee(next_expn_data)
} else {
expn_data
}
}
let expn_data = self.ctxt().outer_expn_data();
if !expn_data.is_root() { Some(source_callee(expn_data)) } else { None }
if !expn_data.is_root() {
Some(source_callee(expn_data))
} else {
None
}
}

/// Checks if a span is "internal" to a macro in which `#[unstable]`
Expand Down Expand Up @@ -1199,7 +1224,11 @@ impl SourceFile {

let line_index = lookup_line(&self.lines[..], pos);
assert!(line_index < self.lines.len() as isize);
if line_index >= 0 { Some(line_index as usize) } else { None }
if line_index >= 0 {
Some(line_index as usize)
} else {
None
}
}

pub fn line_bounds(&self, line_index: usize) -> (BytePos, BytePos) {
Expand Down
1 change: 1 addition & 0 deletions src/librustc_span/symbol.rs
Expand Up @@ -473,6 +473,7 @@ symbols! {
needs_drop,
needs_panic_runtime,
negate_unsigned,
negative_impls,
never,
never_type,
never_type_fallback,
Expand Down
1 change: 1 addition & 0 deletions src/libstd/lib.rs
Expand Up @@ -278,6 +278,7 @@
#![feature(maybe_uninit_ref)]
#![feature(maybe_uninit_slice)]
#![feature(needs_panic_runtime)]
#![cfg_attr(not(bootstrap), feature(negative_impls))]
#![feature(never_type)]
#![feature(nll)]
#![feature(optin_builtin_traits)]
Expand Down
2 changes: 1 addition & 1 deletion src/test/pretty/trait-polarity.rs
@@ -1,4 +1,4 @@
#![feature(optin_builtin_traits)]
#![feature(negative_impls)]

// pp-exact

Expand Down
2 changes: 1 addition & 1 deletion src/test/rustdoc/empty-section.rs
@@ -1,6 +1,6 @@
#![crate_name = "foo"]

#![feature(optin_builtin_traits)]
#![feature(negative_impls)]

pub struct Foo;

Expand Down
2 changes: 1 addition & 1 deletion src/test/rustdoc/impl-parts-crosscrate.rs
@@ -1,7 +1,7 @@
// aux-build:rustdoc-impl-parts-crosscrate.rs
// ignore-cross-compile

#![feature(optin_builtin_traits)]
#![feature(negative_impls)]

extern crate rustdoc_impl_parts_crosscrate;

Expand Down
1 change: 1 addition & 0 deletions src/test/rustdoc/impl-parts.rs
@@ -1,3 +1,4 @@
#![feature(negative_impls)]
#![feature(optin_builtin_traits)]

pub auto trait AnOibit {}
Expand Down
2 changes: 1 addition & 1 deletion src/test/rustdoc/issue-55321.rs
@@ -1,4 +1,4 @@
#![feature(optin_builtin_traits)]
#![feature(negative_impls)]

// @has issue_55321/struct.A.html
// @has - '//*[@id="implementations-list"]/*[@class="impl"]//code' "impl !Send for A"
Expand Down
2 changes: 1 addition & 1 deletion src/test/rustdoc/negative-impl-sidebar.rs
@@ -1,4 +1,4 @@
#![feature(optin_builtin_traits)]
#![feature(negative_impls)]
#![crate_name = "foo"]

pub struct Foo;
Expand Down
2 changes: 1 addition & 1 deletion src/test/rustdoc/negative-impl.rs
@@ -1,4 +1,4 @@
#![feature(optin_builtin_traits)]
#![feature(negative_impls)]

// @matches negative_impl/struct.Alpha.html '//pre' "pub struct Alpha"
pub struct Alpha;
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/async-await/issue-64130-1-sync.rs
@@ -1,4 +1,4 @@
#![feature(optin_builtin_traits)]
#![feature(negative_impls)]
// edition:2018

// This tests the the specialized async-await-specific error when futures don't implement an
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/async-await/issue-64130-2-send.rs
@@ -1,4 +1,4 @@
#![feature(optin_builtin_traits)]
#![feature(negative_impls)]
// edition:2018

// This tests the the specialized async-await-specific error when futures don't implement an
Expand Down
1 change: 1 addition & 0 deletions src/test/ui/async-await/issue-64130-3-other.rs
@@ -1,4 +1,5 @@
#![feature(optin_builtin_traits)]
#![feature(negative_impls)]
// edition:2018

// This tests the the unspecialized async-await-specific error when futures don't implement an
Expand Down

0 comments on commit fda3378

Please sign in to comment.