Implement other logics

This commit is contained in:
tiif 2025-07-14 13:38:19 +00:00
parent dd067a689a
commit 7356ff7517
31 changed files with 132 additions and 9 deletions

View file

@ -70,6 +70,7 @@ impl AttributeKind {
TrackCaller(..) => Yes,
TypeConst(..) => Yes,
UnsafeSpecializationMarker(..) => No,
UnstableFeatureBound(..) => No,
Used { .. } => No,
// tidy-alphabetical-end
}

View file

@ -13,7 +13,9 @@ use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, HirI
use rustc_session::Session;
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
use crate::attributes::allow_unstable::{
AllowConstFnUnstableParser, AllowInternalUnstableParser, UnstableFeatureBoundParser,
};
use crate::attributes::codegen_attrs::{
ColdParser, ExportNameParser, NakedParser, NoMangleParser, OmitGdbPrettyPrinterSectionParser,
OptimizeParser, TargetFeatureParser, TrackCallerParser, UsedParser,
@ -133,6 +135,7 @@ attribute_parsers!(
Combine<AllowInternalUnstableParser>,
Combine<ReprParser>,
Combine<TargetFeatureParser>,
Combine<UnstableFeatureBoundParser>,
// tidy-alphabetical-end
// tidy-alphabetical-start

View file

@ -683,6 +683,10 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
template!(List: r#"feature = "name", reason = "...", issue = "N""#), DuplicatesOk,
EncodeCrossCrate::Yes
),
ungated!(
unstable_feature_bound, Normal, template!(Word, List: "feat1, feat2, ..."),
DuplicatesOk, EncodeCrossCrate::No,
),
ungated!(
rustc_const_unstable, Normal, template!(List: r#"feature = "name""#),
DuplicatesOk, EncodeCrossCrate::Yes

View file

@ -2212,12 +2212,16 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
let implied_obligations = traits::elaborate(tcx, predicates_with_span);
for (pred, obligation_span) in implied_obligations {
// We lower empty bounds like `Vec<dyn Copy>:` as
// `WellFormed(Vec<dyn Copy>)`, which will later get checked by
// regular WF checking
if let ty::ClauseKind::WellFormed(..) = pred.kind().skip_binder() {
continue;
match pred.kind().skip_binder() {
// We lower empty bounds like `Vec<dyn Copy>:` as
// `WellFormed(Vec<dyn Copy>)`, which will later get checked by
// regular WF checking
ty::ClauseKind::WellFormed(..)
// Unstable feature goals cannot be proven in an empty environment so skip them
| ty::ClauseKind::UnstableFeature(..) => continue,
_ => {}
}
// Match the existing behavior.
if pred.is_global() && !pred.has_type_flags(TypeFlags::HAS_BINDER_VARS) {
let pred = self.normalize(span, None, pred);

View file

@ -778,6 +778,7 @@ pub(super) fn assert_only_contains_predicates_from<'tcx>(
ty::ClauseKind::RegionOutlives(_)
| ty::ClauseKind::ConstArgHasType(_, _)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::UnstableFeature(_)
| ty::ClauseKind::ConstEvaluatable(_) => {
bug!(
"unexpected non-`Self` predicate when computing \
@ -805,6 +806,7 @@ pub(super) fn assert_only_contains_predicates_from<'tcx>(
| ty::ClauseKind::ConstArgHasType(_, _)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::ConstEvaluatable(_)
| ty::ClauseKind::UnstableFeature(_)
| ty::ClauseKind::HostEffect(..) => {
bug!(
"unexpected non-`Self` predicate when computing \

View file

@ -499,6 +499,7 @@ fn trait_specialization_kind<'tcx>(
| ty::ClauseKind::ConstArgHasType(..)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::ConstEvaluatable(..)
| ty::ClauseKind::UnstableFeature(_)
| ty::ClauseKind::HostEffect(..) => None,
}
}

View file

@ -54,6 +54,7 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> {
| ty::ClauseKind::ConstArgHasType(_, _)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::ConstEvaluatable(_)
| ty::ClauseKind::UnstableFeature(_)
| ty::ClauseKind::HostEffect(..) => {}
}
}

View file

@ -54,6 +54,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
| ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..))
| ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_))
| ty::PredicateKind::Ambiguous => false,
}
}

View file

@ -921,6 +921,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
| ty::ClauseKind::ConstArgHasType(_, _)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::ConstEvaluatable(_)
| ty::ClauseKind::UnstableFeature(_)
| ty::ClauseKind::HostEffect(..) => None,
}
});

