Skip to content

Commit 3af60f8

Browse files
committed
Auto merge of #31954 - japaric:rfc243, r=nikomatsakis
implement the `?` operator The `?` postfix operator is sugar equivalent to the try! macro, but is more amenable to chaining: `File::open("foo")?.metadata()?.is_dir()`. `?` is accepted on any *expression* that can return a `Result`, e.g. `x()?`, `y!()?`, `{z}?`, `(w)?`, etc. And binds more tightly than unary operators, e.g. `!x?` is parsed as `!(x?)`. cc #31436 --- cc @aturon @eddyb
2 parents a9ffe67 + 2de4932 commit 3af60f8

26 files changed

+369
-16
lines changed

src/librustc/middle/check_match.rs

+4
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,10 @@ fn check_arms(cx: &MatchCheckCtxt,
344344
hir::MatchSource::Normal => {
345345
span_err!(cx.tcx.sess, pat.span, E0001, "unreachable pattern")
346346
},
347+
348+
hir::MatchSource::TryDesugar => {
349+
cx.tcx.sess.span_bug(pat.span, "unreachable try pattern")
350+
},
347351
}
348352
}
349353
Useful => (),

src/librustc_front/hir.rs

+1
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,7 @@ pub enum MatchSource {
835835
},
836836
WhileLetDesugar,
837837
ForLoopDesugar,
838+
TryDesugar,
838839
}
839840

840841
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]

src/librustc_front/lowering.rs

+69
Original file line numberDiff line numberDiff line change
@@ -1605,6 +1605,63 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
16051605
});
16061606
}
16071607

1608+
// Desugar ExprKind::Try
1609+
// From: `<expr>?`
1610+
ExprKind::Try(ref sub_expr) => {
1611+
// to:
1612+
//
1613+
// {
1614+
// match <expr> {
1615+
// Ok(val) => val,
1616+
// Err(err) => {
1617+
// return Err(From::from(err))
1618+
// }
1619+
// }
1620+
// }
1621+
1622+
return cache_ids(lctx, e.id, |lctx| {
1623+
// expand <expr>
1624+
let sub_expr = lower_expr(lctx, sub_expr);
1625+
1626+
// Ok(val) => val
1627+
let ok_arm = {
1628+
let val_ident = lctx.str_to_ident("val");
1629+
let val_pat = pat_ident(lctx, e.span, val_ident);
1630+
let val_expr = expr_ident(lctx, e.span, val_ident, None);
1631+
let ok_pat = pat_ok(lctx, e.span, val_pat);
1632+
1633+
arm(hir_vec![ok_pat], val_expr)
1634+
};
1635+
1636+
// Err(err) => return Err(From::from(err))
1637+
let err_arm = {
1638+
let err_ident = lctx.str_to_ident("err");
1639+
let from_expr = {
1640+
let path = std_path(lctx, &["convert", "From", "from"]);
1641+
let path = path_global(e.span, path);
1642+
let from = expr_path(lctx, path, None);
1643+
let err_expr = expr_ident(lctx, e.span, err_ident, None);
1644+
1645+
expr_call(lctx, e.span, from, hir_vec![err_expr], None)
1646+
};
1647+
let err_expr = {
1648+
let path = std_path(lctx, &["result", "Result", "Err"]);
1649+
let path = path_global(e.span, path);
1650+
let err_ctor = expr_path(lctx, path, None);
1651+
expr_call(lctx, e.span, err_ctor, hir_vec![from_expr], None)
1652+
};
1653+
let err_pat = pat_err(lctx, e.span, pat_ident(lctx, e.span, err_ident));
1654+
let ret_expr = expr(lctx, e.span,
1655+
hir::Expr_::ExprRet(Some(err_expr)), None);
1656+
1657+
arm(hir_vec![err_pat], ret_expr)
1658+
};
1659+
1660+
expr_match(lctx, e.span, sub_expr, hir_vec![err_arm, ok_arm],
1661+
hir::MatchSource::TryDesugar, None)
1662+
})
1663+
}
1664+
16081665
ExprKind::Mac(_) => panic!("Shouldn't exist here"),
16091666
},
16101667
span: e.span,
@@ -1819,6 +1876,18 @@ fn block_all(lctx: &LoweringContext,
18191876
})
18201877
}
18211878

