1use rustc_abi::FieldIdx;
2use rustc_ast::InlineAsmTemplatePiece;
3use rustc_data_structures::fx::FxIndexSet;
4use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level};
5use rustc_hir as hir;
6use rustc_hir::attrs::lang_items::LangItem;
7use rustc_hir::def_id::DefId;
8use rustc_lint_defs::builtin::ASM_SUB_REGISTER;
9use rustc_middle::bug;
10use rustc_middle::ty::{
11 self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, UintTy, Unnormalized,
12};
13use rustc_span::def_id::LocalDefId;
14use rustc_span::{ErrorGuaranteed, Span, Symbol, sym};
15use rustc_target::asm::{
16 InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType, ModifierInfo,
17};
18use rustc_trait_selection::infer::InferCtxtExt;
19
20use crate::FnCtxt;
21use crate::diagnostics::{AsmConstPtrUnstable, RegisterTypeUnstable};
22
23pub(crate) struct InlineAsmCtxt<'a, 'tcx> {
24 target_features: &'tcx FxIndexSet<Symbol>,
25 fcx: &'a FnCtxt<'a, 'tcx>,
26}
27
28enum NonAsmTypeReason<'tcx> {
29 UnevaluatedSIMDArrayLength(DefId, ty::Const<'tcx>),
30 Invalid(Ty<'tcx>),
31 InvalidElement(DefId, Ty<'tcx>),
32 NotSizedPtr(Ty<'tcx>),
33 EmptySIMDArray(Ty<'tcx>),
34 Tainted(ErrorGuaranteed),
35}
36
37impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
38 pub(crate) fn new(fcx: &'a FnCtxt<'a, 'tcx>, def_id: LocalDefId) -> Self {
39 InlineAsmCtxt { target_features: fcx.tcx.asm_target_features(def_id), fcx }
40 }
41
42 fn tcx(&self) -> TyCtxt<'tcx> {
43 self.fcx.tcx
44 }
45
46 fn expr_ty(&self, expr: &hir::Expr<'tcx>) -> Ty<'tcx> {
47 let ty = self.fcx.typeck_results.borrow().expr_ty_adjusted(expr);
48 let ty = self.fcx.resolve_vars_with_obligations(ty);
49 if ty.has_non_region_infer() {
50 Ty::new_misc_error(self.tcx())
51 } else {
52 self.tcx().erase_and_anonymize_regions(ty)
53 }
54 }
55
56 fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool {
58 if self.fcx.type_is_sized_modulo_regions(self.fcx.param_env, ty) {
61 return true;
62 }
63 if let ty::Foreign(..) = self.fcx.resolve_vars_with_obligations(ty).kind() {
64 return true;
65 }
66 false
67 }
68
69 fn get_asm_ty(
70 &self,
71 span: Span,
72 ty: Ty<'tcx>,
73 ) -> Result<InlineAsmType, NonAsmTypeReason<'tcx>> {
74 let asm_ty_isize = match self.tcx().sess.target.pointer_width {
75 16 => InlineAsmType::I16,
76 32 => InlineAsmType::I32,
77 64 => InlineAsmType::I64,
78 width => ::rustc_middle::util::bug::bug_fmt(format_args!("unsupported pointer width: {0}",
width))bug!("unsupported pointer width: {width}"),
79 };
80
81 match *ty.kind() {
82 ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Ok(InlineAsmType::I8),
83 ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Ok(InlineAsmType::I16),
84 ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Ok(InlineAsmType::I32),
85 ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Ok(InlineAsmType::I64),
86 ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => Ok(InlineAsmType::I128),
87 ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Ok(asm_ty_isize),
88 ty::Float(FloatTy::F16) => Ok(InlineAsmType::F16),
89 ty::Float(FloatTy::F32) => Ok(InlineAsmType::F32),
90 ty::Float(FloatTy::F64) => Ok(InlineAsmType::F64),
91 ty::Float(FloatTy::F128) => Ok(InlineAsmType::F128),
92 ty::FnPtr(..) => Ok(asm_ty_isize),
93 ty::RawPtr(elem_ty, _) => {
94 if self.is_thin_ptr_ty(elem_ty) {
95 Ok(asm_ty_isize)
96 } else {
97 Err(NonAsmTypeReason::NotSizedPtr(ty))
98 }
99 }
100 ty::Adt(adt, args) if adt.repr().simd() => {
101 if !adt.is_struct() {
102 let guar = self.fcx.dcx().span_delayed_bug(
103 span,
104 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("repr(simd) should only be used on structs, got {0}",
adt.descr()))
})format!("repr(simd) should only be used on structs, got {}", adt.descr()),
105 );
106 return Err(NonAsmTypeReason::Tainted(guar));
107 }
108
109 let fields = &adt.non_enum_variant().fields;
110 if fields.is_empty() {
111 return Err(NonAsmTypeReason::EmptySIMDArray(ty));
112 }
113 let field = &fields[FieldIdx::ZERO];
114 let elem_ty = field.ty(self.tcx(), args).skip_norm_wip();
115
116 let (size, ty) = match *elem_ty.kind() {
117 ty::Array(ty, len) => {
118 let len = if self.fcx.next_trait_solver() {
121 self.fcx.try_structurally_resolve_const(span, len)
122 } else {
123 self.fcx.tcx.normalize_erasing_regions(
124 self.fcx.typing_env(self.fcx.param_env),
125 Unnormalized::new_wip(len),
126 )
127 };
128 let Some(len) = len.try_to_target_usize(self.tcx()) else {
129 return Err(NonAsmTypeReason::UnevaluatedSIMDArrayLength(
130 field.did, len,
131 ));
132 };
133 (len, ty)
134 }
135 _ => (fields.len() as u64, elem_ty),
136 };
137
138 match ty.kind() {
139 ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Ok(InlineAsmType::VecI8(size)),
140 ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Ok(InlineAsmType::VecI16(size)),
141 ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Ok(InlineAsmType::VecI32(size)),
142 ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Ok(InlineAsmType::VecI64(size)),
143 ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => {
144 Ok(InlineAsmType::VecI128(size))
145 }
146 ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => {
147 Ok(match self.tcx().sess.target.pointer_width {
148 16 => InlineAsmType::VecI16(size),
149 32 => InlineAsmType::VecI32(size),
150 64 => InlineAsmType::VecI64(size),
151 width => ::rustc_middle::util::bug::bug_fmt(format_args!("unsupported pointer width: {0}",
width))bug!("unsupported pointer width: {width}"),
152 })
153 }
154 ty::Float(FloatTy::F16) => Ok(InlineAsmType::VecF16(size)),
155 ty::Float(FloatTy::F32) => Ok(InlineAsmType::VecF32(size)),
156 ty::Float(FloatTy::F64) => Ok(InlineAsmType::VecF64(size)),
157 ty::Float(FloatTy::F128) => Ok(InlineAsmType::VecF128(size)),
158 _ => Err(NonAsmTypeReason::InvalidElement(field.did, ty)),
159 }
160 }
161 ty::Infer(_) => ::rustc_middle::util::bug::bug_fmt(format_args!("unexpected infer ty in asm operand"))bug!("unexpected infer ty in asm operand"),
162 _ => Err(NonAsmTypeReason::Invalid(ty)),
163 }
164 }
165
166 fn check_asm_operand_type(
167 &self,
168 idx: usize,
169 reg: InlineAsmRegOrRegClass,
170 expr: &'tcx hir::Expr<'tcx>,
171 template: &[InlineAsmTemplatePiece],
172 is_input: bool,
173 tied_input: Option<(&'tcx hir::Expr<'tcx>, Option<InlineAsmType>)>,
174 ) -> Option<InlineAsmType> {
175 struct FormattingSubRegisterArg<'a> {
176 expr_span: Span,
177 idx: usize,
178 suggested_modifier: char,
179 suggested_result: &'a str,
180 suggested_size: u16,
181 default_modifier: char,
182 default_result: &'a str,
183 default_size: u16,
184 }
185
186 impl<'a, 'b> Diagnostic<'a, ()> for FormattingSubRegisterArg<'b> {
187 fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
188 let Self {
189 expr_span,
190 idx,
191 suggested_modifier,
192 suggested_result,
193 suggested_size,
194 default_modifier,
195 default_result,
196 default_size,
197 } = self;
198 Diag::new(dcx, level, "formatting may not be suitable for sub-register argument")
199 .with_span_label(expr_span, "for this argument")
200 .with_help(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("use `{{{0}:{1}}}` to have the register formatted as `{2}` (for {3}-bit values)",
idx, suggested_modifier, suggested_result, suggested_size))
})format!(
201 "use `{{{idx}:{suggested_modifier}}}` to have the register formatted as `{suggested_result}` (for {suggested_size}-bit values)",
202 ))
203 .with_help(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("or use `{{{0}:{1}}}` to keep the default formatting of `{2}` (for {3}-bit values)",
idx, default_modifier, default_result, default_size))
})format!(
204 "or use `{{{idx}:{default_modifier}}}` to keep the default formatting of `{default_result}` (for {default_size}-bit values)",
205 ))
206 }
207 }
208
209 let ty = self.expr_ty(expr);
210 if ty.has_non_region_infer() {
211 ::rustc_middle::util::bug::bug_fmt(format_args!("inference variable in asm operand ty: {0:?} {1:?}",
expr, ty));bug!("inference variable in asm operand ty: {:?} {:?}", expr, ty);
212 }
213
214 let asm_ty = match *ty.kind() {
215 ty::Never if is_input => return None,
217 _ if ty.references_error() => return None,
218 ty::Adt(adt, args) if self.tcx().is_lang_item(adt.did(), LangItem::MaybeUninit) => {
219 let ty = args.type_at(0);
220 self.get_asm_ty(expr.span, ty)
221 }
222 _ => self.get_asm_ty(expr.span, ty),
223 };
224 let asm_ty = match asm_ty {
225 Ok(asm_ty) => asm_ty,
226 Err(reason) => {
227 match reason {
228 NonAsmTypeReason::UnevaluatedSIMDArrayLength(did, len) => {
229 let msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot evaluate SIMD vector length `{0}`",
len))
})format!("cannot evaluate SIMD vector length `{len}`");
230 self.fcx
231 .dcx()
232 .struct_span_err(self.tcx().def_span(did), msg)
233 .with_span_note(
234 expr.span,
235 "SIMD vector length needs to be known statically for use in `asm!`",
236 )
237 .emit();
238 }
239 NonAsmTypeReason::Invalid(ty) => {
240 let msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot use value of type `{0}` for inline assembly",
ty))
})format!("cannot use value of type `{ty}` for inline assembly");
241 self.fcx.dcx().struct_span_err(expr.span, msg).with_note(
242 "only integers, floats, SIMD vectors, pointers and function pointers \
243 can be used as arguments for inline assembly",
244 ).emit();
245 }
246 NonAsmTypeReason::NotSizedPtr(ty) => {
247 let msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot use value of unsized pointer type `{0}` for inline assembly",
ty))
})format!(
248 "cannot use value of unsized pointer type `{ty}` for inline assembly"
249 );
250 self.fcx
251 .dcx()
252 .struct_span_err(expr.span, msg)
253 .with_note("only sized pointers can be used in inline assembly")
254 .emit();
255 }
256 NonAsmTypeReason::InvalidElement(did, ty) => {
257 let msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot use SIMD vector with element type `{0}` for inline assembly",
ty))
})format!(
258 "cannot use SIMD vector with element type `{ty}` for inline assembly"
259 );
260 self.fcx.dcx()
261 .struct_span_err(self.tcx().def_span(did), msg).with_span_note(
262 expr.span,
263 "only integers, floats, SIMD vectors, pointers and function pointers \
264 can be used as arguments for inline assembly",
265 ).emit();
266 }
267 NonAsmTypeReason::EmptySIMDArray(ty) => {
268 let msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("use of empty SIMD vector `{0}`",
ty))
})format!("use of empty SIMD vector `{ty}`");
269 self.fcx.dcx().struct_span_err(expr.span, msg).emit();
270 }
271 NonAsmTypeReason::Tainted(_error_guard) => {
272 }
274 }
275 return None;
276 }
277 };
278
279 if !self.fcx.type_is_copy_modulo_regions(self.fcx.param_env, ty) {
282 let msg = "arguments for inline assembly must be copyable";
283 self.fcx
284 .dcx()
285 .struct_span_err(expr.span, msg)
286 .with_note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` does not implement the Copy trait",
ty))
})format!("`{ty}` does not implement the Copy trait"))
287 .emit();
288 }
289
290 if let Some((in_expr, Some(in_asm_ty))) = tied_input {
300 if in_asm_ty != asm_ty {
301 let msg = "incompatible types for asm inout argument";
302 let in_expr_ty = self.expr_ty(in_expr);
303 self.fcx
304 .dcx()
305 .struct_span_err(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[in_expr.span, expr.span]))vec![in_expr.span, expr.span], msg)
306 .with_span_label(in_expr.span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("type `{0}`", in_expr_ty))
})format!("type `{in_expr_ty}`"))
307 .with_span_label(expr.span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("type `{0}`", ty))
})format!("type `{ty}`"))
308 .with_note(
309 "asm inout arguments must have the same type, \
310 unless they are both pointers or integers of the same size",
311 )
312 .emit();
313 }
314
315 return Some(asm_ty);
318 }
319
320 let asm_arch = self.tcx().sess.asm_arch.unwrap();
323 let allow_experimental_reg = self.tcx().features().asm_experimental_reg();
324 let reg_class = reg.reg_class();
325 let supported_tys = reg_class.supported_types(asm_arch, allow_experimental_reg);
326 let Some((_, feature)) = supported_tys.iter().find(|&&(t, _)| t == asm_ty) else {
327 let mut err = if !allow_experimental_reg
328 && reg_class.supported_types(asm_arch, true).iter().any(|&(t, _)| t == asm_ty)
329 {
330 self.tcx().sess.create_feature_err(
331 RegisterTypeUnstable { span: expr.span, ty },
332 sym::asm_experimental_reg,
333 )
334 } else {
335 let msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("type `{0}` cannot be used with this register class",
ty))
})format!("type `{ty}` cannot be used with this register class");
336 let mut err = self.fcx.dcx().struct_span_err(expr.span, msg);
337 let supported_tys: Vec<_> =
338 supported_tys.iter().map(|(t, _)| t.to_string()).collect();
339 err.note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("register class `{0}` supports these types: {1}",
reg_class.name(), supported_tys.join(", ")))
})format!(
340 "register class `{}` supports these types: {}",
341 reg_class.name(),
342 supported_tys.join(", "),
343 ));
344 err
345 };
346 if let Some(suggest) = reg_class.suggest_class(asm_arch, asm_ty) {
347 err.help(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("consider using the `{0}` register class instead",
suggest.name()))
})format!("consider using the `{}` register class instead", suggest.name()));
348 }
349 err.emit();
350 return Some(asm_ty);
351 };
352
353 if let Some(feature) = feature {
364 if !self.target_features.contains(feature) {
365 let msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`{0}` target feature is not enabled",
feature))
})format!("`{feature}` target feature is not enabled");
366 self.fcx
367 .dcx()
368 .struct_span_err(expr.span, msg)
369 .with_note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("this is required to use type `{0}` with register class `{1}`",
ty, reg_class.name()))
})format!(
370 "this is required to use type `{}` with register class `{}`",
371 ty,
372 reg_class.name(),
373 ))
374 .emit();
375 return Some(asm_ty);
376 }
377 }
378
379 if let Some(ModifierInfo {
381 modifier: suggested_modifier,
382 result: suggested_result,
383 size: suggested_size,
384 }) = reg_class.suggest_modifier(asm_arch, asm_ty)
385 {
386 let mut spans = ::alloc::vec::Vec::new()vec![];
389 for piece in template {
390 if let &InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span } = piece
391 {
392 if operand_idx == idx && modifier.is_none() {
393 spans.push(span);
394 }
395 }
396 }
397 if !spans.is_empty() {
398 let ModifierInfo {
399 modifier: default_modifier,
400 result: default_result,
401 size: default_size,
402 } = reg_class.default_modifier(asm_arch).unwrap();
403 self.tcx().emit_node_span_lint(
404 ASM_SUB_REGISTER,
405 expr.hir_id,
406 spans,
407 FormattingSubRegisterArg {
408 expr_span: expr.span,
409 idx,
410 suggested_modifier,
411 suggested_result,
412 suggested_size,
413 default_modifier,
414 default_result,
415 default_size,
416 },
417 );
418 }
419 }
420
421 Some(asm_ty)
422 }
423
424 pub(crate) fn check_asm(&self, asm: &hir::InlineAsm<'tcx>) {
425 let Some(asm_arch) = self.tcx().sess.asm_arch else {
426 self.fcx.dcx().delayed_bug("target architecture does not support asm");
427 return;
428 };
429 let allow_experimental_reg = self.tcx().features().asm_experimental_reg();
430 for (idx, &(op, op_sp)) in asm.operands.iter().enumerate() {
431 if let Some(reg) = op.reg() {
442 if let InlineAsmRegOrRegClass::Reg(reg) = reg {
445 if let InlineAsmReg::Err = reg {
446 continue;
449 }
450 if let Err(msg) = reg.validate(
451 asm_arch,
452 self.tcx().sess.relocation_model(),
453 self.target_features,
454 &self.tcx().sess.target,
455 op.is_clobber(),
456 ) {
457 let msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot use register `{0}`: {1}",
reg.name(), msg))
})format!("cannot use register `{}`: {}", reg.name(), msg);
458 self.fcx.dcx().span_err(op_sp, msg);
459 continue;
460 }
461 }
462
463 if !op.is_clobber() {
464 let mut missing_required_features = ::alloc::vec::Vec::new()vec![];
465 let reg_class = reg.reg_class();
466 if let InlineAsmRegClass::Err = reg_class {
467 continue;
468 }
469 for &(_, feature) in
470 reg_class.supported_types(asm_arch, allow_experimental_reg).as_ref()
471 {
472 match feature {
473 Some(feature) => {
474 if self.target_features.contains(&feature) {
475 missing_required_features.clear();
476 break;
477 } else {
478 missing_required_features.push(feature);
479 }
480 }
481 None => {
482 missing_required_features.clear();
483 break;
484 }
485 }
486 }
487
488 missing_required_features.sort_unstable();
490 missing_required_features.dedup();
491 match &missing_required_features[..] {
492 [] => {}
493 [feature] => {
494 let msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("register class `{0}` requires the `{1}` target feature",
reg_class.name(), feature))
})format!(
495 "register class `{}` requires the `{}` target feature",
496 reg_class.name(),
497 feature
498 );
499 self.fcx.dcx().span_err(op_sp, msg);
500 continue;
502 }
503 features => {
504 let msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("register class `{0}` requires at least one of the following target features: {1}",
reg_class.name(),
features.iter().map(|f|
f.as_str()).intersperse(", ").collect::<String>()))
})format!(
505 "register class `{}` requires at least one of the following target features: {}",
506 reg_class.name(),
507 features
508 .iter()
509 .map(|f| f.as_str())
510 .intersperse(", ")
511 .collect::<String>(),
512 );
513 self.fcx.dcx().span_err(op_sp, msg);
514 continue;
516 }
517 }
518 }
519 }
520
521 match op {
522 hir::InlineAsmOperand::In { reg, expr } => {
523 self.check_asm_operand_type(idx, reg, expr, asm.template, true, None);
524 }
525 hir::InlineAsmOperand::Out { reg, late: _, expr } => {
526 if let Some(expr) = expr {
527 self.check_asm_operand_type(idx, reg, expr, asm.template, false, None);
528 }
529 }
530 hir::InlineAsmOperand::InOut { reg, late: _, expr } => {
531 self.check_asm_operand_type(idx, reg, expr, asm.template, false, None);
532 }
533 hir::InlineAsmOperand::SplitInOut { reg, late: _, in_expr, out_expr } => {
534 let in_ty =
535 self.check_asm_operand_type(idx, reg, in_expr, asm.template, true, None);
536 if let Some(out_expr) = out_expr {
537 self.check_asm_operand_type(
538 idx,
539 reg,
540 out_expr,
541 asm.template,
542 false,
543 Some((in_expr, in_ty)),
544 );
545 }
546 }
547 hir::InlineAsmOperand::Const { anon_const } => {
548 let ty = self.expr_ty(self.tcx().hir_body(anon_const.body).value);
549 match ty.kind() {
550 ty::Error(_) => {}
551 _ if ty.is_integral() => {}
552 ty::FnPtr(..) => {
553 if !self.tcx().features().asm_const_ptr() {
554 self.tcx()
555 .sess
556 .create_feature_err(
557 AsmConstPtrUnstable { span: op_sp },
558 sym::asm_const_ptr,
559 )
560 .emit();
561 }
562 }
563 ty::RawPtr(pointee, _) | ty::Ref(_, pointee, _)
564 if self.is_thin_ptr_ty(*pointee) =>
565 {
566 if !self.tcx().features().asm_const_ptr() {
567 self.tcx()
568 .sess
569 .create_feature_err(
570 AsmConstPtrUnstable { span: op_sp },
571 sym::asm_const_ptr,
572 )
573 .emit();
574 }
575 }
576 _ => {
577 let const_possible_ty = if !self.tcx().features().asm_const_ptr() {
578 "integer"
579 } else {
580 "integer or thin pointer"
581 };
582 self.fcx
583 .dcx()
584 .struct_span_err(op_sp, "invalid type for `const` operand")
585 .with_span_label(
586 self.tcx().def_span(anon_const.def_id),
587 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("is {0} `{1}`", ty.kind().article(),
ty))
})format!("is {} `{}`", ty.kind().article(), ty),
588 )
589 .with_help(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`const` operands must be of an {0} type",
const_possible_ty))
})format!(
590 "`const` operands must be of an {const_possible_ty} type"
591 ))
592 .emit();
593 }
594 }
595 }
596 hir::InlineAsmOperand::SymFn { expr } => {
598 let ty = self.expr_ty(expr);
599 match ty.kind() {
600 ty::FnDef(..) => {}
601 ty::Error(_) => {}
602 _ => {
603 self.fcx
604 .dcx()
605 .struct_span_err(op_sp, "invalid `sym` operand")
606 .with_span_label(
607 expr.span,
608 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("is {0} `{1}`", ty.kind().article(),
ty))
})format!("is {} `{}`", ty.kind().article(), ty),
609 )
610 .with_help(
611 "`sym` operands must refer to either a function or a static",
612 )
613 .emit();
614 }
615 }
616 }
617 hir::InlineAsmOperand::SymStatic { .. } => {}
619 hir::InlineAsmOperand::Label { .. } => {}
621 }
622 }
623 }
624}