diff options
| author | Eduard Burtescu <edy.burt@gmail.com> | 2015-07-02 13:33:01 +0300 |
|---|---|---|
| committer | Eduard Burtescu <edy.burt@gmail.com> | 2015-07-04 06:21:00 +0300 |
| commit | 5620a5879178dab2b929557208e19827eab9bda0 (patch) | |
| tree | 8aa16016b9163f03148b6e629b3b60ecbfe790eb /src | |
| parent | a2fe59afd66f032bea04f55fdb8beb00b53b65d7 (diff) | |
| download | rust-5620a5879178dab2b929557208e19827eab9bda0.tar.gz rust-5620a5879178dab2b929557208e19827eab9bda0.zip | |
rustc_lint: use traits::select for methods in unconditional_recursion.
Diffstat (limited to 'src')
| -rw-r--r-- | src/librustc/middle/astencode.rs | 69 | ||||
| -rw-r--r-- | src/librustc/middle/ty.rs | 8 | ||||
| -rw-r--r-- | src/librustc_lint/builtin.rs | 144 | ||||
| -rw-r--r-- | src/librustc_privacy/lib.rs | 2 | ||||
| -rw-r--r-- | src/librustc_trans/save/dump_csv.rs | 3 | ||||
| -rw-r--r-- | src/librustc_trans/trans/meth.rs | 2 | ||||
| -rw-r--r-- | src/librustc_typeck/check/method/confirm.rs | 6 | ||||
| -rw-r--r-- | src/librustc_typeck/check/method/mod.rs | 2 |
8 files changed, 92 insertions, 144 deletions
diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index 6b476f92196..fa06d502728 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -617,7 +617,7 @@ fn encode_method_callee<'a, 'tcx>(ecx: &e::EncodeContext<'a, 'tcx>, Ok(rbml_w.emit_def_id(method.def_id)) }); rbml_w.emit_struct_field("origin", 2, |rbml_w| { - Ok(rbml_w.emit_method_origin(method.origin)) + method.origin.encode(rbml_w) }); rbml_w.emit_struct_field("ty", 3, |rbml_w| { Ok(rbml_w.emit_ty(ecx, method.ty)) @@ -633,16 +633,14 @@ impl<'a, 'tcx> read_method_callee_helper<'tcx> for reader::Decoder<'a> { -> (u32, ty::MethodCallee<'tcx>) { self.read_struct("MethodCallee", 5, |this| { - let autoderef = this.read_struct_field("autoderef", 0, |this| { - Decodable::decode(this) - }).unwrap(); + let autoderef = this.read_struct_field("autoderef", 0, + Decodable::decode).unwrap(); Ok((autoderef, ty::MethodCallee { def_id: this.read_struct_field("def_id", 1, |this| { Ok(this.read_def_id(dcx)) }).unwrap(), - origin: this.read_struct_field("origin", 2, |this| { - Ok(this.read_method_origin(dcx)) - }).unwrap(), + origin: this.read_struct_field("origin", 2, + Decodable::decode).unwrap(), ty: this.read_struct_field("ty", 3, |this| { Ok(this.read_ty(dcx)) }).unwrap(), @@ -713,7 +711,6 @@ impl<'a, 'tcx> get_ty_str_ctxt<'tcx> for e::EncodeContext<'a, 'tcx> { trait rbml_writer_helpers<'tcx> { fn emit_closure_type<'a>(&mut self, ecx: &e::EncodeContext<'a, 'tcx>, closure_type: &ty::ClosureTy<'tcx>); - fn emit_method_origin(&mut self, method_origin: ty::MethodOrigin); fn emit_ty<'a>(&mut self, ecx: &e::EncodeContext<'a, 'tcx>, ty: Ty<'tcx>); fn emit_tys<'a>(&mut self, ecx: &e::EncodeContext<'a, 'tcx>, tys: &[Ty<'tcx>]); fn emit_type_param_def<'a>(&mut self, ecx: &e::EncodeContext<'a, 'tcx>, @@ -745,37 +742,6 @@ impl<'a, 'tcx> rbml_writer_helpers<'tcx> for Encoder<'a> { }); } - fn emit_method_origin(&mut self, method_origin: ty::MethodOrigin) { - use serialize::Encoder; - - self.emit_enum("MethodOrigin", |this| { - match method_origin { - ty::MethodOrigin::Inherent => { - this.emit_enum_variant("Inherent", 0, 0, |_| Ok(())) - } - - ty::MethodOrigin::Trait(impl_def_id) => { - this.emit_enum_variant("Trait", 1, 1, |this| { - this.emit_option(|this| { - match impl_def_id { - None => this.emit_option_none(), - Some(did) => this.emit_option_some(|this| { - Ok(this.emit_def_id(did)) - }) - } - }) - }) - } - - ty::MethodOrigin::Object(vtable_index) => { - this.emit_enum_variant("Object", 2, 1, |this| { - this.emit_uint(vtable_index) - }) - } - } - }); - } - fn emit_ty<'b>(&mut self, ecx: &e::EncodeContext<'b, 'tcx>, ty: Ty<'tcx>) { self.emit_opaque(|this| Ok(e::write_type(ecx, this, ty))); } @@ -1118,7 +1084,6 @@ impl<'a> doc_decoder_helpers for rbml::Doc<'a> { } trait rbml_decoder_decoder_helpers<'tcx> { - fn read_method_origin(&mut self, dcx: &DecodeContext) -> ty::MethodOrigin; fn read_ty<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>) -> Ty<'tcx>; fn read_tys<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>) -> Vec<Ty<'tcx>>; fn read_trait_ref<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>) @@ -1202,30 +1167,6 @@ impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> { }).unwrap() } - fn read_method_origin(&mut self, dcx: &DecodeContext) -> ty::MethodOrigin { - self.read_enum("MethodOrigin", |this| { - let variants = &["Inherent", "Trait", "Object"]; - this.read_enum_variant(variants, |this, i| { - match i { - 0 => Ok(ty::MethodOrigin::Inherent), - - 1 => this.read_option(|this, b| { - Ok(ty::MethodOrigin::Trait(if b { - Some(this.read_def_id(dcx)) - } else { - None - })) - }), - - 2 => this.read_uint().map(|idx| ty::MethodOrigin::Object(idx)), - - _ => panic!("..") - } - }) - }).unwrap() - } - - fn read_ty<'b, 'c>(&mut self, dcx: &DecodeContext<'b, 'c, 'tcx>) -> Ty<'tcx> { // Note: regions types embed local node ids. In principle, we // should translate these node ids into the new decode diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 2eb1767c928..29ab8de5e65 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -625,17 +625,13 @@ pub enum CustomCoerceUnsized { Struct(usize) } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, RustcEncodable, RustcDecodable, Debug)] pub enum MethodOrigin { /// Inherent impl method call. Inherent, /// Statically dispatched trait method call. - /// The DefId is the impl for the trait from which the method comes. - /// This should only be used for certain linting/heuristic purposes - /// since there is no guarantee that this is Some in every situation - /// that it could/should be. - Trait(Option<ast::DefId>), + Trait, /// Dynamically dispatched trait method call. /// The usize is the index into the actual runtime vtable. diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 7d14bde8a92..d92a5c9c76e 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -33,8 +33,10 @@ use metadata::{csearch, decoder}; use middle::def::*; +use middle::infer; use middle::subst::Substs; use middle::ty::{self, Ty}; +use middle::traits; use middle::{def, pat_util, stability}; use middle::const_eval::{eval_const_expr_partial, ConstVal}; use middle::cfg; @@ -1863,24 +1865,18 @@ impl LintPass for UnconditionalRecursion { fn check_fn(&mut self, cx: &Context, fn_kind: visit::FnKind, _: &ast::FnDecl, blk: &ast::Block, sp: Span, id: ast::NodeId) { - // FIXME(#23542) Replace with type ascription. - #![allow(trivial_casts)] - type F = for<'tcx> fn(&ty::ctxt<'tcx>, ast::NodeId, ast::NodeId, ast::Ident, ast::NodeId) -> bool; - let (name, checker) = match fn_kind { - visit::FkItemFn(name, _, _, _, _, _) => (name, id_refers_to_this_fn as F), - visit::FkMethod(name, _, _) => (name, id_refers_to_this_method as F), + let method = match fn_kind { + visit::FkItemFn(..) => None, + visit::FkMethod(..) => { + cx.tcx.impl_or_trait_item(local_def(id)).as_opt_method() + } // closures can't recur, so they don't matter. visit::FkFnBlock => return }; - let impl_def_id = cx.tcx.impl_of_method(local_def(id)) - .unwrap_or(local_def(ast::DUMMY_NODE_ID)); - assert!(ast_util::is_local(impl_def_id)); - let impl_node_id = impl_def_id.node; - // Walk through this function (say `f`) looking to see if // every possible path references itself, i.e. the function is // called recursively unconditionally. This is done by trying @@ -1931,7 +1927,17 @@ impl LintPass for UnconditionalRecursion { let node_id = cfg.graph.node_data(idx).id(); // is this a recursive call? - if node_id != ast::DUMMY_NODE_ID && checker(cx.tcx, impl_node_id, id, name, node_id) { + let self_recursive = if node_id != ast::DUMMY_NODE_ID { + match method { + Some(ref method) => { + expr_refers_to_this_method(cx.tcx, method, node_id) + } + None => expr_refers_to_this_fn(cx.tcx, id, node_id) + } + } else { + false + }; + if self_recursive { self_call_spans.push(cx.tcx.map.span(node_id)); // this is a self call, so we shouldn't explore past // this node in the CFG. @@ -1970,15 +1976,12 @@ impl LintPass for UnconditionalRecursion { // all done return; - // Functions for identifying if the given NodeId `id` - // represents a call to the function `fn_id`/method - // `method_id`. + // Functions for identifying if the given Expr NodeId `id` + // represents a call to the function `fn_id`/method `method`. - fn id_refers_to_this_fn<'tcx>(tcx: &ty::ctxt<'tcx>, - _: ast::NodeId, - fn_id: ast::NodeId, - _: ast::Ident, - id: ast::NodeId) -> bool { + fn expr_refers_to_this_fn(tcx: &ty::ctxt, + fn_id: ast::NodeId, + id: ast::NodeId) -> bool { match tcx.map.get(id) { ast_map::NodeExpr(&ast::Expr { node: ast::ExprCall(ref callee, _), .. }) => { tcx.def_map.borrow().get(&callee.id) @@ -1988,59 +1991,68 @@ impl LintPass for UnconditionalRecursion { } } - // check if the method call `id` refers to method `method_id` - // (with name `method_name` contained in impl `impl_id`). - fn id_refers_to_this_method<'tcx>(tcx: &ty::ctxt<'tcx>, - impl_id: ast::NodeId, - method_id: ast::NodeId, - method_name: ast::Ident, - id: ast::NodeId) -> bool { - let did = match tcx.tables.borrow().method_map.get(&ty::MethodCall::expr(id)) { - None => return false, - Some(m) => match m.origin { - // There's no way to know if a method call via a - // vtable is recursion, so we assume it's not. - ty::MethodOrigin::Object(_) => return false, - - // This `did` refers directly to the method definition. - ty::MethodOrigin::Inherent => m.def_id, - - // The `impl ... for ...` of this method call - // isn't known, e.g. it might be a default method - // in a trait, so we get the def-id of the trait - // method instead. - ty::MethodOrigin::Trait(None) => { - let on_self = m.substs.self_ty().map_or(false, |t| t.is_self()); - if !on_self { - // we can only be recurring in a default + // Check if the method call `id` refers to method `method`. + fn expr_refers_to_this_method(tcx: &ty::ctxt, + method: &ty::Method, + id: ast::NodeId) -> bool { + let tables = tcx.tables.borrow(); + let callee = match tables.method_map.get(&ty::MethodCall::expr(id)) { + Some(m) => m, + None => return false + }; + let callee_item = tcx.impl_or_trait_item(callee.def_id); + + match callee_item.container() { + // This is an inherent method, so the `def_id` refers + // directly to the method definition. + ty::ImplContainer(_) => { + callee.def_id == method.def_id + } + + // A trait method, from any number of possible sources. + // Attempt to select a concrete impl before checking. + ty::TraitContainer(trait_def_id) => { + let trait_substs = callee.substs.clone().method_to_trait(); + let trait_substs = tcx.mk_substs(trait_substs); + let trait_ref = ty::TraitRef::new(trait_def_id, trait_substs); + let trait_ref = ty::Binder(trait_ref); + let span = tcx.map.span(id); + let obligation = + traits::Obligation::new(traits::ObligationCause::misc(span, id), + trait_ref.to_poly_trait_predicate()); + + let param_env = ty::ParameterEnvironment::for_item(tcx, method.def_id.node); + let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(param_env), false); + let mut selcx = traits::SelectionContext::new(&infcx); + match selcx.select(&obligation) { + // The method comes from a `T: Trait` bound. + // If `T` is `Self`, then this call is inside + // a default method definition. + Ok(Some(traits::VtableParam(_))) => { + let self_ty = callee.substs.self_ty(); + let on_self = self_ty.map_or(false, |t| t.is_self()); + // We can only be recurring in a default // method if we're being called literally // on the `Self` type. - return false + on_self && callee.def_id == method.def_id } - m.def_id - } - - // The `impl` is known, so we check that with a - // special case: - ty::MethodOrigin::Trait(Some(impl_def_id)) => { + // The `impl` is known, so we check that with a + // special case: + Ok(Some(traits::VtableImpl(vtable_impl))) => { + let container = ty::ImplContainer(vtable_impl.impl_def_id); + // It matches if it comes from the same impl, + // and has the same method name. + container == method.container + && callee_item.name() == method.name + } - let name = match tcx.map.expect_expr(id).node { - ast::ExprMethodCall(ref sp_ident, _, _) => sp_ident.node, - _ => tcx.sess.span_bug( - tcx.map.span(id), - "non-method call expr behaving like a method call?") - }; - // It matches if it comes from the same impl, - // and has the same method name. - return ast_util::is_local(impl_def_id) - && impl_def_id.node == impl_id - && method_name.name == name.name + // There's no way to know if this call is + // recursive, so we assume it's not. + _ => return false } } - }; - - ast_util::is_local(did) && did.node == method_id + } } } } diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs index cf0a24ed48c..dcbe4c35113 100644 --- a/src/librustc_privacy/lib.rs +++ b/src/librustc_privacy/lib.rs @@ -852,7 +852,7 @@ impl<'a, 'tcx> PrivacyVisitor<'a, 'tcx> { } // Trait methods are always all public. The only controlling factor // is whether the trait itself is accessible or not. - ty::MethodOrigin::Trait(_) | ty::MethodOrigin::Object(_) => { + ty::MethodOrigin::Trait | ty::MethodOrigin::Object(_) => { let method = self.tcx.impl_or_trait_item(callee.def_id); self.report_error(self.ensure_public(span, method.container().id(), None, "source trait")); diff --git a/src/librustc_trans/save/dump_csv.rs b/src/librustc_trans/save/dump_csv.rs index 2236eda5527..bac5ea494df 100644 --- a/src/librustc_trans/save/dump_csv.rs +++ b/src/librustc_trans/save/dump_csv.rs @@ -892,8 +892,7 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> { ty::MethodOrigin::Inherent => { (Some(method_callee.def_id), None) } - ty::MethodOrigin::Trait(_) | - ty::MethodOrigin::Object(_) => { + ty::MethodOrigin::Trait | ty::MethodOrigin::Object(_) => { (None, Some(method_callee.def_id)) } }; diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs index 3d727cf3347..2f415fcf7fd 100644 --- a/src/librustc_trans/trans/meth.rs +++ b/src/librustc_trans/trans/meth.rs @@ -128,7 +128,7 @@ pub fn trans_method_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, } } - ty::MethodOrigin::Trait(_) => { + ty::MethodOrigin::Trait => { let method_item = bcx.tcx().impl_or_trait_item(method_id); let trait_def_id = method_item.container().id(); diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index 5fa167c7929..26868c16bd8 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -255,7 +255,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> { &impl_polytype.substs, &self.tcx().impl_trait_ref(impl_def_id).unwrap()); let substs = impl_trait_ref.substs.clone(); - (substs, ty::MethodOrigin::Trait(Some(impl_def_id))) + (substs, ty::MethodOrigin::Trait) } probe::TraitPick => { @@ -271,7 +271,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> { &trait_def.generics, self.infcx().next_ty_var()); - (substs, ty::MethodOrigin::Trait(None)) + (substs, ty::MethodOrigin::Trait) } probe::WhereClausePick(ref poly_trait_ref) => { @@ -279,7 +279,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> { // those to convert from a poly-trait-ref to a trait-ref. let trait_ref = self.replace_late_bound_regions_with_fresh_var(&*poly_trait_ref); let substs = trait_ref.substs.clone(); - (substs, ty::MethodOrigin::Trait(None)) + (substs, ty::MethodOrigin::Trait) } } } diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index 95e60a5fbe1..0ba09031724 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -307,7 +307,7 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, let callee = ty::MethodCallee { def_id: method_item.def_id(), - origin: ty::MethodOrigin::Trait(None), + origin: ty::MethodOrigin::Trait, ty: fty, substs: trait_ref.substs }; |
