Skip to content

Commit 8c841f2

Browse files
committed
Extend coherence check to understand subtyping.
1 parent 91eedfe commit 8c841f2

File tree

3 files changed

+123
-39
lines changed

3 files changed

+123
-39
lines changed

src/librustc/middle/traits/coherence.rs

+72-29
Original file line numberDiff line numberDiff line change
@@ -10,53 +10,96 @@
1010

1111
//! See `doc.rs` for high-level documentation
1212
13+
use super::Normalized;
1314
use super::SelectionContext;
14-
use super::{Obligation, ObligationCause};
15+
use super::{ObligationCause};
16+
use super::PredicateObligation;
1517
use super::project;
1618
use super::util;
1719

1820
use middle::subst::{Subst, TypeSpace};
19-
use middle::ty::{self, Ty};
20-
use middle::infer::InferCtxt;
21+
use middle::ty::{self, ToPolyTraitRef, Ty};
22+
use middle::infer::{self, InferCtxt};
2123
use std::collections::HashSet;
2224
use std::rc::Rc;
2325
use syntax::ast;
2426
use syntax::codemap::DUMMY_SP;
2527
use util::ppaux::Repr;
2628

27-
pub fn impl_can_satisfy(infcx: &InferCtxt,
28-
impl1_def_id: ast::DefId,
29-
impl2_def_id: ast::DefId)
30-
-> bool
29+
/// True if there exist types that satisfy both of the two given impls.
30+
pub fn overlapping_impls(infcx: &InferCtxt,
31+
impl1_def_id: ast::DefId,
32+
impl2_def_id: ast::DefId)
33+
-> bool
3134
{
3235
debug!("impl_can_satisfy(\
3336
impl1_def_id={}, \
3437
impl2_def_id={})",
3538
impl1_def_id.repr(infcx.tcx),
3639
impl2_def_id.repr(infcx.tcx));
3740

38-
let param_env = ty::empty_parameter_environment(infcx.tcx);
39-
let mut selcx = SelectionContext::intercrate(infcx, &param_env);
40-
let cause = ObligationCause::dummy();
41-
42-
// `impl1` provides an implementation of `Foo<X,Y> for Z`.
43-
let impl1_substs =
44-
util::fresh_substs_for_impl(infcx, DUMMY_SP, impl1_def_id);
45-
let impl1_trait_ref =
46-
(*ty::impl_trait_ref(infcx.tcx, impl1_def_id).unwrap()).subst(infcx.tcx, &impl1_substs);
47-
let impl1_trait_ref =
48-
project::normalize(&mut selcx, cause.clone(), &impl1_trait_ref);
49-
50-
// Determine whether `impl2` can provide an implementation for those
51-
// same types.
52-
let obligation = Obligation::new(cause,
53-
ty::Binder(ty::TraitPredicate {
54-
trait_ref: Rc::new(impl1_trait_ref.value),
55-
}));
56-
debug!("impl_can_satisfy(obligation={})", obligation.repr(infcx.tcx));
57-
selcx.evaluate_impl(impl2_def_id, &obligation) &&
58-
impl1_trait_ref.obligations.iter().all(
59-
|o| selcx.evaluate_obligation(o))
41+
let param_env = &ty::empty_parameter_environment(infcx.tcx);
42+
let selcx = &mut SelectionContext::intercrate(infcx, param_env);
43+
infcx.probe(|_| {
44+
overlap(selcx, impl1_def_id, impl2_def_id) || overlap(selcx, impl2_def_id, impl1_def_id)
45+
})
46+
}
47+
48+
/// Can the types from impl `a` be used to satisfy impl `b`?
49+
/// (Including all conditions)
50+
fn overlap(selcx: &mut SelectionContext,
51+
a_def_id: ast::DefId,
52+
b_def_id: ast::DefId)
53+
-> bool
54+
{
55+
let (a_trait_ref, a_obligations) = impl_trait_ref_and_oblig(selcx, a_def_id);
56+
let (b_trait_ref, b_obligations) = impl_trait_ref_and_oblig(selcx, b_def_id);
57+
58+
// Does `a <: b` hold? If not, no overlap.
59+
if let Err(_) = infer::mk_sub_poly_trait_refs(selcx.infcx(),
60+
true,
61+
infer::Misc(DUMMY_SP),
62+
a_trait_ref.to_poly_trait_ref(),
63+
b_trait_ref.to_poly_trait_ref()) {
64+
return false;
65+
}
66+
67+
// 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))
71+
}
72+
73+
/// Instantiate fresh variables for all bound parameters of the impl
74+
/// and return the impl trait ref with those variables substituted.
75+
fn impl_trait_ref_and_oblig<'a,'tcx>(selcx: &mut SelectionContext<'a,'tcx>,
76+
impl_def_id: ast::DefId)
77+
-> (Rc<ty::TraitRef<'tcx>>,
78+
Vec<PredicateObligation<'tcx>>)
79+
{
80+
let impl_substs =
81+
&util::fresh_substs_for_impl(selcx.infcx(), DUMMY_SP, impl_def_id);
82+
let impl_trait_ref =
83+
ty::impl_trait_ref(selcx.tcx(), impl_def_id).unwrap();
84+
let impl_trait_ref =
85+
impl_trait_ref.subst(selcx.tcx(), impl_substs);
86+
let Normalized { value: impl_trait_ref, obligations: normalization_obligations1 } =
87+
project::normalize(selcx, ObligationCause::dummy(), &impl_trait_ref);
88+
89+
let predicates = ty::lookup_predicates(selcx.tcx(), impl_def_id);
90+
let predicates = predicates.instantiate(selcx.tcx(), impl_substs);
91+
let Normalized { value: predicates, obligations: normalization_obligations2 } =
92+
project::normalize(selcx, ObligationCause::dummy(), &predicates);
93+
let impl_obligations =
94+
util::predicates_for_generics(selcx.tcx(), ObligationCause::dummy(), 0, &predicates);
95+
96+
let impl_obligations: Vec<_> =
97+
impl_obligations.into_iter()
98+
.chain(normalization_obligations1.into_iter())
99+
.chain(normalization_obligations2.into_iter())
100+
.collect();
101+
102+
(impl_trait_ref, impl_obligations)
60103
}
61104

62105
pub enum OrphanCheckErr<'tcx> {

src/librustc/middle/traits/mod.rs

+1-10
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use util::ppaux::{Repr, UserString};
2828
pub use self::error_reporting::report_fulfillment_errors;
2929
pub use self::error_reporting::suggest_new_overflow_limit;
3030
pub use self::coherence::orphan_check;
31+
pub use self::coherence::overlapping_impls;
3132
pub use self::coherence::OrphanCheckErr;
3233
pub use self::fulfill::{FulfillmentContext, RegionObligation};
3334
pub use self::project::MismatchedProjectionTypes;
@@ -270,16 +271,6 @@ pub struct VtableObjectData<'tcx> {
270271
pub object_ty: Ty<'tcx>,
271272
}
272273

