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

prevent specialized negative impls #74648

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
113 changes: 95 additions & 18 deletions src/librustc_typeck/check/dropck.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::check::regionck::RegionCtxt;
use crate::hir;
use crate::hir::def_id::{DefId, LocalDefId};
use rustc_errors::{struct_span_err, ErrorReported};
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::{InferOk, RegionckMode, TyCtxtInferExt};
Expand All @@ -9,6 +8,7 @@ use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
use rustc_middle::ty::subst::{Subst, SubstsRef};
use rustc_middle::ty::{self, Predicate, Ty, TyCtxt};
use rustc_span::def_id::{DefId, LocalDefId};
use rustc_span::Span;
use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
use rustc_trait_selection::traits::query::dropck_outlives::AtExt;
Expand All @@ -32,9 +32,26 @@ use rustc_trait_selection::traits::{ObligationCause, TraitEngine, TraitEngineExt
/// cannot do `struct S<T>; impl<T:Clone> Drop for S<T> { ... }`).
///
pub fn check_drop_impl(tcx: TyCtxt<'_>, drop_impl_did: DefId) -> Result<(), ErrorReported> {
check_restricted_impl(tcx, drop_impl_did)
}

pub fn check_restricted_impl(tcx: TyCtxt<'_>, drop_impl_did: DefId) -> Result<(), ErrorReported> {
let dtor_self_type = tcx.type_of(drop_impl_did);
let dtor_predicates = tcx.predicates_of(drop_impl_did);
match dtor_self_type.kind {

let check_empty_where_bounds = |s| {
if dtor_predicates.predicates.is_empty() {
Ok(())
} else {
tcx.sess.span_err(
tcx.def_span(drop_impl_did),
&format!("negative impls on {} must not contain where bounds", s),
);
Err(ErrorReported)
}
};

let kind_str = match dtor_self_type.kind {
ty::Adt(adt_def, self_to_impl_substs) => {
ensure_drop_params_and_item_params_correspond(
tcx,
Expand All @@ -43,25 +60,82 @@ pub fn check_drop_impl(tcx: TyCtxt<'_>, drop_impl_did: DefId) -> Result<(), Erro
adt_def.did,
)?;

ensure_drop_predicates_are_implied_by_item_defn(
return ensure_drop_predicates_are_implied_by_item_defn(
tcx,
drop_impl_did.expect_local(),
dtor_predicates,
adt_def.did.expect_local(),
adt_def.did,
self_to_impl_substs,
)
);
}
_ => {
// Destructors only work on nominal types. This was
_ if tcx.lang_items().drop_trait()
== Some(tcx.impl_trait_ref(drop_impl_did).unwrap().def_id) =>
{
// Destructors only work on nominal types. This was
// already checked by coherence, but compilation may
// not have been terminated.
let span = tcx.def_span(drop_impl_did);
tcx.sess.delay_span_bug(
span,
tcx.def_span(drop_impl_did),
&format!("should have been rejected by coherence check: {}", dtor_self_type),
);
Err(ErrorReported)
return Err(ErrorReported);
}
}
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => {
return check_empty_where_bounds("primitive types");
}
ty::Foreign(_) => {
return check_empty_where_bounds("foreign types");
}
ty::Array(..) => {
// FIXME: Support negative impls for `[T; N]` where `T: Sized` is the only predicate.
"arrays"
}
ty::Slice(..) => {
// FIXME: Support negative impls for `[T]` where `T: Sized` is the only predicate.
"slices"
}
ty::RawPtr(..) => {
return check_empty_where_bounds("raw pointers");
}
ty::Ref(..) => {
return check_empty_where_bounds("references");
}
ty::FnPtr(..) => {
// In case we ever get variadic functions allowing this would mean that we have
// specialized negative impls on stable.
"function pointers"
}
ty::Dynamic(..) => {
return check_empty_where_bounds("trait objects");
}
ty::Never => {
return check_empty_where_bounds("`!`");
}
ty::Tuple(..) => {
// In case we ever get variadic tuples allowing this would mean that we have
// specialized negative impls on stable.
"tuples"
}
ty::Projection(..) => "projections",
ty::Opaque(..) => "opaque types",
ty::Param(..) => {
return check_empty_where_bounds("type parameters");
}
ty::Error(..) => return Ok(()),
ty::FnDef(..)
| ty::Closure(..)
| ty::Generator(..)
| ty::GeneratorWitness(..)
| ty::Bound(..)
| ty::Placeholder(..)
| ty::Infer(..) => bug!("unexpected impl self ty: {:?}", dtor_self_type),
};

tcx.sess.span_err(
tcx.def_span(drop_impl_did),
&format!("negative impls are not allowed for {}", kind_str),
);
Err(ErrorReported)
}

fn ensure_drop_params_and_item_params_correspond<'tcx>(
Expand Down Expand Up @@ -94,11 +168,13 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>(
Err(_) => {
let item_span = tcx.def_span(self_type_did);
let self_descr = tcx.def_kind(self_type_did).descr(self_type_did);
let trait_ref = tcx.impl_trait_ref(drop_impl_did).unwrap();
struct_span_err!(
tcx.sess,
drop_impl_span,
E0366,
"`Drop` impls cannot be specialized"
"`{}` impls cannot be specialized",
trait_ref.print_only_trait_path()
)
.span_note(
item_span,
Expand Down Expand Up @@ -142,8 +218,9 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>(
/// implied by assuming the predicates attached to self_type_did.
fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
tcx: TyCtxt<'tcx>,
drop_impl_did: LocalDefId,
dtor_predicates: ty::GenericPredicates<'tcx>,
self_type_did: LocalDefId,
self_type_did: DefId,
self_to_impl_substs: SubstsRef<'tcx>,
) -> Result<(), ErrorReported> {
let mut result = Ok(());
Expand Down Expand Up @@ -183,8 +260,6 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
// absent. So we report an error that the Drop impl injected a
// predicate that is not present on the struct definition.

let self_type_hir_id = tcx.hir().as_local_hir_id(self_type_did);

// We can assume the predicates attached to struct/enum definition
// hold.
let generic_assumptions = tcx.predicates_of(self_type_did);
Expand Down Expand Up @@ -238,13 +313,15 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
};

if !assumptions_in_impl_context.iter().copied().any(predicate_matches_closure) {
let item_span = tcx.hir().span(self_type_hir_id);
let self_descr = tcx.def_kind(self_type_did).descr(self_type_did.to_def_id());
let item_span = tcx.def_span(self_type_did);
let self_descr = tcx.def_kind(self_type_did).descr(self_type_did);
let trait_ref = tcx.impl_trait_ref(drop_impl_did).unwrap();
struct_span_err!(
tcx.sess,
predicate_sp,
E0367,
"`Drop` impl requires `{}` but the {} it is implemented for does not",
"`{}` impl requires `{}` but the {} it is implemented for does not",
trait_ref.print_only_trait_path(),
predicate,
self_descr,
)
Expand Down
9 changes: 6 additions & 3 deletions src/librustc_typeck/check/wfcheck.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use crate::check::{FnCtxt, Inherited};
use crate::check::{dropck::check_restricted_impl, FnCtxt, Inherited};
use crate::constrained_generic_params::{identify_constrained_generic_params, Parameter};

use rustc_ast::ast;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported};
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::itemlikevisit::ParItemLikeVisitor;
Expand Down Expand Up @@ -123,7 +123,10 @@ pub fn check_item_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) {
check_impl(tcx, item, self_ty, of_trait);
}
(ty::ImplPolarity::Negative, ast::ImplPolarity::Negative(span)) => {
// FIXME(#27579): what amount of WF checking do we need for neg impls?
if let Err(ErrorReported) = check_restricted_impl(tcx, def_id.to_def_id()) {
return;
}

if let hir::Defaultness::Default { .. } = defaultness {
let mut spans = vec![span];
spans.extend(defaultness_span);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

auto trait MyTrait {}

impl<T> !MyTrait for *mut T {}
impl<T: ?Sized> !MyTrait for *mut T {}

struct MyS;

Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/issues/issue-17959.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ struct G<T: ?Sized> {
}

impl<T> Drop for G<T> {
//~^ ERROR `Drop` impl requires `T: std::marker::Sized`
//~^ ERROR `std::ops::Drop` impl requires `T: std::marker::Sized`
fn drop(&mut self) {
if !self._ptr.is_null() {
}
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/issues/issue-17959.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error[E0367]: `Drop` impl requires `T: std::marker::Sized` but the struct it is implemented for does not
error[E0367]: `std::ops::Drop` impl requires `T: std::marker::Sized` but the struct it is implemented for does not
--> $DIR/issue-17959.rs:11:6
|
LL | impl<T> Drop for G<T> {
Expand Down
3 changes: 1 addition & 2 deletions src/test/ui/issues/issue-29516.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
// check-pass
#![feature(optin_builtin_traits)]
#![feature(negative_impls)]

auto trait NotSame {}

impl<A> !NotSame for (A, A) {}
impl<A> !NotSame for (A, A) {} //~ ERROR negative impls are not allowed for tuples

trait OneOfEach {}

Expand Down
8 changes: 8 additions & 0 deletions src/test/ui/issues/issue-29516.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
error: negative impls are not allowed for tuples
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we disallow negative impls for all tuples? (I think that'd be ok, actually, though I think we could also permit them for tuples if they are only restricted based on arity, but I don't see the point.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think allowing them for tuples will prevent us from ever adding variadic tuples, so it should be easier to just disallow them for now

--> $DIR/issue-29516.rs:6:1
|
LL | impl<A> !NotSame for (A, A) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

2 changes: 1 addition & 1 deletion src/test/ui/issues/issue-38868.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error[E0366]: `Drop` impls cannot be specialized
error[E0366]: `std::ops::Drop` impls cannot be specialized
--> $DIR/issue-38868.rs:5:1
|
LL | / impl Drop for List<i32> {
Expand Down
13 changes: 6 additions & 7 deletions src/test/ui/issues/issue-41974.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
#[derive(Copy, Clone)]
struct Flags;

trait A {
}
trait A {}

impl<T> Drop for T where T: A { //~ ERROR E0119
//~^ ERROR E0120
//~| ERROR E0210
fn drop(&mut self) {
}
impl<T> Drop for T where T: A {
//~^ ERROR E0119
//~| ERROR E0120
//~| ERROR E0210
fn drop(&mut self) {}
}

fn main() {}
12 changes: 6 additions & 6 deletions src/test/ui/issues/issue-41974.stderr
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
error[E0119]: conflicting implementations of trait `std::ops::Drop` for type `std::boxed::Box<_>`:
--> $DIR/issue-41974.rs:7:1
--> $DIR/issue-41974.rs:6:1
|
LL | impl<T> Drop for T where T: A {
LL | impl<T> Drop for T where T: A {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: conflicting implementation in crate `alloc`:
Expand All @@ -10,15 +10,15 @@ LL | impl<T> Drop for T where T: A {
= note: downstream crates may implement trait `A` for type `std::boxed::Box<_>`

error[E0120]: the `Drop` trait may only be implemented for structs, enums, and unions
--> $DIR/issue-41974.rs:7:18
--> $DIR/issue-41974.rs:6:18
|
LL | impl<T> Drop for T where T: A {
LL | impl<T> Drop for T where T: A {
| ^ must be a struct, enum, or union

error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`)
--> $DIR/issue-41974.rs:7:6
--> $DIR/issue-41974.rs:6:6
|
LL | impl<T> Drop for T where T: A {
LL | impl<T> Drop for T where T: A {
| ^ type parameter `T` must be used as the type parameter for some local type
|
= note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local
Expand Down
18 changes: 9 additions & 9 deletions src/test/ui/reject-specialized-drops-8142.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ struct TupleStruct<T>(T);
union Union<T: Copy> { f: T }

impl<'al,'adds_bnd:'al> Drop for K<'al,'adds_bnd> { // REJECT
//~^ ERROR `Drop` impl requires `'adds_bnd: 'al`
//~^ ERROR `std::ops::Drop` impl requires `'adds_bnd: 'al`
fn drop(&mut self) { } }

impl<'al,'adds_bnd> Drop for L<'al,'adds_bnd> where 'adds_bnd:'al { // REJECT
//~^ ERROR `Drop` impl requires `'adds_bnd: 'al`
//~^ ERROR `std::ops::Drop` impl requires `'adds_bnd: 'al`
fn drop(&mut self) { } }

impl<'ml> Drop for M<'ml> { fn drop(&mut self) { } } // ACCEPT
Expand All @@ -38,13 +38,13 @@ impl Drop for N<'static> { fn drop(&mut self) { } } // RE
impl<COkNoBound> Drop for O<COkNoBound> { fn drop(&mut self) { } } // ACCEPT

impl Drop for P<i8> { fn drop(&mut self) { } } // REJECT
//~^ ERROR `Drop` impls cannot be specialized
//~^ ERROR `std::ops::Drop` impls cannot be specialized

impl<AddsBnd:Bound> Drop for Q<AddsBnd> { fn drop(&mut self) { } } // REJECT
//~^ ERROR `Drop` impl requires `AddsBnd: Bound`
//~^ ERROR `std::ops::Drop` impl requires `AddsBnd: Bound`

impl<'rbnd,AddsRBnd:'rbnd> Drop for R<AddsRBnd> { fn drop(&mut self) { } } // REJECT
//~^ ERROR `Drop` impl requires `AddsRBnd: 'rbnd`
//~^ ERROR `std::ops::Drop` impl requires `AddsRBnd: 'rbnd`

impl<Bs:Bound> Drop for S<Bs> { fn drop(&mut self) { } } // ACCEPT

Expand All @@ -53,18 +53,18 @@ impl<'t,Bt:'t> Drop for T<'t,Bt> { fn drop(&mut self) { } } // ACCEPT
impl Drop for U { fn drop(&mut self) { } } // ACCEPT

impl<One> Drop for V<One,One> { fn drop(&mut self) { } } // REJECT
//~^ ERROR `Drop` impls cannot be specialized
//~^ ERROR `std::ops::Drop` impls cannot be specialized

impl<'lw> Drop for W<'lw,'lw> { fn drop(&mut self) { } } // REJECT
//~^ ERROR cannot infer an appropriate lifetime for lifetime parameter `'lw`

impl<AddsBnd:Bound> Drop for Enum<AddsBnd> { fn drop(&mut self) { } } // REJECT
//~^ ERROR `Drop` impl requires `AddsBnd: Bound`
//~^ ERROR `std::ops::Drop` impl requires `AddsBnd: Bound`

impl<AddsBnd:Bound> Drop for TupleStruct<AddsBnd> { fn drop(&mut self) { } } // REJECT
//~^ ERROR `Drop` impl requires `AddsBnd: Bound`
//~^ ERROR `std::ops::Drop` impl requires `AddsBnd: Bound`

impl<AddsBnd:Copy + Bound> Drop for Union<AddsBnd> { fn drop(&mut self) { } } // REJECT
//~^ ERROR `Drop` impl requires `AddsBnd: Bound`
//~^ ERROR `std::ops::Drop` impl requires `AddsBnd: Bound`

pub fn main() { }
Loading