From 27f5a6ca275ea33edad92c4d64a12cc22256608a Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sun, 11 Dec 2016 14:52:51 +0100 Subject: [PATCH 1/3] Switch `x?` desugaring to use QuestionMark trait and Try enum --- src/libcore/ops.rs | 53 ++++++++++++++++++++++++++++++++ src/librustc/hir/lowering.rs | 58 ++++++++++++++++-------------------- 2 files changed, 78 insertions(+), 33 deletions(-) diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index 07ae5b920b27b..638fd62da9395 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -2813,6 +2813,7 @@ pub trait BoxPlace : Place { /// should not rely on any implementations of `Carrier` other than `Result`, /// i.e., you should not expect `?` to continue to work with `Option`, etc. #[unstable(feature = "question_mark_carrier", issue = "31436")] +#[cfg_attr(not(stage0), rustc_deprecated(since = "", reason = "replaced by `QuestionMark`"))] pub trait Carrier { /// The type of the value when computation succeeds. type Success; @@ -2820,17 +2821,21 @@ pub trait Carrier { type Error; /// Create a `Carrier` from a success value. + #[allow(deprecated)] fn from_success(Self::Success) -> Self; /// Create a `Carrier` from an error value. + #[allow(deprecated)] fn from_error(Self::Error) -> Self; /// Translate this `Carrier` to another implementation of `Carrier` with the /// same associated types. + #[allow(deprecated)] fn translate(self) -> T where T: Carrier; } #[unstable(feature = "question_mark_carrier", issue = "31436")] +#[allow(deprecated)] impl Carrier for Result { type Success = U; type Error = V; @@ -2843,6 +2848,7 @@ impl Carrier for Result { Err(e) } + #[allow(deprecated)] fn translate(self) -> T where T: Carrier { @@ -2855,6 +2861,7 @@ impl Carrier for Result { struct _DummyErrorType; +#[allow(deprecated)] impl Carrier for _DummyErrorType { type Success = (); type Error = (); @@ -2867,9 +2874,55 @@ impl Carrier for _DummyErrorType { _DummyErrorType } + #[allow(deprecated)] fn translate(self) -> T where T: Carrier { T::from_success(()) } } + +/// The `QuestionMark` trait is used to specify the functionality of `?`. +/// +#[cfg(not(stage0))] +#[unstable(feature = "question_mark_qm", issue = "31436")] +pub trait QuestionMark { + /// + type Continue; + /// + fn question_mark(self) -> Try; +} + +/// The `Try` enum. +#[cfg(not(stage0))] +#[unstable(feature = "question_mark_try", issue = "31436")] +#[derive(Debug, Copy, Clone)] +pub enum Try { + /// + Continue(T), + /// + Done(E), +} + +#[cfg(not(stage0))] +#[unstable(feature = "question_mark_impl", issue = "31436")] +impl QuestionMark> for Result + where E: Into +{ + type Continue = T; + fn question_mark(self) -> Try> { + match self { + Ok(x) => Try::Continue(x), + Err(e) => Try::Done(Err(e.into())), + } + } +} + +#[cfg(not(stage0))] +impl QuestionMark<_DummyErrorType> for _DummyErrorType +{ + type Continue = Self; + fn question_mark(self) -> Try { + Try::Continue(self) + } +} diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 74876eb59ee9a..9aa8fd64fd4fe 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -1762,56 +1762,48 @@ impl<'a> LoweringContext<'a> { ExprKind::Try(ref sub_expr) => { // to: // + // match QuestionMark::question_mark() { + // Try::Continue(val) => val, + // Try::Done(r) => return r, + // } // match Carrier::translate() { // Ok(val) => val, // Err(err) => return Carrier::from_error(From::from(err)) // } let unstable_span = self.allow_internal_unstable("?", e.span); - // Carrier::translate() + // QuestionMark::question_mark() let discr = { // expand let sub_expr = self.lower_expr(sub_expr); - let path = &["ops", "Carrier", "translate"]; + let path = &["ops", "QuestionMark", "question_mark"]; let path = P(self.expr_std_path(unstable_span, path, ThinVec::new())); P(self.expr_call(e.span, path, hir_vec![sub_expr])) }; - // Ok(val) => val + // Continue(val) => val let ok_arm = { let val_ident = self.str_to_ident("val"); - let val_pat = self.pat_ident(e.span, val_ident); - let val_expr = P(self.expr_ident(e.span, val_ident, val_pat.id)); - let ok_pat = self.pat_ok(e.span, val_pat); + let val_pat = self.pat_ident(unstable_span, val_ident); + let val_expr = P(self.expr_ident(unstable_span, val_ident, val_pat.id)); + let ok_pat = self.pat_continue(unstable_span, val_pat); self.arm(hir_vec![ok_pat], val_expr) }; - // Err(err) => return Carrier::from_error(From::from(err)) + // Done(r) => return r let err_arm = { let err_ident = self.str_to_ident("err"); - let err_local = self.pat_ident(e.span, err_ident); - let from_expr = { - let path = &["convert", "From", "from"]; - let from = P(self.expr_std_path(e.span, path, ThinVec::new())); - let err_expr = self.expr_ident(e.span, err_ident, err_local.id); - - self.expr_call(e.span, from, hir_vec![err_expr]) - }; - let from_err_expr = { - let path = &["ops", "Carrier", "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])) - }; + let err_local = self.pat_ident(unstable_span, err_ident); - let ret_expr = P(self.expr(e.span, - hir::Expr_::ExprRet(Some(from_err_expr)), + let err_expr = self.expr_ident(unstable_span, err_ident, err_local.id); + let ret_expr = P(self.expr(unstable_span, + hir::Expr_::ExprRet(Some(P(err_expr))), ThinVec::new())); - let err_pat = self.pat_err(e.span, err_local); - self.arm(hir_vec![err_pat], ret_expr) + let done_pat = self.pat_done(unstable_span, err_local); + self.arm(hir_vec![done_pat], ret_expr) }; return self.expr_match(e.span, discr, hir_vec![err_arm, ok_arm], @@ -2060,14 +2052,6 @@ impl<'a> LoweringContext<'a> { } } - fn pat_ok(&mut self, span: Span, pat: P) -> P { - self.pat_std_enum(span, &["result", "Result", "Ok"], hir_vec![pat]) - } - - fn pat_err(&mut self, span: Span, pat: P) -> P { - self.pat_std_enum(span, &["result", "Result", "Err"], hir_vec![pat]) - } - fn pat_some(&mut self, span: Span, pat: P) -> P { self.pat_std_enum(span, &["option", "Option", "Some"], hir_vec![pat]) } @@ -2076,6 +2060,14 @@ impl<'a> LoweringContext<'a> { self.pat_std_enum(span, &["option", "Option", "None"], hir_vec![]) } + fn pat_continue(&mut self, span: Span, pat: P) -> P { + self.pat_std_enum(span, &["ops", "Try", "Continue"], hir_vec![pat]) + } + + fn pat_done(&mut self, span: Span, pat: P) -> P { + self.pat_std_enum(span, &["ops", "Try", "Done"], hir_vec![pat]) + } + fn pat_std_enum(&mut self, span: Span, components: &[&str], From 0ae995d9f75ec3440cdb62b218a6bfc87dd48859 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sun, 11 Dec 2016 23:17:40 +0100 Subject: [PATCH 2/3] Fix QuestionMark impl to use From --- src/libcore/ops.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index 638fd62da9395..7f83a0579ea1b 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -2907,13 +2907,13 @@ pub enum Try { #[cfg(not(stage0))] #[unstable(feature = "question_mark_impl", issue = "31436")] impl QuestionMark> for Result - where E: Into + where F: From { type Continue = T; fn question_mark(self) -> Try> { match self { Ok(x) => Try::Continue(x), - Err(e) => Try::Done(Err(e.into())), + Err(e) => Try::Done(Err(From::from(e))), } } } From 97b92e153440267918fe5f92f8b8f8764f7693d1 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sun, 11 Dec 2016 18:30:56 +0100 Subject: [PATCH 3/3] Adapt the `x?` test for QuestionMark / Try --- src/test/run-pass/try-operator-custom.rs | 61 ++++++++++-------------- 1 file changed, 25 insertions(+), 36 deletions(-) diff --git a/src/test/run-pass/try-operator-custom.rs b/src/test/run-pass/try-operator-custom.rs index 577d19a58960d..87e8a703fc5ef 100644 --- a/src/test/run-pass/try-operator-custom.rs +++ b/src/test/run-pass/try-operator-custom.rs @@ -8,33 +8,25 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(question_mark, question_mark_carrier)] +#![feature(question_mark, question_mark_qm, question_mark_try)] -use std::ops::Carrier; +use std::ops::QuestionMark; +use std::ops::Try; -enum MyResult { - Awesome(T), - Terrible(U) +#[derive(PartialEq)] +enum MyResult { + Awesome(U), + Terrible(V) } -impl Carrier for MyResult { - type Success = U; - type Error = V; - - fn from_success(u: U) -> MyResult { - MyResult::Awesome(u) - } - - fn from_error(e: V) -> MyResult { - MyResult::Terrible(e) - } - - fn translate(self) -> T - where T: Carrier - { +impl QuestionMark> for MyResult + where Y: From, +{ + type Continue = U; + fn question_mark(self) -> Try> { match self { - MyResult::Awesome(u) => T::from_success(u), - MyResult::Terrible(e) => T::from_error(e), + MyResult::Awesome(u) => Try::Continue(u), + MyResult::Terrible(e) => Try::Done(MyResult::Terrible(Y::from(e))), } } } @@ -43,31 +35,28 @@ fn f(x: i32) -> Result { if x == 0 { Ok(42) } else { - let y = g(x)?; + let y = Err(String::new())?; Ok(y) } } fn g(x: i32) -> MyResult { - let _y = f(x - 1)?; - MyResult::Terrible("Hello".to_owned()) -} - -fn h() -> MyResult { - let a: Result = Err("Hello"); - let b = a?; - MyResult::Awesome(b) + if x == 0 { + return MyResult::Awesome(42); + } else { + let _y = i()?; + MyResult::Awesome(1) + } } -fn i() -> MyResult { - let a: MyResult = MyResult::Terrible("Hello"); +fn i() -> MyResult { + let a: MyResult = MyResult::Terrible("Hello"); let b = a?; MyResult::Awesome(b) } fn main() { - assert!(f(0) == Ok(42)); - assert!(f(10) == Err("Hello".to_owned())); - let _ = h(); + assert!(g(0) == MyResult::Awesome(42)); + assert!(g(10) == MyResult::Terrible("Hello".to_owned())); let _ = i(); }