Skip to content

Commit a4151ff

Browse files
committed
add a WF obligation if a type variable appears in bivariant position
1 parent a950c37 commit a4151ff

File tree

2 files changed

+106
-18
lines changed

2 files changed

+106
-18
lines changed

src/librustc/infer/combine.rs

+69-18
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ use ty::{IntType, UintType};
4343
use ty::{self, Ty, TyCtxt};
4444
use ty::error::TypeError;
4545
use ty::relate::{self, Relate, RelateResult, TypeRelation};
46-
use traits::PredicateObligations;
46+
use traits::{Obligation, PredicateObligations};
4747

4848
use syntax::ast;
4949
use syntax_pos::Span;
@@ -206,11 +206,16 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
206206
// `'?2` and `?3` are fresh region/type inference
207207
// variables. (Down below, we will relate `a_ty <: b_ty`,
208208
// adding constraints like `'x: '?2` and `?1 <: ?3`.)
209-
let b_ty = self.generalize(a_ty, b_vid, dir)?;
209+
let Generalization { ty: b_ty, needs_wf } = self.generalize(a_ty, b_vid, dir)?;
210210
debug!("instantiate(a_ty={:?}, dir={:?}, b_vid={:?}, generalized b_ty={:?})",
211211
a_ty, dir, b_vid, b_ty);
212212
self.infcx.type_variables.borrow_mut().instantiate(b_vid, b_ty);
213213

214+
if needs_wf {
215+
self.obligations.push(Obligation::new(self.trace.cause.clone(),
216+
ty::Predicate::WellFormed(b_ty)));
217+
}
218+
214219
// Finally, relate `b_ty` to `a_ty`, as described in previous comment.
215220
//
216221
// FIXME(#16847): This code is non-ideal because all these subtype
@@ -229,10 +234,9 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
229234

230235
/// Attempts to generalize `ty` for the type variable `for_vid`.
231236
/// This checks for cycle -- that is, whether the type `ty`
232-
/// references `for_vid`. If `is_eq_relation` is false, it will
233-
/// also replace all regions/unbound-type-variables with fresh
234-
/// variables. Returns `TyError` in the case of a cycle, `Ok`
235-
/// otherwise.
237+
/// references `for_vid`. The `dir` is the "direction" for which we
238+
/// a performing the generalization (i.e., are we producing a type
239+
/// that can be used as a supertype etc).
236240
///
237241
/// Preconditions:
238242
///
@@ -241,7 +245,7 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
241245
ty: Ty<'tcx>,
242246
for_vid: ty::TyVid,
243247
dir: RelationDir)
244-
-> RelateResult<'tcx, Ty<'tcx>>
248+
-> RelateResult<'tcx, Generalization<'tcx>>
245249
{
246250
// Determine the ambient variance within which `ty` appears.
247251
// The surrounding equation is:
@@ -261,9 +265,12 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
261265
span: self.trace.cause.span,
262266
for_vid_sub_root: self.infcx.type_variables.borrow_mut().sub_root_var(for_vid),
263267
ambient_variance: ambient_variance,
268+
needs_wf: false,
264269
};
265270

266-
generalize.relate(&ty, &ty)
271+
let ty = generalize.relate(&ty, &ty)?;
272+
let needs_wf = generalize.needs_wf;
273+
Ok(Generalization { ty, needs_wf })
267274
}
268275
}
269276

