Skip to content

Commit a50a3e1

Browse files
committed
add flag to dump unstable feature status information
1 parent 609925f commit a50a3e1

File tree

10 files changed

+211
-3
lines changed

10 files changed

+211
-3
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3595,6 +3595,7 @@ dependencies = [
35953595
"rustc_target",
35963596
"rustc_trait_selection",
35973597
"rustc_ty_utils",
3598+
"serde",
35983599
"serde_json",
35993600
"shlex",
36003601
"time",
@@ -4010,6 +4011,7 @@ dependencies = [
40104011
"rustc_span",
40114012
"rustc_target",
40124013
"rustc_type_ir",
4014+
"serde",
40134015
"tempfile",
40144016
"tracing",
40154017
]

compiler/rustc_driver_impl/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ rustc_span = { path = "../rustc_span" }
4747
rustc_target = { path = "../rustc_target" }
4848
rustc_trait_selection = { path = "../rustc_trait_selection" }
4949
rustc_ty_utils = { path = "../rustc_ty_utils" }
50+
serde = { version = "1.0.125", features = [ "derive" ] }
5051
serde_json = "1.0.59"
5152
shlex = "1.0"
5253
time = { version = "0.3.36", default-features = false, features = ["alloc", "formatting", "macros"] }

compiler/rustc_driver_impl/src/lib.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,17 @@
1515
#![feature(panic_update_hook)]
1616
#![feature(result_flattening)]
1717
#![feature(rustdoc_internals)]
18+
#![feature(try_blocks)]
1819
#![warn(unreachable_pub)]
1920
// tidy-alphabetical-end
2021

2122
use std::cmp::max;
2223
use std::collections::BTreeMap;
24+
use std::error::Error;
2325
use std::ffi::OsString;
2426
use std::fmt::Write as _;
2527
use std::fs::{self, File};
26-
use std::io::{self, IsTerminal, Read, Write};
28+
use std::io::{self, BufWriter, IsTerminal, Read, Write, stdout};
2729
use std::panic::{self, PanicHookInfo, catch_unwind};
2830
use std::path::PathBuf;
2931
use std::process::{self, Command, Stdio};
@@ -44,7 +46,7 @@ use rustc_errors::registry::Registry;
4446
use rustc_errors::{
4547
ColorConfig, DiagCtxt, ErrCode, ErrorGuaranteed, FatalError, PResult, markdown,
4648
};
47-
use rustc_feature::find_gated_cfg;
49+
use rustc_feature::{LangFeaturesStatus, find_gated_cfg};
4850
use rustc_interface::util::{self, get_codegen_backend};
4951
use rustc_interface::{Linker, Queries, interface, passes};
5052
use rustc_lint::unerased_lint_store;
@@ -376,6 +378,11 @@ fn run_compiler(
376378
return early_exit();
377379
}
378380

381+
if sess.opts.unstable_opts.dump_features_status {
382+
dump_features_status(sess, codegen_backend);
383+
return early_exit();
384+
}
385+
379386
if !has_input {
380387
#[allow(rustc::diagnostic_outside_of_impl)]
381388
sess.dcx().fatal("no input filename given"); // this is fatal
@@ -1578,6 +1585,30 @@ fn report_ice(
15781585
}
15791586
}
15801587

1588+
fn dump_features_status(sess: &Session, codegen_backend: &dyn CodegenBackend) {
1589+
#[derive(serde::Serialize)]
1590+
struct FeaturesStatus {
1591+
lang_features_status: LangFeaturesStatus,
1592+
lib_features_status: locator::LibFeaturesStatus,
1593+
}
1594+
1595+
let result: Result<(), Box<dyn Error>> = try {
1596+
let lang_features_status = rustc_feature::lang_features_status();
1597+
let lib_features_status =
1598+
rustc_metadata::lib_features_status(sess, &*codegen_backend.metadata_loader())?;
1599+
let value = FeaturesStatus { lang_features_status, lib_features_status };
1600+
let writer = stdout();
1601+
let writer = BufWriter::new(writer);
1602+
serde_json::to_writer_pretty(writer, &value)?;
1603+
};
1604+
1605+
// this happens before the global context and more importantly the diagnostic context is setup,
1606+
// so we can't report with the proper machinery
1607+
if let Err(error) = result {
1608+
panic!("cannot emit feature status json: {error}")
1609+
}
1610+
}
1611+
15811612
/// This allows tools to enable rust logging without having to magically match rustc's
15821613
/// tracing crate version.
15831614
pub fn init_rustc_env_logger(early_dcx: &EarlyDiagCtxt) {

compiler/rustc_feature/src/lib.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,62 @@ pub fn find_feature_issue(feature: Symbol, issue: GateIssue) -> Option<NonZero<u
127127
}
128128
}
129129