View file

@ -1519,8 +1519,9 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints {
ClauseKind::TypeOutlives(..) |
ClauseKind::RegionOutlives(..) => "lifetime",
ClauseKind::UnstableFeature(_)
// `ConstArgHasType` is never global as `ct` is always a param
ClauseKind::ConstArgHasType(..)
| ClauseKind::ConstArgHasType(..)
// Ignore projections, as they can only be global
// if the trait bound is global
| ClauseKind::Projection(..)

View file

@ -137,6 +137,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
type FnInputTys = &'tcx [Ty<'tcx>];
type ParamTy = ParamTy;
type BoundTy = ty::BoundTy;
type Symbol = Symbol;
type PlaceholderTy = ty::PlaceholderType;
type ErrorGuaranteed = ErrorGuaranteed;

View file

@ -131,6 +131,7 @@ impl<'tcx> Predicate<'tcx> {
| PredicateKind::Clause(ClauseKind::TypeOutlives(_))
| PredicateKind::Clause(ClauseKind::Projection(_))
| PredicateKind::Clause(ClauseKind::ConstArgHasType(..))
| PredicateKind::Clause(ClauseKind::UnstableFeature(_))
| PredicateKind::DynCompatible(_)
| PredicateKind::Subtype(_)
| PredicateKind::Coerce(_)
@ -649,6 +650,7 @@ impl<'tcx> Predicate<'tcx> {
PredicateKind::Clause(ClauseKind::Projection(..))
| PredicateKind::Clause(ClauseKind::HostEffect(..))
| PredicateKind::Clause(ClauseKind::ConstArgHasType(..))
| PredicateKind::Clause(ClauseKind::UnstableFeature(_))
| PredicateKind::NormalizesTo(..)
| PredicateKind::AliasRelate(..)
| PredicateKind::Subtype(..)
@ -670,6 +672,7 @@ impl<'tcx> Predicate<'tcx> {
PredicateKind::Clause(ClauseKind::Trait(..))
| PredicateKind::Clause(ClauseKind::HostEffect(..))
| PredicateKind::Clause(ClauseKind::ConstArgHasType(..))
| PredicateKind::Clause(ClauseKind::UnstableFeature(_))
| PredicateKind::NormalizesTo(..)
| PredicateKind::AliasRelate(..)
| PredicateKind::Subtype(..)

View file

@ -3237,6 +3237,7 @@ define_print! {
ty::ClauseKind::ConstEvaluatable(ct) => {
p!("the constant `", print(ct), "` can be evaluated")
}
ty::ClauseKind::UnstableFeature(symbol) => p!("unstable feature: ", write("`{}`", symbol)),
}
}

View file

@ -156,6 +156,7 @@ where
}
ty::ClauseKind::ConstEvaluatable(ct) => ct.visit_with(self),
ty::ClauseKind::WellFormed(term) => term.visit_with(self),
ty::ClauseKind::UnstableFeature(_) => V::Result::output(),
}
}

View file

@ -764,6 +764,9 @@ impl<'tcx> Stable<'tcx> for ty::ClauseKind<'tcx> {
ClauseKind::HostEffect(..) => {
todo!()
}
ClauseKind::UnstableFeature(_) => {
todo!()
}
}
}
}

View file