@@ -272,6 +279,41 @@ struct Generalizer<'cx, 'gcx: 'cx+'tcx, 'tcx: 'cx> {
272279
span: Span,
273280
for_vid_sub_root: ty::TyVid,
274281
ambient_variance: ty::Variance,
282+
needs_wf: bool, // see the field `needs_wf` in `Generalization`
283+
}
284+
285+
/// Result from a generalization operation. This includes
286+
/// not only the generalized type, but also a bool flag
287+
/// indicating whether further WF checks are needed.q
288+
struct Generalization<'tcx> {
289+
ty: Ty<'tcx>,
290+
291+
/// If true, then the generalized type may not be well-formed,
292+
/// even if the source type is well-formed, so we should add an
293+
/// additional check to enforce that it is. This arises in
294+
/// particular around 'bivariant' type parameters that are only
295+
/// constrained by a where-clause. As an example, imagine a type:
296+
///
297+
/// struct Foo<A, B> where A: Iterator<Item=B> {
298+
/// data: A
299+
/// }
300+
///
301+
/// here, `A` will be covariant, but `B` is
302+
/// unconstrained. However, whatever it is, for `Foo` to be WF, it
303+
/// must be equal to `A::Item`. If we have an input `Foo<?A, ?B>`,
304+
/// then after generalization we will wind up with a type like
305+
/// `Foo<?C, ?D>`. When we enforce that `Foo<?A, ?B> <: Foo<?C,
306+
/// ?D>` (or `>:`), we will wind up with the requirement that `?A
307+
/// <: ?C`, but no particular relationship between `?B` and `?D`
308+
/// (after all, we do not know the variance of the normalized form
309+
/// of `A::Item` with respect to `A`). If we do nothing else, this
310+
/// may mean that `?D` goes unconstrained (as in #41677). So, in
311+
/// this scenario where we create a new type variable in a
312+
/// bivariant context, we set the `needs_wf` flag to true. This
313+
/// will force the calling code to check that `WF(Foo<?C, ?D>)`
314+
/// holds, which in turn implies that `?C::Item == ?D`. So once
315+
/// `?C` is constrained, that should suffice to restrict `?D`.
316+
needs_wf: bool,
275317
}
276318

277319
impl<'cx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> for Generalizer<'cx, 'gcx, 'tcx> {
@@ -332,17 +374,26 @@ impl<'cx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> for Generalizer<'cx, 'gcx, '
332374
}
333375
None => {
334376
match self.ambient_variance {
335-
ty::Invariant => Ok(t),
336-
337-
ty::Bivariant | ty::Covariant | ty::Contravariant => {
338-
let origin = variables.origin(vid);
339-
let new_var_id = variables.new_var(false, origin, None);
340-
let u = self.tcx().mk_var(new_var_id);
341-
debug!("generalize: replacing original vid={:?} with new={:?}",
342-
vid, u);
343-
Ok(u)
344-
}
377+
// Invariant: no need to make a fresh type variable.
378+
ty::Invariant => return Ok(t),
379+
380+
// Bivariant: make a fresh var, but we
381+
// may need a WF predicate. See
382+
// comment on `needs_wf` field for
383+
// more info.
384+
ty::Bivariant => self.needs_wf = true,
385+
386+
// Co/contravariant: this will be
387+
// sufficiently constrained later on.
388+
ty::Covariant | ty::Contravariant => (),
345389
}
390+
391+
let origin = variables.origin(vid);
392+
let new_var_id = variables.new_var(false, origin, None);
393+
let u = self.tcx().mk_var(new_var_id);
394+
debug!("generalize: replacing original vid={:?} with new={:?}",
395+
vid, u);
396+
return Ok(u);
346397
}
347398
}
348399
}

src/test/run-pass/issue-41677.rs

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2016 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+
// Regression test for #41677. The local variable was winding up with
12+
// a type `Receiver<?T, H>` where `?T` was unconstrained, because we
13+
// failed to enforce the WF obligations and `?T` is a bivariant type
14+
// parameter position.
15+
16+
#![allow(unused_variables, dead_code)]
17+
18+
use std::marker::PhantomData;
19+
20+
trait Handle {
21+
type Inner;
22+
}
23+
24+
struct ResizingHandle<H>(PhantomData<H>);
25+
impl<H> Handle for ResizingHandle<H> {
26+
type Inner = H;
27+
}
28+
29+
struct Receiver<T, H: Handle<Inner=T>>(PhantomData<H>);
30+
31+
fn channel<T>(size: usize) -> Receiver<T, ResizingHandle<T>> {
32+
let rx = Receiver(PhantomData);
33+
rx
34+
}
35+
36+
fn main() {
37+
}

0 commit comments

Comments
 (0)