Skip to content

Commit

Permalink
Add unsized tuple coercions.
Browse files Browse the repository at this point in the history
  • Loading branch information
qnighy committed Jun 8, 2017
1 parent d6ebe12 commit 1df08f2
Show file tree
Hide file tree
Showing 18 changed files with 549 additions and 18 deletions.
2 changes: 1 addition & 1 deletion src/librustc/traits/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1096,7 +1096,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
err.note("slice and array elements must have `Sized` type");
}
ObligationCauseCode::TupleElem => {
err.note("tuple elements must have `Sized` type");
err.note("only the last element of a tuple may have a dynamically sized type");
}
ObligationCauseCode::ProjectionWf(data) => {
err.note(&format!("required so that the projection `{}` is well-formed",
Expand Down
36 changes: 36 additions & 0 deletions src/librustc/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1653,6 +1653,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
def_id_a == def_id_b
}

// (.., T) -> (.., U).
(&ty::TyTuple(tys_a, _), &ty::TyTuple(tys_b, _)) => {
tys_a.len() == tys_b.len()
}

_ => false
};

Expand Down Expand Up @@ -2612,6 +2617,37 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
&[inner_target]));
}

// (.., T) -> (.., U).
(&ty::TyTuple(tys_a, _), &ty::TyTuple(tys_b, _)) => {
assert_eq!(tys_a.len(), tys_b.len());

// The last field of the tuple has to exist.
let (a_last, a_mid) = if let Some(x) = tys_a.split_last() {
x
} else {
return Err(Unimplemented);
};
let b_last = tys_b.last().unwrap();

// Check that the source tuple with the target's
// last element is a subtype of the target.
let new_tuple = tcx.mk_tup(a_mid.iter().chain(Some(b_last)), false);
let InferOk { obligations, .. } =
self.infcx.at(&obligation.cause, obligation.param_env)
.eq(target, new_tuple)
.map_err(|_| Unimplemented)?;
self.inferred_obligations.extend(obligations);

// Construct the nested T: Unsize<U> predicate.
nested.push(tcx.predicate_for_trait_def(
obligation.param_env,
obligation.cause.clone(),
obligation.predicate.def_id(),
obligation.recursion_depth + 1,
a_last,
&[b_last]));
}

_ => bug!()
};

Expand Down
10 changes: 7 additions & 3 deletions src/librustc/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1220,12 +1220,16 @@ impl<'a, 'tcx> Layout {
}

ty::TyTuple(tys, _) => {
// FIXME(camlorn): if we ever allow unsized tuples, this needs to be checked.
// See the univariant case below to learn how.
let kind = if tys.len() == 0 {
StructKind::AlwaysSizedUnivariant
} else {
StructKind::MaybeUnsizedUnivariant
};

let st = Struct::new(dl,
&tys.iter().map(|ty| ty.layout(tcx, param_env))
.collect::<Result<Vec<_>, _>>()?,
&ReprOptions::default(), StructKind::AlwaysSizedUnivariant, ty)?;
&ReprOptions::default(), kind, ty)?;
Univariant { variant: st, non_zero: false }
}

