From 7d15250b0e5180137f5055b2e4333d5aac6579fe Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 22 Oct 2016 03:33:36 +0300 Subject: [PATCH] Support `?Sized` in where clauses --- src/librustc/hir/lowering.rs | 66 +++++++++++++++++-- src/librustc_passes/ast_validation.rs | 20 +++++- src/libsyntax/parse/parser.rs | 37 +++-------- .../compile-fail/maybe-bounds-where-cpass.rs | 19 ++++++ src/test/compile-fail/maybe-bounds-where.rs | 37 +++++++++++ src/test/compile-fail/maybe-bounds.rs | 17 +++++ 6 files changed, 161 insertions(+), 35 deletions(-) create mode 100644 src/test/compile-fail/maybe-bounds-where-cpass.rs create mode 100644 src/test/compile-fail/maybe-bounds-where.rs create mode 100644 src/test/compile-fail/maybe-bounds.rs diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 5af7c18e1a107..ce80f617afdd9 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -46,6 +46,7 @@ use hir::map::definitions::DefPathData; 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 @@ impl<'a> LoweringContext<'a> { } } - 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 @@ impl<'a> LoweringContext<'a> { 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 { - tps.iter().map(|tp| self.lower_ty_param(tp)).collect() + fn lower_ty_params(&mut self, tps: &P<[TyParam]>, add_bounds: &NodeMap>) + -> hir::HirVec { + 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 @@ impl<'a> LoweringContext<'a> { } 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 @@ impl<'a> LoweringContext<'a> { 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 @@ impl<'a> LoweringContext<'a> { } } - 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() } diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index a3916e7eca351..fa07006aa63e1 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -86,6 +86,19 @@ impl<'a> AstValidator<'a> { _ => {} } } + + 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 @@ impl<'a> Visitor for AstValidator<'a> { err.emit(); }); } + TyKind::ObjectSum(_, ref bounds) | + TyKind::PolyTraitRef(ref bounds) => { + self.no_questions_in_bounds(bounds, "trait object types", false); + } _ => {} } @@ -189,7 +206,8 @@ impl<'a> Visitor for AstValidator<'a> { } } } - 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); diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 49226be4147d7..bdd1606805fef 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -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 @@ impl<'a> Parser<'a> { 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 @@ impl<'a> Parser<'a> { 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 @@ impl<'a> Parser<'a> { 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 @@ impl<'a> Parser<'a> { // 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 @@ impl<'a> Parser<'a> { // 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 @@ impl<'a> Parser<'a> { 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 @@ impl<'a> Parser<'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 @@ impl<'a> Parser<'a> { 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 @@ impl<'a> Parser<'a> { 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 index 0000000000000..f10526200ff14 --- /dev/null +++ b/src/test/compile-fail/maybe-bounds-where-cpass.rs @@ -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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_attrs)] + +struct S(*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 index 0000000000000..211fac2ee234c --- /dev/null +++ b/src/test/compile-fail/maybe-bounds-where.rs @@ -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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct S1(T) where (T): ?Sized; +//~^ ERROR `?Trait` bounds are only permitted at the point where a type parameter is declared + +struct S2(T) where u8: ?Sized; +//~^ ERROR `?Trait` bounds are only permitted at the point where a type parameter is declared + +struct S3(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) where for<'a> T: ?Trait<'a>; +//~^ ERROR `?Trait` bounds are only permitted at the point where a type parameter is declared + +struct S5(*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 S1 { + 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 index 0000000000000..b0b412bbf89ec --- /dev/null +++ b/src/test/compile-fail/maybe-bounds.rs @@ -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 or the MIT license +// , 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() {}