Skip to content

Commit 0fbe68a

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 d222ddc commit 0fbe68a

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
@@ -440,6 +440,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
440440
pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
441441
maybe_stage_features(sess, features, krate);
442442
check_incompatible_features(sess, features);
443+
check_dependent_features(sess, features);
443444
check_new_solver_banned_features(sess, features);
444445

445446
let mut visitor = PostExpansionVisitor { sess, features };
@@ -647,6 +648,27 @@ fn check_incompatible_features(sess: &Session, features: &Features) {
647648
}
648649
}
649650

651+
fn check_dependent_features(sess: &Session, features: &Features) {
652+
for &(parent, children) in
653+
rustc_feature::DEPENDENT_FEATURES.iter().filter(|(parent, _)| features.enabled(*parent))
654+
{
655+
if children.iter().any(|f| !features.enabled(*f)) {
656+
let parent_span = features
657+
.enabled_features_iter_stable_order()
658+
.find_map(|(name, span)| (name == parent).then_some(span))
659+
.unwrap();
660+
// FIXME: should probably format this in fluent instead of here
661+
let missing = children
662+
.iter()
663+
.filter(|f| !features.enabled(**f))
664+
.map(|s| format!("`{}`", s.as_str()))
665+
.intersperse(String::from(", "))
666+
.collect();
667+
sess.dcx().emit_err(errors::MissingDependentFeatures { parent_span, parent, missing });
668+
}
669+
}
670+
}
671+
650672
fn check_new_solver_banned_features(sess: &Session, features: &Features) {
651673
if !sess.opts.unstable_opts.next_solver.globally {
652674
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
@@ -16,7 +16,7 @@
1616
1717
use std::cell::Cell;
1818
use std::iter;
19-
use std::ops::Bound;
19+
use std::ops::{Bound, ControlFlow};
2020

2121
use rustc_abi::{ExternAbi, Size};
2222
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::{
@@ -1510,6 +1511,13 @@ fn anon_const_kind<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> ty::AnonConstKin
15101511
let parent_hir_node = tcx.hir_node(tcx.parent_hir_id(const_arg_id));
15111512
if tcx.features().generic_const_exprs() {
15121513
ty::AnonConstKind::GCE
1514+
} else if tcx.features().opaque_generic_const_args() {
1515+
let body = tcx.hir_body_owned_by(def);
1516+
let mut visitor = OGCAParamVisitor(tcx);
1517+
match visitor.visit_body(body) {
1518+
ControlFlow::Break(UsesParam) => ty::AnonConstKind::OGCA,
1519+
ControlFlow::Continue(()) => ty::AnonConstKind::MCG,
1520+
}
15131521
} else if tcx.features().min_generic_const_args() {
15141522
ty::AnonConstKind::MCG
15151523
} else if let hir::Node::Expr(hir::Expr {
@@ -1527,6 +1535,29 @@ fn anon_const_kind<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> ty::AnonConstKin
15271535
}
15281536
}
15291537

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