diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 62e38ad9bfa66..c7b75be240d54 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1578,6 +1578,13 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { } } + pub fn is_transparent(&self) -> bool { + match self.sty { + Adt(def, _) => def.repr.transparent(), + _ => false, + } + } + pub fn sequence_element_type(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> { match self.sty { Array(ty, _) | Slice(ty) => ty, diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index bc7ad16dc97bc..2c4d10767b828 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -368,10 +368,15 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc ); // Recurse to get the size of the dynamically sized field (must be - // the last field). Can't have foreign types here, how would we - // adjust alignment and size for them? + // the last field). Can't have foreign types here unless they're + // in a #[repr(transparent)] struct, otherwise how would we adjust + // alignment and size for them? let field = layout.field(self, layout.fields.count() - 1)?; - let (unsized_size, unsized_align) = self.size_and_align_of(metadata, field)? + let unsized_size_and_align = self.size_and_align_of(metadata, field)?; + if unsized_size_and_align.is_none() && layout.ty.is_transparent() { + return Ok(None); + } + let (unsized_size, unsized_align) = unsized_size_and_align .expect("Fields cannot be extern types"); // FIXME (#26403, #27023): We should be adding padding diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 0eae2bfb226c6..23aa991ea764c 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -359,9 +359,13 @@ where // Offset may need adjustment for unsized fields let (meta, offset) = if field_layout.is_unsized() { // re-use parent metadata to determine dynamic field layout - let (_, align) = self.size_and_align_of(base.meta, field_layout)? - .expect("Fields cannot be extern types"); - (base.meta, offset.abi_align(align)) + let size_and_align = self.size_and_align_of(base.meta, field_layout)?; + if size_and_align.is_none() && base.layout.ty.is_transparent() { + (base.meta, offset) + } else { + let (_, align) = size_and_align.expect("Fields cannot be extern types"); + (base.meta, offset.abi_align(align)) + } } else { // base.meta could be present; we might be accessing a sized field of an unsized // struct. diff --git a/src/test/run-pass/extern/extern-types-in-transparent.rs b/src/test/run-pass/extern/extern-types-in-transparent.rs new file mode 100644 index 0000000000000..b8cf9fd66ea7e --- /dev/null +++ b/src/test/run-pass/extern/extern-types-in-transparent.rs @@ -0,0 +1,39 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// run-pass +#![allow(dead_code)] + +// Test that extern types can be used as fields within a transparent struct. See issue #55541. + +#![feature(const_transmute, extern_types)] + +extern { + type A; +} +unsafe impl Sync for A {} + +#[repr(transparent)] +struct Foo(A); + +#[repr(transparent)] +struct Bar(std::marker::PhantomData, A); + +static FOO: &'static Foo = { + static VALUE: usize = b'F' as usize; + unsafe { std::mem::transmute(&VALUE) } +}; + +static BAR: &'static Bar = { + static VALUE: usize = b'B' as usize; + unsafe { std::mem::transmute(&VALUE) } +}; + +fn main() {}