]> git.lizzy.rs Git - rust.git/commitdiff
Support `?Sized` in where clauses
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>
Sat, 22 Oct 2016 00:33:36 +0000 (03:33 +0300)
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>
Thu, 24 Nov 2016 21:43:00 +0000 (00:43 +0300)
src/librustc/hir/lowering.rs
src/librustc_passes/ast_validation.rs
src/libsyntax/parse/parser.rs
src/test/compile-fail/maybe-bounds-where-cpass.rs [new file with mode: 0644]
src/test/compile-fail/maybe-bounds-where.rs [new file with mode: 0644]
src/test/compile-fail/maybe-bounds.rs [new file with mode: 0644]

index 5af7c18e1a1078f4c9cc14849f59b0691d8b01ba..ce80f617afdd95817d39244cbb61ccf221f25ad8 100644 (file)
@@ -46,6 +46,7 @@
 use hir::def_id::{DefIndex, DefId};
 use hir::def::{Def, PathResolution};
 use session::Session;
+use util::nodemap::NodeMap;
 
 use std::collections::BTreeMap;
 use std::iter;
@@ -394,7 +395,7 @@ fn lower_ty_param_bound(&mut self, tpb: &TyParamBound) -> hir::TyParamBound {
         }
     }
 
-    fn lower_ty_param(&mut self, tp: &TyParam) -> hir::TyParam {
+    fn lower_ty_param(&mut self, tp: &TyParam, add_bounds: &[TyParamBound]) -> hir::TyParam {
         let mut name = tp.ident.name;
 
         // Don't expose `Self` (recovered "keyword used as ident" parse error).
@@ -404,18 +405,26 @@ fn lower_ty_param(&mut self, tp: &TyParam) -> hir::TyParam {
             name = Symbol::gensym("Self");
         }
 
+        let mut bounds = self.lower_bounds(&tp.bounds);
+        if !add_bounds.is_empty() {
+            bounds = bounds.into_iter().chain(self.lower_bounds(add_bounds).into_iter()).collect();
+        }
+
         hir::TyParam {
             id: tp.id,
             name: name,
-            bounds: self.lower_bounds(&tp.bounds),
+            bounds: bounds,
             default: tp.default.as_ref().map(|x| self.lower_ty(x)),
             span: tp.span,
             pure_wrt_drop: tp.attrs.iter().any(|attr| attr.check_name("may_dangle")),
         }
     }
 
-    fn lower_ty_params(&mut self, tps: &P<[TyParam]>) -> hir::HirVec<hir::TyParam> {
-        tps.iter().map(|tp| self.lower_ty_param(tp)).collect()
+    fn lower_ty_params(&mut self, tps: &P<[TyParam]>, add_bounds: &NodeMap<Vec<TyParamBound>>)
+                       -> hir::HirVec<hir::TyParam> {
+        tps.iter().map(|tp| {
+            self.lower_ty_param(tp, add_bounds.get(&tp.id).map_or(&[][..], |x| &x))
+        }).collect()
     }
 
     fn lower_lifetime(&mut self, l: &Lifetime) -> hir::Lifetime {
@@ -447,8 +456,47 @@ fn lower_opt_lifetime(&mut self, o_lt: &Option<Lifetime>) -> Option<hir::Lifetim
     }
 
     fn lower_generics(&mut self, g: &Generics) -> hir::Generics {
+        // Collect `?Trait` bounds in where clause and move them to parameter definitions.
+        let mut add_bounds = NodeMap();
+        for pred in &g.where_clause.predicates {
+            if let WherePredicate::BoundPredicate(ref bound_pred) = *pred {
+                'next_bound: for bound in &bound_pred.bounds {
+                    if let TraitTyParamBound(_, TraitBoundModifier::Maybe) = *bound {
+                        let report_error = |this: &mut Self| {
+                            this.diagnostic().span_err(bound_pred.bounded_ty.span,
+                                                       "`?Trait` bounds are only permitted at the \
+                                                        point where a type parameter is declared");
+                        };
+                        // Check if the where clause type is a plain type parameter.
+                        match bound_pred.bounded_ty.node {
+                            TyKind::Path(None, ref path)
+                                    if !path.global && path.segments.len() == 1 &&
+                                        bound_pred.bound_lifetimes.is_empty() => {
+                                if let Some(Def::TyParam(def_id)) =
+                                        self.resolver.get_resolution(bound_pred.bounded_ty.id)
+                                                     .map(|d| d.base_def) {
+                                    if let Some(node_id) =
+                                            self.resolver.definitions().as_local_node_id(def_id) {
+                                        for ty_param in &g.ty_params {
+                                            if node_id == ty_param.id {
+                                                add_bounds.entry(ty_param.id).or_insert(Vec::new())
+                                                                            .push(bound.clone());
+                                                continue 'next_bound;
+                                            }
+                                        }
+                                    }
+                                }
+                                report_error(self)
+                            }
+                            _ => report_error(self)
+                        }
+                    }
+                }
+            }
+        }
+
         hir::Generics {
-            ty_params: self.lower_ty_params(&g.ty_params),
+            ty_params: self.lower_ty_params(&g.ty_params, &add_bounds),
             lifetimes: self.lower_lifetime_defs(&g.lifetimes),
             where_clause: self.lower_where_clause(&g.where_clause),
             span: g.span,
@@ -474,7 +522,11 @@ fn lower_where_predicate(&mut self, pred: &WherePredicate) -> hir::WherePredicat
                 hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
                     bound_lifetimes: self.lower_lifetime_defs(bound_lifetimes),
                     bounded_ty: self.lower_ty(bounded_ty),
-                    bounds: bounds.iter().map(|x| self.lower_ty_param_bound(x)).collect(),
+                    bounds: bounds.iter().filter_map(|bound| match *bound {
+                        // Ignore `?Trait` bounds, they were copied into type parameters already.
+                        TraitTyParamBound(_, TraitBoundModifier::Maybe) => None,
+                        _ => Some(self.lower_ty_param_bound(bound))
+                    }).collect(),
                     span: span,
                 })
             }
