check non_exhaustive attr and private fields for transparent types
This commit is contained in:
parent
b3f4c31199
commit
944c0e23b8
@ -3132,6 +3132,56 @@
|
||||
"detects unexpected names and values in `#[cfg]` conditions",
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `repr_transparent_external_private_fields` lint
|
||||
/// detects types marked #[repr(trasparent)] that (transitively)
|
||||
/// contain an external ZST type marked #[non_exhaustive]
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust,ignore (needs external crate)
|
||||
/// #![deny(repr_transparent_external_private_fields)]
|
||||
/// use foo::NonExhaustiveZst;
|
||||
///
|
||||
/// #[repr(transparent)]
|
||||
/// struct Bar(u32, ([u32; 0], NonExhaustiveZst));
|
||||
/// ```
|
||||
///
|
||||
/// This will produce:
|
||||
///
|
||||
/// ```text
|
||||
/// error: deprecated `#[macro_use]` attribute used to import macros should be replaced at use sites with a `use` item to import the macro instead
|
||||
/// --> src/main.rs:3:1
|
||||
/// |
|
||||
/// 3 | #[macro_use]
|
||||
/// | ^^^^^^^^^^^^
|
||||
/// |
|
||||
/// note: the lint level is defined here
|
||||
/// --> src/main.rs:1:9
|
||||
/// |
|
||||
/// 1 | #![deny(repr_transparent_external_private_fields)]
|
||||
/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
/// ```
|
||||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// Previous, Rust accepted fields that contain external private zero-sized types,
|
||||
/// even though it should not be a breaking change to add a non-zero-sized field to
|
||||
/// that private type.
|
||||
///
|
||||
/// This is a [future-incompatible] lint to transition this
|
||||
/// to a hard error in the future. See [issue #78586] for more details.
|
||||
///
|
||||
/// [issue #78586]: https://github.com/rust-lang/rust/issues/78586
|
||||
/// [future-incompatible]: ../index.md#future-incompatible-lints
|
||||
pub REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
|
||||
Warn,
|
||||
"tranparent type contains an external ZST that is marked #[non_exhaustive] or contains private fields",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reference: "issue #78586 <https://github.com/rust-lang/rust/issues/78586>",
|
||||
};
|
||||
}
|
||||
|
||||
declare_lint_pass! {
|
||||
/// Does nothing as a lint pass, but registers some `Lint`s
|
||||
/// that are used by other parts of the compiler.
|
||||
@ -3237,6 +3287,7 @@
|
||||
DEPRECATED_WHERE_CLAUSE_LOCATION,
|
||||
TEST_UNSTABLE_LINT,
|
||||
FFI_UNWIND_CALLS,
|
||||
REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt};
|
||||
use rustc_infer::traits::Obligation;
|
||||
use rustc_lint::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES};
|
||||
use rustc_middle::ty::subst::GenericArgKind;
|
||||
@ -1318,7 +1319,8 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtD
|
||||
}
|
||||
}
|
||||
|
||||
// For each field, figure out if it's known to be a ZST and align(1)
|
||||
// For each field, figure out if it's known to be a ZST and align(1), with "known"
|
||||
// respecting #[non_exhaustive] attributes.
|
||||
let field_infos = adt.all_fields().map(|field| {
|
||||
let ty = field.ty(tcx, InternalSubsts::identity_for_item(tcx, field.did));
|
||||
let param_env = tcx.param_env(field.did);
|
||||
@ -1327,16 +1329,56 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtD
|
||||
let span = tcx.hir().span_if_local(field.did).unwrap();
|
||||
let zst = layout.map_or(false, |layout| layout.is_zst());
|
||||
let align1 = layout.map_or(false, |layout| layout.align.abi.bytes() == 1);
|
||||
(span, zst, align1)
|
||||
if !zst {
|
||||
return (span, zst, align1, None);
|
||||
}
|
||||
|
||||
fn check_non_exhaustive<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
t: Ty<'tcx>,
|
||||
) -> ControlFlow<(&'static str, DefId, SubstsRef<'tcx>, bool)> {
|
||||
match t.kind() {
|
||||
ty::Tuple(list) => list.iter().try_for_each(|t| check_non_exhaustive(tcx, t)),
|
||||
ty::Array(ty, _) => check_non_exhaustive(tcx, *ty),
|
||||
ty::Adt(def, subst) => {
|
||||
if !def.did().is_local() {
|
||||
let non_exhaustive = def.is_variant_list_non_exhaustive()
|
||||
|| def
|
||||
.variants()
|
||||
.iter()
|
||||
.any(ty::VariantDef::is_field_list_non_exhaustive);
|
||||
let has_priv = def.all_fields().any(|f| !f.vis.is_public());
|
||||
if non_exhaustive || has_priv {
|
||||
return ControlFlow::Break((
|
||||
def.descr(),
|
||||
def.did(),
|
||||
subst,
|
||||
non_exhaustive,
|
||||
));
|
||||
}
|
||||
}
|
||||
def.all_fields()
|
||||
.map(|field| field.ty(tcx, subst))
|
||||
.try_for_each(|t| check_non_exhaustive(tcx, t))
|
||||
}
|
||||
_ => ControlFlow::Continue(()),
|
||||
}
|
||||
}
|
||||
|
||||
(span, zst, align1, check_non_exhaustive(tcx, ty).break_value())
|
||||
});
|
||||
|
||||
let non_zst_fields =
|
||||
field_infos.clone().filter_map(|(span, zst, _align1)| if !zst { Some(span) } else { None });
|
||||
let non_zst_fields = field_infos
|
||||
.clone()
|
||||
.filter_map(|(span, zst, _align1, _non_exhaustive)| if !zst { Some(span) } else { None });
|
||||
let non_zst_count = non_zst_fields.clone().count();
|
||||
if non_zst_count >= 2 {
|
||||
bad_non_zero_sized_fields(tcx, adt, non_zst_count, non_zst_fields, sp);
|
||||
}
|
||||
for (span, zst, align1) in field_infos {
|
||||
let incompatible_zst_fields =
|
||||
field_infos.clone().filter(|(_, _, _, opt)| opt.is_some()).count();
|
||||
let incompat = incompatible_zst_fields + non_zst_count >= 2 && non_zst_count < 2;
|
||||
for (span, zst, align1, non_exhaustive) in field_infos {
|
||||
if zst && !align1 {
|
||||
struct_span_err!(
|
||||
tcx.sess,
|
||||
@ -1348,6 +1390,25 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtD
|
||||
.span_label(span, "has alignment larger than 1")
|
||||
.emit();
|
||||
}
|
||||
if incompat && let Some((descr, def_id, substs, non_exhaustive)) = non_exhaustive {
|
||||
tcx.struct_span_lint_hir(
|
||||
REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
|
||||
tcx.hir().local_def_id_to_hir_id(adt.did().expect_local()),
|
||||
span,
|
||||
|lint| {
|
||||
let note = if non_exhaustive {
|
||||
"is marked with `#[non_exhaustive]`"
|
||||
} else {
|
||||
"contains private fields"
|
||||
};
|
||||
let field_ty = tcx.def_path_str_with_substs(def_id, substs);
|
||||
lint.build("zero-sized fields in repr(transparent) cannot contain external non-exhaustive types")
|
||||
.note(format!("this {descr} contains `{field_ty}`, which {note}, \
|
||||
and makes it not a breaking change to become non-zero-sized in the future."))
|
||||
.emit();
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,10 @@
|
||||
#![crate_type = "lib"]
|
||||
|
||||
pub struct Private { _priv: () }
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct NonExhaustive {}
|
||||
|
||||
pub struct ExternalIndirection<T> {
|
||||
pub x: T,
|
||||
}
|
60
src/test/ui/repr/repr-transparent-non-exhaustive.rs
Normal file
60
src/test/ui/repr/repr-transparent-non-exhaustive.rs
Normal file
@ -0,0 +1,60 @@
|
||||
#![deny(repr_transparent_external_private_fields)]
|
||||
|
||||
// aux-build: repr-transparent-non-exhaustive.rs
|
||||
extern crate repr_transparent_non_exhaustive;
|
||||
|
||||
use repr_transparent_non_exhaustive::{Private, NonExhaustive, ExternalIndirection};
|
||||
|
||||
pub struct InternalPrivate {
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct InternalNonExhaustive;
|
||||
|
||||
pub struct InternalIndirection<T> {
|
||||
x: T,
|
||||
}
|
||||
|
||||
pub type Sized = i32;
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct T1(Sized, InternalPrivate);
|
||||
#[repr(transparent)]
|
||||
pub struct T2(Sized, InternalNonExhaustive);
|
||||
#[repr(transparent)]
|
||||
pub struct T3(Sized, InternalIndirection<(InternalPrivate, InternalNonExhaustive)>);
|
||||
#[repr(transparent)]
|
||||
pub struct T4(Sized, ExternalIndirection<(InternalPrivate, InternalNonExhaustive)>);
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct T5(Sized, Private);
|
||||
//~^ ERROR zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
|
||||
//~| WARN this was previously accepted by the compiler
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct T6(Sized, NonExhaustive);
|
||||
//~^ ERROR zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
|
||||
//~| WARN this was previously accepted by the compiler
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct T7(Sized, InternalIndirection<Private>);
|
||||
//~^ ERROR zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
|
||||
//~| WARN this was previously accepted by the compiler
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct T8(Sized, InternalIndirection<NonExhaustive>);
|
||||
//~^ ERROR zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
|
||||
//~| WARN this was previously accepted by the compiler
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct T9(Sized, ExternalIndirection<Private>);
|
||||
//~^ ERROR zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
|
||||
//~| WARN this was previously accepted by the compiler
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct T10(Sized, ExternalIndirection<NonExhaustive>);
|
||||
//~^ ERROR zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
|
||||
//~| WARN this was previously accepted by the compiler
|
||||
|
||||
fn main() {}
|
67
src/test/ui/repr/repr-transparent-non-exhaustive.stderr
Normal file
67
src/test/ui/repr/repr-transparent-non-exhaustive.stderr
Normal file
@ -0,0 +1,67 @@
|
||||
error: zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
|
||||
--> $DIR/repr-transparent-non-exhaustive.rs:31:22
|
||||
|
|
||||
LL | pub struct T5(Sized, Private);
|
||||
| ^^^^^^^
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/repr-transparent-non-exhaustive.rs:1:9
|
||||
|
|
||||
LL | #![deny(repr_transparent_external_private_fields)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
|
||||
= note: this struct contains `Private`, which contains private fields, and makes it not a breaking change to become non-zero-sized in the future.
|
||||
|
||||
error: zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
|
||||
--> $DIR/repr-transparent-non-exhaustive.rs:36:22
|
||||
|
|
||||
LL | pub struct T6(Sized, NonExhaustive);
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
|
||||
= note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
|
||||
|
||||
error: zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
|
||||
--> $DIR/repr-transparent-non-exhaustive.rs:41:22
|
||||
|
|
||||
LL | pub struct T7(Sized, InternalIndirection<Private>);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
|
||||
= note: this struct contains `Private`, which contains private fields, and makes it not a breaking change to become non-zero-sized in the future.
|
||||
|
||||
error: zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
|
||||
--> $DIR/repr-transparent-non-exhaustive.rs:46:22
|
||||
|
|
||||
LL | pub struct T8(Sized, InternalIndirection<NonExhaustive>);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
|
||||
= note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
|
||||
|
||||
error: zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
|
||||
--> $DIR/repr-transparent-non-exhaustive.rs:51:22
|
||||
|
|
||||
LL | pub struct T9(Sized, ExternalIndirection<Private>);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
|
||||
= note: this struct contains `Private`, which contains private fields, and makes it not a breaking change to become non-zero-sized in the future.
|
||||
|
||||
error: zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
|
||||
--> $DIR/repr-transparent-non-exhaustive.rs:56:23
|
||||
|
|
||||
LL | pub struct T10(Sized, ExternalIndirection<NonExhaustive>);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
|
||||
= note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
Loading…
Reference in New Issue
Block a user