Skip to content

Commit a9fb544

Browse files
committed
Implement MVP for opaque generic const arguments
This is meant to be the interim successor to generic const expressions. Essentially, const item RHS's will be allowed to do arbitrary const operations using generics. The limitation is that these const items will be treated opaquely, like ADTs in nominal typing, such that uses of them will only be equal if the same const item is referenced. In other words, two const items with the exact same RHS will not be considered equal. I also added some logic to check feature gates that depend on others being enabled (like oGCA depending on mGCA).
1 parent 32fe406 commit a9fb544

19 files changed

Lines changed: 169 additions & 22 deletions

File tree

compiler/rustc_ast_passes/messages.ftl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,9 @@ ast_passes_impl_fn_const =
218218
ast_passes_incompatible_features = `{$f1}` and `{$f2}` are incompatible, using them at the same time is not allowed
219219
.help = remove one of these features
220220
221+
ast_passes_missing_dependent_features = `{$parent}` requires {$missing} to be enabled
222+
.help = enable all of these features
223+
221224
ast_passes_item_invalid_safety = items outside of `unsafe extern {"{ }"}` cannot be declared with `safe` safety qualifier
222225
.suggestion = remove safe from this item
223226

compiler/rustc_ast_passes/src/errors.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,16 @@ pub(crate) struct IncompatibleFeatures {
834834
pub f2: Symbol,
835835
}
836836

837+
#[derive(Diagnostic)]
838+
#[diag(ast_passes_missing_dependent_features)]
839+
#[help]
840+
pub(crate) struct MissingDependentFeatures {
841+
#[primary_span]
842+
pub parent_span: Span,
843+
pub parent: Symbol,
844+
pub missing: String,
845+
}
846+
837847
#[derive(Diagnostic)]
838848
#[diag(ast_passes_negative_bound_not_supported)]
839849
pub(crate) struct NegativeBoundUnsupported {

compiler/rustc_ast_passes/src/feature_gate.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
446446
pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
447447
maybe_stage_features(sess, features, krate);
448448
check_incompatible_features(sess, features);
449+
check_dependent_features(sess, features);
449450
check_new_solver_banned_features(sess, features);
450451

451452
let mut visitor = PostExpansionVisitor { sess, features };
@@ -656,6 +657,27 @@ fn check_incompatible_features(sess: &Session, features: &Features) {
656657
}
657658
}
658659

