Skip to content
10 changes: 10 additions & 0 deletions compiler/rustc_ast_passes/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -950,6 +950,16 @@ pub(crate) struct IncompatibleFeatures {
pub f2: Symbol,
}

#[derive(Diagnostic)]
#[diag("`{$parent}` requires {$missing} to be enabled")]
#[help("enable all of these features")]
pub(crate) struct MissingDependentFeatures {
#[primary_span]
pub parent_span: Span,
pub parent: Symbol,
pub missing: String,
}

#[derive(Diagnostic)]
#[diag("negative bounds are not supported")]
pub(crate) struct NegativeBoundUnsupported {
Expand Down
22 changes: 22 additions & 0 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
maybe_stage_features(sess, features, krate);
check_incompatible_features(sess, features);
check_dependent_features(sess, features);
check_new_solver_banned_features(sess, features);

let mut visitor = PostExpansionVisitor { sess, features };
Expand Down Expand Up @@ -649,6 +650,27 @@ fn check_incompatible_features(sess: &Session, features: &Features) {
}
}

fn check_dependent_features(sess: &Session, features: &Features) {
for &(parent, children) in
rustc_feature::DEPENDENT_FEATURES.iter().filter(|(parent, _)| features.enabled(*parent))
{
if children.iter().any(|f| !features.enabled(*f)) {
let parent_span = features
.enabled_features_iter_stable_order()
.find_map(|(name, span)| (name == parent).then_some(span))
.unwrap();
// FIXME: should probably format this in fluent instead of here
let missing = children
.iter()
.filter(|f| !features.enabled(**f))
.map(|s| format!("`{}`", s.as_str()))
.intersperse(String::from(", "))
.collect();
sess.dcx().emit_err(errors::MissingDependentFeatures { parent_span, parent, missing });
}
}
}

fn check_new_solver_banned_features(sess: &Session, features: &Features) {
if !sess.opts.unstable_opts.next_solver.globally {
return;
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast_passes/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// tidy-alphabetical-start
#![feature(box_patterns)]
#![feature(if_let_guard)]
#![feature(iter_intersperse)]
#![feature(iter_is_partitioned)]
// tidy-alphabetical-end

Expand Down
28 changes: 28 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,19 @@ impl<S: Stage> CombineAttributeParser<S> for RustcThenThisWouldNeedParser {
}
}

pub(crate) struct RustcInsignificantDtorParser;

impl<S: Stage> NoArgsAttributeParser<S> for RustcInsignificantDtorParser {
const PATH: &[Symbol] = &[sym::rustc_insignificant_dtor];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Enum),
Allow(Target::Struct),
Allow(Target::ForeignTy),
]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcInsignificantDtor;
}

pub(crate) struct RustcEffectiveVisibilityParser;

impl<S: Stage> NoArgsAttributeParser<S> for RustcEffectiveVisibilityParser {
Expand Down Expand Up @@ -845,3 +858,18 @@ impl<S: Stage> NoArgsAttributeParser<S> for RustcIntrinsicConstStableIndirectPar
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcIntrinsicConstStableIndirect;
}

pub(crate) struct RustcStrictCoherenceParser;

