Rollup merge of #37117 - pnkfelix:may-dangle-attr, r=nikomatsakis
`#[may_dangle]` attribute `#[may_dangle]` attribute Second step of #34761. Last big hurdle before we can work in earnest towards Allocator integration (#32838) Note: I am not clear if this is *also* a syntax-breaking change that needs to be part of a breaking-batch.
This commit is contained in:
commit
373fcd1bd3
|
@ -401,6 +401,7 @@ impl<'a> LoweringContext<'a> {
|
|||
bounds: self.lower_bounds(&tp.bounds),
|
||||
default: tp.default.as_ref().map(|x| self.lower_ty(x)),
|
||||
span: tp.span,
|
||||
pure_wrt_drop: tp.attrs.iter().any(|attr| attr.check_name("may_dangle")),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -420,6 +421,7 @@ impl<'a> LoweringContext<'a> {
|
|||
hir::LifetimeDef {
|
||||
lifetime: self.lower_lifetime(&l.lifetime),
|
||||
bounds: self.lower_lifetimes(&l.bounds),
|
||||
pure_wrt_drop: l.attrs.iter().any(|attr| attr.check_name("may_dangle")),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -95,6 +95,7 @@ impl fmt::Debug for Lifetime {
|
|||
pub struct LifetimeDef {
|
||||
pub lifetime: Lifetime,
|
||||
pub bounds: HirVec<Lifetime>,
|
||||
pub pure_wrt_drop: bool,
|
||||
}
|
||||
|
||||
/// A "Path" is essentially Rust's notion of a name; for instance:
|
||||
|
@ -290,6 +291,7 @@ pub struct TyParam {
|
|||
pub bounds: TyParamBounds,
|
||||
pub default: Option<P<Ty>>,
|
||||
pub span: Span,
|
||||
pub pure_wrt_drop: bool,
|
||||
}
|
||||
|
||||
/// Represents lifetimes and type parameters attached to a declaration
|
||||
|
@ -328,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
|
||||
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
|
||||
pub struct WhereClause {
|
||||
|
|
|
@ -1231,16 +1231,17 @@ impl<'a, 'gcx, 'tcx> Rebuilder<'a, 'gcx, 'tcx> {
|
|||
lifetime: hir::Lifetime,
|
||||
region_names: &HashSet<ast::Name>)
|
||||
-> hir::HirVec<hir::TyParam> {
|
||||
ty_params.iter().map(|ty_param| {
|
||||
let bounds = self.rebuild_ty_param_bounds(ty_param.bounds.clone(),
|
||||
ty_params.into_iter().map(|ty_param| {
|
||||
let bounds = self.rebuild_ty_param_bounds(ty_param.bounds,
|
||||
lifetime,
|
||||
region_names);
|
||||
hir::TyParam {
|
||||
name: ty_param.name,
|
||||
id: ty_param.id,
|
||||
bounds: bounds,
|
||||
default: ty_param.default.clone(),
|
||||
default: ty_param.default,
|
||||
span: ty_param.span,
|
||||
pure_wrt_drop: ty_param.pure_wrt_drop,
|
||||
}
|
||||
}).collect()
|
||||
}
|
||||
|
@ -1299,8 +1300,11 @@ impl<'a, 'gcx, 'tcx> Rebuilder<'a, 'gcx, 'tcx> {
|
|||
-> hir::Generics {
|
||||
let mut lifetimes = Vec::new();
|
||||
for lt in add {
|
||||
lifetimes.push(hir::LifetimeDef { lifetime: *lt,
|
||||
bounds: hir::HirVec::new() });
|
||||
lifetimes.push(hir::LifetimeDef {
|
||||
lifetime: *lt,
|
||||
bounds: hir::HirVec::new(),
|
||||
pure_wrt_drop: false,
|
||||
});
|
||||
}
|
||||
for lt in &generics.lifetimes {
|
||||
if keep.contains(<.lifetime.name) ||
|
||||
|
|
|
@ -680,6 +680,11 @@ pub struct TypeParameterDef<'tcx> {
|
|||
pub default_def_id: DefId, // for use in error reporing about defaults
|
||||
pub default: Option<Ty<'tcx>>,
|
||||
pub object_lifetime_default: ObjectLifetimeDefault<'tcx>,
|
||||
|
||||
/// `pure_wrt_drop`, set by the (unsafe) `#[may_dangle]` attribute
|
||||
/// on generic parameter `T`, asserts data behind the parameter
|
||||
/// `T` won't be accessed during the parent type's `Drop` impl.
|
||||
pub pure_wrt_drop: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, RustcEncodable, RustcDecodable)]
|
||||
|
@ -688,6 +693,11 @@ pub struct RegionParameterDef<'tcx> {
|
|||
pub def_id: DefId,
|
||||
pub index: u32,
|
||||
pub bounds: Vec<&'tcx ty::Region>,
|
||||
|
||||
/// `pure_wrt_drop`, set by the (unsafe) `#[may_dangle]` attribute
|
||||
/// on generic parameter `'a`, asserts data of lifetime `'a`
|
||||
/// won't be accessed during the parent type's `Drop` impl.
|
||||
pub pure_wrt_drop: bool,
|
||||
}
|
||||
|
||||
impl<'tcx> RegionParameterDef<'tcx> {
|
||||
|
@ -732,6 +742,14 @@ impl<'tcx> Generics<'tcx> {
|
|||
pub fn count(&self) -> usize {
|
||||
self.parent_count() + self.own_count()
|
||||
}
|
||||
|
||||
pub fn region_param(&self, param: &EarlyBoundRegion) -> &RegionParameterDef<'tcx> {
|
||||
&self.regions[param.index as usize - self.has_self as usize]
|
||||
}
|
||||
|
||||
pub fn type_param(&self, param: &ParamTy) -> &TypeParameterDef<'tcx> {
|
||||
&self.types[param.idx as usize - self.has_self as usize - self.regions.len()]
|
||||
}
|
||||
}
|
||||
|
||||
/// Bounds on generics.
|
||||
|
|
|
@ -716,6 +716,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::TypeParameterDef<'tcx> {
|
|||
default: self.default.fold_with(folder),
|
||||
default_def_id: self.default_def_id,
|
||||
object_lifetime_default: self.object_lifetime_default.fold_with(folder),
|
||||
pure_wrt_drop: self.pure_wrt_drop,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -754,6 +755,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::RegionParameterDef<'tcx> {
|
|||
def_id: self.def_id,
|
||||
index: self.index,
|
||||
bounds: self.bounds.fold_with(folder),
|
||||
pure_wrt_drop: self.pure_wrt_drop,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -71,7 +71,8 @@ fn ensure_drop_params_and_item_params_correspond<'a, 'tcx>(
|
|||
ccx: &CrateCtxt<'a, 'tcx>,
|
||||
drop_impl_did: DefId,
|
||||
drop_impl_ty: Ty<'tcx>,
|
||||
self_type_did: DefId) -> Result<(), ()>
|
||||
self_type_did: DefId)
|
||||
-> Result<(), ()>
|
||||
{
|
||||
let tcx = ccx.tcx;
|
||||
let drop_impl_node_id = tcx.map.as_local_node_id(drop_impl_did).unwrap();
|
||||
|
@ -123,7 +124,9 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'a, 'tcx>(
|
|||
drop_impl_did: DefId,
|
||||
dtor_predicates: &ty::GenericPredicates<'tcx>,
|
||||
self_type_did: DefId,
|
||||
self_to_impl_substs: &Substs<'tcx>) -> Result<(), ()> {
|
||||
self_to_impl_substs: &Substs<'tcx>)
|
||||
-> Result<(), ()>
|
||||
{
|
||||
|
||||
// Here is an example, analogous to that from
|
||||
// `compare_impl_method`.
|
||||
|
@ -350,7 +353,8 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'b, 'gcx, 'tcx>(
|
|||
cx: &mut DropckContext<'a, 'b, 'gcx, 'tcx>,
|
||||
context: TypeContext,
|
||||
ty: Ty<'tcx>,
|
||||
depth: usize) -> Result<(), Error<'tcx>>
|
||||
depth: usize)
|
||||
-> Result<(), Error<'tcx>>
|
||||
{
|
||||
let tcx = cx.rcx.tcx;
|
||||
// Issue #22443: Watch out for overflow. While we are careful to
|
||||
|
@ -402,16 +406,27 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'b, 'gcx, 'tcx>(
|
|||
// unbounded type parameter `T`, we must resume the recursive
|
||||
// analysis on `T` (since it would be ignored by
|
||||
// type_must_outlive).
|
||||
if has_dtor_of_interest(tcx, ty) {
|
||||
debug!("iterate_over_potentially_unsafe_regions_in_type \
|
||||
{}ty: {} - is a dtorck type!",
|
||||
(0..depth).map(|_| ' ').collect::<String>(),
|
||||
ty);
|
||||
|
||||
cx.rcx.type_must_outlive(infer::SubregionOrigin::SafeDestructor(cx.span),
|
||||
ty, tcx.mk_region(ty::ReScope(cx.parent_scope)));
|
||||
|
||||
return Ok(());
|
||||
let dropck_kind = has_dtor_of_interest(tcx, ty);
|
||||
debug!("iterate_over_potentially_unsafe_regions_in_type \
|
||||
ty: {:?} dropck_kind: {:?}", ty, dropck_kind);
|
||||
match dropck_kind {
|
||||
DropckKind::NoBorrowedDataAccessedInMyDtor => {
|
||||
// The maximally blind attribute.
|
||||
}
|
||||
DropckKind::BorrowedDataMustStrictlyOutliveSelf => {
|
||||
cx.rcx.type_must_outlive(infer::SubregionOrigin::SafeDestructor(cx.span),
|
||||
ty, tcx.mk_region(ty::ReScope(cx.parent_scope)));
|
||||
return Ok(());
|
||||
}
|
||||
DropckKind::RevisedSelf(revised_ty) => {
|
||||
cx.rcx.type_must_outlive(infer::SubregionOrigin::SafeDestructor(cx.span),
|
||||
revised_ty, tcx.mk_region(ty::ReScope(cx.parent_scope)));
|
||||
// Do not return early from this case; we want
|
||||
// to recursively process the internal structure of Self
|
||||
// (because even though the Drop for Self has been asserted
|
||||
// safe, the types instantiated for the generics of Self
|
||||
// may themselves carry dropck constraints.)
|
||||
}
|
||||
}
|
||||
|
||||
debug!("iterate_over_potentially_unsafe_regions_in_type \
|
||||
|
@ -492,16 +507,140 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'b, 'gcx, 'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
enum DropckKind<'tcx> {
|
||||
/// The "safe" kind; i.e. conservatively assume any borrow
|
||||
/// accessed by dtor, and therefore such data must strictly
|
||||
/// outlive self.
|
||||
///
|
||||
/// Equivalent to RevisedTy with no change to the self type.
|
||||
BorrowedDataMustStrictlyOutliveSelf,
|
||||
|
||||
/// The nearly completely-unsafe kind.
|
||||
///
|
||||
/// Equivalent to RevisedSelf with *all* parameters remapped to ()
|
||||
/// (maybe...?)
|
||||
NoBorrowedDataAccessedInMyDtor,
|
||||
|
||||
/// Assume all borrowed data access by dtor occurs as if Self has the
|
||||
/// type carried by this variant. In practice this means that some
|
||||
/// of the type parameters are remapped to `()` (and some lifetime
|
||||
/// parameters remapped to `'static`), because the developer has asserted
|
||||
/// that the destructor will not access their contents.
|
||||
RevisedSelf(Ty<'tcx>),
|
||||
}
|
||||
|
||||
/// Returns the classification of what kind of check should be applied
|
||||
/// to `ty`, which may include a revised type where some of the type
|
||||
/// parameters are re-mapped to `()` to reflect the destructor's
|
||||
/// "purity" with respect to their actual contents.
|
||||
fn has_dtor_of_interest<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
ty: Ty<'tcx>) -> bool {
|
||||
ty: Ty<'tcx>)
|
||||
-> DropckKind<'tcx> {
|
||||
match ty.sty {
|
||||
ty::TyAdt(def, _) => {
|
||||
def.is_dtorck(tcx)
|
||||
ty::TyAdt(adt_def, substs) => {
|
||||
if !adt_def.is_dtorck(tcx) {
|
||||
return DropckKind::NoBorrowedDataAccessedInMyDtor;
|
||||
}
|
||||
|
||||
// Find the `impl<..> Drop for _` to inspect any
|
||||
// attributes attached to the impl's generics.
|
||||
let dtor_method = adt_def.destructor()
|
||||
.expect("dtorck type without destructor impossible");
|
||||
let method = tcx.impl_or_trait_item(dtor_method);
|
||||
let impl_id: DefId = method.container().id();
|
||||
let revised_ty = revise_self_ty(tcx, adt_def, impl_id, substs);
|
||||
return DropckKind::RevisedSelf(revised_ty);
|
||||
}
|
||||
ty::TyTrait(..) | ty::TyProjection(..) | ty::TyAnon(..) => {
|
||||
debug!("ty: {:?} isn't known, and therefore is a dropck type", ty);
|
||||
true
|
||||
return DropckKind::BorrowedDataMustStrictlyOutliveSelf;
|
||||
},
|
||||
_ => false
|
||||
_ => {
|
||||
return DropckKind::NoBorrowedDataAccessedInMyDtor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Constructs new Ty just like the type defined by `adt_def` coupled
|
||||
// with `substs`, except each type and lifetime parameter marked as
|
||||
// `#[may_dangle]` in the Drop impl (identified by `impl_id`) is
|
||||
// respectively mapped to `()` or `'static`.
|
||||
//
|
||||
// For example: If the `adt_def` maps to:
|
||||
//
|
||||
// enum Foo<'a, X, Y> { ... }
|
||||
//
|
||||
// and the `impl_id` maps to:
|
||||
//
|
||||
// impl<#[may_dangle] 'a, X, #[may_dangle] Y> Drop for Foo<'a, X, Y> { ... }
|
||||
//
|
||||
// then revises input: `Foo<'r,i64,&'r i64>` to: `Foo<'static,i64,()>`
|
||||
fn revise_self_ty<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
adt_def: ty::AdtDef<'tcx>,
|
||||
impl_id: DefId,
|
||||
substs: &Substs<'tcx>)
|
||||
-> Ty<'tcx> {
|
||||
// Get generics for `impl Drop` to query for `#[may_dangle]` attr.
|
||||
let impl_bindings = tcx.lookup_generics(impl_id);
|
||||
|
||||
// Get Substs attached to Self on `impl Drop`; process in parallel
|
||||
// with `substs`, replacing dangling entries as appropriate.
|
||||
let self_substs = {
|
||||
let impl_self_ty: Ty<'tcx> = tcx.lookup_item_type(impl_id).ty;
|
||||
if let ty::TyAdt(self_adt_def, self_substs) = impl_self_ty.sty {
|
||||
assert_eq!(adt_def, self_adt_def);
|
||||
self_substs
|
||||
} else {
|
||||
bug!("Self in `impl Drop for _` must be an Adt.");
|
||||
}
|
||||
};
|
||||
|
||||
// Walk `substs` + `self_substs`, build new substs appropriate for
|
||||
// `adt_def`; each non-dangling param reuses entry from `substs`.
|
||||
//
|
||||
// Note: The manner we map from a right-hand side (i.e. Region or
|
||||
// Ty) for a given `def` to generic parameter associated with that
|
||||
// right-hand side is tightly coupled to `Drop` impl constraints.
|
||||
//
|
||||
// E.g. we know such a Ty must be `TyParam`, because a destructor
|
||||
// for `struct Foo<X>` is defined via `impl<Y> Drop for Foo<Y>`,
|
||||
// and never by (for example) `impl<Z> Drop for Foo<Vec<Z>>`.
|
||||
let substs = Substs::for_item(
|
||||
tcx,
|
||||
adt_def.did,
|
||||
|def, _| {
|
||||
let r_orig = substs.region_for_def(def);
|
||||
let impl_self_orig = self_substs.region_for_def(def);
|
||||
let r = if let ty::Region::ReEarlyBound(ref ebr) = *impl_self_orig {
|
||||
if impl_bindings.region_param(ebr).pure_wrt_drop {
|
||||
tcx.mk_region(ty::ReStatic)
|
||||
} else {
|
||||
r_orig
|
||||
}
|
||||
} else {
|
||||
bug!("substs for an impl must map regions to ReEarlyBound");
|
||||
};
|
||||
debug!("has_dtor_of_interest mapping def {:?} orig {:?} to {:?}",
|
||||
def, r_orig, r);
|
||||
r
|
||||
},
|
||||
|def, _| {
|
||||
let t_orig = substs.type_for_def(def);
|
||||
let impl_self_orig = self_substs.type_for_def(def);
|
||||
let t = if let ty::TypeVariants::TyParam(ref pt) = impl_self_orig.sty {
|
||||
if impl_bindings.type_param(pt).pure_wrt_drop {
|
||||
tcx.mk_nil()
|
||||
} else {
|
||||
t_orig
|
||||
}
|
||||
} else {
|
||||
bug!("substs for an impl must map types to TyParam");
|
||||
};
|
||||
debug!("has_dtor_of_interest mapping def {:?} orig {:?} {:?} to {:?} {:?}",
|
||||
def, t_orig, t_orig.sty, t, t.sty);
|
||||
t
|
||||
});
|
||||
|
||||
return tcx.mk_adt(adt_def, &substs);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
use rustc::ty::TyCtxt;
|
||||
use rustc::hir::intravisit;
|
||||
use rustc::hir;
|
||||
use rustc::hir::{self, Unsafety};
|
||||
|
||||
pub fn check<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
|
||||
let mut orphan = UnsafetyChecker { tcx: tcx };
|
||||
|
@ -27,6 +27,7 @@ struct UnsafetyChecker<'cx, 'tcx: 'cx> {
|
|||
impl<'cx, 'tcx, 'v> UnsafetyChecker<'cx, 'tcx> {
|
||||
fn check_unsafety_coherence(&mut self,
|
||||
item: &'v hir::Item,
|
||||
impl_generics: Option<&hir::Generics>,
|
||||
unsafety: hir::Unsafety,
|
||||
polarity: hir::ImplPolarity) {
|
||||
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) => {
|
||||
let trait_def = self.tcx.lookup_trait_def(trait_ref.def_id);
|
||||
match (trait_def.unsafety, unsafety, polarity) {
|
||||
(hir::Unsafety::Unsafe, hir::Unsafety::Unsafe, hir::ImplPolarity::Negative) => {
|
||||
let unsafe_attr = impl_generics.and_then(|g| g.carries_unsafe_attr());
|
||||
match (trait_def.unsafety, unsafe_attr, unsafety, polarity) {
|
||||
(_, _, Unsafety::Unsafe, hir::ImplPolarity::Negative) => {
|
||||
span_err!(self.tcx.sess,
|
||||
item.span,
|
||||
E0198,
|
||||
"negative implementations are not unsafe");
|
||||
}
|
||||
|
||||
(hir::Unsafety::Normal, hir::Unsafety::Unsafe, _) => {
|
||||
(Unsafety::Normal, None, Unsafety::Unsafe, _) => {
|
||||
span_err!(self.tcx.sess,
|
||||
item.span,
|
||||
E0199,
|
||||
|
@ -63,7 +65,7 @@ impl<'cx, 'tcx, 'v> UnsafetyChecker<'cx, 'tcx> {
|
|||
trait_ref);
|
||||
}
|
||||
|
||||
(hir::Unsafety::Unsafe, hir::Unsafety::Normal, hir::ImplPolarity::Positive) => {
|
||||
(Unsafety::Unsafe, _, Unsafety::Normal, hir::ImplPolarity::Positive) => {
|
||||
span_err!(self.tcx.sess,
|
||||
item.span,
|
||||
E0200,
|
||||
|
@ -71,9 +73,19 @@ impl<'cx, 'tcx, 'v> UnsafetyChecker<'cx, 'tcx> {
|
|||
trait_ref);
|
||||
}
|
||||
|
||||
(hir::Unsafety::Unsafe, hir::Unsafety::Normal, hir::ImplPolarity::Negative) |
|
||||
(hir::Unsafety::Unsafe, hir::Unsafety::Unsafe, hir::ImplPolarity::Positive) |
|
||||
(hir::Unsafety::Normal, hir::Unsafety::Normal, _) => {
|
||||
(Unsafety::Normal, Some(g), Unsafety::Normal, hir::ImplPolarity::Positive) =>
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -86,10 +98,10 @@ impl<'cx, 'tcx, 'v> intravisit::Visitor<'v> for UnsafetyChecker<'cx, 'tcx> {
|
|||
fn visit_item(&mut self, item: &'v hir::Item) {
|
||||
match item.node {
|
||||
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, ..) => {
|
||||
self.check_unsafety_coherence(item, unsafety, polarity);
|
||||
hir::ItemImpl(unsafety, polarity, ref generics, ..) => {
|
||||
self.check_unsafety_coherence(item, Some(generics), unsafety, polarity);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
|
@ -1482,6 +1482,7 @@ fn generics_of_def_id<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
|
|||
default_def_id: tcx.map.local_def_id(parent),
|
||||
default: None,
|
||||
object_lifetime_default: ty::ObjectLifetimeDefault::BaseDefault,
|
||||
pure_wrt_drop: false,
|
||||
};
|
||||
tcx.ty_param_defs.borrow_mut().insert(param_id, def.clone());
|
||||
opt_self = Some(def);
|
||||
|
@ -1526,7 +1527,8 @@ fn generics_of_def_id<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
|
|||
def_id: tcx.map.local_def_id(l.lifetime.id),
|
||||
bounds: l.bounds.iter().map(|l| {
|
||||
ast_region_to_region(tcx, l)
|
||||
}).collect()
|
||||
}).collect(),
|
||||
pure_wrt_drop: l.pure_wrt_drop,
|
||||
}
|
||||
}).collect::<Vec<_>>();
|
||||
|
||||
|
@ -1926,6 +1928,7 @@ fn get_or_create_type_parameter_def<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
|
|||
default_def_id: ccx.tcx.map.local_def_id(parent),
|
||||
default: default,
|
||||
object_lifetime_default: object_lifetime_default,
|
||||
pure_wrt_drop: param.pure_wrt_drop,
|
||||
};
|
||||
|
||||
if def.name == keywords::SelfType.name() {
|
||||
|
|
|
@ -2819,6 +2819,26 @@ not a distinct static type. Likewise, it's not legal to attempt to
|
|||
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
|
||||
#![feature(generic_param_attrs)]
|
||||
#![feature(dropck_eyepatch)]
|
||||
|
||||
struct Foo<X>(X);
|
||||
impl<#[may_dangle] X> Drop for Foo<X> {
|
||||
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##"
|
||||
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
|
||||
|
|
|
@ -167,6 +167,9 @@ declare_features! (
|
|||
// RFC 1238
|
||||
(active, dropck_parametricity, "1.3.0", Some(28498)),
|
||||
|
||||
// Allows using the may_dangle attribute; RFC 1327
|
||||
(active, dropck_eyepatch, "1.10.0", Some(34761)),
|
||||
|
||||
// Allows the use of custom attributes; RFC 572
|
||||
(active, custom_attribute, "1.0.0", Some(29642)),
|
||||
|
||||
|
@ -616,6 +619,11 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGat
|
|||
"unsafe_destructor_blind_to_params has unstable semantics \
|
||||
and may be removed in the future",
|
||||
cfg_fn!(dropck_parametricity))),
|
||||
("may_dangle",
|
||||
Normal,
|
||||
Gated("dropck_eyepatch",
|
||||
"may_dangle has unstable semantics and may be removed in the future",
|
||||
cfg_fn!(dropck_eyepatch))),
|
||||
("unwind", Whitelisted, Gated("unwind_attributes", "#[unwind] is experimental",
|
||||
cfg_fn!(unwind_attributes))),
|
||||
|
||||
|
|
|
@ -1361,6 +1361,7 @@ impl<'a> State<'a> {
|
|||
if comma {
|
||||
try!(self.word_space(","))
|
||||
}
|
||||
try!(self.print_outer_attributes_inline(&lifetime_def.attrs));
|
||||
try!(self.print_lifetime_bounds(&lifetime_def.lifetime, &lifetime_def.bounds));
|
||||
comma = true;
|
||||
}
|
||||
|
@ -2803,6 +2804,7 @@ impl<'a> State<'a> {
|
|||
try!(self.commasep(Inconsistent, &ints[..], |s, &idx| {
|
||||
if idx < generics.lifetimes.len() {
|
||||
let lifetime_def = &generics.lifetimes[idx];
|
||||
try!(s.print_outer_attributes_inline(&lifetime_def.attrs));
|
||||
s.print_lifetime_bounds(&lifetime_def.lifetime, &lifetime_def.bounds)
|
||||
} else {
|
||||
let idx = idx - generics.lifetimes.len();
|
||||
|
@ -2816,6 +2818,7 @@ impl<'a> State<'a> {
|
|||
}
|
||||
|
||||
pub fn print_ty_param(&mut self, param: &ast::TyParam) -> io::Result<()> {
|
||||
try!(self.print_outer_attributes_inline(¶m.attrs));
|
||||
try!(self.print_ident(param.ident));
|
||||
try!(self.print_bounds(":", ¶m.bounds));
|
||||
match param.default {
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
// 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(optin_builtin_traits)]
|
||||
|
||||
struct Foo;
|
||||
|
||||
unsafe impl !Clone for Foo { } //~ ERROR negative implementations are not unsafe [E0198]
|
||||
|
||||
fn main() {
|
||||
}
|
|
@ -12,7 +12,8 @@
|
|||
|
||||
struct Foo;
|
||||
|
||||
unsafe impl !Clone for Foo { } //~ ERROR E0199
|
||||
trait Bar { }
|
||||
unsafe impl Bar for Foo { } //~ ERROR implementing the trait `Bar` is not unsafe [E0199]
|
||||
|
||||
fn main() {
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
// 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.
|
||||
|
||||
// Check that `may_dangle` is rejected if `dropck_eyepatch` feature gate is absent.
|
||||
|
||||
#![feature(generic_param_attrs)]
|
||||
|
||||
struct Pt<A>(A);
|
||||
impl<#[may_dangle] A> Drop for Pt<A> {
|
||||
//~^ ERROR may_dangle has unstable semantics and may be removed in the future
|
||||
//~| HELP add #![feature(dropck_eyepatch)] to the crate attributes to enable
|
||||
fn drop(&mut self) { }
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
// 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)]
|
||||
|
||||
// The point of this test is to illustrate that the `#[may_dangle]`
|
||||
// attribute specifically allows, in the context of a type
|
||||
// implementing `Drop`, a generic parameter to be instantiated with a
|
||||
// lifetime that does not strictly outlive the owning type itself,
|
||||
// and that this attributes effects are preserved when importing
|
||||
// the type from another crate.
|
||||
//
|
||||
// See also dropck-eyepatch.rs for more information about the general
|
||||
// structure of the test.
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
||||
pub trait Foo { fn foo(&self, _: &str); }
|
||||
|
||||
pub struct Dt<A: Foo>(pub &'static str, pub A);
|
||||
pub struct Dr<'a, B:'a+Foo>(pub &'static str, pub &'a B);
|
||||
pub struct Pt<A,B: Foo>(pub &'static str, pub A, pub B);
|
||||
pub struct Pr<'a, 'b, B:'a+'b+Foo>(pub &'static str, pub &'a B, pub &'b B);
|
||||
pub struct St<A: Foo>(pub &'static str, pub A);
|
||||
pub struct Sr<'a, B:'a+Foo>(pub &'static str, pub &'a B);
|
||||
|
||||
impl<A: Foo> Drop for Dt<A> {
|
||||
fn drop(&mut self) { println!("drop {}", self.0); self.1.foo(self.0); }
|
||||
}
|
||||
impl<'a, B: Foo> Drop for Dr<'a, B> {
|
||||
fn drop(&mut self) { println!("drop {}", self.0); self.1.foo(self.0); }
|
||||
}
|
||||
unsafe impl<#[may_dangle] A, B: Foo> Drop for Pt<A, B> {
|
||||
// (unsafe to access self.1 due to #[may_dangle] on A)
|
||||
fn drop(&mut self) { println!("drop {}", self.0); self.2.foo(self.0); }
|
||||
}
|
||||
unsafe impl<#[may_dangle] 'a, 'b, B: Foo> Drop for Pr<'a, 'b, B> {
|
||||
// (unsafe to access self.1 due to #[may_dangle] on 'a)
|
||||
fn drop(&mut self) { println!("drop {}", self.0); self.2.foo(self.0); }
|
||||
}
|
||||
|
||||
impl Foo for RefCell<String> {
|
||||
fn foo(&self, s: &str) {
|
||||
let s2 = format!("{}|{}", *self.borrow(), s);
|
||||
*self.borrow_mut() = s2;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T:Foo> Foo for &'a T {
|
||||
fn foo(&self, s: &str) {
|
||||
(*self).foo(s);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
// 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.
|
||||
|
||||
// aux-build:dropck_eyepatch_extern_crate.rs
|
||||
|
||||
extern crate dropck_eyepatch_extern_crate as other;
|
||||
|
||||
use other::{Dt,Dr,Pt,Pr,St,Sr};
|
||||
|
||||
fn main() {
|
||||
use std::cell::RefCell;
|
||||
|
||||
struct CheckOnDrop(RefCell<String>, &'static str);
|
||||
impl Drop for CheckOnDrop {
|
||||
fn drop(&mut self) { assert_eq!(*self.0.borrow(), self.1); }
|
||||
}
|
||||
|
||||
let c_long;
|
||||
let (c, dt, dr, pt, pr, st, sr)
|
||||
: (CheckOnDrop, Dt<_>, Dr<_>, Pt<_, _>, Pr<_>, St<_>, Sr<_>);
|
||||
c_long = CheckOnDrop(RefCell::new("c_long".to_string()),
|
||||
"c_long|pr|pt|dr|dt");
|
||||
c = CheckOnDrop(RefCell::new("c".to_string()),
|
||||
"c");
|
||||
|
||||
// No error: sufficiently long-lived state can be referenced in dtors
|
||||
dt = Dt("dt", &c_long.0);
|
||||
dr = Dr("dr", &c_long.0);
|
||||
|
||||
// No error: Drop impl asserts .1 (A and &'a _) are not accessed
|
||||
pt = Pt("pt", &c.0, &c_long.0);
|
||||
pr = Pr("pr", &c.0, &c_long.0);
|
||||
|
||||
// No error: St and Sr have no destructor.
|
||||
st = St("st", &c.0);
|
||||
sr = Sr("sr", &c.0);
|
||||
|
||||
println!("{:?}", (dt.0, dr.0, pt.0, pr.0, st.0, sr.0));
|
||||
assert_eq!(*c_long.0.borrow(), "c_long");
|
||||
assert_eq!(*c.0.borrow(), "c");
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
// 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)]
|
||||
|
||||
// The point of this test is to test uses of `#[may_dangle]` attribute
|
||||
// where the formal declaration order (in the impl generics) does not
|
||||
// match the actual usage order (in the type instantiation).
|
||||
//
|
||||
// See also dropck-eyepatch.rs for more information about the general
|
||||
// structure of the test.
|
||||
|
||||
trait Foo { fn foo(&self, _: &str); }
|
||||
|
||||
struct Dt<A: Foo>(&'static str, A);
|
||||
struct Dr<'a, B:'a+Foo>(&'static str, &'a B);
|
||||
struct Pt<A: Foo, B: Foo>(&'static str, A, B);
|
||||
struct Pr<'a, 'b, B:'a+'b+Foo>(&'static str, &'a B, &'b B);
|
||||
struct St<A: Foo>(&'static str, A);
|
||||
struct Sr<'a, B:'a+Foo>(&'static str, &'a B);
|
||||
|
||||
impl<A: Foo> Drop for Dt<A> {
|
||||
fn drop(&mut self) { println!("drop {}", self.0); self.1.foo(self.0); }
|
||||
}
|
||||
impl<'a, B: Foo> Drop for Dr<'a, B> {
|
||||
fn drop(&mut self) { println!("drop {}", self.0); self.1.foo(self.0); }
|
||||
}
|
||||
unsafe impl<B: Foo, #[may_dangle] A: Foo> Drop for Pt<A, B> {
|
||||
// (unsafe to access self.1 due to #[may_dangle] on A)
|
||||
fn drop(&mut self) { println!("drop {}", self.0); self.2.foo(self.0); }
|
||||
}
|
||||
unsafe impl<'b, #[may_dangle] 'a, B: Foo> Drop for Pr<'a, 'b, B> {
|
||||
// (unsafe to access self.1 due to #[may_dangle] on 'a)
|
||||
fn drop(&mut self) { println!("drop {}", self.0); self.2.foo(self.0); }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
use std::cell::RefCell;
|
||||
|
||||
impl Foo for RefCell<String> {
|
||||
fn foo(&self, s: &str) {
|
||||
let s2 = format!("{}|{}", *self.borrow(), s);
|
||||
*self.borrow_mut() = s2;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T:Foo> Foo for &'a T {
|
||||
fn foo(&self, s: &str) {
|
||||
(*self).foo(s);
|
||||
}
|
||||
}
|
||||
|
||||
struct CheckOnDrop(RefCell<String>, &'static str);
|
||||
impl Drop for CheckOnDrop {
|
||||
fn drop(&mut self) { assert_eq!(*self.0.borrow(), self.1); }
|
||||
}
|
||||
|
||||
let c_long;
|
||||
let (c, dt, dr, pt, pr, st, sr)
|
||||
: (CheckOnDrop, Dt<_>, Dr<_>, Pt<_, _>, Pr<_>, St<_>, Sr<_>);
|
||||
c_long = CheckOnDrop(RefCell::new("c_long".to_string()),
|
||||
"c_long|pr|pt|dr|dt");
|
||||
c = CheckOnDrop(RefCell::new("c".to_string()),
|
||||
"c");
|
||||
|
||||
// No error: sufficiently long-lived state can be referenced in dtors
|
||||
dt = Dt("dt", &c_long.0);
|
||||
dr = Dr("dr", &c_long.0);
|
||||
|
||||
// No error: Drop impl asserts .1 (A and &'a _) are not accessed
|
||||
pt = Pt("pt", &c.0, &c_long.0);
|
||||
pr = Pr("pr", &c.0, &c_long.0);
|
||||
|
||||
// No error: St and Sr have no destructor.
|
||||
st = St("st", &c.0);
|
||||
sr = Sr("sr", &c.0);
|
||||
|
||||
println!("{:?}", (dt.0, dr.0, pt.0, pr.0, st.0, sr.0));
|
||||
assert_eq!(*c_long.0.borrow(), "c_long");
|
||||
assert_eq!(*c.0.borrow(), "c");
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
// 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)]
|
||||
|
||||
// The point of this test is to illustrate that the `#[may_dangle]`
|
||||
// attribute specifically allows, in the context of a type
|
||||
// implementing `Drop`, a generic parameter to be instantiated with a
|
||||
// lifetime that does not strictly outlive the owning type itself.
|
||||
//
|
||||
// Here we test that a model use of `#[may_dangle]` will compile and run.
|
||||
//
|
||||
// The illustration is made concrete by comparison with two variations
|
||||
// on the type with `#[may_dangle]`:
|
||||
//
|
||||
// 1. an analogous type that does not implement `Drop` (and thus
|
||||
// should exhibit maximal flexibility with respect to dropck), and
|
||||
//
|
||||
// 2. an analogous type that does not use `#[may_dangle]` (and thus
|
||||
// should exhibit the standard limitations imposed by dropck.
|
||||
//
|
||||
// The types in this file follow a pattern, {D,P,S}{t,r}, where:
|
||||
//
|
||||
// - D means "I implement Drop"
|
||||
//
|
||||
// - P means "I implement Drop but guarantee my (first) parameter is
|
||||
// pure, i.e. not accessed from the destructor"; no other parameters
|
||||
// are pure.
|
||||
//
|
||||
// - S means "I do not implement Drop"
|
||||
//
|
||||
// - t suffix is used when the first generic is a type
|
||||
//
|
||||
// - r suffix is used when the first generic is a lifetime.
|
||||
|
||||
trait Foo { fn foo(&self, _: &str); }
|
||||
|
||||
struct Dt<A: Foo>(&'static str, A);
|
||||
struct Dr<'a, B:'a+Foo>(&'static str, &'a B);
|
||||
struct Pt<A,B: Foo>(&'static str, A, B);
|
||||
struct Pr<'a, 'b, B:'a+'b+Foo>(&'static str, &'a B, &'b B);
|
||||
struct St<A: Foo>(&'static str, A);
|
||||
struct Sr<'a, B:'a+Foo>(&'static str, &'a B);
|
||||
|
||||
impl<A: Foo> Drop for Dt<A> {
|
||||
fn drop(&mut self) { println!("drop {}", self.0); self.1.foo(self.0); }
|
||||
}
|
||||
impl<'a, B: Foo> Drop for Dr<'a, B> {
|
||||
fn drop(&mut self) { println!("drop {}", self.0); self.1.foo(self.0); }
|
||||
}
|
||||
unsafe impl<#[may_dangle] A, B: Foo> Drop for Pt<A, B> {
|
||||
// (unsafe to access self.1 due to #[may_dangle] on A)
|
||||
fn drop(&mut self) { println!("drop {}", self.0); self.2.foo(self.0); }
|
||||
}
|
||||
unsafe impl<#[may_dangle] 'a, 'b, B: Foo> Drop for Pr<'a, 'b, B> {
|
||||
// (unsafe to access self.1 due to #[may_dangle] on 'a)
|
||||
fn drop(&mut self) { println!("drop {}", self.0); self.2.foo(self.0); }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
use std::cell::RefCell;
|
||||
|
||||
impl Foo for RefCell<String> {
|
||||
fn foo(&self, s: &str) {
|
||||
let s2 = format!("{}|{}", *self.borrow(), s);
|
||||
*self.borrow_mut() = s2;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T:Foo> Foo for &'a T {
|
||||
fn foo(&self, s: &str) {
|
||||
(*self).foo(s);
|
||||
}
|
||||
}
|
||||
|
||||
struct CheckOnDrop(RefCell<String>, &'static str);
|
||||
impl Drop for CheckOnDrop {
|
||||
fn drop(&mut self) { assert_eq!(*self.0.borrow(), self.1); }
|
||||
}
|
||||
|
||||
let c_long;
|
||||
let (c, dt, dr, pt, pr, st, sr)
|
||||
: (CheckOnDrop, Dt<_>, Dr<_>, Pt<_, _>, Pr<_>, St<_>, Sr<_>);
|
||||
c_long = CheckOnDrop(RefCell::new("c_long".to_string()),
|
||||
"c_long|pr|pt|dr|dt");
|
||||
c = CheckOnDrop(RefCell::new("c".to_string()),
|
||||
"c");
|
||||
|
||||
// No error: sufficiently long-lived state can be referenced in dtors
|
||||
dt = Dt("dt", &c_long.0);
|
||||
dr = Dr("dr", &c_long.0);
|
||||
|
||||
// No error: Drop impl asserts .1 (A and &'a _) are not accessed
|
||||
pt = Pt("pt", &c.0, &c_long.0);
|
||||
pr = Pr("pr", &c.0, &c_long.0);
|
||||
|
||||
// No error: St and Sr have no destructor.
|
||||
st = St("st", &c.0);
|
||||
sr = Sr("sr", &c.0);
|
||||
|
||||
println!("{:?}", (dt.0, dr.0, pt.0, pr.0, st.0, sr.0));
|
||||
assert_eq!(*c_long.0.borrow(), "c_long");
|
||||
assert_eq!(*c.0.borrow(), "c");
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
// 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 is a support file for ../dropck-eyepatch-extern-crate.rs
|
||||
//
|
||||
// The point of this test is to illustrate that the `#[may_dangle]`
|
||||
// attribute specifically allows, in the context of a type
|
||||
// implementing `Drop`, a generic parameter to be instantiated with a
|
||||
// lifetime that does not strictly outlive the owning type itself,
|
||||
// and that this attribute's effects are preserved when importing
|
||||
// the type from another crate.
|
||||
//
|
||||
// See also ../dropck-eyepatch.rs for more information about the general
|
||||
// structure of the test.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
pub struct Dt<A: fmt::Debug>(pub &'static str, pub A);
|
||||
pub struct Dr<'a, B:'a+fmt::Debug>(pub &'static str, pub &'a B);
|
||||
pub struct Pt<A,B: fmt::Debug>(pub &'static str, pub A, pub B);
|
||||
pub struct Pr<'a, 'b, B:'a+'b+fmt::Debug>(pub &'static str, pub &'a B, pub &'b B);
|
||||
pub struct St<A: fmt::Debug>(pub &'static str, pub A);
|
||||
pub struct Sr<'a, B:'a+fmt::Debug>(pub &'static str, pub &'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); }
|
||||
}
|
||||
unsafe impl<#[may_dangle] A, B: fmt::Debug> Drop for Pt<A, B> {
|
||||
// (unsafe to access self.1 due to #[may_dangle] on A)
|
||||
fn drop(&mut self) { println!("drop {} {:?}", self.0, self.2); }
|
||||
}
|
||||
unsafe impl<#[may_dangle] 'a, 'b, B: fmt::Debug> Drop for Pr<'a, 'b, B> {
|
||||
// (unsafe to access self.1 due to #[may_dangle] on 'a)
|
||||
fn drop(&mut self) { println!("drop {} {:?}", self.0, self.2); }
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
// 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.
|
||||
|
||||
// aux-build:dropck_eyepatch_extern_crate.rs
|
||||
|
||||
// The point of this test is to illustrate that the `#[may_dangle]`
|
||||
// attribute specifically allows, in the context of a type
|
||||
// implementing `Drop`, a generic parameter to be instantiated with a
|
||||
// lifetime that does not strictly outlive the owning type itself,
|
||||
// and that this attribute's effects are preserved when importing
|
||||
// the type from another crate.
|
||||
//
|
||||
// See also dropck-eyepatch.rs for more information about the general
|
||||
// structure of the test.
|
||||
|
||||
extern crate dropck_eyepatch_extern_crate as other;
|
||||
|
||||
use other::{Dt,Dr,Pt,Pr,St,Sr};
|
||||
|
||||
fn main() {
|
||||
use std::cell::Cell;
|
||||
let c_long;
|
||||
let (c, mut dt, mut dr, mut pt, mut pr, st, sr)
|
||||
: (Cell<_>, Dt<_>, Dr<_>, Pt<_, _>, Pr<_>, St<_>, Sr<_>);
|
||||
c_long = Cell::new(1);
|
||||
c = Cell::new(1);
|
||||
|
||||
// No error: sufficiently long-lived state can be referenced in dtors
|
||||
dt = Dt("dt", &c_long);
|
||||
dr = Dr("dr", &c_long);
|
||||
// Error: destructor order imprecisely modelled
|
||||
dt = Dt("dt", &c); //~ ERROR `c` does not live long enough
|
||||
dr = Dr("dr", &c); //~ ERROR `c` does not live long enough
|
||||
|
||||
// No error: Drop impl asserts .1 (A and &'a _) are not accessed
|
||||
pt = Pt("pt", &c, &c_long);
|
||||
pr = Pr("pr", &c, &c_long);
|
||||
|
||||
// Error: Drop impl's assertion does not apply to `B` nor `&'b _`
|
||||
pt = Pt("pt", &c_long, &c); //~ ERROR `c` does not live long enough
|
||||
pr = Pr("pr", &c_long, &c); //~ ERROR `c` does not live long enough
|
||||
|
||||
// No error: St and Sr have no destructor.
|
||||
st = St("st", &c);
|
||||
sr = Sr("sr", &c);
|
||||
|
||||
println!("{:?}", (dt.0, dr.0, pt.0, pr.0, st.0, sr.0));
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
error: `c` does not live long enough
|
||||
--> $DIR/dropck-eyepatch-extern-crate.rs:39:20
|
||||
|
|
||||
39 | dt = Dt("dt", &c); //~ ERROR `c` does not live long enough
|
||||
| ^ does not live long enough
|
||||
...
|
||||
55 | }
|
||||
| - borrowed value dropped before borrower
|
||||
|
|
||||
= note: values in a scope are dropped in the opposite order they are created
|
||||
|
||||
error: `c` does not live long enough
|
||||
--> $DIR/dropck-eyepatch-extern-crate.rs:40:20
|
||||
|
|
||||
40 | dr = Dr("dr", &c); //~ ERROR `c` does not live long enough
|
||||
| ^ does not live long enough
|
||||
...
|
||||
55 | }
|
||||
| - borrowed value dropped before borrower
|
||||
|
|
||||
= note: values in a scope are dropped in the opposite order they are created
|
||||
|
||||
error: `c` does not live long enough
|
||||
--> $DIR/dropck-eyepatch-extern-crate.rs:47:29
|
||||
|
|
||||
47 | pt = Pt("pt", &c_long, &c); //~ ERROR `c` does not live long enough
|
||||
| ^ does not live long enough
|
||||
...
|
||||
55 | }
|
||||
| - borrowed value dropped before borrower
|
||||
|
|
||||
= note: values in a scope are dropped in the opposite order they are created
|
||||
|
||||
error: `c` does not live long enough
|
||||
--> $DIR/dropck-eyepatch-extern-crate.rs:48:29
|
||||
|
|
||||
48 | pr = Pr("pr", &c_long, &c); //~ ERROR `c` does not live long enough
|
||||
| ^ does not live long enough
|
||||
...
|
||||
55 | }
|
||||
| - borrowed value dropped before borrower
|
||||
|
|
||||
= note: values in a scope are dropped in the opposite order they are created
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
|
@ -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() {
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
error[E0569]: requires an `unsafe impl` declaration due to `#[may_dangle]` attribute
|
||||
--> $DIR/dropck-eyepatch-implies-unsafe-impl.rs:32:1
|
||||
|
|
||||
32 | impl<#[may_dangle] A, B: fmt::Debug> Drop for Pt<A, B> {
|
||||
| ^
|
||||
|
||||
error[E0569]: requires an `unsafe impl` declaration due to `#[may_dangle]` attribute
|
||||
--> $DIR/dropck-eyepatch-implies-unsafe-impl.rs:38:1
|
||||
|
|
||||
38 | impl<#[may_dangle] 'a, 'b, B: fmt::Debug> Drop for Pr<'a, 'b, B> {
|
||||
| ^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
// 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)]
|
||||
|
||||
// The point of this test is to test uses of `#[may_dangle]` attribute
|
||||
// where the formal declaration order (in the impl generics) does not
|
||||
// match the actual usage order (in the type instantiation).
|
||||
//
|
||||
// See also dropck-eyepatch.rs for more information about the general
|
||||
// structure of the test.
|
||||
|
||||
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: fmt::Debug, 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); }
|
||||
}
|
||||
unsafe impl<B: fmt::Debug, #[may_dangle] A: fmt::Debug> Drop for Pt<A, B> {
|
||||
// (unsafe to access self.1 due to #[may_dangle] on A)
|
||||
fn drop(&mut self) { println!("drop {} {:?}", self.0, self.2); }
|
||||
}
|
||||
unsafe impl<'b, #[may_dangle] 'a, B: fmt::Debug> Drop for Pr<'a, 'b, B> {
|
||||
// (unsafe to access self.1 due to #[may_dangle] on 'a)
|
||||
fn drop(&mut self) { println!("drop {} {:?}", self.0, self.2); }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
use std::cell::Cell;
|
||||
let c_long;
|
||||
let (c, mut dt, mut dr, mut pt, mut pr, st, sr)
|
||||
: (Cell<_>, Dt<_>, Dr<_>, Pt<_, _>, Pr<_>, St<_>, Sr<_>);
|
||||
c_long = Cell::new(1);
|
||||
c = Cell::new(1);
|
||||
|
||||
// No error: sufficiently long-lived state can be referenced in dtors
|
||||
dt = Dt("dt", &c_long);
|
||||
dr = Dr("dr", &c_long);
|
||||
// Error: destructor order imprecisely modelled
|
||||
dt = Dt("dt", &c); //~ ERROR `c` does not live long enough
|
||||
dr = Dr("dr", &c); //~ ERROR `c` does not live long enough
|
||||
|
||||
// No error: Drop impl asserts .1 (A and &'a _) are not accessed
|
||||
pt = Pt("pt", &c, &c_long);
|
||||
pr = Pr("pr", &c, &c_long);
|
||||
|
||||
// Error: Drop impl's assertion does not apply to `B` nor `&'b _`
|
||||
pt = Pt("pt", &c_long, &c); //~ ERROR `c` does not live long enough
|
||||
pr = Pr("pr", &c_long, &c); //~ ERROR `c` does not live long enough
|
||||
|
||||
// No error: St and Sr have no destructor.
|
||||
st = St("st", &c);
|
||||
sr = Sr("sr", &c);
|
||||
|
||||
println!("{:?}", (dt.0, dr.0, pt.0, pr.0, st.0, sr.0));
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
error: `c` does not live long enough
|
||||
--> $DIR/dropck-eyepatch-reorder.rs:57:20
|
||||
|
|
||||
57 | dt = Dt("dt", &c); //~ ERROR `c` does not live long enough
|
||||
| ^ does not live long enough
|
||||
...
|
||||
73 | }
|
||||
| - borrowed value dropped before borrower
|
||||
|
|
||||
= note: values in a scope are dropped in the opposite order they are created
|
||||
|
||||
error: `c` does not live long enough
|
||||
--> $DIR/dropck-eyepatch-reorder.rs:58:20
|
||||
|
|
||||
58 | dr = Dr("dr", &c); //~ ERROR `c` does not live long enough
|
||||
| ^ does not live long enough
|
||||
...
|
||||
73 | }
|
||||
| - borrowed value dropped before borrower
|
||||
|
|
||||
= note: values in a scope are dropped in the opposite order they are created
|
||||
|
||||
error: `c` does not live long enough
|
||||
--> $DIR/dropck-eyepatch-reorder.rs:65:29
|
||||
|
|
||||
65 | pt = Pt("pt", &c_long, &c); //~ ERROR `c` does not live long enough
|
||||
| ^ does not live long enough
|
||||
...
|
||||
73 | }
|
||||
| - borrowed value dropped before borrower
|
||||
|
|
||||
= note: values in a scope are dropped in the opposite order they are created
|
||||
|
||||
error: `c` does not live long enough
|
||||
--> $DIR/dropck-eyepatch-reorder.rs:66:29
|
||||
|
|
||||
66 | pr = Pr("pr", &c_long, &c); //~ ERROR `c` does not live long enough
|
||||
| ^ does not live long enough
|
||||
...
|
||||
73 | }
|
||||
| - borrowed value dropped before borrower
|
||||
|
|
||||
= note: values in a scope are dropped in the opposite order they are created
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
// 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)]
|
||||
|
||||
// The point of this test is to illustrate that the `#[may_dangle]`
|
||||
// attribute specifically allows, in the context of a type
|
||||
// implementing `Drop`, a generic parameter to be instantiated with a
|
||||
// lifetime that does not strictly outlive the owning type itself.
|
||||
//
|
||||
// Here we test that only the expected errors are issued.
|
||||
//
|
||||
// The illustration is made concrete by comparison with two variations
|
||||
// on the type with `#[may_dangle]`:
|
||||
//
|
||||
// 1. an analogous type that does not implement `Drop` (and thus
|
||||
// should exhibit maximal flexibility with respect to dropck), and
|
||||
//
|
||||
// 2. an analogous type that does not use `#[may_dangle]` (and thus
|
||||
// should exhibit the standard limitations imposed by dropck.
|
||||
//
|
||||
// The types in this file follow a pattern, {D,P,S}{t,r}, where:
|
||||
//
|
||||
// - D means "I implement Drop"
|
||||
//
|
||||
// - P means "I implement Drop but guarantee my (first) parameter is
|
||||
// pure, i.e. not accessed from the destructor"; no other parameters
|
||||
// are pure.
|
||||
//
|
||||
// - S means "I do not implement Drop"
|
||||
//
|
||||
// - t suffix is used when the first generic is a type
|
||||
//
|
||||
// - r suffix is used when the first generic is a lifetime.
|
||||
|
||||
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); }
|
||||
}
|
||||
unsafe impl<#[may_dangle] A, B: fmt::Debug> Drop for Pt<A, B> {
|
||||
// (unsafe to access self.1 due to #[may_dangle] on A)
|
||||
fn drop(&mut self) { println!("drop {} {:?}", self.0, self.2); }
|
||||
}
|
||||
unsafe impl<#[may_dangle] 'a, 'b, B: fmt::Debug> Drop for Pr<'a, 'b, B> {
|
||||
// (unsafe to access self.1 due to #[may_dangle] on 'a)
|
||||
fn drop(&mut self) { println!("drop {} {:?}", self.0, self.2); }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
use std::cell::Cell;
|
||||
let c_long;
|
||||
let (c, mut dt, mut dr, mut pt, mut pr, st, sr)
|
||||
: (Cell<_>, Dt<_>, Dr<_>, Pt<_, _>, Pr<_>, St<_>, Sr<_>);
|
||||
c_long = Cell::new(1);
|
||||
c = Cell::new(1);
|
||||
|
||||
// No error: sufficiently long-lived state can be referenced in dtors
|
||||
dt = Dt("dt", &c_long);
|
||||
dr = Dr("dr", &c_long);
|
||||
// Error: destructor order imprecisely modelled
|
||||
dt = Dt("dt", &c); //~ ERROR `c` does not live long enough
|
||||
dr = Dr("dr", &c); //~ ERROR `c` does not live long enough
|
||||
|
||||
// No error: Drop impl asserts .1 (A and &'a _) are not accessed
|
||||
pt = Pt("pt", &c, &c_long);
|
||||
pr = Pr("pr", &c, &c_long);
|
||||
|
||||
// Error: Drop impl's assertion does not apply to `B` nor `&'b _`
|
||||
pt = Pt("pt", &c_long, &c); //~ ERROR `c` does not live long enough
|
||||
pr = Pr("pr", &c_long, &c); //~ ERROR `c` does not live long enough
|
||||
|
||||
// No error: St and Sr have no destructor.
|
||||
st = St("st", &c);
|
||||
sr = Sr("sr", &c);
|
||||
|
||||
println!("{:?}", (dt.0, dr.0, pt.0, pr.0, st.0, sr.0));
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
error: `c` does not live long enough
|
||||
--> $DIR/dropck-eyepatch.rs:80:20
|
||||
|
|
||||
80 | dt = Dt("dt", &c); //~ ERROR `c` does not live long enough
|
||||
| ^ does not live long enough
|
||||
...
|
||||
96 | }
|
||||
| - borrowed value dropped before borrower
|
||||
|
|
||||
= note: values in a scope are dropped in the opposite order they are created
|
||||
|
||||
error: `c` does not live long enough
|
||||
--> $DIR/dropck-eyepatch.rs:81:20
|
||||
|
|
||||
81 | dr = Dr("dr", &c); //~ ERROR `c` does not live long enough
|
||||
| ^ does not live long enough
|
||||
...
|
||||
96 | }
|
||||
| - borrowed value dropped before borrower
|
||||
|
|
||||
= note: values in a scope are dropped in the opposite order they are created
|
||||
|
||||
error: `c` does not live long enough
|
||||
--> $DIR/dropck-eyepatch.rs:88:29
|
||||
|
|
||||
88 | pt = Pt("pt", &c_long, &c); //~ ERROR `c` does not live long enough
|
||||
| ^ does not live long enough
|
||||
...
|
||||
96 | }
|
||||
| - borrowed value dropped before borrower
|
||||
|
|
||||
= note: values in a scope are dropped in the opposite order they are created
|
||||
|
||||
error: `c` does not live long enough
|
||||
--> $DIR/dropck-eyepatch.rs:89:29
|
||||
|
|
||||
89 | pr = Pr("pr", &c_long, &c); //~ ERROR `c` does not live long enough
|
||||
| ^ does not live long enough
|
||||
...
|
||||
96 | }
|
||||
| - borrowed value dropped before borrower
|
||||
|
|
||||
= note: values in a scope are dropped in the opposite order they are created
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
Loading…
Reference in New Issue