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

Rollup of 5 pull requests #70427

Merged
merged 27 commits into from
Mar 26, 2020
Merged
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
4920a33
better explain GLOBAL_KIND choice
RalfJung Mar 25, 2020
b5343d6
rename def_id -> static_def_id
RalfJung Mar 25, 2020
4d77d01
Fix for #62691: use the largest niche across all fields
ogoffart Mar 25, 2020
81006f6
Optimize slightly by avoiding to call Niche::reserve when not needed
ogoffart Mar 25, 2020
bb3e513
Revert previous commit and make the optimisation in a nicer way
ogoffart Mar 25, 2020
73c8203
Throw error when encountering `...` instead of `..` while destructing…
rakshith-ravi Mar 26, 2020
c21e25c
simplify match stmt
lcnr Mar 26, 2020
e8a05e2
permit negative impls for non-auto traits
nikomatsakis Jan 8, 2020
6507170
make a custom error for overlap with negative impls
nikomatsakis Jan 8, 2020
fda3378
introduce `negative_impls` feature gate and document
nikomatsakis Jan 9, 2020
cc0d6d0
create a tracking issue and link to it
nikomatsakis Jan 17, 2020
68aa798
bump negative impls version to 1.44.0
nikomatsakis Mar 13, 2020
73a5500
convert to doc comments
nikomatsakis Mar 13, 2020
f662847
move feature-gate-negative-impls test to traits/negative-impls
nikomatsakis Mar 13, 2020
7107f6e
comment the `typeck-negative-impls-builtin` test
nikomatsakis Mar 13, 2020
644b9a0
give the negative-impls-builtin test a more sensible name
nikomatsakis Mar 13, 2020
f5c09ed
move stderr file too
nikomatsakis Mar 13, 2020
b9e09d8
add test for negative specializes negative
nikomatsakis Mar 13, 2020
c35801e
use slice pattern instead of calling `is_empty()` and `[0]`
nikomatsakis Mar 19, 2020
011215b
pacify the merciless x.py fmt
nikomatsakis Mar 19, 2020
0d07026
wip pacify the merciless ui tests
nikomatsakis Mar 26, 2020
0b00c20
Reorganize a bit the code and add a comment
ogoffart Mar 26, 2020
b0a63cb
Rollup merge of #68004 - nikomatsakis:negative-impls, r=varkor
Centril Mar 26, 2020
20771ae
Rollup merge of #70385 - RalfJung:miri-nits, r=eddyb
Centril Mar 26, 2020
f9d1378
Rollup merge of #70411 - ogoffart:fix-62691, r=eddyb
Centril Mar 26, 2020
37e1860
Rollup merge of #70417 - rakshith-ravi:master, r=Centril
Centril Mar 26, 2020
608715b
Rollup merge of #70424 - lcnr:nit, r=Centril
Centril Mar 26, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions src/doc/unstable-book/src/language-features/negative-impls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# `negative_impls`

The tracking issue for this feature is [#68318].

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

----

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.
Original file line number Diff line number Diff line change
@@ -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
@@ -22,6 +23,7 @@ impl !Trait for Type
Example:

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

auto trait Valid {}
@@ -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
Original file line number Diff line number Diff line change
@@ -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)]
7 changes: 6 additions & 1 deletion src/libcore/clone.rs
Original file line number Diff line number Diff line change
@@ -219,12 +219,17 @@ mod impls {
}
}

// Shared references can be cloned, but mutable references *cannot*!
/// Shared references can be cloned, but mutable references *cannot*!
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> Clone for &T {
#[inline]
fn clone(&self) -> Self {
*self
}
}

/// Shared references can be cloned, but mutable references *cannot*!
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg(not(bootstrap))]
impl<T: ?Sized> !Clone for &mut T {}
}
1 change: 1 addition & 0 deletions src/libcore/lib.rs
Original file line number Diff line number Diff line change
@@ -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)]
4 changes: 2 additions & 2 deletions src/libcore/marker.rs
Original file line number Diff line number Diff line change
@@ -87,7 +87,7 @@ impl<T: ?Sized> !Send for *mut T {}
message = "the size for values of type `{Self}` cannot be known at compilation time",
label = "doesn't have a size known at compile-time",
note = "to learn more, visit <https://doc.rust-lang.org/book/\
ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>"
ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>"
)]
#[fundamental] // for Default, for example, which requires that `[T]: !Default` be evaluatable
#[cfg_attr(not(bootstrap), rustc_specialization_trait)]
@@ -790,7 +790,7 @@ mod copy_impls {
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> Copy for *mut T {}

// Shared references can be copied, but mutable references *cannot*!
/// Shared references can be copied, but mutable references *cannot*!
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> Copy for &T {}
}
4 changes: 4 additions & 0 deletions src/libcore/ops/deref.rs
Original file line number Diff line number Diff line change
@@ -81,6 +81,10 @@ impl<T: ?Sized> Deref for &T {
}
}