impl<S: Stage> NoArgsAttributeParser<S> for RustcStrictCoherenceParser {
const PATH: &[Symbol] = &[sym::rustc_strict_coherence];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Trait),
Allow(Target::Struct),
Allow(Target::Enum),
Allow(Target::Union),
Allow(Target::ForeignTy),
]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcStrictCoherence;
}
2 changes: 2 additions & 0 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ attribute_parsers!(
Single<WithoutArgs<RustcEvaluateWhereClausesParser>>,
Single<WithoutArgs<RustcHasIncoherentInherentImplsParser>>,
Single<WithoutArgs<RustcHiddenTypeOfOpaquesParser>>,
Single<WithoutArgs<RustcInsignificantDtorParser>>,
Single<WithoutArgs<RustcIntrinsicConstStableIndirectParser>>,
Single<WithoutArgs<RustcIntrinsicParser>>,
Single<WithoutArgs<RustcLintOptTyParser>>,
Expand All @@ -289,6 +290,7 @@ attribute_parsers!(
Single<WithoutArgs<RustcReallocatorParser>>,
Single<WithoutArgs<RustcRegionsParser>>,
Single<WithoutArgs<RustcShouldNotBeCalledOnConstItems>>,
Single<WithoutArgs<RustcStrictCoherenceParser>>,
Single<WithoutArgs<RustcVarianceOfOpaquesParser>>,
Single<WithoutArgs<RustcVarianceParser>>,
Single<WithoutArgs<SpecializationTraitParser>>,
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_feature/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,5 +136,6 @@ pub use builtin_attrs::{
};
pub use removed::REMOVED_LANG_FEATURES;
pub use unstable::{
EnabledLangFeature, EnabledLibFeature, Features, INCOMPATIBLE_FEATURES, UNSTABLE_LANG_FEATURES,
DEPENDENT_FEATURES, EnabledLangFeature, EnabledLibFeature, Features, INCOMPATIBLE_FEATURES,
UNSTABLE_LANG_FEATURES,
};
8 changes: 8 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,8 @@ declare_features! (
(unstable, offset_of_enum, "1.75.0", Some(120141)),
/// Allows using fields with slice type in offset_of!
(unstable, offset_of_slice, "1.81.0", Some(126151)),
/// Allows using generics in more complex const expressions, based on definitional equality.
(unstable, opaque_generic_const_args, "CURRENT_RUSTC_VERSION", Some(151972)),
/// Allows using `#[optimize(X)]`.
(unstable, optimize_attribute, "1.34.0", Some(54882)),
/// Allows specifying nop padding on functions for dynamic patching.
Expand Down Expand Up @@ -782,3 +784,9 @@ pub const INCOMPATIBLE_FEATURES: &[(Symbol, Symbol)] = &[
// boolean logic required to tell which typing rules to use.
(sym::ref_pat_eat_one_layer_2024, sym::ref_pat_eat_one_layer_2024_structural),
];

/// Some features require one or more other features to be enabled.
pub const DEPENDENT_FEATURES: &[(Symbol, &[Symbol])] = &[
(sym::opaque_generic_const_args, &[sym::min_generic_const_args]),
(sym::unsized_const_params, &[sym::adt_const_params]),
];
7 changes: 7 additions & 0 deletions compiler/rustc_hir/src/attrs/data_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1126,6 +1126,9 @@ pub enum AttributeKind {
/// Represents `#[rustc_if_this_changed]`
RustcIfThisChanged(Span, Option<Symbol>),

/// Represents `#[rustc_insignificant_dtor]`
RustcInsignificantDtor,

/// Represents `#[rustc_intrinsic]`
RustcIntrinsic,

Expand Down Expand Up @@ -1239,6 +1242,9 @@ pub enum AttributeKind {
/// Represents `#[rustc_std_internal_symbol]`.
RustcStdInternalSymbol(Span),

/// Represents `#[rustc_strict_coherence]`.
RustcStrictCoherence(Span),

/// Represents `#[rustc_symbol_name]`
RustcSymbolName(Span),

Expand Down Expand Up @@ -1275,6 +1281,7 @@ pub enum AttributeKind {
/// Span of the attribute.
span: Span,
},

/// Represents `#[target_feature(enable = "...")]` and
/// `#[unsafe(force_target_feature(enable = "...")]`.
TargetFeature { features: ThinVec<(Symbol, Span)>, attr_span: Span, was_forced: bool },
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_hir/src/attrs/encode_cross_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ impl AttributeKind {
RustcHasIncoherentInherentImpls => Yes,
RustcHiddenTypeOfOpaques => No,
RustcIfThisChanged(..) => No,
RustcInsignificantDtor => Yes,
RustcIntrinsic => Yes,
RustcIntrinsicConstStableIndirect => No,
RustcLayout(..) => No,
Expand Down Expand Up @@ -156,6 +157,7 @@ impl AttributeKind {
RustcSkipDuringMethodDispatch { .. } => No,
RustcSpecializationTrait(..) => No,
RustcStdInternalSymbol(..) => No,
RustcStrictCoherence(..) => Yes,
RustcSymbolName(..) => Yes,
RustcThenThisWouldNeed(..) => No,
RustcUnsafeSpecializationMarker(..) => No,
Expand Down
64 changes: 61 additions & 3 deletions compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

use std::cell::Cell;
use std::iter;
use std::ops::Bound;
use std::ops::{Bound, ControlFlow};

use rustc_abi::{ExternAbi, Size};
use rustc_ast::Recovered;
Expand All @@ -26,12 +26,13 @@ use rustc_errors::{
Applicability, Diag, DiagCtxtHandle, E0228, ErrorGuaranteed, StashKey, struct_span_code_err,
};
use rustc_hir::attrs::AttributeKind;
use rustc_hir::def::DefKind;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt};
use rustc_hir::intravisit::{self, InferKind, Visitor, VisitorExt};
use rustc_hir::{self as hir, GenericParamKind, HirId, Node, PreciseCapturingArgKind, find_attr};
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
use rustc_infer::traits::{DynCompatibilityViolation, ObligationCause};
use rustc_middle::hir::nested_filter;
use rustc_middle::query::Providers;
use rustc_middle::ty::util::{Discr, IntTypeExt};
use rustc_middle::ty::{
Expand Down Expand Up @@ -1511,6 +1512,20 @@ fn anon_const_kind<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> ty::AnonConstKin
let parent_hir_node = tcx.hir_node(tcx.parent_hir_id(const_arg_id));
if tcx.features().generic_const_exprs() {
ty::AnonConstKind::GCE
} else if tcx.features().opaque_generic_const_args() {
// Only anon consts that are the RHS of a const item can be OGCA.
// Note: We can't just check tcx.parent because it needs to be EXACTLY
// the RHS, not just part of the RHS.
if !is_anon_const_rhs_of_const_item(tcx, def) {
return ty::AnonConstKind::MCG;
}

let body = tcx.hir_body_owned_by(def);
let mut visitor = OGCAParamVisitor(tcx);
match visitor.visit_body(body) {
ControlFlow::Break(UsesParam) => ty::AnonConstKind::OGCA,
ControlFlow::Continue(()) => ty::AnonConstKind::MCG,
}
} else if tcx.features().min_generic_const_args() {
ty::AnonConstKind::MCG
} else if let hir::Node::Expr(hir::Expr {
Expand All @@ -1528,6 +1543,49 @@ fn anon_const_kind<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> ty::AnonConstKin
}
}

fn is_anon_const_rhs_of_const_item<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> bool {
let hir_id = tcx.local_def_id_to_hir_id(def_id);
let Some((_, grandparent_node)) = tcx.hir_parent_iter(hir_id).nth(1) else { return false };
let (Node::Item(hir::Item { kind: hir::ItemKind::Const(_, _, _, ct_rhs), .. })
| Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(_, ct_rhs), .. })
| Node::TraitItem(hir::TraitItem {
kind: hir::TraitItemKind::Const(_, Some(ct_rhs)), ..
})) = grandparent_node
else {
return false;
};
let hir::ConstItemRhs::TypeConst(hir::ConstArg {
kind: hir::ConstArgKind::Anon(rhs_anon), ..
}) = ct_rhs
else {
return false;
};
def_id == rhs_anon.def_id
}

struct OGCAParamVisitor<'tcx>(TyCtxt<'tcx>);

struct UsesParam;

impl<'tcx> Visitor<'tcx> for OGCAParamVisitor<'tcx> {
type NestedFilter = nested_filter::OnlyBodies;
type Result = ControlFlow<UsesParam>;

fn maybe_tcx(&mut self) -> TyCtxt<'tcx> {
self.0
}

fn visit_path(&mut self, path: &hir::Path<'tcx>, _id: HirId) -> ControlFlow<UsesParam> {
if let Res::Def(DefKind::TyParam | DefKind::ConstParam | DefKind::LifetimeParam, _) =
path.res
{
return ControlFlow::Break(UsesParam);
}

intravisit::walk_path(self, path)
}
}

#[instrument(level = "debug", skip(tcx), ret)]
fn const_of_item<'tcx>(
tcx: TyCtxt<'tcx>,
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_hir_analysis/src/collect/generics_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
match tcx.anon_const_kind(def_id) {
// Stable: anon consts are not able to use any generic parameters...
ty::AnonConstKind::MCG => None,
// OGCA anon consts inherit their parent's generics.
ty::AnonConstKind::OGCA => Some(parent_did),
// we provide generics to repeat expr counts as a backwards compatibility hack. #76200
ty::AnonConstKind::RepeatExprCount => Some(parent_did),

Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,11 @@ impl<'tcx> ForbidMCGParamUsesFolder<'tcx> {
diag.span_note(impl_.self_ty.span, "not a concrete type");
}
}
if self.tcx.features().min_generic_const_args()
&& !self.tcx.features().opaque_generic_const_args()
{
diag.help("add `#![feature(opaque_generic_const_args)]` to allow generic expressions as the RHS of const items");
}
diag.emit()
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ pub(crate) struct StrictCoherenceNeedsNegativeCoherence {
#[primary_span]
pub span: Span,
#[label("due to this attribute")]
pub attr_span: Option<Span>,
pub attr_span: Span,
}

#[derive(Diagnostic)]
Expand Down
19 changes: 6 additions & 13 deletions compiler/rustc_middle/src/traits/specialization_graph.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::ErrorGuaranteed;
use rustc_hir::attrs::AttributeKind;
use rustc_hir::def_id::{DefId, DefIdMap};
use rustc_hir::find_attr;
use rustc_macros::{HashStable, TyDecodable, TyEncodable};
use rustc_span::sym;

use crate::error::StrictCoherenceNeedsNegativeCoherence;
use crate::ty::fast_reject::SimplifiedType;
Expand Down Expand Up @@ -61,23 +62,15 @@ pub enum OverlapMode {
impl OverlapMode {
pub fn get(tcx: TyCtxt<'_>, trait_id: DefId) -> OverlapMode {
let with_negative_coherence = tcx.features().with_negative_coherence();
let strict_coherence = tcx.has_attr(trait_id, sym::rustc_strict_coherence);
let strict_coherence = find_attr!(tcx.get_all_attrs(trait_id), AttributeKind::RustcStrictCoherence(span) => *span);

if with_negative_coherence {
if strict_coherence { OverlapMode::Strict } else { OverlapMode::WithNegative }
if strict_coherence.is_some() { OverlapMode::Strict } else { OverlapMode::WithNegative }
} else {
if strict_coherence {
let attr_span = trait_id
.as_local()
.into_iter()
.flat_map(|local_def_id| {
tcx.hir_attrs(tcx.local_def_id_to_hir_id(local_def_id))
})
.find(|attr| attr.has_name(sym::rustc_strict_coherence))
.map(|attr| attr.span());
if let Some(span) = strict_coherence {
tcx.dcx().emit_err(StrictCoherenceNeedsNegativeCoherence {
span: tcx.def_span(trait_id),
attr_span,
attr_span: span,
});
}
OverlapMode::Stable
Expand Down
15 changes: 1 addition & 14 deletions compiler/rustc_middle/src/ty/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::borrow::Cow;

use rustc_data_structures::intern::Interned;
use rustc_error_messages::MultiSpan;
use rustc_macros::{HashStable, TyDecodable, TyEncodable};
use rustc_macros::HashStable;
use rustc_type_ir::walk::TypeWalker;
use rustc_type_ir::{self as ir, TypeFlags, WithCachedTypeInfo};

Expand Down Expand Up @@ -335,16 +335,3 @@ impl<'tcx> Const<'tcx> {
TypeWalker::new(self.into())
}
}

#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable, HashStable)]
pub enum AnonConstKind {
/// `feature(generic_const_exprs)` anon consts are allowed to use arbitrary generic parameters in scope
GCE,
/// stable `min_const_generics` anon consts are not allowed to use any generic parameters
MCG,
/// anon consts used as the length of a repeat expr are syntactically allowed to use generic parameters
/// but must not depend on the actual instantiation. See #76200 for more information
RepeatExprCount,
/// anon consts outside of the type system, e.g. enum discriminants
NonTypeSystem,
}
3 changes: 3 additions & 0 deletions compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,9 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
fn const_of_item(self, def_id: DefId) -> ty::EarlyBinder<'tcx, Const<'tcx>> {
self.const_of_item(def_id)
}
fn anon_const_kind(self, def_id: DefId) -> ty::AnonConstKind {
self.anon_const_kind(def_id)
}

type AdtDef = ty::AdtDef<'tcx>;
fn adt_def(self, adt_def_id: DefId) -> Self::AdtDef {
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ pub use self::closure::{
place_to_string_for_capture,
};
pub use self::consts::{
AnonConstKind, AtomicOrdering, Const, ConstInt, ConstKind, ConstToValTreeResult, Expr,
ExprKind, ScalarInt, SimdAlign, UnevaluatedConst, ValTree, ValTreeKindExt, Value,
AtomicOrdering, Const, ConstInt, ConstKind, ConstToValTreeResult, Expr, ExprKind, ScalarInt,
SimdAlign, UnevaluatedConst, ValTree, ValTreeKindExt, Value,
};
pub use self::context::{
CtxtInterners, CurrentGcx, Feed, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt, TyCtxtFeed, tls,
Expand Down
Loading
Loading