diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 8486535d18876..68b3ca96de147 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -149,7 +149,7 @@ pub trait TyVisitor { fn visit_fn_input(&mut self, i: uint, mode: uint, inner: *const TyDesc) -> bool; fn visit_fn_output(&mut self, retstyle: uint, variadic: bool, - inner: *const TyDesc) -> bool; + converging: bool, inner: *const TyDesc) -> bool; fn visit_leave_fn(&mut self, purity: uint, proto: uint, n_inputs: uint, retstyle: uint) -> bool; diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 9db79074d1fd1..f405e9df51db0 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -145,5 +145,6 @@ register_diagnostics!( E0162, E0163, E0164, - E0165 + E0165, + E0166 ) diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 8c44adc55d288..99ca5770a8a2f 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -683,7 +683,7 @@ impl LintPass for UnusedResults { let t = ty::expr_ty(cx.tcx, expr); let mut warned = false; match ty::get(t).sty { - ty::ty_nil | ty::ty_bot | ty::ty_bool => return, + ty::ty_nil | ty::ty_bool => return, ty::ty_struct(did, _) | ty::ty_enum(did, _) => { if ast_util::is_local(did) { diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs index b5456e724d28b..60fb490e270b3 100644 --- a/src/librustc/metadata/tydecode.rs +++ b/src/librustc/metadata/tydecode.rs @@ -359,7 +359,6 @@ fn parse_trait_ref(st: &mut PState, conv: conv_did) -> ty::TraitRef { fn parse_ty(st: &mut PState, conv: conv_did) -> ty::t { match next(st) { 'n' => return ty::mk_nil(), - 'z' => return ty::mk_bot(), 'b' => return ty::mk_bool(), 'i' => return ty::mk_int(), 'u' => return ty::mk_uint(), @@ -590,10 +589,16 @@ fn parse_sig(st: &mut PState, conv: conv_did) -> ty::FnSig { 'N' => false, r => fail!(format!("bad variadic: {}", r)), }; - let ret_ty = parse_ty(st, |x,y| conv(x,y)); + let output = match peek(st) { + 'z' => { + st.pos += 1u; + ty::FnDiverging + } + _ => ty::FnConverging(parse_ty(st, |x,y| conv(x,y))) + }; ty::FnSig {binder_id: id, inputs: inputs, - output: ret_ty, + output: output, variadic: variadic} } diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs index 6c59537b37772..5fb1fec53400c 100644 --- a/src/librustc/metadata/tyencode.rs +++ b/src/librustc/metadata/tyencode.rs @@ -200,7 +200,6 @@ pub fn enc_trait_store(w: &mut SeekableMemWriter, cx: &ctxt, s: ty::TraitStore) fn enc_sty(w: &mut SeekableMemWriter, cx: &ctxt, st: &ty::sty) { match *st { ty::ty_nil => mywrite!(w, "n"), - ty::ty_bot => mywrite!(w, "z"), ty::ty_bool => mywrite!(w, "b"), ty::ty_char => mywrite!(w, "c"), ty::ty_int(t) => { @@ -346,7 +345,14 @@ fn enc_fn_sig(w: &mut SeekableMemWriter, cx: &ctxt, fsig: &ty::FnSig) { } else { mywrite!(w, "N"); } - enc_ty(w, cx, fsig.output); + match fsig.output { + ty::FnConverging(result_type) => { + enc_ty(w, cx, result_type); + } + ty::FnDiverging => { + mywrite!(w, "z"); + } + } } pub fn enc_builtin_bounds(w: &mut SeekableMemWriter, _cx: &ctxt, bs: &ty::BuiltinBounds) { diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs index f63dafe861e10..146891825d684 100644 --- a/src/librustc/middle/cfg/construct.rs +++ b/src/librustc/middle/cfg/construct.rs @@ -511,12 +511,15 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { pred: CFGIndex, func_or_rcvr: &ast::Expr, args: I) -> CFGIndex { + let method_call = typeck::MethodCall::expr(call_expr.id); + let return_ty = ty::ty_fn_ret(match self.tcx.method_map.borrow().find(&method_call) { + Some(method) => method.ty, + None => ty::expr_ty(self.tcx, func_or_rcvr) + }); + let func_or_rcvr_exit = self.expr(func_or_rcvr, pred); let ret = self.straightline(call_expr, func_or_rcvr_exit, args); - - let return_ty = ty::node_id_to_type(self.tcx, call_expr.id); - let fails = ty::type_is_bot(return_ty); - if fails { + if return_ty == ty::FnDiverging { self.add_node(ast::DUMMY_NODE_ID, []) } else { ret diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index ee9dc05c0e761..e8a85b89b5847 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -396,13 +396,9 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,TYPER> { // make sure that the thing we are pointing out stays valid // for the lifetime `scope_r` of the resulting ptr: let expr_ty = ty::expr_ty(self.tcx(), expr); - if !ty::type_is_bot(expr_ty) { - let r = ty::ty_region(self.tcx(), expr.span, expr_ty); - let bk = ty::BorrowKind::from_mutbl(m); - self.borrow_expr(&**base, r, bk, AddrOf); - } else { - self.walk_expr(&**base); - } + let r = ty::ty_region(self.tcx(), expr.span, expr_ty); + let bk = ty::BorrowKind::from_mutbl(m); + self.borrow_expr(&**base, r, bk, AddrOf); } ast::ExprInlineAsm(ref ia) => { diff --git a/src/librustc/middle/intrinsicck.rs b/src/librustc/middle/intrinsicck.rs index d7707be58bb58..1dd823539b4c6 100644 --- a/src/librustc/middle/intrinsicck.rs +++ b/src/librustc/middle/intrinsicck.rs @@ -126,9 +126,10 @@ impl<'a, 'tcx, 'v> Visitor<'v> for IntrinsicCheckingVisitor<'a, 'tcx> { match ty::get(typ).sty { ty_bare_fn(ref bare_fn_ty) if bare_fn_ty.abi == RustIntrinsic => { - let from = bare_fn_ty.sig.inputs[0]; - let to = bare_fn_ty.sig.output; - self.check_transmute(expr.span, from, to, expr.id); + if let ty::FnConverging(to) = bare_fn_ty.sig.output { + let from = bare_fn_ty.sig.inputs[0]; + self.check_transmute(expr.span, from, to, expr.id); + } } _ => { self.tcx diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 63e9a80adc61a..4e2ee03877c1e 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -100,11 +100,18 @@ * - `no_ret_var`: a synthetic variable that is only 'read' from, the * fallthrough node. This allows us to detect functions where we fail * to return explicitly. + * - `clean_exit_var`: a synthetic variable that is only 'read' from the + * fallthrough node. It is only live if the function could converge + * via means other than an explicit `return` expression. That is, it is + * only dead if the end of the function's block can never be reached. + * It is the responsibility of typeck to ensure that there are no + * `return` expressions in a function declared as diverging. */ use middle::def::*; use middle::mem_categorization::Typer; use middle::pat_util; +use middle::typeck; use middle::ty; use lint; use util::nodemap::NodeMap; @@ -250,7 +257,8 @@ struct LocalInfo { enum VarKind { Arg(NodeId, Ident), Local(LocalInfo), - ImplicitRet + ImplicitRet, + CleanExit } struct IrMaps<'a, 'tcx: 'a> { @@ -306,7 +314,7 @@ impl<'a, 'tcx> IrMaps<'a, 'tcx> { Local(LocalInfo { id: node_id, .. }) | Arg(node_id, _) => { self.variable_map.insert(node_id, v); }, - ImplicitRet => {} + ImplicitRet | CleanExit => {} } debug!("{} is {}", v.to_string(), vk); @@ -331,7 +339,8 @@ impl<'a, 'tcx> IrMaps<'a, 'tcx> { Local(LocalInfo { ident: nm, .. }) | Arg(_, nm) => { token::get_ident(nm).get().to_string() }, - ImplicitRet => "".to_string() + ImplicitRet => "".to_string(), + CleanExit => "".to_string() } } @@ -397,7 +406,8 @@ fn visit_fn(ir: &mut IrMaps, let specials = Specials { exit_ln: fn_maps.add_live_node(ExitNode), fallthrough_ln: fn_maps.add_live_node(ExitNode), - no_ret_var: fn_maps.add_variable(ImplicitRet) + no_ret_var: fn_maps.add_variable(ImplicitRet), + clean_exit_var: fn_maps.add_variable(CleanExit) }; // compute liveness @@ -546,7 +556,8 @@ fn invalid_users() -> Users { struct Specials { exit_ln: LiveNode, fallthrough_ln: LiveNode, - no_ret_var: Variable + no_ret_var: Variable, + clean_exit_var: Variable } static ACC_READ: uint = 1u; @@ -873,6 +884,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { if blk.expr.is_none() { self.acc(s.fallthrough_ln, s.no_ret_var, ACC_READ) } + self.acc(s.fallthrough_ln, s.clean_exit_var, ACC_READ); self.propagate_through_block(blk, s.fallthrough_ln) } @@ -943,9 +955,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { opt_expr: Option<&Expr>, succ: LiveNode) -> LiveNode { - opt_expr.iter().fold(succ, |succ, expr| { - self.propagate_through_expr(&**expr, succ) - }) + opt_expr.map_or(succ, |expr| self.propagate_through_expr(expr, succ)) } fn propagate_through_expr(&mut self, expr: &Expr, succ: LiveNode) @@ -1146,13 +1156,11 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } ExprCall(ref f, ref args) => { - // calling a fn with bot return type means that the fn - // will fail, and hence the successors can be ignored - let is_bot = !self.ir.tcx.is_method_call(expr.id) && { + let diverges = !self.ir.tcx.is_method_call(expr.id) && { let t_ret = ty::ty_fn_ret(ty::expr_ty(self.ir.tcx, &**f)); - ty::type_is_bot(t_ret) + t_ret == ty::FnDiverging }; - let succ = if is_bot { + let succ = if diverges { self.s.exit_ln } else { succ @@ -1162,11 +1170,14 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } ExprMethodCall(_, _, ref args) => { - // calling a method with bot return type means that the method - // will fail, and hence the successors can be ignored - let t_ret = ty::node_id_to_type(self.ir.tcx, expr.id); - let succ = if ty::type_is_bot(t_ret) {self.s.exit_ln} - else {succ}; + let method_call = typeck::MethodCall::expr(expr.id); + let method_ty = self.ir.tcx.method_map.borrow().find(&method_call).unwrap().ty; + let diverges = ty::ty_fn_ret(method_ty) == ty::FnDiverging; + let succ = if diverges { + self.s.exit_ln + } else { + succ + }; self.propagate_through_exprs(args.as_slice(), succ) } @@ -1507,50 +1518,68 @@ fn check_fn(_v: &Liveness, } impl<'a, 'tcx> Liveness<'a, 'tcx> { + fn fn_ret(&self, id: NodeId) -> ty::FnOutput { + let fn_ty = ty::node_id_to_type(self.ir.tcx, id); + match ty::get(fn_ty).sty { + ty::ty_unboxed_closure(closure_def_id, _, _) => + self.ir.tcx.unboxed_closures() + .borrow() + .find(&closure_def_id) + .unwrap() + .closure_type + .sig + .output, + _ => ty::ty_fn_ret(fn_ty) + } + } + fn check_ret(&self, id: NodeId, sp: Span, _fk: FnKind, entry_ln: LiveNode, body: &Block) { - if self.live_on_entry(entry_ln, self.s.no_ret_var).is_some() { - // if no_ret_var is live, then we fall off the end of the - // function without any kind of return expression: - - let t_ret = ty::ty_fn_ret(ty::node_id_to_type(self.ir.tcx, id)); - if ty::type_is_nil(t_ret) { - // for nil return types, it is ok to not return a value expl. - } else if ty::type_is_bot(t_ret) { - // for bot return types, not ok. Function should fail. - self.ir.tcx.sess.span_err( - sp, "some control paths may return"); - } else { - let ends_with_stmt = match body.expr { - None if body.stmts.len() > 0 => - match body.stmts.last().unwrap().node { - StmtSemi(ref e, _) => { - let t_stmt = ty::expr_ty(self.ir.tcx, &**e); - ty::get(t_stmt).sty == ty::get(t_ret).sty + match self.fn_ret(id) { + ty::FnConverging(t_ret) + if self.live_on_entry(entry_ln, self.s.no_ret_var).is_some() => { + + if ty::type_is_nil(t_ret) { + // for nil return types, it is ok to not return a value expl. + } else { + let ends_with_stmt = match body.expr { + None if body.stmts.len() > 0 => + match body.stmts.last().unwrap().node { + StmtSemi(ref e, _) => { + let t_stmt = ty::expr_ty(self.ir.tcx, &**e); + ty::get(t_stmt).sty == ty::get(t_ret).sty + }, + _ => false }, - _ => false - }, - _ => false - }; - self.ir.tcx.sess.span_err( - sp, "not all control paths return a value"); - if ends_with_stmt { - let last_stmt = body.stmts.last().unwrap(); - let original_span = original_sp(self.ir.tcx.sess.codemap(), - last_stmt.span, sp); - let span_semicolon = Span { - lo: original_span.hi - BytePos(1), - hi: original_span.hi, - expn_id: original_span.expn_id + _ => false }; - self.ir.tcx.sess.span_note( - span_semicolon, "consider removing this semicolon:"); + self.ir.tcx.sess.span_err( + sp, "not all control paths return a value"); + if ends_with_stmt { + let last_stmt = body.stmts.last().unwrap(); + let original_span = original_sp(self.ir.tcx.sess.codemap(), + last_stmt.span, sp); + let span_semicolon = Span { + lo: original_span.hi - BytePos(1), + hi: original_span.hi, + expn_id: original_span.expn_id + }; + self.ir.tcx.sess.span_note( + span_semicolon, "consider removing this semicolon:"); + } } - } + } + ty::FnDiverging + if self.live_on_entry(entry_ln, self.s.clean_exit_var).is_some() => { + self.ir.tcx.sess.span_err(sp, + "computation may converge in a function marked as diverging"); + } + + _ => {} } } diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index abdbd80ebf2e3..c0188cac259ce 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -485,7 +485,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { Some(method_ty) => { // If this is an index implemented by a method call, then it will // include an implicit deref of the result. - let ret_ty = ty::ty_fn_ret(method_ty); + let ret_ty = ty::ty_fn_ret(method_ty).unwrap(); Ok(self.cat_deref(expr, self.cat_rvalue_node(expr.id(), expr.span(), @@ -878,7 +878,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { let base_cmt = match method_ty { Some(method_ty) => { - let ref_ty = ty::ty_fn_ret(method_ty); + let ref_ty = ty::ty_fn_ret(method_ty).unwrap(); self.cat_rvalue_node(node.id(), node.span(), ref_ty) } None => base_cmt @@ -957,7 +957,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { let element_ty = match method_ty { Some(method_ty) => { - let ref_ty = ty::ty_fn_ret(method_ty); + let ref_ty = ty::ty_fn_ret(method_ty).unwrap(); base_cmt = self.cat_rvalue_node(elt.id(), elt.span(), ref_ty); ty::ty_fn_args(method_ty)[0] } diff --git a/src/librustc/middle/traits/coherence.rs b/src/librustc/middle/traits/coherence.rs index e045b9fd4f4a3..8b2ddca313197 100644 --- a/src/librustc/middle/traits/coherence.rs +++ b/src/librustc/middle/traits/coherence.rs @@ -80,7 +80,6 @@ pub fn ty_is_local(tcx: &ty::ctxt, match ty::get(ty).sty { ty::ty_nil | - ty::ty_bot | ty::ty_bool | ty::ty_char | ty::ty_int(..) | diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index 2f345e2e6196c..f8c1c37452b38 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -1193,7 +1193,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::ty_uint(_) | ty::ty_int(_) | ty::ty_nil | - ty::ty_bot | ty::ty_bool | ty::ty_float(_) | ty::ty_bare_fn(_) | @@ -1681,7 +1680,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { def_id: obligation.trait_ref.def_id, substs: Substs::new_trait( vec![arguments_tuple.subst(self.tcx(), substs), - new_signature.output.subst(self.tcx(), substs)], + new_signature.output.unwrap().subst(self.tcx(), substs)], vec![], obligation.self_ty()) }); diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index e4f34d8ab47be..2379a5e7f53a2 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -1462,15 +1462,11 @@ pub fn store_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // General path. let init_datum = 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() { - add_comment(bcx, "creating zeroable ref llval"); - } - let var_scope = cleanup::var_scope(tcx, local.id); - bind_irrefutable_pat(bcx, pat, init_datum.val, var_scope) + if bcx.sess().asm_comments() { + add_comment(bcx, "creating zeroable ref llval"); } + let var_scope = cleanup::var_scope(tcx, local.id); + bind_irrefutable_pat(bcx, pat, init_datum.val, var_scope) } None => { create_dummy_locals(bcx, pat) diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index a739f6e3db279..16ff2f901f6d5 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -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 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| { unsafe { @@ -188,12 +188,9 @@ pub fn decl_fn(ccx: &CrateContext, name: &str, cc: llvm::CallConv, } }); - match ty::get(output).sty { - // functions returning bottom may unwind, but can never return normally - ty::ty_bot => { - llvm::SetFunctionAttribute(llfn, llvm::NoReturnAttribute) - } - _ => {} + // diverging functions may unwind, but can never return normally + if output == ty::FnDiverging { + llvm::SetFunctionAttribute(llfn, llvm::NoReturnAttribute); } if ccx.tcx().sess.opts.cg.no_redzone { @@ -216,7 +213,7 @@ pub fn decl_cdecl_fn(ccx: &CrateContext, name: &str, ty: Type, 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 @@ -231,7 +228,7 @@ pub fn get_extern_fn(ccx: &CrateContext, Some(n) => return *n, 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); f } @@ -1417,7 +1414,7 @@ pub fn new_fn_ctxt<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, llfndecl: ValueRef, id: ast::NodeId, has_env: bool, - output_type: ty::t, + output_type: ty::FnOutput, param_substs: &'a param_substs, sp: Option, block_arena: &'a TypedArena>) @@ -1432,8 +1429,13 @@ pub fn new_fn_ctxt<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, }, id, param_substs.repr(ccx.tcx())); - let substd_output_type = output_type.substp(ccx.tcx(), param_substs); - let uses_outptr = type_of::return_uses_outptr(ccx, substd_output_type); + let uses_outptr = match output_type { + ty::FnConverging(output_type) => { + let substd_output_type = output_type.substp(ccx.tcx(), param_substs); + 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 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. pub fn init_function<'a, 'tcx>(fcx: &'a FunctionContext<'a, 'tcx>, 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"); // Use a dummy instruction as the insertion point for all allocas. @@ -1478,18 +1480,19 @@ pub fn init_function<'a, 'tcx>(fcx: &'a FunctionContext<'a, 'tcx>, llvm::LLVMGetFirstInstruction(entry_bcx.llbb) })); - // This shouldn't need to recompute the return type, - // as new_fn_ctxt did it already. - let substd_output_type = output_type.substp(fcx.ccx.tcx(), fcx.param_substs); - - if !return_type_is_void(fcx.ccx, substd_output_type) { - // If the function returns nil/bot, there is no real return - // value, so do not set `llretslotptr`. - if !skip_retptr || fcx.caller_expects_out_pointer { - // Otherwise, we normally allocate the llretslotptr, unless we - // have been instructed to skip it for immediate return - // values. - fcx.llretslotptr.set(Some(make_return_slot_pointer(fcx, substd_output_type))); + if let ty::FnConverging(output_type) = output { + // This shouldn't need to recompute the return type, + // as new_fn_ctxt did it already. + let substd_output_type = output_type.substp(fcx.ccx.tcx(), fcx.param_substs); + if !return_type_is_void(fcx.ccx, substd_output_type) { + // If the function returns nil/bot, there is no real return + // value, so do not set `llretslotptr`. + if !skip_retptr || fcx.caller_expects_out_pointer { + // Otherwise, we normally allocate the llretslotptr, unless we + // have been instructed to skip it for immediate return + // values. + fcx.llretslotptr.set(Some(make_return_slot_pointer(fcx, substd_output_type))); + } } } @@ -1693,13 +1696,9 @@ fn copy_unboxed_closure_args_to_allocas<'blk, 'tcx>( // and builds the return block. pub fn finish_fn<'blk, 'tcx>(fcx: &'blk FunctionContext<'blk, 'tcx>, last_bcx: Block<'blk, 'tcx>, - retty: ty::t) { + retty: ty::FnOutput) { 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() { Some(llreturn) => { if !last_bcx.terminated.get() { @@ -1709,13 +1708,18 @@ pub fn finish_fn<'blk, 'tcx>(fcx: &'blk FunctionContext<'blk, 'tcx>, } 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); + debuginfo::clear_source_location(fcx); fcx.cleanup(); } // 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() || (!fcx.needs_ret_allocas && fcx.caller_expects_out_pointer) { 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(); } - 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)) } else { retval }; if fcx.caller_expects_out_pointer { - store_ty(ret_cx, retval, get_param(fcx.llfn, 0), retty); - return RetVoid(ret_cx); + if let ty::FnConverging(retty) = retty { + store_ty(ret_cx, retval, get_param(fcx.llfn, 0), retty); + } + RetVoid(ret_cx) } else { - return Ret(ret_cx, retval); + Ret(ret_cx, retval) } } // Otherwise, copy the return value to the ret slot - None => { - if fcx.caller_expects_out_pointer { - memcpy_ty(ret_cx, get_param(fcx.llfn, 0), retslot, retty); - return RetVoid(ret_cx); - } else { - return Ret(ret_cx, load_ty(ret_cx, retslot, retty)); + None => match retty { + ty::FnConverging(retty) => { + if fcx.caller_expects_out_pointer { + memcpy_ty(ret_cx, get_param(fcx.llfn, 0), retslot, retty); + RetVoid(ret_cx) + } else { + 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, _attributes: &[ast::Attribute], arg_types: Vec, - output_type: ty::t, + output_type: ty::FnOutput, abi: Abi, has_env: bool, is_unboxed_closure: IsUnboxedClosureFlag, @@ -1860,7 +1875,7 @@ pub fn trans_closure(ccx: &CrateContext, debuginfo::start_emitting_source_locations(&fcx); 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 => { assert!(type_is_zero_size(bcx.ccx(), block_ty)); expr::Ignore @@ -1965,7 +1980,7 @@ pub fn trans_named_tuple_constructor<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, let tcx = ccx.tcx(); 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( format!("trans_enum_variant_constructor: \ 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()); - 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 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() { let lldestptr = adt::trans_field_ptr(bcx, &*repr, @@ -2393,53 +2408,55 @@ pub fn get_fn_llvm_attributes(ccx: &CrateContext, fn_ty: ty::t) _ => fn_sig.inputs.clone() }; - // A function pointer is called without the declaration - // available, so we have to apply any attributes with ABI - // implications directly to the call instruction. Right now, - // the only attribute we need to worry about is `sret`. - if type_of::return_uses_outptr(ccx, ret_ty) { - let llret_sz = llsize_of_real(ccx, type_of::type_of(ccx, ret_ty)); - - // The outptr can be noalias and nocapture because it's entirely - // invisible to the program. We also know it's nonnull as well - // as how many bytes we can dereference - attrs.arg(1, llvm::StructRetAttribute) - .arg(1, llvm::NoAliasAttribute) - .arg(1, llvm::NoCaptureAttribute) - .arg(1, llvm::DereferenceableAttribute(llret_sz)); - - // Add one more since there's an outptr - first_arg_offset += 1; - } else { - // The `noalias` attribute on the return value is useful to a - // function ptr caller. - match ty::get(ret_ty).sty { - // `~` pointer return values never alias because ownership - // is transferred - ty::ty_uniq(it) if !ty::type_is_sized(ccx.tcx(), it) => {} - ty::ty_uniq(_) => { - attrs.ret(llvm::NoAliasAttribute); + if let ty::FnConverging(ret_ty) = ret_ty { + // A function pointer is called without the declaration + // available, so we have to apply any attributes with ABI + // implications directly to the call instruction. Right now, + // the only attribute we need to worry about is `sret`. + if type_of::return_uses_outptr(ccx, ret_ty) { + let llret_sz = llsize_of_real(ccx, type_of::type_of(ccx, ret_ty)); + + // The outptr can be noalias and nocapture because it's entirely + // invisible to the program. We also know it's nonnull as well + // as how many bytes we can dereference + attrs.arg(1, llvm::StructRetAttribute) + .arg(1, llvm::NoAliasAttribute) + .arg(1, llvm::NoCaptureAttribute) + .arg(1, llvm::DereferenceableAttribute(llret_sz)); + + // Add one more since there's an outptr + first_arg_offset += 1; + } else { + // The `noalias` attribute on the return value is useful to a + // function ptr caller. + match ty::get(ret_ty).sty { + // `~` pointer return values never alias because ownership + // is transferred + ty::ty_uniq(it) if !ty::type_is_sized(ccx.tcx(), it) => {} + ty::ty_uniq(_) => { + attrs.ret(llvm::NoAliasAttribute); + } + _ => {} } - _ => {} - } - // We can also mark the return value as `dereferenceable` in certain cases - match ty::get(ret_ty).sty { - // These are not really pointers but pairs, (pointer, len) - ty::ty_uniq(it) | - ty::ty_rptr(_, ty::mt { ty: it, .. }) if !ty::type_is_sized(ccx.tcx(), it) => {} - ty::ty_uniq(inner) | ty::ty_rptr(_, ty::mt { ty: inner, .. }) => { - let llret_sz = llsize_of_real(ccx, type_of::type_of(ccx, inner)); - attrs.ret(llvm::DereferenceableAttribute(llret_sz)); + // We can also mark the return value as `dereferenceable` in certain cases + match ty::get(ret_ty).sty { + // These are not really pointers but pairs, (pointer, len) + ty::ty_uniq(it) | + ty::ty_rptr(_, ty::mt { ty: it, .. }) if !ty::type_is_sized(ccx.tcx(), it) => {} + ty::ty_uniq(inner) | ty::ty_rptr(_, ty::mt { ty: inner, .. }) => { + let llret_sz = llsize_of_real(ccx, type_of::type_of(ccx, inner)); + attrs.ret(llvm::DereferenceableAttribute(llret_sz)); + } + _ => {} } - _ => {} - } - match ty::get(ret_ty).sty { - ty::ty_bool => { - attrs.ret(llvm::ZExtAttribute); + match ty::get(ret_ty).sty { + ty::ty_bool => { + attrs.ret(llvm::ZExtAttribute); + } + _ => {} } - _ => {} } } @@ -2523,7 +2540,7 @@ pub fn register_fn_llvmty(ccx: &CrateContext, llfty: Type) -> ValueRef { 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); llfn } diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs index b8895e9486a05..54f41b79b0130 100644 --- a/src/librustc/middle/trans/callee.rs +++ b/src/librustc/middle/trans/callee.rs @@ -375,10 +375,9 @@ pub fn trans_unboxing_shim(bcx: Block, llshimmedargs.push(get_param(fcx.llfn, fcx.arg_pos(i) as u32)); } assert!(!fcx.needs_ret_allocas); - let dest = match fcx.llretslotptr.get() { - Some(_) => Some(expr::SaveIn(fcx.get_ret_slot(bcx, return_type, "ret_slot"))), - None => None - }; + let dest = fcx.llretslotptr.get().map(|_| + expr::SaveIn(fcx.get_ret_slot(bcx, return_type, "ret_slot")) + ); bcx = trans_call_inner(bcx, None, 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 // not care about the result, just make a stack slot. - let opt_llretslot = match dest { - None => { - assert!(!type_of::return_uses_outptr(ccx, ret_ty)); - None - } - Some(expr::SaveIn(dst)) => Some(dst), - Some(expr::Ignore) if !is_rust_fn || - type_of::return_uses_outptr(ccx, ret_ty) || - ty::type_needs_drop(bcx.tcx(), ret_ty) => { - if !type_is_zero_size(ccx, ret_ty) { - Some(alloc_ty(bcx, ret_ty, "__llret")) + let opt_llretslot = dest.and_then(|dest| match dest { + expr::SaveIn(dst) => Some(dst), + expr::Ignore => { + let ret_ty = match ret_ty { + ty::FnConverging(ret_ty) => ret_ty, + ty::FnDiverging => ty::mk_nil() + }; + if !is_rust_fn || + type_of::return_uses_outptr(ccx, ret_ty) || + ty::type_needs_drop(bcx.tcx(), ret_ty) { + // Push the out-pointer if we use an out-pointer for this + // return type, otherwise push "undef". + if type_is_zero_size(ccx, ret_ty) { + let llty = type_of::type_of(ccx, ret_ty); + Some(C_undef(llty.ptr_to())) + } else { + Some(alloc_ty(bcx, ret_ty, "__llret")) + } } else { - let llty = type_of::type_of(ccx, ret_ty); - Some(C_undef(llty.ptr_to())) + None } } - Some(expr::Ignore) => None - }; + }); let mut llresult = unsafe { 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 { let mut llargs = Vec::new(); - // Push the out-pointer if we use an out-pointer for this - // return type, otherwise push "undef". - if type_of::return_uses_outptr(ccx, ret_ty) { - llargs.push(opt_llretslot.unwrap()); + if let (ty::FnConverging(ret_ty), Some(llretslot)) = (ret_ty, opt_llretslot) { + if type_of::return_uses_outptr(ccx, ret_ty) { + llargs.push(llretslot); + } } // Push the environment (or a trait object's self). match (llenv, llself) { - (Some(llenv), None) => { - llargs.push(llenv) - }, + (Some(llenv), None) => llargs.push(llenv), (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 // the return value, copy it into llretslot. - match opt_llretslot { - Some(llretslot) => { + match (opt_llretslot, ret_ty) { + (Some(llretslot), ty::FnConverging(ret_ty)) => { if !type_of::return_uses_outptr(bcx.ccx(), ret_ty) && !type_is_zero_size(bcx.ccx(), ret_ty) { store_ty(bcx, llret, llretslot, ret_ty) } } - None => {} + (_, _) => {} } } else { // 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, // drop the temporary slot we made. - match (dest, opt_llretslot) { - (Some(expr::Ignore), Some(llretslot)) => { + match (dest, opt_llretslot, ret_ty) { + (Some(expr::Ignore), Some(llretslot), ty::FnConverging(ret_ty)) => { // drop the value if it is not being saved. bcx = glue::drop_ty(bcx, llretslot, ret_ty, call_info); call_lifetime_end(bcx, llretslot); @@ -874,7 +876,7 @@ pub fn trans_call_inner<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, _ => {} } - if ty::type_is_bot(ret_ty) { + if ret_ty == ty::FnDiverging { Unreachable(bcx); } @@ -1118,52 +1120,41 @@ pub fn trans_arg_datum<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, debug!(" arg datum: {}", arg_datum.to_string(bcx.ccx())); 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()); + // FIXME(#3548) use the adjustments table + match autoref_arg { + DoAutorefArg(arg_id) => { + // We will pass argument by reference + // We want an lvalue, so that we can pass by reference and + let arg_datum = unpack_datum!( + bcx, arg_datum.to_lvalue_datum(bcx, "arg", arg_id)); + val = arg_datum.val; } - } else { - // FIXME(#3548) use the adjustments table - match autoref_arg { - DoAutorefArg(arg_id) => { - // We will pass argument by reference - // We want an lvalue, so that we can pass by reference and - let arg_datum = unpack_datum!( - bcx, arg_datum.to_lvalue_datum(bcx, "arg", arg_id)); - val = arg_datum.val; - } - DontAutorefArg => { - // Make this an rvalue, since we are going to be - // passing ownership. - let arg_datum = unpack_datum!( - bcx, arg_datum.to_rvalue_datum(bcx, "arg")); - - // Now that arg_datum is owned, get it into the appropriate - // mode (ref vs value). - let arg_datum = unpack_datum!( - bcx, arg_datum.to_appropriate_datum(bcx)); - - // Technically, ownership of val passes to the callee. - // However, we must cleanup should we fail before the - // callee is actually invoked. - val = arg_datum.add_clean(bcx.fcx, arg_cleanup_scope); - } + DontAutorefArg => { + // Make this an rvalue, since we are going to be + // passing ownership. + let arg_datum = unpack_datum!( + bcx, arg_datum.to_rvalue_datum(bcx, "arg")); + + // Now that arg_datum is owned, get it into the appropriate + // mode (ref vs value). + let arg_datum = unpack_datum!( + bcx, arg_datum.to_appropriate_datum(bcx)); + + // Technically, ownership of val passes to the callee. + // However, we must cleanup should we fail before the + // callee is actually invoked. + val = arg_datum.add_clean(bcx.fcx, arg_cleanup_scope); } + } - if formal_arg_ty != arg_datum_ty { - // this could happen due to e.g. subtyping - let llformal_arg_ty = type_of::type_of_explicit_arg(ccx, formal_arg_ty); - debug!("casting actual type ({}) to match formal ({})", - bcx.val_to_string(val), bcx.llty_str(llformal_arg_ty)); - debug!("Rust types: {}; {}", ty_to_string(bcx.tcx(), arg_datum_ty), - ty_to_string(bcx.tcx(), formal_arg_ty)); - val = PointerCast(bcx, val, llformal_arg_ty); - } + if formal_arg_ty != arg_datum_ty { + // this could happen due to e.g. subtyping + let llformal_arg_ty = type_of::type_of_explicit_arg(ccx, formal_arg_ty); + debug!("casting actual type ({}) to match formal ({})", + bcx.val_to_string(val), bcx.llty_str(llformal_arg_ty)); + debug!("Rust types: {}; {}", ty_to_string(bcx.tcx(), arg_datum_ty), + ty_to_string(bcx.tcx(), formal_arg_ty)); + val = PointerCast(bcx, val, llformal_arg_ty); } debug!("--- trans_arg_datum passing {}", bcx.val_to_string(val)); diff --git a/src/librustc/middle/trans/closure.rs b/src/librustc/middle/trans/closure.rs index d4e1473f0e5c2..b6f45b2ccda77 100644 --- a/src/librustc/middle/trans/closure.rs +++ b/src/librustc/middle/trans/closure.rs @@ -623,10 +623,17 @@ pub fn get_wrapper_for_bare_fn(ccx: &CrateContext, llargs.extend(args.iter().map(|arg| arg.val)); let retval = Call(bcx, fn_ptr, llargs.as_slice(), None); - if type_is_zero_size(ccx, f.sig.output) || fcx.llretslotptr.get().is_some() { - RetVoid(bcx); - } else { - Ret(bcx, retval); + match f.sig.output { + ty::FnConverging(output_type) => { + if type_is_zero_size(ccx, output_type) || fcx.llretslotptr.get().is_some() { + RetVoid(bcx); + } else { + Ret(bcx, retval); + } + } + ty::FnDiverging => { + RetVoid(bcx); + } } // HACK(eddyb) finish_fn cannot be used here, we returned directly. diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index 496838c9e84f2..5d386b0a706b5 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -74,7 +74,7 @@ pub fn type_is_immediate(ccx: &CrateContext, ty: ty::t) -> bool { let tcx = ccx.tcx(); let simple = ty::type_is_scalar(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); if simple && !ty::type_is_fat_ptr(tcx, ty) { return true; @@ -83,7 +83,6 @@ pub fn type_is_immediate(ccx: &CrateContext, ty: ty::t) -> bool { return false; } match ty::get(ty).sty { - ty::ty_bot => true, ty::ty_struct(..) | ty::ty_enum(..) | ty::ty_tup(..) | ty::ty_unboxed_closure(..) => { 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). */ - 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 @@ -217,7 +216,7 @@ pub trait SubstP { -> Self; } -impl SubstP for T { +impl SubstP for T { fn substp(&self, tcx: &ty::ctxt, substs: ¶m_substs) -> T { self.subst(tcx, &substs.substs) } @@ -343,9 +342,12 @@ impl<'a, 'tcx> FunctionContext<'a, 'tcx> { 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 { - 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 { self.llretslotptr.get().unwrap() } diff --git a/src/librustc/middle/trans/controlflow.rs b/src/librustc/middle/trans/controlflow.rs index 995943c301731..f7210bb4e08bb 100644 --- a/src/librustc/middle/trans/controlflow.rs +++ b/src/librustc/middle/trans/controlflow.rs @@ -296,7 +296,7 @@ pub fn trans_for<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, .borrow())[method_call] .ty; 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_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); - if ty::type_is_bot(node_id_type(bcx, loop_id)) { - Unreachable(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) { (Some(_), Some(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, }; diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs index ea6d9e1dd8c48..8ee258e77fa5d 100644 --- a/src/librustc/middle/trans/datum.rs +++ b/src/librustc/middle/trans/datum.rs @@ -650,7 +650,7 @@ impl Datum { } 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) } } diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs index 3368b2b7765b8..5e039a3c098c7 100644 --- a/src/librustc/middle/trans/debuginfo.rs +++ b/src/librustc/middle/trans/debuginfo.rs @@ -352,7 +352,6 @@ impl TypeMap { match ty::get(type_).sty { ty::ty_nil | - ty::ty_bot | ty::ty_bool | ty::ty_char | ty::ty_str | @@ -451,9 +450,16 @@ impl TypeMap { } unique_type_id.push_str(")->"); - let return_type_id = self.get_unique_type_id_of_type(cx, sig.output); - let return_type_id = self.get_unique_type_id_as_string(return_type_id); - unique_type_id.push_str(return_type_id.as_slice()); + 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); + unique_type_id.push_str(return_type_id.as_slice()); + } + ty::FnDiverging => { + unique_type_id.push_str("!"); + } + } }, ty::ty_closure(box ref closure_ty) => { self.get_unique_type_id_of_closure_type(cx, @@ -578,9 +584,16 @@ impl TypeMap { unique_type_id.push_str("|->"); - let return_type_id = self.get_unique_type_id_of_type(cx, sig.output); - let return_type_id = self.get_unique_type_id_as_string(return_type_id); - unique_type_id.push_str(return_type_id.as_slice()); + 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); + unique_type_id.push_str(return_type_id.as_slice()); + } + ty::FnDiverging => { + unique_type_id.push_str("!"); + } + } 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 { debug!("basic_type_metadata: {}", ty::get(t)); let (name, encoding) = match ty::get(t).sty { 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_char => ("char".to_string(), DW_ATE_unsigned_char), ty::ty_int(int_ty) => match int_ty { @@ -2748,9 +2773,12 @@ fn subroutine_type_metadata(cx: &CrateContext, let mut signature_metadata: Vec = Vec::with_capacity(signature.inputs.len() + 1); // return type - signature_metadata.push(match ty::get(signature.output).sty { - ty::ty_nil => ptr::null_mut(), - _ => type_metadata(cx, signature.output, span) + signature_metadata.push(match signature.output { + ty::FnConverging(ret_ty) => match ty::get(ret_ty).sty { + ty::ty_nil => ptr::null_mut(), + _ => type_metadata(cx, ret_ty, span) + }, + ty::FnDiverging => diverging_type_metadata(cx) }); // regular arguments @@ -2855,7 +2883,6 @@ fn type_metadata(cx: &CrateContext, let sty = &ty::get(t).sty; let MetadataCreationResult { metadata, already_stored_in_typemap } = match *sty { ty::ty_nil | - ty::ty_bot | ty::ty_bool | ty::ty_char | ty::ty_int(_) | @@ -3647,7 +3674,6 @@ fn push_debuginfo_type_name(cx: &CrateContext, output:&mut String) { match ty::get(t).sty { ty::ty_nil => output.push_str("()"), - ty::ty_bot => output.push_str("!"), ty::ty_bool => output.push_str("bool"), ty::ty_char => output.push_str("char"), ty::ty_str => output.push_str("str"), @@ -3749,9 +3775,15 @@ fn push_debuginfo_type_name(cx: &CrateContext, output.push(')'); - if !ty::type_is_nil(sig.output) { - output.push_str(" -> "); - push_debuginfo_type_name(cx, sig.output, true, output); + match sig.output { + ty::FnConverging(result_type) if ty::type_is_nil(result_type) => {} + ty::FnConverging(result_type) => { + output.push_str(" -> "); + push_debuginfo_type_name(cx, result_type, true, output); + } + ty::FnDiverging => { + output.push_str(" -> !"); + } } }, 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); - if !ty::type_is_nil(sig.output) { - output.push_str(" -> "); - push_debuginfo_type_name(cx, sig.output, true, output); + match sig.output { + ty::FnConverging(result_type) if ty::type_is_nil(result_type) => {} + ty::FnConverging(result_type) => { + output.push_str(" -> "); + push_debuginfo_type_name(cx, result_type, true, output); + } + ty::FnDiverging => { + output.push_str(" -> !"); + } } }, ty::ty_unboxed_closure(..) => { diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index 622da4840b047..7d64c42a00030 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -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))); 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"); unpack_result!(bcx, @@ -757,7 +757,7 @@ fn trans_index<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, base_datum, vec![(ix_datum, idx.id)], 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) { None => { 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 is_simd = ty::type_is_simd(tcx, lhs_t); let intype = { - if ty::type_is_bot(lhs_t) { rhs_t } - else if is_simd { ty::simd_type(tcx, lhs_t) } + if is_simd { ty::simd_type(tcx, lhs_t) } else { lhs_t } }; 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) } } ast::BiEq | ast::BiNe | ast::BiLt | ast::BiGe | ast::BiLe | ast::BiGt => { - if ty::type_is_bot(rhs_t) { - C_bool(bcx.ccx(), false) - } else if ty::type_is_scalar(rhs_t) { + if ty::type_is_scalar(rhs_t) { unpack_result!(bcx, base::compare_scalar_types(bcx, lhs, rhs, rhs_t, op)) } else if is_simd { 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 }; - 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"); unpack_result!(bcx, trans_overloaded_op(bcx, expr, method_call, diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs index 406ccc56a6238..aa195837b67d4 100644 --- a/src/librustc/middle/trans/foreign.rs +++ b/src/librustc/middle/trans/foreign.rs @@ -49,9 +49,6 @@ struct ForeignTypes { /// LLVM types that will appear on the foreign function llsig: LlvmSignature, - - /// True if there is a return value (not bottom, not unit) - ret_def: bool, } struct LlvmSignature { @@ -63,6 +60,9 @@ struct LlvmSignature { // function, because the foreign function may opt to return via an // out pointer. 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") }; 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, llsig.llarg_tys.as_slice(), llsig.llret_ty, - ret_def); + llsig.ret_def); 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 // the Rust type. e.g., a {u32,u32} struct could be returned as // 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 llforeign_ret_ty = match fn_type.ret_ty.cast { 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)); 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 { // The actual return type is a struct, but the ABI // 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") }; - 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); debug!("decl_rust_fn_with_foreign_abi(llfn_ty={}, 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. - let rust_uses_outptr = - type_of::return_uses_outptr(ccx, tys.fn_sig.output); + let rust_uses_outptr = match tys.fn_sig.output { + ty::FnConverging(ret_ty) => type_of::return_uses_outptr(ccx, ret_ty), + ty::FnDiverging => false + }; let return_alloca: Option; let llrust_ret_ty = tys.llsig.llret_ty; 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={}", ccx.tn().val_to_string(llforeign_outptr)); let llrust_retptr = - builder.bitcast(llforeign_outptr, llrust_ret_ty.ptr_to()); + builder.bitcast(llforeign_outptr, llrust_retptr_ty); debug!("out pointer, foreign={} (casted)", ccx.tn().val_to_string(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 }; match foreign_outptr { - None if !tys.ret_def => { + None if !tys.llsig.ret_def => { // Function returns `()` or `bot`, which in Rust is the LLVM // type "{}" but in foreign ABIs is "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 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 { 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") }; 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, llsig.llarg_tys.as_slice(), llsig.llret_ty, - ret_def); + llsig.ret_def); debug!("foreign_types_for_fn_ty(\ ty={}, \ llsig={} -> {}, \ @@ -930,12 +941,11 @@ fn foreign_types_for_fn_ty(ccx: &CrateContext, ccx.tn().type_to_string(llsig.llret_ty), ccx.tn().types_to_str(fn_ty.arg_tys.iter().map(|t| t.ty).collect::>().as_slice()), ccx.tn().type_to_string(fn_ty.ret_ty.ty), - ret_def); + llsig.ret_def); ForeignTypes { fn_sig: fn_sig, llsig: llsig, - ret_def: ret_def, fn_ty: fn_ty } } diff --git a/src/librustc/middle/trans/glue.rs b/src/librustc/middle/trans/glue.rs index 8fc3426e37299..e914dbcc082a6 100644 --- a/src/librustc/middle/trans/glue.rs +++ b/src/librustc/middle/trans/glue.rs @@ -538,10 +538,10 @@ fn make_generic_glue(ccx: &CrateContext, let arena = TypedArena::new(); 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); - 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); @@ -556,7 +556,7 @@ fn make_generic_glue(ccx: &CrateContext, let llrawptr0 = get_param(llfn, fcx.arg_pos(0) as c_uint); let bcx = helper(bcx, llrawptr0, t); - finish_fn(&fcx, bcx, ty::mk_nil()); + finish_fn(&fcx, bcx, ty::FnConverging(ty::mk_nil())); llfn } diff --git a/src/librustc/middle/trans/intrinsic.rs b/src/librustc/middle/trans/intrinsic.rs index 3e75b0772fb10..0ddd4894cfa28 100644 --- a/src/librustc/middle/trans/intrinsic.rs +++ b/src/librustc/middle/trans/intrinsic.rs @@ -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, _ => 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 name = token::get_ident(foreign_item.ident); // For `transmute` we can just trans the input expr directly into dest if name.get() == "transmute" { + let llret_ty = type_of::type_of(ccx, ret_ty.unwrap()); match args { callee::ArgExprs(arg_exprs) => { 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); + + // These are the only intrinsic functions that diverge. + 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 // not care about the result, just make a stack slot 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 llval = match (simple, name.get()) { (Some(llfn), _) => { 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") => { let llfn = ccx.get_intrinsic(&("llvm.debugtrap")); Call(bcx, llfn, [], None) diff --git a/src/librustc/middle/trans/type_of.rs b/src/librustc/middle/trans/type_of.rs index 6d7985767ab86..64ab86721bcc7 100644 --- a/src/librustc/middle/trans/type_of.rs +++ b/src/librustc/middle/trans/type_of.rs @@ -97,7 +97,7 @@ pub fn untuple_arguments_if_necessary(ccx: &CrateContext, pub fn type_of_rust_fn(cx: &CrateContext, llenvironment_type: Option, inputs: &[ty::t], - output: ty::t, + output: ty::FnOutput, abi: abi::Abi) -> Type { let mut atys: Vec = Vec::new(); @@ -107,11 +107,22 @@ pub fn type_of_rust_fn(cx: &CrateContext, // Arg 0: Output pointer. // (if the output type is non-immediate) - let use_out_pointer = return_uses_outptr(cx, output); - let lloutputtype = arg_type_of(cx, output); - if use_out_pointer { - atys.push(lloutputtype.ptr_to()); - } + let lloutputtype = match output { + ty::FnConverging(output) => { + let use_out_pointer = return_uses_outptr(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 { + 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 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)); 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 @@ -181,7 +187,7 @@ pub fn sizing_type_of(cx: &CrateContext, t: ty::t) -> Type { 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_char => Type::char(cx), 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 { - ty::ty_nil | ty::ty_bot => Type::nil(cx), + ty::ty_nil => Type::nil(cx), ty::ty_bool => Type::bool(cx), ty::ty_char => Type::char(cx), ty::ty_int(t) => Type::int_from_ty(cx, t), diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 7fedea67f79af..6666b85879a89 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -598,7 +598,6 @@ bitflags! { const HAS_RE_INFER = 0b1000, const HAS_REGIONS = 0b10000, const HAS_TY_ERR = 0b100000, - const HAS_TY_BOT = 0b1000000, const NEEDS_SUBST = HAS_PARAMS.bits | HAS_SELF.bits | HAS_REGIONS.bits, } } @@ -672,6 +671,21 @@ pub struct ClosureTy { 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 * decided to use to refer to the input/output types. @@ -688,7 +702,7 @@ pub struct ClosureTy { pub struct FnSig { pub binder_id: ast::NodeId, pub inputs: Vec, - pub output: t, + pub output: FnOutput, pub variadic: bool } @@ -919,12 +933,6 @@ mod primitives { def_prim_ty!(TY_F32, super::ty_float(ast::TyF32), 14) 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_ { sty: super::ty_err, id: 17, @@ -939,7 +947,6 @@ mod primitives { #[deriving(Clone, PartialEq, Eq, Hash, Show)] pub enum sty { ty_nil, - ty_bot, ty_bool, ty_char, ty_int(ast::IntTy), @@ -1044,6 +1051,7 @@ pub enum type_err { terr_builtin_bounds(expected_found), terr_variadic_mismatch(expected_found), terr_cyclic_ty, + terr_convergence_mismatch(expected_found) } /// Bounds suitable for a named type parameter like `A` in `fn foo` @@ -1578,7 +1586,6 @@ pub fn mk_t(cx: &ctxt, st: sty) -> t { ty_uint(u) => return mk_mach_uint(u), ty_float(f) => return mk_mach_float(f), 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 // neither I (tjc) nor nmatsakis could figure out why, // so we're doing it this way. - &ty_bot => flags = flags | HAS_TY_BOT, &ty_err => flags = flags | HAS_TY_ERR, &ty_param(ref p) => { 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_bare_fn(ref f) => { for a in f.sig.inputs.iter() { flags = flags | get(*a).flags; } - flags = flags | get(f.sig.output).flags; - // T -> _|_ is *not* _|_ ! - flags = flags - HAS_TY_BOT; + if let ty::FnConverging(output) = f.sig.output { + flags = flags | get(output).flags; + } } &ty_closure(ref f) => { 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; } - flags = flags | get(f.sig.output).flags; - // T -> _|_ is *not* _|_ ! - flags = flags - HAS_TY_BOT; + if let ty::FnConverging(output) = f.sig.output { + flags = flags | get(output).flags; + } flags = flags | flags_for_bounds(&f.bounds); } } @@ -1714,9 +1720,6 @@ pub fn mk_nil() -> t { mk_prim_t(&primitives::TY_NIL) } #[inline] pub fn mk_err() -> t { mk_prim_t(&primitives::TY_ERR) } -#[inline] -pub fn mk_bot() -> t { mk_prim_t(&primitives::TY_BOT) } - #[inline] pub fn mk_bool() -> t { mk_prim_t(&primitives::TY_BOOL) } @@ -1862,7 +1865,7 @@ pub fn mk_ctor_fn(cx: &ctxt, sig: FnSig { binder_id: binder_id, inputs: input_args, - output: output, + output: ty::FnConverging(output), variadic: false } }) @@ -1924,7 +1927,7 @@ pub fn maybe_walk_ty(ty: t, f: |t| -> bool) { return; } 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_uniq(ty) | ty_vec(ty, _) | ty_open(ty) => maybe_walk_ty(ty, f), 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_bare_fn(ref ft) => { 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) => { 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 } -pub fn type_is_bot(ty: t) -> bool { - get(ty).flags.intersects(HAS_TY_BOT) -} - pub fn type_is_error(ty: t) -> bool { get(ty).flags.intersects(HAS_TY_ERR) } @@ -2013,8 +2016,8 @@ pub fn trait_ref_contains_error(tref: &ty::TraitRef) -> bool { pub fn type_is_ty_var(ty: t) -> bool { match get(ty).sty { - ty_infer(TyVar(_)) => true, - _ => false + ty_infer(TyVar(_)) => true, + _ => false } } @@ -2170,7 +2173,7 @@ pub fn type_needs_unwind_cleanup(cx: &ctxt, ty: t) -> bool { let mut needs_unwind_cleanup = false; maybe_walk_ty(ty, |ty| { 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_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 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 => { 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 // the current crate; therefore, the only type parameters that // 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 // def-id. 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_nil | - ty_bot | ty_bool | ty_char | 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 { ty_bare_fn(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); match method_type(method_call) { 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 => {} } @@ -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 { 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 => { ::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.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_int: int = 3; static tycat_float: int = 4; - static tycat_bot: int = 5; static tycat_raw_ptr: int = 6; 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_int(_) | ty_uint(_) | ty_infer(IntVar(_)) => tycat_int, ty_float(_) | ty_infer(FloatVar(_)) => tycat_float, - ty_bot => tycat_bot, ty_ptr(_) => tycat_raw_ptr, _ => tycat_other } @@ -5149,7 +5156,6 @@ pub fn hash_crate_independent(tcx: &ctxt, t: t, svh: &Svh) -> u64 { ty::walk_ty(t, |t| { match ty::get(t).sty { ty_nil => byte!(0), - ty_bot => byte!(1), ty_bool => byte!(2), ty_char => byte!(3), ty_int(i) => { @@ -5520,7 +5526,6 @@ pub fn accumulate_lifetimes_in_type(accumulator: &mut Vec, accum_substs(accumulator, substs); } ty_nil | - ty_bot | ty_bool | ty_char | ty_int(_) | diff --git a/src/librustc/middle/ty_fold.rs b/src/librustc/middle/ty_fold.rs index c7fe8a19937c9..a96e81ce20bb4 100644 --- a/src/librustc/middle/ty_fold.rs +++ b/src/librustc/middle/ty_fold.rs @@ -91,6 +91,12 @@ pub trait TypeFolder<'tcx> { 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, fty: &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 { fn fold_with<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::FnSig { folder.fold_sig(self) @@ -453,6 +465,15 @@ pub fn super_fold_sig<'tcx, T: TypeFolder<'tcx>>(this: &mut T, 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, fty: &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, 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_err | ty::ty_infer(_) | ty::ty_param(..) => { diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs index 15e3ee4c8fad3..42990ac79c005 100644 --- a/src/librustc/middle/typeck/astconv.rs +++ b/src/librustc/middle/typeck/astconv.rs @@ -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(|| { match ast_ty.node { ast::TyNil => ty::mk_nil(), - ast::TyBot => ty::mk_bot(), + ast::TyBot => unreachable!(), ast::TyUniq(ref ty) => { mk_pointer(this, rscope, ast::MutImmutable, &**ty, Uniq, |ty| ty::mk_uniq(tcx, ty)) @@ -1171,22 +1171,21 @@ fn ty_of_method_or_bare_fn<'tcx, AC: AstConv<'tcx>>( .collect(); let output_ty = match decl.output.node { - ast::TyInfer => this.ty_infer(decl.output.span), - _ => { - match implied_output_region { - Some(implied_output_region) => { - let rb = SpecificRscope::new(implied_output_region); - ast_ty_to_ty(this, &rb, &*decl.output) - } - None => { - // All regions must be explicitly specified in the output - // if the lifetime elision rules do not apply. This saves - // the user from potentially-confusing errors. - let rb = UnelidableRscope::new(param_lifetimes); - ast_ty_to_ty(this, &rb, &*decl.output) - } + ast::TyBot => ty::FnDiverging, + ast::TyInfer => ty::FnConverging(this.ty_infer(decl.output.span)), + _ => ty::FnConverging(match implied_output_region { + Some(implied_output_region) => { + let rb = SpecificRscope::new(implied_output_region); + ast_ty_to_ty(this, &rb, &*decl.output) } - } + None => { + // All regions must be explicitly specified in the output + // if the lifetime elision rules do not apply. This saves + // the user from potentially-confusing errors. + let rb = UnelidableRscope::new(param_lifetimes); + ast_ty_to_ty(this, &rb, &*decl.output) + } + }) }; (ty::BareFnTy { @@ -1308,10 +1307,12 @@ pub fn ty_of_closure<'tcx, AC: AstConv<'tcx>>( }).collect(); let expected_ret_ty = expected_sig.map(|e| e.output); + 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 => this.ty_infer(decl.output.span), - _ => ast_ty_to_ty(this, &rb, &*decl.output) + ast::TyInfer => ty::FnConverging(this.ty_infer(decl.output.span)), + _ => ty::FnConverging(ast_ty_to_ty(this, &rb, &*decl.output)) }; ty::ClosureTy { diff --git a/src/librustc/middle/typeck/check/_match.rs b/src/librustc/middle/typeck/check/_match.rs index 1dcd8c76f4b84..d9f8625504daf 100644 --- a/src/librustc/middle/typeck/check/_match.rs +++ b/src/librustc/middle/typeck/check/_match.rs @@ -261,7 +261,7 @@ pub fn check_match(fcx: &FnCtxt, // on any empty type and is therefore unreachable; should the flow // of execution reach it, we will fail, so bottom is an appropriate // 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); 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 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 { ctor_pty }; diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs index 82a3d1a523d39..6ace73931bfe8 100644 --- a/src/librustc/middle/typeck/check/method.rs +++ b/src/librustc/middle/typeck/check/method.rs @@ -1082,7 +1082,7 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { ty_bare_fn(..) | ty_uniq(..) | ty_rptr(..) | ty_infer(IntVar(_)) | ty_infer(FloatVar(_)) | - ty_param(..) | ty_nil | ty_bot | ty_bool | + ty_param(..) | ty_nil | ty_bool | ty_char | ty_int(..) | ty_uint(..) | ty_float(..) | ty_enum(..) | ty_ptr(..) | ty_struct(..) | ty_unboxed_closure(..) | ty_tup(..) | ty_open(..) | @@ -1603,8 +1603,10 @@ impl<'a, 'tcx> LookupContext<'a, 'tcx> { return false; } } - if !check_for_self_ty(sig.output) { - return false; + if let ty::FnConverging(result_type) = sig.output { + if !check_for_self_ty(result_type) { + return false; + } } if candidate.method_ty.generics.has_type_params(subst::FnSpace) { diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 53a4a8141e65c..8843be3cf816f 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -280,7 +280,7 @@ pub struct FnCtxt<'a, 'tcx: 'a> { // expects the types within the function to be consistent. err_count_on_creation: uint, - ret_ty: ty::t, + ret_ty: ty::FnOutput, ps: RefCell, @@ -346,7 +346,7 @@ impl<'a, 'tcx> Inherited<'a, 'tcx> { // Used by check_const and check_enum_variants pub fn blank_fn_ctxt<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>, inh: &'a Inherited<'a, 'tcx>, - rty: ty::t, + rty: ty::FnOutput, body_id: ast::NodeId) -> FnCtxt<'a, 'tcx> { FnCtxt { @@ -410,6 +410,7 @@ fn check_bare_fn(ccx: &CrateCtxt, vtable::select_all_fcx_obligations_or_error(&fcx); regionck::regionck_fn(&fcx, id, body); + fcx.default_diverging_type_variables_to_nil(); writeback::resolve_type_vars_in_fn(&fcx, decl, body); } _ => ccx.tcx.sess.impossible_case(body.span, @@ -426,8 +427,7 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { match ty_opt { None => { // infer the variable's type - let var_id = self.fcx.infcx().next_ty_var_id(); - let var_ty = ty::mk_var(self.fcx.tcx(), var_id); + let var_ty = self.fcx.infcx().next_ty_var(); self.fcx.inh.locals.borrow_mut().insert(nid, 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. - let fn_sig_tys: Vec = - arg_tys.iter().chain([ret_ty].iter()).map(|&ty| ty).collect(); + let mut fn_sig_tys: Vec = + 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={}", fn_id, fn_sig_tys.repr(tcx)); @@ -584,9 +592,11 @@ fn check_fn<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>, 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()) { fcx.write_ty(input.id, *arg); @@ -1333,11 +1343,6 @@ fn check_cast(fcx: &FnCtxt, return } - if ty::type_is_bot(t_e) { - fcx.write_bot(id); - return - } - if !ty::type_is_sized(fcx.tcx(), t_1) { let tstr = fcx.infcx().ty_to_string(t_1); 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] pub fn write_ty(&self, node_id: ast::NodeId, ty: ty::t) { debug!("write_ty({}, {}) in fcx {}", @@ -1726,9 +1739,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn write_nil(&self, node_id: ast::NodeId) { 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) { self.write_ty(node_id, ty::mk_err()); } @@ -2051,10 +2061,6 @@ pub fn autoderef(fcx: &FnCtxt, sp: Span, base_ty: ty::t, for autoderefs in range(0, fcx.tcx().sess.recursion_limit.get()) { 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) { Some(x) => return (resolved_t, autoderefs, Some(x)), None => {} @@ -2197,7 +2203,12 @@ fn make_return_type(fcx: &FnCtxt, } None => {} } - ty::deref(ref_ty, true) + match ref_ty { + ty::FnConverging(ref_ty) => + ty::deref(ref_ty, true), + ty::FnDiverging => + None + } } None => None, } @@ -2285,7 +2296,12 @@ fn try_overloaded_slice(fcx: &FnCtxt, } 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, } @@ -2400,9 +2416,11 @@ fn lookup_method_for_for_loop(fcx: &FnCtxt, // We expect the return type to be `Option` or something like it. // Grab the first parameter of its type substitution. - let return_type = structurally_resolved_type(fcx, - iterator_expr.span, - return_type); + let return_type = match return_type { + ty::FnConverging(return_type) => + structurally_resolved_type(fcx, iterator_expr.span, return_type), + ty::FnDiverging => ty::mk_err() + }; match ty::get(return_type).sty { ty::ty_enum(_, ref substs) 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], deref_args: DerefArgs, tuple_arguments: TupleArgumentsFlag) - -> ty::t { + -> ty::FnOutput { if ty::type_is_error(method_fn_ty) { let err_inputs = err_args(args_no_rcvr.len()); check_argument_types(fcx, @@ -2438,7 +2456,7 @@ fn check_method_argument_types<'a>(fcx: &FnCtxt, deref_args, false, tuple_arguments); - method_fn_ty + ty::FnConverging(method_fn_ty) } else { match ty::get(method_fn_ty).sty { ty::ty_bare_fn(ref fty) => { @@ -2654,8 +2672,11 @@ fn err_args(len: uint) -> Vec { Vec::from_fn(len, |_| ty::mk_err()) } -fn write_call(fcx: &FnCtxt, call_expr: &ast::Expr, output: ty::t) { - fcx.write_ty(call_expr.id, output); +fn write_call(fcx: &FnCtxt, call_expr: &ast::Expr, output: ty::FnOutput) { + 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 @@ -2845,8 +2866,8 @@ enum TupleArgumentsFlag { /// strict, _|_ can appear in the type of an expression that does not, /// itself, diverge: for example, fn() -> _|_.) /// Note that inspecting a type's structure *directly* may expose the fact -/// that there are actually multiple representations for both `ty_err` and -/// `ty_bot`, so avoid that when err and bot need to be handled differently. +/// that there are actually multiple representations for `ty_err`, so avoid +/// that when err needs to be handled differently. fn check_expr_with_unifier(fcx: &FnCtxt, expr: &ast::Expr, expected: Expectation, @@ -2873,7 +2894,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt, let error_fn_sig = FnSig { binder_id: ast::CRATE_NODE_ID, inputs: err_args(args.len()), - output: ty::mk_err(), + output: ty::FnConverging(ty::mk_err()), variadic: false }; @@ -3021,8 +3042,6 @@ fn check_expr_with_unifier(fcx: &FnCtxt, let cond_ty = fcx.expr_ty(cond_expr); let if_ty = if ty::type_is_error(cond_ty) { ty::mk_err() - } else if ty::type_is_bot(cond_ty) { - ty::mk_bot() } else { branches_ty }; @@ -3055,13 +3074,16 @@ fn check_expr_with_unifier(fcx: &FnCtxt, // HACK(eddyb) Fully qualified path to work around a resolve bug. let method_call = ::middle::typeck::MethodCall::expr(op_ex.id); fcx.inh.method_map.borrow_mut().insert(method_call, method); - check_method_argument_types(fcx, + match check_method_argument_types(fcx, op_ex.span, method_ty, op_ex, args.as_slice(), DoDerefArgs, - DontTupleArguments) + DontTupleArguments) { + ty::FnConverging(result_type) => result_type, + ty::FnDiverging => ty::mk_err() + } } None => { unbound_method(); @@ -3663,9 +3685,6 @@ fn check_expr_with_unifier(fcx: &FnCtxt, None => {} Some(base_expr) => { 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) { 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) => { 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 // but seems quite dubious with regard to user-defined methods // and so forth. - Niko - if !ty::type_is_error(result_t) - && !ty::type_is_bot(result_t) { + if !ty::type_is_error(result_t) { fcx.write_nil(expr.id); } } @@ -3820,9 +3834,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt, if !ty::type_is_error(oprnd_t) { match unop { 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 => { oprnd_t = structurally_resolved_type(fcx, expr.span, oprnd_t); @@ -3859,27 +3871,23 @@ fn check_expr_with_unifier(fcx: &FnCtxt, }; } ast::UnNot => { - if !ty::type_is_bot(oprnd_t) { - oprnd_t = structurally_resolved_type(fcx, oprnd.span, - oprnd_t); - if !(ty::type_is_integral(oprnd_t) || - ty::get(oprnd_t).sty == ty::ty_bool) { - oprnd_t = check_user_unop(fcx, "!", "not", - tcx.lang_items.not_trait(), - expr, &**oprnd, oprnd_t); - } + oprnd_t = structurally_resolved_type(fcx, oprnd.span, + oprnd_t); + if !(ty::type_is_integral(oprnd_t) || + ty::get(oprnd_t).sty == ty::ty_bool) { + oprnd_t = check_user_unop(fcx, "!", "not", + tcx.lang_items.not_trait(), + expr, &**oprnd, oprnd_t); } } ast::UnNeg => { - if !ty::type_is_bot(oprnd_t) { - oprnd_t = structurally_resolved_type(fcx, oprnd.span, - oprnd_t); - if !(ty::type_is_integral(oprnd_t) || - ty::type_is_fp(oprnd_t)) { - oprnd_t = check_user_unop(fcx, "-", "neg", - tcx.lang_items.neg_trait(), - expr, &**oprnd, oprnd_t); - } + oprnd_t = structurally_resolved_type(fcx, oprnd.span, + oprnd_t); + if !(ty::type_is_integral(oprnd_t) || + ty::type_is_fp(oprnd_t)) { + oprnd_t = check_user_unop(fcx, "-", "neg", + tcx.lang_items.neg_trait(), + expr, &**oprnd, oprnd_t); } } } @@ -3904,10 +3912,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt, let tm = ty::mt { ty: fcx.expr_ty(&**oprnd), mutbl: mutbl }; let oprnd_t = if ty::type_is_error(tm.ty) { ty::mk_err() - } else if ty::type_is_bot(tm.ty) { - ty::mk_bot() - } - else { + } else { // Note: at this point, we cannot say what the best lifetime // is to use for resulting pointer. We want to use the // shortest lifetime possible so as to avoid spurious borrowck @@ -3961,24 +3966,32 @@ fn check_expr_with_unifier(fcx: &FnCtxt, fcx.write_nil(id); } ast::ExprMac(_) => tcx.sess.bug("unexpanded macro"), - ast::ExprBreak(_) => { fcx.write_bot(id); } - ast::ExprAgain(_) => { fcx.write_bot(id); } + ast::ExprBreak(_) => { fcx.write_ty(id, fcx.infcx().next_diverging_ty_var()); } + ast::ExprAgain(_) => { fcx.write_ty(id, fcx.infcx().next_diverging_ty_var()); } ast::ExprRet(ref expr_opt) => { - let ret_ty = fcx.ret_ty; - match *expr_opt { - None => match fcx.mk_eqty(false, infer::Misc(expr.span), - ret_ty, ty::mk_nil()) { - Ok(_) => { /* fall through */ } - Err(_) => { - span_err!(tcx.sess, expr.span, E0069, - "`return;` in function returning non-nil"); + match fcx.ret_ty { + ty::FnConverging(result_type) => { + match *expr_opt { + None => + if let Err(_) = fcx.mk_eqty(false, infer::Misc(expr.span), + result_type, ty::mk_nil()) { + span_err!(tcx.sess, expr.span, E0069, + "`return;` in function returning non-nil"); + }, + Some(ref e) => { + check_expr_coercable_to_type(fcx, &**e, result_type); + } + } + } + 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"); } - }, - Some(ref e) => { - check_expr_coercable_to_type(fcx, &**e, ret_ty); - } } - fcx.write_bot(id); + fcx.write_ty(id, fcx.infcx().next_diverging_ty_var()); } ast::ExprParen(ref a) => { 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) { fcx.write_error(id); - } else if ty::type_is_bot(lhs_ty) || ty::type_is_bot(rhs_ty) { - fcx.write_bot(id); } else { 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) { fcx.write_error(id); } - else if ty::type_is_bot(cond_ty) { - fcx.write_bot(id); - } else { fcx.write_nil(id); } @@ -4052,7 +4060,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt, ast::ExprLoop(ref body, _) => { check_block_no_value(fcx, &**body); if !may_break(tcx, expr.id, &**body) { - fcx.write_bot(id); + fcx.write_ty(id, fcx.infcx().next_diverging_ty_var()); } else { fcx.write_nil(id); } @@ -4100,31 +4108,24 @@ fn check_expr_with_unifier(fcx: &FnCtxt, let args: Vec<_> = args.iter().map(|x| x).collect(); if !try_overloaded_call(fcx, expr, &**f, f_ty, args.as_slice()) { check_call(fcx, expr, &**f, args.as_slice()); - let (args_bot, args_err) = args.iter().fold((false, false), - |(rest_bot, rest_err), a| { + let args_err = args.iter().fold(false, + |rest_err, a| { // is this not working? 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 { 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) => { 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 (args_bot, args_err) = arg_tys.fold((false, false), - |(rest_bot, rest_err), a| { - (rest_bot || ty::type_is_bot(a), - rest_err || ty::type_is_error(a))}); + let args_err = arg_tys.fold(false, + |rest_err, a| { + rest_err || ty::type_is_error(a)}); if args_err { fcx.write_error(id); - } else if args_bot { - fcx.write_bot(id); } } ast::ExprCast(ref e, ref t) => { @@ -4203,8 +4204,6 @@ fn check_expr_with_unifier(fcx: &FnCtxt, if ty::type_is_error(element_ty) { fcx.write_error(id); - } else if ty::type_is_bot(element_ty) { - fcx.write_bot(id); } else { let t = ty::mk_vec(tcx, t, Some(count)); fcx.write_ty(id, t); @@ -4218,7 +4217,6 @@ fn check_expr_with_unifier(fcx: &FnCtxt, _ => None } }); - let mut bot_field = false; let mut err_field = false; 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); - bot_field = bot_field || ty::type_is_bot(t); t }).collect(); - if bot_field { - fcx.write_bot(id); - } else if err_field { + if err_field { fcx.write_error(id); } else { 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), lvalue_pref, |base_t, _| ty::index(base_t)); match field_ty { - Some(ty) if !ty::type_is_bot(ty) => { + Some(ty) => { check_expr_has_type(fcx, &**idx, ty::mk_uint()); fcx.write_ty(id, ty); 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 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); some_err = true; } @@ -4403,7 +4398,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt, let check_slice_idx = |e: &ast::Expr| { check_expr(fcx, 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); some_err = true; } @@ -4543,7 +4538,7 @@ pub fn check_decl_local(fcx: &FnCtxt, local: &ast::Local) { Some(ref init) => { check_decl_initializer(fcx, local.id, &**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); } } @@ -4556,7 +4551,7 @@ pub fn check_decl_local(fcx: &FnCtxt, local: &ast::Local) { }; _match::check_pat(&pcx, &*local.pat, t); 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); } } @@ -4572,7 +4567,7 @@ pub fn check_stmt(fcx: &FnCtxt, stmt: &ast::Stmt) { ast::DeclLocal(ref l) => { check_decl_local(fcx, &**l); 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); } ast::DeclItem(_) => {/* ignore for now */ } @@ -4583,20 +4578,20 @@ pub fn check_stmt(fcx: &FnCtxt, stmt: &ast::Stmt) { // Check with expected type of () check_expr_has_type(fcx, &**expr, ty::mk_nil()); 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); } ast::StmtSemi(ref expr, id) => { node_id = id; check_expr(fcx, &**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); } ast::StmtMac(..) => fcx.ccx.tcx.sess.bug("unexpanded macro") } if saw_bot { - fcx.write_bot(node_id); + fcx.write_ty(node_id, fcx.infcx().next_diverging_ty_var()); } else if saw_err { 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); if ty::type_is_error(blkty) { fcx.write_error(blk.id); - } - else if ty::type_is_bot(blkty) { - fcx.write_bot(blk.id); - } - else { + } else { let nilty = ty::mk_nil(); demand::suptype(fcx, blk.span, nilty, blkty); } @@ -4631,14 +4622,13 @@ fn check_block_with_expected(fcx: &FnCtxt, }; let mut warned = false; - let mut last_was_bot = false; - let mut any_bot = false; + let mut any_diverges = false; let mut any_err = false; for s in blk.stmts.iter() { check_stmt(fcx, &**s); let s_id = ast_util::stmt_id(&**s); 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, _) => { match decl.node { ast::DeclLocal(_) => true, @@ -4657,22 +4647,19 @@ fn check_block_with_expected(fcx: &FnCtxt, "unreachable statement".to_string()); warned = true; } - if ty::type_is_bot(s_ty) { - last_was_bot = true; - } - any_bot = any_bot || ty::type_is_bot(s_ty); + any_diverges = any_diverges || fcx.infcx().type_var_diverges(s_ty); any_err = any_err || ty::type_is_error(s_ty); } match blk.expr { None => if any_err { fcx.write_error(blk.id); - } else if any_bot { - fcx.write_bot(blk.id); + } else if any_diverges { + fcx.write_ty(blk.id, fcx.infcx().next_diverging_ty_var()); } else { fcx.write_nil(blk.id); }, Some(ref e) => { - if any_bot && !warned { + if any_diverges && !warned { fcx.ccx .tcx .sess @@ -4692,11 +4679,12 @@ fn check_block_with_expected(fcx: &FnCtxt, } }; - fcx.write_ty(blk.id, ety); if any_err { fcx.write_error(blk.id); - } else if any_bot { - fcx.write_bot(blk.id); + } else if any_diverges { + 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, }; 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); } @@ -4728,7 +4716,7 @@ pub fn check_const(ccx: &CrateCtxt, id: ast::NodeId) { let inh = static_inherited_fields(ccx); 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; 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)); 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 { attr::ReprAny | attr::ReprPacked | attr::ReprExtern => ty::mk_int(), 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"); //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)), 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); return; } - } - + }; + (n_tps, inputs, ty::FnConverging(output)) + } else if name.get() == "abort" || name.get() == "unreachable" { + (0, Vec::new(), ty::FnDiverging) } else { - match name.get() { - "abort" => (0, Vec::new(), ty::mk_bot()), - "unreachable" => (0, Vec::new(), ty::mk_bot()), + let (n_tps, inputs, output) = match name.get() { "breakpoint" => (0, Vec::new(), ty::mk_nil()), "size_of" | "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); return; } - } + }; + (n_tps, inputs, ty::FnConverging(output)) }; let fty = ty::mk_bare_fn(tcx, ty::BareFnTy { fn_style: ast::UnsafeFn, diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index 8585cacdbc434..bcade1e74ca65 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -334,7 +334,7 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> { /// Try to resolve the type for the given node. pub fn resolve_expr_type_adjusted(&mut self, expr: &ast::Expr) -> ty::t { 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 } else { let tcx = self.fcx.tcx(); @@ -690,7 +690,7 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { Some(method) => { constrain_call(rcx, expr, Some(&**base), None::.iter(), true); - ty::ty_fn_ret(method.ty) + ty::ty_fn_ret(method.ty).unwrap() } None => rcx.resolve_node_type(base.id) }; @@ -1217,9 +1217,14 @@ fn constrain_autoderefs(rcx: &mut Rcx, // Specialized version of constrain_call. type_must_outlive(rcx, infer::CallRcvr(deref_expr.span), self_ty, r_deref_expr); - type_must_outlive(rcx, infer::CallReturn(deref_expr.span), - fn_sig.output, r_deref_expr); - fn_sig.output + match fn_sig.output { + ty::FnConverging(return_type) => { + type_must_outlive(rcx, infer::CallReturn(deref_expr.span), + return_type, r_deref_expr); + return_type + } + ty::FnDiverging => unreachable!() + } } None => derefd_ty }; @@ -1445,7 +1450,7 @@ fn link_region_from_node_type(rcx: &Rcx, */ 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; debug!("rptr_ty={}", ty_to_string(tcx, rptr_ty)); let r = ty::ty_region(tcx, span, rptr_ty); diff --git a/src/librustc/middle/typeck/check/regionmanip.rs b/src/librustc/middle/typeck/check/regionmanip.rs index 06b633ba15b57..225a3162af904 100644 --- a/src/librustc/middle/typeck/check/regionmanip.rs +++ b/src/librustc/middle/typeck/check/regionmanip.rs @@ -92,7 +92,6 @@ impl<'a, 'tcx> Wf<'a, 'tcx> { match ty::get(ty).sty { ty::ty_nil | - ty::ty_bot | ty::ty_bool | ty::ty_char | ty::ty_int(..) | diff --git a/src/librustc/middle/typeck/check/wf.rs b/src/librustc/middle/typeck/check/wf.rs index 3c594fbf2d366..b3449d658f6d6 100644 --- a/src/librustc/middle/typeck/check/wf.rs +++ b/src/librustc/middle/typeck/check/wf.rs @@ -98,7 +98,7 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> { &polytype.generics, item.id); 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); vtable::select_all_fcx_obligations_or_error(&fcx); regionck::regionck_item(&fcx, item); diff --git a/src/librustc/middle/typeck/coherence/mod.rs b/src/librustc/middle/typeck/coherence/mod.rs index 38ac317b7a3d4..92fc61cf4c034 100644 --- a/src/librustc/middle/typeck/coherence/mod.rs +++ b/src/librustc/middle/typeck/coherence/mod.rs @@ -23,7 +23,7 @@ use middle::subst::{Substs}; use middle::ty::get; use middle::ty::{ImplContainer, ImplOrTraitItemId, MethodTraitItemId}; 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_param, Polytype, ty_ptr}; 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) } - 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_infer(..) | ty_param(..) | ty_err | ty_open(..) | ty_uniq(_) | ty_ptr(_) | ty_rptr(_, _) => { diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index 1f4b80b360bd2..4c85418669fbb 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -2236,7 +2236,10 @@ pub fn ty_of_foreign_fn_decl(ccx: &CrateCtxt, .map(|a| ty_of_arg(ccx, &rb, a, None)) .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( ccx.tcx, @@ -2245,7 +2248,7 @@ pub fn ty_of_foreign_fn_decl(ccx: &CrateCtxt, fn_style: ast::UnsafeFn, sig: ty::FnSig {binder_id: def_id.node, inputs: input_tys, - output: output_ty, + output: output, variadic: decl.variadic} }); let pty = Polytype { diff --git a/src/librustc/middle/typeck/infer/combine.rs b/src/librustc/middle/typeck/infer/combine.rs index de59fb80ad515..de9379a3aa763 100644 --- a/src/librustc/middle/typeck/infer/combine.rs +++ b/src/librustc/middle/typeck/infer/combine.rs @@ -359,7 +359,18 @@ pub fn super_fn_sigs<'tcx, C: Combine<'tcx>>(this: &C, let inputs = try!(argvecs(this, a.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, inputs: inputs, output: output, @@ -373,9 +384,7 @@ pub fn super_tys<'tcx, C: Combine<'tcx>>(this: &C, a: ty::t, b: ty::t) -> cres { tcx.sess.bug( diff --git a/src/librustc/middle/typeck/infer/equate.rs b/src/librustc/middle/typeck/infer/equate.rs index fd4a592736218..97453dc86efd4 100644 --- a/src/librustc/middle/typeck/infer/equate.rs +++ b/src/librustc/middle/typeck/infer/equate.rs @@ -112,15 +112,6 @@ impl<'f, 'tcx> Combine<'tcx> for Equate<'f, 'tcx> { let a = infcx.type_variables.borrow().replace_if_possible(a); let b = infcx.type_variables.borrow().replace_if_possible(b); 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))) => { infcx.type_variables.borrow_mut().relate_vars(a_id, EqTo, b_id); Ok(a) diff --git a/src/librustc/middle/typeck/infer/lattice.rs b/src/librustc/middle/typeck/infer/lattice.rs index 24642d5213892..4fb7bebc58f6d 100644 --- a/src/librustc/middle/typeck/infer/lattice.rs +++ b/src/librustc/middle/typeck/infer/lattice.rs @@ -42,20 +42,12 @@ use util::ppaux::Repr; use std::collections::HashMap; 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; - // Relates the type `v` to `a` and `b` such that `v` represents // 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<()>; } impl<'a, 'tcx> LatticeDir for Lub<'a, 'tcx> { - fn ty_bot(&self, t: ty::t) -> cres { - Ok(t) - } - fn relate_bound<'a>(&'a self, v: ty::t, a: ty::t, b: ty::t) -> cres<()> { let sub = self.sub(); 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> { - fn ty_bot(&self, _: ty::t) -> cres { - Ok(ty::mk_bot()) - } - fn relate_bound<'a>(&'a self, v: ty::t, a: ty::t, b: ty::t) -> cres<()> { let sub = self.sub(); 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 b = infcx.type_variables.borrow().replace_if_possible(b); match (&ty::get(a).sty, &ty::get(b).sty) { - (&ty::ty_bot, _) => { this.ty_bot(b) } - (_, &ty::ty_bot) => { this.ty_bot(a) } + (&ty::ty_infer(TyVar(..)), &ty::ty_infer(TyVar(..))) + 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(..))) => { diff --git a/src/librustc/middle/typeck/infer/mod.rs b/src/librustc/middle/typeck/infer/mod.rs index a466581ef394a..fc508db3b2ebd 100644 --- a/src/librustc/middle/typeck/infer/mod.rs +++ b/src/librustc/middle/typeck/infer/mod.rs @@ -510,6 +510,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { 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> { skolemize::TypeSkolemizer::new(self) } @@ -684,14 +691,18 @@ 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 .borrow_mut() - .new_var() + .new_var(diverging) } 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 { diff --git a/src/librustc/middle/typeck/infer/resolve.rs b/src/librustc/middle/typeck/infer/resolve.rs index 87f3fd987871c..db26376fc69db 100644 --- a/src/librustc/middle/typeck/infer/resolve.rs +++ b/src/librustc/middle/typeck/infer/resolve.rs @@ -205,7 +205,8 @@ impl<'a, 'tcx> ResolveState<'a, 'tcx> { pub fn resolve_ty_var(&mut self, vid: TyVid) -> ty::t { 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) => { self.resolve_type(t) } @@ -215,8 +216,7 @@ impl<'a, 'tcx> ResolveState<'a, 'tcx> { } ty::mk_var(tcx, vid) } - }; - return t1; + } } pub fn resolve_int_var(&mut self, vid: IntVid) -> ty::t { diff --git a/src/librustc/middle/typeck/infer/skolemize.rs b/src/librustc/middle/typeck/infer/skolemize.rs index 1b3290c8b5af8..0fe1e2b565bef 100644 --- a/src/librustc/middle/typeck/infer/skolemize.rs +++ b/src/librustc/middle/typeck/infer/skolemize.rs @@ -149,7 +149,6 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeSkolemizer<'a, 'tcx> { } ty::ty_nil | - ty::ty_bot | ty::ty_bool | ty::ty_char | ty::ty_int(..) | diff --git a/src/librustc/middle/typeck/infer/sub.rs b/src/librustc/middle/typeck/infer/sub.rs index 4c04bcc5236f4..f44fa1ac1c696 100644 --- a/src/librustc/middle/typeck/infer/sub.rs +++ b/src/librustc/middle/typeck/infer/sub.rs @@ -129,10 +129,6 @@ impl<'f, 'tcx> Combine<'tcx> for Sub<'f, 'tcx> { let a = infcx.type_variables.borrow().replace_if_possible(a); let b = infcx.type_variables.borrow().replace_if_possible(b); 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))) => { infcx.type_variables .borrow_mut() @@ -154,10 +150,6 @@ impl<'f, 'tcx> Combine<'tcx> for Sub<'f, 'tcx> { Ok(ty::mk_err()) } - (_, &ty::ty_bot) => { - Err(ty::terr_sorts(expected_found(self, a, b))) - } - _ => { super_tys(self, a, b) } diff --git a/src/librustc/middle/typeck/infer/type_variable.rs b/src/librustc/middle/typeck/infer/type_variable.rs index 5f67f8a048aa4..deeb90503df21 100644 --- a/src/librustc/middle/typeck/infer/type_variable.rs +++ b/src/librustc/middle/typeck/infer/type_variable.rs @@ -17,7 +17,8 @@ pub struct TypeVariableTable { } struct TypeVariableData { - value: TypeVariableValue + value: TypeVariableValue, + diverging: bool } enum TypeVariableValue { @@ -63,6 +64,10 @@ impl TypeVariableTable { 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) { /*! * Records that `a <: b`, `a :> b`, or `a == b`, depending on `dir`. @@ -108,10 +113,11 @@ impl TypeVariableTable { self.values.record(SpecifyVar(vid, relations)); } - pub fn new_var(&mut self) -> ty::TyVid { - let index = - self.values.push( - TypeVariableData { value: Bounded(Vec::new()) }); + pub fn new_var(&mut self, diverging: bool) -> ty::TyVid { + let index = self.values.push(TypeVariableData { + value: Bounded(vec![]), + diverging: diverging + }); ty::TyVid { index: index } } diff --git a/src/librustc/middle/typeck/mod.rs b/src/librustc/middle/typeck/mod.rs index 5a23d54c9720b..22898221d9b53 100644 --- a/src/librustc/middle/typeck/mod.rs +++ b/src/librustc/middle/typeck/mod.rs @@ -381,7 +381,7 @@ fn check_main_fn_ty(ccx: &CrateCtxt, sig: ty::FnSig { binder_id: main_id, inputs: Vec::new(), - output: ty::mk_nil(), + output: ty::FnConverging(ty::mk_nil()), variadic: false } }); @@ -433,7 +433,7 @@ fn check_start_fn_ty(ccx: &CrateCtxt, ty::mk_int(), 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 } }); diff --git a/src/librustc/middle/typeck/variance.rs b/src/librustc/middle/typeck/variance.rs index d8541a54bd986..8e4aea4463ee3 100644 --- a/src/librustc/middle/typeck/variance.rs +++ b/src/librustc/middle/typeck/variance.rs @@ -728,7 +728,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { debug!("add_constraints_from_ty(ty={})", ty.repr(self.tcx())); 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_float(_) | ty::ty_str => { /* leaf type -- noop */ @@ -882,7 +882,9 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { for &input in sig.inputs.iter() { 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 diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 943570743e83f..296158dd4a868 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -17,7 +17,7 @@ use middle::ty::{ReEarlyBound, BrFresh, ctxt}; use middle::ty::{ReFree, ReScope, ReInfer, ReStatic, Region, ReEmpty}; use middle::ty::{ReSkolemized, ReVar, BrEnv}; 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_nil, ty_param, ty_ptr, ty_rptr, ty_tup, ty_open}; use middle::ty::{ty_unboxed_closure}; @@ -352,12 +352,15 @@ pub fn ty_to_string(cx: &ctxt, typ: t) -> String { s.push_str(bounds); } - if ty::get(sig.output).sty != ty_nil { - s.push_str(" -> "); - if ty::type_is_bot(sig.output) { - s.push('!'); - } else { - s.push_str(ty_to_string(cx, sig.output).as_slice()); + match sig.output { + ty::FnConverging(t) => { + if !ty::type_is_nil(t) { + s.push_str(" -> "); + s.push_str(ty_to_string(cx, t).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: return match ty::get(typ).sty { ty_nil => "()".to_string(), - ty_bot => "!".to_string(), ty_bool => "bool".to_string(), ty_char => "char".to_string(), ty_int(t) => ast_util::int_ty_to_string(t, None).to_string(), @@ -952,6 +954,17 @@ 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 { fn repr(&self, tcx: &ctxt) -> String { format!("MethodCallee {{origin: {}, ty: {}, {}}}", diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index bdafe192705ce..c4d7e85904cb3 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -880,6 +880,15 @@ impl Clean for ast::FnDecl { } } +impl<'a> Clean for ty::FnOutput { + fn clean(&self, cx: &DocContext) -> Type { + match *self { + ty::FnConverging(ty) => ty.clean(cx), + ty::FnDiverging => Bottom + } + } +} + impl<'a> Clean for (ast::DefId, &'a ty::FnSig) { fn clean(&self, cx: &DocContext) -> FnDecl { let (did, sig) = *self; @@ -1258,7 +1267,6 @@ impl Clean for ast::Ty { impl Clean for ty::t { fn clean(&self, cx: &DocContext) -> Type { match ty::get(*self).sty { - ty::ty_bot => Bottom, ty::ty_nil => Primitive(Unit), ty::ty_bool => Primitive(Bool), ty::ty_char => Primitive(Char), diff --git a/src/test/compile-fail/bad-bang-ann-3.rs b/src/test/compile-fail/bad-bang-ann-3.rs index 943169be00452..d204c8c750aae 100644 --- a/src/test/compile-fail/bad-bang-ann-3.rs +++ b/src/test/compile-fail/bad-bang-ann-3.rs @@ -11,8 +11,7 @@ // Tests that a function with a ! annotation always actually fails fn bad_bang(i: uint) -> ! { - return 7u; - //~^ ERROR expected `!`, found `uint` + return 7u; //~ ERROR `return` in a function declared as diverging [E0166] } fn main() { bad_bang(5u); } diff --git a/src/test/compile-fail/bad-bang-ann.rs b/src/test/compile-fail/bad-bang-ann.rs index 18b98eb3a3881..69200ffedf9bb 100644 --- a/src/test/compile-fail/bad-bang-ann.rs +++ b/src/test/compile-fail/bad-bang-ann.rs @@ -10,9 +10,8 @@ // Tests that a function with a ! annotation always actually fails -fn bad_bang(i: uint) -> ! { +fn bad_bang(i: uint) -> ! { //~ ERROR computation may converge in a function marked as diverging if i < 0u { } else { fail!(); } - //~^ ERROR expected `!`, found `()` } fn main() { bad_bang(5u); } diff --git a/src/test/compile-fail/bang-tailexpr.rs b/src/test/compile-fail/bang-tailexpr.rs index ff95f05279eea..ec576ff4bd8c2 100644 --- a/src/test/compile-fail/bang-tailexpr.rs +++ b/src/test/compile-fail/bang-tailexpr.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -fn f() -> ! { - 3i //~ ERROR expected `!`, found `int` +fn f() -> ! { //~ ERROR computation may converge in a function marked as diverging + 3i } fn main() { } diff --git a/src/test/run-fail/binop-fail-3.rs b/src/test/compile-fail/binop-fail-3.rs similarity index 82% rename from src/test/run-fail/binop-fail-3.rs rename to src/test/compile-fail/binop-fail-3.rs index 150544f16af0c..2d6f8ccbfa25f 100644 --- a/src/test/run-fail/binop-fail-3.rs +++ b/src/test/compile-fail/binop-fail-3.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:quux fn foo() -> ! { fail!("quux"); } -fn main() { foo() == foo(); } +fn main() { + foo() //~ ERROR the type of this value must be known in this context + == + foo(); +} diff --git a/src/test/compile-fail/closure-that-fails.rs b/src/test/compile-fail/closure-that-fails.rs index 7ce8e95a761f2..05317e9c6b029 100644 --- a/src/test/compile-fail/closure-that-fails.rs +++ b/src/test/compile-fail/closure-that-fails.rs @@ -14,7 +14,7 @@ fn main() { // Type inference didn't use to be able to handle this: foo(|| fail!()); foo(|| -> ! fail!()); - foo(|| 22); //~ ERROR mismatched types - foo(|| -> ! 22); //~ ERROR mismatched types - let x = || -> ! 1; //~ ERROR mismatched types + foo(|| 22i); //~ ERROR computation may converge in a function marked as diverging + foo(|| -> ! 22i); //~ ERROR computation may converge in a function marked as diverging + let x = || -> ! 1i; //~ ERROR computation may converge in a function marked as diverging } diff --git a/src/test/compile-fail/index-bot.rs b/src/test/compile-fail/index-bot.rs index cf05cf51640e9..bd01d45fd44ec 100644 --- a/src/test/compile-fail/index-bot.rs +++ b/src/test/compile-fail/index-bot.rs @@ -9,5 +9,5 @@ // except according to those terms. fn main() { - (return)[0u]; //~ ERROR cannot index a value of type `!` + (return)[0u]; //~ ERROR the type of this value must be known in this context } diff --git a/src/test/compile-fail/issue-13847.rs b/src/test/compile-fail/issue-13847.rs index fb6d1d4fc6efe..aa823d9a70e7c 100644 --- a/src/test/compile-fail/issue-13847.rs +++ b/src/test/compile-fail/issue-13847.rs @@ -9,6 +9,5 @@ // except according to those terms. fn main() { - return.is_failure -//~^ ERROR attempted access of field `is_failure` on type `!`, but no field with that name was found + return.is_failure //~ ERROR the type of this value must be known in this context } diff --git a/src/test/compile-fail/issue-15207.rs b/src/test/compile-fail/issue-15207.rs index f1c0b622ae437..37fcabef0ed9b 100644 --- a/src/test/compile-fail/issue-15207.rs +++ b/src/test/compile-fail/issue-15207.rs @@ -9,5 +9,9 @@ // except according to those terms. fn main() { - loop { break.push(1); } //~ ERROR type `!` does not implement any method in scope named `push` + loop { + break.push(1) //~ ERROR the type of this value must be known in this context + //~^ ERROR multiple applicable methods in scope + ; + } } diff --git a/src/test/compile-fail/issue-15965.rs b/src/test/compile-fail/issue-15965.rs index 410b1f94293a9..856cd1b0f3f26 100644 --- a/src/test/compile-fail/issue-15965.rs +++ b/src/test/compile-fail/issue-15965.rs @@ -9,5 +9,10 @@ // except according to those terms. fn main() { - return { return () } (); //~ ERROR expected function, found `!` + return + { return () } //~ ERROR the type of this value must be known in this context + () //~^ ERROR the type of this value must be known in this context +//~^^ ERROR notation; the first type parameter for the function trait is neither a tuple nor unit +//~^^^ ERROR overloaded calls are experimental + ; } diff --git a/src/test/compile-fail/issue-17373.rs b/src/test/compile-fail/issue-17373.rs index 9cfae2662ac00..6895893adc4d0 100644 --- a/src/test/compile-fail/issue-17373.rs +++ b/src/test/compile-fail/issue-17373.rs @@ -9,5 +9,6 @@ // except according to those terms. fn main() { - *return; //~ ERROR type `!` cannot be dereferenced + *return //~ ERROR the type of this value must be known in this context + ; } diff --git a/src/test/compile-fail/issue-5100.rs b/src/test/compile-fail/issue-5100.rs index 6524056df888b..3ddb713528c55 100644 --- a/src/test/compile-fail/issue-5100.rs +++ b/src/test/compile-fail/issue-5100.rs @@ -27,13 +27,13 @@ fn main() { match (true, false) { box (true, false) => () - //~^ ERROR mismatched types: expected `(bool,bool)`, found `Box<>` + //~^ ERROR mismatched types: expected `(bool,bool)`, found `Box<>` // (expected tuple, found box) } match (true, false) { &(true, false) => () - //~^ ERROR mismatched types: expected `(bool,bool)`, found `&` + //~^ ERROR mismatched types: expected `(bool,bool)`, found `&` // (expected tuple, found &-ptr) } diff --git a/src/test/run-fail/issue-5500.rs b/src/test/compile-fail/issue-5500.rs similarity index 83% rename from src/test/run-fail/issue-5500.rs rename to src/test/compile-fail/issue-5500.rs index 45dbe11c76e82..e972379944acc 100644 --- a/src/test/run-fail/issue-5500.rs +++ b/src/test/compile-fail/issue-5500.rs @@ -8,8 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:explicit failure - fn main() { &fail!() + //~^ ERROR mismatched types: expected `()`, found `&` (expected (), found &-ptr) } diff --git a/src/test/compile-fail/issue-897-2.rs b/src/test/compile-fail/issue-897-2.rs index 45b45dbaf5da4..e6b97b727a7e9 100644 --- a/src/test/compile-fail/issue-897-2.rs +++ b/src/test/compile-fail/issue-897-2.rs @@ -12,8 +12,12 @@ fn g() -> ! { fail!(); } fn f() -> ! { - return g(); - g(); //~ ERROR: unreachable statement + return g(); //~ ERROR `return` in a function declared as diverging + g(); +} +fn h() -> ! { + loop {} + g(); } fn main() { f() } diff --git a/src/test/compile-fail/issue-897.rs b/src/test/compile-fail/issue-897.rs index f3429b97cfcb9..944546d0b4a61 100644 --- a/src/test/compile-fail/issue-897.rs +++ b/src/test/compile-fail/issue-897.rs @@ -8,12 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern: unreachable statement - #![deny(unreachable_code)] fn f() -> ! { - return fail!(); + return fail!(); //~ ERROR `return` in a function declared as diverging fail!(); // the unreachable statement error is in , at this line, there // only is a note } diff --git a/src/test/compile-fail/liveness-bad-bang-2.rs b/src/test/compile-fail/liveness-bad-bang-2.rs index f5ad5e6af0c8a..04364e4e01088 100644 --- a/src/test/compile-fail/liveness-bad-bang-2.rs +++ b/src/test/compile-fail/liveness-bad-bang-2.rs @@ -9,8 +9,9 @@ // except according to those terms. // Tests that a function with a ! annotation always actually fails -// error-pattern: some control paths may return -fn bad_bang(i: uint) -> ! { println!("{}", 3i); } +fn bad_bang(i: uint) -> ! { //~ ERROR computation may converge in a function marked as diverging + println!("{}", 3i); +} fn main() { bad_bang(5u); } diff --git a/src/test/compile-fail/loop-does-not-diverge.rs b/src/test/compile-fail/loop-does-not-diverge.rs index d0e5249305493..9488e6bf969f0 100644 --- a/src/test/compile-fail/loop-does-not-diverge.rs +++ b/src/test/compile-fail/loop-does-not-diverge.rs @@ -14,7 +14,7 @@ fn forever() -> ! { loop { break; } - return 42i; //~ ERROR expected `!`, found `int` + return 42i; //~ ERROR `return` in a function declared as diverging } fn main() { diff --git a/src/test/compile-fail/lub-match.rs b/src/test/compile-fail/lub-match.rs index febe5f45d96b0..df39e9e894eae 100644 --- a/src/test/compile-fail/lub-match.rs +++ b/src/test/compile-fail/lub-match.rs @@ -33,7 +33,8 @@ pub fn opt_str1<'a>(maybestr: &'a Option) -> &'a str { } pub fn opt_str2<'a>(maybestr: &'a Option) -> &'static str { - match *maybestr { //~ ERROR cannot infer an appropriate lifetime for automatic coercion due to + match *maybestr { + //~^ ERROR cannot infer an appropriate lifetime due to conflicting requirements None => "(none)", Some(ref s) => { let s: &'a str = s.as_slice(); @@ -43,7 +44,8 @@ pub fn opt_str2<'a>(maybestr: &'a Option) -> &'static str { } pub fn opt_str3<'a>(maybestr: &'a Option) -> &'static str { - match *maybestr { //~ ERROR cannot infer an appropriate lifetime for automatic coercion due to + match *maybestr { + //~^ ERROR cannot infer an appropriate lifetime due to conflicting requirements Some(ref s) => { let s: &'a str = s.as_slice(); s diff --git a/src/test/run-pass/issue-13352.rs b/src/test/run-pass/issue-13352.rs index f0477817281a1..afebcb3cdfc7d 100644 --- a/src/test/run-pass/issue-13352.rs +++ b/src/test/run-pass/issue-13352.rs @@ -17,5 +17,4 @@ fn main() { unsafe { libc::exit(0 as libc::c_int); } }); 2u + (loop {}); - -(loop {}); } diff --git a/src/test/run-pass/unreachable-code.rs b/src/test/run-pass/unreachable-code.rs index 6b6754f34326d..496be7e0e6abb 100644 --- a/src/test/run-pass/unreachable-code.rs +++ b/src/test/run-pass/unreachable-code.rs @@ -23,8 +23,6 @@ fn call_id_2() { id(true) && id(return); } fn call_id_3() { id(return) && id(return); } -fn ret_ret() -> int { return (return 2i) + 3i; } - fn ret_guard() { match 2i { x if (return) => { x; }