Skip to content

Commit 3a49c3b

Browse files
committed
Rollup merge of rust-lang#22785 - nikomatsakis:issue-21750-normalization-with-regions, r=pnkfelix
Two changes: 1. Make traits with assoc types invariant w/r/t their inputs. 2. Fully normalize parameter environments, including any region variables (which were being overlooked). The former supports the latter, but also just seems like a reasonably good idea. Fixes rust-lang#21750 cc @edwardw r? @pnkfelix
2 parents 267c587 + 206c254 commit 3a49c3b

File tree

4 files changed

+134
-53
lines changed

4 files changed

+134
-53
lines changed

src/librustc/middle/infer/combine.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -265,13 +265,7 @@ pub trait Combine<'tcx> : Sized {
265265
Err(ty::terr_projection_name_mismatched(
266266
expected_found(self, a.item_name, b.item_name)))
267267
} else {
268-
// Note that the trait refs for the projection must be
269-
// *equal*. This is because there is no inherent
270-
// relationship between `<T as Foo>::Bar` and `<U as
271-
// Foo>::Bar` that we can derive based on how `T` relates
272-
// to `U`. Issue #21726 contains further discussion and
273-
// in-depth examples.
274-
let trait_ref = try!(self.equate().trait_refs(&*a.trait_ref, &*b.trait_ref));
268+
let trait_ref = try!(self.trait_refs(&*a.trait_ref, &*b.trait_ref));
275269
Ok(ty::ProjectionTy { trait_ref: Rc::new(trait_ref), item_name: a.item_name })
276270
}
277271
}

src/librustc/middle/traits/coherence.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,16 @@ fn overlap(selcx: &mut SelectionContext,
5252
b_def_id: ast::DefId)
5353
-> bool
5454
{
55+
debug!("overlap(a_def_id={}, b_def_id={})",
56+
a_def_id.repr(selcx.tcx()),
57+
b_def_id.repr(selcx.tcx()));
58+
5559
let (a_trait_ref, a_obligations) = impl_trait_ref_and_oblig(selcx, a_def_id);
5660
let (b_trait_ref, b_obligations) = impl_trait_ref_and_oblig(selcx, b_def_id);
5761

62+
debug!("overlap: a_trait_ref={}", a_trait_ref.repr(selcx.tcx()));
63+
debug!("overlap: b_trait_ref={}", b_trait_ref.repr(selcx.tcx()));
64+
5865
// Does `a <: b` hold? If not, no overlap.
5966
if let Err(_) = infer::mk_sub_poly_trait_refs(selcx.infcx(),
6067
true,
@@ -64,10 +71,20 @@ fn overlap(selcx: &mut SelectionContext,
6471
return false;
6572
}
6673

74+
debug!("overlap: subtraitref check succeeded");
75+
6776
// Are any of the obligations unsatisfiable? If so, no overlap.
68-
a_obligations.iter()
69-
.chain(b_obligations.iter())
70-
.all(|o| selcx.evaluate_obligation(o))
77+
let opt_failing_obligation =
78+
a_obligations.iter()
79+
.chain(b_obligations.iter())
80+
.find(|o| !selcx.evaluate_obligation(o));
81+
82+
if let Some(failing_obligation) = opt_failing_obligation {
83+
debug!("overlap: obligation unsatisfiable {}", failing_obligation.repr(selcx.tcx()));
84+
return false;
85+
}
86+
87+
true
7188
}
7289

7390
/// Instantiate fresh variables for all bound parameters of the impl

src/librustc/middle/traits/mod.rs

Lines changed: 50 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub use self::ObligationCauseCode::*;
1818
use middle::subst;
1919
use middle::ty::{self, HasProjectionTypes, Ty};
2020
use middle::ty_fold::TypeFoldable;
21-
use middle::infer::{self, InferCtxt};
21+
use middle::infer::{self, fixup_err_to_string, InferCtxt};
2222
use std::slice::Iter;
2323
use std::rc::Rc;
2424
use syntax::ast;
@@ -395,53 +395,64 @@ pub fn type_known_to_meet_builtin_bound<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>,
395395
}
396396
}
397397

