Skip to content

Commit f8058c4

Browse files
committed
Refactor code to not invert checking
1 parent 03c61f9 commit f8058c4

File tree

3 files changed

+145
-51
lines changed

3 files changed

+145
-51
lines changed

src/librustc/middle/ty.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7299,3 +7299,15 @@ impl<'tcx> Repr<'tcx> for UnboxedClosureUpvar<'tcx> {
72997299
self.ty.repr(tcx))
73007300
}
73017301
}
7302+
7303+
impl<'tcx> Repr<'tcx> for ParameterEnvironment<'tcx> {
7304+
fn repr(&self, tcx: &ctxt<'tcx>) -> String {
7305+
format!("ParameterEnvironment(\
7306+
free_substs={}, \
7307+
implicit_region_bound={}, \
7308+
caller_bounds={})",
7309+
self.free_substs.repr(tcx),
7310+
self.implicit_region_bound.repr(tcx),
7311+
self.caller_bounds.repr(tcx))
7312+
}
7313+
}

src/librustc/middle/ty_fold.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,16 @@ impl<'tcx> TypeFoldable<'tcx> for ty::UnboxedClosureUpvar<'tcx> {
551551
def: self.def,
552552
span: self.span,
553553
ty: self.ty.fold_with(folder),
554+
}
555+
}
556+
}
557+
558+
impl<'tcx> TypeFoldable<'tcx> for ty::ParameterEnvironment<'tcx> {
559+
fn fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::ParameterEnvironment<'tcx> {
560+
ty::ParameterEnvironment {
561+
free_substs: self.free_substs.fold_with(folder),
562+
implicit_region_bound: self.implicit_region_bound.fold_with(folder),
563+
caller_bounds: self.caller_bounds.fold_with(folder),
554564
selection_cache: traits::SelectionCache::new(),
555565
}
556566
}

src/librustc_typeck/check/compare_method.rs

Lines changed: 123 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@
1111
use middle::infer;
1212
use middle::traits;
1313
use middle::ty::{mod};
14-
use middle::subst::{mod, Subst};
14+
use middle::subst::{mod, Subst, VecPerParamSpace};
1515
use util::ppaux::{mod, Repr};
1616

1717
use syntax::ast;
1818
use syntax::codemap::{Span};
1919
use syntax::parse::token;
2020

21+
use super::assoc;
22+
2123
/// Checks that a method from an impl conforms to the signature of
2224
/// the same method as declared in the trait.
2325
///
@@ -42,6 +44,7 @@ pub fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
4244
impl_trait_ref.repr(tcx));
4345

4446
let infcx = infer::new_infer_ctxt(tcx);
47+
let mut fulfillment_cx = traits::FulfillmentContext::new();
4548

4649
let trait_to_impl_substs = &impl_trait_ref.substs;
4750

@@ -174,10 +177,11 @@ pub fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
174177

175178
// Create a parameter environment that represents the implementation's
176179
// method.
177-
let impl_env = ty::ParameterEnvironment::for_item(tcx, impl_m.def_id.node);
180+
let impl_param_env =
181+
ty::ParameterEnvironment::for_item(tcx, impl_m.def_id.node);
178182

179183
// Create mapping from impl to skolemized.
180-
let impl_to_skol_substs = &impl_env.free_substs;
184+
let impl_to_skol_substs = &impl_param_env.free_substs;
181185

182186
// Create mapping from trait to skolemized.
183187
let trait_to_skol_substs =
@@ -188,77 +192,145 @@ pub fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
188192
debug!("compare_impl_method: trait_to_skol_substs={}",
189193
trait_to_skol_substs.repr(tcx));
190194

191-
// Construct trait parameter environment and then shift it into the skolemized viewpoint.
192-
let trait_param_env =
193-
ty::construct_parameter_environment(tcx,
194-
&trait_m.generics,
195-
impl_m_body_id);
196-
let trait_param_env =
197-
trait_param_env.subst(tcx, &trait_to_skol_substs);
198-
debug!("compare_impl_method: trait_param_env={}",
199-
trait_param_env.repr(tcx));
200-
201-
// Create a fresh fulfillment context.
202-
let mut fulfill_cx = traits::FulfillmentContext::new();
203-
204195
// Create obligations for each predicate declared by the impl
205196
// definition in the context of the trait's parameter
206197
// environment. We can't just use `impl_env.caller_bounds`,
207198
// however, because we want to replace all late-bound regions with
208199
// region variables.
209200
let impl_bounds =
210201
impl_m.generics.to_bounds(tcx, impl_to_skol_substs);
202+
211203
let (impl_bounds, _) =
212204
infcx.replace_late_bound_regions_with_fresh_var(
213205
impl_m_span,
214206
infer::HigherRankedType,
215-
&ty::Binder(impl_bounds)); // TODO I think this is right?
207+
&ty::Binder(impl_bounds));
216208
debug!("compare_impl_method: impl_bounds={}",
217209
impl_bounds.repr(tcx));
218-
for predicate in impl_bounds.predicates.into_iter() {
219-
fulfill_cx.register_predicate(
220-
tcx,
221-
traits::Obligation::new(traits::ObligationCause::dummy(),
222-
predicate));
223-
}
224210

