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