@@ -563,7 +615,7 @@ fn lower_mt(&mut self, mt: &MutTy) -> hir::MutTy {
         }
     }
 
-    fn lower_bounds(&mut self, bounds: &TyParamBounds) -> hir::TyParamBounds {
+    fn lower_bounds(&mut self, bounds: &[TyParamBound]) -> hir::TyParamBounds {
         bounds.iter().map(|bound| self.lower_ty_param_bound(bound)).collect()
     }
 
index a3916e7eca35104b8bbd246630ec45db25e85c06..fa07006aa63e1da6312dbb2d2d744df1424a2426 100644 (file)
@@ -86,6 +86,19 @@ fn check_trait_fn_not_const(&self, constness: Spanned<Constness>) {
             _ => {}
         }
     }
+
+    fn no_questions_in_bounds(&self, bounds: &TyParamBounds, where_: &str, is_trait: bool) {
+        for bound in bounds {
+            if let TraitTyParamBound(ref poly, TraitBoundModifier::Maybe) = *bound {
+                let mut err = self.err_handler().struct_span_err(poly.span,
+                                    &format!("`?Trait` is not permitted in {}", where_));
+                if is_trait {
+                    err.note(&format!("traits are `?{}` by default", poly.trait_ref.path));
+                }
+                err.emit();
+            }
+        }
+    }
 }
 
 impl<'a> Visitor for AstValidator<'a> {
@@ -130,6 +143,10 @@ fn visit_ty(&mut self, ty: &Ty) {
                     err.emit();
                 });
             }
+            TyKind::ObjectSum(_, ref bounds) |
+            TyKind::PolyTraitRef(ref bounds) => {
+                self.no_questions_in_bounds(bounds, "trait object types", false);
+            }
             _ => {}
         }
 
@@ -189,7 +206,8 @@ fn visit_item(&mut self, item: &Item) {
                     }
                 }
             }
