Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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]),
];
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
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
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use rustc_type_ir::{self as ty, Interner};
use rustc_type_ir::{self as ty, Interner, TypingMode};
use tracing::instrument;

use crate::delegate::SolverDelegate;
Expand All @@ -14,7 +14,18 @@ where
&mut self,
goal: Goal<I, ty::NormalizesTo<I>>,
) -> QueryResult<I> {
if let Some(normalized_const) = self.evaluate_const(
if self.typing_mode() == TypingMode::Coherence
&& self.cx().anon_const_kind(goal.predicate.alias.def_id) == ty::AnonConstKind::OGCA
{
// During coherence, OGCA consts should be normalized ambiguously
// because they are opaque but eventually resolved to a real value.
// We don't want two OGCAs that have the same value to be treated
// as distinct for coherence purposes. (Just like opaque types.)
//
// We can't rely on evaluate_const below because that particular wrapper
// treats too-generic consts as a successful evaluation.
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
} else if let Some(normalized_const) = self.evaluate_const(
goal.param_env,
ty::UnevaluatedConst::new(
goal.predicate.alias.def_id.try_into().unwrap(),
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_parse/src/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1551,7 +1551,9 @@ impl<'a> Parser<'a> {

let rhs = if self.eat(exp!(Eq)) {
if attr::contains_name(attrs, sym::type_const) {
Some(ConstItemRhs::TypeConst(self.parse_const_arg()?))
let ct =
self.parse_expr_anon_const(|this, expr| this.mgca_direct_lit_hack(expr))?;
Some(ConstItemRhs::TypeConst(ct))
} else {
Some(ConstItemRhs::Body(self.parse_expr()?))
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1628,6 +1628,7 @@ symbols! {
on_const,
on_unimplemented,
opaque,
opaque_generic_const_args,
opaque_module_name_placeholder: "<opaque>",
open_options_new,
ops,
Expand Down
14 changes: 14 additions & 0 deletions compiler/rustc_trait_selection/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,20 @@ pub fn try_evaluate_const<'tcx>(

(args, typing_env)
}
Some(ty::AnonConstKind::OGCA) => {
if infcx.typing_mode() != TypingMode::PostAnalysis {
// OGCA anon consts should be treated as always having generics
// during anything before codegen (or maybe MIR opts too).
return Err(EvaluateConstErr::HasGenericsOrInfers);
}

if uv.args.has_non_region_param() || uv.args.has_non_region_infer() {
return Err(EvaluateConstErr::HasGenericsOrInfers);
}

let typing_env = ty::TypingEnv::fully_monomorphized();
(uv.args, typing_env)
}
Some(ty::AnonConstKind::MCG) | Some(ty::AnonConstKind::NonTypeSystem) | None => {
// We are only dealing with "truly" generic/uninferred constants here:
// - GCEConsts have been handled separately
Expand Down
20 changes: 20 additions & 0 deletions compiler/rustc_type_ir/src/const_kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,3 +200,23 @@ impl<I: Interner> ValTreeKind<I> {
}
}
}

#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(
feature = "nightly",
derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext)
)]
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,
/// `feature(opaque_generic_const_args)` anon consts are allowed to use arbitrary
/// generic parameters in scope, but only if they syntactically reference them.
OGCA,
/// 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,
}
1 change: 1 addition & 0 deletions compiler/rustc_type_ir/src/interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ pub trait Interner:
fn type_of_opaque_hir_typeck(self, def_id: Self::LocalDefId)
-> ty::EarlyBinder<Self, Self::Ty>;
fn const_of_item(self, def_id: Self::DefId) -> ty::EarlyBinder<Self, Self::Const>;
fn anon_const_kind(self, def_id: Self::DefId) -> ty::AnonConstKind;

type AdtDef: AdtDef<Self>;
fn adt_def(self, adt_def_id: Self::AdtId) -> Self::AdtDef;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![feature(generic_const_exprs, unsized_const_params)]
#![feature(adt_const_params, generic_const_exprs, unsized_const_params)]
#![allow(incomplete_features)]

// Regression test for 128232
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,11 @@ help: you might be missing a const parameter
LL | impl<const bar: /* Type */> Wrapper<{ bar() }> {
| +++++++++++++++++++++++

error: using function pointers as const generic parameters is forbidden
error[E0741]: using function pointers as const generic parameters is forbidden
--> $DIR/non_valtreeable_const_arg-2.rs:8:25
|
LL | struct Wrapper<const F: fn()>;
| ^^^^
|
= note: the only supported types are integers, `bool`, and `char`

error[E0599]: the function or associated item `call` exists for struct `Wrapper<function>`, but its trait bounds were not satisfied
--> $DIR/non_valtreeable_const_arg-2.rs:17:26
Expand All @@ -37,5 +35,5 @@ note: the trait `Fn` must be implemented

error: aborting due to 3 previous errors

Some errors have detailed explanations: E0425, E0599.
Some errors have detailed explanations: E0425, E0599, E0741.
For more information about an error, try `rustc --explain E0425`.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// We used to say "ambiguous associated type" on ambiguous associated consts.
// Ensure that we now use the correct label.

#![feature(min_generic_const_args, unsized_const_params)]
#![feature(adt_const_params, min_generic_const_args, unsized_const_params)]
#![allow(incomplete_features)]

trait Trait0: Parent0<i32> + Parent0<u32> {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

//@ check-pass

#![feature(min_generic_const_args, unsized_const_params)]
#![feature(adt_const_params, min_generic_const_args, unsized_const_params)]
#![allow(incomplete_features)]

trait Trait: SuperTrait {
Expand Down
Loading
Loading