Skip to content

Commit a7529f7

Browse files
authored
Rollup merge of rust-lang#67820 - ecstatic-morse:const-trait, r=oli-obk
Parse the syntax described in RFC 2632 This adds support for both `impl const Trait for Ty` and `?const Trait` bound syntax from rust-lang/rfcs#2632 to the parser. For now, both modifiers end up in a newly-added `constness` field on `ast::TraitRef`, although this may change once the implementation is fleshed out. I was planning on using `delay_span_bug` when this syntax is encountered during lowering, but I can't write `should-ice` UI tests. I emit a normal error instead, which causes duplicates when the feature gate is not enabled (see the `.stderr` files for the feature gate tests). Not sure what the desired approach is; Maybe just do nothing when the syntax is encountered with the feature gate is enabled? @oli-obk I went with `const_trait_impl` and `const_trait_bound_opt_out` for the names of these features. Are these to your liking? cc rust-lang#67792 rust-lang#67794 r? @Centril
2 parents 8597644 + 19f74cb commit a7529f7

37 files changed

+586
-25
lines changed

src/librustc_ast_lowering/item.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ impl<'a, 'lowering, 'hir> Visitor<'a> for ItemLowerer<'a, 'lowering, 'hir> {
7171
self.lctx.with_parent_item_lifetime_defs(hir_id, |this| {
7272
let this = &mut ItemLowerer { lctx: this };
7373
if let ItemKind::Impl(.., ref opt_trait_ref, _, _) = item.kind {
74+
if opt_trait_ref.as_ref().map(|tr| tr.constness.is_some()).unwrap_or(false) {
75+
this.lctx
76+
.diagnostic()
77+
.span_err(item.span, "const trait impls are not yet implemented");
78+
}
79+
7480
this.with_trait_impl_ref(opt_trait_ref, |this| visit::walk_item(this, item));
7581
} else {
7682
visit::walk_item(this, item);

src/librustc_ast_lowering/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2577,6 +2577,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
25772577
p: &PolyTraitRef,
25782578
mut itctx: ImplTraitContext<'_, 'hir>,
25792579
) -> hir::PolyTraitRef<'hir> {
2580+
if p.trait_ref.constness.is_some() {
2581+
self.diagnostic().span_err(p.span, "`?const` on trait bounds is not yet implemented");
2582+
}
2583+
25802584
let bound_generic_params = self.lower_generic_params(
25812585
&p.bound_generic_params,
25822586
&NodeMap::default(),

src/librustc_expand/build.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ impl<'a> ExtCtxt<'a> {
110110
}
111111

112112
pub fn trait_ref(&self, path: ast::Path) -> ast::TraitRef {
113-
ast::TraitRef { path, ref_id: ast::DUMMY_NODE_ID }
113+
ast::TraitRef { path, constness: None, ref_id: ast::DUMMY_NODE_ID }
114114
}
115115

116116
pub fn poly_trait_ref(&self, span: Span, path: ast::Path) -> ast::PolyTraitRef {

src/librustc_feature/active.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,12 @@ declare_features! (
544544
/// For example, you can write `x @ Some(y)`.
545545
(active, bindings_after_at, "1.41.0", Some(65490), None),
546546

547+
/// Allows `impl const Trait for T` syntax.
548+
(active, const_trait_impl, "1.42.0", Some(67792), None),
549+
550+
/// Allows `T: ?const Trait` syntax in bounds.
551+
(active, const_trait_bound_opt_out, "1.42.0", Some(67794), None),
552+
547553
// -------------------------------------------------------------------------
548554
// feature-group-end: actual feature gates
549555
// -------------------------------------------------------------------------
@@ -559,4 +565,6 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[
559565
sym::or_patterns,
560566
sym::let_chains,
561567
sym::raw_dylib,
568+
sym::const_trait_impl,
569+
sym::const_trait_bound_opt_out,
562570
];

src/librustc_parse/parser/item.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::maybe_whole;
55

66
use rustc_error_codes::*;
77
use rustc_errors::{Applicability, DiagnosticBuilder, PResult, StashKey};
8-
use rustc_span::source_map::{self, respan, Span};
8+
use rustc_span::source_map::{self, respan, Span, Spanned};
99
use rustc_span::symbol::{kw, sym, Symbol};
1010
use rustc_span::BytePos;
1111
use syntax::ast::{self, AttrKind, AttrStyle, AttrVec, Attribute, Ident, DUMMY_NODE_ID};
@@ -543,10 +543,11 @@ impl<'a> Parser<'a> {
543543
/// impl<'a, T> TYPE { /* impl items */ }
544544
/// impl<'a, T> TRAIT for TYPE { /* impl items */ }
545545
/// impl<'a, T> !TRAIT for TYPE { /* impl items */ }
546+
/// impl<'a, T> const TRAIT for TYPE { /* impl items */ }
546547
///
547548
/// We actually parse slightly more relaxed grammar for better error reporting and recovery.
548-
/// `impl` GENERICS `!`? TYPE `for`? (TYPE | `..`) (`where` PREDICATES)? `{` BODY `}`
549-
/// `impl` GENERICS `!`? TYPE (`where` PREDICATES)? `{` BODY `}`
549+
/// `impl` GENERICS `const`? `!`? TYPE `for`? (TYPE | `..`) (`where` PREDICATES)? `{` BODY `}`
550+
/// `impl` GENERICS `const`? `!`? TYPE (`where` PREDICATES)? `{` BODY `}`
550551
fn parse_item_impl(
551552
&mut self,
552553
unsafety: Unsafety,
@@ -559,6 +560,14 @@ impl<'a> Parser<'a> {
559560
Generics::default()
560561
};
561562

563+
let constness = if self.eat_keyword(kw::Const) {
564+
let span = self.prev_span;
565+
self.sess.gated_spans.gate(sym::const_trait_impl, span);
566+
Some(respan(span, Constness::Const))
567+
} else {
568+
None
569+
};
570+
562571
// Disambiguate `impl !Trait for Type { ... }` and `impl ! { ... }` for the never type.
563572
let polarity = if self.check(&token::Not) && self.look_ahead(1, |t| t.can_begin_type()) {
564573
self.bump(); // `!`
@@ -619,7 +628,8 @@ impl<'a> Parser<'a> {
619628
err_path(ty_first.span)
620629
}
621630
};
622-
let trait_ref = TraitRef { path, ref_id: ty_first.id };
631+
let constness = constness.map(|c| c.node);
632+
let trait_ref = TraitRef { path, constness, ref_id: ty_first.id };
623633

624634
ItemKind::Impl(
625635
unsafety,
@@ -632,6 +642,13 @@ impl<'a> Parser<'a> {
632642
)
633643
}
634644
None => {
645+
// Reject `impl const Type {}` here
646+
if let Some(Spanned { node: Constness::Const, span }) = constness {
647+
self.struct_span_err(span, "`const` cannot modify an inherent impl")
648+
.help("only a trait impl can be `const`")
649+
.emit();
650+
}
651+
635652
// impl Type
636653
ItemKind::Impl(
637654
unsafety,

src/librustc_parse/parser/ty.rs

Lines changed: 77 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
66
use rustc_error_codes::*;
77
use rustc_errors::{pluralize, Applicability, PResult};
88
use rustc_span::source_map::Span;
9-
use rustc_span::symbol::kw;
9+
use rustc_span::symbol::{kw, sym};
1010
use syntax::ast::{
1111
self, BareFnTy, FunctionRetTy, GenericParam, Ident, Lifetime, MutTy, Ty, TyKind,
1212
};
@@ -18,6 +18,24 @@ use syntax::ptr::P;
1818
use syntax::struct_span_err;
1919
use syntax::token::{self, Token};
2020

21+
/// Any `?` or `?const` modifiers that appear at the start of a bound.
22+
struct BoundModifiers {
23+
/// `?Trait`.
24+
maybe: Option<Span>,
25+
26+
/// `?const Trait`.
27+
maybe_const: Option<Span>,
28+
}
29+
30+
impl BoundModifiers {
31+
fn trait_bound_modifier(&self) -> TraitBoundModifier {
32+
match self.maybe {
33+
Some(_) => TraitBoundModifier::Maybe,
34+
None => TraitBoundModifier::None,
35+
}
36+
}
37+
}
38+
2139
/// Returns `true` if `IDENT t` can start a type -- `IDENT::a::b`, `IDENT<u8, u8>`,
2240
/// `IDENT<<u8 as Trait>::AssocTy>`.
2341
///
@@ -196,7 +214,9 @@ impl<'a> Parser<'a> {
196214
lo: Span,
197215
parse_plus: bool,
198216
) -> PResult<'a, TyKind> {
199-
let poly_trait_ref = PolyTraitRef::new(generic_params, path, lo.to(self.prev_span));
217+
assert_ne!(self.token, token::Question);
218+
219+
let poly_trait_ref = PolyTraitRef::new(generic_params, path, None, lo.to(self.prev_span));
200220
let mut bounds = vec![GenericBound::Trait(poly_trait_ref, TraitBoundModifier::None)];
201221
if parse_plus {
202222
self.eat_plus(); // `+`, or `+=` gets split and `+` is discarded
@@ -422,12 +442,15 @@ impl<'a> Parser<'a> {
422442
let has_parens = self.eat(&token::OpenDelim(token::Paren));
423443
let inner_lo = self.token.span;
424444
let is_negative = self.eat(&token::Not);
425-
let question = self.eat(&token::Question).then_some(self.prev_span);
445+
446+
let modifiers = self.parse_ty_bound_modifiers();
426447
let bound = if self.token.is_lifetime() {
427-
self.parse_generic_lt_bound(lo, inner_lo, has_parens, question)?
448+
self.error_lt_bound_with_modifiers(modifiers);
449+
self.parse_generic_lt_bound(lo, inner_lo, has_parens)?
428450
} else {
429-
self.parse_generic_ty_bound(lo, has_parens, question)?
451+
self.parse_generic_ty_bound(lo, has_parens, modifiers)?
430452
};
453+
431454
Ok(if is_negative { Err(anchor_lo.to(self.prev_span)) } else { Ok(bound) })
432455
}
433456

@@ -440,9 +463,7 @@ impl<'a> Parser<'a> {
440463
lo: Span,
441464
inner_lo: Span,
442465
has_parens: bool,
443-
question: Option<Span>,
444466
) -> PResult<'a, GenericBound> {
445-
self.error_opt_out_lifetime(question);
446467
let bound = GenericBound::Outlives(self.expect_lifetime());
447468
if has_parens {
448469
// FIXME(Centril): Consider not erroring here and accepting `('lt)` instead,
@@ -452,8 +473,17 @@ impl<'a> Parser<'a> {
452473
Ok(bound)
453474
}
454475

455-
fn error_opt_out_lifetime(&self, question: Option<Span>) {
456-
if let Some(span) = question {
476+
/// Emits an error if any trait bound modifiers were present.
477+
fn error_lt_bound_with_modifiers(&self, modifiers: BoundModifiers) {
478+
if let Some(span) = modifiers.maybe_const {
479+
self.struct_span_err(
480+
span,
481+
"`?const` may only modify trait bounds, not lifetime bounds",
482+
)
483+
.emit();
484+
}
485+
486+
if let Some(span) = modifiers.maybe {
457487
self.struct_span_err(span, "`?` may only modify trait bounds, not lifetime bounds")
458488
.emit();
459489
}
@@ -479,25 +509,58 @@ impl<'a> Parser<'a> {
479509
Ok(())
480510
}
481511

512+
/// Parses the modifiers that may precede a trait in a bound, e.g. `?Trait` or `?const Trait`.
513+
///
514+
/// If no modifiers are present, this does not consume any tokens.
515+
///
516+
/// ```
517+
/// TY_BOUND_MODIFIERS = "?" ["const" ["?"]]
518+
/// ```
519+
fn parse_ty_bound_modifiers(&mut self) -> BoundModifiers {
520+
if !self.eat(&token::Question) {
521+
return BoundModifiers { maybe: None, maybe_const: None };
522+
}
523+
524+
// `? ...`
525+
let first_question = self.prev_span;
526+
if !self.eat_keyword(kw::Const) {
527+
return BoundModifiers { maybe: Some(first_question), maybe_const: None };
528+
}
529+
530+
// `?const ...`
531+
let maybe_const = first_question.to(self.prev_span);
532+
self.sess.gated_spans.gate(sym::const_trait_bound_opt_out, maybe_const);
533+
if !self.eat(&token::Question) {
534+
return BoundModifiers { maybe: None, maybe_const: Some(maybe_const) };
535+
}
536+
537+
// `?const ? ...`
538+
let second_question = self.prev_span;
539+
BoundModifiers { maybe: Some(second_question), maybe_const: Some(maybe_const) }
540+
}
541+
482542
/// Parses a type bound according to:
483543
/// ```
484544
/// TY_BOUND = TY_BOUND_NOPAREN | (TY_BOUND_NOPAREN)
485-
/// TY_BOUND_NOPAREN = [?] [for<LT_PARAM_DEFS>] SIMPLE_PATH (e.g., `?for<'a: 'b> m::Trait<'a>`)
545+
/// TY_BOUND_NOPAREN = [TY_BOUND_MODIFIERS] [for<LT_PARAM_DEFS>] SIMPLE_PATH
486546
/// ```
547+
///
548+
/// For example, this grammar accepts `?const ?for<'a: 'b> m::Trait<'a>`.
487549
fn parse_generic_ty_bound(
488550
&mut self,
489551
lo: Span,
490552
has_parens: bool,
491-
question: Option<Span>,
553+
modifiers: BoundModifiers,
492554
) -> PResult<'a, GenericBound> {
493555
let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
494556
let path = self.parse_path(PathStyle::Type)?;
495557
if has_parens {
496558
self.expect(&token::CloseDelim(token::Paren))?;
497559
}
498-
let poly_trait = PolyTraitRef::new(lifetime_defs, path, lo.to(self.prev_span));
499-
let modifier = question.map_or(TraitBoundModifier::None, |_| TraitBoundModifier::Maybe);
500-
Ok(GenericBound::Trait(poly_trait, modifier))
560+
561+
let constness = modifiers.maybe_const.map(|_| ast::Constness::NotConst);
562+
let poly_trait = PolyTraitRef::new(lifetime_defs, path, constness, lo.to(self.prev_span));
563+
Ok(GenericBound::Trait(poly_trait, modifiers.trait_bound_modifier()))
501564
}
502565

503566
/// Optionally parses `for<$generic_params>`.

0 commit comments

Comments
 (0)