Expand Down
29 changes: 20 additions & 9 deletions src/librustc/ty/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -316,15 +316,26 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
target: Ty<'tcx>)
-> (Ty<'tcx>, Ty<'tcx>) {
let (mut a, mut b) = (source, target);
while let (&TyAdt(a_def, a_substs), &TyAdt(b_def, b_substs)) = (&a.sty, &b.sty) {
if a_def != b_def || !a_def.is_struct() {
break;
}
match a_def.struct_variant().fields.last() {
Some(f) => {
a = f.ty(self, a_substs);
b = f.ty(self, b_substs);
}
loop {
match (&a.sty, &b.sty) {
(&TyAdt(a_def, a_substs), &TyAdt(b_def, b_substs))
if a_def == b_def && a_def.is_struct() => {
if let Some(f) = a_def.struct_variant().fields.last() {
a = f.ty(self, a_substs);
b = f.ty(self, b_substs);
} else {
break;
}
},
(&TyTuple(a_tys, _), &TyTuple(b_tys, _))
if a_tys.len() == b_tys.len() => {
if let Some(a_last) = a_tys.last() {
a = a_last;
b = b_tys.last().unwrap();
} else {
break;
}
},
_ => break,
}
}
Expand Down
12 changes: 9 additions & 3 deletions src/librustc_trans/glue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ pub fn size_and_align_of_dst<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, inf
}
assert!(!info.is_null());
match t.sty {
ty::TyAdt(def, substs) => {
ty::TyAdt(..) | ty::TyTuple(..) => {
let ccx = bcx.ccx;
// First get the size of all statically known fields.
// Don't use size_of because it also rounds up to alignment, which we
Expand All @@ -101,8 +101,14 @@ pub fn size_and_align_of_dst<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, inf

// Recurse to get the size of the dynamically sized field (must be
// the last field).
let last_field = def.struct_variant().fields.last().unwrap();
let field_ty = monomorphize::field_ty(bcx.tcx(), substs, last_field);
let field_ty = match t.sty {
ty::TyAdt(def, substs) => {
let last_field = def.struct_variant().fields.last().unwrap();
monomorphize::field_ty(bcx.tcx(), substs, last_field)
},
ty::TyTuple(tys, _) => tys.last().unwrap(),
_ => unreachable!(),
};
let (unsized_size, unsized_align) = size_and_align_of_dst(bcx, field_ty, info);

// FIXME (#26403, #27023): We should be adding padding
Expand Down
48 changes: 48 additions & 0 deletions src/test/compile-fail/dst-bad-assign-3.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2014 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.

// Forbid assignment into a dynamically sized type.

type Fat<T: ?Sized> = (isize, &'static str, T);
//~^ WARNING trait bounds are not (yet) enforced

#[derive(PartialEq,Eq)]
struct Bar;

#[derive(PartialEq,Eq)]
struct Bar1 {
f: isize
}

trait ToBar {
fn to_bar(&self) -> Bar;
fn to_val(&self) -> isize;
}

impl ToBar for Bar1 {
fn to_bar(&self) -> Bar {
Bar
}
fn to_val(&self) -> isize {
self.f
}
}

pub fn main() {
// Assignment.
let f5: &mut Fat<ToBar> = &mut (5, "some str", Bar1 {f :42});
let z: Box<ToBar> = Box::new(Bar1 {f: 36});
f5.2 = Bar1 {f: 36};
//~^ ERROR mismatched types
//~| expected type `ToBar`
//~| found type `Bar1`
//~| expected trait ToBar, found struct `Bar1`
//~| ERROR `ToBar: std::marker::Sized` is not satisfied
}
12 changes: 12 additions & 0 deletions src/test/compile-fail/dst-bad-coerce1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,16 @@ pub fn main() {
let f2: &Fat<Foo> = &f1;
let f3: &Fat<Bar> = f2;
//~^ ERROR `Foo: Bar` is not satisfied

// Tuple with a vec of isize.
let f1 = ([1, 2, 3],);
let f2: &([isize; 3],) = &f1;
let f3: &([usize],) = f2;
//~^ ERROR mismatched types

// Tuple with a trait.
let f1 = (Foo,);
let f2: &(Foo,) = &f1;
let f3: &(Bar,) = f2;
//~^ ERROR `Foo: Bar` is not satisfied
}
10 changes: 10 additions & 0 deletions src/test/compile-fail/dst-bad-coerce2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,14 @@ pub fn main() {
let f1 = Fat { ptr: Foo };
let f2: &Fat<Foo> = &f1;
let f3: &mut Fat<Bar> = f2; //~ ERROR mismatched types

// Tuple with a vec of ints.
let f1 = ([1, 2, 3],);
let f2: &([isize; 3],) = &f1;
let f3: &mut ([isize],) = f2; //~ ERROR mismatched types

// Tuple with a trait.
let f1 = (Foo,);
let f2: &(Foo,) = &f1;
let f3: &mut (Bar,) = f2; //~ ERROR mismatched types
}
10 changes: 10 additions & 0 deletions src/test/compile-fail/dst-bad-coerce3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ fn baz<'a>() {
let f1 = Fat { ptr: Foo };
let f2: &Fat<Foo> = &f1; //~ ERROR `f1` does not live long enough
let f3: &'a Fat<Bar> = f2;

// Tuple with a vec of ints.
let f1 = ([1, 2, 3],);
let f2: &([isize; 3],) = &f1; //~ ERROR `f1` does not live long enough
let f3: &'a ([isize],) = f2;

// Tuple with a trait.
let f1 = (Foo,);
let f2: &(Foo,) = &f1; //~ ERROR `f1` does not live long enough
let f3: &'a (Bar,) = f2;
}

pub fn main() {
Expand Down
8 changes: 8 additions & 0 deletions src/test/compile-fail/dst-bad-coerce4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,12 @@ pub fn main() {
//~| expected type `&Fat<[isize; 3]>`
//~| found type `&Fat<[isize]>`
//~| expected array of 3 elements, found slice

// Tuple with a vec of isizes.
let f1: &([isize],) = &([1, 2, 3],);
let f2: &([isize; 3],) = f1;
//~^ ERROR mismatched types
//~| expected type `&([isize; 3],)`
//~| found type `&([isize],)`
//~| expected array of 3 elements, found slice
}
21 changes: 21 additions & 0 deletions src/test/compile-fail/dst-bad-deep-2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2014 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.

// Try to initialise a DST struct where the lost information is deeply nested.
// This is an error because it requires an unsized rvalue. This is a problem
// because it would require stack allocation of an unsized temporary (*g in the
// test).

pub fn main() {
let f: ([isize; 3],) = ([5, 6, 7],);
let g: &([isize],) = &f;
let h: &(([isize],),) = &(*g,);
//~^ ERROR `[isize]: std::marker::Sized` is not satisfied
}
3 changes: 2 additions & 1 deletion src/test/run-pass-valgrind/dst-dtor-1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ struct Fat<T: ?Sized> {

pub fn main() {
{
let _x: Box<Fat<Trait>> = Box::<Fat<Foo>>::new(Fat { f: Foo });
let _x: Box<(i32, Fat<Trait>)> =
Box::<(i32, Fat<Foo>)>::new((42, Fat { f: Foo }));
}
unsafe {
assert!(DROP_RAN);
Expand Down
3 changes: 2 additions & 1 deletion src/test/run-pass-valgrind/dst-dtor-2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ struct Fat<T: ?Sized> {

pub fn main() {
{
let _x: Box<Fat<[Foo]>> = Box::<Fat<[Foo; 3]>>::new(Fat { f: [Foo, Foo, Foo] });
let _x: Box<(Fat<[Foo]>,)> =
Box::<(Fat<[Foo; 3]>,)>::new((Fat { f: [Foo, Foo, Foo] },));
}
unsafe {
assert_eq!(DROP_RAN, 3);
Expand Down
10 changes: 10 additions & 0 deletions src/test/run-pass/dst-irrefutable-bind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,14 @@ fn main() {
let slice = &[1,2,3];
let x = Test(&slice);
let Test(&_slice) = x;


let x = (10, [1,2,3]);
let x : &(i32, [i32]) = &x;

let & ref _y = x;

let slice = &[1,2,3];
let x = (10, &slice);
let (_, &_slice) = x;
}
32 changes: 32 additions & 0 deletions src/test/run-pass/dst-raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ pub fn main() {
};
assert_eq!(r, 42);

// raw DST tuple
let p = (A { f: 42 },);
let o: *const (Trait,) = &p;
let r = unsafe {
(&*o).0.foo()
};
assert_eq!(r, 42);

// raw slice
let a: *const [_] = &[1, 2, 3];
unsafe {
Expand Down Expand Up @@ -72,6 +80,15 @@ pub fn main() {
assert_eq!(len, 3);
}

// raw DST tuple with slice
let c: *const ([_],) = &([1, 2, 3],);
unsafe {
let b = (&*c).0[0];
assert_eq!(b, 1);
let len = (&*c).0.len();
assert_eq!(len, 3);
}

// all of the above with *mut
let mut x = A { f: 42 };
let z: *mut Trait = &mut x;
Expand All @@ -87,6 +104,13 @@ pub fn main() {
};
assert_eq!(r, 42);

let mut p = (A { f: 42 },);
let o: *mut (Trait,) = &mut p;
let r = unsafe {
(&*o).0.foo()
};
assert_eq!(r, 42);

let a: *mut [_] = &mut [1, 2, 3];
unsafe {
let b = (*a)[2];
Expand All @@ -110,4 +134,12 @@ pub fn main() {
let len = (&*c).f.len();
assert_eq!(len, 3);
}

let c: *mut ([_],) = &mut ([1, 2, 3],);
unsafe {
let b = (&*c).0[0];
assert_eq!(b, 1);
let len = (&*c).0.len();
assert_eq!(len, 3);
}
}
Loading

0 comments on commit 1df08f2

Please sign in to comment.