Skip to content

Commit 88747ff

Browse files
committed
Add #[repr(no_niche)].
This repr-hint makes a struct/enum hide any niche within from its surrounding type-construction context. It is meant (at least initially) as an implementation detail for resolving issue 68303. We will not stabilize the repr-hint unless someone finds motivation for doing so. (So, declaration of `no_niche` feature lives in section of file where other internal implementation details are grouped, and deliberately leaves out the tracking issue number.) incorporated review feedback, and fixed post-rebase.
1 parent e6ec0d1 commit 88747ff

File tree

7 files changed

+56
-12
lines changed

7 files changed

+56
-12
lines changed

src/librustc/ty/layout.rs

+18-7
Original file line numberDiff line numberDiff line change
@@ -356,12 +356,14 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
356356
debug!("univariant offset: {:?} field: {:#?}", offset, field);
357357
offsets[i as usize] = offset;
358358

359-
if let Some(mut niche) = field.largest_niche.clone() {
360-
let available = niche.available(dl);
361-
if available > largest_niche_available {
362-
largest_niche_available = available;
363-
niche.offset += offset;
364-
largest_niche = Some(niche);
359+
if !repr.hide_niche() {
360+
if let Some(mut niche) = field.largest_niche.clone() {
361+
let available = niche.available(dl);
362+
if available > largest_niche_available {
363+
largest_niche_available = available;
364+
niche.offset += offset;
365+
largest_niche = Some(niche);
366+
}
365367
}
366368
}
367369

@@ -838,7 +840,11 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
838840
}
839841

840842
// Update `largest_niche` if we have introduced a larger niche.
841-
let niche = Niche::from_scalar(dl, Size::ZERO, scalar.clone());
843+
let niche = if def.repr.hide_niche() {
844+
None
845+
} else {
846+
Niche::from_scalar(dl, Size::ZERO, scalar.clone())
847+
};
842848
if let Some(niche) = niche {
843849
match &st.largest_niche {
844850
Some(largest_niche) => {
@@ -863,6 +869,11 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
863869
return Ok(tcx.intern_layout(st));
864870
}
865871

872+
// At this point, we have handled all unions and
873+
// structs. (We have also handled univariant enums
874+
// that allow representation optimization.)
875+
assert!(def.is_enum());
876+
866877
// The current code for niche-filling relies on variant indices
867878
// instead of actual discriminants, so dataful enums with
868879
// explicit discriminants (RFC #2363) would misbehave.

src/librustc/ty/mod.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -2041,7 +2041,8 @@ bitflags! {
20412041
const IS_TRANSPARENT = 1 << 2;
20422042
// Internal only for now. If true, don't reorder fields.
20432043
const IS_LINEAR = 1 << 3;
2044-
2044+
// If true, don't expose any niche to type's context.
2045+
const HIDE_NICHE = 1 << 4;
20452046
// Any of these flags being set prevent field reordering optimisation.
20462047
const IS_UNOPTIMISABLE = ReprFlags::IS_C.bits |
20472048
ReprFlags::IS_SIMD.bits |
@@ -2078,6 +2079,7 @@ impl ReprOptions {
20782079
ReprFlags::empty()
20792080
}
20802081
attr::ReprTransparent => ReprFlags::IS_TRANSPARENT,
2082+
attr::ReprNoNiche => ReprFlags::HIDE_NICHE,
20812083
attr::ReprSimd => ReprFlags::IS_SIMD,
20822084
attr::ReprInt(i) => {
20832085
size = Some(i);
@@ -2118,6 +2120,10 @@ impl ReprOptions {
21182120
pub fn linear(&self) -> bool {
21192121
self.flags.contains(ReprFlags::IS_LINEAR)
21202122
}
2123+
#[inline]
2124+
pub fn hide_niche(&self) -> bool {
2125+
self.flags.contains(ReprFlags::HIDE_NICHE)
2126+
}
21212127

21222128
pub fn discr_type(&self) -> attr::IntType {
21232129
self.int.unwrap_or(attr::SignedInt(ast::IntTy::Isize))

src/librustc_attr/builtin.rs

+2
Original file line numberDiff line numberDiff line change
@@ -838,6 +838,7 @@ pub enum ReprAttr {
838838
ReprSimd,
839839
ReprTransparent,
840840
ReprAlign(u32),
841+
ReprNoNiche,
841842
}
842843

843844
#[derive(Eq, PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone, HashStable_Generic)]
@@ -893,6 +894,7 @@ pub fn find_repr_attrs(sess: &ParseSess, attr: &Attribute) -> Vec<ReprAttr> {
893894
sym::packed => Some(ReprPacked(1)),
894895
sym::simd => Some(ReprSimd),
895896
sym::transparent => Some(ReprTransparent),
897+
sym::no_niche => Some(ReprNoNiche),
896898
name => int_type_of_word(name).map(ReprInt),
897899
};
898900

src/librustc_builtin_macros/deriving/generic/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -825,7 +825,8 @@ fn find_repr_type_name(sess: &ParseSess, type_attrs: &[ast::Attribute]) -> &'sta
825825
attr::ReprPacked(_)
826826
| attr::ReprSimd
827827
| attr::ReprAlign(_)
828-
| attr::ReprTransparent => continue,
828+
| attr::ReprTransparent
829+
| attr::ReprNoNiche => continue,
829830

830831
attr::ReprC => "i32",
831832

src/librustc_feature/active.rs

+4
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,10 @@ declare_features! (
204204
/// Added for testing E0705; perma-unstable.
205205
(active, test_2018_feature, "1.31.0", None, Some(Edition::Edition2018)),
206206

207+
/// Allows `#[repr(no_niche)]` (an implementation detail of `rustc`,
208+
/// it is not on path for eventual stabilization).
209+
(active, no_niche, "1.42.0", None, None),
210+
207211
// no-tracking-issue-end
208212

209213
// -------------------------------------------------------------------------

src/librustc_passes/check_attr.rs

+21-3
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
1616
use rustc_hir::DUMMY_HIR_ID;
1717
use rustc_hir::{self, HirId, Item, ItemKind, TraitItem};
1818
use rustc_session::lint::builtin::{CONFLICTING_REPR_HINTS, UNUSED_ATTRIBUTES};
19+
use rustc_session::parse::feature_err;
1920
use rustc_span::symbol::sym;
2021
use rustc_span::Span;
21-
use syntax::ast::Attribute;
22+
use syntax::ast::{Attribute, NestedMetaItem};
2223
use syntax::attr;
2324

2425
fn target_from_impl_item<'tcx>(tcx: TyCtxt<'tcx>, impl_item: &hir::ImplItem<'_>) -> Target {
@@ -287,6 +288,21 @@ impl CheckAttrVisitor<'tcx> {
287288
_ => ("a", "struct, enum, or union"),
288289
}
289290
}
291+
sym::no_niche => {
292+
if !self.tcx.features().enabled(sym::no_niche) {
293+
feature_err(
294+
&self.tcx.sess.parse_sess,
295+
sym::no_niche,
296+
hint.span(),
297+
"the attribute `repr(no_niche)` is currently unstable",
298+
)
299+
.emit();
300+
}
301+
match target {
302+
Target::Struct | Target::Enum => continue,
303+
_ => ("a", "struct or enum"),
304+
}
305+
}
290306
sym::i8
291307
| sym::u8
292308
| sym::i16
@@ -314,8 +330,10 @@ impl CheckAttrVisitor<'tcx> {
314330
// This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
315331
let hint_spans = hints.iter().map(|hint| hint.span());
316332

317-
// Error on repr(transparent, <anything else>).
318-
if is_transparent && hints.len() > 1 {
333+
// Error on repr(transparent, <anything else apart from no_niche>).
334+
let non_no_niche = |hint: &&NestedMetaItem| hint.name_or_empty() != sym::no_niche;
335+
let non_no_niche_count = hints.iter().filter(non_no_niche).count();
336+
if is_transparent && non_no_niche_count > 1 {
319337
let hint_spans: Vec<_> = hint_spans.clone().collect();
320338
struct_span_err!(
321339
self.tcx.sess,

src/librustc_span/symbol.rs

+2
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,7 @@ symbols! {
491491
non_exhaustive,
492492
non_modrs_mods,
493493
no_sanitize,
494+
no_niche,
494495
no_stack_check,
495496
no_start,
496497
no_std,
@@ -587,6 +588,7 @@ symbols! {
587588
repr128,
588589
repr_align,
589590
repr_align_enum,
591+
repr_no_niche,
590592
repr_packed,
591593
repr_simd,
592594
repr_transparent,

0 commit comments

Comments
 (0)