1879+
fn pat_ok(lctx: &LoweringContext, span: Span, pat: P<hir::Pat>) -> P<hir::Pat> {
1880+
let ok = std_path(lctx, &["result", "Result", "Ok"]);
1881+
let path = path_global(span, ok);
1882+
pat_enum(lctx, span, path, hir_vec![pat])
1883+
}
1884+
1885+
fn pat_err(lctx: &LoweringContext, span: Span, pat: P<hir::Pat>) -> P<hir::Pat> {
1886+
let err = std_path(lctx, &["result", "Result", "Err"]);
1887+
let path = path_global(span, err);
1888+
pat_enum(lctx, span, path, hir_vec![pat])
1889+
}
1890+
18221891
fn pat_some(lctx: &LoweringContext, span: Span, pat: P<hir::Pat>) -> P<hir::Pat> {
18231892
let some = std_path(lctx, &["option", "Option", "Some"]);
18241893
let path = path_global(span, some);

src/libsyntax/ast.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1022,6 +1022,9 @@ pub enum ExprKind {
10221022

10231023
/// No-op: used solely so we can pretty-print faithfully
10241024
Paren(P<Expr>),
1025+
1026+
/// `expr?`
1027+
Try(P<Expr>),
10251028
}
10261029

10271030
/// The explicit Self type in a "qualified path". The actual

src/libsyntax/feature_gate.rs

+9
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,9 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option<u32>, Status
245245

246246
// a...b and ...b
247247
("inclusive_range_syntax", "1.7.0", Some(28237), Active),
248+
249+
// `expr?`
250+
("question_mark", "1.9.0", Some(31436), Active)
248251
];
249252
// (changing above list without updating src/doc/reference.md makes @cmr sad)
250253

@@ -570,6 +573,7 @@ pub struct Features {
570573
pub staged_api: bool,
571574
pub stmt_expr_attributes: bool,
572575
pub deprecated: bool,
576+
pub question_mark: bool,
573577
}
574578

575579
impl Features {
@@ -603,6 +607,7 @@ impl Features {
603607
staged_api: false,
604608
stmt_expr_attributes: false,
605609
deprecated: false,
610+
question_mark: false,
606611
}
607612
}
608613
}
@@ -1001,6 +1006,9 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
10011006
e.span,
10021007
"inclusive range syntax is experimental");
10031008
}
1009+
ast::ExprKind::Try(..) => {
1010+
self.gate_feature("question_mark", e.span, "the `?` operator is not stable");
1011+
}
10041012
_ => {}
10051013
}
10061014
visit::walk_expr(self, e);
@@ -1203,6 +1211,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &Handler,
12031211
staged_api: cx.has_feature("staged_api"),
12041212
stmt_expr_attributes: cx.has_feature("stmt_expr_attributes"),
12051213
deprecated: cx.has_feature("deprecated"),
1214+
question_mark: cx.has_feature("question_mark"),
12061215
}
12071216
}
12081217

src/libsyntax/fold.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1332,7 +1332,8 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span, attrs}: Expr, folder: &mu
13321332
fields.move_map(|x| folder.fold_field(x)),
13331333
maybe_expr.map(|x| folder.fold_expr(x)))
13341334
},
1335-
ExprKind::Paren(ex) => ExprKind::Paren(folder.fold_expr(ex))
1335+
ExprKind::Paren(ex) => ExprKind::Paren(folder.fold_expr(ex)),
1336+
ExprKind::Try(ex) => ExprKind::Try(folder.fold_expr(ex)),
13361337
},
13371338
span: folder.new_span(span),
13381339
attrs: attrs.map_thin_attrs(|v| fold_attrs(v, folder)),

