Skip to content

add flag to dump unstable feature status information #133351

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3596,6 +3596,7 @@ dependencies = [
"rustc_target",
"rustc_trait_selection",
"rustc_ty_utils",
"serde",
"serde_json",
"shlex",
"time",
Expand Down Expand Up @@ -4012,6 +4013,7 @@ dependencies = [
"rustc_span",
"rustc_target",
"rustc_type_ir",
"serde",
"tempfile",
"tracing",
]
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_driver_impl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ rustc_span = { path = "../rustc_span" }
rustc_target = { path = "../rustc_target" }
rustc_trait_selection = { path = "../rustc_trait_selection" }
rustc_ty_utils = { path = "../rustc_ty_utils" }
serde = { version = "1.0.125", features = [ "derive" ] }
serde_json = "1.0.59"
shlex = "1.0"
time = { version = "0.3.36", default-features = false, features = ["alloc", "formatting", "macros"] }
Expand Down
34 changes: 32 additions & 2 deletions compiler/rustc_driver_impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@

use std::cmp::max;
use std::collections::BTreeMap;
use std::error::Error;
use std::ffi::OsString;
use std::fmt::Write as _;
use std::fs::{self, File};
use std::io::{self, IsTerminal, Read, Write};
use std::io::{self, BufWriter, IsTerminal, Read, Write, stdout};
use std::panic::{self, PanicHookInfo, catch_unwind};
use std::path::PathBuf;
use std::process::{self, Command, Stdio};
Expand All @@ -45,7 +46,7 @@ use rustc_errors::registry::Registry;
use rustc_errors::{
ColorConfig, DiagCtxt, ErrCode, ErrorGuaranteed, FatalError, PResult, markdown,
};
use rustc_feature::find_gated_cfg;
use rustc_feature::{LangFeaturesStatus, find_gated_cfg};
use rustc_interface::util::{self, get_codegen_backend};
use rustc_interface::{Linker, Queries, interface, passes};
use rustc_lint::unerased_lint_store;
Expand Down Expand Up @@ -377,6 +378,11 @@ fn run_compiler(
return early_exit();
}

if sess.opts.unstable_opts.dump_features_status {
dump_features_status(sess, codegen_backend);
return early_exit();
}

if !has_input {
#[allow(rustc::diagnostic_outside_of_impl)]
sess.dcx().fatal("no input filename given"); // this is fatal
Expand Down Expand Up @@ -1571,6 +1577,30 @@ fn report_ice(
}
}

fn dump_features_status(sess: &Session, codegen_backend: &dyn CodegenBackend) {
#[derive(serde::Serialize)]
struct FeaturesStatus {
lang_features_status: LangFeaturesStatus,
lib_features_status: locator::LibFeaturesStatus,
}

let result: Result<(), Box<dyn Error>> = try {
let lang_features_status = rustc_feature::lang_features_status();
let lib_features_status =
rustc_metadata::lib_features_status(sess, &*codegen_backend.metadata_loader())?;
let value = FeaturesStatus { lang_features_status, lib_features_status };
let writer = stdout();
let writer = BufWriter::new(writer);
serde_json::to_writer_pretty(writer, &value)?;
};

// this happens before the global context and more importantly the diagnostic context is setup,
// so we can't report with the proper machinery
if let Err(error) = result {
panic!("cannot emit feature status json: {error}")
}
}

/// This allows tools to enable rust logging without having to magically match rustc's
/// tracing crate version.
pub fn init_rustc_env_logger(early_dcx: &EarlyDiagCtxt) {
Expand Down
56 changes: 56 additions & 0 deletions compiler/rustc_feature/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,62 @@ pub fn find_feature_issue(feature: Symbol, issue: GateIssue) -> Option<NonZero<u
}
}

#[derive(serde::Serialize)]
enum LangFeature {
Unstable {
status: &'static str,
symbol: String,
since: &'static str,
issue: Option<NonZero<u32>>,
},
Accepted {
symbol: String,
since: &'static str,
issue: Option<NonZero<u32>>,
},
Removed {
symbol: String,
since: &'static str,
issue: Option<NonZero<u32>>,
reason: Option<&'static str>,
},
}

#[derive(serde::Serialize)]
pub struct LangFeaturesStatus {
lang_features: Vec<LangFeature>,
}

/// Extract the status of all lang features as a json serializable struct
pub fn lang_features_status() -> LangFeaturesStatus {
let unstable_lang_features =
UNSTABLE_LANG_FEATURES.iter().map(|feature| LangFeature::Unstable {
status: Features::feature_status(feature.name),
symbol: feature.name.to_string(),
since: feature.since,
issue: feature.issue,
});

let accepted_lang_features =
ACCEPTED_LANG_FEATURES.iter().map(|feature| LangFeature::Accepted {
symbol: feature.name.to_string(),
since: feature.since,
issue: feature.issue,
});

let removed_lang_features = REMOVED_LANG_FEATURES.iter().map(|removed| LangFeature::Removed {
symbol: removed.feature.name.to_string(),
since: removed.feature.since,
issue: removed.feature.issue,
reason: removed.reason,
});

let lang_features =
unstable_lang_features.chain(accepted_lang_features).chain(removed_lang_features).collect();

LangFeaturesStatus { lang_features }
}

pub use accepted::ACCEPTED_LANG_FEATURES;
pub use builtin_attrs::{
AttributeDuplicates, AttributeGate, AttributeSafety, AttributeTemplate, AttributeType,
Expand Down
21 changes: 21 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,18 @@ impl Features {
}
}

