Remove ty_bot from the type system

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

View File

@ -149,7 +149,7 @@ pub trait TyVisitor {
fn visit_fn_input(&mut self, i: uint, mode: uint,
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;

View File

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

View File

@ -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) {

View File

@ -359,7 +359,6 @@ fn parse_trait_ref(st: &mut PState, conv: conv_did) -> ty::TraitRef {
fn parse_ty(st: &mut PState, conv: conv_did) -> ty::t {
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}
}

View File

@ -200,7 +200,6 @@ pub fn enc_trait_store(w: &mut SeekableMemWriter, cx: &ctxt, s: ty::TraitStore)
fn enc_sty(w: &mut SeekableMemWriter, cx: &ctxt, st: &ty::sty) {
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) {

View File

@ -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

View File

@ -396,13 +396,9 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,TYPER> {
// make sure that the thing we are pointing out stays valid
// 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) => {

View File

@ -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

View File

@ -100,11 +100,16 @@
* - `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.
*/
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 +255,8 @@ struct LocalInfo {
enum VarKind {
Arg(NodeId, Ident),
Local(LocalInfo),
ImplicitRet
ImplicitRet,
CleanExit
}
struct IrMaps<'a, 'tcx: 'a> {
@ -306,7 +312,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 +337,8 @@ impl<'a, 'tcx> IrMaps<'a, 'tcx> {
Local(LocalInfo { ident: nm, .. }) | Arg(_, nm) => {
token::get_ident(nm).get().to_string()
},
ImplicitRet => "<implicit-ret>".to_string()
ImplicitRet => "<implicit-ret>".to_string(),
CleanExit => "<clean-exit>".to_string()
}
}
@ -397,7 +404,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 +554,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 +882,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 +953,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 +1154,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 +1168,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 +1516,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:
match self.fn_ret(id) {
ty::FnConverging(t_ret)
if self.live_on_entry(entry_ln, self.s.no_ret_var).is_some() => {
let t_ret = ty::ty_fn_ret(ty::node_id_to_type(self.ir.tcx, id));
if ty::type_is_nil(t_ret) {
// 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
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");
}
_ => {}
}
}

View File

@ -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]
}

View File

@ -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(..) |

View File

@ -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())
});

View File

@ -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)

View File

@ -180,7 +180,7 @@ impl<'a, 'tcx> Drop for StatRecorder<'a, 'tcx> {
// only use this for foreign function ABIs and glue, use `decl_rust_fn` for Rust functions
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<Span>,
block_arena: &'a TypedArena<common::BlockS<'a, 'tcx>>)
@ -1432,8 +1429,13 @@ pub fn new_fn_ctxt<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>,
},
id, param_substs.repr(ccx.tcx()));
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<ty::t>,
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));
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));
// 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);
// 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
}

View File

@ -375,10 +375,9 @@ pub fn trans_unboxing_shim(bcx: Block,
llshimmedargs.push(get_param(fcx.llfn, fcx.arg_pos(i) as u32));
}
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,8 +876,11 @@ pub fn trans_call_inner<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
_ => {}
}
if ty::type_is_bot(ret_ty) {
Unreachable(bcx);
match ret_ty {
ty::FnConverging(_) => {},
ty::FnDiverging => {
Unreachable(bcx);
}
}
Result::new(bcx, llresult)
@ -1118,52 +1123,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).
// 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);
}
}
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);
unsafe {
val = llvm::LLVMGetUndef(llformal_arg_ty.to_ref());
}
} 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);
}
}
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!("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));

View File

@ -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.

View File

@ -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<T:Subst+Clone> SubstP for T {
impl<T: Subst + Clone> SubstP for T {
fn substp(&self, tcx: &ty::ctxt, substs: &param_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()
}

View File

@ -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,
};

View File

@ -650,7 +650,7 @@ impl<K: KindOps + fmt::Show> Datum<K> {
}
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)
}
}

View File

@ -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<DIType> = 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(..) => {

View File

@ -609,7 +609,7 @@ fn trans_datum_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
start.as_ref().map(|e| args.push((unpack_datum!(bcx, trans(bcx, &**e)), e.id)));
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,

View File

@ -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<ValueRef>;
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::<Vec<_>>().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
}
}

View File

@ -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
}

View File

@ -149,12 +149,12 @@ pub fn trans_intrinsic_call<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, node: ast::N
ty::ty_bare_fn(ref f) => f.sig.output,
_ => 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);
// The only intrinsic function that diverges.
if name.get() == "abort" {
let llfn = ccx.get_intrinsic(&("llvm.trap"));
Call(bcx, llfn, [], None);
Unreachable(bcx);
return Result::new(bcx, C_undef(Type::nil(ccx).ptr_to()));
} else if name.get() == "unreachable" {
Unreachable(bcx);
return Result::new(bcx, C_nil(ccx));
}
let ret_ty = match ret_ty {
ty::FnConverging(ret_ty) => ret_ty,
ty::FnDiverging => unreachable!()
};
let llret_ty = type_of::type_of(ccx, ret_ty);
// Get location to store the result. If the user does
// 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)

View File

@ -97,7 +97,7 @@ pub fn untuple_arguments_if_necessary(ccx: &CrateContext,
pub fn type_of_rust_fn(cx: &CrateContext,
llenvironment_type: Option<Type>,
inputs: &[ty::t],
output: ty::t,
output: ty::FnOutput,
abi: abi::Abi)
-> Type {
let mut atys: Vec<Type> = 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),

View File

@ -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<t>,
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<BuiltinBounds>),
terr_variadic_mismatch(expected_found<bool>),
terr_cyclic_ty,
terr_convergence_mismatch(expected_found<bool>)
}
/// Bounds suitable for a named type parameter like `A` in `fn foo<A>`
@ -1578,7 +1586,6 @@ pub fn mk_t(cx: &ctxt, st: sty) -> t {
ty_uint(u) => return mk_mach_uint(u),
ty_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<ty::Region>,
accum_substs(accumulator, substs);
}
ty_nil |
ty_bot |
ty_bool |
ty_char |
ty_int(_) |

View File

@ -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(..) => {

View File

@ -793,7 +793,7 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>(
let typ = ast_ty_to_builtin_ty(this, rscope, ast_ty).unwrap_or_else(|| {
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 {

View File

@ -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
};

View File

@ -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) {

View File

@ -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<FnStyleState>,
@ -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<ty::t> =
arg_tys.iter().chain([ret_ty].iter()).map(|&ty| ty).collect();
let mut fn_sig_tys: Vec<ty::t> =
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<T>(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<ast::Expr>],
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<ty::t> {
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,

View File

@ -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::<ast::Expr>.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);

View File

@ -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(..) |

View File

@ -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);

View File

@ -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(_, _) => {

View File

@ -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 {

View File

@ -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<t
let b_sty = &ty::get(b).sty;
debug!("super_tys: a_sty={} b_sty={}", a_sty, b_sty);
return match (a_sty, b_sty) {
// The "subtype" ought to be handling cases involving bot or var:
(&ty::ty_bot, _) |
(_, &ty::ty_bot) |
// The "subtype" ought to be handling cases involving var:
(&ty::ty_infer(TyVar(_)), _) |
(_, &ty::ty_infer(TyVar(_))) => {
tcx.sess.bug(

View File

@ -112,15 +112,6 @@ impl<'f, 'tcx> Combine<'tcx> for Equate<'f, 'tcx> {
let a = infcx.type_variables.borrow().replace_if_possible(a);
let 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)

View File

@ -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<ty::t>;
// 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<ty::t> {
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<ty::t> {
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(..))) => {

View File

@ -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<ty::t> {

View File

@ -205,7 +205,8 @@ impl<'a, 'tcx> ResolveState<'a, 'tcx> {
pub fn resolve_ty_var(&mut self, vid: TyVid) -> ty::t {
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 {

View File

@ -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(..) |

View File

@ -129,10 +129,6 @@ impl<'f, 'tcx> Combine<'tcx> for Sub<'f, 'tcx> {
let a = infcx.type_variables.borrow().replace_if_possible(a);
let 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)
}

View File

@ -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 }
}

View File

@ -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
}
});

View File

@ -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

View File

@ -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,19 @@ impl Repr for ty::FnSig {
}
}
impl Repr for ty::FnOutput {
fn repr(&self, tcx: &ctxt) -> String {
match *self {
ty::FnConverging(ty) => {
format!("FnConverging({0})", ty.repr(tcx))
}
ty::FnDiverging => {
"FnDiverging".to_string()
}
}
}
}
impl Repr for typeck::MethodCallee {
fn repr(&self, tcx: &ctxt) -> String {
format!("MethodCallee {{origin: {}, ty: {}, {}}}",

View File

@ -880,6 +880,15 @@ impl Clean<FnDecl> for ast::FnDecl {
}
}
impl<'a> Clean<Type> for ty::FnOutput {
fn clean(&self, cx: &DocContext) -> Type {
match *self {
ty::FnConverging(ty) => ty.clean(cx),
ty::FnDiverging => Bottom
}
}
}
impl<'a> Clean<FnDecl> for (ast::DefId, &'a ty::FnSig) {
fn clean(&self, cx: &DocContext) -> FnDecl {
let (did, sig) = *self;
@ -1258,7 +1267,6 @@ impl Clean<Type> for ast::Ty {
impl Clean<Type> 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),

View File

@ -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 unconstrained type variable
}