Skip to content

Commit

Permalink
check::method - unify receivers before normalizing method signatures
Browse files Browse the repository at this point in the history
Normalizing method signatures can unify inference variables, which can
cause receiver unification to fail. Unify the receivers first to avoid
that.

Fixes rust-lang#36701.
Fixes rust-lang#45801.
Fixes rust-lang#45855.
  • Loading branch information
arielb1 committed Nov 9, 2017
1 parent 6e29774 commit 5901f1c
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 9 deletions.
30 changes: 21 additions & 9 deletions src/librustc_typeck/check/method/confirm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use hir::def_id::DefId;
use rustc::ty::subst::Substs;
use rustc::traits;
use rustc::ty::{self, LvaluePreference, NoPreference, PreferMutLvalue, Ty};
use rustc::ty::subst::Subst;
use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow, OverloadedDeref};
use rustc::ty::fold::TypeFoldable;
use rustc::infer::{self, InferOk};
Expand Down Expand Up @@ -84,9 +85,6 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
// Adjust the self expression the user provided and obtain the adjusted type.
let self_ty = self.adjust_self_ty(unadjusted_self_ty, &pick);

// Make sure nobody calls `drop()` explicitly.
self.enforce_illegal_method_limitations(&pick);

// Create substitutions for the method's type parameters.
let rcvr_substs = self.fresh_receiver_substs(self_ty, &pick);
let all_substs = self.instantiate_method_substs(&pick, segment, rcvr_substs);
Expand All @@ -96,6 +94,22 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
// Create the final signature for the method, replacing late-bound regions.
let (method_sig, method_predicates) = self.instantiate_method_sig(&pick, all_substs);

// Unify the (adjusted) self type with what the method expects.
//
// SUBTLE: if we want good error messages, because of "guessing" while matching
// traits, no trait system method can be called before this point because they
// could alter our Self-type, except for normalizing the receiver from the
// signature (which is also done during probing).
let method_sig_rcvr =
self.normalize_associated_types_in(self.span, &method_sig.inputs()[0]);
self.unify_receivers(self_ty, method_sig_rcvr);

let (method_sig, method_predicates) =
self.normalize_associated_types_in(self.span, &(method_sig, method_predicates));

// Make sure nobody calls `drop()` explicitly.
self.enforce_illegal_method_limitations(&pick);

// If there is a `Self: Sized` bound and `Self` is a trait object, it is possible that
// something which derefs to `Self` actually implements the trait and the caller
// wanted to make a static dispatch on it but forgot to import the trait.
Expand All @@ -106,9 +120,6 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
// appropriate hint suggesting to import the trait.
let illegal_sized_bound = self.predicates_require_illegal_sized_bound(&method_predicates);

// Unify the (adjusted) self type with what the method expects.
self.unify_receivers(self_ty, method_sig.inputs()[0]);

// Add any trait/regions obligations specified on the method's type parameters.
// We won't add these if we encountered an illegal sized bound, so that we can use
// a custom error in that case.
Expand Down Expand Up @@ -338,6 +349,9 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
///////////////////////////////////////////////////////////////////////////
//

// NOTE: this returns the *unnormalized* predicates and method sig. Because of
// inference guessing, the predicates and method signature can't be normalized
// until we unify the `Self` type.
fn instantiate_method_sig(&mut self,
pick: &probe::Pick<'tcx>,
all_substs: &'tcx Substs<'tcx>)
Expand All @@ -352,8 +366,6 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
let def_id = pick.item.def_id;
let method_predicates = self.tcx.predicates_of(def_id)
.instantiate(self.tcx, all_substs);
let method_predicates = self.normalize_associated_types_in(self.span,
&method_predicates);

debug!("method_predicates after subst = {:?}", method_predicates);

Expand All @@ -369,7 +381,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
debug!("late-bound lifetimes from method instantiated, method_sig={:?}",
method_sig);

let method_sig = self.instantiate_type_scheme(self.span, all_substs, &method_sig);
let method_sig = method_sig.subst(self.tcx, all_substs);
debug!("type scheme substituted, method_sig={:?}", method_sig);

(method_sig, method_predicates)
Expand Down
35 changes: 35 additions & 0 deletions src/test/compile-fail/issue-45801.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

struct Params;

pub trait Plugin<E: ?Sized> {
type Error;
}

pub trait Pluggable {
fn get_ref<P: Plugin<Self>>(&mut self) -> Option<P::Error> {
None
}
}

struct Foo;
impl Plugin<Foo> for Params {
type Error = ();
}

impl<T: Copy> Pluggable for T {}

fn handle(req: &mut i32) {
req.get_ref::<Params>();
//~^ ERROR the trait bound `Params: Plugin<i32>` is not satisfied
}

fn main() {}

0 comments on commit 5901f1c

Please sign in to comment.