From d9c508967a9d0c4bfd0cd968f310693877cb4159 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Tue, 29 Nov 2016 00:12:07 +0200 Subject: [PATCH 1/5] evaluate obligations in LIFO order during closure projection This is an annoying gotcha with the projection cache's handling of nested obligations. Nested projection obligations enter the issue in this case: ``` DEBUG:rustc::traits::project: AssociatedTypeNormalizer: depth=3 normalized , [closure@not-a-recursion-error.rs:5:30: 5:53]> as std::iter::IntoIterator>::Item to _#7t with 12 add'l obligations ``` Here the normalization result is the result of the nested impl `<[closure@not-a-recursion-error.rs:5:30: 5:53] as FnMut(i32)>::Output`, which is an additional obligation that is a part of "add'l obligations". By itself, this is proper behaviour - the additional obligation is returned, and the RFC 447 rules ensure that it is processed before the output `#_7t` is used in any way. However, the projection cache breaks this - it caches the `,[closure@not-a-recursion-error.rs:5:30: 5:53]> as std::iter::IntoIterator>::Item = #_7t` resolution. Now everybody else that attempts to look up the projection will just get `#_7t` *without* any additional obligations. This obviously causes all sorts of trouble (here a spurious `EvaluatedToAmbig` results in specializations not being discarded [here](https://github.com/rust-lang/rust/blob/9ca50bd4d50b55456e88a8c3ad8fcc9798f57522/src/librustc/traits/select.rs#L1705)). The compiler works even with this projection cache gotcha because in most cases during "one-pass evaluation". we tend to process obligations in LIFO order - after an obligation is added to the cache, we process its nested obligations before we do anything else (and if we have a cycle, we handle it specifically) - which makes sure the inference variables are resolved before they are used. That "LIFO" order That was not done when projecting out of a closure, so let's just fix that for the time being. Fixes #38033. --- src/librustc/traits/project.rs | 2 +- src/test/run-pass/issue-38033.rs | 88 ++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 src/test/run-pass/issue-38033.rs diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index ce882c48377f7..9db9e8812fd05 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -1222,8 +1222,8 @@ fn confirm_closure_candidate<'cx, 'gcx, 'tcx>( obligation, &closure_type.sig, util::TupleArgumentsFlag::No) - .with_addl_obligations(obligations) .with_addl_obligations(vtable.nested) + .with_addl_obligations(obligations) } fn confirm_callable_candidate<'cx, 'gcx, 'tcx>( diff --git a/src/test/run-pass/issue-38033.rs b/src/test/run-pass/issue-38033.rs new file mode 100644 index 0000000000000..50549dc8b2380 --- /dev/null +++ b/src/test/run-pass/issue-38033.rs @@ -0,0 +1,88 @@ +// 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::marker; +use std::mem; + +fn main() { + let workers = (0..0).map(|_| result::()); + drop(join_all(workers).poll()); +} + +trait Future { + type Item; + type Error; + + fn poll(&mut self) -> Result; +} + +trait IntoFuture { + type Future: Future; + type Item; + type Error; + + fn into_future(self) -> Self::Future; +} + +impl IntoFuture for F { + type Future = F; + type Item = F::Item; + type Error = F::Error; + + fn into_future(self) -> F { + self + } +} + +struct FutureResult { + _inner: marker::PhantomData<(T, E)>, +} + +fn result() -> FutureResult { + loop {} +} + +impl Future for FutureResult { + type Item = T; + type Error = E; + + fn poll(&mut self) -> Result { + loop {} + } +} + +struct JoinAll + where I: IntoIterator, + I::Item: IntoFuture, +{ + elems: Vec<::Item>, +} + +fn join_all(_: I) -> JoinAll + where I: IntoIterator, + I::Item: IntoFuture, +{ + JoinAll { elems: vec![] } +} + +impl Future for JoinAll + where I: IntoIterator, + I::Item: IntoFuture, +{ + type Item = Vec<::Item>; + type Error = ::Error; + + fn poll(&mut self) -> Result { + let elems = mem::replace(&mut self.elems, Vec::new()); + Ok(elems.into_iter().map(|e| { + e + }).collect::>()) + } +} From 291a10dd28fedd9659928f28f4206bac71776539 Mon Sep 17 00:00:00 2001 From: est31 Date: Wed, 9 Nov 2016 07:23:57 +0100 Subject: [PATCH 2/5] Document the question mark operator --- src/doc/book/syntax-index.md | 2 ++ src/doc/reference.md | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/doc/book/syntax-index.md b/src/doc/book/syntax-index.md index 1e05b01d30d46..28403711cd701 100644 --- a/src/doc/book/syntax-index.md +++ b/src/doc/book/syntax-index.md @@ -94,6 +94,7 @@ * `|=` (`var |= expr`): bitwise or & assignment. Overloadable (`BitOrAssign`). * `||` (`expr || expr`): logical or. * `_`: "ignored" pattern binding (see [Patterns (Ignoring bindings)]). Also used to make integer-literals readable (see [Reference (Integer literals)]). +* `?` (`expr?`): Error propagation. Returns early when `Err(_)` is encountered, unwraps otherwise. Similar to the [`try!` macro]. ## Other Syntax @@ -210,6 +211,7 @@ [Functions]: functions.html [Generics]: generics.html [Iterators]: iterators.html +[`try!` macro]: error-handling.html#the-try-macro [Lifetimes]: lifetimes.html [Loops (`for`)]: loops.html#for [Loops (`loop`)]: loops.html#loop diff --git a/src/doc/reference.md b/src/doc/reference.md index 98238e27133ca..711a13d21f0eb 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -2858,8 +2858,8 @@ assert_eq!(x, y); ### Unary operator expressions -Rust defines the following unary operators. They are all written as prefix operators, -before the expression they apply to. +Rust defines the following unary operators. With the exception of `?`, they are +all written as prefix operators, before the expression they apply to. * `-` : Negation. Signed integer types and floating-point types support negation. It @@ -2888,6 +2888,10 @@ before the expression they apply to. If the `&` or `&mut` operators are applied to an rvalue, a temporary value is created; the lifetime of this temporary value is defined by [syntactic rules](#temporary-lifetimes). +* `?` + : Propagating errors if applied to `Err(_)` and unwrapping if + applied to `Ok(_)`. Only works on the `Result` type, + and written in postfix notation. ### Binary operator expressions From 49b9666bf6c190dc71f87bf2f798c9385901137c Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 12 Nov 2016 00:30:53 +0100 Subject: [PATCH 3/5] Fix invalid src url --- src/libcore/macros.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs index cae46a0dd0feb..23c2e2142c751 100644 --- a/src/libcore/macros.rs +++ b/src/libcore/macros.rs @@ -520,8 +520,7 @@ macro_rules! unimplemented { /// into libsyntax itself. /// /// For more information, see documentation for `std`'s macros. -#[cfg(dox)] -pub mod builtin { +mod builtin { /// The core macro for formatted string creation & output. /// /// For more information, see the documentation for [`std::format_args!`]. @@ -529,6 +528,7 @@ pub mod builtin { /// [`std::format_args!`]: ../std/macro.format_args.html #[stable(feature = "rust1", since = "1.0.0")] #[macro_export] + #[cfg(dox)] macro_rules! format_args { ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ }) } @@ -540,6 +540,7 @@ pub mod builtin { /// [`std::env!`]: ../std/macro.env.html #[stable(feature = "rust1", since = "1.0.0")] #[macro_export] + #[cfg(dox)] macro_rules! env { ($name:expr) => ({ /* compiler built-in */ }) } /// Optionally inspect an environment variable at compile time. @@ -549,6 +550,7 @@ pub mod builtin { /// [`std::option_env!`]: ../std/macro.option_env.html #[stable(feature = "rust1", since = "1.0.0")] #[macro_export] + #[cfg(dox)] macro_rules! option_env { ($name:expr) => ({ /* compiler built-in */ }) } /// Concatenate identifiers into one identifier. @@ -558,6 +560,7 @@ pub mod builtin { /// [`std::concat_idents!`]: ../std/macro.concat_idents.html #[unstable(feature = "concat_idents_macro", issue = "29599")] #[macro_export] + #[cfg(dox)] macro_rules! concat_idents { ($($e:ident),*) => ({ /* compiler built-in */ }) } @@ -569,6 +572,7 @@ pub mod builtin { /// [`std::concat!`]: ../std/macro.concat.html #[stable(feature = "rust1", since = "1.0.0")] #[macro_export] + #[cfg(dox)] macro_rules! concat { ($($e:expr),*) => ({ /* compiler built-in */ }) } /// A macro which expands to the line number on which it was invoked. @@ -578,6 +582,7 @@ pub mod builtin { /// [`std::line!`]: ../std/macro.line.html #[stable(feature = "rust1", since = "1.0.0")] #[macro_export] + #[cfg(dox)] macro_rules! line { () => ({ /* compiler built-in */ }) } /// A macro which expands to the column number on which it was invoked. @@ -587,6 +592,7 @@ pub mod builtin { /// [`std::column!`]: ../std/macro.column.html #[stable(feature = "rust1", since = "1.0.0")] #[macro_export] + #[cfg(dox)] macro_rules! column { () => ({ /* compiler built-in */ }) } /// A macro which expands to the file name from which it was invoked. @@ -596,6 +602,7 @@ pub mod builtin { /// [`std::file!`]: ../std/macro.file.html #[stable(feature = "rust1", since = "1.0.0")] #[macro_export] + #[cfg(dox)] macro_rules! file { () => ({ /* compiler built-in */ }) } /// A macro which stringifies its argument. @@ -605,6 +612,7 @@ pub mod builtin { /// [`std::stringify!`]: ../std/macro.stringify.html #[stable(feature = "rust1", since = "1.0.0")] #[macro_export] + #[cfg(dox)] macro_rules! stringify { ($t:tt) => ({ /* compiler built-in */ }) } /// Includes a utf8-encoded file as a string. @@ -614,6 +622,7 @@ pub mod builtin { /// [`std::include_str!`]: ../std/macro.include_str.html #[stable(feature = "rust1", since = "1.0.0")] #[macro_export] + #[cfg(dox)] macro_rules! include_str { ($file:expr) => ({ /* compiler built-in */ }) } /// Includes a file as a reference to a byte array. @@ -623,6 +632,7 @@ pub mod builtin { /// [`std::include_bytes!`]: ../std/macro.include_bytes.html #[stable(feature = "rust1", since = "1.0.0")] #[macro_export] + #[cfg(dox)] macro_rules! include_bytes { ($file:expr) => ({ /* compiler built-in */ }) } /// Expands to a string that represents the current module path. @@ -632,6 +642,7 @@ pub mod builtin { /// [`std::module_path!`]: ../std/macro.module_path.html #[stable(feature = "rust1", since = "1.0.0")] #[macro_export] + #[cfg(dox)] macro_rules! module_path { () => ({ /* compiler built-in */ }) } /// Boolean evaluation of configuration flags. @@ -641,6 +652,7 @@ pub mod builtin { /// [`std::cfg!`]: ../std/macro.cfg.html #[stable(feature = "rust1", since = "1.0.0")] #[macro_export] + #[cfg(dox)] macro_rules! cfg { ($($cfg:tt)*) => ({ /* compiler built-in */ }) } /// Parse a file as an expression or an item according to the context. @@ -650,5 +662,6 @@ pub mod builtin { /// [`std::include!`]: ../std/macro.include.html #[stable(feature = "rust1", since = "1.0.0")] #[macro_export] + #[cfg(dox)] macro_rules! include { ($file:expr) => ({ /* compiler built-in */ }) } } From 2ea4f05bfa3da6a69f29a83777e2f8190cc6201f Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sun, 27 Nov 2016 02:15:07 +0200 Subject: [PATCH 4/5] don't double-apply variant padding to const enums Fixes #38002. --- src/librustc_trans/adt.rs | 14 ++++------ src/test/run-pass/issue-38002.rs | 45 ++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 9 deletions(-) create mode 100644 src/test/run-pass/issue-38002.rs diff --git a/src/librustc_trans/adt.rs b/src/librustc_trans/adt.rs index 4d3361c1873f0..0f987933df980 100644 --- a/src/librustc_trans/adt.rs +++ b/src/librustc_trans/adt.rs @@ -689,9 +689,8 @@ pub fn trans_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>, discr: D let lldiscr = C_integral(Type::from_integer(ccx, d), discr.0 as u64, true); let mut vals_with_discr = vec![lldiscr]; vals_with_discr.extend_from_slice(vals); - let mut contents = build_const_struct(ccx, &variant, - &vals_with_discr[..]); - let needed_padding = l.size(dl).bytes() - variant.min_size.bytes(); + let mut contents = build_const_struct(ccx, &variant, &vals_with_discr[..]); + let needed_padding = l.size(dl).bytes() - variant.stride().bytes(); if needed_padding > 0 { contents.push(padding(ccx, needed_padding)); } @@ -704,8 +703,7 @@ pub fn trans_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>, discr: D } layout::Univariant { ref variant, .. } => { assert_eq!(discr, Disr(0)); - let contents = build_const_struct(ccx, - &variant, vals); + let contents = build_const_struct(ccx, &variant, vals); C_struct(ccx, &contents[..], variant.packed) } layout::Vector { .. } => { @@ -722,8 +720,7 @@ pub fn trans_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>, discr: D } layout::StructWrappedNullablePointer { ref nonnull, nndiscr, .. } => { if discr.0 == nndiscr { - C_struct(ccx, &build_const_struct(ccx, &nonnull, vals), - false) + C_struct(ccx, &build_const_struct(ccx, &nonnull, vals), false) } else { let fields = compute_fields(ccx, t, nndiscr as usize, false); let vals = fields.iter().map(|&ty| { @@ -731,8 +728,7 @@ pub fn trans_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>, discr: D // field; see #8506. C_null(type_of::sizing_type_of(ccx, ty)) }).collect::>(); - C_struct(ccx, &build_const_struct(ccx, &nonnull, &vals[..]), - false) + C_struct(ccx, &build_const_struct(ccx, &nonnull, &vals[..]), false) } } _ => bug!("trans_const: cannot handle type {} repreented as {:#?}", t, l) diff --git a/src/test/run-pass/issue-38002.rs b/src/test/run-pass/issue-38002.rs new file mode 100644 index 0000000000000..489d35e9147ad --- /dev/null +++ b/src/test/run-pass/issue-38002.rs @@ -0,0 +1,45 @@ +// 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. + +// Check that constant ADTs are translated OK, part k of N. + +#![feature(slice_patterns)] + +enum Bar { + C +} + +enum Foo { + A {}, + B { + y: usize, + z: Bar + }, +} + +const LIST: [(usize, Foo); 2] = [ + (51, Foo::B { y: 42, z: Bar::C }), + (52, Foo::B { y: 45, z: Bar::C }), +]; + +pub fn main() { + match LIST { + [ + (51, Foo::B { y: 42, z: Bar::C }), + (52, Foo::B { y: 45, z: Bar::C }) + ] => {} + _ => { + // I would want to print the enum here, but if + // the discriminant is garbage this causes an + // `unreachable` and silent process exit. + panic!("trivial match failed") + } + } +} From 41a998d320e1de6b9bb2119b6ca41765acf4b425 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Wed, 14 Dec 2016 23:58:49 +0000 Subject: [PATCH 5/5] Bump prereleease version --- mk/main.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mk/main.mk b/mk/main.mk index ac080c36fd1e0..27f491fcd75af 100644 --- a/mk/main.mk +++ b/mk/main.mk @@ -18,7 +18,7 @@ CFG_RELEASE_NUM=1.14.0 # An optional number to put after the label, e.g. '.2' -> '-beta.2' # NB Make sure it starts with a dot to conform to semver pre-release # versions (section 9) -CFG_PRERELEASE_VERSION=.3 +CFG_PRERELEASE_VERSION=.4 ifeq ($(CFG_RELEASE_CHANNEL),stable) # This is the normal semver version string, e.g. "0.12.0", "0.12.0-nightly"