Skip to content

Commit 7f1bfa6

Browse files
committed
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.
1 parent 57cd603 commit 7f1bfa6

File tree

4 files changed

+138
-31
lines changed

4 files changed

+138
-31
lines changed

src/libsyntax_ext/deriving/generic/mod.rs

+64-16
Original file line numberDiff line numberDiff line change
@@ -393,34 +393,47 @@ fn find_type_parameters(ty: &ast::Ty,
393393
}
394394

395395
impl<'a> TraitDef<'a> {
396-
pub fn expand(&self,
396+
pub fn expand(self,
397397
cx: &mut ExtCtxt,
398398
mitem: &ast::MetaItem,
399399
item: &'a Annotatable,
400400
push: &mut FnMut(Annotatable)) {
401401
self.expand_ext(cx, mitem, item, push, false);
402402
}
403403

404-
pub fn expand_ext(&self,
404+
pub fn expand_ext(self,
405405
cx: &mut ExtCtxt,
406406
mitem: &ast::MetaItem,
407407
item: &'a Annotatable,
408408
push: &mut FnMut(Annotatable),
409409
from_scratch: bool) {
410410
match *item {
411411
Annotatable::Item(ref item) => {
412+
let is_packed = item.attrs.iter().any(|attr| {
413+
attr::find_repr_attrs(&cx.parse_sess.span_diagnostic, attr)
414+
.contains(&attr::ReprPacked)
415+
});
416+
let use_temporaries = is_packed;
412417
let newitem = match item.node {
413418
ast::ItemKind::Struct(ref struct_def, ref generics) => {
414-
self.expand_struct_def(cx, &struct_def, item.ident, generics, from_scratch)
419+
self.expand_struct_def(cx, &struct_def, item.ident, generics, from_scratch,
420+
use_temporaries)
415421
}
416422
ast::ItemKind::Enum(ref enum_def, ref generics) => {
423+
// We ignore `use_temporaries` here, because
424+
// `repr(packed)` enums cause an error later on.
425+
//
426+
// This can only cause further compilation errors
427+
// downstream in blatantly illegal code, so it
428+
// is fine.
417429
self.expand_enum_def(cx, enum_def, &item.attrs,
418430
item.ident, generics, from_scratch)
419431
}
420432
ast::ItemKind::Union(ref struct_def, ref generics) => {
421433
if self.supports_unions {
422434
self.expand_struct_def(cx, &struct_def, item.ident,
423-
generics, from_scratch)
435+
generics, from_scratch,
436+
use_temporaries)
424437
} else {
425438
cx.span_err(mitem.span,
426439
"this trait cannot be derived for unions");
@@ -674,7 +687,8 @@ impl<'a> TraitDef<'a> {
674687
struct_def: &'a VariantData,
675688
type_ident: Ident,
676689
generics: &Generics,
677-
from_scratch: bool)
690+
from_scratch: bool,
691+
use_temporaries: bool)
678692
-> P<ast::Item> {
679693
let field_tys: Vec<P<ast::Ty>> = struct_def.fields()
680694
.iter()
@@ -700,7 +714,8 @@ impl<'a> TraitDef<'a> {
700714
struct_def,
701715
type_ident,
702716
&self_args[..],
703-
&nonself_args[..])
717+
&nonself_args[..],
718+
use_temporaries)
704719
};
705720

706721
method_def.create_method(cx,
@@ -957,14 +972,31 @@ impl<'a> MethodDef<'a> {
957972
/// }
958973
/// }
959974
/// }
975+
///
976+
/// // or if A is repr(packed) - note fields are matched by-value
977+
/// // instead of by-reference.
978+
/// impl PartialEq for A {
979+
/// fn eq(&self, __arg_1: &A) -> bool {
980+
/// match *self {
981+
/// A {x: __self_0_0, y: __self_0_1} => {
982+
/// match __arg_1 {
983+
/// A {x: __self_1_0, y: __self_1_1} => {
984+
/// __self_0_0.eq(&__self_1_0) && __self_0_1.eq(&__self_1_1)
985+
/// }
986+
/// }
987+
/// }
988+
/// }
989+
/// }
990+
/// }
960991
/// ```
961992
fn expand_struct_method_body<'b>(&self,
962993
cx: &mut ExtCtxt,
963994
trait_: &TraitDef<'b>,
964995
struct_def: &'b VariantData,
965996
type_ident: Ident,
966997
self_args: &[P<Expr>],
967-
nonself_args: &[P<Expr>])
998+
nonself_args: &[P<Expr>],
999+
use_temporaries: bool)
9681000
-> P<Expr> {
9691001

9701002
let mut raw_fields = Vec::new(); // Vec<[fields of self],
@@ -976,7 +1008,8 @@ impl<'a> MethodDef<'a> {
9761008
struct_path,
9771009
struct_def,
9781010
&format!("__self_{}", i),
979-
ast::Mutability::Immutable);
1011+
ast::Mutability::Immutable,
1012+
use_temporaries);
9801013
patterns.push(pat);
9811014
raw_fields.push(ident_expr);
9821015
}
@@ -1139,7 +1172,6 @@ impl<'a> MethodDef<'a> {
11391172
self_args: Vec<P<Expr>>,
11401173
nonself_args: &[P<Expr>])
11411174
-> P<Expr> {
1142-
11431175
let sp = trait_.span;
11441176
let variants = &enum_def.variants;
11451177

@@ -1511,12 +1543,18 @@ impl<'a> TraitDef<'a> {
15111543
fn create_subpatterns(&self,
15121544
cx: &mut ExtCtxt,
15131545
field_paths: Vec<ast::SpannedIdent>,
1514-
mutbl: ast::Mutability)
1546+
mutbl: ast::Mutability,
1547+
use_temporaries: bool)
15151548
-> Vec<P<ast::Pat>> {
15161549
field_paths.iter()
15171550
.map(|path| {
1551+
let binding_mode = if use_temporaries {
1552+
ast::BindingMode::ByValue(ast::Mutability::Immutable)
1553+
} else {
1554+
ast::BindingMode::ByRef(mutbl)
1555+
};
15181556
cx.pat(path.span,
1519-
PatKind::Ident(ast::BindingMode::ByRef(mutbl), (*path).clone(), None))
1557+
PatKind::Ident(binding_mode, (*path).clone(), None))
15201558
})
15211559
.collect()
15221560
}
@@ -1527,8 +1565,10 @@ impl<'a> TraitDef<'a> {
15271565
struct_path: ast::Path,
15281566
struct_def: &'a VariantData,
15291567
prefix: &str,
1530-
mutbl: ast::Mutability)
1531-
-> (P<ast::Pat>, Vec<(Span, Option<Ident>, P<Expr>, &'a [ast::Attribute])>) {
1568+
mutbl: ast::Mutability,
1569+
use_temporaries: bool)
1570+
-> (P<ast::Pat>, Vec<(Span, Option<Ident>, P<Expr>, &'a [ast::Attribute])>)
1571+
{
15321572
let mut paths = Vec::new();
15331573
let mut ident_exprs = Vec::new();
15341574
for (i, struct_field) in struct_def.fields().iter().enumerate() {
@@ -1538,12 +1578,18 @@ impl<'a> TraitDef<'a> {
15381578
span: sp,
15391579
node: ident,
15401580
});
1541-
let val = cx.expr_deref(sp, cx.expr_path(cx.path_ident(sp, ident)));
1581+
let val = cx.expr_path(cx.path_ident(sp, ident));
1582+
let val = if use_temporaries {
1583+
val
1584+
} else {
1585+
cx.expr_deref(sp, val)
1586+
};
15421587
let val = cx.expr(sp, ast::ExprKind::Paren(val));
1588+
15431589
ident_exprs.push((sp, struct_field.ident, val, &struct_field.attrs[..]));
15441590
}
15451591

1546-
let subpats = self.create_subpatterns(cx, paths, mutbl);
1592+
let subpats = self.create_subpatterns(cx, paths, mutbl, use_temporaries);
15471593
let pattern = match *struct_def {
15481594
VariantData::Struct(..) => {
15491595
let field_pats = subpats.into_iter()
@@ -1587,7 +1633,9 @@ impl<'a> TraitDef<'a> {
15871633
let variant_ident = variant.node.name;
15881634
let sp = variant.span.with_ctxt(self.span.ctxt());
15891635
let variant_path = cx.path(sp, vec![enum_ident, variant_ident]);
1590-
self.create_struct_pattern(cx, variant_path, &variant.node.data, prefix, mutbl)
1636+
let use_temporaries = false; // enums can't be repr(packed)
1637+
self.create_struct_pattern(cx, variant_path, &variant.node.data, prefix, mutbl,
1638+
use_temporaries)
15911639
}
15921640
}
15931641

src/libsyntax_pos/span_encoding.rs

+1-15
Original file line numberDiff line numberDiff line change
@@ -19,30 +19,16 @@ use hygiene::SyntaxContext;
1919

2020
use rustc_data_structures::fx::FxHashMap;
2121
use std::cell::RefCell;
22-
use std::hash::{Hash, Hasher};
2322

2423
/// A compressed span.
2524
/// Contains either fields of `SpanData` inline if they are small, or index into span interner.
2625
/// The primary goal of `Span` is to be as small as possible and fit into other structures
2726
/// (that's why it uses `packed` as well). Decoding speed is the second priority.
2827
/// See `SpanData` for the info on span fields in decoded representation.
29-
#[derive(Clone, Copy)]
28+
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
3029
#[repr(packed)]
3130
pub struct Span(u32);
3231

33-
impl PartialEq for Span {
34-
#[inline]
35-
fn eq(&self, other: &Self) -> bool { ({self.0}) == ({other.0}) }
36-
}
37-
38-
impl Eq for Span {}
39-
40-
impl Hash for Span {
41-
fn hash<H: Hasher>(&self, s: &mut H) {
42-
{self.0}.hash(s)
43-
}
44-
}
45-
4632
/// Dummy span, both position and length are zero, syntax context is zero as well.
4733
/// This span is kept inline and encoded with format 0.
4834
pub const DUMMY_SP: Span = Span(0);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// check that derive on a packed struct with non-Copy fields
12+
// correctly. This can't be made to work perfectly because
13+
// we can't just use the field from the struct as it might
14+
// not be aligned.
15+
16+
#[derive(PartialEq)]
17+
struct Y(usize);
18+
19+
#[derive(PartialEq)]
20+
//~^ ERROR cannot move out of borrowed
21+
//~| ERROR cannot move out of borrowed
22+
//~| ERROR cannot move out of borrowed
23+
//~| ERROR cannot move out of borrowed
24+
#[repr(packed)]
25+
struct X(Y);
26+
27+
fn main() {
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// check that derive on a packed struct does not call field
12+
// methods with a misaligned field.
13+
14+
use std::mem;
15+
16+
#[derive(Copy, Clone)]
17+
struct Aligned(usize);
18+
19+
#[inline(never)]
20+
fn check_align(ptr: *const Aligned) {
21+
assert_eq!(ptr as usize % mem::align_of::<Aligned>(),
22+
0);
23+
}
24+
25+
impl PartialEq for Aligned {
26+
fn eq(&self, other: &Self) -> bool {
27+
check_align(self);
28+
check_align(other);
29+
self.0 == other.0
30+
}
31+
}
32+
33+
#[repr(packed)]
34+
#[derive(PartialEq)]
35+
struct Packed(Aligned, Aligned);
36+
37+
#[derive(PartialEq)]
38+
#[repr(C)]
39+
struct Dealigned<T>(u8, T);
40+
41+
fn main() {
42+
let d1 = Dealigned(0, Packed(Aligned(1), Aligned(2)));
43+
let ck = d1 == d1;
44+
assert!(ck);
45+
}

0 commit comments

Comments
 (0)