130+
#[derive(serde::Serialize)]
131+
enum LangFeature {
132+
Unstable {
133+
status: &'static str,
134+
symbol: String,
135+
since: &'static str,
136+
issue: Option<NonZero<u32>>,
137+
},
138+
Accepted {
139+
symbol: String,
140+
since: &'static str,
141+
issue: Option<NonZero<u32>>,
142+
},
143+
Removed {
144+
symbol: String,
145+
since: &'static str,
146+
issue: Option<NonZero<u32>>,
147+
reason: Option<&'static str>,
148+
},
149+
}
150+
151+
#[derive(serde::Serialize)]
152+
pub struct LangFeaturesStatus {
153+
lang_features: Vec<LangFeature>,
154+
}
155+
156+
/// Extract the status of all lang features as a json serializable struct
157+
pub fn lang_features_status() -> LangFeaturesStatus {
158+
let unstable_lang_features =
159+
UNSTABLE_LANG_FEATURES.iter().map(|feature| LangFeature::Unstable {
160+
status: Features::feature_status(feature.name),
161+
symbol: feature.name.to_string(),
162+
since: feature.since,
163+
issue: feature.issue,
164+
});
165+
166+
let accepted_lang_features =
167+
ACCEPTED_LANG_FEATURES.iter().map(|feature| LangFeature::Accepted {
168+
symbol: feature.name.to_string(),
169+
since: feature.since,
170+
issue: feature.issue,
171+
});
172+
173+
let removed_lang_features = REMOVED_LANG_FEATURES.iter().map(|removed| LangFeature::Removed {
174+
symbol: removed.feature.name.to_string(),
175+
since: removed.feature.since,
176+
issue: removed.feature.issue,
177+
reason: removed.reason,
178+
});
179+
180+
let lang_features =
181+
unstable_lang_features.chain(accepted_lang_features).chain(removed_lang_features).collect();
182+
183+
LangFeaturesStatus { lang_features }
184+
}
185+
130186
pub use accepted::ACCEPTED_LANG_FEATURES;
131187
pub use builtin_attrs::{
132188
AttributeDuplicates, AttributeGate, AttributeSafety, AttributeTemplate, AttributeType,

compiler/rustc_feature/src/unstable.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,18 @@ impl Features {
9999
}
100100
}
101101

102+
macro_rules! status_to_str {
103+
(unstable) => {
104+
"default"
105+
};
106+
(incomplete) => {
107+
"incomplete"
108+
};
109+
(internal) => {
110+
"internal"
111+
};
112+
}
113+
102114
macro_rules! declare_features {
103115
($(
104116
$(#[doc = $doc:tt])* ($status:ident, $feature:ident, $ver:expr, $issue:expr),
@@ -159,6 +171,15 @@ macro_rules! declare_features {
159171
_ => panic!("`{}` was not listed in `declare_features`", feature),
160172
}
161173
}
174+
175+
pub fn feature_status(feature: Symbol) -> &'static str {
176+
match feature {
177+
$(
178+
sym::$feature => status_to_str!($status),
179+
)*
180+
_ => panic!("`{}` was not listed in `declare_features`", feature),
181+
}
182+
}
162183
}
163184
};
164185
}
@@ -648,7 +669,7 @@ declare_features! (
648669

649670
// -------------------------------------------------------------------------
650671
// feature-group-end: actual feature gates
651-
// -------------------------------------------------------------------------
672+
// -------------------------------------------------------------------------
652673
);
653674

654675
impl Features {

compiler/rustc_metadata/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ rustc_session = { path = "../rustc_session" }
2727
rustc_span = { path = "../rustc_span" }
2828
rustc_target = { path = "../rustc_target" }
2929
rustc_type_ir = { path = "../rustc_type_ir" }
30+
serde = { version = "1.0.125", features = [ "derive" ] }
3031
tempfile = "3.2"
3132
tracing = "0.1"
3233
# tidy-alphabetical-end

compiler/rustc_metadata/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#![feature(coroutines)]
66
#![feature(decl_macro)]
77
#![feature(error_iter)]
8+
#![feature(error_reporter)]
89
#![feature(extract_if)]
910
#![feature(file_buffered)]
1011
#![feature(if_let_guard)]
@@ -35,6 +36,7 @@ pub mod locator;
3536

3637
pub use creader::{DylibError, load_symbol_from_dylib};
3738
pub use fs::{METADATA_FILENAME, emit_wrapper_file};
39+
pub use locator::lib_features_status;
3840
pub use native_libs::{
3941
find_native_static_library, try_find_native_dynamic_library, try_find_native_static_library,
4042
walk_native_lib_search_dirs,

compiler/rustc_metadata/src/locator.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@
213213
//! metadata::locator or metadata::creader for all the juicy details!
214214
215215
use std::borrow::Cow;
216+
use std::error::Error;
216217
use std::io::{Result as IoResult, Write};
217218
use std::ops::Deref;
218219
use std::path::{Path, PathBuf};
@@ -224,6 +225,7 @@ use rustc_data_structures::owned_slice::slice_owned;
224225
use rustc_data_structures::svh::Svh;
225226
use rustc_errors::{DiagArgValue, IntoDiagArg};
226227
use rustc_fs_util::try_canonicalize;
228+
use rustc_middle::middle::lib_features::FeatureStability;
227229
use rustc_session::Session;
228230
use rustc_session::cstore::CrateSource;
229231
use rustc_session::filesearch::FileSearch;
@@ -857,6 +859,88 @@ fn get_metadata_section<'p>(
857859
}
858860
}
859861

