Skip to content

Commit 7605dad

Browse files
Port #[target_feature] to the new attribute parsing infrastructure
Signed-off-by: Jonathan Brouwer <[email protected]>
1 parent 71e4c00 commit 7605dad

File tree

25 files changed

+440
-309
lines changed

25 files changed

+440
-309
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4629,6 +4629,7 @@ dependencies = [
46294629
"itertools",
46304630
"rustc_abi",
46314631
"rustc_ast",
4632+
"rustc_attr_data_structures",
46324633
"rustc_data_structures",
46334634
"rustc_errors",
46344635
"rustc_fluent_macro",

compiler/rustc_ast_lowering/src/item.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use rustc_abi::ExternAbi;
22
use rustc_ast::ptr::P;
33
use rustc_ast::visit::AssocCtxt;
44
use rustc_ast::*;
5+
use rustc_attr_data_structures::{AttributeKind, find_attr};
56
use rustc_errors::{E0570, ErrorGuaranteed, struct_span_code_err};
67
use rustc_hir::def::{DefKind, PerNS, Res};
78
use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId};
@@ -1621,7 +1622,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
16211622
let safety = self.lower_safety(h.safety, default_safety);
16221623

16231624
// Treat safe `#[target_feature]` functions as unsafe, but also remember that we did so.
1624-
let safety = if attrs.iter().any(|attr| attr.has_name(sym::target_feature))
1625+
let safety = if find_attr!(attrs, AttributeKind::TargetFeature { .. })
16251626
&& safety.is_safe()
16261627
&& !self.tcx.sess.target.is_like_wasm
16271628
{

compiler/rustc_attr_data_structures/src/attributes.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,10 +198,10 @@ pub enum AttributeKind {
198198
Align { align: Align, span: Span },
199199

200200
/// Represents `#[rustc_allow_const_fn_unstable]`.
201-
AllowConstFnUnstable(ThinVec<Symbol>),
201+
AllowConstFnUnstable(ThinVec<Symbol>, Span),
202202

203203
/// Represents `#[allow_internal_unstable]`.
204-
AllowInternalUnstable(ThinVec<(Symbol, Span)>),
204+
AllowInternalUnstable(ThinVec<(Symbol, Span)>, Span),
205205

206206
/// Represents `#[rustc_as_ptr]` (used by the `dangling_pointers_from_temporaries` lint).
207207
AsPtr(Span),
@@ -309,6 +309,9 @@ pub enum AttributeKind {
309309
span: Span,
310310
},
311311

312+
/// Represents `#[target_feature(enable = "...")]`
313+
TargetFeature(ThinVec<(Symbol, Span)>, Span),
314+
312315
/// Represents `#[track_caller]`
313316
TrackCaller(Span),
314317

compiler/rustc_attr_data_structures/src/encode_cross_crate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ impl AttributeKind {
4242
RustcLayoutScalarValidRangeStart(..) => Yes,
4343
RustcObjectLifetimeDefault => No,
4444
SkipDuringMethodDispatch { .. } => No,
45+
TargetFeature(..) => No,
4546
TrackCaller(..) => Yes,
4647
Used { .. } => No,
4748
}

compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ pub(crate) struct AllowInternalUnstableParser;
1313
impl<S: Stage> CombineAttributeParser<S> for AllowInternalUnstableParser {
1414
const PATH: &[Symbol] = &[sym::allow_internal_unstable];
1515
type Item = (Symbol, Span);
16-
const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowInternalUnstable;
16+
const CONVERT: ConvertFn<Self::Item> =
17+
|items, span| AttributeKind::AllowInternalUnstable(items, span);
1718
const TEMPLATE: AttributeTemplate = template!(Word, List: "feat1, feat2, ...");
1819

1920
fn extend<'c>(
@@ -30,7 +31,8 @@ pub(crate) struct AllowConstFnUnstableParser;
3031
impl<S: Stage> CombineAttributeParser<S> for AllowConstFnUnstableParser {
3132
const PATH: &[Symbol] = &[sym::rustc_allow_const_fn_unstable];
3233
type Item = Symbol;
33-
const CONVERT: ConvertFn<Self::Item> = AttributeKind::AllowConstFnUnstable;
34+
const CONVERT: ConvertFn<Self::Item> =
35+
|items, first_span| AttributeKind::AllowConstFnUnstable(items, first_span);
3436
const TEMPLATE: AttributeTemplate = template!(Word, List: "feat1, feat2, ...");
3537

3638
fn extend<'c>(

compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ use rustc_feature::{AttributeTemplate, template};
33
use rustc_session::parse::feature_err;
44
use rustc_span::{Span, Symbol, sym};
55

6-
use super::{AcceptMapping, AttributeOrder, AttributeParser, OnDuplicate, SingleAttributeParser};
6+
use super::{
7+
AcceptMapping, AttributeOrder, AttributeParser, CombineAttributeParser, ConvertFn, OnDuplicate,
8+
SingleAttributeParser,
9+
};
710
use crate::context::{AcceptContext, FinalizeContext, Stage};
811
use crate::parser::ArgParser;
912
use crate::session_diagnostics::{NakedFunctionIncompatibleAttribute, NullOnExport};
@@ -309,3 +312,53 @@ impl<S: Stage> AttributeParser<S> for UsedParser {
309312
})
310313
}
311314
}
315+
316+
pub(crate) struct TargetFeatureParser;
317+
318+
impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
319+
type Item = (Symbol, Span);
320+
const PATH: &[Symbol] = &[sym::target_feature];
321+
const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature(items, span);
322+
const TEMPLATE: AttributeTemplate = template!(List: "enable = \"feat1, feat2\"");
323+
324+
fn extend<'c>(
325+
cx: &'c mut AcceptContext<'_, '_, S>,
326+
args: &'c ArgParser<'_>,
327+
) -> impl IntoIterator<Item = Self::Item> + 'c {
328+
let mut features = Vec::new();
329+
let ArgParser::List(list) = args else {
330+
cx.expected_list(cx.attr_span);
331+
return features;
332+
};
333+
for item in list.mixed() {
334+
let Some(name_value) = item.meta_item() else {
335+
cx.expected_name_value(item.span(), Some(sym::enable));
336+
return features;
337+
};
338+
339+
// Validate name
340+
let Some(name) = name_value.path().word_sym() else {
341+
cx.expected_name_value(name_value.path().span(), Some(sym::enable));
342+
return features;
343+
};
344+
if name != sym::enable {
345+
cx.expected_name_value(name_value.path().span(), Some(sym::enable));
346+
return features;
347+
}
348+
349+
// Use value
350+
let Some(name_value) = name_value.args().name_value() else {
351+
cx.expected_name_value(item.span(), Some(sym::enable));
352+
return features;
353+
};
354+
let Some(value_str) = name_value.value_as_str() else {
355+
cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit()));
356+
return features;
357+
};
358+
for feature in value_str.as_str().split(",") {
359+
features.push((Symbol::intern(feature), item.span()));
360+
}
361+
}
362+
features
363+
}
364+
}

