Pass InferCtxt to InlineAsmCtxt to properly taint on error

Split up some of the tests bc tainting causes some errors to become
suppressed
This commit is contained in:
Michael Goulet 2025-03-06 22:01:01 +00:00
parent 2b285cd5f0
commit bc4f0bb486
8 changed files with 109 additions and 79 deletions

View File

@ -3,8 +3,11 @@ 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_infer::infer::InferCtxt;
use rustc_middle::bug;
use rustc_middle::ty::{self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, UintTy};
use rustc_middle::ty::{
self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, TypeckResults, UintTy,
};
use rustc_session::lint;
use rustc_span::def_id::LocalDefId;
use rustc_span::{Symbol, sym};
@ -14,12 +17,11 @@ use rustc_target::asm::{
use crate::errors::RegisterTypeUnstable;
pub struct InlineAsmCtxt<'a, 'tcx: 'a> {
tcx: TyCtxt<'tcx>,
pub struct InlineAsmCtxt<'a, 'tcx> {
typing_env: ty::TypingEnv<'tcx>,
target_features: &'tcx FxIndexSet<Symbol>,
expr_ty: Box<dyn Fn(&hir::Expr<'tcx>) -> Ty<'tcx> + 'a>,
node_ty: Box<dyn Fn(hir::HirId) -> Ty<'tcx> + 'a>,
infcx: &'a InferCtxt<'tcx>,
typeck_results: &'a TypeckResults<'tcx>,
}
enum NonAsmTypeReason<'tcx> {
@ -31,34 +33,38 @@ enum NonAsmTypeReason<'tcx> {
impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
pub fn new(
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
infcx: &'a InferCtxt<'tcx>,
typing_env: ty::TypingEnv<'tcx>,
expr_ty: impl Fn(&hir::Expr<'tcx>) -> Ty<'tcx> + 'a,
node_ty: impl Fn(hir::HirId) -> Ty<'tcx> + 'a,
typeck_results: &'a TypeckResults<'tcx>,
) -> Self {
InlineAsmCtxt {
tcx,
typing_env,
target_features: tcx.asm_target_features(def_id),
expr_ty: Box::new(expr_ty),
node_ty: Box::new(node_ty),
target_features: infcx.tcx.asm_target_features(def_id),
infcx,
typeck_results,
}
}
fn expr_ty(&self, expr: &hir::Expr<'tcx>) -> Ty<'tcx> {
(self.expr_ty)(expr)
fn tcx(&self) -> TyCtxt<'tcx> {
self.infcx.tcx
}
fn node_ty(&self, hir_id: hir::HirId) -> Ty<'tcx> {
(self.node_ty)(hir_id)
fn expr_ty(&self, expr: &hir::Expr<'tcx>) -> Ty<'tcx> {
let ty = self.typeck_results.expr_ty_adjusted(expr);
let ty = self.infcx.resolve_vars_if_possible(ty);
if ty.has_non_region_infer() {
Ty::new_misc_error(self.tcx())
} else {
self.tcx().erase_regions(ty)
}
}
// FIXME(compiler-errors): This could use `<$ty as Pointee>::Metadata == ()`
fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool {
// Type still may have region variables, but `Sized` does not depend
// on those, so just erase them before querying.
if ty.is_sized(self.tcx, self.typing_env) {
if ty.is_sized(self.tcx(), self.typing_env) {
return true;
}
if let ty::Foreign(..) = ty.kind() {
@ -68,7 +74,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
}
fn get_asm_ty(&self, ty: Ty<'tcx>) -> Result<InlineAsmType, NonAsmTypeReason<'tcx>> {
let asm_ty_isize = match self.tcx.sess.target.pointer_width {
let asm_ty_isize = match self.tcx().sess.target.pointer_width {
16 => InlineAsmType::I16,
32 => InlineAsmType::I32,
64 => InlineAsmType::I64,
@ -97,12 +103,12 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
ty::Adt(adt, args) if adt.repr().simd() => {
let fields = &adt.non_enum_variant().fields;
let field = &fields[FieldIdx::ZERO];
let elem_ty = field.ty(self.tcx, args);
let elem_ty = field.ty(self.tcx(), args);
let (size, ty) = match elem_ty.kind() {
ty::Array(ty, len) => {
let len = self.tcx.normalize_erasing_regions(self.typing_env, *len);
if let Some(len) = len.try_to_target_usize(self.tcx) {
let len = self.tcx().normalize_erasing_regions(self.typing_env, *len);
if let Some(len) = len.try_to_target_usize(self.tcx()) {
(len, *ty)
} else {
return Err(NonAsmTypeReason::UnevaluatedSIMDArrayLength(
@ -122,7 +128,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
Ok(InlineAsmType::VecI128(size))
}
ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => {
Ok(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),
@ -159,9 +165,9 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
// `!` is allowed for input but not for output (issue #87802)
ty::Never if is_input => return None,
_ if ty.references_error() => return None,
ty::Adt(adt, args) if self.tcx.is_lang_item(adt.did(), LangItem::MaybeUninit) => {
ty::Adt(adt, args) if self.tcx().is_lang_item(adt.did(), LangItem::MaybeUninit) => {
let fields = &adt.non_enum_variant().fields;
let ty = fields[FieldIdx::from_u32(1)].ty(self.tcx, args);
let ty = fields[FieldIdx::from_u32(1)].ty(self.tcx(), args);
// FIXME: Are we just trying to map to the `T` in `MaybeUninit<T>`?
// If so, just get it from the args.
let ty::Adt(ty, args) = ty.kind() else {
@ -172,7 +178,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
"expected first field of `MaybeUninit` to be `ManuallyDrop`"
);
let fields = &ty.non_enum_variant().fields;
let ty = fields[FieldIdx::ZERO].ty(self.tcx, args);
let ty = fields[FieldIdx::ZERO].ty(self.tcx(), args);
self.get_asm_ty(ty)
}
_ => self.get_asm_ty(ty),
@ -183,9 +189,9 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
match reason {
NonAsmTypeReason::UnevaluatedSIMDArrayLength(did, len) => {
let msg = format!("cannot evaluate SIMD vector length `{len}`");
self.tcx
self.infcx
.dcx()
.struct_span_err(self.tcx.def_span(did), msg)
.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!`",
@ -194,7 +200,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
}
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(
self.infcx.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();
@ -203,7 +209,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
let msg = format!(
"cannot use value of unsized pointer type `{ty}` for inline assembly"
);
self.tcx
self.infcx
.dcx()
.struct_span_err(expr.span, msg)
.with_note("only sized pointers can be used in inline assembly")
@ -213,8 +219,8 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
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(
self.infcx.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",
@ -227,9 +233,9 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
// Check that the type implements Copy. The only case where this can
// possibly fail is for SIMD types which don't #[derive(Copy)].
if !self.tcx.type_is_copy_modulo_regions(self.typing_env, ty) {
if !self.tcx().type_is_copy_modulo_regions(self.typing_env, ty) {
let msg = "arguments for inline assembly must be copyable";
self.tcx
self.infcx
.dcx()
.struct_span_err(expr.span, msg)
.with_note(format!("`{ty}` does not implement the Copy trait"))
@ -249,7 +255,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
if in_asm_ty != asm_ty {
let msg = "incompatible types for asm inout argument";
let in_expr_ty = self.expr_ty(in_expr);
self.tcx
self.infcx
.dcx()
.struct_span_err(vec![in_expr.span, expr.span], msg)
.with_span_label(in_expr.span, format!("type `{in_expr_ty}`"))
@ -268,21 +274,21 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
// Check the type against the list of types supported by the selected
// register class.
let asm_arch = self.tcx.sess.asm_arch.unwrap();
let allow_experimental_reg = self.tcx.features().asm_experimental_reg();
let asm_arch = self.tcx().sess.asm_arch.unwrap();
let allow_experimental_reg = self.tcx().features().asm_experimental_reg();
let reg_class = reg.reg_class();
let supported_tys = reg_class.supported_types(asm_arch, allow_experimental_reg);
let Some((_, feature)) = supported_tys.iter().find(|&&(t, _)| t == asm_ty) else {
let mut err = if !allow_experimental_reg
&& reg_class.supported_types(asm_arch, true).iter().any(|&(t, _)| t == asm_ty)
{
self.tcx.sess.create_feature_err(
self.tcx().sess.create_feature_err(
RegisterTypeUnstable { span: expr.span, ty },
sym::asm_experimental_reg,
)
} else {
let msg = format!("type `{ty}` cannot be used with this register class");
let mut err = self.tcx.dcx().struct_span_err(expr.span, msg);
let mut err = self.infcx.dcx().struct_span_err(expr.span, msg);
let supported_tys: Vec<_> =
supported_tys.iter().map(|(t, _)| t.to_string()).collect();
err.note(format!(
@ -312,7 +318,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
if let Some(feature) = feature {
if !self.target_features.contains(feature) {
let msg = format!("`{feature}` target feature is not enabled");
self.tcx
self.infcx
.dcx()
.struct_span_err(expr.span, msg)
.with_note(format!(
@ -349,7 +355,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
result: default_result,
size: default_size,
} = reg_class.default_modifier(asm_arch).unwrap();
self.tcx.node_span_lint(
self.tcx().node_span_lint(
lint::builtin::ASM_SUB_REGISTER,
expr.hir_id,
spans,
@ -371,11 +377,11 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
}
pub fn check_asm(&self, asm: &hir::InlineAsm<'tcx>) {
let Some(asm_arch) = self.tcx.sess.asm_arch else {
self.tcx.dcx().delayed_bug("target architecture does not support asm");
let Some(asm_arch) = self.tcx().sess.asm_arch else {
self.infcx.dcx().delayed_bug("target architecture does not support asm");
return;
};
let allow_experimental_reg = self.tcx.features().asm_experimental_reg();
let allow_experimental_reg = self.tcx().features().asm_experimental_reg();
for (idx, &(op, op_sp)) in asm.operands.iter().enumerate() {
// Validate register classes against currently enabled target
// features. We check that at least one type is available for
@ -398,13 +404,13 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
}
if let Err(msg) = reg.validate(
asm_arch,
self.tcx.sess.relocation_model(),
self.tcx().sess.relocation_model(),
self.target_features,
&self.tcx.sess.target,
&self.tcx().sess.target,
op.is_clobber(),
) {
let msg = format!("cannot use register `{}`: {}", reg.name(), msg);
self.tcx.dcx().span_err(op_sp, msg);
self.infcx.dcx().span_err(op_sp, msg);
continue;
}
}
@ -444,7 +450,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
reg_class.name(),
feature
);
self.tcx.dcx().span_err(op_sp, msg);
self.infcx.dcx().span_err(op_sp, msg);
// register isn't enabled, don't do more checks
continue;
}
@ -458,7 +464,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
.intersperse(", ")
.collect::<String>(),
);
self.tcx.dcx().span_err(op_sp, msg);
self.infcx.dcx().span_err(op_sp, msg);
// register isn't enabled, don't do more checks
continue;
}
@ -493,16 +499,16 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
}
}
hir::InlineAsmOperand::Const { anon_const } => {
let ty = self.node_ty(anon_const.hir_id);
let ty = self.expr_ty(self.tcx().hir_body(anon_const.body).value);
match ty.kind() {
ty::Error(_) => {}
_ if ty.is_integral() => {}
_ => {
self.tcx
self.infcx
.dcx()
.struct_span_err(op_sp, "invalid type for `const` operand")
.with_span_label(
self.tcx.def_span(anon_const.def_id),
self.tcx().def_span(anon_const.def_id),
format!("is {} `{}`", ty.kind().article(), ty),
)
.with_help("`const` operands must be of an integer type")
@ -517,7 +523,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
ty::FnDef(..) => {}
ty::Error(_) => {}
_ => {
self.tcx
self.infcx
.dcx()
.struct_span_err(op_sp, "invalid `sym` operand")
.with_span_label(

View File

@ -99,22 +99,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
debug!("FnCtxt::check_asm: {} deferred checks", deferred_asm_checks.len());
for (asm, hir_id) in deferred_asm_checks.drain(..) {
let enclosing_id = self.tcx.hir_enclosing_body_owner(hir_id);
let expr_ty = |expr: &hir::Expr<'tcx>| {
let ty = self.typeck_results.borrow().expr_ty_adjusted(expr);
let ty = self.resolve_vars_if_possible(ty);
if ty.has_non_region_infer() {
Ty::new_misc_error(self.tcx)
} else {
self.tcx.erase_regions(ty)
}
};
let node_ty = |hir_id: HirId| self.typeck_results.borrow().node_type(hir_id);
InlineAsmCtxt::new(
self.tcx,
enclosing_id,
&self.infcx,
self.typing_env(self.param_env),
expr_ty,
node_ty,
&*self.typeck_results.borrow(),
)
.check_asm(asm);
}

View File

@ -14,7 +14,7 @@ global_asm!("{}", const 0f32);
global_asm!("{}", const 0 as *mut u8);
//~^ ERROR invalid type for `const` operand
fn main() {
fn test1() {
unsafe {
// Const operands must be integers and must be constants.
@ -27,7 +27,11 @@ fn main() {
//~^ ERROR invalid type for `const` operand
asm!("{}", const &0);
//~^ ERROR invalid type for `const` operand
}
}
fn test2() {
unsafe {
// Constants must be... constant
let x = 0;
@ -47,3 +51,5 @@ fn main() {
//~^ ERROR attempt to use a non-constant value in a constant
}
}
fn main() {}

View File

@ -1,5 +1,5 @@
error[E0435]: attempt to use a non-constant value in a constant
--> $DIR/invalid-const-operand.rs:40:26
--> $DIR/invalid-const-operand.rs:44:26
|
LL | asm!("{}", const x);
| ^ non-constant value
@ -11,7 +11,7 @@ LL + const x: /* Type */ = 0;
|
error[E0435]: attempt to use a non-constant value in a constant
--> $DIR/invalid-const-operand.rs:43:36
--> $DIR/invalid-const-operand.rs:47:36
|
LL | asm!("{}", const const_foo(x));
| ^ non-constant value
@ -23,7 +23,7 @@ LL + const x: /* Type */ = 0;
|
error[E0435]: attempt to use a non-constant value in a constant
--> $DIR/invalid-const-operand.rs:46:36
--> $DIR/invalid-const-operand.rs:50:36
|
LL | asm!("{}", const const_bar(x));
| ^ non-constant value
@ -80,7 +80,7 @@ error: invalid type for `const` operand
LL | asm!("{}", const &0);
| ^^^^^^--
| |
| is a `&{integer}`
| is a `&i32`
|
= help: `const` operands must be of an integer type

View File

@ -0,0 +1,13 @@
//@ needs-asm-support
use std::arch::asm;
fn main() {
unsafe {
asm!(
"/* {} */",
sym None::<()>,
//~^ ERROR invalid `sym` operand
);
}
}

View File

@ -0,0 +1,10 @@
error: invalid `sym` operand
--> $DIR/tainting-on-error.rs:9:13
|
LL | sym None::<()>,
| ^^^^^^^^^^^^^^ is an `Option<()>`
|
= help: `sym` operands must refer to either a function or a static
error: aborting due to 1 previous error

View File

@ -7,7 +7,7 @@ use std::arch::{asm, global_asm};
#[repr(simd)]
struct SimdNonCopy([f32; 4]);
fn main() {
fn test1() {
unsafe {
// Inputs must be initialized
@ -26,7 +26,11 @@ fn main() {
asm!("{}", in(reg) v[0]);
asm!("{}", out(reg) v[0]);
asm!("{}", inout(reg) v[0]);
}
}
fn test2() {
unsafe {
// Register operands must be Copy
asm!("{}", in(xmm_reg) SimdNonCopy([0.0, 0.0, 0.0, 0.0]));
@ -68,3 +72,5 @@ fn main() {
asm!("{}", in(reg) u);
}
}
fn main() {}

View File

@ -1,13 +1,13 @@
error: arguments for inline assembly must be copyable
--> $DIR/type-check-2.rs:32:32
--> $DIR/type-check-2.rs:36:32
|
LL | asm!("{}", in(xmm_reg) SimdNonCopy([0.0, 0.0, 0.0, 0.0]));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `SimdNonCopy` does not implement the Copy trait
error: cannot use value of type `{closure@$DIR/type-check-2.rs:44:28: 44:36}` for inline assembly
--> $DIR/type-check-2.rs:44:28
error: cannot use value of type `{closure@$DIR/type-check-2.rs:48:28: 48:36}` for inline assembly
--> $DIR/type-check-2.rs:48:28
|
LL | asm!("{}", in(reg) |x: i32| x);
| ^^^^^^^^^^
@ -15,7 +15,7 @@ LL | asm!("{}", in(reg) |x: i32| x);
= note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly
error: cannot use value of type `Vec<i32>` for inline assembly
--> $DIR/type-check-2.rs:46:28
--> $DIR/type-check-2.rs:50:28
|
LL | asm!("{}", in(reg) vec![0]);
| ^^^^^^^
@ -24,7 +24,7 @@ LL | asm!("{}", in(reg) vec![0]);
= note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
error: cannot use value of type `(i32, i32, i32)` for inline assembly
--> $DIR/type-check-2.rs:48:28
--> $DIR/type-check-2.rs:52:28
|
LL | asm!("{}", in(reg) (1, 2, 3));
| ^^^^^^^^^
@ -32,7 +32,7 @@ LL | asm!("{}", in(reg) (1, 2, 3));
= note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly
error: cannot use value of type `[i32; 3]` for inline assembly
--> $DIR/type-check-2.rs:50:28
--> $DIR/type-check-2.rs:54:28
|
LL | asm!("{}", in(reg) [1, 2, 3]);
| ^^^^^^^^^
@ -40,7 +40,7 @@ LL | asm!("{}", in(reg) [1, 2, 3]);
= note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly
error: cannot use value of type `fn() {main}` for inline assembly
--> $DIR/type-check-2.rs:58:31
--> $DIR/type-check-2.rs:62:31
|
LL | asm!("{}", inout(reg) f);
| ^
@ -48,7 +48,7 @@ LL | asm!("{}", inout(reg) f);
= note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly
error: cannot use value of type `&mut i32` for inline assembly
--> $DIR/type-check-2.rs:61:31
--> $DIR/type-check-2.rs:65:31
|
LL | asm!("{}", inout(reg) r);
| ^