fix #[derive] implementation for repr(packed) structs
Fix the derive implementation for repr(packed) structs to move the fields out instead of calling functions on references to each subfield. That's it, `#[derive(PartialEq)]` on a packed struct now does: ```Rust fn eq(&self, other: &Self) { let field_0 = self.0; let other_field_0 = other.0; &field_0 == &other_field_0 } ``` Instead of ```Rust fn eq(&self, other: &Self) { let ref field_0 = self.0; let ref other_field_0 = other.0; &*field_0 == &*other_field_0 } ``` Taking (unaligned) references to each subfield is undefined, unsound and is an error with MIR effectck, so it had to be prevented. This causes a borrowck error when a `repr(packed)` struct has a non-Copy field (and therefore is a [breaking-change]), but I don't see a sound way to avoid that error.
This commit is contained in:
parent
1a2d443f55
commit
dee8a71cd5
4 changed files with 138 additions and 31 deletions
|
@ -393,7 +393,7 @@ fn find_type_parameters(ty: &ast::Ty,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TraitDef<'a> {
|
impl<'a> TraitDef<'a> {
|
||||||
pub fn expand(&self,
|
pub fn expand(self,
|
||||||
cx: &mut ExtCtxt,
|
cx: &mut ExtCtxt,
|
||||||
mitem: &ast::MetaItem,
|
mitem: &ast::MetaItem,
|
||||||
item: &'a Annotatable,
|
item: &'a Annotatable,
|
||||||
|
@ -401,7 +401,7 @@ impl<'a> TraitDef<'a> {
|
||||||
self.expand_ext(cx, mitem, item, push, false);
|
self.expand_ext(cx, mitem, item, push, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expand_ext(&self,
|
pub fn expand_ext(self,
|
||||||
cx: &mut ExtCtxt,
|
cx: &mut ExtCtxt,
|
||||||
mitem: &ast::MetaItem,
|
mitem: &ast::MetaItem,
|
||||||
item: &'a Annotatable,
|
item: &'a Annotatable,
|
||||||
|
@ -409,18 +409,31 @@ impl<'a> TraitDef<'a> {
|
||||||
from_scratch: bool) {
|
from_scratch: bool) {
|
||||||
match *item {
|
match *item {
|
||||||
Annotatable::Item(ref item) => {
|
Annotatable::Item(ref item) => {
|
||||||
|
let is_packed = item.attrs.iter().any(|attr| {
|
||||||
|
attr::find_repr_attrs(&cx.parse_sess.span_diagnostic, attr)
|
||||||
|
.contains(&attr::ReprPacked)
|
||||||
|
});
|
||||||
|
let use_temporaries = is_packed;
|
||||||
let newitem = match item.node {
|
let newitem = match item.node {
|
||||||
ast::ItemKind::Struct(ref struct_def, ref generics) => {
|
ast::ItemKind::Struct(ref struct_def, ref generics) => {
|
||||||
self.expand_struct_def(cx, &struct_def, item.ident, generics, from_scratch)
|
self.expand_struct_def(cx, &struct_def, item.ident, generics, from_scratch,
|
||||||
|
use_temporaries)
|
||||||
}
|
}
|
||||||
ast::ItemKind::Enum(ref enum_def, ref generics) => {
|
ast::ItemKind::Enum(ref enum_def, ref generics) => {
|
||||||
|
// We ignore `use_temporaries` here, because
|
||||||
|
// `repr(packed)` enums cause an error later on.
|
||||||
|
//
|
||||||
|
// This can only cause further compilation errors
|
||||||
|
// downstream in blatantly illegal code, so it
|
||||||
|
// is fine.
|
||||||
self.expand_enum_def(cx, enum_def, &item.attrs,
|
self.expand_enum_def(cx, enum_def, &item.attrs,
|
||||||
item.ident, generics, from_scratch)
|
item.ident, generics, from_scratch)
|
||||||
}
|
}
|
||||||
ast::ItemKind::Union(ref struct_def, ref generics) => {
|
ast::ItemKind::Union(ref struct_def, ref generics) => {
|
||||||
if self.supports_unions {
|
if self.supports_unions {
|
||||||
self.expand_struct_def(cx, &struct_def, item.ident,
|
self.expand_struct_def(cx, &struct_def, item.ident,
|
||||||
generics, from_scratch)
|
generics, from_scratch,
|
||||||
|
use_temporaries)
|
||||||
} else {
|
} else {
|
||||||
cx.span_err(mitem.span,
|
cx.span_err(mitem.span,
|
||||||
"this trait cannot be derived for unions");
|
"this trait cannot be derived for unions");
|
||||||
|
@ -675,7 +688,8 @@ impl<'a> TraitDef<'a> {
|
||||||
struct_def: &'a VariantData,
|
struct_def: &'a VariantData,
|
||||||
type_ident: Ident,
|
type_ident: Ident,
|
||||||
generics: &Generics,
|
generics: &Generics,
|
||||||
from_scratch: bool)
|
from_scratch: bool,
|
||||||
|
use_temporaries: bool)
|
||||||
-> P<ast::Item> {
|
-> P<ast::Item> {
|
||||||
let field_tys: Vec<P<ast::Ty>> = struct_def.fields()
|
let field_tys: Vec<P<ast::Ty>> = struct_def.fields()
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -701,7 +715,8 @@ impl<'a> TraitDef<'a> {
|
||||||
struct_def,
|
struct_def,
|
||||||
type_ident,
|
type_ident,
|
||||||
&self_args[..],
|
&self_args[..],
|
||||||
&nonself_args[..])
|
&nonself_args[..],
|
||||||
|
use_temporaries)
|
||||||
};
|
};
|
||||||
|
|
||||||
method_def.create_method(cx,
|
method_def.create_method(cx,
|
||||||
|
@ -958,6 +973,22 @@ impl<'a> MethodDef<'a> {
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
|
///
|
||||||
|
/// // or if A is repr(packed) - note fields are matched by-value
|
||||||
|
/// // instead of by-reference.
|
||||||
|
/// impl PartialEq for A {
|
||||||
|
/// fn eq(&self, __arg_1: &A) -> bool {
|
||||||
|
/// match *self {
|
||||||
|
/// A {x: __self_0_0, y: __self_0_1} => {
|
||||||
|
/// match __arg_1 {
|
||||||
|
/// A {x: __self_1_0, y: __self_1_1} => {
|
||||||
|
/// __self_0_0.eq(&__self_1_0) && __self_0_1.eq(&__self_1_1)
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
fn expand_struct_method_body<'b>(&self,
|
fn expand_struct_method_body<'b>(&self,
|
||||||
cx: &mut ExtCtxt,
|
cx: &mut ExtCtxt,
|
||||||
|
@ -965,7 +996,8 @@ impl<'a> MethodDef<'a> {
|
||||||
struct_def: &'b VariantData,
|
struct_def: &'b VariantData,
|
||||||
type_ident: Ident,
|
type_ident: Ident,
|
||||||
self_args: &[P<Expr>],
|
self_args: &[P<Expr>],
|
||||||
nonself_args: &[P<Expr>])
|
nonself_args: &[P<Expr>],
|
||||||
|
use_temporaries: bool)
|
||||||
-> P<Expr> {
|
-> P<Expr> {
|
||||||
|
|
||||||
let mut raw_fields = Vec::new(); // Vec<[fields of self],
|
let mut raw_fields = Vec::new(); // Vec<[fields of self],
|
||||||
|
@ -977,7 +1009,8 @@ impl<'a> MethodDef<'a> {
|
||||||
struct_path,
|
struct_path,
|
||||||
struct_def,
|
struct_def,
|
||||||
&format!("__self_{}", i),
|
&format!("__self_{}", i),
|
||||||
ast::Mutability::Immutable);
|
ast::Mutability::Immutable,
|
||||||
|
use_temporaries);
|
||||||
patterns.push(pat);
|
patterns.push(pat);
|
||||||
raw_fields.push(ident_expr);
|
raw_fields.push(ident_expr);
|
||||||
}
|
}
|
||||||
|
@ -1140,7 +1173,6 @@ impl<'a> MethodDef<'a> {
|
||||||
self_args: Vec<P<Expr>>,
|
self_args: Vec<P<Expr>>,
|
||||||
nonself_args: &[P<Expr>])
|
nonself_args: &[P<Expr>])
|
||||||
-> P<Expr> {
|
-> P<Expr> {
|
||||||
|
|
||||||
let sp = trait_.span;
|
let sp = trait_.span;
|
||||||
let variants = &enum_def.variants;
|
let variants = &enum_def.variants;
|
||||||
|
|
||||||
|
@ -1512,12 +1544,18 @@ impl<'a> TraitDef<'a> {
|
||||||
fn create_subpatterns(&self,
|
fn create_subpatterns(&self,
|
||||||
cx: &mut ExtCtxt,
|
cx: &mut ExtCtxt,
|
||||||
field_paths: Vec<ast::SpannedIdent>,
|
field_paths: Vec<ast::SpannedIdent>,
|
||||||
mutbl: ast::Mutability)
|
mutbl: ast::Mutability,
|
||||||
|
use_temporaries: bool)
|
||||||
-> Vec<P<ast::Pat>> {
|
-> Vec<P<ast::Pat>> {
|
||||||
field_paths.iter()
|
field_paths.iter()
|
||||||
.map(|path| {
|
.map(|path| {
|
||||||
|
let binding_mode = if use_temporaries {
|
||||||
|
ast::BindingMode::ByValue(ast::Mutability::Immutable)
|
||||||
|
} else {
|
||||||
|
ast::BindingMode::ByRef(mutbl)
|
||||||
|
};
|
||||||
cx.pat(path.span,
|
cx.pat(path.span,
|
||||||
PatKind::Ident(ast::BindingMode::ByRef(mutbl), (*path).clone(), None))
|
PatKind::Ident(binding_mode, (*path).clone(), None))
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
@ -1528,8 +1566,10 @@ impl<'a> TraitDef<'a> {
|
||||||
struct_path: ast::Path,
|
struct_path: ast::Path,
|
||||||
struct_def: &'a VariantData,
|
struct_def: &'a VariantData,
|
||||||
prefix: &str,
|
prefix: &str,
|
||||||
mutbl: ast::Mutability)
|
mutbl: ast::Mutability,
|
||||||
-> (P<ast::Pat>, Vec<(Span, Option<Ident>, P<Expr>, &'a [ast::Attribute])>) {
|
use_temporaries: bool)
|
||||||
|
-> (P<ast::Pat>, Vec<(Span, Option<Ident>, P<Expr>, &'a [ast::Attribute])>)
|
||||||
|
{
|
||||||
let mut paths = Vec::new();
|
let mut paths = Vec::new();
|
||||||
let mut ident_exprs = Vec::new();
|
let mut ident_exprs = Vec::new();
|
||||||
for (i, struct_field) in struct_def.fields().iter().enumerate() {
|
for (i, struct_field) in struct_def.fields().iter().enumerate() {
|
||||||
|
@ -1539,12 +1579,18 @@ impl<'a> TraitDef<'a> {
|
||||||
span: sp,
|
span: sp,
|
||||||
node: ident,
|
node: ident,
|
||||||
});
|
});
|
||||||
let val = cx.expr_deref(sp, cx.expr_path(cx.path_ident(sp, ident)));
|
let val = cx.expr_path(cx.path_ident(sp, ident));
|
||||||
|
let val = if use_temporaries {
|
||||||
|
val
|
||||||
|
} else {
|
||||||
|
cx.expr_deref(sp, val)
|
||||||
|
};
|
||||||
let val = cx.expr(sp, ast::ExprKind::Paren(val));
|
let val = cx.expr(sp, ast::ExprKind::Paren(val));
|
||||||
|
|
||||||
ident_exprs.push((sp, struct_field.ident, val, &struct_field.attrs[..]));
|
ident_exprs.push((sp, struct_field.ident, val, &struct_field.attrs[..]));
|
||||||
}
|
}
|
||||||
|
|
||||||
let subpats = self.create_subpatterns(cx, paths, mutbl);
|
let subpats = self.create_subpatterns(cx, paths, mutbl, use_temporaries);
|
||||||
let pattern = match *struct_def {
|
let pattern = match *struct_def {
|
||||||
VariantData::Struct(..) => {
|
VariantData::Struct(..) => {
|
||||||
let field_pats = subpats.into_iter()
|
let field_pats = subpats.into_iter()
|
||||||
|
@ -1588,7 +1634,9 @@ impl<'a> TraitDef<'a> {
|
||||||
let variant_ident = variant.node.name;
|
let variant_ident = variant.node.name;
|
||||||
let sp = variant.span.with_ctxt(self.span.ctxt());
|
let sp = variant.span.with_ctxt(self.span.ctxt());
|
||||||
let variant_path = cx.path(sp, vec![enum_ident, variant_ident]);
|
let variant_path = cx.path(sp, vec![enum_ident, variant_ident]);
|
||||||
self.create_struct_pattern(cx, variant_path, &variant.node.data, prefix, mutbl)
|
let use_temporaries = false; // enums can't be repr(packed)
|
||||||
|
self.create_struct_pattern(cx, variant_path, &variant.node.data, prefix, mutbl,
|
||||||
|
use_temporaries)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,30 +19,16 @@ use hygiene::SyntaxContext;
|
||||||
|
|
||||||
use rustc_data_structures::fx::FxHashMap;
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::hash::{Hash, Hasher};
|
|
||||||
|
|
||||||
/// A compressed span.
|
/// A compressed span.
|
||||||
/// Contains either fields of `SpanData` inline if they are small, or index into span interner.
|
/// Contains either fields of `SpanData` inline if they are small, or index into span interner.
|
||||||
/// The primary goal of `Span` is to be as small as possible and fit into other structures
|
/// The primary goal of `Span` is to be as small as possible and fit into other structures
|
||||||
/// (that's why it uses `packed` as well). Decoding speed is the second priority.
|
/// (that's why it uses `packed` as well). Decoding speed is the second priority.
|
||||||
/// See `SpanData` for the info on span fields in decoded representation.
|
/// See `SpanData` for the info on span fields in decoded representation.
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
pub struct Span(u32);
|
pub struct Span(u32);
|
||||||
|
|
||||||
impl PartialEq for Span {
|
|
||||||
#[inline]
|
|
||||||
fn eq(&self, other: &Self) -> bool { ({self.0}) == ({other.0}) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for Span {}
|
|
||||||
|
|
||||||
impl Hash for Span {
|
|
||||||
fn hash<H: Hasher>(&self, s: &mut H) {
|
|
||||||
{self.0}.hash(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Dummy span, both position and length are zero, syntax context is zero as well.
|
/// Dummy span, both position and length are zero, syntax context is zero as well.
|
||||||
/// This span is kept inline and encoded with format 0.
|
/// This span is kept inline and encoded with format 0.
|
||||||
pub const DUMMY_SP: Span = Span(0);
|
pub const DUMMY_SP: Span = Span(0);
|
||||||
|
|
28
src/test/compile-fail/deriving-with-repr-packed-not-copy.rs
Normal file
28
src/test/compile-fail/deriving-with-repr-packed-not-copy.rs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
// Copyright 2017 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 derive on a packed struct with non-Copy fields
|
||||||
|
// correctly. This can't be made to work perfectly because
|
||||||
|
// we can't just use the field from the struct as it might
|
||||||
|
// not be aligned.
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
struct Y(usize);
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
//~^ ERROR cannot move out of borrowed
|
||||||
|
//~| ERROR cannot move out of borrowed
|
||||||
|
//~| ERROR cannot move out of borrowed
|
||||||
|
//~| ERROR cannot move out of borrowed
|
||||||
|
#[repr(packed)]
|
||||||
|
struct X(Y);
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
}
|
45
src/test/run-pass/deriving-with-repr-packed.rs
Normal file
45
src/test/run-pass/deriving-with-repr-packed.rs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
// Copyright 2017 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 derive on a packed struct does not call field
|
||||||
|
// methods with a misaligned field.
|
||||||
|
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
struct Aligned(usize);
|
||||||
|
|
||||||
|
#[inline(never)]
|
||||||
|
fn check_align(ptr: *const Aligned) {
|
||||||
|
assert_eq!(ptr as usize % mem::align_of::<Aligned>(),
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Aligned {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
check_align(self);
|
||||||
|
check_align(other);
|
||||||
|
self.0 == other.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(packed)]
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
struct Packed(Aligned, Aligned);
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct Dealigned<T>(u8, T);
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let d1 = Dealigned(0, Packed(Aligned(1), Aligned(2)));
|
||||||
|
let ck = d1 == d1;
|
||||||
|
assert!(ck);
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue