Skip to content

Commit 574d824

Browse files
committed
Fix ICE related to late/early bound regions
1 parent 7e88508 commit 574d824

File tree

5 files changed

+194
-532
lines changed

5 files changed

+194
-532
lines changed

src/librustc/middle/ty.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7300,7 +7300,7 @@ impl<'tcx> Repr<'tcx> for UnboxedClosureUpvar<'tcx> {
73007300
}
73017301
}
73027302

7303-
impl<'tcx> Repr<'tcx> for ParameterEnvironment<'tcx> {
7303+
impl<'a, 'tcx> Repr<'tcx> for ParameterEnvironment<'a, 'tcx> {
73047304
fn repr(&self, tcx: &ctxt<'tcx>) -> String {
73057305
format!("ParameterEnvironment(\
73067306
free_substs={}, \

src/librustc/middle/ty_fold.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -555,9 +555,10 @@ impl<'tcx> TypeFoldable<'tcx> for ty::UnboxedClosureUpvar<'tcx> {
555555
}
556556
}
557557

558-
impl<'tcx> TypeFoldable<'tcx> for ty::ParameterEnvironment<'tcx> {
559-
fn fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::ParameterEnvironment<'tcx> {
558+
impl<'a, 'tcx> TypeFoldable<'tcx> for ty::ParameterEnvironment<'a, 'tcx> where 'tcx: 'a {
559+
fn fold_with<F:TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::ParameterEnvironment<'a, 'tcx> {
560560
ty::ParameterEnvironment {
561+
tcx: self.tcx,
561562
free_substs: self.free_substs.fold_with(folder),
562563
implicit_region_bound: self.implicit_region_bound.fold_with(folder),
563564
caller_bounds: self.caller_bounds.fold_with(folder),

src/librustc_typeck/check/compare_method.rs

Lines changed: 168 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010

1111
use middle::infer;
1212
use middle::traits;
13-
use middle::ty::{mod};
14-
use middle::subst::{mod, Subst, VecPerParamSpace};
15-
use util::ppaux::{mod, Repr};
13+
use middle::ty::{self};
14+
use middle::subst::{self, Subst, Substs, VecPerParamSpace};
15+
use util::ppaux::{self, Repr, UserString};
1616

1717
use syntax::ast;
1818
use syntax::codemap::{Span};
@@ -171,9 +171,6 @@ pub fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
171171
//
172172
// Finally we register each of these predicates as an obligation in
173173
// a fresh FulfillmentCtxt, and invoke select_all_or_error.
174-
//
175-
// FIXME(jroesch): Currently the error reporting is not correctly attached
176-
// to a span and results in terrible error reporting.
177174

178175
// Create a parameter environment that represents the implementation's
179176
// method.
@@ -192,6 +189,18 @@ pub fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
192189
debug!("compare_impl_method: trait_to_skol_substs={}",
193190
trait_to_skol_substs.repr(tcx));
194191

192+
// Check region bounds. FIXME(@jroesch) refactor this away when removing
193+
// ParamBounds.
194+
if !check_region_bounds_on_impl_method(tcx,
195+
impl_m_span,
196+
impl_m,
197+
&trait_m.generics,
198+
&impl_m.generics,
199+
&trait_to_skol_substs,
200+
impl_to_skol_substs) {
201+
return;
202+
}
203+
195204
// Create obligations for each predicate declared by the impl
196205
// definition in the context of the trait's parameter
197206
// environment. We can't just use `impl_env.caller_bounds`,
@@ -208,7 +217,7 @@ pub fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
208217
debug!("compare_impl_method: impl_bounds={}",
209218
impl_bounds.repr(tcx));
210219

211-
let mut selcx = traits::SelectionContext::new(&infcx, &impl_param_env, tcx);
220+
let mut selcx = traits::SelectionContext::new(&infcx, &impl_param_env);
212221

213222
let normalize_cause =
214223
traits::ObligationCause::misc(impl_m_span, impl_m_body_id);
@@ -260,6 +269,19 @@ pub fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
260269
traits::Obligation::new(cause, predicate));
261270
}
262271