compiler/rustc_attr_parsing/src/attributes/mod.rs

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ pub(crate) enum AttributeOrder {
229229
KeepLast,
230230
}
231231

232-
type ConvertFn<E> = fn(ThinVec<E>) -> AttributeKind;
232+
type ConvertFn<E> = fn(ThinVec<E>, Span) -> AttributeKind;
233233

234234
/// Alternative to [`AttributeParser`] that automatically handles state management.
235235
/// If multiple attributes appear on an element, combines the values of each into a
@@ -260,25 +260,40 @@ pub(crate) trait CombineAttributeParser<S: Stage>: 'static {
260260

261261
/// Use in combination with [`CombineAttributeParser`].
262262
/// `Combine<T: CombineAttributeParser>` implements [`AttributeParser`].
263-
pub(crate) struct Combine<T: CombineAttributeParser<S>, S: Stage>(
264-
PhantomData<(S, T)>,
265-
ThinVec<<T as CombineAttributeParser<S>>::Item>,
266-
);
263+
pub(crate) struct Combine<T: CombineAttributeParser<S>, S: Stage> {
264+
phantom: PhantomData<(S, T)>,
265+
/// A list of all items produced by parsing attributes so far. One attribute can produce any amount of items.
266+
items: ThinVec<<T as CombineAttributeParser<S>>::Item>,
267+
/// The full span of the first attribute that was encountered.
268+
first_span: Option<Span>,
269+
}
267270

