|
| 1 | +use rustc_ast::ast; |
| 2 | +use rustc_attr::InstructionSetAttr; |
| 3 | +use rustc_data_structures::fx::FxHashMap; |
| 4 | +use rustc_data_structures::fx::FxHashSet; |
| 5 | +use rustc_errors::Applicability; |
| 6 | +use rustc_hir as hir; |
| 7 | +use rustc_hir::def_id::DefId; |
| 8 | +use rustc_hir::def_id::LocalDefId; |
1 | 9 | use rustc_hir::def_id::LOCAL_CRATE;
|
2 | 10 | use rustc_middle::ty::query::Providers;
|
| 11 | +use rustc_middle::ty::TyCtxt; |
| 12 | +use rustc_session::parse::feature_err; |
3 | 13 | use rustc_session::Session;
|
4 | 14 | use rustc_span::symbol::sym;
|
5 | 15 | use rustc_span::symbol::Symbol;
|
| 16 | +use rustc_span::Span; |
6 | 17 |
|
7 | 18 | /// Features that control behaviour of rustc, rather than the codegen.
|
8 | 19 | pub const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"];
|
@@ -322,15 +333,148 @@ pub fn tied_target_features(sess: &Session) -> &'static [&'static [&'static str]
|
322 | 333 | }
|
323 | 334 | }
|
324 | 335 |
|
325 |
| -pub(crate) fn provide(providers: &mut Providers) { |
326 |
| - providers.supported_target_features = |tcx, cnum| { |
327 |
| - assert_eq!(cnum, LOCAL_CRATE); |
328 |
| - if tcx.sess.opts.actually_rustdoc { |
329 |
| - // rustdoc needs to be able to document functions that use all the features, so |
330 |
| - // whitelist them all |
331 |
| - all_known_features().map(|(a, b)| (a.to_string(), b)).collect() |
332 |
| - } else { |
333 |
| - supported_target_features(tcx.sess).iter().map(|&(a, b)| (a.to_string(), b)).collect() |
334 |
| - } |
| 336 | +pub fn from_target_feature( |
| 337 | + tcx: TyCtxt<'_>, |
| 338 | + attr: &ast::Attribute, |
| 339 | + supported_target_features: &FxHashMap<String, Option<Symbol>>, |
| 340 | + target_features: &mut Vec<Symbol>, |
| 341 | +) { |
| 342 | + let Some(list) = attr.meta_item_list() else { return }; |
| 343 | + let bad_item = |span| { |
| 344 | + let msg = "malformed `target_feature` attribute input"; |
| 345 | + let code = "enable = \"..\""; |
| 346 | + tcx.sess |
| 347 | + .struct_span_err(span, msg) |
| 348 | + .span_suggestion(span, "must be of the form", code, Applicability::HasPlaceholders) |
| 349 | + .emit(); |
335 | 350 | };
|
| 351 | + let rust_features = tcx.features(); |
| 352 | + for item in list { |
| 353 | + // Only `enable = ...` is accepted in the meta-item list. |
| 354 | + if !item.has_name(sym::enable) { |
| 355 | + bad_item(item.span()); |
| 356 | + continue; |
| 357 | + } |
| 358 | + |
| 359 | + // Must be of the form `enable = "..."` (a string). |
| 360 | + let Some(value) = item.value_str() else { |
| 361 | + bad_item(item.span()); |
| 362 | + continue; |
| 363 | + }; |
| 364 | + |
| 365 | + // We allow comma separation to enable multiple features. |
| 366 | + target_features.extend(value.as_str().split(',').filter_map(|feature| { |
| 367 | + let Some(feature_gate) = supported_target_features.get(feature) else { |
| 368 | + let msg = |
| 369 | + format!("the feature named `{}` is not valid for this target", feature); |
| 370 | + let mut err = tcx.sess.struct_span_err(item.span(), &msg); |
| 371 | + err.span_label( |
| 372 | + item.span(), |
| 373 | + format!("`{}` is not valid for this target", feature), |
| 374 | + ); |
| 375 | + if let Some(stripped) = feature.strip_prefix('+') { |
| 376 | + let valid = supported_target_features.contains_key(stripped); |
| 377 | + if valid { |
| 378 | + err.help("consider removing the leading `+` in the feature name"); |
| 379 | + } |
| 380 | + } |
| 381 | + err.emit(); |
| 382 | + return None; |
| 383 | + }; |
| 384 | + |
| 385 | + // Only allow features whose feature gates have been enabled. |
| 386 | + let allowed = match feature_gate.as_ref().copied() { |
| 387 | + Some(sym::arm_target_feature) => rust_features.arm_target_feature, |
| 388 | + Some(sym::hexagon_target_feature) => rust_features.hexagon_target_feature, |
| 389 | + Some(sym::powerpc_target_feature) => rust_features.powerpc_target_feature, |
| 390 | + Some(sym::mips_target_feature) => rust_features.mips_target_feature, |
| 391 | + Some(sym::riscv_target_feature) => rust_features.riscv_target_feature, |
| 392 | + Some(sym::avx512_target_feature) => rust_features.avx512_target_feature, |
| 393 | + Some(sym::sse4a_target_feature) => rust_features.sse4a_target_feature, |
| 394 | + Some(sym::tbm_target_feature) => rust_features.tbm_target_feature, |
| 395 | + Some(sym::wasm_target_feature) => rust_features.wasm_target_feature, |
| 396 | + Some(sym::cmpxchg16b_target_feature) => rust_features.cmpxchg16b_target_feature, |
| 397 | + Some(sym::movbe_target_feature) => rust_features.movbe_target_feature, |
| 398 | + Some(sym::rtm_target_feature) => rust_features.rtm_target_feature, |
| 399 | + Some(sym::f16c_target_feature) => rust_features.f16c_target_feature, |
| 400 | + Some(sym::ermsb_target_feature) => rust_features.ermsb_target_feature, |
| 401 | + Some(sym::bpf_target_feature) => rust_features.bpf_target_feature, |
| 402 | + Some(sym::aarch64_ver_target_feature) => rust_features.aarch64_ver_target_feature, |
| 403 | + Some(name) => bug!("unknown target feature gate {}", name), |
| 404 | + None => true, |
| 405 | + }; |
| 406 | + if !allowed { |
| 407 | + feature_err( |
| 408 | + &tcx.sess.parse_sess, |
| 409 | + feature_gate.unwrap(), |
| 410 | + item.span(), |
| 411 | + &format!("the target feature `{}` is currently unstable", feature), |
| 412 | + ) |
| 413 | + .emit(); |
| 414 | + } |
| 415 | + Some(Symbol::intern(feature)) |
| 416 | + })); |
| 417 | + } |
| 418 | +} |
| 419 | + |
| 420 | +/// Computes the set of target features used in a function for the purposes of |
| 421 | +/// inline assembly. |
| 422 | +fn asm_target_features<'tcx>(tcx: TyCtxt<'tcx>, did: DefId) -> &'tcx FxHashSet<Symbol> { |
| 423 | + let mut target_features = tcx.sess.unstable_target_features.clone(); |
| 424 | + if tcx.def_kind(did).has_codegen_attrs() { |
| 425 | + let attrs = tcx.codegen_fn_attrs(did); |
| 426 | + target_features.extend(&attrs.target_features); |
| 427 | + match attrs.instruction_set { |
| 428 | + None => {} |
| 429 | + Some(InstructionSetAttr::ArmA32) => { |
| 430 | + target_features.remove(&sym::thumb_mode); |
| 431 | + } |
| 432 | + Some(InstructionSetAttr::ArmT32) => { |
| 433 | + target_features.insert(sym::thumb_mode); |
| 434 | + } |
| 435 | + } |
| 436 | + } |
| 437 | + |
| 438 | + tcx.arena.alloc(target_features) |
| 439 | +} |
| 440 | + |
| 441 | +/// Checks the function annotated with `#[target_feature]` is not a safe |
| 442 | +/// trait method implementation, reporting an error if it is. |
| 443 | +pub fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_span: Span) { |
| 444 | + let hir_id = tcx.hir().local_def_id_to_hir_id(id); |
| 445 | + let node = tcx.hir().get(hir_id); |
| 446 | + if let hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) = node { |
| 447 | + let parent_id = tcx.hir().get_parent_item(hir_id); |
| 448 | + let parent_item = tcx.hir().expect_item(parent_id.def_id); |
| 449 | + if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = parent_item.kind { |
| 450 | + tcx.sess |
| 451 | + .struct_span_err( |
| 452 | + attr_span, |
| 453 | + "`#[target_feature(..)]` cannot be applied to safe trait method", |
| 454 | + ) |
| 455 | + .span_label(attr_span, "cannot be applied to safe trait method") |
| 456 | + .span_label(tcx.def_span(id), "not an `unsafe` function") |
| 457 | + .emit(); |
| 458 | + } |
| 459 | + } |
| 460 | +} |
| 461 | + |
| 462 | +pub(crate) fn provide(providers: &mut Providers) { |
| 463 | + *providers = Providers { |
| 464 | + supported_target_features: |tcx, cnum| { |
| 465 | + assert_eq!(cnum, LOCAL_CRATE); |
| 466 | + if tcx.sess.opts.actually_rustdoc { |
| 467 | + // rustdoc needs to be able to document functions that use all the features, so |
| 468 | + // whitelist them all |
| 469 | + all_known_features().map(|(a, b)| (a.to_string(), b)).collect() |
| 470 | + } else { |
| 471 | + supported_target_features(tcx.sess) |
| 472 | + .iter() |
| 473 | + .map(|&(a, b)| (a.to_string(), b)) |
| 474 | + .collect() |
| 475 | + } |
| 476 | + }, |
| 477 | + asm_target_features, |
| 478 | + ..*providers |
| 479 | + } |
336 | 480 | }
|
0 commit comments