#[cfg(not(bootstrap))]
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> !DerefMut for &T {}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> Deref for &mut T {
type Target = T;
1 change: 1 addition & 0 deletions src/libproc_macro/lib.rs
Original file line number Diff line number Diff line change
@@ -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))]
27 changes: 14 additions & 13 deletions src/librustc/ty/layout.rs
Original file line number Diff line number Diff line change
@@ -282,8 +282,6 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {

let mut align = if pack.is_some() { dl.i8_align } else { dl.aggregate_align };

let mut sized = true;
let mut offsets = vec![Size::ZERO; fields.len()];
let mut inverse_memory_index: Vec<u32> = (0..fields.len() as u32).collect();

let mut optimize = !repr.inhibit_struct_field_reordering_opt();
@@ -320,6 +318,8 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
// At the bottom of this function, we invert `inverse_memory_index` to
// produce `memory_index` (see `invert_mapping`).

let mut sized = true;
let mut offsets = vec![Size::ZERO; fields.len()];
let mut offset = Size::ZERO;
let mut largest_niche = None;
let mut largest_niche_available = 0;
@@ -900,18 +900,19 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
let count = (niche_variants.end().as_u32()
- niche_variants.start().as_u32()
+ 1) as u128;
// FIXME(#62691) use the largest niche across all fields,
// not just the first one.
for (field_index, &field) in variants[i].iter().enumerate() {
let niche = match &field.largest_niche {
Some(niche) => niche,
_ => continue,
};
let (niche_start, niche_scalar) = match niche.reserve(self, count) {
Some(pair) => pair,
None => continue,
};

// Find the field with the largest niche
let niche_candidate = variants[i]
.iter()
.enumerate()
.filter_map(|(j, &field)| Some((j, field.largest_niche.as_ref()?)))
.max_by_key(|(_, niche)| niche.available(dl));

if let Some((field_index, niche, (niche_start, niche_scalar))) =
niche_candidate.and_then(|(field_index, niche)| {
Some((field_index, niche, niche.reserve(self, count)?))
})
{
let mut align = dl.aggregate_align;
let st = variants
.iter_enumerated()
6 changes: 1 addition & 5 deletions src/librustc_ast_passes/ast_validation.rs
Original file line number Diff line number Diff line change
@@ -289,11 +289,7 @@ impl<'a> AstValidator<'a> {
match expr.kind {
ExprKind::Lit(..) | ExprKind::Err => {}
ExprKind::Path(..) if allow_paths => {}
ExprKind::Unary(UnOp::Neg, ref inner)
if match inner.kind {
ExprKind::Lit(_) => true,
_ => false,
} => {}
ExprKind::Unary(UnOp::Neg, ref inner) if matches!(inner.kind, ExprKind::Lit(_)) => {}
_ => self.err_handler().span_err(
expr.span,
"arbitrary expressions aren't allowed \
10 changes: 5 additions & 5 deletions src/librustc_ast_passes/feature_gate.rs
Original file line number Diff line number Diff line change
@@ -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) {
@@ -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"
);
}
}
@@ -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"
5 changes: 4 additions & 1 deletion src/librustc_error_codes/error_codes.rs
Original file line number Diff line number Diff line change
@@ -97,7 +97,6 @@ E0184: include_str!("./error_codes/E0184.md"),
E0185: include_str!("./error_codes/E0185.md"),
E0186: include_str!("./error_codes/E0186.md"),
E0191: include_str!("./error_codes/E0191.md"),
E0192: include_str!("./error_codes/E0192.md"),
E0193: include_str!("./error_codes/E0193.md"),
E0195: include_str!("./error_codes/E0195.md"),
E0197: include_str!("./error_codes/E0197.md"),
@@ -426,6 +425,9 @@ E0745: include_str!("./error_codes/E0745.md"),
E0746: include_str!("./error_codes/E0746.md"),
E0747: include_str!("./error_codes/E0747.md"),
E0748: include_str!("./error_codes/E0748.md"),
E0749: include_str!("./error_codes/E0749.md"),
E0750: include_str!("./error_codes/E0750.md"),
E0751: include_str!("./error_codes/E0751.md"),
;
// E0006, // merged with E0005
// E0008, // cannot bind by-move into a pattern guard
@@ -460,6 +462,7 @@ E0748: include_str!("./error_codes/E0748.md"),
// E0188, // can not cast an immutable reference to a mutable pointer
// E0189, // deprecated: can only cast a boxed pointer to a boxed object
// E0190, // deprecated: can only cast a &-pointer to an &-object
// E0192, // negative impl only applicable to auto traits
// E0194, // merged into E0403
// E0196, // cannot determine a type for this closure
E0208,
4 changes: 4 additions & 0 deletions src/librustc_error_codes/error_codes/E0749.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Negative impls are not allowed to have any items. Negative impls
declare that a trait is **not** implemented (and never will be) and
hence there is no need to specify the values for trait methods or
other items.
4 changes: 4 additions & 0 deletions src/librustc_error_codes/error_codes/E0750.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Negative impls cannot be default impls. A default impl supplies
default values for the items within to be used by other impls, whereas
a negative impl declares that there are no other impls. These don't
make sense to combine.
12 changes: 12 additions & 0 deletions src/librustc_error_codes/error_codes/E0751.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
There are both a positive and negative trait implementation for the same type.

Erroneous code example:

```compile_fail,E0748
trait MyTrait {}
impl MyTrait for i32 { }
impl !MyTrait for i32 { }
```

Negative implementations are a promise that the trait will never be
implemented for the given types.
4 changes: 2 additions & 2 deletions src/librustc_errors/lib.rs
Original file line number Diff line number Diff line change
@@ -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]
));
}
3 changes: 3 additions & 0 deletions src/librustc_feature/active.rs
Original file line number Diff line number Diff line change
@@ -554,6 +554,9 @@ declare_features! (
// Allows limiting the evaluation steps of const expressions
(active, const_eval_limit, "1.43.0", Some(67217), None),

/// Allow negative trait implementations.
(active, negative_impls, "1.44.0", Some(68318), None),

// -------------------------------------------------------------------------
// feature-group-end: actual feature gates
// -------------------------------------------------------------------------
Loading