about summary refs log tree commit diff
diff options
context:
space:
mode:
authorCorey Farwell <coreyf@rwell.org>2017-06-01 00:09:20 -0400
committerGitHub <noreply@github.com>2017-06-01 00:09:20 -0400
commitdbc9d71b179bfe7e25704c17a574b8f579d3f776 (patch)
tree53da0dbc76d8f47915489c77401a362174ea5cbd
parent422faf7a6fbedd75653f1568e69858f62b5c0fe7 (diff)
parent3119e634e178d8acaed4a2d4a9d52e3b76ae79cf (diff)
downloadrust-dbc9d71b179bfe7e25704c17a574b8f579d3f776.tar.gz
rust-dbc9d71b179bfe7e25704c17a574b8f579d3f776.zip
Rollup merge of #42275 - scottmcm:try-trait, r=nikomatsakis
Lower `?` to `Try` instead of `Carrier`

The easy parts of https://github.com/rust-lang/rfcs/pull/1859, whose FCP completed without further comments.

Just the trait and the lowering -- neither the error message improvements nor the insta-stable impl for Option nor exhaustive docs.

Based on a [github search](https://github.com/search?l=rust&p=1&q=question_mark_carrier&type=Code&utf8=%E2%9C%93), this will break the following:

- https://github.com/pfpacket/rust-9p/blob/00206e34c680198a0ac7c2f066cc2954187d4fac/src/serialize.rs#L38
- https://github.com/peterdelevoryas/bufparse/blob/b1325898f4fc2c67658049196c12da82548af350/src/result.rs#L50

The other results appear to be files from libcore or its tests.  I could also leave Carrier around after stage0 and `impl<T:Carrier> Try for T` if that would be better.

r? @nikomatsakis

Edit: Oh, and it might accidentally improve perf, based on https://github.com/rust-lang/rust/issues/37939#issuecomment-265803670, since `Try::into_result` for `Result` is an obvious no-op, unlike `Carrier::translate`.
-rw-r--r--src/doc/unstable-book/src/SUMMARY.md1
-rw-r--r--src/doc/unstable-book/src/library-features/question-mark-carrier.md6
-rw-r--r--src/doc/unstable-book/src/library-features/try-trait.md50
-rw-r--r--src/libcore/ops.rs65
-rw-r--r--src/libcore/result.rs19
-rw-r--r--src/librustc/hir/lowering.rs12
-rw-r--r--src/test/run-pass/try-operator-custom.rs18
7 files changed, 138 insertions, 33 deletions
diff --git a/src/doc/unstable-book/src/SUMMARY.md b/src/doc/unstable-book/src/SUMMARY.md
index 8b70e8be38a..28bbcf734f8 100644
--- a/src/doc/unstable-book/src/SUMMARY.md
+++ b/src/doc/unstable-book/src/SUMMARY.md
@@ -208,6 +208,7 @@
     - [toowned_clone_into](library-features/toowned-clone-into.md)
     - [trusted_len](library-features/trusted-len.md)
     - [try_from](library-features/try-from.md)
+    - [try_trait](library-features/try-trait.md)
     - [unicode](library-features/unicode.md)
     - [unique](library-features/unique.md)
     - [unsize](library-features/unsize.md)
diff --git a/src/doc/unstable-book/src/library-features/question-mark-carrier.md b/src/doc/unstable-book/src/library-features/question-mark-carrier.md
index 56154acc02b..a5e6965faec 100644
--- a/src/doc/unstable-book/src/library-features/question-mark-carrier.md
+++ b/src/doc/unstable-book/src/library-features/question-mark-carrier.md
@@ -5,3 +5,9 @@ The tracking issue for this feature is: [#31436]
 [#31436]: https://github.com/rust-lang/rust/issues/31436
 
 ------------------------
+
+This feature has been superseded by [`try_trait`][try_trait].
+
+It exists only in stage0 for bootstrapping.
+
+[try_trait]: library-features/try-trait.html
diff --git a/src/doc/unstable-book/src/library-features/try-trait.md b/src/doc/unstable-book/src/library-features/try-trait.md
new file mode 100644
index 00000000000..0c07329025b
--- /dev/null
+++ b/src/doc/unstable-book/src/library-features/try-trait.md
@@ -0,0 +1,50 @@
+# `try_trait`
+
+The tracking issue for this feature is: [#42327]
+
+[#42327]: https://github.com/rust-lang/rust/issues/42327
+
+------------------------
+
+This introduces a new trait `Try` for extending the `?` operator to types
+other than `Result` (a part of [RFC 1859]).  The trait provides the canonical
+way to _view_ a type in terms of a success/failure dichotomy.  This will
+allow `?` to supplant the `try_opt!` macro on `Option` and the `try_ready!`
+macro on `Poll`, among other things.
+
+[RFC 1859]: https://github.com/rust-lang/rfcs/pull/1859
+
+Here's an example implementation of the trait:
+
+```rust,ignore
+/// A distinct type to represent the `None` value of an `Option`.
+///
+/// This enables using the `?` operator on `Option`; it's rarely useful alone.
+#[derive(Debug)]
+#[unstable(feature = "try_trait", issue = "42327")]
+pub struct None { _priv: () }
+
+#[unstable(feature = "try_trait", issue = "42327")]
+impl<T> ops::Try for Option<T>  {
+    type Ok = T;
+    type Error = None;
+
+    fn into_result(self) -> Result<T, None> {
+        self.ok_or(None { _priv: () })
+    }
+
+    fn from_ok(v: T) -> Self {
+        Some(v)
+    }
+
+    fn from_error(_: None) -> Self {
+        None
+    }
+}
+```
+
+Note the `Error` associated type here is a new marker.  The `?` operator
+allows interconversion between different `Try` implementers only when
+the error type can be converted `Into` the error type of the enclosing
+function (or catch block).  Having a distinct error type (as opposed to
+just `()`, or similar) restricts this to where it's semantically meaningful.
diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs
index c76cff4dc34..a1de8fe76e2 100644
--- a/src/libcore/ops.rs
+++ b/src/libcore/ops.rs
@@ -2918,15 +2918,9 @@ pub trait BoxPlace<Data: ?Sized> : Place<Data> {
     fn make_place() -> Self;
 }
 
-/// A trait for types which have success and error states and are meant to work
-/// with the question mark operator.
-/// When the `?` operator is used with a value, whether the value is in the
-/// success or error state is determined by calling `translate`.
-///
-/// This trait is **very** experimental, it will probably be iterated on heavily
-/// before it is stabilised. Implementors should expect change. Users of `?`
-/// should not rely on any implementations of `Carrier` other than `Result`,
-/// i.e., you should not expect `?` to continue to work with `Option`, etc.
+/// This trait has been superseded by the `Try` trait, but must remain
+/// here as `?` is still lowered to it in stage0 .
+#[cfg(stage0)]
 #[unstable(feature = "question_mark_carrier", issue = "31436")]
 pub trait Carrier {
     /// The type of the value when computation succeeds.
@@ -2945,6 +2939,7 @@ pub trait Carrier {
     fn translate<T>(self) -> T where T: Carrier<Success=Self::Success, Error=Self::Error>;
 }
 
+#[cfg(stage0)]
 #[unstable(feature = "question_mark_carrier", issue = "31436")]
 impl<U, V> Carrier for Result<U, V> {
     type Success = U;
@@ -2970,21 +2965,57 @@ impl<U, V> Carrier for Result<U, V> {
 
 struct _DummyErrorType;
 
-impl Carrier for _DummyErrorType {
-    type Success = ();
+impl Try for _DummyErrorType {
+    type Ok = ();
     type Error = ();
 
-    fn from_success(_: ()) -> _DummyErrorType {
+    fn into_result(self) -> Result<Self::Ok, Self::Error> {
+        Ok(())
+    }
+
+    fn from_ok(_: ()) -> _DummyErrorType {
         _DummyErrorType
     }
 
     fn from_error(_: ()) -> _DummyErrorType {
         _DummyErrorType
     }
+}
 
-    fn translate<T>(self) -> T
-        where T: Carrier<Success=(), Error=()>
-    {
-        T::from_success(())
-    }
+/// A trait for customizing the behaviour of the `?` operator.
+///
+/// A type implementing `Try` is one that has a canonical way to view it
+/// in terms of a success/failure dichotomy.  This trait allows both
+/// extracting those success or failure values from an existing instance and
+/// creating a new instance from a success or failure value.
+#[unstable(feature = "try_trait", issue = "42327")]
+pub trait Try {
+    /// The type of this value when viewed as successful.
+    #[unstable(feature = "try_trait", issue = "42327")]
+    type Ok;
+    /// The type of this value when viewed as failed.
+    #[unstable(feature = "try_trait", issue = "42327")]
+    type Error;
+
+    /// Applies the "?" operator. A return of `Ok(t)` means that the
+    /// execution should continue normally, and the result of `?` is the
+    /// value `t`. A return of `Err(e)` means that execution should branch
+    /// to the innermost enclosing `catch`, or return from the function.
+    ///
+    /// If an `Err(e)` result is returned, the value `e` will be "wrapped"
+    /// in the return type of the enclosing scope (which must itself implement
+    /// `Try`). Specifically, the value `X::from_error(From::from(e))`
+    /// is returned, where `X` is the return type of the enclosing function.
+    #[unstable(feature = "try_trait", issue = "42327")]
+    fn into_result(self) -> Result<Self::Ok, Self::Error>;
+
+    /// Wrap an error value to construct the composite result. For example,
+    /// `Result::Err(x)` and `Result::from_error(x)` are equivalent.
+    #[unstable(feature = "try_trait", issue = "42327")]
+    fn from_error(v: Self::Error) -> Self;
+
+    /// Wrap an OK value to construct the composite result. For example,
+    /// `Result::Ok(x)` and `Result::from_ok(x)` are equivalent.
+    #[unstable(feature = "try_trait", issue = "42327")]
+    fn from_ok(v: Self::Ok) -> Self;
 }
diff --git a/src/libcore/result.rs b/src/libcore/result.rs
index c46b0c1324d..df7fff0df92 100644
--- a/src/libcore/result.rs
+++ b/src/libcore/result.rs
@@ -242,6 +242,7 @@
 
 use fmt;
 use iter::{FromIterator, FusedIterator, TrustedLen};
+use ops;
 
 /// `Result` is a type that represents either success (`Ok`) or failure (`Err`).
 ///
@@ -1108,3 +1109,21 @@ impl<A, E, V: FromIterator<A>> FromIterator<Result<A, E>> for Result<V, E> {
         }
     }
 }
+
+#[unstable(feature = "try_trait", issue = "42327")]
+impl<T,E> ops::Try for Result<T, E> {
+    type Ok = T;
+    type Error = E;
+
+    fn into_result(self) -> Self {
+        self
+    }
+
+    fn from_ok(v: T) -> Self {
+        Ok(v)
+    }
+
+    fn from_error(v: E) -> Self {
+        Err(v)
+    }
+}
\ No newline at end of file
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index 91cfbc38aa0..df82fee80f2 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -2265,23 +2265,23 @@ impl<'a> LoweringContext<'a> {
             ExprKind::Try(ref sub_expr) => {
                 // to:
                 //
-                // match Carrier::translate(<expr>) {
+                // match Try::into_result(<expr>) {
                 //     Ok(val) => #[allow(unreachable_code)] val,
                 //     Err(err) => #[allow(unreachable_code)]
                 //                 // If there is an enclosing `catch {...}`
-                //                 break 'catch_target Carrier::from_error(From::from(err)),
+                //                 break 'catch_target Try::from_error(From::from(err)),
                 //                 // Otherwise
-                //                 return Carrier::from_error(From::from(err)),
+                //                 return Try::from_error(From::from(err)),
                 // }
 
                 let unstable_span = self.allow_internal_unstable("?", e.span);
 
-                // Carrier::translate(<expr>)
+                // Try::into_result(<expr>)
                 let discr = {
                     // expand <expr>
                     let sub_expr = self.lower_expr(sub_expr);
 
-                    let path = &["ops", "Carrier", "translate"];
+                    let path = &["ops", "Try", "into_result"];
                     let path = P(self.expr_std_path(unstable_span, path, ThinVec::new()));
                     P(self.expr_call(e.span, path, hir_vec![sub_expr]))
                 };
@@ -2327,7 +2327,7 @@ impl<'a> LoweringContext<'a> {
                         self.expr_call(e.span, from, hir_vec![err_expr])
                     };
                     let from_err_expr = {
-                        let path = &["ops", "Carrier", "from_error"];
+                        let path = &["ops", "Try", "from_error"];
                         let from_err = P(self.expr_std_path(unstable_span, path,
                                                             ThinVec::new()));
                         P(self.expr_call(e.span, from_err, hir_vec![from_expr]))
diff --git a/src/test/run-pass/try-operator-custom.rs b/src/test/run-pass/try-operator-custom.rs
index 577d19a5896..82ba70c9459 100644
--- a/src/test/run-pass/try-operator-custom.rs
+++ b/src/test/run-pass/try-operator-custom.rs
@@ -8,20 +8,20 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(question_mark, question_mark_carrier)]
+#![feature(try_trait)]
 
-use std::ops::Carrier;
+use std::ops::Try;
 
 enum MyResult<T, U> {
     Awesome(T),
     Terrible(U)
 }
 
-impl<U, V> Carrier for MyResult<U, V> {
-    type Success = U;
+impl<U, V> Try for MyResult<U, V> {
+    type Ok = U;
     type Error = V;
 
-    fn from_success(u: U) -> MyResult<U, V> {
+    fn from_ok(u: U) -> MyResult<U, V> {
         MyResult::Awesome(u)
     }
 
@@ -29,12 +29,10 @@ impl<U, V> Carrier for MyResult<U, V> {
         MyResult::Terrible(e)
     }
 
-    fn translate<T>(self) -> T
-        where T: Carrier<Success=U, Error=V>
-    {
+    fn into_result(self) -> Result<U, V> {
         match self {
-            MyResult::Awesome(u) => T::from_success(u),
-            MyResult::Terrible(e) => T::from_error(e),
+            MyResult::Awesome(u) => Ok(u),
+            MyResult::Terrible(e) => Err(e),
         }
     }
 }