Skip to content

Commit 426711d

Browse files
committed
Auto merge of #43786 - scalexm:issue-43784, r=nikomatsakis
Elaborate trait obligations when typechecking impls When typechecking trait impl declarations, we only checked that bounds explictly written on the trait declaration hold. We now also check that bounds which would have been implied by the trait reference do hold. Fixes #43784.
2 parents 0cbe6d8 + 68fd322 commit 426711d

File tree

3 files changed

+90
-5
lines changed

3 files changed

+90
-5
lines changed

src/librustc/ty/wf.rs

+47-5
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ pub fn trait_obligations<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
5858
-> Vec<traits::PredicateObligation<'tcx>>
5959
{
6060
let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![] };
61-
wf.compute_trait_ref(trait_ref);
61+
wf.compute_trait_ref(trait_ref, Elaborate::All);
6262
wf.normalize()
6363
}
6464

@@ -74,7 +74,7 @@ pub fn predicate_obligations<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
7474
// (*) ok to skip binders, because wf code is prepared for it
7575
match *predicate {
7676
ty::Predicate::Trait(ref t) => {
77-
wf.compute_trait_ref(&t.skip_binder().trait_ref); // (*)
77+
wf.compute_trait_ref(&t.skip_binder().trait_ref, Elaborate::None); // (*)
7878
}
7979
ty::Predicate::Equate(ref t) => {
8080
wf.compute(t.skip_binder().0);
@@ -114,6 +114,35 @@ struct WfPredicates<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
114114
out: Vec<traits::PredicateObligation<'tcx>>,
115115
}
116116

117+
/// Controls whether we "elaborate" supertraits and so forth on the WF
118+
/// predicates. This is a kind of hack to address #43784. The
119+
/// underlying problem in that issue was a trait structure like:
120+
///
121+
/// ```
122+
/// trait Foo: Copy { }
123+
/// trait Bar: Foo { }
124+
/// impl<T: Bar> Foo for T { }
125+
/// impl<T> Bar for T { }
126+
/// ```
127+
///
128+
/// Here, in the `Foo` impl, we will check that `T: Copy` holds -- but
129+
/// we decide that this is true because `T: Bar` is in the
130+
/// where-clauses (and we can elaborate that to include `T:
131+
/// Copy`). This wouldn't be a problem, except that when we check the
132+
/// `Bar` impl, we decide that `T: Foo` must hold because of the `Foo`
133+
/// impl. And so nowhere did we check that `T: Copy` holds!
134+
///
135+
/// To resolve this, we elaborate the WF requirements that must be
136+
/// proven when checking impls. This means that (e.g.) the `impl Bar
137+
/// for T` will be forced to prove not only that `T: Foo` but also `T:
138+
/// Copy` (which it won't be able to do, because there is no `Copy`
139+
/// impl for `T`).
140+
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
141+
enum Elaborate {
142+
All,
143+
None,
144+
}
145+
117146
impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
118147
fn cause(&mut self, code: traits::ObligationCauseCode<'tcx>) -> traits::ObligationCause<'tcx> {
119148
traits::ObligationCause::new(self.span, self.body_id, code)
@@ -135,12 +164,25 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
135164

136165
/// Pushes the obligations required for `trait_ref` to be WF into
137166
/// `self.out`.
138-
fn compute_trait_ref(&mut self, trait_ref: &ty::TraitRef<'tcx>) {
167+
fn compute_trait_ref(&mut self, trait_ref: &ty::TraitRef<'tcx>, elaborate: Elaborate) {
139168
let obligations = self.nominal_obligations(trait_ref.def_id, trait_ref.substs);
140-
self.out.extend(obligations);
141169

142170
let cause = self.cause(traits::MiscObligation);
143171
let param_env = self.param_env;
172+
173+
if let Elaborate::All = elaborate {
174+
let predicates = obligations.iter()
175+
.map(|obligation| obligation.predicate.clone())
176+
.collect();
177+
let implied_obligations = traits::elaborate_predicates(self.infcx.tcx, predicates);
178+
let implied_obligations = implied_obligations.map(|pred| {
179+
traits::Obligation::new(cause.clone(), param_env, pred)
180+
});
181+
self.out.extend(implied_obligations);
182+
}
183+
184+
self.out.extend(obligations);
185+
144186
self.out.extend(
145187
trait_ref.substs.types()
146188
.filter(|ty| !ty.has_escaping_regions())
@@ -156,7 +198,7 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
156198
// WF and (b) the trait-ref holds. (It may also be
157199
// normalizable and be WF that way.)
158200
let trait_ref = data.trait_ref(self.infcx.tcx);
159-
self.compute_trait_ref(&trait_ref);
201+
self.compute_trait_ref(&trait_ref, Elaborate::None);
160202

161203
if !data.has_escaping_regions() {
162204
let predicate = trait_ref.to_predicate();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2017 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+
pub trait Partial<X: ?Sized>: Copy {
12+
}
13+
14+
pub trait Complete {
15+
type Assoc: Partial<Self>;
16+
}
17+
18+
impl<T> Partial<T> for T::Assoc where
19+
T: Complete
20+
{
21+
}
22+
23+
impl<T> Complete for T { //~ ERROR the trait bound `T: std::marker::Copy` is not satisfied
24+
type Assoc = T;
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2017 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+
pub trait Partial: Copy {
12+
}
13+
14+
pub trait Complete: Partial {
15+
}
16+
17+
impl<T> Partial for T where T: Complete {}
18+
impl<T> Complete for T {} //~ ERROR the trait bound `T: std::marker::Copy` is not satisfied

0 commit comments

Comments
 (0)