Skip to content

Commit 3989e97

Browse files
committed
unsafe impl
1 parent a06b24a commit 3989e97

File tree

13 files changed

+176
-53
lines changed

13 files changed

+176
-53
lines changed

compiler/rustc_ast/src/ast.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1961,7 +1961,13 @@ pub struct MacroDef {
19611961
/// `true` if macro was defined with `macro_rules`.
19621962
pub macro_rules: bool,
19631963

1964-
pub eii_macro_for: Option<Path>,
1964+
pub eii_macro_for: Option<EiiMacroFor>,
1965+
}
1966+
1967+
#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
1968+
pub struct EiiMacroFor {
1969+
pub extern_item_path: Path,
1970+
pub impl_unsafe: bool,
19651971
}
19661972

19671973
#[derive(Clone, Encodable, Decodable, Debug, Copy, Hash, Eq, PartialEq)]
@@ -3589,7 +3595,16 @@ pub struct Fn {
35893595
pub body: Option<P<Block>>,
35903596

35913597
/// This fn implements some EII, pointed to by the `path`
3592-
pub eii_impl: ThinVec<(NodeId, Path)>,
3598+
pub eii_impl: ThinVec<EIIImpl>,
3599+
}
3600+
3601+
#[derive(Clone, Encodable, Decodable, Debug)]
3602+
pub struct EIIImpl {
3603+
pub node_id: NodeId,
3604+
pub eii_macro_path: Path,
3605+
pub impl_safety: Safety,
3606+
pub span: Span,
3607+
pub inner_span: Span,
35933608
}
35943609

35953610
#[derive(Clone, Encodable, Decodable, Debug)]

