Skip to content
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

Remove ty_bot from the type system #17603

Closed
wants to merge 3 commits into from
Closed
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
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