Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use equality in the coerce-unsized check #41937

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/librustc/infer/combine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,12 @@ use super::sub::Sub;
use super::InferCtxt;
use super::{MiscVariable, TypeTrace};

use hir::def_id::DefId;
use ty::{IntType, UintType};
use ty::{self, Ty, TyCtxt};
use ty::error::TypeError;
use ty::relate::{self, Relate, RelateResult, TypeRelation};
use ty::subst::Substs;
use traits::{Obligation, PredicateObligations};

use syntax::ast;
Expand Down Expand Up @@ -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())?))
}

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,
variance: ty::Variance,
a: &T,
Expand Down
51 changes: 49 additions & 2 deletions src/librustc_typeck/coherence/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,45 @@ pub fn coerce_unsized_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
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 diff_fields = fields.iter()
.enumerate()
Expand All @@ -260,8 +299,16 @@ pub fn coerce_unsized_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
return None;
}

// Ignore fields that aren't significantly changed
if let Ok(ok) = infcx.sub_types(false, &cause, b, a) {
// Ignore fields that aren't changed; it may
// 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() {
return None;
}
Expand Down
38 changes: 38 additions & 0 deletions src/test/run-pass/issue-41936-variance-coerce-unsized-cycle.rs
Original file line number Diff line number Diff line change
@@ -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(){}