-            ItemKind::Trait(.., ref trait_items) => {
+            ItemKind::Trait(.., ref bounds, ref trait_items) => {
+                self.no_questions_in_bounds(bounds, "supertraits", true);
                 for trait_item in trait_items {
                     if let TraitItemKind::Method(ref sig, ref block) = trait_item.node {
                         self.check_trait_fn_not_const(sig.constness);
index 49226be4147d7c4b9b01a062f4b44c4997fcc45f..bdd1606805fefa2573a98230d581b3f858916e4f 100644 (file)
@@ -88,13 +88,6 @@ pub enum PathStyle {
     Expr,
 }
 
-/// How to parse a bound, whether to allow bound modifiers such as `?`.
-#[derive(Copy, Clone, PartialEq)]
-pub enum BoundParsingMode {
-    Bare,
-    Modified,
-}
-
 #[derive(Clone, Copy, PartialEq)]
 pub enum SemiColonMode {
     Break,
@@ -1041,7 +1034,7 @@ pub fn parse_for_in_type(&mut self) -> PResult<'a, TyKind> {
                                                      trait_ref: trait_ref,
                                                      span: mk_sp(lo, hi)};
             let other_bounds = if self.eat(&token::BinOp(token::Plus)) {
-                self.parse_ty_param_bounds(BoundParsingMode::Bare)?
+                self.parse_ty_param_bounds()?
             } else {
                 P::new()
             };
@@ -1059,7 +1052,7 @@ pub fn parse_impl_trait_type(&mut self) -> PResult<'a, TyKind> {
         The `impl` has already been consumed.
         */
 
-        let bounds = self.parse_ty_param_bounds(BoundParsingMode::Modified)?;
+        let bounds = self.parse_ty_param_bounds()?;
 
         if !bounds.iter().any(|b| if let TraitTyParamBound(..) = *b { true } else { false }) {
             self.span_err(self.prev_span, "at least one trait must be specified");
@@ -1271,7 +1264,7 @@ pub fn parse_ty_sum(&mut self) -> PResult<'a, P<Ty>> {
             return Ok(lhs);
         }
 
-        let bounds = self.parse_ty_param_bounds(BoundParsingMode::Bare)?;
+        let bounds = self.parse_ty_param_bounds()?;
 
         // In type grammar, `+` is treated like a binary operator,
         // and hence both L and R side are required.
@@ -4148,14 +4141,12 @@ fn warn_missing_semicolon(&self) {
 
     // Parses a sequence of bounds if a `:` is found,
     // otherwise returns empty list.
-    fn parse_colon_then_ty_param_bounds(&mut self,
-                                        mode: BoundParsingMode)
-                                        -> PResult<'a, TyParamBounds>
+    fn parse_colon_then_ty_param_bounds(&mut self) -> PResult<'a, TyParamBounds>
     {
         if !self.eat(&token::Colon) {
             Ok(P::new())
         } else {
-            self.parse_ty_param_bounds(mode)
+            self.parse_ty_param_bounds()
         }
     }
 
@@ -4163,9 +4154,7 @@ fn parse_colon_then_ty_param_bounds(&mut self,
     // where   boundseq  = ( polybound + boundseq ) | polybound
     // and     polybound = ( 'for' '<' 'region '>' )? bound
     // and     bound     = 'region | trait_ref
-    fn parse_ty_param_bounds(&mut self,
-                             mode: BoundParsingMode)
-                             -> PResult<'a, TyParamBounds>
+    fn parse_ty_param_bounds(&mut self) -> PResult<'a, TyParamBounds>
     {
         let mut result = vec![];
         loop {
@@ -4187,13 +4176,7 @@ fn parse_ty_param_bounds(&mut self,
                 token::ModSep | token::Ident(..) => {
                     let poly_trait_ref = self.parse_poly_trait_ref()?;
                     let modifier = if ate_question {
-                        if mode == BoundParsingMode::Modified {
-                            TraitBoundModifier::Maybe
-                        } else {
-                            self.span_err(question_span,
-                                          "unexpected `?`");
-                            TraitBoundModifier::None
-                        }
+                        TraitBoundModifier::Maybe
                     } else {
                         TraitBoundModifier::None
                     };
@@ -4215,7 +4198,7 @@ fn parse_ty_param(&mut self, preceding_attrs: Vec<ast::Attribute>) -> PResult<'a
         let span = self.span;
         let ident = self.parse_ident()?;
 
-        let bounds = self.parse_colon_then_ty_param_bounds(BoundParsingMode::Modified)?;
+        let bounds = self.parse_colon_then_ty_param_bounds()?;
 
         let default = if self.check(&token::Eq) {
             self.bump();
@@ -4439,7 +4422,7 @@ pub fn parse_where_clause(&mut self) -> PResult<'a, ast::WhereClause> {
                     let bounded_ty = self.parse_ty()?;
 
                     if self.eat(&token::Colon) {
-                        let bounds = self.parse_ty_param_bounds(BoundParsingMode::Bare)?;
+                        let bounds = self.parse_ty_param_bounds()?;
                         let hi = self.prev_span.hi;
                         let span = mk_sp(lo, hi);
 
@@ -4901,7 +4884,7 @@ fn parse_item_trait(&mut self, unsafety: Unsafety) -> PResult<'a, ItemInfo> {
         let mut tps = self.parse_generics()?;
 
         // Parse supertrait bounds.
-        let bounds = self.parse_colon_then_ty_param_bounds(BoundParsingMode::Bare)?;
+        let bounds = self.parse_colon_then_ty_param_bounds()?;
 
         tps.where_clause = self.parse_where_clause()?;
 
diff --git a/src/test/compile-fail/maybe-bounds-where-cpass.rs b/src/test/compile-fail/maybe-bounds-where-cpass.rs
new file mode 100644 (file)
index 0000000..f105262
--- /dev/null
@@ -0,0 +1,19 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(rustc_attrs)]
+
+struct S<T>(*const T) where T: ?Sized;
+
+#[rustc_error]
+fn main() { //~ ERROR compilation successful
+    let u = vec![1, 2, 3];
+    let _s: S<[u8]> = S(&u[..]);
+}
diff --git a/src/test/compile-fail/maybe-bounds-where.rs b/src/test/compile-fail/maybe-bounds-where.rs
new file mode 100644 (file)
index 0000000..211fac2
--- /dev/null
@@ -0,0 +1,37 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+struct S1<T>(T) where (T): ?Sized;
+//~^ ERROR `?Trait` bounds are only permitted at the point where a type parameter is declared
+
+struct S2<T>(T) where u8: ?Sized;
+//~^ ERROR `?Trait` bounds are only permitted at the point where a type parameter is declared
+
+struct S3<T>(T) where &'static T: ?Sized;
+//~^ ERROR `?Trait` bounds are only permitted at the point where a type parameter is declared
+
+trait Trait<'a> {}
+
+struct S4<T>(T) where for<'a> T: ?Trait<'a>;
+//~^ ERROR `?Trait` bounds are only permitted at the point where a type parameter is declared
+
+struct S5<T>(*const T) where T: ?Trait<'static> + ?Sized;
+//~^ ERROR type parameter has more than one relaxed default bound
+//~| WARN default bound relaxed for a type parameter
+
+impl<T> S1<T> {
+    fn f() where T: ?Sized {}
+    //~^ ERROR `?Trait` bounds are only permitted at the point where a type parameter is declared
+}
+
+fn main() {
+    let u = vec![1, 2, 3];
+    let _s: S5<[u8]> = S5(&u[..]); // OK
+}
diff --git a/src/test/compile-fail/maybe-bounds.rs b/src/test/compile-fail/maybe-bounds.rs
new file mode 100644 (file)
index 0000000..b0b412b
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+trait Tr: ?Sized {} //~ ERROR `?Trait` is not permitted in supertraits
+                    //~^ NOTE traits are `?Sized` by default
+
+type A1 = Tr + ?Sized; //~ ERROR `?Trait` is not permitted in trait object types
+type A2 = for<'a> Tr + ?Sized; //~ ERROR `?Trait` is not permitted in trait object types
+
+fn main() {}