Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse the syntax described in RFC 2632 #67820

Merged
merged 13 commits into from
Jan 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/librustc_ast_lowering/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ impl<'a, 'lowering, 'hir> Visitor<'a> for ItemLowerer<'a, 'lowering, 'hir> {
self.lctx.with_parent_item_lifetime_defs(hir_id, |this| {
let this = &mut ItemLowerer { lctx: this };
if let ItemKind::Impl(.., ref opt_trait_ref, _, _) = item.kind {
if opt_trait_ref.as_ref().map(|tr| tr.constness.is_some()).unwrap_or(false) {
this.lctx
.diagnostic()
.span_err(item.span, "const trait impls are not yet implemented");
}

this.with_trait_impl_ref(opt_trait_ref, |this| visit::walk_item(this, item));
} else {
visit::walk_item(this, item);
Expand Down
4 changes: 4 additions & 0 deletions src/librustc_ast_lowering/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2579,6 +2579,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
p: &PolyTraitRef,
mut itctx: ImplTraitContext<'_, 'hir>,
) -> hir::PolyTraitRef<'hir> {
if p.trait_ref.constness.is_some() {
self.diagnostic().span_err(p.span, "`?const` on trait bounds is not yet implemented");
}

let bound_generic_params = self.lower_generic_params(
&p.bound_generic_params,
&NodeMap::default(),
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_expand/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ impl<'a> ExtCtxt<'a> {
}

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

pub fn poly_trait_ref(&self, span: Span, path: ast::Path) -> ast::PolyTraitRef {
Expand Down
8 changes: 8 additions & 0 deletions src/librustc_feature/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,12 @@ declare_features! (
/// For example, you can write `x @ Some(y)`.
(active, bindings_after_at, "1.41.0", Some(65490), None),

/// Allows `impl const Trait for T` syntax.
(active, const_trait_impl, "1.42.0", Some(67792), None),

/// Allows `T: ?const Trait` syntax in bounds.
(active, const_trait_bound_opt_out, "1.42.0", Some(67794), None),

// -------------------------------------------------------------------------
// feature-group-end: actual feature gates
// -------------------------------------------------------------------------
Expand All @@ -559,4 +565,6 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[
sym::or_patterns,
sym::let_chains,
sym::raw_dylib,
sym::const_trait_impl,
sym::const_trait_bound_opt_out,
];
25 changes: 21 additions & 4 deletions src/librustc_parse/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::maybe_whole;

use rustc_error_codes::*;
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, PResult, StashKey};
use rustc_span::source_map::{self, respan, Span};
use rustc_span::source_map::{self, respan, Span, Spanned};
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::BytePos;
use syntax::ast::{self, AttrKind, AttrStyle, AttrVec, Attribute, Ident, DUMMY_NODE_ID};
Expand Down Expand Up @@ -542,10 +542,11 @@ impl<'a> Parser<'a> {
/// impl<'a, T> TYPE { /* impl items */ }
/// impl<'a, T> TRAIT for TYPE { /* impl items */ }
/// impl<'a, T> !TRAIT for TYPE { /* impl items */ }
/// impl<'a, T> const TRAIT for TYPE { /* impl items */ }
///
/// We actually parse slightly more relaxed grammar for better error reporting and recovery.
/// `impl` GENERICS `!`? TYPE `for`? (TYPE | `..`) (`where` PREDICATES)? `{` BODY `}`
/// `impl` GENERICS `!`? TYPE (`where` PREDICATES)? `{` BODY `}`
/// `impl` GENERICS `const`? `!`? TYPE `for`? (TYPE | `..`) (`where` PREDICATES)? `{` BODY `}`
/// `impl` GENERICS `const`? `!`? TYPE (`where` PREDICATES)? `{` BODY `}`
fn parse_item_impl(
&mut self,
unsafety: Unsafety,
Expand All @@ -558,6 +559,14 @@ impl<'a> Parser<'a> {
Generics::default()
};

let constness = if self.eat_keyword(kw::Const) {
let span = self.prev_span;
self.sess.gated_spans.gate(sym::const_trait_impl, span);
Some(respan(span, Constness::Const))
} else {
None
};

// Disambiguate `impl !Trait for Type { ... }` and `impl ! { ... }` for the never type.
let polarity = if self.check(&token::Not) && self.look_ahead(1, |t| t.can_begin_type()) {
self.bump(); // `!`
Expand Down Expand Up @@ -618,7 +627,8 @@ impl<'a> Parser<'a> {
err_path(ty_first.span)
}
};
let trait_ref = TraitRef { path, ref_id: ty_first.id };
let constness = constness.map(|c| c.node);
let trait_ref = TraitRef { path, constness, ref_id: ty_first.id };

ItemKind::Impl(
unsafety,
Expand All @@ -631,6 +641,13 @@ impl<'a> Parser<'a> {
)
}
None => {
// Reject `impl const Type {}` here
if let Some(Spanned { node: Constness::Const, span }) = constness {
self.struct_span_err(span, "`const` cannot modify an inherent impl")
.help("only a trait impl can be `const`")
.emit();
}

oli-obk marked this conversation as resolved.
Show resolved Hide resolved
// impl Type
ItemKind::Impl(
unsafety,
Expand Down
91 changes: 77 additions & 14 deletions src/librustc_parse/parser/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
use rustc_error_codes::*;
use rustc_errors::{pluralize, struct_span_err, Applicability, PResult};
use rustc_span::source_map::Span;
use rustc_span::symbol::kw;
use rustc_span::symbol::{kw, sym};
use syntax::ast::{
self, BareFnTy, FunctionRetTy, GenericParam, Ident, Lifetime, MutTy, Ty, TyKind,
};
Expand All @@ -17,6 +17,24 @@ use syntax::ast::{Mac, Mutability};
use syntax::ptr::P;
use syntax::token::{self, Token};

/// Any `?` or `?const` modifiers that appear at the start of a bound.
struct BoundModifiers {
/// `?Trait`.
maybe: Option<Span>,

/// `?const Trait`.
maybe_const: Option<Span>,
}

impl BoundModifiers {
fn trait_bound_modifier(&self) -> TraitBoundModifier {
match self.maybe {
Some(_) => TraitBoundModifier::Maybe,
None => TraitBoundModifier::None,
}
}
}

/// Returns `true` if `IDENT t` can start a type -- `IDENT::a::b`, `IDENT<u8, u8>`,
/// `IDENT<<u8 as Trait>::AssocTy>`.
///
Expand Down Expand Up @@ -195,7 +213,9 @@ impl<'a> Parser<'a> {
lo: Span,
parse_plus: bool,
) -> PResult<'a, TyKind> {
oli-obk marked this conversation as resolved.
Show resolved Hide resolved
let poly_trait_ref = PolyTraitRef::new(generic_params, path, lo.to(self.prev_span));
assert_ne!(self.token, token::Question);

let poly_trait_ref = PolyTraitRef::new(generic_params, path, None, lo.to(self.prev_span));
let mut bounds = vec![GenericBound::Trait(poly_trait_ref, TraitBoundModifier::None)];
if parse_plus {
self.eat_plus(); // `+`, or `+=` gets split and `+` is discarded
Expand Down Expand Up @@ -421,12 +441,15 @@ impl<'a> Parser<'a> {
let has_parens = self.eat(&token::OpenDelim(token::Paren));
let inner_lo = self.token.span;
let is_negative = self.eat(&token::Not);
let question = self.eat(&token::Question).then_some(self.prev_span);

let modifiers = self.parse_ty_bound_modifiers();
let bound = if self.token.is_lifetime() {
self.parse_generic_lt_bound(lo, inner_lo, has_parens, question)?
self.error_lt_bound_with_modifiers(modifiers);
self.parse_generic_lt_bound(lo, inner_lo, has_parens)?
} else {
self.parse_generic_ty_bound(lo, has_parens, question)?
self.parse_generic_ty_bound(lo, has_parens, modifiers)?
};

Ok(if is_negative { Err(anchor_lo.to(self.prev_span)) } else { Ok(bound) })
}

Expand All @@ -439,9 +462,7 @@ impl<'a> Parser<'a> {
lo: Span,
inner_lo: Span,
has_parens: bool,
question: Option<Span>,
) -> PResult<'a, GenericBound> {
self.error_opt_out_lifetime(question);
let bound = GenericBound::Outlives(self.expect_lifetime());
if has_parens {
// FIXME(Centril): Consider not erroring here and accepting `('lt)` instead,
Expand All @@ -451,8 +472,17 @@ impl<'a> Parser<'a> {
Ok(bound)
}

fn error_opt_out_lifetime(&self, question: Option<Span>) {
if let Some(span) = question {
/// Emits an error if any trait bound modifiers were present.
fn error_lt_bound_with_modifiers(&self, modifiers: BoundModifiers) {
if let Some(span) = modifiers.maybe_const {
self.struct_span_err(
span,
"`?const` may only modify trait bounds, not lifetime bounds",
)
.emit();
}

if let Some(span) = modifiers.maybe {
self.struct_span_err(span, "`?` may only modify trait bounds, not lifetime bounds")
.emit();
}
Expand All @@ -478,25 +508,58 @@ impl<'a> Parser<'a> {
Ok(())
}

/// Parses the modifiers that may precede a trait in a bound, e.g. `?Trait` or `?const Trait`.
///
/// If no modifiers are present, this does not consume any tokens.
///
/// ```
/// TY_BOUND_MODIFIERS = "?" ["const" ["?"]]
/// ```
fn parse_ty_bound_modifiers(&mut self) -> BoundModifiers {
if !self.eat(&token::Question) {
return BoundModifiers { maybe: None, maybe_const: None };
}

// `? ...`
let first_question = self.prev_span;
if !self.eat_keyword(kw::Const) {
return BoundModifiers { maybe: Some(first_question), maybe_const: None };
}

// `?const ...`
let maybe_const = first_question.to(self.prev_span);
self.sess.gated_spans.gate(sym::const_trait_bound_opt_out, maybe_const);
if !self.eat(&token::Question) {
return BoundModifiers { maybe: None, maybe_const: Some(maybe_const) };
}

// `?const ? ...`
let second_question = self.prev_span;
BoundModifiers { maybe: Some(second_question), maybe_const: Some(maybe_const) }
}

/// Parses a type bound according to:
/// ```
/// TY_BOUND = TY_BOUND_NOPAREN | (TY_BOUND_NOPAREN)
/// TY_BOUND_NOPAREN = [?] [for<LT_PARAM_DEFS>] SIMPLE_PATH (e.g., `?for<'a: 'b> m::Trait<'a>`)
/// TY_BOUND_NOPAREN = [TY_BOUND_MODIFIERS] [for<LT_PARAM_DEFS>] SIMPLE_PATH
/// ```
///
/// For example, this grammar accepts `?const ?for<'a: 'b> m::Trait<'a>`.
Centril marked this conversation as resolved.
Show resolved Hide resolved
fn parse_generic_ty_bound(
&mut self,
lo: Span,
has_parens: bool,
question: Option<Span>,
modifiers: BoundModifiers,
) -> PResult<'a, GenericBound> {
let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
let path = self.parse_path(PathStyle::Type)?;
if has_parens {
self.expect(&token::CloseDelim(token::Paren))?;
}
let poly_trait = PolyTraitRef::new(lifetime_defs, path, lo.to(self.prev_span));
let modifier = question.map_or(TraitBoundModifier::None, |_| TraitBoundModifier::Maybe);
Ok(GenericBound::Trait(poly_trait, modifier))

let constness = modifiers.maybe_const.map(|_| ast::Constness::NotConst);
let poly_trait = PolyTraitRef::new(lifetime_defs, path, constness, lo.to(self.prev_span));
Ok(GenericBound::Trait(poly_trait, modifiers.trait_bound_modifier()))
}

/// Optionally parses `for<$generic_params>`.
Expand Down
Loading