diff --git a/src/librustc_ast_lowering/item.rs b/src/librustc_ast_lowering/item.rs index a5892a22d9df..7c95b2a86c68 100644 --- a/src/librustc_ast_lowering/item.rs +++ b/src/librustc_ast_lowering/item.rs @@ -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); diff --git a/src/librustc_ast_lowering/lib.rs b/src/librustc_ast_lowering/lib.rs index 385153b62ce8..73d1e4972582 100644 --- a/src/librustc_ast_lowering/lib.rs +++ b/src/librustc_ast_lowering/lib.rs @@ -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(), diff --git a/src/librustc_expand/build.rs b/src/librustc_expand/build.rs index 11f94ab2e627..bd3d6b589d00 100644 --- a/src/librustc_expand/build.rs +++ b/src/librustc_expand/build.rs @@ -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 { diff --git a/src/librustc_feature/active.rs b/src/librustc_feature/active.rs index 8cb1684491bb..6a15cc5cb0fc 100644 --- a/src/librustc_feature/active.rs +++ b/src/librustc_feature/active.rs @@ -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, ]; diff --git a/src/librustc_parse/parser/item.rs b/src/librustc_parse/parser/item.rs index 918e826fc26b..b209e5a78266 100644 --- a/src/librustc_parse/parser/item.rs +++ b/src/librustc_parse/parser/item.rs @@ -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, diff --git a/src/librustc_parse/parser/ty.rs b/src/librustc_parse/parser/ty.rs index f96c82a1ab37..ea14aa278ac2 100644 --- a/src/librustc_parse/parser/ty.rs +++ b/src/librustc_parse/parser/ty.rs @@ -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, + + /// `?const Trait`. + maybe_const: Option, +} + +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`, /// `IDENT<::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, ) -> 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) { - 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] SIMPLE_PATH (e.g., `?for<'a: 'b> m::Trait<'a>`) + /// TY_BOUND_NOPAREN = [TY_BOUND_MODIFIERS] [for] 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, + 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>`. diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 5000cd5f52f6..724d717304c2 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -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`. outer_impl_trait: Option, + /// Keeps track of the `BoundContext` as we recurse. + /// + /// This is used to forbid `?const Trait` bounds in, e.g., + /// `impl Iterator`. + bound_context: Option, + /// Used to ban `impl Trait` in path projections like `::Item` /// or `Foo::Bar` is_impl_trait_banned: bool, @@ -59,10 +83,20 @@ impl<'a> AstValidator<'a> { fn with_impl_trait(&mut self, outer: Option, 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` @@ -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, diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs index 40abc8b2179b..d9f4b72560ce 100644 --- a/src/librustc_span/symbol.rs +++ b/src/librustc_span/symbol.rs @@ -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, diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 47070261385a..1d3bb7d87686 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1033,7 +1033,7 @@ impl Expr { pub fn to_bound(&self) -> Option { 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, } #[derive(Clone, RustcEncodable, RustcDecodable, Debug)] @@ -2390,10 +2399,15 @@ pub struct PolyTraitRef { } impl PolyTraitRef { - pub fn new(generic_params: Vec, path: Path, span: Span) -> Self { + pub fn new( + generic_params: Vec, + path: Path, + constness: Option, + 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, } } diff --git a/src/libsyntax/feature_gate/check.rs b/src/libsyntax/feature_gate/check.rs index 26545bfa61b6..52eb20d320f7 100644 --- a/src/libsyntax/feature_gate/check.rs +++ b/src/libsyntax/feature_gate/check.rs @@ -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). diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs index 1413f1566d04..264ba25cedec 100644 --- a/src/libsyntax/mut_visit.rs +++ b/src/libsyntax/mut_visit.rs @@ -838,7 +838,8 @@ pub fn noop_visit_variant_data(vdata: &mut VariantData, vis: &mut } } -pub fn noop_visit_trait_ref(TraitRef { path, ref_id }: &mut TraitRef, vis: &mut T) { +pub fn noop_visit_trait_ref(tr: &mut TraitRef, vis: &mut T) { + let TraitRef { path, ref_id, constness: _ } = tr; vis.visit_path(path); vis.visit_id(ref_id); } diff --git a/src/test/ui/parser/bounds-type.rs b/src/test/ui/parser/bounds-type.rs index 9122cb49ebc1..7a187a0518af 100644 --- a/src/test/ui/parser/bounds-type.rs +++ b/src/test/ui/parser/bounds-type.rs @@ -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() {} diff --git a/src/test/ui/parser/bounds-type.stderr b/src/test/ui/parser/bounds-type.stderr index 0b714e40a101..9a1f2ed39824 100644 --- a/src/test/ui/parser/bounds-type.stderr +++ b/src/test/ui/parser/bounds-type.stderr @@ -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 diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.gated.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.gated.stderr new file mode 100644 index 000000000000..0bf337ad08db --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.gated.stderr @@ -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() -> i32 { ::CONST } + | ^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.rs b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.rs new file mode 100644 index 000000000000..cf1ed30da0fc --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.rs @@ -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() -> i32 { ::CONST } +//[stock]~^ ERROR `?const` on trait bounds is experimental +//[stock,gated]~^^ ERROR `?const` on trait bounds is not yet implemented + +fn main() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.stock.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.stock.stderr new file mode 100644 index 000000000000..64388004b5b7 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.stock.stderr @@ -0,0 +1,18 @@ +error[E0658]: `?const` on trait bounds is experimental + --> $DIR/feature-gate.rs:11:29 + | +LL | const fn get_assoc_const() -> i32 { ::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() -> i32 { ::CONST } + | ^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-impl-trait.rs b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-impl-trait.rs new file mode 100644 index 000000000000..e4e6bedd9374 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-impl-trait.rs @@ -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 { 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) {} +//~^ ERROR `?const` is not permitted in `impl Trait` +//~| ERROR `?const` on trait bounds is not yet implemented + +fn main() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-impl-trait.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-impl-trait.stderr new file mode 100644 index 000000000000..f4abd4b714e8 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-impl-trait.stderr @@ -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 { Some(S) } + | ^^^^^^^^ + +error: `?const` is not permitted in `impl Trait` + --> $DIR/in-impl-trait.rs:21:48 + | +LL | fn apit_assoc_bound(_: impl IntoIterator) {} + | ^^^^^^^^ + +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 { 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) {} + | ^^^^^^^^ + +error: aborting due to 8 previous errors + diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-bounds.rs b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-bounds.rs new file mode 100644 index 000000000000..4523b46bc51f --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-bounds.rs @@ -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() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-bounds.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-bounds.stderr new file mode 100644 index 000000000000..8003361be7d2 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-bounds.stderr @@ -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 + diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-object.rs b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-object.rs new file mode 100644 index 000000000000..6cfca7154867 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-object.rs @@ -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>) {} +//~^ ERROR `?const` is not permitted in trait objects +//~| ERROR `?const` on trait bounds is not yet implemented + +fn main() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-object.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-object.stderr new file mode 100644 index 000000000000..c059f1690225 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-object.stderr @@ -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>) {} + | ^^^^^^^^ + +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>) {} + | ^^^^^^^^ + +error: aborting due to 6 previous errors + diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/opt-out-twice.rs b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/opt-out-twice.rs new file mode 100644 index 000000000000..01e941a8fba4 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/opt-out-twice.rs @@ -0,0 +1,8 @@ +// compile-flags: -Z parse-only + +#![feature(const_trait_bound_opt_out)] +#![allow(incomplete_features)] + +struct S; +//~^ ERROR expected identifier, found keyword `const` +//~| ERROR expected one of `(`, `+`, `,`, `::`, `<`, `=`, or `>` diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/opt-out-twice.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/opt-out-twice.stderr new file mode 100644 index 000000000000..f7924b3f24db --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/opt-out-twice.stderr @@ -0,0 +1,14 @@ +error: expected identifier, found keyword `const` + --> $DIR/opt-out-twice.rs:6:21 + | +LL | struct S; + | ^^^^^ expected identifier, found keyword + +error: expected one of `(`, `+`, `,`, `::`, `<`, `=`, or `>`, found `Tr` + --> $DIR/opt-out-twice.rs:6:27 + | +LL | struct S; + | ^^ expected one of 7 possible tokens + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/syntax.rs b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/syntax.rs new file mode 100644 index 000000000000..a0d9610bbb5e --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/syntax.rs @@ -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>, +>; diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/with-maybe-sized.rs b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/with-maybe-sized.rs new file mode 100644 index 000000000000..425784f4e432 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/with-maybe-sized.rs @@ -0,0 +1,8 @@ +#![feature(const_trait_bound_opt_out)] +#![allow(incomplete_features)] + +struct S(std::marker::PhantomData); +//~^ ERROR `?const` and `?` are mutually exclusive +//~| ERROR `?const` on trait bounds is not yet implemented + +fn main() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/with-maybe-sized.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/with-maybe-sized.stderr new file mode 100644 index 000000000000..44f6d464ae6a --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/with-maybe-sized.stderr @@ -0,0 +1,14 @@ +error: `?const` and `?` are mutually exclusive + --> $DIR/with-maybe-sized.rs:4:13 + | +LL | struct S(std::marker::PhantomData); + | ^^^^^^^^^^^^^ + +error: `?const` on trait bounds is not yet implemented + --> $DIR/with-maybe-sized.rs:4:13 + | +LL | struct S(std::marker::PhantomData); + | ^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/without-question-mark.rs b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/without-question-mark.rs new file mode 100644 index 000000000000..b904a2eec0dd --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/without-question-mark.rs @@ -0,0 +1,7 @@ +// compile-flags: -Z parse-only + +#![feature(const_trait_bound_opt_out)] +#![allow(incomplete_features)] + +struct S; +//~^ ERROR expected one of `!`, `(`, `,`, `=`, `>`, `?`, `for`, lifetime, or path diff --git a/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/without-question-mark.stderr b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/without-question-mark.stderr new file mode 100644 index 000000000000..0dbca952c037 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/without-question-mark.stderr @@ -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; + | ^^^^^ expected one of 9 possible tokens + +error: aborting due to previous error + diff --git a/src/test/ui/rfc-2632-const-trait-impl/feature-gate.gated.stderr b/src/test/ui/rfc-2632-const-trait-impl/feature-gate.gated.stderr new file mode 100644 index 000000000000..b196f9ef5738 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/feature-gate.gated.stderr @@ -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 + diff --git a/src/test/ui/rfc-2632-const-trait-impl/feature-gate.rs b/src/test/ui/rfc-2632-const-trait-impl/feature-gate.rs new file mode 100644 index 000000000000..49b6c0926c50 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/feature-gate.rs @@ -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() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/feature-gate.stock.stderr b/src/test/ui/rfc-2632-const-trait-impl/feature-gate.stock.stderr new file mode 100644 index 000000000000..093946f859ac --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/feature-gate.stock.stderr @@ -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`. diff --git a/src/test/ui/rfc-2632-const-trait-impl/impl-opt-out-trait.rs b/src/test/ui/rfc-2632-const-trait-impl/impl-opt-out-trait.rs new file mode 100644 index 000000000000..98d3a220d867 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/impl-opt-out-trait.rs @@ -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() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/impl-opt-out-trait.stderr b/src/test/ui/rfc-2632-const-trait-impl/impl-opt-out-trait.stderr new file mode 100644 index 000000000000..8f923efb093f --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/impl-opt-out-trait.stderr @@ -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 + diff --git a/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.rs b/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.rs new file mode 100644 index 000000000000..9cffe75addd6 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.rs @@ -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() {} diff --git a/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.stderr b/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.stderr new file mode 100644 index 000000000000..1d2455765595 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/inherent-impl.stderr @@ -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 + diff --git a/src/test/ui/rfc-2632-const-trait-impl/syntax.rs b/src/test/ui/rfc-2632-const-trait-impl/syntax.rs new file mode 100644 index 000000000000..354d48d630f7 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/syntax.rs @@ -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 {}