@@ -24,6 +24,23 @@ use syntax::{span_err, struct_span_err, walk_list};
24
24
25
25
use rustc_error_codes:: * ;
26
26
27
+ #[ derive( Clone , Copy ) ]
28
+ enum BoundContext {
29
+ ImplTrait ,
30
+ TraitBounds ,
31
+ TraitObject ,
32
+ }
33
+
34
+ impl BoundContext {
35
+ fn description ( & self ) -> & ' static str {
36
+ match self {
37
+ Self :: ImplTrait => "`impl Trait`" ,
38
+ Self :: TraitBounds => "supertraits" ,
39
+ Self :: TraitObject => "trait objects" ,
40
+ }
41
+ }
42
+ }
43
+
27
44
struct AstValidator < ' a > {
28
45
session : & ' a Session ,
29
46
has_proc_macro_decls : bool ,
@@ -33,6 +50,11 @@ struct AstValidator<'a> {
33
50
/// e.g., `impl Iterator<Item = impl Debug>`.
34
51
outer_impl_trait : Option < Span > ,
35
52
53
+ /// Tracks the context in which a bound can appear.
54
+ ///
55
+ /// This is used to forbid `?const Trait` bounds in certain contexts.
56
+ bound_context_stack : Vec < Option < BoundContext > > ,
57
+
36
58
/// Used to ban `impl Trait` in path projections like `<impl Iterator>::Item`
37
59
/// or `Foo::Bar<impl Trait>`
38
60
is_impl_trait_banned : bool ,
@@ -58,9 +80,21 @@ impl<'a> AstValidator<'a> {
58
80
}
59
81
60
82
fn with_impl_trait ( & mut self , outer : Option < Span > , f : impl FnOnce ( & mut Self ) ) {
83
+ self . bound_context_stack . push ( outer. map ( |_| BoundContext :: ImplTrait ) ) ;
61
84
let old = mem:: replace ( & mut self . outer_impl_trait , outer) ;
62
85
f ( self ) ;
63
86
self . outer_impl_trait = old;
87
+ self . bound_context_stack . pop ( ) ;
88
+ }
89
+
90
+ fn with_bound_context ( & mut self , ctx : Option < BoundContext > , f : impl FnOnce ( & mut Self ) ) {
91
+ self . bound_context_stack . push ( ctx) ;
92
+ f ( self ) ;
93
+ self . bound_context_stack . pop ( ) ;
94
+ }
95
+
96
+ fn innermost_bound_context ( & mut self ) -> Option < BoundContext > {
97
+ self . bound_context_stack . iter ( ) . rev ( ) . find ( |x| x. is_some ( ) ) . copied ( ) . flatten ( )
64
98
}
65
99
66
100
fn visit_assoc_ty_constraint_from_generic_args ( & mut self , constraint : & ' a AssocTyConstraint ) {
@@ -84,6 +118,11 @@ impl<'a> AstValidator<'a> {
84
118
TyKind :: ImplTrait ( ..) => {
85
119
self . with_impl_trait ( Some ( t. span ) , |this| visit:: walk_ty ( this, t) )
86
120
}
121
+ TyKind :: TraitObject ( ..) => {
122
+ self . with_bound_context ( Some ( BoundContext :: TraitObject ) , |this| {
123
+ visit:: walk_ty ( this, t)
124
+ } ) ;
125
+ }
87
126
TyKind :: Path ( ref qself, ref path) => {
88
127
// We allow these:
89
128
// - `Option<impl Trait>`
@@ -192,6 +231,8 @@ impl<'a> AstValidator<'a> {
192
231
}
193
232
}
194
233
234
+ // FIXME(ecstaticmorse): Instead, use the `bound_context_stack` to check this in
235
+ // `visit_param_bound`.
195
236
fn no_questions_in_bounds ( & self , bounds : & GenericBounds , where_ : & str , is_trait : bool ) {
196
237
for bound in bounds {
197
238
if let GenericBound :: Trait ( ref poly, TraitBoundModifier :: Maybe ) = * bound {
@@ -678,6 +719,15 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
678
719
}
679
720
}
680
721
self . no_questions_in_bounds ( bounds, "supertraits" , true ) ;
722
+
723
+ // Equivalent of `visit::walk_item` for `ItemKind::Trait` that inserts a bound
724
+ // context for the supertraits.
725
+ self . visit_generics ( generics) ;
726
+ self . with_bound_context ( Some ( BoundContext :: TraitBounds ) , |this| {
727
+ walk_list ! ( this, visit_param_bound, bounds) ;
728
+ } ) ;
729
+ walk_list ! ( self , visit_trait_item, trait_items) ;
730
+ return ;
681
731
}
682
732
ItemKind :: Mod ( _) => {
683
733
// Ensure that `path` attributes on modules are recorded as used (cf. issue #35584).
@@ -822,6 +872,29 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
822
872
visit:: walk_generic_param ( self , param) ;
823
873
}
824
874
875
+ fn visit_param_bound ( & mut self , bound : & ' a GenericBound ) {
876
+ if let GenericBound :: Trait ( poly, maybe_bound) = bound {
877
+ match poly. trait_ref . constness {
878
+ Some ( Constness :: NotConst ) => {
879
+ if * maybe_bound == TraitBoundModifier :: Maybe {
880
+ self . err_handler ( )
881
+ . span_err ( bound. span ( ) , "`?const` and `?` are mutually exclusive" ) ;
882
+ }
883
+
884
+ if let Some ( ctx) = self . innermost_bound_context ( ) {
885
+ let msg = format ! ( "`?const` is not permitted in {}" , ctx. description( ) ) ;
886
+ self . err_handler ( ) . span_err ( bound. span ( ) , & msg) ;
887
+ }
888
+ }
889
+
890
+ Some ( Constness :: Const ) => bug ! ( "Parser should reject bare `const` on bounds" ) ,
891
+ None => { }
892
+ }
893
+ }
894
+
895
+ visit:: walk_param_bound ( self , bound)
896
+ }
897
+
825
898
fn visit_pat ( & mut self , pat : & ' a Pat ) {
826
899
match pat. kind {
827
900
PatKind :: Lit ( ref expr) => {
@@ -930,6 +1003,7 @@ pub fn check_crate(session: &Session, krate: &Crate, lints: &mut lint::LintBuffe
930
1003
session,
931
1004
has_proc_macro_decls : false ,
932
1005
outer_impl_trait : None ,
1006
+ bound_context_stack : Vec :: new ( ) ,
933
1007
is_impl_trait_banned : false ,
934
1008
is_assoc_ty_bound_banned : false ,
935
1009
lint_buffer : lints,
0 commit comments