862+
#[derive(serde::Serialize)]
863+
enum LibFeature {
864+
Core { name: String, status: Status },
865+
Std { name: String, status: Status },
866+
}
867+
868+
#[derive(serde::Serialize)]
869+
enum Status {
870+
AcceptedSince(String),
871+
Unstable,
872+
}
873+
874+
#[derive(serde::Serialize)]
875+
pub struct LibFeaturesStatus {
876+
lib_features: Vec<LibFeature>,
877+
}
878+
879+
pub fn lib_features_status(
880+
sess: &Session,
881+
metadata_loader: &dyn MetadataLoader,
882+
) -> Result<LibFeaturesStatus, Box<dyn Error>> {
883+
let is_rlib = true;
884+
let hash = None;
885+
let extra_filename = None;
886+
let path_kind = PathKind::Crate;
887+
let extra_prefix = "";
888+
889+
let mut core_locator = CrateLocator::new(
890+
sess,
891+
metadata_loader,
892+
rustc_span::sym::core,
893+
is_rlib,
894+
hash,
895+
extra_filename,
896+
path_kind,
897+
);
898+
let Ok(Some(core)) = core_locator.find_library_crate(extra_prefix, &mut FxHashSet::default())
899+
else {
900+
Err("unable to locate core library")?
901+
};
902+
let mut std_locator = CrateLocator::new(
903+
sess,
904+
metadata_loader,
905+
rustc_span::sym::std,
906+
is_rlib,
907+
hash,
908+
extra_filename,
909+
path_kind,
910+
);
911+
let Ok(Some(std)) = std_locator.find_library_crate(extra_prefix, &mut FxHashSet::default())
912+
else {
913+
Err("unable to locate standard library")?
914+
};
915+
916+
let core_features =
917+
core.metadata.dump_crate_features_json().into_iter().map(|(name, status)| {
918+
LibFeature::Core {
919+
name,
920+
status: match status {
921+
FeatureStability::AcceptedSince(symbol) => {
922+
Status::AcceptedSince(symbol.to_string())
923+
}
924+
FeatureStability::Unstable => Status::Unstable,
925+
},
926+
}
927+
});
928+
let std_features =
929+
std.metadata.dump_crate_features_json().into_iter().map(|(name, status)| LibFeature::Std {
930+
name,
931+
status: match status {
932+
FeatureStability::AcceptedSince(symbol) => {
933+
Status::AcceptedSince(symbol.to_string())
934+
}
935+
FeatureStability::Unstable => Status::Unstable,
936+
},
937+
});
938+
939+
let lib_features = core_features.chain(std_features).collect();
940+
941+
Ok(LibFeaturesStatus { lib_features })
942+
}
943+
860944
/// A diagnostic function for dumping crate metadata to an output stream.
861945
pub fn list_file_metadata(
862946
target: &Target,

compiler/rustc_metadata/src/rmeta/decoder.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,14 @@ impl MetadataBlob {
935935

936936
Ok(())
937937
}
938+
939+
pub(crate) fn dump_crate_features_json(&self) -> Vec<(String, FeatureStability)> {
940+
let root = self.get_root();
941+
root.lib_features
942+
.decode(self)
943+
.map(|(symbol, status)| (symbol.to_string(), status))
944+
.collect()
945+
}
938946
}
939947

940948
impl CrateRoot {

compiler/rustc_session/src/options.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1730,6 +1730,8 @@ options! {
17301730
dump_dep_graph: bool = (false, parse_bool, [UNTRACKED],
17311731
"dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv) \
17321732
(default: no)"),
1733+
dump_features_status: bool = (false, parse_bool, [UNTRACKED],
1734+
"dump the status of rust language and library features as json"),
17331735
dump_mir: Option<String> = (None, parse_opt_string, [UNTRACKED],
17341736
"dump MIR state to file.
17351737
`val` is used to select which passes and functions to dump. For example:

0 commit comments

Comments
 (0)