Skip to content

Commit 087d737

Browse files
committed
use equality in the coerce-unsized check
This seems both to be a safe, conservative choice, and it sidesteps the cycle in #41936. Fixes #41936.
1 parent 8eb6bdc commit 087d737

File tree

3 files changed

+106
-2
lines changed

3 files changed

+106
-2
lines changed

src/librustc/infer/combine.rs

+19
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,12 @@ use super::sub::Sub;
3939
use super::InferCtxt;
4040
use super::{MiscVariable, TypeTrace};
4141

42+
use hir::def_id::DefId;
4243
use ty::{IntType, UintType};
4344
use ty::{self, Ty, TyCtxt};
4445
use ty::error::TypeError;
4546
use ty::relate::{self, Relate, RelateResult, TypeRelation};
47+
use ty::subst::Substs;
4648
use traits::{Obligation, PredicateObligations};
4749

4850
use syntax::ast;
@@ -336,6 +338,23 @@ impl<'cx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> for Generalizer<'cx, 'gcx, '
336338
Ok(ty::Binder(self.relate(a.skip_binder(), b.skip_binder())?))
337339
}
338340

341+
fn relate_item_substs(&mut self,
342+
item_def_id: DefId,
343+
a_subst: &'tcx Substs<'tcx>,
344+
b_subst: &'tcx Substs<'tcx>)
345+
-> RelateResult<'tcx, &'tcx Substs<'tcx>>
346+
{
347+
if self.ambient_variance == ty::Variance::Invariant {
348+
// Avoid fetching the variance if we are in an invariant
349+
// context; no need, and it can induce dependency cycles
350+
// (e.g. #41849).
351+
relate::relate_substs(self, None, a_subst, b_subst)
352+
} else {
353+
let opt_variances = self.tcx().variances_of(item_def_id);
354+
relate::relate_substs(self, Some(&opt_variances), a_subst, b_subst)
355+
}
356+
}
357+
339358
fn relate_with_variance<T: Relate<'tcx>>(&mut self,
340359
variance: ty::Variance,
341360
a: &T,

src/librustc_typeck/coherence/builtin.rs

+49-2
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,45 @@ pub fn coerce_unsized_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
253253
return err_info;
254254
}
255255

256+
// Here we are considering a case of converting
257+
// `S<P0...Pn>` to S<Q0...Qn>`. As an example, let's imagine a struct `Foo<T, U>`,
258+
// which acts like a pointer to `U`, but carries along some extra data of type `T`:
259+
//
260+
// struct Foo<T, U> {
261+
// extra: T,
262+
// ptr: *mut U,
263+
// }
264+
//
265+
// We might have an impl that allows (e.g.) `Foo<T, [i32; 3]>` to be unsized
266+
// to `Foo<T, [i32]>`. That impl would look like:
267+
//
268+
// impl<T, U: Unsize<V>, V> CoerceUnsized<Foo<T, V>> for Foo<T, U> {}
269+
//
270+
// Here `U = [i32; 3]` and `V = [i32]`. At runtime,
271+
// when this coercion occurs, we would be changing the
272+
// field `ptr` from a thin pointer of type `*mut [i32;
273+
// 3]` to a fat pointer of type `*mut [i32]` (with
274+
// extra data `3`). **The purpose of this check is to
275+
// make sure that we know how to do this conversion.**
276+
//
277+
// To check if this impl is legal, we would walk down
278+
// the fields of `Foo` and consider their types with
279+
// both substitutes. We are looking to find that
280+
// exactly one (non-phantom) field has changed its
281+
// type, which we will expect to be the pointer that
282+
// is becoming fat (we could probably generalize this
283+
// to mutiple thin pointers of the same type becoming
284+
// fat, but we don't). In this case:
285+
//
286+
// - `extra` has type `T` before and type `T` after
287+
// - `ptr` has type `*mut U` before and type `*mut V` after
288+
//
289+
// Since just one field changed, we would then check
290+
// that `*mut U: CoerceUnsized<*mut V>` is implemented
291+
// (in other words, that we know how to do this
292+
// conversion). This will work out because `U:
293+
// Unsize<V>`, and we have a builtin rule that `*mut
294+
// U` can be coerced to `*mut V` if `U: Unsize<V>`.
256295
let fields = &def_a.struct_variant().fields;
257296
let diff_fields = fields.iter()
258297
.enumerate()
@@ -264,8 +303,16 @@ pub fn coerce_unsized_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
264303
return None;
265304
}
266305

267-
// Ignore fields that aren't significantly changed
268-
if let Ok(ok) = infcx.sub_types(false, &cause, b, a) {
306+
// Ignore fields that aren't changed; it may
307+
// be that we could get away with subtyping or
308+
// something more accepting, but we use
309+
// equality because we want to be able to
310+
// perform this check without computing
311+
// variance where possible. (This is because
312+
// we may have to evaluate constraint
313+
// expressions in the course of execution.)
314+
// See e.g. #41936.
315+
if let Ok(ok) = infcx.eq_types(false, &cause, b, a) {
269316
if ok.obligations.is_empty() {
270317
return None;
271318
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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 #41936. The coerce-unsized trait check in
12+
// coherence was using subtyping, which triggered variance
13+
// computation, which failed because it required type info for fields
14+
// that had not (yet) been computed.
15+
16+
#![feature(unsize)]
17+
#![feature(coerce_unsized)]
18+
19+
use std::{marker,ops};
20+
21+
// Change the array to a non-array, and error disappears
22+
// Adding a new field to the end keeps the error
23+
struct LogDataBuf([u8;8]);
24+
25+
struct Aref<T: ?Sized>
26+
{
27+
// Inner structure triggers the error, removing the inner removes the message.
28+
ptr: Box<ArefInner<T>>,
29+
}
30+
impl<T: ?Sized + marker::Unsize<U>, U: ?Sized> ops::CoerceUnsized<Aref<U>> for Aref<T> {}
31+
32+
struct ArefInner<T: ?Sized>
33+
{
34+
// Even with this field commented out, the error is raised.
35+
data: T,
36+
}
37+
38+
fn main(){}

0 commit comments

Comments
 (0)