Skip to content

Commit ef1acde

Browse files
committed
Add const effect syntax to the parser for all function headers
1 parent b991728 commit ef1acde

File tree

130 files changed

+919
-366
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

+919
-366
lines changed

compiler/rustc_ast/src/ast.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -3021,7 +3021,7 @@ impl BoundPolarity {
30213021
}
30223022
}
30233023

3024-
/// The constness of a trait bound.
3024+
/// The constness of a trait bound or function.
30253025
#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, Hash)]
30263026
#[derive(HashStable_Generic)]
30273027
pub enum BoundConstness {
@@ -3473,7 +3473,7 @@ pub struct FnHeader {
34733473
/// Whether this is `async`, `gen`, or nothing.
34743474
pub coroutine_kind: Option<CoroutineKind>,
34753475
/// The `const` keyword, if any
3476-
pub constness: Const,
3476+
pub constness: BoundConstness,
34773477
/// The `extern` keyword and corresponding ABI string, if any.
34783478
pub ext: Extern,
34793479
}
@@ -3484,7 +3484,7 @@ impl FnHeader {
34843484
let Self { safety, coroutine_kind, constness, ext } = self;
34853485
matches!(safety, Safety::Unsafe(_))
34863486
|| coroutine_kind.is_some()
3487-
|| matches!(constness, Const::Yes(_))
3487+
|| !matches!(constness, BoundConstness::Never)
34883488
|| !matches!(ext, Extern::None)
34893489
}
34903490
}
@@ -3494,7 +3494,7 @@ impl Default for FnHeader {
34943494
FnHeader {
34953495
safety: Safety::Default,
34963496
coroutine_kind: None,
3497-
constness: Const::No,
3497+
constness: BoundConstness::Never,
34983498
ext: Extern::None,
34993499
}
35003500
}

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 {
@@ -1347,7 +1393,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
13471393

13481394
// Functions cannot both be `const async` or `const gen`
13491395
if let Some(&FnHeader {
1350-
constness: Const::Yes(const_span),
1396+
constness: BoundConstness::Always(const_span) | BoundConstness::Maybe(const_span),
13511397
coroutine_kind: Some(coroutine_kind),
13521398
..
13531399
}) = fk.header()
@@ -1398,14 +1444,14 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
13981444
});
13991445
}
14001446

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

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

@@ -1489,7 +1535,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
14891535
AssocItemKind::Fn(func)
14901536
if parent_is_const
14911537
|| ctxt == AssocCtxt::Trait
1492-
|| matches!(func.sig.header.constness, Const::Yes(_)) =>
1538+
|| !matches!(func.sig.header.constness, ast::BoundConstness::Never) =>
14931539
{
14941540
self.visit_attrs_vis_ident(&item.attrs, &item.vis, &func.ident);
14951541
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
@@ -569,6 +569,13 @@ pub(crate) struct NestedLifetimes {
569569
pub span: Span,
570570
}
571571

572+
#[derive(Diagnostic)]
573+
#[diag(ast_passes_const_in_trait)]
574+
pub(crate) struct ConstInTrait {
575+
#[primary_span]
576+
pub span: Span,
577+
}
578+
572579
#[derive(Diagnostic)]
573580
#[diag(ast_passes_optional_trait_supertrait)]
574581
#[note]

compiler/rustc_ast_pretty/src/pprust/state.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -1979,7 +1979,7 @@ impl<'a> State<'a> {
19791979
}
19801980

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

@@ -2013,6 +2013,14 @@ impl<'a> State<'a> {
20132013
}
20142014
}
20152015

