diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index a4529909e83ef..5087d686b439e 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -2778,7 +2778,6 @@ pub use num::dec2flt::ParseFloatError; // Conversion traits for primitive integer and float types // Conversions T -> T are covered by a blanket impl and therefore excluded -// Some conversions from and to usize/isize are not implemented due to portability concerns macro_rules! impl_from { ($Small: ty, $Large: ty) => { #[stable(feature = "lossless_prim_conv", since = "1.5.0")] @@ -2791,6 +2790,14 @@ macro_rules! impl_from { } } +// Conditionally supported conversions from and to usize/isize +macro_rules! impl_from_cond { + ($Small: ty, $Large: ty | $($width: expr),*) => { + #[cfg(any($(target_pointer_width = $width,)*))] + impl_from! { $Small, $Large } + } +} + // Unsigned -> Unsigned impl_from! { u8, u16 } impl_from! { u8, u32 } @@ -2799,6 +2806,12 @@ impl_from! { u8, usize } impl_from! { u16, u32 } impl_from! { u16, u64 } impl_from! { u32, u64 } +impl_from_cond! { u16, usize | "16", "32", "64" } +impl_from_cond! { u32, usize | "32", "64" } +impl_from_cond! { u64, usize | "64" } +impl_from_cond! { usize, u16 | "16" } +impl_from_cond! { usize, u32 | "16", "32" } +impl_from_cond! { usize, u64 | "16", "32", "64" } // Signed -> Signed impl_from! { i8, i16 } @@ -2808,6 +2821,12 @@ impl_from! { i8, isize } impl_from! { i16, i32 } impl_from! { i16, i64 } impl_from! { i32, i64 } +impl_from_cond! { i16, isize | "16", "32", "64" } +impl_from_cond! { i32, isize | "32", "64" } +impl_from_cond! { i64, isize | "64" } +impl_from_cond! { isize, i16 | "16" } +impl_from_cond! { isize, i32 | "16", "32" } +impl_from_cond! { isize, i64 | "16", "32", "64" } // Unsigned -> Signed impl_from! { u8, i16 } @@ -2816,6 +2835,11 @@ impl_from! { u8, i64 } impl_from! { u16, i32 } impl_from! { u16, i64 } impl_from! { u32, i64 } +impl_from_cond! { u8, isize | "16", "32", "64" } +impl_from_cond! { u16, isize | "32", "64" } +impl_from_cond! { u32, isize | "64" } +impl_from_cond! { usize, i32 | "16" } +impl_from_cond! { usize, i64 | "16", "32" } // Note: integers can only be represented with full precision in a float if // they fit in the significand, which is 24 bits in f32 and 53 bits in f64. diff --git a/src/libcoretest/num/mod.rs b/src/libcoretest/num/mod.rs index 4834c0e072c9e..e4d174f614e77 100644 --- a/src/libcoretest/num/mod.rs +++ b/src/libcoretest/num/mod.rs @@ -117,6 +117,7 @@ fn test_empty() { macro_rules! test_impl_from { ($fn_name: ident, $Small: ty, $Large: ty) => { #[test] + #[allow(nonportable_32_64)] fn $fn_name() { let small_max = <$Small>::max_value(); let small_min = <$Small>::min_value(); @@ -128,6 +129,13 @@ macro_rules! test_impl_from { } } +macro_rules! test_impl_from_cond { + ($fn_name: ident, $Small: ty, $Large: ty | $($width: expr),*) => { + #[cfg(any($(target_pointer_width = $width,)*))] + test_impl_from! { $fn_name, $Small, $Large } + } +} + // Unsigned -> Unsigned test_impl_from! { test_u8u16, u8, u16 } test_impl_from! { test_u8u32, u8, u32 } @@ -136,6 +144,12 @@ test_impl_from! { test_u8usize, u8, usize } test_impl_from! { test_u16u32, u16, u32 } test_impl_from! { test_u16u64, u16, u64 } test_impl_from! { test_u32u64, u32, u64 } +test_impl_from_cond! { test_u16usize, u16, usize | "16", "32", "64" } +test_impl_from_cond! { test_u32usize, u32, usize | "32", "64" } +test_impl_from_cond! { test_u64usize, u64, usize | "64" } +test_impl_from_cond! { test_usizeu16, usize, u16 | "16" } +test_impl_from_cond! { test_usizeu32, usize, u32 | "16", "32" } +test_impl_from_cond! { test_usizeu64, usize, u64 | "16", "32", "64" } // Signed -> Signed test_impl_from! { test_i8i16, i8, i16 } @@ -145,6 +159,12 @@ test_impl_from! { test_i8isize, i8, isize } test_impl_from! { test_i16i32, i16, i32 } test_impl_from! { test_i16i64, i16, i64 } test_impl_from! { test_i32i64, i32, i64 } +test_impl_from_cond! { test_i16isize, i16, isize | "16", "32", "64" } +test_impl_from_cond! { test_i32isize, i32, isize | "32", "64" } +test_impl_from_cond! { test_i64isize, i64, isize | "64" } +test_impl_from_cond! { test_isizei16, isize, i16 | "16" } +test_impl_from_cond! { test_isizei32, isize, i32 | "16", "32" } +test_impl_from_cond! { test_isizei64, isize, i64 | "16", "32", "64" } // Unsigned -> Signed test_impl_from! { test_u8i16, u8, i16 } @@ -153,6 +173,11 @@ test_impl_from! { test_u8i64, u8, i64 } test_impl_from! { test_u16i32, u16, i32 } test_impl_from! { test_u16i64, u16, i64 } test_impl_from! { test_u32i64, u32, i64 } +test_impl_from_cond! { test_u8isize, u8, isize | "16", "32", "64" } +test_impl_from_cond! { test_u16isize, u16, isize | "32", "64" } +test_impl_from_cond! { test_u32isize, u32, isize | "64" } +test_impl_from_cond! { test_isizeu32, usize, i32 | "16" } +test_impl_from_cond! { test_isizeu64, usize, i64 | "16", "32" } // Signed -> Float test_impl_from! { test_i8f32, i8, f32 } diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index eee34324a6583..15b79d7abb34f 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -33,8 +33,8 @@ use rustc::hir::def_id::DefId; use middle::stability; use rustc::cfg; use rustc::ty::subst::Substs; -use rustc::ty::{self, Ty, TyCtxt}; -use rustc::ty::adjustment; +use rustc::ty::{self, Ty, TyCtxt, TyInt, TyUint}; +use rustc::ty::{adjustment, subst}; use rustc::traits::{self, Reveal}; use rustc::hir::map as hir_map; use util::nodemap::NodeSet; @@ -1296,3 +1296,173 @@ impl LateLintPass for UnionsWithDropFields { } } } + +/// Lints for non-portable integer conversions. +pub struct NonPortable1632; +pub struct NonPortable3264; +pub struct NonPortable64128; + +declare_lint! { + NONPORTABLE_16_32, + Allow, + "conversions not portable between 32-bit and 16-bit platforms" +} +declare_lint! { + NONPORTABLE_32_64, + Warn, + "conversions not portable between 64-bit and 32-bit platforms" +} +declare_lint! { + NONPORTABLE_64_128, + Allow, + "conversions not portable between 64-bit and 128-bit platforms" +} + +impl LintPass for NonPortable1632 { + fn get_lints(&self) -> LintArray { + lint_array!(NONPORTABLE_16_32) + } +} +impl LintPass for NonPortable3264 { + fn get_lints(&self) -> LintArray { + lint_array!(NONPORTABLE_32_64) + } +} +impl LintPass for NonPortable64128 { + fn get_lints(&self) -> LintArray { + lint_array!(NONPORTABLE_64_128) + } +} + +mod pred { + use super::*; + use syntax::ast::IntTy::*; + use syntax::ast::UintTy::*; + + pub fn is_nonportable_conv_32_64(src: &ty::TypeVariants, dst: &ty::TypeVariants) -> bool { + match (src, dst) { + // All conditional impls from libcore/num/mod.rs + // not including "32" and "64" at the same time. + (&TyUint(U64), &TyUint(Us)) | + (&TyUint(Us), &TyUint(U16)) | + (&TyUint(Us), &TyUint(U32)) | + (&TyInt(I64), &TyInt(Is)) | + (&TyInt(Is), &TyInt(I16)) | + (&TyInt(Is), &TyInt(I32)) | + (&TyUint(U32), &TyInt(Is)) | + (&TyUint(Us), &TyInt(I32)) | + (&TyUint(Us), &TyInt(I64)) => true, + _ => false, + } + } + + pub fn is_nonportable_conv_16_32(src: &ty::TypeVariants, dst: &ty::TypeVariants) -> bool { + match (src, dst) { + // All conditional impls from libcore/num/mod.rs + // not including "16" and "32" at the same time. + (&TyUint(U32), &TyUint(Us)) | + (&TyUint(Us), &TyUint(U16)) | + (&TyInt(I32), &TyInt(Is)) | + (&TyInt(Is), &TyInt(I16)) | + (&TyUint(U16), &TyInt(Is)) | + (&TyUint(Us), &TyInt(I32)) => true, + _ => false, + } + } + + pub fn is_nonportable_conv_64_128(src: &ty::TypeVariants, dst: &ty::TypeVariants) -> bool { + match (src, dst) { + // All conditional impls from libcore/num/mod.rs + // not including "64" and potentially "128" at the same time. + (&TyUint(Us), &TyUint(U64)) | + (&TyInt(Is), &TyInt(I64)) => true, + _ => false, + } + } +} + +fn is_nonportable_conv(src: subst::Kind, dst: subst::Kind, pred: Pred) -> bool + where Pred: Fn(&ty::TypeVariants, &ty::TypeVariants) -> bool +{ + match (src.as_type(), dst.as_type()) { + (Some(src), Some(dst)) => pred(&src.sty, &dst.sty), + _ => false, + } +} + +fn nonportable_check_expr(cx: &LateContext, e: &hir::Expr, w1: u8, w2: u8, + lint: &'static Lint, pred: Pred) + where Pred: Fn(&ty::TypeVariants, &ty::TypeVariants) -> bool +{ + let tcx = cx.tcx; + let report_lint = |span, src: subst::Kind, dst: subst::Kind| { + let src_ty = src.as_type().unwrap(); + let dst_ty = dst.as_type().unwrap(); + cx.span_lint(lint, span, + &format!("conversion `{}` -> `{}` is not portable \ + between {}-bit and {}-bit platforms", src_ty, dst_ty, w1, w2)); + }; + match e.node { + hir::ExprMethodCall(name, ..) => { + if name.node.as_str() == "into" { + if let Some(callee) = tcx.tables.borrow().method_map + .get(&ty::MethodCall::expr(e.id)).cloned() { + if let ty::TyFnDef(def_id, substs, _) = callee.ty.sty { + let ti = tcx.impl_or_trait_item(def_id); + if let ty::TraitContainer(trait_def_id) = ti.container() { + if substs.len() == 2 { + if tcx.item_name(trait_def_id).as_str() == "Into" { + if is_nonportable_conv(substs[0], substs[1], pred) { + report_lint(name.span, substs[0], substs[1]); + } + } + } + } + } + } + } + } + hir::ExprPath(..) => { + if let Def::Method(def_id) = tcx.expect_def(e.id) { + let ti = tcx.impl_or_trait_item(def_id); + if let ty::MethodTraitItem(ref method) = ti { + if let ty::TraitContainer(trait_def_id) = ti.container() { + let substs = tcx.node_id_item_substs(e.id).substs; + if substs.len() == 2 { + if method.name.as_str() == "into" { + if tcx.item_name(trait_def_id).as_str() == "Into" { + if is_nonportable_conv(substs[0], substs[1], pred) { + report_lint(e.span, substs[0], substs[1]); + } + } + } else if method.name.as_str() == "from" { + if tcx.item_name(trait_def_id).as_str() == "From" { + if is_nonportable_conv(substs[1], substs[0], pred) { + report_lint(e.span, substs[1], substs[0]); + } + } + } + } + } + } + } + } + _ => {} + } +} + +impl LateLintPass for NonPortable1632 { + fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) { + nonportable_check_expr(cx, e, 32, 16, NONPORTABLE_16_32, pred::is_nonportable_conv_16_32) + } +} +impl LateLintPass for NonPortable3264 { + fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) { + nonportable_check_expr(cx, e, 64, 32, NONPORTABLE_32_64, pred::is_nonportable_conv_32_64) + } +} +impl LateLintPass for NonPortable64128 { + fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) { + nonportable_check_expr(cx, e, 64, 128, NONPORTABLE_64_128, pred::is_nonportable_conv_64_128) + } +} diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index d191c82abed71..cbc78e9b478e2 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -32,6 +32,7 @@ #![feature(box_patterns)] #![feature(box_syntax)] #![feature(dotdot_in_tuple_patterns)] +#![feature(item_like_imports)] #![feature(quote)] #![feature(rustc_diagnostic_macros)] #![feature(rustc_private)] @@ -143,6 +144,9 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { PluginAsLibrary, MutableTransmutes, UnionsWithDropFields, + NonPortable1632, + NonPortable3264, + NonPortable64128, ); add_builtin_with_new!(sess, diff --git a/src/test/compile-fail/lint-nonportable-16-32.rs b/src/test/compile-fail/lint-nonportable-16-32.rs new file mode 100644 index 0000000000000..594bff85c7f0e --- /dev/null +++ b/src/test/compile-fail/lint-nonportable-16-32.rs @@ -0,0 +1,37 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused)] +#![deny(nonportable_16_32)] + +fn check() { + let _: usize = 0u32.into(); + //~^ ERROR conversion `u32` -> `usize` is not portable between 32-bit and 16-bit platforms + let _: usize = Into::into(0u32); + //~^ ERROR conversion `u32` -> `usize` is not portable between 32-bit and 16-bit platforms + let _: usize = From::from(0u32); + //~^ ERROR conversion `u32` -> `usize` is not portable between 32-bit and 16-bit platforms + + let _: isize = 0i32.into(); + //~^ ERROR conversion `i32` -> `isize` is not portable between 32-bit and 16-bit platforms + let _: isize = Into::into(0i32); + //~^ ERROR conversion `i32` -> `isize` is not portable between 32-bit and 16-bit platforms + let _: isize = From::from(0i32); + //~^ ERROR conversion `i32` -> `isize` is not portable between 32-bit and 16-bit platforms + + let _: isize = 0u16.into(); + //~^ ERROR conversion `u16` -> `isize` is not portable between 32-bit and 16-bit platforms + let _: isize = Into::into(0u16); + //~^ ERROR conversion `u16` -> `isize` is not portable between 32-bit and 16-bit platforms + let _: isize = From::from(0u16); + //~^ ERROR conversion `u16` -> `isize` is not portable between 32-bit and 16-bit platforms +} + +fn main() {} diff --git a/src/test/compile-fail/lint-nonportable-32-64.rs b/src/test/compile-fail/lint-nonportable-32-64.rs new file mode 100644 index 0000000000000..11a30f9c098aa --- /dev/null +++ b/src/test/compile-fail/lint-nonportable-32-64.rs @@ -0,0 +1,64 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// FIXME: make compiletest understand ignore-64bit / ignore-32bit + +#![allow(unused)] +#![deny(nonportable_32_64)] + +#[cfg(target_pointer_width = "64")] +fn check64() { + let _: usize = 0u64.into(); + //~^ ERROR conversion `u64` -> `usize` is not portable between 64-bit and 32-bit platforms + let _: usize = Into::into(0u64); + //~^ ERROR conversion `u64` -> `usize` is not portable between 64-bit and 32-bit platforms + let _: usize = From::from(0u64); + //~^ ERROR conversion `u64` -> `usize` is not portable between 64-bit and 32-bit platforms + + let _: isize = 0i64.into(); + //~^ ERROR conversion `i64` -> `isize` is not portable between 64-bit and 32-bit platforms + let _: isize = Into::into(0i64); + //~^ ERROR conversion `i64` -> `isize` is not portable between 64-bit and 32-bit platforms + let _: isize = From::from(0i64); + //~^ ERROR conversion `i64` -> `isize` is not portable between 64-bit and 32-bit platforms + + let _: isize = 0u32.into(); + //~^ ERROR conversion `u32` -> `isize` is not portable between 64-bit and 32-bit platforms + let _: isize = Into::into(0u32); + //~^ ERROR conversion `u32` -> `isize` is not portable between 64-bit and 32-bit platforms + let _: isize = From::from(0u32); + //~^ ERROR conversion `u32` -> `isize` is not portable between 64-bit and 32-bit platforms +} + +#[cfg(target_pointer_width = "32")] +fn check32() { + let _: u32 = 0usize.into(); + // ERROR conversion `usize` -> `u32` is not portable between 64-bit and 32-bit platforms + let _: u32 = Into::into(0usize); + // ERROR conversion `usize` -> `u32` is not portable between 64-bit and 32-bit platforms + let _: u32 = From::from(0usize); + // ERROR conversion `usize` -> `u32` is not portable between 64-bit and 32-bit platforms + + let _: i32 = 0isize.into(); + // ERROR conversion `isize` -> `i32` is not portable between 64-bit and 32-bit platforms + let _: i32 = Into::into(0isize); + // ERROR conversion `isize` -> `i32` is not portable between 64-bit and 32-bit platforms + let _: i32 = From::from(0isize); + // ERROR conversion `isize` -> `i32` is not portable between 64-bit and 32-bit platforms + + let _: i64 = 0usize.into(); + // ERROR conversion `usize` -> `i64` is not portable between 64-bit and 32-bit platforms + let _: i64 = Into::into(0usize); + // ERROR conversion `usize` -> `i64` is not portable between 64-bit and 32-bit platforms + let _: i64 = From::from(0usize); + // ERROR conversion `usize` -> `i64` is not portable between 64-bit and 32-bit platforms +} + +fn main() {} diff --git a/src/test/compile-fail/lint-nonportable-64-128.rs b/src/test/compile-fail/lint-nonportable-64-128.rs new file mode 100644 index 0000000000000..969ce1838c5d6 --- /dev/null +++ b/src/test/compile-fail/lint-nonportable-64-128.rs @@ -0,0 +1,30 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused)] +#![deny(nonportable_64_128)] + +fn check() { + let _: u64 = 0usize.into(); + //~^ ERROR conversion `usize` -> `u64` is not portable between 64-bit and 128-bit platforms + let _: u64 = Into::into(0usize); + //~^ ERROR conversion `usize` -> `u64` is not portable between 64-bit and 128-bit platforms + let _: u64 = From::from(0usize); + //~^ ERROR conversion `usize` -> `u64` is not portable between 64-bit and 128-bit platforms + + let _: i64 = 0isize.into(); + //~^ ERROR conversion `isize` -> `i64` is not portable between 64-bit and 128-bit platforms + let _: i64 = Into::into(0isize); + //~^ ERROR conversion `isize` -> `i64` is not portable between 64-bit and 128-bit platforms + let _: i64 = From::from(0isize); + //~^ ERROR conversion `isize` -> `i64` is not portable between 64-bit and 128-bit platforms +} + +fn main() {}