273-
/// True if there exist types that satisfy both of the two given impls.
274-
pub fn overlapping_impls(infcx: &InferCtxt,
275-
impl1_def_id: ast::DefId,
276-
impl2_def_id: ast::DefId)
277-
-> bool
278-
{
279-
coherence::impl_can_satisfy(infcx, impl1_def_id, impl2_def_id) &&
280-
coherence::impl_can_satisfy(infcx, impl2_def_id, impl1_def_id)
281-
}
282-
283274
/// Creates predicate obligations from the generic bounds.
284275
pub fn predicates_for_generics<'tcx>(tcx: &ty::ctxt<'tcx>,
285276
cause: ObligationCause<'tcx>,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test that two distinct impls which match subtypes of one another
12+
// yield coherence errors (or not) depending on the variance.
13+
14+
trait Contravariant {
15+
fn foo(&self) { }
16+
}
17+
18+
impl Contravariant for for<'a,'b> fn(&'a u8, &'b u8) {
19+
//~^ ERROR E0119
20+
}
21+
22+
impl Contravariant for for<'a> fn(&'a u8, &'a u8) {
23+
}
24+
25+
///////////////////////////////////////////////////////////////////////////
26+
27+
trait Covariant {
28+
fn foo(&self) { }
29+
}
30+
31+
impl Covariant for for<'a,'b> fn(&'a u8, &'b u8) {
32+
//~^ ERROR E0119
33+
}
34+
35+
impl Covariant for for<'a> fn(&'a u8, &'a u8) {
36+
}
37+
38+
///////////////////////////////////////////////////////////////////////////
39+
40+
trait Invariant {
41+
fn foo(&self) -> Self { }
42+
}
43+
44+
impl Invariant for for<'a,'b> fn(&'a u8, &'b u8) {
45+
}
46+
47+
impl Invariant for for<'a> fn(&'a u8, &'a u8) {
48+
}
49+
50+
fn main() { }

0 commit comments

Comments
 (0)