Skip to content

Commit

Permalink
auto merge of #17603 : jakub-/rust/ty_bot, r=nikomatsakis
Browse files Browse the repository at this point in the history
We now instead use a fresh variable for expressions that diverge.

Closes #14973.
Closes #13847.

[Work in progress]

cc @nikomatsakis
  • Loading branch information
bors committed Oct 28, 2014
2 parents 98bbccf + 6d2080c commit 1652a1f
Show file tree
Hide file tree
Showing 67 changed files with 843 additions and 675 deletions.
2 changes: 1 addition & 1 deletion src/libcore/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ pub trait TyVisitor {
fn visit_fn_input(&mut self, i: uint, mode: uint,
inner: *const TyDesc) -> bool;
fn visit_fn_output(&mut self, retstyle: uint, variadic: bool,
inner: *const TyDesc) -> bool;
converging: bool, inner: *const TyDesc) -> bool;
fn visit_leave_fn(&mut self, purity: uint, proto: uint,
n_inputs: uint, retstyle: uint) -> bool;

Expand Down
3 changes: 2 additions & 1 deletion src/librustc/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,5 +145,6 @@ register_diagnostics!(
E0162,
E0163,
E0164,
E0165
E0165,
E0166
)
2 changes: 1 addition & 1 deletion src/librustc/lint/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,7 @@ impl LintPass for UnusedResults {
let t = ty::expr_ty(cx.tcx, expr);
let mut warned = false;
match ty::get(t).sty {
ty::ty_nil | ty::ty_bot | ty::ty_bool => return,
ty::ty_nil | ty::ty_bool => return,
ty::ty_struct(did, _) |
ty::ty_enum(did, _) => {
if ast_util::is_local(did) {
Expand Down
11 changes: 8 additions & 3 deletions src/librustc/metadata/tydecode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,6 @@ fn parse_trait_ref(st: &mut PState, conv: conv_did) -> ty::TraitRef {
fn parse_ty(st: &mut PState, conv: conv_did) -> ty::t {
match next(st) {
'n' => return ty::mk_nil(),
'z' => return ty::mk_bot(),
'b' => return ty::mk_bool(),
'i' => return ty::mk_int(),
'u' => return ty::mk_uint(),
Expand Down Expand Up @@ -590,10 +589,16 @@ fn parse_sig(st: &mut PState, conv: conv_did) -> ty::FnSig {
'N' => false,
r => fail!(format!("bad variadic: {}", r)),
};
let ret_ty = parse_ty(st, |x,y| conv(x,y));
let output = match peek(st) {
'z' => {
st.pos += 1u;
ty::FnDiverging
}
_ => ty::FnConverging(parse_ty(st, |x,y| conv(x,y)))
};
ty::FnSig {binder_id: id,
inputs: inputs,
output: ret_ty,
output: output,
variadic: variadic}
}

Expand Down
10 changes: 8 additions & 2 deletions src/librustc/metadata/tyencode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,6 @@ pub fn enc_trait_store(w: &mut SeekableMemWriter, cx: &ctxt, s: ty::TraitStore)
fn enc_sty(w: &mut SeekableMemWriter, cx: &ctxt, st: &ty::sty) {
match *st {
ty::ty_nil => mywrite!(w, "n"),
ty::ty_bot => mywrite!(w, "z"),
ty::ty_bool => mywrite!(w, "b"),
ty::ty_char => mywrite!(w, "c"),
ty::ty_int(t) => {
Expand Down Expand Up @@ -346,7 +345,14 @@ fn enc_fn_sig(w: &mut SeekableMemWriter, cx: &ctxt, fsig: &ty::FnSig) {
} else {
mywrite!(w, "N");
}
enc_ty(w, cx, fsig.output);
match fsig.output {
ty::FnConverging(result_type) => {
enc_ty(w, cx, result_type);
}
ty::FnDiverging => {
mywrite!(w, "z");
}
}
}

pub fn enc_builtin_bounds(w: &mut SeekableMemWriter, _cx: &ctxt, bs: &ty::BuiltinBounds) {
Expand Down
11 changes: 7 additions & 4 deletions src/librustc/middle/cfg/construct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,12 +511,15 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
pred: CFGIndex,
func_or_rcvr: &ast::Expr,
args: I) -> CFGIndex {
let method_call = typeck::MethodCall::expr(call_expr.id);
let return_ty = ty::ty_fn_ret(match self.tcx.method_map.borrow().find(&method_call) {
Some(method) => method.ty,
None => ty::expr_ty(self.tcx, func_or_rcvr)
});

let func_or_rcvr_exit = self.expr(func_or_rcvr, pred);
let ret = self.straightline(call_expr, func_or_rcvr_exit, args);

let return_ty = ty::node_id_to_type(self.tcx, call_expr.id);
let fails = ty::type_is_bot(return_ty);
if fails {
if return_ty == ty::FnDiverging {
self.add_node(ast::DUMMY_NODE_ID, [])
} else {
ret
Expand Down
10 changes: 3 additions & 7 deletions src/librustc/middle/expr_use_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -396,13 +396,9 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,TYPER> {
// make sure that the thing we are pointing out stays valid
// for the lifetime `scope_r` of the resulting ptr:
let expr_ty = ty::expr_ty(self.tcx(), expr);
if !ty::type_is_bot(expr_ty) {
let r = ty::ty_region(self.tcx(), expr.span, expr_ty);
let bk = ty::BorrowKind::from_mutbl(m);
self.borrow_expr(&**base, r, bk, AddrOf);
} else {
self.walk_expr(&**base);
}
let r = ty::ty_region(self.tcx(), expr.span, expr_ty);
let bk = ty::BorrowKind::from_mutbl(m);
self.borrow_expr(&**base, r, bk, AddrOf);
}

ast::ExprInlineAsm(ref ia) => {
Expand Down
7 changes: 4 additions & 3 deletions src/librustc/middle/intrinsicck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,10 @@ impl<'a, 'tcx, 'v> Visitor<'v> for IntrinsicCheckingVisitor<'a, 'tcx> {
match ty::get(typ).sty {
ty_bare_fn(ref bare_fn_ty)
if bare_fn_ty.abi == RustIntrinsic => {
let from = bare_fn_ty.sig.inputs[0];
let to = bare_fn_ty.sig.output;
self.check_transmute(expr.span, from, to, expr.id);
if let ty::FnConverging(to) = bare_fn_ty.sig.output {
let from = bare_fn_ty.sig.inputs[0];
self.check_transmute(expr.span, from, to, expr.id);
}
}
_ => {
self.tcx
Expand Down
135 changes: 82 additions & 53 deletions src/librustc/middle/liveness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,18 @@
* - `no_ret_var`: a synthetic variable that is only 'read' from, the
* fallthrough node. This allows us to detect functions where we fail
* to return explicitly.
* - `clean_exit_var`: a synthetic variable that is only 'read' from the
* fallthrough node. It is only live if the function could converge
* via means other than an explicit `return` expression. That is, it is
* only dead if the end of the function's block can never be reached.
* It is the responsibility of typeck to ensure that there are no
* `return` expressions in a function declared as diverging.
*/

use middle::def::*;
use middle::mem_categorization::Typer;
use middle::pat_util;
use middle::typeck;
use middle::ty;
use lint;
use util::nodemap::NodeMap;
Expand Down Expand Up @@ -250,7 +257,8 @@ struct LocalInfo {
enum VarKind {
Arg(NodeId, Ident),
Local(LocalInfo),
ImplicitRet
ImplicitRet,
CleanExit
}

struct IrMaps<'a, 'tcx: 'a> {
Expand Down Expand Up @@ -306,7 +314,7 @@ impl<'a, 'tcx> IrMaps<'a, 'tcx> {
Local(LocalInfo { id: node_id, .. }) | Arg(node_id, _) => {
self.variable_map.insert(node_id, v);
},
ImplicitRet => {}
ImplicitRet | CleanExit => {}
}

debug!("{} is {}", v.to_string(), vk);
Expand All @@ -331,7 +339,8 @@ impl<'a, 'tcx> IrMaps<'a, 'tcx> {
Local(LocalInfo { ident: nm, .. }) | Arg(_, nm) => {
token::get_ident(nm).get().to_string()
},
ImplicitRet => "<implicit-ret>".to_string()
ImplicitRet => "<implicit-ret>".to_string(),
CleanExit => "<clean-exit>".to_string()
}
}

Expand Down Expand Up @@ -397,7 +406,8 @@ fn visit_fn(ir: &mut IrMaps,
let specials = Specials {
exit_ln: fn_maps.add_live_node(ExitNode),
fallthrough_ln: fn_maps.add_live_node(ExitNode),
no_ret_var: fn_maps.add_variable(ImplicitRet)
no_ret_var: fn_maps.add_variable(ImplicitRet),
clean_exit_var: fn_maps.add_variable(CleanExit)
};

// compute liveness
Expand Down Expand Up @@ -546,7 +556,8 @@ fn invalid_users() -> Users {
struct Specials {
exit_ln: LiveNode,
fallthrough_ln: LiveNode,
no_ret_var: Variable
no_ret_var: Variable,
clean_exit_var: Variable
}

static ACC_READ: uint = 1u;
Expand Down Expand Up @@ -873,6 +884,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
if blk.expr.is_none() {
self.acc(s.fallthrough_ln, s.no_ret_var, ACC_READ)
}
self.acc(s.fallthrough_ln, s.clean_exit_var, ACC_READ);

self.propagate_through_block(blk, s.fallthrough_ln)
}
Expand Down Expand Up @@ -943,9 +955,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
opt_expr: Option<&Expr>,
succ: LiveNode)
-> LiveNode {
opt_expr.iter().fold(succ, |succ, expr| {
self.propagate_through_expr(&**expr, succ)
})
opt_expr.map_or(succ, |expr| self.propagate_through_expr(expr, succ))
}

fn propagate_through_expr(&mut self, expr: &Expr, succ: LiveNode)
Expand Down Expand Up @@ -1146,13 +1156,11 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
}

ExprCall(ref f, ref args) => {
// calling a fn with bot return type means that the fn
// will fail, and hence the successors can be ignored
let is_bot = !self.ir.tcx.is_method_call(expr.id) && {
let diverges = !self.ir.tcx.is_method_call(expr.id) && {
let t_ret = ty::ty_fn_ret(ty::expr_ty(self.ir.tcx, &**f));
ty::type_is_bot(t_ret)
t_ret == ty::FnDiverging
};
let succ = if is_bot {
let succ = if diverges {
self.s.exit_ln
} else {
succ
Expand All @@ -1162,11 +1170,14 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
}

ExprMethodCall(_, _, ref args) => {
// calling a method with bot return type means that the method
// will fail, and hence the successors can be ignored
let t_ret = ty::node_id_to_type(self.ir.tcx, expr.id);
let succ = if ty::type_is_bot(t_ret) {self.s.exit_ln}
else {succ};
let method_call = typeck::MethodCall::expr(expr.id);
let method_ty = self.ir.tcx.method_map.borrow().find(&method_call).unwrap().ty;
let diverges = ty::ty_fn_ret(method_ty) == ty::FnDiverging;
let succ = if diverges {
self.s.exit_ln
} else {
succ
};
self.propagate_through_exprs(args.as_slice(), succ)
}

Expand Down Expand Up @@ -1507,50 +1518,68 @@ fn check_fn(_v: &Liveness,
}

impl<'a, 'tcx> Liveness<'a, 'tcx> {
fn fn_ret(&self, id: NodeId) -> ty::FnOutput {
let fn_ty = ty::node_id_to_type(self.ir.tcx, id);
match ty::get(fn_ty).sty {
ty::ty_unboxed_closure(closure_def_id, _, _) =>
self.ir.tcx.unboxed_closures()
.borrow()
.find(&closure_def_id)
.unwrap()
.closure_type
.sig
.output,
_ => ty::ty_fn_ret(fn_ty)
}
}

fn check_ret(&self,
id: NodeId,
sp: Span,
_fk: FnKind,
entry_ln: LiveNode,
body: &Block) {
if self.live_on_entry(entry_ln, self.s.no_ret_var).is_some() {
// if no_ret_var is live, then we fall off the end of the
// function without any kind of return expression:

let t_ret = ty::ty_fn_ret(ty::node_id_to_type(self.ir.tcx, id));
if ty::type_is_nil(t_ret) {
// for nil return types, it is ok to not return a value expl.
} else if ty::type_is_bot(t_ret) {
// for bot return types, not ok. Function should fail.
self.ir.tcx.sess.span_err(
sp, "some control paths may return");
} else {
let ends_with_stmt = match body.expr {
None if body.stmts.len() > 0 =>
match body.stmts.last().unwrap().node {
StmtSemi(ref e, _) => {
let t_stmt = ty::expr_ty(self.ir.tcx, &**e);
ty::get(t_stmt).sty == ty::get(t_ret).sty
match self.fn_ret(id) {
ty::FnConverging(t_ret)
if self.live_on_entry(entry_ln, self.s.no_ret_var).is_some() => {

if ty::type_is_nil(t_ret) {
// for nil return types, it is ok to not return a value expl.
} else {
let ends_with_stmt = match body.expr {
None if body.stmts.len() > 0 =>
match body.stmts.last().unwrap().node {
StmtSemi(ref e, _) => {
let t_stmt = ty::expr_ty(self.ir.tcx, &**e);
ty::get(t_stmt).sty == ty::get(t_ret).sty
},
_ => false
},
_ => false
},
_ => false
};
self.ir.tcx.sess.span_err(
sp, "not all control paths return a value");
if ends_with_stmt {
let last_stmt = body.stmts.last().unwrap();
let original_span = original_sp(self.ir.tcx.sess.codemap(),
last_stmt.span, sp);
let span_semicolon = Span {
lo: original_span.hi - BytePos(1),
hi: original_span.hi,
expn_id: original_span.expn_id
_ => false
};
self.ir.tcx.sess.span_note(
span_semicolon, "consider removing this semicolon:");
self.ir.tcx.sess.span_err(
sp, "not all control paths return a value");
if ends_with_stmt {
let last_stmt = body.stmts.last().unwrap();
let original_span = original_sp(self.ir.tcx.sess.codemap(),
last_stmt.span, sp);
let span_semicolon = Span {
lo: original_span.hi - BytePos(1),
hi: original_span.hi,
expn_id: original_span.expn_id
};
self.ir.tcx.sess.span_note(
span_semicolon, "consider removing this semicolon:");
}
}
}
}
ty::FnDiverging
if self.live_on_entry(entry_ln, self.s.clean_exit_var).is_some() => {
self.ir.tcx.sess.span_err(sp,
"computation may converge in a function marked as diverging");
}

_ => {}
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/librustc/middle/mem_categorization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
Some(method_ty) => {
// If this is an index implemented by a method call, then it will
// include an implicit deref of the result.
let ret_ty = ty::ty_fn_ret(method_ty);
let ret_ty = ty::ty_fn_ret(method_ty).unwrap();
Ok(self.cat_deref(expr,
self.cat_rvalue_node(expr.id(),
expr.span(),
Expand Down Expand Up @@ -878,7 +878,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {

let base_cmt = match method_ty {
Some(method_ty) => {
let ref_ty = ty::ty_fn_ret(method_ty);
let ref_ty = ty::ty_fn_ret(method_ty).unwrap();
self.cat_rvalue_node(node.id(), node.span(), ref_ty)
}
None => base_cmt
Expand Down Expand Up @@ -957,7 +957,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {

let element_ty = match method_ty {
Some(method_ty) => {
let ref_ty = ty::ty_fn_ret(method_ty);
let ref_ty = ty::ty_fn_ret(method_ty).unwrap();
base_cmt = self.cat_rvalue_node(elt.id(), elt.span(), ref_ty);
ty::ty_fn_args(method_ty)[0]
}
Expand Down
1 change: 0 additions & 1 deletion src/librustc/middle/traits/coherence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ pub fn ty_is_local(tcx: &ty::ctxt,

match ty::get(ty).sty {
ty::ty_nil |
ty::ty_bot |
ty::ty_bool |
ty::ty_char |
ty::ty_int(..) |
Expand Down
Loading

0 comments on commit 1652a1f

Please sign in to comment.