From c7088b2b95da9fd92df760428a219826edfda490 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 9 Dec 2024 10:37:47 +0000 Subject: [PATCH] Clarify why a type is rejected for asm! --- .../src/check/intrinsicck.rs | 117 +++++++++++------- tests/ui/asm/generic_const_simd_vec_len.rs | 5 +- .../ui/asm/generic_const_simd_vec_len.stderr | 12 +- 3 files changed, 85 insertions(+), 49 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs index c4762b1235867..90e93bdbb5075 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs @@ -3,6 +3,7 @@ use std::assert_matches::debug_assert_matches; use rustc_abi::FieldIdx; use rustc_ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::FxIndexSet; +use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, LangItem}; use rustc_middle::bug; use rustc_middle::ty::{self, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, UintTy}; @@ -21,6 +22,12 @@ pub struct InlineAsmCtxt<'a, 'tcx> { get_operand_ty: Box) -> Ty<'tcx> + 'a>, } +enum NonAsmTypeReason<'tcx> { + UnevaluatedSIMDArrayLength(DefId, ty::Const<'tcx>), + Invalid(Ty<'tcx>), + InvalidElement(DefId, Ty<'tcx>), +} + impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { pub fn new_global_asm(tcx: TyCtxt<'tcx>) -> Self { InlineAsmCtxt { @@ -56,7 +63,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { false } - fn get_asm_ty(&self, ty: Ty<'tcx>) -> Option { + fn get_asm_ty(&self, ty: Ty<'tcx>) -> Result> { let asm_ty_isize = match self.tcx.sess.target.pointer_width { 16 => InlineAsmType::I16, 32 => InlineAsmType::I32, @@ -65,21 +72,22 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { }; match *ty.kind() { - ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Some(InlineAsmType::I8), - ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Some(InlineAsmType::I16), - ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Some(InlineAsmType::I32), - ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Some(InlineAsmType::I64), - ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => Some(InlineAsmType::I128), - ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Some(asm_ty_isize), - ty::Float(FloatTy::F16) => Some(InlineAsmType::F16), - ty::Float(FloatTy::F32) => Some(InlineAsmType::F32), - ty::Float(FloatTy::F64) => Some(InlineAsmType::F64), - ty::Float(FloatTy::F128) => Some(InlineAsmType::F128), - ty::FnPtr(..) => Some(asm_ty_isize), - ty::RawPtr(ty, _) if self.is_thin_ptr_ty(ty) => Some(asm_ty_isize), + ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Ok(InlineAsmType::I8), + ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Ok(InlineAsmType::I16), + ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Ok(InlineAsmType::I32), + ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Ok(InlineAsmType::I64), + ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => Ok(InlineAsmType::I128), + ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Ok(asm_ty_isize), + ty::Float(FloatTy::F16) => Ok(InlineAsmType::F16), + ty::Float(FloatTy::F32) => Ok(InlineAsmType::F32), + ty::Float(FloatTy::F64) => Ok(InlineAsmType::F64), + ty::Float(FloatTy::F128) => Ok(InlineAsmType::F128), + ty::FnPtr(..) => Ok(asm_ty_isize), + ty::RawPtr(ty, _) if self.is_thin_ptr_ty(ty) => Ok(asm_ty_isize), ty::Adt(adt, args) if adt.repr().simd() => { let fields = &adt.non_enum_variant().fields; - let elem_ty = fields[FieldIdx::ZERO].ty(self.tcx, args); + let field = &fields[FieldIdx::ZERO]; + let elem_ty = field.ty(self.tcx, args); let (size, ty) = match elem_ty.kind() { ty::Array(ty, len) => { @@ -87,43 +95,39 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { if let Some(len) = len.try_to_target_usize(self.tcx) { (len, *ty) } else { - return None; + return Err(NonAsmTypeReason::UnevaluatedSIMDArrayLength( + field.did, len, + )); } } _ => (fields.len() as u64, elem_ty), }; match ty.kind() { - ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Some(InlineAsmType::VecI8(size)), - ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => { - Some(InlineAsmType::VecI16(size)) - } - ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => { - Some(InlineAsmType::VecI32(size)) - } - ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => { - Some(InlineAsmType::VecI64(size)) - } + ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Ok(InlineAsmType::VecI8(size)), + ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Ok(InlineAsmType::VecI16(size)), + ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Ok(InlineAsmType::VecI32(size)), + ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Ok(InlineAsmType::VecI64(size)), ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => { - Some(InlineAsmType::VecI128(size)) + Ok(InlineAsmType::VecI128(size)) } ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => { - Some(match self.tcx.sess.target.pointer_width { + Ok(match self.tcx.sess.target.pointer_width { 16 => InlineAsmType::VecI16(size), 32 => InlineAsmType::VecI32(size), 64 => InlineAsmType::VecI64(size), width => bug!("unsupported pointer width: {width}"), }) } - ty::Float(FloatTy::F16) => Some(InlineAsmType::VecF16(size)), - ty::Float(FloatTy::F32) => Some(InlineAsmType::VecF32(size)), - ty::Float(FloatTy::F64) => Some(InlineAsmType::VecF64(size)), - ty::Float(FloatTy::F128) => Some(InlineAsmType::VecF128(size)), - _ => None, + ty::Float(FloatTy::F16) => Ok(InlineAsmType::VecF16(size)), + ty::Float(FloatTy::F32) => Ok(InlineAsmType::VecF32(size)), + ty::Float(FloatTy::F64) => Ok(InlineAsmType::VecF64(size)), + ty::Float(FloatTy::F128) => Ok(InlineAsmType::VecF128(size)), + _ => Err(NonAsmTypeReason::InvalidElement(field.did, ty)), } } ty::Infer(_) => bug!("unexpected infer ty in asm operand"), - _ => None, + _ => Err(NonAsmTypeReason::Invalid(ty)), } } @@ -164,17 +168,42 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { } _ => self.get_asm_ty(ty), }; - let Some(asm_ty) = asm_ty else { - let msg = format!("cannot use value of type `{ty}` for inline assembly"); - self.tcx - .dcx() - .struct_span_err(expr.span, msg) - .with_note( - "only integers, floats, SIMD vectors, pointers and function pointers \ - can be used as arguments for inline assembly", - ) - .emit(); - return None; + let asm_ty = match asm_ty { + Ok(asm_ty) => asm_ty, + Err(reason) => { + match reason { + NonAsmTypeReason::UnevaluatedSIMDArrayLength(did, len) => { + let msg = format!("cannot evaluate SIMD vector length `{len}`"); + self.tcx + .dcx() + .struct_span_err(self.tcx.def_span(did), msg) + .with_span_note( + expr.span, + "SIMD vector length needs to be known statically for use in `asm!`", + ) + .emit(); + } + NonAsmTypeReason::Invalid(ty) => { + let msg = format!("cannot use value of type `{ty}` for inline assembly"); + self.tcx.dcx().struct_span_err(expr.span, msg).with_note( + "only integers, floats, SIMD vectors, pointers and function pointers \ + can be used as arguments for inline assembly", + ).emit(); + } + NonAsmTypeReason::InvalidElement(did, ty) => { + let msg = format!( + "cannot use SIMD vector with element type `{ty}` for inline assembly" + ); + self.tcx.dcx() + .struct_span_err(self.tcx.def_span(did), msg).with_span_note( + expr.span, + "only integers, floats, SIMD vectors, pointers and function pointers \ + can be used as arguments for inline assembly", + ).emit(); + } + } + return None; + } }; // Check that the type implements Copy. The only case where this can diff --git a/tests/ui/asm/generic_const_simd_vec_len.rs b/tests/ui/asm/generic_const_simd_vec_len.rs index 4ef839e381674..fb8c5274ddb82 100644 --- a/tests/ui/asm/generic_const_simd_vec_len.rs +++ b/tests/ui/asm/generic_const_simd_vec_len.rs @@ -1,17 +1,20 @@ //! This is a regression test to ensure that we emit a diagnostic pointing to the //! reason the type was rejected in inline assembly. +//@ only-x86_64 + #![feature(repr_simd)] #[repr(simd)] #[derive(Copy, Clone)] pub struct Foo([u8; C]); +//~^ ERROR: cannot evaluate SIMD vector length pub unsafe fn foo(a: Foo) { std::arch::asm!( "movaps {src}, {src}", src = in(xmm_reg) a, - //~^ ERROR: cannot use value of type `Foo` for inline assembly + //~^ NOTE: SIMD vector length needs to be known statically ); } diff --git a/tests/ui/asm/generic_const_simd_vec_len.stderr b/tests/ui/asm/generic_const_simd_vec_len.stderr index d0044d144adcb..486281b6062d9 100644 --- a/tests/ui/asm/generic_const_simd_vec_len.stderr +++ b/tests/ui/asm/generic_const_simd_vec_len.stderr @@ -1,10 +1,14 @@ -error: cannot use value of type `Foo` for inline assembly - --> $DIR/generic_const_simd_vec_len.rs:13:27 +error: cannot evaluate SIMD vector length `C` + --> $DIR/generic_const_simd_vec_len.rs:10:32 + | +LL | pub struct Foo([u8; C]); + | ^^^^^^^ + | +note: SIMD vector length needs to be known statically for use in `asm!` + --> $DIR/generic_const_simd_vec_len.rs:16:27 | LL | src = in(xmm_reg) a, | ^ - | - = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly error: aborting due to 1 previous error