diff options
| author | bors <bors@rust-lang.org> | 2022-07-13 11:10:51 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2022-07-13 11:10:51 +0000 |
| commit | ca4e39400ef33198e2715973d1c67a1d3cee15e7 (patch) | |
| tree | ef1ab0f047db44028413e3bdeb9ed024a8e4e491 /compiler | |
| parent | a639f89d0414a34b7a72702849f3e9f95b46de5c (diff) | |
| parent | f290811aaf4a434d7bf25dda1ff16fa6bd930c8f (diff) | |
Auto merge of #99203 - GuillaumeGomez:rollup-b2re0dv, r=GuillaumeGomez
Rollup of 10 pull requests Successful merges: - #98789 (rustdoc-json-types: Clean up derives.) - #98848 (Build the Clippy book as part of x.py doc) - #99020 (check non_exhaustive attr and private fields for transparent types) - #99132 (Add some autolabels for A-bootstrap and T-infra) - #99148 (Clarify that [iu]size bounds were only defined for the target arch) - #99152 (Use CSS variables to handle theming (part 2)) - #99168 (Add regression test for #74713) - #99176 (:arrow_up: rust-analyzer) - #99183 (Mention rust-analyzer maintainers when `proc_macro` bridge is changed) - #99185 (llvm-wrapper: adapt for LLVM API change) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_lint_defs/src/builtin.rs | 55 | ||||
| -rw-r--r-- | compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 6 | ||||
| -rw-r--r-- | compiler/rustc_typeck/src/check/check.rs | 71 |
3 files changed, 127 insertions, 5 deletions
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 9fc2249b290..6d2cb63c1d7 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3132,6 +3132,60 @@ declare_lint! { "detects unexpected names and values in `#[cfg]` conditions", } +declare_lint! { + /// The `repr_transparent_external_private_fields` lint + /// detects types marked `#[repr(transparent)]` that (transitively) + /// contain an external ZST type marked `#[non_exhaustive]` or containing + /// private fields + /// + /// ### 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: zero-sized fields in repr(transparent) cannot contain external non-exhaustive types + /// --> src/main.rs:5:28 + /// | + /// 5 | struct Bar(u32, ([u32; 0], NonExhaustiveZst)); + /// | ^^^^^^^^^^^^^^^^ + /// | + /// note: the lint level is defined here + /// --> src/main.rs:1:9 + /// | + /// 1 | #![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 `NonExhaustiveZst`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future. + /// ``` + /// + /// ### 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 +3291,7 @@ declare_lint_pass! { DEPRECATED_WHERE_CLAUSE_LOCATION, TEST_UNSTABLE_LINT, FFI_UNWIND_CALLS, + REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, ] } diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 8c5b4e2dc96..7ac3157e7a1 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -411,8 +411,14 @@ LLVMRustInlineAsm(LLVMTypeRef Ty, char *AsmString, size_t AsmStringLen, extern "C" bool LLVMRustInlineAsmVerify(LLVMTypeRef Ty, char *Constraints, size_t ConstraintsLen) { +#if LLVM_VERSION_LT(15, 0) return InlineAsm::Verify(unwrap<FunctionType>(Ty), StringRef(Constraints, ConstraintsLen)); +#else + // llvm::Error converts to true if it is an error. + return !llvm::errorToBool(InlineAsm::verify( + unwrap<FunctionType>(Ty), StringRef(Constraints, ConstraintsLen))); +#endif } extern "C" void LLVMRustAppendModuleInlineAsm(LLVMModuleRef M, const char *Asm, diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs index e9709b64d93..79edbeab9c7 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_typeck/src/check/check.rs @@ -17,6 +17,7 @@ use rustc_infer::infer::outlives::env::OutlivesEnvironment; 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(); + }, + ) + } } } |
