1
Fork 0

Remove ty_bot from the type system

We now instead use a fresh variable for expressions that diverge.
This commit is contained in:
Jakub Bukaj 2014-10-24 21:14:37 +02:00
parent 58dc0a05ab
commit cca84e9e21
49 changed files with 802 additions and 643 deletions

View file

@ -149,7 +149,7 @@ pub trait TyVisitor {
fn visit_fn_input(&mut self, i: uint, mode: uint, fn visit_fn_input(&mut self, i: uint, mode: uint,
inner: *const TyDesc) -> bool; inner: *const TyDesc) -> bool;
fn visit_fn_output(&mut self, retstyle: uint, variadic: 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, fn visit_leave_fn(&mut self, purity: uint, proto: uint,
n_inputs: uint, retstyle: uint) -> bool; n_inputs: uint, retstyle: uint) -> bool;

View file

@ -145,5 +145,6 @@ register_diagnostics!(
E0162, E0162,
E0163, E0163,
E0164, E0164,
E0165 E0165,
E0166
) )

View file

@ -683,7 +683,7 @@ impl LintPass for UnusedResults {
let t = ty::expr_ty(cx.tcx, expr); let t = ty::expr_ty(cx.tcx, expr);
let mut warned = false; let mut warned = false;
match ty::get(t).sty { 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_struct(did, _) |
ty::ty_enum(did, _) => { ty::ty_enum(did, _) => {
if ast_util::is_local(did) { if ast_util::is_local(did) {

View file

@ -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 { fn parse_ty(st: &mut PState, conv: conv_did) -> ty::t {
match next(st) { match next(st) {
'n' => return ty::mk_nil(), 'n' => return ty::mk_nil(),
'z' => return ty::mk_bot(),
'b' => return ty::mk_bool(), 'b' => return ty::mk_bool(),
'i' => return ty::mk_int(), 'i' => return ty::mk_int(),
'u' => return ty::mk_uint(), 'u' => return ty::mk_uint(),
@ -590,10 +589,16 @@ fn parse_sig(st: &mut PState, conv: conv_did) -> ty::FnSig {
'N' => false, 'N' => false,
r => fail!(format!("bad variadic: {}", r)), 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, ty::FnSig {binder_id: id,
inputs: inputs, inputs: inputs,
output: ret_ty, output: output,
variadic: variadic} variadic: variadic}
} }

View file

@ -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) { fn enc_sty(w: &mut SeekableMemWriter, cx: &ctxt, st: &ty::sty) {
match *st { match *st {
ty::ty_nil => mywrite!(w, "n"), ty::ty_nil => mywrite!(w, "n"),
ty::ty_bot => mywrite!(w, "z"),
ty::ty_bool => mywrite!(w, "b"), ty::ty_bool => mywrite!(w, "b"),
ty::ty_char => mywrite!(w, "c"), ty::ty_char => mywrite!(w, "c"),
ty::ty_int(t) => { ty::ty_int(t) => {
@ -346,7 +345,14 @@ fn enc_fn_sig(w: &mut SeekableMemWriter, cx: &ctxt, fsig: &ty::FnSig) {
} else { } else {
mywrite!(w, "N"); 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) { pub fn enc_builtin_bounds(w: &mut SeekableMemWriter, _cx: &ctxt, bs: &ty::BuiltinBounds) {

View file

@ -511,12 +511,15 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
pred: CFGIndex, pred: CFGIndex,
func_or_rcvr: &ast::Expr, func_or_rcvr: &ast::Expr,
args: I) -> CFGIndex { 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 func_or_rcvr_exit = self.expr(func_or_rcvr, pred);
let ret = self.straightline(call_expr, func_or_rcvr_exit, args); let ret = self.straightline(call_expr, func_or_rcvr_exit, args);
if return_ty == ty::FnDiverging {
let return_ty = ty::node_id_to_type(self.tcx, call_expr.id);
let fails = ty::type_is_bot(return_ty);
if fails {
self.add_node(ast::DUMMY_NODE_ID, []) self.add_node(ast::DUMMY_NODE_ID, [])
} else { } else {
ret ret

View file

@ -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 // make sure that the thing we are pointing out stays valid
// for the lifetime `scope_r` of the resulting ptr: // for the lifetime `scope_r` of the resulting ptr:
let expr_ty = ty::expr_ty(self.tcx(), expr); 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 r = ty::ty_region(self.tcx(), expr.span, expr_ty);
let bk = ty::BorrowKind::from_mutbl(m); let bk = ty::BorrowKind::from_mutbl(m);
self.borrow_expr(&**base, r, bk, AddrOf); self.borrow_expr(&**base, r, bk, AddrOf);
} else {
self.walk_expr(&**base);
}
} }
ast::ExprInlineAsm(ref ia) => { ast::ExprInlineAsm(ref ia) => {

View file

@ -126,10 +126,11 @@ impl<'a, 'tcx, 'v> Visitor<'v> for IntrinsicCheckingVisitor<'a, 'tcx> {
match ty::get(typ).sty { match ty::get(typ).sty {
ty_bare_fn(ref bare_fn_ty) ty_bare_fn(ref bare_fn_ty)
if bare_fn_ty.abi == RustIntrinsic => { if bare_fn_ty.abi == RustIntrinsic => {
if let ty::FnConverging(to) = bare_fn_ty.sig.output {
let from = bare_fn_ty.sig.inputs[0]; let from = bare_fn_ty.sig.inputs[0];
let to = bare_fn_ty.sig.output;
self.check_transmute(expr.span, from, to, expr.id); self.check_transmute(expr.span, from, to, expr.id);
} }
}
_ => { _ => {
self.tcx self.tcx
.sess .sess

View file

@ -100,11 +100,16 @@
* - `no_ret_var`: a synthetic variable that is only 'read' from, the * - `no_ret_var`: a synthetic variable that is only 'read' from, the
* fallthrough node. This allows us to detect functions where we fail * fallthrough node. This allows us to detect functions where we fail
* to return explicitly. * 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.
*/ */
use middle::def::*; use middle::def::*;
use middle::mem_categorization::Typer; use middle::mem_categorization::Typer;
use middle::pat_util; use middle::pat_util;
use middle::typeck;
use middle::ty; use middle::ty;
use lint; use lint;
use util::nodemap::NodeMap; use util::nodemap::NodeMap;
@ -250,7 +255,8 @@ struct LocalInfo {
enum VarKind { enum VarKind {
Arg(NodeId, Ident), Arg(NodeId, Ident),
Local(LocalInfo), Local(LocalInfo),
ImplicitRet ImplicitRet,
CleanExit
} }
struct IrMaps<'a, 'tcx: 'a> { struct IrMaps<'a, 'tcx: 'a> {
@ -306,7 +312,7 @@ impl<'a, 'tcx> IrMaps<'a, 'tcx> {
Local(LocalInfo { id: node_id, .. }) | Arg(node_id, _) => { Local(LocalInfo { id: node_id, .. }) | Arg(node_id, _) => {
self.variable_map.insert(node_id, v); self.variable_map.insert(node_id, v);
}, },
ImplicitRet => {} ImplicitRet | CleanExit => {}
} }
debug!("{} is {}", v.to_string(), vk); debug!("{} is {}", v.to_string(), vk);
@ -331,7 +337,8 @@ impl<'a, 'tcx> IrMaps<'a, 'tcx> {
Local(LocalInfo { ident: nm, .. }) | Arg(_, nm) => { Local(LocalInfo { ident: nm, .. }) | Arg(_, nm) => {
token::get_ident(nm).get().to_string() token::get_ident(nm).get().to_string()
}, },
ImplicitRet => "<implicit-ret>".to_string() ImplicitRet => "<implicit-ret>".to_string(),
CleanExit => "<clean-exit>".to_string()
} }
} }
@ -397,7 +404,8 @@ fn visit_fn(ir: &mut IrMaps,
let specials = Specials { let specials = Specials {
exit_ln: fn_maps.add_live_node(ExitNode), exit_ln: fn_maps.add_live_node(ExitNode),
fallthrough_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 // compute liveness
@ -546,7 +554,8 @@ fn invalid_users() -> Users {
struct Specials { struct Specials {
exit_ln: LiveNode, exit_ln: LiveNode,
fallthrough_ln: LiveNode, fallthrough_ln: LiveNode,
no_ret_var: Variable no_ret_var: Variable,
clean_exit_var: Variable
} }
static ACC_READ: uint = 1u; static ACC_READ: uint = 1u;
@ -873,6 +882,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
if blk.expr.is_none() { if blk.expr.is_none() {
self.acc(s.fallthrough_ln, s.no_ret_var, ACC_READ) 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) self.propagate_through_block(blk, s.fallthrough_ln)
} }
@ -943,9 +953,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
opt_expr: Option<&Expr>, opt_expr: Option<&Expr>,
succ: LiveNode) succ: LiveNode)
-> LiveNode { -> LiveNode {
opt_expr.iter().fold(succ, |succ, expr| { opt_expr.map_or(succ, |expr| self.propagate_through_expr(expr, succ))
self.propagate_through_expr(&**expr, succ)
})
} }
fn propagate_through_expr(&mut self, expr: &Expr, succ: LiveNode) fn propagate_through_expr(&mut self, expr: &Expr, succ: LiveNode)
@ -1146,13 +1154,11 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
} }
ExprCall(ref f, ref args) => { ExprCall(ref f, ref args) => {
// calling a fn with bot return type means that the fn let diverges = !self.ir.tcx.is_method_call(expr.id) && {
// will fail, and hence the successors can be ignored
let is_bot = !self.ir.tcx.is_method_call(expr.id) && {
let t_ret = ty::ty_fn_ret(ty::expr_ty(self.ir.tcx, &**f)); 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 self.s.exit_ln
} else { } else {
succ succ
@ -1162,11 +1168,14 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
} }
ExprMethodCall(_, _, ref args) => { ExprMethodCall(_, _, ref args) => {
// calling a method with bot return type means that the method let method_call = typeck::MethodCall::expr(expr.id);
// will fail, and hence the successors can be ignored let method_ty = self.ir.tcx.method_map.borrow().find(&method_call).unwrap().ty;
let t_ret = ty::node_id_to_type(self.ir.tcx, expr.id); let diverges = ty::ty_fn_ret(method_ty) == ty::FnDiverging;
let succ = if ty::type_is_bot(t_ret) {self.s.exit_ln} let succ = if diverges {
else {succ}; self.s.exit_ln
} else {
succ
};
self.propagate_through_exprs(args.as_slice(), succ) self.propagate_through_exprs(args.as_slice(), succ)
} }
@ -1507,23 +1516,33 @@ fn check_fn(_v: &Liveness,
} }
impl<'a, 'tcx> Liveness<'a, 'tcx> { 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, fn check_ret(&self,
id: NodeId, id: NodeId,
sp: Span, sp: Span,
_fk: FnKind, _fk: FnKind,
entry_ln: LiveNode, entry_ln: LiveNode,
body: &Block) { body: &Block) {
if self.live_on_entry(entry_ln, self.s.no_ret_var).is_some() { match self.fn_ret(id) {
// if no_ret_var is live, then we fall off the end of the ty::FnConverging(t_ret)
// function without any kind of return expression: if self.live_on_entry(entry_ln, self.s.no_ret_var).is_some() => {
let t_ret = ty::ty_fn_ret(ty::node_id_to_type(self.ir.tcx, id));
if ty::type_is_nil(t_ret) { if ty::type_is_nil(t_ret) {
// for nil return types, it is ok to not return a value expl. // 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 { } else {
let ends_with_stmt = match body.expr { let ends_with_stmt = match body.expr {
None if body.stmts.len() > 0 => None if body.stmts.len() > 0 =>
@ -1552,6 +1571,14 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
} }
} }
} }
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");
}
_ => {}
}
} }
fn check_lvalue(&mut self, expr: &Expr) { fn check_lvalue(&mut self, expr: &Expr) {

View file

@ -485,7 +485,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
Some(method_ty) => { Some(method_ty) => {
// If this is an index implemented by a method call, then it will // If this is an index implemented by a method call, then it will
// include an implicit deref of the result. // 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, Ok(self.cat_deref(expr,
self.cat_rvalue_node(expr.id(), self.cat_rvalue_node(expr.id(),
expr.span(), expr.span(),
@ -878,7 +878,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
let base_cmt = match method_ty { let base_cmt = match method_ty {
Some(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) self.cat_rvalue_node(node.id(), node.span(), ref_ty)
} }
None => base_cmt None => base_cmt
@ -957,7 +957,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
let element_ty = match method_ty { let element_ty = match method_ty {
Some(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); base_cmt = self.cat_rvalue_node(elt.id(), elt.span(), ref_ty);
ty::ty_fn_args(method_ty)[0] ty::ty_fn_args(method_ty)[0]
} }

View file

@ -80,7 +80,6 @@ pub fn ty_is_local(tcx: &ty::ctxt,
match ty::get(ty).sty { match ty::get(ty).sty {
ty::ty_nil | ty::ty_nil |
ty::ty_bot |
ty::ty_bool | ty::ty_bool |
ty::ty_char | ty::ty_char |
ty::ty_int(..) | ty::ty_int(..) |

View file

@ -1193,7 +1193,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ty::ty_uint(_) | ty::ty_uint(_) |
ty::ty_int(_) | ty::ty_int(_) |
ty::ty_nil | ty::ty_nil |
ty::ty_bot |
ty::ty_bool | ty::ty_bool |
ty::ty_float(_) | ty::ty_float(_) |
ty::ty_bare_fn(_) | ty::ty_bare_fn(_) |
@ -1681,7 +1680,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
def_id: obligation.trait_ref.def_id, def_id: obligation.trait_ref.def_id,
substs: Substs::new_trait( substs: Substs::new_trait(
vec![arguments_tuple.subst(self.tcx(), substs), vec![arguments_tuple.subst(self.tcx(), substs),
new_signature.output.subst(self.tcx(), substs)], new_signature.output.unwrap().subst(self.tcx(), substs)],
vec![], vec![],
obligation.self_ty()) obligation.self_ty())
}); });

View file

@ -1462,16 +1462,12 @@ pub fn store_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
// General path. // General path.
let init_datum = let init_datum =
unpack_datum!(bcx, expr::trans_to_lvalue(bcx, &**init_expr, "let")); unpack_datum!(bcx, expr::trans_to_lvalue(bcx, &**init_expr, "let"));
if ty::type_is_bot(expr_ty(bcx, &**init_expr)) {
create_dummy_locals(bcx, pat)
} else {
if bcx.sess().asm_comments() { if bcx.sess().asm_comments() {
add_comment(bcx, "creating zeroable ref llval"); add_comment(bcx, "creating zeroable ref llval");
} }
let var_scope = cleanup::var_scope(tcx, local.id); let var_scope = cleanup::var_scope(tcx, local.id);
bind_irrefutable_pat(bcx, pat, init_datum.val, var_scope) bind_irrefutable_pat(bcx, pat, init_datum.val, var_scope)
} }
}
None => { None => {
create_dummy_locals(bcx, pat) create_dummy_locals(bcx, pat)
} }

View file

@ -180,7 +180,7 @@ impl<'a, 'tcx> Drop for StatRecorder<'a, 'tcx> {
// only use this for foreign function ABIs and glue, use `decl_rust_fn` for Rust functions // only use this for foreign function ABIs and glue, use `decl_rust_fn` for Rust functions
pub fn decl_fn(ccx: &CrateContext, name: &str, cc: llvm::CallConv, pub fn decl_fn(ccx: &CrateContext, name: &str, cc: llvm::CallConv,
ty: Type, output: ty::t) -> ValueRef { ty: Type, output: ty::FnOutput) -> ValueRef {
let llfn: ValueRef = name.with_c_str(|buf| { let llfn: ValueRef = name.with_c_str(|buf| {
unsafe { unsafe {
@ -188,12 +188,9 @@ pub fn decl_fn(ccx: &CrateContext, name: &str, cc: llvm::CallConv,
} }
}); });
match ty::get(output).sty { // diverging functions may unwind, but can never return normally
// functions returning bottom may unwind, but can never return normally if output == ty::FnDiverging {
ty::ty_bot => { llvm::SetFunctionAttribute(llfn, llvm::NoReturnAttribute);
llvm::SetFunctionAttribute(llfn, llvm::NoReturnAttribute)
}
_ => {}
} }
if ccx.tcx().sess.opts.cg.no_redzone { if ccx.tcx().sess.opts.cg.no_redzone {
@ -216,7 +213,7 @@ pub fn decl_cdecl_fn(ccx: &CrateContext,
name: &str, name: &str,
ty: Type, ty: Type,
output: ty::t) -> ValueRef { output: ty::t) -> ValueRef {
decl_fn(ccx, name, llvm::CCallConv, ty, output) decl_fn(ccx, name, llvm::CCallConv, ty, ty::FnConverging(output))
} }
// only use this for foreign function ABIs and glue, use `get_extern_rust_fn` for Rust functions // only use this for foreign function ABIs and glue, use `get_extern_rust_fn` for Rust functions
@ -231,7 +228,7 @@ pub fn get_extern_fn(ccx: &CrateContext,
Some(n) => return *n, Some(n) => return *n,
None => {} None => {}
} }
let f = decl_fn(ccx, name, cc, ty, output); let f = decl_fn(ccx, name, cc, ty, ty::FnConverging(output));
externs.insert(name.to_string(), f); externs.insert(name.to_string(), f);
f f
} }
@ -1417,7 +1414,7 @@ pub fn new_fn_ctxt<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
llfndecl: ValueRef, llfndecl: ValueRef,
id: ast::NodeId, id: ast::NodeId,
has_env: bool, has_env: bool,
output_type: ty::t, output_type: ty::FnOutput,
param_substs: &'a param_substs, param_substs: &'a param_substs,
sp: Option<Span>, sp: Option<Span>,
block_arena: &'a TypedArena<common::BlockS<'a, 'tcx>>) block_arena: &'a TypedArena<common::BlockS<'a, 'tcx>>)
@ -1432,8 +1429,13 @@ pub fn new_fn_ctxt<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
}, },
id, param_substs.repr(ccx.tcx())); id, param_substs.repr(ccx.tcx()));
let uses_outptr = match output_type {
ty::FnConverging(output_type) => {
let substd_output_type = output_type.substp(ccx.tcx(), param_substs); let substd_output_type = output_type.substp(ccx.tcx(), param_substs);
let uses_outptr = type_of::return_uses_outptr(ccx, substd_output_type); type_of::return_uses_outptr(ccx, substd_output_type)
}
ty::FnDiverging => false
};
let debug_context = debuginfo::create_function_debug_context(ccx, id, param_substs, llfndecl); let debug_context = debuginfo::create_function_debug_context(ccx, id, param_substs, llfndecl);
let nested_returns = has_nested_returns(ccx.tcx(), id); let nested_returns = has_nested_returns(ccx.tcx(), id);
@ -1468,7 +1470,7 @@ pub fn new_fn_ctxt<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
/// and allocating space for the return pointer. /// and allocating space for the return pointer.
pub fn init_function<'a, 'tcx>(fcx: &'a FunctionContext<'a, 'tcx>, pub fn init_function<'a, 'tcx>(fcx: &'a FunctionContext<'a, 'tcx>,
skip_retptr: bool, skip_retptr: bool,
output_type: ty::t) -> Block<'a, 'tcx> { output: ty::FnOutput) -> Block<'a, 'tcx> {
let entry_bcx = fcx.new_temp_block("entry-block"); let entry_bcx = fcx.new_temp_block("entry-block");
// Use a dummy instruction as the insertion point for all allocas. // Use a dummy instruction as the insertion point for all allocas.
@ -1478,10 +1480,10 @@ pub fn init_function<'a, 'tcx>(fcx: &'a FunctionContext<'a, 'tcx>,
llvm::LLVMGetFirstInstruction(entry_bcx.llbb) llvm::LLVMGetFirstInstruction(entry_bcx.llbb)
})); }));
if let ty::FnConverging(output_type) = output {
// This shouldn't need to recompute the return type, // This shouldn't need to recompute the return type,
// as new_fn_ctxt did it already. // as new_fn_ctxt did it already.
let substd_output_type = output_type.substp(fcx.ccx.tcx(), fcx.param_substs); let substd_output_type = output_type.substp(fcx.ccx.tcx(), fcx.param_substs);
if !return_type_is_void(fcx.ccx, substd_output_type) { if !return_type_is_void(fcx.ccx, substd_output_type) {
// If the function returns nil/bot, there is no real return // If the function returns nil/bot, there is no real return
// value, so do not set `llretslotptr`. // value, so do not set `llretslotptr`.
@ -1492,6 +1494,7 @@ pub fn init_function<'a, 'tcx>(fcx: &'a FunctionContext<'a, 'tcx>,
fcx.llretslotptr.set(Some(make_return_slot_pointer(fcx, substd_output_type))); fcx.llretslotptr.set(Some(make_return_slot_pointer(fcx, substd_output_type)));
} }
} }
}
entry_bcx entry_bcx
} }
@ -1693,13 +1696,9 @@ fn copy_unboxed_closure_args_to_allocas<'blk, 'tcx>(
// and builds the return block. // and builds the return block.
pub fn finish_fn<'blk, 'tcx>(fcx: &'blk FunctionContext<'blk, 'tcx>, pub fn finish_fn<'blk, 'tcx>(fcx: &'blk FunctionContext<'blk, 'tcx>,
last_bcx: Block<'blk, 'tcx>, last_bcx: Block<'blk, 'tcx>,
retty: ty::t) { retty: ty::FnOutput) {
let _icx = push_ctxt("finish_fn"); let _icx = push_ctxt("finish_fn");
// This shouldn't need to recompute the return type,
// as new_fn_ctxt did it already.
let substd_retty = retty.substp(fcx.ccx.tcx(), fcx.param_substs);
let ret_cx = match fcx.llreturn.get() { let ret_cx = match fcx.llreturn.get() {
Some(llreturn) => { Some(llreturn) => {
if !last_bcx.terminated.get() { if !last_bcx.terminated.get() {
@ -1709,13 +1708,18 @@ pub fn finish_fn<'blk, 'tcx>(fcx: &'blk FunctionContext<'blk, 'tcx>,
} }
None => last_bcx None => last_bcx
}; };
// This shouldn't need to recompute the return type,
// as new_fn_ctxt did it already.
let substd_retty = retty.substp(fcx.ccx.tcx(), fcx.param_substs);
build_return_block(fcx, ret_cx, substd_retty); build_return_block(fcx, ret_cx, substd_retty);
debuginfo::clear_source_location(fcx); debuginfo::clear_source_location(fcx);
fcx.cleanup(); fcx.cleanup();
} }
// Builds the return block for a function. // Builds the return block for a function.
pub fn build_return_block(fcx: &FunctionContext, ret_cx: Block, retty: ty::t) { pub fn build_return_block(fcx: &FunctionContext, ret_cx: Block, retty: ty::FnOutput) {
if fcx.llretslotptr.get().is_none() || if fcx.llretslotptr.get().is_none() ||
(!fcx.needs_ret_allocas && fcx.caller_expects_out_pointer) { (!fcx.needs_ret_allocas && fcx.caller_expects_out_pointer) {
return RetVoid(ret_cx); return RetVoid(ret_cx);
@ -1738,26 +1742,37 @@ pub fn build_return_block(fcx: &FunctionContext, ret_cx: Block, retty: ty::t) {
retptr.erase_from_parent(); retptr.erase_from_parent();
} }
let retval = if ty::type_is_bool(retty) { let retval = if retty == ty::FnConverging(ty::mk_bool()) {
Trunc(ret_cx, retval, Type::i1(fcx.ccx)) Trunc(ret_cx, retval, Type::i1(fcx.ccx))
} else { } else {
retval retval
}; };
if fcx.caller_expects_out_pointer { if fcx.caller_expects_out_pointer {
if let ty::FnConverging(retty) = retty {
store_ty(ret_cx, retval, get_param(fcx.llfn, 0), retty); store_ty(ret_cx, retval, get_param(fcx.llfn, 0), retty);
return RetVoid(ret_cx); }
RetVoid(ret_cx)
} else { } else {
return Ret(ret_cx, retval); Ret(ret_cx, retval)
} }
} }
// Otherwise, copy the return value to the ret slot // Otherwise, copy the return value to the ret slot
None => { None => match retty {
ty::FnConverging(retty) => {
if fcx.caller_expects_out_pointer { if fcx.caller_expects_out_pointer {
memcpy_ty(ret_cx, get_param(fcx.llfn, 0), retslot, retty); memcpy_ty(ret_cx, get_param(fcx.llfn, 0), retslot, retty);
return RetVoid(ret_cx); RetVoid(ret_cx)
} else { } else {
return Ret(ret_cx, load_ty(ret_cx, retslot, retty)); Ret(ret_cx, load_ty(ret_cx, retslot, retty))
}
}
ty::FnDiverging => {
if fcx.caller_expects_out_pointer {
RetVoid(ret_cx)
} else {
Ret(ret_cx, C_undef(Type::nil(fcx.ccx)))
}
} }
} }
} }
@ -1780,7 +1795,7 @@ pub fn trans_closure(ccx: &CrateContext,
fn_ast_id: ast::NodeId, fn_ast_id: ast::NodeId,
_attributes: &[ast::Attribute], _attributes: &[ast::Attribute],
arg_types: Vec<ty::t>, arg_types: Vec<ty::t>,
output_type: ty::t, output_type: ty::FnOutput,
abi: Abi, abi: Abi,
has_env: bool, has_env: bool,
is_unboxed_closure: IsUnboxedClosureFlag, is_unboxed_closure: IsUnboxedClosureFlag,
@ -1860,7 +1875,7 @@ pub fn trans_closure(ccx: &CrateContext,
debuginfo::start_emitting_source_locations(&fcx); debuginfo::start_emitting_source_locations(&fcx);
let dest = match fcx.llretslotptr.get() { let dest = match fcx.llretslotptr.get() {
Some(_) => expr::SaveIn(fcx.get_ret_slot(bcx, block_ty, "iret_slot")), Some(_) => expr::SaveIn(fcx.get_ret_slot(bcx, ty::FnConverging(block_ty), "iret_slot")),
None => { None => {
assert!(type_is_zero_size(bcx.ccx(), block_ty)); assert!(type_is_zero_size(bcx.ccx(), block_ty));
expr::Ignore expr::Ignore
@ -1965,7 +1980,7 @@ pub fn trans_named_tuple_constructor<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
let tcx = ccx.tcx(); let tcx = ccx.tcx();
let result_ty = match ty::get(ctor_ty).sty { let result_ty = match ty::get(ctor_ty).sty {
ty::ty_bare_fn(ref bft) => bft.sig.output, ty::ty_bare_fn(ref bft) => bft.sig.output.unwrap(),
_ => ccx.sess().bug( _ => ccx.sess().bug(
format!("trans_enum_variant_constructor: \ format!("trans_enum_variant_constructor: \
unexpected ctor return type {}", unexpected ctor return type {}",
@ -2055,9 +2070,9 @@ fn trans_enum_variant_or_tuple_like_struct(ccx: &CrateContext,
let arg_datums = create_datums_for_fn_args(&fcx, arg_tys.as_slice()); let arg_datums = create_datums_for_fn_args(&fcx, arg_tys.as_slice());
if !type_is_zero_size(fcx.ccx, result_ty) { if !type_is_zero_size(fcx.ccx, result_ty.unwrap()) {
let dest = fcx.get_ret_slot(bcx, result_ty, "eret_slot"); let dest = fcx.get_ret_slot(bcx, result_ty, "eret_slot");
let repr = adt::represent_type(ccx, result_ty); let repr = adt::represent_type(ccx, result_ty.unwrap());
for (i, arg_datum) in arg_datums.into_iter().enumerate() { for (i, arg_datum) in arg_datums.into_iter().enumerate() {
let lldestptr = adt::trans_field_ptr(bcx, let lldestptr = adt::trans_field_ptr(bcx,
&*repr, &*repr,
@ -2393,6 +2408,7 @@ pub fn get_fn_llvm_attributes(ccx: &CrateContext, fn_ty: ty::t)
_ => fn_sig.inputs.clone() _ => fn_sig.inputs.clone()
}; };
if let ty::FnConverging(ret_ty) = ret_ty {
// A function pointer is called without the declaration // A function pointer is called without the declaration
// available, so we have to apply any attributes with ABI // available, so we have to apply any attributes with ABI
// implications directly to the call instruction. Right now, // implications directly to the call instruction. Right now,
@ -2442,6 +2458,7 @@ pub fn get_fn_llvm_attributes(ccx: &CrateContext, fn_ty: ty::t)
_ => {} _ => {}
} }
} }
}
for (idx, &t) in input_tys.iter().enumerate().map(|(i, v)| (i + first_arg_offset, v)) { for (idx, &t) in input_tys.iter().enumerate().map(|(i, v)| (i + first_arg_offset, v)) {
match ty::get(t).sty { match ty::get(t).sty {
@ -2523,7 +2540,7 @@ pub fn register_fn_llvmty(ccx: &CrateContext,
llfty: Type) -> ValueRef { llfty: Type) -> ValueRef {
debug!("register_fn_llvmty id={} sym={}", node_id, sym); debug!("register_fn_llvmty id={} sym={}", node_id, sym);
let llfn = decl_fn(ccx, sym.as_slice(), cc, llfty, ty::mk_nil()); let llfn = decl_fn(ccx, sym.as_slice(), cc, llfty, ty::FnConverging(ty::mk_nil()));
finish_register_fn(ccx, sp, sym, node_id, llfn); finish_register_fn(ccx, sp, sym, node_id, llfn);
llfn llfn
} }

View file

@ -375,10 +375,9 @@ pub fn trans_unboxing_shim(bcx: Block,
llshimmedargs.push(get_param(fcx.llfn, fcx.arg_pos(i) as u32)); llshimmedargs.push(get_param(fcx.llfn, fcx.arg_pos(i) as u32));
} }
assert!(!fcx.needs_ret_allocas); assert!(!fcx.needs_ret_allocas);
let dest = match fcx.llretslotptr.get() { let dest = fcx.llretslotptr.get().map(|_|
Some(_) => Some(expr::SaveIn(fcx.get_ret_slot(bcx, return_type, "ret_slot"))), expr::SaveIn(fcx.get_ret_slot(bcx, return_type, "ret_slot"))
None => None );
};
bcx = trans_call_inner(bcx, bcx = trans_call_inner(bcx,
None, None,
function_type, function_type,
@ -757,24 +756,29 @@ pub fn trans_call_inner<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
// Generate a location to store the result. If the user does // Generate a location to store the result. If the user does
// not care about the result, just make a stack slot. // not care about the result, just make a stack slot.
let opt_llretslot = match dest { let opt_llretslot = dest.and_then(|dest| match dest {
None => { expr::SaveIn(dst) => Some(dst),
assert!(!type_of::return_uses_outptr(ccx, ret_ty)); expr::Ignore => {
None let ret_ty = match ret_ty {
} ty::FnConverging(ret_ty) => ret_ty,
Some(expr::SaveIn(dst)) => Some(dst), ty::FnDiverging => ty::mk_nil()
Some(expr::Ignore) if !is_rust_fn || };
if !is_rust_fn ||
type_of::return_uses_outptr(ccx, ret_ty) || type_of::return_uses_outptr(ccx, ret_ty) ||
ty::type_needs_drop(bcx.tcx(), ret_ty) => { ty::type_needs_drop(bcx.tcx(), ret_ty) {
if !type_is_zero_size(ccx, ret_ty) { // Push the out-pointer if we use an out-pointer for this
Some(alloc_ty(bcx, ret_ty, "__llret")) // return type, otherwise push "undef".
} else { if type_is_zero_size(ccx, ret_ty) {
let llty = type_of::type_of(ccx, ret_ty); let llty = type_of::type_of(ccx, ret_ty);
Some(C_undef(llty.ptr_to())) Some(C_undef(llty.ptr_to()))
} else {
Some(alloc_ty(bcx, ret_ty, "__llret"))
}
} else {
None
} }
} }
Some(expr::Ignore) => None });
};
let mut llresult = unsafe { let mut llresult = unsafe {
llvm::LLVMGetUndef(Type::nil(ccx).ptr_to().to_ref()) llvm::LLVMGetUndef(Type::nil(ccx).ptr_to().to_ref())
@ -789,17 +793,15 @@ pub fn trans_call_inner<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
if is_rust_fn { if is_rust_fn {
let mut llargs = Vec::new(); let mut llargs = Vec::new();
// Push the out-pointer if we use an out-pointer for this if let (ty::FnConverging(ret_ty), Some(llretslot)) = (ret_ty, opt_llretslot) {
// return type, otherwise push "undef".
if type_of::return_uses_outptr(ccx, ret_ty) { if type_of::return_uses_outptr(ccx, ret_ty) {
llargs.push(opt_llretslot.unwrap()); llargs.push(llretslot);
}
} }
// Push the environment (or a trait object's self). // Push the environment (or a trait object's self).
match (llenv, llself) { match (llenv, llself) {
(Some(llenv), None) => { (Some(llenv), None) => llargs.push(llenv),
llargs.push(llenv)
},
(None, Some(llself)) => llargs.push(llself), (None, Some(llself)) => llargs.push(llself),
_ => {} _ => {}
} }
@ -827,15 +829,15 @@ pub fn trans_call_inner<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
// If the Rust convention for this type is return via // If the Rust convention for this type is return via
// the return value, copy it into llretslot. // the return value, copy it into llretslot.
match opt_llretslot { match (opt_llretslot, ret_ty) {
Some(llretslot) => { (Some(llretslot), ty::FnConverging(ret_ty)) => {
if !type_of::return_uses_outptr(bcx.ccx(), ret_ty) && if !type_of::return_uses_outptr(bcx.ccx(), ret_ty) &&
!type_is_zero_size(bcx.ccx(), ret_ty) !type_is_zero_size(bcx.ccx(), ret_ty)
{ {
store_ty(bcx, llret, llretslot, ret_ty) store_ty(bcx, llret, llretslot, ret_ty)
} }
} }
None => {} (_, _) => {}
} }
} else { } else {
// Lang items are the only case where dest is None, and // Lang items are the only case where dest is None, and
@ -865,8 +867,8 @@ pub fn trans_call_inner<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
// If the caller doesn't care about the result of this fn call, // If the caller doesn't care about the result of this fn call,
// drop the temporary slot we made. // drop the temporary slot we made.
match (dest, opt_llretslot) { match (dest, opt_llretslot, ret_ty) {
(Some(expr::Ignore), Some(llretslot)) => { (Some(expr::Ignore), Some(llretslot), ty::FnConverging(ret_ty)) => {
// drop the value if it is not being saved. // drop the value if it is not being saved.
bcx = glue::drop_ty(bcx, llretslot, ret_ty, call_info); bcx = glue::drop_ty(bcx, llretslot, ret_ty, call_info);
call_lifetime_end(bcx, llretslot); call_lifetime_end(bcx, llretslot);
@ -874,9 +876,12 @@ pub fn trans_call_inner<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
_ => {} _ => {}
} }
if ty::type_is_bot(ret_ty) { match ret_ty {
ty::FnConverging(_) => {},
ty::FnDiverging => {
Unreachable(bcx); Unreachable(bcx);
} }
}
Result::new(bcx, llresult) Result::new(bcx, llresult)
} }
@ -1118,16 +1123,6 @@ pub fn trans_arg_datum<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
debug!(" arg datum: {}", arg_datum.to_string(bcx.ccx())); debug!(" arg datum: {}", arg_datum.to_string(bcx.ccx()));
let mut val; let mut val;
if ty::type_is_bot(arg_datum_ty) {
// For values of type _|_, we generate an
// "undef" value, as such a value should never
// be inspected. It's important for the value
// to have type lldestty (the callee's expected type).
let llformal_arg_ty = type_of::type_of_explicit_arg(ccx, formal_arg_ty);
unsafe {
val = llvm::LLVMGetUndef(llformal_arg_ty.to_ref());
}
} else {
// FIXME(#3548) use the adjustments table // FIXME(#3548) use the adjustments table
match autoref_arg { match autoref_arg {
DoAutorefArg(arg_id) => { DoAutorefArg(arg_id) => {
@ -1164,7 +1159,6 @@ pub fn trans_arg_datum<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
ty_to_string(bcx.tcx(), formal_arg_ty)); ty_to_string(bcx.tcx(), formal_arg_ty));
val = PointerCast(bcx, val, llformal_arg_ty); val = PointerCast(bcx, val, llformal_arg_ty);
} }
}
debug!("--- trans_arg_datum passing {}", bcx.val_to_string(val)); debug!("--- trans_arg_datum passing {}", bcx.val_to_string(val));
Result::new(bcx, val) Result::new(bcx, val)

View file

@ -623,11 +623,18 @@ pub fn get_wrapper_for_bare_fn(ccx: &CrateContext,
llargs.extend(args.iter().map(|arg| arg.val)); llargs.extend(args.iter().map(|arg| arg.val));
let retval = Call(bcx, fn_ptr, llargs.as_slice(), None); let retval = Call(bcx, fn_ptr, llargs.as_slice(), None);
if type_is_zero_size(ccx, f.sig.output) || fcx.llretslotptr.get().is_some() { match f.sig.output {
ty::FnConverging(output_type) => {
if type_is_zero_size(ccx, output_type) || fcx.llretslotptr.get().is_some() {
RetVoid(bcx); RetVoid(bcx);
} else { } else {
Ret(bcx, retval); Ret(bcx, retval);
} }
}
ty::FnDiverging => {
RetVoid(bcx);
}
}
// HACK(eddyb) finish_fn cannot be used here, we returned directly. // HACK(eddyb) finish_fn cannot be used here, we returned directly.
debuginfo::clear_source_location(&fcx); debuginfo::clear_source_location(&fcx);

View file

@ -74,7 +74,7 @@ pub fn type_is_immediate(ccx: &CrateContext, ty: ty::t) -> bool {
let tcx = ccx.tcx(); let tcx = ccx.tcx();
let simple = ty::type_is_scalar(ty) || let simple = ty::type_is_scalar(ty) ||
ty::type_is_unique(ty) || ty::type_is_region_ptr(ty) || ty::type_is_unique(ty) || ty::type_is_region_ptr(ty) ||
type_is_newtype_immediate(ccx, ty) || ty::type_is_bot(ty) || type_is_newtype_immediate(ccx, ty) ||
ty::type_is_simd(tcx, ty); ty::type_is_simd(tcx, ty);
if simple && !ty::type_is_fat_ptr(tcx, ty) { if simple && !ty::type_is_fat_ptr(tcx, ty) {
return true; return true;
@ -83,7 +83,6 @@ pub fn type_is_immediate(ccx: &CrateContext, ty: ty::t) -> bool {
return false; return false;
} }
match ty::get(ty).sty { match ty::get(ty).sty {
ty::ty_bot => true,
ty::ty_struct(..) | ty::ty_enum(..) | ty::ty_tup(..) | ty::ty_struct(..) | ty::ty_enum(..) | ty::ty_tup(..) |
ty::ty_unboxed_closure(..) => { ty::ty_unboxed_closure(..) => {
let llty = sizing_type_of(ccx, ty); let llty = sizing_type_of(ccx, ty);
@ -113,7 +112,7 @@ pub fn return_type_is_void(ccx: &CrateContext, ty: ty::t) -> bool {
* return type (in order to aid with C ABI compatibility). * return type (in order to aid with C ABI compatibility).
*/ */
ty::type_is_nil(ty) || ty::type_is_bot(ty) || ty::type_is_empty(ccx.tcx(), ty) ty::type_is_nil(ty) || ty::type_is_empty(ccx.tcx(), ty)
} }
/// Generates a unique symbol based off the name given. This is used to create /// Generates a unique symbol based off the name given. This is used to create
@ -217,7 +216,7 @@ pub trait SubstP {
-> Self; -> Self;
} }
impl<T:Subst+Clone> SubstP for T { impl<T: Subst + Clone> SubstP for T {
fn substp(&self, tcx: &ty::ctxt, substs: &param_substs) -> T { fn substp(&self, tcx: &ty::ctxt, substs: &param_substs) -> T {
self.subst(tcx, &substs.substs) self.subst(tcx, &substs.substs)
} }
@ -343,9 +342,12 @@ impl<'a, 'tcx> FunctionContext<'a, 'tcx> {
self.llreturn.get().unwrap() self.llreturn.get().unwrap()
} }
pub fn get_ret_slot(&self, bcx: Block, ty: ty::t, name: &str) -> ValueRef { pub fn get_ret_slot(&self, bcx: Block, output: ty::FnOutput, name: &str) -> ValueRef {
if self.needs_ret_allocas { if self.needs_ret_allocas {
base::alloca_no_lifetime(bcx, type_of::type_of(bcx.ccx(), ty), name) base::alloca_no_lifetime(bcx, match output {
ty::FnConverging(output_type) => type_of::type_of(bcx.ccx(), output_type),
ty::FnDiverging => Type::void(bcx.ccx())
}, name)
} else { } else {
self.llretslotptr.get().unwrap() self.llretslotptr.get().unwrap()
} }

View file

@ -296,7 +296,7 @@ pub fn trans_for<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
.borrow())[method_call] .borrow())[method_call]
.ty; .ty;
let method_type = monomorphize_type(loopback_bcx_in, method_type); let method_type = monomorphize_type(loopback_bcx_in, method_type);
let method_result_type = ty::ty_fn_ret(method_type); let method_result_type = ty::ty_fn_ret(method_type).unwrap();
let option_cleanup_scope = body_bcx_in.fcx.push_custom_cleanup_scope(); let option_cleanup_scope = body_bcx_in.fcx.push_custom_cleanup_scope();
let option_cleanup_scope_id = cleanup::CustomScope(option_cleanup_scope); let option_cleanup_scope_id = cleanup::CustomScope(option_cleanup_scope);
@ -402,10 +402,6 @@ pub fn trans_loop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
fcx.pop_loop_cleanup_scope(loop_id); fcx.pop_loop_cleanup_scope(loop_id);
if ty::type_is_bot(node_id_type(bcx, loop_id)) {
Unreachable(next_bcx_in);
}
return next_bcx_in; return next_bcx_in;
} }
@ -465,7 +461,7 @@ pub fn trans_ret<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
let dest = match (fcx.llretslotptr.get(), e) { let dest = match (fcx.llretslotptr.get(), e) {
(Some(_), Some(e)) => { (Some(_), Some(e)) => {
let ret_ty = expr_ty(bcx, &*e); let ret_ty = expr_ty(bcx, &*e);
expr::SaveIn(fcx.get_ret_slot(bcx, ret_ty, "ret_slot")) expr::SaveIn(fcx.get_ret_slot(bcx, ty::FnConverging(ret_ty), "ret_slot"))
} }
_ => expr::Ignore, _ => expr::Ignore,
}; };

View file

@ -650,7 +650,7 @@ impl<K: KindOps + fmt::Show> Datum<K> {
} }
pub fn to_llbool(self, bcx: Block) -> ValueRef { pub fn to_llbool(self, bcx: Block) -> ValueRef {
assert!(ty::type_is_bool(self.ty) || ty::type_is_bot(self.ty)) assert!(ty::type_is_bool(self.ty))
self.to_llscalarish(bcx) self.to_llscalarish(bcx)
} }
} }

View file

@ -352,7 +352,6 @@ impl TypeMap {
match ty::get(type_).sty { match ty::get(type_).sty {
ty::ty_nil | ty::ty_nil |
ty::ty_bot |
ty::ty_bool | ty::ty_bool |
ty::ty_char | ty::ty_char |
ty::ty_str | ty::ty_str |
@ -451,9 +450,16 @@ impl TypeMap {
} }
unique_type_id.push_str(")->"); unique_type_id.push_str(")->");
let return_type_id = self.get_unique_type_id_of_type(cx, sig.output); match sig.output {
ty::FnConverging(ret_ty) => {
let return_type_id = self.get_unique_type_id_of_type(cx, ret_ty);
let return_type_id = self.get_unique_type_id_as_string(return_type_id); let return_type_id = self.get_unique_type_id_as_string(return_type_id);
unique_type_id.push_str(return_type_id.as_slice()); unique_type_id.push_str(return_type_id.as_slice());
}
ty::FnDiverging => {
unique_type_id.push_str("!");
}
}
}, },
ty::ty_closure(box ref closure_ty) => { ty::ty_closure(box ref closure_ty) => {
self.get_unique_type_id_of_closure_type(cx, self.get_unique_type_id_of_closure_type(cx,
@ -578,9 +584,16 @@ impl TypeMap {
unique_type_id.push_str("|->"); unique_type_id.push_str("|->");
let return_type_id = self.get_unique_type_id_of_type(cx, sig.output); match sig.output {
ty::FnConverging(ret_ty) => {
let return_type_id = self.get_unique_type_id_of_type(cx, ret_ty);
let return_type_id = self.get_unique_type_id_as_string(return_type_id); let return_type_id = self.get_unique_type_id_as_string(return_type_id);
unique_type_id.push_str(return_type_id.as_slice()); unique_type_id.push_str(return_type_id.as_slice());
}
ty::FnDiverging => {
unique_type_id.push_str("!");
}
}
unique_type_id.push(':'); unique_type_id.push(':');
@ -1707,13 +1720,25 @@ fn scope_metadata(fcx: &FunctionContext,
} }
} }
fn diverging_type_metadata(cx: &CrateContext) -> DIType {
"!".with_c_str(|name| {
unsafe {
llvm::LLVMDIBuilderCreateBasicType(
DIB(cx),
name,
bytes_to_bits(0),
bytes_to_bits(0),
DW_ATE_unsigned)
}
})
}
fn basic_type_metadata(cx: &CrateContext, t: ty::t) -> DIType { fn basic_type_metadata(cx: &CrateContext, t: ty::t) -> DIType {
debug!("basic_type_metadata: {}", ty::get(t)); debug!("basic_type_metadata: {}", ty::get(t));
let (name, encoding) = match ty::get(t).sty { let (name, encoding) = match ty::get(t).sty {
ty::ty_nil => ("()".to_string(), DW_ATE_unsigned), ty::ty_nil => ("()".to_string(), DW_ATE_unsigned),
ty::ty_bot => ("!".to_string(), DW_ATE_unsigned),
ty::ty_bool => ("bool".to_string(), DW_ATE_boolean), ty::ty_bool => ("bool".to_string(), DW_ATE_boolean),
ty::ty_char => ("char".to_string(), DW_ATE_unsigned_char), ty::ty_char => ("char".to_string(), DW_ATE_unsigned_char),
ty::ty_int(int_ty) => match int_ty { ty::ty_int(int_ty) => match int_ty {
@ -2748,9 +2773,12 @@ fn subroutine_type_metadata(cx: &CrateContext,
let mut signature_metadata: Vec<DIType> = Vec::with_capacity(signature.inputs.len() + 1); let mut signature_metadata: Vec<DIType> = Vec::with_capacity(signature.inputs.len() + 1);
// return type // return type
signature_metadata.push(match ty::get(signature.output).sty { signature_metadata.push(match signature.output {
ty::FnConverging(ret_ty) => match ty::get(ret_ty).sty {
ty::ty_nil => ptr::null_mut(), ty::ty_nil => ptr::null_mut(),
_ => type_metadata(cx, signature.output, span) _ => type_metadata(cx, ret_ty, span)
},
ty::FnDiverging => diverging_type_metadata(cx)
}); });
// regular arguments // regular arguments
@ -2855,7 +2883,6 @@ fn type_metadata(cx: &CrateContext,
let sty = &ty::get(t).sty; let sty = &ty::get(t).sty;
let MetadataCreationResult { metadata, already_stored_in_typemap } = match *sty { let MetadataCreationResult { metadata, already_stored_in_typemap } = match *sty {
ty::ty_nil | ty::ty_nil |
ty::ty_bot |
ty::ty_bool | ty::ty_bool |
ty::ty_char | ty::ty_char |
ty::ty_int(_) | ty::ty_int(_) |
@ -3647,7 +3674,6 @@ fn push_debuginfo_type_name(cx: &CrateContext,
output:&mut String) { output:&mut String) {
match ty::get(t).sty { match ty::get(t).sty {
ty::ty_nil => output.push_str("()"), ty::ty_nil => output.push_str("()"),
ty::ty_bot => output.push_str("!"),
ty::ty_bool => output.push_str("bool"), ty::ty_bool => output.push_str("bool"),
ty::ty_char => output.push_str("char"), ty::ty_char => output.push_str("char"),
ty::ty_str => output.push_str("str"), ty::ty_str => output.push_str("str"),
@ -3749,9 +3775,15 @@ fn push_debuginfo_type_name(cx: &CrateContext,
output.push(')'); output.push(')');
if !ty::type_is_nil(sig.output) { match sig.output {
ty::FnConverging(result_type) if ty::type_is_nil(result_type) => {}
ty::FnConverging(result_type) => {
output.push_str(" -> "); output.push_str(" -> ");
push_debuginfo_type_name(cx, sig.output, true, output); push_debuginfo_type_name(cx, result_type, true, output);
}
ty::FnDiverging => {
output.push_str(" -> !");
}
} }
}, },
ty::ty_closure(box ty::ClosureTy { fn_style, ty::ty_closure(box ty::ClosureTy { fn_style,
@ -3803,9 +3835,15 @@ fn push_debuginfo_type_name(cx: &CrateContext,
output.push(param_list_closing_char); output.push(param_list_closing_char);
if !ty::type_is_nil(sig.output) { match sig.output {
ty::FnConverging(result_type) if ty::type_is_nil(result_type) => {}
ty::FnConverging(result_type) => {
output.push_str(" -> "); output.push_str(" -> ");
push_debuginfo_type_name(cx, sig.output, true, output); push_debuginfo_type_name(cx, result_type, true, output);
}
ty::FnDiverging => {
output.push_str(" -> !");
}
} }
}, },
ty::ty_unboxed_closure(..) => { ty::ty_unboxed_closure(..) => {

View file

@ -609,7 +609,7 @@ fn trans_datum_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
start.as_ref().map(|e| args.push((unpack_datum!(bcx, trans(bcx, &**e)), e.id))); start.as_ref().map(|e| args.push((unpack_datum!(bcx, trans(bcx, &**e)), e.id)));
end.as_ref().map(|e| args.push((unpack_datum!(bcx, trans(bcx, &**e)), e.id))); end.as_ref().map(|e| args.push((unpack_datum!(bcx, trans(bcx, &**e)), e.id)));
let result_ty = ty::ty_fn_ret(monomorphize_type(bcx, method_ty.unwrap())); let result_ty = ty::ty_fn_ret(monomorphize_type(bcx, method_ty.unwrap())).unwrap();
let scratch = rvalue_scratch_datum(bcx, result_ty, "trans_slice"); let scratch = rvalue_scratch_datum(bcx, result_ty, "trans_slice");
unpack_result!(bcx, unpack_result!(bcx,
@ -757,7 +757,7 @@ fn trans_index<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
base_datum, base_datum,
vec![(ix_datum, idx.id)], vec![(ix_datum, idx.id)],
None)); None));
let ref_ty = ty::ty_fn_ret(monomorphize_type(bcx, method_ty)); let ref_ty = ty::ty_fn_ret(monomorphize_type(bcx, method_ty)).unwrap();
let elt_ty = match ty::deref(ref_ty, true) { let elt_ty = match ty::deref(ref_ty, true) {
None => { None => {
bcx.tcx().sess.span_bug(index_expr.span, bcx.tcx().sess.span_bug(index_expr.span,
@ -1614,8 +1614,7 @@ fn trans_eager_binop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
let tcx = bcx.tcx(); let tcx = bcx.tcx();
let is_simd = ty::type_is_simd(tcx, lhs_t); let is_simd = ty::type_is_simd(tcx, lhs_t);
let intype = { let intype = {
if ty::type_is_bot(lhs_t) { rhs_t } if is_simd { ty::simd_type(tcx, lhs_t) }
else if is_simd { ty::simd_type(tcx, lhs_t) }
else { lhs_t } else { lhs_t }
}; };
let is_float = ty::type_is_fp(intype); let is_float = ty::type_is_fp(intype);
@ -1675,9 +1674,7 @@ fn trans_eager_binop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
} else { LShr(bcx, lhs, rhs) } } else { LShr(bcx, lhs, rhs) }
} }
ast::BiEq | ast::BiNe | ast::BiLt | ast::BiGe | ast::BiLe | ast::BiGt => { ast::BiEq | ast::BiNe | ast::BiLt | ast::BiGe | ast::BiLe | ast::BiGt => {
if ty::type_is_bot(rhs_t) { if ty::type_is_scalar(rhs_t) {
C_bool(bcx.ccx(), false)
} else if ty::type_is_scalar(rhs_t) {
unpack_result!(bcx, base::compare_scalar_types(bcx, lhs, rhs, rhs_t, op)) unpack_result!(bcx, base::compare_scalar_types(bcx, lhs, rhs, rhs_t, op))
} else if is_simd { } else if is_simd {
base::compare_simd_types(bcx, lhs, rhs, intype, ty::simd_size(tcx, lhs_t), op) base::compare_simd_types(bcx, lhs, rhs, intype, ty::simd_size(tcx, lhs_t), op)
@ -2098,7 +2095,7 @@ fn deref_once<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
_ => datum _ => datum
}; };
let ref_ty = ty::ty_fn_ret(monomorphize_type(bcx, method_ty)); let ref_ty = ty::ty_fn_ret(monomorphize_type(bcx, method_ty)).unwrap();
let scratch = rvalue_scratch_datum(bcx, ref_ty, "overloaded_deref"); let scratch = rvalue_scratch_datum(bcx, ref_ty, "overloaded_deref");
unpack_result!(bcx, trans_overloaded_op(bcx, expr, method_call, unpack_result!(bcx, trans_overloaded_op(bcx, expr, method_call,

View file

@ -49,9 +49,6 @@ struct ForeignTypes {
/// LLVM types that will appear on the foreign function /// LLVM types that will appear on the foreign function
llsig: LlvmSignature, llsig: LlvmSignature,
/// True if there is a return value (not bottom, not unit)
ret_def: bool,
} }
struct LlvmSignature { struct LlvmSignature {
@ -63,6 +60,9 @@ struct LlvmSignature {
// function, because the foreign function may opt to return via an // function, because the foreign function may opt to return via an
// out pointer. // out pointer.
llret_ty: Type, llret_ty: Type,
/// True if there is a return value (not bottom, not unit)
ret_def: bool,
} }
@ -286,11 +286,10 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
_ => ccx.sess().bug("trans_native_call called on non-function type") _ => ccx.sess().bug("trans_native_call called on non-function type")
}; };
let llsig = foreign_signature(ccx, &fn_sig, passed_arg_tys.as_slice()); let llsig = foreign_signature(ccx, &fn_sig, passed_arg_tys.as_slice());
let ret_def = !return_type_is_void(bcx.ccx(), fn_sig.output);
let fn_type = cabi::compute_abi_info(ccx, let fn_type = cabi::compute_abi_info(ccx,
llsig.llarg_tys.as_slice(), llsig.llarg_tys.as_slice(),
llsig.llret_ty, llsig.llret_ty,
ret_def); llsig.ret_def);
let arg_tys: &[cabi::ArgType] = fn_type.arg_tys.as_slice(); let arg_tys: &[cabi::ArgType] = fn_type.arg_tys.as_slice();
@ -437,7 +436,7 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
// type to match because some ABIs will use a different type than // type to match because some ABIs will use a different type than
// the Rust type. e.g., a {u32,u32} struct could be returned as // the Rust type. e.g., a {u32,u32} struct could be returned as
// u64. // u64.
if ret_def && !fn_type.ret_ty.is_indirect() { if llsig.ret_def && !fn_type.ret_ty.is_indirect() {
let llrust_ret_ty = llsig.llret_ty; let llrust_ret_ty = llsig.llret_ty;
let llforeign_ret_ty = match fn_type.ret_ty.cast { let llforeign_ret_ty = match fn_type.ret_ty.cast {
Some(ty) => ty, Some(ty) => ty,
@ -450,7 +449,12 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
debug!("llforeign_ret_ty={}", ccx.tn().type_to_string(llforeign_ret_ty)); debug!("llforeign_ret_ty={}", ccx.tn().type_to_string(llforeign_ret_ty));
if llrust_ret_ty == llforeign_ret_ty { if llrust_ret_ty == llforeign_ret_ty {
base::store_ty(bcx, llforeign_retval, llretptr, fn_sig.output) match fn_sig.output {
ty::FnConverging(result_ty) => {
base::store_ty(bcx, llforeign_retval, llretptr, result_ty)
}
ty::FnDiverging => {}
}
} else { } else {
// The actual return type is a struct, but the ABI // The actual return type is a struct, but the ABI
// adaptation code has cast it into some scalar type. The // adaptation code has cast it into some scalar type. The
@ -549,7 +553,7 @@ pub fn decl_rust_fn_with_foreign_abi(ccx: &CrateContext,
} }
_ => fail!("expected bare fn in decl_rust_fn_with_foreign_abi") _ => fail!("expected bare fn in decl_rust_fn_with_foreign_abi")
}; };
let llfn = base::decl_fn(ccx, name, cconv, llfn_ty, ty::mk_nil()); let llfn = base::decl_fn(ccx, name, cconv, llfn_ty, ty::FnConverging(ty::mk_nil()));
add_argument_attributes(&tys, llfn); add_argument_attributes(&tys, llfn);
debug!("decl_rust_fn_with_foreign_abi(llfn_ty={}, llfn={})", debug!("decl_rust_fn_with_foreign_abi(llfn_ty={}, llfn={})",
ccx.tn().type_to_string(llfn_ty), ccx.tn().val_to_string(llfn)); ccx.tn().type_to_string(llfn_ty), ccx.tn().val_to_string(llfn));
@ -698,8 +702,10 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
}; };
// Push Rust return pointer, using null if it will be unused. // Push Rust return pointer, using null if it will be unused.
let rust_uses_outptr = let rust_uses_outptr = match tys.fn_sig.output {
type_of::return_uses_outptr(ccx, tys.fn_sig.output); ty::FnConverging(ret_ty) => type_of::return_uses_outptr(ccx, ret_ty),
ty::FnDiverging => false
};
let return_alloca: Option<ValueRef>; let return_alloca: Option<ValueRef>;
let llrust_ret_ty = tys.llsig.llret_ty; let llrust_ret_ty = tys.llsig.llret_ty;
let llrust_retptr_ty = llrust_ret_ty.ptr_to(); let llrust_retptr_ty = llrust_ret_ty.ptr_to();
@ -714,7 +720,7 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
debug!("out pointer, foreign={}", debug!("out pointer, foreign={}",
ccx.tn().val_to_string(llforeign_outptr)); ccx.tn().val_to_string(llforeign_outptr));
let llrust_retptr = let llrust_retptr =
builder.bitcast(llforeign_outptr, llrust_ret_ty.ptr_to()); builder.bitcast(llforeign_outptr, llrust_retptr_ty);
debug!("out pointer, foreign={} (casted)", debug!("out pointer, foreign={} (casted)",
ccx.tn().val_to_string(llrust_retptr)); ccx.tn().val_to_string(llrust_retptr));
llrust_args.push(llrust_retptr); llrust_args.push(llrust_retptr);
@ -817,7 +823,7 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
None => tys.fn_ty.ret_ty.ty None => tys.fn_ty.ret_ty.ty
}; };
match foreign_outptr { match foreign_outptr {
None if !tys.ret_def => { None if !tys.llsig.ret_def => {
// Function returns `()` or `bot`, which in Rust is the LLVM // Function returns `()` or `bot`, which in Rust is the LLVM
// type "{}" but in foreign ABIs is "Void". // type "{}" but in foreign ABIs is "Void".
builder.ret_void(); builder.ret_void();
@ -896,10 +902,16 @@ fn foreign_signature(ccx: &CrateContext, fn_sig: &ty::FnSig, arg_tys: &[ty::t])
*/ */
let llarg_tys = arg_tys.iter().map(|&arg| arg_type_of(ccx, arg)).collect(); let llarg_tys = arg_tys.iter().map(|&arg| arg_type_of(ccx, arg)).collect();
let llret_ty = type_of::arg_type_of(ccx, fn_sig.output); let (llret_ty, ret_def) = match fn_sig.output {
ty::FnConverging(ret_ty) =>
(type_of::arg_type_of(ccx, ret_ty), !return_type_is_void(ccx, ret_ty)),
ty::FnDiverging =>
(Type::nil(ccx), false)
};
LlvmSignature { LlvmSignature {
llarg_tys: llarg_tys, llarg_tys: llarg_tys,
llret_ty: llret_ty llret_ty: llret_ty,
ret_def: ret_def
} }
} }
@ -915,11 +927,10 @@ fn foreign_types_for_fn_ty(ccx: &CrateContext,
_ => ccx.sess().bug("foreign_types_for_fn_ty called on non-function type") _ => ccx.sess().bug("foreign_types_for_fn_ty called on non-function type")
}; };
let llsig = foreign_signature(ccx, &fn_sig, fn_sig.inputs.as_slice()); let llsig = foreign_signature(ccx, &fn_sig, fn_sig.inputs.as_slice());
let ret_def = !return_type_is_void(ccx, fn_sig.output);
let fn_ty = cabi::compute_abi_info(ccx, let fn_ty = cabi::compute_abi_info(ccx,
llsig.llarg_tys.as_slice(), llsig.llarg_tys.as_slice(),
llsig.llret_ty, llsig.llret_ty,
ret_def); llsig.ret_def);
debug!("foreign_types_for_fn_ty(\ debug!("foreign_types_for_fn_ty(\
ty={}, \ ty={}, \
llsig={} -> {}, \ llsig={} -> {}, \
@ -930,12 +941,11 @@ fn foreign_types_for_fn_ty(ccx: &CrateContext,
ccx.tn().type_to_string(llsig.llret_ty), ccx.tn().type_to_string(llsig.llret_ty),
ccx.tn().types_to_str(fn_ty.arg_tys.iter().map(|t| t.ty).collect::<Vec<_>>().as_slice()), ccx.tn().types_to_str(fn_ty.arg_tys.iter().map(|t| t.ty).collect::<Vec<_>>().as_slice()),
ccx.tn().type_to_string(fn_ty.ret_ty.ty), ccx.tn().type_to_string(fn_ty.ret_ty.ty),
ret_def); llsig.ret_def);
ForeignTypes { ForeignTypes {
fn_sig: fn_sig, fn_sig: fn_sig,
llsig: llsig, llsig: llsig,
ret_def: ret_def,
fn_ty: fn_ty fn_ty: fn_ty
} }
} }

View file

@ -538,10 +538,10 @@ fn make_generic_glue(ccx: &CrateContext,
let arena = TypedArena::new(); let arena = TypedArena::new();
let empty_param_substs = param_substs::empty(); let empty_param_substs = param_substs::empty();
let fcx = new_fn_ctxt(ccx, llfn, ast::DUMMY_NODE_ID, false, ty::mk_nil(), let fcx = new_fn_ctxt(ccx, llfn, ast::DUMMY_NODE_ID, false, ty::FnConverging(ty::mk_nil()),
&empty_param_substs, None, &arena); &empty_param_substs, None, &arena);
let bcx = init_function(&fcx, false, ty::mk_nil()); let bcx = init_function(&fcx, false, ty::FnConverging(ty::mk_nil()));
update_linkage(ccx, llfn, None, OriginalTranslation); update_linkage(ccx, llfn, None, OriginalTranslation);
@ -556,7 +556,7 @@ fn make_generic_glue(ccx: &CrateContext,
let llrawptr0 = get_param(llfn, fcx.arg_pos(0) as c_uint); let llrawptr0 = get_param(llfn, fcx.arg_pos(0) as c_uint);
let bcx = helper(bcx, llrawptr0, t); let bcx = helper(bcx, llrawptr0, t);
finish_fn(&fcx, bcx, ty::mk_nil()); finish_fn(&fcx, bcx, ty::FnConverging(ty::mk_nil()));
llfn llfn
} }

View file

@ -149,12 +149,12 @@ pub fn trans_intrinsic_call<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, node: ast::N
ty::ty_bare_fn(ref f) => f.sig.output, ty::ty_bare_fn(ref f) => f.sig.output,
_ => fail!("expected bare_fn in trans_intrinsic_call") _ => fail!("expected bare_fn in trans_intrinsic_call")
}; };
let llret_ty = type_of::type_of(ccx, ret_ty);
let foreign_item = tcx.map.expect_foreign_item(node); let foreign_item = tcx.map.expect_foreign_item(node);
let name = token::get_ident(foreign_item.ident); let name = token::get_ident(foreign_item.ident);
// For `transmute` we can just trans the input expr directly into dest // For `transmute` we can just trans the input expr directly into dest
if name.get() == "transmute" { if name.get() == "transmute" {
let llret_ty = type_of::type_of(ccx, ret_ty.unwrap());
match args { match args {
callee::ArgExprs(arg_exprs) => { callee::ArgExprs(arg_exprs) => {
assert_eq!(arg_exprs.len(), 1); assert_eq!(arg_exprs.len(), 1);
@ -192,6 +192,36 @@ pub fn trans_intrinsic_call<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, node: ast::N
} }
} }
// Push the arguments.
let mut llargs = Vec::new();
bcx = callee::trans_args(bcx,
args,
callee_ty,
&mut llargs,
cleanup::CustomScope(cleanup_scope),
false,
RustIntrinsic);
fcx.pop_custom_cleanup_scope(cleanup_scope);
// The only intrinsic function that diverges.
if name.get() == "abort" {
let llfn = ccx.get_intrinsic(&("llvm.trap"));
Call(bcx, llfn, [], None);
Unreachable(bcx);
return Result::new(bcx, C_undef(Type::nil(ccx).ptr_to()));
} else if name.get() == "unreachable" {
Unreachable(bcx);
return Result::new(bcx, C_nil(ccx));
}
let ret_ty = match ret_ty {
ty::FnConverging(ret_ty) => ret_ty,
ty::FnDiverging => unreachable!()
};
let llret_ty = type_of::type_of(ccx, ret_ty);
// Get location to store the result. If the user does // Get location to store the result. If the user does
// not care about the result, just make a stack slot // not care about the result, just make a stack slot
let llresult = match dest { let llresult = match dest {
@ -205,34 +235,11 @@ pub fn trans_intrinsic_call<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, node: ast::N
} }
}; };
// Push the arguments.
let mut llargs = Vec::new();
bcx = callee::trans_args(bcx,
args,
callee_ty,
&mut llargs,
cleanup::CustomScope(cleanup_scope),
false,
RustIntrinsic);
fcx.pop_custom_cleanup_scope(cleanup_scope);
let simple = get_simple_intrinsic(ccx, &*foreign_item); let simple = get_simple_intrinsic(ccx, &*foreign_item);
let llval = match (simple, name.get()) { let llval = match (simple, name.get()) {
(Some(llfn), _) => { (Some(llfn), _) => {
Call(bcx, llfn, llargs.as_slice(), None) Call(bcx, llfn, llargs.as_slice(), None)
} }
(_, "abort") => {
let llfn = ccx.get_intrinsic(&("llvm.trap"));
let v = Call(bcx, llfn, [], None);
Unreachable(bcx);
v
}
(_, "unreachable") => {
Unreachable(bcx);
C_nil(ccx)
}
(_, "breakpoint") => { (_, "breakpoint") => {
let llfn = ccx.get_intrinsic(&("llvm.debugtrap")); let llfn = ccx.get_intrinsic(&("llvm.debugtrap"));
Call(bcx, llfn, [], None) Call(bcx, llfn, [], None)

View file

@ -97,7 +97,7 @@ pub fn untuple_arguments_if_necessary(ccx: &CrateContext,
pub fn type_of_rust_fn(cx: &CrateContext, pub fn type_of_rust_fn(cx: &CrateContext,
llenvironment_type: Option<Type>, llenvironment_type: Option<Type>,
inputs: &[ty::t], inputs: &[ty::t],
output: ty::t, output: ty::FnOutput,
abi: abi::Abi) abi: abi::Abi)
-> Type { -> Type {
let mut atys: Vec<Type> = Vec::new(); let mut atys: Vec<Type> = Vec::new();
@ -107,11 +107,22 @@ pub fn type_of_rust_fn(cx: &CrateContext,
// Arg 0: Output pointer. // Arg 0: Output pointer.
// (if the output type is non-immediate) // (if the output type is non-immediate)
let lloutputtype = match output {
ty::FnConverging(output) => {
let use_out_pointer = return_uses_outptr(cx, output); let use_out_pointer = return_uses_outptr(cx, output);
let lloutputtype = arg_type_of(cx, output); let lloutputtype = arg_type_of(cx, output);
// Use the output as the actual return value if it's immediate.
if use_out_pointer { if use_out_pointer {
atys.push(lloutputtype.ptr_to()); atys.push(lloutputtype.ptr_to());
Type::void(cx)
} else if return_type_is_void(cx, output) {
Type::void(cx)
} else {
lloutputtype
} }
}
ty::FnDiverging => Type::void(cx)
};
// Arg 1: Environment // Arg 1: Environment
match llenvironment_type { match llenvironment_type {
@ -123,12 +134,7 @@ pub fn type_of_rust_fn(cx: &CrateContext,
let input_tys = inputs.iter().map(|&arg_ty| type_of_explicit_arg(cx, arg_ty)); let input_tys = inputs.iter().map(|&arg_ty| type_of_explicit_arg(cx, arg_ty));
atys.extend(input_tys); atys.extend(input_tys);
// Use the output as the actual return value if it's immediate.
if use_out_pointer || return_type_is_void(cx, output) {
Type::func(atys.as_slice(), &Type::void(cx))
} else {
Type::func(atys.as_slice(), &lloutputtype) Type::func(atys.as_slice(), &lloutputtype)
}
} }
// Given a function type and a count of ty params, construct an llvm type // Given a function type and a count of ty params, construct an llvm type
@ -181,7 +187,7 @@ pub fn sizing_type_of(cx: &CrateContext, t: ty::t) -> Type {
ppaux::ty_to_string(cx.tcx(), t)).as_slice()) ppaux::ty_to_string(cx.tcx(), t)).as_slice())
} }
ty::ty_nil | ty::ty_bot => Type::nil(cx), ty::ty_nil => Type::nil(cx),
ty::ty_bool => Type::bool(cx), ty::ty_bool => Type::bool(cx),
ty::ty_char => Type::char(cx), ty::ty_char => Type::char(cx),
ty::ty_int(t) => Type::int_from_ty(cx, t), ty::ty_int(t) => Type::int_from_ty(cx, t),
@ -293,7 +299,7 @@ pub fn type_of(cx: &CrateContext, t: ty::t) -> Type {
} }
let mut llty = match ty::get(t).sty { let mut llty = match ty::get(t).sty {
ty::ty_nil | ty::ty_bot => Type::nil(cx), ty::ty_nil => Type::nil(cx),
ty::ty_bool => Type::bool(cx), ty::ty_bool => Type::bool(cx),
ty::ty_char => Type::char(cx), ty::ty_char => Type::char(cx),
ty::ty_int(t) => Type::int_from_ty(cx, t), ty::ty_int(t) => Type::int_from_ty(cx, t),

View file

@ -598,7 +598,6 @@ bitflags! {
const HAS_RE_INFER = 0b1000, const HAS_RE_INFER = 0b1000,
const HAS_REGIONS = 0b10000, const HAS_REGIONS = 0b10000,
const HAS_TY_ERR = 0b100000, const HAS_TY_ERR = 0b100000,
const HAS_TY_BOT = 0b1000000,
const NEEDS_SUBST = HAS_PARAMS.bits | HAS_SELF.bits | HAS_REGIONS.bits, const NEEDS_SUBST = HAS_PARAMS.bits | HAS_SELF.bits | HAS_REGIONS.bits,
} }
} }
@ -672,6 +671,21 @@ pub struct ClosureTy {
pub abi: abi::Abi, pub abi: abi::Abi,
} }
#[deriving(Clone, PartialEq, Eq, Hash)]
pub enum FnOutput {
FnConverging(ty::t),
FnDiverging
}
impl FnOutput {
pub fn unwrap(&self) -> ty::t {
match *self {
ty::FnConverging(ref t) => *t,
ty::FnDiverging => unreachable!()
}
}
}
/** /**
* Signature of a function type, which I have arbitrarily * Signature of a function type, which I have arbitrarily
* decided to use to refer to the input/output types. * decided to use to refer to the input/output types.
@ -688,7 +702,7 @@ pub struct ClosureTy {
pub struct FnSig { pub struct FnSig {
pub binder_id: ast::NodeId, pub binder_id: ast::NodeId,
pub inputs: Vec<t>, pub inputs: Vec<t>,
pub output: t, pub output: FnOutput,
pub variadic: bool pub variadic: bool
} }
@ -919,12 +933,6 @@ mod primitives {
def_prim_ty!(TY_F32, super::ty_float(ast::TyF32), 14) def_prim_ty!(TY_F32, super::ty_float(ast::TyF32), 14)
def_prim_ty!(TY_F64, super::ty_float(ast::TyF64), 15) def_prim_ty!(TY_F64, super::ty_float(ast::TyF64), 15)
pub static TY_BOT: t_box_ = t_box_ {
sty: super::ty_bot,
id: 16,
flags: super::HAS_TY_BOT,
};
pub static TY_ERR: t_box_ = t_box_ { pub static TY_ERR: t_box_ = t_box_ {
sty: super::ty_err, sty: super::ty_err,
id: 17, id: 17,
@ -939,7 +947,6 @@ mod primitives {
#[deriving(Clone, PartialEq, Eq, Hash, Show)] #[deriving(Clone, PartialEq, Eq, Hash, Show)]
pub enum sty { pub enum sty {
ty_nil, ty_nil,
ty_bot,
ty_bool, ty_bool,
ty_char, ty_char,
ty_int(ast::IntTy), ty_int(ast::IntTy),
@ -1044,6 +1051,7 @@ pub enum type_err {
terr_builtin_bounds(expected_found<BuiltinBounds>), terr_builtin_bounds(expected_found<BuiltinBounds>),
terr_variadic_mismatch(expected_found<bool>), terr_variadic_mismatch(expected_found<bool>),
terr_cyclic_ty, terr_cyclic_ty,
terr_convergence_mismatch(expected_found<bool>)
} }
/// Bounds suitable for a named type parameter like `A` in `fn foo<A>` /// Bounds suitable for a named type parameter like `A` in `fn foo<A>`
@ -1578,7 +1586,6 @@ pub fn mk_t(cx: &ctxt, st: sty) -> t {
ty_uint(u) => return mk_mach_uint(u), ty_uint(u) => return mk_mach_uint(u),
ty_float(f) => return mk_mach_float(f), ty_float(f) => return mk_mach_float(f),
ty_char => return mk_char(), ty_char => return mk_char(),
ty_bot => return mk_bot(),
_ => {} _ => {}
}; };
@ -1627,7 +1634,6 @@ pub fn mk_t(cx: &ctxt, st: sty) -> t {
// But doing so caused sporadic memory corruption, and // But doing so caused sporadic memory corruption, and
// neither I (tjc) nor nmatsakis could figure out why, // neither I (tjc) nor nmatsakis could figure out why,
// so we're doing it this way. // so we're doing it this way.
&ty_bot => flags = flags | HAS_TY_BOT,
&ty_err => flags = flags | HAS_TY_ERR, &ty_err => flags = flags | HAS_TY_ERR,
&ty_param(ref p) => { &ty_param(ref p) => {
if p.space == subst::SelfSpace { if p.space == subst::SelfSpace {
@ -1661,9 +1667,9 @@ pub fn mk_t(cx: &ctxt, st: sty) -> t {
&ty_tup(ref ts) => for tt in ts.iter() { flags = flags | get(*tt).flags; }, &ty_tup(ref ts) => for tt in ts.iter() { flags = flags | get(*tt).flags; },
&ty_bare_fn(ref f) => { &ty_bare_fn(ref f) => {
for a in f.sig.inputs.iter() { flags = flags | get(*a).flags; } for a in f.sig.inputs.iter() { flags = flags | get(*a).flags; }
flags = flags | get(f.sig.output).flags; if let ty::FnConverging(output) = f.sig.output {
// T -> _|_ is *not* _|_ ! flags = flags | get(output).flags;
flags = flags - HAS_TY_BOT; }
} }
&ty_closure(ref f) => { &ty_closure(ref f) => {
match f.store { match f.store {
@ -1673,9 +1679,9 @@ pub fn mk_t(cx: &ctxt, st: sty) -> t {
_ => {} _ => {}
} }
for a in f.sig.inputs.iter() { flags = flags | get(*a).flags; } for a in f.sig.inputs.iter() { flags = flags | get(*a).flags; }
flags = flags | get(f.sig.output).flags; if let ty::FnConverging(output) = f.sig.output {
// T -> _|_ is *not* _|_ ! flags = flags | get(output).flags;
flags = flags - HAS_TY_BOT; }
flags = flags | flags_for_bounds(&f.bounds); flags = flags | flags_for_bounds(&f.bounds);
} }
} }
@ -1714,9 +1720,6 @@ pub fn mk_nil() -> t { mk_prim_t(&primitives::TY_NIL) }
#[inline] #[inline]
pub fn mk_err() -> t { mk_prim_t(&primitives::TY_ERR) } pub fn mk_err() -> t { mk_prim_t(&primitives::TY_ERR) }
#[inline]
pub fn mk_bot() -> t { mk_prim_t(&primitives::TY_BOT) }
#[inline] #[inline]
pub fn mk_bool() -> t { mk_prim_t(&primitives::TY_BOOL) } pub fn mk_bool() -> t { mk_prim_t(&primitives::TY_BOOL) }
@ -1862,7 +1865,7 @@ pub fn mk_ctor_fn(cx: &ctxt,
sig: FnSig { sig: FnSig {
binder_id: binder_id, binder_id: binder_id,
inputs: input_args, inputs: input_args,
output: output, output: ty::FnConverging(output),
variadic: false variadic: false
} }
}) })
@ -1924,7 +1927,7 @@ pub fn maybe_walk_ty(ty: t, f: |t| -> bool) {
return; return;
} }
match get(ty).sty { match get(ty).sty {
ty_nil | ty_bot | ty_bool | ty_char | ty_int(_) | ty_uint(_) | ty_float(_) | ty_nil | ty_bool | ty_char | ty_int(_) | ty_uint(_) | ty_float(_) |
ty_str | ty_infer(_) | ty_param(_) | ty_err => {} ty_str | ty_infer(_) | ty_param(_) | ty_err => {}
ty_uniq(ty) | ty_vec(ty, _) | ty_open(ty) => maybe_walk_ty(ty, f), ty_uniq(ty) | ty_vec(ty, _) | ty_open(ty) => maybe_walk_ty(ty, f),
ty_ptr(ref tm) | ty_rptr(_, ref tm) => { ty_ptr(ref tm) | ty_rptr(_, ref tm) => {
@ -1939,11 +1942,15 @@ pub fn maybe_walk_ty(ty: t, f: |t| -> bool) {
ty_tup(ref ts) => { for tt in ts.iter() { maybe_walk_ty(*tt, |x| f(x)); } } ty_tup(ref ts) => { for tt in ts.iter() { maybe_walk_ty(*tt, |x| f(x)); } }
ty_bare_fn(ref ft) => { ty_bare_fn(ref ft) => {
for a in ft.sig.inputs.iter() { maybe_walk_ty(*a, |x| f(x)); } for a in ft.sig.inputs.iter() { maybe_walk_ty(*a, |x| f(x)); }
maybe_walk_ty(ft.sig.output, f); if let ty::FnConverging(output) = ft.sig.output {
maybe_walk_ty(output, f);
}
} }
ty_closure(ref ft) => { ty_closure(ref ft) => {
for a in ft.sig.inputs.iter() { maybe_walk_ty(*a, |x| f(x)); } for a in ft.sig.inputs.iter() { maybe_walk_ty(*a, |x| f(x)); }
maybe_walk_ty(ft.sig.output, f); if let ty::FnConverging(output) = ft.sig.output {
maybe_walk_ty(output, f);
}
} }
} }
} }
@ -1995,10 +2002,6 @@ pub fn type_is_nil(ty: t) -> bool {
get(ty).sty == ty_nil get(ty).sty == ty_nil
} }
pub fn type_is_bot(ty: t) -> bool {
get(ty).flags.intersects(HAS_TY_BOT)
}
pub fn type_is_error(ty: t) -> bool { pub fn type_is_error(ty: t) -> bool {
get(ty).flags.intersects(HAS_TY_ERR) get(ty).flags.intersects(HAS_TY_ERR)
} }
@ -2170,7 +2173,7 @@ pub fn type_needs_unwind_cleanup(cx: &ctxt, ty: t) -> bool {
let mut needs_unwind_cleanup = false; let mut needs_unwind_cleanup = false;
maybe_walk_ty(ty, |ty| { maybe_walk_ty(ty, |ty| {
needs_unwind_cleanup |= match get(ty).sty { needs_unwind_cleanup |= match get(ty).sty {
ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_nil | ty_bool | ty_int(_) | ty_uint(_) |
ty_float(_) | ty_tup(_) | ty_ptr(_) => false, ty_float(_) | ty_tup(_) | ty_ptr(_) => false,
ty_enum(did, ref substs) => ty_enum(did, ref substs) =>
@ -2430,7 +2433,7 @@ pub fn type_contents(cx: &ctxt, ty: t) -> TypeContents {
// Scalar and unique types are sendable, and durable // Scalar and unique types are sendable, and durable
ty_infer(ty::SkolemizedIntTy(_)) | ty_infer(ty::SkolemizedIntTy(_)) |
ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) | ty_nil | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) |
ty_bare_fn(_) | ty::ty_char => { ty_bare_fn(_) | ty::ty_char => {
TC::None TC::None
} }
@ -2560,7 +2563,7 @@ pub fn type_contents(cx: &ctxt, ty: t) -> TypeContents {
// We only ever ask for the kind of types that are defined in // We only ever ask for the kind of types that are defined in
// the current crate; therefore, the only type parameters that // the current crate; therefore, the only type parameters that
// could be in scope are those defined in the current crate. // could be in scope are those defined in the current crate.
// If this assertion failures, it is likely because of a // If this assertion fails, it is likely because of a
// failure in the cross-crate inlining code to translate a // failure in the cross-crate inlining code to translate a
// def-id. // def-id.
assert_eq!(p.def_id.krate, ast::LOCAL_CRATE); assert_eq!(p.def_id.krate, ast::LOCAL_CRATE);
@ -2742,7 +2745,6 @@ pub fn is_instantiable(cx: &ctxt, r_ty: t) -> bool {
ty_vec(ty, Some(_)) => type_requires(cx, seen, r_ty, ty), ty_vec(ty, Some(_)) => type_requires(cx, seen, r_ty, ty),
ty_nil | ty_nil |
ty_bot |
ty_bool | ty_bool |
ty_char | ty_char |
ty_int(_) | ty_int(_) |
@ -3276,7 +3278,7 @@ pub fn ty_closure_store(fty: t) -> TraitStore {
} }
} }
pub fn ty_fn_ret(fty: t) -> t { pub fn ty_fn_ret(fty: t) -> FnOutput {
match get(fty).sty { match get(fty).sty {
ty_bare_fn(ref f) => f.sig.output, ty_bare_fn(ref f) => f.sig.output,
ty_closure(ref f) => f.sig.output, ty_closure(ref f) => f.sig.output,
@ -3451,7 +3453,9 @@ pub fn adjust_ty(cx: &ctxt,
let method_call = typeck::MethodCall::autoderef(expr_id, i); let method_call = typeck::MethodCall::autoderef(expr_id, i);
match method_type(method_call) { match method_type(method_call) {
Some(method_ty) => { Some(method_ty) => {
adjusted_ty = ty_fn_ret(method_ty); if let ty::FnConverging(result_type) = ty_fn_ret(method_ty) {
adjusted_ty = result_type;
}
} }
None => {} None => {}
} }
@ -3779,7 +3783,7 @@ pub fn impl_or_trait_item_idx(id: ast::Name, trait_items: &[ImplOrTraitItem])
pub fn ty_sort_string(cx: &ctxt, t: t) -> String { pub fn ty_sort_string(cx: &ctxt, t: t) -> String {
match get(t).sty { match get(t).sty {
ty_nil | ty_bot | ty_bool | ty_char | ty_int(_) | ty_nil | ty_bool | ty_char | ty_int(_) |
ty_uint(_) | ty_float(_) | ty_str => { ty_uint(_) | ty_float(_) | ty_str => {
::util::ppaux::ty_to_string(cx, t) ::util::ppaux::ty_to_string(cx, t)
} }
@ -3959,6 +3963,11 @@ pub fn type_err_to_str(cx: &ctxt, err: &type_err) -> String {
if values.expected { "variadic" } else { "non-variadic" }, if values.expected { "variadic" } else { "non-variadic" },
if values.found { "variadic" } else { "non-variadic" }) if values.found { "variadic" } else { "non-variadic" })
} }
terr_convergence_mismatch(ref values) => {
format!("expected {} fn, found {} function",
if values.expected { "converging" } else { "diverging" },
if values.found { "converging" } else { "diverging" })
}
} }
} }
@ -4667,7 +4676,6 @@ pub fn is_binopable(cx: &ctxt, ty: t, op: ast::BinOp) -> bool {
static tycat_char: int = 2; static tycat_char: int = 2;
static tycat_int: int = 3; static tycat_int: int = 3;
static tycat_float: int = 4; static tycat_float: int = 4;
static tycat_bot: int = 5;
static tycat_raw_ptr: int = 6; static tycat_raw_ptr: int = 6;
static opcat_add: int = 0; static opcat_add: int = 0;
@ -4712,7 +4720,6 @@ pub fn is_binopable(cx: &ctxt, ty: t, op: ast::BinOp) -> bool {
ty_bool => tycat_bool, ty_bool => tycat_bool,
ty_int(_) | ty_uint(_) | ty_infer(IntVar(_)) => tycat_int, ty_int(_) | ty_uint(_) | ty_infer(IntVar(_)) => tycat_int,
ty_float(_) | ty_infer(FloatVar(_)) => tycat_float, ty_float(_) | ty_infer(FloatVar(_)) => tycat_float,
ty_bot => tycat_bot,
ty_ptr(_) => tycat_raw_ptr, ty_ptr(_) => tycat_raw_ptr,
_ => tycat_other _ => tycat_other
} }
@ -5149,7 +5156,6 @@ pub fn hash_crate_independent(tcx: &ctxt, t: t, svh: &Svh) -> u64 {
ty::walk_ty(t, |t| { ty::walk_ty(t, |t| {
match ty::get(t).sty { match ty::get(t).sty {
ty_nil => byte!(0), ty_nil => byte!(0),
ty_bot => byte!(1),
ty_bool => byte!(2), ty_bool => byte!(2),
ty_char => byte!(3), ty_char => byte!(3),
ty_int(i) => { ty_int(i) => {
@ -5520,7 +5526,6 @@ pub fn accumulate_lifetimes_in_type(accumulator: &mut Vec<ty::Region>,
accum_substs(accumulator, substs); accum_substs(accumulator, substs);
} }
ty_nil | ty_nil |
ty_bot |
ty_bool | ty_bool |
ty_char | ty_char |
ty_int(_) | ty_int(_) |

View file

@ -91,6 +91,12 @@ pub trait TypeFolder<'tcx> {
super_fold_sig(self, sig) super_fold_sig(self, sig)
} }
fn fold_output(&mut self,
output: &ty::FnOutput)
-> ty::FnOutput {
super_fold_output(self, output)
}
fn fold_bare_fn_ty(&mut self, fn fold_bare_fn_ty(&mut self,
fty: &ty::BareFnTy) fty: &ty::BareFnTy)
-> ty::BareFnTy -> ty::BareFnTy
@ -207,6 +213,12 @@ impl TypeFoldable for ty::mt {
} }
} }
impl TypeFoldable for ty::FnOutput {
fn fold_with<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::FnOutput {
folder.fold_output(self)
}
}
impl TypeFoldable for ty::FnSig { impl TypeFoldable for ty::FnSig {
fn fold_with<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::FnSig { fn fold_with<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::FnSig {
folder.fold_sig(self) folder.fold_sig(self)
@ -453,6 +465,15 @@ pub fn super_fold_sig<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
variadic: sig.variadic } variadic: sig.variadic }
} }
pub fn super_fold_output<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
output: &ty::FnOutput)
-> ty::FnOutput {
match *output {
ty::FnConverging(ref ty) => ty::FnConverging(ty.fold_with(this)),
ty::FnDiverging => ty::FnDiverging
}
}
pub fn super_fold_bare_fn_ty<'tcx, T: TypeFolder<'tcx>>(this: &mut T, pub fn super_fold_bare_fn_ty<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
fty: &ty::BareFnTy) fty: &ty::BareFnTy)
-> ty::BareFnTy -> ty::BareFnTy
@ -537,7 +558,7 @@ pub fn super_fold_sty<'tcx, T: TypeFolder<'tcx>>(this: &mut T,
ty::ty_unboxed_closure(did, ref region, ref substs) => { ty::ty_unboxed_closure(did, ref region, ref substs) => {
ty::ty_unboxed_closure(did, region.fold_with(this), substs.fold_with(this)) ty::ty_unboxed_closure(did, region.fold_with(this), substs.fold_with(this))
} }
ty::ty_nil | ty::ty_bot | ty::ty_bool | ty::ty_char | ty::ty_str | ty::ty_nil | ty::ty_bool | ty::ty_char | ty::ty_str |
ty::ty_int(_) | ty::ty_uint(_) | ty::ty_float(_) | ty::ty_int(_) | ty::ty_uint(_) | ty::ty_float(_) |
ty::ty_err | ty::ty_infer(_) | ty::ty_err | ty::ty_infer(_) |
ty::ty_param(..) => { ty::ty_param(..) => {

View file

@ -793,7 +793,7 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>(
let typ = ast_ty_to_builtin_ty(this, rscope, ast_ty).unwrap_or_else(|| { let typ = ast_ty_to_builtin_ty(this, rscope, ast_ty).unwrap_or_else(|| {
match ast_ty.node { match ast_ty.node {
ast::TyNil => ty::mk_nil(), ast::TyNil => ty::mk_nil(),
ast::TyBot => ty::mk_bot(), ast::TyBot => unreachable!(),
ast::TyUniq(ref ty) => { ast::TyUniq(ref ty) => {
mk_pointer(this, rscope, ast::MutImmutable, &**ty, Uniq, mk_pointer(this, rscope, ast::MutImmutable, &**ty, Uniq,
|ty| ty::mk_uniq(tcx, ty)) |ty| ty::mk_uniq(tcx, ty))
@ -1171,9 +1171,9 @@ fn ty_of_method_or_bare_fn<'tcx, AC: AstConv<'tcx>>(
.collect(); .collect();
let output_ty = match decl.output.node { let output_ty = match decl.output.node {
ast::TyInfer => this.ty_infer(decl.output.span), ast::TyBot => ty::FnDiverging,
_ => { ast::TyInfer => ty::FnConverging(this.ty_infer(decl.output.span)),
match implied_output_region { _ => ty::FnConverging(match implied_output_region {
Some(implied_output_region) => { Some(implied_output_region) => {
let rb = SpecificRscope::new(implied_output_region); let rb = SpecificRscope::new(implied_output_region);
ast_ty_to_ty(this, &rb, &*decl.output) ast_ty_to_ty(this, &rb, &*decl.output)
@ -1185,8 +1185,7 @@ fn ty_of_method_or_bare_fn<'tcx, AC: AstConv<'tcx>>(
let rb = UnelidableRscope::new(param_lifetimes); let rb = UnelidableRscope::new(param_lifetimes);
ast_ty_to_ty(this, &rb, &*decl.output) ast_ty_to_ty(this, &rb, &*decl.output)
} }
} })
}
}; };
(ty::BareFnTy { (ty::BareFnTy {
@ -1308,10 +1307,12 @@ pub fn ty_of_closure<'tcx, AC: AstConv<'tcx>>(
}).collect(); }).collect();
let expected_ret_ty = expected_sig.map(|e| e.output); let expected_ret_ty = expected_sig.map(|e| e.output);
let output_ty = match decl.output.node { let output_ty = match decl.output.node {
ast::TyBot => ty::FnDiverging,
ast::TyInfer if expected_ret_ty.is_some() => expected_ret_ty.unwrap(), ast::TyInfer if expected_ret_ty.is_some() => expected_ret_ty.unwrap(),
ast::TyInfer => this.ty_infer(decl.output.span), ast::TyInfer => ty::FnConverging(this.ty_infer(decl.output.span)),
_ => ast_ty_to_ty(this, &rb, &*decl.output) _ => ty::FnConverging(ast_ty_to_ty(this, &rb, &*decl.output))
}; };
ty::ClosureTy { ty::ClosureTy {

View file

@ -261,7 +261,7 @@ pub fn check_match(fcx: &FnCtxt,
// on any empty type and is therefore unreachable; should the flow // on any empty type and is therefore unreachable; should the flow
// of execution reach it, we will fail, so bottom is an appropriate // of execution reach it, we will fail, so bottom is an appropriate
// type in that case) // type in that case)
let result_ty = arms.iter().fold(ty::mk_bot(), |result_ty, arm| { let result_ty = arms.iter().fold(fcx.infcx().next_diverging_ty_var(), |result_ty, arm| {
check_expr(fcx, &*arm.body); check_expr(fcx, &*arm.body);
let bty = fcx.node_ty(arm.body.id); let bty = fcx.node_ty(arm.body.id);
@ -347,7 +347,10 @@ pub fn check_pat_enum(pcx: &pat_ctxt, pat: &ast::Pat,
let ctor_pty = ty::lookup_item_type(tcx, enum_def); let ctor_pty = ty::lookup_item_type(tcx, enum_def);
let path_ty = if ty::is_fn_ty(ctor_pty.ty) { let path_ty = if ty::is_fn_ty(ctor_pty.ty) {
ty::Polytype { ty: ty::ty_fn_ret(ctor_pty.ty), ..ctor_pty } ty::Polytype {
ty: ty::ty_fn_ret(ctor_pty.ty).unwrap(),
..ctor_pty
}
} else { } else {
ctor_pty ctor_pty
}; };

View file

@ -1082,7 +1082,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> {
ty_bare_fn(..) | ty_uniq(..) | ty_rptr(..) | ty_bare_fn(..) | ty_uniq(..) | ty_rptr(..) |
ty_infer(IntVar(_)) | ty_infer(IntVar(_)) |
ty_infer(FloatVar(_)) | ty_infer(FloatVar(_)) |
ty_param(..) | ty_nil | ty_bot | ty_bool | ty_param(..) | ty_nil | ty_bool |
ty_char | ty_int(..) | ty_uint(..) | ty_char | ty_int(..) | ty_uint(..) |
ty_float(..) | ty_enum(..) | ty_ptr(..) | ty_struct(..) | ty_float(..) | ty_enum(..) | ty_ptr(..) | ty_struct(..) |
ty_unboxed_closure(..) | ty_tup(..) | ty_open(..) | ty_unboxed_closure(..) | ty_tup(..) | ty_open(..) |
@ -1603,9 +1603,11 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> {
return false; return false;
} }
} }
if !check_for_self_ty(sig.output) { if let ty::FnConverging(result_type) = sig.output {
if !check_for_self_ty(result_type) {
return false; return false;
} }
}
if candidate.method_ty.generics.has_type_params(subst::FnSpace) { if candidate.method_ty.generics.has_type_params(subst::FnSpace) {
// reason (b) above // reason (b) above

View file

@ -280,7 +280,7 @@ pub struct FnCtxt<'a, 'tcx: 'a> {
// expects the types within the function to be consistent. // expects the types within the function to be consistent.
err_count_on_creation: uint, err_count_on_creation: uint,
ret_ty: ty::t, ret_ty: ty::FnOutput,
ps: RefCell<FnStyleState>, ps: RefCell<FnStyleState>,
@ -346,7 +346,7 @@ impl<'a, 'tcx> Inherited<'a, 'tcx> {
// Used by check_const and check_enum_variants // Used by check_const and check_enum_variants
pub fn blank_fn_ctxt<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>, pub fn blank_fn_ctxt<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>,
inh: &'a Inherited<'a, 'tcx>, inh: &'a Inherited<'a, 'tcx>,
rty: ty::t, rty: ty::FnOutput,
body_id: ast::NodeId) body_id: ast::NodeId)
-> FnCtxt<'a, 'tcx> { -> FnCtxt<'a, 'tcx> {
FnCtxt { FnCtxt {
@ -410,6 +410,7 @@ fn check_bare_fn(ccx: &CrateCtxt,
vtable::select_all_fcx_obligations_or_error(&fcx); vtable::select_all_fcx_obligations_or_error(&fcx);
regionck::regionck_fn(&fcx, id, body); regionck::regionck_fn(&fcx, id, body);
fcx.default_diverging_type_variables_to_nil();
writeback::resolve_type_vars_in_fn(&fcx, decl, body); writeback::resolve_type_vars_in_fn(&fcx, decl, body);
} }
_ => ccx.tcx.sess.impossible_case(body.span, _ => ccx.tcx.sess.impossible_case(body.span,
@ -426,8 +427,7 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
match ty_opt { match ty_opt {
None => { None => {
// infer the variable's type // infer the variable's type
let var_id = self.fcx.infcx().next_ty_var_id(); let var_ty = self.fcx.infcx().next_ty_var();
let var_ty = ty::mk_var(self.fcx.tcx(), var_id);
self.fcx.inh.locals.borrow_mut().insert(nid, var_ty); self.fcx.inh.locals.borrow_mut().insert(nid, var_ty);
var_ty var_ty
} }
@ -551,8 +551,16 @@ fn check_fn<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>,
}; };
// Remember return type so that regionck can access it later. // Remember return type so that regionck can access it later.
let fn_sig_tys: Vec<ty::t> = let mut fn_sig_tys: Vec<ty::t> =
arg_tys.iter().chain([ret_ty].iter()).map(|&ty| ty).collect(); arg_tys.iter()
.map(|&ty| ty)
.collect();
if let ty::FnConverging(ret_ty) = ret_ty {
fcx.require_type_is_sized(ret_ty, decl.output.span, traits::ReturnType);
fn_sig_tys.push(ret_ty);
}
debug!("fn-sig-map: fn_id={} fn_sig_tys={}", debug!("fn-sig-map: fn_id={} fn_sig_tys={}",
fn_id, fn_id,
fn_sig_tys.repr(tcx)); fn_sig_tys.repr(tcx));
@ -584,9 +592,11 @@ fn check_fn<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>,
visit.visit_block(body); visit.visit_block(body);
} }
fcx.require_type_is_sized(ret_ty, decl.output.span, traits::ReturnType);
check_block_with_expected(&fcx, body, ExpectHasType(ret_ty)); check_block_with_expected(&fcx, body, match ret_ty {
ty::FnConverging(result_type) => ExpectHasType(result_type),
ty::FnDiverging => NoExpectation
});
for (input, arg) in decl.inputs.iter().zip(arg_tys.iter()) { for (input, arg) in decl.inputs.iter().zip(arg_tys.iter()) {
fcx.write_ty(input.id, *arg); fcx.write_ty(input.id, *arg);
@ -1333,11 +1343,6 @@ fn check_cast(fcx: &FnCtxt,
return return
} }
if ty::type_is_bot(t_e) {
fcx.write_bot(id);
return
}
if !ty::type_is_sized(fcx.tcx(), t_1) { if !ty::type_is_sized(fcx.tcx(), t_1) {
let tstr = fcx.infcx().ty_to_string(t_1); let tstr = fcx.infcx().ty_to_string(t_1);
fcx.type_error_message(span, |actual| { fcx.type_error_message(span, |actual| {
@ -1562,6 +1567,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} }
} }
pub fn default_diverging_type_variables_to_nil(&self) {
for (_, &ref ty) in self.inh.node_types.borrow_mut().iter_mut() {
if self.infcx().type_var_diverges(self.infcx().resolve_type_vars_if_possible(*ty)) {
demand::eqtype(self, codemap::DUMMY_SP, *ty, ty::mk_nil());
}
}
}
#[inline] #[inline]
pub fn write_ty(&self, node_id: ast::NodeId, ty: ty::t) { pub fn write_ty(&self, node_id: ast::NodeId, ty: ty::t) {
debug!("write_ty({}, {}) in fcx {}", debug!("write_ty({}, {}) in fcx {}",
@ -1726,9 +1739,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub fn write_nil(&self, node_id: ast::NodeId) { pub fn write_nil(&self, node_id: ast::NodeId) {
self.write_ty(node_id, ty::mk_nil()); self.write_ty(node_id, ty::mk_nil());
} }
pub fn write_bot(&self, node_id: ast::NodeId) {
self.write_ty(node_id, ty::mk_bot());
}
pub fn write_error(&self, node_id: ast::NodeId) { pub fn write_error(&self, node_id: ast::NodeId) {
self.write_ty(node_id, ty::mk_err()); self.write_ty(node_id, ty::mk_err());
} }
@ -2051,10 +2061,6 @@ pub fn autoderef<T>(fcx: &FnCtxt, sp: Span, base_ty: ty::t,
for autoderefs in range(0, fcx.tcx().sess.recursion_limit.get()) { for autoderefs in range(0, fcx.tcx().sess.recursion_limit.get()) {
let resolved_t = structurally_resolved_type(fcx, sp, t); let resolved_t = structurally_resolved_type(fcx, sp, t);
if ty::type_is_bot(resolved_t) {
return (resolved_t, autoderefs, None);
}
match should_stop(resolved_t, autoderefs) { match should_stop(resolved_t, autoderefs) {
Some(x) => return (resolved_t, autoderefs, Some(x)), Some(x) => return (resolved_t, autoderefs, Some(x)),
None => {} None => {}
@ -2197,7 +2203,12 @@ fn make_return_type(fcx: &FnCtxt,
} }
None => {} None => {}
} }
ty::deref(ref_ty, true) match ref_ty {
ty::FnConverging(ref_ty) =>
ty::deref(ref_ty, true),
ty::FnDiverging =>
None
}
} }
None => None, None => None,
} }
@ -2285,7 +2296,12 @@ fn try_overloaded_slice(fcx: &FnCtxt,
} }
None => {} None => {}
} }
Some(ty::mt { ty: result_ty, mutbl: ast::MutImmutable }) match result_ty {
ty::FnConverging(result_ty) =>
Some(ty::mt { ty: result_ty, mutbl: ast::MutImmutable }),
ty::FnDiverging =>
None
}
} }
None => None, None => None,
} }
@ -2400,9 +2416,11 @@ fn lookup_method_for_for_loop(fcx: &FnCtxt,
// We expect the return type to be `Option` or something like it. // We expect the return type to be `Option` or something like it.
// Grab the first parameter of its type substitution. // Grab the first parameter of its type substitution.
let return_type = structurally_resolved_type(fcx, let return_type = match return_type {
iterator_expr.span, ty::FnConverging(return_type) =>
return_type); structurally_resolved_type(fcx, iterator_expr.span, return_type),
ty::FnDiverging => ty::mk_err()
};
match ty::get(return_type).sty { match ty::get(return_type).sty {
ty::ty_enum(_, ref substs) ty::ty_enum(_, ref substs)
if !substs.types.is_empty_in(subst::TypeSpace) => { if !substs.types.is_empty_in(subst::TypeSpace) => {
@ -2427,7 +2445,7 @@ fn check_method_argument_types<'a>(fcx: &FnCtxt,
args_no_rcvr: &[&'a P<ast::Expr>], args_no_rcvr: &[&'a P<ast::Expr>],
deref_args: DerefArgs, deref_args: DerefArgs,
tuple_arguments: TupleArgumentsFlag) tuple_arguments: TupleArgumentsFlag)
-> ty::t { -> ty::FnOutput {
if ty::type_is_error(method_fn_ty) { if ty::type_is_error(method_fn_ty) {
let err_inputs = err_args(args_no_rcvr.len()); let err_inputs = err_args(args_no_rcvr.len());
check_argument_types(fcx, check_argument_types(fcx,
@ -2438,7 +2456,7 @@ fn check_method_argument_types<'a>(fcx: &FnCtxt,
deref_args, deref_args,
false, false,
tuple_arguments); tuple_arguments);
method_fn_ty ty::FnConverging(method_fn_ty)
} else { } else {
match ty::get(method_fn_ty).sty { match ty::get(method_fn_ty).sty {
ty::ty_bare_fn(ref fty) => { ty::ty_bare_fn(ref fty) => {
@ -2654,8 +2672,11 @@ fn err_args(len: uint) -> Vec<ty::t> {
Vec::from_fn(len, |_| ty::mk_err()) Vec::from_fn(len, |_| ty::mk_err())
} }
fn write_call(fcx: &FnCtxt, call_expr: &ast::Expr, output: ty::t) { fn write_call(fcx: &FnCtxt, call_expr: &ast::Expr, output: ty::FnOutput) {
fcx.write_ty(call_expr.id, output); fcx.write_ty(call_expr.id, match output {
ty::FnConverging(output_ty) => output_ty,
ty::FnDiverging => fcx.infcx().next_diverging_ty_var()
});
} }
// AST fragment checking // AST fragment checking
@ -2845,8 +2866,8 @@ enum TupleArgumentsFlag {
/// strict, _|_ can appear in the type of an expression that does not, /// strict, _|_ can appear in the type of an expression that does not,
/// itself, diverge: for example, fn() -> _|_.) /// itself, diverge: for example, fn() -> _|_.)
/// Note that inspecting a type's structure *directly* may expose the fact /// Note that inspecting a type's structure *directly* may expose the fact
/// that there are actually multiple representations for both `ty_err` and /// that there are actually multiple representations for `ty_err`, so avoid
/// `ty_bot`, so avoid that when err and bot need to be handled differently. /// that when err needs to be handled differently.
fn check_expr_with_unifier(fcx: &FnCtxt, fn check_expr_with_unifier(fcx: &FnCtxt,
expr: &ast::Expr, expr: &ast::Expr,
expected: Expectation, expected: Expectation,
@ -2873,7 +2894,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
let error_fn_sig = FnSig { let error_fn_sig = FnSig {
binder_id: ast::CRATE_NODE_ID, binder_id: ast::CRATE_NODE_ID,
inputs: err_args(args.len()), inputs: err_args(args.len()),
output: ty::mk_err(), output: ty::FnConverging(ty::mk_err()),
variadic: false variadic: false
}; };
@ -3021,8 +3042,6 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
let cond_ty = fcx.expr_ty(cond_expr); let cond_ty = fcx.expr_ty(cond_expr);
let if_ty = if ty::type_is_error(cond_ty) { let if_ty = if ty::type_is_error(cond_ty) {
ty::mk_err() ty::mk_err()
} else if ty::type_is_bot(cond_ty) {
ty::mk_bot()
} else { } else {
branches_ty branches_ty
}; };
@ -3055,13 +3074,16 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
// HACK(eddyb) Fully qualified path to work around a resolve bug. // HACK(eddyb) Fully qualified path to work around a resolve bug.
let method_call = ::middle::typeck::MethodCall::expr(op_ex.id); let method_call = ::middle::typeck::MethodCall::expr(op_ex.id);
fcx.inh.method_map.borrow_mut().insert(method_call, method); fcx.inh.method_map.borrow_mut().insert(method_call, method);
check_method_argument_types(fcx, match check_method_argument_types(fcx,
op_ex.span, op_ex.span,
method_ty, method_ty,
op_ex, op_ex,
args.as_slice(), args.as_slice(),
DoDerefArgs, DoDerefArgs,
DontTupleArguments) DontTupleArguments) {
ty::FnConverging(result_type) => result_type,
ty::FnDiverging => ty::mk_err()
}
} }
None => { None => {
unbound_method(); unbound_method();
@ -3663,9 +3685,6 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
None => {} None => {}
Some(base_expr) => { Some(base_expr) => {
check_expr_has_type(fcx, &*base_expr, struct_type); check_expr_has_type(fcx, &*base_expr, struct_type);
if ty::type_is_bot(fcx.node_ty(base_expr.id)) {
struct_type = ty::mk_bot();
}
} }
} }
@ -3763,10 +3782,6 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
ty::type_is_error(rhs_ty) { ty::type_is_error(rhs_ty) {
fcx.write_error(id); fcx.write_error(id);
} }
else if ty::type_is_bot(lhs_ty) ||
(ty::type_is_bot(rhs_ty) && !ast_util::lazy_binop(op)) {
fcx.write_bot(id);
}
} }
ast::ExprAssignOp(op, ref lhs, ref rhs) => { ast::ExprAssignOp(op, ref lhs, ref rhs) => {
check_binop(fcx, expr, op, &**lhs, rhs, BinopAssignment); check_binop(fcx, expr, op, &**lhs, rhs, BinopAssignment);
@ -3785,8 +3800,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
// Overwrite result of check_binop...this preserves existing behavior // Overwrite result of check_binop...this preserves existing behavior
// but seems quite dubious with regard to user-defined methods // but seems quite dubious with regard to user-defined methods
// and so forth. - Niko // and so forth. - Niko
if !ty::type_is_error(result_t) if !ty::type_is_error(result_t) {
&& !ty::type_is_bot(result_t) {
fcx.write_nil(expr.id); fcx.write_nil(expr.id);
} }
} }
@ -3820,10 +3834,8 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
if !ty::type_is_error(oprnd_t) { if !ty::type_is_error(oprnd_t) {
match unop { match unop {
ast::UnUniq => { ast::UnUniq => {
if !ty::type_is_bot(oprnd_t) {
oprnd_t = ty::mk_uniq(tcx, oprnd_t); oprnd_t = ty::mk_uniq(tcx, oprnd_t);
} }
}
ast::UnDeref => { ast::UnDeref => {
oprnd_t = structurally_resolved_type(fcx, expr.span, oprnd_t); oprnd_t = structurally_resolved_type(fcx, expr.span, oprnd_t);
oprnd_t = match ty::deref(oprnd_t, true) { oprnd_t = match ty::deref(oprnd_t, true) {
@ -3859,7 +3871,6 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
}; };
} }
ast::UnNot => { ast::UnNot => {
if !ty::type_is_bot(oprnd_t) {
oprnd_t = structurally_resolved_type(fcx, oprnd.span, oprnd_t = structurally_resolved_type(fcx, oprnd.span,
oprnd_t); oprnd_t);
if !(ty::type_is_integral(oprnd_t) || if !(ty::type_is_integral(oprnd_t) ||
@ -3869,9 +3880,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
expr, &**oprnd, oprnd_t); expr, &**oprnd, oprnd_t);
} }
} }
}
ast::UnNeg => { ast::UnNeg => {
if !ty::type_is_bot(oprnd_t) {
oprnd_t = structurally_resolved_type(fcx, oprnd.span, oprnd_t = structurally_resolved_type(fcx, oprnd.span,
oprnd_t); oprnd_t);
if !(ty::type_is_integral(oprnd_t) || if !(ty::type_is_integral(oprnd_t) ||
@ -3883,7 +3892,6 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
} }
} }
} }
}
fcx.write_ty(id, oprnd_t); fcx.write_ty(id, oprnd_t);
} }
ast::ExprAddrOf(mutbl, ref oprnd) => { ast::ExprAddrOf(mutbl, ref oprnd) => {
@ -3904,10 +3912,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
let tm = ty::mt { ty: fcx.expr_ty(&**oprnd), mutbl: mutbl }; let tm = ty::mt { ty: fcx.expr_ty(&**oprnd), mutbl: mutbl };
let oprnd_t = if ty::type_is_error(tm.ty) { let oprnd_t = if ty::type_is_error(tm.ty) {
ty::mk_err() ty::mk_err()
} else if ty::type_is_bot(tm.ty) { } else {
ty::mk_bot()
}
else {
// Note: at this point, we cannot say what the best lifetime // Note: at this point, we cannot say what the best lifetime
// is to use for resulting pointer. We want to use the // is to use for resulting pointer. We want to use the
// shortest lifetime possible so as to avoid spurious borrowck // shortest lifetime possible so as to avoid spurious borrowck
@ -3961,24 +3966,32 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
fcx.write_nil(id); fcx.write_nil(id);
} }
ast::ExprMac(_) => tcx.sess.bug("unexpanded macro"), ast::ExprMac(_) => tcx.sess.bug("unexpanded macro"),
ast::ExprBreak(_) => { fcx.write_bot(id); } ast::ExprBreak(_) => { fcx.write_ty(id, fcx.infcx().next_diverging_ty_var()); }
ast::ExprAgain(_) => { fcx.write_bot(id); } ast::ExprAgain(_) => { fcx.write_ty(id, fcx.infcx().next_diverging_ty_var()); }
ast::ExprRet(ref expr_opt) => { ast::ExprRet(ref expr_opt) => {
let ret_ty = fcx.ret_ty; match fcx.ret_ty {
ty::FnConverging(result_type) => {
match *expr_opt { match *expr_opt {
None => match fcx.mk_eqty(false, infer::Misc(expr.span), None =>
ret_ty, ty::mk_nil()) { if let Err(_) = fcx.mk_eqty(false, infer::Misc(expr.span),
Ok(_) => { /* fall through */ } result_type, ty::mk_nil()) {
Err(_) => {
span_err!(tcx.sess, expr.span, E0069, span_err!(tcx.sess, expr.span, E0069,
"`return;` in function returning non-nil"); "`return;` in function returning non-nil");
}
}, },
Some(ref e) => { Some(ref e) => {
check_expr_coercable_to_type(fcx, &**e, ret_ty); check_expr_coercable_to_type(fcx, &**e, result_type);
} }
} }
fcx.write_bot(id); }
ty::FnDiverging => {
if let Some(ref e) = *expr_opt {
check_expr(fcx, &**e);
}
span_err!(tcx.sess, expr.span, E0166,
"`return` in a function declared as diverging");
}
}
fcx.write_ty(id, fcx.infcx().next_diverging_ty_var());
} }
ast::ExprParen(ref a) => { ast::ExprParen(ref a) => {
check_expr_with_expectation_and_lvalue_pref(fcx, check_expr_with_expectation_and_lvalue_pref(fcx,
@ -4004,8 +4017,6 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
if ty::type_is_error(lhs_ty) || ty::type_is_error(rhs_ty) { if ty::type_is_error(lhs_ty) || ty::type_is_error(rhs_ty) {
fcx.write_error(id); fcx.write_error(id);
} else if ty::type_is_bot(lhs_ty) || ty::type_is_bot(rhs_ty) {
fcx.write_bot(id);
} else { } else {
fcx.write_nil(id); fcx.write_nil(id);
} }
@ -4025,9 +4036,6 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
if ty::type_is_error(cond_ty) || ty::type_is_error(body_ty) { if ty::type_is_error(cond_ty) || ty::type_is_error(body_ty) {
fcx.write_error(id); fcx.write_error(id);
} }
else if ty::type_is_bot(cond_ty) {
fcx.write_bot(id);
}
else { else {
fcx.write_nil(id); fcx.write_nil(id);
} }
@ -4052,7 +4060,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
ast::ExprLoop(ref body, _) => { ast::ExprLoop(ref body, _) => {
check_block_no_value(fcx, &**body); check_block_no_value(fcx, &**body);
if !may_break(tcx, expr.id, &**body) { if !may_break(tcx, expr.id, &**body) {
fcx.write_bot(id); fcx.write_ty(id, fcx.infcx().next_diverging_ty_var());
} else { } else {
fcx.write_nil(id); fcx.write_nil(id);
} }
@ -4100,31 +4108,24 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
let args: Vec<_> = args.iter().map(|x| x).collect(); let args: Vec<_> = args.iter().map(|x| x).collect();
if !try_overloaded_call(fcx, expr, &**f, f_ty, args.as_slice()) { if !try_overloaded_call(fcx, expr, &**f, f_ty, args.as_slice()) {
check_call(fcx, expr, &**f, args.as_slice()); check_call(fcx, expr, &**f, args.as_slice());
let (args_bot, args_err) = args.iter().fold((false, false), let args_err = args.iter().fold(false,
|(rest_bot, rest_err), a| { |rest_err, a| {
// is this not working? // is this not working?
let a_ty = fcx.expr_ty(&***a); let a_ty = fcx.expr_ty(&***a);
(rest_bot || ty::type_is_bot(a_ty), rest_err || ty::type_is_error(a_ty)});
rest_err || ty::type_is_error(a_ty))});
if ty::type_is_error(f_ty) || args_err { if ty::type_is_error(f_ty) || args_err {
fcx.write_error(id); fcx.write_error(id);
} }
else if ty::type_is_bot(f_ty) || args_bot {
fcx.write_bot(id);
}
} }
} }
ast::ExprMethodCall(ident, ref tps, ref args) => { ast::ExprMethodCall(ident, ref tps, ref args) => {
check_method_call(fcx, expr, ident, args.as_slice(), tps.as_slice(), lvalue_pref); check_method_call(fcx, expr, ident, args.as_slice(), tps.as_slice(), lvalue_pref);
let mut arg_tys = args.iter().map(|a| fcx.expr_ty(&**a)); let mut arg_tys = args.iter().map(|a| fcx.expr_ty(&**a));
let (args_bot, args_err) = arg_tys.fold((false, false), let args_err = arg_tys.fold(false,
|(rest_bot, rest_err), a| { |rest_err, a| {
(rest_bot || ty::type_is_bot(a), rest_err || ty::type_is_error(a)});
rest_err || ty::type_is_error(a))});
if args_err { if args_err {
fcx.write_error(id); fcx.write_error(id);
} else if args_bot {
fcx.write_bot(id);
} }
} }
ast::ExprCast(ref e, ref t) => { ast::ExprCast(ref e, ref t) => {
@ -4203,8 +4204,6 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
if ty::type_is_error(element_ty) { if ty::type_is_error(element_ty) {
fcx.write_error(id); fcx.write_error(id);
} else if ty::type_is_bot(element_ty) {
fcx.write_bot(id);
} else { } else {
let t = ty::mk_vec(tcx, t, Some(count)); let t = ty::mk_vec(tcx, t, Some(count));
fcx.write_ty(id, t); fcx.write_ty(id, t);
@ -4218,7 +4217,6 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
_ => None _ => None
} }
}); });
let mut bot_field = false;
let mut err_field = false; let mut err_field = false;
let elt_ts = elts.iter().enumerate().map(|(i, e)| { let elt_ts = elts.iter().enumerate().map(|(i, e)| {
@ -4234,12 +4232,9 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
} }
}; };
err_field = err_field || ty::type_is_error(t); err_field = err_field || ty::type_is_error(t);
bot_field = bot_field || ty::type_is_bot(t);
t t
}).collect(); }).collect();
if bot_field { if err_field {
fcx.write_bot(id);
} else if err_field {
fcx.write_error(id); fcx.write_error(id);
} else { } else {
let typ = ty::mk_tup(tcx, elt_ts); let typ = ty::mk_tup(tcx, elt_ts);
@ -4352,7 +4347,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
autoderef(fcx, expr.span, raw_base_t, Some(base.id), autoderef(fcx, expr.span, raw_base_t, Some(base.id),
lvalue_pref, |base_t, _| ty::index(base_t)); lvalue_pref, |base_t, _| ty::index(base_t));
match field_ty { match field_ty {
Some(ty) if !ty::type_is_bot(ty) => { Some(ty) => {
check_expr_has_type(fcx, &**idx, ty::mk_uint()); check_expr_has_type(fcx, &**idx, ty::mk_uint());
fcx.write_ty(id, ty); fcx.write_ty(id, ty);
fcx.write_autoderef_adjustment(base.id, base.span, autoderefs); fcx.write_autoderef_adjustment(base.id, base.span, autoderefs);
@ -4394,7 +4389,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
let raw_base_t = fcx.expr_ty(&**base); let raw_base_t = fcx.expr_ty(&**base);
let mut some_err = false; let mut some_err = false;
if ty::type_is_error(raw_base_t) || ty::type_is_bot(raw_base_t) { if ty::type_is_error(raw_base_t) {
fcx.write_ty(id, raw_base_t); fcx.write_ty(id, raw_base_t);
some_err = true; some_err = true;
} }
@ -4403,7 +4398,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
let check_slice_idx = |e: &ast::Expr| { let check_slice_idx = |e: &ast::Expr| {
check_expr(fcx, e); check_expr(fcx, e);
let e_t = fcx.expr_ty(e); let e_t = fcx.expr_ty(e);
if ty::type_is_error(e_t) || ty::type_is_bot(e_t) { if ty::type_is_error(e_t) {
fcx.write_ty(id, e_t); fcx.write_ty(id, e_t);
some_err = true; some_err = true;
} }
@ -4543,7 +4538,7 @@ pub fn check_decl_local(fcx: &FnCtxt, local: &ast::Local) {
Some(ref init) => { Some(ref init) => {
check_decl_initializer(fcx, local.id, &**init); check_decl_initializer(fcx, local.id, &**init);
let init_ty = fcx.expr_ty(&**init); let init_ty = fcx.expr_ty(&**init);
if ty::type_is_error(init_ty) || ty::type_is_bot(init_ty) { if ty::type_is_error(init_ty) {
fcx.write_ty(local.id, init_ty); fcx.write_ty(local.id, init_ty);
} }
} }
@ -4556,7 +4551,7 @@ pub fn check_decl_local(fcx: &FnCtxt, local: &ast::Local) {
}; };
_match::check_pat(&pcx, &*local.pat, t); _match::check_pat(&pcx, &*local.pat, t);
let pat_ty = fcx.node_ty(local.pat.id); let pat_ty = fcx.node_ty(local.pat.id);
if ty::type_is_error(pat_ty) || ty::type_is_bot(pat_ty) { if ty::type_is_error(pat_ty) {
fcx.write_ty(local.id, pat_ty); fcx.write_ty(local.id, pat_ty);
} }
} }
@ -4572,7 +4567,7 @@ pub fn check_stmt(fcx: &FnCtxt, stmt: &ast::Stmt) {
ast::DeclLocal(ref l) => { ast::DeclLocal(ref l) => {
check_decl_local(fcx, &**l); check_decl_local(fcx, &**l);
let l_t = fcx.node_ty(l.id); let l_t = fcx.node_ty(l.id);
saw_bot = saw_bot || ty::type_is_bot(l_t); saw_bot = saw_bot || fcx.infcx().type_var_diverges(l_t);
saw_err = saw_err || ty::type_is_error(l_t); saw_err = saw_err || ty::type_is_error(l_t);
} }
ast::DeclItem(_) => {/* ignore for now */ } ast::DeclItem(_) => {/* ignore for now */ }
@ -4583,20 +4578,20 @@ pub fn check_stmt(fcx: &FnCtxt, stmt: &ast::Stmt) {
// Check with expected type of () // Check with expected type of ()
check_expr_has_type(fcx, &**expr, ty::mk_nil()); check_expr_has_type(fcx, &**expr, ty::mk_nil());
let expr_ty = fcx.expr_ty(&**expr); let expr_ty = fcx.expr_ty(&**expr);
saw_bot = saw_bot || ty::type_is_bot(expr_ty); saw_bot = saw_bot || fcx.infcx().type_var_diverges(expr_ty);
saw_err = saw_err || ty::type_is_error(expr_ty); saw_err = saw_err || ty::type_is_error(expr_ty);
} }
ast::StmtSemi(ref expr, id) => { ast::StmtSemi(ref expr, id) => {
node_id = id; node_id = id;
check_expr(fcx, &**expr); check_expr(fcx, &**expr);
let expr_ty = fcx.expr_ty(&**expr); let expr_ty = fcx.expr_ty(&**expr);
saw_bot |= ty::type_is_bot(expr_ty); saw_bot |= fcx.infcx().type_var_diverges(expr_ty);
saw_err |= ty::type_is_error(expr_ty); saw_err |= ty::type_is_error(expr_ty);
} }
ast::StmtMac(..) => fcx.ccx.tcx.sess.bug("unexpanded macro") ast::StmtMac(..) => fcx.ccx.tcx.sess.bug("unexpanded macro")
} }
if saw_bot { if saw_bot {
fcx.write_bot(node_id); fcx.write_ty(node_id, fcx.infcx().next_diverging_ty_var());
} }
else if saw_err { else if saw_err {
fcx.write_error(node_id); fcx.write_error(node_id);
@ -4611,11 +4606,7 @@ pub fn check_block_no_value(fcx: &FnCtxt, blk: &ast::Block) {
let blkty = fcx.node_ty(blk.id); let blkty = fcx.node_ty(blk.id);
if ty::type_is_error(blkty) { if ty::type_is_error(blkty) {
fcx.write_error(blk.id); fcx.write_error(blk.id);
} } else {
else if ty::type_is_bot(blkty) {
fcx.write_bot(blk.id);
}
else {
let nilty = ty::mk_nil(); let nilty = ty::mk_nil();
demand::suptype(fcx, blk.span, nilty, blkty); demand::suptype(fcx, blk.span, nilty, blkty);
} }
@ -4631,14 +4622,13 @@ fn check_block_with_expected(fcx: &FnCtxt,
}; };
let mut warned = false; let mut warned = false;
let mut last_was_bot = false; let mut any_diverges = false;
let mut any_bot = false;
let mut any_err = false; let mut any_err = false;
for s in blk.stmts.iter() { for s in blk.stmts.iter() {
check_stmt(fcx, &**s); check_stmt(fcx, &**s);
let s_id = ast_util::stmt_id(&**s); let s_id = ast_util::stmt_id(&**s);
let s_ty = fcx.node_ty(s_id); let s_ty = fcx.node_ty(s_id);
if last_was_bot && !warned && match s.node { if any_diverges && !warned && match s.node {
ast::StmtDecl(ref decl, _) => { ast::StmtDecl(ref decl, _) => {
match decl.node { match decl.node {
ast::DeclLocal(_) => true, ast::DeclLocal(_) => true,
@ -4657,22 +4647,19 @@ fn check_block_with_expected(fcx: &FnCtxt,
"unreachable statement".to_string()); "unreachable statement".to_string());
warned = true; warned = true;
} }
if ty::type_is_bot(s_ty) { any_diverges = any_diverges || fcx.infcx().type_var_diverges(s_ty);
last_was_bot = true;
}
any_bot = any_bot || ty::type_is_bot(s_ty);
any_err = any_err || ty::type_is_error(s_ty); any_err = any_err || ty::type_is_error(s_ty);
} }
match blk.expr { match blk.expr {
None => if any_err { None => if any_err {
fcx.write_error(blk.id); fcx.write_error(blk.id);
} else if any_bot { } else if any_diverges {
fcx.write_bot(blk.id); fcx.write_ty(blk.id, fcx.infcx().next_diverging_ty_var());
} else { } else {
fcx.write_nil(blk.id); fcx.write_nil(blk.id);
}, },
Some(ref e) => { Some(ref e) => {
if any_bot && !warned { if any_diverges && !warned {
fcx.ccx fcx.ccx
.tcx .tcx
.sess .sess
@ -4692,11 +4679,12 @@ fn check_block_with_expected(fcx: &FnCtxt,
} }
}; };
fcx.write_ty(blk.id, ety);
if any_err { if any_err {
fcx.write_error(blk.id); fcx.write_error(blk.id);
} else if any_bot { } else if any_diverges {
fcx.write_bot(blk.id); fcx.write_ty(blk.id, fcx.infcx().next_diverging_ty_var());
} else {
fcx.write_ty(blk.id, ety);
} }
} }
}; };
@ -4718,7 +4706,7 @@ pub fn check_const_in_type(tcx: &ty::ctxt,
tcx: tcx, tcx: tcx,
}; };
let inh = static_inherited_fields(&ccx); let inh = static_inherited_fields(&ccx);
let fcx = blank_fn_ctxt(&ccx, &inh, expected_type, expr.id); let fcx = blank_fn_ctxt(&ccx, &inh, ty::FnConverging(expected_type), expr.id);
check_const_with_ty(&fcx, expr.span, expr, expected_type); check_const_with_ty(&fcx, expr.span, expr, expected_type);
} }
@ -4728,7 +4716,7 @@ pub fn check_const(ccx: &CrateCtxt,
id: ast::NodeId) { id: ast::NodeId) {
let inh = static_inherited_fields(ccx); let inh = static_inherited_fields(ccx);
let rty = ty::node_id_to_type(ccx.tcx, id); let rty = ty::node_id_to_type(ccx.tcx, id);
let fcx = blank_fn_ctxt(ccx, &inh, rty, e.id); let fcx = blank_fn_ctxt(ccx, &inh, ty::FnConverging(rty), e.id);
let declty = (*fcx.ccx.tcx.tcache.borrow())[local_def(id)].ty; let declty = (*fcx.ccx.tcx.tcache.borrow())[local_def(id)].ty;
check_const_with_ty(&fcx, sp, e, declty); check_const_with_ty(&fcx, sp, e, declty);
} }
@ -4892,7 +4880,7 @@ pub fn check_enum_variants(ccx: &CrateCtxt,
debug!("disr expr, checking {}", pprust::expr_to_string(&**e)); debug!("disr expr, checking {}", pprust::expr_to_string(&**e));
let inh = static_inherited_fields(ccx); let inh = static_inherited_fields(ccx);
let fcx = blank_fn_ctxt(ccx, &inh, rty, e.id); let fcx = blank_fn_ctxt(ccx, &inh, ty::FnConverging(rty), e.id);
let declty = match hint { let declty = match hint {
attr::ReprAny | attr::ReprPacked | attr::ReprExtern => ty::mk_int(), attr::ReprAny | attr::ReprPacked | attr::ReprExtern => ty::mk_int(),
attr::ReprInt(_, attr::SignedInt(ity)) => { attr::ReprInt(_, attr::SignedInt(ity)) => {
@ -5494,7 +5482,7 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) {
assert!(split.len() >= 2, "Atomic intrinsic not correct format"); assert!(split.len() >= 2, "Atomic intrinsic not correct format");
//We only care about the operation here //We only care about the operation here
match split[1] { let (n_tps, inputs, output) = match split[1] {
"cxchg" => (1, vec!(ty::mk_mut_ptr(tcx, param(ccx, 0)), "cxchg" => (1, vec!(ty::mk_mut_ptr(tcx, param(ccx, 0)),
param(ccx, 0), param(ccx, 0),
param(ccx, 0)), param(ccx, 0)),
@ -5517,12 +5505,12 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) {
"unrecognized atomic operation function: `{}`", op); "unrecognized atomic operation function: `{}`", op);
return; return;
} }
} };
(n_tps, inputs, ty::FnConverging(output))
} else if name.get() == "abort" || name.get() == "unreachable" {
(0, Vec::new(), ty::FnDiverging)
} else { } else {
match name.get() { let (n_tps, inputs, output) = match name.get() {
"abort" => (0, Vec::new(), ty::mk_bot()),
"unreachable" => (0, Vec::new(), ty::mk_bot()),
"breakpoint" => (0, Vec::new(), ty::mk_nil()), "breakpoint" => (0, Vec::new(), ty::mk_nil()),
"size_of" | "size_of" |
"pref_align_of" | "min_align_of" => (1u, Vec::new(), ty::mk_uint()), "pref_align_of" | "min_align_of" => (1u, Vec::new(), ty::mk_uint()),
@ -5730,7 +5718,8 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) {
"unrecognized intrinsic function: `{}`", *other); "unrecognized intrinsic function: `{}`", *other);
return; return;
} }
} };
(n_tps, inputs, ty::FnConverging(output))
}; };
let fty = ty::mk_bare_fn(tcx, ty::BareFnTy { let fty = ty::mk_bare_fn(tcx, ty::BareFnTy {
fn_style: ast::UnsafeFn, fn_style: ast::UnsafeFn,

View file

@ -334,7 +334,7 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> {
/// Try to resolve the type for the given node. /// Try to resolve the type for the given node.
pub fn resolve_expr_type_adjusted(&mut self, expr: &ast::Expr) -> ty::t { pub fn resolve_expr_type_adjusted(&mut self, expr: &ast::Expr) -> ty::t {
let ty_unadjusted = self.resolve_node_type(expr.id); let ty_unadjusted = self.resolve_node_type(expr.id);
if ty::type_is_error(ty_unadjusted) || ty::type_is_bot(ty_unadjusted) { if ty::type_is_error(ty_unadjusted) {
ty_unadjusted ty_unadjusted
} else { } else {
let tcx = self.fcx.tcx(); let tcx = self.fcx.tcx();
@ -690,7 +690,7 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) {
Some(method) => { Some(method) => {
constrain_call(rcx, expr, Some(&**base), constrain_call(rcx, expr, Some(&**base),
None::<ast::Expr>.iter(), true); None::<ast::Expr>.iter(), true);
ty::ty_fn_ret(method.ty) ty::ty_fn_ret(method.ty).unwrap()
} }
None => rcx.resolve_node_type(base.id) None => rcx.resolve_node_type(base.id)
}; };
@ -1217,9 +1217,14 @@ fn constrain_autoderefs(rcx: &mut Rcx,
// Specialized version of constrain_call. // Specialized version of constrain_call.
type_must_outlive(rcx, infer::CallRcvr(deref_expr.span), type_must_outlive(rcx, infer::CallRcvr(deref_expr.span),
self_ty, r_deref_expr); self_ty, r_deref_expr);
match fn_sig.output {
ty::FnConverging(return_type) => {
type_must_outlive(rcx, infer::CallReturn(deref_expr.span), type_must_outlive(rcx, infer::CallReturn(deref_expr.span),
fn_sig.output, r_deref_expr); return_type, r_deref_expr);
fn_sig.output return_type
}
ty::FnDiverging => unreachable!()
}
} }
None => derefd_ty None => derefd_ty
}; };
@ -1445,7 +1450,7 @@ fn link_region_from_node_type(rcx: &Rcx,
*/ */
let rptr_ty = rcx.resolve_node_type(id); let rptr_ty = rcx.resolve_node_type(id);
if !ty::type_is_bot(rptr_ty) && !ty::type_is_error(rptr_ty) { if !ty::type_is_error(rptr_ty) {
let tcx = rcx.fcx.ccx.tcx; let tcx = rcx.fcx.ccx.tcx;
debug!("rptr_ty={}", ty_to_string(tcx, rptr_ty)); debug!("rptr_ty={}", ty_to_string(tcx, rptr_ty));
let r = ty::ty_region(tcx, span, rptr_ty); let r = ty::ty_region(tcx, span, rptr_ty);

View file

@ -92,7 +92,6 @@ impl<'a, 'tcx> Wf<'a, 'tcx> {
match ty::get(ty).sty { match ty::get(ty).sty {
ty::ty_nil | ty::ty_nil |
ty::ty_bot |
ty::ty_bool | ty::ty_bool |
ty::ty_char | ty::ty_char |
ty::ty_int(..) | ty::ty_int(..) |

View file

@ -98,7 +98,7 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
&polytype.generics, &polytype.generics,
item.id); item.id);
let inh = Inherited::new(ccx.tcx, param_env); let inh = Inherited::new(ccx.tcx, param_env);
let fcx = blank_fn_ctxt(ccx, &inh, polytype.ty, item.id); let fcx = blank_fn_ctxt(ccx, &inh, ty::FnConverging(polytype.ty), item.id);
f(self, &fcx); f(self, &fcx);
vtable::select_all_fcx_obligations_or_error(&fcx); vtable::select_all_fcx_obligations_or_error(&fcx);
regionck::regionck_item(&fcx, item); regionck::regionck_item(&fcx, item);

View file

@ -23,7 +23,7 @@ use middle::subst::{Substs};
use middle::ty::get; use middle::ty::get;
use middle::ty::{ImplContainer, ImplOrTraitItemId, MethodTraitItemId}; use middle::ty::{ImplContainer, ImplOrTraitItemId, MethodTraitItemId};
use middle::ty::{TypeTraitItemId, lookup_item_type}; use middle::ty::{TypeTraitItemId, lookup_item_type};
use middle::ty::{t, ty_bool, ty_char, ty_bot, ty_enum, ty_err}; use middle::ty::{t, ty_bool, ty_char, ty_enum, ty_err};
use middle::ty::{ty_str, ty_vec, ty_float, ty_infer, ty_int, ty_nil, ty_open}; use middle::ty::{ty_str, ty_vec, ty_float, ty_infer, ty_int, ty_nil, ty_open};
use middle::ty::{ty_param, Polytype, ty_ptr}; use middle::ty::{ty_param, Polytype, ty_ptr};
use middle::ty::{ty_rptr, ty_struct, ty_trait, ty_tup}; use middle::ty::{ty_rptr, ty_struct, ty_trait, ty_tup};
@ -82,7 +82,7 @@ fn get_base_type(inference_context: &InferCtxt,
Some(resolved_type) Some(resolved_type)
} }
ty_nil | ty_bot | ty_bool | ty_char | ty_int(..) | ty_uint(..) | ty_float(..) | ty_nil | ty_bool | ty_char | ty_int(..) | ty_uint(..) | ty_float(..) |
ty_str(..) | ty_vec(..) | ty_bare_fn(..) | ty_closure(..) | ty_tup(..) | ty_str(..) | ty_vec(..) | ty_bare_fn(..) | ty_closure(..) | ty_tup(..) |
ty_infer(..) | ty_param(..) | ty_err | ty_open(..) | ty_uniq(_) | ty_infer(..) | ty_param(..) | ty_err | ty_open(..) | ty_uniq(_) |
ty_ptr(_) | ty_rptr(_, _) => { ty_ptr(_) | ty_rptr(_, _) => {

View file

@ -2236,7 +2236,10 @@ pub fn ty_of_foreign_fn_decl(ccx: &CrateCtxt,
.map(|a| ty_of_arg(ccx, &rb, a, None)) .map(|a| ty_of_arg(ccx, &rb, a, None))
.collect(); .collect();
let output_ty = ast_ty_to_ty(ccx, &rb, &*decl.output); let output = match decl.output.node {
ast::TyBot => ty::FnDiverging,
_ => ty::FnConverging(ast_ty_to_ty(ccx, &rb, &*decl.output))
};
let t_fn = ty::mk_bare_fn( let t_fn = ty::mk_bare_fn(
ccx.tcx, ccx.tcx,
@ -2245,7 +2248,7 @@ pub fn ty_of_foreign_fn_decl(ccx: &CrateCtxt,
fn_style: ast::UnsafeFn, fn_style: ast::UnsafeFn,
sig: ty::FnSig {binder_id: def_id.node, sig: ty::FnSig {binder_id: def_id.node,
inputs: input_tys, inputs: input_tys,
output: output_ty, output: output,
variadic: decl.variadic} variadic: decl.variadic}
}); });
let pty = Polytype { let pty = Polytype {

View file

@ -359,7 +359,18 @@ pub fn super_fn_sigs<'tcx, C: Combine<'tcx>>(this: &C,
let inputs = try!(argvecs(this, let inputs = try!(argvecs(this,
a.inputs.as_slice(), a.inputs.as_slice(),
b.inputs.as_slice())); b.inputs.as_slice()));
let output = try!(this.tys(a.output, b.output));
let output = try!(match (a.output, b.output) {
(ty::FnConverging(a_ty), ty::FnConverging(b_ty)) =>
Ok(ty::FnConverging(try!(this.tys(a_ty, b_ty)))),
(ty::FnDiverging, ty::FnDiverging) =>
Ok(ty::FnDiverging),
(a, b) =>
Err(ty::terr_convergence_mismatch(
expected_found(this, a != ty::FnDiverging, b != ty::FnDiverging)
)),
});
Ok(FnSig {binder_id: a.binder_id, Ok(FnSig {binder_id: a.binder_id,
inputs: inputs, inputs: inputs,
output: output, output: output,
@ -373,9 +384,7 @@ pub fn super_tys<'tcx, C: Combine<'tcx>>(this: &C, a: ty::t, b: ty::t) -> cres<t
let b_sty = &ty::get(b).sty; let b_sty = &ty::get(b).sty;
debug!("super_tys: a_sty={} b_sty={}", a_sty, b_sty); debug!("super_tys: a_sty={} b_sty={}", a_sty, b_sty);
return match (a_sty, b_sty) { return match (a_sty, b_sty) {
// The "subtype" ought to be handling cases involving bot or var: // The "subtype" ought to be handling cases involving var:
(&ty::ty_bot, _) |
(_, &ty::ty_bot) |
(&ty::ty_infer(TyVar(_)), _) | (&ty::ty_infer(TyVar(_)), _) |
(_, &ty::ty_infer(TyVar(_))) => { (_, &ty::ty_infer(TyVar(_))) => {
tcx.sess.bug( tcx.sess.bug(

View file

@ -112,15 +112,6 @@ impl<'f, 'tcx> Combine<'tcx> for Equate<'f, 'tcx> {
let a = infcx.type_variables.borrow().replace_if_possible(a); let a = infcx.type_variables.borrow().replace_if_possible(a);
let b = infcx.type_variables.borrow().replace_if_possible(b); let b = infcx.type_variables.borrow().replace_if_possible(b);
match (&ty::get(a).sty, &ty::get(b).sty) { match (&ty::get(a).sty, &ty::get(b).sty) {
(&ty::ty_bot, &ty::ty_bot) => {
Ok(a)
}
(&ty::ty_bot, _) |
(_, &ty::ty_bot) => {
Err(ty::terr_sorts(expected_found(self, a, b)))
}
(&ty::ty_infer(TyVar(a_id)), &ty::ty_infer(TyVar(b_id))) => { (&ty::ty_infer(TyVar(a_id)), &ty::ty_infer(TyVar(b_id))) => {
infcx.type_variables.borrow_mut().relate_vars(a_id, EqTo, b_id); infcx.type_variables.borrow_mut().relate_vars(a_id, EqTo, b_id);
Ok(a) Ok(a)

View file

@ -42,20 +42,12 @@ use util::ppaux::Repr;
use std::collections::HashMap; use std::collections::HashMap;
pub trait LatticeDir { pub trait LatticeDir {
// Relates the bottom type to `t` and returns LUB(t, _|_) or
// GLB(t, _|_) as appropriate.
fn ty_bot(&self, t: ty::t) -> cres<ty::t>;
// Relates the type `v` to `a` and `b` such that `v` represents // Relates the type `v` to `a` and `b` such that `v` represents
// the LUB/GLB of `a` and `b` as appropriate. // the LUB/GLB of `a` and `b` as appropriate.
fn relate_bound<'a>(&'a self, v: ty::t, a: ty::t, b: ty::t) -> cres<()>; fn relate_bound<'a>(&'a self, v: ty::t, a: ty::t, b: ty::t) -> cres<()>;
} }
impl<'a, 'tcx> LatticeDir for Lub<'a, 'tcx> { impl<'a, 'tcx> LatticeDir for Lub<'a, 'tcx> {
fn ty_bot(&self, t: ty::t) -> cres<ty::t> {
Ok(t)
}
fn relate_bound<'a>(&'a self, v: ty::t, a: ty::t, b: ty::t) -> cres<()> { fn relate_bound<'a>(&'a self, v: ty::t, a: ty::t, b: ty::t) -> cres<()> {
let sub = self.sub(); let sub = self.sub();
try!(sub.tys(a, v)); try!(sub.tys(a, v));
@ -65,10 +57,6 @@ impl<'a, 'tcx> LatticeDir for Lub<'a, 'tcx> {
} }
impl<'a, 'tcx> LatticeDir for Glb<'a, 'tcx> { impl<'a, 'tcx> LatticeDir for Glb<'a, 'tcx> {
fn ty_bot(&self, _: ty::t) -> cres<ty::t> {
Ok(ty::mk_bot())
}
fn relate_bound<'a>(&'a self, v: ty::t, a: ty::t, b: ty::t) -> cres<()> { fn relate_bound<'a>(&'a self, v: ty::t, a: ty::t, b: ty::t) -> cres<()> {
let sub = self.sub(); let sub = self.sub();
try!(sub.tys(v, a)); try!(sub.tys(v, a));
@ -95,8 +83,12 @@ pub fn super_lattice_tys<'tcx, L:LatticeDir+Combine<'tcx>>(this: &L,
let a = infcx.type_variables.borrow().replace_if_possible(a); let a = infcx.type_variables.borrow().replace_if_possible(a);
let b = infcx.type_variables.borrow().replace_if_possible(b); let b = infcx.type_variables.borrow().replace_if_possible(b);
match (&ty::get(a).sty, &ty::get(b).sty) { match (&ty::get(a).sty, &ty::get(b).sty) {
(&ty::ty_bot, _) => { this.ty_bot(b) } (&ty::ty_infer(TyVar(..)), &ty::ty_infer(TyVar(..)))
(_, &ty::ty_bot) => { this.ty_bot(a) } if infcx.type_var_diverges(a) && infcx.type_var_diverges(b) => {
let v = infcx.next_diverging_ty_var();
try!(this.relate_bound(v, a, b));
Ok(v)
}
(&ty::ty_infer(TyVar(..)), _) | (&ty::ty_infer(TyVar(..)), _) |
(_, &ty::ty_infer(TyVar(..))) => { (_, &ty::ty_infer(TyVar(..))) => {

View file

@ -510,6 +510,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
t.fold_with(&mut self.skolemizer()) t.fold_with(&mut self.skolemizer())
} }
pub fn type_var_diverges(&'a self, ty: ty::t) -> bool {
match ty::get(ty).sty {
ty::ty_infer(ty::TyVar(vid)) => self.type_variables.borrow().var_diverges(vid),
_ => false
}
}
pub fn skolemizer<'a>(&'a self) -> TypeSkolemizer<'a, 'tcx> { pub fn skolemizer<'a>(&'a self) -> TypeSkolemizer<'a, 'tcx> {
skolemize::TypeSkolemizer::new(self) skolemize::TypeSkolemizer::new(self)
} }
@ -684,14 +691,18 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
} }
impl<'a, 'tcx> InferCtxt<'a, 'tcx> { impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
pub fn next_ty_var_id(&self) -> TyVid { pub fn next_ty_var_id(&self, diverging: bool) -> TyVid {
self.type_variables self.type_variables
.borrow_mut() .borrow_mut()
.new_var() .new_var(diverging)
} }
pub fn next_ty_var(&self) -> ty::t { pub fn next_ty_var(&self) -> ty::t {
ty::mk_var(self.tcx, self.next_ty_var_id()) ty::mk_var(self.tcx, self.next_ty_var_id(false))
}
pub fn next_diverging_ty_var(&self) -> ty::t {
ty::mk_var(self.tcx, self.next_ty_var_id(true))
} }
pub fn next_ty_vars(&self, n: uint) -> Vec<ty::t> { pub fn next_ty_vars(&self, n: uint) -> Vec<ty::t> {

View file

@ -205,7 +205,8 @@ impl<'a, 'tcx> ResolveState<'a, 'tcx> {
pub fn resolve_ty_var(&mut self, vid: TyVid) -> ty::t { pub fn resolve_ty_var(&mut self, vid: TyVid) -> ty::t {
let tcx = self.infcx.tcx; let tcx = self.infcx.tcx;
let t1 = match self.infcx.type_variables.borrow().probe(vid) { let tv = self.infcx.type_variables.borrow();
match tv.probe(vid) {
Some(t) => { Some(t) => {
self.resolve_type(t) self.resolve_type(t)
} }
@ -215,8 +216,7 @@ impl<'a, 'tcx> ResolveState<'a, 'tcx> {
} }
ty::mk_var(tcx, vid) ty::mk_var(tcx, vid)
} }
}; }
return t1;
} }
pub fn resolve_int_var(&mut self, vid: IntVid) -> ty::t { pub fn resolve_int_var(&mut self, vid: IntVid) -> ty::t {

View file

@ -149,7 +149,6 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeSkolemizer<'a, 'tcx> {
} }
ty::ty_nil | ty::ty_nil |
ty::ty_bot |
ty::ty_bool | ty::ty_bool |
ty::ty_char | ty::ty_char |
ty::ty_int(..) | ty::ty_int(..) |

View file

@ -129,10 +129,6 @@ impl<'f, 'tcx> Combine<'tcx> for Sub<'f, 'tcx> {
let a = infcx.type_variables.borrow().replace_if_possible(a); let a = infcx.type_variables.borrow().replace_if_possible(a);
let b = infcx.type_variables.borrow().replace_if_possible(b); let b = infcx.type_variables.borrow().replace_if_possible(b);
match (&ty::get(a).sty, &ty::get(b).sty) { match (&ty::get(a).sty, &ty::get(b).sty) {
(&ty::ty_bot, _) => {
Ok(a)
}
(&ty::ty_infer(TyVar(a_id)), &ty::ty_infer(TyVar(b_id))) => { (&ty::ty_infer(TyVar(a_id)), &ty::ty_infer(TyVar(b_id))) => {
infcx.type_variables infcx.type_variables
.borrow_mut() .borrow_mut()
@ -154,10 +150,6 @@ impl<'f, 'tcx> Combine<'tcx> for Sub<'f, 'tcx> {
Ok(ty::mk_err()) Ok(ty::mk_err())
} }
(_, &ty::ty_bot) => {
Err(ty::terr_sorts(expected_found(self, a, b)))
}
_ => { _ => {
super_tys(self, a, b) super_tys(self, a, b)
} }

View file

@ -17,7 +17,8 @@ pub struct TypeVariableTable {
} }
struct TypeVariableData { struct TypeVariableData {
value: TypeVariableValue value: TypeVariableValue,
diverging: bool
} }
enum TypeVariableValue { enum TypeVariableValue {
@ -63,6 +64,10 @@ impl TypeVariableTable {
relations(self.values.get_mut(a.index)) relations(self.values.get_mut(a.index))
} }
pub fn var_diverges<'a>(&'a self, vid: ty::TyVid) -> bool {
self.values.get(vid.index).diverging
}
pub fn relate_vars(&mut self, a: ty::TyVid, dir: RelationDir, b: ty::TyVid) { pub fn relate_vars(&mut self, a: ty::TyVid, dir: RelationDir, b: ty::TyVid) {
/*! /*!
* Records that `a <: b`, `a :> b`, or `a == b`, depending on `dir`. * Records that `a <: b`, `a :> b`, or `a == b`, depending on `dir`.
@ -108,10 +113,11 @@ impl TypeVariableTable {
self.values.record(SpecifyVar(vid, relations)); self.values.record(SpecifyVar(vid, relations));
} }
pub fn new_var(&mut self) -> ty::TyVid { pub fn new_var(&mut self, diverging: bool) -> ty::TyVid {
let index = let index = self.values.push(TypeVariableData {
self.values.push( value: Bounded(vec![]),
TypeVariableData { value: Bounded(Vec::new()) }); diverging: diverging
});
ty::TyVid { index: index } ty::TyVid { index: index }
} }

View file

@ -381,7 +381,7 @@ fn check_main_fn_ty(ccx: &CrateCtxt,
sig: ty::FnSig { sig: ty::FnSig {
binder_id: main_id, binder_id: main_id,
inputs: Vec::new(), inputs: Vec::new(),
output: ty::mk_nil(), output: ty::FnConverging(ty::mk_nil()),
variadic: false variadic: false
} }
}); });
@ -433,7 +433,7 @@ fn check_start_fn_ty(ccx: &CrateCtxt,
ty::mk_int(), ty::mk_int(),
ty::mk_imm_ptr(tcx, ty::mk_imm_ptr(tcx, ty::mk_u8())) ty::mk_imm_ptr(tcx, ty::mk_imm_ptr(tcx, ty::mk_u8()))
), ),
output: ty::mk_int(), output: ty::FnConverging(ty::mk_int()),
variadic: false variadic: false
} }
}); });

View file

@ -728,7 +728,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
debug!("add_constraints_from_ty(ty={})", ty.repr(self.tcx())); debug!("add_constraints_from_ty(ty={})", ty.repr(self.tcx()));
match ty::get(ty).sty { match ty::get(ty).sty {
ty::ty_nil | ty::ty_bot | ty::ty_bool | ty::ty_nil | ty::ty_bool |
ty::ty_char | ty::ty_int(_) | ty::ty_uint(_) | ty::ty_char | ty::ty_int(_) | ty::ty_uint(_) |
ty::ty_float(_) | ty::ty_str => { ty::ty_float(_) | ty::ty_str => {
/* leaf type -- noop */ /* leaf type -- noop */
@ -882,7 +882,9 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
for &input in sig.inputs.iter() { for &input in sig.inputs.iter() {
self.add_constraints_from_ty(input, contra); self.add_constraints_from_ty(input, contra);
} }
self.add_constraints_from_ty(sig.output, variance); if let ty::FnConverging(result_type) = sig.output {
self.add_constraints_from_ty(result_type, variance);
}
} }
/// Adds constraints appropriate for a region appearing in a /// Adds constraints appropriate for a region appearing in a

View file

@ -17,7 +17,7 @@ use middle::ty::{ReEarlyBound, BrFresh, ctxt};
use middle::ty::{ReFree, ReScope, ReInfer, ReStatic, Region, ReEmpty}; use middle::ty::{ReFree, ReScope, ReInfer, ReStatic, Region, ReEmpty};
use middle::ty::{ReSkolemized, ReVar, BrEnv}; use middle::ty::{ReSkolemized, ReVar, BrEnv};
use middle::ty::{mt, t, ParamTy}; use middle::ty::{mt, t, ParamTy};
use middle::ty::{ty_bool, ty_char, ty_bot, ty_struct, ty_enum}; use middle::ty::{ty_bool, ty_char, ty_struct, ty_enum};
use middle::ty::{ty_err, ty_str, ty_vec, ty_float, ty_bare_fn, ty_closure}; use middle::ty::{ty_err, ty_str, ty_vec, ty_float, ty_bare_fn, ty_closure};
use middle::ty::{ty_nil, ty_param, ty_ptr, ty_rptr, ty_tup, ty_open}; use middle::ty::{ty_nil, ty_param, ty_ptr, ty_rptr, ty_tup, ty_open};
use middle::ty::{ty_unboxed_closure}; use middle::ty::{ty_unboxed_closure};
@ -352,12 +352,15 @@ pub fn ty_to_string(cx: &ctxt, typ: t) -> String {
s.push_str(bounds); s.push_str(bounds);
} }
if ty::get(sig.output).sty != ty_nil { match sig.output {
ty::FnConverging(t) => {
if !ty::type_is_nil(t) {
s.push_str(" -> "); s.push_str(" -> ");
if ty::type_is_bot(sig.output) { s.push_str(ty_to_string(cx, t).as_slice());
s.push('!'); }
} else { }
s.push_str(ty_to_string(cx, sig.output).as_slice()); ty::FnDiverging => {
s.push_str(" -> !");
} }
} }
} }
@ -371,7 +374,6 @@ pub fn ty_to_string(cx: &ctxt, typ: t) -> String {
// pretty print the structural type representation: // pretty print the structural type representation:
return match ty::get(typ).sty { return match ty::get(typ).sty {
ty_nil => "()".to_string(), ty_nil => "()".to_string(),
ty_bot => "!".to_string(),
ty_bool => "bool".to_string(), ty_bool => "bool".to_string(),
ty_char => "char".to_string(), ty_char => "char".to_string(),
ty_int(t) => ast_util::int_ty_to_string(t, None).to_string(), ty_int(t) => ast_util::int_ty_to_string(t, None).to_string(),
@ -952,6 +954,19 @@ impl Repr for ty::FnSig {
} }
} }
impl Repr for ty::FnOutput {
fn repr(&self, tcx: &ctxt) -> String {
match *self {
ty::FnConverging(ty) => {
format!("FnConverging({0})", ty.repr(tcx))
}
ty::FnDiverging => {
"FnDiverging".to_string()
}
}
}
}
impl Repr for typeck::MethodCallee { impl Repr for typeck::MethodCallee {
fn repr(&self, tcx: &ctxt) -> String { fn repr(&self, tcx: &ctxt) -> String {
format!("MethodCallee {{origin: {}, ty: {}, {}}}", format!("MethodCallee {{origin: {}, ty: {}, {}}}",

View file

@ -880,6 +880,15 @@ impl Clean<FnDecl> for ast::FnDecl {
} }
} }
impl<'a> Clean<Type> for ty::FnOutput {
fn clean(&self, cx: &DocContext) -> Type {
match *self {
ty::FnConverging(ty) => ty.clean(cx),
ty::FnDiverging => Bottom
}
}
}
impl<'a> Clean<FnDecl> for (ast::DefId, &'a ty::FnSig) { impl<'a> Clean<FnDecl> for (ast::DefId, &'a ty::FnSig) {
fn clean(&self, cx: &DocContext) -> FnDecl { fn clean(&self, cx: &DocContext) -> FnDecl {
let (did, sig) = *self; let (did, sig) = *self;
@ -1258,7 +1267,6 @@ impl Clean<Type> for ast::Ty {
impl Clean<Type> for ty::t { impl Clean<Type> for ty::t {
fn clean(&self, cx: &DocContext) -> Type { fn clean(&self, cx: &DocContext) -> Type {
match ty::get(*self).sty { match ty::get(*self).sty {
ty::ty_bot => Bottom,
ty::ty_nil => Primitive(Unit), ty::ty_nil => Primitive(Unit),
ty::ty_bool => Primitive(Bool), ty::ty_bool => Primitive(Bool),
ty::ty_char => Primitive(Char), ty::ty_char => Primitive(Char),

View file

@ -9,6 +9,5 @@
// except according to those terms. // except according to those terms.
fn main() { fn main() {
return.is_failure return.is_failure //~ ERROR unconstrained type variable
//~^ ERROR attempted access of field `is_failure` on type `!`, but no field with that name was found
} }