Use only the appropriate trait when looking up operator overloads.

This commit is contained in:
Eduard Burtescu 2014-02-24 10:33:50 +02:00
parent 818203e9d2
commit d096eefd80
4 changed files with 198 additions and 160 deletions

View File

@ -134,7 +134,6 @@ pub fn lookup(
check_traits: CheckTraitsFlag, // Whether we check traits only.
autoderef_receiver: AutoderefReceiverFlag)
-> Option<method_origin> {
let impl_dups = @RefCell::new(HashSet::new());
let lcx = LookupContext {
fcx: fcx,
expr: expr,
@ -142,7 +141,7 @@ pub fn lookup(
callee_id: callee_id,
m_name: m_name,
supplied_tps: supplied_tps,
impl_dups: impl_dups,
impl_dups: @RefCell::new(HashSet::new()),
inherent_candidates: @RefCell::new(~[]),
extension_candidates: @RefCell::new(~[]),
deref_args: deref_args,
@ -164,11 +163,50 @@ pub fn lookup(
debug!("searching extension candidates");
lcx.reset_candidates();
lcx.push_bound_candidates(self_ty);
lcx.push_bound_candidates(self_ty, None);
lcx.push_extension_candidates();
return lcx.search(self_ty);
}
pub fn lookup_in_trait(
fcx: @FnCtxt,
// In a call `a.b::<X, Y, ...>(...)`:
expr: &ast::Expr, // The expression `a.b(...)`.
self_expr: &ast::Expr, // The expression `a`.
callee_id: NodeId, /* Where to store `a.b`'s type,
* also the scope of the call */
m_name: ast::Name, // The name `b`.
trait_did: DefId, // The trait to limit the lookup to.
self_ty: ty::t, // The type of `a`.
supplied_tps: &[ty::t], // The list of types X, Y, ... .
autoderef_receiver: AutoderefReceiverFlag)
-> Option<method_origin> {
let lcx = LookupContext {
fcx: fcx,
expr: expr,
self_expr: self_expr,
callee_id: callee_id,
m_name: m_name,
supplied_tps: supplied_tps,
impl_dups: @RefCell::new(HashSet::new()),
inherent_candidates: @RefCell::new(~[]),
extension_candidates: @RefCell::new(~[]),
deref_args: check::DoDerefArgs,
check_traits: CheckTraitsOnly,
autoderef_receiver: autoderef_receiver,
};
let self_ty = structurally_resolved_type(fcx, self_expr.span, self_ty);
debug!("method lookup_in_trait(self_ty={}, expr={}, self_expr={})",
self_ty.repr(fcx.tcx()), expr.repr(fcx.tcx()),
self_expr.repr(fcx.tcx()));
lcx.push_bound_candidates(self_ty, Some(trait_did));
lcx.push_extension_candidate(trait_did);
lcx.search(self_ty)
}
pub struct LookupContext<'a> {
fcx: @FnCtxt,
expr: &'a ast::Expr,
@ -319,17 +357,17 @@ impl<'a> LookupContext<'a> {
}
}
fn push_bound_candidates(&self, self_ty: ty::t) {
fn push_bound_candidates(&self, self_ty: ty::t, restrict_to: Option<DefId>) {
let mut self_ty = self_ty;
loop {
match get(self_ty).sty {
ty_param(p) => {
self.push_inherent_candidates_from_param(self_ty, p);
self.push_inherent_candidates_from_param(self_ty, restrict_to, p);
}
ty_self(..) => {
// Call is of the form "self.foo()" and appears in one
// of a trait's default method implementations.
self.push_inherent_candidates_from_self(self_ty);
self.push_inherent_candidates_from_self(self_ty, restrict_to);
}
_ => { /* No bound methods in these types */ }
}
@ -341,6 +379,19 @@ impl<'a> LookupContext<'a> {
}
}
fn push_extension_candidate(&self, trait_did: DefId) {
ty::populate_implementations_for_trait_if_necessary(self.tcx(), trait_did);
// Look for explicit implementations.
let trait_impls = self.tcx().trait_impls.borrow();
for impl_infos in trait_impls.get().find(&trait_did).iter() {
for impl_info in impl_infos.borrow().get().iter() {
self.push_candidates_from_impl(
self.extension_candidates.borrow_mut().get(), *impl_info);
}
}
}
fn push_extension_candidates(&self) {
// If the method being called is associated with a trait, then
// find all the impls of that trait. Each of those are
@ -428,7 +479,7 @@ impl<'a> LookupContext<'a> {
self.construct_transformed_self_ty_for_object(
did, &rcvr_substs, &m);
Candidate {
Some(Candidate {
rcvr_match_condition: RcvrMatchesIfObject(did),
rcvr_substs: new_trait_ref.substs.clone(),
method_ty: @m,
@ -438,49 +489,61 @@ impl<'a> LookupContext<'a> {
method_num: method_num,
real_index: vtable_index
})
}
})
});
}
fn push_inherent_candidates_from_param(&self,
rcvr_ty: ty::t,
restrict_to: Option<DefId>,
param_ty: param_ty) {
debug!("push_inherent_candidates_from_param(param_ty={:?})",
param_ty);
self.push_inherent_candidates_from_bounds(
rcvr_ty,
self.fcx.inh.param_env.type_param_bounds[param_ty.idx].trait_bounds,
restrict_to,
param_numbered(param_ty.idx));
}
fn push_inherent_candidates_from_self(&self,
rcvr_ty: ty::t) {
rcvr_ty: ty::t,
restrict_to: Option<DefId>) {
debug!("push_inherent_candidates_from_self()");
self.push_inherent_candidates_from_bounds(
rcvr_ty,
[self.fcx.inh.param_env.self_param_bound.unwrap()],
restrict_to,
param_self)
}
fn push_inherent_candidates_from_bounds(&self,
self_ty: ty::t,
bounds: &[@TraitRef],
restrict_to: Option<DefId>,
param: param_index) {
self.push_inherent_candidates_from_bounds_inner(bounds,
|trait_ref, m, method_num, bound_num| {
Candidate {
rcvr_match_condition: RcvrMatchesIfSubtype(self_ty),
rcvr_substs: trait_ref.substs.clone(),
method_ty: m,
origin: method_param(
method_param {
match restrict_to {
Some(trait_did) => {
if trait_did != trait_ref.def_id {
return None;
}
}
_ => {}
}
Some(Candidate {
rcvr_match_condition: RcvrMatchesIfSubtype(self_ty),
rcvr_substs: trait_ref.substs.clone(),
method_ty: m,
origin: method_param(method_param {
trait_id: trait_ref.def_id,
method_num: method_num,
param_num: param,
bound_num: bound_num,
})
}
})
})
}
@ -492,7 +555,7 @@ impl<'a> LookupContext<'a> {
m: @ty::Method,
method_num: uint,
bound_num: uint|
-> Candidate) {
-> Option<Candidate>) {
let tcx = self.tcx();
let mut next_bound_idx = 0; // count only trait bounds
@ -508,17 +571,17 @@ impl<'a> LookupContext<'a> {
Some(pos) => {
let method = trait_methods[pos];
let cand = mk_cand(bound_trait_ref, method,
pos, this_bound_idx);
debug!("pushing inherent candidate for param: {:?}", cand);
let mut inherent_candidates = self.inherent_candidates
.borrow_mut();
inherent_candidates.get().push(cand);
match mk_cand(bound_trait_ref, method, pos, this_bound_idx) {
Some(cand) => {
debug!("pushing inherent candidate for param: {:?}", cand);
self.inherent_candidates.borrow_mut().get().push(cand);
}
None => {}
}
}
None => {
debug!("trait doesn't contain method: {:?}",
bound_trait_ref.def_id);
bound_trait_ref.def_id);
// check next trait or bound
}
}

View File

@ -96,7 +96,7 @@ use middle::typeck::check::_match::pat_ctxt;
use middle::typeck::check::method::{AutoderefReceiver};
use middle::typeck::check::method::{AutoderefReceiverFlag};
use middle::typeck::check::method::{CheckTraitsAndInherentMethods};
use middle::typeck::check::method::{CheckTraitsOnly, DontAutoderefReceiver};
use middle::typeck::check::method::{DontAutoderefReceiver};
use middle::typeck::check::regionmanip::replace_bound_regions_in_fn_sig;
use middle::typeck::check::regionmanip::relate_free_regions;
use middle::typeck::check::vtable::{LocationInfo, VtableContext};
@ -1936,35 +1936,34 @@ pub fn check_expr_with_unifier(fcx: @FnCtxt,
op_ex: &ast::Expr,
self_t: ty::t,
opname: ast::Name,
trait_did: Option<ast::DefId>,
args: &[@ast::Expr],
deref_args: DerefArgs,
autoderef_receiver: AutoderefReceiverFlag,
unbound_method: ||,
_expected_result: Option<ty::t>
)
-> ty::t {
match method::lookup(fcx, op_ex, args[0],
callee_id, opname, self_t, [],
deref_args, CheckTraitsOnly, autoderef_receiver) {
Some(ref origin) => {
unbound_method: ||) -> ty::t {
let origin = match trait_did {
Some(trait_did) => {
method::lookup_in_trait(fcx, op_ex, args[0], callee_id, opname,
trait_did, self_t, [], autoderef_receiver)
}
None => None
};
match origin {
Some(origin) => {
let method_ty = fcx.node_ty(callee_id);
let method_map = fcx.inh.method_map;
{
let mut method_map = method_map.borrow_mut();
method_map.get().insert(op_ex.id, *origin);
}
method_map.borrow_mut().get().insert(op_ex.id, origin);
check_method_argument_types(fcx, op_ex.span,
method_ty, op_ex,
args, deref_args)
args, DoDerefArgs)
}
_ => {
None => {
unbound_method();
// Check the args anyway
// so we get all the error messages
let expected_ty = ty::mk_err();
check_method_argument_types(fcx, op_ex.span,
expected_ty, op_ex,
args, deref_args);
args, DoDerefArgs);
ty::mk_err()
}
}
@ -1977,10 +1976,7 @@ pub fn check_expr_with_unifier(fcx: @FnCtxt,
op: ast::BinOp,
lhs: @ast::Expr,
rhs: @ast::Expr,
// Used only in the error case
expected_result: Option<ty::t>,
is_binop_assignment: IsBinopAssignment
) {
is_binop_assignment: IsBinopAssignment) {
let tcx = fcx.ccx.tcx;
check_expr(fcx, lhs);
@ -2004,12 +2000,8 @@ pub fn check_expr_with_unifier(fcx: @FnCtxt,
let result_t = match op {
ast::BiEq | ast::BiNe | ast::BiLt | ast::BiLe | ast::BiGe |
ast::BiGt => {
ty::mk_bool()
}
_ => {
lhs_t
}
ast::BiGt => ty::mk_bool(),
_ => lhs_t
};
fcx.write_ty(expr.id, result_t);
@ -2030,16 +2022,8 @@ pub fn check_expr_with_unifier(fcx: @FnCtxt,
}
// Check for overloaded operators if not an assignment.
let result_t;
if is_binop_assignment == SimpleBinop {
result_t = check_user_binop(fcx,
callee_id,
expr,
lhs,
lhs_t,
op,
rhs,
expected_result);
let result_t = if is_binop_assignment == SimpleBinop {
check_user_binop(fcx, callee_id, expr, lhs, lhs_t, op, rhs)
} else {
fcx.type_error_message(expr.span,
|actual| {
@ -2051,8 +2035,8 @@ pub fn check_expr_with_unifier(fcx: @FnCtxt,
lhs_t,
None);
check_expr(fcx, rhs);
result_t = ty::mk_err();
}
ty::mk_err()
};
fcx.write_ty(expr.id, result_t);
if ty::type_is_error(result_t) {
@ -2066,48 +2050,54 @@ pub fn check_expr_with_unifier(fcx: @FnCtxt,
lhs_expr: @ast::Expr,
lhs_resolved_t: ty::t,
op: ast::BinOp,
rhs: @ast::Expr,
expected_result: Option<ty::t>) -> ty::t {
rhs: @ast::Expr) -> ty::t {
let tcx = fcx.ccx.tcx;
match ast_util::binop_to_method_name(op) {
Some(ref name) => {
let if_op_unbound = || {
fcx.type_error_message(ex.span, |actual| {
format!("binary operation `{}` cannot be applied \
to type `{}`",
ast_util::binop_to_str(op), actual)},
lhs_resolved_t, None)
};
return lookup_op_method(fcx, callee_id, ex, lhs_resolved_t,
token::intern(*name), [lhs_expr, rhs],
DoDerefArgs,DontAutoderefReceiver,
if_op_unbound, expected_result);
let lang = tcx.lang_items;
let (name, trait_did) = match op {
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 ty::mk_err();
}
None => ()
};
check_expr(fcx, rhs);
ty::mk_err()
lookup_op_method(fcx, callee_id, ex, lhs_resolved_t, token::intern(name),
trait_did, [lhs_expr, rhs], DontAutoderefReceiver, || {
fcx.type_error_message(ex.span, |actual| {
format!("binary operation `{}` cannot be applied to type `{}`",
ast_util::binop_to_str(op), actual)
}, lhs_resolved_t, None)
})
}
fn check_user_unop(fcx: @FnCtxt,
callee_id: ast::NodeId,
op_str: &str,
mname: &str,
trait_did: Option<ast::DefId>,
ex: &ast::Expr,
rhs_expr: @ast::Expr,
rhs_t: ty::t,
expected_t: Option<ty::t>)
-> ty::t {
lookup_op_method(
fcx, callee_id, ex, rhs_t, token::intern(mname),
[rhs_expr], DoDerefArgs, DontAutoderefReceiver,
|| {
fcx.type_error_message(ex.span, |actual| {
format!("cannot apply unary operator `{}` to type `{}`",
op_str, actual)
}, rhs_t, None);
}, expected_t)
rhs_t: ty::t) -> ty::t {
lookup_op_method(fcx, callee_id, ex, rhs_t, token::intern(mname),
trait_did, [rhs_expr], DontAutoderefReceiver, || {
fcx.type_error_message(ex.span, |actual| {
format!("cannot apply unary operator `{}` to type `{}`", op_str, actual)
}, rhs_t, None);
})
}
// Resolves `expected` by a single level if it is a variable and passes it
@ -2631,14 +2621,7 @@ pub fn check_expr_with_unifier(fcx: @FnCtxt,
fcx.write_ty(id, typ);
}
ast::ExprBinary(callee_id, op, lhs, rhs) => {
check_binop(fcx,
callee_id,
expr,
op,
lhs,
rhs,
expected,
SimpleBinop);
check_binop(fcx, callee_id, expr, op, lhs, rhs, SimpleBinop);
let lhs_ty = fcx.expr_ty(lhs);
let rhs_ty = fcx.expr_ty(rhs);
@ -2652,14 +2635,7 @@ pub fn check_expr_with_unifier(fcx: @FnCtxt,
}
}
ast::ExprAssignOp(callee_id, op, lhs, rhs) => {
check_binop(fcx,
callee_id,
expr,
op,
lhs,
rhs,
expected,
BinopAssignment);
check_binop(fcx, callee_id, expr, op, lhs, rhs, BinopAssignment);
let lhs_t = fcx.expr_ty(lhs);
let result_t = fcx.expr_ty(expr);
@ -2734,9 +2710,9 @@ pub fn check_expr_with_unifier(fcx: @FnCtxt,
oprnd_t);
if !(ty::type_is_integral(oprnd_t) ||
ty::get(oprnd_t).sty == ty::ty_bool) {
oprnd_t = check_user_unop(fcx, callee_id,
"!", "not", expr, oprnd, oprnd_t,
expected);
oprnd_t = check_user_unop(fcx, callee_id, "!", "not",
tcx.lang_items.not_trait(),
expr, oprnd, oprnd_t);
}
}
ast::UnNeg => {
@ -2744,8 +2720,9 @@ pub fn check_expr_with_unifier(fcx: @FnCtxt,
oprnd_t);
if !(ty::type_is_integral(oprnd_t) ||
ty::type_is_fp(oprnd_t)) {
oprnd_t = check_user_unop(fcx, callee_id,
"-", "neg", expr, oprnd, oprnd_t, expected);
oprnd_t = check_user_unop(fcx, callee_id, "-", "neg",
tcx.lang_items.neg_trait(),
expr, oprnd, oprnd_t);
}
}
}
@ -3160,11 +3137,10 @@ pub fn check_expr_with_unifier(fcx: @FnCtxt,
expr,
resolved,
token::intern("index"),
tcx.lang_items.index_trait(),
[base, idx],
DoDerefArgs,
AutoderefReceiver,
error_message,
expected);
error_message);
fcx.write_ty(id, ret_ty);
}
}

View File

@ -79,48 +79,26 @@ pub fn def_id_of_def(d: Def) -> DefId {
}
}
pub fn binop_to_str(op: BinOp) -> ~str {
pub fn binop_to_str(op: BinOp) -> &'static str {
match op {
BiAdd => return ~"+",
BiSub => return ~"-",
BiMul => return ~"*",
BiDiv => return ~"/",
BiRem => return ~"%",
BiAnd => return ~"&&",
BiOr => return ~"||",
BiBitXor => return ~"^",
BiBitAnd => return ~"&",
BiBitOr => return ~"|",
BiShl => return ~"<<",
BiShr => return ~">>",
BiEq => return ~"==",
BiLt => return ~"<",
BiLe => return ~"<=",
BiNe => return ~"!=",
BiGe => return ~">=",
BiGt => return ~">"
}
}
pub fn binop_to_method_name(op: BinOp) -> Option<~str> {
match op {
BiAdd => return Some(~"add"),
BiSub => return Some(~"sub"),
BiMul => return Some(~"mul"),
BiDiv => return Some(~"div"),
BiRem => return Some(~"rem"),
BiBitXor => return Some(~"bitxor"),
BiBitAnd => return Some(~"bitand"),
BiBitOr => return Some(~"bitor"),
BiShl => return Some(~"shl"),
BiShr => return Some(~"shr"),
BiLt => return Some(~"lt"),
BiLe => return Some(~"le"),
BiGe => return Some(~"ge"),
BiGt => return Some(~"gt"),
BiEq => return Some(~"eq"),
BiNe => return Some(~"ne"),
BiAnd | BiOr => return None
BiAdd => "+",
BiSub => "-",
BiMul => "*",
BiDiv => "/",
BiRem => "%",
BiAnd => "&&",
BiOr => "||",
BiBitXor => "^",
BiBitAnd => "&",
BiBitOr => "|",
BiShl => "<<",
BiShr => ">>",
BiEq => "==",
BiLt => "<",
BiLe => "<=",
BiNe => "!=",
BiGe => ">=",
BiGt => ">"
}
}

View File

@ -0,0 +1,21 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// #12402 Operator overloading only considers the method name, not which trait is implemented
trait MyMul<Rhs, Res> {
fn mul(&self, rhs: &Rhs) -> Res;
}
fn foo<T: MyMul<f64, f64>>(a: &T, b: f64) -> f64 {
a * b //~ ERROR binary operation `*` cannot be applied to type `&T`
}
fn main() {}