|
8 | 8 | // option. This file may not be copied, modified, or distributed
|
9 | 9 | // except according to those terms.
|
10 | 10 |
|
| 11 | +use hir; |
11 | 12 | use hir::def::Def;
|
12 | 13 | use hir::def_id::DefId;
|
13 |
| -use ty::{self, Ty, TyCtxt}; |
| 14 | +use hir::intravisit::{self, Visitor, NestedVisitorMap}; |
14 | 15 | use ty::layout::{LayoutError, Pointer, SizeSkeleton};
|
| 16 | +use ty::{self, Ty, AdtKind, TyCtxt, TypeFoldable}; |
| 17 | +use ty::subst::Substs; |
15 | 18 |
|
16 | 19 | use rustc_target::spec::abi::Abi::RustIntrinsic;
|
| 20 | +use syntax_pos::DUMMY_SP; |
17 | 21 | use syntax_pos::Span;
|
18 |
| -use hir::intravisit::{self, Visitor, NestedVisitorMap}; |
19 |
| -use hir; |
20 | 22 |
|
21 | 23 | pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
|
22 | 24 | let mut visitor = ItemVisitor {
|
@@ -64,17 +66,164 @@ fn unpack_option_like<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
64 | 66 | ty
|
65 | 67 | }
|
66 | 68 |
|
| 69 | +/// Check if this enum can be safely exported based on the |
| 70 | +/// "nullable pointer optimization". Currently restricted |
| 71 | +/// to function pointers and references, but could be |
| 72 | +/// expanded to cover NonZero raw pointers and newtypes. |
| 73 | +/// FIXME: This duplicates code in codegen. |
| 74 | +pub fn is_repr_nullable_ptr<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, |
| 75 | + def: &'tcx ty::AdtDef, |
| 76 | + substs: &Substs<'tcx>) |
| 77 | + -> bool { |
| 78 | + if def.variants.len() == 2 { |
| 79 | + let data_idx; |
| 80 | + |
| 81 | + if def.variants[0].fields.is_empty() { |
| 82 | + data_idx = 1; |
| 83 | + } else if def.variants[1].fields.is_empty() { |
| 84 | + data_idx = 0; |
| 85 | + } else { |
| 86 | + return false; |
| 87 | + } |
| 88 | + |
| 89 | + if def.variants[data_idx].fields.len() == 1 { |
| 90 | + match def.variants[data_idx].fields[0].ty(tcx, substs).sty { |
| 91 | + ty::TyFnPtr(_) => { |
| 92 | + return true; |
| 93 | + } |
| 94 | + ty::TyRef(..) => { |
| 95 | + return true; |
| 96 | + } |
| 97 | + _ => {} |
| 98 | + } |
| 99 | + } |
| 100 | + } |
| 101 | + false |
| 102 | +} |
| 103 | + |
67 | 104 | impl<'a, 'tcx> ExprVisitor<'a, 'tcx> {
|
68 | 105 | fn def_id_is_transmute(&self, def_id: DefId) -> bool {
|
69 | 106 | self.tcx.fn_sig(def_id).abi() == RustIntrinsic &&
|
70 | 107 | self.tcx.item_name(def_id) == "transmute"
|
71 | 108 | }
|
72 | 109 |
|
| 110 | + /// Calculate whether a type has a specified layout. |
| 111 | + /// |
| 112 | + /// The function returns `None` in indeterminate cases (such as `TyError`). |
| 113 | + fn is_layout_specified(&self, ty: Ty<'tcx>) -> Option<bool> { |
| 114 | + match ty.sty { |
| 115 | + // These types have a specified layout |
| 116 | + // Reference: Primitive type layout |
| 117 | + ty::TyBool | |
| 118 | + ty::TyChar | |
| 119 | + ty::TyInt(_) | |
| 120 | + ty::TyUint(_) | |
| 121 | + ty::TyFloat(_) | |
| 122 | + // Reference: Pointers and references layout |
| 123 | + ty::TyFnPtr(_) => Some(true), |
| 124 | + // Reference: Array layout (depends on the contained type) |
| 125 | + ty::TyArray(ty, _) => self.is_layout_specified(ty), |
| 126 | + // Reference: Tuple layout (only specified if empty) |
| 127 | + ty::TyTuple(ref tys) => Some(tys.is_empty()), |
| 128 | + |
| 129 | + // Cases with definitely unspecified layouts |
| 130 | + ty::TyClosure(_, _) | |
| 131 | + ty::TyGenerator(_, _, _) | |
| 132 | + ty::TyGeneratorWitness(_) => Some(false), |
| 133 | + // Currently ZST, but does not seem to be guaranteed |
| 134 | + ty::TyFnDef(_, _) => Some(false), |
| 135 | + // Unsized types |
| 136 | + ty::TyForeign(_) | |
| 137 | + ty::TyNever | |
| 138 | + ty::TyStr | |
| 139 | + ty::TySlice(_) | |
| 140 | + ty::TyDynamic(_, _) => Some(false), |
| 141 | + |
| 142 | + // Indeterminate cases |
| 143 | + ty::TyInfer(_) | |
| 144 | + // should we report `Some(false)` for TyParam(_)? It this possible to reach this branch? |
| 145 | + ty::TyParam(_) | |
| 146 | + ty::TyError => None, |
| 147 | + |
| 148 | + // The “it’s complicated™” cases |
| 149 | + // Reference: Pointers and references layout |
| 150 | + ty::TyRawPtr(ty::TypeAndMut { ty: pointee, .. }) | |
| 151 | + ty::TyRef(_, pointee, _) => { |
| 152 | + let pointee = self.tcx.normalize_erasing_regions(self.param_env, pointee); |
| 153 | + // Pointers to unsized types have no specified layout. |
| 154 | + Some(pointee.is_sized(self.tcx.at(DUMMY_SP), self.param_env)) |
| 155 | + } |
| 156 | + ty::TyProjection(_) | ty::TyAnon(_, _) => { |
| 157 | + let normalized = self.tcx.normalize_erasing_regions(self.param_env, ty); |
| 158 | + if ty == normalized { |
| 159 | + None |
| 160 | + } else { |
| 161 | + self.is_layout_specified(normalized) |
| 162 | + } |
| 163 | + } |
| 164 | + ty::TyAdt(def, substs) => { |
| 165 | + // Documentation guarantees 0-size. |
| 166 | + if def.is_phantom_data() { |
| 167 | + return Some(true); |
| 168 | + } |
| 169 | + match def.adt_kind() { |
| 170 | + AdtKind::Struct | AdtKind::Union => { |
| 171 | + if !def.repr.c() && !def.repr.transparent() && !def.repr.simd() { |
| 172 | + return Some(false); |
| 173 | + } |
| 174 | + // FIXME: do we guarantee 0-sizedness for structs with 0 fields? |
| 175 | + // If not, they should cause Some(false) here. |
| 176 | + let mut seen_none = false; |
| 177 | + for field in &def.non_enum_variant().fields { |
| 178 | + let field_ty = field.ty(self.tcx, substs); |
| 179 | + match self.is_layout_specified(field_ty) { |
| 180 | + Some(true) => continue, |
| 181 | + None => { |
| 182 | + seen_none = true; |
| 183 | + continue; |
| 184 | + } |
| 185 | + x => return x, |
| 186 | + } |
| 187 | + } |
| 188 | + return if seen_none { None } else { Some(true) }; |
| 189 | + } |
| 190 | + AdtKind::Enum => { |
| 191 | + if !def.repr.c() && def.repr.int.is_none() { |
| 192 | + if !is_repr_nullable_ptr(self.tcx, def, substs) { |
| 193 | + return Some(false); |
| 194 | + } |
| 195 | + } |
| 196 | + return Some(true); |
| 197 | + } |
| 198 | + } |
| 199 | + } |
| 200 | + } |
| 201 | + } |
| 202 | + |
73 | 203 | fn check_transmute(&self, span: Span, from: Ty<'tcx>, to: Ty<'tcx>) {
|
| 204 | + // Check for unspecified types before checking for same size. |
| 205 | + assert!(!from.has_infer_types()); |
| 206 | + assert!(!to.has_infer_types()); |
| 207 | + |
| 208 | + let unspecified_layout = |msg, ty| { |
| 209 | + struct_span_err!(self.tcx.sess, span, E0912, "{}", msg) |
| 210 | + .note(&format!("{} has an unspecified layout", ty)) |
| 211 | + .note("this will become a hard error in the future") |
| 212 | + .emit(); |
| 213 | + }; |
| 214 | + |
| 215 | + if self.is_layout_specified(from) == Some(false) { |
| 216 | + unspecified_layout("transmutation from a type with an unspecified layout", from); |
| 217 | + } |
| 218 | + |
| 219 | + if self.is_layout_specified(to) == Some(false) { |
| 220 | + unspecified_layout("transmutation to a type with an unspecified layout", to); |
| 221 | + } |
| 222 | + |
| 223 | + // Check for same size using the skeletons. |
74 | 224 | let sk_from = SizeSkeleton::compute(from, self.tcx, self.param_env);
|
75 | 225 | let sk_to = SizeSkeleton::compute(to, self.tcx, self.param_env);
|
76 | 226 |
|
77 |
| - // Check for same size using the skeletons. |
78 | 227 | if let (Ok(sk_from), Ok(sk_to)) = (sk_from, sk_to) {
|
79 | 228 | if sk_from.same_size(sk_to) {
|
80 | 229 | return;
|
|
0 commit comments