From 6a5fbe3b7db1c8d0273348b935ae7c9cc9c18575 Mon Sep 17 00:00:00 2001 From: Luca Versari Date: Sun, 27 Oct 2024 01:35:33 +0200 Subject: [PATCH] Draft implementation of the unsafe-fields RFC. Co-Authored-By: Jacob Pratt --- compiler/rustc_ast/src/ast.rs | 1 + compiler/rustc_ast/src/mut_visit.rs | 3 +- compiler/rustc_ast/src/visit.rs | 2 +- compiler/rustc_ast_lowering/src/item.rs | 1 + compiler/rustc_ast_passes/src/feature_gate.rs | 1 + compiler/rustc_expand/src/placeholders.rs | 3 +- compiler/rustc_feature/src/unstable.rs | 2 + compiler/rustc_hir/src/hir.rs | 1 + compiler/rustc_hir_analysis/messages.ftl | 7 ++ .../rustc_hir_analysis/src/check/check.rs | 105 +++++++++++++----- compiler/rustc_hir_analysis/src/collect.rs | 1 + compiler/rustc_hir_analysis/src/errors.rs | 23 ++++ compiler/rustc_metadata/src/rmeta/decoder.rs | 6 + compiler/rustc_metadata/src/rmeta/encoder.rs | 5 + compiler/rustc_metadata/src/rmeta/mod.rs | 1 + compiler/rustc_metadata/src/rmeta/table.rs | 7 ++ compiler/rustc_middle/src/ty/mod.rs | 17 ++- compiler/rustc_middle/src/ty/parameterized.rs | 1 + compiler/rustc_mir_build/messages.ftl | 30 +++++ .../rustc_mir_build/src/check_unsafety.rs | 90 +++++++++++---- compiler/rustc_mir_build/src/errors.rs | 67 +++++++++++ compiler/rustc_parse/src/parser/item.rs | 22 +++- compiler/rustc_span/src/symbol.rs | 1 + src/tools/rustfmt/src/items.rs | 6 +- .../rustfmt/tests/source/unsafe-field.rs | 16 +++ .../rustfmt/tests/target/unsafe-field.rs | 11 ++ .../feature-gate-unsafe_fields.rs | 23 ++++ ...ure-gate-unsafe_fields.without_gate.stderr | 33 ++++++ tests/ui/stats/hir-stats.stderr | 30 ++--- tests/ui/thir-print/thir-tree-match.stdout | 6 +- tests/ui/unsafe-fields.rs | 43 +++++++ tests/ui/unsafe-fields.stderr | 35 ++++++ 32 files changed, 521 insertions(+), 79 deletions(-) create mode 100644 src/tools/rustfmt/tests/source/unsafe-field.rs create mode 100644 src/tools/rustfmt/tests/target/unsafe-field.rs create mode 100644 tests/ui/feature-gates/feature-gate-unsafe_fields.rs create mode 100644 tests/ui/feature-gates/feature-gate-unsafe_fields.without_gate.stderr create mode 100644 tests/ui/unsafe-fields.rs create mode 100644 tests/ui/unsafe-fields.stderr diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 997c44cffff0..609d62fefa1a 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3036,6 +3036,7 @@ pub struct FieldDef { pub id: NodeId, pub span: Span, pub vis: Visibility, + pub safety: Safety, pub ident: Option, pub ty: P, diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 44bb44cb7288..4bb98778f482 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1044,10 +1044,11 @@ pub fn walk_flat_map_field_def( visitor: &mut T, mut fd: FieldDef, ) -> SmallVec<[FieldDef; 1]> { - let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _ } = &mut fd; + let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _, safety } = &mut fd; visitor.visit_id(id); visit_attrs(visitor, attrs); visitor.visit_vis(vis); + visit_safety(visitor, safety); visit_opt(ident, |ident| visitor.visit_ident(ident)); visitor.visit_ty(ty); visitor.visit_span(span); diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 2f8115441de9..50c64189f138 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -929,7 +929,7 @@ pub fn walk_struct_def<'a, V: Visitor<'a>>( } pub fn walk_field_def<'a, V: Visitor<'a>>(visitor: &mut V, field: &'a FieldDef) -> V::Result { - let FieldDef { attrs, id: _, span: _, vis, ident, ty, is_placeholder: _ } = field; + let FieldDef { attrs, id: _, span: _, vis, ident, ty, is_placeholder: _, safety: _ } = field; walk_list!(visitor, visit_attribute, attrs); try_visit!(visitor.visit_vis(vis)); visit_opt!(visitor, visit_ident, ident); diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index d72456075011..48b42fa2e976 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -724,6 +724,7 @@ impl<'hir> LoweringContext<'_, 'hir> { }, vis_span: self.lower_span(f.vis.span), ty, + safety: self.lower_safety(f.safety, hir::Safety::Safe), } } diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 396aac345156..26b36cd718fa 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -548,6 +548,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(global_registration, "global registration is experimental"); gate_all!(return_type_notation, "return type notation is experimental"); gate_all!(pin_ergonomics, "pinned reference syntax is experimental"); + gate_all!(unsafe_fields, "`unsafe` fields are experimental"); if !visitor.features.never_patterns() { if let Some(spans) = spans.get(&sym::never_patterns) { diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index 610c69e9d21b..aa8a4adc84d4 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -2,7 +2,7 @@ use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; use rustc_ast::token::Delimiter; use rustc_ast::visit::AssocCtxt; -use rustc_ast::{self as ast}; +use rustc_ast::{self as ast, Safety}; use rustc_data_structures::fx::FxHashMap; use rustc_span::DUMMY_SP; use rustc_span::symbol::Ident; @@ -173,6 +173,7 @@ pub(crate) fn placeholder( ty: ty(), vis, is_placeholder: true, + safety: Safety::Default, }]), AstFragmentKind::Variants => AstFragment::Variants(smallvec![ast::Variant { attrs: Default::default(), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 8326d0031ea6..a663ce15957a 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -623,6 +623,8 @@ declare_features! ( /// Allows creation of instances of a struct by moving fields that have /// not changed from prior instances of the same struct (RFC #2528) (unstable, type_changing_struct_update, "1.58.0", Some(86555)), + /// Allows declaring fields `unsafe`. + (incomplete, unsafe_fields, "CURRENT_RUSTC_VERSION", Some(1234567)), // FIXME: proper tracking issue. /// Allows const generic parameters to be defined with types that /// are not `Sized`, e.g. `fn foo() {`. (incomplete, unsized_const_params, "1.82.0", Some(95174)), diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 554097bf1151..16e53a27128b 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -3177,6 +3177,7 @@ pub struct FieldDef<'hir> { pub hir_id: HirId, pub def_id: LocalDefId, pub ty: &'hir Ty<'hir>, + pub safety: Safety, } impl FieldDef<'_> { diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 6e8ba51612ec..e6f74053253e 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -253,6 +253,13 @@ hir_analysis_invalid_union_field = hir_analysis_invalid_union_field_sugg = wrap the field type in `ManuallyDrop<...>` +hir_analysis_invalid_unsafe_field = + field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be unsafe + .note = unsafe fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>` + +hir_analysis_invalid_unsafe_field_sugg = + wrap the field type in `ManuallyDrop<...>` + hir_analysis_late_bound_const_in_apit = `impl Trait` can only mention const parameters from an fn or impl .label = const parameter declared here diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 3080d8b35108..ab99f72c9d45 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -6,7 +6,7 @@ use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::MultiSpan; use rustc_errors::codes::*; use rustc_hir::def::{CtorKind, DefKind}; -use rustc_hir::{Node, intravisit}; +use rustc_hir::{Node, Safety, intravisit}; use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::{Obligation, ObligationCauseCode}; use rustc_lint_defs::builtin::{ @@ -71,6 +71,7 @@ fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) { check_transparent(tcx, def); check_packed(tcx, span, def); + check_unsafe_fields(tcx, span, def_id); } fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) { @@ -82,38 +83,38 @@ fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) { check_packed(tcx, span, def); } +fn allowed_union_or_unsafe_field<'tcx>( + ty: Ty<'tcx>, + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, +) -> bool { + // We don't just accept all !needs_drop fields, due to semver concerns. + match ty.kind() { + ty::Ref(..) => true, // references never drop (even mutable refs, which are non-Copy and hence fail the later check) + ty::Tuple(tys) => { + // allow tuples of allowed types + tys.iter().all(|ty| allowed_union_or_unsafe_field(ty, tcx, param_env)) + } + ty::Array(elem, _len) => { + // Like `Copy`, we do *not* special-case length 0. + allowed_union_or_unsafe_field(*elem, tcx, param_env) + } + _ => { + // Fallback case: allow `ManuallyDrop` and things that are `Copy`, + // also no need to report an error if the type is unresolved. + ty.ty_adt_def().is_some_and(|adt_def| adt_def.is_manually_drop()) + || ty.is_copy_modulo_regions(tcx, param_env) + || ty.references_error() + } + } +} + /// Check that the fields of the `union` do not need dropping. fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> bool { let item_type = tcx.type_of(item_def_id).instantiate_identity(); if let ty::Adt(def, args) = item_type.kind() { assert!(def.is_union()); - fn allowed_union_field<'tcx>( - ty: Ty<'tcx>, - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> bool { - // We don't just accept all !needs_drop fields, due to semver concerns. - match ty.kind() { - ty::Ref(..) => true, // references never drop (even mutable refs, which are non-Copy and hence fail the later check) - ty::Tuple(tys) => { - // allow tuples of allowed types - tys.iter().all(|ty| allowed_union_field(ty, tcx, param_env)) - } - ty::Array(elem, _len) => { - // Like `Copy`, we do *not* special-case length 0. - allowed_union_field(*elem, tcx, param_env) - } - _ => { - // Fallback case: allow `ManuallyDrop` and things that are `Copy`, - // also no need to report an error if the type is unresolved. - ty.ty_adt_def().is_some_and(|adt_def| adt_def.is_manually_drop()) - || ty.is_copy_modulo_regions(tcx, param_env) - || ty.references_error() - } - } - } - let param_env = tcx.param_env(item_def_id); for field in &def.non_enum_variant().fields { let Ok(field_ty) = tcx.try_normalize_erasing_regions(param_env, field.ty(tcx, args)) @@ -122,7 +123,7 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b continue; }; - if !allowed_union_field(field_ty, tcx, param_env) { + if !allowed_union_or_unsafe_field(field_ty, tcx, param_env) { let (field_span, ty_span) = match tcx.hir().get_if_local(field.did) { // We are currently checking the type this field came from, so it must be local. Some(Node::Field(field)) => (field.span, field.ty.span), @@ -149,6 +150,53 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b true } +/// Check that the unsafe fields do not need dropping. +fn check_unsafe_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> bool { + let item_type = tcx.type_of(item_def_id).instantiate_identity(); + if let ty::Adt(def, args) = item_type.kind() { + let param_env = tcx.param_env(item_def_id); + for variant in def.variants() { + for field in &variant.fields { + if field.safety != Safety::Unsafe { + continue; + } + let Ok(field_ty) = + tcx.try_normalize_erasing_regions(param_env, field.ty(tcx, args)) + else { + tcx.dcx().span_delayed_bug(span, "could not normalize field type"); + continue; + }; + + if !allowed_union_or_unsafe_field(field_ty, tcx, param_env) { + let (field_span, ty_span) = match tcx.hir().get_if_local(field.did) { + // We are currently checking the type this field came from, so it must be local. + Some(Node::Field(field)) => (field.span, field.ty.span), + _ => unreachable!("mir field has to correspond to hir field"), + }; + tcx.dcx().emit_err(errors::InvalidUnsafeField { + field_span, + sugg: errors::InvalidUnsafeFieldSuggestion { + lo: ty_span.shrink_to_lo(), + hi: ty_span.shrink_to_hi(), + }, + note: (), + }); + return false; + } else if field_ty.needs_drop(tcx, param_env) { + // This should never happen. But we can get here e.g. in case of name resolution errors. + tcx.dcx().span_delayed_bug( + span, + "we should never accept maybe-dropping union fields", + ); + } + } + } + } else { + span_bug!(span, "structs/enums must be ty::Adt, but got {:?}", item_type.kind()); + } + true +} + /// Check that a `static` is inhabited. fn check_static_inhabited(tcx: TyCtxt<'_>, def_id: LocalDefId) { // Make sure statics are inhabited. @@ -1460,6 +1508,7 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) { detect_discriminant_duplicate(tcx, def); check_transparent(tcx, def); + check_unsafe_fields(tcx, tcx.def_span(def_id), def_id); } /// Part of enum check. Given the discriminants of an enum, errors if two or more discriminants are equal diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 63a0e7d31c3b..81e27217ae8b 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -1031,6 +1031,7 @@ fn lower_variant( did: f.def_id.to_def_id(), name: f.ident.name, vis: tcx.visibility(f.def_id), + safety: f.safety, }) .collect(); let recovered = match def { diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index a92a5e4278c5..ad5d5b9b62e6 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -734,6 +734,17 @@ pub(crate) struct InvalidUnionField { pub note: (), } +#[derive(Diagnostic)] +#[diag(hir_analysis_invalid_unsafe_field, code = E0740)] +pub(crate) struct InvalidUnsafeField { + #[primary_span] + pub field_span: Span, + #[subdiagnostic] + pub sugg: InvalidUnsafeFieldSuggestion, + #[note] + pub note: (), +} + #[derive(Diagnostic)] #[diag(hir_analysis_return_type_notation_on_non_rpitit)] pub(crate) struct ReturnTypeNotationOnNonRpitit<'tcx> { @@ -755,6 +766,18 @@ pub(crate) struct InvalidUnionFieldSuggestion { pub hi: Span, } +#[derive(Subdiagnostic)] +#[multipart_suggestion( + hir_analysis_invalid_unsafe_field_sugg, + applicability = "machine-applicable" +)] +pub(crate) struct InvalidUnsafeFieldSuggestion { + #[suggestion_part(code = "std::mem::ManuallyDrop<")] + pub lo: Span, + #[suggestion_part(code = ">")] + pub hi: Span, +} + #[derive(Diagnostic)] #[diag(hir_analysis_return_type_notation_equality_bound)] pub(crate) struct ReturnTypeNotationEqualityBound { diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 87357b74c411..56beff5aa642 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -15,6 +15,7 @@ use rustc_data_structures::sync::{Lock, Lrc, OnceLock}; use rustc_data_structures::unhash::UnhashMap; use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind}; use rustc_expand::proc_macro::{AttrProcMacro, BangProcMacro, DeriveProcMacro}; +use rustc_hir::Safety; use rustc_hir::def::Res; use rustc_hir::def_id::{CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::definitions::{DefPath, DefPathData}; @@ -1101,6 +1102,7 @@ impl<'a> CrateMetadataRef<'a> { did, name: self.item_name(did.index), vis: self.get_visibility(did.index), + safety: self.get_safety(did.index), }) .collect(), adt_kind, @@ -1162,6 +1164,10 @@ impl<'a> CrateMetadataRef<'a> { .map_id(|index| self.local_def_id(index)) } + fn get_safety(self, id: DefIndex) -> Safety { + self.root.tables.safety.get(self, id).unwrap_or_else(|| self.missing("safety", id)) + } + fn get_trait_item_def_id(self, id: DefIndex) -> Option { self.root.tables.trait_item_def_id.get(self, id).map(|d| d.decode_from_cdata(self)) } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index b5391247cea5..442c6a40a942 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1595,6 +1595,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { f.did.index })); + for field in &variant.fields { + // FIXME: this probably blows up rmeta size. + self.tables.safety.set_some(field.did.index, field.safety); + } + if let Some((CtorKind::Fn, ctor_def_id)) = variant.ctor { let fn_sig = tcx.fn_sig(ctor_def_id); // FIXME only encode signature for ctor_def_id diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 58f58efb116f..1f4f84f9706f 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -411,6 +411,7 @@ define_tables! { associated_item_or_field_def_ids: Table>, def_kind: Table, visibility: Table>>, + safety: Table, def_span: Table>, def_ident_span: Table>, lookup_stability: Table>, diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index 63abd2be9a57..9438350ca097 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -198,6 +198,13 @@ fixed_size_enum! { } } +fixed_size_enum! { + hir::Safety { + ( Unsafe ) + ( Safe ) + } +} + fixed_size_enum! { ty::Asyncness { ( Yes ) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 1a3128ed936a..2186974cb326 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -34,9 +34,9 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::steal::Steal; use rustc_data_structures::tagged_ptr::CopyTaggedPtr; use rustc_errors::{Diag, ErrorGuaranteed, StashKey}; -use rustc_hir::LangItem; use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap}; +use rustc_hir::{LangItem, Safety}; use rustc_index::IndexVec; use rustc_macros::{ Decodable, Encodable, HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable, @@ -1275,6 +1275,11 @@ impl VariantDef { pub fn tail(&self) -> &FieldDef { self.tail_opt().expect("expected unsized ADT to have a tail field") } + + /// Returns whether this variant has unsafe fields. + pub fn has_unsafe_fields(&self) -> bool { + self.fields.iter().any(|x| x.safety == Safety::Unsafe) + } } impl PartialEq for VariantDef { @@ -1357,6 +1362,7 @@ pub struct FieldDef { pub did: DefId, pub name: Symbol, pub vis: Visibility, + pub safety: hir::Safety, } impl PartialEq for FieldDef { @@ -1369,15 +1375,16 @@ impl PartialEq for FieldDef { // of `FieldDef` changes, a compile-error will be produced, reminding // us to revisit this assumption. - let Self { did: lhs_did, name: _, vis: _ } = &self; + let Self { did: lhs_did, name: _, vis: _, safety: _ } = &self; - let Self { did: rhs_did, name: _, vis: _ } = other; + let Self { did: rhs_did, name: _, vis: _, safety: _ } = other; let res = lhs_did == rhs_did; // Double check that implicit assumption detailed above. if cfg!(debug_assertions) && res { - let deep = self.name == other.name && self.vis == other.vis; + let deep = + self.name == other.name && self.vis == other.vis && self.safety == other.safety; assert!(deep, "FieldDef for the same def-id has differing data"); } @@ -1397,7 +1404,7 @@ impl Hash for FieldDef { // of `FieldDef` changes, a compile-error will be produced, reminding // us to revisit this assumption. - let Self { did, name: _, vis: _ } = &self; + let Self { did, name: _, vis: _, safety: _ } = &self; did.hash(s) } diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index 7c280bc8b49e..f7322217aa33 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -86,6 +86,7 @@ trivially_parameterized_over_tcx! { rustc_attr::Stability, rustc_hir::Constness, rustc_hir::Defaultness, + rustc_hir::Safety, rustc_hir::CoroutineKind, rustc_hir::IsAsync, rustc_hir::LangItem, diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index 55149570dbc4..02ed3b09053b 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -125,6 +125,16 @@ mir_build_initializing_type_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed .note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior .label = initializing type with `rustc_layout_scalar_valid_range` attr +mir_build_initializing_type_with_unsafe_field_requires_unsafe = + initializing type with an unsafe field is unsafe and requires unsafe block + .note = unsafe fields may carry library invariants + .label = initialization of struct with unsafe field + +mir_build_initializing_type_with_unsafe_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = + initializing type with an unsafe field is unsafe and requires unsafe block + .note = unsafe fields may carry library invariants + .label = initialization of struct with unsafe field + mir_build_inline_assembly_requires_unsafe = use of inline assembly is unsafe and requires unsafe block .note = inline assembly is entirely unchecked and can cause undefined behavior @@ -340,6 +350,16 @@ mir_build_unreachable_pattern = unreachable pattern .unreachable_covered_by_many = multiple earlier patterns match some of the same values .suggestion = remove the match arm +mir_build_unsafe_field_requires_unsafe = + use of unsafe field is unsafe and requires unsafe block + .note = unsafe fields may carry library invariants + .label = use of unsafe field + +mir_build_unsafe_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = + use of unsafe field is unsafe and requires unsafe block + .note = unsafe fields may carry library invariants + .label = use of unsafe field + mir_build_unsafe_fn_safe_body = an unsafe function restricts its caller, but its body is safe by default mir_build_unsafe_not_inherited = items do not inherit unsafety from separate enclosing items @@ -388,6 +408,11 @@ mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_requires_unsafe = .note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior .label = initializing type with `rustc_layout_scalar_valid_range` attr +mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_unsafe_field_requires_unsafe = + initializing type with an unsafe field is unsafe and requires unsafe block + .note = unsafe fields may carry library invariants + .label = initialization of struct with unsafe field + mir_build_unsafe_op_in_unsafe_fn_inline_assembly_requires_unsafe = use of inline assembly is unsafe and requires unsafe block .note = inline assembly is entirely unchecked and can cause undefined behavior @@ -408,6 +433,11 @@ mir_build_unsafe_op_in_unsafe_fn_union_field_requires_unsafe = .note = the field may not be properly initialized: using uninitialized data will cause undefined behavior .label = access to union field +mir_build_unsafe_op_in_unsafe_fn_unsafe_field_requires_unsafe = + use of unsafe field is unsafe and requires unsafe block + .note = unsafe fields may carry library invariants + .label = use of unsafe field + mir_build_unsized_pattern = cannot use unsized non-slice type `{$non_sm_ty}` in constant patterns mir_build_unused_unsafe = unnecessary `unsafe` block diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index f3e6301d9d16..f59ce977671c 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -4,7 +4,7 @@ use std::ops::Bound; use rustc_errors::DiagArgValue; use rustc_hir::def::DefKind; -use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability}; +use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability, Safety}; use rustc_middle::middle::codegen_fn_attrs::TargetFeature; use rustc_middle::mir::BorrowKind; use rustc_middle::span_bug; @@ -538,15 +538,20 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { } ExprKind::Adt(box AdtExpr { adt_def, - variant_index: _, + variant_index, args: _, user_ty: _, fields: _, base: _, - }) => match self.tcx.layout_scalar_valid_range(adt_def.did()) { - (Bound::Unbounded, Bound::Unbounded) => {} - _ => self.requires_unsafe(expr.span, InitializingTypeWith), - }, + }) => { + if adt_def.variant(variant_index).has_unsafe_fields() { + self.requires_unsafe(expr.span, InitializingTypeWithUnsafeField) + } + match self.tcx.layout_scalar_valid_range(adt_def.did()) { + (Bound::Unbounded, Bound::Unbounded) => {} + _ => self.requires_unsafe(expr.span, InitializingTypeWith), + } + } ExprKind::Closure(box ClosureExpr { closure_id, args: _, @@ -560,23 +565,25 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { let def_id = did.expect_local(); self.visit_inner_body(def_id); } - ExprKind::Field { lhs, .. } => { + ExprKind::Field { lhs, variant_index, name } => { let lhs = &self.thir[lhs]; - if let ty::Adt(adt_def, _) = lhs.ty.kind() - && adt_def.is_union() - { - if let Some(assigned_ty) = self.assignment_info { - if assigned_ty.needs_drop(self.tcx, self.param_env) { - // This would be unsafe, but should be outright impossible since we - // reject such unions. - assert!( - self.tcx.dcx().has_errors().is_some(), - "union fields that need dropping should be impossible: \ + if let ty::Adt(adt_def, _) = lhs.ty.kind() { + if adt_def.variant(variant_index).fields[name].safety == Safety::Unsafe { + self.requires_unsafe(expr.span, UseOfUnsafeField); + } else if adt_def.is_union() { + if let Some(assigned_ty) = self.assignment_info { + if assigned_ty.needs_drop(self.tcx, self.param_env) { + // This would be unsafe, but should be outright impossible since we + // reject such unions. + assert!( + self.tcx.dcx().has_errors().is_some(), + "union fields that need dropping should be impossible: \ {assigned_ty}" - ); + ); + } + } else { + self.requires_unsafe(expr.span, AccessToUnionField); } - } else { - self.requires_unsafe(expr.span, AccessToUnionField); } } } @@ -648,8 +655,10 @@ enum UnsafeOpKind { CallToUnsafeFunction(Option), UseOfInlineAssembly, InitializingTypeWith, + InitializingTypeWithUnsafeField, UseOfMutableStatic, UseOfExternStatic, + UseOfUnsafeField, DerefOfRawPointer, AccessToUnionField, MutationOfLayoutConstrainedField, @@ -729,6 +738,15 @@ impl UnsafeOpKind { unsafe_not_inherited_note, }, ), + InitializingTypeWithUnsafeField => tcx.emit_node_span_lint( + UNSAFE_OP_IN_UNSAFE_FN, + hir_id, + span, + UnsafeOpInUnsafeFnInitializingTypeWithUnsafeFieldRequiresUnsafe { + span, + unsafe_not_inherited_note, + }, + ), UseOfMutableStatic => tcx.emit_node_span_lint( UNSAFE_OP_IN_UNSAFE_FN, hir_id, @@ -747,6 +765,15 @@ impl UnsafeOpKind { unsafe_not_inherited_note, }, ), + UseOfUnsafeField => tcx.emit_node_span_lint( + UNSAFE_OP_IN_UNSAFE_FN, + hir_id, + span, + UnsafeOpInUnsafeFnUseOfUnsafeFieldRequiresUnsafe { + span, + unsafe_not_inherited_note, + }, + ), DerefOfRawPointer => tcx.emit_node_span_lint( UNSAFE_OP_IN_UNSAFE_FN, hir_id, @@ -886,6 +913,20 @@ impl UnsafeOpKind { unsafe_not_inherited_note, }); } + InitializingTypeWithUnsafeField if unsafe_op_in_unsafe_fn_allowed => { + dcx.emit_err( + InitializingTypeWithUnsafeFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + span, + unsafe_not_inherited_note, + }, + ); + } + InitializingTypeWithUnsafeField => { + dcx.emit_err(InitializingTypeWithUnsafeFieldRequiresUnsafe { + span, + unsafe_not_inherited_note, + }); + } UseOfMutableStatic if unsafe_op_in_unsafe_fn_allowed => { dcx.emit_err(UseOfMutableStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span, @@ -904,6 +945,15 @@ impl UnsafeOpKind { UseOfExternStatic => { dcx.emit_err(UseOfExternStaticRequiresUnsafe { span, unsafe_not_inherited_note }); } + UseOfUnsafeField if unsafe_op_in_unsafe_fn_allowed => { + dcx.emit_err(UseOfUnsafeFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + span, + unsafe_not_inherited_note, + }); + } + UseOfUnsafeField => { + dcx.emit_err(UseOfUnsafeFieldRequiresUnsafe { span, unsafe_not_inherited_note }); + } DerefOfRawPointer if unsafe_op_in_unsafe_fn_allowed => { dcx.emit_err(DerefOfRawPointerRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span, diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 00f65e0c7d09..7dba4c0e1e8d 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -86,6 +86,16 @@ pub(crate) struct UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe { pub(crate) unsafe_not_inherited_note: Option, } +#[derive(LintDiagnostic)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_unsafe_field_requires_unsafe, code = E0133)] +#[note] +pub(crate) struct UnsafeOpInUnsafeFnInitializingTypeWithUnsafeFieldRequiresUnsafe { + #[label] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) unsafe_not_inherited_note: Option, +} + #[derive(LintDiagnostic)] #[diag(mir_build_unsafe_op_in_unsafe_fn_mutable_static_requires_unsafe, code = E0133)] #[note] @@ -106,6 +116,16 @@ pub(crate) struct UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe { pub(crate) unsafe_not_inherited_note: Option, } +#[derive(LintDiagnostic)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_unsafe_field_requires_unsafe, code = E0133)] +#[note] +pub(crate) struct UnsafeOpInUnsafeFnUseOfUnsafeFieldRequiresUnsafe { + #[label] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) unsafe_not_inherited_note: Option, +} + #[derive(LintDiagnostic)] #[diag(mir_build_unsafe_op_in_unsafe_fn_deref_raw_pointer_requires_unsafe, code = E0133)] #[note] @@ -250,6 +270,17 @@ pub(crate) struct InitializingTypeWithRequiresUnsafe { pub(crate) unsafe_not_inherited_note: Option, } +#[derive(Diagnostic)] +#[diag(mir_build_initializing_type_with_unsafe_field_requires_unsafe, code = E0133)] +#[note] +pub(crate) struct InitializingTypeWithUnsafeFieldRequiresUnsafe { + #[primary_span] + #[label] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) unsafe_not_inherited_note: Option, +} + #[derive(Diagnostic)] #[diag( mir_build_initializing_type_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, @@ -264,6 +295,20 @@ pub(crate) struct InitializingTypeWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed { pub(crate) unsafe_not_inherited_note: Option, } +#[derive(Diagnostic)] +#[diag( + mir_build_initializing_type_with_unsafe_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, + code = E0133 +)] +#[note] +pub(crate) struct InitializingTypeWithUnsafeFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + #[primary_span] + #[label] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) unsafe_not_inherited_note: Option, +} + #[derive(Diagnostic)] #[diag(mir_build_mutable_static_requires_unsafe, code = E0133)] #[note] @@ -308,6 +353,28 @@ pub(crate) struct UseOfExternStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { pub(crate) unsafe_not_inherited_note: Option, } +#[derive(Diagnostic)] +#[diag(mir_build_unsafe_field_requires_unsafe, code = E0133)] +#[note] +pub(crate) struct UseOfUnsafeFieldRequiresUnsafe { + #[primary_span] + #[label] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) unsafe_not_inherited_note: Option, +} + +#[derive(Diagnostic)] +#[diag(mir_build_unsafe_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = E0133)] +#[note] +pub(crate) struct UseOfUnsafeFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + #[primary_span] + #[label] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) unsafe_not_inherited_note: Option, +} + #[derive(Diagnostic)] #[diag(mir_build_deref_raw_pointer_requires_unsafe, code = E0133)] #[note] diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 6b4e2d0f4e2f..e1daf3bca0e1 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1774,6 +1774,17 @@ impl<'a> Parser<'a> { Ok((fields, recovered)) } + fn parse_unsafe_field(&mut self) -> Safety { + // not using parse_safety as that also accepts `safe`. + if self.eat_keyword(kw::Unsafe) { + let span = self.prev_token.uninterpolated_span(); + self.psess.gated_spans.gate(sym::unsafe_fields, span); + Safety::Unsafe(span) + } else { + Safety::Default + } + } + pub(super) fn parse_tuple_struct_body(&mut self) -> PResult<'a, ThinVec> { // This is the case where we find `struct Foo(T) where T: Copy;` // Unit like structs are handled in parse_item_struct function @@ -1797,6 +1808,8 @@ impl<'a> Parser<'a> { return Err(err); } }; + // Unsafe fields are not supported in tuple structs, as doing so would result in a + // parsing ambiguity for `struct X(unsafe fn())`. let ty = match p.parse_ty() { Ok(ty) => ty, Err(err) => { @@ -1811,6 +1824,7 @@ impl<'a> Parser<'a> { FieldDef { span: lo.to(ty.span), vis, + safety: Safety::Default, ident: None, id: DUMMY_NODE_ID, ty, @@ -1833,7 +1847,8 @@ impl<'a> Parser<'a> { self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| { let lo = this.token.span; let vis = this.parse_visibility(FollowedByType::No)?; - this.parse_single_struct_field(adt_ty, lo, vis, attrs) + let safety = this.parse_unsafe_field(); + this.parse_single_struct_field(adt_ty, lo, vis, safety, attrs) .map(|field| (field, Trailing::No, UsePreAttrPos::No)) }) } @@ -1844,10 +1859,11 @@ impl<'a> Parser<'a> { adt_ty: &str, lo: Span, vis: Visibility, + safety: Safety, attrs: AttrVec, ) -> PResult<'a, FieldDef> { let mut seen_comma: bool = false; - let a_var = self.parse_name_and_ty(adt_ty, lo, vis, attrs)?; + let a_var = self.parse_name_and_ty(adt_ty, lo, vis, safety, attrs)?; if self.token == token::Comma { seen_comma = true; } @@ -1975,6 +1991,7 @@ impl<'a> Parser<'a> { adt_ty: &str, lo: Span, vis: Visibility, + safety: Safety, attrs: AttrVec, ) -> PResult<'a, FieldDef> { let name = self.parse_field_ident(adt_ty, lo)?; @@ -2000,6 +2017,7 @@ impl<'a> Parser<'a> { span: lo.to(self.prev_token.span), ident: Some(name), vis, + safety, id: DUMMY_NODE_ID, ty, attrs, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index f89fd0f7f2ae..e48375915ff8 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2081,6 +2081,7 @@ symbols! { unsafe_cell, unsafe_cell_raw_get, unsafe_extern_blocks, + unsafe_fields, unsafe_no_drop_flag, unsafe_pin_internals, unsize, diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs index 327a547b2959..8a7b5aa1919d 100644 --- a/src/tools/rustfmt/src/items.rs +++ b/src/tools/rustfmt/src/items.rs @@ -1916,15 +1916,15 @@ pub(crate) fn rewrite_struct_field_prefix( field: &ast::FieldDef, ) -> RewriteResult { let vis = format_visibility(context, &field.vis); + let safety = format_safety(field.safety); let type_annotation_spacing = type_annotation_spacing(context.config); Ok(match field.ident { Some(name) => format!( - "{}{}{}:", - vis, + "{vis}{safety}{}{}:", rewrite_ident(context, name), type_annotation_spacing.0 ), - None => vis.to_string(), + None => format!("{vis}{safety}"), }) } diff --git a/src/tools/rustfmt/tests/source/unsafe-field.rs b/src/tools/rustfmt/tests/source/unsafe-field.rs new file mode 100644 index 000000000000..35cf40a22d36 --- /dev/null +++ b/src/tools/rustfmt/tests/source/unsafe-field.rs @@ -0,0 +1,16 @@ +struct Foo { + unsafe + field: (), +} + +enum Bar { + Variant { + unsafe + field: (), + }, +} + +union Baz { + unsafe + field: (), +} diff --git a/src/tools/rustfmt/tests/target/unsafe-field.rs b/src/tools/rustfmt/tests/target/unsafe-field.rs new file mode 100644 index 000000000000..fc7fd3750c60 --- /dev/null +++ b/src/tools/rustfmt/tests/target/unsafe-field.rs @@ -0,0 +1,11 @@ +struct Foo { + unsafe field: (), +} + +enum Bar { + Variant { unsafe field: () }, +} + +union Baz { + unsafe field: (), +} diff --git a/tests/ui/feature-gates/feature-gate-unsafe_fields.rs b/tests/ui/feature-gates/feature-gate-unsafe_fields.rs new file mode 100644 index 000000000000..e7d817e4e470 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-unsafe_fields.rs @@ -0,0 +1,23 @@ +//@ compile-flags: --crate-type=lib +//@ revisions: with_gate without_gate +//@ [with_gate] check-pass + +#![allow(incomplete_features)] +#![cfg_attr(with_gate, feature(unsafe_fields))] + +struct Foo { + unsafe field: (), //[without_gate]~ ERROR +} + +// This should not parse as an unsafe field definition. +struct FooTuple(unsafe fn()); + +enum Bar { + Variant { unsafe field: () }, //[without_gate]~ ERROR + // This should not parse as an unsafe field definition. + VariantTuple(unsafe fn()), +} + +union Baz { + unsafe field: (), //[without_gate]~ ERROR +} diff --git a/tests/ui/feature-gates/feature-gate-unsafe_fields.without_gate.stderr b/tests/ui/feature-gates/feature-gate-unsafe_fields.without_gate.stderr new file mode 100644 index 000000000000..bfb29244cd56 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-unsafe_fields.without_gate.stderr @@ -0,0 +1,33 @@ +error[E0658]: `unsafe` fields are experimental + --> $DIR/feature-gate-unsafe_fields.rs:9:5 + | +LL | unsafe field: (), + | ^^^^^^ + | + = note: see issue #1234567 for more information + = help: add `#![feature(unsafe_fields)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `unsafe` fields are experimental + --> $DIR/feature-gate-unsafe_fields.rs:16:15 + | +LL | Variant { unsafe field: () }, + | ^^^^^^ + | + = note: see issue #1234567 for more information + = help: add `#![feature(unsafe_fields)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `unsafe` fields are experimental + --> $DIR/feature-gate-unsafe_fields.rs:22:5 + | +LL | unsafe field: (), + | ^^^^^^ + | + = note: see issue #1234567 for more information + = help: add `#![feature(unsafe_fields)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/stats/hir-stats.stderr b/tests/ui/stats/hir-stats.stderr index bd0c8cbc3b12..1f255f99e0b6 100644 --- a/tests/ui/stats/hir-stats.stderr +++ b/tests/ui/stats/hir-stats.stderr @@ -15,17 +15,17 @@ ast-stats-1 ForeignItem 88 ( 1.3%) 1 88 ast-stats-1 - Fn 88 ( 1.3%) 1 ast-stats-1 Arm 96 ( 1.4%) 2 48 ast-stats-1 FnDecl 120 ( 1.8%) 5 24 -ast-stats-1 FieldDef 160 ( 2.4%) 2 80 ast-stats-1 Param 160 ( 2.4%) 4 40 ast-stats-1 Stmt 160 ( 2.4%) 5 32 ast-stats-1 - Let 32 ( 0.5%) 1 ast-stats-1 - MacCall 32 ( 0.5%) 1 ast-stats-1 - Expr 96 ( 1.4%) 3 +ast-stats-1 FieldDef 176 ( 2.6%) 2 88 ast-stats-1 Block 192 ( 2.9%) 6 32 ast-stats-1 Variant 208 ( 3.1%) 2 104 ast-stats-1 AssocItem 352 ( 5.3%) 4 88 -ast-stats-1 - Type 176 ( 2.7%) 2 -ast-stats-1 - Fn 176 ( 2.7%) 2 +ast-stats-1 - Type 176 ( 2.6%) 2 +ast-stats-1 - Fn 176 ( 2.6%) 2 ast-stats-1 GenericBound 352 ( 5.3%) 4 88 ast-stats-1 - Trait 352 ( 5.3%) 4 ast-stats-1 GenericParam 480 ( 7.2%) 5 96 @@ -38,7 +38,7 @@ ast-stats-1 - Path 72 ( 1.1%) 1 ast-stats-1 - Match 72 ( 1.1%) 1 ast-stats-1 - Struct 72 ( 1.1%) 1 ast-stats-1 - Lit 144 ( 2.2%) 2 -ast-stats-1 - Block 216 ( 3.3%) 3 +ast-stats-1 - Block 216 ( 3.2%) 3 ast-stats-1 PathSegment 744 (11.2%) 31 24 ast-stats-1 Ty 896 (13.5%) 14 64 ast-stats-1 - Ref 64 ( 1.0%) 1 @@ -53,7 +53,7 @@ ast-stats-1 - Enum 136 ( 2.0%) 1 ast-stats-1 - Fn 272 ( 4.1%) 2 ast-stats-1 - Use 408 ( 6.1%) 3 ast-stats-1 ---------------------------------------------------------------- -ast-stats-1 Total 6_640 +ast-stats-1 Total 6_656 ast-stats-1 ast-stats-2 POST EXPANSION AST STATS ast-stats-2 Name Accumulated Size Count Item Size @@ -73,14 +73,14 @@ ast-stats-2 InlineAsm 120 ( 1.6%) 1 120 ast-stats-2 Attribute 128 ( 1.8%) 4 32 ast-stats-2 - DocComment 32 ( 0.4%) 1 ast-stats-2 - Normal 96 ( 1.3%) 3 -ast-stats-2 FieldDef 160 ( 2.2%) 2 80 ast-stats-2 Param 160 ( 2.2%) 4 40 ast-stats-2 Stmt 160 ( 2.2%) 5 32 ast-stats-2 - Let 32 ( 0.4%) 1 ast-stats-2 - Semi 32 ( 0.4%) 1 ast-stats-2 - Expr 96 ( 1.3%) 3 +ast-stats-2 FieldDef 176 ( 2.4%) 2 88 ast-stats-2 Block 192 ( 2.6%) 6 32 -ast-stats-2 Variant 208 ( 2.9%) 2 104 +ast-stats-2 Variant 208 ( 2.8%) 2 104 ast-stats-2 AssocItem 352 ( 4.8%) 4 88 ast-stats-2 - Type 176 ( 2.4%) 2 ast-stats-2 - Fn 176 ( 2.4%) 2 @@ -98,7 +98,7 @@ ast-stats-2 - Struct 72 ( 1.0%) 1 ast-stats-2 - InlineAsm 72 ( 1.0%) 1 ast-stats-2 - Lit 144 ( 2.0%) 2 ast-stats-2 - Block 216 ( 3.0%) 3 -ast-stats-2 PathSegment 864 (11.9%) 36 24 +ast-stats-2 PathSegment 864 (11.8%) 36 24 ast-stats-2 Ty 896 (12.3%) 14 64 ast-stats-2 - Ref 64 ( 0.9%) 1 ast-stats-2 - Ptr 64 ( 0.9%) 1 @@ -111,9 +111,9 @@ ast-stats-2 - Impl 136 ( 1.9%) 1 ast-stats-2 - ExternCrate 136 ( 1.9%) 1 ast-stats-2 - ForeignMod 136 ( 1.9%) 1 ast-stats-2 - Fn 272 ( 3.7%) 2 -ast-stats-2 - Use 544 ( 7.5%) 4 +ast-stats-2 - Use 544 ( 7.4%) 4 ast-stats-2 ---------------------------------------------------------------- -ast-stats-2 Total 7_288 +ast-stats-2 Total 7_304 ast-stats-2 hir-stats HIR STATS hir-stats Name Accumulated Size Count Item Size @@ -132,11 +132,11 @@ hir-stats Body 72 ( 0.8%) 3 24 hir-stats ImplItemRef 72 ( 0.8%) 2 36 hir-stats InlineAsm 72 ( 0.8%) 1 72 hir-stats Arm 80 ( 0.9%) 2 40 -hir-stats FieldDef 96 ( 1.1%) 2 48 hir-stats Stmt 96 ( 1.1%) 3 32 hir-stats - Let 32 ( 0.4%) 1 hir-stats - Semi 32 ( 0.4%) 1 hir-stats - Expr 32 ( 0.4%) 1 +hir-stats FieldDef 112 ( 1.2%) 2 56 hir-stats FnDecl 120 ( 1.3%) 3 40 hir-stats Attribute 128 ( 1.4%) 4 32 hir-stats GenericArgs 144 ( 1.6%) 3 48 @@ -162,17 +162,17 @@ hir-stats - Match 64 ( 0.7%) 1 hir-stats - Struct 64 ( 0.7%) 1 hir-stats - InlineAsm 64 ( 0.7%) 1 hir-stats - Lit 128 ( 1.4%) 2 -hir-stats - Block 384 ( 4.3%) 6 +hir-stats - Block 384 ( 4.2%) 6 hir-stats Item 968 (10.7%) 11 88 hir-stats - Enum 88 ( 1.0%) 1 hir-stats - Trait 88 ( 1.0%) 1 hir-stats - Impl 88 ( 1.0%) 1 hir-stats - ExternCrate 88 ( 1.0%) 1 hir-stats - ForeignMod 88 ( 1.0%) 1 -hir-stats - Fn 176 ( 2.0%) 2 +hir-stats - Fn 176 ( 1.9%) 2 hir-stats - Use 352 ( 3.9%) 4 hir-stats Path 1_240 (13.7%) 31 40 -hir-stats PathSegment 1_920 (21.3%) 40 48 +hir-stats PathSegment 1_920 (21.2%) 40 48 hir-stats ---------------------------------------------------------------- -hir-stats Total 9_024 +hir-stats Total 9_040 hir-stats diff --git a/tests/ui/thir-print/thir-tree-match.stdout b/tests/ui/thir-print/thir-tree-match.stdout index 8cff78876618..6a59e3913240 100644 --- a/tests/ui/thir-print/thir-tree-match.stdout +++ b/tests/ui/thir-print/thir-tree-match.stdout @@ -92,7 +92,7 @@ body: adt_def: AdtDef { did: DefId(0:10 ~ thir_tree_match[fcf8]::Foo) - variants: [VariantDef { def_id: DefId(0:11 ~ thir_tree_match[fcf8]::Foo::FooOne), ctor: Some((Fn, DefId(0:12 ~ thir_tree_match[fcf8]::Foo::FooOne::{constructor#0}))), name: "FooOne", discr: Relative(0), fields: [FieldDef { did: DefId(0:13 ~ thir_tree_match[fcf8]::Foo::FooOne::0), name: "0", vis: Restricted(DefId(0:0 ~ thir_tree_match[fcf8])) }], tainted: None, flags: }, VariantDef { def_id: DefId(0:14 ~ thir_tree_match[fcf8]::Foo::FooTwo), ctor: Some((Const, DefId(0:15 ~ thir_tree_match[fcf8]::Foo::FooTwo::{constructor#0}))), name: "FooTwo", discr: Relative(1), fields: [], tainted: None, flags: }] + variants: [VariantDef { def_id: DefId(0:11 ~ thir_tree_match[fcf8]::Foo::FooOne), ctor: Some((Fn, DefId(0:12 ~ thir_tree_match[fcf8]::Foo::FooOne::{constructor#0}))), name: "FooOne", discr: Relative(0), fields: [FieldDef { did: DefId(0:13 ~ thir_tree_match[fcf8]::Foo::FooOne::0), name: "0", vis: Restricted(DefId(0:0 ~ thir_tree_match[fcf8])), safety: Safe }], tainted: None, flags: }, VariantDef { def_id: DefId(0:14 ~ thir_tree_match[fcf8]::Foo::FooTwo), ctor: Some((Const, DefId(0:15 ~ thir_tree_match[fcf8]::Foo::FooTwo::{constructor#0}))), name: "FooTwo", discr: Relative(1), fields: [], tainted: None, flags: }] flags: IS_ENUM repr: ReprOptions { int: None, align: None, pack: None, flags: , field_shuffle_seed: 3477539199540094892 } args: [] @@ -154,7 +154,7 @@ body: adt_def: AdtDef { did: DefId(0:10 ~ thir_tree_match[fcf8]::Foo) - variants: [VariantDef { def_id: DefId(0:11 ~ thir_tree_match[fcf8]::Foo::FooOne), ctor: Some((Fn, DefId(0:12 ~ thir_tree_match[fcf8]::Foo::FooOne::{constructor#0}))), name: "FooOne", discr: Relative(0), fields: [FieldDef { did: DefId(0:13 ~ thir_tree_match[fcf8]::Foo::FooOne::0), name: "0", vis: Restricted(DefId(0:0 ~ thir_tree_match[fcf8])) }], tainted: None, flags: }, VariantDef { def_id: DefId(0:14 ~ thir_tree_match[fcf8]::Foo::FooTwo), ctor: Some((Const, DefId(0:15 ~ thir_tree_match[fcf8]::Foo::FooTwo::{constructor#0}))), name: "FooTwo", discr: Relative(1), fields: [], tainted: None, flags: }] + variants: [VariantDef { def_id: DefId(0:11 ~ thir_tree_match[fcf8]::Foo::FooOne), ctor: Some((Fn, DefId(0:12 ~ thir_tree_match[fcf8]::Foo::FooOne::{constructor#0}))), name: "FooOne", discr: Relative(0), fields: [FieldDef { did: DefId(0:13 ~ thir_tree_match[fcf8]::Foo::FooOne::0), name: "0", vis: Restricted(DefId(0:0 ~ thir_tree_match[fcf8])), safety: Safe }], tainted: None, flags: }, VariantDef { def_id: DefId(0:14 ~ thir_tree_match[fcf8]::Foo::FooTwo), ctor: Some((Const, DefId(0:15 ~ thir_tree_match[fcf8]::Foo::FooTwo::{constructor#0}))), name: "FooTwo", discr: Relative(1), fields: [], tainted: None, flags: }] flags: IS_ENUM repr: ReprOptions { int: None, align: None, pack: None, flags: , field_shuffle_seed: 3477539199540094892 } args: [] @@ -206,7 +206,7 @@ body: adt_def: AdtDef { did: DefId(0:10 ~ thir_tree_match[fcf8]::Foo) - variants: [VariantDef { def_id: DefId(0:11 ~ thir_tree_match[fcf8]::Foo::FooOne), ctor: Some((Fn, DefId(0:12 ~ thir_tree_match[fcf8]::Foo::FooOne::{constructor#0}))), name: "FooOne", discr: Relative(0), fields: [FieldDef { did: DefId(0:13 ~ thir_tree_match[fcf8]::Foo::FooOne::0), name: "0", vis: Restricted(DefId(0:0 ~ thir_tree_match[fcf8])) }], tainted: None, flags: }, VariantDef { def_id: DefId(0:14 ~ thir_tree_match[fcf8]::Foo::FooTwo), ctor: Some((Const, DefId(0:15 ~ thir_tree_match[fcf8]::Foo::FooTwo::{constructor#0}))), name: "FooTwo", discr: Relative(1), fields: [], tainted: None, flags: }] + variants: [VariantDef { def_id: DefId(0:11 ~ thir_tree_match[fcf8]::Foo::FooOne), ctor: Some((Fn, DefId(0:12 ~ thir_tree_match[fcf8]::Foo::FooOne::{constructor#0}))), name: "FooOne", discr: Relative(0), fields: [FieldDef { did: DefId(0:13 ~ thir_tree_match[fcf8]::Foo::FooOne::0), name: "0", vis: Restricted(DefId(0:0 ~ thir_tree_match[fcf8])), safety: Safe }], tainted: None, flags: }, VariantDef { def_id: DefId(0:14 ~ thir_tree_match[fcf8]::Foo::FooTwo), ctor: Some((Const, DefId(0:15 ~ thir_tree_match[fcf8]::Foo::FooTwo::{constructor#0}))), name: "FooTwo", discr: Relative(1), fields: [], tainted: None, flags: }] flags: IS_ENUM repr: ReprOptions { int: None, align: None, pack: None, flags: , field_shuffle_seed: 3477539199540094892 } args: [] diff --git a/tests/ui/unsafe-fields.rs b/tests/ui/unsafe-fields.rs new file mode 100644 index 000000000000..3b95a68b6670 --- /dev/null +++ b/tests/ui/unsafe-fields.rs @@ -0,0 +1,43 @@ +//@ compile-flags: --crate-type=lib +#![allow(incomplete_features)] +#![feature(unsafe_fields)] + +struct WithUnsafeField { + unsafe unsafe_field: u32, + safe_field: u32, +} + +struct WithInvalidUnsafeField { + unsafe unsafe_noncopy_field: Vec, //~ ERROR +} + +impl WithUnsafeField { + fn new() -> WithUnsafeField { + unsafe { + WithUnsafeField { + unsafe_field: 0, + safe_field: 0, + } + } + } + + fn new_without_unsafe() -> WithUnsafeField { + WithUnsafeField { //~ ERROR + unsafe_field: 0, + safe_field: 0, + } + } + + fn set_safe_field(&mut self) { + self.safe_field = 2; + } + fn set_unsafe_field(&mut self) { + unsafe { + self.unsafe_field = 2; + } + } + fn set_unsafe_field_without_unsafe(&mut self) { + self.unsafe_field = 2; + //~^ ERROR + } +} diff --git a/tests/ui/unsafe-fields.stderr b/tests/ui/unsafe-fields.stderr new file mode 100644 index 000000000000..99428db7dfbb --- /dev/null +++ b/tests/ui/unsafe-fields.stderr @@ -0,0 +1,35 @@ +error[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be unsafe + --> $DIR/unsafe-fields.rs:11:5 + | +LL | unsafe unsafe_noncopy_field: Vec, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: unsafe fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>` +help: wrap the field type in `ManuallyDrop<...>` + | +LL | unsafe unsafe_noncopy_field: std::mem::ManuallyDrop>, + | +++++++++++++++++++++++ + + +error[E0133]: initializing type with an unsafe field is unsafe and requires unsafe block + --> $DIR/unsafe-fields.rs:25:9 + | +LL | / WithUnsafeField { +LL | | unsafe_field: 0, +LL | | safe_field: 0, +LL | | } + | |_________^ initialization of struct with unsafe field + | + = note: unsafe fields may carry library invariants + +error[E0133]: use of unsafe field is unsafe and requires unsafe block + --> $DIR/unsafe-fields.rs:40:9 + | +LL | self.unsafe_field = 2; + | ^^^^^^^^^^^^^^^^^ use of unsafe field + | + = note: unsafe fields may carry library invariants + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0133, E0740. +For more information about an error, try `rustc --explain E0133`.