Skip to content

Commit 7e888f0

Browse files
committed
coercion: use a generalization of the target to specialize the source to coerce instead of propagating the wrong type.
1 parent a4c4cd8 commit 7e888f0

File tree

2 files changed

+254
-23
lines changed

2 files changed

+254
-23
lines changed

src/librustc_typeck/check/coercion.rs

+120-23
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,10 @@
6060
//! sort of a minor point so I've opted to leave it for later---after all
6161
//! we may want to adjust precisely when coercions occur.
6262
63-
use check::{autoderef, FnCtxt, LvaluePreference, UnresolvedTypeAction};
63+
use check::{autoderef, impl_self_ty, FnCtxt, LvaluePreference, UnresolvedTypeAction};
6464

6565
use middle::infer::{self, Coercion};
66+
use middle::subst::Substs;
6667
use middle::traits::{self, ObligationCause};
6768
use middle::traits::{predicate_for_trait_def, report_selection_error};
6869
use middle::ty::{AutoDerefRef, AdjustDerefRef};
@@ -79,6 +80,24 @@ struct Coerce<'a, 'tcx: 'a> {
7980

8081
type CoerceResult<'tcx> = RelateResult<'tcx, Option<ty::AutoAdjustment<'tcx>>>;
8182

83+
/// The result of an attempt at coercing a Source type to a
84+
/// Target type via unsizing (`Source: CoerceUnsized<Target>`).
85+
/// If successful, all `CoerceUnsized` and `Unsized` obligations were selected.
86+
/// Other obligations, such as `T: Trait` for `&T -> &Trait`, are provided
87+
/// alongside the adjustment, to be enforced later.
88+
type CoerceUnsizedResult<'tcx> = Result<(AutoDerefRef<'tcx>,
89+
Vec<traits::PredicateObligation<'tcx>>),
90+
CoerceUnsizedError>;
91+
92+
#[derive(PartialEq, Eq)]
93+
enum CoerceUnsizedError {
94+
/// Source definitely does not implement `CoerceUnsized<Target>`.
95+
Unapplicable,
96+
97+
/// Source might implement `CoerceUnsized<Target>`.
98+
Ambiguous,
99+
}
100+
82101
impl<'f, 'tcx> Coerce<'f, 'tcx> {
83102
fn tcx(&self) -> &ty::ctxt<'tcx> {
84103
self.fcx.tcx()
@@ -99,8 +118,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
99118

100119
// Consider coercing the subtype to a DST
101120
let unsize = self.fcx.infcx().commit_if_ok(|_| self.coerce_unsized(a, b));
102-
if unsize.is_ok() {
103-
return unsize;
121+
if let Ok((adjustment, leftover_predicates)) = unsize {
122+
for obligation in leftover_predicates {
123+
self.fcx.register_predicate(obligation);
124+
}
125+
return Ok(Some(AdjustDerefRef(adjustment)));
104126
}
105127

106128
// Examine the supertype and consider auto-borrowing.
@@ -119,23 +141,97 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
119141
_ => {}
120142
}
121143

144+
let b = self.fcx.infcx().shallow_resolve(b);
122145
match a.sty {
123146
ty::TyBareFn(Some(_), a_f) => {
124147
// Function items are coercible to any closure
125148
// type; function pointers are not (that would
126149
// require double indirection).
127-
self.coerce_from_fn_item(a, a_f, b)
150+
return self.coerce_from_fn_item(a, a_f, b);
128151
}
129152
ty::TyBareFn(None, a_f) => {
130153
// We permit coercion of fn pointers to drop the
131154
// unsafe qualifier.
132-
self.coerce_from_fn_pointer(a, a_f, b)
155+
return self.coerce_from_fn_pointer(a, a_f, b);
133156
}
134-
_ => {
135-
// Otherwise, just use subtyping rules.
136-
self.subtype(a, b)
157+
_ => {}
158+
}
159+
160+
// Attempt to generalize the expected type in hopes of an unsizing
161+
// coercion, where an intermediary stop-gap is usually necessary.
162+
// This is the case with trait method calls where the returned type
163+
// was not inferred, e.g. `Make::make(x: T): Box<Trait>`, if `Make`
164+
// has many implementations. Unsizing coercion will be ambiguous
165+
// and subtyping would result in a selection failure, if `Box<Trait>`
166+
// does not implement `Make`, but `Box<T>` does. The stop-gap fix
167+
// is `Make::make(x: T): Box<T>: Box<Trait>`.
168+
// In that same case, the following generalization will attempt to
169+
// apply `Box<_>` to the otherwise unconstrained `Make::make` return
170+
// type and trigger selection, hoping to get the unambiguous source
171+
// type `Box<T>` for the coercion to `Box<Trait>`.
172+
// Subtyping rules are used if the generalization and second attempt
173+
// at coercions through unsizing do not apply.
174+
175+
// The first unsizing coercion must have failed due to ambiguity.
176+
if unsize.err() != Some(CoerceUnsizedError::Ambiguous) {
177+
return self.subtype(a, b);
178+
}
179+
180+
// The target type needs to be a structure or Box<T>.
181+
// The only other types that implement CoerceUnsized are
182+
// references and pointers and those have multiple forms,
183+
// such as `*mut T -> *const Trait`.
184+
match b.sty {
185+
ty::TyBox(_) | ty::TyStruct(..) => {}
186+
_ => return self.subtype(a, b)
187+
}
188+
189+
// Construct a `Target: CoerceUnsized<Target>` predicate.
190+
let trait_predicate = ty::Binder(ty::TraitRef {
191+
def_id: self.tcx().lang_items.coerce_unsized_trait().unwrap(),
192+
substs: self.tcx().mk_substs(Substs::new_trait(vec![b], vec![], b))
193+
}).to_poly_trait_predicate();
194+
195+
// Select `Target: CoerceUnsized<Target>`.
196+
let mut selcx = traits::SelectionContext::new(self.fcx.infcx());
197+
let cause = ObligationCause::misc(self.origin.span(), self.fcx.body_id);
198+
let obligation = traits::Obligation::new(cause, trait_predicate);
199+
if let Ok(Some(traits::VtableImpl(i))) = selcx.select(&obligation) {
200+
// There is a single applicable impl. If `Target = P<Trait>`, then
201+
// the `Self` of this impl is some kind of supertype of `P<Trait>`,
202+
// most likely `P<T> forall T: Unsize<U>`.
203+
// This `Self` type, when all impl type parameters have been
204+
// substituted with fresh inference variables (e.g. `P<_>`),
205+
// will unify with the target type and all possible source types
206+
// for a coercion.
207+
// It can thus be used as a supertype of the source type,
208+
// the generalized form that can allow fulfilling pending
209+
// obligations and ultimately an unsizing coercion.
210+
let success = self.fcx.infcx().commit_if_ok(|_| {
211+
self.subtype(a, impl_self_ty(self.fcx,
212+
self.origin.span(),
213+
i.impl_def_id).ty)
214+
});
215+
216+
if success.is_ok() {
217+
// Select pending obligations to constrain the
218+
// source type further, and resolve it again.
219+
self.fcx.select_obligations_where_possible();
220+
let a = self.fcx.infcx().shallow_resolve(a);
221+
222+
// Finally, attempt a coercion by unsizing again,
223+
// now that the types are (hopefully) better known.
224+
let unsize = self.fcx.infcx().commit_if_ok(|_| self.coerce_unsized(a, b));
225+
if let Ok((adjustment, leftover_predicates)) = unsize {
226+
for obligation in leftover_predicates {
227+
self.fcx.register_predicate(obligation);
228+
}
229+
return Ok(Some(AdjustDerefRef(adjustment)));
230+
}
137231
}
138232
}
233+
234+
self.subtype(a, b)
139235
}
140236

141237
/// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`.
@@ -218,7 +314,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
218314
fn coerce_unsized(&self,
219315
source: Ty<'tcx>,
220316
target: Ty<'tcx>)
221-
-> CoerceResult<'tcx> {
317+
-> CoerceUnsizedResult<'tcx> {
222318
debug!("coerce_unsized(source={:?}, target={:?})",
223319
source,
224320
target);
@@ -229,7 +325,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
229325
(u, cu)
230326
} else {
231327
debug!("Missing Unsize or CoerceUnsized traits");
232-
return Err(TypeError::Mismatch);
328+
return Err(CoerceUnsizedError::Unapplicable);
233329
};
234330

235331
// Note, we want to avoid unnecessary unsizing. We don't want to coerce to
@@ -240,15 +336,19 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
240336
// Handle reborrows before selecting `Source: CoerceUnsized<Target>`.
241337
let (source, reborrow) = match (&source.sty, &target.sty) {
242338
(&ty::TyRef(_, mt_a), &ty::TyRef(_, mt_b)) => {
243-
try!(coerce_mutbls(mt_a.mutbl, mt_b.mutbl));
339+
if coerce_mutbls(mt_a.mutbl, mt_b.mutbl).is_err() {
340+
return Err(CoerceUnsizedError::Unapplicable);
341+
}
244342

245343
let coercion = Coercion(self.origin.span());
246344
let r_borrow = self.fcx.infcx().next_region_var(coercion);
247345
let region = self.tcx().mk_region(r_borrow);
248346
(mt_a.ty, Some(ty::AutoPtr(region, mt_b.mutbl)))
249347
}
250348
(&ty::TyRef(_, mt_a), &ty::TyRawPtr(mt_b)) => {
251-
try!(coerce_mutbls(mt_a.mutbl, mt_b.mutbl));
349+
if coerce_mutbls(mt_a.mutbl, mt_b.mutbl).is_err() {
350+
return Err(CoerceUnsizedError::Unapplicable);
351+
}
252352
(mt_a.ty, Some(ty::AutoUnsafe(mt_b.mutbl)))
253353
}
254354
_ => (source, None)
@@ -287,9 +387,13 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
287387
};
288388
match selcx.select(&obligation.with(trait_ref)) {
289389
// Uncertain or unimplemented.
290-
Ok(None) | Err(traits::Unimplemented) => {
291-
debug!("coerce_unsized: early return - can't prove obligation");
292-
return Err(TypeError::Mismatch);
390+
Ok(None) => {
391+
debug!("coerce_unsized: early return (Ambiguous)");
392+
return Err(CoerceUnsizedError::Ambiguous);
393+
}
394+
Err(traits::Unimplemented) => {
395+
debug!("coerce_unsized: early return (Unapplicable)");
396+
return Err(CoerceUnsizedError::Unapplicable);
293397
}
294398

295399
// Object safety violations or miscellaneous.
@@ -308,18 +412,13 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
308412
}
309413
}
310414

311-
// Save all the obligations that are not for CoerceUnsized or Unsize.
312-
for obligation in leftover_predicates {
313-
self.fcx.register_predicate(obligation);
314-
}
315-
316415
let adjustment = AutoDerefRef {
317416
autoderefs: if reborrow.is_some() { 1 } else { 0 },
318417
autoref: reborrow,
319418
unsize: Some(target)
320419
};
321420
debug!("Success, coerced with {:?}", adjustment);
322-
Ok(Some(AdjustDerefRef(adjustment)))
421+
Ok((adjustment, leftover_predicates))
323422
}
324423