225-
// Check that all obligations are satisfied by the implementation's
226-
// version.
227-
match fulfill_cx.select_all_or_error(&infcx, &trait_param_env, tcx) {
228-
Err(ref errors) => { traits::report_fulfillment_errors(&infcx, errors) }
229-
Ok(_) => {}
211+
let mut selcx = traits::SelectionContext::new(&infcx, &impl_param_env, tcx);
212+
213+
// Normalize the associated types in the impl_bounds.
214+
let traits::Normalized { value: impl_bounds, .. } =
215+
traits::normalize(&mut selcx, traits::ObligationCause::dummy(), &impl_bounds);
216+
217+
// Normalize the associated types in the trait_boubnds.
218+
let trait_bounds = trait_m.generics.to_bounds(tcx, &trait_to_skol_substs);
219+
let traits::Normalized { value: trait_bounds, .. } =
220+
traits::normalize(&mut selcx, traits::ObligationCause::dummy(), &trait_bounds);
221+
222+
// Obtain the predicate split predicate sets for each.
223+
let trait_pred = trait_bounds.predicates.split();
224+
let impl_pred = impl_bounds.predicates.split();
225+
226+
// This is the only tricky bit of the new way we check implementation methods
227+
// We need to build a set of predicates where only the FnSpace bounds
228+
// are from the trait and we assume all other bounds from the implementation
229+
// to be previously satisfied.
230+
//
231+
// We then register the obligations from the impl_m and check to see
232+
// if all constraints hold.
233+
let hybrid_preds = VecPerParamSpace::new(
234+
impl_pred.types,
235+
impl_pred.selfs,
236+
trait_pred.fns
237+
);
238+
239+
// Construct trait parameter environment and then shift it into the skolemized viewpoint.
240+
let mut trait_param_env = impl_param_env.clone();
241+
// The key step here is to update the caller_bounds's predicates to be
242+
// the new hybrid bounds we computed.
243+
trait_param_env.caller_bounds.predicates = hybrid_preds;
244+
245+
debug!("compare_impl_method: trait_bounds={}",
246+
trait_param_env.caller_bounds.repr(tcx));
247+
248+
for predicate in impl_pred.fns.into_iter() {
249+
fulfillment_cx.register_predicate_obligation(
250+
&infcx,
251+
traits::Obligation::new(traits::ObligationCause::dummy(), predicate));
230252
}
231253

232254
// Compute skolemized form of impl and trait method tys.
233-
let impl_fty = ty::mk_bare_fn(tcx, None, impl_m.fty.clone());
255+
let impl_fty = ty::mk_bare_fn(tcx, None, tcx.mk_bare_fn(impl_m.fty.clone()));
234256
let impl_fty = impl_fty.subst(tcx, impl_to_skol_substs);
235-
let trait_fty = ty::mk_bare_fn(tcx, None, trait_m.fty.clone());
257+
let trait_fty = ty::mk_bare_fn(tcx, None, tcx.mk_bare_fn(trait_m.fty.clone()));
236258
let trait_fty = trait_fty.subst(tcx, &trait_to_skol_substs);
237259

238-
// Check the impl method type IM is a subtype of the trait method
239-
// type TM. To see why this makes sense, think of a vtable. The
240-
// expected type of the function pointers in the vtable is the
241-
// type TM of the trait method. The actual type will be the type
242-
// IM of the impl method. Because we know that IM <: TM, that
243-
// means that anywhere a TM is expected, a IM will do instead. In
244-
// other words, anyone expecting to call a method with the type
245-
// from the trait, can safely call a method with the type from the
246-
// impl instead.
247-
debug!("checking trait method for compatibility: impl ty {}, trait ty {}",
248-
impl_fty.repr(tcx),
249-
trait_fty.repr(tcx));
250-
match infer::mk_subty(&infcx, false, infer::MethodCompatCheck(impl_m_span),
251-
impl_fty, trait_fty) {
252-
Ok(()) => {}
253-
Err(ref terr) => {
254-
span_err!(tcx.sess, impl_m_span, E0053,
255-
"method `{}` has an incompatible type for trait: {}",
256-
token::get_name(trait_m.name),
257-
ty::type_err_to_str(tcx, terr));
258-
ty::note_and_explain_type_err(tcx, terr);
260+
let err = infcx.try(|snapshot| {
261+
let origin = infer::MethodCompatCheck(impl_m_span);
262+
263+
let (impl_sig, _) =
264+
infcx.replace_late_bound_regions_with_fresh_var(impl_m_span,
265+
infer::HigherRankedType,
266+
&impl_m.fty.sig);
267+
268+
let impl_sig =
269+
impl_sig.subst(tcx, impl_to_skol_substs);
270+
let impl_sig =
271+
assoc::normalize_associated_types_in(&infcx,
272+
&impl_param_env,
273+
infcx.tcx,
274+
&mut fulfillment_cx,
275+
impl_m_span,
276+
impl_m_body_id,
277+
&impl_sig);
278+
let impl_fty =
279+
ty::mk_bare_fn(tcx,
280+
None,
281+
tcx.mk_bare_fn(ty::BareFnTy {
282+
unsafety: impl_m.fty.unsafety,
283+
abi: impl_m.fty.abi,
284+
sig: ty::Binder(impl_sig) }));
285+
debug!("compare_impl_method: impl_fty={}",
286+
impl_fty.repr(tcx));
287+
288+
let (trait_sig, skol_map) =
289+
infcx.skolemize_late_bound_regions(&trait_m.fty.sig, snapshot);
290+
let trait_sig =
291+
trait_sig.subst(tcx, &trait_to_skol_substs);
292+
let trait_sig =
293+
assoc::normalize_associated_types_in(&infcx,
294+
&impl_param_env,
295+
infcx.tcx,
296+
&mut fulfillment_cx,
297+
impl_m_span,
298+
impl_m_body_id,
299+
&trait_sig);
300+
let trait_fty =
301+
ty::mk_bare_fn(tcx,
302+
None,
303+
tcx.mk_bare_fn(ty::BareFnTy {
304+
unsafety: trait_m.fty.unsafety,
305+
abi: trait_m.fty.abi,
306+
sig: ty::Binder(trait_sig) }));
307+
308+
debug!("compare_impl_method: trait_fty={}", trait_fty.repr(tcx));
309+
try!(infer::mk_subty(&infcx, false, origin, impl_fty, trait_fty));
310+
infcx.leak_check(&skol_map, snapshot)
311+
});
312+
313+
match err {
314+
Ok(()) => { }
315+
Err(terr) => {
316+
debug!("checking trait method for compatibility: impl ty {}, trait ty {}",
317+
impl_fty.repr(tcx),
318+
trait_fty.repr(tcx));
319+
span_err!(tcx.sess, impl_m_span, E0053,
320+
"method `{}` has an incompatible type for trait: {}",
321+
token::get_name(trait_m.name),
322+
ty::type_err_to_str(tcx, &terr));
323+
return;
259324
}
260325
}
261326

327+
// Check that all obligations are satisfied by the implementation's
328+
// version.
329+
match fulfillment_cx.select_all_or_error(&infcx, &trait_param_env, tcx) {
330+
Err(ref errors) => { traits::report_fulfillment_errors(&infcx, errors) }
331+
Ok(_) => {}
332+
}
333+
262334
// Finally, resolve all regions. This catches wily misuses of lifetime
263335
// parameters.
264336
infcx.resolve_regions_and_report_errors(impl_m_body_id);

0 commit comments

Comments
 (0)