660+
fn check_dependent_features(sess: &Session, features: &Features) {
661+
for &(parent, children) in
662+
rustc_feature::DEPENDENT_FEATURES.iter().filter(|(parent, _)| features.enabled(*parent))
663+
{
664+
if children.iter().any(|f| !features.enabled(*f)) {
665+
let parent_span = features
666+
.enabled_features_iter_stable_order()
667+
.find_map(|(name, span)| (name == parent).then_some(span))
668+
.unwrap();
669+
// FIXME: should probably format this in fluent instead of here
670+
let missing = children
671+
.iter()
672+
.filter(|f| !features.enabled(**f))
673+
.map(|s| format!("`{}`", s.as_str()))
674+
.intersperse(String::from(", "))
675+
.collect();
676+
sess.dcx().emit_err(errors::MissingDependentFeatures { parent_span, parent, missing });
677+
}
678+
}
679+
}
680+
659681
fn check_new_solver_banned_features(sess: &Session, features: &Features) {
660682
if !sess.opts.unstable_opts.next_solver.globally {
661683
return;

compiler/rustc_ast_passes/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// tidy-alphabetical-start
66
#![feature(box_patterns)]
77
#![feature(if_let_guard)]
8+
#![feature(iter_intersperse)]
89
#![feature(iter_is_partitioned)]
910
// tidy-alphabetical-end
1011

compiler/rustc_feature/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,5 +136,6 @@ pub use builtin_attrs::{
136136
};
137137
pub use removed::REMOVED_LANG_FEATURES;
138138
pub use unstable::{
139-
EnabledLangFeature, EnabledLibFeature, Features, INCOMPATIBLE_FEATURES, UNSTABLE_LANG_FEATURES,
139+
DEPENDENT_FEATURES, EnabledLangFeature, EnabledLibFeature, Features, INCOMPATIBLE_FEATURES,
140+
UNSTABLE_LANG_FEATURES,
140141
};

compiler/rustc_feature/src/unstable.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,9 @@ declare_features! (
599599
(unstable, offset_of_enum, "1.75.0", Some(120141)),
600600
/// Allows using fields with slice type in offset_of!
601601
(unstable, offset_of_slice, "1.81.0", Some(126151)),
602+
/// Allows using generics in more complex const expressions, based on definitional equality.
603+
// TODO: create tracking issue
604+
(unstable, opaque_generic_const_args, "CURRENT_RUSTC_VERSION", None),
602605
/// Allows using `#[optimize(X)]`.
603606
(unstable, optimize_attribute, "1.34.0", Some(54882)),
604607
/// Allows specifying nop padding on functions for dynamic patching.
@@ -774,3 +777,9 @@ pub const INCOMPATIBLE_FEATURES: &[(Symbol, Symbol)] = &[
774777
// boolean logic required to tell which typing rules to use.
775778
(sym::ref_pat_eat_one_layer_2024, sym::ref_pat_eat_one_layer_2024_structural),
776779
];
780+
781+
/// Some features require one or more other features to be enabled.
782+
pub const DEPENDENT_FEATURES: &[(Symbol, &[Symbol])] = &[
783+
(sym::opaque_generic_const_args, &[sym::min_generic_const_args]),
784+
(sym::unsized_const_params, &[sym::adt_const_params]),
785+
];

compiler/rustc_hir_analysis/src/collect.rs

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
use std::assert_matches::assert_matches;
1818
use std::cell::Cell;
1919
use std::iter;
20-
use std::ops::Bound;
20+
use std::ops::{Bound, ControlFlow};
2121

2222
use rustc_abi::{ExternAbi, Size};
2323
use rustc_ast::Recovered;
@@ -26,12 +26,13 @@ use rustc_errors::{
2626
Applicability, Diag, DiagCtxtHandle, E0228, ErrorGuaranteed, StashKey, struct_span_code_err,
2727
};
2828
use rustc_hir::attrs::AttributeKind;
29-
use rustc_hir::def::DefKind;
29+
use rustc_hir::def::{DefKind, Res};
3030
use rustc_hir::def_id::{DefId, LocalDefId};
31-
use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt};
31+
use rustc_hir::intravisit::{self, InferKind, Visitor, VisitorExt};
3232
use rustc_hir::{self as hir, GenericParamKind, HirId, Node, PreciseCapturingArgKind, find_attr};
3333
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
3434
use rustc_infer::traits::{DynCompatibilityViolation, ObligationCause};
35+
use rustc_middle::hir::nested_filter;
3536
use rustc_middle::query::Providers;
3637
use rustc_middle::ty::util::{Discr, IntTypeExt};
3738
use rustc_middle::ty::{
@@ -1511,6 +1512,13 @@ fn anon_const_kind<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> ty::AnonConstKin
15111512
let parent_hir_node = tcx.hir_node(tcx.parent_hir_id(const_arg_id));
15121513
if tcx.features().generic_const_exprs() {
15131514
ty::AnonConstKind::GCE
1515+
} else if tcx.features().opaque_generic_const_args() {
1516+
let body = tcx.hir_body_owned_by(def);
1517+
let mut visitor = OGCAParamVisitor(tcx);
1518+
match visitor.visit_body(body) {
1519+
ControlFlow::Break(UsesParam) => ty::AnonConstKind::OGCA,
1520+
ControlFlow::Continue(()) => ty::AnonConstKind::MCG,
1521+
}
15141522
} else if tcx.features().min_generic_const_args() {
15151523
ty::AnonConstKind::MCG
15161524
} else if let hir::Node::Expr(hir::Expr {
@@ -1528,6 +1536,29 @@ fn anon_const_kind<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> ty::AnonConstKin
15281536
}
15291537
}
15301538

1539+
struct OGCAParamVisitor<'tcx>(TyCtxt<'tcx>);
1540+
1541+
struct UsesParam;
1542+
1543+
impl<'tcx> Visitor<'tcx> for OGCAParamVisitor<'tcx> {
1544+
type NestedFilter = nested_filter::OnlyBodies;
1545+
type Result = ControlFlow<UsesParam>;
1546+
1547+
fn maybe_tcx(&mut self) -> TyCtxt<'tcx> {
1548+
self.0
1549+
}
1550+
1551+
fn visit_path(&mut self, path: &crate::hir::Path<'tcx>, _id: HirId) -> ControlFlow<UsesParam> {
1552+
if let Res::Def(DefKind::TyParam | DefKind::ConstParam | DefKind::LifetimeParam, _) =
1553+
path.res
1554+
{
1555+
return ControlFlow::Break(UsesParam);
1556+
}
1557+
1558+
intravisit::walk_path(self, path)
1559+
}
1560+
}
1561+
15311562
#[instrument(level = "debug", skip(tcx), ret)]
15321563
fn const_of_item<'tcx>(
15331564
tcx: TyCtxt<'tcx>,

compiler/rustc_hir_analysis/src/collect/generics_of.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
9292
match tcx.anon_const_kind(def_id) {
9393
// Stable: anon consts are not able to use any generic parameters...
9494
ty::AnonConstKind::MCG => None,
95+
// OGCA anon consts inherit their parent's generics.
96+
ty::AnonConstKind::OGCA => Some(parent_did),
9597
// we provide generics to repeat expr counts as a backwards compatibility hack. #76200
9698
ty::AnonConstKind::RepeatExprCount => Some(parent_did),
9799

compiler/rustc_middle/src/ty/consts.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,9 @@ pub enum AnonConstKind {
342342
GCE,
343343
/// stable `min_const_generics` anon consts are not allowed to use any generic parameters
344344
MCG,
345+
/// `feature(opaque_generic_const_args)` anon consts are allowed to use arbitrary
346+
/// generic parameters in scope, but only if they syntactically reference them.
347+
OGCA,
345348
/// anon consts used as the length of a repeat expr are syntactically allowed to use generic parameters
346349
/// but must not depend on the actual instantiation. See #76200 for more information
347350
RepeatExprCount,

compiler/rustc_parse/src/parser/item.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1509,7 +1509,17 @@ impl<'a> Parser<'a> {
15091509

15101510
let rhs = if self.eat(exp!(Eq)) {
15111511
if attr::contains_name(attrs, sym::type_const) {
1512-
Some(ConstItemRhs::TypeConst(self.parse_const_arg()?))
1512+
let ct = if self.eat_keyword(exp!(Const)) {
1513+
// While we could just disambiguate `Direct` from `AnonConst` by
1514+
// treating all const block exprs as `AnonConst`, that would
1515+
// complicate the DefCollector and likely all other visitors.
1516+
// So we strip the const blockiness and just store it as a block
1517+
// in the AST with the extra disambiguator on the AnonConst
1518+
self.parse_mgca_const_block(false)?
1519+
} else {
1520+
self.parse_expr_anon_const(|this, expr| this.mgca_direct_lit_hack(expr))?
1521+
};
1522+
Some(ConstItemRhs::TypeConst(ct))
15131523
} else {
15141524
Some(ConstItemRhs::Body(self.parse_expr()?))
15151525
}

0 commit comments

Comments
 (0)