Refactor parsing of trait object types
This commit is contained in:
parent
58c701f5c7
commit
b5e889791a
@ -17,7 +17,7 @@ pub use self::PathParameters::*;
|
||||
pub use symbol::Symbol as Name;
|
||||
pub use util::ThinVec;
|
||||
|
||||
use syntax_pos::{mk_sp, Span, DUMMY_SP, ExpnId};
|
||||
use syntax_pos::{mk_sp, BytePos, Span, DUMMY_SP, ExpnId};
|
||||
use codemap::{respan, Spanned};
|
||||
use abi::Abi;
|
||||
use ext::hygiene::SyntaxContext;
|
||||
@ -1716,6 +1716,16 @@ pub struct PolyTraitRef {
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl PolyTraitRef {
|
||||
pub fn new(lifetimes: Vec<LifetimeDef>, path: Path, lo: BytePos, hi: BytePos) -> Self {
|
||||
PolyTraitRef {
|
||||
bound_lifetimes: lifetimes,
|
||||
trait_ref: TraitRef { path: path, ref_id: DUMMY_NODE_ID },
|
||||
span: mk_sp(lo, hi),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
|
||||
pub enum Visibility {
|
||||
Public,
|
||||
|
@ -657,7 +657,7 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
ExpansionKind::Expr => Expansion::Expr(self.parse_expr()?),
|
||||
ExpansionKind::OptExpr => Expansion::OptExpr(Some(self.parse_expr()?)),
|
||||
ExpansionKind::Ty => Expansion::Ty(self.parse_ty_no_plus()?),
|
||||
ExpansionKind::Ty => Expansion::Ty(self.parse_ty()?),
|
||||
ExpansionKind::Pat => Expansion::Pat(self.parse_pat()?),
|
||||
})
|
||||
}
|
||||
|
@ -414,7 +414,7 @@ pub fn parse_arm_panic(parser: &mut Parser) -> Arm {
|
||||
}
|
||||
|
||||
pub fn parse_ty_panic(parser: &mut Parser) -> P<Ty> {
|
||||
panictry!(parser.parse_ty_no_plus())
|
||||
panictry!(parser.parse_ty())
|
||||
}
|
||||
|
||||
pub fn parse_stmt_panic(parser: &mut Parser) -> Option<Stmt> {
|
||||
|
@ -512,7 +512,7 @@ fn parse_nt<'a>(p: &mut Parser<'a>, sp: Span, name: &str) -> Nonterminal {
|
||||
},
|
||||
"pat" => token::NtPat(panictry!(p.parse_pat())),
|
||||
"expr" => token::NtExpr(panictry!(p.parse_expr())),
|
||||
"ty" => token::NtTy(panictry!(p.parse_ty_no_plus())),
|
||||
"ty" => token::NtTy(panictry!(p.parse_ty())),
|
||||
// this could be handled like a token, since it is one
|
||||
"ident" => match p.token {
|
||||
token::Ident(sn) => {
|
||||
|
@ -41,7 +41,7 @@ use ast::{BinOpKind, UnOp};
|
||||
use ast::RangeEnd;
|
||||
use {ast, attr};
|
||||
use codemap::{self, CodeMap, Spanned, spanned, respan};
|
||||
use syntax_pos::{self, Span, Pos, BytePos, mk_sp};
|
||||
use syntax_pos::{self, Span, BytePos, mk_sp};
|
||||
use errors::{self, DiagnosticBuilder};
|
||||
use parse::{self, classify, token};
|
||||
use parse::common::SeqSep;
|
||||
@ -1116,57 +1116,13 @@ impl<'a> Parser<'a> {
|
||||
self.check_keyword(keywords::Extern)
|
||||
}
|
||||
|
||||
pub fn get_lifetime(&mut self) -> ast::Ident {
|
||||
fn get_label(&mut self) -> ast::Ident {
|
||||
match self.token {
|
||||
token::Lifetime(ref ident) => *ident,
|
||||
_ => self.bug("not a lifetime"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_for_in_type(&mut self) -> PResult<'a, TyKind> {
|
||||
/*
|
||||
Parses whatever can come after a `for` keyword in a type.
|
||||
The `for` hasn't been consumed.
|
||||
|
||||
- for <'lt> [unsafe] [extern "ABI"] fn (S) -> T
|
||||
- for <'lt> path::foo(a, b) + Trait + 'a
|
||||
*/
|
||||
|
||||
let lo = self.span.lo;
|
||||
let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
|
||||
|
||||
// examine next token to decide to do
|
||||
if self.token_is_bare_fn_keyword() {
|
||||
self.parse_ty_bare_fn(lifetime_defs)
|
||||
} else {
|
||||
let hi = self.span.hi;
|
||||
let trait_ref = self.parse_trait_ref()?;
|
||||
let poly_trait_ref = PolyTraitRef { bound_lifetimes: lifetime_defs,
|
||||
trait_ref: trait_ref,
|
||||
span: mk_sp(lo, hi)};
|
||||
let other_bounds = if self.eat(&token::BinOp(token::Plus)) {
|
||||
self.parse_ty_param_bounds()?
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
let all_bounds =
|
||||
Some(TraitTyParamBound(poly_trait_ref, TraitBoundModifier::None)).into_iter()
|
||||
.chain(other_bounds)
|
||||
.collect();
|
||||
Ok(ast::TyKind::TraitObject(all_bounds))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_impl_trait_type(&mut self) -> PResult<'a, TyKind> {
|
||||
// Parses whatever can come after a `impl` keyword in a type.
|
||||
// The `impl` has already been consumed.
|
||||
Ok(ast::TyKind::ImplTrait(self.parse_ty_param_bounds()?))
|
||||
}
|
||||
|
||||
pub fn parse_ty_path(&mut self) -> PResult<'a, TyKind> {
|
||||
Ok(TyKind::Path(None, self.parse_path(PathStyle::Type)?))
|
||||
}
|
||||
|
||||
/// parse a TyKind::BareFn type:
|
||||
pub fn parse_ty_bare_fn(&mut self, lifetime_defs: Vec<LifetimeDef>)
|
||||
-> PResult<'a, TyKind> {
|
||||
@ -1347,84 +1303,9 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a type.
|
||||
// Parse a type
|
||||
pub fn parse_ty(&mut self) -> PResult<'a, P<Ty>> {
|
||||
let lo = self.span.lo;
|
||||
let lhs = self.parse_ty_no_plus()?;
|
||||
|
||||
if !self.eat(&token::BinOp(token::Plus)) {
|
||||
return Ok(lhs);
|
||||
}
|
||||
|
||||
let mut bounds = self.parse_ty_param_bounds()?;
|
||||
|
||||
// In type grammar, `+` is treated like a binary operator,
|
||||
// and hence both L and R side are required.
|
||||
if bounds.is_empty() {
|
||||
let prev_span = self.prev_span;
|
||||
self.span_err(prev_span,
|
||||
"at least one type parameter bound \
|
||||
must be specified");
|
||||
}
|
||||
|
||||
let mut lhs = lhs.unwrap();
|
||||
if let TyKind::Paren(ty) = lhs.node {
|
||||
// We have to accept the first bound in parens for backward compatibility.
|
||||
// Example: `(Bound) + Bound + Bound`
|
||||
lhs = ty.unwrap();
|
||||
}
|
||||
if let TyKind::Path(None, path) = lhs.node {
|
||||
let poly_trait_ref = PolyTraitRef {
|
||||
bound_lifetimes: Vec::new(),
|
||||
trait_ref: TraitRef { path: path, ref_id: lhs.id },
|
||||
span: lhs.span,
|
||||
};
|
||||
let poly_trait_ref = TraitTyParamBound(poly_trait_ref, TraitBoundModifier::None);
|
||||
bounds.insert(0, poly_trait_ref);
|
||||
} else {
|
||||
let mut err = struct_span_err!(self.sess.span_diagnostic, lhs.span, E0178,
|
||||
"expected a path on the left-hand side \
|
||||
of `+`, not `{}`",
|
||||
pprust::ty_to_string(&lhs));
|
||||
err.span_label(lhs.span, &format!("expected a path"));
|
||||
let hi = bounds.iter().map(|x| match *x {
|
||||
TraitTyParamBound(ref tr, _) => tr.span.hi,
|
||||
RegionTyParamBound(ref r) => r.span.hi,
|
||||
}).max_by_key(|x| x.to_usize());
|
||||
let full_span = hi.map(|hi| Span {
|
||||
lo: lhs.span.lo,
|
||||
hi: hi,
|
||||
expn_id: lhs.span.expn_id,
|
||||
});
|
||||
match (&lhs.node, full_span) {
|
||||
(&TyKind::Rptr(ref lifetime, ref mut_ty), Some(full_span)) => {
|
||||
let ty_str = pprust::to_string(|s| {
|
||||
use print::pp::word;
|
||||
use print::pprust::PrintState;
|
||||
|
||||
word(&mut s.s, "&")?;
|
||||
s.print_opt_lifetime(lifetime)?;
|
||||
s.print_mutability(mut_ty.mutbl)?;
|
||||
s.popen()?;
|
||||
s.print_type(&mut_ty.ty)?;
|
||||
s.print_bounds(" +", &bounds)?;
|
||||
s.pclose()
|
||||
});
|
||||
err.span_suggestion(full_span, "try adding parentheses (per RFC 438):",
|
||||
ty_str);
|
||||
}
|
||||
|
||||
_ => {
|
||||
help!(&mut err,
|
||||
"perhaps you forgot parentheses? (per RFC 438)");
|
||||
}
|
||||
}
|
||||
err.emit();
|
||||
}
|
||||
|
||||
let sp = mk_sp(lo, self.prev_span.hi);
|
||||
let sum = TyKind::TraitObject(bounds);
|
||||
Ok(P(Ty {id: ast::DUMMY_NODE_ID, node: sum, span: sp}))
|
||||
self.parse_ty_common(true)
|
||||
}
|
||||
|
||||
/// Parse a type in restricted contexts where `+` is not permitted.
|
||||
@ -1432,15 +1313,17 @@ impl<'a> Parser<'a> {
|
||||
/// `+` is prohibited to maintain operator priority (P(+) < P(&)).
|
||||
/// Example 2: `value1 as TYPE + value2`
|
||||
/// `+` is prohibited to avoid interactions with expression grammar.
|
||||
pub fn parse_ty_no_plus(&mut self) -> PResult<'a, P<Ty>> {
|
||||
fn parse_ty_no_plus(&mut self) -> PResult<'a, P<Ty>> {
|
||||
self.parse_ty_common(false)
|
||||
}
|
||||
|
||||
fn parse_ty_common(&mut self, allow_plus: bool) -> PResult<'a, P<Ty>> {
|
||||
maybe_whole!(self, NtTy, |x| x);
|
||||
|
||||
let lo = self.span.lo;
|
||||
|
||||
let t = if self.eat(&token::OpenDelim(token::Paren)) {
|
||||
// (t) is a parenthesized ty
|
||||
// (t,) is the type of a tuple with only one field,
|
||||
// of type t
|
||||
let node = if self.eat(&token::OpenDelim(token::Paren)) {
|
||||
// `(TYPE)` is a parenthesized type.
|
||||
// `(TYPE,)` is a tuple with a single field of type TYPE.
|
||||
let mut ts = vec![];
|
||||
let mut last_comma = false;
|
||||
while self.token != token::CloseDelim(token::Paren) {
|
||||
@ -1452,81 +1335,162 @@ impl<'a> Parser<'a> {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.expect(&token::CloseDelim(token::Paren))?;
|
||||
|
||||
if ts.len() == 1 && !last_comma {
|
||||
TyKind::Paren(ts.into_iter().nth(0).unwrap())
|
||||
let ty = ts.into_iter().nth(0).unwrap().unwrap();
|
||||
match ty.node {
|
||||
// Accept `(Trait1) + Trait2 + 'a` for backward compatibility (#39318).
|
||||
TyKind::Path(None, ref path)
|
||||
if allow_plus && self.token == token::BinOp(token::Plus) => {
|
||||
self.bump(); // `+`
|
||||
let pt = PolyTraitRef::new(Vec::new(), path.clone(), lo, self.prev_span.hi);
|
||||
let mut bounds = vec![TraitTyParamBound(pt, TraitBoundModifier::None)];
|
||||
bounds.append(&mut self.parse_ty_param_bounds()?);
|
||||
TyKind::TraitObject(bounds)
|
||||
}
|
||||
_ => TyKind::Paren(P(ty))
|
||||
}
|
||||
} else {
|
||||
TyKind::Tup(ts)
|
||||
}
|
||||
} else if self.eat(&token::Not) {
|
||||
// Never type `!`
|
||||
TyKind::Never
|
||||
} else if self.eat(&token::BinOp(token::Star)) {
|
||||
// STAR POINTER (bare pointer?)
|
||||
// Raw pointer
|
||||
TyKind::Ptr(self.parse_ptr()?)
|
||||
} else if self.eat(&token::OpenDelim(token::Bracket)) {
|
||||
// VECTOR
|
||||
// Array or slice
|
||||
let t = self.parse_ty()?;
|
||||
|
||||
// Parse the `; e` in `[ i32; e ]`
|
||||
// where `e` is a const expression
|
||||
// Parse optional `; EXPR` in `[TYPE; EXPR]`
|
||||
let t = match self.maybe_parse_fixed_length_of_vec()? {
|
||||
None => TyKind::Slice(t),
|
||||
Some(suffix) => TyKind::Array(t, suffix)
|
||||
Some(suffix) => TyKind::Array(t, suffix),
|
||||
};
|
||||
self.expect(&token::CloseDelim(token::Bracket))?;
|
||||
t
|
||||
} else if self.check(&token::BinOp(token::And)) ||
|
||||
self.check(&token::AndAnd) {
|
||||
// BORROWED POINTER
|
||||
} else if self.check(&token::BinOp(token::And)) || self.check(&token::AndAnd) {
|
||||
// Reference
|
||||
self.expect_and()?;
|
||||
self.parse_borrowed_pointee()?
|
||||
} else if self.check_keyword(keywords::For) {
|
||||
// FIXME `+` has incorrect priority in trait object types starting with `for` (#39317).
|
||||
self.parse_for_in_type()?
|
||||
} else if self.eat_keyword(keywords::Impl) {
|
||||
// FIXME figure out priority of `+` in `impl Trait1 + Trait2` (#34511).
|
||||
self.parse_impl_trait_type()?
|
||||
} else if self.token_is_bare_fn_keyword() {
|
||||
// BARE FUNCTION
|
||||
self.parse_ty_bare_fn(Vec::new())?
|
||||
} else if self.eat_keyword_noexpect(keywords::Typeof) {
|
||||
// TYPEOF
|
||||
// `typeof(EXPR)`
|
||||
// In order to not be ambiguous, the type must be surrounded by parens.
|
||||
self.expect(&token::OpenDelim(token::Paren))?;
|
||||
let e = self.parse_expr()?;
|
||||
self.expect(&token::CloseDelim(token::Paren))?;
|
||||
TyKind::Typeof(e)
|
||||
} else if self.eat(&token::Underscore) {
|
||||
// A type to be inferred `_`
|
||||
TyKind::Infer
|
||||
} else if self.eat_lt() {
|
||||
// Qualified path
|
||||
let (qself, path) = self.parse_qualified_path(PathStyle::Type)?;
|
||||
TyKind::Path(Some(qself), path)
|
||||
} else if self.token.is_path_start() {
|
||||
// Simple path
|
||||
let path = self.parse_path(PathStyle::Type)?;
|
||||
if self.eat(&token::Not) {
|
||||
// MACRO INVOCATION
|
||||
// Macro invocation in type position
|
||||
let (_, tts) = self.expect_delimited_token_tree()?;
|
||||
let hi = self.span.hi;
|
||||
TyKind::Mac(spanned(lo, hi, Mac_ { path: path, tts: tts }))
|
||||
TyKind::Mac(spanned(lo, self.span.hi, Mac_ { path: path, tts: tts }))
|
||||
} else {
|
||||
// NAMED TYPE
|
||||
TyKind::Path(None, path)
|
||||
// Just a type path or bound list (trait object type) starting with a trait.
|
||||
// `Type`
|
||||
// `Trait1 + Trait2 + 'a`
|
||||
if allow_plus && self.eat(&token::BinOp(token::Plus)) {
|
||||
let poly_trait = PolyTraitRef::new(Vec::new(), path, lo, self.prev_span.hi);
|
||||
let mut bounds = vec![TraitTyParamBound(poly_trait, TraitBoundModifier::None)];
|
||||
bounds.append(&mut self.parse_ty_param_bounds()?);
|
||||
TyKind::TraitObject(bounds)
|
||||
} else {
|
||||
TyKind::Path(None, path)
|
||||
}
|
||||
}
|
||||
} else if self.eat(&token::Underscore) {
|
||||
// TYPE TO BE INFERRED
|
||||
TyKind::Infer
|
||||
} else if self.token_is_bare_fn_keyword() {
|
||||
// Function pointer type
|
||||
self.parse_ty_bare_fn(Vec::new())?
|
||||
} else if self.check_keyword(keywords::For) {
|
||||
// Function pointer type or bound list (trait object type) starting with a poly-trait.
|
||||
// `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T`
|
||||
// `for<'lt> Trait1<'lt> + Trait2 + 'a`
|
||||
let lo = self.span.lo;
|
||||
let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
|
||||
if self.token_is_bare_fn_keyword() {
|
||||
self.parse_ty_bare_fn(lifetime_defs)?
|
||||
} else {
|
||||
let path = self.parse_path(PathStyle::Type)?;
|
||||
let poly_trait = PolyTraitRef::new(lifetime_defs, path, lo, self.prev_span.hi);
|
||||
let mut bounds = vec![TraitTyParamBound(poly_trait, TraitBoundModifier::None)];
|
||||
if allow_plus && self.eat(&token::BinOp(token::Plus)) {
|
||||
bounds.append(&mut self.parse_ty_param_bounds()?)
|
||||
}
|
||||
TyKind::TraitObject(bounds)
|
||||
}
|
||||
} else if self.eat_keyword(keywords::Impl) {
|
||||
// FIXME: figure out priority of `+` in `impl Trait1 + Trait2` (#34511).
|
||||
TyKind::ImplTrait(self.parse_ty_param_bounds()?)
|
||||
} else if self.check(&token::Question) {
|
||||
// Bound list (trait object type)
|
||||
// Bound lists starting with `'lt` are not currently supported (#40043)
|
||||
TyKind::TraitObject(self.parse_ty_param_bounds_common(allow_plus)?)
|
||||
} else {
|
||||
let msg = format!("expected type, found {}", self.this_token_descr());
|
||||
return Err(self.fatal(&msg));
|
||||
};
|
||||
|
||||
let sp = mk_sp(lo, self.prev_span.hi);
|
||||
Ok(P(Ty {id: ast::DUMMY_NODE_ID, node: t, span: sp}))
|
||||
let span = mk_sp(lo, self.prev_span.hi);
|
||||
let ty = Ty { node: node, span: span, id: ast::DUMMY_NODE_ID };
|
||||
|
||||
// Try to recover from use of `+` with incorrect priority.
|
||||
self.maybe_recover_from_bad_type_plus(allow_plus, &ty)?;
|
||||
|
||||
Ok(P(ty))
|
||||
}
|
||||
|
||||
pub fn parse_borrowed_pointee(&mut self) -> PResult<'a, TyKind> {
|
||||
// look for `&'lt` or `&'foo ` and interpret `foo` as the region name:
|
||||
let opt_lifetime = self.eat_lifetime();
|
||||
let mutbl = self.parse_mutability()?;
|
||||
fn maybe_recover_from_bad_type_plus(&mut self, allow_plus: bool, ty: &Ty) -> PResult<'a, ()> {
|
||||
// Do not add `+` to expected tokens.
|
||||
if !allow_plus || self.token != token::BinOp(token::Plus) {
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
self.bump(); // `+`
|
||||
let bounds = self.parse_ty_param_bounds()?;
|
||||
let sum_span = mk_sp(ty.span.lo, self.prev_span.hi);
|
||||
|
||||
let mut err = struct_span_err!(self.sess.span_diagnostic, ty.span, E0178,
|
||||
"expected a path on the left-hand side of `+`, not `{}`", pprust::ty_to_string(&ty));
|
||||
err.span_label(ty.span, &format!("expected a path"));
|
||||
|
||||
match ty.node {
|
||||
TyKind::Rptr(ref lifetime, ref mut_ty) => {
|
||||
let sum_with_parens = pprust::to_string(|s| {
|
||||
use print::pp::word;
|
||||
use print::pprust::PrintState;
|
||||
|
||||
word(&mut s.s, "&")?;
|
||||
s.print_opt_lifetime(lifetime)?;
|
||||
s.print_mutability(mut_ty.mutbl)?;
|
||||
s.popen()?;
|
||||
s.print_type(&mut_ty.ty)?;
|
||||
s.print_bounds(" +", &bounds)?;
|
||||
s.pclose()
|
||||
});
|
||||
err.span_suggestion(sum_span, "try adding parentheses:", sum_with_parens);
|
||||
}
|
||||
TyKind::Ptr(..) | TyKind::BareFn(..) => {
|
||||
help!(&mut err, "perhaps you forgot parentheses?");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
err.emit();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_borrowed_pointee(&mut self) -> PResult<'a, TyKind> {
|
||||
let opt_lifetime = if self.check_lifetime() { Some(self.expect_lifetime()) } else { None };
|
||||
let mutbl = self.parse_mutability();
|
||||
let ty = self.parse_ty_no_plus()?;
|
||||
return Ok(TyKind::Rptr(opt_lifetime, MutTy { ty: ty, mutbl: mutbl }));
|
||||
}
|
||||
@ -1927,30 +1891,28 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse single lifetime 'a or nothing.
|
||||
pub fn eat_lifetime(&mut self) -> Option<Lifetime> {
|
||||
fn check_lifetime(&mut self) -> bool {
|
||||
self.expected_tokens.push(TokenType::Lifetime);
|
||||
self.token.is_lifetime()
|
||||
}
|
||||
|
||||
/// Parse single lifetime 'a or panic.
|
||||
fn expect_lifetime(&mut self) -> Lifetime {
|
||||
match self.token {
|
||||
token::Lifetime(ident) => {
|
||||
self.bump();
|
||||
Some(Lifetime {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
span: self.prev_span,
|
||||
name: ident.name
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
self.expected_tokens.push(TokenType::Lifetime);
|
||||
None
|
||||
Lifetime { name: ident.name, span: self.prev_span, id: ast::DUMMY_NODE_ID }
|
||||
}
|
||||
_ => self.span_bug(self.span, "not a lifetime")
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse mutability (`mut` or nothing).
|
||||
pub fn parse_mutability(&mut self) -> PResult<'a, Mutability> {
|
||||
fn parse_mutability(&mut self) -> Mutability {
|
||||
if self.eat_keyword(keywords::Mut) {
|
||||
Ok(Mutability::Mutable)
|
||||
Mutability::Mutable
|
||||
} else {
|
||||
Ok(Mutability::Immutable)
|
||||
Mutability::Immutable
|
||||
}
|
||||
}
|
||||
|
||||
@ -2207,7 +2169,7 @@ impl<'a> Parser<'a> {
|
||||
return self.parse_while_expr(None, lo, attrs);
|
||||
}
|
||||
if self.token.is_lifetime() {
|
||||
let label = Spanned { node: self.get_lifetime(),
|
||||
let label = Spanned { node: self.get_label(),
|
||||
span: self.span };
|
||||
let lo = self.span.lo;
|
||||
self.bump();
|
||||
@ -2230,7 +2192,7 @@ impl<'a> Parser<'a> {
|
||||
if self.eat_keyword(keywords::Continue) {
|
||||
let ex = if self.token.is_lifetime() {
|
||||
let ex = ExprKind::Continue(Some(Spanned{
|
||||
node: self.get_lifetime(),
|
||||
node: self.get_label(),
|
||||
span: self.span
|
||||
}));
|
||||
self.bump();
|
||||
@ -2267,7 +2229,7 @@ impl<'a> Parser<'a> {
|
||||
} else if self.eat_keyword(keywords::Break) {
|
||||
let lt = if self.token.is_lifetime() {
|
||||
let spanned_lt = Spanned {
|
||||
node: self.get_lifetime(),
|
||||
node: self.get_label(),
|
||||
span: self.span
|
||||
};
|
||||
self.bump();
|
||||
@ -2700,7 +2662,7 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
token::BinOp(token::And) | token::AndAnd => {
|
||||
self.expect_and()?;
|
||||
let m = self.parse_mutability()?;
|
||||
let m = self.parse_mutability();
|
||||
let e = self.parse_prefix_expr(None);
|
||||
let (span, e) = self.interpolated_or_expr_span(e)?;
|
||||
hi = span.hi;
|
||||
@ -3422,7 +3384,7 @@ impl<'a> Parser<'a> {
|
||||
token::BinOp(token::And) | token::AndAnd => {
|
||||
// Parse &pat / &mut pat
|
||||
self.expect_and()?;
|
||||
let mutbl = self.parse_mutability()?;
|
||||
let mutbl = self.parse_mutability();
|
||||
if let token::Lifetime(ident) = self.token {
|
||||
return Err(self.fatal(&format!("unexpected lifetime `{}` in pattern", ident)));
|
||||
}
|
||||
@ -3449,7 +3411,7 @@ impl<'a> Parser<'a> {
|
||||
pat = self.parse_pat_ident(BindingMode::ByValue(Mutability::Mutable))?;
|
||||
} else if self.eat_keyword(keywords::Ref) {
|
||||
// Parse ref ident @ pat / ref mut ident @ pat
|
||||
let mutbl = self.parse_mutability()?;
|
||||
let mutbl = self.parse_mutability();
|
||||
pat = self.parse_pat_ident(BindingMode::ByRef(mutbl))?;
|
||||
} else if self.eat_keyword(keywords::Box) {
|
||||
// Parse box pat
|
||||
@ -4069,30 +4031,32 @@ impl<'a> Parser<'a> {
|
||||
// BOUND = TY_BOUND | LT_BOUND
|
||||
// LT_BOUND = LIFETIME (e.g. `'a`)
|
||||
// TY_BOUND = [?] [for<LT_PARAM_DEFS>] SIMPLE_PATH (e.g. `?for<'a: 'b> m::Trait<'a>`)
|
||||
fn parse_ty_param_bounds(&mut self) -> PResult<'a, TyParamBounds>
|
||||
{
|
||||
fn parse_ty_param_bounds_common(&mut self, allow_plus: bool) -> PResult<'a, TyParamBounds> {
|
||||
let mut bounds = Vec::new();
|
||||
loop {
|
||||
let question = if self.eat(&token::Question) { Some(self.prev_span) } else { None };
|
||||
if let Some(lifetime) = self.eat_lifetime() {
|
||||
if self.check_lifetime() {
|
||||
if let Some(question_span) = question {
|
||||
self.span_err(question_span,
|
||||
"`?` may only modify trait bounds, not lifetime bounds");
|
||||
}
|
||||
bounds.push(RegionTyParamBound(lifetime));
|
||||
} else {if self.check_keyword(keywords::For) || self.check_path() {
|
||||
let poly_trait_ref = self.parse_poly_trait_ref()?;
|
||||
bounds.push(RegionTyParamBound(self.expect_lifetime()));
|
||||
} else if self.check_keyword(keywords::For) || self.check_path() {
|
||||
let lo = self.span.lo;
|
||||
let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
|
||||
let path = self.parse_path(PathStyle::Type)?;
|
||||
let poly_trait = PolyTraitRef::new(lifetime_defs, path, lo, self.prev_span.hi);
|
||||
let modifier = if question.is_some() {
|
||||
TraitBoundModifier::Maybe
|
||||
} else {
|
||||
TraitBoundModifier::None
|
||||
};
|
||||
bounds.push(TraitTyParamBound(poly_trait_ref, modifier));
|
||||
bounds.push(TraitTyParamBound(poly_trait, modifier));
|
||||
} else {
|
||||
break
|
||||
}}
|
||||
}
|
||||
|
||||
if !self.eat(&token::BinOp(token::Plus)) {
|
||||
if !allow_plus || !self.eat(&token::BinOp(token::Plus)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -4100,12 +4064,16 @@ impl<'a> Parser<'a> {
|
||||
return Ok(bounds);
|
||||
}
|
||||
|
||||
fn parse_ty_param_bounds(&mut self) -> PResult<'a, TyParamBounds> {
|
||||
self.parse_ty_param_bounds_common(true)
|
||||
}
|
||||
|
||||
// Parse bounds of a type parameter `BOUND + BOUND + BOUND` without trailing `+`.
|
||||
// BOUND = LT_BOUND (e.g. `'a`)
|
||||
fn parse_lt_param_bounds(&mut self) -> Vec<Lifetime> {
|
||||
let mut lifetimes = Vec::new();
|
||||
while let Some(lifetime) = self.eat_lifetime() {
|
||||
lifetimes.push(lifetime);
|
||||
while self.check_lifetime() {
|
||||
lifetimes.push(self.expect_lifetime());
|
||||
|
||||
if !self.eat(&token::BinOp(token::Plus)) {
|
||||
break
|
||||
@ -4150,7 +4118,8 @@ impl<'a> Parser<'a> {
|
||||
let mut seen_ty_param = false;
|
||||
loop {
|
||||
let attrs = self.parse_outer_attributes()?;
|
||||
if let Some(lifetime) = self.eat_lifetime() {
|
||||
if self.check_lifetime() {
|
||||
let lifetime = self.expect_lifetime();
|
||||
// Parse lifetime parameter.
|
||||
let bounds = if self.eat(&token::Colon) {
|
||||
self.parse_lt_param_bounds()
|
||||
@ -4166,7 +4135,7 @@ impl<'a> Parser<'a> {
|
||||
self.span_err(self.prev_span,
|
||||
"lifetime parameters must be declared prior to type parameters");
|
||||
}
|
||||
} else {if self.check_ident() {
|
||||
} else if self.check_ident() {
|
||||
// Parse type parameter.
|
||||
ty_params.push(self.parse_ty_param(attrs)?);
|
||||
seen_ty_param = true;
|
||||
@ -4178,7 +4147,7 @@ impl<'a> Parser<'a> {
|
||||
&format!("trailing attribute after {} parameters", param_kind));
|
||||
}
|
||||
break
|
||||
}}
|
||||
}
|
||||
|
||||
if !self.eat(&token::Comma) {
|
||||
break
|
||||
@ -4224,14 +4193,14 @@ impl<'a> Parser<'a> {
|
||||
let mut seen_type = false;
|
||||
let mut seen_binding = false;
|
||||
loop {
|
||||
if let Some(lifetime) = self.eat_lifetime() {
|
||||
if self.check_lifetime() && self.look_ahead(1, |t| t != &token::BinOp(token::Plus)) {
|
||||
// Parse lifetime argument.
|
||||
lifetimes.push(lifetime);
|
||||
lifetimes.push(self.expect_lifetime());
|
||||
if seen_type || seen_binding {
|
||||
self.span_err(self.prev_span,
|
||||
"lifetime parameters must be declared prior to type parameters");
|
||||
}
|
||||
} else {if self.check_ident() && self.look_ahead(1, |t| t == &token::Eq) {
|
||||
} else if self.check_ident() && self.look_ahead(1, |t| t == &token::Eq) {
|
||||
// Parse associated type binding.
|
||||
let lo = self.span.lo;
|
||||
let ident = self.parse_ident()?;
|
||||
@ -4254,7 +4223,7 @@ impl<'a> Parser<'a> {
|
||||
seen_type = true;
|
||||
} else {
|
||||
break
|
||||
}}
|
||||
}
|
||||
|
||||
if !self.eat(&token::Comma) {
|
||||
break
|
||||
@ -4299,7 +4268,8 @@ impl<'a> Parser<'a> {
|
||||
|
||||
loop {
|
||||
let lo = self.span.lo;
|
||||
if let Some(lifetime) = self.eat_lifetime() {
|
||||
if self.check_lifetime() && self.look_ahead(1, |t| t != &token::BinOp(token::Plus)) {
|
||||
let lifetime = self.expect_lifetime();
|
||||
// Bounds starting with a colon are mandatory, but possibly empty.
|
||||
self.expect(&token::Colon)?;
|
||||
let bounds = self.parse_lt_param_bounds();
|
||||
@ -4310,7 +4280,7 @@ impl<'a> Parser<'a> {
|
||||
bounds: bounds,
|
||||
}
|
||||
));
|
||||
} else {if self.check_type() {
|
||||
} else if self.check_type() {
|
||||
// Parse optional `for<'a, 'b>`.
|
||||
// This `for` is parsed greedily and applies to the whole predicate,
|
||||
// the bounded type can have its own `for` applying only to it.
|
||||
@ -4348,7 +4318,7 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}}
|
||||
}
|
||||
|
||||
if !self.eat(&token::Comma) {
|
||||
break
|
||||
@ -4453,13 +4423,13 @@ impl<'a> Parser<'a> {
|
||||
} else if self.look_ahead(1, |t| t.is_lifetime()) &&
|
||||
isolated_self(self, 2) {
|
||||
self.bump();
|
||||
let lt = self.eat_lifetime().expect("not a lifetime");
|
||||
let lt = self.expect_lifetime();
|
||||
(SelfKind::Region(Some(lt), Mutability::Immutable), expect_ident(self))
|
||||
} else if self.look_ahead(1, |t| t.is_lifetime()) &&
|
||||
self.look_ahead(2, |t| t.is_keyword(keywords::Mut)) &&
|
||||
isolated_self(self, 3) {
|
||||
self.bump();
|
||||
let lt = self.eat_lifetime().expect("not a lifetime");
|
||||
let lt = self.expect_lifetime();
|
||||
self.bump();
|
||||
(SelfKind::Region(Some(lt), Mutability::Mutable), expect_ident(self))
|
||||
} else {
|
||||
@ -4852,14 +4822,6 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a::B<String,i32>
|
||||
fn parse_trait_ref(&mut self) -> PResult<'a, TraitRef> {
|
||||
Ok(TraitRef {
|
||||
path: self.parse_path(PathStyle::Type)?,
|
||||
ref_id: ast::DUMMY_NODE_ID,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_late_bound_lifetime_defs(&mut self) -> PResult<'a, Vec<LifetimeDef>> {
|
||||
if self.eat_keyword(keywords::For) {
|
||||
self.expect_lt()?;
|
||||
@ -4875,18 +4837,6 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse for<'l> a::B<String,i32>
|
||||
fn parse_poly_trait_ref(&mut self) -> PResult<'a, PolyTraitRef> {
|
||||
let lo = self.span.lo;
|
||||
let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
|
||||
|
||||
Ok(PolyTraitRef {
|
||||
bound_lifetimes: lifetime_defs,
|
||||
trait_ref: self.parse_trait_ref()?,
|
||||
span: mk_sp(lo, self.prev_span.hi),
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse struct Foo { ... }
|
||||
fn parse_item_struct(&mut self) -> PResult<'a, ItemInfo> {
|
||||
let class_name = self.parse_ident()?;
|
||||
|
@ -103,6 +103,21 @@ fn ident_can_begin_expr(ident: ast::Ident) -> bool {
|
||||
].contains(&ident.name)
|
||||
}
|
||||
|
||||
fn ident_can_begin_type(ident: ast::Ident) -> bool {
|
||||
let ident_token: Token = Ident(ident);
|
||||
|
||||
!ident_token.is_any_keyword() ||
|
||||
ident_token.is_path_segment_keyword() ||
|
||||
[
|
||||
keywords::For.name(),
|
||||
keywords::Impl.name(),
|
||||
keywords::Fn.name(),
|
||||
keywords::Unsafe.name(),
|
||||
keywords::Extern.name(),
|
||||
keywords::Typeof.name(),
|
||||
].contains(&ident.name)
|
||||
}
|
||||
|
||||
#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum Token {
|
||||
/* Expression-operator symbols. */
|
||||
@ -182,23 +197,21 @@ impl Token {
|
||||
/// Returns `true` if the token can appear at the start of an expression.
|
||||
pub fn can_begin_expr(&self) -> bool {
|
||||
match *self {
|
||||
OpenDelim(..) => true,
|
||||
Ident(ident) => ident_can_begin_expr(ident),
|
||||
Literal(..) => true,
|
||||
Not => true,
|
||||
BinOp(Minus) => true,
|
||||
BinOp(Star) => true,
|
||||
BinOp(And) => true,
|
||||
BinOp(Or) => true, // in lambda syntax
|
||||
OrOr => true, // in lambda syntax
|
||||
AndAnd => true, // double borrow
|
||||
Ident(ident) => ident_can_begin_expr(ident), // value name or keyword
|
||||
OpenDelim(..) => true, // tuple, array or block
|
||||
Literal(..) => true, // literal
|
||||
Not => true, // operator not
|
||||
BinOp(Minus) => true, // unary minus
|
||||
BinOp(Star) => true, // dereference
|
||||
BinOp(Or) | OrOr => true, // closure
|
||||
BinOp(And) => true, // reference
|
||||
AndAnd => true, // double reference
|
||||
DotDot | DotDotDot => true, // range notation
|
||||
Lt | BinOp(Shl) => true, // associated path
|
||||
ModSep => true,
|
||||
Pound => true, // for expression attributes
|
||||
ModSep => true, // global path
|
||||
Pound => true, // expression attributes
|
||||
Interpolated(ref nt) => match **nt {
|
||||
NtExpr(..) => true,
|
||||
NtIdent(..) => true,
|
||||
NtBlock(..) => true,
|
||||
NtPath(..) => true,
|
||||
_ => false,
|
||||
@ -210,19 +223,20 @@ impl Token {
|
||||
/// Returns `true` if the token can appear at the start of a type.
|
||||
pub fn can_begin_type(&self) -> bool {
|
||||
match *self {
|
||||
Ident(ident) => ident_can_begin_type(ident), // type name or keyword
|
||||
OpenDelim(Paren) => true, // tuple
|
||||
OpenDelim(Bracket) => true, // array
|
||||
Ident(..) => true, // type name or keyword
|
||||
Underscore => true, // placeholder
|
||||
Not => true, // never
|
||||
BinOp(Star) => true, // raw pointer
|
||||
BinOp(And) => true, // reference
|
||||
AndAnd => true, // double reference
|
||||
Question => true, // maybe bound in trait object
|
||||
Lifetime(..) => true, // lifetime bound in trait object
|
||||
Lt | BinOp(Shl) => true, // associated path
|
||||
ModSep => true, // global path
|
||||
Interpolated(ref nt) => match **nt {
|
||||
NtTy(..) => true,
|
||||
NtIdent(..) => true,
|
||||
NtPath(..) => true,
|
||||
_ => false,
|
||||
},
|
||||
|
@ -17,15 +17,12 @@ struct Bar<'a> {
|
||||
x: &'a Foo + 'a,
|
||||
//~^ ERROR E0178
|
||||
//~| NOTE expected a path
|
||||
//~| ERROR at least one non-builtin trait is required for an object type
|
||||
y: &'a mut Foo + 'a,
|
||||
//~^ ERROR E0178
|
||||
//~| NOTE expected a path
|
||||
//~| ERROR at least one non-builtin trait is required for an object type
|
||||
z: fn() -> Foo + 'a,
|
||||
//~^ ERROR E0178
|
||||
//~| NOTE expected a path
|
||||
//~| ERROR at least one non-builtin trait is required for an object type
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -9,7 +9,7 @@
|
||||
// except according to those terms.
|
||||
|
||||
fn main () {
|
||||
let sr: Vec<(u32, _, _) = vec![]; //~ ERROR expected one of `+`, `,`, or `>`, found `=`
|
||||
let sr: Vec<(u32, _, _) = vec![]; //~ ERROR expected one of `,` or `>`, found `=`
|
||||
let sr2: Vec<(u32, _, _)> = sr.iter().map(|(faction, th_sender, th_receiver)| {}).collect();
|
||||
//~^ ERROR cannot find value `sr` in this scope
|
||||
}
|
||||
|
@ -11,6 +11,6 @@
|
||||
mod foo {
|
||||
type T = ();
|
||||
struct S1(pub(foo) (), pub(T), pub(crate) (), pub(((), T)));
|
||||
struct S2(pub((foo)) ()); //~ ERROR expected one of `+` or `,`, found `(`
|
||||
//~| ERROR expected one of `+`, `;`, or `where`, found `(`
|
||||
struct S2(pub((foo)) ()); //~ ERROR expected `,`, found `(`
|
||||
//~| ERROR expected one of `;` or `where`, found `(`
|
||||
}
|
||||
|
@ -12,8 +12,8 @@ macro_rules! define_struct {
|
||||
($t:ty) => {
|
||||
struct S1(pub $t);
|
||||
struct S2(pub (foo) ());
|
||||
struct S3(pub $t ()); //~ ERROR expected one of `+` or `,`, found `(`
|
||||
//~| ERROR expected one of `+`, `;`, or `where`, found `(`
|
||||
struct S3(pub $t ()); //~ ERROR expected `,`, found `(`
|
||||
//~| ERROR expected one of `;` or `where`, found `(`
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,8 +12,8 @@ macro_rules! define_struct {
|
||||
($t:ty) => {
|
||||
struct S1(pub($t));
|
||||
struct S2(pub (foo) ());
|
||||
struct S3(pub($t) ()); //~ ERROR expected one of `+` or `,`, found `(`
|
||||
//~| ERROR expected one of `+`, `;`, or `where`, found `(`
|
||||
struct S3(pub($t) ()); //~ ERROR expected `,`, found `(`
|
||||
//~| ERROR expected one of `;` or `where`, found `(`
|
||||
}
|
||||
}
|
||||
|
||||
|
19
src/test/compile-fail/trait-object-macro-matcher.rs
Normal file
19
src/test/compile-fail/trait-object-macro-matcher.rs
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright 2017 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.
|
||||
|
||||
// `ty` matcher accepts trait object types
|
||||
|
||||
macro_rules! m {
|
||||
($t: ty) => ( let _: $t; )
|
||||
}
|
||||
|
||||
fn main() {
|
||||
m!(Copy + Send + 'static); //~ ERROR the trait `std::marker::Copy` cannot be made into an object
|
||||
}
|
@ -13,10 +13,9 @@ fn main() {
|
||||
//~^ ERROR expected a path
|
||||
//~| HELP try adding parentheses
|
||||
//~| SUGGESTION let _: &(Copy + 'static);
|
||||
//~| ERROR at least one non-builtin trait is required for an object type
|
||||
//~| ERROR the trait `std::marker::Copy` cannot be made into an object
|
||||
let _: &'static Copy + 'static;
|
||||
//~^ ERROR expected a path
|
||||
//~| HELP try adding parentheses
|
||||
//~| SUGGESTION let _: &'static (Copy + 'static);
|
||||
//~| ERROR at least one non-builtin trait is required for an object type
|
||||
}
|
||||
|
@ -10,6 +10,6 @@
|
||||
|
||||
// compile-flags: -Z parse-only
|
||||
|
||||
type A = Box<(Fn(D::Error) -> E) + 'static + Send + Sync>; // OK
|
||||
type A = Box<(Fn(D::Error) -> E) + 'static + Send + Sync>; // OK (but see #39318)
|
||||
|
||||
FAIL //~ ERROR
|
||||
|
@ -13,6 +13,6 @@
|
||||
struct Baz<U> where U: Eq(U); //This is parsed as the new Fn* style parenthesis syntax.
|
||||
struct Baz<U> where U: Eq(U) -> R; // Notice this parses as well.
|
||||
struct Baz<U>(U) where U: Eq; // This rightfully signals no error as well.
|
||||
struct Foo<T> where T: Copy, (T); //~ ERROR expected one of `+`, `:`, `==`, or `=`, found `;`
|
||||
struct Foo<T> where T: Copy, (T); //~ ERROR expected one of `:`, `==`, or `=`, found `;`
|
||||
|
||||
fn main() {}
|
||||
|
@ -10,4 +10,4 @@
|
||||
|
||||
// compile-flags: -Z parse-only
|
||||
|
||||
type bptr = &lifetime/isize; //~ ERROR expected one of `!`, `(`, `+`, `::`, `;`, or `<`, found `/`
|
||||
type bptr = &lifetime/isize; //~ ERROR expected one of `!`, `(`, `::`, `;`, or `<`, found `/`
|
||||
|
@ -10,4 +10,4 @@
|
||||
|
||||
// compile-flags: -Z parse-only
|
||||
|
||||
type mut_box = Box<mut isize>; //~ ERROR expected type, found keyword `mut`
|
||||
type mut_box = Box<mut isize>; //~ ERROR expected one of `>`, lifetime, or type, found `mut`
|
||||
|
@ -13,7 +13,7 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
fn main() {
|
||||
let x: Box<Debug+> = box 3 as Box<Debug+>;
|
||||
//~^ ERROR at least one type parameter bound must be specified
|
||||
//~^^ ERROR at least one type parameter bound must be specified
|
||||
let x: Box<Debug+> = box 3 as Box<Debug+>; // Trailing `+` is OK
|
||||
}
|
||||
|
||||
FAIL //~ ERROR
|
||||
|
20
src/test/parse-fail/trait-object-macro-matcher.rs
Normal file
20
src/test/parse-fail/trait-object-macro-matcher.rs
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright 2017 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.
|
||||
|
||||
// A single lifetime is not parsed as a type.
|
||||
// `ty` matcher in particular doesn't accept a single lifetime
|
||||
|
||||
macro_rules! m {
|
||||
($t: ty) => ( let _: $t; )
|
||||
}
|
||||
|
||||
fn main() {
|
||||
m!('static); //~ ERROR expected type, found `'static`
|
||||
}
|
19
src/test/parse-fail/trait-object-polytrait-priority.rs
Normal file
19
src/test/parse-fail/trait-object-polytrait-priority.rs
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright 2017 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 Trait<'a> {}
|
||||
|
||||
fn main() {
|
||||
let _: &for<'a> Trait<'a> + 'static;
|
||||
//~^ ERROR expected a path on the left-hand side of `+`, not `& for<'a>Trait<'a>`
|
||||
//~| NOTE expected a path
|
||||
//~| HELP try adding parentheses
|
||||
//~| SUGGESTION &( for<'a>Trait<'a> + 'static)
|
||||
}
|
@ -18,7 +18,7 @@ fn test1() -> Rc<for<'a> Fn(&'a usize) + 'static> {
|
||||
}
|
||||
}
|
||||
|
||||
fn test2() -> *mut for<'a> Fn(&'a usize) + 'static {
|
||||
fn test2() -> *mut (for<'a> Fn(&'a usize) + 'static) {
|
||||
if let Some(_) = Some(1) {
|
||||
loop{}
|
||||
} else {
|
||||
@ -27,4 +27,3 @@ fn test2() -> *mut for<'a> Fn(&'a usize) + 'static {
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user