Skip to content

Commit

Permalink
Draft implementation of the unsafe-fields RFC.
Browse files Browse the repository at this point in the history
Co-Authored-By: Jacob Pratt <jacob@jhpratt.dev>
  • Loading branch information
veluca93 and jhpratt committed Nov 11, 2024
1 parent d4822c2 commit 2cda185
Show file tree
Hide file tree
Showing 29 changed files with 419 additions and 50 deletions.
1 change: 1 addition & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3036,6 +3036,7 @@ pub struct FieldDef {
pub id: NodeId,
pub span: Span,
pub vis: Visibility,
pub safety: Safety,
pub ident: Option<Ident>,

pub ty: P<Ty>,
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1044,10 +1044,11 @@ pub fn walk_flat_map_field_def<T: MutVisitor>(
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);
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
}

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_expand/src/placeholders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(),
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
(unstable, 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<const N: [u8]>() {`.
(incomplete, unsized_const_params, "1.82.0", Some(95174)),
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<'_> {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_metadata/src/rmeta/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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<DefId> {
self.root.tables.trait_item_def_id.get(self, id).map(|d| d.decode_from_cdata(self))
}
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_metadata/src/rmeta/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_metadata/src/rmeta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ define_tables! {
associated_item_or_field_def_ids: Table<DefIndex, LazyArray<DefIndex>>,
def_kind: Table<DefIndex, DefKind>,
visibility: Table<DefIndex, LazyValue<ty::Visibility<DefIndex>>>,
safety: Table<DefIndex, hir::Safety>,
def_span: Table<DefIndex, LazyValue<Span>>,
def_ident_span: Table<DefIndex, LazyValue<Span>>,
lookup_stability: Table<DefIndex, LazyValue<attr::Stability>>,
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_metadata/src/rmeta/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,13 @@ fixed_size_enum! {
}
}

fixed_size_enum! {
hir::Safety {
( Unsafe )
( Safe )
}
}

fixed_size_enum! {
ty::Asyncness {
( Yes )
Expand Down
10 changes: 6 additions & 4 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1357,6 +1357,7 @@ pub struct FieldDef {
pub did: DefId,
pub name: Symbol,
pub vis: Visibility<DefId>,
pub safety: hir::Safety,
}

impl PartialEq for FieldDef {
Expand All @@ -1369,15 +1370,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");
}

Expand All @@ -1397,7 +1399,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)
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/ty/parameterized.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
30 changes: 30 additions & 0 deletions compiler/rustc_mir_build/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
Loading

0 comments on commit 2cda185

Please sign in to comment.