272+
// We now need to check that the signature of the impl method is
273+
// compatible with that of the trait method. We do this by
274+
// checking that `impl_fty <: trait_fty`.
275+
//
276+
// FIXME. Unfortunately, this doesn't quite work right now because
277+
// associated type normalization is not integrated into subtype
278+
// checks. For the comparison to be valid, we need to
279+
// normalize the associated types in the impl/trait methods
280+
// first. However, because function types bind regions, just
281+
// calling `normalize_associated_types_in` would have no effect on
282+
// any associated types appearing in the fn arguments or return
283+
// type.
284+
263285
// Compute skolemized form of impl and trait method tys.
264286
let impl_fty = ty::mk_bare_fn(tcx, None, tcx.mk_bare_fn(impl_m.fty.clone()));
265287
let impl_fty = impl_fty.subst(tcx, impl_to_skol_substs);
@@ -271,28 +293,25 @@ pub fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
271293

272294
let (impl_sig, _) =
273295
infcx.replace_late_bound_regions_with_fresh_var(impl_m_span,
274-
infer::HigherRankedType,
275-
&impl_m.fty.sig);
276-
296+
infer::HigherRankedType,
297+
&impl_m.fty.sig);
277298
let impl_sig =
278299
impl_sig.subst(tcx, impl_to_skol_substs);
279300
let impl_sig =
280301
assoc::normalize_associated_types_in(&infcx,
281302
&impl_param_env,
282-
infcx.tcx,
283303
&mut fulfillment_cx,
284304
impl_m_span,
285305
impl_m_body_id,
286306
&impl_sig);
287307
let impl_fty =
288308
ty::mk_bare_fn(tcx,
289309
None,
290-
tcx.mk_bare_fn(ty::BareFnTy {
291-
unsafety: impl_m.fty.unsafety,
292-
abi: impl_m.fty.abi,
293-
sig: ty::Binder(impl_sig) }));
294-
debug!("compare_impl_method: impl_fty={}",
295-
impl_fty.repr(tcx));
310+
tcx.mk_bare_fn(ty::BareFnTy { unsafety: impl_m.fty.unsafety,
311+
abi: impl_m.fty.abi,
312+
sig: ty::Binder(impl_sig) }));
313+
debug!("compare_impl_method: impl_fty={}",
314+
impl_fty.repr(tcx));
296315

297316
let (trait_sig, skol_map) =
298317
infcx.skolemize_late_bound_regions(&trait_m.fty.sig, snapshot);
@@ -301,46 +320,164 @@ pub fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
301320
let trait_sig =
302321
assoc::normalize_associated_types_in(&infcx,
303322
&impl_param_env,
304-
infcx.tcx,
305323
&mut fulfillment_cx,
306324
impl_m_span,
307325
impl_m_body_id,
308326
&trait_sig);
309327
let trait_fty =
310328
ty::mk_bare_fn(tcx,
311329
None,
312-
tcx.mk_bare_fn(ty::BareFnTy {
313-
unsafety: trait_m.fty.unsafety,
314-
abi: trait_m.fty.abi,
315-
sig: ty::Binder(trait_sig) }));
330+
tcx.mk_bare_fn(ty::BareFnTy { unsafety: trait_m.fty.unsafety,
331+
abi: trait_m.fty.abi,
332+
sig: ty::Binder(trait_sig) }));
333+
334+
debug!("compare_impl_method: trait_fty={}",
335+
trait_fty.repr(tcx));
316336

317-
debug!("compare_impl_method: trait_fty={}", trait_fty.repr(tcx));
318337
try!(infer::mk_subty(&infcx, false, origin, impl_fty, trait_fty));
338+
319339
infcx.leak_check(&skol_map, snapshot)
320340
});
321341

322342
match err {
323343
Ok(()) => { }
324344
Err(terr) => {
325345
debug!("checking trait method for compatibility: impl ty {}, trait ty {}",
326-
impl_fty.repr(tcx),
327-
trait_fty.repr(tcx));
328-
span_err!(tcx.sess, impl_m_span, E0053,
329-
"method `{}` has an incompatible type for trait: {}",
330-
token::get_name(trait_m.name),
331-
ty::type_err_to_str(tcx, &terr));
332-
return;
346+
impl_fty.repr(tcx),
347+
trait_fty.repr(tcx));
348+
span_err!(tcx.sess, impl_m_span, E0053,
349+
"method `{}` has an incompatible type for trait: {}",
350+
token::get_name(trait_m.name),
351+
ty::type_err_to_str(tcx, &terr));
352+
return;
333353
}
334354
}
335355

