Rollup merge of #41937 - nikomatsakis:issue-41936-variance-coerce-unsized-cycle, r=eddyb
use equality in the coerce-unsized check This seems both to be a safe, conservative choice, and it sidesteps the cycle in #41849. Note that, before I converted variance into proper queries, we were using a hybrid of subtyping and equality, due to the presence of a flag that forced invariance if variance had not yet been computed. (Also, Coerce Unsized is unstable.) Fixes #41936. r? @eddyb
This commit is contained in:
commit
8f61055c52
3 changed files with 106 additions and 2 deletions
|
@ -39,10 +39,12 @@ use super::sub::Sub;
|
||||||
use super::InferCtxt;
|
use super::InferCtxt;
|
||||||
use super::{MiscVariable, TypeTrace};
|
use super::{MiscVariable, TypeTrace};
|
||||||
|
|
||||||
|
use hir::def_id::DefId;
|
||||||
use ty::{IntType, UintType};
|
use ty::{IntType, UintType};
|
||||||
use ty::{self, Ty, TyCtxt};
|
use ty::{self, Ty, TyCtxt};
|
||||||
use ty::error::TypeError;
|
use ty::error::TypeError;
|
||||||
use ty::relate::{self, Relate, RelateResult, TypeRelation};
|
use ty::relate::{self, Relate, RelateResult, TypeRelation};
|
||||||
|
use ty::subst::Substs;
|
||||||
use traits::{Obligation, PredicateObligations};
|
use traits::{Obligation, PredicateObligations};
|
||||||
|
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
|
@ -336,6 +338,23 @@ impl<'cx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> for Generalizer<'cx, 'gcx, '
|
||||||
Ok(ty::Binder(self.relate(a.skip_binder(), b.skip_binder())?))
|
Ok(ty::Binder(self.relate(a.skip_binder(), b.skip_binder())?))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn relate_item_substs(&mut self,
|
||||||
|
item_def_id: DefId,
|
||||||
|
a_subst: &'tcx Substs<'tcx>,
|
||||||
|
b_subst: &'tcx Substs<'tcx>)
|
||||||
|
-> RelateResult<'tcx, &'tcx Substs<'tcx>>
|
||||||
|
{
|
||||||
|
if self.ambient_variance == ty::Variance::Invariant {
|
||||||
|
// Avoid fetching the variance if we are in an invariant
|
||||||
|
// context; no need, and it can induce dependency cycles
|
||||||
|
// (e.g. #41849).
|
||||||
|
relate::relate_substs(self, None, a_subst, b_subst)
|
||||||
|
} else {
|
||||||
|
let opt_variances = self.tcx().variances_of(item_def_id);
|
||||||
|
relate::relate_substs(self, Some(&opt_variances), a_subst, b_subst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn relate_with_variance<T: Relate<'tcx>>(&mut self,
|
fn relate_with_variance<T: Relate<'tcx>>(&mut self,
|
||||||
variance: ty::Variance,
|
variance: ty::Variance,
|
||||||
a: &T,
|
a: &T,
|
||||||
|
|
|
@ -249,6 +249,45 @@ pub fn coerce_unsized_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
return err_info;
|
return err_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Here we are considering a case of converting
|
||||||
|
// `S<P0...Pn>` to S<Q0...Qn>`. As an example, let's imagine a struct `Foo<T, U>`,
|
||||||
|
// which acts like a pointer to `U`, but carries along some extra data of type `T`:
|
||||||
|
//
|
||||||
|
// struct Foo<T, U> {
|
||||||
|
// extra: T,
|
||||||
|
// ptr: *mut U,
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// We might have an impl that allows (e.g.) `Foo<T, [i32; 3]>` to be unsized
|
||||||
|
// to `Foo<T, [i32]>`. That impl would look like:
|
||||||
|
//
|
||||||
|
// impl<T, U: Unsize<V>, V> CoerceUnsized<Foo<T, V>> for Foo<T, U> {}
|
||||||
|
//
|
||||||
|
// Here `U = [i32; 3]` and `V = [i32]`. At runtime,
|
||||||
|
// when this coercion occurs, we would be changing the
|
||||||
|
// field `ptr` from a thin pointer of type `*mut [i32;
|
||||||
|
// 3]` to a fat pointer of type `*mut [i32]` (with
|
||||||
|
// extra data `3`). **The purpose of this check is to
|
||||||
|
// make sure that we know how to do this conversion.**
|
||||||
|
//
|
||||||
|
// To check if this impl is legal, we would walk down
|
||||||
|
// the fields of `Foo` and consider their types with
|
||||||
|
// both substitutes. We are looking to find that
|
||||||
|
// exactly one (non-phantom) field has changed its
|
||||||
|
// type, which we will expect to be the pointer that
|
||||||
|
// is becoming fat (we could probably generalize this
|
||||||
|
// to mutiple thin pointers of the same type becoming
|
||||||
|
// fat, but we don't). In this case:
|
||||||
|
//
|
||||||
|
// - `extra` has type `T` before and type `T` after
|
||||||
|
// - `ptr` has type `*mut U` before and type `*mut V` after
|
||||||
|
//
|
||||||
|
// Since just one field changed, we would then check
|
||||||
|
// that `*mut U: CoerceUnsized<*mut V>` is implemented
|
||||||
|
// (in other words, that we know how to do this
|
||||||
|
// conversion). This will work out because `U:
|
||||||
|
// Unsize<V>`, and we have a builtin rule that `*mut
|
||||||
|
// U` can be coerced to `*mut V` if `U: Unsize<V>`.
|
||||||
let fields = &def_a.struct_variant().fields;
|
let fields = &def_a.struct_variant().fields;
|
||||||
let diff_fields = fields.iter()
|
let diff_fields = fields.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
|
@ -260,8 +299,16 @@ pub fn coerce_unsized_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore fields that aren't significantly changed
|
// Ignore fields that aren't changed; it may
|
||||||
if let Ok(ok) = infcx.sub_types(false, &cause, b, a) {
|
// be that we could get away with subtyping or
|
||||||
|
// something more accepting, but we use
|
||||||
|
// equality because we want to be able to
|
||||||
|
// perform this check without computing
|
||||||
|
// variance where possible. (This is because
|
||||||
|
// we may have to evaluate constraint
|
||||||
|
// expressions in the course of execution.)
|
||||||
|
// See e.g. #41936.
|
||||||
|
if let Ok(ok) = infcx.eq_types(false, &cause, b, a) {
|
||||||
if ok.obligations.is_empty() {
|
if ok.obligations.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
// Regression test for #41936. The coerce-unsized trait check in
|
||||||
|
// coherence was using subtyping, which triggered variance
|
||||||
|
// computation, which failed because it required type info for fields
|
||||||
|
// that had not (yet) been computed.
|
||||||
|
|
||||||
|
#![feature(unsize)]
|
||||||
|
#![feature(coerce_unsized)]
|
||||||
|
|
||||||
|
use std::{marker,ops};
|
||||||
|
|
||||||
|
// Change the array to a non-array, and error disappears
|
||||||
|
// Adding a new field to the end keeps the error
|
||||||
|
struct LogDataBuf([u8;8]);
|
||||||
|
|
||||||
|
struct Aref<T: ?Sized>
|
||||||
|
{
|
||||||
|
// Inner structure triggers the error, removing the inner removes the message.
|
||||||
|
ptr: Box<ArefInner<T>>,
|
||||||
|
}
|
||||||
|
impl<T: ?Sized + marker::Unsize<U>, U: ?Sized> ops::CoerceUnsized<Aref<U>> for Aref<T> {}
|
||||||
|
|
||||||
|
struct ArefInner<T: ?Sized>
|
||||||
|
{
|
||||||
|
// Even with this field commented out, the error is raised.
|
||||||
|
data: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main(){}
|
Loading…
Add table
Add a link
Reference in a new issue