src/libsyntax/parse/parser.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -2534,6 +2534,12 @@ impl<'a> Parser<'a> {
25342534
let mut e = e0;
25352535
let mut hi;
25362536
loop {
2537+
// expr?
2538+
while self.eat(&token::Question) {
2539+
let hi = self.span.hi;
2540+
e = self.mk_expr(lo, hi, ExprKind::Try(e), None);
2541+
}
2542+
25372543
// expr.f
25382544
if self.eat(&token::Dot) {
25392545
match self.token {
@@ -2907,7 +2913,6 @@ impl<'a> Parser<'a> {
29072913
}
29082914
};
29092915

2910-
29112916
if self.expr_is_complete(&lhs) {
29122917
// Semi-statement forms are odd. See https://github.com/rust-lang/rust/issues/29071
29132918
return Ok(lhs);

src/libsyntax/print/pprust.rs

+4
Original file line numberDiff line numberDiff line change
@@ -2277,6 +2277,10 @@ impl<'a> State<'a> {
22772277
try!(self.print_inner_attributes_inline(attrs));
22782278
try!(self.print_expr(&e));
22792279
try!(self.pclose());
2280+
},
2281+
ast::ExprKind::Try(ref e) => {
2282+
try!(self.print_expr(e));
2283+
try!(word(&mut self.s, "?"))
22802284
}
22812285
}
22822286
try!(self.ann.post(self, NodeExpr(expr)));

src/libsyntax/visit.rs

+3
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,9 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
793793
visitor.visit_expr(&output.expr)
794794
}
795795
}
796+
ExprKind::Try(ref subexpression) => {
797+
visitor.visit_expr(subexpression)
798+
}
796799
}
797800

798801
visitor.visit_expr_post(expression)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
macro_rules! id {
12+
($e:expr) => { $e }
13+
}
14+
15+
fn main() {
16+
id!(x?); //~ error: the `?` operator is not stable (see issue #31436)
17+
//~^ help: add #![feature(question_mark)] to the crate attributes to enable
18+
y?; //~ error: the `?` operator is not stable (see issue #31436)
19+
//~^ help: add #![feature(question_mark)] to the crate attributes to enable
20+
}

src/test/parse-fail/issue-19096.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@
1212

1313
fn main() {
1414
let t = (42, 42);
15-
t.0::<isize>; //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `::`
15+
t.0::<isize>; //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `::`
1616
}

src/test/parse-fail/issue-3036.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@
1515
fn main()
1616
{
1717
let x = 3
18-
} //~ ERROR: expected one of `.`, `;`, or an operator, found `}`
18+
} //~ ERROR: expected one of `.`, `;`, `?`, or an operator, found `}`

src/test/parse-fail/macros-no-semicolon.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@
1212

1313
fn main() {
1414
assert_eq!(1, 2)
15-
assert_eq!(3, 4) //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `assert_eq`
15+
assert_eq!(3, 4) //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `assert_eq`
1616
println!("hello");
1717
}

src/test/parse-fail/match-refactor-to-expr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ fn main() {
1414
let foo =
1515
match //~ NOTE did you mean to remove this `match` keyword?
1616
Some(4).unwrap_or_else(5)
17-
; //~ ERROR expected one of `.`, `{`, or an operator, found `;`
17+
; //~ ERROR expected one of `.`, `?`, `{`, or an operator, found `;`
1818

1919
println!("{}", foo)
2020
}

src/test/parse-fail/range-3.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@
1414

1515
pub fn main() {
1616
let r = 1..2..3;
17-
//~^ ERROR expected one of `.`, `;`, or an operator, found `..`
17+
//~^ ERROR expected one of `.`, `;`, `?`, or an operator, found `..`
1818
}

