diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs index 09c822f7508a0..035241a7965ab 100644 --- a/src/librustc/mir/interpret/error.rs +++ b/src/librustc/mir/interpret/error.rs @@ -137,6 +137,7 @@ impl<'tcx> ConstEvalErr<'tcx> { ) -> Result, ErrorHandled> { let must_error = match self.error { err_inval!(Layout(LayoutError::Unknown(_))) | + err_inval!(Layout(LayoutError::Unsized(_))) | err_inval!(TooGeneric) => return Err(ErrorHandled::TooGeneric), err_inval!(TypeckError) => diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 5ec4754c4535b..b383e458f6863 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -155,12 +155,16 @@ pub const FAT_PTR_EXTRA: usize = 1; #[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)] pub enum LayoutError<'tcx> { Unknown(Ty<'tcx>), - SizeOverflow(Ty<'tcx>) + SizeOverflow(Ty<'tcx>), + Unsized(Ty<'tcx>), } impl<'tcx> fmt::Display for LayoutError<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { + LayoutError::Unsized(ty) => { + write!(f, "the type `{:?}` has unknown size", ty) + } LayoutError::Unknown(ty) => { write!(f, "the type `{:?}` has an unknown layout", ty) } @@ -745,7 +749,11 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { let mut abi = Abi::Aggregate { sized: true }; let index = VariantIdx::new(0); for field in &variants[index] { - assert!(!field.is_unsized()); + if field.is_unsized() { + // Instead of asserting, return an error because this can happen with + // generators/async-await and unsized locals. + return Err(LayoutError::Unsized(field.ty)); + } align = align.max(field.align); // If all non-ZST fields have the same ABI, forward this ABI @@ -2492,6 +2500,7 @@ impl<'a, 'tcx> HashStable> for LayoutError<'tcx> { mem::discriminant(self).hash_stable(hcx, hasher); match *self { + Unsized(t) | Unknown(t) | SizeOverflow(t) => t.hash_stable(hcx, hasher) } diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 3b8c06ba154c6..17f17ab32e31d 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -999,6 +999,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VariantSizeDifferences { let ty = cx.tcx.erase_regions(&t); let layout = match cx.layout_of(ty) { Ok(layout) => layout, + Err(ty::layout::LayoutError::Unsized(_)) | Err(ty::layout::LayoutError::Unknown(_)) => return, Err(err @ ty::layout::LayoutError::SizeOverflow(_)) => { bug!("failed to get layout for `{}`: {}", t, err); diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 614d5d2a4a2fb..9c1603e658fea 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -669,28 +669,43 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> { let place_ty: Ty<'tcx> = place .ty(&self.local_decls, self.tcx) .ty; - if let Ok(place_layout) = self.tcx.layout_of(self.param_env.and(place_ty)) { - if let Some(value) = self.const_prop(rval, place_layout, statement.source_info) { - if let Place { - base: PlaceBase::Local(local), - projection: box [], - } = *place { - trace!("checking whether {:?} can be stored to {:?}", value, local); - if self.can_const_prop[local] { - trace!("storing {:?} to {:?}", value, local); - assert!(self.get_const(local).is_none()); - self.set_const(local, value); - - if self.should_const_prop() { - self.replace_with_const( - rval, - value, - statement.source_info, - ); + + match self.tcx.layout_of(self.param_env.and(place_ty)) { + Ok(place_layout) => { + if let Some(value) = self.const_prop( + rval, + place_layout, + statement.source_info, + ) { + if let Place { + base: PlaceBase::Local(local), + projection: box [], + } = *place { + trace!("checking whether {:?} can be stored to {:?}", value, local); + if self.can_const_prop[local] { + trace!("storing {:?} to {:?}", value, local); + assert!(self.get_const(local).is_none()); + self.set_const(local, value); + + if self.should_const_prop() { + self.replace_with_const( + rval, + value, + statement.source_info, + ); } } } } + Err(LayoutError::Unsized(_ty)) => { + let sp = statement.source_info.span; // async fn block + self.tcx.sess.struct_span_err( + sp, + "unsized values can't be used in `async` functions", + ).span_label(sp, "contains unsized values") + .emit(); + } + Err(_) => {} } } self.super_statement(statement, location); diff --git a/src/test/ui/async-await/unsized-locals.rs b/src/test/ui/async-await/unsized-locals.rs new file mode 100644 index 0000000000000..ab75c41be3b91 --- /dev/null +++ b/src/test/ui/async-await/unsized-locals.rs @@ -0,0 +1,26 @@ +// edition:2018 + +#![feature(unsized_locals)] +#![feature(gen_future)] + +use std::future::poll_with_tls_context; +use std::pin::Pin; +use std::fmt::Display; + +async fn foo2() {} + +async fn foo(x: Box) { //~ ERROR unsized values can't be used in `async` functions + let x = *x; + foo2().await; + println!("hello {}", &x); +} + +fn main() { + let mut a = foo(Box::new(5)); + let b = unsafe { + Pin::new_unchecked(&mut a) + }; + match poll_with_tls_context(b) { + _ => () + }; +} diff --git a/src/test/ui/async-await/unsized-locals.stderr b/src/test/ui/async-await/unsized-locals.stderr new file mode 100644 index 0000000000000..ac22f62f45566 --- /dev/null +++ b/src/test/ui/async-await/unsized-locals.stderr @@ -0,0 +1,13 @@ +error: unsized values can't be used in `async` functions + --> $DIR/unsized-locals.rs:12:35 + | +LL | async fn foo(x: Box) { + | ___________________________________^ +LL | | let x = *x; +LL | | foo2().await; +LL | | println!("hello {}", &x); +LL | | } + | |_^ contains unsized values + +error: aborting due to previous error +