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