@ -205,6 +205,46 @@ pub fn add_feature_diagnostics_for_issue<G: EmissionGuarantee>(
}
}
/// This is only used by unstable_feature_bound as it does not have issue number information for now.
/// This is basically the same as `feature_err_issue`
/// but without the feature issue note. If we can do a lookup for issue number from feature name,
/// then we should directly use `feature_err_issue` for ambiguity error of
/// #[unstable_feature_bound].
#[track_caller]
pub fn feature_err_unstable_feature_bound(
sess: &Session,
feature: Symbol,
span: impl Into<MultiSpan>,
explain: impl Into<DiagMessage>,
) -> Diag<'_> {
let span = span.into();
// Cancel an earlier warning for this same error, if it exists.
if let Some(span) = span.primary_span() {
if let Some(err) = sess.dcx().steal_non_err(span, StashKey::EarlySyntaxWarning) {
err.cancel()
}
}
let mut err = sess.dcx().create_err(FeatureGateError { span, explain: explain.into() });
// #23973: do not suggest `#![feature(...)]` if we are in beta/stable
if sess.psess.unstable_features.is_nightly_build() {
err.subdiagnostic(FeatureDiagnosticHelp { feature });
if feature == sym::rustc_attrs {
// We're unlikely to stabilize something out of `rustc_attrs`
// without at least renaming it, so pointing out how old
// the compiler is will do little good.
} else if sess.opts.unstable_opts.ui_testing {
err.subdiagnostic(SuggestUpgradeCompiler::ui_testing());
} else if let Some(suggestion) = SuggestUpgradeCompiler::new() {
err.subdiagnostic(suggestion);
}
}
err
}
/// Info about a parsing session.
pub struct ParseSess {
dcx: DiagCtxt,

View file

@ -2284,6 +2284,7 @@ symbols! {
unsized_locals,
unsized_tuple_coercion,
unstable,
unstable_feature_bound,
unstable_location_reason_default: "this crate is being loaded from the sysroot, an \
unstable location; did you mean to load this crate \
from crates.io via `Cargo.toml` instead?",

View file

@ -12,6 +12,7 @@ use rustc_infer::traits::{
Obligation, ObligationCause, ObligationCauseCode, PolyTraitObligation, PredicateObligation,
};
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable as _, TypeVisitableExt as _};
use rustc_session::parse::feature_err_unstable_feature_bound;
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
use tracing::{debug, instrument};
@ -611,6 +612,30 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
)
.with_span_label(span, format!("cannot normalize `{alias}`"))
}
ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(sym)) => {
if let Some(e) = self.tainted_by_errors() {
return e;
}
let mut err;
if self.tcx.features().staged_api() {
err = self.dcx().struct_span_err(
span,
format!("unstable feature `{sym}` is used without being enabled."),
);
err.help(format!("The feature can be enabled by marking the current item with `#[unstable_feature_bound({sym})]`"));
} else {
err = feature_err_unstable_feature_bound(
&self.tcx.sess,
sym,
span,
format!("use of unstable library feature `{sym}`"),
);
}
err
}
_ => {
if let Some(e) = self.tainted_by_errors() {

View file

@ -647,6 +647,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
| ty::PredicateKind::ConstEquate { .. }
// Ambiguous predicates should never error
| ty::PredicateKind::Ambiguous
// We never return Err when proving UnstableFeature goal.
| ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature{ .. })
| ty::PredicateKind::NormalizesTo { .. }
| ty::PredicateKind::AliasRelate { .. }
| ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType { .. }) => {

View file

@ -800,6 +800,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
// FIXME(generic_const_exprs): you can absolutely add this as a where clauses
| ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_))
| ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) => {}
ty::PredicateKind::Ambiguous => return false,
};

View file

@ -238,6 +238,7 @@ fn predicate_references_self<'tcx>(
// FIXME(generic_const_exprs): this can mention `Self`
| ty::ClauseKind::ConstEvaluatable(..)
| ty::ClauseKind::HostEffect(..)
| ty::ClauseKind::UnstableFeature(_)
=> None,
}
}
@ -278,6 +279,7 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
| ty::ClauseKind::ConstArgHasType(_, _)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::ConstEvaluatable(_)
| ty::ClauseKind::UnstableFeature(_)
| ty::ClauseKind::HostEffect(..) => false,
})
}

View file

