]>
Commit | Line | Data |
---|---|---|
5869c6ff | 1 | use rustc_ast::InlineAsmTemplatePiece; |
dfeec247 XL |
2 | use rustc_errors::struct_span_err; |
3 | use rustc_hir as hir; | |
4 | use rustc_hir::def::{DefKind, Res}; | |
f035d41b | 5 | use rustc_hir::def_id::{DefId, LocalDefId}; |
dfeec247 | 6 | use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; |
e74abb32 | 7 | use rustc_index::vec::Idx; |
ba9703b0 XL |
8 | use rustc_middle::ty::layout::{LayoutError, SizeSkeleton}; |
9 | use rustc_middle::ty::query::Providers; | |
5869c6ff | 10 | use rustc_middle::ty::{self, FloatTy, IntTy, Ty, TyCtxt, UintTy}; |
f9f354fc XL |
11 | use rustc_session::lint; |
12 | use rustc_span::{sym, Span, Symbol, DUMMY_SP}; | |
ba9703b0 | 13 | use rustc_target::abi::{Pointer, VariantIdx}; |
f9f354fc | 14 | use rustc_target::asm::{InlineAsmRegOrRegClass, InlineAsmType}; |
dfeec247 | 15 | use rustc_target::spec::abi::Abi::RustIntrinsic; |
60c5eb7d | 16 | |
f035d41b | 17 | fn check_mod_intrinsics(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { |
dfeec247 | 18 | tcx.hir().visit_item_likes_in_module(module_def_id, &mut ItemVisitor { tcx }.as_deep_visitor()); |
9fa01778 XL |
19 | } |
20 | ||
f035d41b | 21 | pub fn provide(providers: &mut Providers) { |
dfeec247 | 22 | *providers = Providers { check_mod_intrinsics, ..*providers }; |
1a4d82fc JJ |
23 | } |
24 | ||
dc9dc135 XL |
25 | struct ItemVisitor<'tcx> { |
26 | tcx: TyCtxt<'tcx>, | |
54a0048b | 27 | } |
1a4d82fc | 28 | |
dc9dc135 XL |
29 | struct ExprVisitor<'tcx> { |
30 | tcx: TyCtxt<'tcx>, | |
3dfed10e | 31 | typeck_results: &'tcx ty::TypeckResults<'tcx>, |
7cac9316 | 32 | param_env: ty::ParamEnv<'tcx>, |
1a4d82fc JJ |
33 | } |
34 | ||
8bb4bdeb XL |
35 | /// If the type is `Option<T>`, it will return `T`, otherwise |
36 | /// the type itself. Works on most `Option`-like types. | |
dc9dc135 | 37 | fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { |
1b1a35ee | 38 | let (def, substs) = match *ty.kind() { |
b7449926 | 39 | ty::Adt(def, substs) => (def, substs), |
dfeec247 | 40 | _ => return ty, |
8bb4bdeb XL |
41 | }; |
42 | ||
cc61c64b | 43 | if def.variants.len() == 2 && !def.repr.c() && def.repr.int.is_none() { |
8bb4bdeb XL |
44 | let data_idx; |
45 | ||
a1dfa0c6 XL |
46 | let one = VariantIdx::new(1); |
47 | let zero = VariantIdx::new(0); | |
48 | ||
49 | if def.variants[zero].fields.is_empty() { | |
50 | data_idx = one; | |
51 | } else if def.variants[one].fields.is_empty() { | |
52 | data_idx = zero; | |
8bb4bdeb XL |
53 | } else { |
54 | return ty; | |
55 | } | |
56 | ||
57 | if def.variants[data_idx].fields.len() == 1 { | |
58 | return def.variants[data_idx].fields[0].ty(tcx, substs); | |
59 | } | |
60 | } | |
61 | ||
62 | ty | |
63 | } | |
64 | ||
dc9dc135 | 65 | impl ExprVisitor<'tcx> { |
1a4d82fc | 66 | fn def_id_is_transmute(&self, def_id: DefId) -> bool { |
dfeec247 XL |
67 | self.tcx.fn_sig(def_id).abi() == RustIntrinsic |
68 | && self.tcx.item_name(def_id) == sym::transmute | |
1a4d82fc JJ |
69 | } |
70 | ||
7cac9316 XL |
71 | fn check_transmute(&self, span: Span, from: Ty<'tcx>, to: Ty<'tcx>) { |
72 | let sk_from = SizeSkeleton::compute(from, self.tcx, self.param_env); | |
73 | let sk_to = SizeSkeleton::compute(to, self.tcx, self.param_env); | |
1a4d82fc | 74 | |
54a0048b SL |
75 | // Check for same size using the skeletons. |
76 | if let (Ok(sk_from), Ok(sk_to)) = (sk_from, sk_to) { | |
77 | if sk_from.same_size(sk_to) { | |
78 | return; | |
1a4d82fc | 79 | } |
1a4d82fc | 80 | |
5869c6ff | 81 | // Special-case transmuting from `typeof(function)` and |
8bb4bdeb | 82 | // `Option<typeof(function)>` to present a clearer error. |
e74abb32 | 83 | let from = unpack_option_like(self.tcx, from); |
1b1a35ee | 84 | if let (&ty::FnDef(..), SizeSkeleton::Known(size_to)) = (from.kind(), sk_to) { |
a1dfa0c6 | 85 | if size_to == Pointer.size(&self.tcx) { |
dfeec247 | 86 | struct_span_err!(self.tcx.sess, span, E0591, "can't transmute zero-sized type") |
041b39d2 XL |
87 | .note(&format!("source type: {}", from)) |
88 | .note(&format!("target type: {}", to)) | |
89 | .help("cast with `as` to a pointer instead") | |
8bb4bdeb | 90 | .emit(); |
54a0048b SL |
91 | return; |
92 | } | |
54a0048b | 93 | } |
1a4d82fc JJ |
94 | } |
95 | ||
54a0048b | 96 | // Try to display a sensible error with as much information as possible. |
dfeec247 XL |
97 | let skeleton_string = |ty: Ty<'tcx>, sk| match sk { |
98 | Ok(SizeSkeleton::Known(size)) => format!("{} bits", size.bits()), | |
99 | Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{}`", tail), | |
100 | Err(LayoutError::Unknown(bad)) => { | |
101 | if bad == ty { | |
102 | "this type does not have a fixed size".to_owned() | |
103 | } else { | |
104 | format!("size can vary because of {}", bad) | |
54a0048b | 105 | } |
1a4d82fc | 106 | } |
dfeec247 | 107 | Err(err) => err.to_string(), |
54a0048b | 108 | }; |
1a4d82fc | 109 | |
dfeec247 XL |
110 | let mut err = struct_span_err!( |
111 | self.tcx.sess, | |
112 | span, | |
113 | E0512, | |
114 | "cannot transmute between types of different sizes, \ | |
115 | or dependently-sized types" | |
116 | ); | |
0731742a XL |
117 | if from == to { |
118 | err.note(&format!("`{}` does not have a fixed size", from)); | |
119 | } else { | |
120 | err.note(&format!("source type: `{}` ({})", from, skeleton_string(from, sk_from))) | |
121 | .note(&format!("target type: `{}` ({})", to, skeleton_string(to, sk_to))); | |
122 | } | |
123 | err.emit() | |
54a0048b | 124 | } |
f9f354fc XL |
125 | |
126 | fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool { | |
127 | if ty.is_sized(self.tcx.at(DUMMY_SP), self.param_env) { | |
128 | return true; | |
129 | } | |
1b1a35ee | 130 | if let ty::Foreign(..) = ty.kind() { |
f9f354fc XL |
131 | return true; |
132 | } | |
133 | false | |
134 | } | |
135 | ||
136 | fn check_asm_operand_type( | |
137 | &self, | |
138 | idx: usize, | |
139 | reg: InlineAsmRegOrRegClass, | |
140 | expr: &hir::Expr<'tcx>, | |
141 | template: &[InlineAsmTemplatePiece], | |
94222f64 | 142 | is_input: bool, |
f9f354fc XL |
143 | tied_input: Option<(&hir::Expr<'tcx>, Option<InlineAsmType>)>, |
144 | ) -> Option<InlineAsmType> { | |
145 | // Check the type against the allowed types for inline asm. | |
3dfed10e | 146 | let ty = self.typeck_results.expr_ty_adjusted(expr); |
29967ef6 | 147 | let asm_ty_isize = match self.tcx.sess.target.pointer_width { |
f9f354fc XL |
148 | 16 => InlineAsmType::I16, |
149 | 32 => InlineAsmType::I32, | |
150 | 64 => InlineAsmType::I64, | |
151 | _ => unreachable!(), | |
152 | }; | |
1b1a35ee | 153 | let asm_ty = match *ty.kind() { |
94222f64 XL |
154 | // `!` is allowed for input but not for output (issue #87802) |
155 | ty::Never if is_input => return None, | |
156 | ty::Error(_) => return None, | |
f9f354fc XL |
157 | ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Some(InlineAsmType::I8), |
158 | ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Some(InlineAsmType::I16), | |
159 | ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Some(InlineAsmType::I32), | |
160 | ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Some(InlineAsmType::I64), | |
161 | ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => Some(InlineAsmType::I128), | |
162 | ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Some(asm_ty_isize), | |
163 | ty::Float(FloatTy::F32) => Some(InlineAsmType::F32), | |
164 | ty::Float(FloatTy::F64) => Some(InlineAsmType::F64), | |
165 | ty::FnPtr(_) => Some(asm_ty_isize), | |
166 | ty::RawPtr(ty::TypeAndMut { ty, mutbl: _ }) if self.is_thin_ptr_ty(ty) => { | |
167 | Some(asm_ty_isize) | |
168 | } | |
169 | ty::Adt(adt, substs) if adt.repr.simd() => { | |
170 | let fields = &adt.non_enum_variant().fields; | |
171 | let elem_ty = fields[0].ty(self.tcx, substs); | |
1b1a35ee | 172 | match elem_ty.kind() { |
f035d41b | 173 | ty::Never | ty::Error(_) => return None, |
f9f354fc XL |
174 | ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => { |
175 | Some(InlineAsmType::VecI8(fields.len() as u64)) | |
176 | } | |
177 | ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => { | |
178 | Some(InlineAsmType::VecI16(fields.len() as u64)) | |
179 | } | |
180 | ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => { | |
181 | Some(InlineAsmType::VecI32(fields.len() as u64)) | |
182 | } | |
183 | ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => { | |
184 | Some(InlineAsmType::VecI64(fields.len() as u64)) | |
185 | } | |
186 | ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => { | |
187 | Some(InlineAsmType::VecI128(fields.len() as u64)) | |
188 | } | |
189 | ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => { | |
29967ef6 | 190 | Some(match self.tcx.sess.target.pointer_width { |
f9f354fc XL |
191 | 16 => InlineAsmType::VecI16(fields.len() as u64), |
192 | 32 => InlineAsmType::VecI32(fields.len() as u64), | |
193 | 64 => InlineAsmType::VecI64(fields.len() as u64), | |
194 | _ => unreachable!(), | |
195 | }) | |
196 | } | |
197 | ty::Float(FloatTy::F32) => Some(InlineAsmType::VecF32(fields.len() as u64)), | |
198 | ty::Float(FloatTy::F64) => Some(InlineAsmType::VecF64(fields.len() as u64)), | |
199 | _ => None, | |
200 | } | |
201 | } | |
202 | _ => None, | |
203 | }; | |
204 | let asm_ty = match asm_ty { | |
205 | Some(asm_ty) => asm_ty, | |
206 | None => { | |
207 | let msg = &format!("cannot use value of type `{}` for inline assembly", ty); | |
208 | let mut err = self.tcx.sess.struct_span_err(expr.span, msg); | |
209 | err.note( | |
210 | "only integers, floats, SIMD vectors, pointers and function pointers \ | |
211 | can be used as arguments for inline assembly", | |
212 | ); | |
213 | err.emit(); | |
214 | return None; | |
215 | } | |
216 | }; | |
217 | ||
218 | // Check that the type implements Copy. The only case where this can | |
219 | // possibly fail is for SIMD types which don't #[derive(Copy)]. | |
f035d41b | 220 | if !ty.is_copy_modulo_regions(self.tcx.at(DUMMY_SP), self.param_env) { |
f9f354fc XL |
221 | let msg = "arguments for inline assembly must be copyable"; |
222 | let mut err = self.tcx.sess.struct_span_err(expr.span, msg); | |
223 | err.note(&format!("`{}` does not implement the Copy trait", ty)); | |
224 | err.emit(); | |
225 | } | |
226 | ||
227 | // Ideally we wouldn't need to do this, but LLVM's register allocator | |
228 | // really doesn't like it when tied operands have different types. | |
229 | // | |
230 | // This is purely an LLVM limitation, but we have to live with it since | |
231 | // there is no way to hide this with implicit conversions. | |
232 | // | |
233 | // For the purposes of this check we only look at the `InlineAsmType`, | |
234 | // which means that pointers and integers are treated as identical (modulo | |
235 | // size). | |
236 | if let Some((in_expr, Some(in_asm_ty))) = tied_input { | |
237 | if in_asm_ty != asm_ty { | |
f035d41b | 238 | let msg = "incompatible types for asm inout argument"; |
f9f354fc XL |
239 | let mut err = self.tcx.sess.struct_span_err(vec![in_expr.span, expr.span], msg); |
240 | err.span_label( | |
241 | in_expr.span, | |
3dfed10e | 242 | &format!("type `{}`", self.typeck_results.expr_ty_adjusted(in_expr)), |
f9f354fc XL |
243 | ); |
244 | err.span_label(expr.span, &format!("type `{}`", ty)); | |
245 | err.note( | |
246 | "asm inout arguments must have the same type, \ | |
247 | unless they are both pointers or integers of the same size", | |
248 | ); | |
249 | err.emit(); | |
250 | } | |
251 | ||
252 | // All of the later checks have already been done on the input, so | |
253 | // let's not emit errors and warnings twice. | |
254 | return Some(asm_ty); | |
255 | } | |
256 | ||
257 | // Check the type against the list of types supported by the selected | |
258 | // register class. | |
259 | let asm_arch = self.tcx.sess.asm_arch.unwrap(); | |
260 | let reg_class = reg.reg_class(); | |
261 | let supported_tys = reg_class.supported_types(asm_arch); | |
262 | let feature = match supported_tys.iter().find(|&&(t, _)| t == asm_ty) { | |
263 | Some((_, feature)) => feature, | |
264 | None => { | |
265 | let msg = &format!("type `{}` cannot be used with this register class", ty); | |
266 | let mut err = self.tcx.sess.struct_span_err(expr.span, msg); | |
267 | let supported_tys: Vec<_> = | |
268 | supported_tys.iter().map(|(t, _)| t.to_string()).collect(); | |
269 | err.note(&format!( | |
270 | "register class `{}` supports these types: {}", | |
271 | reg_class.name(), | |
272 | supported_tys.join(", "), | |
273 | )); | |
274 | if let Some(suggest) = reg_class.suggest_class(asm_arch, asm_ty) { | |
275 | err.help(&format!( | |
276 | "consider using the `{}` register class instead", | |
277 | suggest.name() | |
278 | )); | |
279 | } | |
280 | err.emit(); | |
281 | return Some(asm_ty); | |
282 | } | |
283 | }; | |
284 | ||
285 | // Check whether the selected type requires a target feature. Note that | |
286 | // this is different from the feature check we did earlier in AST | |
287 | // lowering. While AST lowering checked that this register class is | |
288 | // usable at all with the currently enabled features, some types may | |
289 | // only be usable with a register class when a certain feature is | |
290 | // enabled. We check this here since it depends on the results of typeck. | |
291 | // | |
292 | // Also note that this check isn't run when the operand type is never | |
293 | // (!). In that case we still need the earlier check in AST lowering to | |
294 | // verify that the register class is usable at all. | |
295 | if let Some(feature) = feature { | |
296 | if !self.tcx.sess.target_features.contains(&Symbol::intern(feature)) { | |
297 | let msg = &format!("`{}` target feature is not enabled", feature); | |
298 | let mut err = self.tcx.sess.struct_span_err(expr.span, msg); | |
299 | err.note(&format!( | |
300 | "this is required to use type `{}` with register class `{}`", | |
301 | ty, | |
302 | reg_class.name(), | |
303 | )); | |
304 | err.emit(); | |
305 | return Some(asm_ty); | |
306 | } | |
307 | } | |
308 | ||
309 | // Check whether a modifier is suggested for using this type. | |
310 | if let Some((suggested_modifier, suggested_result)) = | |
311 | reg_class.suggest_modifier(asm_arch, asm_ty) | |
312 | { | |
313 | // Search for any use of this operand without a modifier and emit | |
314 | // the suggestion for them. | |
315 | let mut spans = vec![]; | |
316 | for piece in template { | |
317 | if let &InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span } = piece | |
318 | { | |
319 | if operand_idx == idx && modifier.is_none() { | |
320 | spans.push(span); | |
321 | } | |
322 | } | |
323 | } | |
324 | if !spans.is_empty() { | |
325 | let (default_modifier, default_result) = | |
326 | reg_class.default_modifier(asm_arch).unwrap(); | |
327 | self.tcx.struct_span_lint_hir( | |
328 | lint::builtin::ASM_SUB_REGISTER, | |
329 | expr.hir_id, | |
330 | spans, | |
331 | |lint| { | |
332 | let msg = "formatting may not be suitable for sub-register argument"; | |
333 | let mut err = lint.build(msg); | |
334 | err.span_label(expr.span, "for this argument"); | |
335 | err.help(&format!( | |
336 | "use the `{}` modifier to have the register formatted as `{}`", | |
337 | suggested_modifier, suggested_result, | |
338 | )); | |
339 | err.help(&format!( | |
340 | "or use the `{}` modifier to keep the default formatting of `{}`", | |
341 | default_modifier, default_result, | |
342 | )); | |
343 | err.emit(); | |
344 | }, | |
345 | ); | |
346 | } | |
347 | } | |
348 | ||
349 | Some(asm_ty) | |
350 | } | |
351 | ||
352 | fn check_asm(&self, asm: &hir::InlineAsm<'tcx>) { | |
17df50a5 | 353 | for (idx, (op, _)) in asm.operands.iter().enumerate() { |
f9f354fc XL |
354 | match *op { |
355 | hir::InlineAsmOperand::In { reg, ref expr } => { | |
94222f64 | 356 | self.check_asm_operand_type(idx, reg, expr, asm.template, true, None); |
f9f354fc XL |
357 | } |
358 | hir::InlineAsmOperand::Out { reg, late: _, ref expr } => { | |
359 | if let Some(expr) = expr { | |
94222f64 | 360 | self.check_asm_operand_type(idx, reg, expr, asm.template, false, None); |
f9f354fc XL |
361 | } |
362 | } | |
363 | hir::InlineAsmOperand::InOut { reg, late: _, ref expr } => { | |
94222f64 | 364 | self.check_asm_operand_type(idx, reg, expr, asm.template, false, None); |
f9f354fc XL |
365 | } |
366 | hir::InlineAsmOperand::SplitInOut { reg, late: _, ref in_expr, ref out_expr } => { | |
94222f64 XL |
367 | let in_ty = |
368 | self.check_asm_operand_type(idx, reg, in_expr, asm.template, true, None); | |
f9f354fc XL |
369 | if let Some(out_expr) = out_expr { |
370 | self.check_asm_operand_type( | |
371 | idx, | |
372 | reg, | |
373 | out_expr, | |
374 | asm.template, | |
94222f64 | 375 | false, |
f9f354fc XL |
376 | Some((in_expr, in_ty)), |
377 | ); | |
378 | } | |
379 | } | |
17df50a5 | 380 | hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::Sym { .. } => {} |
f9f354fc XL |
381 | } |
382 | } | |
383 | } | |
54a0048b | 384 | } |
1a4d82fc | 385 | |
dc9dc135 | 386 | impl Visitor<'tcx> for ItemVisitor<'tcx> { |
ba9703b0 | 387 | type Map = intravisit::ErasedMap<'tcx>; |
dfeec247 | 388 | |
ba9703b0 | 389 | fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { |
32a655c1 | 390 | NestedVisitorMap::None |
1a4d82fc JJ |
391 | } |
392 | ||
32a655c1 | 393 | fn visit_nested_body(&mut self, body_id: hir::BodyId) { |
0731742a XL |
394 | let owner_def_id = self.tcx.hir().body_owner_def_id(body_id); |
395 | let body = self.tcx.hir().body(body_id); | |
ba9703b0 | 396 | let param_env = self.tcx.param_env(owner_def_id.to_def_id()); |
3dfed10e XL |
397 | let typeck_results = self.tcx.typeck(owner_def_id); |
398 | ExprVisitor { tcx: self.tcx, param_env, typeck_results }.visit_body(body); | |
32a655c1 | 399 | self.visit_body(body); |
1a4d82fc | 400 | } |
54a0048b | 401 | } |
1a4d82fc | 402 | |
dc9dc135 | 403 | impl Visitor<'tcx> for ExprVisitor<'tcx> { |
ba9703b0 | 404 | type Map = intravisit::ErasedMap<'tcx>; |
dfeec247 | 405 | |
ba9703b0 | 406 | fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { |
32a655c1 | 407 | NestedVisitorMap::None |
476ff2be SL |
408 | } |
409 | ||
dfeec247 | 410 | fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { |
f9f354fc XL |
411 | match expr.kind { |
412 | hir::ExprKind::Path(ref qpath) => { | |
3dfed10e | 413 | let res = self.typeck_results.qpath_res(qpath, expr.hir_id); |
f9f354fc XL |
414 | if let Res::Def(DefKind::Fn, did) = res { |
415 | if self.def_id_is_transmute(did) { | |
3dfed10e | 416 | let typ = self.typeck_results.node_type(expr.hir_id); |
f9f354fc XL |
417 | let sig = typ.fn_sig(self.tcx); |
418 | let from = sig.inputs().skip_binder()[0]; | |
f035d41b | 419 | let to = sig.output().skip_binder(); |
f9f354fc XL |
420 | self.check_transmute(expr.span, from, to); |
421 | } | |
422 | } | |
1a4d82fc | 423 | } |
f9f354fc XL |
424 | |
425 | hir::ExprKind::InlineAsm(asm) => self.check_asm(asm), | |
426 | ||
427 | _ => {} | |
1a4d82fc JJ |
428 | } |
429 | ||
92a42be0 | 430 | intravisit::walk_expr(self, expr); |
1a4d82fc JJ |
431 | } |
432 | } |