268271
impl<T: CombineAttributeParser<S>, S: Stage> Default for Combine<T, S> {
269272
fn default() -> Self {
270-
Self(Default::default(), Default::default())
273+
Self {
274+
phantom: Default::default(),
275+
items: Default::default(),
276+
first_span: Default::default(),
277+
}
271278
}
272279
}
273280

274281
impl<T: CombineAttributeParser<S>, S: Stage> AttributeParser<S> for Combine<T, S> {
275282
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
276283
T::PATH,
277284
<T as CombineAttributeParser<S>>::TEMPLATE,
278-
|group: &mut Combine<T, S>, cx, args| group.1.extend(T::extend(cx, args)),
285+
|group: &mut Combine<T, S>, cx, args| {
286+
// Keep track of the span of the first attribute, for diagnostics
287+
group.first_span.get_or_insert(cx.attr_span);
288+
group.items.extend(T::extend(cx, args))
289+
},
279290
)];
280291

281292
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
282-
if self.1.is_empty() { None } else { Some(T::CONVERT(self.1)) }
293+
if let Some(first_span) = self.first_span {
294+
Some(T::CONVERT(self.items, first_span))
295+
} else {
296+
None
297+
}
283298
}
284299
}

compiler/rustc_attr_parsing/src/attributes/repr.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub(crate) struct ReprParser;
2323
impl<S: Stage> CombineAttributeParser<S> for ReprParser {
2424
type Item = (ReprAttr, Span);
2525
const PATH: &[Symbol] = &[sym::repr];
26-
const CONVERT: ConvertFn<Self::Item> = AttributeKind::Repr;
26+
const CONVERT: ConvertFn<Self::Item> = |items, _| AttributeKind::Repr(items);
2727
// FIXME(jdonszelmann): never used
2828
const TEMPLATE: AttributeTemplate =
2929
template!(List: "C | Rust | align(...) | packed(...) | <integer type> | transparent");

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
1616

1717
use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
1818
use crate::attributes::codegen_attrs::{
19-
ColdParser, ExportNameParser, NakedParser, NoMangleParser, OptimizeParser, TrackCallerParser,
20-
UsedParser,
19+
ColdParser, ExportNameParser, NakedParser, NoMangleParser, OptimizeParser, TargetFeatureParser,
20+
TrackCallerParser, UsedParser,
2121
};
2222
use crate::attributes::confusables::ConfusablesParser;
2323
use crate::attributes::deprecation::DeprecationParser;
@@ -116,6 +116,7 @@ attribute_parsers!(
116116
Combine<AllowConstFnUnstableParser>,
117117
Combine<AllowInternalUnstableParser>,
118118
Combine<ReprParser>,
119+
Combine<TargetFeatureParser>,
119120
// tidy-alphabetical-end
120121

121122
// tidy-alphabetical-start

compiler/rustc_codegen_ssa/messages.ftl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ codegen_ssa_failed_to_get_layout = failed to get layout for {$ty}: {$err}
6262
6363
codegen_ssa_failed_to_write = failed to write {$path}: {$error}
6464
65+
codegen_ssa_feature_not_valid = the feature named `{$feature}` is not valid for this target
66+
.label = `{$feature}` is not valid for this target
67+
.help = consider removing the leading `+` in the feature name
68+
6569
codegen_ssa_field_associated_value_expected = associated value expected for `{$name}`
6670
6771
codegen_ssa_forbidden_ctarget_feature =

compiler/rustc_codegen_ssa/src/codegen_attrs.rs

Lines changed: 47 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,49 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
141141
});
142142
}
143143
}
144+
AttributeKind::TargetFeature(features, attr_span) => {
145+
let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else {
146+
tcx.dcx().span_delayed_bug(*attr_span, "target_feature applied to non-fn");
147+
continue;
148+
};
149+
let safe_target_features =
150+
matches!(sig.header.safety, hir::HeaderSafety::SafeTargetFeatures);
151+
codegen_fn_attrs.safe_target_features = safe_target_features;
152+
if safe_target_features {
153+
if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc {
154+
// The `#[target_feature]` attribute is allowed on
155+
// WebAssembly targets on all functions. Prior to stabilizing
156+
// the `target_feature_11` feature, `#[target_feature]` was
157+
// only permitted on unsafe functions because on most targets
158+
// execution of instructions that are not supported is
159+
// considered undefined behavior. For WebAssembly which is a
160+
// 100% safe target at execution time it's not possible to
161+
// execute undefined instructions, and even if a future
162+
// feature was added in some form for this it would be a
163+
// deterministic trap. There is no undefined behavior when
164+
// executing WebAssembly so `#[target_feature]` is allowed
165+
// on safe functions (but again, only for WebAssembly)
166+
//
167+
// Note that this is also allowed if `actually_rustdoc` so
168+
// if a target is documenting some wasm-specific code then
169+
// it's not spuriously denied.
170+
//
171+
// Now that `#[target_feature]` is permitted on safe functions,
172+
// this exception must still exist for allowing the attribute on
173+
// `main`, `start`, and other functions that are not usually
174+
// allowed.
175+
} else {
176+
check_target_feature_trait_unsafe(tcx, did, *attr_span);
177+
}
178+
}
179+
from_target_feature_attr(
180+
tcx,
181+
did,
182+
features,
183+
rust_target_features,
184+
&mut codegen_fn_attrs.target_features,
185+
);
186+
}
144187
AttributeKind::TrackCaller(attr_span) => {
145188
let is_closure = tcx.is_closure_like(did.to_def_id());
146189

@@ -190,49 +233,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
190233
codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL
191234
}
192235
sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL,
193-
sym::target_feature => {
194-
let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else {
195-
tcx.dcx().span_delayed_bug(attr.span(), "target_feature applied to non-fn");
196-
continue;
197-
};
198-
let safe_target_features =
199-
matches!(sig.header.safety, hir::HeaderSafety::SafeTargetFeatures);
200-
codegen_fn_attrs.safe_target_features = safe_target_features;
201-
if safe_target_features {
202-
if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc {
203-
// The `#[target_feature]` attribute is allowed on
204-
// WebAssembly targets on all functions. Prior to stabilizing
205-
// the `target_feature_11` feature, `#[target_feature]` was
206-
// only permitted on unsafe functions because on most targets
207-
// execution of instructions that are not supported is
208-
// considered undefined behavior. For WebAssembly which is a
209-
// 100% safe target at execution time it's not possible to
210-
// execute undefined instructions, and even if a future
211-
// feature was added in some form for this it would be a
212-
// deterministic trap. There is no undefined behavior when
213-
// executing WebAssembly so `#[target_feature]` is allowed
214-
// on safe functions (but again, only for WebAssembly)
215-
//
216-
// Note that this is also allowed if `actually_rustdoc` so
217-
// if a target is documenting some wasm-specific code then
218-
// it's not spuriously denied.
219-
//
220-
// Now that `#[target_feature]` is permitted on safe functions,
221-
// this exception must still exist for allowing the attribute on
222-
// `main`, `start`, and other functions that are not usually
223-
// allowed.
224-
} else {
225-
check_target_feature_trait_unsafe(tcx, did, attr.span());
226-
}
227-
}
228-
from_target_feature_attr(
229-
tcx,
230-
did,
231-
attr,
232-
rust_target_features,
233-
&mut codegen_fn_attrs.target_features,
234-
);
235-
}
236236
sym::linkage => {
237237
if let Some(val) = attr.value_str() {
238238
let linkage = Some(linkage_by_name(tcx, did, val.as_str()));
@@ -536,10 +536,10 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
536536
.map(|features| (features.name.as_str(), true))
537537
.collect(),
538538
) {
539-
let span = tcx
540-
.get_attrs(did, sym::target_feature)
541-
.next()
542-
.map_or_else(|| tcx.def_span(did), |a| a.span());
539+
let span =
540+
find_attr!(tcx.get_all_attrs(did), AttributeKind::TargetFeature(_, span) => *span)
541+
.unwrap_or_else(|| tcx.def_span(did));
542+
543543
tcx.dcx()
544544
.create_err(errors::TargetFeatureDisableOrEnable {
545545
features,

compiler/rustc_codegen_ssa/src/errors.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1292,3 +1292,14 @@ pub(crate) struct NoMangleNameless {
12921292
pub span: Span,
12931293
pub definition: String,
12941294
}
1295+
1296+
#[derive(Diagnostic)]
1297+
#[diag(codegen_ssa_feature_not_valid)]
1298+
pub(crate) struct FeatureNotValid<'a> {
1299+
pub feature: &'a str,
1300+
#[primary_span]
1301+
#[label]
1302+
pub span: Span,
1303+
#[help]
1304+
pub plus_hint: bool,
1305+
}

0 commit comments

Comments
 (0)