From f030b5dbc29b20b964f2cc448d893998080b1a46 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 3 May 2016 06:11:20 -0400 Subject: [PATCH 1/8] degrade gracefully with empty spans --- src/libsyntax/errors/snippet/mod.rs | 12 ++++++++- src/libsyntax/errors/snippet/test.rs | 38 ++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/libsyntax/errors/snippet/mod.rs b/src/libsyntax/errors/snippet/mod.rs index e213f623ab85d..237e6823e0f87 100644 --- a/src/libsyntax/errors/snippet/mod.rs +++ b/src/libsyntax/errors/snippet/mod.rs @@ -376,11 +376,21 @@ impl FileInfo { // Basically, although this loses information, multi-line spans just // never look good. - let (line, start_col, end_col) = if lines.len() == 1 { + let (line, start_col, mut end_col) = if lines.len() == 1 { (lines[0].line_index, lines[0].start_col, lines[0].end_col) } else { (lines[0].line_index, lines[0].start_col, CharPos(lines[0].start_col.0 + 1)) }; + + // Watch out for "empty spans". If we get a span like 6..6, we + // want to just display a `^` at 6, so convert that to + // 6..7. This is degenerate input, but it's best to degrade + // gracefully -- and the parser likes to suply a span like + // that for EOF, in particular. + if start_col == end_col { + end_col.0 += 1; + } + let index = self.ensure_source_line(line); self.lines[index].push_annotation(start_col, end_col, diff --git a/src/libsyntax/errors/snippet/test.rs b/src/libsyntax/errors/snippet/test.rs index 569d11199190c..5a888b488191b 100644 --- a/src/libsyntax/errors/snippet/test.rs +++ b/src/libsyntax/errors/snippet/test.rs @@ -519,3 +519,41 @@ fn span_overlap_label3() { |> ----- bar "#[1..]); } + +#[test] +fn span_empty() { + // In one of the unit tests, we found that the parser sometimes + // gives empty spans, and in particular it supplied an EOF span + // like this one, which points at the very end. We want to + // fallback gracefully in this case. + + let file_text = r#" +fn main() { + struct Foo; + + impl !Sync for Foo {} + + unsafe impl Send for &'static Foo { + // error: cross-crate traits with a default impl, like `core::marker::Send`, + // can only be implemented for a struct/enum type, not + // `&'static Foo` +}"#; + + + let cm = Rc::new(CodeMap::new()); + let foo = cm.new_filemap_and_lines("foo.rs", file_text); + + let mut rbrace_span = cm.span_substr(&foo, file_text, "}", 1); + rbrace_span.lo = rbrace_span.hi; + + let mut snippet = SnippetData::new(cm.clone(), Some(rbrace_span)); + snippet.push(rbrace_span, false, None); + let lines = snippet.render_lines(); + let text: String = make_string(&lines); + println!("r#\"\n{}\"", text); + assert_eq!(text, &r#" + --> foo.rs:11:2 +11 |> } + |> - +"#[1..]); +} From 421e6fc24d2e5bfc59746d2662531fb22f7e0720 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 3 May 2016 14:02:32 +0200 Subject: [PATCH 2/8] rustdoc: use btree map for where clauses to get more reproducible output. Fixes: #32555 --- src/librustdoc/clean/simplify.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs index 4ba412cdc851b..c0faa04323e47 100644 --- a/src/librustdoc/clean/simplify.rs +++ b/src/librustdoc/clean/simplify.rs @@ -27,7 +27,7 @@ //! bounds by special casing scenarios such as these. Fun! use std::mem; -use std::collections::HashMap; +use std::collections::BTreeMap; use rustc::hir::def_id::DefId; use rustc::ty::subst; @@ -39,7 +39,7 @@ use core::DocContext; pub fn where_clauses(cx: &DocContext, clauses: Vec) -> Vec { // First, partition the where clause into its separate components - let mut params = HashMap::new(); + let mut params = BTreeMap::new(); let mut lifetimes = Vec::new(); let mut equalities = Vec::new(); let mut tybounds = Vec::new(); @@ -62,7 +62,7 @@ pub fn where_clauses(cx: &DocContext, clauses: Vec) -> Vec { // Simplify the type parameter bounds on all the generics let mut params = params.into_iter().map(|(k, v)| { (k, ty_bounds(v)) - }).collect::>(); + }).collect::>(); // Look for equality predicates on associated types that can be merged into // general bound predicates From b3de04214646a33fa5a14fbb2e8ba55e7ee5a707 Mon Sep 17 00:00:00 2001 From: Garrett Squire Date: Wed, 4 May 2016 10:25:17 -0700 Subject: [PATCH 3/8] add a check to make tidy to ensure cargo lock file is updated --- src/rustc/Cargo.lock | 33 +++++------------------- src/tools/tidy/src/cargo_lock.rs | 43 ++++++++++++++++++++++++++++++++ src/tools/tidy/src/main.rs | 2 ++ 3 files changed, 51 insertions(+), 27 deletions(-) create mode 100644 src/tools/tidy/src/cargo_lock.rs diff --git a/src/rustc/Cargo.lock b/src/rustc/Cargo.lock index 1fa4d5398f489..b4c2e21651edb 100644 --- a/src/rustc/Cargo.lock +++ b/src/rustc/Cargo.lock @@ -7,15 +7,6 @@ dependencies = [ "rustdoc 0.0.0", ] -[[package]] -name = "advapi32-sys" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "arena" version = "0.0.0" @@ -29,7 +20,7 @@ name = "flate" version = "0.0.0" dependencies = [ "build_helper 0.1.0", - "gcc 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -38,12 +29,8 @@ version = "0.0.0" [[package]] name = "gcc" -version = "0.3.17" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "advapi32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "graphviz" @@ -192,7 +179,7 @@ name = "rustc_llvm" version = "0.0.0" dependencies = [ "build_helper 0.1.0", - "gcc 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_bitflags 0.0.0", ] @@ -233,6 +220,7 @@ dependencies = [ "log 0.0.0", "rustc 0.0.0", "rustc_const_eval 0.0.0", + "rustc_const_math 0.0.0", "syntax 0.0.0", ] @@ -278,6 +266,7 @@ version = "0.0.0" dependencies = [ "log 0.0.0", "rustc 0.0.0", + "serialize 0.0.0", "syntax 0.0.0", ] @@ -323,7 +312,7 @@ version = "0.0.0" dependencies = [ "arena 0.0.0", "build_helper 0.1.0", - "gcc 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.0.0", "rustc 0.0.0", "rustc_back 0.0.0", @@ -362,13 +351,3 @@ dependencies = [ "syntax 0.0.0", ] -[[package]] -name = "winapi" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - diff --git a/src/tools/tidy/src/cargo_lock.rs b/src/tools/tidy/src/cargo_lock.rs new file mode 100644 index 0000000000000..4324db489b7f8 --- /dev/null +++ b/src/tools/tidy/src/cargo_lock.rs @@ -0,0 +1,43 @@ +// 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. + +use std::path::Path; + +const CARGO_LOCK: &'static str = "Cargo.lock"; + +pub fn check(path: &Path, bad: &mut bool) { + use std::process::Command; + + super::walk(path, + &mut |path| super::filter_dirs(path) || path.ends_with("src/test"), + &mut |file| { + let name = file.file_name().unwrap().to_string_lossy(); + if name == CARGO_LOCK { + let rel_path = file.strip_prefix(path).unwrap(); + let ret_code = Command::new("git") + .arg("diff-index") + .arg("--quiet") + .arg("HEAD") + .arg(rel_path) + .current_dir(path) + .status() + .unwrap_or_else(|e| { + panic!("could not run git diff-index: {}", e); + }); + if !ret_code.success() { + let parent_path = file.parent().unwrap().join("Cargo.toml"); + print!("dirty lock file found at {} ", rel_path.display()); + println!("please commit your changes or update the lock file by running:"); + println!("\n\tcargo update --manifest-path {}", parent_path.display()); + *bad = true; + } + } + }); +} diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index e9e2508aba9bd..2839bbded1a5f 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -35,6 +35,7 @@ mod style; mod errors; mod features; mod cargo; +mod cargo_lock; fn main() { let path = env::args_os().skip(1).next().expect("need an argument"); @@ -46,6 +47,7 @@ fn main() { errors::check(&path, &mut bad); cargo::check(&path, &mut bad); features::check(&path, &mut bad); + cargo_lock::check(&path, &mut bad); if bad { panic!("some tidy checks failed"); From 5f9e30431021f1c8eae7f4cfa6db9b1d39a8e216 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Wed, 4 May 2016 22:55:35 +0530 Subject: [PATCH 4/8] Improve diagnostics for constants being used in irrefutable patterns It's pretty confusing and this error triggers in resolve only when "shadowing" a const, so let's make that clearer. --- src/librustc_resolve/lib.rs | 8 ++++---- src/test/compile-fail/const-pattern-irrefutable.rs | 6 +++--- src/test/compile-fail/issue-27033.rs | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index e747ed1526061..a617f425ecdaa 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -141,7 +141,7 @@ enum ResolutionError<'a> { /// error E0413: declaration shadows an enum variant or unit-like struct in scope DeclarationShadowsEnumVariantOrUnitLikeStruct(Name), /// error E0414: only irrefutable patterns allowed here - OnlyIrrefutablePatternsAllowedHere(Name), + ConstantForIrrefutableBinding(Name), /// error E0415: identifier is bound more than once in this parameter list IdentifierBoundMoreThanOnceInParameterList(&'a str), /// error E0416: identifier is bound more than once in the same pattern @@ -323,11 +323,11 @@ fn resolve_struct_error<'b, 'a: 'b, 'tcx: 'a>(resolver: &'b Resolver<'a, 'tcx>, or unit-like struct in scope", name) } - ResolutionError::OnlyIrrefutablePatternsAllowedHere(name) => { + ResolutionError::ConstantForIrrefutableBinding(name) => { let mut err = struct_span_err!(resolver.session, span, E0414, - "only irrefutable patterns allowed here"); + "variable bindings cannot shadow constants"); err.span_note(span, "there already is a constant in scope sharing the same \ name as this pattern"); @@ -2233,7 +2233,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { resolve_error( self, pattern.span, - ResolutionError::OnlyIrrefutablePatternsAllowedHere(name) + ResolutionError::ConstantForIrrefutableBinding(name) ); self.record_def(pattern.id, err_path_resolution()); } diff --git a/src/test/compile-fail/const-pattern-irrefutable.rs b/src/test/compile-fail/const-pattern-irrefutable.rs index 825c39011fcc1..0be1e974e7d7d 100644 --- a/src/test/compile-fail/const-pattern-irrefutable.rs +++ b/src/test/compile-fail/const-pattern-irrefutable.rs @@ -19,10 +19,10 @@ use foo::d; //~ NOTE constant imported here const a: u8 = 2; //~ NOTE constant defined here fn main() { - let a = 4; //~ ERROR only irrefutable + let a = 4; //~ ERROR variable bindings cannot //~^ NOTE there already is a constant in scope - let c = 4; //~ ERROR only irrefutable + let c = 4; //~ ERROR variable bindings cannot //~^ NOTE there already is a constant in scope - let d = 4; //~ ERROR only irrefutable + let d = 4; //~ ERROR variable bindings cannot //~^ NOTE there already is a constant in scope } diff --git a/src/test/compile-fail/issue-27033.rs b/src/test/compile-fail/issue-27033.rs index 051edfe5f451b..a729cf95a7bf3 100644 --- a/src/test/compile-fail/issue-27033.rs +++ b/src/test/compile-fail/issue-27033.rs @@ -14,7 +14,7 @@ fn main() { }; const C: u8 = 1; match 1 { - C @ 2 => { //~ ERROR only irrefutable patterns allowed here + C @ 2 => { //~ ERROR variable bindings cannot shadow constants println!("{}", C); } _ => {} From c5aa8794908425a36e71e9628a23c5d15f66c65e Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Wed, 4 May 2016 22:09:51 -0400 Subject: [PATCH 5/8] implement RFC 1521 Adds documentation to Clone, specifying that Copy types should have a trivial Clone impl. Fixes #33416. --- src/libcore/clone.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libcore/clone.rs b/src/libcore/clone.rs index ad2a205a82376..e8ea993c6940a 100644 --- a/src/libcore/clone.rs +++ b/src/libcore/clone.rs @@ -49,6 +49,11 @@ use marker::Sized; /// A common trait for cloning an object. /// /// This trait can be used with `#[derive]`. +/// +/// Types that are `Copy` should have a trivial implementation of `Clone`. More formally: +/// if `T: Copy`, `x: T`, and `y: &T`, then `let x = y.clone();` is equivalent to `let x = *y;`. +/// Manual implementations should be careful to uphold this invariant; however, unsafe code +/// must not rely on it to ensure memory safety. #[stable(feature = "rust1", since = "1.0.0")] pub trait Clone : Sized { /// Returns a copy of the value. From 9f302b6bcdc7b3d35a074ff565e17350f12038b7 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Wed, 4 May 2016 23:28:50 +0530 Subject: [PATCH 6/8] Add long diagnostics for E0414 --- src/librustc_resolve/diagnostics.rs | 64 ++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/src/librustc_resolve/diagnostics.rs b/src/librustc_resolve/diagnostics.rs index 56d3b927dc592..fda7c7aeb3306 100644 --- a/src/librustc_resolve/diagnostics.rs +++ b/src/librustc_resolve/diagnostics.rs @@ -623,6 +623,69 @@ let Foo = 12i32; // ok! The goal here is to avoid a conflict of names. "##, +E0414: r##" +A variable binding in an irrefutable pattern is shadowing the name of a +constant. Example of erroneous code: + +```compile_fail +const FOO: u8 = 7; + +let FOO = 5; // error: variable bindings cannot shadow constants + +// or + +fn bar(FOO: u8) { // error: variable bindings cannot shadow constants + +} + +// or + +for FOO in bar { + +} +``` + +Introducing a new variable in Rust is done through a pattern. Thus you can have +`let` bindings like `let (a, b) = ...`. However, patterns also allow constants +in them, e.g. if you want to match over a constant: + +```ignore +const FOO: u8 = 1; + +match (x,y) { + (3, 4) => { .. }, // it is (3,4) + (FOO, 1) => { .. }, // it is (1,1) + (foo, 1) => { .. }, // it is (anything, 1) + // call the value in the first slot "foo" + _ => { .. } // it is anything +} +``` + +Here, the second arm matches the value of `x` against the constant `FOO`, +whereas the third arm will accept any value of `x` and call it `foo`. + +This works for `match`, however in cases where an irrefutable pattern is +required, constants can't be used. An irrefutable pattern is one which always +matches, whose purpose is only to bind variable names to values. These are +required by let, for, and function argument patterns. + +Refutable patterns in such a situation do not make sense, for example: + +```ignore +let Some(x) = foo; // what if foo is None, instead? + +let (1, x) = foo; // what if foo.0 is not 1? + +let (SOME_CONST, x) = foo; // what if foo.0 is not SOME_CONST? + +let SOME_CONST = foo; // what if foo is not SOME_CONST? +``` + +Thus, an irrefutable variable binding can't contain a constant. + +To fix this error, just give the marked variable a different name. +"##, + E0415: r##" More than one function parameter have the same name. Example of erroneous code: @@ -1086,7 +1149,6 @@ register_diagnostics! { E0409, // variable is bound with different mode in pattern # than in // pattern #1 E0410, // variable from pattern is not bound in pattern 1 - E0414, // only irrefutable patterns allowed here E0418, // is not an enum variant, struct or const E0420, // is not an associated const E0421, // unresolved associated const From 21117259b096b5e3438bcb0bcb918f8df860c9a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Marie?= Date: Thu, 5 May 2016 06:41:49 +0200 Subject: [PATCH 7/8] specify the archive file as stdout If the `-f` option isn't given, GNU tar will use environment variable `TAPE` first, and next use the compiled-in default, which isn't necessary `stdout` (it is the tape device `/dev/rst0` under OpenBSD for example). --- mk/dist.mk | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mk/dist.mk b/mk/dist.mk index 7fe28a7e26250..9491311ea7c12 100644 --- a/mk/dist.mk +++ b/mk/dist.mk @@ -77,6 +77,7 @@ $(PKG_TAR): $(PKG_FILES) $(Q)mkdir -p tmp/dist/$(PKG_NAME) $(Q)tar \ -C $(S) \ + -f - \ --exclude-vcs \ --exclude=*~ \ --exclude=*.pyc \ @@ -86,7 +87,7 @@ $(PKG_TAR): $(PKG_FILES) --exclude=*/llvm/test/*/*/*.ll \ --exclude=*/llvm/test/*/*/*.td \ --exclude=*/llvm/test/*/*/*.s \ - -c $(UNROOTED_PKG_FILES) | tar -x -C tmp/dist/$(PKG_NAME) + -c $(UNROOTED_PKG_FILES) | tar -x -f - -C tmp/dist/$(PKG_NAME) @$(call E, making $@) $(Q)tar -czf $(PKG_TAR) -C tmp/dist $(PKG_NAME) $(Q)rm -Rf tmp/dist/$(PKG_NAME) From a9779df1886e18923f40cf0bb67ab72d4e4942df Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Wed, 4 May 2016 22:42:14 -0700 Subject: [PATCH 8/8] Implement RFC 1542 cc #33417 --- src/libcore/convert.rs | 57 ++++- src/libcore/num/mod.rs | 95 +++++++- src/libcoretest/lib.rs | 1 + src/libcoretest/num/mod.rs | 447 +++++++++++++++++++++++++------------ src/libstd/error.rs | 7 + src/libstd/lib.rs | 1 + src/libstd/num/mod.rs | 2 +- 7 files changed, 451 insertions(+), 159 deletions(-) diff --git a/src/libcore/convert.rs b/src/libcore/convert.rs index 2d999868f71ec..48421abc7bbdf 100644 --- a/src/libcore/convert.rs +++ b/src/libcore/convert.rs @@ -20,18 +20,19 @@ //! - Impl the `As*` traits for reference-to-reference conversions //! - Impl the `Into` trait when you want to consume the value in the conversion //! - The `From` trait is the most flexible, useful for value _and_ reference conversions +//! - The `TryFrom` and `TryInto` traits behave like `From` and `Into`, but allow for the +//! conversion to fail //! -//! As a library author, you should prefer implementing `From` rather than -//! `Into`, as `From` provides greater flexibility and offers an equivalent `Into` -//! implementation for free, thanks to a blanket implementation in the standard library. -//! -//! **Note: these traits must not fail**. If the conversion can fail, you must use a dedicated -//! method which returns an `Option` or a `Result`. +//! As a library author, you should prefer implementing `From` or `TryFrom` rather than +//! `Into` or `TryInto`, as `From` and `TryFrom` provide greater flexibility and offer +//! equivalent `Into` or `TryInto` implementations for free, thanks to a blanket implementation +//! in the standard library. //! //! # Generic impl //! //! - `AsRef` and `AsMut` auto-dereference if the inner type is a reference //! - `From for T` implies `Into for U` +//! - `TryFrom for T` implies `TryInto for U` //! - `From` and `Into` are reflexive, which means that all types can `into()` //! themselves and `from()` themselves //! @@ -40,6 +41,7 @@ #![stable(feature = "rust1", since = "1.0.0")] use marker::Sized; +use result::Result; /// A cheap, reference-to-reference conversion. /// @@ -98,8 +100,8 @@ pub trait AsMut { /// A conversion that consumes `self`, which may or may not be expensive. /// -/// **Note: this trait must not fail**. If the conversion can fail, use a dedicated method which -/// returns an `Option` or a `Result`. +/// **Note: this trait must not fail**. If the conversion can fail, use `TryInto` or a dedicated +/// method which returns an `Option` or a `Result`. /// /// Library authors should not directly implement this trait, but should prefer implementing /// the `From` trait, which offers greater flexibility and provides an equivalent `Into` @@ -133,8 +135,8 @@ pub trait Into: Sized { /// Construct `Self` via a conversion. /// -/// **Note: this trait must not fail**. If the conversion can fail, use a dedicated method which -/// returns an `Option` or a `Result`. +/// **Note: this trait must not fail**. If the conversion can fail, use `TryFrom` or a dedicated +/// method which returns an `Option` or a `Result`. /// /// # Examples /// @@ -158,6 +160,30 @@ pub trait From: Sized { fn from(T) -> Self; } +/// An attempted conversion that consumes `self`, which may or may not be expensive. +/// +/// Library authors should not directly implement this trait, but should prefer implementing +/// the `TryFrom` trait, which offers greater flexibility and provides an equivalent `TryInto` +/// implementation for free, thanks to a blanket implementation in the standard library. +#[unstable(feature = "try_from", issue = "33417")] +pub trait TryInto: Sized { + /// The type returned in the event of a conversion error. + type Err; + + /// Performs the conversion. + fn try_into(self) -> Result; +} + +/// Attempt to construct `Self` via a conversion. +#[unstable(feature = "try_from", issue = "33417")] +pub trait TryFrom: Sized { + /// The type returned in the event of a conversion error. + type Err; + + /// Performs the conversion. + fn try_from(T) -> Result; +} + //////////////////////////////////////////////////////////////////////////////// // GENERIC IMPLS //////////////////////////////////////////////////////////////////////////////// @@ -216,6 +242,17 @@ impl From for T { fn from(t: T) -> T { t } } + +// TryFrom implies TryInto +#[unstable(feature = "try_from", issue = "33417")] +impl TryInto for T where U: TryFrom { + type Err = U::Err; + + fn try_into(self) -> Result { + U::try_from(self) + } +} + //////////////////////////////////////////////////////////////////////////////// // CONCRETE IMPLS //////////////////////////////////////////////////////////////////////////////// diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 589ac90b308ad..e048c963d1556 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -15,7 +15,7 @@ use char::CharExt; use cmp::PartialOrd; -use convert::From; +use convert::{From, TryFrom}; use fmt; use intrinsics; use marker::{Copy, Sized}; @@ -2341,9 +2341,101 @@ macro_rules! from_str_radix_int_impl { } from_str_radix_int_impl! { isize i8 i16 i32 i64 usize u8 u16 u32 u64 } +/// The error type returned when a checked integral type conversion fails. +#[unstable(feature = "try_from", issue = "33417")] +#[derive(Debug, Copy, Clone)] +pub struct TryFromIntError(()); + +impl TryFromIntError { + #[unstable(feature = "int_error_internals", + reason = "available through Error trait and this method should \ + not be exposed publicly", + issue = "0")] + #[doc(hidden)] + pub fn __description(&self) -> &str { + "out of range integral type conversion attempted" + } +} + +#[unstable(feature = "try_from", issue = "33417")] +impl fmt::Display for TryFromIntError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + self.__description().fmt(fmt) + } +} + +macro_rules! same_sign_from_int_impl { + ($storage:ty, $target:ty, $($source:ty),*) => {$( + #[stable(feature = "rust1", since = "1.0.0")] + impl TryFrom<$source> for $target { + type Err = TryFromIntError; + + fn try_from(u: $source) -> Result<$target, TryFromIntError> { + let min = <$target as FromStrRadixHelper>::min_value() as $storage; + let max = <$target as FromStrRadixHelper>::max_value() as $storage; + if u as $storage < min || u as $storage > max { + Err(TryFromIntError(())) + } else { + Ok(u as $target) + } + } + } + )*} +} + +same_sign_from_int_impl!(u64, u8, u8, u16, u32, u64, usize); +same_sign_from_int_impl!(i64, i8, i8, i16, i32, i64, isize); +same_sign_from_int_impl!(u64, u16, u8, u16, u32, u64, usize); +same_sign_from_int_impl!(i64, i16, i8, i16, i32, i64, isize); +same_sign_from_int_impl!(u64, u32, u8, u16, u32, u64, usize); +same_sign_from_int_impl!(i64, i32, i8, i16, i32, i64, isize); +same_sign_from_int_impl!(u64, u64, u8, u16, u32, u64, usize); +same_sign_from_int_impl!(i64, i64, i8, i16, i32, i64, isize); +same_sign_from_int_impl!(u64, usize, u8, u16, u32, u64, usize); +same_sign_from_int_impl!(i64, isize, i8, i16, i32, i64, isize); + +macro_rules! cross_sign_from_int_impl { + ($unsigned:ty, $($signed:ty),*) => {$( + #[stable(feature = "rust1", since = "1.0.0")] + impl TryFrom<$unsigned> for $signed { + type Err = TryFromIntError; + + fn try_from(u: $unsigned) -> Result<$signed, TryFromIntError> { + let max = <$signed as FromStrRadixHelper>::max_value() as u64; + if u as u64 > max { + Err(TryFromIntError(())) + } else { + Ok(u as $signed) + } + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + impl TryFrom<$signed> for $unsigned { + type Err = TryFromIntError; + + fn try_from(u: $signed) -> Result<$unsigned, TryFromIntError> { + let max = <$unsigned as FromStrRadixHelper>::max_value() as u64; + if u < 0 || u as u64 > max { + Err(TryFromIntError(())) + } else { + Ok(u as $unsigned) + } + } + } + )*} +} + +cross_sign_from_int_impl!(u8, i8, i16, i32, i64, isize); +cross_sign_from_int_impl!(u16, i8, i16, i32, i64, isize); +cross_sign_from_int_impl!(u32, i8, i16, i32, i64, isize); +cross_sign_from_int_impl!(u64, i8, i16, i32, i64, isize); +cross_sign_from_int_impl!(usize, i8, i16, i32, i64, isize); + #[doc(hidden)] trait FromStrRadixHelper: PartialOrd + Copy { fn min_value() -> Self; + fn max_value() -> Self; fn from_u32(u: u32) -> Self; fn checked_mul(&self, other: u32) -> Option; fn checked_sub(&self, other: u32) -> Option; @@ -2353,6 +2445,7 @@ trait FromStrRadixHelper: PartialOrd + Copy { macro_rules! doit { ($($t:ty)*) => ($(impl FromStrRadixHelper for $t { fn min_value() -> Self { Self::min_value() } + fn max_value() -> Self { Self::max_value() } fn from_u32(u: u32) -> Self { u as Self } fn checked_mul(&self, other: u32) -> Option { Self::checked_mul(*self, other as Self) diff --git a/src/libcoretest/lib.rs b/src/libcoretest/lib.rs index aa7ab4b4e3f85..a3c46ffcea5d6 100644 --- a/src/libcoretest/lib.rs +++ b/src/libcoretest/lib.rs @@ -34,6 +34,7 @@ #![feature(unboxed_closures)] #![feature(unicode)] #![feature(unique)] +#![feature(try_from)] extern crate core; extern crate test; diff --git a/src/libcoretest/num/mod.rs b/src/libcoretest/num/mod.rs index 11c1bd667fb36..642aea9f4f733 100644 --- a/src/libcoretest/num/mod.rs +++ b/src/libcoretest/num/mod.rs @@ -8,10 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use core::convert::TryFrom; use core::cmp::PartialEq; use core::fmt::Debug; -use core::ops::{Add, Sub, Mul, Div, Rem}; use core::marker::Copy; +use core::num::Float; +use core::ops::{Add, Sub, Mul, Div, Rem}; +use core::option::Option; +use core::option::Option::{Some, None}; #[macro_use] mod int_macros; @@ -48,169 +52,318 @@ pub fn test_num(ten: T, two: T) where assert_eq!(ten.rem(two), ten % two); } -#[cfg(test)] -mod tests { - use core::option::Option; - use core::option::Option::{Some, None}; - use core::num::Float; - - #[test] - fn from_str_issue7588() { - let u : Option = u8::from_str_radix("1000", 10).ok(); - assert_eq!(u, None); - let s : Option = i16::from_str_radix("80000", 10).ok(); - assert_eq!(s, None); - } +#[test] +fn from_str_issue7588() { + let u : Option = u8::from_str_radix("1000", 10).ok(); + assert_eq!(u, None); + let s : Option = i16::from_str_radix("80000", 10).ok(); + assert_eq!(s, None); +} - #[test] - fn test_int_from_str_overflow() { - let mut i8_val: i8 = 127; - assert_eq!("127".parse::().ok(), Some(i8_val)); - assert_eq!("128".parse::().ok(), None); +#[test] +fn test_int_from_str_overflow() { + let mut i8_val: i8 = 127; + assert_eq!("127".parse::().ok(), Some(i8_val)); + assert_eq!("128".parse::().ok(), None); - i8_val = i8_val.wrapping_add(1); - assert_eq!("-128".parse::().ok(), Some(i8_val)); - assert_eq!("-129".parse::().ok(), None); + i8_val = i8_val.wrapping_add(1); + assert_eq!("-128".parse::().ok(), Some(i8_val)); + assert_eq!("-129".parse::().ok(), None); - let mut i16_val: i16 = 32_767; - assert_eq!("32767".parse::().ok(), Some(i16_val)); - assert_eq!("32768".parse::().ok(), None); + let mut i16_val: i16 = 32_767; + assert_eq!("32767".parse::().ok(), Some(i16_val)); + assert_eq!("32768".parse::().ok(), None); - i16_val = i16_val.wrapping_add(1); - assert_eq!("-32768".parse::().ok(), Some(i16_val)); - assert_eq!("-32769".parse::().ok(), None); + i16_val = i16_val.wrapping_add(1); + assert_eq!("-32768".parse::().ok(), Some(i16_val)); + assert_eq!("-32769".parse::().ok(), None); - let mut i32_val: i32 = 2_147_483_647; - assert_eq!("2147483647".parse::().ok(), Some(i32_val)); - assert_eq!("2147483648".parse::().ok(), None); + let mut i32_val: i32 = 2_147_483_647; + assert_eq!("2147483647".parse::().ok(), Some(i32_val)); + assert_eq!("2147483648".parse::().ok(), None); - i32_val = i32_val.wrapping_add(1); - assert_eq!("-2147483648".parse::().ok(), Some(i32_val)); - assert_eq!("-2147483649".parse::().ok(), None); + i32_val = i32_val.wrapping_add(1); + assert_eq!("-2147483648".parse::().ok(), Some(i32_val)); + assert_eq!("-2147483649".parse::().ok(), None); - let mut i64_val: i64 = 9_223_372_036_854_775_807; - assert_eq!("9223372036854775807".parse::().ok(), Some(i64_val)); - assert_eq!("9223372036854775808".parse::().ok(), None); + let mut i64_val: i64 = 9_223_372_036_854_775_807; + assert_eq!("9223372036854775807".parse::().ok(), Some(i64_val)); + assert_eq!("9223372036854775808".parse::().ok(), None); - i64_val = i64_val.wrapping_add(1); - assert_eq!("-9223372036854775808".parse::().ok(), Some(i64_val)); - assert_eq!("-9223372036854775809".parse::().ok(), None); - } + i64_val = i64_val.wrapping_add(1); + assert_eq!("-9223372036854775808".parse::().ok(), Some(i64_val)); + assert_eq!("-9223372036854775809".parse::().ok(), None); +} + +#[test] +fn test_leading_plus() { + assert_eq!("+127".parse::().ok(), Some(127)); + assert_eq!("+9223372036854775807".parse::().ok(), Some(9223372036854775807)); +} - #[test] - fn test_leading_plus() { - assert_eq!("+127".parse::().ok(), Some(127)); - assert_eq!("+9223372036854775807".parse::().ok(), Some(9223372036854775807)); +#[test] +fn test_invalid() { + assert_eq!("--129".parse::().ok(), None); + assert_eq!("++129".parse::().ok(), None); + assert_eq!("Съешь".parse::().ok(), None); +} + +#[test] +fn test_empty() { + assert_eq!("-".parse::().ok(), None); + assert_eq!("+".parse::().ok(), None); + assert_eq!("".parse::().ok(), None); +} + +macro_rules! test_impl_from { + ($fn_name: ident, $Small: ty, $Large: ty) => { + #[test] + fn $fn_name() { + let small_max = <$Small>::max_value(); + let small_min = <$Small>::min_value(); + let large_max: $Large = small_max.into(); + let large_min: $Large = small_min.into(); + assert_eq!(large_max as $Small, small_max); + assert_eq!(large_min as $Small, small_min); + } } +} + +// Unsigned -> Unsigned +test_impl_from! { test_u8u16, u8, u16 } +test_impl_from! { test_u8u32, u8, u32 } +test_impl_from! { test_u8u64, u8, u64 } +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 } + +// Signed -> Signed +test_impl_from! { test_i8i16, i8, i16 } +test_impl_from! { test_i8i32, i8, i32 } +test_impl_from! { test_i8i64, i8, i64 } +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 } + +// Unsigned -> Signed +test_impl_from! { test_u8i16, u8, i16 } +test_impl_from! { test_u8i32, u8, i32 } +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 } + +// Signed -> Float +test_impl_from! { test_i8f32, i8, f32 } +test_impl_from! { test_i8f64, i8, f64 } +test_impl_from! { test_i16f32, i16, f32 } +test_impl_from! { test_i16f64, i16, f64 } +test_impl_from! { test_i32f64, i32, f64 } + +// Unsigned -> Float +test_impl_from! { test_u8f32, u8, f32 } +test_impl_from! { test_u8f64, u8, f64 } +test_impl_from! { test_u16f32, u16, f32 } +test_impl_from! { test_u16f64, u16, f64 } +test_impl_from! { test_u32f64, u32, f64 } + +// Float -> Float +#[test] +fn test_f32f64() { + use core::f32; + + let max: f64 = f32::MAX.into(); + assert_eq!(max as f32, f32::MAX); + assert!(max.is_normal()); + + let min: f64 = f32::MIN.into(); + assert_eq!(min as f32, f32::MIN); + assert!(min.is_normal()); + + let min_positive: f64 = f32::MIN_POSITIVE.into(); + assert_eq!(min_positive as f32, f32::MIN_POSITIVE); + assert!(min_positive.is_normal()); + + let epsilon: f64 = f32::EPSILON.into(); + assert_eq!(epsilon as f32, f32::EPSILON); + assert!(epsilon.is_normal()); + + let zero: f64 = (0.0f32).into(); + assert_eq!(zero as f32, 0.0f32); + assert!(zero.is_sign_positive()); + + let neg_zero: f64 = (-0.0f32).into(); + assert_eq!(neg_zero as f32, -0.0f32); + assert!(neg_zero.is_sign_negative()); + + let infinity: f64 = f32::INFINITY.into(); + assert_eq!(infinity as f32, f32::INFINITY); + assert!(infinity.is_infinite()); + assert!(infinity.is_sign_positive()); + + let neg_infinity: f64 = f32::NEG_INFINITY.into(); + assert_eq!(neg_infinity as f32, f32::NEG_INFINITY); + assert!(neg_infinity.is_infinite()); + assert!(neg_infinity.is_sign_negative()); + + let nan: f64 = f32::NAN.into(); + assert!(nan.is_nan()); +} - #[test] - fn test_invalid() { - assert_eq!("--129".parse::().ok(), None); - assert_eq!("++129".parse::().ok(), None); - assert_eq!("Съешь".parse::().ok(), None); +macro_rules! test_impl_try_from_always_ok { + ($fn_name:ident, $source:ty, $target: ty) => { + #[test] + fn $fn_name() { + let max = <$source>::max_value(); + let min = <$source>::min_value(); + let zero: $source = 0; + assert_eq!(<$target as TryFrom<$source>>::try_from(max).unwrap(), + max as $target); + assert_eq!(<$target as TryFrom<$source>>::try_from(min).unwrap(), + min as $target); + assert_eq!(<$target as TryFrom<$source>>::try_from(zero).unwrap(), + zero as $target); + } } +} - #[test] - fn test_empty() { - assert_eq!("-".parse::().ok(), None); - assert_eq!("+".parse::().ok(), None); - assert_eq!("".parse::().ok(), None); +test_impl_try_from_always_ok! { test_try_u8u8, u8, u8 } +test_impl_try_from_always_ok! { test_try_u8u16, u8, u16 } +test_impl_try_from_always_ok! { test_try_u8u32, u8, u32 } +test_impl_try_from_always_ok! { test_try_u8u64, u8, u64 } +test_impl_try_from_always_ok! { test_try_u8i16, u8, i16 } +test_impl_try_from_always_ok! { test_try_u8i32, u8, i32 } +test_impl_try_from_always_ok! { test_try_u8i64, u8, i64 } + +test_impl_try_from_always_ok! { test_try_u16u16, u16, u16 } +test_impl_try_from_always_ok! { test_try_u16u32, u16, u32 } +test_impl_try_from_always_ok! { test_try_u16u64, u16, u64 } +test_impl_try_from_always_ok! { test_try_u16i32, u16, i32 } +test_impl_try_from_always_ok! { test_try_u16i64, u16, i64 } + +test_impl_try_from_always_ok! { test_try_u32u32, u32, u32 } +test_impl_try_from_always_ok! { test_try_u32u64, u32, u64 } +test_impl_try_from_always_ok! { test_try_u32i64, u32, i64 } + +test_impl_try_from_always_ok! { test_try_u64u64, u64, u64 } + +test_impl_try_from_always_ok! { test_try_i8i8, i8, i8 } +test_impl_try_from_always_ok! { test_try_i8i16, i8, i16 } +test_impl_try_from_always_ok! { test_try_i8i32, i8, i32 } +test_impl_try_from_always_ok! { test_try_i8i64, i8, i64 } + +test_impl_try_from_always_ok! { test_try_i16i16, i16, i16 } +test_impl_try_from_always_ok! { test_try_i16i32, i16, i32 } +test_impl_try_from_always_ok! { test_try_i16i64, i16, i64 } + +test_impl_try_from_always_ok! { test_try_i32i32, i32, i32 } +test_impl_try_from_always_ok! { test_try_i32i64, i32, i64 } + +test_impl_try_from_always_ok! { test_try_i64i64, i64, i64 } + +macro_rules! test_impl_try_from_signed_to_unsigned_upper_ok { + ($fn_name:ident, $source:ty, $target:ty) => { + #[test] + fn $fn_name() { + let max = <$source>::max_value(); + let min = <$source>::min_value(); + let zero: $source = 0; + let neg_one: $source = -1; + assert_eq!(<$target as TryFrom<$source>>::try_from(max).unwrap(), + max as $target); + assert!(<$target as TryFrom<$source>>::try_from(min).is_err()); + assert_eq!(<$target as TryFrom<$source>>::try_from(zero).unwrap(), + zero as $target); + assert!(<$target as TryFrom<$source>>::try_from(neg_one).is_err()); + } } +} - macro_rules! test_impl_from { - ($fn_name: ident, $Small: ty, $Large: ty) => { - #[test] - fn $fn_name() { - let small_max = <$Small>::max_value(); - let small_min = <$Small>::min_value(); - let large_max: $Large = small_max.into(); - let large_min: $Large = small_min.into(); - assert_eq!(large_max as $Small, small_max); - assert_eq!(large_min as $Small, small_min); - } +test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i8u8, i8, u8 } +test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i8u16, i8, u16 } +test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i8u32, i8, u32 } +test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i8u64, i8, u64 } + +test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i16u16, i16, u16 } +test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i16u32, i16, u32 } +test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i16u64, i16, u64 } + +test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i32u32, i32, u32 } +test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i32u64, i32, u64 } + +test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i64u64, i64, u64 } + +macro_rules! test_impl_try_from_unsigned_to_signed_upper_err { + ($fn_name:ident, $source:ty, $target:ty) => { + #[test] + fn $fn_name() { + let max = <$source>::max_value(); + let min = <$source>::min_value(); + let zero: $source = 0; + assert!(<$target as TryFrom<$source>>::try_from(max).is_err()); + assert_eq!(<$target as TryFrom<$source>>::try_from(min).unwrap(), + min as $target); + assert_eq!(<$target as TryFrom<$source>>::try_from(zero).unwrap(), + zero as $target); } } +} - // Unsigned -> Unsigned - test_impl_from! { test_u8u16, u8, u16 } - test_impl_from! { test_u8u32, u8, u32 } - test_impl_from! { test_u8u64, u8, u64 } - 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 } - - // Signed -> Signed - test_impl_from! { test_i8i16, i8, i16 } - test_impl_from! { test_i8i32, i8, i32 } - test_impl_from! { test_i8i64, i8, i64 } - 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 } - - // Unsigned -> Signed - test_impl_from! { test_u8i16, u8, i16 } - test_impl_from! { test_u8i32, u8, i32 } - 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 } - - // Signed -> Float - test_impl_from! { test_i8f32, i8, f32 } - test_impl_from! { test_i8f64, i8, f64 } - test_impl_from! { test_i16f32, i16, f32 } - test_impl_from! { test_i16f64, i16, f64 } - test_impl_from! { test_i32f64, i32, f64 } - - // Unsigned -> Float - test_impl_from! { test_u8f32, u8, f32 } - test_impl_from! { test_u8f64, u8, f64 } - test_impl_from! { test_u16f32, u16, f32 } - test_impl_from! { test_u16f64, u16, f64 } - test_impl_from! { test_u32f64, u32, f64 } - - // Float -> Float - #[test] - fn test_f32f64() { - use core::f32; - - let max: f64 = f32::MAX.into(); - assert_eq!(max as f32, f32::MAX); - assert!(max.is_normal()); - - let min: f64 = f32::MIN.into(); - assert_eq!(min as f32, f32::MIN); - assert!(min.is_normal()); - - let min_positive: f64 = f32::MIN_POSITIVE.into(); - assert_eq!(min_positive as f32, f32::MIN_POSITIVE); - assert!(min_positive.is_normal()); - - let epsilon: f64 = f32::EPSILON.into(); - assert_eq!(epsilon as f32, f32::EPSILON); - assert!(epsilon.is_normal()); - - let zero: f64 = (0.0f32).into(); - assert_eq!(zero as f32, 0.0f32); - assert!(zero.is_sign_positive()); - - let neg_zero: f64 = (-0.0f32).into(); - assert_eq!(neg_zero as f32, -0.0f32); - assert!(neg_zero.is_sign_negative()); - - let infinity: f64 = f32::INFINITY.into(); - assert_eq!(infinity as f32, f32::INFINITY); - assert!(infinity.is_infinite()); - assert!(infinity.is_sign_positive()); - - let neg_infinity: f64 = f32::NEG_INFINITY.into(); - assert_eq!(neg_infinity as f32, f32::NEG_INFINITY); - assert!(neg_infinity.is_infinite()); - assert!(neg_infinity.is_sign_negative()); - - let nan: f64 = f32::NAN.into(); - assert!(nan.is_nan()); +test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u8i8, u8, i8 } + +test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u16i8, u16, i8 } +test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u16i16, u16, i16 } + +test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u32i8, u32, i8 } +test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u32i16, u32, i16 } +test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u32i32, u32, i32 } + +test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u64i8, u64, i8 } +test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u64i16, u64, i16 } +test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u64i32, u64, i32 } +test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u64i64, u64, i64 } + +macro_rules! test_impl_try_from_same_sign_err { + ($fn_name:ident, $source:ty, $target:ty) => { + #[test] + fn $fn_name() { + let max = <$source>::max_value(); + let min = <$source>::min_value(); + let zero: $source = 0; + let t_max = <$target>::max_value(); + let t_min = <$target>::min_value(); + assert!(<$target as TryFrom<$source>>::try_from(max).is_err()); + if min != 0 { + assert!(<$target as TryFrom<$source>>::try_from(min).is_err()); + } + assert_eq!(<$target as TryFrom<$source>>::try_from(zero).unwrap(), + zero as $target); + assert_eq!(<$target as TryFrom<$source>>::try_from(t_max as $source) + .unwrap(), + t_max as $target); + assert_eq!(<$target as TryFrom<$source>>::try_from(t_min as $source) + .unwrap(), + t_min as $target); + } } } + +test_impl_try_from_same_sign_err! { test_try_u16u8, u16, u8 } + +test_impl_try_from_same_sign_err! { test_try_u32u8, u32, u8 } +test_impl_try_from_same_sign_err! { test_try_u32u16, u32, u16 } + +test_impl_try_from_same_sign_err! { test_try_u64u8, u64, u8 } +test_impl_try_from_same_sign_err! { test_try_u64u16, u64, u16 } +test_impl_try_from_same_sign_err! { test_try_u64u32, u64, u32 } + +test_impl_try_from_same_sign_err! { test_try_i16i8, i16, i8 } + +test_impl_try_from_same_sign_err! { test_try_i32i8, i32, i8 } +test_impl_try_from_same_sign_err! { test_try_i32i16, i32, i16 } + +test_impl_try_from_same_sign_err! { test_try_i64i8, i64, i8 } +test_impl_try_from_same_sign_err! { test_try_i64i16, i64, i16 } +test_impl_try_from_same_sign_err! { test_try_i64i32, i64, i32 } diff --git a/src/libstd/error.rs b/src/libstd/error.rs index 35cd4a5ec5292..d49d97649467a 100644 --- a/src/libstd/error.rs +++ b/src/libstd/error.rs @@ -159,6 +159,13 @@ impl Error for num::ParseIntError { } } +#[unstable(feature = "try_from", issue = "33417")] +impl Error for num::TryFromIntError { + fn description(&self) -> &str { + self.__description() + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Error for num::ParseFloatError { fn description(&self) -> &str { diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index e14a31453d381..d4b40b844fce5 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -270,6 +270,7 @@ #![feature(vec_push_all)] #![feature(zero_one)] #![feature(question_mark)] +#![feature(try_from)] // Issue# 30592: Systematically use alloc_system during stage0 since jemalloc // might be unavailable or disabled diff --git a/src/libstd/num/mod.rs b/src/libstd/num/mod.rs index 1886b4fdf59c6..d33df05acf224 100644 --- a/src/libstd/num/mod.rs +++ b/src/libstd/num/mod.rs @@ -19,7 +19,7 @@ #[stable(feature = "rust1", since = "1.0.0")] pub use core::num::{Zero, One}; #[stable(feature = "rust1", since = "1.0.0")] -pub use core::num::{FpCategory, ParseIntError, ParseFloatError}; +pub use core::num::{FpCategory, ParseIntError, ParseFloatError, TryFromIntError}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::num::Wrapping;