Skip to main content

rustc_hir_typeck/
inline_asm.rs

1use rustc_abi::FieldIdx;
2use rustc_ast::InlineAsmTemplatePiece;
3use rustc_data_structures::fx::FxIndexSet;
4use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level};
5use rustc_hir::def_id::DefId;
6use rustc_hir::{self as hir, LangItem};
7use rustc_middle::bug;
8use rustc_middle::ty::{self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, UintTy};
9use rustc_session::lint;
10use rustc_span::def_id::LocalDefId;
11use rustc_span::{ErrorGuaranteed, Span, Symbol, sym};
12use rustc_target::asm::{
13    InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType, ModifierInfo,
14};
15use rustc_trait_selection::infer::InferCtxtExt;
16
17use crate::FnCtxt;
18use crate::errors::RegisterTypeUnstable;
19
20pub(crate) struct InlineAsmCtxt<'a, 'tcx> {
21    target_features: &'tcx FxIndexSet<Symbol>,
22    fcx: &'a FnCtxt<'a, 'tcx>,
23}
24
25enum NonAsmTypeReason<'tcx> {
26    UnevaluatedSIMDArrayLength(DefId, ty::Const<'tcx>),
27    Invalid(Ty<'tcx>),
28    InvalidElement(DefId, Ty<'tcx>),
29    NotSizedPtr(Ty<'tcx>),
30    EmptySIMDArray(Ty<'tcx>),
31    Tainted(ErrorGuaranteed),
32}
33
34impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
35    pub(crate) fn new(fcx: &'a FnCtxt<'a, 'tcx>, def_id: LocalDefId) -> Self {
36        InlineAsmCtxt { target_features: fcx.tcx.asm_target_features(def_id), fcx }
37    }
38
39    fn tcx(&self) -> TyCtxt<'tcx> {
40        self.fcx.tcx
41    }
42
43    fn expr_ty(&self, expr: &hir::Expr<'tcx>) -> Ty<'tcx> {
44        let ty = self.fcx.typeck_results.borrow().expr_ty_adjusted(expr);
45        let ty = self.fcx.try_structurally_resolve_type(expr.span, ty);
46        if ty.has_non_region_infer() {
47            Ty::new_misc_error(self.tcx())
48        } else {
49            self.tcx().erase_and_anonymize_regions(ty)
50        }
51    }
52
53    // FIXME(compiler-errors): This could use `<$ty as Pointee>::Metadata == ()`
54    fn is_thin_ptr_ty(&self, span: Span, ty: Ty<'tcx>) -> bool {
55        // Type still may have region variables, but `Sized` does not depend
56        // on those, so just erase them before querying.
57        if self.fcx.type_is_sized_modulo_regions(self.fcx.param_env, ty) {
58            return true;
59        }
60        if let ty::Foreign(..) = self.fcx.try_structurally_resolve_type(span, ty).kind() {
61            return true;
62        }
63        false
64    }
65
66    fn get_asm_ty(
67        &self,
68        span: Span,
69        ty: Ty<'tcx>,
70    ) -> Result<InlineAsmType, NonAsmTypeReason<'tcx>> {
71        let asm_ty_isize = match self.tcx().sess.target.pointer_width {
72            16 => InlineAsmType::I16,
73            32 => InlineAsmType::I32,
74            64 => InlineAsmType::I64,
75            width => ::rustc_middle::util::bug::bug_fmt(format_args!("unsupported pointer width: {0}",
        width))bug!("unsupported pointer width: {width}"),
76        };
77
78        match *ty.kind() {
79            ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Ok(InlineAsmType::I8),
80            ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Ok(InlineAsmType::I16),
81            ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Ok(InlineAsmType::I32),
82            ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Ok(InlineAsmType::I64),
83            ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => Ok(InlineAsmType::I128),
84            ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Ok(asm_ty_isize),
85            ty::Float(FloatTy::F16) => Ok(InlineAsmType::F16),
86            ty::Float(FloatTy::F32) => Ok(InlineAsmType::F32),
87            ty::Float(FloatTy::F64) => Ok(InlineAsmType::F64),
88            ty::Float(FloatTy::F128) => Ok(InlineAsmType::F128),
89            ty::FnPtr(..) => Ok(asm_ty_isize),
90            ty::RawPtr(elem_ty, _) => {
91                if self.is_thin_ptr_ty(span, elem_ty) {
92                    Ok(asm_ty_isize)
93                } else {
94                    Err(NonAsmTypeReason::NotSizedPtr(ty))
95                }
96            }
97            ty::Adt(adt, args) if adt.repr().simd() => {
98                if !adt.is_struct() {
99                    let guar = self.fcx.dcx().span_delayed_bug(
100                        span,
101                        ::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()),
102                    );
103                    return Err(NonAsmTypeReason::Tainted(guar));
104                }
105
106                let fields = &adt.non_enum_variant().fields;
107                if fields.is_empty() {
108                    return Err(NonAsmTypeReason::EmptySIMDArray(ty));
109                }
110                let field = &fields[FieldIdx::ZERO];
111                let elem_ty = field.ty(self.tcx(), args);
112
113                let (size, ty) = match *elem_ty.kind() {
114                    ty::Array(ty, len) => {
115                        // FIXME: `try_structurally_resolve_const` doesn't eval consts
116                        // in the old solver.
117                        let len = if self.fcx.next_trait_solver() {
118                            self.fcx.try_structurally_resolve_const(span, len)
119                        } else {
120                            self.fcx.tcx.normalize_erasing_regions(
121                                self.fcx.typing_env(self.fcx.param_env),
122                                len,
123                            )
124                        };
125                        let Some(len) = len.try_to_target_usize(self.tcx()) else {
126                            return Err(NonAsmTypeReason::UnevaluatedSIMDArrayLength(
127                                field.did, len,
128                            ));
129                        };
130                        (len, ty)
131                    }
132                    _ => (fields.len() as u64, elem_ty),
133                };
134
135                match ty.kind() {
136                    ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Ok(InlineAsmType::VecI8(size)),
137                    ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Ok(InlineAsmType::VecI16(size)),
138                    ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Ok(InlineAsmType::VecI32(size)),
139                    ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Ok(InlineAsmType::VecI64(size)),
140                    ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => {
141                        Ok(InlineAsmType::VecI128(size))
142                    }
143                    ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => {
144                        Ok(match self.tcx().sess.target.pointer_width {
145                            16 => InlineAsmType::VecI16(size),
146                            32 => InlineAsmType::VecI32(size),
147                            64 => InlineAsmType::VecI64(size),
148                            width => ::rustc_middle::util::bug::bug_fmt(format_args!("unsupported pointer width: {0}",
        width))bug!("unsupported pointer width: {width}"),
149                        })
150                    }
151                    ty::Float(FloatTy::F16) => Ok(InlineAsmType::VecF16(size)),
152                    ty::Float(FloatTy::F32) => Ok(InlineAsmType::VecF32(size)),
153                    ty::Float(FloatTy::F64) => Ok(InlineAsmType::VecF64(size)),
154                    ty::Float(FloatTy::F128) => Ok(InlineAsmType::VecF128(size)),
155                    _ => Err(NonAsmTypeReason::InvalidElement(field.did, ty)),
156                }
157            }
158            ty::Infer(_) => ::rustc_middle::util::bug::bug_fmt(format_args!("unexpected infer ty in asm operand"))bug!("unexpected infer ty in asm operand"),
159            _ => Err(NonAsmTypeReason::Invalid(ty)),
160        }
161    }
162
163    fn check_asm_operand_type(
164        &self,
165        idx: usize,
166        reg: InlineAsmRegOrRegClass,
167        expr: &'tcx hir::Expr<'tcx>,
168        template: &[InlineAsmTemplatePiece],
169        is_input: bool,
170        tied_input: Option<(&'tcx hir::Expr<'tcx>, Option<InlineAsmType>)>,
171    ) -> Option<InlineAsmType> {
172        struct FormattingSubRegisterArg<'a> {
173            expr_span: Span,
174            idx: usize,
175            suggested_modifier: char,
176            suggested_result: &'a str,
177            suggested_size: u16,
178            default_modifier: char,
179            default_result: &'a str,
180            default_size: u16,
181        }
182
183        impl<'a, 'b> Diagnostic<'a, ()> for FormattingSubRegisterArg<'b> {
184            fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
185                let Self {
186                    expr_span,
187                    idx,
188                    suggested_modifier,
189                    suggested_result,
190                    suggested_size,
191                    default_modifier,
192                    default_result,
193                    default_size,
194                } = self;
195                Diag::new(dcx, level, "formatting may not be suitable for sub-register argument")
196                    .with_span_label(expr_span, "for this argument")
197                    .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!(
198                        "use `{{{idx}:{suggested_modifier}}}` to have the register formatted as `{suggested_result}` (for {suggested_size}-bit values)",
199                    ))
200                    .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!(
201                        "or use `{{{idx}:{default_modifier}}}` to keep the default formatting of `{default_result}` (for {default_size}-bit values)",
202                    ))
203            }
204        }
205
206        let ty = self.expr_ty(expr);
207        if ty.has_non_region_infer() {
208            ::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);
209        }
210
211        let asm_ty = match *ty.kind() {
212            // `!` is allowed for input but not for output (issue #87802)
213            ty::Never if is_input => return None,
214            _ if ty.references_error() => return None,
215            ty::Adt(adt, args) if self.tcx().is_lang_item(adt.did(), LangItem::MaybeUninit) => {
216                let ty = args.type_at(0);
217                self.get_asm_ty(expr.span, ty)
218            }
219            _ => self.get_asm_ty(expr.span, ty),
220        };
221        let asm_ty = match asm_ty {
222            Ok(asm_ty) => asm_ty,
223            Err(reason) => {
224                match reason {
225                    NonAsmTypeReason::UnevaluatedSIMDArrayLength(did, len) => {
226                        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}`");
227                        self.fcx
228                            .dcx()
229                            .struct_span_err(self.tcx().def_span(did), msg)
230                            .with_span_note(
231                                expr.span,
232                                "SIMD vector length needs to be known statically for use in `asm!`",
233                            )
234                            .emit();
235                    }
236                    NonAsmTypeReason::Invalid(ty) => {
237                        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");
238                        self.fcx.dcx().struct_span_err(expr.span, msg).with_note(
239                            "only integers, floats, SIMD vectors, pointers and function pointers \
240                            can be used as arguments for inline assembly",
241                        ).emit();
242                    }
243                    NonAsmTypeReason::NotSizedPtr(ty) => {
244                        let msg = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("cannot use value of unsized pointer type `{0}` for inline assembly",
                ty))
    })format!(
245                            "cannot use value of unsized pointer type `{ty}` for inline assembly"
246                        );
247                        self.fcx
248                            .dcx()
249                            .struct_span_err(expr.span, msg)
250                            .with_note("only sized pointers can be used in inline assembly")
251                            .emit();
252                    }
253                    NonAsmTypeReason::InvalidElement(did, ty) => {
254                        let msg = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("cannot use SIMD vector with element type `{0}` for inline assembly",
                ty))
    })format!(
255                            "cannot use SIMD vector with element type `{ty}` for inline assembly"
256                        );
257                        self.fcx.dcx()
258                        .struct_span_err(self.tcx().def_span(did), msg).with_span_note(
259                            expr.span,
260                            "only integers, floats, SIMD vectors, pointers and function pointers \
261                            can be used as arguments for inline assembly",
262                        ).emit();
263                    }
264                    NonAsmTypeReason::EmptySIMDArray(ty) => {
265                        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}`");
266                        self.fcx.dcx().struct_span_err(expr.span, msg).emit();
267                    }
268                    NonAsmTypeReason::Tainted(_error_guard) => {
269                        // An error has already been reported.
270                    }
271                }
272                return None;
273            }
274        };
275
276        // Check that the type implements Copy. The only case where this can
277        // possibly fail is for SIMD types which don't #[derive(Copy)].
278        if !self.fcx.type_is_copy_modulo_regions(self.fcx.param_env, ty) {
279            let msg = "arguments for inline assembly must be copyable";
280            self.fcx
281                .dcx()
282                .struct_span_err(expr.span, msg)
283                .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"))
284                .emit();
285        }
286
287        // Ideally we wouldn't need to do this, but LLVM's register allocator
288        // really doesn't like it when tied operands have different types.
289        //
290        // This is purely an LLVM limitation, but we have to live with it since
291        // there is no way to hide this with implicit conversions.
292        //
293        // For the purposes of this check we only look at the `InlineAsmType`,
294        // which means that pointers and integers are treated as identical (modulo
295        // size).
296        if let Some((in_expr, Some(in_asm_ty))) = tied_input {
297            if in_asm_ty != asm_ty {
298                let msg = "incompatible types for asm inout argument";
299                let in_expr_ty = self.expr_ty(in_expr);
300                self.fcx
301                    .dcx()
302                    .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)
303                    .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}`"))
304                    .with_span_label(expr.span, ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("type `{0}`", ty))
    })format!("type `{ty}`"))
305                    .with_note(
306                        "asm inout arguments must have the same type, \
307                        unless they are both pointers or integers of the same size",
308                    )
309                    .emit();
310            }
311
312            // All of the later checks have already been done on the input, so
313            // let's not emit errors and warnings twice.
314            return Some(asm_ty);
315        }
316
317        // Check the type against the list of types supported by the selected
318        // register class.
319        let asm_arch = self.tcx().sess.asm_arch.unwrap();
320        let allow_experimental_reg = self.tcx().features().asm_experimental_reg();
321        let reg_class = reg.reg_class();
322        let supported_tys = reg_class.supported_types(asm_arch, allow_experimental_reg);
323        let Some((_, feature)) = supported_tys.iter().find(|&&(t, _)| t == asm_ty) else {
324            let mut err = if !allow_experimental_reg
325                && reg_class.supported_types(asm_arch, true).iter().any(|&(t, _)| t == asm_ty)
326            {
327                self.tcx().sess.create_feature_err(
328                    RegisterTypeUnstable { span: expr.span, ty },
329                    sym::asm_experimental_reg,
330                )
331            } else {
332                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");
333                let mut err = self.fcx.dcx().struct_span_err(expr.span, msg);
334                let supported_tys: Vec<_> =
335                    supported_tys.iter().map(|(t, _)| t.to_string()).collect();
336                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!(
337                    "register class `{}` supports these types: {}",
338                    reg_class.name(),
339                    supported_tys.join(", "),
340                ));
341                err
342            };
343            if let Some(suggest) = reg_class.suggest_class(asm_arch, asm_ty) {
344                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()));
345            }
346            err.emit();
347            return Some(asm_ty);
348        };
349
350        // Check whether the selected type requires a target feature. Note that
351        // this is different from the feature check we did earlier. While the
352        // previous check checked that this register class is usable at all
353        // with the currently enabled features, some types may only be usable
354        // with a register class when a certain feature is enabled. We check
355        // this here since it depends on the results of typeck.
356        //
357        // Also note that this check isn't run when the operand type is never
358        // (!). In that case we still need the earlier check to verify that the
359        // register class is usable at all.
360        if let Some(feature) = feature {
361            if !self.target_features.contains(feature) {
362                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");
363                self.fcx
364                    .dcx()
365                    .struct_span_err(expr.span, msg)
366                    .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!(
367                        "this is required to use type `{}` with register class `{}`",
368                        ty,
369                        reg_class.name(),
370                    ))
371                    .emit();
372                return Some(asm_ty);
373            }
374        }
375
376        // Check whether a modifier is suggested for using this type.
377        if let Some(ModifierInfo {
378            modifier: suggested_modifier,
379            result: suggested_result,
380            size: suggested_size,
381        }) = reg_class.suggest_modifier(asm_arch, asm_ty)
382        {
383            // Search for any use of this operand without a modifier and emit
384            // the suggestion for them.
385            let mut spans = ::alloc::vec::Vec::new()vec![];
386            for piece in template {
387                if let &InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span } = piece
388                {
389                    if operand_idx == idx && modifier.is_none() {
390                        spans.push(span);
391                    }
392                }
393            }
394            if !spans.is_empty() {
395                let ModifierInfo {
396                    modifier: default_modifier,
397                    result: default_result,
398                    size: default_size,
399                } = reg_class.default_modifier(asm_arch).unwrap();
400                self.tcx().emit_node_span_lint(
401                    lint::builtin::ASM_SUB_REGISTER,
402                    expr.hir_id,
403                    spans,
404                    FormattingSubRegisterArg {
405                        expr_span: expr.span,
406                        idx,
407                        suggested_modifier,
408                        suggested_result,
409                        suggested_size,
410                        default_modifier,
411                        default_result,
412                        default_size,
413                    },
414                );
415            }
416        }
417
418        Some(asm_ty)
419    }
420
421    pub(crate) fn check_asm(&self, asm: &hir::InlineAsm<'tcx>) {
422        let Some(asm_arch) = self.tcx().sess.asm_arch else {
423            self.fcx.dcx().delayed_bug("target architecture does not support asm");
424            return;
425        };
426        let allow_experimental_reg = self.tcx().features().asm_experimental_reg();
427        for (idx, &(op, op_sp)) in asm.operands.iter().enumerate() {
428            // Validate register classes against currently enabled target
429            // features. We check that at least one type is available for
430            // the enabled features.
431            //
432            // We ignore target feature requirements for clobbers: if the
433            // feature is disabled then the compiler doesn't care what we
434            // do with the registers.
435            //
436            // Note that this is only possible for explicit register
437            // operands, which cannot be used in the asm string.
438            if let Some(reg) = op.reg() {
439                // Some explicit registers cannot be used depending on the
440                // target. Reject those here.
441                if let InlineAsmRegOrRegClass::Reg(reg) = reg {
442                    if let InlineAsmReg::Err = reg {
443                        // `validate` will panic on `Err`, as an error must
444                        // already have been reported.
445                        continue;
446                    }
447                    if let Err(msg) = reg.validate(
448                        asm_arch,
449                        self.tcx().sess.relocation_model(),
450                        self.target_features,
451                        &self.tcx().sess.target,
452                        op.is_clobber(),
453                    ) {
454                        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);
455                        self.fcx.dcx().span_err(op_sp, msg);
456                        continue;
457                    }
458                }
459
460                if !op.is_clobber() {
461                    let mut missing_required_features = ::alloc::vec::Vec::new()vec![];
462                    let reg_class = reg.reg_class();
463                    if let InlineAsmRegClass::Err = reg_class {
464                        continue;
465                    }
466                    for &(_, feature) in reg_class.supported_types(asm_arch, allow_experimental_reg)
467                    {
468                        match feature {
469                            Some(feature) => {
470                                if self.target_features.contains(&feature) {
471                                    missing_required_features.clear();
472                                    break;
473                                } else {
474                                    missing_required_features.push(feature);
475                                }
476                            }
477                            None => {
478                                missing_required_features.clear();
479                                break;
480                            }
481                        }
482                    }
483
484                    // We are sorting primitive strs here and can use unstable sort here
485                    missing_required_features.sort_unstable();
486                    missing_required_features.dedup();
487                    match &missing_required_features[..] {
488                        [] => {}
489                        [feature] => {
490                            let msg = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("register class `{0}` requires the `{1}` target feature",
                reg_class.name(), feature))
    })format!(
491                                "register class `{}` requires the `{}` target feature",
492                                reg_class.name(),
493                                feature
494                            );
495                            self.fcx.dcx().span_err(op_sp, msg);
496                            // register isn't enabled, don't do more checks
497                            continue;
498                        }
499                        features => {
500                            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!(
501                                "register class `{}` requires at least one of the following target features: {}",
502                                reg_class.name(),
503                                features
504                                    .iter()
505                                    .map(|f| f.as_str())
506                                    .intersperse(", ")
507                                    .collect::<String>(),
508                            );
509                            self.fcx.dcx().span_err(op_sp, msg);
510                            // register isn't enabled, don't do more checks
511                            continue;
512                        }
513                    }
514                }
515            }
516
517            match op {
518                hir::InlineAsmOperand::In { reg, expr } => {
519                    self.check_asm_operand_type(idx, reg, expr, asm.template, true, None);
520                }
521                hir::InlineAsmOperand::Out { reg, late: _, expr } => {
522                    if let Some(expr) = expr {
523                        self.check_asm_operand_type(idx, reg, expr, asm.template, false, None);
524                    }
525                }
526                hir::InlineAsmOperand::InOut { reg, late: _, expr } => {
527                    self.check_asm_operand_type(idx, reg, expr, asm.template, false, None);
528                }
529                hir::InlineAsmOperand::SplitInOut { reg, late: _, in_expr, out_expr } => {
530                    let in_ty =
531                        self.check_asm_operand_type(idx, reg, in_expr, asm.template, true, None);
532                    if let Some(out_expr) = out_expr {
533                        self.check_asm_operand_type(
534                            idx,
535                            reg,
536                            out_expr,
537                            asm.template,
538                            false,
539                            Some((in_expr, in_ty)),
540                        );
541                    }
542                }
543                hir::InlineAsmOperand::Const { anon_const } => {
544                    let ty = self.expr_ty(self.tcx().hir_body(anon_const.body).value);
545                    match ty.kind() {
546                        ty::Error(_) => {}
547                        _ if ty.is_integral() => {}
548                        _ => {
549                            self.fcx
550                                .dcx()
551                                .struct_span_err(op_sp, "invalid type for `const` operand")
552                                .with_span_label(
553                                    self.tcx().def_span(anon_const.def_id),
554                                    ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("is {0} `{1}`", ty.kind().article(),
                ty))
    })format!("is {} `{}`", ty.kind().article(), ty),
555                                )
556                                .with_help("`const` operands must be of an integer type")
557                                .emit();
558                        }
559                    }
560                }
561                // Typeck has checked that SymFn refers to a function.
562                hir::InlineAsmOperand::SymFn { expr } => {
563                    let ty = self.expr_ty(expr);
564                    match ty.kind() {
565                        ty::FnDef(..) => {}
566                        ty::Error(_) => {}
567                        _ => {
568                            self.fcx
569                                .dcx()
570                                .struct_span_err(op_sp, "invalid `sym` operand")
571                                .with_span_label(
572                                    expr.span,
573                                    ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("is {0} `{1}`", ty.kind().article(),
                ty))
    })format!("is {} `{}`", ty.kind().article(), ty),
574                                )
575                                .with_help(
576                                    "`sym` operands must refer to either a function or a static",
577                                )
578                                .emit();
579                        }
580                    }
581                }
582                // AST lowering guarantees that SymStatic points to a static.
583                hir::InlineAsmOperand::SymStatic { .. } => {}
584                // No special checking is needed for labels.
585                hir::InlineAsmOperand::Label { .. } => {}
586            }
587        }
588    }
589}