2016+
fn print_bound_constness(&mut self, s: ast::BoundConstness) {
2017+
match s {
2018+
ast::BoundConstness::Never => {}
2019+
ast::BoundConstness::Always(_) => self.word_nbsp("const"),
2020+
ast::BoundConstness::Maybe(_) => self.word_nbsp("~const"),
2021+
}
2022+
}
2023+
20162024
fn print_is_auto(&mut self, s: ast::IsAuto) {
20172025
match s {
20182026
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
@@ -1019,7 +1019,18 @@ impl<'a> MethodDef<'a> {
10191019

10201020
let trait_lo_sp = span.shrink_to_lo();
10211021

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

10251036
// Create the method.

compiler/rustc_parse/src/parser/item.rs

+36-6
Original file line numberDiff line numberDiff line change
@@ -861,13 +861,20 @@ impl<'a> Parser<'a> {
861861
false
862862
}
863863

864+
fn is_paren_const(&self, i: usize) -> bool {
865+
self.look_ahead(i, |t| *t == token::OpenDelim(Delimiter::Parenthesis))
866+
&& self.look_ahead(1 + i, |t| t.is_keyword(kw::Const))
867+
&& self.look_ahead(2 + i, |t| *t == token::CloseDelim(Delimiter::Parenthesis))
868+
}
869+
864870
/// Parses defaultness (i.e., `default` or nothing).
865871
fn parse_defaultness(&mut self) -> Defaultness {
866872
// We are interested in `default` followed by another identifier.
867873
// However, we must avoid keywords that occur as binary operators.
868874
// Currently, the only applicable keyword is `as` (`default as Ty`).
869875
if self.check_keyword(exp!(Default))
870-
&& self.look_ahead(1, |t| t.is_non_raw_ident_where(|i| i.name != kw::As))
876+
&& (self.look_ahead(1, |t| t.is_non_raw_ident_where(|i| i.name != kw::As))
877+
|| self.is_paren_const(1))
871878
{
872879
self.bump(); // `default`
873880
Defaultness::Default(self.prev_token_uninterpolated_span())
@@ -2606,6 +2613,8 @@ impl<'a> Parser<'a> {
26062613
// Rule out `async gen {` and `async gen move {`
26072614
&& !self.is_async_gen_block())
26082615
})
2616+
// `(const)`
2617+
|| self.is_paren_const(0)
26092618
// `extern ABI fn`
26102619
|| self.check_keyword_case(exp!(Extern), case)
26112620
// Use `tree_look_ahead` because `ABI` might be a metavariable,
@@ -2640,12 +2649,31 @@ impl<'a> Parser<'a> {
26402649
)
26412650
}
26422651

2652+
pub fn parse_fn_constness(&mut self, case: Case) -> PResult<'a, BoundConstness> {
2653+
Ok(if self.eat_keyword_case(exp!(Const), case) {
2654+
BoundConstness::Always(self.prev_token.span)
2655+
} else if self.check(exp!(OpenParen))
2656+
&& self.look_ahead(1, |t| t.is_keyword(kw::Const))
2657+
&& self.look_ahead(2, |t| *t == token::CloseDelim(Delimiter::Parenthesis))
2658+
{
2659+
let start = self.token.span;
2660+
self.bump();
2661+
self.expect_keyword(exp!(Const)).unwrap();
2662+
self.bump();
2663+
let span = start.to(self.prev_token.span);
2664+
self.psess.gated_spans.gate(sym::const_trait_impl, span);
2665+
BoundConstness::Maybe(span)
2666+
} else {
2667+
BoundConstness::Never
2668+
})
2669+
}
2670+
26432671
/// Parses all the "front matter" (or "qualifiers") for a `fn` declaration,
26442672
/// up to and including the `fn` keyword. The formal grammar is:
26452673
///
26462674
/// ```text
26472675
/// Extern = "extern" StringLit? ;
2648-
/// FnQual = "const"? "async"? "unsafe"? Extern? ;
2676+
/// FnQual = "(const)"? "const"? "async"? "unsafe"? Extern? ;
26492677
/// FnFrontMatter = FnQual "fn" ;
26502678
/// ```
26512679
///
@@ -2657,7 +2685,7 @@ impl<'a> Parser<'a> {
26572685
case: Case,
26582686
) -> PResult<'a, FnHeader> {
26592687
let sp_start = self.token.span;
2660-
let constness = self.parse_constness(case);
2688+
let constness = self.parse_fn_constness(case)?;
26612689

26622690
let async_start_sp = self.token.span;
26632691
let coroutine_kind = self.parse_coroutine_kind(case);
@@ -2706,9 +2734,11 @@ impl<'a> Parser<'a> {
27062734
// that the keyword is already present and the second instance should be removed.
27072735
let wrong_kw = if self.check_keyword(exp!(Const)) {
27082736
match constness {
2709-
Const::Yes(sp) => Some(WrongKw::Duplicated(sp)),
2710-
Const::No => {
2711-
recover_constness = Const::Yes(self.token.span);
2737+
BoundConstness::Always(sp) | BoundConstness::Maybe(sp) => {
2738+
Some(WrongKw::Duplicated(sp))
2739+
}
2740+
BoundConstness::Never => {
2741+
recover_constness = BoundConstness::Always(self.token.span);
27122742
Some(WrongKw::Misplaced(async_start_sp))
27132743
}
27142744
}

0 commit comments

Comments
 (0)