use metadata::{csearch, decoder};
use middle::{cfg, def, infer, pat_util, stability, traits};
-use middle::def::*;
use middle::subst::Substs;
use middle::ty::{self, Ty};
use middle::const_eval::{eval_const_expr_partial, ConstVal};
}
}
- // Check if the method call `id` refers to method `method`.
+ // Check if the expression `id` performs a call to `method`.
fn expr_refers_to_this_method(tcx: &ty::ctxt,
method: &ty::Method,
id: ast::NodeId) -> bool {
- let method_call = ty::MethodCall::expr(id);
- let callee = match tcx.tables.borrow().method_map.get(&method_call) {
- Some(&m) => m,
- None => return false
- };
- let callee_item = tcx.impl_or_trait_item(callee.def_id);
+ let tables = tcx.tables.borrow();
+
+ // Check for method calls and overloaded operators.
+ if let Some(m) = tables.method_map.get(&ty::MethodCall::expr(id)) {
+ if method_call_refers_to_method(tcx, method, m.def_id, m.substs, id) {
+ return true;
+ }
+ }
+
+ // Check for overloaded autoderef method calls.
+ if let Some(&ty::AdjustDerefRef(ref adj)) = tables.adjustments.get(&id) {
+ for i in 0..adj.autoderefs {
+ let method_call = ty::MethodCall::autoderef(id, i as u32);
+ if let Some(m) = tables.method_map.get(&method_call) {
+ if method_call_refers_to_method(tcx, method, m.def_id, m.substs, id) {
+ return true;
+ }
+ }
+ }
+ }
+
+ // Check for calls to methods via explicit paths (e.g. `T::method()`).
+ match tcx.map.get(id) {
+ ast_map::NodeExpr(&ast::Expr { node: ast::ExprCall(ref callee, _), .. }) => {
+ match tcx.def_map.borrow().get(&callee.id).map(|d| d.full_def()) {
+ Some(def::DefMethod(def_id)) => {
+ let no_substs = &ty::ItemSubsts::empty();
+ let ts = tables.item_substs.get(&callee.id).unwrap_or(no_substs);
+ method_call_refers_to_method(tcx, method, def_id, &ts.substs, id)
+ }
+ _ => false
+ }
+ }
+ _ => false
+ }
+ }
+
+ // Check if the method call to the method with the ID `callee_id`
+ // and instantiated with `callee_substs` refers to method `method`.
+ fn method_call_refers_to_method<'tcx>(tcx: &ty::ctxt<'tcx>,
+ method: &ty::Method,
+ callee_id: ast::DefId,
+ callee_substs: &Substs<'tcx>,
+ expr_id: ast::NodeId) -> bool {
+ let callee_item = tcx.impl_or_trait_item(callee_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
+ callee_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 = 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 span = tcx.map.span(expr_id);
let obligation =
- traits::Obligation::new(traits::ObligationCause::misc(span, id),
+ traits::Obligation::new(traits::ObligationCause::misc(span, expr_id),
trait_ref.to_poly_trait_predicate());
let param_env = ty::ParameterEnvironment::for_item(tcx, method.def_id.node);
// 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 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.
- on_self && callee.def_id == method.def_id
+ on_self && callee_id == method.def_id
}
// The `impl` is known, so we check that with a
ast::ExprPath(..) => (),
_ => return None
}
- if let DefFn(did, _) = cx.tcx.resolve_expr(expr) {
+ if let def::DefFn(did, _) = cx.tcx.resolve_expr(expr) {
if !def_id_is_transmute(cx, did) {
return None;
}
}
}
+// Trait method calls.
trait Foo {
fn bar(&self) { //~ ERROR function cannot return without recurring
self.bar() //~ NOTE recursive call site
self.bar() //~ NOTE recursive call site
}
}
+}
+
+// Trait method call with integer fallback after method resolution.
+impl Foo for i32 {
+ fn bar(&self) { //~ ERROR function cannot return without recurring
+ 0.bar() //~ NOTE recursive call site
+ }
+}
+
+impl Foo for u32 {
+ fn bar(&self) {
+ 0.bar()
+ }
+}
+// Trait method calls via paths.
+trait Foo2 {
+ fn bar(&self) { //~ ERROR function cannot return without recurring
+ Foo2::bar(self) //~ NOTE recursive call site
+ }
+}
+
+impl Foo2 for Box<Foo2+'static> {
+ fn bar(&self) { //~ ERROR function cannot return without recurring
+ loop {
+ Foo2::bar(self) //~ NOTE recursive call site
+ }
+ }
}
struct Baz;
impl Baz {
+ // Inherent method call.
fn qux(&self) { //~ ERROR function cannot return without recurring
self.qux(); //~ NOTE recursive call site
}
+
+ // Inherent method call via path.
+ fn as_ref(&self) -> &Self { //~ ERROR function cannot return without recurring
+ Baz::as_ref(self) //~ NOTE recursive call site
+ }
+}
+
+// Trait method calls to impls via paths.
+impl Default for Baz {
+ fn default() -> Baz { //~ ERROR function cannot return without recurring
+ let x = Default::default(); //~ NOTE recursive call site
+ x
+ }
+}
+
+// Overloaded operators.
+impl std::ops::Deref for Baz {
+ type Target = ();
+ fn deref(&self) -> &() { //~ ERROR function cannot return without recurring
+ &**self //~ NOTE recursive call site
+ }
+}
+
+impl std::ops::Index<usize> for Baz {
+ type Output = Baz;
+ fn index(&self, x: usize) -> &Baz { //~ ERROR function cannot return without recurring
+ &self[x] //~ NOTE recursive call site
+ }
+}
+
+// Overloaded autoderef.
+struct Quux;
+impl std::ops::Deref for Quux {
+ type Target = Baz;
+ fn deref(&self) -> &Baz { //~ ERROR function cannot return without recurring
+ self.as_ref() //~ NOTE recursive call site
+ }
}
fn all_fine() {