We now instead use a fresh variable for expressions that diverge.
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;
E0162,
E0163,
E0164,
- E0165
+ E0165,
+ E0166
)
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) {
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(),
'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}
}
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) => {
} 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) {
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
// 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) => {
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
* - `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;
enum VarKind {
Arg(NodeId, Ident),
Local(LocalInfo),
- ImplicitRet
+ ImplicitRet,
+ CleanExit
}
struct IrMaps<'a, 'tcx: 'a> {
Local(LocalInfo { id: node_id, .. }) | Arg(node_id, _) => {
self.variable_map.insert(node_id, v);
},
- ImplicitRet => {}
+ ImplicitRet | CleanExit => {}
}
debug!("{} is {}", v.to_string(), vk);
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()
}
}
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
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;
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)
}
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)
}
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
}
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)
}
}
impl<'a, 'tcx> Liveness<'a, 'tcx> {
+ fn fn_ret(&self, id: NodeId) -> ty::FnOutput {
+ let fn_ty = ty::node_id_to_type(self.ir.tcx, id);
+ match ty::get(fn_ty).sty {
+ ty::ty_unboxed_closure(closure_def_id, _, _) =>
+ self.ir.tcx.unboxed_closures()
+ .borrow()
+ .find(&closure_def_id)
+ .unwrap()
+ .closure_type
+ .sig
+ .output,
+ _ => ty::ty_fn_ret(fn_ty)
+ }
+ }
+
fn check_ret(&self,
id: NodeId,
sp: Span,
_fk: FnKind,
entry_ln: LiveNode,
body: &Block) {
- if self.live_on_entry(entry_ln, self.s.no_ret_var).is_some() {
- // if no_ret_var is live, then we fall off the end of the
- // function without any kind of return expression:
-
- let t_ret = ty::ty_fn_ret(ty::node_id_to_type(self.ir.tcx, id));
- if ty::type_is_nil(t_ret) {
- // for nil return types, it is ok to not return a value expl.
- } else if ty::type_is_bot(t_ret) {
- // for bot return types, not ok. Function should fail.
- self.ir.tcx.sess.span_err(
- sp, "some control paths may return");
- } else {
- let ends_with_stmt = match body.expr {
- None if body.stmts.len() > 0 =>
- match body.stmts.last().unwrap().node {
- StmtSemi(ref e, _) => {
- let t_stmt = ty::expr_ty(self.ir.tcx, &**e);
- ty::get(t_stmt).sty == ty::get(t_ret).sty
+ match self.fn_ret(id) {
+ ty::FnConverging(t_ret)
+ if self.live_on_entry(entry_ln, self.s.no_ret_var).is_some() => {
+
+ if ty::type_is_nil(t_ret) {
+ // for nil return types, it is ok to not return a value expl.
+ } else {
+ let ends_with_stmt = match body.expr {
+ None if body.stmts.len() > 0 =>
+ match body.stmts.last().unwrap().node {
+ StmtSemi(ref e, _) => {
+ let t_stmt = ty::expr_ty(self.ir.tcx, &**e);
+ ty::get(t_stmt).sty == ty::get(t_ret).sty
+ },
+ _ => false
},
- _ => false
- },
- _ => false
- };
- self.ir.tcx.sess.span_err(
- sp, "not all control paths return a value");
- if ends_with_stmt {
- let last_stmt = body.stmts.last().unwrap();
- let original_span = original_sp(self.ir.tcx.sess.codemap(),
- last_stmt.span, sp);
- let span_semicolon = Span {
- lo: original_span.hi - BytePos(1),
- hi: original_span.hi,
- expn_id: original_span.expn_id
+ _ => false
};
- self.ir.tcx.sess.span_note(
- span_semicolon, "consider removing this semicolon:");
+ self.ir.tcx.sess.span_err(
+ sp, "not all control paths return a value");
+ if ends_with_stmt {
+ let last_stmt = body.stmts.last().unwrap();
+ let original_span = original_sp(self.ir.tcx.sess.codemap(),
+ last_stmt.span, sp);
+ let span_semicolon = Span {
+ lo: original_span.hi - BytePos(1),
+ hi: original_span.hi,
+ expn_id: original_span.expn_id
+ };
+ self.ir.tcx.sess.span_note(
+ span_semicolon, "consider removing this semicolon:");
+ }
}
- }
+ }
+ ty::FnDiverging
+ if self.live_on_entry(entry_ln, self.s.clean_exit_var).is_some() => {
+ self.ir.tcx.sess.span_err(sp,
+ "computation may converge in a function marked as diverging");
+ }
+
+ _ => {}
}
}
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(),
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
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]
}
match ty::get(ty).sty {
ty::ty_nil |
- ty::ty_bot |
ty::ty_bool |
ty::ty_char |
ty::ty_int(..) |
ty::ty_uint(_) |
ty::ty_int(_) |
ty::ty_nil |
- ty::ty_bot |
ty::ty_bool |
ty::ty_float(_) |
ty::ty_bare_fn(_) |
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())
});
// 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)
// 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 {
}
});
- 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 {
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
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
}
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>>)
},
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);
/// 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.
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)));
+ }
}
}
// 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() {
}
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);
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)))
+ }
}
}
}
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,
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
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 {}",
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,
_ => fn_sig.inputs.clone()
};
- // A function pointer is called without the declaration
- // available, so we have to apply any attributes with ABI
- // implications directly to the call instruction. Right now,
- // the only attribute we need to worry about is `sret`.
- if type_of::return_uses_outptr(ccx, ret_ty) {
- let llret_sz = llsize_of_real(ccx, type_of::type_of(ccx, ret_ty));
-
- // The outptr can be noalias and nocapture because it's entirely
- // invisible to the program. We also know it's nonnull as well
- // as how many bytes we can dereference
- attrs.arg(1, llvm::StructRetAttribute)
- .arg(1, llvm::NoAliasAttribute)
- .arg(1, llvm::NoCaptureAttribute)
- .arg(1, llvm::DereferenceableAttribute(llret_sz));
-
- // Add one more since there's an outptr
- first_arg_offset += 1;
- } else {
- // The `noalias` attribute on the return value is useful to a
- // function ptr caller.
- match ty::get(ret_ty).sty {
- // `~` pointer return values never alias because ownership
- // is transferred
- ty::ty_uniq(it) if !ty::type_is_sized(ccx.tcx(), it) => {}
- ty::ty_uniq(_) => {
- attrs.ret(llvm::NoAliasAttribute);
+ if let ty::FnConverging(ret_ty) = ret_ty {
+ // A function pointer is called without the declaration
+ // available, so we have to apply any attributes with ABI
+ // implications directly to the call instruction. Right now,
+ // the only attribute we need to worry about is `sret`.
+ if type_of::return_uses_outptr(ccx, ret_ty) {
+ let llret_sz = llsize_of_real(ccx, type_of::type_of(ccx, ret_ty));
+
+ // The outptr can be noalias and nocapture because it's entirely
+ // invisible to the program. We also know it's nonnull as well
+ // as how many bytes we can dereference
+ attrs.arg(1, llvm::StructRetAttribute)
+ .arg(1, llvm::NoAliasAttribute)
+ .arg(1, llvm::NoCaptureAttribute)
+ .arg(1, llvm::DereferenceableAttribute(llret_sz));
+
+ // Add one more since there's an outptr
+ first_arg_offset += 1;
+ } else {
+ // The `noalias` attribute on the return value is useful to a
+ // function ptr caller.
+ match ty::get(ret_ty).sty {
+ // `~` pointer return values never alias because ownership
+ // is transferred
+ ty::ty_uniq(it) if !ty::type_is_sized(ccx.tcx(), it) => {}
+ ty::ty_uniq(_) => {
+ attrs.ret(llvm::NoAliasAttribute);
+ }
+ _ => {}
}
- _ => {}
- }
- // We can also mark the return value as `dereferenceable` in certain cases
- match ty::get(ret_ty).sty {
- // These are not really pointers but pairs, (pointer, len)
- ty::ty_uniq(it) |
- ty::ty_rptr(_, ty::mt { ty: it, .. }) if !ty::type_is_sized(ccx.tcx(), it) => {}
- ty::ty_uniq(inner) | ty::ty_rptr(_, ty::mt { ty: inner, .. }) => {
- let llret_sz = llsize_of_real(ccx, type_of::type_of(ccx, inner));
- attrs.ret(llvm::DereferenceableAttribute(llret_sz));
+ // We can also mark the return value as `dereferenceable` in certain cases
+ match ty::get(ret_ty).sty {
+ // These are not really pointers but pairs, (pointer, len)
+ ty::ty_uniq(it) |
+ ty::ty_rptr(_, ty::mt { ty: it, .. }) if !ty::type_is_sized(ccx.tcx(), it) => {}
+ ty::ty_uniq(inner) | ty::ty_rptr(_, ty::mt { ty: inner, .. }) => {
+ let llret_sz = llsize_of_real(ccx, type_of::type_of(ccx, inner));
+ attrs.ret(llvm::DereferenceableAttribute(llret_sz));
+ }
+ _ => {}
}
- _ => {}
- }
- match ty::get(ret_ty).sty {
- ty::ty_bool => {
- attrs.ret(llvm::ZExtAttribute);
+ match ty::get(ret_ty).sty {
+ ty::ty_bool => {
+ attrs.ret(llvm::ZExtAttribute);
+ }
+ _ => {}
}
- _ => {}
}
}
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
}
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,
// 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())
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),
_ => {}
}
// 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
// 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);
_ => {}
}
- if ty::type_is_bot(ret_ty) {
- Unreachable(bcx);
+ match ret_ty {
+ ty::FnConverging(_) => {},
+ ty::FnDiverging => {
+ Unreachable(bcx);
+ }
}
Result::new(bcx, llresult)
debug!(" arg datum: {}", arg_datum.to_string(bcx.ccx()));
let mut val;
- if ty::type_is_bot(arg_datum_ty) {
- // For values of type _|_, we generate an
- // "undef" value, as such a value should never
- // be inspected. It's important for the value
- // to have type lldestty (the callee's expected type).
- let llformal_arg_ty = type_of::type_of_explicit_arg(ccx, formal_arg_ty);
- unsafe {
- val = llvm::LLVMGetUndef(llformal_arg_ty.to_ref());
+ // FIXME(#3548) use the adjustments table
+ match autoref_arg {
+ DoAutorefArg(arg_id) => {
+ // We will pass argument by reference
+ // We want an lvalue, so that we can pass by reference and
+ let arg_datum = unpack_datum!(
+ bcx, arg_datum.to_lvalue_datum(bcx, "arg", arg_id));
+ val = arg_datum.val;
}
- } else {
- // FIXME(#3548) use the adjustments table
- match autoref_arg {
- DoAutorefArg(arg_id) => {
- // We will pass argument by reference
- // We want an lvalue, so that we can pass by reference and
- let arg_datum = unpack_datum!(
- bcx, arg_datum.to_lvalue_datum(bcx, "arg", arg_id));
- val = arg_datum.val;
- }
- DontAutorefArg => {
- // Make this an rvalue, since we are going to be
- // passing ownership.
- let arg_datum = unpack_datum!(
- bcx, arg_datum.to_rvalue_datum(bcx, "arg"));
-
- // Now that arg_datum is owned, get it into the appropriate
- // mode (ref vs value).
- let arg_datum = unpack_datum!(
- bcx, arg_datum.to_appropriate_datum(bcx));
-
- // Technically, ownership of val passes to the callee.
- // However, we must cleanup should we fail before the
- // callee is actually invoked.
- val = arg_datum.add_clean(bcx.fcx, arg_cleanup_scope);
- }
+ DontAutorefArg => {
+ // Make this an rvalue, since we are going to be
+ // passing ownership.
+ let arg_datum = unpack_datum!(
+ bcx, arg_datum.to_rvalue_datum(bcx, "arg"));
+
+ // Now that arg_datum is owned, get it into the appropriate
+ // mode (ref vs value).
+ let arg_datum = unpack_datum!(
+ bcx, arg_datum.to_appropriate_datum(bcx));
+
+ // Technically, ownership of val passes to the callee.
+ // However, we must cleanup should we fail before the
+ // callee is actually invoked.
+ val = arg_datum.add_clean(bcx.fcx, arg_cleanup_scope);
}
+ }
- if formal_arg_ty != arg_datum_ty {
- // this could happen due to e.g. subtyping
- let llformal_arg_ty = type_of::type_of_explicit_arg(ccx, formal_arg_ty);
- debug!("casting actual type ({}) to match formal ({})",
- bcx.val_to_string(val), bcx.llty_str(llformal_arg_ty));
- debug!("Rust types: {}; {}", ty_to_string(bcx.tcx(), arg_datum_ty),
- ty_to_string(bcx.tcx(), formal_arg_ty));
- val = PointerCast(bcx, val, llformal_arg_ty);
- }
+ if formal_arg_ty != arg_datum_ty {
+ // this could happen due to e.g. subtyping
+ let llformal_arg_ty = type_of::type_of_explicit_arg(ccx, formal_arg_ty);
+ debug!("casting actual type ({}) to match formal ({})",
+ bcx.val_to_string(val), bcx.llty_str(llformal_arg_ty));
+ debug!("Rust types: {}; {}", ty_to_string(bcx.tcx(), arg_datum_ty),
+ ty_to_string(bcx.tcx(), formal_arg_ty));
+ val = PointerCast(bcx, val, llformal_arg_ty);
}
debug!("--- trans_arg_datum passing {}", bcx.val_to_string(val));
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.
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;
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);
* 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
-> Self;
}
-impl<T:Subst+Clone> SubstP for T {
+impl<T: Subst + Clone> SubstP for T {
fn substp(&self, tcx: &ty::ctxt, substs: ¶m_substs) -> T {
self.subst(tcx, &substs.substs)
}
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()
}
.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);
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;
}
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,
};
}
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)
}
}
match ty::get(type_).sty {
ty::ty_nil |
- ty::ty_bot |
ty::ty_bool |
ty::ty_char |
ty::ty_str |
}
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,
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(':');
}
}
+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 {
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
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(_) |
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"),
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,
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(..) => {
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,
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,
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);
} 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)
_ => 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,
/// 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 {
// 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,
}
_ => 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();
// 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,
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
}
_ => 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));
};
// 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();
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);
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();
*/
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
}
}
_ => 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={} -> {}, \
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
}
}
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);
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
}
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);
}
}
+ // 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 {
}
};
- // 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)
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();
// 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 {
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
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),
}
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),
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,
}
}
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.
pub struct FnSig {
pub binder_id: ast::NodeId,
pub inputs: Vec<t>,
- pub output: t,
+ pub output: FnOutput,
pub variadic: bool
}
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,
#[deriving(Clone, PartialEq, Eq, Hash, Show)]
pub enum sty {
ty_nil,
- ty_bot,
ty_bool,
ty_char,
ty_int(ast::IntTy),
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>`
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(),
_ => {}
};
// 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 {
&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 {
_ => {}
}
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);
}
}
#[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) }
sig: FnSig {
binder_id: binder_id,
inputs: input_args,
- output: output,
+ output: ty::FnConverging(output),
variadic: false
}
})
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) => {
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);
+ }
}
}
}
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)
}
pub fn type_is_ty_var(ty: t) -> bool {
match get(ty).sty {
- ty_infer(TyVar(_)) => true,
- _ => false
+ ty_infer(TyVar(_)) => true,
+ _ => false
}
}
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) =>
// 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
}
// 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);
ty_vec(ty, Some(_)) => type_requires(cx, seen, r_ty, ty),
ty_nil |
- ty_bot |
ty_bool |
ty_char |
ty_int(_) |
}
}
-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,
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 => {}
}
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)
}
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" })
+ }
}
}
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;
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
}
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) => {
accum_substs(accumulator, substs);
}
ty_nil |
- ty_bot |
ty_bool |
ty_char |
ty_int(_) |
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
}
}
+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)
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
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(..) => {
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))
.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 {
}).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 {
// 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);
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
};
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(..) |
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) {
// 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>,
// 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 {
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,
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
}
};
// 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));
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);
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| {
}
}
+ 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 {}",
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());
}
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 => {}
}
None => {}
}
- ty::deref(ref_ty, true)
+ match ref_ty {
+ ty::FnConverging(ref_ty) =>
+ ty::deref(ref_ty, true),
+ ty::FnDiverging =>
+ None
+ }
}
None => None,
}
}
None => {}
}
- Some(ty::mt { ty: result_ty, mutbl: ast::MutImmutable })
+ match result_ty {
+ ty::FnConverging(result_ty) =>
+ Some(ty::mt { ty: result_ty, mutbl: ast::MutImmutable }),
+ ty::FnDiverging =>
+ None
+ }
}
None => None,
}
// 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) => {
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,
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) => {
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
/// 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,
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
};
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
};
// 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();
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();
- }
}
}
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);
// 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);
}
}
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);
};
}
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);
}
}
}
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
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,
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);
}
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);
}
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);
}
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) => {
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);
_ => None
}
});
- let mut bot_field = false;
let mut err_field = false;
let elt_ts = elts.iter().enumerate().map(|(i, e)| {
}
};
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);
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);
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;
}
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;
}
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);
}
}
};
_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);
}
}
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 */ }
// 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);
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);
}
};
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,
"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
}
};
- 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);
}
}
};
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);
}
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);
}
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)) => {
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)),
"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()),
"unrecognized intrinsic function: `{}`", *other);
return;
}
- }
+ };
+ (n_tps, inputs, ty::FnConverging(output))
};
let fty = ty::mk_bare_fn(tcx, ty::BareFnTy {
fn_style: ast::UnsafeFn,
/// 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();
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)
};
// 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
};
*/
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);
match ty::get(ty).sty {
ty::ty_nil |
- ty::ty_bot |
ty::ty_bool |
ty::ty_char |
ty::ty_int(..) |
&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);
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};
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(_, _) => {
.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,
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 {
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,
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(
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)
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));
}
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));
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(..))) => {
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)
}
}
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> {
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)
}
}
ty::mk_var(tcx, vid)
}
- };
- return t1;
+ }
}
pub fn resolve_int_var(&mut self, vid: IntVid) -> ty::t {
}
ty::ty_nil |
- ty::ty_bot |
ty::ty_bool |
ty::ty_char |
ty::ty_int(..) |
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()
Ok(ty::mk_err())
}
- (_, &ty::ty_bot) => {
- Err(ty::terr_sorts(expected_found(self, a, b)))
- }
-
_ => {
super_tys(self, a, b)
}
}
struct TypeVariableData {
- value: TypeVariableValue
+ value: TypeVariableValue,
+ diverging: bool
}
enum TypeVariableValue {
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`.
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 }
}
sig: ty::FnSig {
binder_id: main_id,
inputs: Vec::new(),
- output: ty::mk_nil(),
+ output: ty::FnConverging(ty::mk_nil()),
variadic: false
}
});
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
}
});
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 */
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
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};
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(" -> !");
}
}
}
// 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(),
}
}
+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: {}, {}}}",
}
}
+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;
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),
// 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
}