Skip to content

Commit

Permalink
Auto merge of #56219 - arielb1:never-coerce-box, r=nikomatsakis
Browse files Browse the repository at this point in the history
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.

This should be mostly backwards-compatible, because in these cases not doing the unsize coercion should have caused the `$0: Sized` obligation to fail.

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).

r? @eddyb
cc @nikomatsakis
  • Loading branch information
bors committed Dec 19, 2018
2 parents d99a320 + 5b74438 commit 3b1845c
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 65 deletions.
4 changes: 4 additions & 0 deletions src/librustc/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1270,6 +1270,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>,
Expand Down
72 changes: 10 additions & 62 deletions src/librustc_typeck/check/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -219,13 +219,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
&self,
expected_vid: ty::TyVid,
) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) {
let fulfillment_cx = self.fulfillment_cx.borrow();
// Here `expected_ty` is known to be a type inference variable.

let expected_sig = fulfillment_cx
.pending_obligations()
.iter()
.filter_map(|obligation| {
let expected_sig = self.obligations_for_self_ty(expected_vid)
.find_map(|(_, obligation)| {
debug!(
"deduce_expectations_from_obligations: obligation.predicate={:?}",
obligation.predicate
Expand All @@ -234,52 +229,21 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
if let ty::Predicate::Projection(ref proj_predicate) = obligation.predicate {
// 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
)
})
self.deduce_sig_from_projection(
Some(obligation.cause.span),
proj_predicate
)
} else {
None
}
})
.next();
});

// Even if we can't infer the full signature, we may be able to
// 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)))
});
Expand Down Expand Up @@ -339,22 +303,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,
Expand Down
28 changes: 27 additions & 1 deletion src/librustc_typeck/check/coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
71 changes: 69 additions & 2 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -142,6 +142,7 @@ use require_c_abi_if_variadic;
use session::{CompileIncomplete, config, Session};
use TypeAndSubsts;
use lint;
use util::captures::Captures;
use util::common::{ErrorReported, indenter};
use util::nodemap::{DefIdMap, DefIdSet, FxHashMap, FxHashSet, NodeMap};

Expand Down Expand Up @@ -2731,6 +2732,72 @@ 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(found_vid)) => {
// FIXME: consider using `sub_root_var` here so we
// can see through subtyping.
let found_vid = self.root_var(found_vid);
debug!("self_type_matches_expected_vid - found_vid={:?}", found_vid);
expected_vid == found_vid
}
_ => false
}
}

fn obligations_for_self_ty<'b>(&'b self, self_ty: ty::TyVid)
-> impl Iterator<Item=(ty::PolyTraitRef<'tcx>, traits::PredicateObligation<'tcx>)>
+ Captures<'gcx> + 'b
{
// FIXME: consider using `sub_root_var` here so we
// can see through subtyping.
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(move |obligation| match obligation.predicate {
ty::Predicate::Projection(ref data) =>
Some((data.to_poly_trait_ref(self.tcx), obligation)),
ty::Predicate::Trait(ref data) =>
Some((data.to_poly_trait_ref(), obligation)),
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(move |(tr, _)| self.self_type_matches_expected_vid(*tr, 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,
Expand Down
62 changes: 62 additions & 0 deletions src/test/ui/coercion/coerce-issue-49593-box-never.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// 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::mem;

fn raw_ptr_box<T>(t: T) -> *mut T {
panic!()
}

fn foo(x: !) -> Box<dyn Error> {
/* *mut $0 is coerced to Box<dyn Error> here */ Box::<_ /* ! */>::new(x)
}

fn foo_raw_ptr(x: !) -> *mut dyn Error {
/* *mut $0 is coerced to *mut Error here */ raw_ptr_box::<_ /* ! */>(x)
}

fn no_coercion(d: *mut dyn Error) -> *mut dyn 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 /* : Option<S> */ = None;
let mut first_iter = false;
loop {
if !first_iter {
let y: Box<dyn Xyz>
= /* Box<$0> is coerced to Box<Xyz> here */ Box::new(x.unwrap());
}

x = Some(S);
first_iter = true;
}

let mut y : Option<S> = None;
// assert types are equal
mem::swap(&mut x, &mut y);
}

fn main() {
}

0 comments on commit 3b1845c

Please sign in to comment.