src/test/parse-fail/range-4.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@
1414

1515
pub fn main() {
1616
let r = ..1..2;
17-
//~^ ERROR expected one of `.`, `;`, or an operator, found `..`
17+
//~^ ERROR expected one of `.`, `;`, `?`, or an operator, found `..`
1818
}

src/test/parse-fail/raw-str-unbalanced.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@
1212

1313
static s: &'static str =
1414
r#"
15-
"## //~ ERROR expected one of `.`, `;`, or an operator, found `#`
15+
"## //~ ERROR expected one of `.`, `;`, `?`, or an operator, found `#`
1616
;

src/test/parse-fail/removed-syntax-mut-vec-expr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@
1313
fn f() {
1414
let v = [mut 1, 2, 3, 4];
1515
//~^ ERROR expected identifier, found keyword `mut`
16-
//~^^ ERROR expected one of `!`, `,`, `.`, `::`, `;`, `]`, `{`, or an operator, found `1`
16+
//~^^ ERROR expected one of `!`, `,`, `.`, `::`, `;`, `?`, `]`, `{`, or an operator, found `1`
1717
}

src/test/parse-fail/removed-syntax-uniq-mut-expr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@
1313
fn f() {
1414
let a_box = box mut 42;
1515
//~^ ERROR expected identifier, found keyword `mut`
16-
//~^^ ERROR expected one of `!`, `.`, `::`, `;`, `{`, or an operator, found `42`
16+
//~^^ ERROR expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `42`
1717
}

src/test/parse-fail/removed-syntax-with-1.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@ fn removed_with() {
1818

1919
let a = S { foo: (), bar: () };
2020
let b = S { foo: () with a };
21-
//~^ ERROR expected one of `,`, `.`, `}`, or an operator, found `with`
21+
//~^ ERROR expected one of `,`, `.`, `?`, `}`, or an operator, found `with`
2222
}

src/test/parse-fail/struct-literal-in-for.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ impl Foo {
2323
fn main() {
2424
for x in Foo {
2525
x: 3 //~ ERROR expected type, found `3`
26-
}.hi() { //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `{`
26+
}.hi() { //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `{`
2727
println!("yo");
2828
}
2929
}

src/test/parse-fail/struct-literal-in-if.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ impl Foo {
2323
fn main() {
2424
if Foo {
2525
x: 3 //~ ERROR expected type, found `3`
26-
}.hi() { //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `{`
26+
}.hi() { //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `{`
2727
println!("yo");
2828
}
2929
}

src/test/parse-fail/struct-literal-in-match-discriminant.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ fn main() {
2020
} {
2121
Foo {
2222
x: x
23-
} => {} //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `=>`
23+
} => {} //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `=>`
2424
}
2525
}

src/test/parse-fail/struct-literal-in-while.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ impl Foo {
2323
fn main() {
2424
while Foo {
2525
x: 3 //~ ERROR expected type, found `3`
26-
}.hi() { //~ ERROR expected one of `.`, `;`, `}`, or an operator, found `{`
26+
}.hi() { //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `{`
2727
println!("yo");
2828
}
2929
}
+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// `expr?` expands to:
12+
//
13+
// match expr {
14+
// Ok(val) => val,
15+
// Err(err) => return From::from(err),
16+
// }
17+
//
18+
// This test verifies that the expansion is hygienic, i.e. it's not affected by other `val` and
19+
// `err` bindings that may be in scope.
20+
21+
#![feature(question_mark)]
22+
23+
use std::num::ParseIntError;
24+
25+
fn main() {
26+
assert_eq!(parse(), Ok(1));
27+
}
28+
29+
fn parse() -> Result<i32, ParseIntError> {
30+
const val: char = 'a';
31+
const err: char = 'b';
32+
33+
Ok("1".parse::<i32>()?)
34+
}

0 commit comments

Comments
 (0)