macro_rules! status_to_str {
(unstable) => {
"default"
};
(incomplete) => {
"incomplete"
};
(internal) => {
"internal"
};
}

macro_rules! declare_features {
($(
$(#[doc = $doc:tt])* ($status:ident, $feature:ident, $ver:expr, $issue:expr),
Expand Down Expand Up @@ -159,6 +171,15 @@ macro_rules! declare_features {
_ => panic!("`{}` was not listed in `declare_features`", feature),
}
}

pub fn feature_status(feature: Symbol) -> &'static str {
match feature {
$(
sym::$feature => status_to_str!($status),
)*
_ => panic!("`{}` was not listed in `declare_features`", feature),
}
}
}
};
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_metadata/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
rustc_target = { path = "../rustc_target" }
rustc_type_ir = { path = "../rustc_type_ir" }
serde = { version = "1.0.125", features = [ "derive" ] }
tempfile = "3.2"
tracing = "0.1"
# tidy-alphabetical-end
2 changes: 2 additions & 0 deletions compiler/rustc_metadata/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#![feature(coroutines)]
#![feature(decl_macro)]
#![feature(error_iter)]
#![feature(error_reporter)]
#![feature(extract_if)]
#![feature(file_buffered)]
#![feature(if_let_guard)]
Expand Down Expand Up @@ -35,6 +36,7 @@ pub mod locator;

pub use creader::{DylibError, load_symbol_from_dylib};
pub use fs::{METADATA_FILENAME, emit_wrapper_file};
pub use locator::lib_features_status;
pub use native_libs::{
find_native_static_library, try_find_native_dynamic_library, try_find_native_static_library,
walk_native_lib_search_dirs,
Expand Down
84 changes: 84 additions & 0 deletions compiler/rustc_metadata/src/locator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@
//! metadata::locator or metadata::creader for all the juicy details!

use std::borrow::Cow;
use std::error::Error;
use std::io::{Result as IoResult, Write};
use std::ops::Deref;
use std::path::{Path, PathBuf};
Expand All @@ -224,6 +225,7 @@ use rustc_data_structures::owned_slice::slice_owned;
use rustc_data_structures::svh::Svh;
use rustc_errors::{DiagArgValue, IntoDiagArg};
use rustc_fs_util::try_canonicalize;
use rustc_middle::middle::lib_features::FeatureStability;
use rustc_session::Session;
use rustc_session::cstore::CrateSource;
use rustc_session::filesearch::FileSearch;
Expand Down Expand Up @@ -864,6 +866,88 @@ fn get_metadata_section<'p>(
}
}

#[derive(serde::Serialize)]
enum LibFeature {
Core { name: String, status: Status },
Std { name: String, status: Status },
}

#[derive(serde::Serialize)]
enum Status {
AcceptedSince(String),
Unstable,
}

#[derive(serde::Serialize)]
pub struct LibFeaturesStatus {
lib_features: Vec<LibFeature>,
}

pub fn lib_features_status(
sess: &Session,
metadata_loader: &dyn MetadataLoader,
) -> Result<LibFeaturesStatus, Box<dyn Error>> {
let is_rlib = true;
let hash = None;
let extra_filename = None;
let path_kind = PathKind::Crate;
let extra_prefix = "";

let mut core_locator = CrateLocator::new(
sess,
metadata_loader,
rustc_span::sym::core,
is_rlib,
hash,
extra_filename,
path_kind,
);
let Ok(Some(core)) = core_locator.find_library_crate(extra_prefix, &mut FxHashSet::default())
else {
Err("unable to locate core library")?
};
let mut std_locator = CrateLocator::new(
sess,
metadata_loader,
rustc_span::sym::std,
is_rlib,
hash,
extra_filename,
path_kind,
);
let Ok(Some(std)) = std_locator.find_library_crate(extra_prefix, &mut FxHashSet::default())
else {
Err("unable to locate standard library")?
};

let core_features =
core.metadata.dump_crate_features_json().into_iter().map(|(name, status)| {
LibFeature::Core {
name,
status: match status {
FeatureStability::AcceptedSince(symbol) => {
Status::AcceptedSince(symbol.to_string())
}
FeatureStability::Unstable => Status::Unstable,
},
}
});
let std_features =
std.metadata.dump_crate_features_json().into_iter().map(|(name, status)| LibFeature::Std {
name,
status: match status {
FeatureStability::AcceptedSince(symbol) => {
Status::AcceptedSince(symbol.to_string())
}
FeatureStability::Unstable => Status::Unstable,
},
});

let lib_features = core_features.chain(std_features).collect();

Ok(LibFeaturesStatus { lib_features })
}

/// A diagnostic function for dumping crate metadata to an output stream.
pub fn list_file_metadata(
target: &Target,
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_metadata/src/rmeta/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,14 @@ impl MetadataBlob {

Ok(())
}

pub(crate) fn dump_crate_features_json(&self) -> Vec<(String, FeatureStability)> {
let root = self.get_root();
root.lib_features
.decode(self)
.map(|(symbol, status)| (symbol.to_string(), status))
.collect()
}
}

impl CrateRoot {
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_session/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1730,6 +1730,8 @@ options! {
dump_dep_graph: bool = (false, parse_bool, [UNTRACKED],
"dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv) \
(default: no)"),
dump_features_status: bool = (false, parse_bool, [UNTRACKED],
"dump the status of rust language and library features as json"),
dump_mir: Option<String> = (None, parse_opt_string, [UNTRACKED],
"dump MIR state to file.
`val` is used to select which passes and functions to dump. For example:
Expand Down
Loading