325424
fn coerce_from_fn_pointer(&self,
@@ -333,7 +432,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
333432
* into a closure or a `proc`.
334433
*/
335434

336-
let b = self.fcx.infcx().shallow_resolve(b);
337435
debug!("coerce_from_fn_pointer(a={:?}, b={:?})",
338436
a, b);
339437

@@ -360,7 +458,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
360458
* into a closure or a `proc`.
361459
*/
362460

363-
let b = self.fcx.infcx().shallow_resolve(b);
364461
debug!("coerce_from_fn_item(a={:?}, b={:?})",
365462
a, b);
366463

+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// Copyright 2015 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+
use std::rc::Rc;
12+
13+
mod protocol {
14+
use std::ptr;
15+
16+
pub trait BoxPlace<T>: Place<T> {
17+
fn make() -> Self;
18+
}
19+
20+
pub trait Place<T> {
21+
// NOTE(eddyb) The use of `&mut T` here is to force
22+
// the LLVM `noalias` and `dereferenceable(sizeof(T))`
23+
// attributes, which are required for eliding the copy
24+
// and producing actual in-place initialization via RVO.
25+
// Neither of those attributes are present on `*mut T`,
26+
// but `&mut T` is not a great choice either, the proper
27+
// way might be to add those attributes to `Unique<T>`.
28+
unsafe fn pointer(&mut self) -> &mut T;
29+
}
30+
31+
pub trait Boxed<T>: Sized {
32+
type P: Place<T>;
33+
fn write_and_fin(mut place: Self::P,
34+
value: T)
35+
-> Self {
36+
unsafe {
37+
ptr::write(place.pointer(), value);
38+
Self::fin(place)
39+
}
40+
}
41+
unsafe fn fin(filled: Self::P) -> Self;
42+
}
43+
}
44+
45+
macro_rules! box_ {
46+
($x:expr) => {
47+
::protocol::Boxed::write_and_fin(::protocol::BoxPlace::make(), $x)
48+
}
49+
}
50+
51+
// Hacky implementations of the box protocol for Box<T> and Rc<T>.
52+
// They pass mem::uninitialized() to Box::new, and Rc::new, respectively,
53+
// to allocate memory and will leak the allocation in case of unwinding.
54+
mod boxed {
55+
use std::mem;
56+
use protocol;
57+
58+
pub struct Place<T> {
59+
ptr: *mut T
60+
}
61+
62+
impl<T> protocol::BoxPlace<T> for Place<T> {
63+
fn make() -> Place<T> {
64+
unsafe {
65+
Place {
66+
ptr: mem::transmute(Box::new(mem::uninitialized::<T>()))
67+
}
68+
}
69+
}
70+
}
71+
72+
impl<T> protocol::Place<T> for Place<T> {
73+
unsafe fn pointer(&mut self) -> &mut T { &mut *self.ptr }
74+
}
75+
76+
impl<T> protocol::Boxed<T> for Box<T> {
77+
type P = Place<T>;
78+
unsafe fn fin(place: Place<T>) -> Box<T> {
79+
mem::transmute(place.ptr)
80+
}
81+
}
82+
}
83+
84+
mod rc {
85+
use std::mem;
86+
use std::rc::Rc;
87+
use protocol;
88+
89+
pub struct Place<T> {
90+
rc_ptr: *mut (),
91+
data_ptr: *mut T
92+
}
93+
94+
impl<T> protocol::BoxPlace<T> for Place<T> {
95+
fn make() -> Place<T> {
96+
unsafe {
97+
let rc = Rc::new(mem::uninitialized::<T>());
98+
Place {
99+
data_ptr: &*rc as *const _ as *mut _,
100+
rc_ptr: mem::transmute(rc)
101+
}
102+
}
103+
}
104+
}
105+
106+
impl<T> protocol::Place<T> for Place<T> {
107+
unsafe fn pointer(&mut self) -> &mut T {
108+
&mut *self.data_ptr
109+
}
110+
}
111+
112+
impl<T> protocol::Boxed<T> for Rc<T> {
113+
type P = Place<T>;
114+
unsafe fn fin(place: Place<T>) -> Rc<T> {
115+
mem::transmute(place.rc_ptr)
116+
}
117+
}
118+
}
119+
120+
fn main() {
121+
let v = vec![1, 2, 3];
122+
123+
let bx: Box<_> = box_!(|| &v);
124+
let rc: Rc<_> = box_!(|| &v);
125+
126+
assert_eq!(bx(), &v);
127+
assert_eq!(rc(), &v);
128+
129+
let bx_trait: Box<Fn() -> _> = box_!(|| &v);
130+
let rc_trait: Rc<Fn() -> _> = box_!(|| &v);
131+
132+
assert_eq!(bx(), &v);
133+
assert_eq!(rc(), &v);
134+
}

0 commit comments

Comments
 (0)