Skip to content

Commit 0068b8b

Browse files
committed
Auto merge of #100328 - davidtwco:perf-implications, r=nnethercote
passes: load `defined_lib_features` query less Hopefully addresses the perf regressions from #99212 (see #99905). Re-structure the stability checks for library features to avoid calling `defined_lib_features` for any more crates than necessary for each of the implications or local feature attributes that need validation. r? `@ghost` (just checking perf at first)
2 parents 569788e + 5e2e478 commit 0068b8b

File tree

1 file changed

+91
-36
lines changed

1 file changed

+91
-36
lines changed

compiler/rustc_passes/src/stability.rs

+91-36
Original file line numberDiff line numberDiff line change
@@ -962,58 +962,113 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
962962
remaining_lib_features.remove(&sym::libc);
963963
remaining_lib_features.remove(&sym::test);
964964

965-
// We always collect the lib features declared in the current crate, even if there are
966-
// no unknown features, because the collection also does feature attribute validation.
967-
let local_defined_features = tcx.lib_features(());
968-
let mut all_lib_features: FxHashMap<_, _> =
969-
local_defined_features.to_vec().iter().map(|el| *el).collect();
970-
let mut implications = tcx.stability_implications(rustc_hir::def_id::LOCAL_CRATE).clone();
971-
for &cnum in tcx.crates(()) {
972-
implications.extend(tcx.stability_implications(cnum));
973-
all_lib_features.extend(tcx.defined_lib_features(cnum).iter().map(|el| *el));
974-
}
975-
976-
// Check that every feature referenced by an `implied_by` exists (for features defined in the
977-
// local crate).
978-
for (implied_by, feature) in tcx.stability_implications(rustc_hir::def_id::LOCAL_CRATE) {
979-
// Only `implied_by` needs to be checked, `feature` is guaranteed to exist.
980-
if !all_lib_features.contains_key(implied_by) {
981-
let span = local_defined_features
982-
.stable
983-
.get(feature)
984-
.map(|(_, span)| span)
985-
.or_else(|| local_defined_features.unstable.get(feature))
986-
.expect("feature that implied another does not exist");
987-
tcx.sess
988-
.struct_span_err(
989-
*span,
990-
format!("feature `{implied_by}` implying `{feature}` does not exist"),
991-
)
992-
.emit();
993-
}
994-
}
995-
996-
if !remaining_lib_features.is_empty() {
997-
for (feature, since) in all_lib_features.iter() {
965+
/// For each feature in `defined_features`..
966+
///
967+
/// - If it is in `remaining_lib_features` (those features with `#![feature(..)]` attributes in
968+
/// the current crate), check if it is stable (or partially stable) and thus an unnecessary
969+
/// attribute.
970+
/// - If it is in `remaining_implications` (a feature that is referenced by an `implied_by`
971+
/// from the current crate), then remove it from the remaining implications.
972+
///
973+
/// Once this function has been invoked for every feature (local crate and all extern crates),
974+
/// then..
975+
///
976+
/// - If features remain in `remaining_lib_features`, then the user has enabled a feature that
977+
/// does not exist.
978+
/// - If features remain in `remaining_implications`, the `implied_by` refers to a feature that
979+
/// does not exist.
980+
///
981+
/// By structuring the code in this way: checking the features defined from each crate one at a
982+
/// time, less loading from metadata is performed and thus compiler performance is improved.
983+
fn check_features<'tcx>(
984+
tcx: TyCtxt<'tcx>,
985+
remaining_lib_features: &mut FxIndexMap<&Symbol, Span>,
986+
remaining_implications: &mut FxHashMap<Symbol, Symbol>,
987+
defined_features: &[(Symbol, Option<Symbol>)],
988+
all_implications: &FxHashMap<Symbol, Symbol>,
989+
) {
990+
for (feature, since) in defined_features {
998991
if let Some(since) = since && let Some(span) = remaining_lib_features.get(&feature) {
999992
// Warn if the user has enabled an already-stable lib feature.
1000-
if let Some(implies) = implications.get(&feature) {
993+
if let Some(implies) = all_implications.get(&feature) {
1001994
unnecessary_partially_stable_feature_lint(tcx, *span, *feature, *implies, *since);
1002995
} else {
1003996
unnecessary_stable_feature_lint(tcx, *span, *feature, *since);
1004997
}
998+
1005999
}
1006-
remaining_lib_features.remove(&feature);
1007-
if remaining_lib_features.is_empty() {
1000+
remaining_lib_features.remove(feature);
1001+
1002+
// `feature` is the feature doing the implying, but `implied_by` is the feature with
1003+
// the attribute that establishes this relationship. `implied_by` is guaranteed to be a
1004+
// feature defined in the local crate because `remaining_implications` is only the
1005+
// implications from this crate.
1006+
remaining_implications.remove(feature);
1007+
1008+
if remaining_lib_features.is_empty() && remaining_implications.is_empty() {
10081009
break;
10091010
}
10101011
}
10111012
}
10121013

1014+
// All local crate implications need to have the feature that implies it confirmed to exist.
1015+
let mut remaining_implications =
1016+
tcx.stability_implications(rustc_hir::def_id::LOCAL_CRATE).clone();
1017+
1018+
// We always collect the lib features declared in the current crate, even if there are
1019+
// no unknown features, because the collection also does feature attribute validation.
1020+
let local_defined_features = tcx.lib_features(()).to_vec();
1021+
if !remaining_lib_features.is_empty() || !remaining_implications.is_empty() {
1022+
// Loading the implications of all crates is unavoidable to be able to emit the partial
1023+
// stabilization diagnostic, but it can be avoided when there are no
1024+
// `remaining_lib_features`.
1025+
let mut all_implications = remaining_implications.clone();
1026+
for &cnum in tcx.crates(()) {
1027+
all_implications.extend(tcx.stability_implications(cnum));
1028+
}
1029+
1030+
check_features(
1031+
tcx,
1032+
&mut remaining_lib_features,
1033+
&mut remaining_implications,
1034+
local_defined_features.as_slice(),
1035+
&all_implications,
1036+
);
1037+
1038+
for &cnum in tcx.crates(()) {
1039+
if remaining_lib_features.is_empty() && remaining_implications.is_empty() {
1040+
break;
1041+
}
1042+
check_features(
1043+
tcx,
1044+
&mut remaining_lib_features,
1045+
&mut remaining_implications,
1046+
tcx.defined_lib_features(cnum).to_vec().as_slice(),
1047+
&all_implications,
1048+
);
1049+
}
1050+
}
1051+
10131052
for (feature, span) in remaining_lib_features {
10141053
struct_span_err!(tcx.sess, span, E0635, "unknown feature `{}`", feature).emit();
10151054
}
10161055

1056+
for (implied_by, feature) in remaining_implications {
1057+
let local_defined_features = tcx.lib_features(());
1058+
let span = local_defined_features
1059+
.stable
1060+
.get(&feature)
1061+
.map(|(_, span)| span)
1062+
.or_else(|| local_defined_features.unstable.get(&feature))
1063+
.expect("feature that implied another does not exist");
1064+
tcx.sess
1065+
.struct_span_err(
1066+
*span,
1067+
format!("feature `{implied_by}` implying `{feature}` does not exist"),
1068+
)
1069+
.emit();
1070+
}
1071+
10171072
// FIXME(#44232): the `used_features` table no longer exists, so we
10181073
// don't lint about unused features. We should re-enable this one day!
10191074
}

0 commit comments

Comments
 (0)