Skip to content

Commit

Permalink
Support ?Sized in where clauses
Browse files Browse the repository at this point in the history
  • Loading branch information
petrochenkov committed Nov 24, 2016
1 parent a31ad75 commit 7d15250
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 35 deletions.
66 changes: 59 additions & 7 deletions src/librustc/hir/lowering.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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).
Expand All @@ -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<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 {
Expand Down Expand Up @@ -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,
Expand All @@ -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,
})
}
Expand Down Expand Up @@ -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()
}

Expand Down
20 changes: 19 additions & 1 deletion src/librustc_passes/ast_validation.rs
Expand Up @@ -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> {
Expand Down Expand Up @@ -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);
}
_ => {}
}

Expand Down Expand Up @@ -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);
Expand Down
37 changes: 10 additions & 27 deletions src/libsyntax/parse/parser.rs
Expand Up @@ -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,
Expand Down Expand Up @@ -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()
};
Expand All @@ -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");
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -4148,24 +4141,20 @@ 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()
}
}

// matches bounds = ( boundseq )?
// 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 {
Expand All @@ -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
};
Expand All @@ -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();
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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()?;

Expand Down
19 changes: 19 additions & 0 deletions 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 <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[..]);
}
37 changes: 37 additions & 0 deletions 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 <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
}
17 changes: 17 additions & 0 deletions 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 <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() {}

0 comments on commit 7d15250

Please sign in to comment.