compiler/rustc_ast/src/mut_visit.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -751,8 +751,8 @@ fn walk_mac<T: MutVisitor>(vis: &mut T, mac: &mut MacCall) {
751751

752752
fn walk_macro_def<T: MutVisitor>(vis: &mut T, macro_def: &mut MacroDef) {
753753
let MacroDef { body, macro_rules: _, eii_macro_for } = macro_def;
754-
if let Some(path) = eii_macro_for {
755-
vis.visit_path(path);
754+
if let Some(EiiMacroFor { extern_item_path, impl_unsafe: _ }) = eii_macro_for {
755+
vis.visit_path(extern_item_path);
756756
}
757757
visit_delim_args(vis, body);
758758
}
@@ -946,9 +946,9 @@ fn walk_fn<T: MutVisitor>(vis: &mut T, kind: FnKind<'_>) {
946946
visit_defaultness(vis, defaultness);
947947
vis.visit_ident(ident);
948948

949-
for (node_id, path) in eii_impl {
949+
for EIIImpl { node_id, eii_macro_path, .. } in eii_impl {
950950
vis.visit_id(node_id);
951-
vis.visit_path(path);
951+
vis.visit_path(eii_macro_path);
952952
}
953953
vis.visit_fn_header(header);
954954
vis.visit_generics(generics);

compiler/rustc_ast/src/visit.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -479,8 +479,8 @@ impl WalkItemKind for ItemKind {
479479
ItemKind::MacroDef(ident, ts) => {
480480
try_visit!(visitor.visit_ident(ident));
481481
try_visit!(visitor.visit_mac_def(ts, id));
482-
if let Some(i) = &ts.eii_macro_for {
483-
try_visit!(visitor.visit_path(i, id));
482+
if let Some(EiiMacroFor { extern_item_path, impl_unsafe: _ }) = &ts.eii_macro_for {
483+
try_visit!(visitor.visit_path(extern_item_path, id));
484484
}
485485
}
486486
ItemKind::Delegation(box Delegation {
@@ -960,9 +960,8 @@ pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>) -> V::Resu
960960
// Visibility is visited as a part of the item.
961961
try_visit!(visitor.visit_ident(ident));
962962

963-
// Identifier and visibility are visited as a part of the item.
964-
for (node_id, path) in eii_impl {
965-
try_visit!(visitor.visit_path(path, *node_id));
963+
for EIIImpl { node_id, eii_macro_path, .. } in eii_impl {
964+
try_visit!(visitor.visit_path(eii_macro_path, *node_id));
966965
}
967966

968967
try_visit!(visitor.visit_fn_header(header));

compiler/rustc_ast_lowering/src/item.rs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -155,18 +155,25 @@ impl<'hir> LoweringContext<'_, 'hir> {
155155
) -> Vec<hir::Attribute> {
156156
match i {
157157
ItemKind::Fn(box Fn { eii_impl, .. }) => {
158-
let mut res = Vec::new();
159-
160-
for (id, path) in eii_impl {
161-
let did = self.lower_path_simple_eii(*id, path);
162-
res.push(hir::Attribute::Parsed(AttributeKind::EiiImpl { eii_macro: did }));
158+
let mut eii_impls = ThinVec::new();
159+
for EIIImpl { node_id, eii_macro_path, impl_safety, span, inner_span } in eii_impl {
160+
let did = self.lower_path_simple_eii(*node_id, eii_macro_path);
161+
eii_impls.push(rustc_attr_parsing::EIIImpl {
162+
eii_macro: did,
163+
span: self.lower_span(*span),
164+
inner_span: self.lower_span(*inner_span),
165+
impl_marked_unsafe: self
166+
.lower_safety(*impl_safety, hir::Safety::Safe)
167+
.is_unsafe(),
168+
})
163169
}
164170

165-
res
171+
vec![hir::Attribute::Parsed(AttributeKind::EiiImpl(eii_impls))]
166172
}
167-
ItemKind::MacroDef(_, MacroDef { eii_macro_for: Some(path), .. }) => {
173+
ItemKind::MacroDef(_, MacroDef { eii_macro_for: Some(EiiMacroFor { extern_item_path, impl_unsafe }), .. }) => {
168174
vec![hir::Attribute::Parsed(AttributeKind::EiiMacroFor {
169-
eii_extern_item: self.lower_path_simple_eii(id, path),
175+
eii_extern_item: self.lower_path_simple_eii(id, extern_item_path),
176+
impl_unsafe: *impl_unsafe,
170177
})]
171178
}
172179
ItemKind::ExternCrate(..)

compiler/rustc_ast_passes/src/ast_validation.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -948,8 +948,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
948948
self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
949949
self.check_defaultness(item.span, *defaultness);
950950

951-
for (id, path) in eii_impl {
952-
self.visit_path(path, *id);
951+
for EIIImpl { node_id, eii_macro_path, .. } in eii_impl {
952+
self.visit_path(eii_macro_path, *node_id);
953953
}
954954

955955
let is_intrinsic =

compiler/rustc_ast_pretty/src/pprust/state/item.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
use ast::StaticItem;
22
use itertools::{Itertools, Position};
3-
use rustc_ast as ast;
4-
use rustc_ast::ModKind;
53
use rustc_ast::ptr::P;
4+
use rustc_ast::{self as ast, EIIImpl, ModKind, Safety};
65
use rustc_span::Ident;
76

87
use crate::pp::Breaks::Inconsistent;
@@ -682,9 +681,16 @@ impl<'a> State<'a> {
682681
let ast::Fn { defaultness, ident, generics, sig, contract, body, define_opaque, eii_impl } = func;
683682
self.print_define_opaques(define_opaque.as_deref());
684683

685-
for (_, path) in eii_impl {
684+
for EIIImpl { eii_macro_path, impl_safety, .. } in eii_impl {
686685
self.word("#[");
687-
self.print_path(path, false, 0);
686+
if let Safety::Unsafe(..) = impl_safety {
687+
self.word("unsafe");
688+
self.popen();
689+
}
690+
self.print_path(eii_macro_path, false, 0);
691+
if let Safety::Unsafe(..) = impl_safety {
692+
self.pclose();
693+
}
688694
self.word("]");
689695
self.hardbreak();
690696
}

compiler/rustc_attr_data_structures/src/attributes.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,14 @@ impl Deprecation {
139139
}
140140
}
141141

142+
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
143+
pub struct EIIImpl {
144+
pub eii_macro: DefId,
145+
pub impl_marked_unsafe: bool,
146+
pub span: Span,
147+
pub inner_span: Span,
148+
}
149+
142150
/// Represent parsed, *built in*, inert attributes.
143151
///
144152
/// That means attributes that are not actually ever expanded.
@@ -190,12 +198,11 @@ pub enum AttributeKind {
190198
span: Span,
191199
comment: Symbol,
192200
},
193-
Eii(Span),
194-
EiiImpl {
195-
eii_macro: DefId,
196-
},
201+
EiiImpl(ThinVec<EIIImpl>),
197202
EiiMacroFor {
198203
eii_extern_item: DefId,
204+
/// whether or not it is unsafe to implement this EII
205+
impl_unsafe: bool,
199206
},
200207
MacroTransparency(Transparency),
201208
Repr(ThinVec<(ReprAttr, Span)>),

compiler/rustc_builtin_macros/src/eii.rs

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
use rustc_ast::{DUMMY_NODE_ID, ItemKind, ast};
1+
use rustc_ast::{DUMMY_NODE_ID, EIIImpl, EiiMacroFor, ItemKind, ast};
22
use rustc_expand::base::{Annotatable, ExtCtxt};
3-
use rustc_span::Span;
3+
use rustc_span::{Span, kw};
44

55
pub(crate) fn eii_macro_for(
66
ecx: &mut ExtCtxt<'_>,
@@ -13,7 +13,22 @@ pub(crate) fn eii_macro_for(
1313

1414
let Some(list) = meta_item.meta_item_list() else { panic!("expected list") };
1515

16-
d.eii_macro_for = Some(list[0].meta_item().unwrap().path.clone());
16+
let Some(extern_item_path) = list.get(0).and_then(|i| i.meta_item()).map(|i| i.path.clone())
17+
else {
18+
panic!("expected a path to an `extern` item");
19+
};
20+
21+
let impl_unsafe = if let Some(i) = list.get(1) {
22+
if i.lit().and_then(|i| i.kind.str()).is_some_and(|i| i == kw::Unsafe) {
23+
true
24+
} else {
25+
panic!("expected the string `\"unsafe\"` here or no other arguments");
26+
}
27+
} else {
28+
false
29+
};
30+
31+
d.eii_macro_for = Some(EiiMacroFor { extern_item_path, impl_unsafe });
1732

1833
// Return the original item and the new methods.
1934
vec![item]
@@ -31,7 +46,14 @@ pub(crate) fn eii_macro(
3146

3247
assert!(meta_item.is_word());
3348

34-
f.eii_impl.push((DUMMY_NODE_ID, meta_item.path.clone()));
49+
f.eii_impl.push(EIIImpl {
50+
node_id: DUMMY_NODE_ID,
51+
eii_macro_path: meta_item.path.clone(),
52+
impl_safety: meta_item.unsafety,
53+
span,
54+
inner_span: meta_item.span,
55+
is_default: false,
56+
});
3557

3658
vec![item]
3759
}

compiler/rustc_hir_analysis/src/check/wfcheck.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::ops::{ControlFlow, Deref};
33

44
use hir::intravisit::{self, Visitor};
55
use rustc_abi::ExternAbi;
6-
use rustc_attr_parsing::{AttributeKind, find_attr};
6+
use rustc_attr_parsing::{AttributeKind, EIIImpl, find_attr};
77
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
88
use rustc_errors::codes::*;
99
use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_err};
@@ -40,7 +40,7 @@ use rustc_trait_selection::traits::{
4040
use tracing::{debug, instrument};
4141
use {rustc_ast as ast, rustc_hir as hir};
4242

43-
use super::compare_eii::compare_eii_predicate_entailment;
43+
use super::compare_eii::{compare_eii_function_types, compare_eii_predicate_entailment};
4444
use crate::autoderef::Autoderef;
4545
use crate::collect::CollectItemTypesVisitor;
4646
use crate::constrained_generic_params::{Parameter, identify_constrained_generic_params};
@@ -1298,14 +1298,16 @@ fn check_item_fn(
12981298
enter_wf_checking_ctxt(tcx, span, def_id, |wfcx| {
12991299
// does the function have an EiiImpl attribute? that contains the defid of a *macro*
13001300
// that was used to mark the implementation. This is a two step process.
1301-
if let Some(eii_macro) =
1302-
find_attr!(tcx.get_all_attrs(def_id), AttributeKind::EiiImpl {eii_macro} => *eii_macro)
1301+
for EIIImpl { eii_macro, .. } in
1302+
find_attr!(tcx.get_all_attrs(def_id), AttributeKind::EiiImpl(impls) => impls)
1303+
.into_iter()
1304+
.flatten()
13031305
{
13041306
// we expect this macro to have the `EiiMacroFor` attribute, that points to a function
13051307
// signature that we'd like to compare the function we're currently checking with
1306-
if let Some(eii_extern_item) = find_attr!(tcx.get_all_attrs(eii_macro), AttributeKind::EiiMacroFor {eii_extern_item} => *eii_extern_item)
1308+
if let Some(eii_extern_item) = find_attr!(tcx.get_all_attrs(*eii_macro), AttributeKind::EiiMacroFor {eii_extern_item, ..} => *eii_extern_item)
13071309
{
1308-
let _ = compare_eii_predicate_entailment(tcx, def_id, eii_extern_item);
1310+
let _ = compare_eii_function_types(tcx, def_id, eii_extern_item);
13091311
} else {
13101312
panic!(
13111313
"EII impl macro {eii_macro:?} did not have an eii macro for attribute pointing to a function"

compiler/rustc_passes/messages.ftl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,12 @@ passes_duplicate_lang_item_crate_depends =
311311
.first_definition_path = first definition in `{$orig_crate_name}` loaded from {$orig_path}
312312
.second_definition_path = second definition in `{$crate_name}` loaded from {$path}
313313
314+
passes_eii_impl_not_function =
315+
`eii_macro_for` is only valid on functions
316+
317+
passes_eii_impl_requires_unsafe =
318+
`#[{$name}]` is unsafe to implement
319+
passes_eii_impl_requires_unsafe_suggestion = wrap the attribute in `unsafe(...)`
314320
passes_export_name =
315321
attribute should be applied to a free function, impl method or static
316322
.label = not a free function, impl method or static

compiler/rustc_passes/src/check_attr.rs

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use std::collections::hash_map::Entry;
1010

1111
use rustc_abi::{Align, ExternAbi, Size};
1212
use rustc_ast::{AttrStyle, LitKind, MetaItemInner, MetaItemKind, MetaItemLit, ast};
13-
use rustc_attr_parsing::{AttributeKind, ReprAttr, find_attr};
13+
use rustc_attr_parsing::{AttributeKind, EIIImpl, ReprAttr, find_attr};
1414
use rustc_data_structures::fx::FxHashMap;
1515
use rustc_errors::{Applicability, DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey};
1616
use rustc_feature::{AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute};
@@ -124,8 +124,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
124124
AttributeKind::Stability { span, .. }
125125
| AttributeKind::ConstStability { span, .. },
126126
) => self.check_stability_promotable(*span, target),
127-
Attribute::Parsed(AttributeKind::Eii(attr_span)) => {
128-
self.check_eii(hir_id, *attr_span, span, target)
127+
Attribute::Parsed(AttributeKind::EiiImpl(impls)) => {
128+
self.check_eii_impl(hir_id, impls, span, target)
129+
}
130+
Attribute::Parsed(AttributeKind::EiiMacroFor { .. }) => {
131+
// no checks needed
129132
}
130133
Attribute::Parsed(AttributeKind::AllowInternalUnstable(syms)) => self
131134
.check_allow_internal_unstable(
@@ -474,13 +477,32 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
474477
}
475478
}
476479

477-
/// Checks if an `#[inline]` is applied to a function or a closure.
478-
fn check_eii(&self, _hir_id: HirId, _attr_span: Span, _defn_span: Span, target: Target) {
479-
match target {
480-
Target::ForeignFn => {}
481-
target => {
482-
// TODO:
483-
bug!("wrong target for EII: {target:?}");
480+
fn check_eii_impl(
481+
&self,
482+
_hir_id: HirId,
483+
impls: &[EIIImpl],
484+
_target_span: Span,
485+
target: Target,
486+
) {
487+
for EIIImpl { span, inner_span, eii_macro, impl_marked_unsafe } in impls {
488+
match target {
489+
Target::Fn => {}
490+
_ => {
491+
self.dcx().emit_err(errors::EIIImplNotFunction { span: *span });
492+
}
493+
}
494+
495+
if find_attr!(self.tcx.get_all_attrs(*eii_macro), AttributeKind::EiiMacroFor { impl_unsafe, .. } if *impl_unsafe)
496+
&& !impl_marked_unsafe
497+
{
498+
self.dcx().emit_err(errors::EIIImplRequiresUnsafe {
499+
span: *span,
500+
name: self.tcx.item_name(*eii_macro),
501+
suggestion: errors::EIIImplRequiresUnsafeSuggestion {
502+
left: inner_span.shrink_to_lo(),
503+
right: inner_span.shrink_to_hi(),
504+
},
505+
});
484506
}
485507
}
486508
}

compiler/rustc_passes/src/errors.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1920,3 +1920,32 @@ pub(crate) struct UnsupportedAttributesInWhere {
19201920
#[primary_span]
19211921
pub span: MultiSpan,
19221922
}
1923+
1924+
#[derive(Diagnostic)]
1925+
#[diag(passes_eii_impl_not_function)]
1926+
pub(crate) struct EIIImplNotFunction {
1927+
#[primary_span]
1928+
pub span: Span,
1929+
}
1930+
1931+
#[derive(Diagnostic)]
1932+
#[diag(passes_eii_impl_requires_unsafe)]
1933+
pub(crate) struct EIIImplRequiresUnsafe {
1934+
#[primary_span]
1935+
pub span: Span,
1936+
pub name: Symbol,
1937+
#[subdiagnostic]
1938+
pub suggestion: EIIImplRequiresUnsafeSuggestion,
1939+
}
1940+
1941+
#[derive(Subdiagnostic)]
1942+
#[multipart_suggestion(
1943+
passes_eii_impl_requires_unsafe_suggestion,
1944+
applicability = "machine-applicable"
1945+
)]
1946+
pub(crate) struct EIIImplRequiresUnsafeSuggestion {
1947+
#[suggestion_part(code = "unsafe(")]
1948+
pub left: Span,
1949+
#[suggestion_part(code = ")")]
1950+
pub right: Span,
1951+
}

0 commit comments

Comments
 (0)