Skip to content

Commit 1652a1f

Browse files
committed
auto merge of #17603 : jakub-/rust/ty_bot, r=nikomatsakis
We now instead use a fresh variable for expressions that diverge. Closes #14973. Closes #13847. [Work in progress] cc @nikomatsakis
2 parents 98bbccf + 6d2080c commit 1652a1f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+843
-675
lines changed

src/libcore/intrinsics.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ pub trait TyVisitor {
149149
fn visit_fn_input(&mut self, i: uint, mode: uint,
150150
inner: *const TyDesc) -> bool;
151151
fn visit_fn_output(&mut self, retstyle: uint, variadic: bool,
152-
inner: *const TyDesc) -> bool;
152+
converging: bool, inner: *const TyDesc) -> bool;
153153
fn visit_leave_fn(&mut self, purity: uint, proto: uint,
154154
n_inputs: uint, retstyle: uint) -> bool;
155155

src/librustc/diagnostics.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -145,5 +145,6 @@ register_diagnostics!(
145145
E0162,
146146
E0163,
147147
E0164,
148-
E0165
148+
E0165,
149+
E0166
149150
)

src/librustc/lint/builtin.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,7 @@ impl LintPass for UnusedResults {
683683
let t = ty::expr_ty(cx.tcx, expr);
684684
let mut warned = false;
685685
match ty::get(t).sty {
686-
ty::ty_nil | ty::ty_bot | ty::ty_bool => return,
686+
ty::ty_nil | ty::ty_bool => return,
687687
ty::ty_struct(did, _) |
688688
ty::ty_enum(did, _) => {
689689
if ast_util::is_local(did) {

src/librustc/metadata/tydecode.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,6 @@ fn parse_trait_ref(st: &mut PState, conv: conv_did) -> ty::TraitRef {
359359
fn parse_ty(st: &mut PState, conv: conv_did) -> ty::t {
360360
match next(st) {
361361
'n' => return ty::mk_nil(),
362-
'z' => return ty::mk_bot(),
363362
'b' => return ty::mk_bool(),
364363
'i' => return ty::mk_int(),
365364
'u' => return ty::mk_uint(),
@@ -590,10 +589,16 @@ fn parse_sig(st: &mut PState, conv: conv_did) -> ty::FnSig {
590589
'N' => false,
591590
r => fail!(format!("bad variadic: {}", r)),
592591
};
593-
let ret_ty = parse_ty(st, |x,y| conv(x,y));
592+
let output = match peek(st) {
593+
'z' => {
594+
st.pos += 1u;
595+
ty::FnDiverging
596+
}
597+
_ => ty::FnConverging(parse_ty(st, |x,y| conv(x,y)))
598+
};
594599
ty::FnSig {binder_id: id,
595600
inputs: inputs,
596-
output: ret_ty,
601+
output: output,
597602
variadic: variadic}
598603
}
599604

src/librustc/metadata/tyencode.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,6 @@ pub fn enc_trait_store(w: &mut SeekableMemWriter, cx: &ctxt, s: ty::TraitStore)
200200
fn enc_sty(w: &mut SeekableMemWriter, cx: &ctxt, st: &ty::sty) {
201201
match *st {
202202
ty::ty_nil => mywrite!(w, "n"),
203-
ty::ty_bot => mywrite!(w, "z"),
204203
ty::ty_bool => mywrite!(w, "b"),
205204
ty::ty_char => mywrite!(w, "c"),
206205
ty::ty_int(t) => {
@@ -346,7 +345,14 @@ fn enc_fn_sig(w: &mut SeekableMemWriter, cx: &ctxt, fsig: &ty::FnSig) {
346345
} else {
347346
mywrite!(w, "N");
348347
}
349-
enc_ty(w, cx, fsig.output);
348+
match fsig.output {
349+
ty::FnConverging(result_type) => {
350+
enc_ty(w, cx, result_type);
351+
}
352+
ty::FnDiverging => {
353+
mywrite!(w, "z");
354+
}
355+
}
350356
}
351357

352358
pub fn enc_builtin_bounds(w: &mut SeekableMemWriter, _cx: &ctxt, bs: &ty::BuiltinBounds) {

src/librustc/middle/cfg/construct.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -511,12 +511,15 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
511511
pred: CFGIndex,
512512
func_or_rcvr: &ast::Expr,
513513
args: I) -> CFGIndex {
514+
let method_call = typeck::MethodCall::expr(call_expr.id);
515+
let return_ty = ty::ty_fn_ret(match self.tcx.method_map.borrow().find(&method_call) {
516+
Some(method) => method.ty,
517+
None => ty::expr_ty(self.tcx, func_or_rcvr)
518+
});
519+
514520
let func_or_rcvr_exit = self.expr(func_or_rcvr, pred);
515521
let ret = self.straightline(call_expr, func_or_rcvr_exit, args);
516-
517-
let return_ty = ty::node_id_to_type(self.tcx, call_expr.id);
518-
let fails = ty::type_is_bot(return_ty);
519-
if fails {
522+
if return_ty == ty::FnDiverging {
520523
self.add_node(ast::DUMMY_NODE_ID, [])
521524
} else {
522525
ret

src/librustc/middle/expr_use_visitor.rs

+3-7
Original file line numberDiff line numberDiff line change
@@ -396,13 +396,9 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,TYPER> {
396396
// make sure that the thing we are pointing out stays valid
397397
// for the lifetime `scope_r` of the resulting ptr:
398398
let expr_ty = ty::expr_ty(self.tcx(), expr);
399-
if !ty::type_is_bot(expr_ty) {
400-
let r = ty::ty_region(self.tcx(), expr.span, expr_ty);
401-
let bk = ty::BorrowKind::from_mutbl(m);
402-
self.borrow_expr(&**base, r, bk, AddrOf);
403-
} else {
404-
self.walk_expr(&**base);
405-
}
399+
let r = ty::ty_region(self.tcx(), expr.span, expr_ty);
400+
let bk = ty::BorrowKind::from_mutbl(m);
401+
self.borrow_expr(&**base, r, bk, AddrOf);
406402
}
407403

408404
ast::ExprInlineAsm(ref ia) => {

src/librustc/middle/intrinsicck.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,10 @@ impl<'a, 'tcx, 'v> Visitor<'v> for IntrinsicCheckingVisitor<'a, 'tcx> {
126126
match ty::get(typ).sty {
127127
ty_bare_fn(ref bare_fn_ty)
128128
if bare_fn_ty.abi == RustIntrinsic => {
129-
let from = bare_fn_ty.sig.inputs[0];
130-
let to = bare_fn_ty.sig.output;
131-
self.check_transmute(expr.span, from, to, expr.id);
129+
if let ty::FnConverging(to) = bare_fn_ty.sig.output {
130+
let from = bare_fn_ty.sig.inputs[0];
131+
self.check_transmute(expr.span, from, to, expr.id);
132+
}
132133
}
133134
_ => {
134135
self.tcx

src/librustc/middle/liveness.rs

+82-53
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,18 @@
100100
* - `no_ret_var`: a synthetic variable that is only 'read' from, the
101101
* fallthrough node. This allows us to detect functions where we fail
102102
* to return explicitly.
103+
* - `clean_exit_var`: a synthetic variable that is only 'read' from the
104+
* fallthrough node. It is only live if the function could converge
105+
* via means other than an explicit `return` expression. That is, it is
106+
* only dead if the end of the function's block can never be reached.
107+
* It is the responsibility of typeck to ensure that there are no
108+
* `return` expressions in a function declared as diverging.
103109
*/
104110

105111
use middle::def::*;
106112
use middle::mem_categorization::Typer;
107113
use middle::pat_util;
114+
use middle::typeck;
108115
use middle::ty;
109116
use lint;
110117
use util::nodemap::NodeMap;
@@ -250,7 +257,8 @@ struct LocalInfo {
250257
enum VarKind {
251258
Arg(NodeId, Ident),
252259
Local(LocalInfo),
253-
ImplicitRet
260+
ImplicitRet,
261+
CleanExit
254262
}
255263

256264
struct IrMaps<'a, 'tcx: 'a> {
@@ -306,7 +314,7 @@ impl<'a, 'tcx> IrMaps<'a, 'tcx> {
306314
Local(LocalInfo { id: node_id, .. }) | Arg(node_id, _) => {
307315
self.variable_map.insert(node_id, v);
308316
},
309-
ImplicitRet => {}
317+
ImplicitRet | CleanExit => {}
310318
}
311319

312320
debug!("{} is {}", v.to_string(), vk);
@@ -331,7 +339,8 @@ impl<'a, 'tcx> IrMaps<'a, 'tcx> {
331339
Local(LocalInfo { ident: nm, .. }) | Arg(_, nm) => {
332340
token::get_ident(nm).get().to_string()
333341
},
334-
ImplicitRet => "<implicit-ret>".to_string()
342+
ImplicitRet => "<implicit-ret>".to_string(),
343+
CleanExit => "<clean-exit>".to_string()
335344
}
336345
}
337346

@@ -397,7 +406,8 @@ fn visit_fn(ir: &mut IrMaps,
397406
let specials = Specials {
398407
exit_ln: fn_maps.add_live_node(ExitNode),
399408
fallthrough_ln: fn_maps.add_live_node(ExitNode),
400-
no_ret_var: fn_maps.add_variable(ImplicitRet)
409+
no_ret_var: fn_maps.add_variable(ImplicitRet),
410+
clean_exit_var: fn_maps.add_variable(CleanExit)
401411
};
402412

403413
// compute liveness
@@ -546,7 +556,8 @@ fn invalid_users() -> Users {
546556
struct Specials {
547557
exit_ln: LiveNode,
548558
fallthrough_ln: LiveNode,
549-
no_ret_var: Variable
559+
no_ret_var: Variable,
560+
clean_exit_var: Variable
550561
}
551562

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

877889
self.propagate_through_block(blk, s.fallthrough_ln)
878890
}
@@ -943,9 +955,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
943955
opt_expr: Option<&Expr>,
944956
succ: LiveNode)
945957
-> LiveNode {
946-
opt_expr.iter().fold(succ, |succ, expr| {
947-
self.propagate_through_expr(&**expr, succ)
948-
})
958+
opt_expr.map_or(succ, |expr| self.propagate_through_expr(expr, succ))
949959
}
950960

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

11481158
ExprCall(ref f, ref args) => {
1149-
// calling a fn with bot return type means that the fn
1150-
// will fail, and hence the successors can be ignored
1151-
let is_bot = !self.ir.tcx.is_method_call(expr.id) && {
1159+
let diverges = !self.ir.tcx.is_method_call(expr.id) && {
11521160
let t_ret = ty::ty_fn_ret(ty::expr_ty(self.ir.tcx, &**f));
1153-
ty::type_is_bot(t_ret)
1161+
t_ret == ty::FnDiverging
11541162
};
1155-
let succ = if is_bot {
1163+
let succ = if diverges {
11561164
self.s.exit_ln
11571165
} else {
11581166
succ
@@ -1162,11 +1170,14 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
11621170
}
11631171

11641172
ExprMethodCall(_, _, ref args) => {
1165-
// calling a method with bot return type means that the method
1166-
// will fail, and hence the successors can be ignored
1167-
let t_ret = ty::node_id_to_type(self.ir.tcx, expr.id);
1168-
let succ = if ty::type_is_bot(t_ret) {self.s.exit_ln}
1169-
else {succ};
1173+
let method_call = typeck::MethodCall::expr(expr.id);
1174+
let method_ty = self.ir.tcx.method_map.borrow().find(&method_call).unwrap().ty;
1175+
let diverges = ty::ty_fn_ret(method_ty) == ty::FnDiverging;
1176+
let succ = if diverges {
1177+
self.s.exit_ln
1178+
} else {
1179+
succ
1180+
};
11701181
self.propagate_through_exprs(args.as_slice(), succ)
11711182
}
11721183

@@ -1507,50 +1518,68 @@ fn check_fn(_v: &Liveness,
15071518
}
15081519

15091520
impl<'a, 'tcx> Liveness<'a, 'tcx> {
1521+
fn fn_ret(&self, id: NodeId) -> ty::FnOutput {
1522+
let fn_ty = ty::node_id_to_type(self.ir.tcx, id);
1523+
match ty::get(fn_ty).sty {
1524+
ty::ty_unboxed_closure(closure_def_id, _, _) =>
1525+
self.ir.tcx.unboxed_closures()
1526+
.borrow()
1527+
.find(&closure_def_id)
1528+
.unwrap()
1529+
.closure_type
1530+
.sig
1531+
.output,
1532+
_ => ty::ty_fn_ret(fn_ty)
1533+
}
1534+
}
1535+
15101536
fn check_ret(&self,
15111537
id: NodeId,
15121538
sp: Span,
15131539
_fk: FnKind,
15141540
entry_ln: LiveNode,
15151541
body: &Block) {
1516-
if self.live_on_entry(entry_ln, self.s.no_ret_var).is_some() {
1517-
// if no_ret_var is live, then we fall off the end of the
1518-
// function without any kind of return expression:
1519-
1520-
let t_ret = ty::ty_fn_ret(ty::node_id_to_type(self.ir.tcx, id));
1521-
if ty::type_is_nil(t_ret) {
1522-
// for nil return types, it is ok to not return a value expl.
1523-
} else if ty::type_is_bot(t_ret) {
1524-
// for bot return types, not ok. Function should fail.
1525-
self.ir.tcx.sess.span_err(
1526-
sp, "some control paths may return");
1527-
} else {
1528-
let ends_with_stmt = match body.expr {
1529-
None if body.stmts.len() > 0 =>
1530-
match body.stmts.last().unwrap().node {
1531-
StmtSemi(ref e, _) => {
1532-
let t_stmt = ty::expr_ty(self.ir.tcx, &**e);
1533-
ty::get(t_stmt).sty == ty::get(t_ret).sty
1542+
match self.fn_ret(id) {
1543+
ty::FnConverging(t_ret)
1544+
if self.live_on_entry(entry_ln, self.s.no_ret_var).is_some() => {
1545+
1546+
if ty::type_is_nil(t_ret) {
1547+
// for nil return types, it is ok to not return a value expl.
1548+
} else {
1549+
let ends_with_stmt = match body.expr {
1550+
None if body.stmts.len() > 0 =>
1551+
match body.stmts.last().unwrap().node {
1552+
StmtSemi(ref e, _) => {
1553+
let t_stmt = ty::expr_ty(self.ir.tcx, &**e);
1554+
ty::get(t_stmt).sty == ty::get(t_ret).sty
1555+
},
1556+
_ => false
15341557
},
1535-
_ => false
1536-
},
1537-
_ => false
1538-
};
1539-
self.ir.tcx.sess.span_err(
1540-
sp, "not all control paths return a value");
1541-
if ends_with_stmt {
1542-
let last_stmt = body.stmts.last().unwrap();
1543-
let original_span = original_sp(self.ir.tcx.sess.codemap(),
1544-
last_stmt.span, sp);
1545-
let span_semicolon = Span {
1546-
lo: original_span.hi - BytePos(1),
1547-
hi: original_span.hi,
1548-
expn_id: original_span.expn_id
1558+
_ => false
15491559
};
1550-
self.ir.tcx.sess.span_note(
1551-
span_semicolon, "consider removing this semicolon:");
1560+
self.ir.tcx.sess.span_err(
1561+
sp, "not all control paths return a value");
1562+
if ends_with_stmt {
1563+
let last_stmt = body.stmts.last().unwrap();
1564+
let original_span = original_sp(self.ir.tcx.sess.codemap(),
1565+
last_stmt.span, sp);
1566+
let span_semicolon = Span {
1567+
lo: original_span.hi - BytePos(1),
1568+
hi: original_span.hi,
1569+
expn_id: original_span.expn_id
1570+
};
1571+
self.ir.tcx.sess.span_note(
1572+
span_semicolon, "consider removing this semicolon:");
1573+
}
15521574
}
1553-
}
1575+
}
1576+
ty::FnDiverging
1577+
if self.live_on_entry(entry_ln, self.s.clean_exit_var).is_some() => {
1578+
self.ir.tcx.sess.span_err(sp,
1579+
"computation may converge in a function marked as diverging");
1580+
}
1581+
1582+
_ => {}
15541583
}
15551584
}
15561585

src/librustc/middle/mem_categorization.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
485485
Some(method_ty) => {
486486
// If this is an index implemented by a method call, then it will
487487
// include an implicit deref of the result.
488-
let ret_ty = ty::ty_fn_ret(method_ty);
488+
let ret_ty = ty::ty_fn_ret(method_ty).unwrap();
489489
Ok(self.cat_deref(expr,
490490
self.cat_rvalue_node(expr.id(),
491491
expr.span(),
@@ -878,7 +878,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
878878

879879
let base_cmt = match method_ty {
880880
Some(method_ty) => {
881-
let ref_ty = ty::ty_fn_ret(method_ty);
881+
let ref_ty = ty::ty_fn_ret(method_ty).unwrap();
882882
self.cat_rvalue_node(node.id(), node.span(), ref_ty)
883883
}
884884
None => base_cmt
@@ -957,7 +957,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
957957

958958
let element_ty = match method_ty {
959959
Some(method_ty) => {
960-
let ref_ty = ty::ty_fn_ret(method_ty);
960+
let ref_ty = ty::ty_fn_ret(method_ty).unwrap();
961961
base_cmt = self.cat_rvalue_node(elt.id(), elt.span(), ref_ty);
962962
ty::ty_fn_args(method_ty)[0]
963963
}

src/librustc/middle/traits/coherence.rs

-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ pub fn ty_is_local(tcx: &ty::ctxt,
8080

8181
match ty::get(ty).sty {
8282
ty::ty_nil |
83-
ty::ty_bot |
8483
ty::ty_bool |
8584
ty::ty_char |
8685
ty::ty_int(..) |

0 commit comments

Comments
 (0)