336356
// Check that all obligations are satisfied by the implementation's
337357
// version.
338-
match fulfillment_cx.select_all_or_error(&infcx, &trait_param_env, tcx) {
358+
match fulfillment_cx.select_all_or_error(&infcx, &trait_param_env) {
339359
Err(ref errors) => { traits::report_fulfillment_errors(&infcx, errors) }
340360
Ok(_) => {}
341361
}
342362

343363
// Finally, resolve all regions. This catches wily misuses of lifetime
344364
// parameters.
345365
infcx.resolve_regions_and_report_errors(impl_m_body_id);
366+
367+
fn check_region_bounds_on_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
368+
span: Span,
369+
impl_m: &ty::Method<'tcx>,
370+
trait_generics: &ty::Generics<'tcx>,
371+
impl_generics: &ty::Generics<'tcx>,
372+
trait_to_skol_substs: &Substs<'tcx>,
373+
impl_to_skol_substs: &Substs<'tcx>)
374+
-> bool
375+
{
376+
377+
let trait_params = trait_generics.regions.get_slice(subst::FnSpace);
378+
let impl_params = impl_generics.regions.get_slice(subst::FnSpace);
379+
380+
debug!("check_region_bounds_on_impl_method: \
381+
trait_generics={} \
382+
impl_generics={} \
383+
trait_to_skol_substs={} \
384+
impl_to_skol_substs={}",
385+
trait_generics.repr(tcx),
386+
impl_generics.repr(tcx),
387+
trait_to_skol_substs.repr(tcx),
388+
impl_to_skol_substs.repr(tcx));
389+
390+
// Must have same number of early-bound lifetime parameters.
391+
// Unfortunately, if the user screws up the bounds, then this
392+
// will change classification between early and late. E.g.,
393+
// if in trait we have `<'a,'b:'a>`, and in impl we just have
394+
// `<'a,'b>`, then we have 2 early-bound lifetime parameters
395+
// in trait but 0 in the impl. But if we report "expected 2
396+
// but found 0" it's confusing, because it looks like there
397+
// are zero. Since I don't quite know how to phrase things at
398+
// the moment, give a kind of vague error message.
399+
if trait_params.len() != impl_params.len() {
400+
tcx.sess.span_err(
401+
span,
402+
format!("lifetime parameters or bounds on method `{}` do \
403+
not match the trait declaration",
404+
token::get_name(impl_m.name))[]);
405+
return false;
406+
}
407+
408+
// Each parameter `'a:'b+'c+'d` in trait should have the same
409+
// set of bounds in the impl, after subst.
410+
for (trait_param, impl_param) in
411+
trait_params.iter().zip(
412+
impl_params.iter())
413+
{
414+
let trait_bounds =
415+
trait_param.bounds.subst(tcx, trait_to_skol_substs);
416+
let impl_bounds =
417+
impl_param.bounds.subst(tcx, impl_to_skol_substs);
418+
419+
debug!("check_region_bounds_on_impl_method: \
420+
trait_param={} \
421+
impl_param={} \
422+
trait_bounds={} \
423+
impl_bounds={}",
424+
trait_param.repr(tcx),
425+
impl_param.repr(tcx),
426+
trait_bounds.repr(tcx),
427+
impl_bounds.repr(tcx));
428+
429+
// Collect the set of bounds present in trait but not in
430+
// impl.
431+
let missing: Vec<ty::Region> =
432+
trait_bounds.iter()
433+
.filter(|&b| !impl_bounds.contains(b))
434+
.map(|&b| b)
435+
.collect();
436+
437+
// Collect set present in impl but not in trait.
438+
let extra: Vec<ty::Region> =
439+
impl_bounds.iter()
440+
.filter(|&b| !trait_bounds.contains(b))
441+
.map(|&b| b)
442+
.collect();
443+
444+
debug!("missing={} extra={}",
445+
missing.repr(tcx), extra.repr(tcx));
446+
447+
let err = if missing.len() != 0 || extra.len() != 0 {
448+
tcx.sess.span_err(
449+
span,
450+
format!(
451+
"the lifetime parameter `{}` declared in the impl \
452+
has a distinct set of bounds \
453+
from its counterpart `{}` \
454+
declared in the trait",
455+
impl_param.name.user_string(tcx),
456+
trait_param.name.user_string(tcx))[]);
457+
true
458+
} else {
459+
false
460+
};
461+
462+
if missing.len() != 0 {
463+
tcx.sess.span_note(
464+
span,
465+
format!("the impl is missing the following bounds: `{}`",
466+
missing.user_string(tcx))[]);
467+
}
468+
469+
if extra.len() != 0 {
470+
tcx.sess.span_note(
471+
span,
472+
format!("the impl has the following extra bounds: `{}`",
473+
extra.user_string(tcx))[]);
474+
}
475+
476+
if err {
477+
return false;
478+
}
479+
}
480+
481+
return true;
482+
}
346483
}

0 commit comments

Comments
 (0)