From d096eefd8016103b524a6ef39a9d42092e51fc69 Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Mon, 24 Feb 2014 10:33:50 +0200 Subject: [PATCH] Use only the appropriate trait when looking up operator overloads. --- src/librustc/middle/typeck/check/method.rs | 113 +++++++++--- src/librustc/middle/typeck/check/mod.rs | 164 ++++++++---------- src/libsyntax/ast_util.rs | 60 ++----- .../trait-resolution-in-overloaded-op.rs | 21 +++ 4 files changed, 198 insertions(+), 160 deletions(-) create mode 100644 src/test/compile-fail/trait-resolution-in-overloaded-op.rs diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs index 3837c0b55d1..0dd0041344f 100644 --- a/src/librustc/middle/typeck/check/method.rs +++ b/src/librustc/middle/typeck/check/method.rs @@ -134,7 +134,6 @@ pub fn lookup( check_traits: CheckTraitsFlag, // Whether we check traits only. autoderef_receiver: AutoderefReceiverFlag) -> Option { - 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::(...)`: + 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 { + 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) { 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, 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) { 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, 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) { 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 } } diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 77474c743c2..0b26bb1b218 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -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, args: &[@ast::Expr], - deref_args: DerefArgs, autoderef_receiver: AutoderefReceiverFlag, - unbound_method: ||, - _expected_result: Option - ) - -> 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, - 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 { + 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, ex: &ast::Expr, rhs_expr: @ast::Expr, - rhs_t: ty::t, - expected_t: Option) - -> 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); } } diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 8ff66bc6629..f6066e5385d 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -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 => ">" } } diff --git a/src/test/compile-fail/trait-resolution-in-overloaded-op.rs b/src/test/compile-fail/trait-resolution-in-overloaded-op.rs new file mode 100644 index 00000000000..1f57c4cf93a --- /dev/null +++ b/src/test/compile-fail/trait-resolution-in-overloaded-op.rs @@ -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 or the MIT license +// , 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 { + fn mul(&self, rhs: &Rhs) -> Res; +} + +fn foo>(a: &T, b: f64) -> f64 { + a * b //~ ERROR binary operation `*` cannot be applied to type `&T` +} + +fn main() {}