398+
/// Normalizes the parameter environment, reporting errors if they occur.
398399
pub fn normalize_param_env_or_error<'a,'tcx>(unnormalized_env: ty::ParameterEnvironment<'a,'tcx>,
399400
cause: ObligationCause<'tcx>)
400401
-> ty::ParameterEnvironment<'a,'tcx>
401402
{
402-
match normalize_param_env(&unnormalized_env, cause) {
403-
Ok(p) => p,
403+
// I'm not wild about reporting errors here; I'd prefer to
404+
// have the errors get reported at a defined place (e.g.,
405+
// during typeck). Instead I have all parameter
406+
// environments, in effect, going through this function
407+
// and hence potentially reporting errors. This ensurse of
408+
// course that we never forget to normalize (the
409+
// alternative seemed like it would involve a lot of
410+
// manual invocations of this fn -- and then we'd have to
411+
// deal with the errors at each of those sites).
412+
//
413+
// In any case, in practice, typeck constructs all the
414+
// parameter environments once for every fn as it goes,
415+
// and errors will get reported then; so after typeck we
416+
// can be sure that no errors should occur.
417+
418+
let tcx = unnormalized_env.tcx;
419+
let span = cause.span;
420+
let body_id = cause.body_id;
421+
422+
debug!("normalize_param_env_or_error(unnormalized_env={})",
423+
unnormalized_env.repr(tcx));
424+
425+
let infcx = infer::new_infer_ctxt(tcx);
426+
let predicates = match fully_normalize(&infcx, &unnormalized_env, cause,
427+
&unnormalized_env.caller_bounds) {
428+
Ok(predicates) => predicates,
404429
Err(errors) => {
405-
// I'm not wild about reporting errors here; I'd prefer to
406-
// have the errors get reported at a defined place (e.g.,
407-
// during typeck). Instead I have all parameter
408-
// environments, in effect, going through this function
409-
// and hence potentially reporting errors. This ensurse of
410-
// course that we never forget to normalize (the
411-
// alternative seemed like it would involve a lot of
412-
// manual invocations of this fn -- and then we'd have to
413-
// deal with the errors at each of those sites).
414-
//
415-
// In any case, in practice, typeck constructs all the
416-
// parameter environments once for every fn as it goes,
417-
// and errors will get reported then; so after typeck we
418-
// can be sure that no errors should occur.
419-
let infcx = infer::new_infer_ctxt(unnormalized_env.tcx);
420430
report_fulfillment_errors(&infcx, &errors);
421-
422-
// Normalized failed? use what they gave us, it's better than nothing.
423-
unnormalized_env
431+
return unnormalized_env; // an unnormalized env is better than nothing
424432
}
425-
}
426-
}
427-
428-
pub fn normalize_param_env<'a,'tcx>(param_env: &ty::ParameterEnvironment<'a,'tcx>,
429-
cause: ObligationCause<'tcx>)
430-
-> Result<ty::ParameterEnvironment<'a,'tcx>,
431-
Vec<FulfillmentError<'tcx>>>
432-
{
433-
let tcx = param_env.tcx;
434-
435-
debug!("normalize_param_env(param_env={})",
436-
param_env.repr(tcx));
433+
};
437434

438-
let infcx = infer::new_infer_ctxt(tcx);
439-
let predicates = try!(fully_normalize(&infcx, param_env, cause, &param_env.caller_bounds));
435+
infcx.resolve_regions_and_report_errors(body_id);
436+
let predicates = match infcx.fully_resolve(&predicates) {
437+
Ok(predicates) => predicates,
438+
Err(fixup_err) => {
439+
// If we encounter a fixup error, it means that some type
440+
// variable wound up unconstrained. I actually don't know
441+
// if this can happen, and I certainly don't expect it to
442+
// happen often, but if it did happen it probably
443+
// represents a legitimate failure due to some kind of
444+
// unconstrained variable, and it seems better not to ICE,
445+
// all things considered.
446+
let err_msg = fixup_err_to_string(fixup_err);
447+
tcx.sess.span_err(span, &err_msg);
448+
return unnormalized_env; // an unnormalized env is better than nothing
449+
}
450+
};
440451

441-
debug!("normalize_param_env: predicates={}",
452+
debug!("normalize_param_env_or_error: predicates={}",
442453
predicates.repr(tcx));
443454

444-
Ok(param_env.with_caller_bounds(predicates))
455+
unnormalized_env.with_caller_bounds(predicates)
445456
}
446457

447458
pub fn fully_normalize<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>,
@@ -453,8 +464,7 @@ pub fn fully_normalize<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>,
453464
{
454465
let tcx = closure_typer.tcx();
455466

456-
debug!("normalize_param_env(value={})",
457-
value.repr(tcx));
467+
debug!("normalize_param_env(value={})", value.repr(tcx));
458468

459469
let mut selcx = &mut SelectionContext::new(infcx, closure_typer);
460470
let mut fulfill_cx = FulfillmentContext::new();
@@ -468,8 +478,7 @@ pub fn fully_normalize<'a,'tcx,T>(infcx: &InferCtxt<'a,'tcx>,
468478
}
469479
try!(fulfill_cx.select_all_or_error(infcx, closure_typer));
470480
let resolved_value = infcx.resolve_type_vars_if_possible(&normalized_value);
471-
debug!("normalize_param_env: resolved_value={}",
472-
resolved_value.repr(tcx));
481+
debug!("normalize_param_env: resolved_value={}", resolved_value.repr(tcx));
473482
Ok(resolved_value)
474483
}
475484

src/librustc_typeck/variance.rs

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,56 @@
203203
//! failure, but rather because the target type `Foo<Y>` is itself just
204204
//! not well-formed. Basically we get to assume well-formedness of all
205205
//! types involved before considering variance.
206+
//!
207+
//! ### Associated types
208+
//!
209+
//! Any trait with an associated type is invariant with respect to all
210+
//! of its inputs. To see why this makes sense, consider what
211+
//! subtyping for a trait reference means:
212+
//!
213+
//! <T as Trait> <: <U as Trait>
214+
//!
215+
//! means that if I know that `T as Trait`,
216+
//! I also know that `U as
217+
//! Trait`. Moreover, if you think of it as
218+
//! dictionary passing style, it means that
219+
//! a dictionary for `<T as Trait>` is safe
220+
//! to use where a dictionary for `<U as
221+
//! Trait>` is expected.
222+
//!
223+
//! The problem is that when you can
224+
//! project types out from `<T as Trait>`,
225+
//! the relationship to types projected out
226+
//! of `<U as Trait>` is completely unknown
227+
//! unless `T==U` (see #21726 for more
228+
//! details). Making `Trait` invariant
229+
//! ensures that this is true.
230+
//!
231+
//! *Historical note: we used to preserve this invariant another way,
232+
//! by tweaking the subtyping rules and requiring that when a type `T`
233+
//! appeared as part of a projection, that was considered an invariant
234+
//! location, but this version does away with the need for those
235+
//! somewhat "special-case-feeling" rules.*
236+
//!
237+
//! Another related reason is that if we didn't make traits with
238+
//! associated types invariant, then projection is no longer a
239+
//! function with a single result. Consider:
240+
//!
241+
//! ```
242+
//! trait Identity { type Out; fn foo(&self); }
243+
//! impl<T> Identity for T { type Out = T; ... }
244+
//! ```
245+
//!
246+
//! Now if I have `<&'static () as Identity>::Out`, this can be
247+
//! validly derived as `&'a ()` for any `'a`:
248+
//!
249+
//! <&'a () as Identity> <: <&'static () as Identity>
250+
//! if &'static () < : &'a () -- Identity is contravariant in Self
251+
//! if 'static : 'a -- Subtyping rules for relations
252+
//!
253+
//! This change otoh means that `<'static () as Identity>::Out` is
254+
//! always `&'static ()` (which might then be upcast to `'a ()`,
255+
//! separately). This was helpful in solving #21750.
206256
207257
use self::VarianceTerm::*;
208258
use self::ParamKind::*;
@@ -613,7 +663,18 @@ impl<'a, 'tcx, 'v> Visitor<'v> for ConstraintContext<'a, 'tcx> {
613663
&method.fty.sig,
614664
self.covariant);
615665
}
616-
ty::TypeTraitItem(_) => {}
666+
ty::TypeTraitItem(ref data) => {
667+
// Any trait with an associated type is
668+
// invariant with respect to all of its
669+
// inputs. See length discussion in the comment
670+
// on this module.
671+
let projection_ty = ty::mk_projection(tcx,
672+
trait_def.trait_ref.clone(),
673+
data.name);
674+
self.add_constraints_from_ty(&trait_def.generics,
675+
projection_ty,
676+
self.invariant);
677+
}
617678
}
618679
}
619680
}
@@ -893,7 +954,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
893954
trait_def.generics.types.as_slice(),
894955
trait_def.generics.regions.as_slice(),
895956
trait_ref.substs,
896-
self.invariant);
957+
variance);
897958
}
898959

899960
ty::ty_trait(ref data) => {

0 commit comments

Comments
 (0)