Skip to content

Commit

Permalink
Do not ICE when e.g. call_mut() is called on a closure whose kind i…
Browse files Browse the repository at this point in the history
…s not yet known.
  • Loading branch information
nikomatsakis committed Feb 1, 2015
1 parent a9c3841 commit 870aea2
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 28 deletions.
10 changes: 7 additions & 3 deletions src/librustc_typeck/check/method/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ pub enum MethodError {

// Multiple methods might apply.
Ambiguity(Vec<CandidateSource>),

// Using a `Fn`/`FnMut`/etc method on a raw closure type before we have inferred its kind.
ClosureAmbiguity(/* DefId of fn trait */ ast::DefId),
}

// A pared down enum describing just the places from which a method
Expand All @@ -65,9 +68,10 @@ pub fn exists<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-> bool
{
match probe::probe(fcx, span, method_name, self_ty, call_expr_id) {
Ok(_) => true,
Err(NoMatch(_, _)) => false,
Err(Ambiguity(_)) => true,
Ok(..) => true,
Err(NoMatch(..)) => false,
Err(Ambiguity(..)) => true,
Err(ClosureAmbiguity(..)) => true,
}
}

Expand Down
60 changes: 36 additions & 24 deletions src/librustc_typeck/check/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use super::{MethodError,Ambiguity,NoMatch};
use super::{MethodError};
use super::MethodIndex;
use super::{CandidateSource,ImplSource,TraitSource};
use super::suggest;
Expand Down Expand Up @@ -129,7 +129,7 @@ pub fn probe<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
// take place in the `fcx.infcx().probe` below.
let steps = match create_steps(fcx, span, self_ty) {
Some(steps) => steps,
None => return Err(NoMatch(Vec::new(), Vec::new())),
None => return Err(MethodError::NoMatch(Vec::new(), Vec::new())),
};

// Create a list of simplified self types, if we can.
Expand Down Expand Up @@ -158,7 +158,7 @@ pub fn probe<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
let (steps, opt_simplified_steps) = dummy.take().unwrap();
let mut probe_cx = ProbeContext::new(fcx, span, method_name, steps, opt_simplified_steps);
probe_cx.assemble_inherent_candidates();
probe_cx.assemble_extension_candidates_for_traits_in_scope(call_expr_id);
try!(probe_cx.assemble_extension_candidates_for_traits_in_scope(call_expr_id));
probe_cx.pick()
})
}
Expand Down Expand Up @@ -444,29 +444,34 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {

fn assemble_extension_candidates_for_traits_in_scope(&mut self,
expr_id: ast::NodeId)
-> Result<(),MethodError>
{
let mut duplicates = HashSet::new();
let opt_applicable_traits = self.fcx.ccx.trait_map.get(&expr_id);
for applicable_traits in opt_applicable_traits.into_iter() {
for &trait_did in applicable_traits.iter() {
if duplicates.insert(trait_did) {
self.assemble_extension_candidates_for_trait(trait_did);
try!(self.assemble_extension_candidates_for_trait(trait_did));
}
}
}
Ok(())
}

fn assemble_extension_candidates_for_all_traits(&mut self) {
fn assemble_extension_candidates_for_all_traits(&mut self) -> Result<(),MethodError> {
let mut duplicates = HashSet::new();
for trait_info in suggest::all_traits(self.fcx.ccx) {
if duplicates.insert(trait_info.def_id) {
self.assemble_extension_candidates_for_trait(trait_info.def_id)
try!(self.assemble_extension_candidates_for_trait(trait_info.def_id));
}
}
Ok(())
}

fn assemble_extension_candidates_for_trait(&mut self,
trait_def_id: ast::DefId) {
trait_def_id: ast::DefId)
-> Result<(),MethodError>
{
debug!("assemble_extension_candidates_for_trait(trait_def_id={})",
trait_def_id.repr(self.tcx()));

Expand All @@ -478,26 +483,27 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
.position(|item| item.name() == self.method_name);
let matching_index = match matching_index {
Some(i) => i,
None => { return; }
None => { return Ok(()); }
};
let method = match (&*trait_items)[matching_index].as_opt_method() {
Some(m) => m,
None => { return; }
None => { return Ok(()); }
};

// Check whether `trait_def_id` defines a method with suitable name:
if !self.has_applicable_self(&*method) {
debug!("method has inapplicable self");
return self.record_static_candidate(TraitSource(trait_def_id));
self.record_static_candidate(TraitSource(trait_def_id));
return Ok(());
}

self.assemble_extension_candidates_for_trait_impls(trait_def_id,
method.clone(),
matching_index);

self.assemble_closure_candidates(trait_def_id,
method.clone(),
matching_index);
try!(self.assemble_closure_candidates(trait_def_id,
method.clone(),
matching_index));

self.assemble_projection_candidates(trait_def_id,
method.clone(),
Expand All @@ -506,6 +512,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
self.assemble_where_clause_candidates(trait_def_id,
method,
matching_index);

Ok(())
}

fn assemble_extension_candidates_for_trait_impls(&mut self,
Expand Down Expand Up @@ -576,6 +584,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
trait_def_id: ast::DefId,
method_ty: Rc<ty::Method<'tcx>>,
method_index: uint)
-> Result<(),MethodError>
{
// Check if this is one of the Fn,FnMut,FnOnce traits.
let tcx = self.tcx();
Expand All @@ -586,7 +595,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
} else if Some(trait_def_id) == tcx.lang_items.fn_once_trait() {
ty::FnOnceClosureKind
} else {
return;
return Ok(());
};

// Check if there is an unboxed-closure self-type in the list of receivers.
Expand All @@ -602,10 +611,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
let closure_kind = match closure_kinds.get(&closure_def_id) {
Some(&k) => k,
None => {
self.tcx().sess.span_bug(
self.span,
&format!("No entry for closure: {}",
closure_def_id.repr(self.tcx()))[]);
return Err(MethodError::ClosureAmbiguity(trait_def_id));
}
};

Expand All @@ -630,6 +636,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
kind: ClosureCandidate(trait_def_id, method_index)
});
}

