Skip to content

Carrier trait (third attempt) #35777

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 22, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions src/libcore/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
use cmp::PartialOrd;
use fmt;
use marker::{Sized, Unsize};
use result::Result::{self, Ok, Err};

/// The `Drop` trait is used to run some code when a value goes out of scope.
/// This is sometimes called a 'destructor'.
Expand Down Expand Up @@ -2150,3 +2151,74 @@ pub trait BoxPlace<Data: ?Sized> : Place<Data> {
/// Creates a globally fresh place.
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.
#[unstable(feature = "question_mark_carrier", issue = "31436")]
pub trait Carrier {
/// The type of the value when computation succeeds.
type Success;
/// The type of the value when computation errors out.
type Error;

/// Create a `Carrier` from a success value.
fn from_success(Self::Success) -> Self;

/// Create a `Carrier` from an error value.
fn from_error(Self::Error) -> Self;

/// Translate this `Carrier` to another implementation of `Carrier` with the
/// same associated types.
fn translate<T>(self) -> T where T: Carrier<Success=Self::Success, Error=Self::Error>;
}

#[unstable(feature = "question_mark_carrier", issue = "31436")]
impl<U, V> Carrier for Result<U, V> {
type Success = U;
type Error = V;

fn from_success(u: U) -> Result<U, V> {
Ok(u)
}

fn from_error(e: V) -> Result<U, V> {
Err(e)
}

fn translate<T>(self) -> T
where T: Carrier<Success=U, Error=V>
{
match self {
Ok(u) => T::from_success(u),
Err(e) => T::from_error(e),
}
}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps it's a bit late since this already got merged, but shouldn't there be a comment explaining why an impl for a dummy type exists? I assume that some people already know this, but others would not (like me :) ).

struct _DummyErrorType;

impl Carrier for _DummyErrorType {
type Success = ();
type Error = ();

fn from_success(_: ()) -> _DummyErrorType {
_DummyErrorType
}

fn from_error(_: ()) -> _DummyErrorType {
_DummyErrorType
}

fn translate<T>(self) -> T
where T: Carrier<Success=(), Error=()>
{
T::from_success(())
}
}
130 changes: 81 additions & 49 deletions src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -966,7 +966,7 @@ impl<'a> LoweringContext<'a> {
let inplace_finalize = ["ops", "InPlace", "finalize"];

let make_call = |this: &mut LoweringContext, p, args| {
let path = this.core_path(e.span, p);
let path = this.std_path(e.span, p);
let path = this.expr_path(path, ThinVec::new());
this.expr_call(e.span, path, args)
};
Expand Down Expand Up @@ -1159,15 +1159,13 @@ impl<'a> LoweringContext<'a> {
ast_expr: &Expr,
path: &[&str],
fields: &[(&str, &P<Expr>)]) -> P<hir::Expr> {
let strs = this.std_path(&iter::once(&"ops")
.chain(path)
.map(|s| *s)
.collect::<Vec<_>>());

let structpath = this.path_global(ast_expr.span, strs);
let struct_path = this.std_path(ast_expr.span,
&iter::once(&"ops").chain(path)
.map(|s| *s)
.collect::<Vec<_>>());

let hir_expr = if fields.len() == 0 {
this.expr_path(structpath, ast_expr.attrs.clone())
this.expr_path(struct_path, ast_expr.attrs.clone())
} else {
let fields = fields.into_iter().map(|&(s, e)| {
let expr = this.lower_expr(&e);
Expand All @@ -1180,7 +1178,7 @@ impl<'a> LoweringContext<'a> {
}).collect();
let attrs = ast_expr.attrs.clone();

this.expr_struct(ast_expr.span, structpath, fields, None, attrs)
this.expr_struct(ast_expr.span, struct_path, fields, None, attrs)
};

this.signal_block_expr(hir_vec![],
Expand Down Expand Up @@ -1463,11 +1461,7 @@ impl<'a> LoweringContext<'a> {

// `match ::std::iter::Iterator::next(&mut iter) { ... }`
let match_expr = {
let next_path = {
let strs = self.std_path(&["iter", "Iterator", "next"]);

self.path_global(e.span, strs)
};
let next_path = self.std_path(e.span, &["iter", "Iterator", "next"]);
let iter = self.expr_ident(e.span, iter, iter_pat.id);
let ref_mut_iter = self.expr_mut_addr_of(e.span, iter);
let next_path = self.expr_path(next_path, ThinVec::new());
Expand All @@ -1494,11 +1488,8 @@ impl<'a> LoweringContext<'a> {

// `match ::std::iter::IntoIterator::into_iter(<head>) { ... }`
let into_iter_expr = {
let into_iter_path = {
let strs = self.std_path(&["iter", "IntoIterator", "into_iter"]);

self.path_global(e.span, strs)
};
let into_iter_path = self.std_path(e.span,
&["iter", "IntoIterator", "into_iter"]);

let into_iter = self.expr_path(into_iter_path, ThinVec::new());
self.expr_call(e.span, into_iter, hir_vec![head])
Expand Down Expand Up @@ -1527,16 +1518,32 @@ impl<'a> LoweringContext<'a> {
// to:
//
// {
// match <expr> {
// match { Carrier::translate( { <expr> } ) } {
// Ok(val) => val,
// Err(err) => {
// return Err(From::from(err))
// }
// Err(err) => { return Carrier::from_error(From::from(err)); }
// }
// }

// expand <expr>
let sub_expr = self.lower_expr(sub_expr);
// { Carrier::translate( { <expr> } ) }
let discr = {
// expand <expr>
let sub_expr = self.lower_expr(sub_expr);
let sub_expr = self.signal_block_expr(hir_vec![],
sub_expr,
e.span,
hir::PopUnstableBlock,
Copy link
Contributor

@nikomatsakis nikomatsakis Aug 18, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this really be hir::PopUnstableBlock? I'd expect hir::DefaultBlock. I see, unstable, of course. I read this as unsafe.

ThinVec::new());

let path = self.std_path(e.span, &["ops", "Carrier", "translate"]);
let path = self.expr_path(path, ThinVec::new());
let call = self.expr_call(e.span, path, hir_vec![sub_expr]);

self.signal_block_expr(hir_vec![],
call,
e.span,
hir::PushUnstableBlock,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, unstable -- I was reading as unsafe, never mind.

ThinVec::new())
};

// Ok(val) => val
let ok_arm = {
Expand All @@ -1548,32 +1555,35 @@ impl<'a> LoweringContext<'a> {
self.arm(hir_vec![ok_pat], val_expr)
};

// Err(err) => return Err(From::from(err))
// Err(err) => { return Carrier::from_error(From::from(err)); }
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 = self.std_path(&["convert", "From", "from"]);
let path = self.path_global(e.span, path);
let path = self.std_path(e.span, &["convert", "From", "from"]);
let from = self.expr_path(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 err_expr = {
let path = self.std_path(&["result", "Result", "Err"]);
let path = self.path_global(e.span, path);
let err_ctor = self.expr_path(path, ThinVec::new());
self.expr_call(e.span, err_ctor, hir_vec![from_expr])
let from_err_expr = {
let path = self.std_path(e.span, &["ops", "Carrier", "from_error"]);
let from_err = self.expr_path(path, ThinVec::new());
self.expr_call(e.span, from_err, hir_vec![from_expr])
};
let err_pat = self.pat_err(e.span, err_local);

let ret_expr = self.expr(e.span,
hir::Expr_::ExprRet(Some(err_expr)),
ThinVec::new());
self.arm(hir_vec![err_pat], ret_expr)
hir::Expr_::ExprRet(Some(from_err_expr)),
ThinVec::new());
let ret_stmt = self.stmt_expr(ret_expr);
let block = self.signal_block_stmt(ret_stmt, e.span,
hir::PushUnstableBlock, ThinVec::new());

let err_pat = self.pat_err(e.span, err_local);
self.arm(hir_vec![err_pat], block)
};

return self.expr_match(e.span, sub_expr, hir_vec![err_arm, ok_arm],
return self.expr_match(e.span, discr, hir_vec![err_arm, ok_arm],
hir::MatchSource::TryDesugar);
}

Expand Down Expand Up @@ -1787,6 +1797,15 @@ impl<'a> LoweringContext<'a> {
(respan(sp, hir::StmtDecl(P(decl), self.next_id())), pat_id)
}

// Turns `<expr>` into `<expr>;`, note that this produces a StmtSemi, not a
// StmtExpr.
fn stmt_expr(&self, expr: P<hir::Expr>) -> hir::Stmt {
hir::Stmt {
span: expr.span,
node: hir::StmtSemi(expr, self.next_id()),
}
}

fn block_expr(&mut self, expr: P<hir::Expr>) -> P<hir::Block> {
self.block_all(expr.span, hir::HirVec::new(), Some(expr))
}
Expand All @@ -1803,26 +1822,22 @@ impl<'a> LoweringContext<'a> {
}

fn pat_ok(&mut self, span: Span, pat: P<hir::Pat>) -> P<hir::Pat> {
let ok = self.std_path(&["result", "Result", "Ok"]);
let path = self.path_global(span, ok);
let path = self.std_path(span, &["result", "Result", "Ok"]);
self.pat_enum(span, path, hir_vec![pat])
}

fn pat_err(&mut self, span: Span, pat: P<hir::Pat>) -> P<hir::Pat> {
let err = self.std_path(&["result", "Result", "Err"]);
let path = self.path_global(span, err);
let path = self.std_path(span, &["result", "Result", "Err"]);
self.pat_enum(span, path, hir_vec![pat])
}

fn pat_some(&mut self, span: Span, pat: P<hir::Pat>) -> P<hir::Pat> {
let some = self.std_path(&["option", "Option", "Some"]);
let path = self.path_global(span, some);
let path = self.std_path(span, &["option", "Option", "Some"]);
self.pat_enum(span, path, hir_vec![pat])
}

fn pat_none(&mut self, span: Span) -> P<hir::Pat> {
let none = self.std_path(&["option", "Option", "None"]);
let path = self.path_global(span, none);
let path = self.std_path(span, &["option", "Option", "None"]);
self.pat_enum(span, path, hir_vec![])
}

Expand Down Expand Up @@ -1920,7 +1935,7 @@ impl<'a> LoweringContext<'a> {
}
}

fn std_path(&mut self, components: &[&str]) -> Vec<Name> {
fn std_path_components(&mut self, components: &[&str]) -> Vec<Name> {
let mut v = Vec::new();
if let Some(s) = self.crate_root {
v.push(token::intern(s));
Expand All @@ -1931,8 +1946,8 @@ impl<'a> LoweringContext<'a> {

// Given suffix ["b","c","d"], returns path `::std::b::c::d` when
// `fld.cx.use_std`, and `::core::b::c::d` otherwise.
fn core_path(&mut self, span: Span, components: &[&str]) -> hir::Path {
let idents = self.std_path(components);
fn std_path(&mut self, span: Span, components: &[&str]) -> hir::Path {
let idents = self.std_path_components(components);
self.path_global(span, idents)
}

Expand All @@ -1953,4 +1968,21 @@ impl<'a> LoweringContext<'a> {
});
self.expr_block(block, attrs)
}

fn signal_block_stmt(&mut self,
stmt: hir::Stmt,
span: Span,
rule: hir::BlockCheckMode,
attrs: ThinVec<Attribute>)
-> P<hir::Expr> {
let id = self.next_id();
let block = P(hir::Block {
rules: rule,
span: span,
id: id,
stmts: hir_vec![stmt],
expr: None,
});
self.expr_block(block, attrs)
}
}
4 changes: 2 additions & 2 deletions src/librustc/ty/relate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,13 +151,13 @@ pub fn relate_substs<'a, 'gcx, 'tcx, R>(relation: &mut R,
let b_ty = &b_subst.types[i];
let variance = variances.map_or(ty::Invariant, |v| v.types[i]);
relation.relate_with_variance(variance, a_ty, b_ty)
}).collect()?;
}).collect::<Result<_, _>>()?;

let regions = a_subst.regions.iter().enumerate().map(|(i, a_r)| {
let b_r = &b_subst.regions[i];
let variance = variances.map_or(ty::Invariant, |v| v.regions[i]);
relation.relate_with_variance(variance, a_r, b_r)
}).collect()?;
}).collect::<Result<_, _>>()?;

Ok(Substs::new(tcx, types, regions))
}
Expand Down
27 changes: 27 additions & 0 deletions src/test/compile-fail/question-mark-type-infer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(question_mark, question_mark_carrier)]

// Test that type inference fails where there are multiple possible return types
// for the `?` operator.

fn f(x: &i32) -> Result<i32, ()> {
Ok(*x)
}

fn g() -> Result<Vec<i32>, ()> {
let l = [1, 2, 3, 4];
l.iter().map(f).collect()? //~ ERROR type annotations required: cannot resolve
}

fn main() {
g();
}
Loading