@@ -10,6 +10,7 @@ use rustc_attr_parsing::{
1010use rustc_data_structures::unord::UnordMap;
1111use rustc_errors::{Applicability, Diag, EmissionGuarantee};
1212use rustc_feature::GateIssue;
13+ use rustc_hir::def::DefKind;
1314use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdMap};
1415use rustc_hir::{self as hir, HirId};
1516use rustc_macros::{Decodable, Encodable, HashStable, Subdiagnostic};
@@ -18,7 +19,7 @@ use rustc_session::Session;
1819use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE};
1920use rustc_session::lint::{BuiltinLintDiag, DeprecatedSinceKind, Level, Lint, LintBuffer};
2021use rustc_session::parse::feature_err_issue;
21- use rustc_span::{Span, Symbol, sym};
22+ use rustc_span::{ErrorGuaranteed, Span, Symbol, sym};
2223use tracing::debug;
2324
2425pub use self::StabilityLevel::*;
@@ -597,4 +598,93 @@ impl<'tcx> TyCtxt<'tcx> {
597598 pub fn lookup_deprecation(self, id: DefId) -> Option<Deprecation> {
598599 self.lookup_deprecation_entry(id).map(|depr| depr.attr)
599600 }
601+
602+ /// Returns true if `def_id` has an attribute that allows usage of the const unstable feature `feature_gate`.
603+ pub fn rustc_allow_const_fn_unstable(self, def_id: LocalDefId, feature_gate: Symbol) -> bool {
604+ let attrs = self.hir().attrs(self.local_def_id_to_hir_id(def_id));
605+ attr::rustc_allow_const_fn_unstable(self.sess, attrs).any(|name| name == feature_gate)
606+ }
607+
608+ pub fn enforce_trait_const_stability(
609+ self,
610+ trait_def_id: DefId,
611+ span: Span,
612+ parent_def: Option<LocalDefId>,
613+ ) {
614+ // This should work pretty much exactly like the function stability logic in
615+ // `compiler/rustc_const_eval/src/check_consts/check.rs`.
616+ // FIXME: Find some way to not duplicate that logic.
617+ let Some(ConstStability {
618+ level: attr::StabilityLevel::Unstable { implied_by: implied_feature, .. },
619+ feature,
620+ ..
621+ }) = self.lookup_const_stability(trait_def_id)
622+ else {
623+ return;
624+ };
625+
626+ let unstable_feature_allowed = span.allows_unstable(feature)
627+ || implied_feature.is_some_and(|f| span.allows_unstable(f));
628+
629+ let feature_enabled = trait_def_id.is_local()
630+ || self.features().enabled(feature)
631+ || implied_feature.is_some_and(|f| self.features().enabled(f));
632+
633+ if !unstable_feature_allowed && !feature_enabled {
634+ let mut diag = self.dcx().create_err(crate::error::UnstableConstTrait {
635+ span,
636+ def_path: self.def_path_str(trait_def_id),
637+ });
638+ self.disabled_nightly_features(&mut diag, None, [(String::new(), feature)]);
639+ diag.emit();
640+ } else if let Some(parent) = parent_def {
641+ // user either has enabled the feature or the unstable feature is allowed inside a macro,
642+ // but if we consider the item we're in to be const stable, we should error as const stable
643+ // items cannot use unstable features.
644+ let is_stable =
645+ matches!(self.def_kind(parent), DefKind::AssocFn | DefKind::Fn | DefKind::Trait)
646+ && match self.lookup_const_stability(parent) {
647+ None => {
648+ // `const fn`s without const stability attributes in a `staged_api` crate
649+ // are implicitly stable.
650+ self.features().staged_api()
651+ }
652+ Some(stab) => {
653+ // an explicitly stable `const fn`, or an unstable `const fn` that claims to not use any
654+ // other unstably-const features with `const_stable_indirect`
655+ stab.is_const_stable() || stab.const_stable_indirect
656+ }
657+ };
658+
659+ // if our parent function is unstable, no need to error
660+ if !is_stable {
661+ return;
662+ }
663+
664+ // if the feature is explicitly allowed, don't error
665+ if self.rustc_allow_const_fn_unstable(parent, feature) {
666+ return;
667+ }
668+
669+ emit_const_unstable_in_const_stable_exposed_error(self, parent, span, feature, false);
670+ }
671+ }
672+ }
673+
674+ pub fn emit_const_unstable_in_const_stable_exposed_error(
675+ tcx: TyCtxt<'_>,
676+ def_id: LocalDefId,
677+ span: Span,
678+ gate: Symbol,
679+ is_function_call: bool,
680+ ) -> ErrorGuaranteed {
681+ let attr_span = tcx.def_span(def_id).shrink_to_lo();
682+
683+ tcx.dcx().emit_err(crate::error::ConstUnstableInConstStableExposed {
684+ gate: gate.to_string(),
685+ span,
686+ attr_span,
687+ is_function_call,
688+ is_function_call2: is_function_call,
689+ })
600690}
0 commit comments