trigger unsized coercions keyed on Sized bounds

This PR causes unsized coercions to not be disabled by `$0: Unsize<dyn
Object>` coercion obligations when we have an `$0: Sized` obligation
somewhere.

Note that `X: Unsize<dyn Object>` obligations can't fail *as
obligations* if `X: Sized` holds, so this still maintains some version
of monotonicity (I think that an unsized coercion can't be converted to
no coercion by unifying type variables).

Fixes #49593 (unblocking never_type).
This commit is contained in:
Ariel Ben-Yehuda 2018-11-25 16:14:54 +02:00
parent a8a2a887d0
commit 95bec6ed09
6 changed files with 197 additions and 55 deletions

View file

@ -1253,6 +1253,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
self.inlined_shallow_resolve(typ)
}
pub fn root_var(&self, var: ty::TyVid) -> ty::TyVid {
self.type_variables.borrow_mut().root_var(var)
}
pub fn resolve_type_vars_if_possible<T>(&self, value: &T) -> T
where
T: TypeFoldable<'tcx>,

View file

@ -20,7 +20,7 @@ use rustc::infer::LateBoundRegionConversionTime;
use rustc::infer::type_variable::TypeVariableOrigin;
use rustc::traits::Obligation;
use rustc::traits::error_reporting::ArgKind;
use rustc::ty::{self, ToPolyTraitRef, Ty, GenericParamDefKind};
use rustc::ty::{self, Ty, GenericParamDefKind};
use rustc::ty::fold::TypeFoldable;
use rustc::ty::subst::Substs;
use std::cmp;
@ -222,6 +222,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let fulfillment_cx = self.fulfillment_cx.borrow();
// Here `expected_ty` is known to be a type inference variable.
let expected_vid = self.root_var(expected_vid);
let expected_sig = fulfillment_cx
.pending_obligations()
.iter()
@ -235,13 +236,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// Given a Projection predicate, we can potentially infer
// the complete signature.
let trait_ref = proj_predicate.to_poly_trait_ref(self.tcx);
self.self_type_matches_expected_vid(trait_ref, expected_vid)
.and_then(|_| {
self.deduce_sig_from_projection(
Some(obligation.cause.span),
proj_predicate
)
})
Some(()).filter(|()| {
self.self_type_matches_expected_vid(trait_ref, expected_vid)
}).and_then(|()| {
self.deduce_sig_from_projection(
Some(obligation.cause.span),
proj_predicate
)
})
} else {
None
}
@ -252,34 +254,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// infer the kind. This can occur if there is a trait-reference
// like `F : Fn<A>`. Note that due to subtyping we could encounter
// many viable options, so pick the most restrictive.
let expected_kind = fulfillment_cx
.pending_obligations()
.iter()
.filter_map(|obligation| {
let opt_trait_ref = match obligation.predicate {
ty::Predicate::Projection(ref data) => Some(data.to_poly_trait_ref(self.tcx)),
ty::Predicate::Trait(ref data) => Some(data.to_poly_trait_ref()),
ty::Predicate::Subtype(..) => None,
ty::Predicate::RegionOutlives(..) => None,
ty::Predicate::TypeOutlives(..) => None,
ty::Predicate::WellFormed(..) => None,
ty::Predicate::ObjectSafe(..) => None,
ty::Predicate::ConstEvaluatable(..) => None,
// N.B., this predicate is created by breaking down a
// `ClosureType: FnFoo()` predicate, where
// `ClosureType` represents some `Closure`. It can't
// possibly be referring to the current closure,
// because we haven't produced the `Closure` for
// this closure yet; this is exactly why the other
// code is looking for a self type of a unresolved
// inference variable.
ty::Predicate::ClosureKind(..) => None,
};
opt_trait_ref
.and_then(|tr| self.self_type_matches_expected_vid(tr, expected_vid))
.and_then(|tr| self.tcx.lang_items().fn_trait_kind(tr.def_id()))
})
let expected_kind = self.obligations_for_self_ty(expected_vid)
.filter_map(|tr| self.tcx.lang_items().fn_trait_kind(tr.def_id()))
.fold(None, |best, cur| {
Some(best.map_or(cur, |best| cmp::min(best, cur)))
});
@ -339,22 +315,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
Some(ExpectedSig { cause_span, sig })
}
fn self_type_matches_expected_vid(
&self,
trait_ref: ty::PolyTraitRef<'tcx>,
expected_vid: ty::TyVid,
) -> Option<ty::PolyTraitRef<'tcx>> {
let self_ty = self.shallow_resolve(trait_ref.self_ty());
debug!(
"self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?})",
trait_ref, self_ty
);
match self_ty.sty {
ty::Infer(ty::TyVar(v)) if expected_vid == v => Some(trait_ref),
_ => None,
}
}
fn sig_of_closure(
&self,
expr_def_id: DefId,

View file

@ -579,7 +579,33 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
};
match selcx.select(&obligation.with(trait_ref)) {
// Uncertain or unimplemented.
Ok(None) |
Ok(None) => {
if trait_ref.def_id() == unsize_did {
let trait_ref = self.resolve_type_vars_if_possible(&trait_ref);
let self_ty = trait_ref.skip_binder().self_ty();
let unsize_ty = trait_ref.skip_binder().input_types().nth(1).unwrap();
debug!("coerce_unsized: ambiguous unsize case for {:?}", trait_ref);
match (&self_ty.sty, &unsize_ty.sty) {
(ty::Infer(ty::TyVar(v)),
ty::Dynamic(..)) if self.type_var_is_sized(*v) => {
debug!("coerce_unsized: have sized infer {:?}", v);
coercion.obligations.push(obligation);
// `$0: Unsize<dyn Trait>` where we know that `$0: Sized`, try going
// for unsizing.
}
_ => {
// Some other case for `$0: Unsize<Something>`. Note that we
// hit this case even if `Something` is a sized type, so just
// don't do the coercion.
debug!("coerce_unsized: ambiguous unsize");
return Err(TypeError::Mismatch);
}
}
} else {
debug!("coerce_unsized: early return - ambiguous");
return Err(TypeError::Mismatch);
}
}
Err(traits::Unimplemented) => {
debug!("coerce_unsized: early return - can't prove obligation");
return Err(TypeError::Mismatch);

View file

@ -113,8 +113,8 @@ use rustc::mir::interpret::{ConstValue, GlobalId};
use rustc::ty::subst::{CanonicalUserSubsts, UnpackedKind, Subst, Substs,
UserSelfTy, UserSubsts};
use rustc::traits::{self, ObligationCause, ObligationCauseCode, TraitEngine};
use rustc::ty::{self, AdtKind, Ty, TyCtxt, GenericParamDefKind, Visibility, ToPredicate,
RegionKind};
use rustc::ty::{self, AdtKind, Ty, TyCtxt, GenericParamDefKind, RegionKind, Visibility,
ToPolyTraitRef, ToPredicate};
use rustc::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
use rustc::ty::fold::TypeFoldable;
use rustc::ty::query::Providers;
@ -134,6 +134,7 @@ use std::collections::hash_map::Entry;
use std::cmp;
use std::fmt::Display;
use std::iter;
use std::vec;
use std::mem::replace;
use std::ops::{self, Deref};
use std::slice;
@ -2731,6 +2732,97 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
method.sig.output()
}
fn self_type_matches_expected_vid(
&self,
trait_ref: ty::PolyTraitRef<'tcx>,
expected_vid: ty::TyVid,
) -> bool {
let self_ty = self.shallow_resolve(trait_ref.self_ty());
debug!(
"self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?}, expected_vid={:?})",
trait_ref, self_ty, expected_vid
);
match self_ty.sty {
ty::Infer(ty::TyVar(v)) => {
let root_vid = self.root_var(v);
debug!("self_type_matches_expected_vid - root_vid={:?}", root_vid);
if root_vid == expected_vid {
true
} else {
false
}
}
_ => false
}
}
}
/// FIXME: impl Trait why u give me lifetime errors?
pub struct ObligationMapper<'a, 'gcx, 'tcx>(&'a FnCtxt<'a, 'gcx, 'tcx>, ty::TyVid);
impl<'a, 'gcx, 'tcx> FnOnce<(traits::PredicateObligation<'tcx>,)>
for ObligationMapper<'a, 'gcx, 'tcx>
{
type Output = Option<ty::PolyTraitRef<'tcx>>;
extern "rust-call" fn call_once(mut self, args: (traits::PredicateObligation<'tcx>,))
-> Self::Output {
self.call_mut(args)
}
}
impl<'a, 'gcx, 'tcx> FnMut<(traits::PredicateObligation<'tcx>,)>
for ObligationMapper<'a, 'gcx, 'tcx>
{
extern "rust-call" fn call_mut(&mut self, args: (traits::PredicateObligation<'tcx>,))
-> Self::Output {
match args.0.predicate {
ty::Predicate::Projection(ref data) => Some(data.to_poly_trait_ref(self.0.tcx)),
ty::Predicate::Trait(ref data) => Some(data.to_poly_trait_ref()),
ty::Predicate::Subtype(..) => None,
ty::Predicate::RegionOutlives(..) => None,
ty::Predicate::TypeOutlives(..) => None,
ty::Predicate::WellFormed(..) => None,
ty::Predicate::ObjectSafe(..) => None,
ty::Predicate::ConstEvaluatable(..) => None,
// N.B., this predicate is created by breaking down a
// `ClosureType: FnFoo()` predicate, where
// `ClosureType` represents some `Closure`. It can't
// possibly be referring to the current closure,
// because we haven't produced the `Closure` for
// this closure yet; this is exactly why the other
// code is looking for a self type of a unresolved
// inference variable.
ty::Predicate::ClosureKind(..) => None,
}.filter(|tr| {
self.0.self_type_matches_expected_vid(*tr, self.1)
})
}
}
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
fn obligations_for_self_ty<'b>(&'b self, self_ty: ty::TyVid)
-> iter::FilterMap<vec::IntoIter<traits::PredicateObligation<'tcx>>,
ObligationMapper<'b, 'gcx, 'tcx>>
{
let ty_var_root = self.root_var(self_ty);
debug!("obligations_for_self_ty: self_ty={:?} ty_var_root={:?} pending_obligations={:?}",
self_ty, ty_var_root,
self.fulfillment_cx.borrow().pending_obligations());
self.fulfillment_cx
.borrow()
.pending_obligations()
.into_iter()
.filter_map(ObligationMapper(self, ty_var_root))
}
fn type_var_is_sized(&self, self_ty: ty::TyVid) -> bool {
self.obligations_for_self_ty(self_ty).any(|tr| {
Some(tr.def_id()) == self.tcx.lang_items().sized_trait()
})
}
/// Generic function that factors out common logic from function calls,
/// method calls and overloaded operators.
fn check_argument_types(&self,

View file

@ -75,6 +75,7 @@ This API is completely unstable and subject to change.
#![feature(box_syntax)]
#![feature(crate_visibility_modifier)]
#![feature(exhaustive_patterns)]
#![feature(fn_traits)]
#![feature(nll)]
#![feature(quote)]
#![feature(refcell_replace_swap)]
@ -82,6 +83,7 @@ This API is completely unstable and subject to change.
#![feature(slice_patterns)]
#![feature(slice_sort_by_cached_key)]
#![feature(never_type)]
#![feature(unboxed_closures)]
#![recursion_limit="256"]

View file

@ -0,0 +1,58 @@
// Copyright 2018 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.
// compile-pass
#![feature(never_type)]
#![allow(unreachable_code)]
use std::error::Error;
use std::char::ParseCharError; /* some Error */
fn raw_ptr_box<T>(t: T) -> *mut T {
panic!()
}
fn foo(x: !) -> Box<Error> {
/* *mut $0 is coerced to *mut Error here */ Box::<_ /* ! */>::new(x)
}
fn foo_raw_ptr(x: !) -> *mut Error {
/* *mut $0 is coerced to *mut Error here */ raw_ptr_box::<_ /* ! */>(x)
}
fn no_coercion(d: *mut Error) -> *mut Error {
/* an unsize coercion won't compile here, and it is indeed not used
because there is nothing requiring the _ to be Sized */
d as *mut _
}
trait Xyz {}
struct S;
struct T;
impl Xyz for S {}
impl Xyz for T {}
fn foo_no_never() {
let mut x /* : Box<S> */ = None;
let mut first_iter = false;
loop {
if !first_iter {
let y: Box<Xyz>
= /* Box<$0> is coerced to Box<Xyz> here */ Box::new(x.unwrap());
}
x = Some(S);
first_iter = true;
}
}
fn main() {
}