1use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
2use rustc_data_structures::unord::{UnordMap, UnordSet};
3use rustc_hir::attrs::InstructionSetAttr;
4use rustc_hir::def::DefKind;
5use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
6use rustc_middle::middle::codegen_fn_attrs::{TargetFeature, TargetFeatureKind};
7use rustc_middle::query::Providers;
8use rustc_middle::ty::TyCtxt;
9use rustc_session::Session;
10use rustc_session::lint::builtin::AARCH64_SOFTFLOAT_NEON;
11use rustc_session::parse::feature_err;
12use rustc_span::{Span, Symbol, sym};
13use rustc_target::spec::Arch;
14use rustc_target::target_features::{RUSTC_SPECIFIC_FEATURES, Stability};
15use smallvec::SmallVec;
16
17use crate::errors::FeatureNotValid;
18use crate::{errors, target_features};
19
20pub(crate) fn from_target_feature_attr(
23 tcx: TyCtxt<'_>,
24 did: LocalDefId,
25 features: &[(Symbol, Span)],
26 was_forced: bool,
27 rust_target_features: &UnordMap<String, target_features::Stability>,
28 target_features: &mut Vec<TargetFeature>,
29) {
30 let rust_features = tcx.features();
31 let abi_feature_constraints = tcx.sess.target.abi_required_features();
32 for &(feature, feature_span) in features {
33 let feature_str = feature.as_str();
34 let Some(stability) = rust_target_features.get(feature_str) else {
35 let plus_hint = feature_str
36 .strip_prefix('+')
37 .is_some_and(|stripped| rust_target_features.contains_key(stripped));
38 tcx.dcx().emit_err(FeatureNotValid {
39 feature: feature_str,
40 span: feature_span,
41 plus_hint,
42 });
43 continue;
44 };
45
46 if let Err(reason) = stability.toggle_allowed() {
49 tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
50 span: feature_span,
51 feature: feature_str,
52 reason,
53 });
54 } else if let Some(nightly_feature) = stability.requires_nightly()
55 && !rust_features.enabled(nightly_feature)
56 {
57 feature_err(
58 &tcx.sess,
59 nightly_feature,
60 feature_span,
61 format!("the target feature `{feature}` is currently unstable"),
62 )
63 .emit();
64 } else {
65 for &name in tcx.implied_target_features(feature) {
67 if !tcx.sess.opts.actually_rustdoc {
74 if abi_feature_constraints.incompatible.contains(&name.as_str()) {
75 if tcx.sess.target.arch == Arch::AArch64 && name.as_str() == "neon" {
78 tcx.emit_node_span_lint(
79 AARCH64_SOFTFLOAT_NEON,
80 tcx.local_def_id_to_hir_id(did),
81 feature_span,
82 errors::Aarch64SoftfloatNeon,
83 );
84 } else {
85 tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
86 span: feature_span,
87 feature: name.as_str(),
88 reason: "this feature is incompatible with the target ABI",
89 });
90 }
91 }
92 }
93 let kind = if name != feature {
94 TargetFeatureKind::Implied
95 } else if was_forced {
96 TargetFeatureKind::Forced
97 } else {
98 TargetFeatureKind::Enabled
99 };
100 target_features.push(TargetFeature { name, kind })
101 }
102 }
103 }
104}
105
106fn asm_target_features(tcx: TyCtxt<'_>, did: DefId) -> &FxIndexSet<Symbol> {
109 let mut target_features = tcx.sess.unstable_target_features.clone();
110 if tcx.def_kind(did).has_codegen_attrs() {
111 let attrs = tcx.codegen_fn_attrs(did);
112 target_features.extend(attrs.target_features.iter().map(|feature| feature.name));
113 match attrs.instruction_set {
114 None => {}
115 Some(InstructionSetAttr::ArmA32) => {
116 target_features.swap_remove(&sym::thumb_mode);
118 }
119 Some(InstructionSetAttr::ArmT32) => {
120 target_features.insert(sym::thumb_mode);
121 }
122 }
123 }
124
125 tcx.arena.alloc(target_features)
126}
127
128pub(crate) fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_span: Span) {
131 if let DefKind::AssocFn = tcx.def_kind(id) {
132 let parent_id = tcx.local_parent(id);
133 if let DefKind::Trait | DefKind::Impl { of_trait: true } = tcx.def_kind(parent_id) {
134 tcx.dcx().emit_err(errors::TargetFeatureSafeTrait {
135 span: attr_span,
136 def: tcx.def_span(id),
137 });
138 }
139 }
140}
141
142fn parse_rust_feature_flag<'a>(
146 sess: &'a Session,
147 err_callback: impl Fn(&'a str),
148 mut callback: impl FnMut(
149 &'a str,
150 FxHashSet<&'a str>,
151 bool,
152 ),
153) {
154 let mut inverse_implied_features: Option<FxHashMap<&str, FxHashSet<&str>>> = None;
156
157 for feature in sess.opts.cg.target_feature.split(',') {
158 if let Some(base_feature) = feature.strip_prefix('+') {
159 if RUSTC_SPECIFIC_FEATURES.contains(&base_feature) {
161 continue;
162 }
163
164 callback(base_feature, sess.target.implied_target_features(base_feature), true)
165 } else if let Some(base_feature) = feature.strip_prefix('-') {
166 if RUSTC_SPECIFIC_FEATURES.contains(&base_feature) {
168 continue;
169 }
170
171 let inverse_implied_features = inverse_implied_features.get_or_insert_with(|| {
176 let mut set: FxHashMap<&str, FxHashSet<&str>> = FxHashMap::default();
177 for (f, _, is) in sess.target.rust_target_features() {
178 for i in is.iter() {
179 set.entry(i).or_default().insert(f);
180 }
181 }
182 set
183 });
184
185 let mut features = FxHashSet::default();
188 let mut new_features = vec![base_feature];
189 while let Some(new_feature) = new_features.pop() {
190 if features.insert(new_feature) {
191 if let Some(implied_features) = inverse_implied_features.get(&new_feature) {
192 #[allow(rustc::potential_query_instability)]
193 new_features.extend(implied_features)
194 }
195 }
196 }
197
198 callback(base_feature, features, false)
199 } else if !feature.is_empty() {
200 err_callback(feature)
201 }
202 }
203}
204
205pub fn cfg_target_feature<'a, const N: usize>(
220 sess: &Session,
221 to_backend_features: impl Fn(&'a str) -> SmallVec<[&'a str; N]>,
222 mut target_base_has_feature: impl FnMut(&str) -> bool,
223) -> (Vec<Symbol>, Vec<Symbol>) {
224 let known_features = sess.target.rust_target_features();
225
226 let mut features: UnordSet<Symbol> = sess
229 .target
230 .rust_target_features()
231 .iter()
232 .filter(|(feature, _, _)| target_base_has_feature(feature))
233 .flat_map(|(base_feature, _, _)| {
234 #[allow(rustc::potential_query_instability)]
240 sess.target.implied_target_features(base_feature).into_iter().map(|f| Symbol::intern(f))
241 })
242 .collect();
243
244 let mut enabled_disabled_features = FxHashMap::default();
245
246 parse_rust_feature_flag(
248 sess,
249 |feature| {
251 sess.dcx().emit_warn(errors::UnknownCTargetFeaturePrefix { feature });
252 },
253 |base_feature, new_features, enable| {
254 #[allow(rustc::potential_query_instability)]
256 enabled_disabled_features.extend(new_features.iter().map(|&s| (s, enable)));
257
258 #[allow(rustc::potential_query_instability)]
260 if enable {
261 features.extend(new_features.into_iter().map(|f| Symbol::intern(f)));
262 } else {
263 for new in new_features {
265 features.remove(&Symbol::intern(new));
266 }
267 }
268
269 let feature_state = known_features.iter().find(|&&(v, _, _)| v == base_feature);
271 match feature_state {
272 None => {
273 let rust_feature = known_features.iter().find_map(|&(rust_feature, _, _)| {
276 let backend_features = to_backend_features(rust_feature);
277 if backend_features.contains(&base_feature)
278 && !backend_features.contains(&rust_feature)
279 {
280 Some(rust_feature)
281 } else {
282 None
283 }
284 });
285 let unknown_feature = if let Some(rust_feature) = rust_feature {
286 errors::UnknownCTargetFeature {
287 feature: base_feature,
288 rust_feature: errors::PossibleFeature::Some { rust_feature },
289 }
290 } else {
291 errors::UnknownCTargetFeature {
292 feature: base_feature,
293 rust_feature: errors::PossibleFeature::None,
294 }
295 };
296 sess.dcx().emit_warn(unknown_feature);
297 }
298 Some((_, stability, _)) => {
299 if let Err(reason) = stability.toggle_allowed() {
300 sess.dcx().emit_warn(errors::ForbiddenCTargetFeature {
301 feature: base_feature,
302 enabled: if enable { "enabled" } else { "disabled" },
303 reason,
304 });
305 } else if stability.requires_nightly().is_some() {
306 sess.dcx()
310 .emit_warn(errors::UnstableCTargetFeature { feature: base_feature });
311 }
312 }
313 }
314 },
315 );
316
317 if let Some(f) = check_tied_features(sess, &enabled_disabled_features) {
318 sess.dcx().emit_err(errors::TargetFeatureDisableOrEnable {
319 features: f,
320 span: None,
321 missing_features: None,
322 });
323 }
324
325 let f = |allow_unstable| {
327 sess.target
328 .rust_target_features()
329 .iter()
330 .filter_map(|(feature, gate, _)| {
331 if allow_unstable
335 || (gate.in_cfg()
336 && (sess.is_nightly_build() || gate.requires_nightly().is_none()))
337 {
338 Some(Symbol::intern(feature))
339 } else {
340 None
341 }
342 })
343 .filter(|feature| features.contains(&feature))
344 .collect()
345 };
346
347 (f(true), f(false))
348}
349
350pub fn check_tied_features(
353 sess: &Session,
354 features: &FxHashMap<&str, bool>,
355) -> Option<&'static [&'static str]> {
356 if !features.is_empty() {
357 for tied in sess.target.tied_target_features() {
358 let mut tied_iter = tied.iter();
360 let enabled = features.get(tied_iter.next().unwrap());
361 if tied_iter.any(|f| enabled != features.get(f)) {
362 return Some(tied);
363 }
364 }
365 }
366 None
367}
368
369pub fn flag_to_backend_features<'a>(
374 sess: &'a Session,
375 mut extend_backend_features: impl FnMut(&'a str, bool),
376) {
377 let mut rust_features = vec![];
379 parse_rust_feature_flag(
380 sess,
381 |_feature| {
383 },
385 |_base_feature, new_features, enable| {
386 rust_features.extend(
387 UnordSet::from(new_features).to_sorted_stable_ord().iter().map(|&&s| (enable, s)),
388 );
389 },
390 );
391
392 for (enable, feature) in rust_features {
394 extend_backend_features(feature, enable);
395 }
396}
397
398pub fn retpoline_features_by_flags(sess: &Session, features: &mut Vec<String>) {
401 let unstable_opts = &sess.opts.unstable_opts;
404 if unstable_opts.retpoline && !unstable_opts.retpoline_external_thunk {
405 features.push("+retpoline-indirect-branches".into());
406 features.push("+retpoline-indirect-calls".into());
407 }
408 if unstable_opts.retpoline_external_thunk {
412 features.push("+retpoline-external-thunk".into());
413 features.push("+retpoline-indirect-branches".into());
414 features.push("+retpoline-indirect-calls".into());
415 }
416}
417
418pub(crate) fn provide(providers: &mut Providers) {
419 *providers = Providers {
420 rust_target_features: |tcx, cnum| {
421 assert_eq!(cnum, LOCAL_CRATE);
422 if tcx.sess.opts.actually_rustdoc {
423 let mut result: UnordMap<String, Stability> = Default::default();
429 for (name, stability) in rustc_target::target_features::all_rust_features() {
430 use std::collections::hash_map::Entry;
431 match result.entry(name.to_owned()) {
432 Entry::Vacant(vacant_entry) => {
433 vacant_entry.insert(stability);
434 }
435 Entry::Occupied(mut occupied_entry) => {
436 match (occupied_entry.get(), stability) {
438 (Stability::Stable, _)
439 | (
440 Stability::Unstable { .. },
441 Stability::Unstable { .. } | Stability::Forbidden { .. },
442 )
443 | (Stability::Forbidden { .. }, Stability::Forbidden { .. }) => {
444 }
447 _ => {
448 occupied_entry.insert(stability);
450 }
451 }
452 }
453 }
454 }
455 result
456 } else {
457 tcx.sess
458 .target
459 .rust_target_features()
460 .iter()
461 .map(|(a, b, _)| (a.to_string(), *b))
462 .collect()
463 }
464 },
465 implied_target_features: |tcx, feature: Symbol| {
466 let feature = feature.as_str();
467 UnordSet::from(tcx.sess.target.implied_target_features(feature))
468 .into_sorted_stable_ord()
469 .into_iter()
470 .map(|s| Symbol::intern(s))
471 .collect()
472 },
473 asm_target_features,
474 ..*providers
475 }
476}