@ -406,6 +406,9 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
ty::PredicateKind::AliasRelate(..) => {
bug!("AliasRelate is only used by the new solver")
}
ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_)) => {
unreachable!("unexpected higher ranked `UnstableFeature` goal")
}
},
Some(pred) => match pred {
ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => {

View file

@ -110,6 +110,7 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>(
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::Ambiguous
| ty::PredicateKind::NormalizesTo(..)
| ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_))
| ty::PredicateKind::AliasRelate(..) => {}
// We need to search through *all* WellFormed predicates

View file

@ -80,6 +80,7 @@ pub fn expand_trait_aliases<'tcx>(
| ty::ClauseKind::ConstArgHasType(_, _)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::ConstEvaluatable(_)
| ty::ClauseKind::UnstableFeature(_)
| ty::ClauseKind::HostEffect(..) => {}
}
}

View file

@ -197,6 +197,7 @@ pub fn clause_obligations<'tcx>(
ty::ClauseKind::ConstEvaluatable(ct) => {
wf.add_wf_preds_for_term(ct.into());
}
ty::ClauseKind::UnstableFeature(_) => {}
}
wf.normalize(infcx)
@ -1095,6 +1096,7 @@ pub fn object_region_bounds<'tcx>(
| ty::ClauseKind::Projection(_)
| ty::ClauseKind::ConstArgHasType(_, _)
| ty::ClauseKind::WellFormed(_)
| ty::ClauseKind::UnstableFeature(_)
| ty::ClauseKind::ConstEvaluatable(_) => None,
}
})

View file

@ -57,6 +57,7 @@ fn not_outlives_predicate(p: ty::Predicate<'_>) -> bool {
| ty::PredicateKind::Clause(ty::ClauseKind::Projection(..))
| ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..))
| ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
| ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_))
| ty::PredicateKind::NormalizesTo(..)
| ty::PredicateKind::AliasRelate(..)
| ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(..))

View file

@ -234,6 +234,9 @@ impl<I: Interner, O: Elaboratable<I>> Elaborator<I, O> {
ty::ClauseKind::ConstArgHasType(..) => {
// Nothing to elaborate
}
ty::ClauseKind::UnstableFeature(_) => {
// Nothing to elaborate
}
}
}
}

View file

@ -401,7 +401,6 @@ impl<I: Interner> FlagComputation<I> {
self.add_const(expected);
self.add_const(found);
}
ty::PredicateKind::Ambiguous => {}
ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term }) => {
self.add_alias_term(alias);
self.add_term(term);
@ -410,6 +409,8 @@ impl<I: Interner> FlagComputation<I> {
self.add_term(t1);
self.add_term(t2);
}
ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_sym)) => {}
ty::PredicateKind::Ambiguous => {}
}
}

View file

@ -106,6 +106,7 @@ pub trait Interner:
type ParamTy: ParamLike;
type BoundTy: BoundVarLike<Self>;
type PlaceholderTy: PlaceholderLike<Self, Bound = Self::BoundTy>;
type Symbol: Copy + Hash + PartialEq + Eq + Debug;
// Things stored inside of tys
type ErrorGuaranteed: Copy + Debug + Hash + Eq;

View file

@ -46,6 +46,13 @@ pub enum ClauseKind<I: Interner> {
/// corresponding trait clause; this just enforces the *constness* of that
/// implementation.
HostEffect(ty::HostEffectPredicate<I>),
/// Support marking impl as unstable.
UnstableFeature(
#[type_foldable(identity)]
#[type_visitable(ignore)]
I::Symbol,
),
}
#[derive_where(Clone, Copy, Hash, PartialEq, Eq; I: Interner)]
@ -134,6 +141,9 @@ impl<I: Interner> fmt::Debug for ClauseKind<I> {
ClauseKind::ConstEvaluatable(ct) => {
write!(f, "ConstEvaluatable({ct:?})")
}
ClauseKind::UnstableFeature(feature_name) => {
write!(f, "UnstableFeature({feature_name:?})")
}
}
}
}

View file

@ -390,7 +390,8 @@ pub(crate) fn clean_predicate<'tcx>(
ty::ClauseKind::ConstEvaluatable(..)
| ty::ClauseKind::WellFormed(..)
| ty::ClauseKind::ConstArgHasType(..)
// FIXME(const_trait_impl): We can probably use this `HostEffect` pred to render `[const]`.
| ty::ClauseKind::UnstableFeature(..)
// FIXME(const_trait_impl): We can probably use this `HostEffect` pred to render `~const`.
| ty::ClauseKind::HostEffect(_) => None,
}
}