Skip to content

Commit 5fa25d4

Browse files
committed
Add const effect syntax to the parser for all function headers
1 parent 0052157 commit 5fa25d4

File tree

130 files changed

+909
-348
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

130 files changed

+909
-348
lines changed

compiler/rustc_ast/src/ast.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -3009,7 +3009,7 @@ impl BoundPolarity {
30093009
}
30103010
}
30113011

3012-
/// The constness of a trait bound.
3012+
/// The constness of a trait bound or function.
30133013
#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, Hash)]
30143014
#[derive(HashStable_Generic)]
30153015
pub enum BoundConstness {
@@ -3461,7 +3461,7 @@ pub struct FnHeader {
34613461
/// Whether this is `async`, `gen`, or nothing.
34623462
pub coroutine_kind: Option<CoroutineKind>,
34633463
/// The `const` keyword, if any
3464-
pub constness: Const,
3464+
pub constness: BoundConstness,
34653465
/// The `extern` keyword and corresponding ABI string, if any.
34663466
pub ext: Extern,
34673467
}
@@ -3472,7 +3472,7 @@ impl FnHeader {
34723472
let Self { safety, coroutine_kind, constness, ext } = self;
34733473
matches!(safety, Safety::Unsafe(_))
34743474
|| coroutine_kind.is_some()
3475-
|| matches!(constness, Const::Yes(_))
3475+
|| !matches!(constness, BoundConstness::Never)
34763476
|| !matches!(ext, Extern::None)
34773477
}
34783478
}
@@ -3482,7 +3482,7 @@ impl Default for FnHeader {
34823482
FnHeader {
34833483
safety: Safety::Default,
34843484
coroutine_kind: None,
3485-
constness: Const::No,
3485+
constness: BoundConstness::Never,
34863486
ext: Extern::None,
34873487
}
34883488
}

compiler/rustc_ast/src/mut_visit.rs

+10-5
Original file line numberDiff line numberDiff line change
@@ -902,6 +902,14 @@ fn visit_constness<T: MutVisitor>(vis: &mut T, constness: &mut Const) {
902902
}
903903
}
904904

