1use rustc_attr_data_structures::InstructionSetAttr;
2use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
3use rustc_data_structures::unord::{UnordMap, UnordSet};
4use rustc_hir::def::DefKind;
5use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
6use rustc_middle::middle::codegen_fn_attrs::TargetFeature;
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::target_features::{RUSTC_SPECIFIC_FEATURES, Stability};
14use smallvec::SmallVec;
15
16use crate::errors::FeatureNotValid;
17use crate::{errors, target_features};
18
19pub(crate) fn from_target_feature_attr(
22 tcx: TyCtxt<'_>,
23 did: LocalDefId,
24 features: &[(Symbol, Span)],
25 rust_target_features: &UnordMap<String, target_features::Stability>,
26 target_features: &mut Vec<TargetFeature>,
27) {
28 let rust_features = tcx.features();
29 let abi_feature_constraints = tcx.sess.target.abi_required_features();
30 for &(feature, feature_span) in features {
31 let feature_str = feature.as_str();
32 let Some(stability) = rust_target_features.get(feature_str) else {
33 let plus_hint = feature_str
34 .strip_prefix('+')
35 .is_some_and(|stripped| rust_target_features.contains_key(stripped));
36 tcx.dcx().emit_err(FeatureNotValid {
37 feature: feature_str,
38 span: feature_span,
39 plus_hint,
40 });
41 continue;
42 };
43
44 if let Err(reason) = stability.toggle_allowed() {
47 tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
48 span: feature_span,
49 feature: feature_str,
50 reason,
51 });
52 } else if let Some(nightly_feature) = stability.requires_nightly()
53 && !rust_features.enabled(nightly_feature)
54 {
55 feature_err(
56 &tcx.sess,
57 nightly_feature,
58 feature_span,
59 format!("the target feature `{feature}` is currently unstable"),
60 )
61 .emit();
62 } else {
63 for &name in tcx.implied_target_features(feature) {
65 if !tcx.sess.opts.actually_rustdoc {
72 if abi_feature_constraints.incompatible.contains(&name.as_str()) {
73 if tcx.sess.target.arch == "aarch64" && name.as_str() == "neon" {
76 tcx.emit_node_span_lint(
77 AARCH64_SOFTFLOAT_NEON,
78 tcx.local_def_id_to_hir_id(did),
79 feature_span,
80 errors::Aarch64SoftfloatNeon,
81 );
82 } else {
83 tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
84 span: feature_span,
85 feature: name.as_str(),
86 reason: "this feature is incompatible with the target ABI",
87 });
88 }
89 }
90 }
91 target_features.push(TargetFeature { name, implied: name != feature })
92 }
93 }
94 }
95}
96
97fn asm_target_features(tcx: TyCtxt<'_>, did: DefId) -> &FxIndexSet<Symbol> {
100 let mut target_features = tcx.sess.unstable_target_features.clone();
101 if tcx.def_kind(did).has_codegen_attrs() {
102 let attrs = tcx.codegen_fn_attrs(did);
103 target_features.extend(attrs.target_features.iter().map(|feature| feature.name));
104 match attrs.instruction_set {
105 None => {}
106 Some(InstructionSetAttr::ArmA32) => {
107 target_features.swap_remove(&sym::thumb_mode);
109 }
110 Some(InstructionSetAttr::ArmT32) => {
111 target_features.insert(sym::thumb_mode);
112 }
113 }
114 }
115
116 tcx.arena.alloc(target_features)
117}
118
119pub(crate) fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_span: Span) {
122 if let DefKind::AssocFn = tcx.def_kind(id) {
123 let parent_id = tcx.local_parent(id);
124 if let DefKind::Trait | DefKind::Impl { of_trait: true } = tcx.def_kind(parent_id) {
125 tcx.dcx().emit_err(errors::TargetFeatureSafeTrait {
126 span: attr_span,
127 def: tcx.def_span(id),
128 });
129 }
130 }
131}
132
133fn parse_rust_feature_flag<'a>(
137 sess: &'a Session,
138 err_callback: impl Fn(&'a str),
139 mut callback: impl FnMut(
140 &'a str,
141 FxHashSet<&'a str>,
142 bool,
143 ),
144) {
145 let mut inverse_implied_features: Option<FxHashMap<&str, FxHashSet<&str>>> = None;
147
148 for feature in sess.opts.cg.target_feature.split(',') {
149 if let Some(base_feature) = feature.strip_prefix('+') {
150 if RUSTC_SPECIFIC_FEATURES.contains(&base_feature) {
152 return;
153 }
154
155 callback(base_feature, sess.target.implied_target_features(base_feature), true)
156 } else if let Some(base_feature) = feature.strip_prefix('-') {
157 if RUSTC_SPECIFIC_FEATURES.contains(&base_feature) {
159 return;
160 }
161
162 let inverse_implied_features = inverse_implied_features.get_or_insert_with(|| {
167 let mut set: FxHashMap<&str, FxHashSet<&str>> = FxHashMap::default();
168 for (f, _, is) in sess.target.rust_target_features() {
169 for i in is.iter() {
170 set.entry(i).or_default().insert(f);
171 }
172 }
173 set
174 });
175
176 let mut features = FxHashSet::default();
179 let mut new_features = vec![base_feature];
180 while let Some(new_feature) = new_features.pop() {
181 if features.insert(new_feature) {
182 if let Some(implied_features) = inverse_implied_features.get(&new_feature) {
183 new_features.extend(implied_features)
184 }
185 }
186 }
187
188 callback(base_feature, features, false)
189 } else if !feature.is_empty() {
190 err_callback(feature)
191 }
192 }
193}
194
195pub fn cfg_target_feature(
204 sess: &Session,
205 mut target_base_has_feature: impl FnMut(&str) -> bool,
206) -> (Vec<Symbol>, Vec<Symbol>) {
207 let mut features: UnordSet<Symbol> = sess
210 .target
211 .rust_target_features()
212 .iter()
213 .filter(|(feature, _, _)| target_base_has_feature(feature))
214 .map(|(feature, _, _)| Symbol::intern(feature))
215 .collect();
216
217 parse_rust_feature_flag(
219 sess,
220 |_| {
222 },
224 |_base_feature, new_features, enabled| {
225 #[allow(rustc::potential_query_instability)]
227 if enabled {
228 features.extend(new_features.into_iter().map(|f| Symbol::intern(f)));
229 } else {
230 for new in new_features {
232 features.remove(&Symbol::intern(new));
233 }
234 }
235 },
236 );
237
238 let f = |allow_unstable| {
240 sess.target
241 .rust_target_features()
242 .iter()
243 .filter_map(|(feature, gate, _)| {
244 if allow_unstable
248 || (gate.in_cfg()
249 && (sess.is_nightly_build() || gate.requires_nightly().is_none()))
250 {
251 Some(Symbol::intern(feature))
252 } else {
253 None
254 }
255 })
256 .filter(|feature| features.contains(&feature))
257 .collect()
258 };
259
260 (f(true), f(false))
261}
262
263pub fn check_tied_features(
266 sess: &Session,
267 features: &FxHashMap<&str, bool>,
268) -> Option<&'static [&'static str]> {
269 if !features.is_empty() {
270 for tied in sess.target.tied_target_features() {
271 let mut tied_iter = tied.iter();
273 let enabled = features.get(tied_iter.next().unwrap());
274 if tied_iter.any(|f| enabled != features.get(f)) {
275 return Some(tied);
276 }
277 }
278 }
279 None
280}
281
282pub fn flag_to_backend_features<'a, const N: usize>(
290 sess: &'a Session,
291 diagnostics: bool,
292 to_backend_features: impl Fn(&'a str) -> SmallVec<[&'a str; N]>,
293 mut extend_backend_features: impl FnMut(&'a str, bool),
294) {
295 let known_features = sess.target.rust_target_features();
296
297 let mut rust_features = vec![];
299 parse_rust_feature_flag(
300 sess,
301 |feature| {
303 if diagnostics {
304 sess.dcx().emit_warn(errors::UnknownCTargetFeaturePrefix { feature });
305 }
306 },
307 |base_feature, new_features, enable| {
308 rust_features.extend(
309 UnordSet::from(new_features).to_sorted_stable_ord().iter().map(|&&s| (enable, s)),
310 );
311 if diagnostics {
313 let feature_state = known_features.iter().find(|&&(v, _, _)| v == base_feature);
314 match feature_state {
315 None => {
316 let rust_feature =
319 known_features.iter().find_map(|&(rust_feature, _, _)| {
320 let backend_features = to_backend_features(rust_feature);
321 if backend_features.contains(&base_feature)
322 && !backend_features.contains(&rust_feature)
323 {
324 Some(rust_feature)
325 } else {
326 None
327 }
328 });
329 let unknown_feature = if let Some(rust_feature) = rust_feature {
330 errors::UnknownCTargetFeature {
331 feature: base_feature,
332 rust_feature: errors::PossibleFeature::Some { rust_feature },
333 }
334 } else {
335 errors::UnknownCTargetFeature {
336 feature: base_feature,
337 rust_feature: errors::PossibleFeature::None,
338 }
339 };
340 sess.dcx().emit_warn(unknown_feature);
341 }
342 Some((_, stability, _)) => {
343 if let Err(reason) = stability.toggle_allowed() {
344 sess.dcx().emit_warn(errors::ForbiddenCTargetFeature {
345 feature: base_feature,
346 enabled: if enable { "enabled" } else { "disabled" },
347 reason,
348 });
349 } else if stability.requires_nightly().is_some() {
350 sess.dcx().emit_warn(errors::UnstableCTargetFeature {
354 feature: base_feature,
355 });
356 }
357 }
358 }
359 }
360 },
361 );
362
363 if diagnostics {
364 if let Some(f) = check_tied_features(
366 sess,
367 &FxHashMap::from_iter(rust_features.iter().map(|&(enable, feature)| (feature, enable))),
368 ) {
369 sess.dcx().emit_err(errors::TargetFeatureDisableOrEnable {
370 features: f,
371 span: None,
372 missing_features: None,
373 });
374 }
375 }
376
377 for (enable, feature) in rust_features {
379 extend_backend_features(feature, enable);
380 }
381}
382
383pub fn retpoline_features_by_flags(sess: &Session, features: &mut Vec<String>) {
386 let unstable_opts = &sess.opts.unstable_opts;
389 if unstable_opts.retpoline && !unstable_opts.retpoline_external_thunk {
390 features.push("+retpoline-indirect-branches".into());
391 features.push("+retpoline-indirect-calls".into());
392 }
393 if unstable_opts.retpoline_external_thunk {
397 features.push("+retpoline-external-thunk".into());
398 features.push("+retpoline-indirect-branches".into());
399 features.push("+retpoline-indirect-calls".into());
400 }
401}
402
403pub(crate) fn provide(providers: &mut Providers) {
404 *providers = Providers {
405 rust_target_features: |tcx, cnum| {
406 assert_eq!(cnum, LOCAL_CRATE);
407 if tcx.sess.opts.actually_rustdoc {
408 let mut result: UnordMap<String, Stability> = Default::default();
414 for (name, stability) in rustc_target::target_features::all_rust_features() {
415 use std::collections::hash_map::Entry;
416 match result.entry(name.to_owned()) {
417 Entry::Vacant(vacant_entry) => {
418 vacant_entry.insert(stability);
419 }
420 Entry::Occupied(mut occupied_entry) => {
421 match (occupied_entry.get(), stability) {
423 (Stability::Stable, _)
424 | (
425 Stability::Unstable { .. },
426 Stability::Unstable { .. } | Stability::Forbidden { .. },
427 )
428 | (Stability::Forbidden { .. }, Stability::Forbidden { .. }) => {
429 }
432 _ => {
433 occupied_entry.insert(stability);
435 }
436 }
437 }
438 }
439 }
440 result
441 } else {
442 tcx.sess
443 .target
444 .rust_target_features()
445 .iter()
446 .map(|(a, b, _)| (a.to_string(), *b))
447 .collect()
448 }
449 },
450 implied_target_features: |tcx, feature: Symbol| {
451 let feature = feature.as_str();
452 UnordSet::from(tcx.sess.target.implied_target_features(feature))
453 .into_sorted_stable_ord()
454 .into_iter()
455 .map(|s| Symbol::intern(s))
456 .collect()
457 },
458 asm_target_features,
459 ..*providers
460 }
461}