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 as hir;
6use rustc_hir::attrs::lang_items::LangItem;
7use rustc_hir::def_id::DefId;
8use rustc_middle::bug;
9use rustc_middle::ty::{
10    self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, UintTy, Unnormalized,
11};
12use rustc_session::lint;
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    // FIXME(compiler-errors): This could use `<$ty as Pointee>::Metadata == ()`
57    fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool {
58        // Type still may have region variables, but `Sized` does not depend
59        // on those, so just erase them before querying.
60        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                        // FIXME: `try_structurally_resolve_const` doesn't eval consts
119                        // in the old solver.
120                        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            // `!` is allowed for input but not for output (issue #87802)
216            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                        // An error has already been reported.
273                    }
274                }
275                return None;
276            }
277        };
278
279        // Check that the type implements Copy. The only case where this can
280        // possibly fail is for SIMD types which don't #[derive(Copy)].
281        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        // Ideally we wouldn't need to do this, but LLVM's register allocator
291        // really doesn't like it when tied operands have different types.
292        //
293        // This is purely an LLVM limitation, but we have to live with it since
294        // there is no way to hide this with implicit conversions.
295        //
296        // For the purposes of this check we only look at the `InlineAsmType`,
297        // which means that pointers and integers are treated as identical (modulo
298        // size).
299        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            // All of the later checks have already been done on the input, so
316            // let's not emit errors and warnings twice.
317            return Some(asm_ty);
318        }
319
320        // Check the type against the list of types supported by the selected
321        // register class.
322        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        // Check whether the selected type requires a target feature. Note that
354        // this is different from the feature check we did earlier. While the
355        // previous check checked that this register class is usable at all
356        // with the currently enabled features, some types may only be usable
357        // with a register class when a certain feature is enabled. We check
358        // this here since it depends on the results of typeck.
359        //
360        // Also note that this check isn't run when the operand type is never
361        // (!). In that case we still need the earlier check to verify that the
362        // register class is usable at all.
363        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        // Check whether a modifier is suggested for using this type.
380        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            // Search for any use of this operand without a modifier and emit
387            // the suggestion for them.
388            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                    lint::builtin::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            // Validate register classes against currently enabled target
432            // features. We check that at least one type is available for
433            // the enabled features.
434            //
435            // We ignore target feature requirements for clobbers: if the
436            // feature is disabled then the compiler doesn't care what we
437            // do with the registers.
438            //
439            // Note that this is only possible for explicit register
440            // operands, which cannot be used in the asm string.
441            if let Some(reg) = op.reg() {
442                // Some explicit registers cannot be used depending on the
443                // target. Reject those here.
444                if let InlineAsmRegOrRegClass::Reg(reg) = reg {
445                    if let InlineAsmReg::Err = reg {
446                        // `validate` will panic on `Err`, as an error must
447                        // already have been reported.
448                        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                    // We are sorting primitive strs here and can use unstable sort here
489                    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                            // register isn't enabled, don't do more checks
501                            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                            // register isn't enabled, don't do more checks
515                            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                // Typeck has checked that SymFn refers to a function.
597                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                // AST lowering guarantees that SymStatic points to a static.
618                hir::InlineAsmOperand::SymStatic { .. } => {}
619                // No special checking is needed for labels.
620                hir::InlineAsmOperand::Label { .. } => {}
621            }
622        }
623    }
624}