Require destructors using #[may_dangle] to use unsafe impl.

This commit is contained in:
Felix S. Klock II 2016-10-12 14:23:38 +02:00
parent 7d2d5bcbcf
commit 9a649c328c
4 changed files with 116 additions and 11 deletions

View file

@ -330,6 +330,36 @@ impl Generics {
} }
} }
pub enum UnsafeGeneric {
Region(LifetimeDef, &'static str),
Type(TyParam, &'static str),
}
impl UnsafeGeneric {
pub fn attr_name(&self) -> &'static str {
match *self {
UnsafeGeneric::Region(_, s) => s,
UnsafeGeneric::Type(_, s) => s,
}
}
}
impl Generics {
pub fn carries_unsafe_attr(&self) -> Option<UnsafeGeneric> {
for r in &self.lifetimes {
if r.pure_wrt_drop {
return Some(UnsafeGeneric::Region(r.clone(), "may_dangle"));
}
}
for t in &self.ty_params {
if t.pure_wrt_drop {
return Some(UnsafeGeneric::Type(t.clone(), "may_dangle"));
}
}
return None;
}
}
/// A `where` clause in a definition /// A `where` clause in a definition
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct WhereClause { pub struct WhereClause {

View file

@ -13,7 +13,7 @@
use rustc::ty::TyCtxt; use rustc::ty::TyCtxt;
use rustc::hir::intravisit; use rustc::hir::intravisit;
use rustc::hir; use rustc::hir::{self, Unsafety};
pub fn check<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { pub fn check<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
let mut orphan = UnsafetyChecker { tcx: tcx }; let mut orphan = UnsafetyChecker { tcx: tcx };
@ -27,6 +27,7 @@ struct UnsafetyChecker<'cx, 'tcx: 'cx> {
impl<'cx, 'tcx, 'v> UnsafetyChecker<'cx, 'tcx> { impl<'cx, 'tcx, 'v> UnsafetyChecker<'cx, 'tcx> {
fn check_unsafety_coherence(&mut self, fn check_unsafety_coherence(&mut self,
item: &'v hir::Item, item: &'v hir::Item,
impl_generics: Option<&hir::Generics>,
unsafety: hir::Unsafety, unsafety: hir::Unsafety,
polarity: hir::ImplPolarity) { polarity: hir::ImplPolarity) {
match self.tcx.impl_trait_ref(self.tcx.map.local_def_id(item.id)) { match self.tcx.impl_trait_ref(self.tcx.map.local_def_id(item.id)) {
@ -47,15 +48,16 @@ impl<'cx, 'tcx, 'v> UnsafetyChecker<'cx, 'tcx> {
Some(trait_ref) => { Some(trait_ref) => {
let trait_def = self.tcx.lookup_trait_def(trait_ref.def_id); let trait_def = self.tcx.lookup_trait_def(trait_ref.def_id);
match (trait_def.unsafety, unsafety, polarity) { let unsafe_attr = impl_generics.and_then(|g| g.carries_unsafe_attr());
(hir::Unsafety::Unsafe, hir::Unsafety::Unsafe, hir::ImplPolarity::Negative) => { match (trait_def.unsafety, unsafe_attr, unsafety, polarity) {
(_, _, Unsafety::Unsafe, hir::ImplPolarity::Negative) => {
span_err!(self.tcx.sess, span_err!(self.tcx.sess,
item.span, item.span,
E0198, E0198,
"negative implementations are not unsafe"); "negative implementations are not unsafe");
} }
(hir::Unsafety::Normal, hir::Unsafety::Unsafe, _) => { (Unsafety::Normal, None, Unsafety::Unsafe, _) => {
span_err!(self.tcx.sess, span_err!(self.tcx.sess,
item.span, item.span,
E0199, E0199,
@ -63,7 +65,7 @@ impl<'cx, 'tcx, 'v> UnsafetyChecker<'cx, 'tcx> {
trait_ref); trait_ref);
} }
(hir::Unsafety::Unsafe, hir::Unsafety::Normal, hir::ImplPolarity::Positive) => { (Unsafety::Unsafe, _, Unsafety::Normal, hir::ImplPolarity::Positive) => {
span_err!(self.tcx.sess, span_err!(self.tcx.sess,
item.span, item.span,
E0200, E0200,
@ -71,9 +73,19 @@ impl<'cx, 'tcx, 'v> UnsafetyChecker<'cx, 'tcx> {
trait_ref); trait_ref);
} }
(hir::Unsafety::Unsafe, hir::Unsafety::Normal, hir::ImplPolarity::Negative) | (Unsafety::Normal, Some(g), Unsafety::Normal, hir::ImplPolarity::Positive) =>
(hir::Unsafety::Unsafe, hir::Unsafety::Unsafe, hir::ImplPolarity::Positive) | {
(hir::Unsafety::Normal, hir::Unsafety::Normal, _) => { span_err!(self.tcx.sess,
item.span,
E0569,
"requires an `unsafe impl` declaration due to `#[{}]` attribute",
g.attr_name());
}
(_, _, Unsafety::Normal, hir::ImplPolarity::Negative) |
(Unsafety::Unsafe, _, Unsafety::Unsafe, hir::ImplPolarity::Positive) |
(Unsafety::Normal, Some(_), Unsafety::Unsafe, hir::ImplPolarity::Positive) |
(Unsafety::Normal, None, Unsafety::Normal, _) => {
// OK // OK
} }
} }
@ -86,10 +98,10 @@ impl<'cx, 'tcx, 'v> intravisit::Visitor<'v> for UnsafetyChecker<'cx, 'tcx> {
fn visit_item(&mut self, item: &'v hir::Item) { fn visit_item(&mut self, item: &'v hir::Item) {
match item.node { match item.node {
hir::ItemDefaultImpl(unsafety, _) => { hir::ItemDefaultImpl(unsafety, _) => {
self.check_unsafety_coherence(item, unsafety, hir::ImplPolarity::Positive); self.check_unsafety_coherence(item, None, unsafety, hir::ImplPolarity::Positive);
} }
hir::ItemImpl(unsafety, polarity, ..) => { hir::ItemImpl(unsafety, polarity, ref generics, ..) => {
self.check_unsafety_coherence(item, unsafety, polarity); self.check_unsafety_coherence(item, Some(generics), unsafety, polarity);
} }
_ => {} _ => {}
} }

View file

@ -2819,6 +2819,23 @@ not a distinct static type. Likewise, it's not legal to attempt to
behavior for specific enum variants. behavior for specific enum variants.
"##, "##,
E0569: r##"
If an impl has a generic parameter with the `#[may_dangle]` attribute, then
that impl must be declared as an `unsafe impl. For example:
```compile_fail,E0569
struct Foo<X>(X);
impl<#[may_dangle] X> Drop for Foo {
fn drop(&mut self) { }
}
```
In this example, we are asserting that the destructor for `Foo` will not
access any data of type `X`, and require this assertion to be true for
overall safety in our program. The compiler does not currently attempt to
verify this assertion; therefore we must tag this `impl` as unsafe.
"##,
E0318: r##" E0318: r##"
Default impls for a trait must be located in the same crate where the trait was Default impls for a trait must be located in the same crate where the trait was
defined. For more information see the [opt-in builtin traits RFC](https://github defined. For more information see the [opt-in builtin traits RFC](https://github

View file

@ -0,0 +1,46 @@
// Copyright 2016 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(generic_param_attrs)]
#![feature(dropck_eyepatch)]
// This test ensures that a use of `#[may_dangle]` is rejected if
// it is not attached to an `unsafe impl`.
use std::fmt;
struct Dt<A: fmt::Debug>(&'static str, A);
struct Dr<'a, B:'a+fmt::Debug>(&'static str, &'a B);
struct Pt<A,B: fmt::Debug>(&'static str, A, B);
struct Pr<'a, 'b, B:'a+'b+fmt::Debug>(&'static str, &'a B, &'b B);
struct St<A: fmt::Debug>(&'static str, A);
struct Sr<'a, B:'a+fmt::Debug>(&'static str, &'a B);
impl<A: fmt::Debug> Drop for Dt<A> {
fn drop(&mut self) { println!("drop {} {:?}", self.0, self.1); }
}
impl<'a, B: fmt::Debug> Drop for Dr<'a, B> {
fn drop(&mut self) { println!("drop {} {:?}", self.0, self.1); }
}
impl<#[may_dangle] A, B: fmt::Debug> Drop for Pt<A, B> {
//~^ ERROR requires an `unsafe impl` declaration due to `#[may_dangle]` attribute
// (unsafe to access self.1 due to #[may_dangle] on A)
fn drop(&mut self) { println!("drop {} {:?}", self.0, self.2); }
}
impl<#[may_dangle] 'a, 'b, B: fmt::Debug> Drop for Pr<'a, 'b, B> {
//~^ ERROR requires an `unsafe impl` declaration due to `#[may_dangle]` attribute
// (unsafe to access self.1 due to #[may_dangle] on 'a)
fn drop(&mut self) { println!("drop {} {:?}", self.0, self.2); }
}
fn main() {
}