intrinsicck.rs 18.4 KB
Newer Older
1
use rustc_ast::InlineAsmTemplatePiece;
2
use rustc_errors::struct_span_err;
3
use rustc_hir as hir;
4
use rustc_hir::def::{DefKind, Res};
5
use rustc_hir::def_id::{DefId, LocalDefId};
6
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
M
Mark Rousskov 已提交
7
use rustc_index::vec::Idx;
8
use rustc_middle::ty::layout::{LayoutError, SizeSkeleton};
9
use rustc_middle::ty::query::Providers;
10
use rustc_middle::ty::{self, FloatTy, IntTy, Ty, TyCtxt, UintTy};
A
Amanieu d'Antras 已提交
11 12
use rustc_session::lint;
use rustc_span::{sym, Span, Symbol, DUMMY_SP};
13
use rustc_target::abi::{Pointer, VariantIdx};
14
use rustc_target::asm::{InlineAsmRegOrRegClass, InlineAsmType};
M
Mark Rousskov 已提交
15
use rustc_target::spec::abi::Abi::RustIntrinsic;
16

17
fn check_mod_intrinsics(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
M
Mark Rousskov 已提交
18
    tcx.hir().visit_item_likes_in_module(module_def_id, &mut ItemVisitor { tcx }.as_deep_visitor());
19 20
}

21
pub fn provide(providers: &mut Providers) {
M
Mark Rousskov 已提交
22
    *providers = Providers { check_mod_intrinsics, ..*providers };
23 24
}

25
struct ItemVisitor<'tcx> {
26
    tcx: TyCtxt<'tcx>,
27
}
28

29
struct ExprVisitor<'tcx> {
30
    tcx: TyCtxt<'tcx>,
31
    typeck_results: &'tcx ty::TypeckResults<'tcx>,
32
    param_env: ty::ParamEnv<'tcx>,
33 34
}

35 36
/// If the type is `Option<T>`, it will return `T`, otherwise
/// the type itself. Works on most `Option`-like types.
37
fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
L
LeSeulArtichaut 已提交
38
    let (def, substs) = match *ty.kind() {
V
varkor 已提交
39
        ty::Adt(def, substs) => (def, substs),
M
Mark Rousskov 已提交
40
        _ => return ty,
41 42
    };

43
    if def.variants.len() == 2 && !def.repr.c() && def.repr.int.is_none() {
44 45
        let data_idx;

46 47 48 49 50 51 52
        let one = VariantIdx::new(1);
        let zero = VariantIdx::new(0);

        if def.variants[zero].fields.is_empty() {
            data_idx = one;
        } else if def.variants[one].fields.is_empty() {
            data_idx = zero;
53 54 55 56 57 58 59 60 61 62 63 64
        } else {
            return ty;
        }

        if def.variants[data_idx].fields.len() == 1 {
            return def.variants[data_idx].fields[0].ty(tcx, substs);
        }
    }

    ty
}

65
impl ExprVisitor<'tcx> {
66
    fn def_id_is_transmute(&self, def_id: DefId) -> bool {
M
Mark Rousskov 已提交
67 68
        self.tcx.fn_sig(def_id).abi() == RustIntrinsic
            && self.tcx.item_name(def_id) == sym::transmute
69 70
    }

