Remove ty_bot from the type system
We now instead use a fresh variable for expressions that diverge.
This commit is contained in:
parent
58dc0a05ab
commit
cca84e9e21
@ -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;
|
||||
|
||||
|
@ -145,5 +145,6 @@ register_diagnostics!(
|
||||
E0162,
|
||||
E0163,
|
||||
E0164,
|
||||
E0165
|
||||
E0165,
|
||||
E0166
|
||||
)
|
||||
|
@ -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) {
|
||||
|
@ -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}
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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) => {
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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]
|
||||
}
|
||||
|
@ -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(..) |
|
||||
|
@ -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())
|
||||
});
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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.
|
||||
|
@ -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: ¶m_substs) -> T {
|
||||
self.subst(tcx, &substs.substs)
|
||||
}
|
||||
@ -343,9 +342,12 @@ impl<'a, 'tcx> FunctionContext<'a, 'tcx> {
|
||||
self.llreturn.get().unwrap()
|
||||
}
|
||||
|
||||
pub fn get_ret_slot(&self, bcx: Block, ty: ty::t, name: &str) -> ValueRef {
|
||||
pub fn get_ret_slot(&self, bcx: Block, output: ty::FnOutput, name: &str) -> ValueRef {
|
||||
if self.needs_ret_allocas {
|
||||
base::alloca_no_lifetime(bcx, type_of::type_of(bcx.ccx(), ty), name)
|
||||
base::alloca_no_lifetime(bcx, match output {
|
||||
ty::FnConverging(output_type) => type_of::type_of(bcx.ccx(), output_type),
|
||||
ty::FnDiverging => Type::void(bcx.ccx())
|
||||
}, name)
|
||||
} else {
|
||||
self.llretslotptr.get().unwrap()
|
||||
}
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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(..) => {
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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),
|
||||
|
@ -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(_) |
|
||||
|
@ -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(..) => {
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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(..) |
|
||||
|
@ -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);
|
||||
|
@ -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(_, _) => {
|
||||
|
@ -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 {
|
||||
|
@ -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(
|
||||
|
@ -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)
|
||||
|
@ -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(..))) => {
|
||||
|
@ -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> {
|
||||
|
@ -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 {
|
||||
|
@ -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(..) |
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 }
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
});
|
||||
|
@ -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
|
||||
|
@ -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: {}, {}}}",
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user