Rollup merge of #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 #67792 #67794 r? @Centril
This commit is contained in:
commit
f3963873aa
37 changed files with 586 additions and 25 deletions
|
@ -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);
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
// -------------------------------------------------------------------------
|
||||
|
@ -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,
|
||||
];
|
||||
|
|
|
@ -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};
|
||||
|
@ -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,
|
||||
|
@ -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(); // `!`
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
// impl Type
|
||||
ItemKind::Impl(
|
||||
unsafety,
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
@ -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>`.
|
||||
///
|
||||
|
@ -195,7 +213,9 @@ impl<'a> Parser<'a> {
|
|||
lo: Span,
|
||||
parse_plus: bool,
|
||||
) -> PResult<'a, TyKind> {
|
||||
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
|
||||
|
@ -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) })
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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>`.
|
||||
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>`.
|
||||
|
|
|
@ -24,6 +24,24 @@ use syntax::walk_list;
|
|||
|
||||
use rustc_error_codes::*;
|
||||
|
||||
/// A syntactic context that disallows certain kinds of bounds (e.g., `?Trait` or `?const Trait`).
|
||||
#[derive(Clone, Copy)]
|
||||
enum BoundContext {
|
||||
ImplTrait,
|
||||
TraitBounds,
|
||||
TraitObject,
|
||||
}
|
||||
|
||||
impl BoundContext {
|
||||
fn description(&self) -> &'static str {
|
||||
match self {
|
||||
Self::ImplTrait => "`impl Trait`",
|
||||
Self::TraitBounds => "supertraits",
|
||||
Self::TraitObject => "trait objects",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AstValidator<'a> {
|
||||
session: &'a Session,
|
||||
has_proc_macro_decls: bool,
|
||||
|
@ -33,6 +51,12 @@ struct AstValidator<'a> {
|
|||
/// e.g., `impl Iterator<Item = impl Debug>`.
|
||||
outer_impl_trait: Option<Span>,
|
||||
|
||||
/// Keeps track of the `BoundContext` as we recurse.
|
||||
///
|
||||
/// This is used to forbid `?const Trait` bounds in, e.g.,
|
||||
/// `impl Iterator<Item = Box<dyn ?const Trait>`.
|
||||
bound_context: Option<BoundContext>,
|
||||
|
||||
/// Used to ban `impl Trait` in path projections like `<impl Iterator>::Item`
|
||||
/// or `Foo::Bar<impl Trait>`
|
||||
is_impl_trait_banned: bool,
|
||||
|
@ -59,10 +83,20 @@ impl<'a> AstValidator<'a> {
|
|||
|
||||
fn with_impl_trait(&mut self, outer: Option<Span>, f: impl FnOnce(&mut Self)) {
|
||||
let old = mem::replace(&mut self.outer_impl_trait, outer);
|
||||
f(self);
|
||||
if outer.is_some() {
|
||||
self.with_bound_context(BoundContext::ImplTrait, |this| f(this));
|
||||
} else {
|
||||
f(self)
|
||||
}
|
||||
self.outer_impl_trait = old;
|
||||
}
|
||||
|
||||
fn with_bound_context(&mut self, ctx: BoundContext, f: impl FnOnce(&mut Self)) {
|
||||
let old = self.bound_context.replace(ctx);
|
||||
f(self);
|
||||
self.bound_context = old;
|
||||
}
|
||||
|
||||
fn visit_assoc_ty_constraint_from_generic_args(&mut self, constraint: &'a AssocTyConstraint) {
|
||||
match constraint.kind {
|
||||
AssocTyConstraintKind::Equality { .. } => {}
|
||||
|
@ -84,6 +118,9 @@ impl<'a> AstValidator<'a> {
|
|||
TyKind::ImplTrait(..) => {
|
||||
self.with_impl_trait(Some(t.span), |this| visit::walk_ty(this, t))
|
||||
}
|
||||
TyKind::TraitObject(..) => {
|
||||
self.with_bound_context(BoundContext::TraitObject, |this| visit::walk_ty(this, t));
|
||||
}
|
||||
TyKind::Path(ref qself, ref path) => {
|
||||
// We allow these:
|
||||
// - `Option<impl Trait>`
|
||||
|
@ -192,6 +229,7 @@ impl<'a> AstValidator<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME(ecstaticmorse): Instead, use `bound_context` to check this in `visit_param_bound`.
|
||||
fn no_questions_in_bounds(&self, bounds: &GenericBounds, where_: &str, is_trait: bool) {
|
||||
for bound in bounds {
|
||||
if let GenericBound::Trait(ref poly, TraitBoundModifier::Maybe) = *bound {
|
||||
|
@ -697,6 +735,18 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
|||
}
|
||||
}
|
||||
self.no_questions_in_bounds(bounds, "supertraits", true);
|
||||
|
||||
// Equivalent of `visit::walk_item` for `ItemKind::Trait` that inserts a bound
|
||||
// context for the supertraits.
|
||||
self.visit_vis(&item.vis);
|
||||
self.visit_ident(item.ident);
|
||||
self.visit_generics(generics);
|
||||
self.with_bound_context(BoundContext::TraitBounds, |this| {
|
||||
walk_list!(this, visit_param_bound, bounds);
|
||||
});
|
||||
walk_list!(self, visit_trait_item, trait_items);
|
||||
walk_list!(self, visit_attribute, &item.attrs);
|
||||
return;
|
||||
}
|
||||
ItemKind::Mod(_) => {
|
||||
// Ensure that `path` attributes on modules are recorded as used (cf. issue #35584).
|
||||
|
@ -841,6 +891,29 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
|||
visit::walk_generic_param(self, param);
|
||||
}
|
||||
|
||||
fn visit_param_bound(&mut self, bound: &'a GenericBound) {
|
||||
if let GenericBound::Trait(poly, maybe_bound) = bound {
|
||||
match poly.trait_ref.constness {
|
||||
Some(Constness::NotConst) => {
|
||||
if *maybe_bound == TraitBoundModifier::Maybe {
|
||||
self.err_handler()
|
||||
.span_err(bound.span(), "`?const` and `?` are mutually exclusive");
|
||||
}
|
||||
|
||||
if let Some(ctx) = self.bound_context {
|
||||
let msg = format!("`?const` is not permitted in {}", ctx.description());
|
||||
self.err_handler().span_err(bound.span(), &msg);
|
||||
}
|
||||
}
|
||||
|
||||
Some(Constness::Const) => bug!("Parser should reject bare `const` on bounds"),
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
visit::walk_param_bound(self, bound)
|
||||
}
|
||||
|
||||
fn visit_pat(&mut self, pat: &'a Pat) {
|
||||
match pat.kind {
|
||||
PatKind::Lit(ref expr) => {
|
||||
|
@ -949,6 +1022,7 @@ pub fn check_crate(session: &Session, krate: &Crate, lints: &mut lint::LintBuffe
|
|||
session,
|
||||
has_proc_macro_decls: false,
|
||||
outer_impl_trait: None,
|
||||
bound_context: None,
|
||||
is_impl_trait_banned: false,
|
||||
is_assoc_ty_bound_banned: false,
|
||||
lint_buffer: lints,
|
||||
|
|
|
@ -219,6 +219,8 @@ symbols! {
|
|||
const_raw_ptr_deref,
|
||||
const_raw_ptr_to_usize_cast,
|
||||
const_transmute,
|
||||
const_trait_bound_opt_out,
|
||||
const_trait_impl,
|
||||
contents,
|
||||
context,
|
||||
convert,
|
||||
|
|
|
@ -1033,7 +1033,7 @@ impl Expr {
|
|||
pub fn to_bound(&self) -> Option<GenericBound> {
|
||||
match &self.kind {
|
||||
ExprKind::Path(None, path) => Some(GenericBound::Trait(
|
||||
PolyTraitRef::new(Vec::new(), path.clone(), self.span),
|
||||
PolyTraitRef::new(Vec::new(), path.clone(), None, self.span),
|
||||
TraitBoundModifier::None,
|
||||
)),
|
||||
_ => None,
|
||||
|
@ -2376,6 +2376,15 @@ pub enum AttrKind {
|
|||
pub struct TraitRef {
|
||||
pub path: Path,
|
||||
pub ref_id: NodeId,
|
||||
|
||||
/// The `const` modifier, if any, that appears before this trait.
|
||||
///
|
||||
/// | | `constness` |
|
||||
/// |----------------|-----------------------------|
|
||||
/// | `Trait` | `None` |
|
||||
/// | `const Trait` | `Some(Constness::Const)` |
|
||||
/// | `?const Trait` | `Some(Constness::NotConst)` |
|
||||
pub constness: Option<Constness>,
|
||||
}
|
||||
|
||||
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
|
||||
|
@ -2390,10 +2399,15 @@ pub struct PolyTraitRef {
|
|||
}
|
||||
|
||||
impl PolyTraitRef {
|
||||
pub fn new(generic_params: Vec<GenericParam>, path: Path, span: Span) -> Self {
|
||||
pub fn new(
|
||||
generic_params: Vec<GenericParam>,
|
||||
path: Path,
|
||||
constness: Option<Constness>,
|
||||
span: Span,
|
||||
) -> Self {
|
||||
PolyTraitRef {
|
||||
bound_generic_params: generic_params,
|
||||
trait_ref: TraitRef { path, ref_id: DUMMY_NODE_ID },
|
||||
trait_ref: TraitRef { path, constness, ref_id: DUMMY_NODE_ID },
|
||||
span,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -909,6 +909,8 @@ pub fn check_crate(
|
|||
gate_all!(or_patterns, "or-patterns syntax is experimental");
|
||||
gate_all!(const_extern_fn, "`const extern fn` definitions are unstable");
|
||||
gate_all!(raw_ref_op, "raw address of syntax is experimental");
|
||||
gate_all!(const_trait_bound_opt_out, "`?const` on trait bounds is experimental");
|
||||
gate_all!(const_trait_impl, "const trait impls are experimental");
|
||||
|
||||
// All uses of `gate_all!` below this point were added in #65742,
|
||||
// and subsequently disabled (with the non-early gating readded).
|
||||
|
|
|
@ -838,7 +838,8 @@ pub fn noop_visit_variant_data<T: MutVisitor>(vdata: &mut VariantData, vis: &mut
|
|||
}
|
||||
}
|
||||
|
||||
pub fn noop_visit_trait_ref<T: MutVisitor>(TraitRef { path, ref_id }: &mut TraitRef, vis: &mut T) {
|
||||
pub fn noop_visit_trait_ref<T: MutVisitor>(tr: &mut TraitRef, vis: &mut T) {
|
||||
let TraitRef { path, ref_id, constness: _ } = tr;
|
||||
vis.visit_path(path);
|
||||
vis.visit_id(ref_id);
|
||||
}
|
||||
|
|
|
@ -8,6 +8,11 @@ struct S<
|
|||
T: ?for<'a> Trait, // OK
|
||||
T: Tr +, // OK
|
||||
T: ?'a, //~ ERROR `?` may only modify trait bounds, not lifetime bounds
|
||||
|
||||
T: ?const Tr, // OK
|
||||
T: ?const ?Tr, // OK
|
||||
T: ?const Tr + 'a, // OK
|
||||
T: ?const 'a, //~ ERROR `?const` may only modify trait bounds, not lifetime bounds
|
||||
>;
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -4,5 +4,11 @@ error: `?` may only modify trait bounds, not lifetime bounds
|
|||
LL | T: ?'a,
|
||||
| ^
|
||||
|
||||
error: aborting due to previous error
|
||||
error: `?const` may only modify trait bounds, not lifetime bounds
|
||||
--> $DIR/bounds-type.rs:15:8
|
||||
|
|
||||
LL | T: ?const 'a,
|
||||
| ^^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
error: `?const` on trait bounds is not yet implemented
|
||||
--> $DIR/feature-gate.rs:11:29
|
||||
|
|
||||
LL | const fn get_assoc_const<S: ?const T>() -> i32 { <S as T>::CONST }
|
||||
| ^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
// revisions: stock gated
|
||||
// gate-test-const_trait_bound_opt_out
|
||||
|
||||
#![cfg_attr(gated, feature(const_trait_bound_opt_out))]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait T {
|
||||
const CONST: i32;
|
||||
}
|
||||
|
||||
const fn get_assoc_const<S: ?const T>() -> i32 { <S as T>::CONST }
|
||||
//[stock]~^ ERROR `?const` on trait bounds is experimental
|
||||
//[stock,gated]~^^ ERROR `?const` on trait bounds is not yet implemented
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,18 @@
|
|||
error[E0658]: `?const` on trait bounds is experimental
|
||||
--> $DIR/feature-gate.rs:11:29
|
||||
|
|
||||
LL | const fn get_assoc_const<S: ?const T>() -> i32 { <S as T>::CONST }
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: for more information, see https://github.com/rust-lang/rust/issues/67794
|
||||
= help: add `#![feature(const_trait_bound_opt_out)]` to the crate attributes to enable
|
||||
|
||||
error: `?const` on trait bounds is not yet implemented
|
||||
--> $DIR/feature-gate.rs:11:29
|
||||
|
|
||||
LL | const fn get_assoc_const<S: ?const T>() -> i32 { <S as T>::CONST }
|
||||
| ^^^^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
|
@ -0,0 +1,25 @@
|
|||
#![feature(const_trait_bound_opt_out)]
|
||||
#![feature(associated_type_bounds)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait T {}
|
||||
struct S;
|
||||
impl T for S {}
|
||||
|
||||
fn rpit() -> impl ?const T { S }
|
||||
//~^ ERROR `?const` is not permitted in `impl Trait`
|
||||
//~| ERROR `?const` on trait bounds is not yet implemented
|
||||
|
||||
fn apit(_: impl ?const T) {}
|
||||
//~^ ERROR `?const` is not permitted in `impl Trait`
|
||||
//~| ERROR `?const` on trait bounds is not yet implemented
|
||||
|
||||
fn rpit_assoc_bound() -> impl IntoIterator<Item: ?const T> { Some(S) }
|
||||
//~^ ERROR `?const` is not permitted in `impl Trait`
|
||||
//~| ERROR `?const` on trait bounds is not yet implemented
|
||||
|
||||
fn apit_assoc_bound(_: impl IntoIterator<Item: ?const T>) {}
|
||||
//~^ ERROR `?const` is not permitted in `impl Trait`
|
||||
//~| ERROR `?const` on trait bounds is not yet implemented
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,50 @@
|
|||
error: `?const` is not permitted in `impl Trait`
|
||||
--> $DIR/in-impl-trait.rs:9:19
|
||||
|
|
||||
LL | fn rpit() -> impl ?const T { S }
|
||||
| ^^^^^^^^
|
||||
|
||||
error: `?const` is not permitted in `impl Trait`
|
||||
--> $DIR/in-impl-trait.rs:13:17
|
||||
|
|
||||
LL | fn apit(_: impl ?const T) {}
|
||||
| ^^^^^^^^
|
||||
|
||||
error: `?const` is not permitted in `impl Trait`
|
||||
--> $DIR/in-impl-trait.rs:17:50
|
||||
|
|
||||
LL | fn rpit_assoc_bound() -> impl IntoIterator<Item: ?const T> { Some(S) }
|
||||
| ^^^^^^^^
|
||||
|
||||
error: `?const` is not permitted in `impl Trait`
|
||||
--> $DIR/in-impl-trait.rs:21:48
|
||||
|
|
||||
LL | fn apit_assoc_bound(_: impl IntoIterator<Item: ?const T>) {}
|
||||
| ^^^^^^^^
|
||||
|
||||
error: `?const` on trait bounds is not yet implemented
|
||||
--> $DIR/in-impl-trait.rs:9:19
|
||||
|
|
||||
LL | fn rpit() -> impl ?const T { S }
|
||||
| ^^^^^^^^
|
||||
|
||||
error: `?const` on trait bounds is not yet implemented
|
||||
--> $DIR/in-impl-trait.rs:13:17
|
||||
|
|
||||
LL | fn apit(_: impl ?const T) {}
|
||||
| ^^^^^^^^
|
||||
|
||||
error: `?const` on trait bounds is not yet implemented
|
||||
--> $DIR/in-impl-trait.rs:17:50
|
||||
|
|
||||
LL | fn rpit_assoc_bound() -> impl IntoIterator<Item: ?const T> { Some(S) }
|
||||
| ^^^^^^^^
|
||||
|
||||
error: `?const` on trait bounds is not yet implemented
|
||||
--> $DIR/in-impl-trait.rs:21:48
|
||||
|
|
||||
LL | fn apit_assoc_bound(_: impl IntoIterator<Item: ?const T>) {}
|
||||
| ^^^^^^^^
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
#![feature(const_trait_bound_opt_out)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Super {}
|
||||
trait T: ?const Super {}
|
||||
//~^ ERROR `?const` is not permitted in supertraits
|
||||
//~| ERROR `?const` on trait bounds is not yet implemented
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,14 @@
|
|||
error: `?const` is not permitted in supertraits
|
||||
--> $DIR/in-trait-bounds.rs:5:10
|
||||
|
|
||||
LL | trait T: ?const Super {}
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: `?const` on trait bounds is not yet implemented
|
||||
--> $DIR/in-trait-bounds.rs:5:10
|
||||
|
|
||||
LL | trait T: ?const Super {}
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
#![feature(const_trait_bound_opt_out)]
|
||||
#![allow(bare_trait_objects)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
struct S;
|
||||
trait T {}
|
||||
impl T for S {}
|
||||
|
||||
// An inherent impl for the trait object `?const T`.
|
||||
impl ?const T {}
|
||||
//~^ ERROR `?const` is not permitted in trait objects
|
||||
//~| ERROR `?const` on trait bounds is not yet implemented
|
||||
|
||||
fn trait_object() -> &'static dyn ?const T { &S }
|
||||
//~^ ERROR `?const` is not permitted in trait objects
|
||||
//~| ERROR `?const` on trait bounds is not yet implemented
|
||||
|
||||
fn trait_object_in_apit(_: impl IntoIterator<Item = Box<dyn ?const T>>) {}
|
||||
//~^ ERROR `?const` is not permitted in trait objects
|
||||
//~| ERROR `?const` on trait bounds is not yet implemented
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,38 @@
|
|||
error: `?const` is not permitted in trait objects
|
||||
--> $DIR/in-trait-object.rs:10:6
|
||||
|
|
||||
LL | impl ?const T {}
|
||||
| ^^^^^^^^
|
||||
|
||||
error: `?const` is not permitted in trait objects
|
||||
--> $DIR/in-trait-object.rs:14:35
|
||||
|
|
||||
LL | fn trait_object() -> &'static dyn ?const T { &S }
|
||||
| ^^^^^^^^
|
||||
|
||||
error: `?const` is not permitted in trait objects
|
||||
--> $DIR/in-trait-object.rs:18:61
|
||||
|
|
||||
LL | fn trait_object_in_apit(_: impl IntoIterator<Item = Box<dyn ?const T>>) {}
|
||||
| ^^^^^^^^
|
||||
|
||||
error: `?const` on trait bounds is not yet implemented
|
||||
--> $DIR/in-trait-object.rs:10:6
|
||||
|
|
||||
LL | impl ?const T {}
|
||||
| ^^^^^^^^
|
||||
|
||||
error: `?const` on trait bounds is not yet implemented
|
||||
--> $DIR/in-trait-object.rs:14:35
|
||||
|
|
||||
LL | fn trait_object() -> &'static dyn ?const T { &S }
|
||||
| ^^^^^^^^
|
||||
|
||||
error: `?const` on trait bounds is not yet implemented
|
||||
--> $DIR/in-trait-object.rs:18:61
|
||||
|
|
||||
LL | fn trait_object_in_apit(_: impl IntoIterator<Item = Box<dyn ?const T>>) {}
|
||||
| ^^^^^^^^
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
// compile-flags: -Z parse-only
|
||||
|
||||
#![feature(const_trait_bound_opt_out)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
struct S<T: ?const ?const Tr>;
|
||||
//~^ ERROR expected identifier, found keyword `const`
|
||||
//~| ERROR expected one of `(`, `+`, `,`, `::`, `<`, `=`, or `>`
|
|
@ -0,0 +1,14 @@
|
|||
error: expected identifier, found keyword `const`
|
||||
--> $DIR/opt-out-twice.rs:6:21
|
||||
|
|
||||
LL | struct S<T: ?const ?const Tr>;
|
||||
| ^^^^^ expected identifier, found keyword
|
||||
|
||||
error: expected one of `(`, `+`, `,`, `::`, `<`, `=`, or `>`, found `Tr`
|
||||
--> $DIR/opt-out-twice.rs:6:27
|
||||
|
|
||||
LL | struct S<T: ?const ?const Tr>;
|
||||
| ^^ expected one of 7 possible tokens
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// compile-flags: -Z parse-only
|
||||
// check-pass
|
||||
|
||||
#![feature(const_trait_bound_opt_out)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
struct S<
|
||||
T: ?const ?for<'a> Tr<'a> + 'static + ?const std::ops::Add,
|
||||
T: ?const ?for<'a: 'b> m::Trait<'a>,
|
||||
>;
|
|
@ -0,0 +1,8 @@
|
|||
#![feature(const_trait_bound_opt_out)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
struct S<T: ?const ?Sized>(std::marker::PhantomData<T>);
|
||||
//~^ ERROR `?const` and `?` are mutually exclusive
|
||||
//~| ERROR `?const` on trait bounds is not yet implemented
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,14 @@
|
|||
error: `?const` and `?` are mutually exclusive
|
||||
--> $DIR/with-maybe-sized.rs:4:13
|
||||
|
|
||||
LL | struct S<T: ?const ?Sized>(std::marker::PhantomData<T>);
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: `?const` on trait bounds is not yet implemented
|
||||
--> $DIR/with-maybe-sized.rs:4:13
|
||||
|
|
||||
LL | struct S<T: ?const ?Sized>(std::marker::PhantomData<T>);
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
// compile-flags: -Z parse-only
|
||||
|
||||
#![feature(const_trait_bound_opt_out)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
struct S<T: const Tr>;
|
||||
//~^ ERROR expected one of `!`, `(`, `,`, `=`, `>`, `?`, `for`, lifetime, or path
|
|
@ -0,0 +1,8 @@
|
|||
error: expected one of `!`, `(`, `,`, `=`, `>`, `?`, `for`, lifetime, or path, found keyword `const`
|
||||
--> $DIR/without-question-mark.rs:6:13
|
||||
|
|
||||
LL | struct S<T: const Tr>;
|
||||
| ^^^^^ expected one of 9 possible tokens
|
||||
|
||||
error: aborting due to previous error
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
error: const trait impls are not yet implemented
|
||||
--> $DIR/feature-gate.rs:9:1
|
||||
|
|
||||
LL | impl const T for S {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
13
src/test/ui/rfc-2632-const-trait-impl/feature-gate.rs
Normal file
13
src/test/ui/rfc-2632-const-trait-impl/feature-gate.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
// revisions: stock gated
|
||||
// gate-test-const_trait_impl
|
||||
|
||||
#![cfg_attr(gated, feature(const_trait_impl))]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
struct S;
|
||||
trait T {}
|
||||
impl const T for S {}
|
||||
//[stock]~^ ERROR const trait impls are experimental
|
||||
//[stock,gated]~^^ ERROR const trait impls are not yet implemented
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,18 @@
|
|||
error[E0658]: const trait impls are experimental
|
||||
--> $DIR/feature-gate.rs:9:6
|
||||
|
|
||||
LL | impl const T for S {}
|
||||
| ^^^^^
|
||||
|
|
||||
= note: for more information, see https://github.com/rust-lang/rust/issues/67792
|
||||
= help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
|
||||
|
||||
error: const trait impls are not yet implemented
|
||||
--> $DIR/feature-gate.rs:9:1
|
||||
|
|
||||
LL | impl const T for S {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
11
src/test/ui/rfc-2632-const-trait-impl/impl-opt-out-trait.rs
Normal file
11
src/test/ui/rfc-2632-const-trait-impl/impl-opt-out-trait.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
#![feature(const_trait_bound_opt_out)]
|
||||
#![feature(const_trait_impl)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
struct S;
|
||||
trait T {}
|
||||
|
||||
impl ?const T for S {}
|
||||
//~^ ERROR expected a trait, found type
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,8 @@
|
|||
error: expected a trait, found type
|
||||
--> $DIR/impl-opt-out-trait.rs:8:6
|
||||
|
|
||||
LL | impl ?const T for S {}
|
||||
| ^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
14
src/test/ui/rfc-2632-const-trait-impl/inherent-impl.rs
Normal file
14
src/test/ui/rfc-2632-const-trait-impl/inherent-impl.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
// compile-flags: -Z parse-only
|
||||
|
||||
#![feature(const_trait_impl)]
|
||||
#![feature(const_trait_bound_opt_out)]
|
||||
#![allow(incomplete_features)]
|
||||
#![allow(bare_trait_objects)]
|
||||
|
||||
struct S;
|
||||
trait T {}
|
||||
|
||||
impl const T {}
|
||||
//~^ ERROR `const` cannot modify an inherent impl
|
||||
|
||||
fn main() {}
|
10
src/test/ui/rfc-2632-const-trait-impl/inherent-impl.stderr
Normal file
10
src/test/ui/rfc-2632-const-trait-impl/inherent-impl.stderr
Normal file
|
@ -0,0 +1,10 @@
|
|||
error: `const` cannot modify an inherent impl
|
||||
--> $DIR/inherent-impl.rs:11:6
|
||||
|
|
||||
LL | impl const T {}
|
||||
| ^^^^^
|
||||
|
|
||||
= help: only a trait impl can be `const`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
9
src/test/ui/rfc-2632-const-trait-impl/syntax.rs
Normal file
9
src/test/ui/rfc-2632-const-trait-impl/syntax.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
// compile-flags: -Z parse-only
|
||||
// check-pass
|
||||
|
||||
#![feature(const_trait_bound_opt_out)]
|
||||
#![feature(const_trait_impl)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
// For now, this parses since an error does not occur until AST lowering.
|
||||
impl ?const T {}
|
Loading…
Add table
Reference in a new issue