diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index e80ea16f565a..8896d57ade5d 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -675,7 +675,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> { } } - // There's no good place to insert stability check for non-Copy unions, + // There's no good place to insert stability check for non-Copy or unsized unions, // so semi-randomly perform it here in stability.rs hir::ItemUnion(..) if !self.tcx.sess.features.borrow().untagged_unions => { let def_id = self.tcx.hir.local_def_id(item.id); @@ -692,6 +692,10 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> { emit_feature_err(&self.tcx.sess.parse_sess, "untagged_unions", item.span, GateIssue::Language, "unions with non-`Copy` fields are unstable"); + } else if !ty.is_sized(self.tcx, param_env, item.span) { + emit_feature_err(&self.tcx.sess.parse_sess, + "untagged_unions", item.span, GateIssue::Language, + "unsized unions are unstable"); } } } diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index dba23c22647f..cb4edaced569 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -1342,7 +1342,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { sized type"); } AdtKind::Union => { - err.note("no field of a union may have a dynamically sized type"); + err.note("only single-field unions may have a dynamically \ + sized type"); } AdtKind::Enum => { err.note("no field of an enum variant may have a dynamically sized type"); diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 9e24a4e6afac..5b3b04e30a8d 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -1957,7 +1957,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { (&ty::TyArray(..), &ty::TySlice(_)) => true, // Struct -> Struct. - (&ty::TyAdt(def_id_a, _), &ty::TyAdt(def_id_b, _)) if def_id_a.is_struct() => { + // Union -> Union. + (&ty::TyAdt(def_id_a, _), &ty::TyAdt(def_id_b, _)) + if def_id_a.is_struct() || def_id_a.is_union() => { def_id_a == def_id_b } diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index c3cd65230bd8..f32c8d10bd59 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -897,6 +897,7 @@ fn layout_raw<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, tcx.layout_depth.set(depth+1); let cx = LayoutCx { tcx, param_env }; let layout = cx.layout_raw_uncached(ty); + debug!("computed layout, ty = {:?}, layout = {:?}", ty, layout); tcx.layout_depth.set(depth); layout @@ -1208,6 +1209,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> { } let unsized_part = tcx.struct_tail(pointee); + debug!("compute_uncached: unsized_part = {:?}", unsized_part.sty); let metadata = match unsized_part.sty { ty::TyForeign(..) => { return Ok(tcx.intern_layout(LayoutDetails::scalar(self, data_ptr))); @@ -1220,7 +1222,11 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> { vtable.valid_range.start = 1; vtable } - _ => return Err(LayoutError::Unknown(unsized_part)) + _ => { + debug!("compute_uncached: unsized type with unknown \ + pointer layout: {:?}", unsized_part.sty); + return Err(LayoutError::Unknown(ty)) + } }; // Effectively a (ptr, meta) tuple. @@ -1361,6 +1367,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> { }).collect::, _>>()?; if def.is_union() { + debug!("compute_uncached: calculating layout for union: {:?}", ty.sty); let packed = def.repr.packed(); if packed && def.repr.align > 0 { bug!("Union cannot be packed and aligned"); @@ -1379,8 +1386,17 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> { } let mut size = Size::from_bytes(0); + let mut sized = true; for field in &variants[0] { - assert!(!field.is_unsized()); + if field.is_unsized() { + if variants[0].len() > 1 { + bug!("compute_uncached: field {:?} of union {:?} \ + is unsized, but is not the only field", + field, ty); + } else { + sized = false; + } + } if !packed { align = align.max(field.align); @@ -1391,7 +1407,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> { return Ok(tcx.intern_layout(LayoutDetails { variants: Variants::Single { index: 0 }, fields: FieldPlacement::Union(variants[0].len()), - abi: Abi::Aggregate { sized: true }, + abi: Abi::Aggregate { sized }, align, size: size.abi_align(align) })); diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 44771444c8aa..61e33c93234a 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -290,7 +290,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { loop { match ty.sty { ty::TyAdt(def, substs) => { - if !def.is_struct() { + if !def.is_struct() && !def.is_union() { break; } match def.non_enum_variant().fields.last() { @@ -328,7 +328,9 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { 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 a_def == b_def && ( + a_def.is_struct() || a_def.is_union() + ) => { if let Some(f) = a_def.non_enum_variant().fields.last() { a = f.ty(self, a_substs); b = f.ty(self, b_substs); diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs index 39e757c52ff2..2bcc4baf07e1 100644 --- a/src/librustc_typeck/check/wfcheck.rs +++ b/src/librustc_typeck/check/wfcheck.rs @@ -134,21 +134,21 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> { self.check_item_type(item); } hir::ItemStruct(ref struct_def, ref ast_generics) => { - self.check_type_defn(item, false, |fcx| { + self.check_type_defn(item, |fcx| { vec![fcx.non_enum_variant(struct_def)] }); self.check_variances_for_type_defn(item, ast_generics); } hir::ItemUnion(ref struct_def, ref ast_generics) => { - self.check_type_defn(item, true, |fcx| { + self.check_type_defn(item, |fcx| { vec![fcx.non_enum_variant(struct_def)] }); self.check_variances_for_type_defn(item, ast_generics); } hir::ItemEnum(ref enum_def, ref ast_generics) => { - self.check_type_defn(item, true, |fcx| { + self.check_type_defn(item, |fcx| { fcx.enum_variants(enum_def) }); @@ -221,7 +221,7 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> { } /// In a type definition, we check that to ensure that the types of the fields are well-formed. - fn check_type_defn(&mut self, item: &hir::Item, all_sized: bool, mut lookup_fields: F) + fn check_type_defn(&mut self, item: &hir::Item, mut lookup_fields: F) where F: for<'fcx, 'tcx> FnMut(&FnCtxt<'fcx, 'gcx, 'tcx>) -> Vec> { self.for_item(item).with_fcx(|fcx, this| { @@ -242,16 +242,37 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> { ty.needs_drop(this.tcx, this.tcx.param_env(def_id)) } }; - let unsized_len = if - all_sized || - variant.fields.is_empty() || - needs_drop_copy() - { - 0 + + let fields_that_must_be_sized = if needs_drop_copy() { + // all fields must be sized + // see definition of needs_drop_copy above^ + &variant.fields[..] } else { - 1 + match item.node { + hir::ItemStruct(..) => { + // last field in a struct may be unsized + &variant.fields[..variant.fields.len().max(1)-1] + } + hir::ItemUnion(..) => { + if variant.fields.len() == 1 { + // if there is only one field, it may be unsized + &[] + } else { + // otherwise all fields must be sized + &variant.fields[..] + } + } + hir::ItemEnum(..) => { + // all fields in an enum variant must be sized + &variant.fields[..] + } + + _ => span_bug!(item.span, + "unexpected item {:?}", item.node) + } }; - for field in &variant.fields[..variant.fields.len() - unsized_len] { + + for field in fields_that_must_be_sized { fcx.register_bound( field.ty, fcx.tcx.require_lang_item(lang_items::SizedTraitLangItem), diff --git a/src/test/compile-fail/union/union-unsized.rs b/src/test/compile-fail/union/union-unsized.rs index a238eaf05250..13437672d639 100644 --- a/src/test/compile-fail/union/union-unsized.rs +++ b/src/test/compile-fail/union/union-unsized.rs @@ -17,7 +17,7 @@ union U { union W { a: u8, - b: str, //~ ERROR the trait bound `str: std::marker::Sized` is not satisfied + b: str, } fn main() {} diff --git a/src/test/run-pass/union/union-unsized.rs b/src/test/run-pass/union/union-unsized.rs new file mode 100644 index 000000000000..a1b786e985b8 --- /dev/null +++ b/src/test/run-pass/union/union-unsized.rs @@ -0,0 +1,64 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(untagged_unions)] +#![feature(nll)] + +use std::ptr; + +trait Trait {} +impl Trait for T {} + +#[allow(unions_with_drop_fields)] +union NoDrop { + value: T, +} + +struct ShouldDrop<'a> { + dropped: &'a mut bool +} + +impl<'a> Drop for ShouldDrop<'a> { + fn drop(&mut self) { + *self.dropped = true; + } +} + +struct ShouldntDrop; + +impl Drop for ShouldntDrop { + fn drop(&mut self) { + unsafe { + panic!("This should not be dropped!"); + } + } +} + +fn main() { + let mut dropped = false; + { + let mut should_drop = &mut NoDrop { + value: ShouldDrop { + dropped: &mut dropped + } + } as &mut NoDrop; + + unsafe { + ptr::drop_in_place(&mut should_drop.value); + } + } + + assert!(dropped); + + // NoDrop will be dropped, but the ShouldntDrop won't be + Box::new(NoDrop { value: ShouldntDrop }) as Box>; + + // FIXME: do something with Bar +} diff --git a/src/test/ui/feature-gate-untagged_unions.rs b/src/test/ui/feature-gate-untagged_unions.rs index 6533fddd9471..e22d563680a7 100644 --- a/src/test/ui/feature-gate-untagged_unions.rs +++ b/src/test/ui/feature-gate-untagged_unions.rs @@ -32,4 +32,15 @@ impl Drop for U5 { fn drop(&mut self) {} } +// the non-`Copy` error message has priority +// replace this with an "unsized unions are unstable" error message +// if non-`Copy` unions are stabilized before unsized unions +union U6 { //~ ERROR unions with non-`Copy` fields are unstable + a: T +} + +union U7 { //~ ERROR unsized unions are unstable + a: T +} + fn main() {} diff --git a/src/test/ui/feature-gate-untagged_unions.stderr b/src/test/ui/feature-gate-untagged_unions.stderr index eef42e8b6fd6..00af921879d3 100644 --- a/src/test/ui/feature-gate-untagged_unions.stderr +++ b/src/test/ui/feature-gate-untagged_unions.stderr @@ -28,6 +28,16 @@ LL | | } | = help: add #![feature(untagged_unions)] to the crate attributes to enable -error: aborting due to 3 previous errors +error[E0658]: unions with non-`Copy` fields are unstable (see issue #32836) + --> $DIR/feature-gate-untagged_unions.rs:38:1 + | +LL | / union U6 { //~ ERROR unions with non-`Copy` fields are unstable +LL | | a: T +LL | | } + | |_^ + | + = help: add #![feature(untagged_unions)] to the crate attributes to enable + +error: aborting due to 4 previous errors If you want more information on this error, try using "rustc --explain E0658" diff --git a/src/test/ui/union/union-sized-field.rs b/src/test/ui/union/union-sized-field.rs index 8999f1e0930b..2dbb5fdc13ed 100644 --- a/src/test/ui/union/union-sized-field.rs +++ b/src/test/ui/union/union-sized-field.rs @@ -11,6 +11,7 @@ #![feature(untagged_unions)] union Foo { + t: u32, value: T, //~ ERROR the trait bound `T: std::marker::Sized` is not satisfied } diff --git a/src/test/ui/union/union-sized-field.stderr b/src/test/ui/union/union-sized-field.stderr index 4f2d00aaa3e2..9576380f6c63 100644 --- a/src/test/ui/union/union-sized-field.stderr +++ b/src/test/ui/union/union-sized-field.stderr @@ -1,15 +1,15 @@ error[E0277]: the trait bound `T: std::marker::Sized` is not satisfied - --> $DIR/union-sized-field.rs:14:5 + --> $DIR/union-sized-field.rs:15:5 | LL | value: T, //~ ERROR the trait bound `T: std::marker::Sized` is not satisfied | ^^^^^^^^ `T` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `T` = help: consider adding a `where T: std::marker::Sized` bound - = note: no field of a union may have a dynamically sized type + = note: only single-field unions may have a dynamically sized type error[E0277]: the trait bound `T: std::marker::Sized` is not satisfied - --> $DIR/union-sized-field.rs:18:5 + --> $DIR/union-sized-field.rs:19:5 | LL | value: T, //~ ERROR the trait bound `T: std::marker::Sized` is not satisfied | ^^^^^^^^ `T` does not have a constant size known at compile-time @@ -19,7 +19,7 @@ LL | value: T, //~ ERROR the trait bound `T: std::marker::Sized` is not sati = note: only the last field of a struct may have a dynamically sized type error[E0277]: the trait bound `T: std::marker::Sized` is not satisfied - --> $DIR/union-sized-field.rs:23:11 + --> $DIR/union-sized-field.rs:24:11 | LL | Value(T), //~ ERROR the trait bound `T: std::marker::Sized` is not satisfied | ^^ `T` does not have a constant size known at compile-time