Skip to main content

rustc_hir_typeck/
inline_asm.rs

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