HIR passes for asm!
This commit is contained in:
parent
ec1ad61f88
commit
10510b5820
9 changed files with 469 additions and 13 deletions
|
@ -180,6 +180,8 @@ pub enum ObligationCauseCode<'tcx> {
|
|||
SizedReturnType,
|
||||
/// Yield type must be `Sized`.
|
||||
SizedYieldType,
|
||||
/// Inline asm operand type must be `Sized`.
|
||||
InlineAsmSized,
|
||||
/// `[T, ..n]` implies that `T` must be `Copy`.
|
||||
/// If `true`, suggest `const_in_array_repeat_expressions` feature flag.
|
||||
RepeatVec(bool),
|
||||
|
|
|
@ -152,6 +152,7 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> {
|
|||
super::SizedArgumentType => Some(super::SizedArgumentType),
|
||||
super::SizedReturnType => Some(super::SizedReturnType),
|
||||
super::SizedYieldType => Some(super::SizedYieldType),
|
||||
super::InlineAsmSized => Some(super::InlineAsmSized),
|
||||
super::RepeatVec(suggest_flag) => Some(super::RepeatVec(suggest_flag)),
|
||||
super::FieldSized { adt_kind, last } => Some(super::FieldSized { adt_kind, last }),
|
||||
super::ConstSized => Some(super::ConstSized),
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use rustc_ast::ast::{FloatTy, IntTy, UintTy};
|
||||
use rustc_errors::struct_span_err;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
|
@ -7,8 +8,10 @@ use rustc_index::vec::Idx;
|
|||
use rustc_middle::ty::layout::{LayoutError, SizeSkeleton};
|
||||
use rustc_middle::ty::query::Providers;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_session::lint;
|
||||
use rustc_span::{sym, Span, Symbol, DUMMY_SP};
|
||||
use rustc_target::abi::{Pointer, VariantIdx};
|
||||
use rustc_target::asm::{InlineAsmRegOrRegClass, InlineAsmTemplatePiece, InlineAsmType};
|
||||
use rustc_target::spec::abi::Abi::RustIntrinsic;
|
||||
|
||||
fn check_mod_intrinsics(tcx: TyCtxt<'_>, module_def_id: DefId) {
|
||||
|
@ -119,6 +122,262 @@ impl ExprVisitor<'tcx> {
|
|||
}
|
||||
err.emit()
|
||||
}
|
||||
|
||||
fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool {
|
||||
if ty.is_sized(self.tcx.at(DUMMY_SP), self.param_env) {
|
||||
return true;
|
||||
}
|
||||
if let ty::Foreign(..) = ty.kind {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn check_asm_operand_type(
|
||||
&self,
|
||||
idx: usize,
|
||||
reg: InlineAsmRegOrRegClass,
|
||||
expr: &hir::Expr<'tcx>,
|
||||
template: &[InlineAsmTemplatePiece],
|
||||
tied_input: Option<(&hir::Expr<'tcx>, Option<InlineAsmType>)>,
|
||||
) -> Option<InlineAsmType> {
|
||||
// Check the type against the allowed types for inline asm.
|
||||
let ty = self.tables.expr_ty_adjusted(expr);
|
||||
let asm_ty_isize = match self.tcx.sess.target.ptr_width {
|
||||
16 => InlineAsmType::I16,
|
||||
32 => InlineAsmType::I32,
|
||||
64 => InlineAsmType::I64,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let asm_ty = match ty.kind {
|
||||
ty::Never | ty::Error => return None,
|
||||
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::F32) => Some(InlineAsmType::F32),
|
||||
ty::Float(FloatTy::F64) => Some(InlineAsmType::F64),
|
||||
ty::FnPtr(_) => Some(asm_ty_isize),
|
||||
ty::RawPtr(ty::TypeAndMut { ty, mutbl: _ }) if self.is_thin_ptr_ty(ty) => {
|
||||
Some(asm_ty_isize)
|
||||
}
|
||||
ty::Adt(adt, substs) if adt.repr.simd() => {
|
||||
let fields = &adt.non_enum_variant().fields;
|
||||
let elem_ty = fields[0].ty(self.tcx, substs);
|
||||
match elem_ty.kind {
|
||||
ty::Never | ty::Error => return None,
|
||||
ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => {
|
||||
Some(InlineAsmType::VecI8(fields.len() as u64))
|
||||
}
|
||||
ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => {
|
||||
Some(InlineAsmType::VecI16(fields.len() as u64))
|
||||
}
|
||||
ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => {
|
||||
Some(InlineAsmType::VecI32(fields.len() as u64))
|
||||
}
|
||||
ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => {
|
||||
Some(InlineAsmType::VecI64(fields.len() as u64))
|
||||
}
|
||||
ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => {
|
||||
Some(InlineAsmType::VecI128(fields.len() as u64))
|
||||
}
|
||||
ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => {
|
||||
Some(match self.tcx.sess.target.ptr_width {
|
||||
16 => InlineAsmType::VecI16(fields.len() as u64),
|
||||
32 => InlineAsmType::VecI32(fields.len() as u64),
|
||||
64 => InlineAsmType::VecI64(fields.len() as u64),
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
ty::Float(FloatTy::F32) => Some(InlineAsmType::VecF32(fields.len() as u64)),
|
||||
ty::Float(FloatTy::F64) => Some(InlineAsmType::VecF64(fields.len() as u64)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let asm_ty = match asm_ty {
|
||||
Some(asm_ty) => asm_ty,
|
||||
None => {
|
||||
let msg = &format!("cannot use value of type `{}` for inline assembly", ty);
|
||||
let mut err = self.tcx.sess.struct_span_err(expr.span, msg);
|
||||
err.note(
|
||||
"only integers, floats, SIMD vectors, pointers and function pointers \
|
||||
can be used as arguments for inline assembly",
|
||||
);
|
||||
err.emit();
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
// Check that the type implements Copy. The only case where this can
|
||||
// possibly fail is for SIMD types which don't #[derive(Copy)].
|
||||
if !ty.is_copy_modulo_regions(self.tcx, self.param_env, DUMMY_SP) {
|
||||
let msg = "arguments for inline assembly must be copyable";
|
||||
let mut err = self.tcx.sess.struct_span_err(expr.span, msg);
|
||||
err.note(&format!("`{}` does not implement the Copy trait", ty));
|
||||
err.emit();
|
||||
}
|
||||
|
||||
// Ideally we wouldn't need to do this, but LLVM's register allocator
|
||||
// really doesn't like it when tied operands have different types.
|
||||
//
|
||||
// This is purely an LLVM limitation, but we have to live with it since
|
||||
// there is no way to hide this with implicit conversions.
|
||||
//
|
||||
// For the purposes of this check we only look at the `InlineAsmType`,
|
||||
// which means that pointers and integers are treated as identical (modulo
|
||||
// size).
|
||||
if let Some((in_expr, Some(in_asm_ty))) = tied_input {
|
||||
if in_asm_ty != asm_ty {
|
||||
let msg = &format!("incompatible types for asm inout argument");
|
||||
let mut err = self.tcx.sess.struct_span_err(vec![in_expr.span, expr.span], msg);
|
||||
err.span_label(
|
||||
in_expr.span,
|
||||
&format!("type `{}`", self.tables.expr_ty_adjusted(in_expr)),
|
||||
);
|
||||
err.span_label(expr.span, &format!("type `{}`", ty));
|
||||
err.note("asm inout arguments must have the same type");
|
||||
err.note("unless they are both pointers or integers of the same size");
|
||||
err.emit();
|
||||
}
|
||||
|
||||
// All of the later checks have already been done on the input, so
|
||||
// let's not emit errors and warnings twice.
|
||||
return Some(asm_ty);
|
||||
}
|
||||
|
||||
// Check the type against the list of types supported by the selected
|
||||
// register class.
|
||||
let asm_arch = self.tcx.sess.asm_arch.unwrap();
|
||||
let reg_class = reg.reg_class();
|
||||
let supported_tys = reg_class.supported_types(asm_arch);
|
||||
let feature = match supported_tys.iter().find(|&&(t, _)| t == asm_ty) {
|
||||
Some((_, feature)) => feature,
|
||||
None => {
|
||||
let msg = &format!("type `{}` cannot be used with this register class", ty);
|
||||
let mut err = self.tcx.sess.struct_span_err(expr.span, msg);
|
||||
let supported_tys: Vec<_> =
|
||||
supported_tys.iter().map(|(t, _)| t.to_string()).collect();
|
||||
err.note(&format!(
|
||||
"register class `{}` supports these types: {}",
|
||||
reg_class.name(),
|
||||
supported_tys.join(", "),
|
||||
));
|
||||
err.emit();
|
||||
return Some(asm_ty);
|
||||
}
|
||||
};
|
||||
|
||||
// Check whether the selected type requires a target feature.
|
||||
if let Some(feature) = feature {
|
||||
if !self.tcx.sess.target_features.contains(&Symbol::intern(feature)) {
|
||||
let msg = &format!("`{}` target feature is not enabled", feature);
|
||||
let mut err = self.tcx.sess.struct_span_err(expr.span, msg);
|
||||
err.note(&format!(
|
||||
"this is required to use type `{}` with register class `{}`",
|
||||
ty,
|
||||
reg_class.name(),
|
||||
));
|
||||
err.emit();
|
||||
return Some(asm_ty);
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether a modifier is suggested for using this type.
|
||||
if let Some((suggested_modifier, suggested_result, switch_reg_class)) =
|
||||
reg_class.suggest_modifier(asm_arch, asm_ty)
|
||||
{
|
||||
// Search for any use of this operand without a modifier and emit
|
||||
// the suggestion for them.
|
||||
let mut spans = vec![];
|
||||
for piece in template {
|
||||
if let &InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span } = piece
|
||||
{
|
||||
if operand_idx == idx && modifier.is_none() {
|
||||
spans.push(span);
|
||||
}
|
||||
}
|
||||
}
|
||||
if !spans.is_empty() {
|
||||
let (default_modifier, default_result) =
|
||||
reg_class.default_modifier(asm_arch).unwrap();
|
||||
self.tcx.struct_span_lint_hir(
|
||||
lint::builtin::ASM_SUB_REGISTER,
|
||||
expr.hir_id,
|
||||
spans,
|
||||
|lint| {
|
||||
let msg = "formatting may not be suitable for sub-register argument";
|
||||
let mut err = lint.build(msg);
|
||||
err.span_label(expr.span, "for this argument");
|
||||
if let Some(switch_reg_class) = switch_reg_class {
|
||||
err.help(&format!(
|
||||
"use the `{}` modifier with the `{}` register class \
|
||||
to have the register formatted as `{}`",
|
||||
suggested_modifier, switch_reg_class, suggested_result,
|
||||
));
|
||||
} else {
|
||||
err.help(&format!(
|
||||
"use the `{}` modifier to have the register formatted as `{}`",
|
||||
suggested_modifier, suggested_result,
|
||||
));
|
||||
}
|
||||
err.help(&format!(
|
||||
"or use the `{}` modifier to keep the default formatting of `{}`",
|
||||
default_modifier, default_result,
|
||||
));
|
||||
err.emit();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Some(asm_ty)
|
||||
}
|
||||
|
||||
fn check_asm(&self, asm: &hir::InlineAsm<'tcx>) {
|
||||
for (idx, op) in asm.operands.iter().enumerate() {
|
||||
match *op {
|
||||
hir::InlineAsmOperand::In { reg, ref expr } => {
|
||||
self.check_asm_operand_type(idx, reg, expr, asm.template, None);
|
||||
}
|
||||
hir::InlineAsmOperand::Out { reg, late: _, ref expr } => {
|
||||
if let Some(expr) = expr {
|
||||
self.check_asm_operand_type(idx, reg, expr, asm.template, None);
|
||||
}
|
||||
}
|
||||
hir::InlineAsmOperand::InOut { reg, late: _, ref expr } => {
|
||||
self.check_asm_operand_type(idx, reg, expr, asm.template, None);
|
||||
}
|
||||
hir::InlineAsmOperand::SplitInOut { reg, late: _, ref in_expr, ref out_expr } => {
|
||||
let in_ty = self.check_asm_operand_type(idx, reg, in_expr, asm.template, None);
|
||||
if let Some(out_expr) = out_expr {
|
||||
self.check_asm_operand_type(
|
||||
idx,
|
||||
reg,
|
||||
out_expr,
|
||||
asm.template,
|
||||
Some((in_expr, in_ty)),
|
||||
);
|
||||
}
|
||||
}
|
||||
hir::InlineAsmOperand::Const { ref expr } => {
|
||||
let ty = self.tables.expr_ty_adjusted(expr);
|
||||
match ty.kind {
|
||||
ty::Int(_) | ty::Uint(_) | ty::Float(_) => {}
|
||||
_ => {
|
||||
let msg =
|
||||
"asm `const` arguments must be integer or floating-point values";
|
||||
self.tcx.sess.span_err(expr.span, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
hir::InlineAsmOperand::Sym { .. } => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Visitor<'tcx> for ItemVisitor<'tcx> {
|
||||
|
@ -146,19 +405,23 @@ impl Visitor<'tcx> for ExprVisitor<'tcx> {
|
|||
}
|
||||
|
||||
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
|
||||
let res = if let hir::ExprKind::Path(ref qpath) = expr.kind {
|
||||
self.tables.qpath_res(qpath, expr.hir_id)
|
||||
} else {
|
||||
Res::Err
|
||||
};
|
||||
if let Res::Def(DefKind::Fn, did) = res {
|
||||
if self.def_id_is_transmute(did) {
|
||||
let typ = self.tables.node_type(expr.hir_id);
|
||||
let sig = typ.fn_sig(self.tcx);
|
||||
let from = sig.inputs().skip_binder()[0];
|
||||
let to = *sig.output().skip_binder();
|
||||
self.check_transmute(expr.span, from, to);
|
||||
match expr.kind {
|
||||
hir::ExprKind::Path(ref qpath) => {
|
||||
let res = self.tables.qpath_res(qpath, expr.hir_id);
|
||||
if let Res::Def(DefKind::Fn, did) = res {
|
||||
if self.def_id_is_transmute(did) {
|
||||
let typ = self.tables.node_type(expr.hir_id);
|
||||
let sig = typ.fn_sig(self.tcx);
|
||||
let from = sig.inputs().skip_binder()[0];
|
||||
let to = *sig.output().skip_binder();
|
||||
self.check_transmute(expr.span, from, to);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hir::ExprKind::InlineAsm(asm) => self.check_asm(asm),
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
intravisit::walk_expr(self, expr);
|
||||
|
|
|
@ -109,6 +109,7 @@ use rustc_middle::ty::{self, TyCtxt};
|
|||
use rustc_session::lint;
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
use rustc_span::Span;
|
||||
use rustc_target::asm::InlineAsmOptions;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt;
|
||||
|
@ -531,6 +532,7 @@ fn visit_expr<'tcx>(ir: &mut IrMaps<'tcx>, expr: &'tcx Expr<'tcx>) {
|
|||
| hir::ExprKind::AssignOp(..)
|
||||
| hir::ExprKind::Struct(..)
|
||||
| hir::ExprKind::Repeat(..)
|
||||
| hir::ExprKind::InlineAsm(..)
|
||||
| hir::ExprKind::LlvmInlineAsm(..)
|
||||
| hir::ExprKind::Box(..)
|
||||
| hir::ExprKind::Yield(..)
|
||||
|
@ -1176,6 +1178,64 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
|||
| hir::ExprKind::Yield(ref e, _)
|
||||
| hir::ExprKind::Repeat(ref e, _) => self.propagate_through_expr(&e, succ),
|
||||
|
||||
hir::ExprKind::InlineAsm(ref asm) => {
|
||||
// Handle non-returning asm
|
||||
let mut succ = if asm.options.contains(InlineAsmOptions::NORETURN) {
|
||||
self.s.exit_ln
|
||||
} else {
|
||||
succ
|
||||
};
|
||||
|
||||
// Do a first pass for writing outputs only
|
||||
for op in asm.operands.iter().rev() {
|
||||
match op {
|
||||
hir::InlineAsmOperand::In { .. }
|
||||
| hir::InlineAsmOperand::Const { .. }
|
||||
| hir::InlineAsmOperand::Sym { .. } => {}
|
||||
hir::InlineAsmOperand::Out { expr, .. } => {
|
||||
if let Some(expr) = expr {
|
||||
succ = self.write_place(expr, succ, ACC_WRITE);
|
||||
}
|
||||
}
|
||||
hir::InlineAsmOperand::InOut { expr, .. } => {
|
||||
succ = self.write_place(expr, succ, ACC_READ | ACC_WRITE);
|
||||
}
|
||||
hir::InlineAsmOperand::SplitInOut { out_expr, .. } => {
|
||||
if let Some(expr) = out_expr {
|
||||
succ = self.write_place(expr, succ, ACC_WRITE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Then do a second pass for inputs
|
||||
let mut succ = succ;
|
||||
for op in asm.operands.iter().rev() {
|
||||
match op {
|
||||
hir::InlineAsmOperand::In { expr, .. }
|
||||
| hir::InlineAsmOperand::Const { expr, .. }
|
||||
| hir::InlineAsmOperand::Sym { expr, .. } => {
|
||||
succ = self.propagate_through_expr(expr, succ)
|
||||
}
|
||||
hir::InlineAsmOperand::Out { expr, .. } => {
|
||||
if let Some(expr) = expr {
|
||||
succ = self.propagate_through_place_components(expr, succ);
|
||||
}
|
||||
}
|
||||
hir::InlineAsmOperand::InOut { expr, .. } => {
|
||||
succ = self.propagate_through_place_components(expr, succ);
|
||||
}
|
||||
hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
|
||||
if let Some(expr) = out_expr {
|
||||
succ = self.propagate_through_place_components(expr, succ);
|
||||
}
|
||||
succ = self.propagate_through_expr(in_expr, succ);
|
||||
}
|
||||
}
|
||||
}
|
||||
succ
|
||||
}
|
||||
|
||||
hir::ExprKind::LlvmInlineAsm(ref asm) => {
|
||||
let ia = &asm.inner;
|
||||
let outputs = asm.outputs_exprs;
|
||||
|
@ -1397,6 +1457,33 @@ fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr<'tcx>) {
|
|||
}
|
||||
}
|
||||
|
||||
hir::ExprKind::InlineAsm(ref asm) => {
|
||||
for op in asm.operands {
|
||||
match op {
|
||||
hir::InlineAsmOperand::In { expr, .. }
|
||||
| hir::InlineAsmOperand::Const { expr, .. }
|
||||
| hir::InlineAsmOperand::Sym { expr, .. } => this.visit_expr(expr),
|
||||
hir::InlineAsmOperand::Out { expr, .. } => {
|
||||
if let Some(expr) = expr {
|
||||
this.check_place(expr);
|
||||
this.visit_expr(expr);
|
||||
}
|
||||
}
|
||||
hir::InlineAsmOperand::InOut { expr, .. } => {
|
||||
this.check_place(expr);
|
||||
this.visit_expr(expr);
|
||||
}
|
||||
hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
|
||||
this.visit_expr(in_expr);
|
||||
if let Some(out_expr) = out_expr {
|
||||
this.check_place(out_expr);
|
||||
this.visit_expr(out_expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hir::ExprKind::LlvmInlineAsm(ref asm) => {
|
||||
for input in asm.inputs_exprs {
|
||||
this.visit_expr(input);
|
||||
|
|
|
@ -508,6 +508,12 @@ declare_lint! {
|
|||
"detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`",
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
pub ASM_SUB_REGISTER,
|
||||
Warn,
|
||||
"using only a subset of a register for inline asm inputs",
|
||||
}
|
||||
|
||||
declare_lint_pass! {
|
||||
/// Does nothing as a lint pass, but registers some `Lint`s
|
||||
/// that are used by other parts of the compiler.
|
||||
|
@ -576,6 +582,7 @@ declare_lint_pass! {
|
|||
INDIRECT_STRUCTURAL_MATCH,
|
||||
SOFT_UNSTABLE,
|
||||
INLINE_NO_SANITIZE,
|
||||
ASM_SUB_REGISTER,
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
@ -1746,6 +1746,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
|||
ObligationCauseCode::ConstSized => {
|
||||
err.note("constant expressions must have a statically known size");
|
||||
}
|
||||
ObligationCauseCode::InlineAsmSized => {
|
||||
err.note("all inline asm arguments must have a statically known size");
|
||||
}
|
||||
ObligationCauseCode::ConstPatternStructural => {
|
||||
err.note("constants used for pattern-matching must derive `PartialEq` and `Eq`");
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ use rustc_middle::ty::{AdtKind, Visibility};
|
|||
use rustc_span::hygiene::DesugaringKind;
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||
use rustc_target::asm::InlineAsmOptions;
|
||||
use rustc_trait_selection::traits::{self, ObligationCauseCode};
|
||||
|
||||
use std::fmt::Display;
|
||||
|
@ -232,6 +233,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
self.check_expr_addr_of(kind, mutbl, oprnd, expected, expr)
|
||||
}
|
||||
ExprKind::Path(ref qpath) => self.check_expr_path(qpath, expr),
|
||||
ExprKind::InlineAsm(asm) => self.check_expr_asm(asm),
|
||||
ExprKind::LlvmInlineAsm(ref asm) => {
|
||||
for expr in asm.outputs_exprs.iter().chain(asm.inputs_exprs.iter()) {
|
||||
self.check_expr(expr);
|
||||
|
@ -1811,6 +1813,72 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr_asm_operand(&self, expr: &'tcx hir::Expr<'tcx>, is_input: bool) {
|
||||
let needs = if is_input { Needs::None } else { Needs::MutPlace };
|
||||
let ty = self.check_expr_with_needs(expr, needs);
|
||||
self.require_type_is_sized(ty, expr.span, traits::InlineAsmSized);
|
||||
|
||||
if !is_input && !expr.is_syntactic_place_expr() {
|
||||
let mut err = self.tcx.sess.struct_span_err(expr.span, "invalid asm output");
|
||||
err.span_label(expr.span, "cannot assign to this expression");
|
||||
err.emit();
|
||||
}
|
||||
|
||||
// If this is an input value, we require its type to be fully resolved
|
||||
// at this point. This allows us to provide helpful coercions which help
|
||||
// pass the type whitelist in a later pass.
|
||||
//
|
||||
// We don't require output types to be resolved at this point, which
|
||||
// allows them to be inferred based on how they are used later in the
|
||||
// function.
|
||||
if is_input {
|
||||
let ty = self.structurally_resolved_type(expr.span, &ty);
|
||||
match ty.kind {
|
||||
ty::FnDef(..) => {
|
||||
let fnptr_ty = self.tcx.mk_fn_ptr(ty.fn_sig(self.tcx));
|
||||
self.demand_coerce(expr, ty, fnptr_ty, AllowTwoPhase::No);
|
||||
}
|
||||
ty::Ref(_, base_ty, mutbl) => {
|
||||
let ptr_ty = self.tcx.mk_ptr(ty::TypeAndMut { ty: base_ty, mutbl });
|
||||
self.demand_coerce(expr, ty, ptr_ty, AllowTwoPhase::No);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr_asm(&self, asm: &'tcx hir::InlineAsm<'tcx>) -> Ty<'tcx> {
|
||||
for op in asm.operands {
|
||||
match op {
|
||||
hir::InlineAsmOperand::In { expr, .. } | hir::InlineAsmOperand::Const { expr } => {
|
||||
self.check_expr_asm_operand(expr, true);
|
||||
}
|
||||
hir::InlineAsmOperand::Out { expr, .. } => {
|
||||
if let Some(expr) = expr {
|
||||
self.check_expr_asm_operand(expr, false);
|
||||
}
|
||||
}
|
||||
hir::InlineAsmOperand::InOut { expr, .. } => {
|
||||
self.check_expr_asm_operand(expr, false);
|
||||
}
|
||||
hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
|
||||
self.check_expr_asm_operand(in_expr, true);
|
||||
if let Some(out_expr) = out_expr {
|
||||
self.check_expr_asm_operand(out_expr, false);
|
||||
}
|
||||
}
|
||||
hir::InlineAsmOperand::Sym { expr } => {
|
||||
self.check_expr(expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
if asm.options.contains(InlineAsmOptions::NORETURN) {
|
||||
self.tcx.types.never
|
||||
} else {
|
||||
self.tcx.mk_unit()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn ty_kind_suggestion(ty: Ty<'_>) -> Option<&'static str> {
|
||||
|
|
|
@ -220,6 +220,30 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
|||
self.borrow_expr(&base, bk);
|
||||
}
|
||||
|
||||
hir::ExprKind::InlineAsm(ref asm) => {
|
||||
for op in asm.operands {
|
||||
match op {
|
||||
hir::InlineAsmOperand::In { expr, .. }
|
||||
| hir::InlineAsmOperand::Const { expr, .. }
|
||||
| hir::InlineAsmOperand::Sym { expr, .. } => self.consume_expr(expr),
|
||||
hir::InlineAsmOperand::Out { expr, .. } => {
|
||||
if let Some(expr) = expr {
|
||||
self.mutate_expr(expr);
|
||||
}
|
||||
}
|
||||
hir::InlineAsmOperand::InOut { expr, .. } => {
|
||||
self.mutate_expr(expr);
|
||||
}
|
||||
hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
|
||||
self.consume_expr(in_expr);
|
||||
if let Some(out_expr) = out_expr {
|
||||
self.mutate_expr(out_expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hir::ExprKind::LlvmInlineAsm(ref ia) => {
|
||||
for (o, output) in ia.inner.outputs.iter().zip(ia.outputs_exprs) {
|
||||
if o.is_indirect {
|
||||
|
|
|
@ -405,6 +405,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
|
|||
| hir::ExprKind::Continue(..)
|
||||
| hir::ExprKind::Struct(..)
|
||||
| hir::ExprKind::Repeat(..)
|
||||
| hir::ExprKind::InlineAsm(..)
|
||||
| hir::ExprKind::LlvmInlineAsm(..)
|
||||
| hir::ExprKind::Box(..)
|
||||
| hir::ExprKind::Err => Ok(self.cat_rvalue(expr.hir_id, expr.span, expr_ty)),
|
||||
|
|
Loading…
Add table
Reference in a new issue