905+
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
906+
fn walk_bound_constness<T: MutVisitor>(vis: &mut T, constness: &mut BoundConstness) {
907+
match constness {
908+
BoundConstness::Never => {}
909+
BoundConstness::Always(span) | BoundConstness::Maybe(span) => vis.visit_span(span),
910+
}
911+
}
912+
905913
fn walk_closure_binder<T: MutVisitor>(vis: &mut T, binder: &mut ClosureBinder) {
906914
match binder {
907915
ClosureBinder::NotPresent => {}
@@ -1131,10 +1139,7 @@ fn walk_poly_trait_ref<T: MutVisitor>(vis: &mut T, p: &mut PolyTraitRef) {
11311139

11321140
fn walk_modifiers<V: MutVisitor>(vis: &mut V, m: &mut TraitBoundModifiers) {
11331141
let TraitBoundModifiers { constness, asyncness, polarity } = m;
1134-
match constness {
1135-
BoundConstness::Never => {}
1136-
BoundConstness::Always(span) | BoundConstness::Maybe(span) => vis.visit_span(span),
1137-
}
1142+
walk_bound_constness(vis, constness);
11381143
match asyncness {
11391144
BoundAsyncness::Normal => {}
11401145
BoundAsyncness::Async(span) => vis.visit_span(span),
@@ -1443,7 +1448,7 @@ fn walk_const_item<T: MutVisitor>(vis: &mut T, item: &mut ConstItem) {
14431448

14441449
fn walk_fn_header<T: MutVisitor>(vis: &mut T, header: &mut FnHeader) {
14451450
let FnHeader { safety, coroutine_kind, constness, ext: _ } = header;
1446-
visit_constness(vis, constness);
1451+
walk_bound_constness(vis, constness);
14471452
coroutine_kind.as_mut().map(|coroutine_kind| vis.visit_coroutine_kind(coroutine_kind));
14481453
visit_safety(vis, safety);
14491454
}

compiler/rustc_ast_lowering/src/item.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1600,7 +1600,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
16001600
hir::FnHeader {
16011601
safety,
16021602
asyncness,
1603-
constness: self.lower_constness(h.constness),
1603+
constness: self.lower_fn_header_constness(h.constness),
16041604
abi: self.lower_extern(h.ext),
16051605
}
16061606
}

compiler/rustc_ast_lowering/src/lib.rs

+9
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
198198
pub(crate) fn dcx(&self) -> DiagCtxtHandle<'hir> {
199199
self.tcx.dcx()
200200
}
201+
202+
fn lower_fn_header_constness(&self, constness: BoundConstness) -> hir::Constness {
203+
match constness {
204+
BoundConstness::Never => hir::Constness::NotConst,
205+
BoundConstness::Always(_) => hir::Constness::Const,
206+
// FIXME(const_trait_impls): support non-const fn and `(const)` fns within the same trait
207+
BoundConstness::Maybe(_) => hir::Constness::NotConst,
208+
}
209+
}
201210
}
202211

203212
#[extension(trait ResolverAstLoweringExt)]

compiler/rustc_ast_passes/messages.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ ast_passes_const_and_coroutine = functions cannot be both `const` and `{$corouti
4747
4848
ast_passes_const_bound_trait_object = const trait bounds are not allowed in trait object types
4949
50+
ast_passes_const_in_trait = `const fn` in traits is unstable
51+
5052
ast_passes_const_without_body =
5153
free constant item without body
5254
.suggestion = provide a definition for the constant

compiler/rustc_ast_passes/src/ast_validation.rs

+64-18
Original file line numberDiff line numberDiff line change
@@ -253,12 +253,55 @@ impl<'a> AstValidator<'a> {
253253
}
254254
}
255255

256-
fn check_trait_fn_not_const(&self, constness: Const, parent: &TraitOrTraitImpl) {
257-
let Const::Yes(span) = constness else {
258-
return;
256+
fn check_trait_fn_not_const(
257+
&self,
258+
constness: BoundConstness,
259+
sig_span: Span,
260+
parent: &TraitOrTraitImpl,
261+
) {
262+
let const_trait_impl = self.features.const_trait_impl();
263+
264+
let span = match (constness, parent) {
265+
(BoundConstness::Never, toti) => {
266+
// only `(const)` or `const` fn are allowed in traits or impls respectively.
267+
// But for bootstrap purposes we allow the stage1 std and the stage0 std to be the same.
268+
if toti.constness().is_some() && !self.features.staged_api() {
269+
// FIXME(const_trait_impls): allow non-const fns
270+
self.dcx()
271+
.struct_span_err(
272+
sig_span.shrink_to_lo(),
273+
"non-const fn in const traits are not supported yet",
274+
)
275+
.with_span_suggestion(
276+
sig_span.shrink_to_lo(),
277+
"mark the function as const",
278+
match toti {
279+
TraitOrTraitImpl::Trait { .. } => "(const) ",
280+
TraitOrTraitImpl::TraitImpl { .. } => "const ",
281+
},
282+
rustc_errors::Applicability::MachineApplicable,
283+
)
284+
.emit();
285+
}
286+
return;
287+
}
288+
// `(const) fn` in `const Trait` or `impl const Trait` is ok
289+
(BoundConstness::Always(span), _) => span,
290+
(
291+
BoundConstness::Maybe(span),
292+
TraitOrTraitImpl::Trait { constness_span: Some(_), .. }
293+
| TraitOrTraitImpl::TraitImpl { constness: Const::Yes(_), .. },
294+
) => {
295+
if !const_trait_impl {
296+
self.sess
297+
.create_feature_err(errors::ConstInTrait { span }, sym::const_trait_impl)
298+
.emit();
299+
}
300+
return;
301+
}
302+
(BoundConstness::Maybe(span), _) => span,
259303
};
260304

261-
let const_trait_impl = self.features.const_trait_impl();
262305
let make_impl_const_sugg = if const_trait_impl
263306
&& let TraitOrTraitImpl::TraitImpl {
264307
constness: Const::No,
@@ -500,8 +543,9 @@ impl<'a> AstValidator<'a> {
500543
None => (),
501544
}
502545
match constness {
503-
Const::Yes(span) => report_err(span, "const"),
504-
Const::No => (),
546+
BoundConstness::Always(span) => report_err(span, "const"),
547+
BoundConstness::Maybe(span) => report_err(span, "~const"),
548+
BoundConstness::Never => (),
505549
}
506550
match ext {
507551
Extern::None => (),
@@ -538,7 +582,9 @@ impl<'a> AstValidator<'a> {
538582
}
539583

540584
if let Some(header) = fk.header() {
541-
if let Const::Yes(const_span) = header.constness {
585+
if let BoundConstness::Always(const_span) | BoundConstness::Maybe(const_span) =
586+
header.constness
587+
{
542588
let mut spans = variadic_spans.clone();
543589
spans.push(const_span);
544590
self.dcx().emit_err(errors::ConstAndCVariadic {
@@ -1348,7 +1394,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
13481394

13491395
// Functions cannot both be `const async` or `const gen`
13501396
if let Some(&FnHeader {
1351-
constness: Const::Yes(const_span),
1397+
constness: BoundConstness::Always(const_span) | BoundConstness::Maybe(const_span),
13521398
coroutine_kind: Some(coroutine_kind),
13531399
..
13541400
}) = fk.header()
@@ -1399,14 +1445,14 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
13991445
});
14001446
}
14011447

1402-
let tilde_const_allowed =
1403-
matches!(fk.header(), Some(FnHeader { constness: ast::Const::Yes(_), .. }))
1404-
|| matches!(fk.ctxt(), Some(FnCtxt::Assoc(_)))
1405-
&& self
1406-
.outer_trait_or_trait_impl
1407-
.as_ref()
1408-
.and_then(TraitOrTraitImpl::constness)
1409-
.is_some();
1448+
let tilde_const_allowed = matches!(fk.header(), Some(FnHeader { constness: ast::BoundConstness::Always(_) | ast::BoundConstness::Maybe(_), .. }))
1449+
// FIXME(const_trait_impls): remove this, we don't want to allow `~const` trait bounds in non-const methods
1450+
|| matches!(fk.ctxt(), Some(FnCtxt::Assoc(_)))
1451+
&& self
1452+
.outer_trait_or_trait_impl
1453+
.as_ref()
1454+
.and_then(TraitOrTraitImpl::constness)
1455+
.is_some();
14101456

14111457
let disallowed = (!tilde_const_allowed).then(|| match fk {
14121458
FnKind::Fn(_, _, f) => TildeConstReason::Function { ident: f.ident.span },
@@ -1475,7 +1521,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
14751521
if let Some(parent) = &self.outer_trait_or_trait_impl {
14761522
self.visibility_not_permitted(&item.vis, errors::VisibilityNotPermittedNote::TraitImpl);
14771523
if let AssocItemKind::Fn(box Fn { sig, .. }) = &item.kind {
1478-
self.check_trait_fn_not_const(sig.header.constness, parent);
1524+
self.check_trait_fn_not_const(sig.header.constness, sig.span, parent);
14791525
}
14801526
}
14811527

@@ -1490,7 +1536,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
14901536
AssocItemKind::Fn(func)
14911537
if parent_is_const
14921538
|| ctxt == AssocCtxt::Trait
1493-
|| matches!(func.sig.header.constness, Const::Yes(_)) =>
1539+
|| !matches!(func.sig.header.constness, ast::BoundConstness::Never) =>
14941540
{
14951541
self.visit_attrs_vis_ident(&item.attrs, &item.vis, &func.ident);
14961542
let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), &item.vis, &*func);

compiler/rustc_ast_passes/src/errors.rs

+7
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,13 @@ pub(crate) struct NestedLifetimes {
565565
pub span: Span,
566566
}
567567

568+
#[derive(Diagnostic)]
569+
#[diag(ast_passes_const_in_trait)]
570+
pub(crate) struct ConstInTrait {
571+
#[primary_span]
572+
pub span: Span,
573+
}
574+
568575
#[derive(Diagnostic)]
569576
#[diag(ast_passes_optional_trait_supertrait)]
570577
#[note]

compiler/rustc_ast_pretty/src/pprust/state.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -1981,7 +1981,7 @@ impl<'a> State<'a> {
19811981
}
19821982

19831983
fn print_fn_header_info(&mut self, header: ast::FnHeader) {
1984-
self.print_constness(header.constness);
1984+
self.print_bound_constness(header.constness);
19851985
header.coroutine_kind.map(|coroutine_kind| self.print_coroutine_kind(coroutine_kind));
19861986
self.print_safety(header.safety);
19871987

@@ -2015,6 +2015,14 @@ impl<'a> State<'a> {
20152015
}
20162016
}
20172017

2018+
fn print_bound_constness(&mut self, s: ast::BoundConstness) {
2019+
match s {
2020+
ast::BoundConstness::Never => {}
2021+
ast::BoundConstness::Always(_) => self.word_nbsp("const"),
2022+
ast::BoundConstness::Maybe(_) => self.word_nbsp("~const"),
2023+
}
2024+
}
2025+
20182026
fn print_is_auto(&mut self, s: ast::IsAuto) {
20192027
match s {
20202028
ast::IsAuto::Yes => self.word_nbsp("auto"),

compiler/rustc_builtin_macros/src/deriving/generic/mod.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -1018,7 +1018,18 @@ impl<'a> MethodDef<'a> {
10181018

10191019
let trait_lo_sp = span.shrink_to_lo();
10201020

1021-
let sig = ast::FnSig { header: ast::FnHeader::default(), decl: fn_decl, span };
1021+
let sig = ast::FnSig {
1022+
header: ast::FnHeader {
1023+
constness: if trait_.is_const {
1024+
ast::BoundConstness::Maybe(span)
1025+
} else {
1026+
ast::BoundConstness::Never
1027+
},
1028+
..Default::default()
1029+
},
1030+
decl: fn_decl,
1031+
span,
1032+
};
10221033
let defaultness = ast::Defaultness::Final;
10231034

10241035
// Create the method.

compiler/rustc_parse/src/parser/item.rs

+36-6
Original file line numberDiff line numberDiff line change
@@ -857,13 +857,20 @@ impl<'a> Parser<'a> {
857857
false
858858
}
859859

860+
fn is_paren_const(&self, i: usize) -> bool {
861+
self.look_ahead(i, |t| *t == token::OpenDelim(Delimiter::Parenthesis))
862+
&& self.look_ahead(1 + i, |t| t.is_keyword(kw::Const))
863+
&& self.look_ahead(2 + i, |t| *t == token::CloseDelim(Delimiter::Parenthesis))
864+
}
865+
860866
/// Parses defaultness (i.e., `default` or nothing).
861867
fn parse_defaultness(&mut self) -> Defaultness {
862868
// We are interested in `default` followed by another identifier.
863869
// However, we must avoid keywords that occur as binary operators.
864870
// Currently, the only applicable keyword is `as` (`default as Ty`).
865871
if self.check_keyword(exp!(Default))
866-
&& self.look_ahead(1, |t| t.is_non_raw_ident_where(|i| i.name != kw::As))
872+
&& (self.look_ahead(1, |t| t.is_non_raw_ident_where(|i| i.name != kw::As))
873+
|| self.is_paren_const(1))
867874
{
868875
self.bump(); // `default`
869876
Defaultness::Default(self.prev_token_uninterpolated_span())
@@ -2613,6 +2620,8 @@ impl<'a> Parser<'a> {
26132620
// Rule out `async gen {` and `async gen move {`
26142621
&& !self.is_async_gen_block())
26152622
})
2623+
// `(const)`
2624+
|| self.is_paren_const(0)
26162625
// `extern ABI fn`
26172626
|| self.check_keyword_case(exp!(Extern), case)
26182627
// Use `tree_look_ahead` because `ABI` might be a metavariable,
@@ -2647,12 +2656,31 @@ impl<'a> Parser<'a> {
26472656
)
26482657
}
26492658

2659+
pub fn parse_fn_constness(&mut self, case: Case) -> PResult<'a, BoundConstness> {
2660+
Ok(if self.eat_keyword_case(exp!(Const), case) {
2661+
BoundConstness::Always(self.prev_token.span)
2662+
} else if self.check(exp!(OpenParen))
2663+
&& self.look_ahead(1, |t| t.is_keyword(kw::Const))
2664+
&& self.look_ahead(2, |t| *t == token::CloseDelim(Delimiter::Parenthesis))
2665+
{
2666+
let start = self.token.span;
2667+
self.bump();
2668+
self.expect_keyword(exp!(Const)).unwrap();
2669+
self.bump();
2670+
let span = start.to(self.prev_token.span);
2671+
self.psess.gated_spans.gate(sym::const_trait_impl, span);
2672+
BoundConstness::Maybe(span)
2673+
} else {
2674+
BoundConstness::Never
2675+
})
2676+
}
2677+
26502678
/// Parses all the "front matter" (or "qualifiers") for a `fn` declaration,
26512679
/// up to and including the `fn` keyword. The formal grammar is:
26522680
///
26532681
/// ```text
26542682
/// Extern = "extern" StringLit? ;
2655-
/// FnQual = "const"? "async"? "unsafe"? Extern? ;
2683+
/// FnQual = "(const)"? "const"? "async"? "unsafe"? Extern? ;
26562684
/// FnFrontMatter = FnQual "fn" ;
26572685
/// ```
26582686
///
@@ -2664,7 +2692,7 @@ impl<'a> Parser<'a> {
26642692
case: Case,
26652693
) -> PResult<'a, FnHeader> {
26662694
let sp_start = self.token.span;
2667-
let constness = self.parse_constness(case);
2695+
let constness = self.parse_fn_constness(case)?;
26682696

26692697
let async_start_sp = self.token.span;
26702698
let coroutine_kind = self.parse_coroutine_kind(case);
@@ -2713,9 +2741,11 @@ impl<'a> Parser<'a> {
27132741
// that the keyword is already present and the second instance should be removed.
27142742
let wrong_kw = if self.check_keyword(exp!(Const)) {
27152743
match constness {
2716-
Const::Yes(sp) => Some(WrongKw::Duplicated(sp)),
2717-
Const::No => {
2718-
recover_constness = Const::Yes(self.token.span);
2744+
BoundConstness::Always(sp) | BoundConstness::Maybe(sp) => {
2745+
Some(WrongKw::Duplicated(sp))
2746+
}
2747+
BoundConstness::Never => {
2748+
recover_constness = BoundConstness::Always(self.token.span);
27192749
Some(WrongKw::Misplaced(async_start_sp))
27202750
}
27212751
}

0 commit comments

Comments
 (0)