diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs index 24c54b53590c0..da34eb2de6e37 100644 --- a/src/librustc/middle/cfg/construct.rs +++ b/src/librustc/middle/cfg/construct.rs @@ -243,6 +243,10 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { self.tcx.sess.span_bug(expr.span, "non-desugared ExprForLoop"); } + ast::ExprQuestion(..) => { + self.tcx.sess.span_bug(expr.span, "non-desugared ExprQuestion"); + } + ast::ExprLoop(ref body, _) => { // // [pred] diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index 497022ac6ac49..bed82560fff7a 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc/middle/check_const.rs @@ -596,6 +596,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, ast::ExprBreak(_) | ast::ExprAgain(_) | ast::ExprRet(_) | + ast::ExprQuestion(_) | // Miscellaneous expressions that could be implemented. ast::ExprRange(..) | diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index a1e38a1c8bda7..dde02343e0d4e 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -539,6 +539,10 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { self.tcx().sess.span_bug(expr.span, "non-desugared ExprForLoop"); } + ast::ExprQuestion(..) => { + self.tcx().sess.span_bug(expr.span, "non-desugared ExprQuestion"); + } + ast::ExprUnary(op, ref lhs) => { let pass_args = if ast_util::is_by_value_unop(op) { PassArgs::ByValue diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 892452ccc1c29..784d7fd7a8188 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -491,6 +491,9 @@ fn visit_expr(ir: &mut IrMaps, expr: &Expr) { ast::ExprForLoop(..) => { ir.tcx.sess.span_bug(expr.span, "non-desugared ExprForLoop"); } + ast::ExprQuestion(..) => { + ir.tcx.sess.span_bug(expr.span, "non-desugared ExprQuestion"); + } ast::ExprBinary(op, _, _) if ast_util::lazy_binop(op.node) => { ir.add_live_node_for_node(expr.id, ExprNode(expr.span)); visit::walk_expr(ir, expr); @@ -1025,6 +1028,10 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { self.ir.tcx.sess.span_bug(expr.span, "non-desugared ExprForLoop"); } + ast::ExprQuestion(..) => { + self.ir.tcx.sess.span_bug(expr.span, "non-desugared ExprQuestion"); + } + // Note that labels have been resolved, so we don't need to look // at the label ident ast::ExprLoop(ref blk, _) => { @@ -1480,6 +1487,9 @@ fn check_expr(this: &mut Liveness, expr: &Expr) { ast::ExprForLoop(..) => { this.ir.tcx.sess.span_bug(expr.span, "non-desugared ExprForLoop"); } + ast::ExprQuestion(..) => { + this.ir.tcx.sess.span_bug(expr.span, "non-desugared ExprQuestion"); + } } } diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 1f02f13a4a178..d5266f842d82c 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -561,6 +561,9 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { ast::ExprForLoop(..) => { self.tcx().sess.span_bug(expr.span, "non-desugared ExprForLoop"); } + ast::ExprQuestion(..) => { + self.tcx().sess.span_bug(expr.span, "non-desugared ExprQuestion"); + } } } diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 36c42b7079547..727c34ce74b64 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -4646,6 +4646,10 @@ pub fn expr_kind(tcx: &ctxt, expr: &ast::Expr) -> ExprKind { tcx.sess.span_bug(expr.span, "non-desugared ExprForLoop"); } + ast::ExprQuestion(..) => { + tcx.sess.span_bug(expr.span, "non-desugared ExprQuestion"); + } + ast::ExprLit(ref lit) if lit_is_str(&**lit) => { RvalueDpsExpr } diff --git a/src/librustc_back/svh.rs b/src/librustc_back/svh.rs index e16df61c25c47..c06b9dc8dfa50 100644 --- a/src/librustc_back/svh.rs +++ b/src/librustc_back/svh.rs @@ -291,6 +291,7 @@ mod svh_visitor { ExprIfLet(..) => unreachable!(), ExprWhileLet(..) => unreachable!(), ExprMac(..) => unreachable!(), + ExprQuestion(..) => unreachable!(), } } diff --git a/src/librustc_trans/trans/debuginfo.rs b/src/librustc_trans/trans/debuginfo.rs index f3b7058336b2f..76d421c6c93df 100644 --- a/src/librustc_trans/trans/debuginfo.rs +++ b/src/librustc_trans/trans/debuginfo.rs @@ -3586,6 +3586,11 @@ fn create_scope_map(cx: &CrateContext, Found unexpanded macro."); } + ast::ExprQuestion(..) => { + cx.sess().span_bug(exp.span, "debuginfo::create_scope_map() - \ + Found unexpanded `expr?`."); + } + ast::ExprLoop(ref block, _) | ast::ExprBlock(ref block) => { with_new_scope(cx, diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index a12ff04912c49..8b38e1543da81 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3775,6 +3775,9 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, ast::ExprForLoop(..) => { tcx.sess.span_bug(expr.span, "non-desugared ExprForLoop"); } + ast::ExprQuestion(..) => { + tcx.sess.span_bug(expr.span, "non-desugared ExprQuestion"); + } ast::ExprLoop(ref body, _) => { check_block_no_value(fcx, &**body); if !may_break(tcx, expr.id, &**body) { diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 6d6fdffa95095..c230909a128fc 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -774,7 +774,10 @@ pub enum Expr_ { ExprRepeat(P /* element */, P /* count */), /// No-op: used solely so we can pretty-print faithfully - ExprParen(P) + ExprParen(P), + + /// `expr?` + ExprQuestion(P), } /// The explicit Self type in a "qualified path". The actual diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index bea57ae14e4af..e250dba020953 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -336,6 +336,62 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { Some(fld.cx.expr_ident(span, result_ident)))) } + // Desugar `expr?` + // From: `?` + ast::ExprQuestion(expr) => { + // to: + // + // { + // match { + // Ok(val) => val, + // Err(err) => { + // return Err(FromError::from_error(err)); + // } + // } + // } + + // expand + let expr = fld.fold_expr(expr); + + // Ok(val) => val, + let ok_arm = { + let val = fld.cx.ident_of("val"); + let ok_pat = fld.cx.pat_ok(span, fld.cx.pat_ident(span, val)); + + fld.cx.arm(span, vec![ok_pat], fld.cx.expr_ident(span, val)) + }; + + // Err(err) => return Err(FromError::from_error(err)), + let err_arm = { + let err = fld.cx.ident_of("err"); + let from_error_expr = { + let path = { + let strs = vec![ + fld.cx.ident_of_std("core"), + fld.cx.ident_of("error"), + fld.cx.ident_of("FromError"), + fld.cx.ident_of("from_error"), + ]; + + fld.cx.expr_path(fld.cx.path_global(span, strs)) + }; + let args = vec![fld.cx.expr_ident(span, err)]; + + fld.cx.expr_call(span, path, args) + }; + let err_expr = fld.cx.expr_err(span, from_error_expr); + + let err_pat = fld.cx.pat_err(span, fld.cx.pat_ident(span, err)); + + let ret_expr = fld.cx.expr(span, ast::ExprRet(Some(err_expr))); + + fld.cx.arm(span, vec![err_pat], ret_expr) + }; + + // match { .. } + fld.cx.expr_match(span, expr, vec![err_arm, ok_arm]) + } + ast::ExprClosure(capture_clause, fn_decl, block) => { let (rewritten_fn_decl, rewritten_block) = expand_and_rename_fn_decl_and_block(fn_decl, block, fld); diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index a556b2dfd2a99..0cc62a65c7894 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -1311,6 +1311,9 @@ pub fn noop_fold_expr(Expr {id, node, span}: Expr, folder: &mut T) -> folder.fold_block(body), opt_ident.map(|i| folder.fold_ident(i))) } + ExprQuestion(e) => { + ExprQuestion(folder.fold_expr(e)) + } ExprLoop(body, opt_ident) => { ExprLoop(folder.fold_block(body), opt_ident.map(|i| folder.fold_ident(i))) diff --git a/src/libsyntax/parse/classify.rs b/src/libsyntax/parse/classify.rs index d46d078c776be..4e1e81f1e2ac4 100644 --- a/src/libsyntax/parse/classify.rs +++ b/src/libsyntax/parse/classify.rs @@ -30,6 +30,7 @@ pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool { | ast::ExprWhile(..) | ast::ExprWhileLet(..) | ast::ExprLoop(..) + | ast::ExprQuestion(..) | ast::ExprForLoop(..) => false, _ => true } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index c1acee57cf806..fa25f4525ba08 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -26,7 +26,7 @@ use ast::{ExprBreak, ExprCall, ExprCast}; use ast::{ExprField, ExprTupField, ExprClosure, ExprIf, ExprIfLet, ExprIndex}; use ast::{ExprLit, ExprLoop, ExprMac, ExprRange}; use ast::{ExprMethodCall, ExprParen, ExprPath}; -use ast::{ExprRepeat, ExprRet, ExprStruct, ExprTup, ExprUnary}; +use ast::{ExprRepeat, ExprRet, ExprStruct, ExprTup, ExprUnary, ExprQuestion}; use ast::{ExprVec, ExprWhile, ExprWhileLet, ExprForLoop, Field, FnDecl}; use ast::{ForeignItem, ForeignItemStatic, ForeignItemFn, ForeignMod, FunctionRetTy}; use ast::{Ident, Inherited, ImplItem, Item, Item_, ItemStatic}; @@ -2059,6 +2059,10 @@ impl<'a> Parser<'a> { }) } + pub fn mk_question(&mut self, expr: P) -> ast::Expr_ { + ExprQuestion(expr) + } + pub fn mk_unary(&mut self, unop: ast::UnOp, expr: P) -> ast::Expr_ { ExprUnary(unop, expr) } @@ -2481,7 +2485,17 @@ impl<'a> Parser<'a> { es.insert(0, e); let id = spanned(dot, hi, i); let nd = self.mk_method_call(id, tys, es); - e = self.mk_expr(lo, hi, nd); + let expr = self.mk_expr(lo, hi, nd); + + // expr.f()? + if self.eat(&token::Question) { + hi = self.last_span.hi; + + let nd = self.mk_question(expr); + e = self.mk_expr(lo, hi, nd); + } else { + e = expr; + } } _ => { if !tys.is_empty() { @@ -2556,7 +2570,17 @@ impl<'a> Parser<'a> { hi = self.last_span.hi; let nd = self.mk_call(e, es); - e = self.mk_expr(lo, hi, nd); + let expr = self.mk_expr(lo, hi, nd); + + // expr(...)? + if self.eat(&token::Question) { + hi = self.last_span.hi; + + let nd = self.mk_question(expr); + e = self.mk_expr(lo, hi, nd); + } else { + e = expr; + } } // expr[...] diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index af16e19c9f034..ee5dfc6c3743c 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1760,6 +1760,10 @@ impl<'a> State<'a> { try!(space(&mut self.s)); try!(self.print_block(&**blk)); } + ast::ExprQuestion(ref expr) => { + try!(self.print_expr(&**expr)); + try!(word(&mut self.s, "?")) + } ast::ExprLoop(ref blk, opt_ident) => { if let Some(ident) = opt_ident { try!(self.print_ident(ident)); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 33d8d56b4b114..fd81332a991fd 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -821,6 +821,9 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) { visitor.visit_expr(&**subexpression); visitor.visit_block(&**block) } + ExprQuestion(ref expr) => { + visitor.visit_expr(&**expr); + } ExprLoop(ref block, _) => visitor.visit_block(&**block), ExprMatch(ref subexpression, ref arms, _) => { visitor.visit_expr(&**subexpression); diff --git a/src/test/run-pass/question-operator.rs b/src/test/run-pass/question-operator.rs new file mode 100644 index 0000000000000..114e6691b9737 --- /dev/null +++ b/src/test/run-pass/question-operator.rs @@ -0,0 +1,71 @@ +// Copyright 2015 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::error::FromError; +use std::fs::File; +use std::io::{Read, self}; +use std::num::ParseIntError; +use std::str::FromStr; + +fn parse(s: &str) -> Result { + s.parse() +} + +fn on_method() -> Result { + Ok("1".parse::()? + "2".parse()?) +} + +fn in_chain() -> Result { + Ok("3".parse::()?.to_string()) +} + +fn on_call() -> Result { + Ok(parse("4")?) +} + +fn nested() -> Result { + Ok("5".parse::()?.to_string().parse()?) +} + +fn main() { + assert_eq!(Ok(3), on_method()); + + assert_eq!(Ok("3".to_string()), in_chain()); + + assert_eq!(Ok(4), on_call()); + + assert_eq!(Ok(5), nested()); +} + +enum Error { + Io(io::Error), + Parse(ParseIntError), +} + +// just type check +fn merge_error() -> Result { + let mut s = String::new(); + + File::open("foo.txt")?.read_to_string(&mut s)?; + + Ok(s.parse::()? + 1) +} + +impl FromError for Error { + fn from_error(e: io::Error) -> Error { + Error::Io(e) + } +} + +impl FromError for Error { + fn from_error(e: ParseIntError) -> Error { + Error::Parse(e) + } +}