mk_tup(cx, Vec::new())
}
+pub fn mk_bool<'tcx>(cx: &ctxt<'tcx>) -> Ty<'tcx> {
+ mk_t(cx, ty_bool)
+}
+
pub fn mk_bare_fn<'tcx>(cx: &ctxt<'tcx>,
opt_def_id: Option<ast::DefId>,
fty: &'tcx BareFnTy<'tcx>) -> Ty<'tcx> {
/// Returns true if this type is a floating point type and false otherwise.
pub fn type_is_floating_point(ty: Ty) -> bool {
match ty.sty {
- ty_float(_) => true,
- _ => false,
+ ty_float(_) |
+ ty_infer(FloatVar(_)) =>
+ true,
+
+ _ =>
+ false,
}
}
pub fn enum_variants<'tcx>(cx: &ctxt<'tcx>, id: ast::DefId)
-> Rc<Vec<Rc<VariantInfo<'tcx>>>> {
- use std::num::Int; // For checked_add
memoized(&cx.enum_var_cache, id, |id: ast::DefId| {
if ast::LOCAL_CRATE != id.krate {
Rc::new(csearch::get_enum_variants(cx, id))
}
}
-pub fn is_binopable<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>, op: ast::BinOp) -> bool {
- #![allow(non_upper_case_globals)]
- const tycat_other: isize = 0;
- const tycat_bool: isize = 1;
- const tycat_char: isize = 2;
- const tycat_int: isize = 3;
- const tycat_float: isize = 4;
- const tycat_raw_ptr: isize = 6;
-
- const opcat_add: isize = 0;
- const opcat_sub: isize = 1;
- const opcat_mult: isize = 2;
- const opcat_shift: isize = 3;
- const opcat_rel: isize = 4;
- const opcat_eq: isize = 5;
- const opcat_bit: isize = 6;
- const opcat_logic: isize = 7;
- const opcat_mod: isize = 8;
-
- fn opcat(op: ast::BinOp) -> isize {
- match op.node {
- ast::BiAdd => opcat_add,
- ast::BiSub => opcat_sub,
- ast::BiMul => opcat_mult,
- ast::BiDiv => opcat_mult,
- ast::BiRem => opcat_mod,
- ast::BiAnd => opcat_logic,
- ast::BiOr => opcat_logic,
- ast::BiBitXor => opcat_bit,
- ast::BiBitAnd => opcat_bit,
- ast::BiBitOr => opcat_bit,
- ast::BiShl => opcat_shift,
- ast::BiShr => opcat_shift,
- ast::BiEq => opcat_eq,
- ast::BiNe => opcat_eq,
- ast::BiLt => opcat_rel,
- ast::BiLe => opcat_rel,
- ast::BiGe => opcat_rel,
- ast::BiGt => opcat_rel
- }
- }
-
- fn tycat<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> isize {
- if type_is_simd(cx, ty) {
- return tycat(cx, simd_type(cx, ty))
- }
- match ty.sty {
- ty_char => tycat_char,
- ty_bool => tycat_bool,
- ty_int(_) | ty_uint(_) | ty_infer(IntVar(_)) => tycat_int,
- ty_float(_) | ty_infer(FloatVar(_)) => tycat_float,
- ty_ptr(_) => tycat_raw_ptr,
- _ => tycat_other
- }
- }
-
- const t: bool = true;
- const f: bool = false;
-
- let tbl = [
- // +, -, *, shift, rel, ==, bit, logic, mod
- /*other*/ [f, f, f, f, f, f, f, f, f],
- /*bool*/ [f, f, f, f, t, t, t, t, f],
- /*char*/ [f, f, f, f, t, t, f, f, f],
- /*isize*/ [t, t, t, t, t, t, t, f, t],
- /*float*/ [t, t, t, f, t, t, f, f, f],
- /*bot*/ [t, t, t, t, t, t, t, t, t],
- /*raw ptr*/ [f, f, f, f, t, t, f, f, f]];
-
- return tbl[tycat(cx, ty) as usize ][opcat(op) as usize];
-}
-
// Returns the repeat count for a repeating vector expression.
pub fn eval_repeat_count(tcx: &ctxt, count_expr: &ast::Expr) -> usize {
match const_eval::eval_const_expr_partial(tcx, count_expr, Some(tcx.types.usize)) {
pub use self::LvaluePreference::*;
pub use self::Expectation::*;
pub use self::compare_method::compare_impl_method;
-use self::IsBinopAssignment::*;
use self::TupleArgumentsFlag::*;
use astconv::{self, ast_region_to_region, ast_ty_to_ty, AstConv, PathParamMode};
mod closure;
mod callee;
mod compare_method;
+mod op;
/// closures defined within the function. For example:
///
}
}
-/// Whether `check_binop` is part of an assignment or not.
-/// Used to know whether we allow user overloads and to print
-/// better messages on error.
-#[derive(PartialEq)]
-enum IsBinopAssignment{
- SimpleBinop,
- BinopAssignment,
-}
-
#[derive(Clone)]
pub struct FnCtxt<'a, 'tcx: 'a> {
body_id: ast::NodeId,
/// version, this version will also select obligations if it seems
/// useful, in an effort to get more type information.
fn resolve_type_vars_if_possible(&self, mut ty: Ty<'tcx>) -> Ty<'tcx> {
+ debug!("resolve_type_vars_if_possible(ty={})", ty.repr(self.tcx()));
+
// No ty::infer()? Nothing needs doing.
if !ty::type_has_ty_infer(ty) {
+ debug!("resolve_type_vars_if_possible: ty={}", ty.repr(self.tcx()));
return ty;
}
// If `ty` is a type variable, see whether we already know what it is.
ty = self.infcx().resolve_type_vars_if_possible(&ty);
if !ty::type_has_ty_infer(ty) {
+ debug!("resolve_type_vars_if_possible: ty={}", ty.repr(self.tcx()));
return ty;
}
vtable::select_new_fcx_obligations(self);
ty = self.infcx().resolve_type_vars_if_possible(&ty);
if !ty::type_has_ty_infer(ty) {
+ debug!("resolve_type_vars_if_possible: ty={}", ty.repr(self.tcx()));
return ty;
}
// indirect dependencies that don't seem worth tracking
// precisely.
vtable::select_fcx_obligations_where_possible(self);
- self.infcx().resolve_type_vars_if_possible(&ty)
+ ty = self.infcx().resolve_type_vars_if_possible(&ty);
+
+ debug!("resolve_type_vars_if_possible: ty={}", ty.repr(self.tcx()));
+ ty
}
/// Resolves all type variables in `t` and then, if any were left
{
match method {
Some(method) => {
- let ref_ty = // invoked methods have all LB regions instantiated
- ty::no_late_bound_regions(
- fcx.tcx(), &ty::ty_fn_ret(method.ty)).unwrap();
- match method_call {
- Some(method_call) => {
- fcx.inh.method_map.borrow_mut().insert(method_call,
- method);
- }
- None => {}
- }
- match ref_ty {
- ty::FnConverging(ref_ty) => {
- ty::deref(ref_ty, true)
- }
- ty::FnDiverging => {
- fcx.tcx().sess.bug("index/deref traits do not define a `!` return")
- }
+ // extract method method return type, which will be &T;
+ // all LB regions should have been instantiated during method lookup
+ let ret_ty = ty::ty_fn_ret(method.ty);
+ let ret_ty = ty::no_late_bound_regions(fcx.tcx(), &ret_ty).unwrap().unwrap();
+
+ if let Some(method_call) = method_call {
+ fcx.inh.method_map.borrow_mut().insert(method_call, method);
}
+
+ // method returns &T, but the type as visible to user is T, so deref
+ ty::deref(ret_ty, true)
}
None => None,
}
method_fn_ty: Ty<'tcx>,
callee_expr: &'tcx ast::Expr,
args_no_rcvr: &'tcx [P<ast::Expr>],
- autoref_args: AutorefArgs,
tuple_arguments: TupleArgumentsFlag,
expected: Expectation<'tcx>)
-> ty::FnOutput<'tcx> {
&err_inputs[..],
&[],
args_no_rcvr,
- autoref_args,
false,
tuple_arguments);
ty::FnConverging(fcx.tcx().types.err)
&fty.sig.0.inputs[1..],
&expected_arg_tys[..],
args_no_rcvr,
- autoref_args,
fty.sig.0.variadic,
tuple_arguments);
fty.sig.0.output
fn_inputs: &[Ty<'tcx>],
expected_arg_tys: &[Ty<'tcx>],
args: &'tcx [P<ast::Expr>],
- autoref_args: AutorefArgs,
variadic: bool,
tuple_arguments: TupleArgumentsFlag) {
let tcx = fcx.ccx.tcx;
if is_block == check_blocks {
debug!("checking the argument");
- let mut formal_ty = formal_tys[i];
-
- match autoref_args {
- AutorefArgs::Yes => {
- match formal_ty.sty {
- ty::ty_rptr(_, mt) => formal_ty = mt.ty,
- ty::ty_err => (),
- _ => {
- // So we hit this case when one implements the
- // operator traits but leaves an argument as
- // just T instead of &T. We'll catch it in the
- // mismatch impl/trait method phase no need to
- // ICE here.
- // See: #11450
- formal_ty = tcx.types.err;
- }
- }
- }
- AutorefArgs::No => {}
- }
+ let formal_ty = formal_tys[i];
// The special-cased logic below has three functions:
// 1. Provide as good of an expected type as possible.
TypeAndSubsts { substs: substs, ty: substd_ty }
}
-// Controls whether the arguments are automatically referenced. This is useful
-// for overloaded binary and unary operators.
-#[derive(Copy, PartialEq)]
-pub enum AutorefArgs {
- Yes,
- No,
-}
-
/// Controls whether the arguments are tupled. This is used for the call
/// operator.
///
fn_ty,
expr,
&args[1..],
- AutorefArgs::No,
DontTupleArguments,
expected);
fcx.write_ty(id, if_ty);
}
- fn lookup_op_method<'a, 'tcx, F>(fcx: &'a FnCtxt<'a, 'tcx>,
- op_ex: &'tcx ast::Expr,
- lhs_ty: Ty<'tcx>,
- opname: ast::Name,
- trait_did: Option<ast::DefId>,
- lhs: &'a ast::Expr,
- rhs: Option<&'tcx P<ast::Expr>>,
- unbound_method: F,
- autoref_args: AutorefArgs) -> Ty<'tcx> where
- F: FnOnce(),
- {
- let method = match trait_did {
- Some(trait_did) => {
- // We do eager coercions to make using operators
- // more ergonomic:
- //
- // - If the input is of type &'a T (resp. &'a mut T),
- // then reborrow it to &'b T (resp. &'b mut T) where
- // 'b <= 'a. This makes things like `x == y`, where
- // `x` and `y` are both region pointers, work. We
- // could also solve this with variance or different
- // traits that don't force left and right to have same
- // type.
- let (adj_ty, adjustment) = match lhs_ty.sty {
- ty::ty_rptr(r_in, mt) => {
- let r_adj = fcx.infcx().next_region_var(infer::Autoref(lhs.span));
- fcx.mk_subr(infer::Reborrow(lhs.span), r_adj, *r_in);
- let adjusted_ty = ty::mk_rptr(fcx.tcx(), fcx.tcx().mk_region(r_adj), mt);
- let autoptr = ty::AutoPtr(r_adj, mt.mutbl, None);
- let adjustment = ty::AutoDerefRef { autoderefs: 1, autoref: Some(autoptr) };
- (adjusted_ty, adjustment)
- }
- _ => {
- (lhs_ty, ty::AutoDerefRef { autoderefs: 0, autoref: None })
- }
- };
-
- debug!("adjusted_ty={} adjustment={:?}",
- adj_ty.repr(fcx.tcx()),
- adjustment);
-
- method::lookup_in_trait_adjusted(fcx, op_ex.span, Some(lhs), opname,
- trait_did, adjustment, adj_ty, None)
- }
- None => None
- };
- let args = match rhs {
- Some(rhs) => slice::ref_slice(rhs),
- None => &[][..]
- };
- match method {
- Some(method) => {
- let method_ty = method.ty;
- // HACK(eddyb) Fully qualified path to work around a resolve bug.
- let method_call = ::middle::ty::MethodCall::expr(op_ex.id);
- fcx.inh.method_map.borrow_mut().insert(method_call, method);
- match check_method_argument_types(fcx,
- op_ex.span,
- method_ty,
- op_ex,
- args,
- autoref_args,
- DontTupleArguments,
- NoExpectation) {
- ty::FnConverging(result_type) => result_type,
- ty::FnDiverging => fcx.tcx().types.err
- }
- }
- None => {
- unbound_method();
- // Check the args anyway
- // so we get all the error messages
- let expected_ty = fcx.tcx().types.err;
- check_method_argument_types(fcx,
- op_ex.span,
- expected_ty,
- op_ex,
- args,
- autoref_args,
- DontTupleArguments,
- NoExpectation);
- fcx.tcx().types.err
- }
- }
- }
-
- // could be either an expr_binop or an expr_assign_binop
- fn check_binop<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
- expr: &'tcx ast::Expr,
- op: ast::BinOp,
- lhs: &'tcx ast::Expr,
- rhs: &'tcx P<ast::Expr>,
- is_binop_assignment: IsBinopAssignment) {
- let tcx = fcx.ccx.tcx;
-
- let lvalue_pref = match is_binop_assignment {
- BinopAssignment => PreferMutLvalue,
- SimpleBinop => NoPreference
- };
- check_expr_with_lvalue_pref(fcx, lhs, lvalue_pref);
-
- // Callee does bot / err checking
- let lhs_t =
- structurally_resolve_type_or_else(fcx, lhs.span, fcx.expr_ty(lhs), || {
- if ast_util::is_symmetric_binop(op.node) {
- // Try RHS first
- check_expr(fcx, &**rhs);
- fcx.expr_ty(&**rhs)
- } else {
- fcx.tcx().types.err
- }
- });
-
- if ty::type_is_integral(lhs_t) && ast_util::is_shift_binop(op.node) {
- // Shift is a special case: rhs must be usize, no matter what lhs is
- check_expr(fcx, &**rhs);
- let rhs_ty = fcx.expr_ty(&**rhs);
- let rhs_ty = structurally_resolved_type(fcx, rhs.span, rhs_ty);
- if ty::type_is_integral(rhs_ty) {
- fcx.write_ty(expr.id, lhs_t);
- } else {
- fcx.type_error_message(
- expr.span,
- |actual| {
- format!(
- "right-hand-side of a shift operation must have integral type, \
- not `{}`",
- actual)
- },
- rhs_ty,
- None);
- fcx.write_ty(expr.id, fcx.tcx().types.err);
- }
- return;
- }
-
- if ty::is_binopable(tcx, lhs_t, op) {
- let tvar = fcx.infcx().next_ty_var();
- demand::suptype(fcx, expr.span, tvar, lhs_t);
- check_expr_has_type(fcx, &**rhs, tvar);
-
- let result_t = match op.node {
- ast::BiEq | ast::BiNe | ast::BiLt | ast::BiLe | ast::BiGe |
- ast::BiGt => {
- if ty::type_is_simd(tcx, lhs_t) {
- if ty::type_is_fp(ty::simd_type(tcx, lhs_t)) {
- fcx.type_error_message(expr.span,
- |actual| {
- format!("binary comparison \
- operation `{}` not \
- supported for floating \
- point SIMD vector `{}`",
- ast_util::binop_to_string(op.node),
- actual)
- },
- lhs_t,
- None
- );
- fcx.tcx().types.err
- } else {
- lhs_t
- }
- } else {
- fcx.tcx().types.bool
- }
- },
- _ => lhs_t,
- };
-
- fcx.write_ty(expr.id, result_t);
- return;
- }
-
- if op.node == ast::BiOr || op.node == ast::BiAnd {
- // This is an error; one of the operands must have the wrong
- // type
- fcx.write_error(expr.id);
- fcx.write_error(rhs.id);
- fcx.type_error_message(expr.span,
- |actual| {
- format!("binary operation `{}` cannot be applied \
- to type `{}`",
- ast_util::binop_to_string(op.node),
- actual)
- },
- lhs_t,
- None)
- }
-
- // Check for overloaded operators if not an assignment.
- let result_t = if is_binop_assignment == SimpleBinop {
- check_user_binop(fcx, expr, lhs, lhs_t, op, rhs)
- } else {
- fcx.type_error_message(expr.span,
- |actual| {
- format!("binary assignment \
- operation `{}=` \
- cannot be applied to \
- type `{}`",
- ast_util::binop_to_string(op.node),
- actual)
- },
- lhs_t,
- None);
- check_expr(fcx, &**rhs);
- fcx.tcx().types.err
- };
-
- fcx.write_ty(expr.id, result_t);
- if ty::type_is_error(result_t) {
- fcx.write_ty(rhs.id, result_t);
- }
- }
-
- fn check_user_binop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
- ex: &'tcx ast::Expr,
- lhs_expr: &'tcx ast::Expr,
- lhs_resolved_t: Ty<'tcx>,
- op: ast::BinOp,
- rhs: &'tcx P<ast::Expr>) -> Ty<'tcx> {
- let tcx = fcx.ccx.tcx;
- let lang = &tcx.lang_items;
- let (name, trait_did) = match op.node {
- ast::BiAdd => ("add", lang.add_trait()),
- ast::BiSub => ("sub", lang.sub_trait()),
- ast::BiMul => ("mul", lang.mul_trait()),
- ast::BiDiv => ("div", lang.div_trait()),
- ast::BiRem => ("rem", lang.rem_trait()),
- ast::BiBitXor => ("bitxor", lang.bitxor_trait()),
- ast::BiBitAnd => ("bitand", lang.bitand_trait()),
- ast::BiBitOr => ("bitor", lang.bitor_trait()),
- ast::BiShl => ("shl", lang.shl_trait()),
- ast::BiShr => ("shr", lang.shr_trait()),
- ast::BiLt => ("lt", lang.ord_trait()),
- ast::BiLe => ("le", lang.ord_trait()),
- ast::BiGe => ("ge", lang.ord_trait()),
- ast::BiGt => ("gt", lang.ord_trait()),
- ast::BiEq => ("eq", lang.eq_trait()),
- ast::BiNe => ("ne", lang.eq_trait()),
- ast::BiAnd | ast::BiOr => {
- check_expr(fcx, &**rhs);
- return tcx.types.err;
- }
- };
- lookup_op_method(fcx, ex, lhs_resolved_t, token::intern(name),
- trait_did, lhs_expr, Some(rhs), || {
- fcx.type_error_message(ex.span, |actual| {
- format!("binary operation `{}` cannot be applied to type `{}`",
- ast_util::binop_to_string(op.node),
- actual)
- }, lhs_resolved_t, None)
- }, if ast_util::is_by_value_binop(op.node) { AutorefArgs::No } else { AutorefArgs::Yes })
- }
-
- fn check_user_unop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
- op_str: &str,
- mname: &str,
- trait_did: Option<ast::DefId>,
- ex: &'tcx ast::Expr,
- rhs_expr: &'tcx ast::Expr,
- rhs_t: Ty<'tcx>,
- op: ast::UnOp) -> Ty<'tcx> {
- lookup_op_method(fcx, ex, rhs_t, token::intern(mname),
- trait_did, rhs_expr, None, || {
- fcx.type_error_message(ex.span, |actual| {
- format!("cannot apply unary operator `{}` to type `{}`",
- op_str, actual)
- }, rhs_t, None);
- }, if ast_util::is_by_value_unop(op) { AutorefArgs::No } else { AutorefArgs::Yes })
- }
-
// Check field access expressions
fn check_field<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
expr: &'tcx ast::Expr,
fcx.write_ty(id, typ);
}
ast::ExprBinary(op, ref lhs, ref rhs) => {
- check_binop(fcx, expr, op, &**lhs, rhs, SimpleBinop);
-
- let lhs_ty = fcx.expr_ty(&**lhs);
- let rhs_ty = fcx.expr_ty(&**rhs);
- if ty::type_is_error(lhs_ty) ||
- ty::type_is_error(rhs_ty) {
- fcx.write_error(id);
- }
+ op::check_binop(fcx, expr, op, lhs, rhs);
}
ast::ExprAssignOp(op, ref lhs, ref rhs) => {
- check_binop(fcx, expr, op, &**lhs, rhs, BinopAssignment);
-
- let lhs_t = fcx.expr_ty(&**lhs);
- let result_t = fcx.expr_ty(expr);
- demand::suptype(fcx, expr.span, result_t, lhs_t);
-
- let tcx = fcx.tcx();
- if !ty::expr_is_lval(tcx, &**lhs) {
- span_err!(tcx.sess, lhs.span, E0067, "illegal left-hand side expression");
- }
-
- fcx.require_expr_have_sized_type(&**lhs, traits::AssignmentLhsSized);
-
- // 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) {
- fcx.write_nil(expr.id);
- }
+ op::check_binop_assign(fcx, expr, op, lhs, rhs);
}
ast::ExprUnary(unop, ref oprnd) => {
let expected_inner = expected.to_option(fcx).map_or(NoExpectation, |ty| {
oprnd_t);
if !(ty::type_is_integral(oprnd_t) ||
oprnd_t.sty == ty::ty_bool) {
- oprnd_t = check_user_unop(fcx, "!", "not",
- tcx.lang_items.not_trait(),
- expr, &**oprnd, oprnd_t, unop);
+ oprnd_t = op::check_user_unop(fcx, "!", "not",
+ tcx.lang_items.not_trait(),
+ expr, &**oprnd, oprnd_t, unop);
}
}
ast::UnNeg => {
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, unop);
+ oprnd_t = op::check_user_unop(fcx, "-", "neg",
+ tcx.lang_items.neg_trait(),
+ expr, &**oprnd, oprnd_t, unop);
}
}
}
match result {
Some((index_ty, element_ty)) => {
- // FIXME: we've already checked idx above, we should
- // probably just demand subtype or something here.
- check_expr_has_type(fcx, &**idx, index_ty);
+ let idx_expr_ty = fcx.expr_ty(idx);
+ demand::eqtype(fcx, expr.span, index_ty, idx_expr_ty);
fcx.write_ty(id, element_ty);
}
_ => {
id: ast::NodeId,
hint: attr::ReprAttr)
-> Vec<Rc<ty::VariantInfo<'tcx>>> {
- use std::num::Int;
+ #![allow(trivial_numeric_casts)]
let rty = ty::node_id_to_type(ccx.tcx, id);
let mut variants: Vec<Rc<ty::VariantInfo>> = Vec::new();
mutbl: ast::MutImmutable
}))
}
- "copy" | "copy_nonoverlapping" |
+ "copy" | "copy_nonoverlapping" => {
+ (1,
+ vec!(
+ ty::mk_ptr(tcx, ty::mt {
+ ty: param(ccx, 0),
+ mutbl: ast::MutImmutable
+ }),
+ ty::mk_ptr(tcx, ty::mt {
+ ty: param(ccx, 0),
+ mutbl: ast::MutMutable
+ }),
+ tcx.types.usize,
+ ),
+ ty::mk_nil(tcx))
+ }
"volatile_copy_memory" | "volatile_copy_nonoverlapping_memory" => {
(1,
vec!(