Ok(())
}

fn assemble_projection_candidates(&mut self,
Expand Down Expand Up @@ -735,11 +743,11 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
let span = self.span;
let tcx = self.tcx();

self.assemble_extension_candidates_for_all_traits();
try!(self.assemble_extension_candidates_for_all_traits());

let out_of_scope_traits = match self.pick_core() {
Some(Ok(p)) => vec![p.method_ty.container.id()],
Some(Err(Ambiguity(v))) => v.into_iter().map(|source| {
Some(Err(MethodError::Ambiguity(v))) => v.into_iter().map(|source| {
match source {
TraitSource(id) => id,
ImplSource(impl_id) => {
Expand All @@ -752,14 +760,18 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
}
}
}).collect(),
Some(Err(NoMatch(_, others))) => {
Some(Err(MethodError::NoMatch(_, others))) => {
assert!(others.is_empty());
vec![]
}
Some(Err(MethodError::ClosureAmbiguity(..))) => {
// this error only occurs when assembling candidates
tcx.sess.span_bug(span, "encountered ClosureAmbiguity from pick_core");
}
None => vec![],
};
;
Err(NoMatch(static_candidates, out_of_scope_traits))

Err(MethodError::NoMatch(static_candidates, out_of_scope_traits))
}

fn pick_core(&mut self) -> Option<PickResult<'tcx>> {
Expand Down Expand Up @@ -895,7 +907,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {

if applicable_candidates.len() > 1 {
let sources = probes.iter().map(|p| p.to_source()).collect();
return Some(Err(Ambiguity(sources)));
return Some(Err(MethodError::Ambiguity(sources)));
}

applicable_candidates.pop().map(|probe| {
Expand Down
14 changes: 14 additions & 0 deletions src/librustc_typeck/check/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use util::ppaux::UserString;

use syntax::{ast, ast_util};
use syntax::codemap::Span;
use syntax::print::pprust;

use std::cell;
use std::cmp::Ordering;
Expand All @@ -32,6 +33,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
span: Span,
rcvr_ty: Ty<'tcx>,
method_name: ast::Name,
callee_expr: &ast::Expr,
error: MethodError)
{
match error {
Expand Down Expand Up @@ -84,6 +86,18 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,

report_candidates(fcx, span, method_name, sources);
}

MethodError::ClosureAmbiguity(trait_def_id) => {
fcx.sess().span_err(
span,
&*format!("the `{}` method from the `{}` trait cannot be explicitly \
invoked on this closure as we have not yet inferred what \
kind of closure it is; use overloaded call notation instead \
(e.g., `{}()`)",
method_name.user_string(fcx.tcx()),
ty::item_path_str(fcx.tcx(), trait_def_id),
pprust::expr_to_string(callee_expr)));
}
}

fn report_candidates(fcx: &FnCtxt,
Expand Down
3 changes: 2 additions & 1 deletion src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2695,7 +2695,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
method_ty
}
Err(error) => {
method::report_error(fcx, method_name.span, expr_t, method_name.node.name, error);
method::report_error(fcx, method_name.span, expr_t,
method_name.node.name, rcvr, error);
fcx.write_error(expr.id);
fcx.tcx().types.err
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// 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.

#![feature(unboxed_closures)]

fn main() {
let mut zero = || {};
let () = zero.call_mut(());
//~^ ERROR we have not yet inferred what kind of closure it is
}

0 comments on commit 870aea2

Please sign in to comment.