71 72 73
    fn check_transmute(&self, span: Span, from: Ty<'tcx>, to: Ty<'tcx>) {
        let sk_from = SizeSkeleton::compute(from, self.tcx, self.param_env);
        let sk_to = SizeSkeleton::compute(to, self.tcx, self.param_env);
74

75 76 77 78
        // Check for same size using the skeletons.
        if let (Ok(sk_from), Ok(sk_to)) = (sk_from, sk_to) {
            if sk_from.same_size(sk_to) {
                return;
79 80
            }

C
Camelid 已提交
81
            // Special-case transmuting from `typeof(function)` and
82
            // `Option<typeof(function)>` to present a clearer error.
M
Mark Rousskov 已提交
83
            let from = unpack_option_like(self.tcx, from);
L
LeSeulArtichaut 已提交
84
            if let (&ty::FnDef(..), SizeSkeleton::Known(size_to)) = (from.kind(), sk_to) {
85
                if size_to == Pointer.size(&self.tcx) {
M
Mark Rousskov 已提交
86
                    struct_span_err!(self.tcx.sess, span, E0591, "can't transmute zero-sized type")
M
Mark Simulacrum 已提交
87 88 89
                        .note(&format!("source type: {}", from))
                        .note(&format!("target type: {}", to))
                        .help("cast with `as` to a pointer instead")
90
                        .emit();
91 92 93
                    return;
                }
            }
94
        }
95

96
        // Try to display a sensible error with as much information as possible.
M
Mark Rousskov 已提交
97 98 99 100 101 102 103 104
        let skeleton_string = |ty: Ty<'tcx>, sk| match sk {
            Ok(SizeSkeleton::Known(size)) => format!("{} bits", size.bits()),
            Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{}`", tail),
            Err(LayoutError::Unknown(bad)) => {
                if bad == ty {
                    "this type does not have a fixed size".to_owned()
                } else {
                    format!("size can vary because of {}", bad)
105
                }
106
            }
M
Mark Rousskov 已提交
107
            Err(err) => err.to_string(),
108
        };
109

M
Mark Rousskov 已提交
110 111 112 113 114 115 116
        let mut err = struct_span_err!(
            self.tcx.sess,
            span,
            E0512,
            "cannot transmute between types of different sizes, \
                                        or dependently-sized types"
        );
117 118 119
        if from == to {
            err.note(&format!("`{}` does not have a fixed size", from));
        } else {
V
varkor 已提交
120 121
            err.note(&format!("source type: `{}` ({})", from, skeleton_string(from, sk_from)))
                .note(&format!("target type: `{}` ({})", to, skeleton_string(to, sk_to)));
122
        }
V
varkor 已提交
123
        err.emit()
124
    }
A
Amanieu d'Antras 已提交
125 126 127 128 129

    fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool {
        if ty.is_sized(self.tcx.at(DUMMY_SP), self.param_env) {
            return true;
        }
L
LeSeulArtichaut 已提交
130
        if let ty::Foreign(..) = ty.kind() {
A
Amanieu d'Antras 已提交
131 132 133 134 135 136 137 138 139 140 141 142 143 144
            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.
145
        let ty = self.typeck_results.expr_ty_adjusted(expr);
146
        let asm_ty_isize = match self.tcx.sess.target.pointer_width {
A
Amanieu d'Antras 已提交
147 148 149 150 151
            16 => InlineAsmType::I16,
            32 => InlineAsmType::I32,
            64 => InlineAsmType::I64,
            _ => unreachable!(),
        };
L
LeSeulArtichaut 已提交
152
        let asm_ty = match *ty.kind() {
153
            ty::Never | ty::Error(_) => return None,
A
Amanieu d'Antras 已提交
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
            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);
L
LeSeulArtichaut 已提交
169
                match elem_ty.kind() {
170
                    ty::Never | ty::Error(_) => return None,
A
Amanieu d'Antras 已提交
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
                    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) => {
187
                        Some(match self.tcx.sess.target.pointer_width {
A
Amanieu d'Antras 已提交
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
                            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)].
217
        if !ty.is_copy_modulo_regions(self.tcx.at(DUMMY_SP), self.param_env) {
A
Amanieu d'Antras 已提交
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
            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 {
M
Matthias Krüger 已提交
235
                let msg = "incompatible types for asm inout argument";
A
Amanieu d'Antras 已提交
236 237 238
                let mut err = self.tcx.sess.struct_span_err(vec![in_expr.span, expr.span], msg);
                err.span_label(
                    in_expr.span,
239
                    &format!("type `{}`", self.typeck_results.expr_ty_adjusted(in_expr)),
A
Amanieu d'Antras 已提交
240 241
                );
                err.span_label(expr.span, &format!("type `{}`", ty));
A
Amanieu d'Antras 已提交
242 243 244 245
                err.note(
                    "asm inout arguments must have the same type, \
                    unless they are both pointers or integers of the same size",
                );
A
Amanieu d'Antras 已提交
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
                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(", "),
                ));
271 272 273 274 275 276
                if let Some(suggest) = reg_class.suggest_class(asm_arch, asm_ty) {
                    err.help(&format!(
                        "consider using the `{}` register class instead",
                        suggest.name()
                    ));
                }
A
Amanieu d'Antras 已提交
277 278 279 280 281
                err.emit();
                return Some(asm_ty);
            }
        };

A
Amanieu d'Antras 已提交
282 283 284 285 286 287 288 289 290 291
        // Check whether the selected type requires a target feature. Note that
        // this is different from the feature check we did earlier in AST
        // lowering. While AST lowering checked that this register class is
        // usable at all with the currently enabled features, some types may
        // only be usable with a register class when a certain feature is
        // enabled. We check this here since it depends on the results of typeck.
        //
        // Also note that this check isn't run when the operand type is never
        // (!). In that case we still need the earlier check in AST lowering to
        // verify that the register class is usable at all.
A
Amanieu d'Antras 已提交
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
        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.
307
        if let Some((suggested_modifier, suggested_result)) =
A
Amanieu d'Antras 已提交
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
            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");
332 333 334 335
                        err.help(&format!(
                            "use the `{}` modifier to have the register formatted as `{}`",
                            suggested_modifier, suggested_result,
                        ));
A
Amanieu d'Antras 已提交
336 337 338 339 340 341 342 343 344 345 346 347 348 349
                        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>) {
350
        for (idx, (op, op_sp)) in asm.operands.iter().enumerate() {
A
Amanieu d'Antras 已提交
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
            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)),
                        );
                    }
                }
375 376 377 378
                hir::InlineAsmOperand::Const { ref anon_const } => {
                    let anon_const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id);
                    let value = ty::Const::from_anon_const(self.tcx, anon_const_def_id);
                    match value.ty.kind() {
A
Amanieu d'Antras 已提交
379 380 381 382
                        ty::Int(_) | ty::Uint(_) | ty::Float(_) => {}
                        _ => {
                            let msg =
                                "asm `const` arguments must be integer or floating-point values";
383
                            self.tcx.sess.span_err(*op_sp, msg);
A
Amanieu d'Antras 已提交
384 385 386 387 388 389 390
                        }
                    }
                }
                hir::InlineAsmOperand::Sym { .. } => {}
            }
        }
    }
391
}
392

393
impl Visitor<'tcx> for ItemVisitor<'tcx> {
394
    type Map = intravisit::ErasedMap<'tcx>;
395

396
    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
397
        NestedVisitorMap::None
398 399
    }

400
    fn visit_nested_body(&mut self, body_id: hir::BodyId) {
401 402
        let owner_def_id = self.tcx.hir().body_owner_def_id(body_id);
        let body = self.tcx.hir().body(body_id);
403
        let param_env = self.tcx.param_env(owner_def_id.to_def_id());
404 405
        let typeck_results = self.tcx.typeck(owner_def_id);
        ExprVisitor { tcx: self.tcx, param_env, typeck_results }.visit_body(body);
406
        self.visit_body(body);
407
    }
408
}
409

410
impl Visitor<'tcx> for ExprVisitor<'tcx> {
411
    type Map = intravisit::ErasedMap<'tcx>;
412

413
    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
414
        NestedVisitorMap::None
415 416
    }

C
Camille GILLOT 已提交
417
    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
A
Amanieu d'Antras 已提交
418 419
        match expr.kind {
            hir::ExprKind::Path(ref qpath) => {
420
                let res = self.typeck_results.qpath_res(qpath, expr.hir_id);
A
Amanieu d'Antras 已提交
421 422
                if let Res::Def(DefKind::Fn, did) = res {
                    if self.def_id_is_transmute(did) {
423
                        let typ = self.typeck_results.node_type(expr.hir_id);
A
Amanieu d'Antras 已提交
424 425
                        let sig = typ.fn_sig(self.tcx);
                        let from = sig.inputs().skip_binder()[0];
426
                        let to = sig.output().skip_binder();
A
Amanieu d'Antras 已提交
427 428 429
                        self.check_transmute(expr.span, from, to);
                    }
                }
430
            }
A
Amanieu d'Antras 已提交
431 432 433 434

            hir::ExprKind::InlineAsm(asm) => self.check_asm(asm),

            _ => {}
435 436
        }

437
        intravisit::walk_expr(self, expr);
438 439
    }
}