Skip to content

Commit 5e2e478

Browse files
committed
passes: load defined_lib_features query less
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.
1 parent cc4dd6f commit 5e2e478

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
@@ -949,58 +949,113 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
949949
remaining_lib_features.remove(&sym::libc);
950950
remaining_lib_features.remove(&sym::test);
951951

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

1001+
// All local crate implications need to have the feature that implies it confirmed to exist.
1002+
let mut remaining_implications =
1003+
tcx.stability_implications(rustc_hir::def_id::LOCAL_CRATE).clone();
1004+
1005+
// We always collect the lib features declared in the current crate, even if there are
1006+
// no unknown features, because the collection also does feature attribute validation.
1007+
let local_defined_features = tcx.lib_features(()).to_vec();
1008+
if !remaining_lib_features.is_empty() || !remaining_implications.is_empty() {
1009+
// Loading the implications of all crates is unavoidable to be able to emit the partial
1010+
// stabilization diagnostic, but it can be avoided when there are no
1011+
// `remaining_lib_features`.
1012+
let mut all_implications = remaining_implications.clone();
1013+
for &cnum in tcx.crates(()) {
1014+
all_implications.extend(tcx.stability_implications(cnum));
1015+
}
1016+
1017+
check_features(
1018+
tcx,
1019+
&mut remaining_lib_features,
1020+
&mut remaining_implications,
1021+
local_defined_features.as_slice(),
1022+
&all_implications,
1023+
);
1024+
1025+
for &cnum in tcx.crates(()) {
1026+
if remaining_lib_features.is_empty() && remaining_implications.is_empty() {
1027+
break;
1028+
}
1029+
check_features(
1030+
tcx,
1031+
&mut remaining_lib_features,
1032+
&mut remaining_implications,
1033+
tcx.defined_lib_features(cnum).to_vec().as_slice(),
1034+
&all_implications,
1035+
);
1036+
}
1037+
}
1038+
10001039
for (feature, span) in remaining_lib_features {
10011040
struct_span_err!(tcx.sess, span, E0635, "unknown feature `{}`", feature).emit();
10021041
}
10031042

1043+
for (implied_by, feature) in remaining_implications {
1044+
let local_defined_features = tcx.lib_features(());
1045+
let span = local_defined_features
1046+
.stable
1047+
.get(&feature)
1048+
.map(|(_, span)| span)
1049+
.or_else(|| local_defined_features.unstable.get(&feature))
1050+
.expect("feature that implied another does not exist");
1051+
tcx.sess
1052+
.struct_span_err(
1053+
*span,
1054+
format!("feature `{implied_by}` implying `{feature}` does not exist"),
1055+
)
1056+
.emit();
1057+
}
1058+
10041059
// FIXME(#44232): the `used_features` table no longer exists, so we
10051060
// don't lint about unused features. We should re-enable this one day!
10061061
}

0 commit comments

Comments
 (0)