mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 06:44:35 +00:00
Fixup indentation after methodification.
This commit is contained in:
parent
a1c170fc35
commit
42eb7032fa
@ -61,95 +61,95 @@ pub struct CombineFields<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
pub fn super_combine_tys<R>(&self,
|
||||
relation: &mut R,
|
||||
a: Ty<'tcx>,
|
||||
b: Ty<'tcx>)
|
||||
pub fn super_combine_tys<R>(&self,
|
||||
relation: &mut R,
|
||||
a: Ty<'tcx>,
|
||||
b: Ty<'tcx>)
|
||||
-> RelateResult<'tcx, Ty<'tcx>>
|
||||
where R: TypeRelation<'a, 'gcx, 'tcx>
|
||||
{
|
||||
let a_is_expected = relation.a_is_expected();
|
||||
|
||||
match (&a.sty, &b.sty) {
|
||||
// Relate integral variables to other types
|
||||
(&ty::TyInfer(ty::IntVar(a_id)), &ty::TyInfer(ty::IntVar(b_id))) => {
|
||||
self.int_unification_table
|
||||
.borrow_mut()
|
||||
.unify_var_var(a_id, b_id)
|
||||
.map_err(|e| int_unification_error(a_is_expected, e))?;
|
||||
Ok(a)
|
||||
}
|
||||
(&ty::TyInfer(ty::IntVar(v_id)), &ty::TyInt(v)) => {
|
||||
self.unify_integral_variable(a_is_expected, v_id, IntType(v))
|
||||
}
|
||||
(&ty::TyInt(v), &ty::TyInfer(ty::IntVar(v_id))) => {
|
||||
self.unify_integral_variable(!a_is_expected, v_id, IntType(v))
|
||||
}
|
||||
(&ty::TyInfer(ty::IntVar(v_id)), &ty::TyUint(v)) => {
|
||||
self.unify_integral_variable(a_is_expected, v_id, UintType(v))
|
||||
}
|
||||
(&ty::TyUint(v), &ty::TyInfer(ty::IntVar(v_id))) => {
|
||||
self.unify_integral_variable(!a_is_expected, v_id, UintType(v))
|
||||
}
|
||||
|
||||
// Relate floating-point variables to other types
|
||||
(&ty::TyInfer(ty::FloatVar(a_id)), &ty::TyInfer(ty::FloatVar(b_id))) => {
|
||||
self.float_unification_table
|
||||
.borrow_mut()
|
||||
.unify_var_var(a_id, b_id)
|
||||
.map_err(|e| float_unification_error(relation.a_is_expected(), e))?;
|
||||
Ok(a)
|
||||
}
|
||||
(&ty::TyInfer(ty::FloatVar(v_id)), &ty::TyFloat(v)) => {
|
||||
self.unify_float_variable(a_is_expected, v_id, v)
|
||||
}
|
||||
(&ty::TyFloat(v), &ty::TyInfer(ty::FloatVar(v_id))) => {
|
||||
self.unify_float_variable(!a_is_expected, v_id, v)
|
||||
}
|
||||
|
||||
// All other cases of inference are errors
|
||||
(&ty::TyInfer(_), _) |
|
||||
(_, &ty::TyInfer(_)) => {
|
||||
Err(TypeError::Sorts(ty::relate::expected_found(relation, &a, &b)))
|
||||
}
|
||||
|
||||
|
||||
_ => {
|
||||
ty::relate::super_relate_tys(relation, a, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unify_integral_variable(&self,
|
||||
vid_is_expected: bool,
|
||||
vid: ty::IntVid,
|
||||
val: ty::IntVarValue)
|
||||
-> RelateResult<'tcx, Ty<'tcx>>
|
||||
{
|
||||
self.int_unification_table
|
||||
.borrow_mut()
|
||||
.unify_var_value(vid, val)
|
||||
.map_err(|e| int_unification_error(vid_is_expected, e))?;
|
||||
match val {
|
||||
IntType(v) => Ok(self.tcx.mk_mach_int(v)),
|
||||
UintType(v) => Ok(self.tcx.mk_mach_uint(v)),
|
||||
}
|
||||
}
|
||||
|
||||
fn unify_float_variable(&self,
|
||||
vid_is_expected: bool,
|
||||
vid: ty::FloatVid,
|
||||
val: ast::FloatTy)
|
||||
-> RelateResult<'tcx, Ty<'tcx>>
|
||||
where R: TypeRelation<'a, 'gcx, 'tcx>
|
||||
{
|
||||
let a_is_expected = relation.a_is_expected();
|
||||
|
||||
match (&a.sty, &b.sty) {
|
||||
// Relate integral variables to other types
|
||||
(&ty::TyInfer(ty::IntVar(a_id)), &ty::TyInfer(ty::IntVar(b_id))) => {
|
||||
self.int_unification_table
|
||||
.borrow_mut()
|
||||
.unify_var_var(a_id, b_id)
|
||||
.map_err(|e| int_unification_error(a_is_expected, e))?;
|
||||
Ok(a)
|
||||
}
|
||||
(&ty::TyInfer(ty::IntVar(v_id)), &ty::TyInt(v)) => {
|
||||
self.unify_integral_variable(a_is_expected, v_id, IntType(v))
|
||||
}
|
||||
(&ty::TyInt(v), &ty::TyInfer(ty::IntVar(v_id))) => {
|
||||
self.unify_integral_variable(!a_is_expected, v_id, IntType(v))
|
||||
}
|
||||
(&ty::TyInfer(ty::IntVar(v_id)), &ty::TyUint(v)) => {
|
||||
self.unify_integral_variable(a_is_expected, v_id, UintType(v))
|
||||
}
|
||||
(&ty::TyUint(v), &ty::TyInfer(ty::IntVar(v_id))) => {
|
||||
self.unify_integral_variable(!a_is_expected, v_id, UintType(v))
|
||||
}
|
||||
|
||||
// Relate floating-point variables to other types
|
||||
(&ty::TyInfer(ty::FloatVar(a_id)), &ty::TyInfer(ty::FloatVar(b_id))) => {
|
||||
self.float_unification_table
|
||||
.borrow_mut()
|
||||
.unify_var_var(a_id, b_id)
|
||||
.map_err(|e| float_unification_error(relation.a_is_expected(), e))?;
|
||||
Ok(a)
|
||||
}
|
||||
(&ty::TyInfer(ty::FloatVar(v_id)), &ty::TyFloat(v)) => {
|
||||
self.unify_float_variable(a_is_expected, v_id, v)
|
||||
}
|
||||
(&ty::TyFloat(v), &ty::TyInfer(ty::FloatVar(v_id))) => {
|
||||
self.unify_float_variable(!a_is_expected, v_id, v)
|
||||
}
|
||||
|
||||
// All other cases of inference are errors
|
||||
(&ty::TyInfer(_), _) |
|
||||
(_, &ty::TyInfer(_)) => {
|
||||
Err(TypeError::Sorts(ty::relate::expected_found(relation, &a, &b)))
|
||||
}
|
||||
|
||||
|
||||
_ => {
|
||||
ty::relate::super_relate_tys(relation, a, b)
|
||||
}
|
||||
{
|
||||
self.float_unification_table
|
||||
.borrow_mut()
|
||||
.unify_var_value(vid, val)
|
||||
.map_err(|e| float_unification_error(vid_is_expected, e))?;
|
||||
Ok(self.tcx.mk_mach_float(val))
|
||||
}
|
||||
}
|
||||
|
||||
fn unify_integral_variable(&self,
|
||||
vid_is_expected: bool,
|
||||
vid: ty::IntVid,
|
||||
val: ty::IntVarValue)
|
||||
-> RelateResult<'tcx, Ty<'tcx>>
|
||||
{
|
||||
self.int_unification_table
|
||||
.borrow_mut()
|
||||
.unify_var_value(vid, val)
|
||||
.map_err(|e| int_unification_error(vid_is_expected, e))?;
|
||||
match val {
|
||||
IntType(v) => Ok(self.tcx.mk_mach_int(v)),
|
||||
UintType(v) => Ok(self.tcx.mk_mach_uint(v)),
|
||||
}
|
||||
}
|
||||
|
||||
fn unify_float_variable(&self,
|
||||
vid_is_expected: bool,
|
||||
vid: ty::FloatVid,
|
||||
val: ast::FloatTy)
|
||||
-> RelateResult<'tcx, Ty<'tcx>>
|
||||
{
|
||||
self.float_unification_table
|
||||
.borrow_mut()
|
||||
.unify_var_value(vid, val)
|
||||
.map_err(|e| float_unification_error(vid_is_expected, e))?;
|
||||
Ok(self.tcx.mk_mach_float(val))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
|
||||
pub fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> {
|
||||
self.infcx.tcx
|
||||
|
@ -422,169 +422,169 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
region_vars
|
||||
}
|
||||
|
||||
pub fn skolemize_late_bound_regions<T>(&self,
|
||||
binder: &ty::Binder<T>,
|
||||
snapshot: &CombinedSnapshot)
|
||||
-> (T, SkolemizationMap)
|
||||
where T : TypeFoldable<'tcx>
|
||||
{
|
||||
/*!
|
||||
* Replace all regions bound by `binder` with skolemized regions and
|
||||
* return a map indicating which bound-region was replaced with what
|
||||
* skolemized region. This is the first step of checking subtyping
|
||||
* when higher-ranked things are involved. See `README.md` for more
|
||||
* details.
|
||||
*/
|
||||
pub fn skolemize_late_bound_regions<T>(&self,
|
||||
binder: &ty::Binder<T>,
|
||||
snapshot: &CombinedSnapshot)
|
||||
-> (T, SkolemizationMap)
|
||||
where T : TypeFoldable<'tcx>
|
||||
{
|
||||
/*!
|
||||
* Replace all regions bound by `binder` with skolemized regions and
|
||||
* return a map indicating which bound-region was replaced with what
|
||||
* skolemized region. This is the first step of checking subtyping
|
||||
* when higher-ranked things are involved. See `README.md` for more
|
||||
* details.
|
||||
*/
|
||||
|
||||
let (result, map) = self.tcx.replace_late_bound_regions(binder, |br| {
|
||||
self.region_vars.new_skolemized(br, &snapshot.region_vars_snapshot)
|
||||
});
|
||||
let (result, map) = self.tcx.replace_late_bound_regions(binder, |br| {
|
||||
self.region_vars.new_skolemized(br, &snapshot.region_vars_snapshot)
|
||||
});
|
||||
|
||||
debug!("skolemize_bound_regions(binder={:?}, result={:?}, map={:?})",
|
||||
binder,
|
||||
result,
|
||||
map);
|
||||
debug!("skolemize_bound_regions(binder={:?}, result={:?}, map={:?})",
|
||||
binder,
|
||||
result,
|
||||
map);
|
||||
|
||||
(result, map)
|
||||
}
|
||||
|
||||
pub fn leak_check(&self,
|
||||
overly_polymorphic: bool,
|
||||
skol_map: &SkolemizationMap,
|
||||
snapshot: &CombinedSnapshot)
|
||||
-> RelateResult<'tcx, ()>
|
||||
{
|
||||
/*!
|
||||
* Searches the region constriants created since `snapshot` was started
|
||||
* and checks to determine whether any of the skolemized regions created
|
||||
* in `skol_map` would "escape" -- meaning that they are related to
|
||||
* other regions in some way. If so, the higher-ranked subtyping doesn't
|
||||
* hold. See `README.md` for more details.
|
||||
*/
|
||||
|
||||
debug!("leak_check: skol_map={:?}",
|
||||
skol_map);
|
||||
|
||||
let new_vars = self.region_vars_confined_to_snapshot(snapshot);
|
||||
for (&skol_br, &skol) in skol_map {
|
||||
let tainted = self.tainted_regions(snapshot, skol);
|
||||
for &tainted_region in &tainted {
|
||||
// Each skolemized should only be relatable to itself
|
||||
// or new variables:
|
||||
match tainted_region {
|
||||
ty::ReVar(vid) => {
|
||||
if new_vars.iter().any(|&x| x == vid) { continue; }
|
||||
}
|
||||
_ => {
|
||||
if tainted_region == skol { continue; }
|
||||
}
|
||||
};
|
||||
|
||||
debug!("{:?} (which replaced {:?}) is tainted by {:?}",
|
||||
skol,
|
||||
skol_br,
|
||||
tainted_region);
|
||||
|
||||
if overly_polymorphic {
|
||||
debug!("Overly polymorphic!");
|
||||
return Err(TypeError::RegionsOverlyPolymorphic(skol_br,
|
||||
tainted_region));
|
||||
} else {
|
||||
debug!("Not as polymorphic!");
|
||||
return Err(TypeError::RegionsInsufficientlyPolymorphic(skol_br,
|
||||
tainted_region));
|
||||
}
|
||||
}
|
||||
(result, map)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// This code converts from skolemized regions back to late-bound
|
||||
/// regions. It works by replacing each region in the taint set of a
|
||||
/// skolemized region with a bound-region. The bound region will be bound
|
||||
/// by the outer-most binder in `value`; the caller must ensure that there is
|
||||
/// such a binder and it is the right place.
|
||||
///
|
||||
/// This routine is only intended to be used when the leak-check has
|
||||
/// passed; currently, it's used in the trait matching code to create
|
||||
/// a set of nested obligations frmo an impl that matches against
|
||||
/// something higher-ranked. More details can be found in
|
||||
/// `librustc/middle/traits/README.md`.
|
||||
///
|
||||
/// As a brief example, consider the obligation `for<'a> Fn(&'a int)
|
||||
/// -> &'a int`, and the impl:
|
||||
///
|
||||
/// impl<A,R> Fn<A,R> for SomethingOrOther
|
||||
/// where A : Clone
|
||||
/// { ... }
|
||||
///
|
||||
/// Here we will have replaced `'a` with a skolemized region
|
||||
/// `'0`. This means that our substitution will be `{A=>&'0
|
||||
/// int, R=>&'0 int}`.
|
||||
///
|
||||
/// When we apply the substitution to the bounds, we will wind up with
|
||||
/// `&'0 int : Clone` as a predicate. As a last step, we then go and
|
||||
/// replace `'0` with a late-bound region `'a`. The depth is matched
|
||||
/// to the depth of the predicate, in this case 1, so that the final
|
||||
/// predicate is `for<'a> &'a int : Clone`.
|
||||
pub fn plug_leaks<T>(&self,
|
||||
skol_map: SkolemizationMap,
|
||||
snapshot: &CombinedSnapshot,
|
||||
value: &T) -> T
|
||||
where T : TypeFoldable<'tcx>
|
||||
{
|
||||
debug_assert!(self.leak_check(false, &skol_map, snapshot).is_ok());
|
||||
pub fn leak_check(&self,
|
||||
overly_polymorphic: bool,
|
||||
skol_map: &SkolemizationMap,
|
||||
snapshot: &CombinedSnapshot)
|
||||
-> RelateResult<'tcx, ()>
|
||||
{
|
||||
/*!
|
||||
* Searches the region constriants created since `snapshot` was started
|
||||
* and checks to determine whether any of the skolemized regions created
|
||||
* in `skol_map` would "escape" -- meaning that they are related to
|
||||
* other regions in some way. If so, the higher-ranked subtyping doesn't
|
||||
* hold. See `README.md` for more details.
|
||||
*/
|
||||
|
||||
debug!("plug_leaks(skol_map={:?}, value={:?})",
|
||||
skol_map,
|
||||
value);
|
||||
debug!("leak_check: skol_map={:?}",
|
||||
skol_map);
|
||||
|
||||
// Compute a mapping from the "taint set" of each skolemized
|
||||
// region back to the `ty::BoundRegion` that it originally
|
||||
// represented. Because `leak_check` passed, we know that
|
||||
// these taint sets are mutually disjoint.
|
||||
let inv_skol_map: FnvHashMap<ty::Region, ty::BoundRegion> =
|
||||
skol_map
|
||||
.into_iter()
|
||||
.flat_map(|(skol_br, skol)| {
|
||||
self.tainted_regions(snapshot, skol)
|
||||
.into_iter()
|
||||
.map(move |tainted_region| (tainted_region, skol_br))
|
||||
})
|
||||
.collect();
|
||||
let new_vars = self.region_vars_confined_to_snapshot(snapshot);
|
||||
for (&skol_br, &skol) in skol_map {
|
||||
let tainted = self.tainted_regions(snapshot, skol);
|
||||
for &tainted_region in &tainted {
|
||||
// Each skolemized should only be relatable to itself
|
||||
// or new variables:
|
||||
match tainted_region {
|
||||
ty::ReVar(vid) => {
|
||||
if new_vars.iter().any(|&x| x == vid) { continue; }
|
||||
}
|
||||
_ => {
|
||||
if tainted_region == skol { continue; }
|
||||
}
|
||||
};
|
||||
|
||||
debug!("plug_leaks: inv_skol_map={:?}",
|
||||
inv_skol_map);
|
||||
debug!("{:?} (which replaced {:?}) is tainted by {:?}",
|
||||
skol,
|
||||
skol_br,
|
||||
tainted_region);
|
||||
|
||||
// Remove any instantiated type variables from `value`; those can hide
|
||||
// references to regions from the `fold_regions` code below.
|
||||
let value = self.resolve_type_vars_if_possible(value);
|
||||
|
||||
// Map any skolemization byproducts back to a late-bound
|
||||
// region. Put that late-bound region at whatever the outermost
|
||||
// binder is that we encountered in `value`. The caller is
|
||||
// responsible for ensuring that (a) `value` contains at least one
|
||||
// binder and (b) that binder is the one we want to use.
|
||||
let result = self.tcx.fold_regions(&value, &mut false, |r, current_depth| {
|
||||
match inv_skol_map.get(&r) {
|
||||
None => r,
|
||||
Some(br) => {
|
||||
// It is the responsibility of the caller to ensure
|
||||
// that each skolemized region appears within a
|
||||
// binder. In practice, this routine is only used by
|
||||
// trait checking, and all of the skolemized regions
|
||||
// appear inside predicates, which always have
|
||||
// binders, so this assert is satisfied.
|
||||
assert!(current_depth > 1);
|
||||
|
||||
ty::ReLateBound(ty::DebruijnIndex::new(current_depth - 1), br.clone())
|
||||
if overly_polymorphic {
|
||||
debug!("Overly polymorphic!");
|
||||
return Err(TypeError::RegionsOverlyPolymorphic(skol_br,
|
||||
tainted_region));
|
||||
} else {
|
||||
debug!("Not as polymorphic!");
|
||||
return Err(TypeError::RegionsInsufficientlyPolymorphic(skol_br,
|
||||
tainted_region));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
debug!("plug_leaks: result={:?}",
|
||||
result);
|
||||
/// This code converts from skolemized regions back to late-bound
|
||||
/// regions. It works by replacing each region in the taint set of a
|
||||
/// skolemized region with a bound-region. The bound region will be bound
|
||||
/// by the outer-most binder in `value`; the caller must ensure that there is
|
||||
/// such a binder and it is the right place.
|
||||
///
|
||||
/// This routine is only intended to be used when the leak-check has
|
||||
/// passed; currently, it's used in the trait matching code to create
|
||||
/// a set of nested obligations frmo an impl that matches against
|
||||
/// something higher-ranked. More details can be found in
|
||||
/// `librustc/middle/traits/README.md`.
|
||||
///
|
||||
/// As a brief example, consider the obligation `for<'a> Fn(&'a int)
|
||||
/// -> &'a int`, and the impl:
|
||||
///
|
||||
/// impl<A,R> Fn<A,R> for SomethingOrOther
|
||||
/// where A : Clone
|
||||
/// { ... }
|
||||
///
|
||||
/// Here we will have replaced `'a` with a skolemized region
|
||||
/// `'0`. This means that our substitution will be `{A=>&'0
|
||||
/// int, R=>&'0 int}`.
|
||||
///
|
||||
/// When we apply the substitution to the bounds, we will wind up with
|
||||
/// `&'0 int : Clone` as a predicate. As a last step, we then go and
|
||||
/// replace `'0` with a late-bound region `'a`. The depth is matched
|
||||
/// to the depth of the predicate, in this case 1, so that the final
|
||||
/// predicate is `for<'a> &'a int : Clone`.
|
||||
pub fn plug_leaks<T>(&self,
|
||||
skol_map: SkolemizationMap,
|
||||
snapshot: &CombinedSnapshot,
|
||||
value: &T) -> T
|
||||
where T : TypeFoldable<'tcx>
|
||||
{
|
||||
debug_assert!(self.leak_check(false, &skol_map, snapshot).is_ok());
|
||||
|
||||
result
|
||||
}
|
||||
debug!("plug_leaks(skol_map={:?}, value={:?})",
|
||||
skol_map,
|
||||
value);
|
||||
|
||||
// Compute a mapping from the "taint set" of each skolemized
|
||||
// region back to the `ty::BoundRegion` that it originally
|
||||
// represented. Because `leak_check` passed, we know that
|
||||
// these taint sets are mutually disjoint.
|
||||
let inv_skol_map: FnvHashMap<ty::Region, ty::BoundRegion> =
|
||||
skol_map
|
||||
.into_iter()
|
||||
.flat_map(|(skol_br, skol)| {
|
||||
self.tainted_regions(snapshot, skol)
|
||||
.into_iter()
|
||||
.map(move |tainted_region| (tainted_region, skol_br))
|
||||
})
|
||||
.collect();
|
||||
|
||||
debug!("plug_leaks: inv_skol_map={:?}",
|
||||
inv_skol_map);
|
||||
|
||||
// Remove any instantiated type variables from `value`; those can hide
|
||||
// references to regions from the `fold_regions` code below.
|
||||
let value = self.resolve_type_vars_if_possible(value);
|
||||
|
||||
// Map any skolemization byproducts back to a late-bound
|
||||
// region. Put that late-bound region at whatever the outermost
|
||||
// binder is that we encountered in `value`. The caller is
|
||||
// responsible for ensuring that (a) `value` contains at least one
|
||||
// binder and (b) that binder is the one we want to use.
|
||||
let result = self.tcx.fold_regions(&value, &mut false, |r, current_depth| {
|
||||
match inv_skol_map.get(&r) {
|
||||
None => r,
|
||||
Some(br) => {
|
||||
// It is the responsibility of the caller to ensure
|
||||
// that each skolemized region appears within a
|
||||
// binder. In practice, this routine is only used by
|
||||
// trait checking, and all of the skolemized regions
|
||||
// appear inside predicates, which always have
|
||||
// binders, so this assert is satisfied.
|
||||
assert!(current_depth > 1);
|
||||
|
||||
ty::ReLateBound(ty::DebruijnIndex::new(current_depth - 1), br.clone())
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
debug!("plug_leaks: result={:?}",
|
||||
result);
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
@ -628,53 +628,53 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
self.drain_fulfillment_cx_or_panic(DUMMY_SP, &mut fulfill_cx, &result)
|
||||
}
|
||||
|
||||
pub fn drain_fulfillment_cx_or_panic<T>(&self,
|
||||
span: Span,
|
||||
fulfill_cx: &mut traits::FulfillmentContext<'tcx>,
|
||||
result: &T)
|
||||
-> T::Lifted
|
||||
where T: TypeFoldable<'tcx> + ty::Lift<'gcx>
|
||||
{
|
||||
let when = "resolving bounds after type-checking";
|
||||
let v = match self.drain_fulfillment_cx(fulfill_cx, result) {
|
||||
Ok(v) => v,
|
||||
Err(errors) => {
|
||||
span_bug!(span, "Encountered errors `{:?}` {}", errors, when);
|
||||
}
|
||||
};
|
||||
pub fn drain_fulfillment_cx_or_panic<T>(&self,
|
||||
span: Span,
|
||||
fulfill_cx: &mut traits::FulfillmentContext<'tcx>,
|
||||
result: &T)
|
||||
-> T::Lifted
|
||||
where T: TypeFoldable<'tcx> + ty::Lift<'gcx>
|
||||
{
|
||||
let when = "resolving bounds after type-checking";
|
||||
let v = match self.drain_fulfillment_cx(fulfill_cx, result) {
|
||||
Ok(v) => v,
|
||||
Err(errors) => {
|
||||
span_bug!(span, "Encountered errors `{:?}` {}", errors, when);
|
||||
}
|
||||
};
|
||||
|
||||
match self.tcx.lift_to_global(&v) {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
span_bug!(span, "Uninferred types/regions in `{:?}` {}", v, when);
|
||||
match self.tcx.lift_to_global(&v) {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
span_bug!(span, "Uninferred types/regions in `{:?}` {}", v, when);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Finishes processes any obligations that remain in the fulfillment
|
||||
/// context, and then "freshens" and returns `result`. This is
|
||||
/// primarily used during normalization and other cases where
|
||||
/// processing the obligations in `fulfill_cx` may cause type
|
||||
/// inference variables that appear in `result` to be unified, and
|
||||
/// hence we need to process those obligations to get the complete
|
||||
/// picture of the type.
|
||||
pub fn drain_fulfillment_cx<T>(&self,
|
||||
fulfill_cx: &mut traits::FulfillmentContext<'tcx>,
|
||||
result: &T)
|
||||
-> Result<T,Vec<traits::FulfillmentError<'tcx>>>
|
||||
where T : TypeFoldable<'tcx>
|
||||
{
|
||||
debug!("drain_fulfillment_cx(result={:?})",
|
||||
result);
|
||||
/// Finishes processes any obligations that remain in the fulfillment
|
||||
/// context, and then "freshens" and returns `result`. This is
|
||||
/// primarily used during normalization and other cases where
|
||||
/// processing the obligations in `fulfill_cx` may cause type
|
||||
/// inference variables that appear in `result` to be unified, and
|
||||
/// hence we need to process those obligations to get the complete
|
||||
/// picture of the type.
|
||||
pub fn drain_fulfillment_cx<T>(&self,
|
||||
fulfill_cx: &mut traits::FulfillmentContext<'tcx>,
|
||||
result: &T)
|
||||
-> Result<T,Vec<traits::FulfillmentError<'tcx>>>
|
||||
where T : TypeFoldable<'tcx>
|
||||
{
|
||||
debug!("drain_fulfillment_cx(result={:?})",
|
||||
result);
|
||||
|
||||
// In principle, we only need to do this so long as `result`
|
||||
// contains unbound type parameters. It could be a slight
|
||||
// optimization to stop iterating early.
|
||||
fulfill_cx.select_all_or_error(self)?;
|
||||
// In principle, we only need to do this so long as `result`
|
||||
// contains unbound type parameters. It could be a slight
|
||||
// optimization to stop iterating early.
|
||||
fulfill_cx.select_all_or_error(self)?;
|
||||
|
||||
let result = self.resolve_type_vars_if_possible(result);
|
||||
Ok(self.tcx.erase_regions(&result))
|
||||
}
|
||||
let result = self.resolve_type_vars_if_possible(result);
|
||||
Ok(self.tcx.erase_regions(&result))
|
||||
}
|
||||
|
||||
pub fn projection_mode(&self) -> ProjectionMode {
|
||||
self.projection_mode
|
||||
|
@ -21,63 +21,63 @@ use syntax::codemap::Span;
|
||||
use hir as ast;
|
||||
|
||||
impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
|
||||
pub fn prohibit_type_params(self, segments: &[ast::PathSegment]) {
|
||||
for segment in segments {
|
||||
for typ in segment.parameters.types() {
|
||||
span_err!(self.sess, typ.span, E0109,
|
||||
"type parameters are not allowed on this type");
|
||||
break;
|
||||
}
|
||||
for lifetime in segment.parameters.lifetimes() {
|
||||
span_err!(self.sess, lifetime.span, E0110,
|
||||
"lifetime parameters are not allowed on this type");
|
||||
break;
|
||||
}
|
||||
for binding in segment.parameters.bindings() {
|
||||
self.prohibit_projection(binding.span);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prohibit_projection(self, span: Span)
|
||||
{
|
||||
span_err!(self.sess, span, E0229,
|
||||
"associated type bindings are not allowed here");
|
||||
}
|
||||
|
||||
pub fn prim_ty_to_ty(self,
|
||||
segments: &[ast::PathSegment],
|
||||
nty: ast::PrimTy)
|
||||
-> Ty<'tcx> {
|
||||
self.prohibit_type_params(segments);
|
||||
match nty {
|
||||
ast::TyBool => self.types.bool,
|
||||
ast::TyChar => self.types.char,
|
||||
ast::TyInt(it) => self.mk_mach_int(it),
|
||||
ast::TyUint(uit) => self.mk_mach_uint(uit),
|
||||
ast::TyFloat(ft) => self.mk_mach_float(ft),
|
||||
ast::TyStr => self.mk_str()
|
||||
}
|
||||
}
|
||||
|
||||
/// If a type in the AST is a primitive type, return the ty::Ty corresponding
|
||||
/// to it.
|
||||
pub fn ast_ty_to_prim_ty(self, ast_ty: &ast::Ty) -> Option<Ty<'tcx>> {
|
||||
if let ast::TyPath(None, ref path) = ast_ty.node {
|
||||
let def = match self.def_map.borrow().get(&ast_ty.id) {
|
||||
None => {
|
||||
span_bug!(ast_ty.span, "unbound path {:?}", path)
|
||||
pub fn prohibit_type_params(self, segments: &[ast::PathSegment]) {
|
||||
for segment in segments {
|
||||
for typ in segment.parameters.types() {
|
||||
span_err!(self.sess, typ.span, E0109,
|
||||
"type parameters are not allowed on this type");
|
||||
break;
|
||||
}
|
||||
for lifetime in segment.parameters.lifetimes() {
|
||||
span_err!(self.sess, lifetime.span, E0110,
|
||||
"lifetime parameters are not allowed on this type");
|
||||
break;
|
||||
}
|
||||
for binding in segment.parameters.bindings() {
|
||||
self.prohibit_projection(binding.span);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prohibit_projection(self, span: Span)
|
||||
{
|
||||
span_err!(self.sess, span, E0229,
|
||||
"associated type bindings are not allowed here");
|
||||
}
|
||||
|
||||
pub fn prim_ty_to_ty(self,
|
||||
segments: &[ast::PathSegment],
|
||||
nty: ast::PrimTy)
|
||||
-> Ty<'tcx> {
|
||||
self.prohibit_type_params(segments);
|
||||
match nty {
|
||||
ast::TyBool => self.types.bool,
|
||||
ast::TyChar => self.types.char,
|
||||
ast::TyInt(it) => self.mk_mach_int(it),
|
||||
ast::TyUint(uit) => self.mk_mach_uint(uit),
|
||||
ast::TyFloat(ft) => self.mk_mach_float(ft),
|
||||
ast::TyStr => self.mk_str()
|
||||
}
|
||||
}
|
||||
|
||||
/// If a type in the AST is a primitive type, return the ty::Ty corresponding
|
||||
/// to it.
|
||||
pub fn ast_ty_to_prim_ty(self, ast_ty: &ast::Ty) -> Option<Ty<'tcx>> {
|
||||
if let ast::TyPath(None, ref path) = ast_ty.node {
|
||||
let def = match self.def_map.borrow().get(&ast_ty.id) {
|
||||
None => {
|
||||
span_bug!(ast_ty.span, "unbound path {:?}", path)
|
||||
}
|
||||
Some(d) => d.full_def()
|
||||
};
|
||||
if let Def::PrimTy(nty) = def {
|
||||
Some(self.prim_ty_to_ty(&path.segments, nty))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
Some(d) => d.full_def()
|
||||
};
|
||||
if let Def::PrimTy(nty) = def {
|
||||
Some(self.prim_ty_to_ty(&path.segments, nty))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -670,46 +670,46 @@ fn is_staged_api<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefId) -> bool {
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
|
||||
/// Lookup the stability for a node, loading external crate
|
||||
/// metadata as necessary.
|
||||
pub fn lookup_stability(self, id: DefId) -> Option<&'tcx Stability> {
|
||||
if let Some(st) = self.stability.borrow().stab_map.get(&id) {
|
||||
return *st;
|
||||
/// Lookup the stability for a node, loading external crate
|
||||
/// metadata as necessary.
|
||||
pub fn lookup_stability(self, id: DefId) -> Option<&'tcx Stability> {
|
||||
if let Some(st) = self.stability.borrow().stab_map.get(&id) {
|
||||
return *st;
|
||||
}
|
||||
|
||||
let st = self.lookup_stability_uncached(id);
|
||||
self.stability.borrow_mut().stab_map.insert(id, st);
|
||||
st
|
||||
}
|
||||
|
||||
let st = self.lookup_stability_uncached(id);
|
||||
self.stability.borrow_mut().stab_map.insert(id, st);
|
||||
st
|
||||
}
|
||||
pub fn lookup_deprecation(self, id: DefId) -> Option<Deprecation> {
|
||||
if let Some(depr) = self.stability.borrow().depr_map.get(&id) {
|
||||
return depr.clone();
|
||||
}
|
||||
|
||||
pub fn lookup_deprecation(self, id: DefId) -> Option<Deprecation> {
|
||||
if let Some(depr) = self.stability.borrow().depr_map.get(&id) {
|
||||
return depr.clone();
|
||||
let depr = self.lookup_deprecation_uncached(id);
|
||||
self.stability.borrow_mut().depr_map.insert(id, depr.clone());
|
||||
depr
|
||||
}
|
||||
|
||||
let depr = self.lookup_deprecation_uncached(id);
|
||||
self.stability.borrow_mut().depr_map.insert(id, depr.clone());
|
||||
depr
|
||||
}
|
||||
|
||||
fn lookup_stability_uncached(self, id: DefId) -> Option<&'tcx Stability> {
|
||||
debug!("lookup(id={:?})", id);
|
||||
if id.is_local() {
|
||||
None // The stability cache is filled partially lazily
|
||||
} else {
|
||||
self.sess.cstore.stability(id).map(|st| self.intern_stability(st))
|
||||
fn lookup_stability_uncached(self, id: DefId) -> Option<&'tcx Stability> {
|
||||
debug!("lookup(id={:?})", id);
|
||||
if id.is_local() {
|
||||
None // The stability cache is filled partially lazily
|
||||
} else {
|
||||
self.sess.cstore.stability(id).map(|st| self.intern_stability(st))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lookup_deprecation_uncached(self, id: DefId) -> Option<Deprecation> {
|
||||
debug!("lookup(id={:?})", id);
|
||||
if id.is_local() {
|
||||
None // The stability cache is filled partially lazily
|
||||
} else {
|
||||
self.sess.cstore.deprecation(id)
|
||||
fn lookup_deprecation_uncached(self, id: DefId) -> Option<Deprecation> {
|
||||
debug!("lookup(id={:?})", id);
|
||||
if id.is_local() {
|
||||
None // The stability cache is filled partially lazily
|
||||
} else {
|
||||
self.sess.cstore.deprecation(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Given the list of enabled features that were not language features (i.e. that
|
||||
/// were expected to be library features), and the list of features used from
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -54,320 +54,320 @@ pub enum MethodViolationCode {
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
|
||||
pub fn is_object_safe(self, trait_def_id: DefId) -> bool {
|
||||
// Because we query yes/no results frequently, we keep a cache:
|
||||
let def = self.lookup_trait_def(trait_def_id);
|
||||
pub fn is_object_safe(self, trait_def_id: DefId) -> bool {
|
||||
// Because we query yes/no results frequently, we keep a cache:
|
||||
let def = self.lookup_trait_def(trait_def_id);
|
||||
|
||||
let result = def.object_safety().unwrap_or_else(|| {
|
||||
let result = self.object_safety_violations(trait_def_id).is_empty();
|
||||
let result = def.object_safety().unwrap_or_else(|| {
|
||||
let result = self.object_safety_violations(trait_def_id).is_empty();
|
||||
|
||||
// Record just a yes/no result in the cache; this is what is
|
||||
// queried most frequently. Note that this may overwrite a
|
||||
// previous result, but always with the same thing.
|
||||
def.set_object_safety(result);
|
||||
// Record just a yes/no result in the cache; this is what is
|
||||
// queried most frequently. Note that this may overwrite a
|
||||
// previous result, but always with the same thing.
|
||||
def.set_object_safety(result);
|
||||
|
||||
result
|
||||
});
|
||||
|
||||
debug!("is_object_safe({:?}) = {}", trait_def_id, result);
|
||||
|
||||
result
|
||||
});
|
||||
|
||||
debug!("is_object_safe({:?}) = {}", trait_def_id, result);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Returns the object safety violations that affect
|
||||
/// astconv - currently, Self in supertraits. This is needed
|
||||
/// because `object_safety_violations` can't be used during
|
||||
/// type collection.
|
||||
pub fn astconv_object_safety_violations(self, trait_def_id: DefId)
|
||||
-> Vec<ObjectSafetyViolation<'tcx>>
|
||||
{
|
||||
let mut violations = vec![];
|
||||
|
||||
if self.supertraits_reference_self(trait_def_id) {
|
||||
violations.push(ObjectSafetyViolation::SupertraitSelf);
|
||||
}
|
||||
|
||||
debug!("astconv_object_safety_violations(trait_def_id={:?}) = {:?}",
|
||||
trait_def_id,
|
||||
violations);
|
||||
/// Returns the object safety violations that affect
|
||||
/// astconv - currently, Self in supertraits. This is needed
|
||||
/// because `object_safety_violations` can't be used during
|
||||
/// type collection.
|
||||
pub fn astconv_object_safety_violations(self, trait_def_id: DefId)
|
||||
-> Vec<ObjectSafetyViolation<'tcx>>
|
||||
{
|
||||
let mut violations = vec![];
|
||||
|
||||
violations
|
||||
}
|
||||
|
||||
pub fn object_safety_violations(self, trait_def_id: DefId)
|
||||
-> Vec<ObjectSafetyViolation<'tcx>>
|
||||
{
|
||||
traits::supertrait_def_ids(self, trait_def_id)
|
||||
.flat_map(|def_id| self.object_safety_violations_for_trait(def_id))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn object_safety_violations_for_trait(self, trait_def_id: DefId)
|
||||
-> Vec<ObjectSafetyViolation<'tcx>>
|
||||
{
|
||||
// Check methods for violations.
|
||||
let mut violations: Vec<_> =
|
||||
self.trait_items(trait_def_id).iter()
|
||||
.filter_map(|item| {
|
||||
match *item {
|
||||
ty::MethodTraitItem(ref m) => {
|
||||
self.object_safety_violation_for_method(trait_def_id, &m)
|
||||
.map(|code| ObjectSafetyViolation::Method(m.clone(), code))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Check the trait itself.
|
||||
if self.trait_has_sized_self(trait_def_id) {
|
||||
violations.push(ObjectSafetyViolation::SizedSelf);
|
||||
}
|
||||
if self.supertraits_reference_self(trait_def_id) {
|
||||
violations.push(ObjectSafetyViolation::SupertraitSelf);
|
||||
}
|
||||
|
||||
debug!("object_safety_violations_for_trait(trait_def_id={:?}) = {:?}",
|
||||
trait_def_id,
|
||||
violations);
|
||||
|
||||
violations
|
||||
}
|
||||
|
||||
fn supertraits_reference_self(self, trait_def_id: DefId) -> bool {
|
||||
let trait_def = self.lookup_trait_def(trait_def_id);
|
||||
let trait_ref = trait_def.trait_ref.clone();
|
||||
let trait_ref = trait_ref.to_poly_trait_ref();
|
||||
let predicates = self.lookup_super_predicates(trait_def_id);
|
||||
predicates
|
||||
.predicates
|
||||
.into_iter()
|
||||
.map(|predicate| predicate.subst_supertrait(self, &trait_ref))
|
||||
.any(|predicate| {
|
||||
match predicate {
|
||||
ty::Predicate::Trait(ref data) => {
|
||||
// In the case of a trait predicate, we can skip the "self" type.
|
||||
data.0.trait_ref.substs.types.get_slice(TypeSpace)
|
||||
.iter()
|
||||
.cloned()
|
||||
.any(|t| t.has_self_ty())
|
||||
}
|
||||
ty::Predicate::Projection(..) |
|
||||
ty::Predicate::WellFormed(..) |
|
||||
ty::Predicate::ObjectSafe(..) |
|
||||
ty::Predicate::TypeOutlives(..) |
|
||||
ty::Predicate::RegionOutlives(..) |
|
||||
ty::Predicate::ClosureKind(..) |
|
||||
ty::Predicate::Rfc1592(..) |
|
||||
ty::Predicate::Equate(..) => {
|
||||
false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn trait_has_sized_self(self, trait_def_id: DefId) -> bool {
|
||||
let trait_def = self.lookup_trait_def(trait_def_id);
|
||||
let trait_predicates = self.lookup_predicates(trait_def_id);
|
||||
self.generics_require_sized_self(&trait_def.generics, &trait_predicates)
|
||||
}
|
||||
|
||||
fn generics_require_sized_self(self,
|
||||
generics: &ty::Generics<'gcx>,
|
||||
predicates: &ty::GenericPredicates<'gcx>)
|
||||
-> bool
|
||||
{
|
||||
let sized_def_id = match self.lang_items.sized_trait() {
|
||||
Some(def_id) => def_id,
|
||||
None => { return false; /* No Sized trait, can't require it! */ }
|
||||
};
|
||||
|
||||
// Search for a predicate like `Self : Sized` amongst the trait bounds.
|
||||
let free_substs = self.construct_free_substs(generics,
|
||||
self.region_maps.node_extent(ast::DUMMY_NODE_ID));
|
||||
let predicates = predicates.instantiate(self, &free_substs).predicates.into_vec();
|
||||
elaborate_predicates(self, predicates)
|
||||
.any(|predicate| {
|
||||
match predicate {
|
||||
ty::Predicate::Trait(ref trait_pred) if trait_pred.def_id() == sized_def_id => {
|
||||
trait_pred.0.self_ty().is_self()
|
||||
}
|
||||
ty::Predicate::Projection(..) |
|
||||
ty::Predicate::Trait(..) |
|
||||
ty::Predicate::Rfc1592(..) |
|
||||
ty::Predicate::Equate(..) |
|
||||
ty::Predicate::RegionOutlives(..) |
|
||||
ty::Predicate::WellFormed(..) |
|
||||
ty::Predicate::ObjectSafe(..) |
|
||||
ty::Predicate::ClosureKind(..) |
|
||||
ty::Predicate::TypeOutlives(..) => {
|
||||
false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns `Some(_)` if this method makes the containing trait not object safe.
|
||||
fn object_safety_violation_for_method(self,
|
||||
trait_def_id: DefId,
|
||||
method: &ty::Method<'gcx>)
|
||||
-> Option<MethodViolationCode>
|
||||
{
|
||||
// Any method that has a `Self : Sized` requisite is otherwise
|
||||
// exempt from the regulations.
|
||||
if self.generics_require_sized_self(&method.generics, &method.predicates) {
|
||||
return None;
|
||||
}
|
||||
|
||||
self.virtual_call_violation_for_method(trait_def_id, method)
|
||||
}
|
||||
|
||||
/// We say a method is *vtable safe* if it can be invoked on a trait
|
||||
/// object. Note that object-safe traits can have some
|
||||
/// non-vtable-safe methods, so long as they require `Self:Sized` or
|
||||
/// otherwise ensure that they cannot be used when `Self=Trait`.
|
||||
pub fn is_vtable_safe_method(self,
|
||||
trait_def_id: DefId,
|
||||
method: &ty::Method<'tcx>)
|
||||
-> bool
|
||||
{
|
||||
self.virtual_call_violation_for_method(trait_def_id, method).is_none()
|
||||
}
|
||||
|
||||
/// Returns `Some(_)` if this method cannot be called on a trait
|
||||
/// object; this does not necessarily imply that the enclosing trait
|
||||
/// is not object safe, because the method might have a where clause
|
||||
/// `Self:Sized`.
|
||||
fn virtual_call_violation_for_method(self,
|
||||
trait_def_id: DefId,
|
||||
method: &ty::Method<'tcx>)
|
||||
-> Option<MethodViolationCode>
|
||||
{
|
||||
// The method's first parameter must be something that derefs (or
|
||||
// autorefs) to `&self`. For now, we only accept `self`, `&self`
|
||||
// and `Box<Self>`.
|
||||
match method.explicit_self {
|
||||
ty::ExplicitSelfCategory::Static => {
|
||||
return Some(MethodViolationCode::StaticMethod);
|
||||
if self.supertraits_reference_self(trait_def_id) {
|
||||
violations.push(ObjectSafetyViolation::SupertraitSelf);
|
||||
}
|
||||
|
||||
ty::ExplicitSelfCategory::ByValue |
|
||||
ty::ExplicitSelfCategory::ByReference(..) |
|
||||
ty::ExplicitSelfCategory::ByBox => {
|
||||
}
|
||||
debug!("astconv_object_safety_violations(trait_def_id={:?}) = {:?}",
|
||||
trait_def_id,
|
||||
violations);
|
||||
|
||||
violations
|
||||
}
|
||||
|
||||
// The `Self` type is erased, so it should not appear in list of
|
||||
// arguments or return type apart from the receiver.
|
||||
let ref sig = method.fty.sig;
|
||||
for &input_ty in &sig.0.inputs[1..] {
|
||||
if self.contains_illegal_self_type_reference(trait_def_id, input_ty) {
|
||||
return Some(MethodViolationCode::ReferencesSelf);
|
||||
}
|
||||
}
|
||||
if let ty::FnConverging(result_type) = sig.0.output {
|
||||
if self.contains_illegal_self_type_reference(trait_def_id, result_type) {
|
||||
return Some(MethodViolationCode::ReferencesSelf);
|
||||
}
|
||||
pub fn object_safety_violations(self, trait_def_id: DefId)
|
||||
-> Vec<ObjectSafetyViolation<'tcx>>
|
||||
{
|
||||
traits::supertrait_def_ids(self, trait_def_id)
|
||||
.flat_map(|def_id| self.object_safety_violations_for_trait(def_id))
|
||||
.collect()
|
||||
}
|
||||
|
||||
// We can't monomorphize things like `fn foo<A>(...)`.
|
||||
if !method.generics.types.is_empty_in(subst::FnSpace) {
|
||||
return Some(MethodViolationCode::Generic);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn contains_illegal_self_type_reference(self,
|
||||
trait_def_id: DefId,
|
||||
ty: Ty<'tcx>)
|
||||
-> bool
|
||||
{
|
||||
// This is somewhat subtle. In general, we want to forbid
|
||||
// references to `Self` in the argument and return types,
|
||||
// since the value of `Self` is erased. However, there is one
|
||||
// exception: it is ok to reference `Self` in order to access
|
||||
// an associated type of the current trait, since we retain
|
||||
// the value of those associated types in the object type
|
||||
// itself.
|
||||
//
|
||||
// ```rust
|
||||
// trait SuperTrait {
|
||||
// type X;
|
||||
// }
|
||||
//
|
||||
// trait Trait : SuperTrait {
|
||||
// type Y;
|
||||
// fn foo(&self, x: Self) // bad
|
||||
// fn foo(&self) -> Self // bad
|
||||
// fn foo(&self) -> Option<Self> // bad
|
||||
// fn foo(&self) -> Self::Y // OK, desugars to next example
|
||||
// fn foo(&self) -> <Self as Trait>::Y // OK
|
||||
// fn foo(&self) -> Self::X // OK, desugars to next example
|
||||
// fn foo(&self) -> <Self as SuperTrait>::X // OK
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// However, it is not as simple as allowing `Self` in a projected
|
||||
// type, because there are illegal ways to use `Self` as well:
|
||||
//
|
||||
// ```rust
|
||||
// trait Trait : SuperTrait {
|
||||
// ...
|
||||
// fn foo(&self) -> <Self as SomeOtherTrait>::X;
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// Here we will not have the type of `X` recorded in the
|
||||
// object type, and we cannot resolve `Self as SomeOtherTrait`
|
||||
// without knowing what `Self` is.
|
||||
|
||||
let mut supertraits: Option<Vec<ty::PolyTraitRef<'tcx>>> = None;
|
||||
let mut error = false;
|
||||
ty.maybe_walk(|ty| {
|
||||
match ty.sty {
|
||||
ty::TyParam(ref param_ty) => {
|
||||
if param_ty.space == SelfSpace {
|
||||
error = true;
|
||||
fn object_safety_violations_for_trait(self, trait_def_id: DefId)
|
||||
-> Vec<ObjectSafetyViolation<'tcx>>
|
||||
{
|
||||
// Check methods for violations.
|
||||
let mut violations: Vec<_> =
|
||||
self.trait_items(trait_def_id).iter()
|
||||
.filter_map(|item| {
|
||||
match *item {
|
||||
ty::MethodTraitItem(ref m) => {
|
||||
self.object_safety_violation_for_method(trait_def_id, &m)
|
||||
.map(|code| ObjectSafetyViolation::Method(m.clone(), code))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
false // no contained types to walk
|
||||
// Check the trait itself.
|
||||
if self.trait_has_sized_self(trait_def_id) {
|
||||
violations.push(ObjectSafetyViolation::SizedSelf);
|
||||
}
|
||||
if self.supertraits_reference_self(trait_def_id) {
|
||||
violations.push(ObjectSafetyViolation::SupertraitSelf);
|
||||
}
|
||||
|
||||
debug!("object_safety_violations_for_trait(trait_def_id={:?}) = {:?}",
|
||||
trait_def_id,
|
||||
violations);
|
||||
|
||||
violations
|
||||
}
|
||||
|
||||
fn supertraits_reference_self(self, trait_def_id: DefId) -> bool {
|
||||
let trait_def = self.lookup_trait_def(trait_def_id);
|
||||
let trait_ref = trait_def.trait_ref.clone();
|
||||
let trait_ref = trait_ref.to_poly_trait_ref();
|
||||
let predicates = self.lookup_super_predicates(trait_def_id);
|
||||
predicates
|
||||
.predicates
|
||||
.into_iter()
|
||||
.map(|predicate| predicate.subst_supertrait(self, &trait_ref))
|
||||
.any(|predicate| {
|
||||
match predicate {
|
||||
ty::Predicate::Trait(ref data) => {
|
||||
// In the case of a trait predicate, we can skip the "self" type.
|
||||
data.0.trait_ref.substs.types.get_slice(TypeSpace)
|
||||
.iter()
|
||||
.cloned()
|
||||
.any(|t| t.has_self_ty())
|
||||
}
|
||||
ty::Predicate::Projection(..) |
|
||||
ty::Predicate::WellFormed(..) |
|
||||
ty::Predicate::ObjectSafe(..) |
|
||||
ty::Predicate::TypeOutlives(..) |
|
||||
ty::Predicate::RegionOutlives(..) |
|
||||
ty::Predicate::ClosureKind(..) |
|
||||
ty::Predicate::Rfc1592(..) |
|
||||
ty::Predicate::Equate(..) => {
|
||||
false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn trait_has_sized_self(self, trait_def_id: DefId) -> bool {
|
||||
let trait_def = self.lookup_trait_def(trait_def_id);
|
||||
let trait_predicates = self.lookup_predicates(trait_def_id);
|
||||
self.generics_require_sized_self(&trait_def.generics, &trait_predicates)
|
||||
}
|
||||
|
||||
fn generics_require_sized_self(self,
|
||||
generics: &ty::Generics<'gcx>,
|
||||
predicates: &ty::GenericPredicates<'gcx>)
|
||||
-> bool
|
||||
{
|
||||
let sized_def_id = match self.lang_items.sized_trait() {
|
||||
Some(def_id) => def_id,
|
||||
None => { return false; /* No Sized trait, can't require it! */ }
|
||||
};
|
||||
|
||||
// Search for a predicate like `Self : Sized` amongst the trait bounds.
|
||||
let free_substs = self.construct_free_substs(generics,
|
||||
self.region_maps.node_extent(ast::DUMMY_NODE_ID));
|
||||
let predicates = predicates.instantiate(self, &free_substs).predicates.into_vec();
|
||||
elaborate_predicates(self, predicates)
|
||||
.any(|predicate| {
|
||||
match predicate {
|
||||
ty::Predicate::Trait(ref trait_pred) if trait_pred.def_id() == sized_def_id => {
|
||||
trait_pred.0.self_ty().is_self()
|
||||
}
|
||||
ty::Predicate::Projection(..) |
|
||||
ty::Predicate::Trait(..) |
|
||||
ty::Predicate::Rfc1592(..) |
|
||||
ty::Predicate::Equate(..) |
|
||||
ty::Predicate::RegionOutlives(..) |
|
||||
ty::Predicate::WellFormed(..) |
|
||||
ty::Predicate::ObjectSafe(..) |
|
||||
ty::Predicate::ClosureKind(..) |
|
||||
ty::Predicate::TypeOutlives(..) => {
|
||||
false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns `Some(_)` if this method makes the containing trait not object safe.
|
||||
fn object_safety_violation_for_method(self,
|
||||
trait_def_id: DefId,
|
||||
method: &ty::Method<'gcx>)
|
||||
-> Option<MethodViolationCode>
|
||||
{
|
||||
// Any method that has a `Self : Sized` requisite is otherwise
|
||||
// exempt from the regulations.
|
||||
if self.generics_require_sized_self(&method.generics, &method.predicates) {
|
||||
return None;
|
||||
}
|
||||
|
||||
self.virtual_call_violation_for_method(trait_def_id, method)
|
||||
}
|
||||
|
||||
/// We say a method is *vtable safe* if it can be invoked on a trait
|
||||
/// object. Note that object-safe traits can have some
|
||||
/// non-vtable-safe methods, so long as they require `Self:Sized` or
|
||||
/// otherwise ensure that they cannot be used when `Self=Trait`.
|
||||
pub fn is_vtable_safe_method(self,
|
||||
trait_def_id: DefId,
|
||||
method: &ty::Method<'tcx>)
|
||||
-> bool
|
||||
{
|
||||
self.virtual_call_violation_for_method(trait_def_id, method).is_none()
|
||||
}
|
||||
|
||||
/// Returns `Some(_)` if this method cannot be called on a trait
|
||||
/// object; this does not necessarily imply that the enclosing trait
|
||||
/// is not object safe, because the method might have a where clause
|
||||
/// `Self:Sized`.
|
||||
fn virtual_call_violation_for_method(self,
|
||||
trait_def_id: DefId,
|
||||
method: &ty::Method<'tcx>)
|
||||
-> Option<MethodViolationCode>
|
||||
{
|
||||
// The method's first parameter must be something that derefs (or
|
||||
// autorefs) to `&self`. For now, we only accept `self`, `&self`
|
||||
// and `Box<Self>`.
|
||||
match method.explicit_self {
|
||||
ty::ExplicitSelfCategory::Static => {
|
||||
return Some(MethodViolationCode::StaticMethod);
|
||||
}
|
||||
|
||||
ty::TyProjection(ref data) => {
|
||||
// This is a projected type `<Foo as SomeTrait>::X`.
|
||||
|
||||
// Compute supertraits of current trait lazily.
|
||||
if supertraits.is_none() {
|
||||
let trait_def = self.lookup_trait_def(trait_def_id);
|
||||
let trait_ref = ty::Binder(trait_def.trait_ref.clone());
|
||||
supertraits = Some(traits::supertraits(self, trait_ref).collect());
|
||||
}
|
||||
|
||||
// Determine whether the trait reference `Foo as
|
||||
// SomeTrait` is in fact a supertrait of the
|
||||
// current trait. In that case, this type is
|
||||
// legal, because the type `X` will be specified
|
||||
// in the object type. Note that we can just use
|
||||
// direct equality here because all of these types
|
||||
// are part of the formal parameter listing, and
|
||||
// hence there should be no inference variables.
|
||||
let projection_trait_ref = ty::Binder(data.trait_ref.clone());
|
||||
let is_supertrait_of_current_trait =
|
||||
supertraits.as_ref().unwrap().contains(&projection_trait_ref);
|
||||
|
||||
if is_supertrait_of_current_trait {
|
||||
false // do not walk contained types, do not report error, do collect $200
|
||||
} else {
|
||||
true // DO walk contained types, POSSIBLY reporting an error
|
||||
}
|
||||
ty::ExplicitSelfCategory::ByValue |
|
||||
ty::ExplicitSelfCategory::ByReference(..) |
|
||||
ty::ExplicitSelfCategory::ByBox => {
|
||||
}
|
||||
|
||||
_ => true, // walk contained types, if any
|
||||
}
|
||||
});
|
||||
|
||||
error
|
||||
}
|
||||
// The `Self` type is erased, so it should not appear in list of
|
||||
// arguments or return type apart from the receiver.
|
||||
let ref sig = method.fty.sig;
|
||||
for &input_ty in &sig.0.inputs[1..] {
|
||||
if self.contains_illegal_self_type_reference(trait_def_id, input_ty) {
|
||||
return Some(MethodViolationCode::ReferencesSelf);
|
||||
}
|
||||
}
|
||||
if let ty::FnConverging(result_type) = sig.0.output {
|
||||
if self.contains_illegal_self_type_reference(trait_def_id, result_type) {
|
||||
return Some(MethodViolationCode::ReferencesSelf);
|
||||
}
|
||||
}
|
||||
|
||||
// We can't monomorphize things like `fn foo<A>(...)`.
|
||||
if !method.generics.types.is_empty_in(subst::FnSpace) {
|
||||
return Some(MethodViolationCode::Generic);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn contains_illegal_self_type_reference(self,
|
||||
trait_def_id: DefId,
|
||||
ty: Ty<'tcx>)
|
||||
-> bool
|
||||
{
|
||||
// This is somewhat subtle. In general, we want to forbid
|
||||
// references to `Self` in the argument and return types,
|
||||
// since the value of `Self` is erased. However, there is one
|
||||
// exception: it is ok to reference `Self` in order to access
|
||||
// an associated type of the current trait, since we retain
|
||||
// the value of those associated types in the object type
|
||||
// itself.
|
||||
//
|
||||
// ```rust
|
||||
// trait SuperTrait {
|
||||
// type X;
|
||||
// }
|
||||
//
|
||||
// trait Trait : SuperTrait {
|
||||
// type Y;
|
||||
// fn foo(&self, x: Self) // bad
|
||||
// fn foo(&self) -> Self // bad
|
||||
// fn foo(&self) -> Option<Self> // bad
|
||||
// fn foo(&self) -> Self::Y // OK, desugars to next example
|
||||
// fn foo(&self) -> <Self as Trait>::Y // OK
|
||||
// fn foo(&self) -> Self::X // OK, desugars to next example
|
||||
// fn foo(&self) -> <Self as SuperTrait>::X // OK
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// However, it is not as simple as allowing `Self` in a projected
|
||||
// type, because there are illegal ways to use `Self` as well:
|
||||
//
|
||||
// ```rust
|
||||
// trait Trait : SuperTrait {
|
||||
// ...
|
||||
// fn foo(&self) -> <Self as SomeOtherTrait>::X;
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// Here we will not have the type of `X` recorded in the
|
||||
// object type, and we cannot resolve `Self as SomeOtherTrait`
|
||||
// without knowing what `Self` is.
|
||||
|
||||
let mut supertraits: Option<Vec<ty::PolyTraitRef<'tcx>>> = None;
|
||||
let mut error = false;
|
||||
ty.maybe_walk(|ty| {
|
||||
match ty.sty {
|
||||
ty::TyParam(ref param_ty) => {
|
||||
if param_ty.space == SelfSpace {
|
||||
error = true;
|
||||
}
|
||||
|
||||
false // no contained types to walk
|
||||
}
|
||||
|
||||
ty::TyProjection(ref data) => {
|
||||
// This is a projected type `<Foo as SomeTrait>::X`.
|
||||
|
||||
// Compute supertraits of current trait lazily.
|
||||
if supertraits.is_none() {
|
||||
let trait_def = self.lookup_trait_def(trait_def_id);
|
||||
let trait_ref = ty::Binder(trait_def.trait_ref.clone());
|
||||
supertraits = Some(traits::supertraits(self, trait_ref).collect());
|
||||
}
|
||||
|
||||
// Determine whether the trait reference `Foo as
|
||||
// SomeTrait` is in fact a supertrait of the
|
||||
// current trait. In that case, this type is
|
||||
// legal, because the type `X` will be specified
|
||||
// in the object type. Note that we can just use
|
||||
// direct equality here because all of these types
|
||||
// are part of the formal parameter listing, and
|
||||
// hence there should be no inference variables.
|
||||
let projection_trait_ref = ty::Binder(data.trait_ref.clone());
|
||||
let is_supertrait_of_current_trait =
|
||||
supertraits.as_ref().unwrap().contains(&projection_trait_ref);
|
||||
|
||||
if is_supertrait_of_current_trait {
|
||||
false // do not walk contained types, do not report error, do collect $200
|
||||
} else {
|
||||
true // DO walk contained types, POSSIBLY reporting an error
|
||||
}
|
||||
}
|
||||
|
||||
_ => true, // walk contained types, if any
|
||||
}
|
||||
});
|
||||
|
||||
error
|
||||
}
|
||||
}
|
||||
|
@ -393,130 +393,130 @@ pub fn predicate_for_trait_ref<'tcx>(
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
|
||||
pub fn trait_ref_for_builtin_bound(self,
|
||||
builtin_bound: ty::BuiltinBound,
|
||||
param_ty: Ty<'tcx>)
|
||||
-> Result<ty::TraitRef<'tcx>, ErrorReported>
|
||||
{
|
||||
match self.lang_items.from_builtin_kind(builtin_bound) {
|
||||
Ok(def_id) => {
|
||||
Ok(ty::TraitRef {
|
||||
def_id: def_id,
|
||||
substs: self.mk_substs(Substs::empty().with_self_ty(param_ty))
|
||||
})
|
||||
}
|
||||
Err(e) => {
|
||||
self.sess.err(&e);
|
||||
Err(ErrorReported)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn predicate_for_trait_def(self,
|
||||
cause: ObligationCause<'tcx>,
|
||||
trait_def_id: DefId,
|
||||
recursion_depth: usize,
|
||||
param_ty: Ty<'tcx>,
|
||||
ty_params: Vec<Ty<'tcx>>)
|
||||
-> PredicateObligation<'tcx>
|
||||
{
|
||||
let trait_ref = ty::TraitRef {
|
||||
def_id: trait_def_id,
|
||||
substs: self.mk_substs(Substs::new_trait(ty_params, vec![], param_ty))
|
||||
};
|
||||
predicate_for_trait_ref(cause, trait_ref, recursion_depth)
|
||||
}
|
||||
|
||||
pub fn predicate_for_builtin_bound(self,
|
||||
cause: ObligationCause<'tcx>,
|
||||
builtin_bound: ty::BuiltinBound,
|
||||
recursion_depth: usize,
|
||||
param_ty: Ty<'tcx>)
|
||||
-> Result<PredicateObligation<'tcx>, ErrorReported>
|
||||
{
|
||||
let trait_ref = self.trait_ref_for_builtin_bound(builtin_bound, param_ty)?;
|
||||
Ok(predicate_for_trait_ref(cause, trait_ref, recursion_depth))
|
||||
}
|
||||
|
||||
/// Cast a trait reference into a reference to one of its super
|
||||
/// traits; returns `None` if `target_trait_def_id` is not a
|
||||
/// supertrait.
|
||||
pub fn upcast_choices(self,
|
||||
source_trait_ref: ty::PolyTraitRef<'tcx>,
|
||||
target_trait_def_id: DefId)
|
||||
-> Vec<ty::PolyTraitRef<'tcx>>
|
||||
{
|
||||
if source_trait_ref.def_id() == target_trait_def_id {
|
||||
return vec![source_trait_ref]; // shorcut the most common case
|
||||
}
|
||||
|
||||
supertraits(self, source_trait_ref)
|
||||
.filter(|r| r.def_id() == target_trait_def_id)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Given a trait `trait_ref`, returns the number of vtable entries
|
||||
/// that come from `trait_ref`, excluding its supertraits. Used in
|
||||
/// computing the vtable base for an upcast trait of a trait object.
|
||||
pub fn count_own_vtable_entries(self, trait_ref: ty::PolyTraitRef<'tcx>) -> usize {
|
||||
let mut entries = 0;
|
||||
// Count number of methods and add them to the total offset.
|
||||
// Skip over associated types and constants.
|
||||
for trait_item in &self.trait_items(trait_ref.def_id())[..] {
|
||||
if let ty::MethodTraitItem(_) = *trait_item {
|
||||
entries += 1;
|
||||
}
|
||||
}
|
||||
entries
|
||||
}
|
||||
|
||||
/// Given an upcast trait object described by `object`, returns the
|
||||
/// index of the method `method_def_id` (which should be part of
|
||||
/// `object.upcast_trait_ref`) within the vtable for `object`.
|
||||
pub fn get_vtable_index_of_object_method(self,
|
||||
object: &super::VtableObjectData<'tcx>,
|
||||
method_def_id: DefId) -> usize {
|
||||
// Count number of methods preceding the one we are selecting and
|
||||
// add them to the total offset.
|
||||
// Skip over associated types and constants.
|
||||
let mut entries = object.vtable_base;
|
||||
for trait_item in &self.trait_items(object.upcast_trait_ref.def_id())[..] {
|
||||
if trait_item.def_id() == method_def_id {
|
||||
// The item with the ID we were given really ought to be a method.
|
||||
assert!(match *trait_item {
|
||||
ty::MethodTraitItem(_) => true,
|
||||
_ => false
|
||||
});
|
||||
|
||||
return entries;
|
||||
}
|
||||
if let ty::MethodTraitItem(_) = *trait_item {
|
||||
entries += 1;
|
||||
pub fn trait_ref_for_builtin_bound(self,
|
||||
builtin_bound: ty::BuiltinBound,
|
||||
param_ty: Ty<'tcx>)
|
||||
-> Result<ty::TraitRef<'tcx>, ErrorReported>
|
||||
{
|
||||
match self.lang_items.from_builtin_kind(builtin_bound) {
|
||||
Ok(def_id) => {
|
||||
Ok(ty::TraitRef {
|
||||
def_id: def_id,
|
||||
substs: self.mk_substs(Substs::empty().with_self_ty(param_ty))
|
||||
})
|
||||
}
|
||||
Err(e) => {
|
||||
self.sess.err(&e);
|
||||
Err(ErrorReported)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bug!("get_vtable_index_of_object_method: {:?} was not found",
|
||||
method_def_id);
|
||||
}
|
||||
pub fn predicate_for_trait_def(self,
|
||||
cause: ObligationCause<'tcx>,
|
||||
trait_def_id: DefId,
|
||||
recursion_depth: usize,
|
||||
param_ty: Ty<'tcx>,
|
||||
ty_params: Vec<Ty<'tcx>>)
|
||||
-> PredicateObligation<'tcx>
|
||||
{
|
||||
let trait_ref = ty::TraitRef {
|
||||
def_id: trait_def_id,
|
||||
substs: self.mk_substs(Substs::new_trait(ty_params, vec![], param_ty))
|
||||
};
|
||||
predicate_for_trait_ref(cause, trait_ref, recursion_depth)
|
||||
}
|
||||
|
||||
pub fn closure_trait_ref_and_return_type(self,
|
||||
fn_trait_def_id: DefId,
|
||||
self_ty: Ty<'tcx>,
|
||||
sig: &ty::PolyFnSig<'tcx>,
|
||||
tuple_arguments: TupleArgumentsFlag)
|
||||
-> ty::Binder<(ty::TraitRef<'tcx>, Ty<'tcx>)>
|
||||
{
|
||||
let arguments_tuple = match tuple_arguments {
|
||||
TupleArgumentsFlag::No => sig.0.inputs[0],
|
||||
TupleArgumentsFlag::Yes => self.mk_tup(sig.0.inputs.to_vec()),
|
||||
};
|
||||
let trait_substs = Substs::new_trait(vec![arguments_tuple], vec![], self_ty);
|
||||
let trait_ref = ty::TraitRef {
|
||||
def_id: fn_trait_def_id,
|
||||
substs: self.mk_substs(trait_substs),
|
||||
};
|
||||
ty::Binder((trait_ref, sig.0.output.unwrap_or(self.mk_nil())))
|
||||
}
|
||||
pub fn predicate_for_builtin_bound(self,
|
||||
cause: ObligationCause<'tcx>,
|
||||
builtin_bound: ty::BuiltinBound,
|
||||
recursion_depth: usize,
|
||||
param_ty: Ty<'tcx>)
|
||||
-> Result<PredicateObligation<'tcx>, ErrorReported>
|
||||
{
|
||||
let trait_ref = self.trait_ref_for_builtin_bound(builtin_bound, param_ty)?;
|
||||
Ok(predicate_for_trait_ref(cause, trait_ref, recursion_depth))
|
||||
}
|
||||
|
||||
/// Cast a trait reference into a reference to one of its super
|
||||
/// traits; returns `None` if `target_trait_def_id` is not a
|
||||
/// supertrait.
|
||||
pub fn upcast_choices(self,
|
||||
source_trait_ref: ty::PolyTraitRef<'tcx>,
|
||||
target_trait_def_id: DefId)
|
||||
-> Vec<ty::PolyTraitRef<'tcx>>
|
||||
{
|
||||
if source_trait_ref.def_id() == target_trait_def_id {
|
||||
return vec![source_trait_ref]; // shorcut the most common case
|
||||
}
|
||||
|
||||
supertraits(self, source_trait_ref)
|
||||
.filter(|r| r.def_id() == target_trait_def_id)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Given a trait `trait_ref`, returns the number of vtable entries
|
||||
/// that come from `trait_ref`, excluding its supertraits. Used in
|
||||
/// computing the vtable base for an upcast trait of a trait object.
|
||||
pub fn count_own_vtable_entries(self, trait_ref: ty::PolyTraitRef<'tcx>) -> usize {
|
||||
let mut entries = 0;
|
||||
// Count number of methods and add them to the total offset.
|
||||
// Skip over associated types and constants.
|
||||
for trait_item in &self.trait_items(trait_ref.def_id())[..] {
|
||||
if let ty::MethodTraitItem(_) = *trait_item {
|
||||
entries += 1;
|
||||
}
|
||||
}
|
||||
entries
|
||||
}
|
||||
|
||||
/// Given an upcast trait object described by `object`, returns the
|
||||
/// index of the method `method_def_id` (which should be part of
|
||||
/// `object.upcast_trait_ref`) within the vtable for `object`.
|
||||
pub fn get_vtable_index_of_object_method(self,
|
||||
object: &super::VtableObjectData<'tcx>,
|
||||
method_def_id: DefId) -> usize {
|
||||
// Count number of methods preceding the one we are selecting and
|
||||
// add them to the total offset.
|
||||
// Skip over associated types and constants.
|
||||
let mut entries = object.vtable_base;
|
||||
for trait_item in &self.trait_items(object.upcast_trait_ref.def_id())[..] {
|
||||
if trait_item.def_id() == method_def_id {
|
||||
// The item with the ID we were given really ought to be a method.
|
||||
assert!(match *trait_item {
|
||||
ty::MethodTraitItem(_) => true,
|
||||
_ => false
|
||||
});
|
||||
|
||||
return entries;
|
||||
}
|
||||
if let ty::MethodTraitItem(_) = *trait_item {
|
||||
entries += 1;
|
||||
}
|
||||
}
|
||||
|
||||
bug!("get_vtable_index_of_object_method: {:?} was not found",
|
||||
method_def_id);
|
||||
}
|
||||
|
||||
pub fn closure_trait_ref_and_return_type(self,
|
||||
fn_trait_def_id: DefId,
|
||||
self_ty: Ty<'tcx>,
|
||||
sig: &ty::PolyFnSig<'tcx>,
|
||||
tuple_arguments: TupleArgumentsFlag)
|
||||
-> ty::Binder<(ty::TraitRef<'tcx>, Ty<'tcx>)>
|
||||
{
|
||||
let arguments_tuple = match tuple_arguments {
|
||||
TupleArgumentsFlag::No => sig.0.inputs[0],
|
||||
TupleArgumentsFlag::Yes => self.mk_tup(sig.0.inputs.to_vec()),
|
||||
};
|
||||
let trait_substs = Substs::new_trait(vec![arguments_tuple], vec![], self_ty);
|
||||
let trait_ref = ty::TraitRef {
|
||||
def_id: fn_trait_def_id,
|
||||
substs: self.mk_substs(trait_substs),
|
||||
};
|
||||
ty::Binder((trait_ref, sig.0.output.unwrap_or(self.mk_nil())))
|
||||
}
|
||||
}
|
||||
|
||||
pub enum TupleArgumentsFlag { Yes, No }
|
||||
|
@ -56,156 +56,156 @@ pub enum Component<'tcx> {
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
/// Returns all the things that must outlive `'a` for the condition
|
||||
/// `ty0: 'a` to hold.
|
||||
pub fn outlives_components(&self, ty0: Ty<'tcx>)
|
||||
-> Vec<Component<'tcx>> {
|
||||
let mut components = vec![];
|
||||
self.compute_components(ty0, &mut components);
|
||||
debug!("components({:?}) = {:?}", ty0, components);
|
||||
components
|
||||
}
|
||||
/// Returns all the things that must outlive `'a` for the condition
|
||||
/// `ty0: 'a` to hold.
|
||||
pub fn outlives_components(&self, ty0: Ty<'tcx>)
|
||||
-> Vec<Component<'tcx>> {
|
||||
let mut components = vec![];
|
||||
self.compute_components(ty0, &mut components);
|
||||
debug!("components({:?}) = {:?}", ty0, components);
|
||||
components
|
||||
}
|
||||
|
||||
fn compute_components(&self, ty: Ty<'tcx>, out: &mut Vec<Component<'tcx>>) {
|
||||
// Descend through the types, looking for the various "base"
|
||||
// components and collecting them into `out`. This is not written
|
||||
// with `collect()` because of the need to sometimes skip subtrees
|
||||
// in the `subtys` iterator (e.g., when encountering a
|
||||
// projection).
|
||||
match ty.sty {
|
||||
ty::TyClosure(_, ref substs) => {
|
||||
// FIXME(#27086). We do not accumulate from substs, since they
|
||||
// don't represent reachable data. This means that, in
|
||||
// practice, some of the lifetime parameters might not
|
||||
// be in scope when the body runs, so long as there is
|
||||
// no reachable data with that lifetime. For better or
|
||||
// worse, this is consistent with fn types, however,
|
||||
// which can also encapsulate data in this fashion
|
||||
// (though it's somewhat harder, and typically
|
||||
// requires virtual dispatch).
|
||||
//
|
||||
// Note that changing this (in a naive way, at least)
|
||||
// causes regressions for what appears to be perfectly
|
||||
// reasonable code like this:
|
||||
//
|
||||
// ```
|
||||
// fn foo<'a>(p: &Data<'a>) {
|
||||
// bar(|q: &mut Parser| q.read_addr())
|
||||
// }
|
||||
// fn bar(p: Box<FnMut(&mut Parser)+'static>) {
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// Note that `p` (and `'a`) are not used in the
|
||||
// closure at all, but to meet the requirement that
|
||||
// the closure type `C: 'static` (so it can be coerced
|
||||
// to the object type), we get the requirement that
|
||||
// `'a: 'static` since `'a` appears in the closure
|
||||
// type `C`.
|
||||
//
|
||||
// A smarter fix might "prune" unused `func_substs` --
|
||||
// this would avoid breaking simple examples like
|
||||
// this, but would still break others (which might
|
||||
// indeed be invalid, depending on your POV). Pruning
|
||||
// would be a subtle process, since we have to see
|
||||
// what func/type parameters are used and unused,
|
||||
// taking into consideration UFCS and so forth.
|
||||
fn compute_components(&self, ty: Ty<'tcx>, out: &mut Vec<Component<'tcx>>) {
|
||||
// Descend through the types, looking for the various "base"
|
||||
// components and collecting them into `out`. This is not written
|
||||
// with `collect()` because of the need to sometimes skip subtrees
|
||||
// in the `subtys` iterator (e.g., when encountering a
|
||||
// projection).
|
||||
match ty.sty {
|
||||
ty::TyClosure(_, ref substs) => {
|
||||
// FIXME(#27086). We do not accumulate from substs, since they
|
||||
// don't represent reachable data. This means that, in
|
||||
// practice, some of the lifetime parameters might not
|
||||
// be in scope when the body runs, so long as there is
|
||||
// no reachable data with that lifetime. For better or
|
||||
// worse, this is consistent with fn types, however,
|
||||
// which can also encapsulate data in this fashion
|
||||
// (though it's somewhat harder, and typically
|
||||
// requires virtual dispatch).
|
||||
//
|
||||
// Note that changing this (in a naive way, at least)
|
||||
// causes regressions for what appears to be perfectly
|
||||
// reasonable code like this:
|
||||
//
|
||||
// ```
|
||||
// fn foo<'a>(p: &Data<'a>) {
|
||||
// bar(|q: &mut Parser| q.read_addr())
|
||||
// }
|
||||
// fn bar(p: Box<FnMut(&mut Parser)+'static>) {
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// Note that `p` (and `'a`) are not used in the
|
||||
// closure at all, but to meet the requirement that
|
||||
// the closure type `C: 'static` (so it can be coerced
|
||||
// to the object type), we get the requirement that
|
||||
// `'a: 'static` since `'a` appears in the closure
|
||||
// type `C`.
|
||||
//
|
||||
// A smarter fix might "prune" unused `func_substs` --
|
||||
// this would avoid breaking simple examples like
|
||||
// this, but would still break others (which might
|
||||
// indeed be invalid, depending on your POV). Pruning
|
||||
// would be a subtle process, since we have to see
|
||||
// what func/type parameters are used and unused,
|
||||
// taking into consideration UFCS and so forth.
|
||||
|
||||
for &upvar_ty in substs.upvar_tys {
|
||||
self.compute_components(upvar_ty, out);
|
||||
for &upvar_ty in substs.upvar_tys {
|
||||
self.compute_components(upvar_ty, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// OutlivesTypeParameterEnv -- the actual checking that `X:'a`
|
||||
// is implied by the environment is done in regionck.
|
||||
ty::TyParam(p) => {
|
||||
out.push(Component::Param(p));
|
||||
}
|
||||
|
||||
// For projections, we prefer to generate an obligation like
|
||||
// `<P0 as Trait<P1...Pn>>::Foo: 'a`, because this gives the
|
||||
// regionck more ways to prove that it holds. However,
|
||||
// regionck is not (at least currently) prepared to deal with
|
||||
// higher-ranked regions that may appear in the
|
||||
// trait-ref. Therefore, if we see any higher-ranke regions,
|
||||
// we simply fallback to the most restrictive rule, which
|
||||
// requires that `Pi: 'a` for all `i`.
|
||||
ty::TyProjection(ref data) => {
|
||||
if !data.has_escaping_regions() {
|
||||
// best case: no escaping regions, so push the
|
||||
// projection and skip the subtree (thus generating no
|
||||
// constraints for Pi). This defers the choice between
|
||||
// the rules OutlivesProjectionEnv,
|
||||
// OutlivesProjectionTraitDef, and
|
||||
// OutlivesProjectionComponents to regionck.
|
||||
out.push(Component::Projection(*data));
|
||||
} else {
|
||||
// fallback case: hard code
|
||||
// OutlivesProjectionComponents. Continue walking
|
||||
// through and constrain Pi.
|
||||
let subcomponents = self.capture_components(ty);
|
||||
out.push(Component::EscapingProjection(subcomponents));
|
||||
// OutlivesTypeParameterEnv -- the actual checking that `X:'a`
|
||||
// is implied by the environment is done in regionck.
|
||||
ty::TyParam(p) => {
|
||||
out.push(Component::Param(p));
|
||||
}
|
||||
}
|
||||
|
||||
// If we encounter an inference variable, try to resolve it
|
||||
// and proceed with resolved version. If we cannot resolve it,
|
||||
// then record the unresolved variable as a component.
|
||||
ty::TyInfer(_) => {
|
||||
let ty = self.resolve_type_vars_if_possible(&ty);
|
||||
if let ty::TyInfer(infer_ty) = ty.sty {
|
||||
out.push(Component::UnresolvedInferenceVariable(infer_ty));
|
||||
} else {
|
||||
self.compute_components(ty, out);
|
||||
// For projections, we prefer to generate an obligation like
|
||||
// `<P0 as Trait<P1...Pn>>::Foo: 'a`, because this gives the
|
||||
// regionck more ways to prove that it holds. However,
|
||||
// regionck is not (at least currently) prepared to deal with
|
||||
// higher-ranked regions that may appear in the
|
||||
// trait-ref. Therefore, if we see any higher-ranke regions,
|
||||
// we simply fallback to the most restrictive rule, which
|
||||
// requires that `Pi: 'a` for all `i`.
|
||||
ty::TyProjection(ref data) => {
|
||||
if !data.has_escaping_regions() {
|
||||
// best case: no escaping regions, so push the
|
||||
// projection and skip the subtree (thus generating no
|
||||
// constraints for Pi). This defers the choice between
|
||||
// the rules OutlivesProjectionEnv,
|
||||
// OutlivesProjectionTraitDef, and
|
||||
// OutlivesProjectionComponents to regionck.
|
||||
out.push(Component::Projection(*data));
|
||||
} else {
|
||||
// fallback case: hard code
|
||||
// OutlivesProjectionComponents. Continue walking
|
||||
// through and constrain Pi.
|
||||
let subcomponents = self.capture_components(ty);
|
||||
out.push(Component::EscapingProjection(subcomponents));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Most types do not introduce any region binders, nor
|
||||
// involve any other subtle cases, and so the WF relation
|
||||
// simply constraints any regions referenced directly by
|
||||
// the type and then visits the types that are lexically
|
||||
// contained within. (The comments refer to relevant rules
|
||||
// from RFC1214.)
|
||||
ty::TyBool | // OutlivesScalar
|
||||
ty::TyChar | // OutlivesScalar
|
||||
ty::TyInt(..) | // OutlivesScalar
|
||||
ty::TyUint(..) | // OutlivesScalar
|
||||
ty::TyFloat(..) | // OutlivesScalar
|
||||
ty::TyEnum(..) | // OutlivesNominalType
|
||||
ty::TyStruct(..) | // OutlivesNominalType
|
||||
ty::TyBox(..) | // OutlivesNominalType (ish)
|
||||
ty::TyStr | // OutlivesScalar (ish)
|
||||
ty::TyArray(..) | // ...
|
||||
ty::TySlice(..) | // ...
|
||||
ty::TyRawPtr(..) | // ...
|
||||
ty::TyRef(..) | // OutlivesReference
|
||||
ty::TyTuple(..) | // ...
|
||||
ty::TyFnDef(..) | // OutlivesFunction (*)
|
||||
ty::TyFnPtr(_) | // OutlivesFunction (*)
|
||||
ty::TyTrait(..) | // OutlivesObject, OutlivesFragment (*)
|
||||
ty::TyError => {
|
||||
// (*) Bare functions and traits are both binders. In the
|
||||
// RFC, this means we would add the bound regions to the
|
||||
// "bound regions list". In our representation, no such
|
||||
// list is maintained explicitly, because bound regions
|
||||
// themselves can be readily identified.
|
||||
// If we encounter an inference variable, try to resolve it
|
||||
// and proceed with resolved version. If we cannot resolve it,
|
||||
// then record the unresolved variable as a component.
|
||||
ty::TyInfer(_) => {
|
||||
let ty = self.resolve_type_vars_if_possible(&ty);
|
||||
if let ty::TyInfer(infer_ty) = ty.sty {
|
||||
out.push(Component::UnresolvedInferenceVariable(infer_ty));
|
||||
} else {
|
||||
self.compute_components(ty, out);
|
||||
}
|
||||
}
|
||||
|
||||
push_region_constraints(out, ty.regions());
|
||||
for subty in ty.walk_shallow() {
|
||||
self.compute_components(subty, out);
|
||||
// Most types do not introduce any region binders, nor
|
||||
// involve any other subtle cases, and so the WF relation
|
||||
// simply constraints any regions referenced directly by
|
||||
// the type and then visits the types that are lexically
|
||||
// contained within. (The comments refer to relevant rules
|
||||
// from RFC1214.)
|
||||
ty::TyBool | // OutlivesScalar
|
||||
ty::TyChar | // OutlivesScalar
|
||||
ty::TyInt(..) | // OutlivesScalar
|
||||
ty::TyUint(..) | // OutlivesScalar
|
||||
ty::TyFloat(..) | // OutlivesScalar
|
||||
ty::TyEnum(..) | // OutlivesNominalType
|
||||
ty::TyStruct(..) | // OutlivesNominalType
|
||||
ty::TyBox(..) | // OutlivesNominalType (ish)
|
||||
ty::TyStr | // OutlivesScalar (ish)
|
||||
ty::TyArray(..) | // ...
|
||||
ty::TySlice(..) | // ...
|
||||
ty::TyRawPtr(..) | // ...
|
||||
ty::TyRef(..) | // OutlivesReference
|
||||
ty::TyTuple(..) | // ...
|
||||
ty::TyFnDef(..) | // OutlivesFunction (*)
|
||||
ty::TyFnPtr(_) | // OutlivesFunction (*)
|
||||
ty::TyTrait(..) | // OutlivesObject, OutlivesFragment (*)
|
||||
ty::TyError => {
|
||||
// (*) Bare functions and traits are both binders. In the
|
||||
// RFC, this means we would add the bound regions to the
|
||||
// "bound regions list". In our representation, no such
|
||||
// list is maintained explicitly, because bound regions
|
||||
// themselves can be readily identified.
|
||||
|
||||
push_region_constraints(out, ty.regions());
|
||||
for subty in ty.walk_shallow() {
|
||||
self.compute_components(subty, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn capture_components(&self, ty: Ty<'tcx>) -> Vec<Component<'tcx>> {
|
||||
let mut temp = vec![];
|
||||
push_region_constraints(&mut temp, ty.regions());
|
||||
for subty in ty.walk_shallow() {
|
||||
self.compute_components(subty, &mut temp);
|
||||
fn capture_components(&self, ty: Ty<'tcx>) -> Vec<Component<'tcx>> {
|
||||
let mut temp = vec![];
|
||||
push_region_constraints(&mut temp, ty.regions());
|
||||
for subty in ty.walk_shallow() {
|
||||
self.compute_components(subty, &mut temp);
|
||||
}
|
||||
temp
|
||||
}
|
||||
temp
|
||||
}
|
||||
}
|
||||
|
||||
fn push_region_constraints<'tcx>(out: &mut Vec<Component<'tcx>>, regions: Vec<ty::Region>) {
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -64,267 +64,267 @@ enum CallStep<'tcx> {
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
pub fn check_call(&self,
|
||||
call_expr: &'gcx hir::Expr,
|
||||
callee_expr: &'gcx hir::Expr,
|
||||
arg_exprs: &'gcx [P<hir::Expr>],
|
||||
expected: Expectation<'tcx>)
|
||||
{
|
||||
self.check_expr(callee_expr);
|
||||
let original_callee_ty = self.expr_ty(callee_expr);
|
||||
let (callee_ty, _, result) =
|
||||
self.autoderef(callee_expr.span,
|
||||
original_callee_ty,
|
||||
|| Some(callee_expr),
|
||||
UnresolvedTypeAction::Error,
|
||||
LvaluePreference::NoPreference,
|
||||
|adj_ty, idx| {
|
||||
self.try_overloaded_call_step(call_expr, callee_expr, adj_ty, idx)
|
||||
});
|
||||
pub fn check_call(&self,
|
||||
call_expr: &'gcx hir::Expr,
|
||||
callee_expr: &'gcx hir::Expr,
|
||||
arg_exprs: &'gcx [P<hir::Expr>],
|
||||
expected: Expectation<'tcx>)
|
||||
{
|
||||
self.check_expr(callee_expr);
|
||||
let original_callee_ty = self.expr_ty(callee_expr);
|
||||
let (callee_ty, _, result) =
|
||||
self.autoderef(callee_expr.span,
|
||||
original_callee_ty,
|
||||
|| Some(callee_expr),
|
||||
UnresolvedTypeAction::Error,
|
||||
LvaluePreference::NoPreference,
|
||||
|adj_ty, idx| {
|
||||
self.try_overloaded_call_step(call_expr, callee_expr, adj_ty, idx)
|
||||
});
|
||||
|
||||
match result {
|
||||
None => {
|
||||
// this will report an error since original_callee_ty is not a fn
|
||||
self.confirm_builtin_call(call_expr, original_callee_ty, arg_exprs, expected);
|
||||
}
|
||||
|
||||
Some(CallStep::Builtin) => {
|
||||
self.confirm_builtin_call(call_expr, callee_ty, arg_exprs, expected);
|
||||
}
|
||||
|
||||
Some(CallStep::DeferredClosure(fn_sig)) => {
|
||||
self.confirm_deferred_closure_call(call_expr, arg_exprs, expected, fn_sig);
|
||||
}
|
||||
|
||||
Some(CallStep::Overloaded(method_callee)) => {
|
||||
self.confirm_overloaded_call(call_expr, callee_expr,
|
||||
arg_exprs, expected, method_callee);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_overloaded_call_step(&self,
|
||||
call_expr: &'gcx hir::Expr,
|
||||
callee_expr: &'gcx hir::Expr,
|
||||
adjusted_ty: Ty<'tcx>,
|
||||
autoderefs: usize)
|
||||
-> Option<CallStep<'tcx>>
|
||||
{
|
||||
debug!("try_overloaded_call_step(call_expr={:?}, adjusted_ty={:?}, autoderefs={})",
|
||||
call_expr,
|
||||
adjusted_ty,
|
||||
autoderefs);
|
||||
|
||||
// If the callee is a bare function or a closure, then we're all set.
|
||||
match self.structurally_resolved_type(callee_expr.span, adjusted_ty).sty {
|
||||
ty::TyFnDef(..) | ty::TyFnPtr(_) => {
|
||||
self.write_autoderef_adjustment(callee_expr.id, autoderefs);
|
||||
return Some(CallStep::Builtin);
|
||||
}
|
||||
|
||||
ty::TyClosure(def_id, substs) => {
|
||||
assert_eq!(def_id.krate, LOCAL_CRATE);
|
||||
|
||||
// Check whether this is a call to a closure where we
|
||||
// haven't yet decided on whether the closure is fn vs
|
||||
// fnmut vs fnonce. If so, we have to defer further processing.
|
||||
if self.closure_kind(def_id).is_none() {
|
||||
let closure_ty =
|
||||
self.closure_type(def_id, substs);
|
||||
let fn_sig =
|
||||
self.replace_late_bound_regions_with_fresh_var(call_expr.span,
|
||||
infer::FnCall,
|
||||
&closure_ty.sig).0;
|
||||
self.record_deferred_call_resolution(def_id, Box::new(CallResolution {
|
||||
call_expr: call_expr,
|
||||
callee_expr: callee_expr,
|
||||
adjusted_ty: adjusted_ty,
|
||||
autoderefs: autoderefs,
|
||||
fn_sig: fn_sig.clone(),
|
||||
closure_def_id: def_id
|
||||
}));
|
||||
return Some(CallStep::DeferredClosure(fn_sig));
|
||||
match result {
|
||||
None => {
|
||||
// this will report an error since original_callee_ty is not a fn
|
||||
self.confirm_builtin_call(call_expr, original_callee_ty, arg_exprs, expected);
|
||||
}
|
||||
}
|
||||
|
||||
// Hack: we know that there are traits implementing Fn for &F
|
||||
// where F:Fn and so forth. In the particular case of types
|
||||
// like `x: &mut FnMut()`, if there is a call `x()`, we would
|
||||
// normally translate to `FnMut::call_mut(&mut x, ())`, but
|
||||
// that winds up requiring `mut x: &mut FnMut()`. A little
|
||||
// over the top. The simplest fix by far is to just ignore
|
||||
// this case and deref again, so we wind up with
|
||||
// `FnMut::call_mut(&mut *x, ())`.
|
||||
ty::TyRef(..) if autoderefs == 0 => {
|
||||
return None;
|
||||
}
|
||||
Some(CallStep::Builtin) => {
|
||||
self.confirm_builtin_call(call_expr, callee_ty, arg_exprs, expected);
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
Some(CallStep::DeferredClosure(fn_sig)) => {
|
||||
self.confirm_deferred_closure_call(call_expr, arg_exprs, expected, fn_sig);
|
||||
}
|
||||
|
||||
self.try_overloaded_call_traits(call_expr, callee_expr, adjusted_ty, autoderefs)
|
||||
.map(|method_callee| CallStep::Overloaded(method_callee))
|
||||
}
|
||||
|
||||
fn try_overloaded_call_traits(&self,
|
||||
call_expr: &hir::Expr,
|
||||
callee_expr: &hir::Expr,
|
||||
adjusted_ty: Ty<'tcx>,
|
||||
autoderefs: usize)
|
||||
-> Option<ty::MethodCallee<'tcx>>
|
||||
{
|
||||
// Try the options that are least restrictive on the caller first.
|
||||
for &(opt_trait_def_id, method_name) in &[
|
||||
(self.tcx.lang_items.fn_trait(), token::intern("call")),
|
||||
(self.tcx.lang_items.fn_mut_trait(), token::intern("call_mut")),
|
||||
(self.tcx.lang_items.fn_once_trait(), token::intern("call_once")),
|
||||
] {
|
||||
let trait_def_id = match opt_trait_def_id {
|
||||
Some(def_id) => def_id,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
match self.lookup_method_in_trait_adjusted(call_expr.span,
|
||||
Some(&callee_expr),
|
||||
method_name,
|
||||
trait_def_id,
|
||||
autoderefs,
|
||||
false,
|
||||
adjusted_ty,
|
||||
None) {
|
||||
None => continue,
|
||||
Some(method_callee) => {
|
||||
return Some(method_callee);
|
||||
Some(CallStep::Overloaded(method_callee)) => {
|
||||
self.confirm_overloaded_call(call_expr, callee_expr,
|
||||
arg_exprs, expected, method_callee);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
fn try_overloaded_call_step(&self,
|
||||
call_expr: &'gcx hir::Expr,
|
||||
callee_expr: &'gcx hir::Expr,
|
||||
adjusted_ty: Ty<'tcx>,
|
||||
autoderefs: usize)
|
||||
-> Option<CallStep<'tcx>>
|
||||
{
|
||||
debug!("try_overloaded_call_step(call_expr={:?}, adjusted_ty={:?}, autoderefs={})",
|
||||
call_expr,
|
||||
adjusted_ty,
|
||||
autoderefs);
|
||||
|
||||
fn confirm_builtin_call(&self,
|
||||
call_expr: &hir::Expr,
|
||||
callee_ty: Ty<'tcx>,
|
||||
arg_exprs: &'gcx [P<hir::Expr>],
|
||||
expected: Expectation<'tcx>)
|
||||
{
|
||||
let error_fn_sig;
|
||||
// If the callee is a bare function or a closure, then we're all set.
|
||||
match self.structurally_resolved_type(callee_expr.span, adjusted_ty).sty {
|
||||
ty::TyFnDef(..) | ty::TyFnPtr(_) => {
|
||||
self.write_autoderef_adjustment(callee_expr.id, autoderefs);
|
||||
return Some(CallStep::Builtin);
|
||||
}
|
||||
|
||||
let fn_sig = match callee_ty.sty {
|
||||
ty::TyFnDef(_, _, &ty::BareFnTy {ref sig, ..}) |
|
||||
ty::TyFnPtr(&ty::BareFnTy {ref sig, ..}) => {
|
||||
sig
|
||||
}
|
||||
_ => {
|
||||
let mut err = self.type_error_struct(call_expr.span, |actual| {
|
||||
format!("expected function, found `{}`", actual)
|
||||
}, callee_ty, None);
|
||||
ty::TyClosure(def_id, substs) => {
|
||||
assert_eq!(def_id.krate, LOCAL_CRATE);
|
||||
|
||||
if let hir::ExprCall(ref expr, _) = call_expr.node {
|
||||
let tcx = self.tcx;
|
||||
if let Some(pr) = tcx.def_map.borrow().get(&expr.id) {
|
||||
if pr.depth == 0 && pr.base_def != Def::Err {
|
||||
if let Some(span) = tcx.map.span_if_local(pr.def_id()) {
|
||||
err.span_note(span, "defined here");
|
||||
}
|
||||
}
|
||||
// Check whether this is a call to a closure where we
|
||||
// haven't yet decided on whether the closure is fn vs
|
||||
// fnmut vs fnonce. If so, we have to defer further processing.
|
||||
if self.closure_kind(def_id).is_none() {
|
||||
let closure_ty =
|
||||
self.closure_type(def_id, substs);
|
||||
let fn_sig =
|
||||
self.replace_late_bound_regions_with_fresh_var(call_expr.span,
|
||||
infer::FnCall,
|
||||
&closure_ty.sig).0;
|
||||
self.record_deferred_call_resolution(def_id, Box::new(CallResolution {
|
||||
call_expr: call_expr,
|
||||
callee_expr: callee_expr,
|
||||
adjusted_ty: adjusted_ty,
|
||||
autoderefs: autoderefs,
|
||||
fn_sig: fn_sig.clone(),
|
||||
closure_def_id: def_id
|
||||
}));
|
||||
return Some(CallStep::DeferredClosure(fn_sig));
|
||||
}
|
||||
}
|
||||
|
||||
err.emit();
|
||||
// Hack: we know that there are traits implementing Fn for &F
|
||||
// where F:Fn and so forth. In the particular case of types
|
||||
// like `x: &mut FnMut()`, if there is a call `x()`, we would
|
||||
// normally translate to `FnMut::call_mut(&mut x, ())`, but
|
||||
// that winds up requiring `mut x: &mut FnMut()`. A little
|
||||
// over the top. The simplest fix by far is to just ignore
|
||||
// this case and deref again, so we wind up with
|
||||
// `FnMut::call_mut(&mut *x, ())`.
|
||||
ty::TyRef(..) if autoderefs == 0 => {
|
||||
return None;
|
||||
}
|
||||
|
||||
// This is the "default" function signature, used in case of error.
|
||||
// In that case, we check each argument against "error" in order to
|
||||
// set up all the node type bindings.
|
||||
error_fn_sig = ty::Binder(ty::FnSig {
|
||||
inputs: self.err_args(arg_exprs.len()),
|
||||
output: ty::FnConverging(self.tcx.types.err),
|
||||
variadic: false
|
||||
});
|
||||
|
||||
&error_fn_sig
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
|
||||
// Replace any late-bound regions that appear in the function
|
||||
// signature with region variables. We also have to
|
||||
// renormalize the associated types at this point, since they
|
||||
// previously appeared within a `Binder<>` and hence would not
|
||||
// have been normalized before.
|
||||
let fn_sig =
|
||||
self.replace_late_bound_regions_with_fresh_var(call_expr.span,
|
||||
infer::FnCall,
|
||||
fn_sig).0;
|
||||
let fn_sig =
|
||||
self.normalize_associated_types_in(call_expr.span, &fn_sig);
|
||||
self.try_overloaded_call_traits(call_expr, callee_expr, adjusted_ty, autoderefs)
|
||||
.map(|method_callee| CallStep::Overloaded(method_callee))
|
||||
}
|
||||
|
||||
// Call the generic checker.
|
||||
let expected_arg_tys = self.expected_types_for_fn_args(call_expr.span,
|
||||
expected,
|
||||
fn_sig.output,
|
||||
&fn_sig.inputs);
|
||||
self.check_argument_types(call_expr.span,
|
||||
&fn_sig.inputs,
|
||||
&expected_arg_tys[..],
|
||||
arg_exprs,
|
||||
fn_sig.variadic,
|
||||
TupleArgumentsFlag::DontTupleArguments);
|
||||
fn try_overloaded_call_traits(&self,
|
||||
call_expr: &hir::Expr,
|
||||
callee_expr: &hir::Expr,
|
||||
adjusted_ty: Ty<'tcx>,
|
||||
autoderefs: usize)
|
||||
-> Option<ty::MethodCallee<'tcx>>
|
||||
{
|
||||
// Try the options that are least restrictive on the caller first.
|
||||
for &(opt_trait_def_id, method_name) in &[
|
||||
(self.tcx.lang_items.fn_trait(), token::intern("call")),
|
||||
(self.tcx.lang_items.fn_mut_trait(), token::intern("call_mut")),
|
||||
(self.tcx.lang_items.fn_once_trait(), token::intern("call_once")),
|
||||
] {
|
||||
let trait_def_id = match opt_trait_def_id {
|
||||
Some(def_id) => def_id,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
self.write_call(call_expr, fn_sig.output);
|
||||
}
|
||||
match self.lookup_method_in_trait_adjusted(call_expr.span,
|
||||
Some(&callee_expr),
|
||||
method_name,
|
||||
trait_def_id,
|
||||
autoderefs,
|
||||
false,
|
||||
adjusted_ty,
|
||||
None) {
|
||||
None => continue,
|
||||
Some(method_callee) => {
|
||||
return Some(method_callee);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn confirm_deferred_closure_call(&self,
|
||||
call_expr: &hir::Expr,
|
||||
arg_exprs: &'gcx [P<hir::Expr>],
|
||||
expected: Expectation<'tcx>,
|
||||
fn_sig: ty::FnSig<'tcx>)
|
||||
{
|
||||
// `fn_sig` is the *signature* of the cosure being called. We
|
||||
// don't know the full details yet (`Fn` vs `FnMut` etc), but we
|
||||
// do know the types expected for each argument and the return
|
||||
// type.
|
||||
None
|
||||
}
|
||||
|
||||
let expected_arg_tys =
|
||||
self.expected_types_for_fn_args(call_expr.span,
|
||||
expected,
|
||||
fn_sig.output.clone(),
|
||||
&fn_sig.inputs);
|
||||
fn confirm_builtin_call(&self,
|
||||
call_expr: &hir::Expr,
|
||||
callee_ty: Ty<'tcx>,
|
||||
arg_exprs: &'gcx [P<hir::Expr>],
|
||||
expected: Expectation<'tcx>)
|
||||
{
|
||||
let error_fn_sig;
|
||||
|
||||
self.check_argument_types(call_expr.span,
|
||||
&fn_sig.inputs,
|
||||
&expected_arg_tys,
|
||||
arg_exprs,
|
||||
fn_sig.variadic,
|
||||
TupleArgumentsFlag::TupleArguments);
|
||||
let fn_sig = match callee_ty.sty {
|
||||
ty::TyFnDef(_, _, &ty::BareFnTy {ref sig, ..}) |
|
||||
ty::TyFnPtr(&ty::BareFnTy {ref sig, ..}) => {
|
||||
sig
|
||||
}
|
||||
_ => {
|
||||
let mut err = self.type_error_struct(call_expr.span, |actual| {
|
||||
format!("expected function, found `{}`", actual)
|
||||
}, callee_ty, None);
|
||||
|
||||
self.write_call(call_expr, fn_sig.output);
|
||||
}
|
||||
if let hir::ExprCall(ref expr, _) = call_expr.node {
|
||||
let tcx = self.tcx;
|
||||
if let Some(pr) = tcx.def_map.borrow().get(&expr.id) {
|
||||
if pr.depth == 0 && pr.base_def != Def::Err {
|
||||
if let Some(span) = tcx.map.span_if_local(pr.def_id()) {
|
||||
err.span_note(span, "defined here");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn confirm_overloaded_call(&self,
|
||||
call_expr: &hir::Expr,
|
||||
callee_expr: &'gcx hir::Expr,
|
||||
arg_exprs: &'gcx [P<hir::Expr>],
|
||||
expected: Expectation<'tcx>,
|
||||
method_callee: ty::MethodCallee<'tcx>)
|
||||
{
|
||||
let output_type =
|
||||
self.check_method_argument_types(call_expr.span,
|
||||
method_callee.ty,
|
||||
callee_expr,
|
||||
arg_exprs,
|
||||
TupleArgumentsFlag::TupleArguments,
|
||||
expected);
|
||||
self.write_call(call_expr, output_type);
|
||||
err.emit();
|
||||
|
||||
self.write_overloaded_call_method_map(call_expr, method_callee);
|
||||
}
|
||||
// This is the "default" function signature, used in case of error.
|
||||
// In that case, we check each argument against "error" in order to
|
||||
// set up all the node type bindings.
|
||||
error_fn_sig = ty::Binder(ty::FnSig {
|
||||
inputs: self.err_args(arg_exprs.len()),
|
||||
output: ty::FnConverging(self.tcx.types.err),
|
||||
variadic: false
|
||||
});
|
||||
|
||||
fn write_overloaded_call_method_map(&self,
|
||||
call_expr: &hir::Expr,
|
||||
method_callee: ty::MethodCallee<'tcx>) {
|
||||
let method_call = ty::MethodCall::expr(call_expr.id);
|
||||
self.tables.borrow_mut().method_map.insert(method_call, method_callee);
|
||||
}
|
||||
&error_fn_sig
|
||||
}
|
||||
};
|
||||
|
||||
// Replace any late-bound regions that appear in the function
|
||||
// signature with region variables. We also have to
|
||||
// renormalize the associated types at this point, since they
|
||||
// previously appeared within a `Binder<>` and hence would not
|
||||
// have been normalized before.
|
||||
let fn_sig =
|
||||
self.replace_late_bound_regions_with_fresh_var(call_expr.span,
|
||||
infer::FnCall,
|
||||
fn_sig).0;
|
||||
let fn_sig =
|
||||
self.normalize_associated_types_in(call_expr.span, &fn_sig);
|
||||
|
||||
// Call the generic checker.
|
||||
let expected_arg_tys = self.expected_types_for_fn_args(call_expr.span,
|
||||
expected,
|
||||
fn_sig.output,
|
||||
&fn_sig.inputs);
|
||||
self.check_argument_types(call_expr.span,
|
||||
&fn_sig.inputs,
|
||||
&expected_arg_tys[..],
|
||||
arg_exprs,
|
||||
fn_sig.variadic,
|
||||
TupleArgumentsFlag::DontTupleArguments);
|
||||
|
||||
self.write_call(call_expr, fn_sig.output);
|
||||
}
|
||||
|
||||
fn confirm_deferred_closure_call(&self,
|
||||
call_expr: &hir::Expr,
|
||||
arg_exprs: &'gcx [P<hir::Expr>],
|
||||
expected: Expectation<'tcx>,
|
||||
fn_sig: ty::FnSig<'tcx>)
|
||||
{
|
||||
// `fn_sig` is the *signature* of the cosure being called. We
|
||||
// don't know the full details yet (`Fn` vs `FnMut` etc), but we
|
||||
// do know the types expected for each argument and the return
|
||||
// type.
|
||||
|
||||
let expected_arg_tys =
|
||||
self.expected_types_for_fn_args(call_expr.span,
|
||||
expected,
|
||||
fn_sig.output.clone(),
|
||||
&fn_sig.inputs);
|
||||
|
||||
self.check_argument_types(call_expr.span,
|
||||
&fn_sig.inputs,
|
||||
&expected_arg_tys,
|
||||
arg_exprs,
|
||||
fn_sig.variadic,
|
||||
TupleArgumentsFlag::TupleArguments);
|
||||
|
||||
self.write_call(call_expr, fn_sig.output);
|
||||
}
|
||||
|
||||
fn confirm_overloaded_call(&self,
|
||||
call_expr: &hir::Expr,
|
||||
callee_expr: &'gcx hir::Expr,
|
||||
arg_exprs: &'gcx [P<hir::Expr>],
|
||||
expected: Expectation<'tcx>,
|
||||
method_callee: ty::MethodCallee<'tcx>)
|
||||
{
|
||||
let output_type =
|
||||
self.check_method_argument_types(call_expr.span,
|
||||
method_callee.ty,
|
||||
callee_expr,
|
||||
arg_exprs,
|
||||
TupleArgumentsFlag::TupleArguments,
|
||||
expected);
|
||||
self.write_call(call_expr, output_type);
|
||||
|
||||
self.write_overloaded_call_method_map(call_expr, method_callee);
|
||||
}
|
||||
|
||||
fn write_overloaded_call_method_map(&self,
|
||||
call_expr: &hir::Expr,
|
||||
method_callee: ty::MethodCallee<'tcx>) {
|
||||
let method_call = ty::MethodCall::expr(call_expr.id);
|
||||
self.tables.borrow_mut().method_map.insert(method_call, method_callee);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -73,26 +73,26 @@ enum UnsizeKind<'tcx> {
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
/// Returns the kind of unsize information of t, or None
|
||||
/// if t is sized or it is unknown.
|
||||
fn unsize_kind(&self, t: Ty<'tcx>) -> Option<UnsizeKind<'tcx>> {
|
||||
match t.sty {
|
||||
ty::TySlice(_) | ty::TyStr => Some(UnsizeKind::Length),
|
||||
ty::TyTrait(ref tty) => Some(UnsizeKind::Vtable(tty.principal_def_id())),
|
||||
ty::TyStruct(def, substs) => {
|
||||
// FIXME(arielb1): do some kind of normalization
|
||||
match def.struct_variant().fields.last() {
|
||||
None => None,
|
||||
Some(f) => self.unsize_kind(f.ty(self.tcx, substs))
|
||||
/// Returns the kind of unsize information of t, or None
|
||||
/// if t is sized or it is unknown.
|
||||
fn unsize_kind(&self, t: Ty<'tcx>) -> Option<UnsizeKind<'tcx>> {
|
||||
match t.sty {
|
||||
ty::TySlice(_) | ty::TyStr => Some(UnsizeKind::Length),
|
||||
ty::TyTrait(ref tty) => Some(UnsizeKind::Vtable(tty.principal_def_id())),
|
||||
ty::TyStruct(def, substs) => {
|
||||
// FIXME(arielb1): do some kind of normalization
|
||||
match def.struct_variant().fields.last() {
|
||||
None => None,
|
||||
Some(f) => self.unsize_kind(f.ty(self.tcx, substs))
|
||||
}
|
||||
}
|
||||
// We should really try to normalize here.
|
||||
ty::TyProjection(ref pi) => Some(UnsizeKind::OfProjection(pi)),
|
||||
ty::TyParam(ref p) => Some(UnsizeKind::OfParam(p)),
|
||||
_ => None
|
||||
}
|
||||
// We should really try to normalize here.
|
||||
ty::TyProjection(ref pi) => Some(UnsizeKind::OfProjection(pi)),
|
||||
ty::TyParam(ref p) => Some(UnsizeKind::OfParam(p)),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum CastError {
|
||||
|
@ -20,227 +20,227 @@ use syntax::abi::Abi;
|
||||
use rustc::hir;
|
||||
|
||||
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
pub fn check_expr_closure(&self,
|
||||
expr: &hir::Expr,
|
||||
_capture: hir::CaptureClause,
|
||||
decl: &'gcx hir::FnDecl,
|
||||
body: &'gcx hir::Block,
|
||||
expected: Expectation<'tcx>) {
|
||||
debug!("check_expr_closure(expr={:?},expected={:?})",
|
||||
expr,
|
||||
expected);
|
||||
pub fn check_expr_closure(&self,
|
||||
expr: &hir::Expr,
|
||||
_capture: hir::CaptureClause,
|
||||
decl: &'gcx hir::FnDecl,
|
||||
body: &'gcx hir::Block,
|
||||
expected: Expectation<'tcx>) {
|
||||
debug!("check_expr_closure(expr={:?},expected={:?})",
|
||||
expr,
|
||||
expected);
|
||||
|
||||
// It's always helpful for inference if we know the kind of
|
||||
// closure sooner rather than later, so first examine the expected
|
||||
// type, and see if can glean a closure kind from there.
|
||||
let (expected_sig,expected_kind) = match expected.to_option(self) {
|
||||
Some(ty) => self.deduce_expectations_from_expected_type(ty),
|
||||
None => (None, None)
|
||||
};
|
||||
self.check_closure(expr, expected_kind, decl, body, expected_sig)
|
||||
}
|
||||
|
||||
fn check_closure(&self,
|
||||
expr: &hir::Expr,
|
||||
opt_kind: Option<ty::ClosureKind>,
|
||||
decl: &'gcx hir::FnDecl,
|
||||
body: &'gcx hir::Block,
|
||||
expected_sig: Option<ty::FnSig<'tcx>>) {
|
||||
let expr_def_id = self.tcx.map.local_def_id(expr.id);
|
||||
|
||||
debug!("check_closure opt_kind={:?} expected_sig={:?}",
|
||||
opt_kind,
|
||||
expected_sig);
|
||||
|
||||
let mut fn_ty = AstConv::ty_of_closure(self,
|
||||
hir::Unsafety::Normal,
|
||||
decl,
|
||||
Abi::RustCall,
|
||||
expected_sig);
|
||||
|
||||
// Create type variables (for now) to represent the transformed
|
||||
// types of upvars. These will be unified during the upvar
|
||||
// inference phase (`upvar.rs`).
|
||||
let num_upvars = self.tcx.with_freevars(expr.id, |fv| fv.len());
|
||||
let upvar_tys = self.next_ty_vars(num_upvars);
|
||||
|
||||
debug!("check_closure: expr.id={:?} upvar_tys={:?}",
|
||||
expr.id, upvar_tys);
|
||||
|
||||
let closure_type = self.tcx.mk_closure(expr_def_id,
|
||||
self.parameter_environment.free_substs,
|
||||
upvar_tys);
|
||||
|
||||
self.write_ty(expr.id, closure_type);
|
||||
|
||||
let fn_sig = self.tcx.liberate_late_bound_regions(
|
||||
self.tcx.region_maps.call_site_extent(expr.id, body.id), &fn_ty.sig);
|
||||
|
||||
check_fn(self, hir::Unsafety::Normal, expr.id, &fn_sig, decl, expr.id, &body);
|
||||
|
||||
// Tuple up the arguments and insert the resulting function type into
|
||||
// the `closures` table.
|
||||
fn_ty.sig.0.inputs = vec![self.tcx.mk_tup(fn_ty.sig.0.inputs)];
|
||||
|
||||
debug!("closure for {:?} --> sig={:?} opt_kind={:?}",
|
||||
expr_def_id,
|
||||
fn_ty.sig,
|
||||
opt_kind);
|
||||
|
||||
self.tables.borrow_mut().closure_tys.insert(expr_def_id, fn_ty);
|
||||
match opt_kind {
|
||||
Some(kind) => { self.tables.borrow_mut().closure_kinds.insert(expr_def_id, kind); }
|
||||
None => { }
|
||||
// It's always helpful for inference if we know the kind of
|
||||
// closure sooner rather than later, so first examine the expected
|
||||
// type, and see if can glean a closure kind from there.
|
||||
let (expected_sig,expected_kind) = match expected.to_option(self) {
|
||||
Some(ty) => self.deduce_expectations_from_expected_type(ty),
|
||||
None => (None, None)
|
||||
};
|
||||
self.check_closure(expr, expected_kind, decl, body, expected_sig)
|
||||
}
|
||||
}
|
||||
|
||||
fn deduce_expectations_from_expected_type(&self, expected_ty: Ty<'tcx>)
|
||||
-> (Option<ty::FnSig<'tcx>>,Option<ty::ClosureKind>)
|
||||
{
|
||||
debug!("deduce_expectations_from_expected_type(expected_ty={:?})",
|
||||
expected_ty);
|
||||
fn check_closure(&self,
|
||||
expr: &hir::Expr,
|
||||
opt_kind: Option<ty::ClosureKind>,
|
||||
decl: &'gcx hir::FnDecl,
|
||||
body: &'gcx hir::Block,
|
||||
expected_sig: Option<ty::FnSig<'tcx>>) {
|
||||
let expr_def_id = self.tcx.map.local_def_id(expr.id);
|
||||
|
||||
match expected_ty.sty {
|
||||
ty::TyTrait(ref object_type) => {
|
||||
let proj_bounds = object_type.projection_bounds_with_self_ty(self.tcx,
|
||||
self.tcx.types.err);
|
||||
let sig = proj_bounds.iter()
|
||||
.filter_map(|pb| self.deduce_sig_from_projection(pb))
|
||||
.next();
|
||||
let kind = self.tcx.lang_items.fn_trait_kind(object_type.principal_def_id());
|
||||
(sig, kind)
|
||||
}
|
||||
ty::TyInfer(ty::TyVar(vid)) => {
|
||||
self.deduce_expectations_from_obligations(vid)
|
||||
}
|
||||
_ => {
|
||||
(None, None)
|
||||
debug!("check_closure opt_kind={:?} expected_sig={:?}",
|
||||
opt_kind,
|
||||
expected_sig);
|
||||
|
||||
let mut fn_ty = AstConv::ty_of_closure(self,
|
||||
hir::Unsafety::Normal,
|
||||
decl,
|
||||
Abi::RustCall,
|
||||
expected_sig);
|
||||
|
||||
// Create type variables (for now) to represent the transformed
|
||||
// types of upvars. These will be unified during the upvar
|
||||
// inference phase (`upvar.rs`).
|
||||
let num_upvars = self.tcx.with_freevars(expr.id, |fv| fv.len());
|
||||
let upvar_tys = self.next_ty_vars(num_upvars);
|
||||
|
||||
debug!("check_closure: expr.id={:?} upvar_tys={:?}",
|
||||
expr.id, upvar_tys);
|
||||
|
||||
let closure_type = self.tcx.mk_closure(expr_def_id,
|
||||
self.parameter_environment.free_substs,
|
||||
upvar_tys);
|
||||
|
||||
self.write_ty(expr.id, closure_type);
|
||||
|
||||
let fn_sig = self.tcx.liberate_late_bound_regions(
|
||||
self.tcx.region_maps.call_site_extent(expr.id, body.id), &fn_ty.sig);
|
||||
|
||||
check_fn(self, hir::Unsafety::Normal, expr.id, &fn_sig, decl, expr.id, &body);
|
||||
|
||||
// Tuple up the arguments and insert the resulting function type into
|
||||
// the `closures` table.
|
||||
fn_ty.sig.0.inputs = vec![self.tcx.mk_tup(fn_ty.sig.0.inputs)];
|
||||
|
||||
debug!("closure for {:?} --> sig={:?} opt_kind={:?}",
|
||||
expr_def_id,
|
||||
fn_ty.sig,
|
||||
opt_kind);
|
||||
|
||||
self.tables.borrow_mut().closure_tys.insert(expr_def_id, fn_ty);
|
||||
match opt_kind {
|
||||
Some(kind) => { self.tables.borrow_mut().closure_kinds.insert(expr_def_id, kind); }
|
||||
None => { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn deduce_expectations_from_obligations(&self, expected_vid: ty::TyVid)
|
||||
-> (Option<ty::FnSig<'tcx>>, Option<ty::ClosureKind>)
|
||||
{
|
||||
let fulfillment_cx = self.fulfillment_cx.borrow();
|
||||
// Here `expected_ty` is known to be a type inference variable.
|
||||
fn deduce_expectations_from_expected_type(&self, expected_ty: Ty<'tcx>)
|
||||
-> (Option<ty::FnSig<'tcx>>,Option<ty::ClosureKind>)
|
||||
{
|
||||
debug!("deduce_expectations_from_expected_type(expected_ty={:?})",
|
||||
expected_ty);
|
||||
|
||||
let expected_sig =
|
||||
fulfillment_cx
|
||||
.pending_obligations()
|
||||
.iter()
|
||||
.map(|obligation| &obligation.obligation)
|
||||
.filter_map(|obligation| {
|
||||
debug!("deduce_expectations_from_obligations: obligation.predicate={:?}",
|
||||
obligation.predicate);
|
||||
|
||||
match obligation.predicate {
|
||||
// Given a Projection predicate, we can potentially infer
|
||||
// the complete signature.
|
||||
ty::Predicate::Projection(ref proj_predicate) => {
|
||||
let trait_ref = proj_predicate.to_poly_trait_ref();
|
||||
self.self_type_matches_expected_vid(trait_ref, expected_vid)
|
||||
.and_then(|_| self.deduce_sig_from_projection(proj_predicate))
|
||||
}
|
||||
_ => {
|
||||
None
|
||||
}
|
||||
match expected_ty.sty {
|
||||
ty::TyTrait(ref object_type) => {
|
||||
let proj_bounds = object_type.projection_bounds_with_self_ty(self.tcx,
|
||||
self.tcx.types.err);
|
||||
let sig = proj_bounds.iter()
|
||||
.filter_map(|pb| self.deduce_sig_from_projection(pb))
|
||||
.next();
|
||||
let kind = self.tcx.lang_items.fn_trait_kind(object_type.principal_def_id());
|
||||
(sig, kind)
|
||||
}
|
||||
})
|
||||
.next();
|
||||
|
||||
// Even if we can't infer the full signature, we may be able to
|
||||
// infer the kind. This can occur if there is a trait-reference
|
||||
// like `F : Fn<A>`. Note that due to subtyping we could encounter
|
||||
// many viable options, so pick the most restrictive.
|
||||
let expected_kind =
|
||||
fulfillment_cx
|
||||
.pending_obligations()
|
||||
.iter()
|
||||
.map(|obligation| &obligation.obligation)
|
||||
.filter_map(|obligation| {
|
||||
let opt_trait_ref = match obligation.predicate {
|
||||
ty::Predicate::Projection(ref data) => Some(data.to_poly_trait_ref()),
|
||||
ty::Predicate::Trait(ref data) => Some(data.to_poly_trait_ref()),
|
||||
ty::Predicate::Equate(..) => None,
|
||||
ty::Predicate::RegionOutlives(..) => None,
|
||||
ty::Predicate::TypeOutlives(..) => None,
|
||||
ty::Predicate::WellFormed(..) => None,
|
||||
ty::Predicate::ObjectSafe(..) => None,
|
||||
ty::Predicate::Rfc1592(..) => None,
|
||||
|
||||
// NB: This predicate is created by breaking down a
|
||||
// `ClosureType: FnFoo()` predicate, where
|
||||
// `ClosureType` represents some `TyClosure`. It can't
|
||||
// possibly be referring to the current closure,
|
||||
// because we haven't produced the `TyClosure` for
|
||||
// this closure yet; this is exactly why the other
|
||||
// code is looking for a self type of a unresolved
|
||||
// inference variable.
|
||||
ty::Predicate::ClosureKind(..) => None,
|
||||
};
|
||||
opt_trait_ref
|
||||
.and_then(|trait_ref| self.self_type_matches_expected_vid(trait_ref, expected_vid))
|
||||
.and_then(|trait_ref| self.tcx.lang_items.fn_trait_kind(trait_ref.def_id()))
|
||||
})
|
||||
.fold(None, |best, cur| Some(best.map_or(cur, |best| cmp::min(best, cur))));
|
||||
|
||||
(expected_sig, expected_kind)
|
||||
}
|
||||
|
||||
/// Given a projection like "<F as Fn(X)>::Result == Y", we can deduce
|
||||
/// everything we need to know about a closure.
|
||||
fn deduce_sig_from_projection(&self,
|
||||
projection: &ty::PolyProjectionPredicate<'tcx>)
|
||||
-> Option<ty::FnSig<'tcx>>
|
||||
{
|
||||
let tcx = self.tcx;
|
||||
|
||||
debug!("deduce_sig_from_projection({:?})",
|
||||
projection);
|
||||
|
||||
let trait_ref = projection.to_poly_trait_ref();
|
||||
|
||||
if tcx.lang_items.fn_trait_kind(trait_ref.def_id()).is_none() {
|
||||
return None;
|
||||
ty::TyInfer(ty::TyVar(vid)) => {
|
||||
self.deduce_expectations_from_obligations(vid)
|
||||
}
|
||||
_ => {
|
||||
(None, None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let arg_param_ty = *trait_ref.substs().types.get(subst::TypeSpace, 0);
|
||||
let arg_param_ty = self.resolve_type_vars_if_possible(&arg_param_ty);
|
||||
debug!("deduce_sig_from_projection: arg_param_ty {:?}", arg_param_ty);
|
||||
fn deduce_expectations_from_obligations(&self, expected_vid: ty::TyVid)
|
||||
-> (Option<ty::FnSig<'tcx>>, Option<ty::ClosureKind>)
|
||||
{
|
||||
let fulfillment_cx = self.fulfillment_cx.borrow();
|
||||
// Here `expected_ty` is known to be a type inference variable.
|
||||
|
||||
let input_tys = match arg_param_ty.sty {
|
||||
ty::TyTuple(tys) => tys.to_vec(),
|
||||
_ => { return None; }
|
||||
};
|
||||
debug!("deduce_sig_from_projection: input_tys {:?}", input_tys);
|
||||
let expected_sig =
|
||||
fulfillment_cx
|
||||
.pending_obligations()
|
||||
.iter()
|
||||
.map(|obligation| &obligation.obligation)
|
||||
.filter_map(|obligation| {
|
||||
debug!("deduce_expectations_from_obligations: obligation.predicate={:?}",
|
||||
obligation.predicate);
|
||||
|
||||
let ret_param_ty = projection.0.ty;
|
||||
let ret_param_ty = self.resolve_type_vars_if_possible(&ret_param_ty);
|
||||
debug!("deduce_sig_from_projection: ret_param_ty {:?}", ret_param_ty);
|
||||
match obligation.predicate {
|
||||
// Given a Projection predicate, we can potentially infer
|
||||
// the complete signature.
|
||||
ty::Predicate::Projection(ref proj_predicate) => {
|
||||
let trait_ref = proj_predicate.to_poly_trait_ref();
|
||||
self.self_type_matches_expected_vid(trait_ref, expected_vid)
|
||||
.and_then(|_| self.deduce_sig_from_projection(proj_predicate))
|
||||
}
|
||||
_ => {
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
.next();
|
||||
|
||||
let fn_sig = ty::FnSig {
|
||||
inputs: input_tys,
|
||||
output: ty::FnConverging(ret_param_ty),
|
||||
variadic: false
|
||||
};
|
||||
debug!("deduce_sig_from_projection: fn_sig {:?}", fn_sig);
|
||||
// Even if we can't infer the full signature, we may be able to
|
||||
// infer the kind. This can occur if there is a trait-reference
|
||||
// like `F : Fn<A>`. Note that due to subtyping we could encounter
|
||||
// many viable options, so pick the most restrictive.
|
||||
let expected_kind =
|
||||
fulfillment_cx
|
||||
.pending_obligations()
|
||||
.iter()
|
||||
.map(|obligation| &obligation.obligation)
|
||||
.filter_map(|obligation| {
|
||||
let opt_trait_ref = match obligation.predicate {
|
||||
ty::Predicate::Projection(ref data) => Some(data.to_poly_trait_ref()),
|
||||
ty::Predicate::Trait(ref data) => Some(data.to_poly_trait_ref()),
|
||||
ty::Predicate::Equate(..) => None,
|
||||
ty::Predicate::RegionOutlives(..) => None,
|
||||
ty::Predicate::TypeOutlives(..) => None,
|
||||
ty::Predicate::WellFormed(..) => None,
|
||||
ty::Predicate::ObjectSafe(..) => None,
|
||||
ty::Predicate::Rfc1592(..) => None,
|
||||
|
||||
Some(fn_sig)
|
||||
}
|
||||
// NB: This predicate is created by breaking down a
|
||||
// `ClosureType: FnFoo()` predicate, where
|
||||
// `ClosureType` represents some `TyClosure`. It can't
|
||||
// possibly be referring to the current closure,
|
||||
// because we haven't produced the `TyClosure` for
|
||||
// this closure yet; this is exactly why the other
|
||||
// code is looking for a self type of a unresolved
|
||||
// inference variable.
|
||||
ty::Predicate::ClosureKind(..) => None,
|
||||
};
|
||||
opt_trait_ref
|
||||
.and_then(|tr| self.self_type_matches_expected_vid(tr, expected_vid))
|
||||
.and_then(|tr| self.tcx.lang_items.fn_trait_kind(tr.def_id()))
|
||||
})
|
||||
.fold(None, |best, cur| Some(best.map_or(cur, |best| cmp::min(best, cur))));
|
||||
|
||||
fn self_type_matches_expected_vid(&self,
|
||||
trait_ref: ty::PolyTraitRef<'tcx>,
|
||||
expected_vid: ty::TyVid)
|
||||
-> Option<ty::PolyTraitRef<'tcx>>
|
||||
{
|
||||
let self_ty = self.shallow_resolve(trait_ref.self_ty());
|
||||
debug!("self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?})",
|
||||
trait_ref,
|
||||
self_ty);
|
||||
match self_ty.sty {
|
||||
ty::TyInfer(ty::TyVar(v)) if expected_vid == v => Some(trait_ref),
|
||||
_ => None,
|
||||
(expected_sig, expected_kind)
|
||||
}
|
||||
|
||||
/// Given a projection like "<F as Fn(X)>::Result == Y", we can deduce
|
||||
/// everything we need to know about a closure.
|
||||
fn deduce_sig_from_projection(&self,
|
||||
projection: &ty::PolyProjectionPredicate<'tcx>)
|
||||
-> Option<ty::FnSig<'tcx>>
|
||||
{
|
||||
let tcx = self.tcx;
|
||||
|
||||
debug!("deduce_sig_from_projection({:?})",
|
||||
projection);
|
||||
|
||||
let trait_ref = projection.to_poly_trait_ref();
|
||||
|
||||
if tcx.lang_items.fn_trait_kind(trait_ref.def_id()).is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let arg_param_ty = *trait_ref.substs().types.get(subst::TypeSpace, 0);
|
||||
let arg_param_ty = self.resolve_type_vars_if_possible(&arg_param_ty);
|
||||
debug!("deduce_sig_from_projection: arg_param_ty {:?}", arg_param_ty);
|
||||
|
||||
let input_tys = match arg_param_ty.sty {
|
||||
ty::TyTuple(tys) => tys.to_vec(),
|
||||
_ => { return None; }
|
||||
};
|
||||
debug!("deduce_sig_from_projection: input_tys {:?}", input_tys);
|
||||
|
||||
let ret_param_ty = projection.0.ty;
|
||||
let ret_param_ty = self.resolve_type_vars_if_possible(&ret_param_ty);
|
||||
debug!("deduce_sig_from_projection: ret_param_ty {:?}", ret_param_ty);
|
||||
|
||||
let fn_sig = ty::FnSig {
|
||||
inputs: input_tys,
|
||||
output: ty::FnConverging(ret_param_ty),
|
||||
variadic: false
|
||||
};
|
||||
debug!("deduce_sig_from_projection: fn_sig {:?}", fn_sig);
|
||||
|
||||
Some(fn_sig)
|
||||
}
|
||||
|
||||
fn self_type_matches_expected_vid(&self,
|
||||
trait_ref: ty::PolyTraitRef<'tcx>,
|
||||
expected_vid: ty::TyVid)
|
||||
-> Option<ty::PolyTraitRef<'tcx>>
|
||||
{
|
||||
let self_ty = self.shallow_resolve(trait_ref.self_ty());
|
||||
debug!("self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?})",
|
||||
trait_ref,
|
||||
self_ty);
|
||||
match self_ty.sty {
|
||||
ty::TyInfer(ty::TyVar(v)) if expected_vid == v => Some(trait_ref),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -618,167 +618,167 @@ fn apply<'a, 'b, 'gcx, 'tcx, E, I>(coerce: &mut Coerce<'a, 'gcx, 'tcx>,
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
/// Attempt to coerce an expression to a type, and return the
|
||||
/// adjusted type of the expression, if successful.
|
||||
/// Adjustments are only recorded if the coercion succeeded.
|
||||
/// The expressions *must not* have any pre-existing adjustments.
|
||||
pub fn try_coerce(&self,
|
||||
expr: &hir::Expr,
|
||||
target: Ty<'tcx>)
|
||||
-> RelateResult<'tcx, Ty<'tcx>> {
|
||||
let source = self.resolve_type_vars_with_obligations(self.expr_ty(expr));
|
||||
debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target);
|
||||
/// Attempt to coerce an expression to a type, and return the
|
||||
/// adjusted type of the expression, if successful.
|
||||
/// Adjustments are only recorded if the coercion succeeded.
|
||||
/// The expressions *must not* have any pre-existing adjustments.
|
||||
pub fn try_coerce(&self,
|
||||
expr: &hir::Expr,
|
||||
target: Ty<'tcx>)
|
||||
-> RelateResult<'tcx, Ty<'tcx>> {
|
||||
let source = self.resolve_type_vars_with_obligations(self.expr_ty(expr));
|
||||
debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target);
|
||||
|
||||
let mut coerce = Coerce::new(self, TypeOrigin::ExprAssignable(expr.span));
|
||||
self.commit_if_ok(|_| {
|
||||
let (ty, adjustment) =
|
||||
apply(&mut coerce, &|| Some(expr), source, target)?;
|
||||
if !adjustment.is_identity() {
|
||||
debug!("Success, coerced with {:?}", adjustment);
|
||||
assert!(!self.tables.borrow().adjustments.contains_key(&expr.id));
|
||||
self.write_adjustment(expr.id, adjustment);
|
||||
let mut coerce = Coerce::new(self, TypeOrigin::ExprAssignable(expr.span));
|
||||
self.commit_if_ok(|_| {
|
||||
let (ty, adjustment) =
|
||||
apply(&mut coerce, &|| Some(expr), source, target)?;
|
||||
if !adjustment.is_identity() {
|
||||
debug!("Success, coerced with {:?}", adjustment);
|
||||
assert!(!self.tables.borrow().adjustments.contains_key(&expr.id));
|
||||
self.write_adjustment(expr.id, adjustment);
|
||||
}
|
||||
Ok(ty)
|
||||
})
|
||||
}
|
||||
|
||||
/// Given some expressions, their known unified type and another expression,
|
||||
/// tries to unify the types, potentially inserting coercions on any of the
|
||||
/// provided expressions and returns their LUB (aka "common supertype").
|
||||
pub fn try_find_coercion_lub<'b, E, I>(&self,
|
||||
origin: TypeOrigin,
|
||||
exprs: E,
|
||||
prev_ty: Ty<'tcx>,
|
||||
new: &'b hir::Expr)
|
||||
-> RelateResult<'tcx, Ty<'tcx>>
|
||||
// FIXME(eddyb) use copyable iterators when that becomes ergonomic.
|
||||
where E: Fn() -> I,
|
||||
I: IntoIterator<Item=&'b hir::Expr> {
|
||||
|
||||
let prev_ty = self.resolve_type_vars_with_obligations(prev_ty);
|
||||
let new_ty = self.resolve_type_vars_with_obligations(self.expr_ty(new));
|
||||
debug!("coercion::try_find_lub({:?}, {:?})", prev_ty, new_ty);
|
||||
|
||||
let trace = TypeTrace::types(origin, true, prev_ty, new_ty);
|
||||
|
||||
// Special-case that coercion alone cannot handle:
|
||||
// Two function item types of differing IDs or Substs.
|
||||
match (&prev_ty.sty, &new_ty.sty) {
|
||||
(&ty::TyFnDef(a_def_id, a_substs, a_fty),
|
||||
&ty::TyFnDef(b_def_id, b_substs, b_fty)) => {
|
||||
// The signature must always match.
|
||||
let fty = self.lub(true, trace.clone(), &a_fty, &b_fty)
|
||||
.map(|InferOk { value, obligations }| {
|
||||
// FIXME(#32730) propagate obligations
|
||||
assert!(obligations.is_empty());
|
||||
value
|
||||
})?;
|
||||
|
||||
if a_def_id == b_def_id {
|
||||
// Same function, maybe the parameters match.
|
||||
let substs = self.commit_if_ok(|_| {
|
||||
self.lub(true, trace.clone(), &a_substs, &b_substs)
|
||||
.map(|InferOk { value, obligations }| {
|
||||
// FIXME(#32730) propagate obligations
|
||||
assert!(obligations.is_empty());
|
||||
value
|
||||
})
|
||||
});
|
||||
|
||||
if let Ok(substs) = substs {
|
||||
// We have a LUB of prev_ty and new_ty, just return it.
|
||||
return Ok(self.tcx.mk_fn_def(a_def_id, substs, fty));
|
||||
}
|
||||
}
|
||||
|
||||
// Reify both sides and return the reified fn pointer type.
|
||||
for expr in exprs().into_iter().chain(Some(new)) {
|
||||
// No adjustments can produce a fn item, so this should never trip.
|
||||
assert!(!self.tables.borrow().adjustments.contains_key(&expr.id));
|
||||
self.write_adjustment(expr.id, AdjustReifyFnPointer);
|
||||
}
|
||||
return Ok(self.tcx.mk_fn_ptr(fty));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(ty)
|
||||
})
|
||||
}
|
||||
|
||||
/// Given some expressions, their known unified type and another expression,
|
||||
/// tries to unify the types, potentially inserting coercions on any of the
|
||||
/// provided expressions and returns their LUB (aka "common supertype").
|
||||
pub fn try_find_coercion_lub<'b, E, I>(&self,
|
||||
origin: TypeOrigin,
|
||||
exprs: E,
|
||||
prev_ty: Ty<'tcx>,
|
||||
new: &'b hir::Expr)
|
||||
-> RelateResult<'tcx, Ty<'tcx>>
|
||||
// FIXME(eddyb) use copyable iterators when that becomes ergonomic.
|
||||
where E: Fn() -> I,
|
||||
I: IntoIterator<Item=&'b hir::Expr> {
|
||||
let mut coerce = Coerce::new(self, origin);
|
||||
coerce.use_lub = true;
|
||||
|
||||
let prev_ty = self.resolve_type_vars_with_obligations(prev_ty);
|
||||
let new_ty = self.resolve_type_vars_with_obligations(self.expr_ty(new));
|
||||
debug!("coercion::try_find_lub({:?}, {:?})", prev_ty, new_ty);
|
||||
// First try to coerce the new expression to the type of the previous ones,
|
||||
// but only if the new expression has no coercion already applied to it.
|
||||
let mut first_error = None;
|
||||
if !self.tables.borrow().adjustments.contains_key(&new.id) {
|
||||
let result = self.commit_if_ok(|_| {
|
||||
apply(&mut coerce, &|| Some(new), new_ty, prev_ty)
|
||||
});
|
||||
match result {
|
||||
Ok((ty, adjustment)) => {
|
||||
if !adjustment.is_identity() {
|
||||
self.write_adjustment(new.id, adjustment);
|
||||
}
|
||||
return Ok(ty);
|
||||
}
|
||||
Err(e) => first_error = Some(e)
|
||||
}
|
||||
}
|
||||
|
||||
let trace = TypeTrace::types(origin, true, prev_ty, new_ty);
|
||||
// Then try to coerce the previous expressions to the type of the new one.
|
||||
// This requires ensuring there are no coercions applied to *any* of the
|
||||
// previous expressions, other than noop reborrows (ignoring lifetimes).
|
||||
for expr in exprs() {
|
||||
let noop = match self.tables.borrow().adjustments.get(&expr.id) {
|
||||
Some(&AdjustDerefRef(AutoDerefRef {
|
||||
autoderefs: 1,
|
||||
autoref: Some(AutoPtr(_, mutbl_adj)),
|
||||
unsize: None
|
||||
})) => match self.expr_ty(expr).sty {
|
||||
ty::TyRef(_, mt_orig) => {
|
||||
// Reborrow that we can safely ignore.
|
||||
mutbl_adj == mt_orig.mutbl
|
||||
}
|
||||
_ => false
|
||||
},
|
||||
Some(_) => false,
|
||||
None => true
|
||||
};
|
||||
|
||||
// Special-case that coercion alone cannot handle:
|
||||
// Two function item types of differing IDs or Substs.
|
||||
match (&prev_ty.sty, &new_ty.sty) {
|
||||
(&ty::TyFnDef(a_def_id, a_substs, a_fty),
|
||||
&ty::TyFnDef(b_def_id, b_substs, b_fty)) => {
|
||||
// The signature must always match.
|
||||
let fty = self.lub(true, trace.clone(), &a_fty, &b_fty)
|
||||
.map(|InferOk { value, obligations }| {
|
||||
// FIXME(#32730) propagate obligations
|
||||
assert!(obligations.is_empty());
|
||||
value
|
||||
})?;
|
||||
|
||||
if a_def_id == b_def_id {
|
||||
// Same function, maybe the parameters match.
|
||||
let substs = self.commit_if_ok(|_| {
|
||||
self.lub(true, trace.clone(), &a_substs, &b_substs)
|
||||
if !noop {
|
||||
return self.commit_if_ok(|_| {
|
||||
self.lub(true, trace.clone(), &prev_ty, &new_ty)
|
||||
.map(|InferOk { value, obligations }| {
|
||||
// FIXME(#32730) propagate obligations
|
||||
assert!(obligations.is_empty());
|
||||
value
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(substs) = substs {
|
||||
// We have a LUB of prev_ty and new_ty, just return it.
|
||||
return Ok(self.tcx.mk_fn_def(a_def_id, substs, fty));
|
||||
match self.commit_if_ok(|_| apply(&mut coerce, &exprs, prev_ty, new_ty)) {
|
||||
Err(_) => {
|
||||
// Avoid giving strange errors on failed attempts.
|
||||
if let Some(e) = first_error {
|
||||
Err(e)
|
||||
} else {
|
||||
self.commit_if_ok(|_| {
|
||||
self.lub(true, trace, &prev_ty, &new_ty)
|
||||
.map(|InferOk { value, obligations }| {
|
||||
// FIXME(#32730) propagate obligations
|
||||
assert!(obligations.is_empty());
|
||||
value
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Reify both sides and return the reified fn pointer type.
|
||||
for expr in exprs().into_iter().chain(Some(new)) {
|
||||
// No adjustments can produce a fn item, so this should never trip.
|
||||
assert!(!self.tables.borrow().adjustments.contains_key(&expr.id));
|
||||
self.write_adjustment(expr.id, AdjustReifyFnPointer);
|
||||
}
|
||||
return Ok(self.tcx.mk_fn_ptr(fty));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let mut coerce = Coerce::new(self, origin);
|
||||
coerce.use_lub = true;
|
||||
|
||||
// First try to coerce the new expression to the type of the previous ones,
|
||||
// but only if the new expression has no coercion already applied to it.
|
||||
let mut first_error = None;
|
||||
if !self.tables.borrow().adjustments.contains_key(&new.id) {
|
||||
let result = self.commit_if_ok(|_| {
|
||||
apply(&mut coerce, &|| Some(new), new_ty, prev_ty)
|
||||
});
|
||||
match result {
|
||||
Ok((ty, adjustment)) => {
|
||||
if !adjustment.is_identity() {
|
||||
self.write_adjustment(new.id, adjustment);
|
||||
for expr in exprs() {
|
||||
self.write_adjustment(expr.id, adjustment);
|
||||
}
|
||||
}
|
||||
return Ok(ty);
|
||||
Ok(ty)
|
||||
}
|
||||
Err(e) => first_error = Some(e)
|
||||
}
|
||||
}
|
||||
|
||||
// Then try to coerce the previous expressions to the type of the new one.
|
||||
// This requires ensuring there are no coercions applied to *any* of the
|
||||
// previous expressions, other than noop reborrows (ignoring lifetimes).
|
||||
for expr in exprs() {
|
||||
let noop = match self.tables.borrow().adjustments.get(&expr.id) {
|
||||
Some(&AdjustDerefRef(AutoDerefRef {
|
||||
autoderefs: 1,
|
||||
autoref: Some(AutoPtr(_, mutbl_adj)),
|
||||
unsize: None
|
||||
})) => match self.expr_ty(expr).sty {
|
||||
ty::TyRef(_, mt_orig) => {
|
||||
// Reborrow that we can safely ignore.
|
||||
mutbl_adj == mt_orig.mutbl
|
||||
}
|
||||
_ => false
|
||||
},
|
||||
Some(_) => false,
|
||||
None => true
|
||||
};
|
||||
|
||||
if !noop {
|
||||
return self.commit_if_ok(|_| {
|
||||
self.lub(true, trace.clone(), &prev_ty, &new_ty)
|
||||
.map(|InferOk { value, obligations }| {
|
||||
// FIXME(#32730) propagate obligations
|
||||
assert!(obligations.is_empty());
|
||||
value
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
match self.commit_if_ok(|_| apply(&mut coerce, &exprs, prev_ty, new_ty)) {
|
||||
Err(_) => {
|
||||
// Avoid giving strange errors on failed attempts.
|
||||
if let Some(e) = first_error {
|
||||
Err(e)
|
||||
} else {
|
||||
self.commit_if_ok(|_| {
|
||||
self.lub(true, trace, &prev_ty, &new_ty)
|
||||
.map(|InferOk { value, obligations }| {
|
||||
// FIXME(#32730) propagate obligations
|
||||
assert!(obligations.is_empty());
|
||||
value
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
Ok((ty, adjustment)) => {
|
||||
if !adjustment.is_identity() {
|
||||
for expr in exprs() {
|
||||
self.write_adjustment(expr.id, adjustment);
|
||||
}
|
||||
}
|
||||
Ok(ty)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,53 +17,53 @@ use syntax::codemap::Span;
|
||||
use rustc::hir;
|
||||
|
||||
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
// Requires that the two types unify, and prints an error message if
|
||||
// they don't.
|
||||
pub fn demand_suptype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
|
||||
let origin = TypeOrigin::Misc(sp);
|
||||
match self.sub_types(false, origin, actual, expected) {
|
||||
Ok(InferOk { obligations, .. }) => {
|
||||
// FIXME(#32730) propagate obligations
|
||||
assert!(obligations.is_empty());
|
||||
},
|
||||
Err(e) => {
|
||||
self.report_mismatched_types(origin, expected, actual, e);
|
||||
// Requires that the two types unify, and prints an error message if
|
||||
// they don't.
|
||||
pub fn demand_suptype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
|
||||
let origin = TypeOrigin::Misc(sp);
|
||||
match self.sub_types(false, origin, actual, expected) {
|
||||
Ok(InferOk { obligations, .. }) => {
|
||||
// FIXME(#32730) propagate obligations
|
||||
assert!(obligations.is_empty());
|
||||
},
|
||||
Err(e) => {
|
||||
self.report_mismatched_types(origin, expected, actual, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn demand_eqtype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
|
||||
let origin = TypeOrigin::Misc(sp);
|
||||
match self.eq_types(false, origin, actual, expected) {
|
||||
Ok(InferOk { obligations, .. }) => {
|
||||
// FIXME(#32730) propagate obligations
|
||||
assert!(obligations.is_empty());
|
||||
},
|
||||
Err(e) => {
|
||||
self.report_mismatched_types(origin, expected, actual, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Checks that the type of `expr` can be coerced to `expected`.
|
||||
pub fn demand_coerce(&self, expr: &hir::Expr, expected: Ty<'tcx>) {
|
||||
let expected = self.resolve_type_vars_with_obligations(expected);
|
||||
if let Err(e) = self.try_coerce(expr, expected) {
|
||||
let origin = TypeOrigin::Misc(expr.span);
|
||||
let expr_ty = self.resolve_type_vars_with_obligations(self.expr_ty(expr));
|
||||
self.report_mismatched_types(origin, expected, expr_ty, e);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn require_same_types(&self, span: Span, t1: Ty<'tcx>, t2: Ty<'tcx>, msg: &str)
|
||||
-> bool {
|
||||
if let Err(err) = self.eq_types(false, TypeOrigin::Misc(span), t1, t2) {
|
||||
let found_ty = self.resolve_type_vars_if_possible(&t1);
|
||||
let expected_ty = self.resolve_type_vars_if_possible(&t2);
|
||||
::emit_type_err(self.tcx, span, found_ty, expected_ty, &err, msg);
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn demand_eqtype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
|
||||
let origin = TypeOrigin::Misc(sp);
|
||||
match self.eq_types(false, origin, actual, expected) {
|
||||
Ok(InferOk { obligations, .. }) => {
|
||||
// FIXME(#32730) propagate obligations
|
||||
assert!(obligations.is_empty());
|
||||
},
|
||||
Err(e) => {
|
||||
self.report_mismatched_types(origin, expected, actual, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Checks that the type of `expr` can be coerced to `expected`.
|
||||
pub fn demand_coerce(&self, expr: &hir::Expr, expected: Ty<'tcx>) {
|
||||
let expected = self.resolve_type_vars_with_obligations(expected);
|
||||
if let Err(e) = self.try_coerce(expr, expected) {
|
||||
let origin = TypeOrigin::Misc(expr.span);
|
||||
let expr_ty = self.resolve_type_vars_with_obligations(self.expr_ty(expr));
|
||||
self.report_mismatched_types(origin, expected, expr_ty, e);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn require_same_types(&self, span: Span, t1: Ty<'tcx>, t2: Ty<'tcx>, msg: &str)
|
||||
-> bool {
|
||||
if let Err(err) = self.eq_types(false, TypeOrigin::Misc(span), t1, t2) {
|
||||
let found_ty = self.resolve_type_vars_if_possible(&t1);
|
||||
let expected_ty = self.resolve_type_vars_if_possible(&t2);
|
||||
::emit_type_err(self.tcx, span, found_ty, expected_ty, &err, msg);
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -53,23 +53,23 @@ struct InstantiatedMethodSig<'tcx> {
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
pub fn confirm_method(&self,
|
||||
span: Span,
|
||||
self_expr: &'gcx hir::Expr,
|
||||
call_expr: &'gcx hir::Expr,
|
||||
unadjusted_self_ty: Ty<'tcx>,
|
||||
pick: probe::Pick<'tcx>,
|
||||
supplied_method_types: Vec<Ty<'tcx>>)
|
||||
-> ty::MethodCallee<'tcx>
|
||||
{
|
||||
debug!("confirm(unadjusted_self_ty={:?}, pick={:?}, supplied_method_types={:?})",
|
||||
unadjusted_self_ty,
|
||||
pick,
|
||||
supplied_method_types);
|
||||
pub fn confirm_method(&self,
|
||||
span: Span,
|
||||
self_expr: &'gcx hir::Expr,
|
||||
call_expr: &'gcx hir::Expr,
|
||||
unadjusted_self_ty: Ty<'tcx>,
|
||||
pick: probe::Pick<'tcx>,
|
||||
supplied_method_types: Vec<Ty<'tcx>>)
|
||||
-> ty::MethodCallee<'tcx>
|
||||
{
|
||||
debug!("confirm(unadjusted_self_ty={:?}, pick={:?}, supplied_method_types={:?})",
|
||||
unadjusted_self_ty,
|
||||
pick,
|
||||
supplied_method_types);
|
||||
|
||||
let mut confirm_cx = ConfirmContext::new(self, span, self_expr, call_expr);
|
||||
confirm_cx.confirm(unadjusted_self_ty, pick, supplied_method_types)
|
||||
}
|
||||
let mut confirm_cx = ConfirmContext::new(self, span, self_expr, call_expr);
|
||||
confirm_cx.confirm(unadjusted_self_ty, pick, supplied_method_types)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
||||
|
@ -79,311 +79,314 @@ pub enum CandidateSource {
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
/// Determines whether the type `self_ty` supports a method name `method_name` or not.
|
||||
pub fn method_exists(&self,
|
||||
span: Span,
|
||||
method_name: ast::Name,
|
||||
self_ty: ty::Ty<'tcx>,
|
||||
call_expr_id: ast::NodeId)
|
||||
-> bool
|
||||
{
|
||||
let mode = probe::Mode::MethodCall;
|
||||
match self.probe_method(span, mode, method_name, self_ty, call_expr_id) {
|
||||
Ok(..) => true,
|
||||
Err(NoMatch(..)) => false,
|
||||
Err(Ambiguity(..)) => true,
|
||||
Err(ClosureAmbiguity(..)) => true,
|
||||
Err(PrivateMatch(..)) => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs method lookup. If lookup is successful, it will return the callee and store an
|
||||
/// appropriate adjustment for the self-expr. In some cases it may report an error (e.g., invoking
|
||||
/// the `drop` method).
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// Given a method call like `foo.bar::<T1,...Tn>(...)`:
|
||||
///
|
||||
/// * `fcx`: the surrounding `FnCtxt` (!)
|
||||
/// * `span`: the span for the method call
|
||||
/// * `method_name`: the name of the method being called (`bar`)
|
||||
/// * `self_ty`: the (unadjusted) type of the self expression (`foo`)
|
||||
/// * `supplied_method_types`: the explicit method type parameters, if any (`T1..Tn`)
|
||||
/// * `self_expr`: the self expression (`foo`)
|
||||
pub fn lookup_method(&self,
|
||||
span: Span,
|
||||
method_name: ast::Name,
|
||||
self_ty: ty::Ty<'tcx>,
|
||||
supplied_method_types: Vec<ty::Ty<'tcx>>,
|
||||
call_expr: &'gcx hir::Expr,
|
||||
self_expr: &'gcx hir::Expr)
|
||||
-> Result<ty::MethodCallee<'tcx>, MethodError<'tcx>>
|
||||
{
|
||||
debug!("lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})",
|
||||
method_name,
|
||||
self_ty,
|
||||
call_expr,
|
||||
self_expr);
|
||||
|
||||
let mode = probe::Mode::MethodCall;
|
||||
let self_ty = self.resolve_type_vars_if_possible(&self_ty);
|
||||
let pick = self.probe_method(span, mode, method_name, self_ty, call_expr.id)?;
|
||||
|
||||
if let Some(import_id) = pick.import_id {
|
||||
self.tcx.used_trait_imports.borrow_mut().insert(import_id);
|
||||
}
|
||||
|
||||
Ok(self.confirm_method(span, self_expr, call_expr, self_ty, pick, supplied_method_types))
|
||||
}
|
||||
|
||||
pub fn lookup_method_in_trait(&self,
|
||||
span: Span,
|
||||
self_expr: Option<&hir::Expr>,
|
||||
m_name: ast::Name,
|
||||
trait_def_id: DefId,
|
||||
self_ty: ty::Ty<'tcx>,
|
||||
opt_input_types: Option<Vec<ty::Ty<'tcx>>>)
|
||||
-> Option<ty::MethodCallee<'tcx>>
|
||||
{
|
||||
self.lookup_method_in_trait_adjusted(span, self_expr, m_name, trait_def_id,
|
||||
0, false, self_ty, opt_input_types)
|
||||
}
|
||||
|
||||
/// `lookup_in_trait_adjusted` is used for overloaded operators. It does a very narrow slice of
|
||||
/// what the normal probe/confirm path does. In particular, it doesn't really do any probing: it
|
||||
/// simply constructs an obligation for a particular trait with the given self-type and checks
|
||||
/// whether that trait is implemented.
|
||||
///
|
||||
/// FIXME(#18741) -- It seems likely that we can consolidate some of this code with the other
|
||||
/// method-lookup code. In particular, autoderef on index is basically identical to autoderef with
|
||||
/// normal probes, except that the test also looks for built-in indexing. Also, the second half of
|
||||
/// this method is basically the same as confirmation.
|
||||
pub fn lookup_method_in_trait_adjusted(&self,
|
||||
span: Span,
|
||||
self_expr: Option<&hir::Expr>,
|
||||
m_name: ast::Name,
|
||||
trait_def_id: DefId,
|
||||
autoderefs: usize,
|
||||
unsize: bool,
|
||||
self_ty: ty::Ty<'tcx>,
|
||||
opt_input_types: Option<Vec<ty::Ty<'tcx>>>)
|
||||
-> Option<ty::MethodCallee<'tcx>>
|
||||
{
|
||||
debug!("lookup_in_trait_adjusted(self_ty={:?}, self_expr={:?}, m_name={}, trait_def_id={:?})",
|
||||
self_ty,
|
||||
self_expr,
|
||||
m_name,
|
||||
trait_def_id);
|
||||
|
||||
let trait_def = self.tcx.lookup_trait_def(trait_def_id);
|
||||
|
||||
let type_parameter_defs = trait_def.generics.types.get_slice(subst::TypeSpace);
|
||||
let expected_number_of_input_types = type_parameter_defs.len();
|
||||
|
||||
assert_eq!(trait_def.generics.types.len(subst::FnSpace), 0);
|
||||
assert!(trait_def.generics.regions.is_empty());
|
||||
|
||||
// Construct a trait-reference `self_ty : Trait<input_tys>`
|
||||
let mut substs = subst::Substs::new_trait(Vec::new(), Vec::new(), self_ty);
|
||||
|
||||
match opt_input_types {
|
||||
Some(input_types) => {
|
||||
assert_eq!(expected_number_of_input_types, input_types.len());
|
||||
substs.types.replace(subst::ParamSpace::TypeSpace, input_types);
|
||||
}
|
||||
|
||||
None => {
|
||||
self.type_vars_for_defs(
|
||||
span,
|
||||
subst::ParamSpace::TypeSpace,
|
||||
&mut substs,
|
||||
type_parameter_defs);
|
||||
/// Determines whether the type `self_ty` supports a method name `method_name` or not.
|
||||
pub fn method_exists(&self,
|
||||
span: Span,
|
||||
method_name: ast::Name,
|
||||
self_ty: ty::Ty<'tcx>,
|
||||
call_expr_id: ast::NodeId)
|
||||
-> bool
|
||||
{
|
||||
let mode = probe::Mode::MethodCall;
|
||||
match self.probe_method(span, mode, method_name, self_ty, call_expr_id) {
|
||||
Ok(..) => true,
|
||||
Err(NoMatch(..)) => false,
|
||||
Err(Ambiguity(..)) => true,
|
||||
Err(ClosureAmbiguity(..)) => true,
|
||||
Err(PrivateMatch(..)) => true,
|
||||
}
|
||||
}
|
||||
|
||||
let trait_ref = ty::TraitRef::new(trait_def_id, self.tcx.mk_substs(substs));
|
||||
/// Performs method lookup. If lookup is successful, it will return the callee
|
||||
/// and store an appropriate adjustment for the self-expr. In some cases it may
|
||||
/// report an error (e.g., invoking the `drop` method).
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// Given a method call like `foo.bar::<T1,...Tn>(...)`:
|
||||
///
|
||||
/// * `fcx`: the surrounding `FnCtxt` (!)
|
||||
/// * `span`: the span for the method call
|
||||
/// * `method_name`: the name of the method being called (`bar`)
|
||||
/// * `self_ty`: the (unadjusted) type of the self expression (`foo`)
|
||||
/// * `supplied_method_types`: the explicit method type parameters, if any (`T1..Tn`)
|
||||
/// * `self_expr`: the self expression (`foo`)
|
||||
pub fn lookup_method(&self,
|
||||
span: Span,
|
||||
method_name: ast::Name,
|
||||
self_ty: ty::Ty<'tcx>,
|
||||
supplied_method_types: Vec<ty::Ty<'tcx>>,
|
||||
call_expr: &'gcx hir::Expr,
|
||||
self_expr: &'gcx hir::Expr)
|
||||
-> Result<ty::MethodCallee<'tcx>, MethodError<'tcx>>
|
||||
{
|
||||
debug!("lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})",
|
||||
method_name,
|
||||
self_ty,
|
||||
call_expr,
|
||||
self_expr);
|
||||
|
||||
// Construct an obligation
|
||||
let poly_trait_ref = trait_ref.to_poly_trait_ref();
|
||||
let obligation = traits::Obligation::misc(span,
|
||||
self.body_id,
|
||||
poly_trait_ref.to_predicate());
|
||||
let mode = probe::Mode::MethodCall;
|
||||
let self_ty = self.resolve_type_vars_if_possible(&self_ty);
|
||||
let pick = self.probe_method(span, mode, method_name, self_ty, call_expr.id)?;
|
||||
|
||||
// Now we want to know if this can be matched
|
||||
let mut selcx = traits::SelectionContext::new(self);
|
||||
if !selcx.evaluate_obligation(&obligation) {
|
||||
debug!("--> Cannot match obligation");
|
||||
return None; // Cannot be matched, no such method resolution is possible.
|
||||
if let Some(import_id) = pick.import_id {
|
||||
self.tcx.used_trait_imports.borrow_mut().insert(import_id);
|
||||
}
|
||||
|
||||
Ok(self.confirm_method(span, self_expr, call_expr, self_ty, pick, supplied_method_types))
|
||||
}
|
||||
|
||||
// Trait must have a method named `m_name` and it should not have
|
||||
// type parameters or early-bound regions.
|
||||
let tcx = self.tcx;
|
||||
let method_item = self.trait_item(trait_def_id, m_name).unwrap();
|
||||
let method_ty = method_item.as_opt_method().unwrap();
|
||||
assert_eq!(method_ty.generics.types.len(subst::FnSpace), 0);
|
||||
assert_eq!(method_ty.generics.regions.len(subst::FnSpace), 0);
|
||||
pub fn lookup_method_in_trait(&self,
|
||||
span: Span,
|
||||
self_expr: Option<&hir::Expr>,
|
||||
m_name: ast::Name,
|
||||
trait_def_id: DefId,
|
||||
self_ty: ty::Ty<'tcx>,
|
||||
opt_input_types: Option<Vec<ty::Ty<'tcx>>>)
|
||||
-> Option<ty::MethodCallee<'tcx>>
|
||||
{
|
||||
self.lookup_method_in_trait_adjusted(span, self_expr, m_name, trait_def_id,
|
||||
0, false, self_ty, opt_input_types)
|
||||
}
|
||||
|
||||
debug!("lookup_in_trait_adjusted: method_item={:?} method_ty={:?}",
|
||||
method_item, method_ty);
|
||||
/// `lookup_in_trait_adjusted` is used for overloaded operators.
|
||||
/// It does a very narrow slice of what the normal probe/confirm path does.
|
||||
/// In particular, it doesn't really do any probing: it simply constructs
|
||||
/// an obligation for aparticular trait with the given self-type and checks
|
||||
/// whether that trait is implemented.
|
||||
///
|
||||
/// FIXME(#18741) -- It seems likely that we can consolidate some of this
|
||||
/// code with the other method-lookup code. In particular, autoderef on
|
||||
/// index is basically identical to autoderef with normal probes, except
|
||||
/// that the test also looks for built-in indexing. Also, the second half of
|
||||
/// this method is basically the same as confirmation.
|
||||
pub fn lookup_method_in_trait_adjusted(&self,
|
||||
span: Span,
|
||||
self_expr: Option<&hir::Expr>,
|
||||
m_name: ast::Name,
|
||||
trait_def_id: DefId,
|
||||
autoderefs: usize,
|
||||
unsize: bool,
|
||||
self_ty: ty::Ty<'tcx>,
|
||||
opt_input_types: Option<Vec<ty::Ty<'tcx>>>)
|
||||
-> Option<ty::MethodCallee<'tcx>>
|
||||
{
|
||||
debug!("lookup_in_trait_adjusted(self_ty={:?}, self_expr={:?}, \
|
||||
m_name={}, trait_def_id={:?})",
|
||||
self_ty,
|
||||
self_expr,
|
||||
m_name,
|
||||
trait_def_id);
|
||||
|
||||
// Instantiate late-bound regions and substitute the trait
|
||||
// parameters into the method type to get the actual method type.
|
||||
//
|
||||
// NB: Instantiate late-bound regions first so that
|
||||
// `instantiate_type_scheme` can normalize associated types that
|
||||
// may reference those regions.
|
||||
let fn_sig = self.replace_late_bound_regions_with_fresh_var(span,
|
||||
infer::FnCall,
|
||||
&method_ty.fty.sig).0;
|
||||
let fn_sig = self.instantiate_type_scheme(span, trait_ref.substs, &fn_sig);
|
||||
let transformed_self_ty = fn_sig.inputs[0];
|
||||
let def_id = method_item.def_id();
|
||||
let fty = tcx.mk_fn_def(def_id, trait_ref.substs,
|
||||
tcx.mk_bare_fn(ty::BareFnTy {
|
||||
sig: ty::Binder(fn_sig),
|
||||
unsafety: method_ty.fty.unsafety,
|
||||
abi: method_ty.fty.abi.clone(),
|
||||
}));
|
||||
let trait_def = self.tcx.lookup_trait_def(trait_def_id);
|
||||
|
||||
debug!("lookup_in_trait_adjusted: matched method fty={:?} obligation={:?}",
|
||||
fty,
|
||||
obligation);
|
||||
let type_parameter_defs = trait_def.generics.types.get_slice(subst::TypeSpace);
|
||||
let expected_number_of_input_types = type_parameter_defs.len();
|
||||
|
||||
// Register obligations for the parameters. This will include the
|
||||
// `Self` parameter, which in turn has a bound of the main trait,
|
||||
// so this also effectively registers `obligation` as well. (We
|
||||
// used to register `obligation` explicitly, but that resulted in
|
||||
// double error messages being reported.)
|
||||
//
|
||||
// Note that as the method comes from a trait, it should not have
|
||||
// any late-bound regions appearing in its bounds.
|
||||
let method_bounds = self.instantiate_bounds(span, trait_ref.substs, &method_ty.predicates);
|
||||
assert!(!method_bounds.has_escaping_regions());
|
||||
self.add_obligations_for_parameters(
|
||||
traits::ObligationCause::misc(span, self.body_id),
|
||||
&method_bounds);
|
||||
assert_eq!(trait_def.generics.types.len(subst::FnSpace), 0);
|
||||
assert!(trait_def.generics.regions.is_empty());
|
||||
|
||||
// Also register an obligation for the method type being well-formed.
|
||||
self.register_wf_obligation(fty, span, traits::MiscObligation);
|
||||
// Construct a trait-reference `self_ty : Trait<input_tys>`
|
||||
let mut substs = subst::Substs::new_trait(Vec::new(), Vec::new(), self_ty);
|
||||
|
||||
// FIXME(#18653) -- Try to resolve obligations, giving us more
|
||||
// typing information, which can sometimes be needed to avoid
|
||||
// pathological region inference failures.
|
||||
self.select_obligations_where_possible();
|
||||
match opt_input_types {
|
||||
Some(input_types) => {
|
||||
assert_eq!(expected_number_of_input_types, input_types.len());
|
||||
substs.types.replace(subst::ParamSpace::TypeSpace, input_types);
|
||||
}
|
||||
|
||||
// Insert any adjustments needed (always an autoref of some mutability).
|
||||
match self_expr {
|
||||
None => { }
|
||||
None => {
|
||||
self.type_vars_for_defs(
|
||||
span,
|
||||
subst::ParamSpace::TypeSpace,
|
||||
&mut substs,
|
||||
type_parameter_defs);
|
||||
}
|
||||
}
|
||||
|
||||
Some(self_expr) => {
|
||||
debug!("lookup_in_trait_adjusted: inserting adjustment if needed \
|
||||
(self-id={}, autoderefs={}, unsize={}, explicit_self={:?})",
|
||||
self_expr.id, autoderefs, unsize,
|
||||
method_ty.explicit_self);
|
||||
let trait_ref = ty::TraitRef::new(trait_def_id, self.tcx.mk_substs(substs));
|
||||
|
||||
match method_ty.explicit_self {
|
||||
ty::ExplicitSelfCategory::ByValue => {
|
||||
// Trait method is fn(self), no transformation needed.
|
||||
assert!(!unsize);
|
||||
self.write_autoderef_adjustment(self_expr.id, autoderefs);
|
||||
}
|
||||
// Construct an obligation
|
||||
let poly_trait_ref = trait_ref.to_poly_trait_ref();
|
||||
let obligation = traits::Obligation::misc(span,
|
||||
self.body_id,
|
||||
poly_trait_ref.to_predicate());
|
||||
|
||||
ty::ExplicitSelfCategory::ByReference(..) => {
|
||||
// Trait method is fn(&self) or fn(&mut self), need an
|
||||
// autoref. Pull the region etc out of the type of first argument.
|
||||
match transformed_self_ty.sty {
|
||||
ty::TyRef(region, ty::TypeAndMut { mutbl, ty: _ }) => {
|
||||
self.write_adjustment(self_expr.id,
|
||||
AdjustDerefRef(AutoDerefRef {
|
||||
autoderefs: autoderefs,
|
||||
autoref: Some(AutoPtr(region, mutbl)),
|
||||
unsize: if unsize {
|
||||
Some(transformed_self_ty)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}));
|
||||
}
|
||||
// Now we want to know if this can be matched
|
||||
let mut selcx = traits::SelectionContext::new(self);
|
||||
if !selcx.evaluate_obligation(&obligation) {
|
||||
debug!("--> Cannot match obligation");
|
||||
return None; // Cannot be matched, no such method resolution is possible.
|
||||
}
|
||||
|
||||
_ => {
|
||||
span_bug!(
|
||||
span,
|
||||
"trait method is &self but first arg is: {}",
|
||||
transformed_self_ty);
|
||||
// Trait must have a method named `m_name` and it should not have
|
||||
// type parameters or early-bound regions.
|
||||
let tcx = self.tcx;
|
||||
let method_item = self.trait_item(trait_def_id, m_name).unwrap();
|
||||
let method_ty = method_item.as_opt_method().unwrap();
|
||||
assert_eq!(method_ty.generics.types.len(subst::FnSpace), 0);
|
||||
assert_eq!(method_ty.generics.regions.len(subst::FnSpace), 0);
|
||||
|
||||
debug!("lookup_in_trait_adjusted: method_item={:?} method_ty={:?}",
|
||||
method_item, method_ty);
|
||||
|
||||
// Instantiate late-bound regions and substitute the trait
|
||||
// parameters into the method type to get the actual method type.
|
||||
//
|
||||
// NB: Instantiate late-bound regions first so that
|
||||
// `instantiate_type_scheme` can normalize associated types that
|
||||
// may reference those regions.
|
||||
let fn_sig = self.replace_late_bound_regions_with_fresh_var(span,
|
||||
infer::FnCall,
|
||||
&method_ty.fty.sig).0;
|
||||
let fn_sig = self.instantiate_type_scheme(span, trait_ref.substs, &fn_sig);
|
||||
let transformed_self_ty = fn_sig.inputs[0];
|
||||
let def_id = method_item.def_id();
|
||||
let fty = tcx.mk_fn_def(def_id, trait_ref.substs,
|
||||
tcx.mk_bare_fn(ty::BareFnTy {
|
||||
sig: ty::Binder(fn_sig),
|
||||
unsafety: method_ty.fty.unsafety,
|
||||
abi: method_ty.fty.abi.clone(),
|
||||
}));
|
||||
|
||||
debug!("lookup_in_trait_adjusted: matched method fty={:?} obligation={:?}",
|
||||
fty,
|
||||
obligation);
|
||||
|
||||
// Register obligations for the parameters. This will include the
|
||||
// `Self` parameter, which in turn has a bound of the main trait,
|
||||
// so this also effectively registers `obligation` as well. (We
|
||||
// used to register `obligation` explicitly, but that resulted in
|
||||
// double error messages being reported.)
|
||||
//
|
||||
// Note that as the method comes from a trait, it should not have
|
||||
// any late-bound regions appearing in its bounds.
|
||||
let method_bounds = self.instantiate_bounds(span, trait_ref.substs, &method_ty.predicates);
|
||||
assert!(!method_bounds.has_escaping_regions());
|
||||
self.add_obligations_for_parameters(
|
||||
traits::ObligationCause::misc(span, self.body_id),
|
||||
&method_bounds);
|
||||
|
||||
// Also register an obligation for the method type being well-formed.
|
||||
self.register_wf_obligation(fty, span, traits::MiscObligation);
|
||||
|
||||
// FIXME(#18653) -- Try to resolve obligations, giving us more
|
||||
// typing information, which can sometimes be needed to avoid
|
||||
// pathological region inference failures.
|
||||
self.select_obligations_where_possible();
|
||||
|
||||
// Insert any adjustments needed (always an autoref of some mutability).
|
||||
match self_expr {
|
||||
None => { }
|
||||
|
||||
Some(self_expr) => {
|
||||
debug!("lookup_in_trait_adjusted: inserting adjustment if needed \
|
||||
(self-id={}, autoderefs={}, unsize={}, explicit_self={:?})",
|
||||
self_expr.id, autoderefs, unsize,
|
||||
method_ty.explicit_self);
|
||||
|
||||
match method_ty.explicit_self {
|
||||
ty::ExplicitSelfCategory::ByValue => {
|
||||
// Trait method is fn(self), no transformation needed.
|
||||
assert!(!unsize);
|
||||
self.write_autoderef_adjustment(self_expr.id, autoderefs);
|
||||
}
|
||||
|
||||
ty::ExplicitSelfCategory::ByReference(..) => {
|
||||
// Trait method is fn(&self) or fn(&mut self), need an
|
||||
// autoref. Pull the region etc out of the type of first argument.
|
||||
match transformed_self_ty.sty {
|
||||
ty::TyRef(region, ty::TypeAndMut { mutbl, ty: _ }) => {
|
||||
self.write_adjustment(self_expr.id,
|
||||
AdjustDerefRef(AutoDerefRef {
|
||||
autoderefs: autoderefs,
|
||||
autoref: Some(AutoPtr(region, mutbl)),
|
||||
unsize: if unsize {
|
||||
Some(transformed_self_ty)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
_ => {
|
||||
span_bug!(
|
||||
span,
|
||||
"trait method is &self but first arg is: {}",
|
||||
transformed_self_ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
span_bug!(
|
||||
span,
|
||||
"unexpected explicit self type in operator method: {:?}",
|
||||
method_ty.explicit_self);
|
||||
_ => {
|
||||
span_bug!(
|
||||
span,
|
||||
"unexpected explicit self type in operator method: {:?}",
|
||||
method_ty.explicit_self);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let callee = ty::MethodCallee {
|
||||
def_id: def_id,
|
||||
ty: fty,
|
||||
substs: trait_ref.substs
|
||||
};
|
||||
|
||||
debug!("callee = {:?}", callee);
|
||||
|
||||
Some(callee)
|
||||
}
|
||||
|
||||
let callee = ty::MethodCallee {
|
||||
def_id: def_id,
|
||||
ty: fty,
|
||||
substs: trait_ref.substs
|
||||
};
|
||||
pub fn resolve_ufcs(&self,
|
||||
span: Span,
|
||||
method_name: ast::Name,
|
||||
self_ty: ty::Ty<'tcx>,
|
||||
expr_id: ast::NodeId)
|
||||
-> Result<Def, MethodError<'tcx>>
|
||||
{
|
||||
let mode = probe::Mode::Path;
|
||||
let pick = self.probe_method(span, mode, method_name, self_ty, expr_id)?;
|
||||
|
||||
debug!("callee = {:?}", callee);
|
||||
|
||||
Some(callee)
|
||||
}
|
||||
|
||||
pub fn resolve_ufcs(&self,
|
||||
span: Span,
|
||||
method_name: ast::Name,
|
||||
self_ty: ty::Ty<'tcx>,
|
||||
expr_id: ast::NodeId)
|
||||
-> Result<Def, MethodError<'tcx>>
|
||||
{
|
||||
let mode = probe::Mode::Path;
|
||||
let pick = self.probe_method(span, mode, method_name, self_ty, expr_id)?;
|
||||
|
||||
if let Some(import_id) = pick.import_id {
|
||||
self.tcx.used_trait_imports.borrow_mut().insert(import_id);
|
||||
}
|
||||
|
||||
let def = pick.item.def();
|
||||
if let probe::InherentImplPick = pick.kind {
|
||||
if !pick.item.vis().is_accessible_from(self.body_id, &self.tcx.map) {
|
||||
let msg = format!("{} `{}` is private", def.kind_name(), &method_name.as_str());
|
||||
self.tcx.sess.span_err(span, &msg);
|
||||
if let Some(import_id) = pick.import_id {
|
||||
self.tcx.used_trait_imports.borrow_mut().insert(import_id);
|
||||
}
|
||||
|
||||
let def = pick.item.def();
|
||||
if let probe::InherentImplPick = pick.kind {
|
||||
if !pick.item.vis().is_accessible_from(self.body_id, &self.tcx.map) {
|
||||
let msg = format!("{} `{}` is private", def.kind_name(), &method_name.as_str());
|
||||
self.tcx.sess.span_err(span, &msg);
|
||||
}
|
||||
}
|
||||
Ok(def)
|
||||
}
|
||||
Ok(def)
|
||||
}
|
||||
|
||||
/// Find item with name `item_name` defined in `trait_def_id`
|
||||
/// and return it, or `None`, if no such item.
|
||||
pub fn trait_item(&self,
|
||||
trait_def_id: DefId,
|
||||
item_name: ast::Name)
|
||||
-> Option<ty::ImplOrTraitItem<'tcx>>
|
||||
{
|
||||
let trait_items = self.tcx.trait_items(trait_def_id);
|
||||
trait_items.iter()
|
||||
.find(|item| item.name() == item_name)
|
||||
.cloned()
|
||||
}
|
||||
/// Find item with name `item_name` defined in `trait_def_id`
|
||||
/// and return it, or `None`, if no such item.
|
||||
pub fn trait_item(&self,
|
||||
trait_def_id: DefId,
|
||||
item_name: ast::Name)
|
||||
-> Option<ty::ImplOrTraitItem<'tcx>>
|
||||
{
|
||||
let trait_items = self.tcx.trait_items(trait_def_id);
|
||||
trait_items.iter()
|
||||
.find(|item| item.name() == item_name)
|
||||
.cloned()
|
||||
}
|
||||
|
||||
pub fn impl_item(&self,
|
||||
impl_def_id: DefId,
|
||||
item_name: ast::Name)
|
||||
-> Option<ty::ImplOrTraitItem<'tcx>>
|
||||
{
|
||||
let impl_items = self.tcx.impl_items.borrow();
|
||||
let impl_items = impl_items.get(&impl_def_id).unwrap();
|
||||
impl_items
|
||||
.iter()
|
||||
.map(|&did| self.tcx.impl_or_trait_item(did.def_id()))
|
||||
.find(|m| m.name() == item_name)
|
||||
}
|
||||
pub fn impl_item(&self,
|
||||
impl_def_id: DefId,
|
||||
item_name: ast::Name)
|
||||
-> Option<ty::ImplOrTraitItem<'tcx>>
|
||||
{
|
||||
let impl_items = self.tcx.impl_items.borrow();
|
||||
let impl_items = impl_items.get(&impl_def_id).unwrap();
|
||||
impl_items
|
||||
.iter()
|
||||
.map(|&did| self.tcx.impl_or_trait_item(did.def_id()))
|
||||
.find(|m| m.name() == item_name)
|
||||
}
|
||||
}
|
||||
|
@ -137,108 +137,108 @@ pub enum Mode {
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
pub fn probe_method(&self,
|
||||
span: Span,
|
||||
mode: Mode,
|
||||
item_name: ast::Name,
|
||||
self_ty: Ty<'tcx>,
|
||||
scope_expr_id: ast::NodeId)
|
||||
-> PickResult<'tcx>
|
||||
{
|
||||
debug!("probe(self_ty={:?}, item_name={}, scope_expr_id={})",
|
||||
self_ty,
|
||||
item_name,
|
||||
scope_expr_id);
|
||||
pub fn probe_method(&self,
|
||||
span: Span,
|
||||
mode: Mode,
|
||||
item_name: ast::Name,
|
||||
self_ty: Ty<'tcx>,
|
||||
scope_expr_id: ast::NodeId)
|
||||
-> PickResult<'tcx>
|
||||
{
|
||||
debug!("probe(self_ty={:?}, item_name={}, scope_expr_id={})",
|
||||
self_ty,
|
||||
item_name,
|
||||
scope_expr_id);
|
||||
|
||||
// FIXME(#18741) -- right now, creating the steps involves evaluating the
|
||||
// `*` operator, which registers obligations that then escape into
|
||||
// the global fulfillment context and thus has global
|
||||
// side-effects. This is a bit of a pain to refactor. So just let
|
||||
// it ride, although it's really not great, and in fact could I
|
||||
// think cause spurious errors. Really though this part should
|
||||
// take place in the `self.probe` below.
|
||||
let steps = if mode == Mode::MethodCall {
|
||||
match self.create_steps(span, self_ty) {
|
||||
Some(steps) => steps,
|
||||
None =>return Err(MethodError::NoMatch(NoMatchData::new(Vec::new(), Vec::new(),
|
||||
Vec::new(), mode))),
|
||||
}
|
||||
} else {
|
||||
vec![CandidateStep {
|
||||
self_ty: self_ty,
|
||||
autoderefs: 0,
|
||||
unsize: false
|
||||
}]
|
||||
};
|
||||
|
||||
// Create a list of simplified self types, if we can.
|
||||
let mut simplified_steps = Vec::new();
|
||||
for step in &steps {
|
||||
match ty::fast_reject::simplify_type(self.tcx, step.self_ty, true) {
|
||||
None => { break; }
|
||||
Some(simplified_type) => { simplified_steps.push(simplified_type); }
|
||||
}
|
||||
}
|
||||
let opt_simplified_steps =
|
||||
if simplified_steps.len() < steps.len() {
|
||||
None // failed to convert at least one of the steps
|
||||
// FIXME(#18741) -- right now, creating the steps involves evaluating the
|
||||
// `*` operator, which registers obligations that then escape into
|
||||
// the global fulfillment context and thus has global
|
||||
// side-effects. This is a bit of a pain to refactor. So just let
|
||||
// it ride, although it's really not great, and in fact could I
|
||||
// think cause spurious errors. Really though this part should
|
||||
// take place in the `self.probe` below.
|
||||
let steps = if mode == Mode::MethodCall {
|
||||
match self.create_steps(span, self_ty) {
|
||||
Some(steps) => steps,
|
||||
None =>return Err(MethodError::NoMatch(NoMatchData::new(Vec::new(), Vec::new(),
|
||||
Vec::new(), mode))),
|
||||
}
|
||||
} else {
|
||||
Some(simplified_steps)
|
||||
vec![CandidateStep {
|
||||
self_ty: self_ty,
|
||||
autoderefs: 0,
|
||||
unsize: false
|
||||
}]
|
||||
};
|
||||
|
||||
debug!("ProbeContext: steps for self_ty={:?} are {:?}",
|
||||
self_ty,
|
||||
steps);
|
||||
|
||||
// this creates one big transaction so that all type variables etc
|
||||
// that we create during the probe process are removed later
|
||||
self.probe(|_| {
|
||||
let mut probe_cx = ProbeContext::new(self,
|
||||
span,
|
||||
mode,
|
||||
item_name,
|
||||
steps,
|
||||
opt_simplified_steps);
|
||||
probe_cx.assemble_inherent_candidates();
|
||||
probe_cx.assemble_extension_candidates_for_traits_in_scope(scope_expr_id)?;
|
||||
probe_cx.pick()
|
||||
})
|
||||
}
|
||||
|
||||
fn create_steps(&self,
|
||||
span: Span,
|
||||
self_ty: Ty<'tcx>)
|
||||
-> Option<Vec<CandidateStep<'tcx>>> {
|
||||
let mut steps = Vec::new();
|
||||
|
||||
let (final_ty, dereferences, _) = self.autoderef(span,
|
||||
self_ty,
|
||||
|| None,
|
||||
UnresolvedTypeAction::Error,
|
||||
NoPreference,
|
||||
|t, d| {
|
||||
steps.push(CandidateStep {
|
||||
self_ty: t,
|
||||
autoderefs: d,
|
||||
unsize: false
|
||||
});
|
||||
None::<()> // keep iterating until we can't anymore
|
||||
});
|
||||
|
||||
match final_ty.sty {
|
||||
ty::TyArray(elem_ty, _) => {
|
||||
steps.push(CandidateStep {
|
||||
self_ty: self.tcx.mk_slice(elem_ty),
|
||||
autoderefs: dereferences,
|
||||
unsize: true
|
||||
});
|
||||
// Create a list of simplified self types, if we can.
|
||||
let mut simplified_steps = Vec::new();
|
||||
for step in &steps {
|
||||
match ty::fast_reject::simplify_type(self.tcx, step.self_ty, true) {
|
||||
None => { break; }
|
||||
Some(simplified_type) => { simplified_steps.push(simplified_type); }
|
||||
}
|
||||
}
|
||||
ty::TyError => return None,
|
||||
_ => (),
|
||||
let opt_simplified_steps =
|
||||
if simplified_steps.len() < steps.len() {
|
||||
None // failed to convert at least one of the steps
|
||||
} else {
|
||||
Some(simplified_steps)
|
||||
};
|
||||
|
||||
debug!("ProbeContext: steps for self_ty={:?} are {:?}",
|
||||
self_ty,
|
||||
steps);
|
||||
|
||||
// this creates one big transaction so that all type variables etc
|
||||
// that we create during the probe process are removed later
|
||||
self.probe(|_| {
|
||||
let mut probe_cx = ProbeContext::new(self,
|
||||
span,
|
||||
mode,
|
||||
item_name,
|
||||
steps,
|
||||
opt_simplified_steps);
|
||||
probe_cx.assemble_inherent_candidates();
|
||||
probe_cx.assemble_extension_candidates_for_traits_in_scope(scope_expr_id)?;
|
||||
probe_cx.pick()
|
||||
})
|
||||
}
|
||||
|
||||
Some(steps)
|
||||
}
|
||||
fn create_steps(&self,
|
||||
span: Span,
|
||||
self_ty: Ty<'tcx>)
|
||||
-> Option<Vec<CandidateStep<'tcx>>> {
|
||||
let mut steps = Vec::new();
|
||||
|
||||
let (final_ty, dereferences, _) = self.autoderef(span,
|
||||
self_ty,
|
||||
|| None,
|
||||
UnresolvedTypeAction::Error,
|
||||
NoPreference,
|
||||
|t, d| {
|
||||
steps.push(CandidateStep {
|
||||
self_ty: t,
|
||||
autoderefs: d,
|
||||
unsize: false
|
||||
});
|
||||
None::<()> // keep iterating until we can't anymore
|
||||
});
|
||||
|
||||
match final_ty.sty {
|
||||
ty::TyArray(elem_ty, _) => {
|
||||
steps.push(CandidateStep {
|
||||
self_ty: self.tcx.mk_slice(elem_ty),
|
||||
autoderefs: dereferences,
|
||||
unsize: true
|
||||
});
|
||||
}
|
||||
ty::TyError => return None,
|
||||
_ => (),
|
||||
}
|
||||
|
||||
Some(steps)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
|
||||
|
@ -40,353 +40,353 @@ use super::{MethodError, NoMatchData, CandidateSource};
|
||||
use super::probe::Mode;
|
||||
|
||||
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
fn is_fn_ty(&self, ty: &Ty<'tcx>, span: Span) -> bool {
|
||||
let tcx = self.tcx;
|
||||
match ty.sty {
|
||||
// Not all of these (e.g. unsafe fns) implement FnOnce
|
||||
// so we look for these beforehand
|
||||
ty::TyClosure(..) | ty::TyFnDef(..) | ty::TyFnPtr(_) => true,
|
||||
// If it's not a simple function, look for things which implement FnOnce
|
||||
_ => {
|
||||
if let Ok(fn_once_trait_did) =
|
||||
tcx.lang_items.require(FnOnceTraitLangItem) {
|
||||
let (_, _, opt_is_fn) = self.autoderef(span,
|
||||
ty,
|
||||
|| None,
|
||||
UnresolvedTypeAction::Ignore,
|
||||
LvaluePreference::NoPreference,
|
||||
|ty, _| {
|
||||
self.probe(|_| {
|
||||
let fn_once_substs =
|
||||
Substs::new_trait(vec![self.next_ty_var()], vec![], ty);
|
||||
let trait_ref =
|
||||
ty::TraitRef::new(fn_once_trait_did,
|
||||
tcx.mk_substs(fn_once_substs));
|
||||
let poly_trait_ref = trait_ref.to_poly_trait_ref();
|
||||
let obligation = Obligation::misc(span,
|
||||
self.body_id,
|
||||
poly_trait_ref
|
||||
.to_predicate());
|
||||
let mut selcx = SelectionContext::new(self);
|
||||
fn is_fn_ty(&self, ty: &Ty<'tcx>, span: Span) -> bool {
|
||||
let tcx = self.tcx;
|
||||
match ty.sty {
|
||||
// Not all of these (e.g. unsafe fns) implement FnOnce
|
||||
// so we look for these beforehand
|
||||
ty::TyClosure(..) | ty::TyFnDef(..) | ty::TyFnPtr(_) => true,
|
||||
// If it's not a simple function, look for things which implement FnOnce
|
||||
_ => {
|
||||
if let Ok(fn_once_trait_did) =
|
||||
tcx.lang_items.require(FnOnceTraitLangItem) {
|
||||
let (_, _, opt_is_fn) = self.autoderef(span,
|
||||
ty,
|
||||
|| None,
|
||||
UnresolvedTypeAction::Ignore,
|
||||
LvaluePreference::NoPreference,
|
||||
|ty, _| {
|
||||
self.probe(|_| {
|
||||
let fn_once_substs =
|
||||
Substs::new_trait(vec![self.next_ty_var()], vec![], ty);
|
||||
let trait_ref =
|
||||
ty::TraitRef::new(fn_once_trait_did,
|
||||
tcx.mk_substs(fn_once_substs));
|
||||
let poly_trait_ref = trait_ref.to_poly_trait_ref();
|
||||
let obligation = Obligation::misc(span,
|
||||
self.body_id,
|
||||
poly_trait_ref
|
||||
.to_predicate());
|
||||
let mut selcx = SelectionContext::new(self);
|
||||
|
||||
if selcx.evaluate_obligation(&obligation) {
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
opt_is_fn.is_some()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn report_method_error(&self,
|
||||
span: Span,
|
||||
rcvr_ty: Ty<'tcx>,
|
||||
item_name: ast::Name,
|
||||
rcvr_expr: Option<&hir::Expr>,
|
||||
error: MethodError<'tcx>)
|
||||
{
|
||||
// avoid suggestions when we don't know what's going on.
|
||||
if rcvr_ty.references_error() {
|
||||
return
|
||||
}
|
||||
|
||||
let report_candidates = |err: &mut DiagnosticBuilder,
|
||||
mut sources: Vec<CandidateSource>| {
|
||||
|
||||
sources.sort();
|
||||
sources.dedup();
|
||||
|
||||
for (idx, source) in sources.iter().enumerate() {
|
||||
match *source {
|
||||
CandidateSource::ImplSource(impl_did) => {
|
||||
// Provide the best span we can. Use the item, if local to crate, else
|
||||
// the impl, if local to crate (item may be defaulted), else nothing.
|
||||
let item = self.impl_item(impl_did, item_name)
|
||||
.or_else(|| {
|
||||
self.trait_item(
|
||||
self.tcx.impl_trait_ref(impl_did).unwrap().def_id,
|
||||
|
||||
item_name
|
||||
)
|
||||
}).unwrap();
|
||||
let note_span = self.tcx.map.span_if_local(item.def_id()).or_else(|| {
|
||||
self.tcx.map.span_if_local(impl_did)
|
||||
if selcx.evaluate_obligation(&obligation) {
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
let impl_ty = self.impl_self_ty(span, impl_did).ty;
|
||||
|
||||
let insertion = match self.tcx.impl_trait_ref(impl_did) {
|
||||
None => format!(""),
|
||||
Some(trait_ref) => {
|
||||
format!(" of the trait `{}`",
|
||||
self.tcx.item_path_str(trait_ref.def_id))
|
||||
}
|
||||
};
|
||||
|
||||
let note_str = format!("candidate #{} is defined in an impl{} \
|
||||
for the type `{}`",
|
||||
idx + 1,
|
||||
insertion,
|
||||
impl_ty);
|
||||
if let Some(note_span) = note_span {
|
||||
// We have a span pointing to the method. Show note with snippet.
|
||||
err.span_note(note_span, ¬e_str);
|
||||
} else {
|
||||
err.note(¬e_str);
|
||||
}
|
||||
}
|
||||
CandidateSource::TraitSource(trait_did) => {
|
||||
let item = self.trait_item(trait_did, item_name).unwrap();
|
||||
let item_span = self.tcx.map.def_id_span(item.def_id(), span);
|
||||
span_note!(err, item_span,
|
||||
"candidate #{} is defined in the trait `{}`",
|
||||
idx + 1,
|
||||
self.tcx.item_path_str(trait_did));
|
||||
opt_is_fn.is_some()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match error {
|
||||
MethodError::NoMatch(NoMatchData { static_candidates: static_sources,
|
||||
unsatisfied_predicates,
|
||||
out_of_scope_traits,
|
||||
mode, .. }) => {
|
||||
let tcx = self.tcx;
|
||||
|
||||
let mut err = self.type_error_struct(
|
||||
span,
|
||||
|actual| {
|
||||
format!("no {} named `{}` found for type `{}` \
|
||||
in the current scope",
|
||||
if mode == Mode::MethodCall { "method" }
|
||||
else { "associated item" },
|
||||
item_name,
|
||||
actual)
|
||||
},
|
||||
rcvr_ty,
|
||||
None);
|
||||
|
||||
// If the item has the name of a field, give a help note
|
||||
if let (&ty::TyStruct(def, substs), Some(expr)) = (&rcvr_ty.sty, rcvr_expr) {
|
||||
if let Some(field) = def.struct_variant().find_field_named(item_name) {
|
||||
let expr_string = match tcx.sess.codemap().span_to_snippet(expr.span) {
|
||||
Ok(expr_string) => expr_string,
|
||||
_ => "s".into() // Default to a generic placeholder for the
|
||||
// expression when we can't generate a string
|
||||
// snippet
|
||||
};
|
||||
|
||||
let field_ty = field.ty(tcx, substs);
|
||||
|
||||
if self.is_fn_ty(&field_ty, span) {
|
||||
err.span_note(span,
|
||||
&format!("use `({0}.{1})(...)` if you meant to call \
|
||||
the function stored in the `{1}` field",
|
||||
expr_string, item_name));
|
||||
} else {
|
||||
err.span_note(span, &format!("did you mean to write `{0}.{1}`?",
|
||||
expr_string, item_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.is_fn_ty(&rcvr_ty, span) {
|
||||
macro_rules! report_function {
|
||||
($span:expr, $name:expr) => {
|
||||
err.note(&format!("{} is a function, perhaps you wish to call it",
|
||||
$name));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(expr) = rcvr_expr {
|
||||
if let Ok (expr_string) = tcx.sess.codemap().span_to_snippet(expr.span) {
|
||||
report_function!(expr.span, expr_string);
|
||||
}
|
||||
else if let Expr_::ExprPath(_, path) = expr.node.clone() {
|
||||
if let Some(segment) = path.segments.last() {
|
||||
report_function!(expr.span, segment.identifier.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !static_sources.is_empty() {
|
||||
err.note(
|
||||
"found the following associated functions; to be used as \
|
||||
methods, functions must have a `self` parameter");
|
||||
|
||||
report_candidates(&mut err, static_sources);
|
||||
}
|
||||
|
||||
if !unsatisfied_predicates.is_empty() {
|
||||
let bound_list = unsatisfied_predicates.iter()
|
||||
.map(|p| format!("`{} : {}`",
|
||||
p.self_ty(),
|
||||
p))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
err.note(
|
||||
&format!("the method `{}` exists but the \
|
||||
following trait bounds were not satisfied: {}",
|
||||
item_name,
|
||||
bound_list));
|
||||
}
|
||||
|
||||
self.suggest_traits_to_import(&mut err, span, rcvr_ty, item_name,
|
||||
rcvr_expr, out_of_scope_traits);
|
||||
err.emit();
|
||||
}
|
||||
|
||||
MethodError::Ambiguity(sources) => {
|
||||
let mut err = struct_span_err!(self.sess(), span, E0034,
|
||||
"multiple applicable items in scope");
|
||||
|
||||
report_candidates(&mut err, sources);
|
||||
err.emit();
|
||||
}
|
||||
|
||||
MethodError::ClosureAmbiguity(trait_def_id) => {
|
||||
let msg = 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",
|
||||
item_name,
|
||||
self.tcx.item_path_str(trait_def_id));
|
||||
let msg = if let Some(callee) = rcvr_expr {
|
||||
format!("{}; use overloaded call notation instead (e.g., `{}()`)",
|
||||
msg, pprust::expr_to_string(callee))
|
||||
} else {
|
||||
msg
|
||||
};
|
||||
self.sess().span_err(span, &msg);
|
||||
}
|
||||
|
||||
MethodError::PrivateMatch(def) => {
|
||||
let msg = format!("{} `{}` is private", def.kind_name(), item_name);
|
||||
self.tcx.sess.span_err(span, &msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn report_method_error(&self,
|
||||
span: Span,
|
||||
rcvr_ty: Ty<'tcx>,
|
||||
item_name: ast::Name,
|
||||
rcvr_expr: Option<&hir::Expr>,
|
||||
error: MethodError<'tcx>)
|
||||
{
|
||||
// avoid suggestions when we don't know what's going on.
|
||||
if rcvr_ty.references_error() {
|
||||
return
|
||||
}
|
||||
|
||||
fn suggest_traits_to_import(&self,
|
||||
err: &mut DiagnosticBuilder,
|
||||
let report_candidates = |err: &mut DiagnosticBuilder,
|
||||
mut sources: Vec<CandidateSource>| {
|
||||
|
||||
sources.sort();
|
||||
sources.dedup();
|
||||
|
||||
for (idx, source) in sources.iter().enumerate() {
|
||||
match *source {
|
||||
CandidateSource::ImplSource(impl_did) => {
|
||||
// Provide the best span we can. Use the item, if local to crate, else
|
||||
// the impl, if local to crate (item may be defaulted), else nothing.
|
||||
let item = self.impl_item(impl_did, item_name)
|
||||
.or_else(|| {
|
||||
self.trait_item(
|
||||
self.tcx.impl_trait_ref(impl_did).unwrap().def_id,
|
||||
|
||||
item_name
|
||||
)
|
||||
}).unwrap();
|
||||
let note_span = self.tcx.map.span_if_local(item.def_id()).or_else(|| {
|
||||
self.tcx.map.span_if_local(impl_did)
|
||||
});
|
||||
|
||||
let impl_ty = self.impl_self_ty(span, impl_did).ty;
|
||||
|
||||
let insertion = match self.tcx.impl_trait_ref(impl_did) {
|
||||
None => format!(""),
|
||||
Some(trait_ref) => {
|
||||
format!(" of the trait `{}`",
|
||||
self.tcx.item_path_str(trait_ref.def_id))
|
||||
}
|
||||
};
|
||||
|
||||
let note_str = format!("candidate #{} is defined in an impl{} \
|
||||
for the type `{}`",
|
||||
idx + 1,
|
||||
insertion,
|
||||
impl_ty);
|
||||
if let Some(note_span) = note_span {
|
||||
// We have a span pointing to the method. Show note with snippet.
|
||||
err.span_note(note_span, ¬e_str);
|
||||
} else {
|
||||
err.note(¬e_str);
|
||||
}
|
||||
}
|
||||
CandidateSource::TraitSource(trait_did) => {
|
||||
let item = self.trait_item(trait_did, item_name).unwrap();
|
||||
let item_span = self.tcx.map.def_id_span(item.def_id(), span);
|
||||
span_note!(err, item_span,
|
||||
"candidate #{} is defined in the trait `{}`",
|
||||
idx + 1,
|
||||
self.tcx.item_path_str(trait_did));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match error {
|
||||
MethodError::NoMatch(NoMatchData { static_candidates: static_sources,
|
||||
unsatisfied_predicates,
|
||||
out_of_scope_traits,
|
||||
mode, .. }) => {
|
||||
let tcx = self.tcx;
|
||||
|
||||
let mut err = self.type_error_struct(
|
||||
span,
|
||||
|actual| {
|
||||
format!("no {} named `{}` found for type `{}` \
|
||||
in the current scope",
|
||||
if mode == Mode::MethodCall { "method" }
|
||||
else { "associated item" },
|
||||
item_name,
|
||||
actual)
|
||||
},
|
||||
rcvr_ty,
|
||||
None);
|
||||
|
||||
// If the item has the name of a field, give a help note
|
||||
if let (&ty::TyStruct(def, substs), Some(expr)) = (&rcvr_ty.sty, rcvr_expr) {
|
||||
if let Some(field) = def.struct_variant().find_field_named(item_name) {
|
||||
let expr_string = match tcx.sess.codemap().span_to_snippet(expr.span) {
|
||||
Ok(expr_string) => expr_string,
|
||||
_ => "s".into() // Default to a generic placeholder for the
|
||||
// expression when we can't generate a string
|
||||
// snippet
|
||||
};
|
||||
|
||||
let field_ty = field.ty(tcx, substs);
|
||||
|
||||
if self.is_fn_ty(&field_ty, span) {
|
||||
err.span_note(span,
|
||||
&format!("use `({0}.{1})(...)` if you meant to call \
|
||||
the function stored in the `{1}` field",
|
||||
expr_string, item_name));
|
||||
} else {
|
||||
err.span_note(span, &format!("did you mean to write `{0}.{1}`?",
|
||||
expr_string, item_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.is_fn_ty(&rcvr_ty, span) {
|
||||
macro_rules! report_function {
|
||||
($span:expr, $name:expr) => {
|
||||
err.note(&format!("{} is a function, perhaps you wish to call it",
|
||||
$name));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(expr) = rcvr_expr {
|
||||
if let Ok (expr_string) = tcx.sess.codemap().span_to_snippet(expr.span) {
|
||||
report_function!(expr.span, expr_string);
|
||||
}
|
||||
else if let Expr_::ExprPath(_, path) = expr.node.clone() {
|
||||
if let Some(segment) = path.segments.last() {
|
||||
report_function!(expr.span, segment.identifier.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !static_sources.is_empty() {
|
||||
err.note(
|
||||
"found the following associated functions; to be used as \
|
||||
methods, functions must have a `self` parameter");
|
||||
|
||||
report_candidates(&mut err, static_sources);
|
||||
}
|
||||
|
||||
if !unsatisfied_predicates.is_empty() {
|
||||
let bound_list = unsatisfied_predicates.iter()
|
||||
.map(|p| format!("`{} : {}`",
|
||||
p.self_ty(),
|
||||
p))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
err.note(
|
||||
&format!("the method `{}` exists but the \
|
||||
following trait bounds were not satisfied: {}",
|
||||
item_name,
|
||||
bound_list));
|
||||
}
|
||||
|
||||
self.suggest_traits_to_import(&mut err, span, rcvr_ty, item_name,
|
||||
rcvr_expr, out_of_scope_traits);
|
||||
err.emit();
|
||||
}
|
||||
|
||||
MethodError::Ambiguity(sources) => {
|
||||
let mut err = struct_span_err!(self.sess(), span, E0034,
|
||||
"multiple applicable items in scope");
|
||||
|
||||
report_candidates(&mut err, sources);
|
||||
err.emit();
|
||||
}
|
||||
|
||||
MethodError::ClosureAmbiguity(trait_def_id) => {
|
||||
let msg = 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",
|
||||
item_name,
|
||||
self.tcx.item_path_str(trait_def_id));
|
||||
let msg = if let Some(callee) = rcvr_expr {
|
||||
format!("{}; use overloaded call notation instead (e.g., `{}()`)",
|
||||
msg, pprust::expr_to_string(callee))
|
||||
} else {
|
||||
msg
|
||||
};
|
||||
self.sess().span_err(span, &msg);
|
||||
}
|
||||
|
||||
MethodError::PrivateMatch(def) => {
|
||||
let msg = format!("{} `{}` is private", def.kind_name(), item_name);
|
||||
self.tcx.sess.span_err(span, &msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn suggest_traits_to_import(&self,
|
||||
err: &mut DiagnosticBuilder,
|
||||
span: Span,
|
||||
rcvr_ty: Ty<'tcx>,
|
||||
item_name: ast::Name,
|
||||
rcvr_expr: Option<&hir::Expr>,
|
||||
valid_out_of_scope_traits: Vec<DefId>)
|
||||
{
|
||||
if !valid_out_of_scope_traits.is_empty() {
|
||||
let mut candidates = valid_out_of_scope_traits;
|
||||
candidates.sort();
|
||||
candidates.dedup();
|
||||
let msg = format!(
|
||||
"items from traits can only be used if the trait is in scope; \
|
||||
the following {traits_are} implemented but not in scope, \
|
||||
perhaps add a `use` for {one_of_them}:",
|
||||
traits_are = if candidates.len() == 1 {"trait is"} else {"traits are"},
|
||||
one_of_them = if candidates.len() == 1 {"it"} else {"one of them"});
|
||||
|
||||
err.help(&msg[..]);
|
||||
|
||||
for (i, trait_did) in candidates.iter().enumerate() {
|
||||
err.help(&format!("candidate #{}: `use {}`",
|
||||
i + 1,
|
||||
self.tcx.item_path_str(*trait_did)));
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
let type_is_local = self.type_derefs_to_local(span, rcvr_ty, rcvr_expr);
|
||||
|
||||
// there's no implemented traits, so lets suggest some traits to
|
||||
// implement, by finding ones that have the item name, and are
|
||||
// legal to implement.
|
||||
let mut candidates = all_traits(self.ccx)
|
||||
.filter(|info| {
|
||||
// we approximate the coherence rules to only suggest
|
||||
// traits that are legal to implement by requiring that
|
||||
// either the type or trait is local. Multidispatch means
|
||||
// this isn't perfect (that is, there are cases when
|
||||
// implementing a trait would be legal but is rejected
|
||||
// here).
|
||||
(type_is_local || info.def_id.is_local())
|
||||
&& self.trait_item(info.def_id, item_name).is_some()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if !candidates.is_empty() {
|
||||
// sort from most relevant to least relevant
|
||||
candidates.sort_by(|a, b| a.cmp(b).reverse());
|
||||
candidates.dedup();
|
||||
|
||||
// FIXME #21673 this help message could be tuned to the case
|
||||
// of a type parameter: suggest adding a trait bound rather
|
||||
// than implementing.
|
||||
let msg = format!(
|
||||
"items from traits can only be used if the trait is implemented and in scope; \
|
||||
the following {traits_define} an item `{name}`, \
|
||||
perhaps you need to implement {one_of_them}:",
|
||||
traits_define = if candidates.len() == 1 {"trait defines"} else {"traits define"},
|
||||
one_of_them = if candidates.len() == 1 {"it"} else {"one of them"},
|
||||
name = item_name);
|
||||
|
||||
err.help(&msg[..]);
|
||||
|
||||
for (i, trait_info) in candidates.iter().enumerate() {
|
||||
err.help(&format!("candidate #{}: `{}`",
|
||||
i + 1,
|
||||
self.tcx.item_path_str(trait_info.def_id)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether there is a local type somewhere in the chain of
|
||||
/// autoderefs of `rcvr_ty`.
|
||||
fn type_derefs_to_local(&self,
|
||||
span: Span,
|
||||
rcvr_ty: Ty<'tcx>,
|
||||
item_name: ast::Name,
|
||||
rcvr_expr: Option<&hir::Expr>,
|
||||
valid_out_of_scope_traits: Vec<DefId>)
|
||||
{
|
||||
if !valid_out_of_scope_traits.is_empty() {
|
||||
let mut candidates = valid_out_of_scope_traits;
|
||||
candidates.sort();
|
||||
candidates.dedup();
|
||||
let msg = format!(
|
||||
"items from traits can only be used if the trait is in scope; \
|
||||
the following {traits_are} implemented but not in scope, \
|
||||
perhaps add a `use` for {one_of_them}:",
|
||||
traits_are = if candidates.len() == 1 {"trait is"} else {"traits are"},
|
||||
one_of_them = if candidates.len() == 1 {"it"} else {"one of them"});
|
||||
rcvr_expr: Option<&hir::Expr>) -> bool {
|
||||
fn is_local(ty: Ty) -> bool {
|
||||
match ty.sty {
|
||||
ty::TyEnum(def, _) | ty::TyStruct(def, _) => def.did.is_local(),
|
||||
|
||||
err.help(&msg[..]);
|
||||
ty::TyTrait(ref tr) => tr.principal_def_id().is_local(),
|
||||
|
||||
for (i, trait_did) in candidates.iter().enumerate() {
|
||||
err.help(&format!("candidate #{}: `use {}`",
|
||||
i + 1,
|
||||
self.tcx.item_path_str(*trait_did)));
|
||||
ty::TyParam(_) => true,
|
||||
|
||||
// everything else (primitive types etc.) is effectively
|
||||
// non-local (there are "edge" cases, e.g. (LocalType,), but
|
||||
// the noise from these sort of types is usually just really
|
||||
// annoying, rather than any sort of help).
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
let type_is_local = self.type_derefs_to_local(span, rcvr_ty, rcvr_expr);
|
||||
|
||||
// there's no implemented traits, so lets suggest some traits to
|
||||
// implement, by finding ones that have the item name, and are
|
||||
// legal to implement.
|
||||
let mut candidates = all_traits(self.ccx)
|
||||
.filter(|info| {
|
||||
// we approximate the coherence rules to only suggest
|
||||
// traits that are legal to implement by requiring that
|
||||
// either the type or trait is local. Multidispatch means
|
||||
// this isn't perfect (that is, there are cases when
|
||||
// implementing a trait would be legal but is rejected
|
||||
// here).
|
||||
(type_is_local || info.def_id.is_local())
|
||||
&& self.trait_item(info.def_id, item_name).is_some()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if !candidates.is_empty() {
|
||||
// sort from most relevant to least relevant
|
||||
candidates.sort_by(|a, b| a.cmp(b).reverse());
|
||||
candidates.dedup();
|
||||
|
||||
// FIXME #21673 this help message could be tuned to the case
|
||||
// of a type parameter: suggest adding a trait bound rather
|
||||
// than implementing.
|
||||
let msg = format!(
|
||||
"items from traits can only be used if the trait is implemented and in scope; \
|
||||
the following {traits_define} an item `{name}`, \
|
||||
perhaps you need to implement {one_of_them}:",
|
||||
traits_define = if candidates.len() == 1 {"trait defines"} else {"traits define"},
|
||||
one_of_them = if candidates.len() == 1 {"it"} else {"one of them"},
|
||||
name = item_name);
|
||||
|
||||
err.help(&msg[..]);
|
||||
|
||||
for (i, trait_info) in candidates.iter().enumerate() {
|
||||
err.help(&format!("candidate #{}: `{}`",
|
||||
i + 1,
|
||||
self.tcx.item_path_str(trait_info.def_id)));
|
||||
// This occurs for UFCS desugaring of `T::method`, where there is no
|
||||
// receiver expression for the method call, and thus no autoderef.
|
||||
if rcvr_expr.is_none() {
|
||||
return is_local(self.resolve_type_vars_with_obligations(rcvr_ty));
|
||||
}
|
||||
|
||||
self.autoderef(span, rcvr_ty, || None,
|
||||
check::UnresolvedTypeAction::Ignore, ty::NoPreference,
|
||||
|ty, _| {
|
||||
if is_local(ty) {
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}).2.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether there is a local type somewhere in the chain of
|
||||
/// autoderefs of `rcvr_ty`.
|
||||
fn type_derefs_to_local(&self,
|
||||
span: Span,
|
||||
rcvr_ty: Ty<'tcx>,
|
||||
rcvr_expr: Option<&hir::Expr>) -> bool {
|
||||
fn is_local(ty: Ty) -> bool {
|
||||
match ty.sty {
|
||||
ty::TyEnum(def, _) | ty::TyStruct(def, _) => def.did.is_local(),
|
||||
|
||||
ty::TyTrait(ref tr) => tr.principal_def_id().is_local(),
|
||||
|
||||
ty::TyParam(_) => true,
|
||||
|
||||
// everything else (primitive types etc.) is effectively
|
||||
// non-local (there are "edge" cases, e.g. (LocalType,), but
|
||||
// the noise from these sort of types is usually just really
|
||||
// annoying, rather than any sort of help).
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
// This occurs for UFCS desugaring of `T::method`, where there is no
|
||||
// receiver expression for the method call, and thus no autoderef.
|
||||
if rcvr_expr.is_none() {
|
||||
return is_local(self.resolve_type_vars_with_obligations(rcvr_ty));
|
||||
}
|
||||
|
||||
self.autoderef(span, rcvr_ty, || None,
|
||||
check::UnresolvedTypeAction::Ignore, ty::NoPreference,
|
||||
|ty, _| {
|
||||
if is_local(ty) {
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}).2.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
pub type AllTraitsVec = Vec<TraitInfo>;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -18,330 +18,335 @@ use syntax::parse::token;
|
||||
use rustc::hir;
|
||||
|
||||
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
/// Check a `a <op>= b`
|
||||
pub fn check_binop_assign(&self,
|
||||
expr: &'gcx hir::Expr,
|
||||
op: hir::BinOp,
|
||||
lhs_expr: &'gcx hir::Expr,
|
||||
rhs_expr: &'gcx hir::Expr)
|
||||
{
|
||||
self.check_expr_with_lvalue_pref(lhs_expr, PreferMutLvalue);
|
||||
/// Check a `a <op>= b`
|
||||
pub fn check_binop_assign(&self,
|
||||
expr: &'gcx hir::Expr,
|
||||
op: hir::BinOp,
|
||||
lhs_expr: &'gcx hir::Expr,
|
||||
rhs_expr: &'gcx hir::Expr)
|
||||
{
|
||||
self.check_expr_with_lvalue_pref(lhs_expr, PreferMutLvalue);
|
||||
|
||||
let lhs_ty = self.resolve_type_vars_with_obligations(self.expr_ty(lhs_expr));
|
||||
let (rhs_ty, return_ty) =
|
||||
self.check_overloaded_binop(expr, lhs_expr, lhs_ty, rhs_expr, op, IsAssign::Yes);
|
||||
let rhs_ty = self.resolve_type_vars_with_obligations(rhs_ty);
|
||||
|
||||
if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) {
|
||||
self.enforce_builtin_binop_types(lhs_expr, lhs_ty, rhs_expr, rhs_ty, op);
|
||||
self.write_nil(expr.id);
|
||||
} else {
|
||||
self.write_ty(expr.id, return_ty);
|
||||
}
|
||||
|
||||
let tcx = self.tcx;
|
||||
if !tcx.expr_is_lval(lhs_expr) {
|
||||
span_err!(tcx.sess, lhs_expr.span, E0067, "invalid left-hand side expression");
|
||||
}
|
||||
}
|
||||
|
||||
/// Check a potentially overloaded binary operator.
|
||||
pub fn check_binop(&self,
|
||||
expr: &'gcx hir::Expr,
|
||||
op: hir::BinOp,
|
||||
lhs_expr: &'gcx hir::Expr,
|
||||
rhs_expr: &'gcx hir::Expr)
|
||||
{
|
||||
let tcx = self.tcx;
|
||||
|
||||
debug!("check_binop(expr.id={}, expr={:?}, op={:?}, lhs_expr={:?}, rhs_expr={:?})",
|
||||
expr.id,
|
||||
expr,
|
||||
op,
|
||||
lhs_expr,
|
||||
rhs_expr);
|
||||
|
||||
self.check_expr(lhs_expr);
|
||||
let lhs_ty = self.resolve_type_vars_with_obligations(self.expr_ty(lhs_expr));
|
||||
|
||||
match BinOpCategory::from(op) {
|
||||
BinOpCategory::Shortcircuit => {
|
||||
// && and || are a simple case.
|
||||
self.demand_suptype(lhs_expr.span, tcx.mk_bool(), lhs_ty);
|
||||
self.check_expr_coercable_to_type(rhs_expr, tcx.mk_bool());
|
||||
self.write_ty(expr.id, tcx.mk_bool());
|
||||
}
|
||||
_ => {
|
||||
// Otherwise, we always treat operators as if they are
|
||||
// overloaded. This is the way to be most flexible w/r/t
|
||||
// types that get inferred.
|
||||
let (rhs_ty, return_ty) =
|
||||
self.check_overloaded_binop(expr, lhs_expr, lhs_ty, rhs_expr, op, IsAssign::No);
|
||||
|
||||
// Supply type inference hints if relevant. Probably these
|
||||
// hints should be enforced during select as part of the
|
||||
// `consider_unification_despite_ambiguity` routine, but this
|
||||
// more convenient for now.
|
||||
//
|
||||
// The basic idea is to help type inference by taking
|
||||
// advantage of things we know about how the impls for
|
||||
// scalar types are arranged. This is important in a
|
||||
// scenario like `1_u32 << 2`, because it lets us quickly
|
||||
// deduce that the result type should be `u32`, even
|
||||
// though we don't know yet what type 2 has and hence
|
||||
// can't pin this down to a specific impl.
|
||||
let rhs_ty = self.resolve_type_vars_with_obligations(rhs_ty);
|
||||
if
|
||||
!lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() &&
|
||||
is_builtin_binop(lhs_ty, rhs_ty, op)
|
||||
{
|
||||
let builtin_return_ty =
|
||||
self.enforce_builtin_binop_types(lhs_expr, lhs_ty, rhs_expr, rhs_ty, op);
|
||||
self.demand_suptype(expr.span, builtin_return_ty, return_ty);
|
||||
}
|
||||
let lhs_ty = self.resolve_type_vars_with_obligations(self.expr_ty(lhs_expr));
|
||||
let (rhs_ty, return_ty) =
|
||||
self.check_overloaded_binop(expr, lhs_expr, lhs_ty, rhs_expr, op, IsAssign::Yes);
|
||||
let rhs_ty = self.resolve_type_vars_with_obligations(rhs_ty);
|
||||
|
||||
if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) {
|
||||
self.enforce_builtin_binop_types(lhs_expr, lhs_ty, rhs_expr, rhs_ty, op);
|
||||
self.write_nil(expr.id);
|
||||
} else {
|
||||
self.write_ty(expr.id, return_ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn enforce_builtin_binop_types(&self,
|
||||
lhs_expr: &'gcx hir::Expr,
|
||||
lhs_ty: Ty<'tcx>,
|
||||
rhs_expr: &'gcx hir::Expr,
|
||||
rhs_ty: Ty<'tcx>,
|
||||
op: hir::BinOp)
|
||||
-> Ty<'tcx>
|
||||
{
|
||||
debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, op));
|
||||
|
||||
let tcx = self.tcx;
|
||||
match BinOpCategory::from(op) {
|
||||
BinOpCategory::Shortcircuit => {
|
||||
self.demand_suptype(lhs_expr.span, tcx.mk_bool(), lhs_ty);
|
||||
self.demand_suptype(rhs_expr.span, tcx.mk_bool(), rhs_ty);
|
||||
tcx.mk_bool()
|
||||
}
|
||||
|
||||
BinOpCategory::Shift => {
|
||||
// result type is same as LHS always
|
||||
lhs_ty
|
||||
}
|
||||
|
||||
BinOpCategory::Math |
|
||||
BinOpCategory::Bitwise => {
|
||||
// both LHS and RHS and result will have the same type
|
||||
self.demand_suptype(rhs_expr.span, lhs_ty, rhs_ty);
|
||||
lhs_ty
|
||||
}
|
||||
|
||||
BinOpCategory::Comparison => {
|
||||
// both LHS and RHS and result will have the same type
|
||||
self.demand_suptype(rhs_expr.span, lhs_ty, rhs_ty);
|
||||
tcx.mk_bool()
|
||||
let tcx = self.tcx;
|
||||
if !tcx.expr_is_lval(lhs_expr) {
|
||||
span_err!(tcx.sess, lhs_expr.span, E0067, "invalid left-hand side expression");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_overloaded_binop(&self,
|
||||
expr: &'gcx hir::Expr,
|
||||
lhs_expr: &'gcx hir::Expr,
|
||||
lhs_ty: Ty<'tcx>,
|
||||
rhs_expr: &'gcx hir::Expr,
|
||||
op: hir::BinOp,
|
||||
is_assign: IsAssign)
|
||||
-> (Ty<'tcx>, Ty<'tcx>)
|
||||
{
|
||||
debug!("check_overloaded_binop(expr.id={}, lhs_ty={:?}, is_assign={:?})",
|
||||
expr.id,
|
||||
lhs_ty,
|
||||
is_assign);
|
||||
/// Check a potentially overloaded binary operator.
|
||||
pub fn check_binop(&self,
|
||||
expr: &'gcx hir::Expr,
|
||||
op: hir::BinOp,
|
||||
lhs_expr: &'gcx hir::Expr,
|
||||
rhs_expr: &'gcx hir::Expr)
|
||||
{
|
||||
let tcx = self.tcx;
|
||||
|
||||
let (name, trait_def_id) = self.name_and_trait_def_id(op, is_assign);
|
||||
debug!("check_binop(expr.id={}, expr={:?}, op={:?}, lhs_expr={:?}, rhs_expr={:?})",
|
||||
expr.id,
|
||||
expr,
|
||||
op,
|
||||
lhs_expr,
|
||||
rhs_expr);
|
||||
|
||||
// NB: As we have not yet type-checked the RHS, we don't have the
|
||||
// type at hand. Make a variable to represent it. The whole reason
|
||||
// for this indirection is so that, below, we can check the expr
|
||||
// using this variable as the expected type, which sometimes lets
|
||||
// us do better coercions than we would be able to do otherwise,
|
||||
// particularly for things like `String + &String`.
|
||||
let rhs_ty_var = self.next_ty_var();
|
||||
self.check_expr(lhs_expr);
|
||||
let lhs_ty = self.resolve_type_vars_with_obligations(self.expr_ty(lhs_expr));
|
||||
|
||||
let return_ty = match self.lookup_op_method(expr, lhs_ty, vec![rhs_ty_var],
|
||||
token::intern(name), trait_def_id,
|
||||
lhs_expr) {
|
||||
Ok(return_ty) => return_ty,
|
||||
Err(()) => {
|
||||
// error types are considered "builtin"
|
||||
if !lhs_ty.references_error() {
|
||||
if let IsAssign::Yes = is_assign {
|
||||
span_err!(self.tcx.sess, lhs_expr.span, E0368,
|
||||
"binary assignment operation `{}=` cannot be applied to type `{}`",
|
||||
op.node.as_str(),
|
||||
lhs_ty);
|
||||
} else {
|
||||
let mut err = struct_span_err!(self.tcx.sess, lhs_expr.span, E0369,
|
||||
"binary operation `{}` cannot be applied to type `{}`",
|
||||
op.node.as_str(),
|
||||
lhs_ty);
|
||||
let missing_trait = match op.node {
|
||||
hir::BiAdd => Some("std::ops::Add"),
|
||||
hir::BiSub => Some("std::ops::Sub"),
|
||||
hir::BiMul => Some("std::ops::Mul"),
|
||||
hir::BiDiv => Some("std::ops::Div"),
|
||||
hir::BiRem => Some("std::ops::Rem"),
|
||||
hir::BiBitAnd => Some("std::ops::BitAnd"),
|
||||
hir::BiBitOr => Some("std::ops::BitOr"),
|
||||
hir::BiShl => Some("std::ops::Shl"),
|
||||
hir::BiShr => Some("std::ops::Shr"),
|
||||
hir::BiEq | hir::BiNe => Some("std::cmp::PartialEq"),
|
||||
hir::BiLt | hir::BiLe | hir::BiGt | hir::BiGe =>
|
||||
Some("std::cmp::PartialOrd"),
|
||||
_ => None
|
||||
};
|
||||
match BinOpCategory::from(op) {
|
||||
BinOpCategory::Shortcircuit => {
|
||||
// && and || are a simple case.
|
||||
self.demand_suptype(lhs_expr.span, tcx.mk_bool(), lhs_ty);
|
||||
self.check_expr_coercable_to_type(rhs_expr, tcx.mk_bool());
|
||||
self.write_ty(expr.id, tcx.mk_bool());
|
||||
}
|
||||
_ => {
|
||||
// Otherwise, we always treat operators as if they are
|
||||
// overloaded. This is the way to be most flexible w/r/t
|
||||
// types that get inferred.
|
||||
let (rhs_ty, return_ty) =
|
||||
self.check_overloaded_binop(expr, lhs_expr, lhs_ty,
|
||||
rhs_expr, op, IsAssign::No);
|
||||
|
||||
if let Some(missing_trait) = missing_trait {
|
||||
span_note!(&mut err, lhs_expr.span,
|
||||
"an implementation of `{}` might be missing for `{}`",
|
||||
missing_trait, lhs_ty);
|
||||
// Supply type inference hints if relevant. Probably these
|
||||
// hints should be enforced during select as part of the
|
||||
// `consider_unification_despite_ambiguity` routine, but this
|
||||
// more convenient for now.
|
||||
//
|
||||
// The basic idea is to help type inference by taking
|
||||
// advantage of things we know about how the impls for
|
||||
// scalar types are arranged. This is important in a
|
||||
// scenario like `1_u32 << 2`, because it lets us quickly
|
||||
// deduce that the result type should be `u32`, even
|
||||
// though we don't know yet what type 2 has and hence
|
||||
// can't pin this down to a specific impl.
|
||||
let rhs_ty = self.resolve_type_vars_with_obligations(rhs_ty);
|
||||
if
|
||||
!lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() &&
|
||||
is_builtin_binop(lhs_ty, rhs_ty, op)
|
||||
{
|
||||
let builtin_return_ty =
|
||||
self.enforce_builtin_binop_types(lhs_expr, lhs_ty, rhs_expr, rhs_ty, op);
|
||||
self.demand_suptype(expr.span, builtin_return_ty, return_ty);
|
||||
}
|
||||
|
||||
self.write_ty(expr.id, return_ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn enforce_builtin_binop_types(&self,
|
||||
lhs_expr: &'gcx hir::Expr,
|
||||
lhs_ty: Ty<'tcx>,
|
||||
rhs_expr: &'gcx hir::Expr,
|
||||
rhs_ty: Ty<'tcx>,
|
||||
op: hir::BinOp)
|
||||
-> Ty<'tcx>
|
||||
{
|
||||
debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, op));
|
||||
|
||||
let tcx = self.tcx;
|
||||
match BinOpCategory::from(op) {
|
||||
BinOpCategory::Shortcircuit => {
|
||||
self.demand_suptype(lhs_expr.span, tcx.mk_bool(), lhs_ty);
|
||||
self.demand_suptype(rhs_expr.span, tcx.mk_bool(), rhs_ty);
|
||||
tcx.mk_bool()
|
||||
}
|
||||
|
||||
BinOpCategory::Shift => {
|
||||
// result type is same as LHS always
|
||||
lhs_ty
|
||||
}
|
||||
|
||||
BinOpCategory::Math |
|
||||
BinOpCategory::Bitwise => {
|
||||
// both LHS and RHS and result will have the same type
|
||||
self.demand_suptype(rhs_expr.span, lhs_ty, rhs_ty);
|
||||
lhs_ty
|
||||
}
|
||||
|
||||
BinOpCategory::Comparison => {
|
||||
// both LHS and RHS and result will have the same type
|
||||
self.demand_suptype(rhs_expr.span, lhs_ty, rhs_ty);
|
||||
tcx.mk_bool()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_overloaded_binop(&self,
|
||||
expr: &'gcx hir::Expr,
|
||||
lhs_expr: &'gcx hir::Expr,
|
||||
lhs_ty: Ty<'tcx>,
|
||||
rhs_expr: &'gcx hir::Expr,
|
||||
op: hir::BinOp,
|
||||
is_assign: IsAssign)
|
||||
-> (Ty<'tcx>, Ty<'tcx>)
|
||||
{
|
||||
debug!("check_overloaded_binop(expr.id={}, lhs_ty={:?}, is_assign={:?})",
|
||||
expr.id,
|
||||
lhs_ty,
|
||||
is_assign);
|
||||
|
||||
let (name, trait_def_id) = self.name_and_trait_def_id(op, is_assign);
|
||||
|
||||
// NB: As we have not yet type-checked the RHS, we don't have the
|
||||
// type at hand. Make a variable to represent it. The whole reason
|
||||
// for this indirection is so that, below, we can check the expr
|
||||
// using this variable as the expected type, which sometimes lets
|
||||
// us do better coercions than we would be able to do otherwise,
|
||||
// particularly for things like `String + &String`.
|
||||
let rhs_ty_var = self.next_ty_var();
|
||||
|
||||
let return_ty = match self.lookup_op_method(expr, lhs_ty, vec![rhs_ty_var],
|
||||
token::intern(name), trait_def_id,
|
||||
lhs_expr) {
|
||||
Ok(return_ty) => return_ty,
|
||||
Err(()) => {
|
||||
// error types are considered "builtin"
|
||||
if !lhs_ty.references_error() {
|
||||
if let IsAssign::Yes = is_assign {
|
||||
span_err!(self.tcx.sess, lhs_expr.span, E0368,
|
||||
"binary assignment operation `{}=` \
|
||||
cannot be applied to type `{}`",
|
||||
op.node.as_str(),
|
||||
lhs_ty);
|
||||
} else {
|
||||
let mut err = struct_span_err!(self.tcx.sess, lhs_expr.span, E0369,
|
||||
"binary operation `{}` cannot be applied to type `{}`",
|
||||
op.node.as_str(),
|
||||
lhs_ty);
|
||||
let missing_trait = match op.node {
|
||||
hir::BiAdd => Some("std::ops::Add"),
|
||||
hir::BiSub => Some("std::ops::Sub"),
|
||||
hir::BiMul => Some("std::ops::Mul"),
|
||||
hir::BiDiv => Some("std::ops::Div"),
|
||||
hir::BiRem => Some("std::ops::Rem"),
|
||||
hir::BiBitAnd => Some("std::ops::BitAnd"),
|
||||
hir::BiBitOr => Some("std::ops::BitOr"),
|
||||
hir::BiShl => Some("std::ops::Shl"),
|
||||
hir::BiShr => Some("std::ops::Shr"),
|
||||
hir::BiEq | hir::BiNe => Some("std::cmp::PartialEq"),
|
||||
hir::BiLt | hir::BiLe | hir::BiGt | hir::BiGe =>
|
||||
Some("std::cmp::PartialOrd"),
|
||||
_ => None
|
||||
};
|
||||
|
||||
if let Some(missing_trait) = missing_trait {
|
||||
span_note!(&mut err, lhs_expr.span,
|
||||
"an implementation of `{}` might be missing for `{}`",
|
||||
missing_trait, lhs_ty);
|
||||
}
|
||||
err.emit();
|
||||
}
|
||||
err.emit();
|
||||
}
|
||||
self.tcx.types.err
|
||||
}
|
||||
};
|
||||
|
||||
// see `NB` above
|
||||
self.check_expr_coercable_to_type(rhs_expr, rhs_ty_var);
|
||||
|
||||
(rhs_ty_var, return_ty)
|
||||
}
|
||||
|
||||
pub fn check_user_unop(&self,
|
||||
op_str: &str,
|
||||
mname: &str,
|
||||
trait_did: Option<DefId>,
|
||||
ex: &'gcx hir::Expr,
|
||||
operand_expr: &'gcx hir::Expr,
|
||||
operand_ty: Ty<'tcx>,
|
||||
op: hir::UnOp)
|
||||
-> Ty<'tcx>
|
||||
{
|
||||
assert!(op.is_by_value());
|
||||
match self.lookup_op_method(ex, operand_ty, vec![],
|
||||
token::intern(mname), trait_did,
|
||||
operand_expr) {
|
||||
Ok(t) => t,
|
||||
Err(()) => {
|
||||
self.type_error_message(ex.span, |actual| {
|
||||
format!("cannot apply unary operator `{}` to type `{}`",
|
||||
op_str, actual)
|
||||
}, operand_ty, None);
|
||||
self.tcx.types.err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn name_and_trait_def_id(&self,
|
||||
op: hir::BinOp,
|
||||
is_assign: IsAssign)
|
||||
-> (&'static str, Option<DefId>) {
|
||||
let lang = &self.tcx.lang_items;
|
||||
|
||||
if let IsAssign::Yes = is_assign {
|
||||
match op.node {
|
||||
hir::BiAdd => ("add_assign", lang.add_assign_trait()),
|
||||
hir::BiSub => ("sub_assign", lang.sub_assign_trait()),
|
||||
hir::BiMul => ("mul_assign", lang.mul_assign_trait()),
|
||||
hir::BiDiv => ("div_assign", lang.div_assign_trait()),
|
||||
hir::BiRem => ("rem_assign", lang.rem_assign_trait()),
|
||||
hir::BiBitXor => ("bitxor_assign", lang.bitxor_assign_trait()),
|
||||
hir::BiBitAnd => ("bitand_assign", lang.bitand_assign_trait()),
|
||||
hir::BiBitOr => ("bitor_assign", lang.bitor_assign_trait()),
|
||||
hir::BiShl => ("shl_assign", lang.shl_assign_trait()),
|
||||
hir::BiShr => ("shr_assign", lang.shr_assign_trait()),
|
||||
hir::BiLt | hir::BiLe |
|
||||
hir::BiGe | hir::BiGt |
|
||||
hir::BiEq | hir::BiNe |
|
||||
hir::BiAnd | hir::BiOr => {
|
||||
span_bug!(op.span,
|
||||
"impossible assignment operation: {}=",
|
||||
op.node.as_str())
|
||||
}
|
||||
}
|
||||
self.tcx.types.err
|
||||
}
|
||||
};
|
||||
|
||||
// see `NB` above
|
||||
self.check_expr_coercable_to_type(rhs_expr, rhs_ty_var);
|
||||
|
||||
(rhs_ty_var, return_ty)
|
||||
}
|
||||
|
||||
pub fn check_user_unop(&self,
|
||||
op_str: &str,
|
||||
mname: &str,
|
||||
trait_did: Option<DefId>,
|
||||
ex: &'gcx hir::Expr,
|
||||
operand_expr: &'gcx hir::Expr,
|
||||
operand_ty: Ty<'tcx>,
|
||||
op: hir::UnOp)
|
||||
-> Ty<'tcx>
|
||||
{
|
||||
assert!(op.is_by_value());
|
||||
match self.lookup_op_method(ex, operand_ty, vec![],
|
||||
token::intern(mname), trait_did,
|
||||
operand_expr) {
|
||||
Ok(t) => t,
|
||||
Err(()) => {
|
||||
self.type_error_message(ex.span, |actual| {
|
||||
format!("cannot apply unary operator `{}` to type `{}`",
|
||||
op_str, actual)
|
||||
}, operand_ty, None);
|
||||
self.tcx.types.err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn name_and_trait_def_id(&self,
|
||||
op: hir::BinOp,
|
||||
is_assign: IsAssign)
|
||||
-> (&'static str, Option<DefId>) {
|
||||
let lang = &self.tcx.lang_items;
|
||||
|
||||
if let IsAssign::Yes = is_assign {
|
||||
match op.node {
|
||||
hir::BiAdd => ("add_assign", lang.add_assign_trait()),
|
||||
hir::BiSub => ("sub_assign", lang.sub_assign_trait()),
|
||||
hir::BiMul => ("mul_assign", lang.mul_assign_trait()),
|
||||
hir::BiDiv => ("div_assign", lang.div_assign_trait()),
|
||||
hir::BiRem => ("rem_assign", lang.rem_assign_trait()),
|
||||
hir::BiBitXor => ("bitxor_assign", lang.bitxor_assign_trait()),
|
||||
hir::BiBitAnd => ("bitand_assign", lang.bitand_assign_trait()),
|
||||
hir::BiBitOr => ("bitor_assign", lang.bitor_assign_trait()),
|
||||
hir::BiShl => ("shl_assign", lang.shl_assign_trait()),
|
||||
hir::BiShr => ("shr_assign", lang.shr_assign_trait()),
|
||||
hir::BiLt | hir::BiLe | hir::BiGe | hir::BiGt | hir::BiEq | hir::BiNe | hir::BiAnd |
|
||||
hir::BiOr => {
|
||||
span_bug!(op.span,
|
||||
"impossible assignment operation: {}=",
|
||||
op.node.as_str())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match op.node {
|
||||
hir::BiAdd => ("add", lang.add_trait()),
|
||||
hir::BiSub => ("sub", lang.sub_trait()),
|
||||
hir::BiMul => ("mul", lang.mul_trait()),
|
||||
hir::BiDiv => ("div", lang.div_trait()),
|
||||
hir::BiRem => ("rem", lang.rem_trait()),
|
||||
hir::BiBitXor => ("bitxor", lang.bitxor_trait()),
|
||||
hir::BiBitAnd => ("bitand", lang.bitand_trait()),
|
||||
hir::BiBitOr => ("bitor", lang.bitor_trait()),
|
||||
hir::BiShl => ("shl", lang.shl_trait()),
|
||||
hir::BiShr => ("shr", lang.shr_trait()),
|
||||
hir::BiLt => ("lt", lang.ord_trait()),
|
||||
hir::BiLe => ("le", lang.ord_trait()),
|
||||
hir::BiGe => ("ge", lang.ord_trait()),
|
||||
hir::BiGt => ("gt", lang.ord_trait()),
|
||||
hir::BiEq => ("eq", lang.eq_trait()),
|
||||
hir::BiNe => ("ne", lang.eq_trait()),
|
||||
hir::BiAnd | hir::BiOr => {
|
||||
span_bug!(op.span, "&& and || are not overloadable")
|
||||
} else {
|
||||
match op.node {
|
||||
hir::BiAdd => ("add", lang.add_trait()),
|
||||
hir::BiSub => ("sub", lang.sub_trait()),
|
||||
hir::BiMul => ("mul", lang.mul_trait()),
|
||||
hir::BiDiv => ("div", lang.div_trait()),
|
||||
hir::BiRem => ("rem", lang.rem_trait()),
|
||||
hir::BiBitXor => ("bitxor", lang.bitxor_trait()),
|
||||
hir::BiBitAnd => ("bitand", lang.bitand_trait()),
|
||||
hir::BiBitOr => ("bitor", lang.bitor_trait()),
|
||||
hir::BiShl => ("shl", lang.shl_trait()),
|
||||
hir::BiShr => ("shr", lang.shr_trait()),
|
||||
hir::BiLt => ("lt", lang.ord_trait()),
|
||||
hir::BiLe => ("le", lang.ord_trait()),
|
||||
hir::BiGe => ("ge", lang.ord_trait()),
|
||||
hir::BiGt => ("gt", lang.ord_trait()),
|
||||
hir::BiEq => ("eq", lang.eq_trait()),
|
||||
hir::BiNe => ("ne", lang.eq_trait()),
|
||||
hir::BiAnd | hir::BiOr => {
|
||||
span_bug!(op.span, "&& and || are not overloadable")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lookup_op_method(&self,
|
||||
expr: &'gcx hir::Expr,
|
||||
lhs_ty: Ty<'tcx>,
|
||||
other_tys: Vec<Ty<'tcx>>,
|
||||
opname: ast::Name,
|
||||
trait_did: Option<DefId>,
|
||||
lhs_expr: &'a hir::Expr)
|
||||
-> Result<Ty<'tcx>,()>
|
||||
{
|
||||
debug!("lookup_op_method(expr={:?}, lhs_ty={:?}, opname={:?}, trait_did={:?}, lhs_expr={:?})",
|
||||
expr,
|
||||
lhs_ty,
|
||||
opname,
|
||||
trait_did,
|
||||
lhs_expr);
|
||||
fn lookup_op_method(&self,
|
||||
expr: &'gcx hir::Expr,
|
||||
lhs_ty: Ty<'tcx>,
|
||||
other_tys: Vec<Ty<'tcx>>,
|
||||
opname: ast::Name,
|
||||
trait_did: Option<DefId>,
|
||||
lhs_expr: &'a hir::Expr)
|
||||
-> Result<Ty<'tcx>,()>
|
||||
{
|
||||
debug!("lookup_op_method(expr={:?}, lhs_ty={:?}, opname={:?}, \
|
||||
trait_did={:?}, lhs_expr={:?})",
|
||||
expr,
|
||||
lhs_ty,
|
||||
opname,
|
||||
trait_did,
|
||||
lhs_expr);
|
||||
|
||||
let method = match trait_did {
|
||||
Some(trait_did) => {
|
||||
self.lookup_method_in_trait_adjusted(expr.span,
|
||||
Some(lhs_expr),
|
||||
opname,
|
||||
trait_did,
|
||||
0,
|
||||
false,
|
||||
lhs_ty,
|
||||
Some(other_tys))
|
||||
}
|
||||
None => None
|
||||
};
|
||||
let method = match trait_did {
|
||||
Some(trait_did) => {
|
||||
self.lookup_method_in_trait_adjusted(expr.span,
|
||||
Some(lhs_expr),
|
||||
opname,
|
||||
trait_did,
|
||||
0,
|
||||
false,
|
||||
lhs_ty,
|
||||
Some(other_tys))
|
||||
}
|
||||
None => None
|
||||
};
|
||||
|
||||
match method {
|
||||
Some(method) => {
|
||||
let method_ty = method.ty;
|
||||
match method {
|
||||
Some(method) => {
|
||||
let method_ty = method.ty;
|
||||
|
||||
// HACK(eddyb) Fully qualified path to work around a resolve bug.
|
||||
let method_call = ::rustc::ty::MethodCall::expr(expr.id);
|
||||
self.tables.borrow_mut().method_map.insert(method_call, method);
|
||||
// HACK(eddyb) Fully qualified path to work around a resolve bug.
|
||||
let method_call = ::rustc::ty::MethodCall::expr(expr.id);
|
||||
self.tables.borrow_mut().method_map.insert(method_call, method);
|
||||
|
||||
// extract return type for method; all late bound regions
|
||||
// should have been instantiated by now
|
||||
let ret_ty = method_ty.fn_ret();
|
||||
Ok(self.tcx.no_late_bound_regions(&ret_ty).unwrap().unwrap())
|
||||
}
|
||||
None => {
|
||||
Err(())
|
||||
// extract return type for method; all late bound regions
|
||||
// should have been instantiated by now
|
||||
let ret_ty = method_ty.fn_ret();
|
||||
Ok(self.tcx.no_late_bound_regions(&ret_ty).unwrap().unwrap())
|
||||
}
|
||||
None => {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Binary operator categories. These categories summarize the behavior
|
||||
// with respect to the builtin operationrs supported.
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -57,29 +57,29 @@ use rustc::hir::intravisit::{self, Visitor};
|
||||
// PUBLIC ENTRY POINTS
|
||||
|
||||
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
pub fn closure_analyze_fn(&self, body: &hir::Block) {
|
||||
let mut seed = SeedBorrowKind::new(self);
|
||||
seed.visit_block(body);
|
||||
let closures_with_inferred_kinds = seed.closures_with_inferred_kinds;
|
||||
pub fn closure_analyze_fn(&self, body: &hir::Block) {
|
||||
let mut seed = SeedBorrowKind::new(self);
|
||||
seed.visit_block(body);
|
||||
let closures_with_inferred_kinds = seed.closures_with_inferred_kinds;
|
||||
|
||||
let mut adjust = AdjustBorrowKind::new(self, &closures_with_inferred_kinds);
|
||||
adjust.visit_block(body);
|
||||
let mut adjust = AdjustBorrowKind::new(self, &closures_with_inferred_kinds);
|
||||
adjust.visit_block(body);
|
||||
|
||||
// it's our job to process these.
|
||||
assert!(self.deferred_call_resolutions.borrow().is_empty());
|
||||
}
|
||||
// it's our job to process these.
|
||||
assert!(self.deferred_call_resolutions.borrow().is_empty());
|
||||
}
|
||||
|
||||
pub fn closure_analyze_const(&self, body: &hir::Expr) {
|
||||
let mut seed = SeedBorrowKind::new(self);
|
||||
seed.visit_expr(body);
|
||||
let closures_with_inferred_kinds = seed.closures_with_inferred_kinds;
|
||||
pub fn closure_analyze_const(&self, body: &hir::Expr) {
|
||||
let mut seed = SeedBorrowKind::new(self);
|
||||
seed.visit_expr(body);
|
||||
let closures_with_inferred_kinds = seed.closures_with_inferred_kinds;
|
||||
|
||||
let mut adjust = AdjustBorrowKind::new(self, &closures_with_inferred_kinds);
|
||||
adjust.visit_expr(body);
|
||||
let mut adjust = AdjustBorrowKind::new(self, &closures_with_inferred_kinds);
|
||||
adjust.visit_expr(body);
|
||||
|
||||
// it's our job to process these.
|
||||
assert!(self.deferred_call_resolutions.borrow().is_empty());
|
||||
}
|
||||
// it's our job to process these.
|
||||
assert!(self.deferred_call_resolutions.borrow().is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
@ -288,7 +288,8 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
|
||||
upvar_id);
|
||||
|
||||
// to move out of an upvar, this must be a FnOnce closure
|
||||
self.adjust_closure_kind(upvar_id.closure_expr_id, ty::ClosureKind::FnOnce);
|
||||
self.adjust_closure_kind(upvar_id.closure_expr_id,
|
||||
ty::ClosureKind::FnOnce);
|
||||
|
||||
let upvar_capture_map =
|
||||
&mut self.fcx.tables.borrow_mut().upvar_capture_map;
|
||||
@ -301,7 +302,8 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
|
||||
// must still adjust the kind of the closure
|
||||
// to be a FnOnce closure to permit moves out
|
||||
// of the environment.
|
||||
self.adjust_closure_kind(upvar_id.closure_expr_id, ty::ClosureKind::FnOnce);
|
||||
self.adjust_closure_kind(upvar_id.closure_expr_id,
|
||||
ty::ClosureKind::FnOnce);
|
||||
}
|
||||
mc::NoteNone => {
|
||||
}
|
||||
@ -423,9 +425,10 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// We infer the borrow_kind with which to borrow upvars in a stack closure. The borrow_kind
|
||||
/// basically follows a lattice of `imm < unique-imm < mut`, moving from left to right as needed
|
||||
/// (but never right to left). Here the argument `mutbl` is the borrow_kind that is required by
|
||||
/// We infer the borrow_kind with which to borrow upvars in a stack closure.
|
||||
/// The borrow_kind basically follows a lattice of `imm < unique-imm < mut`,
|
||||
/// moving from left to right as needed (but never right to left).
|
||||
/// Here the argument `mutbl` is the borrow_kind that is required by
|
||||
/// some particular use.
|
||||
fn adjust_upvar_borrow_kind(&self,
|
||||
upvar_id: ty::UpvarId,
|
||||
|
@ -576,46 +576,46 @@ struct AdtField<'tcx> {
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
fn struct_variant(&self, struct_def: &hir::VariantData) -> AdtVariant<'tcx> {
|
||||
let fields =
|
||||
struct_def.fields().iter()
|
||||
.map(|field| {
|
||||
let field_ty = self.tcx.node_id_to_type(field.id);
|
||||
let field_ty = self.instantiate_type_scheme(field.span,
|
||||
&self.parameter_environment
|
||||
.free_substs,
|
||||
&field_ty);
|
||||
AdtField { ty: field_ty, span: field.span }
|
||||
})
|
||||
.collect();
|
||||
AdtVariant { fields: fields }
|
||||
}
|
||||
fn struct_variant(&self, struct_def: &hir::VariantData) -> AdtVariant<'tcx> {
|
||||
let fields =
|
||||
struct_def.fields().iter()
|
||||
.map(|field| {
|
||||
let field_ty = self.tcx.node_id_to_type(field.id);
|
||||
let field_ty = self.instantiate_type_scheme(field.span,
|
||||
&self.parameter_environment
|
||||
.free_substs,
|
||||
&field_ty);
|
||||
AdtField { ty: field_ty, span: field.span }
|
||||
})
|
||||
.collect();
|
||||
AdtVariant { fields: fields }
|
||||
}
|
||||
|
||||
fn enum_variants(&self, enum_def: &hir::EnumDef) -> Vec<AdtVariant<'tcx>> {
|
||||
enum_def.variants.iter()
|
||||
.map(|variant| self.struct_variant(&variant.node.data))
|
||||
.collect()
|
||||
}
|
||||
fn enum_variants(&self, enum_def: &hir::EnumDef) -> Vec<AdtVariant<'tcx>> {
|
||||
enum_def.variants.iter()
|
||||
.map(|variant| self.struct_variant(&variant.node.data))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn impl_implied_bounds(&self, impl_def_id: DefId, span: Span) -> Vec<Ty<'tcx>> {
|
||||
let free_substs = &self.parameter_environment.free_substs;
|
||||
match self.tcx.impl_trait_ref(impl_def_id) {
|
||||
Some(ref trait_ref) => {
|
||||
// Trait impl: take implied bounds from all types that
|
||||
// appear in the trait reference.
|
||||
let trait_ref = self.instantiate_type_scheme(span, free_substs, trait_ref);
|
||||
trait_ref.substs.types.as_slice().to_vec()
|
||||
}
|
||||
fn impl_implied_bounds(&self, impl_def_id: DefId, span: Span) -> Vec<Ty<'tcx>> {
|
||||
let free_substs = &self.parameter_environment.free_substs;
|
||||
match self.tcx.impl_trait_ref(impl_def_id) {
|
||||
Some(ref trait_ref) => {
|
||||
// Trait impl: take implied bounds from all types that
|
||||
// appear in the trait reference.
|
||||
let trait_ref = self.instantiate_type_scheme(span, free_substs, trait_ref);
|
||||
trait_ref.substs.types.as_slice().to_vec()
|
||||
}
|
||||
|
||||
None => {
|
||||
// Inherent impl: take implied bounds from the self type.
|
||||
let self_ty = self.tcx.lookup_item_type(impl_def_id).ty;
|
||||
let self_ty = self.instantiate_type_scheme(span, free_substs, &self_ty);
|
||||
vec![self_ty]
|
||||
None => {
|
||||
// Inherent impl: take implied bounds from the self type.
|
||||
let self_ty = self.tcx.lookup_item_type(impl_def_id).ty;
|
||||
let self_ty = self.instantiate_type_scheme(span, free_substs, &self_ty);
|
||||
vec![self_ty]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn error_192(ccx: &CrateCtxt, span: Span) {
|
||||
span_err!(ccx.tcx.sess, span, E0192,
|
||||
|
@ -35,35 +35,35 @@ use rustc::hir;
|
||||
// Entry point functions
|
||||
|
||||
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||
pub fn resolve_type_vars_in_expr(&self, e: &hir::Expr) {
|
||||
assert_eq!(self.writeback_errors.get(), false);
|
||||
let mut wbcx = WritebackCx::new(self);
|
||||
wbcx.visit_expr(e);
|
||||
wbcx.visit_upvar_borrow_map();
|
||||
wbcx.visit_closures();
|
||||
wbcx.visit_liberated_fn_sigs();
|
||||
wbcx.visit_fru_field_types();
|
||||
}
|
||||
|
||||
pub fn resolve_type_vars_in_fn(&self, decl: &hir::FnDecl, blk: &hir::Block) {
|
||||
assert_eq!(self.writeback_errors.get(), false);
|
||||
let mut wbcx = WritebackCx::new(self);
|
||||
wbcx.visit_block(blk);
|
||||
for arg in &decl.inputs {
|
||||
wbcx.visit_node_id(ResolvingPattern(arg.pat.span), arg.id);
|
||||
wbcx.visit_pat(&arg.pat);
|
||||
|
||||
// Privacy needs the type for the whole pattern, not just each binding
|
||||
if !pat_util::pat_is_binding(&self.tcx.def_map.borrow(), &arg.pat) {
|
||||
wbcx.visit_node_id(ResolvingPattern(arg.pat.span),
|
||||
arg.pat.id);
|
||||
}
|
||||
pub fn resolve_type_vars_in_expr(&self, e: &hir::Expr) {
|
||||
assert_eq!(self.writeback_errors.get(), false);
|
||||
let mut wbcx = WritebackCx::new(self);
|
||||
wbcx.visit_expr(e);
|
||||
wbcx.visit_upvar_borrow_map();
|
||||
wbcx.visit_closures();
|
||||
wbcx.visit_liberated_fn_sigs();
|
||||
wbcx.visit_fru_field_types();
|
||||
}
|
||||
|
||||
pub fn resolve_type_vars_in_fn(&self, decl: &hir::FnDecl, blk: &hir::Block) {
|
||||
assert_eq!(self.writeback_errors.get(), false);
|
||||
let mut wbcx = WritebackCx::new(self);
|
||||
wbcx.visit_block(blk);
|
||||
for arg in &decl.inputs {
|
||||
wbcx.visit_node_id(ResolvingPattern(arg.pat.span), arg.id);
|
||||
wbcx.visit_pat(&arg.pat);
|
||||
|
||||
// Privacy needs the type for the whole pattern, not just each binding
|
||||
if !pat_util::pat_is_binding(&self.tcx.def_map.borrow(), &arg.pat) {
|
||||
wbcx.visit_node_id(ResolvingPattern(arg.pat.span),
|
||||
arg.pat.id);
|
||||
}
|
||||
}
|
||||
wbcx.visit_upvar_borrow_map();
|
||||
wbcx.visit_closures();
|
||||
wbcx.visit_liberated_fn_sigs();
|
||||
wbcx.visit_fru_field_types();
|
||||
}
|
||||
wbcx.visit_upvar_borrow_map();
|
||||
wbcx.visit_closures();
|
||||
wbcx.visit_liberated_fn_sigs();
|
||||
wbcx.visit_fru_field_types();
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
@ -66,39 +66,39 @@ impl<'a, 'gcx, 'tcx, 'v> intravisit::Visitor<'v> for CoherenceCheckVisitor<'a, '
|
||||
|
||||
impl<'a, 'gcx, 'tcx> CoherenceChecker<'a, 'gcx, 'tcx> {
|
||||
|
||||
// Returns the def ID of the base type, if there is one.
|
||||
fn get_base_type_def_id(&self, span: Span, ty: Ty<'tcx>) -> Option<DefId> {
|
||||
match ty.sty {
|
||||
TyEnum(def, _) |
|
||||
TyStruct(def, _) => {
|
||||
Some(def.did)
|
||||
}
|
||||
// Returns the def ID of the base type, if there is one.
|
||||
fn get_base_type_def_id(&self, span: Span, ty: Ty<'tcx>) -> Option<DefId> {
|
||||
match ty.sty {
|
||||
TyEnum(def, _) |
|
||||
TyStruct(def, _) => {
|
||||
Some(def.did)
|
||||
}
|
||||
|
||||
TyTrait(ref t) => {
|
||||
Some(t.principal_def_id())
|
||||
}
|
||||
TyTrait(ref t) => {
|
||||
Some(t.principal_def_id())
|
||||
}
|
||||
|
||||
TyBox(_) => {
|
||||
self.inference_context.tcx.lang_items.owned_box()
|
||||
}
|
||||
TyBox(_) => {
|
||||
self.inference_context.tcx.lang_items.owned_box()
|
||||
}
|
||||
|
||||
TyBool | TyChar | TyInt(..) | TyUint(..) | TyFloat(..) |
|
||||
TyStr | TyArray(..) | TySlice(..) | TyFnDef(..) | TyFnPtr(_) |
|
||||
TyTuple(..) | TyParam(..) | TyError |
|
||||
TyRawPtr(_) | TyRef(_, _) | TyProjection(..) => {
|
||||
None
|
||||
}
|
||||
TyBool | TyChar | TyInt(..) | TyUint(..) | TyFloat(..) |
|
||||
TyStr | TyArray(..) | TySlice(..) | TyFnDef(..) | TyFnPtr(_) |
|
||||
TyTuple(..) | TyParam(..) | TyError |
|
||||
TyRawPtr(_) | TyRef(_, _) | TyProjection(..) => {
|
||||
None
|
||||
}
|
||||
|
||||
TyInfer(..) | TyClosure(..) => {
|
||||
// `ty` comes from a user declaration so we should only expect types
|
||||
// that the user can type
|
||||
span_bug!(
|
||||
span,
|
||||
"coherence encountered unexpected type searching for base type: {}",
|
||||
ty);
|
||||
TyInfer(..) | TyClosure(..) => {
|
||||
// `ty` comes from a user declaration so we should only expect types
|
||||
// that the user can type
|
||||
span_bug!(
|
||||
span,
|
||||
"coherence encountered unexpected type searching for base type: {}",
|
||||
ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check(&self) {
|
||||
// Check implementations and traits. This populates the tables
|
||||
|
Loading…
Reference in New Issue
Block a user