From 63e2e44eb94c375527ddc6438479a0dd10ed4310 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Thu, 2 Jan 2020 15:45:48 -0800 Subject: [PATCH 01/13] Add `const_trait_impl` feature gate --- src/librustc_feature/active.rs | 4 ++++ src/librustc_span/symbol.rs | 1 + src/libsyntax/feature_gate/check.rs | 1 + 3 files changed, 6 insertions(+) diff --git a/src/librustc_feature/active.rs b/src/librustc_feature/active.rs index 8cb1684491bb8..77ee9f40b6c71 100644 --- a/src/librustc_feature/active.rs +++ b/src/librustc_feature/active.rs @@ -544,6 +544,9 @@ 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), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- @@ -559,4 +562,5 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[ sym::or_patterns, sym::let_chains, sym::raw_dylib, + sym::const_trait_impl, ]; diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs index 40abc8b2179b8..5ba45e0a67399 100644 --- a/src/librustc_span/symbol.rs +++ b/src/librustc_span/symbol.rs @@ -219,6 +219,7 @@ symbols! { const_raw_ptr_deref, const_raw_ptr_to_usize_cast, const_transmute, + const_trait_impl, contents, context, convert, diff --git a/src/libsyntax/feature_gate/check.rs b/src/libsyntax/feature_gate/check.rs index 26545bfa61b60..f5090bf15536d 100644 --- a/src/libsyntax/feature_gate/check.rs +++ b/src/libsyntax/feature_gate/check.rs @@ -909,6 +909,7 @@ 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_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). From 6fc41585043927c3b9b404ddc357e67ab443eb70 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Thu, 2 Jan 2020 15:46:30 -0800 Subject: [PATCH 02/13] Add `const_trait_bound_opt_out` feature gate --- src/librustc_feature/active.rs | 4 ++++ src/librustc_span/symbol.rs | 1 + src/libsyntax/feature_gate/check.rs | 1 + 3 files changed, 6 insertions(+) diff --git a/src/librustc_feature/active.rs b/src/librustc_feature/active.rs index 77ee9f40b6c71..6a15cc5cb0fce 100644 --- a/src/librustc_feature/active.rs +++ b/src/librustc_feature/active.rs @@ -547,6 +547,9 @@ declare_features! ( /// 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 // ------------------------------------------------------------------------- @@ -563,4 +566,5 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[ sym::let_chains, sym::raw_dylib, sym::const_trait_impl, + sym::const_trait_bound_opt_out, ]; diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs index 5ba45e0a67399..d9f4b72560ceb 100644 --- a/src/librustc_span/symbol.rs +++ b/src/librustc_span/symbol.rs @@ -219,6 +219,7 @@ symbols! { const_raw_ptr_deref, const_raw_ptr_to_usize_cast, const_transmute, + const_trait_bound_opt_out, const_trait_impl, contents, context, diff --git a/src/libsyntax/feature_gate/check.rs b/src/libsyntax/feature_gate/check.rs index f5090bf15536d..52eb20d320f7b 100644 --- a/src/libsyntax/feature_gate/check.rs +++ b/src/libsyntax/feature_gate/check.rs @@ -909,6 +909,7 @@ 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, From fd4a6a12136c5b5d6bce4081e95890df1fd1febd Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Thu, 2 Jan 2020 15:47:27 -0800 Subject: [PATCH 03/13] Add a `constness` field to `ast::TraitRef` This is used for both the `?const` syntax in bounds as well as the `impl const Trait` syntax. I also considered handling these separately by adding a variant of `TraitBoundModifier` and a field to `ItemKind::Impl`, but this approach was less intrusive. --- src/librustc_expand/build.rs | 2 +- src/libsyntax/ast.rs | 20 +++++++++++++++++--- src/libsyntax/mut_visit.rs | 3 ++- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/librustc_expand/build.rs b/src/librustc_expand/build.rs index 11f94ab2e6279..bd3d6b589d00a 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/libsyntax/ast.rs b/src/libsyntax/ast.rs index 47070261385a2..1d3bb7d87686c 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/mut_visit.rs b/src/libsyntax/mut_visit.rs index 1413f1566d043..264ba25cedecc 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); } From 1c3fe9de4e9b7c41cc0ba86696b4e58f9e0e36e4 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Thu, 2 Jan 2020 15:49:45 -0800 Subject: [PATCH 04/13] Parse `impl const Trait for Ty` syntax --- src/librustc_parse/parser/item.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/librustc_parse/parser/item.rs b/src/librustc_parse/parser/item.rs index 918e826fc26bf..86ab8bf57a3e3 100644 --- a/src/librustc_parse/parser/item.rs +++ b/src/librustc_parse/parser/item.rs @@ -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,13 @@ impl<'a> Parser<'a> { Generics::default() }; + let constness = if self.eat_keyword(kw::Const) { + self.sess.gated_spans.gate(sym::const_trait_impl, self.prev_span); + Some(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 +626,7 @@ impl<'a> Parser<'a> { err_path(ty_first.span) } }; - let trait_ref = TraitRef { path, ref_id: ty_first.id }; + let trait_ref = TraitRef { path, constness, ref_id: ty_first.id }; ItemKind::Impl( unsafety, From 0cf52a7dd8fbe68e85471fd9254d8e2e025a03d8 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Thu, 2 Jan 2020 15:50:18 -0800 Subject: [PATCH 05/13] Parse `?const Trait` bound syntax The grammar also handles `?const ?Trait` even though this is semantically redundant. --- src/librustc_parse/parser/ty.rs | 91 ++++++++++++++++++++++++++++----- 1 file changed, 77 insertions(+), 14 deletions(-) diff --git a/src/librustc_parse/parser/ty.rs b/src/librustc_parse/parser/ty.rs index f96c82a1ab37d..ea14aa278ac29 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>`. From b390fc4cf1e7293716cd8d80ec532df232022324 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Thu, 2 Jan 2020 16:35:07 -0800 Subject: [PATCH 06/13] Error when new syntax is lowered This means the new syntax will always fail to compile, even when the feature gate is enabled. These checks will be removed in a later PR once the implementation is done. --- src/librustc_ast_lowering/item.rs | 6 ++++++ src/librustc_ast_lowering/lib.rs | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/src/librustc_ast_lowering/item.rs b/src/librustc_ast_lowering/item.rs index a5892a22d9dfa..7c95b2a86c685 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 385153b62ce82..73d1e49725826 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(), From 31edbe9acacceb24f0e31a73e0c45f852977c663 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Fri, 3 Jan 2020 16:32:01 -0800 Subject: [PATCH 07/13] Reject `const` in inherent impls --- src/librustc_parse/parser/item.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/librustc_parse/parser/item.rs b/src/librustc_parse/parser/item.rs index 86ab8bf57a3e3..b209e5a78266a 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}; @@ -560,8 +560,9 @@ impl<'a> Parser<'a> { }; let constness = if self.eat_keyword(kw::Const) { - self.sess.gated_spans.gate(sym::const_trait_impl, self.prev_span); - Some(Constness::Const) + let span = self.prev_span; + self.sess.gated_spans.gate(sym::const_trait_impl, span); + Some(respan(span, Constness::Const)) } else { None }; @@ -626,6 +627,7 @@ impl<'a> Parser<'a> { err_path(ty_first.span) } }; + let constness = constness.map(|c| c.node); let trait_ref = TraitRef { path, constness, ref_id: ty_first.id }; ItemKind::Impl( @@ -639,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, From d843e002bb836be3164bef80d6218228aec974a8 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Sat, 4 Jan 2020 16:29:45 -0800 Subject: [PATCH 08/13] Check for `?const` in invalid contexts during AST validation --- src/librustc_passes/ast_validation.rs | 74 +++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 5000cd5f52f65..1e5e39217b7aa 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -24,6 +24,23 @@ use syntax::walk_list; use rustc_error_codes::*; +#[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 +50,11 @@ struct AstValidator<'a> { /// e.g., `impl Iterator`. outer_impl_trait: Option, + /// Tracks the context in which a bound can appear. + /// + /// This is used to forbid `?const Trait` bounds in certain contexts. + bound_context_stack: Vec>, + /// Used to ban `impl Trait` in path projections like `::Item` /// or `Foo::Bar` is_impl_trait_banned: bool, @@ -58,9 +80,21 @@ impl<'a> AstValidator<'a> { } fn with_impl_trait(&mut self, outer: Option, f: impl FnOnce(&mut Self)) { + self.bound_context_stack.push(outer.map(|_| BoundContext::ImplTrait)); let old = mem::replace(&mut self.outer_impl_trait, outer); f(self); self.outer_impl_trait = old; + self.bound_context_stack.pop(); + } + + fn with_bound_context(&mut self, ctx: Option, f: impl FnOnce(&mut Self)) { + self.bound_context_stack.push(ctx); + f(self); + self.bound_context_stack.pop(); + } + + fn innermost_bound_context(&mut self) -> Option { + self.bound_context_stack.iter().rev().find(|x| x.is_some()).copied().flatten() } fn visit_assoc_ty_constraint_from_generic_args(&mut self, constraint: &'a AssocTyConstraint) { @@ -84,6 +118,11 @@ 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(Some(BoundContext::TraitObject), |this| { + visit::walk_ty(this, t) + }); + } TyKind::Path(ref qself, ref path) => { // We allow these: // - `Option` @@ -192,6 +231,8 @@ impl<'a> AstValidator<'a> { } } + // FIXME(ecstaticmorse): Instead, use the `bound_context_stack` 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 +738,15 @@ 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_generics(generics); + self.with_bound_context(Some(BoundContext::TraitBounds), |this| { + walk_list!(this, visit_param_bound, bounds); + }); + walk_list!(self, visit_trait_item, trait_items); + 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.innermost_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_stack: Vec::new(), is_impl_trait_banned: false, is_assoc_ty_bound_banned: false, lint_buffer: lints, From 343e1570a9b460e3dda85fe87fd6819df99d7943 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Thu, 2 Jan 2020 16:31:30 -0800 Subject: [PATCH 09/13] Add tests for RFC 2632 --- src/test/ui/parser/bounds-type.rs | 5 ++ src/test/ui/parser/bounds-type.stderr | 8 ++- .../feature-gate.gated.stderr | 8 +++ .../const-trait-bound-opt-out/feature-gate.rs | 15 ++++++ .../feature-gate.stock.stderr | 18 +++++++ .../in-impl-trait.rs | 25 ++++++++++ .../in-impl-trait.stderr | 50 +++++++++++++++++++ .../in-trait-bounds.rs | 9 ++++ .../in-trait-bounds.stderr | 14 ++++++ .../in-trait-object.rs | 18 +++++++ .../in-trait-object.stderr | 26 ++++++++++ .../opt-out-twice.rs | 8 +++ .../opt-out-twice.stderr | 14 ++++++ .../const-trait-bound-opt-out/syntax.rs | 10 ++++ .../without-question-mark.rs | 7 +++ .../without-question-mark.stderr | 8 +++ .../feature-gate.gated.stderr | 8 +++ .../rfc-2632-const-trait-impl/feature-gate.rs | 13 +++++ .../feature-gate.stock.stderr | 18 +++++++ .../impl-opt-out-trait.rs | 11 ++++ .../impl-opt-out-trait.stderr | 8 +++ .../inherent-impl.rs | 14 ++++++ .../inherent-impl.stderr | 10 ++++ .../ui/rfc-2632-const-trait-impl/syntax.rs | 9 ++++ 24 files changed, 333 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.gated.stderr create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.stock.stderr create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-impl-trait.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-impl-trait.stderr create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-bounds.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-bounds.stderr create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-object.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-object.stderr create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/opt-out-twice.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/opt-out-twice.stderr create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/syntax.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/without-question-mark.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/without-question-mark.stderr create mode 100644 src/test/ui/rfc-2632-const-trait-impl/feature-gate.gated.stderr create mode 100644 src/test/ui/rfc-2632-const-trait-impl/feature-gate.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/feature-gate.stock.stderr create mode 100644 src/test/ui/rfc-2632-const-trait-impl/impl-opt-out-trait.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/impl-opt-out-trait.stderr create mode 100644 src/test/ui/rfc-2632-const-trait-impl/inherent-impl.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/inherent-impl.stderr create mode 100644 src/test/ui/rfc-2632-const-trait-impl/syntax.rs diff --git a/src/test/ui/parser/bounds-type.rs b/src/test/ui/parser/bounds-type.rs index 9122cb49ebc1a..7a187a0518af9 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 0b714e40a1012..9a1f2ed398240 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 0000000000000..0bf337ad08dbf --- /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 0000000000000..cf1ed30da0fcc --- /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 0000000000000..64388004b5b72 --- /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 0000000000000..e4e6bedd93746 --- /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 0000000000000..f4abd4b714e8a --- /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 0000000000000..4523b46bc51f6 --- /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 0000000000000..8003361be7d2e --- /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 0000000000000..490fae6d91a15 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-object.rs @@ -0,0 +1,18 @@ +#![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 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 0000000000000..19d52e51b3797 --- /dev/null +++ b/src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/in-trait-object.stderr @@ -0,0 +1,26 @@ +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` 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: aborting due to 4 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 0000000000000..01e941a8fba45 --- /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 0000000000000..f7924b3f24db3 --- /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 0000000000000..a0d9610bbb5e2 --- /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/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 0000000000000..b904a2eec0dd0 --- /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 0000000000000..0dbca952c037e --- /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 0000000000000..b196f9ef57380 --- /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 0000000000000..49b6c0926c50c --- /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 0000000000000..093946f859ac3 --- /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 0000000000000..98d3a220d8674 --- /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 0000000000000..8f923efb093f3 --- /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 0000000000000..9cffe75addd63 --- /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 0000000000000..1d24557655951 --- /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 0000000000000..354d48d630f7b --- /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 {} From b6b11f0f282081b2baa3961cc9f78313eafcc8b4 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Sat, 4 Jan 2020 18:34:10 -0800 Subject: [PATCH 10/13] Call all visit methods on trait definitions --- src/librustc_passes/ast_validation.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 1e5e39217b7aa..cb035e586c109 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -741,11 +741,14 @@ impl<'a> Visitor<'a> for AstValidator<'a> { // 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(Some(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(_) => { From 9950a1f3bdfec11db6bbe2019b819b4410e26905 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Sat, 4 Jan 2020 21:47:11 -0800 Subject: [PATCH 11/13] Add test for `?const` and `?` on the same bound --- .../const-trait-bound-opt-out/with-maybe-sized.rs | 8 ++++++++ .../with-maybe-sized.stderr | 14 ++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/with-maybe-sized.rs create mode 100644 src/test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/with-maybe-sized.stderr 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 0000000000000..425784f4e4326 --- /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 0000000000000..44f6d464ae6a8 --- /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 + From 14730ed44536b5d50aa8b73fbb1023fffe6eba3d Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Sun, 5 Jan 2020 16:33:26 -0800 Subject: [PATCH 12/13] Make `bound_context` more like neighboring functions --- src/librustc_passes/ast_validation.rs | 39 +++++++++++++-------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index cb035e586c109..724d717304c20 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -24,6 +24,7 @@ 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, @@ -50,10 +51,11 @@ struct AstValidator<'a> { /// e.g., `impl Iterator`. outer_impl_trait: Option, - /// Tracks the context in which a bound can appear. + /// Keeps track of the `BoundContext` as we recurse. /// - /// This is used to forbid `?const Trait` bounds in certain contexts. - bound_context_stack: Vec>, + /// 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` @@ -80,21 +82,19 @@ impl<'a> AstValidator<'a> { } fn with_impl_trait(&mut self, outer: Option, f: impl FnOnce(&mut Self)) { - self.bound_context_stack.push(outer.map(|_| BoundContext::ImplTrait)); 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; - self.bound_context_stack.pop(); } - fn with_bound_context(&mut self, ctx: Option, f: impl FnOnce(&mut Self)) { - self.bound_context_stack.push(ctx); + 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_stack.pop(); - } - - fn innermost_bound_context(&mut self) -> Option { - self.bound_context_stack.iter().rev().find(|x| x.is_some()).copied().flatten() + self.bound_context = old; } fn visit_assoc_ty_constraint_from_generic_args(&mut self, constraint: &'a AssocTyConstraint) { @@ -119,9 +119,7 @@ impl<'a> AstValidator<'a> { self.with_impl_trait(Some(t.span), |this| visit::walk_ty(this, t)) } TyKind::TraitObject(..) => { - self.with_bound_context(Some(BoundContext::TraitObject), |this| { - visit::walk_ty(this, t) - }); + self.with_bound_context(BoundContext::TraitObject, |this| visit::walk_ty(this, t)); } TyKind::Path(ref qself, ref path) => { // We allow these: @@ -231,8 +229,7 @@ impl<'a> AstValidator<'a> { } } - // FIXME(ecstaticmorse): Instead, use the `bound_context_stack` to check this in - // `visit_param_bound`. + // 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 { @@ -744,7 +741,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.visit_vis(&item.vis); self.visit_ident(item.ident); self.visit_generics(generics); - self.with_bound_context(Some(BoundContext::TraitBounds), |this| { + self.with_bound_context(BoundContext::TraitBounds, |this| { walk_list!(this, visit_param_bound, bounds); }); walk_list!(self, visit_trait_item, trait_items); @@ -903,7 +900,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { .span_err(bound.span(), "`?const` and `?` are mutually exclusive"); } - if let Some(ctx) = self.innermost_bound_context() { + 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); } @@ -1025,7 +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_stack: Vec::new(), + bound_context: None, is_impl_trait_banned: false, is_assoc_ty_bound_banned: false, lint_buffer: lints, From fd1c00348b7b3521f7340a2d034b32406229fe1b Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Sun, 5 Jan 2020 16:34:26 -0800 Subject: [PATCH 13/13] Add test for `?const` in nested impl/dyn trait --- .../const-trait-bound-opt-out/in-trait-object.rs | 4 ++++ .../in-trait-object.stderr | 14 +++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) 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 index 490fae6d91a15..6cfca71548674 100644 --- 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 @@ -15,4 +15,8 @@ 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 index 19d52e51b3797..c059f16902250 100644 --- 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 @@ -10,6 +10,12 @@ error: `?const` is not permitted in trait objects 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 | @@ -22,5 +28,11 @@ error: `?const` on trait bounds is not yet implemented LL | fn trait_object() -> &'static dyn ?const T { &S } | ^^^^^^^^ -error: aborting due to 4 previous errors +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