Auto merge of #23606 - quantheory:associated_const, r=nikomatsakis

Closes #17841.

The majority of the work should be done, e.g. trait and inherent impls, different forms of UFCS syntax, defaults, and cross-crate usage. It's probably enough to replace the constants in `f32`, `i8`, and so on, or close to good enough.

There is still some significant functionality missing from this commit:

 - ~~Associated consts can't be used in match patterns at all. This is simply because I haven't updated the relevant bits in the parser or `resolve`, but it's *probably* not hard to get working.~~
 - Since you can't select an impl for trait-associated consts until partway through type-checking, there are some problems with code that assumes that you can check constants earlier. Associated consts that are not in inherent impls cause ICEs if you try to use them in array sizes or match ranges. For similar reasons, `check_static_recursion` doesn't check them properly, so the stack goes ka-blooey if you use an associated constant that's recursively defined. That's a bit trickier to solve; I'm not entirely sure what the best approach is yet.
 - Dealing with consts associated with type parameters will raise some new issues (e.g. if you have a `T: Int` type parameter and want to use `<T>::ZERO`). See rust-lang/rfcs#865.
 - ~~Unused associated consts don't seem to trigger the `dead_code` lint when they should. Probably easy to fix.~~

Also, this is the first time I've been spelunking in rustc to such a large extent, so I've probably done some silly things in a couple of places.
This commit is contained in:
bors 2015-04-27 16:45:21 +00:00
commit 857ef6e272
93 changed files with 2804 additions and 727 deletions

View file

@ -17,8 +17,8 @@ use ast::{Public, Unsafety};
use ast::{Mod, BiAdd, Arg, Arm, Attribute, BindByRef, BindByValue};
use ast::{BiBitAnd, BiBitOr, BiBitXor, BiRem, BiLt, BiGt, Block};
use ast::{BlockCheckMode, CaptureByRef, CaptureByValue, CaptureClause};
use ast::{Crate, CrateConfig, Decl, DeclItem};
use ast::{DeclLocal, DefaultBlock, DefaultReturn};
use ast::{ConstImplItem, ConstTraitItem, Crate, CrateConfig};
use ast::{Decl, DeclItem, DeclLocal, DefaultBlock, DefaultReturn};
use ast::{UnDeref, BiDiv, EMPTY_CTXT, EnumDef, ExplicitSelf};
use ast::{Expr, Expr_, ExprAddrOf, ExprMatch, ExprAgain};
use ast::{ExprAssign, ExprAssignOp, ExprBinary, ExprBlock, ExprBox};
@ -40,8 +40,9 @@ use ast::{MacStmtWithBraces, MacStmtWithSemicolon, MacStmtWithoutBraces};
use ast::{MutImmutable, MutMutable, Mac_, MacInvocTT, MatchSource};
use ast::{MutTy, BiMul, Mutability};
use ast::{MethodImplItem, NamedField, UnNeg, NoReturn, UnNot};
use ast::{Pat, PatBox, PatEnum, PatIdent, PatLit, PatMac, PatRange, PatRegion};
use ast::{PatStruct, PatTup, PatVec, PatWild, PatWildMulti, PatWildSingle};
use ast::{Pat, PatBox, PatEnum, PatIdent, PatLit, PatQPath, PatMac, PatRange};
use ast::{PatRegion, PatStruct, PatTup, PatVec, PatWild, PatWildMulti};
use ast::PatWildSingle;
use ast::{PolyTraitRef, QSelf};
use ast::{Return, BiShl, BiShr, Stmt, StmtDecl};
use ast::{StmtExpr, StmtSemi, StmtMac, StructDef, StructField};
@ -109,6 +110,15 @@ pub enum PathParsingMode {
LifetimeAndTypesWithColons,
}
/// How to parse a qualified path, whether to allow trailing parameters.
#[derive(Copy, Clone, PartialEq)]
pub enum QPathParsingMode {
/// No trailing parameters, e.g. `<T as Trait>::Item`
NoParameters,
/// Optional parameters, e.g. `<T as Trait>::item::<'a, U>`
MaybeParameters,
}
/// How to parse a bound, whether to allow bound modifiers such as `?`.
#[derive(Copy, Clone, PartialEq)]
pub enum BoundParsingMode {
@ -1161,6 +1171,20 @@ impl<'a> Parser<'a> {
let TyParam {ident, bounds, default, ..} = try!(p.parse_ty_param());
try!(p.expect(&token::Semi));
(ident, TypeTraitItem(bounds, default))
} else if try!(p.eat_keyword(keywords::Const)) {
let ident = try!(p.parse_ident());
try!(p.expect(&token::Colon));
let ty = try!(p.parse_ty_sum());
let default = if p.check(&token::Eq) {
try!(p.bump());
let expr = try!(p.parse_expr_nopanic());
try!(p.commit_expr_expecting(&expr, token::Semi));
Some(expr)
} else {
try!(p.expect(&token::Semi));
None
};
(ident, ConstTraitItem(ty, default))
} else {
let style = try!(p.parse_unsafety());
let abi = if try!(p.eat_keyword(keywords::Extern)) {
@ -1334,36 +1358,9 @@ impl<'a> Parser<'a> {
try!(self.expect(&token::CloseDelim(token::Paren)));
TyTypeof(e)
} else if try!(self.eat_lt()) {
// QUALIFIED PATH `<TYPE as TRAIT_REF>::item`
let self_type = try!(self.parse_ty_sum());
let mut path = if try!(self.eat_keyword(keywords::As) ){
try!(self.parse_path(LifetimeAndTypesWithoutColons))
} else {
ast::Path {
span: self.span,
global: false,
segments: vec![]
}
};
let qself = QSelf {
ty: self_type,
position: path.segments.len()
};
try!(self.expect(&token::Gt));
try!(self.expect(&token::ModSep));
path.segments.push(ast::PathSegment {
identifier: try!(self.parse_ident()),
parameters: ast::PathParameters::none()
});
if path.segments.len() == 1 {
path.span.lo = self.last_span.lo;
}
path.span.hi = self.last_span.hi;
let (qself, path) =
try!(self.parse_qualified_path(QPathParsingMode::NoParameters));
TyPath(Some(qself), path)
} else if self.check(&token::ModSep) ||
@ -1580,6 +1577,61 @@ impl<'a> Parser<'a> {
}
}
// QUALIFIED PATH `<TYPE [as TRAIT_REF]>::IDENT[::<PARAMS>]`
// Assumes that the leading `<` has been parsed already.
pub fn parse_qualified_path(&mut self, mode: QPathParsingMode)
-> PResult<(QSelf, ast::Path)> {
let self_type = try!(self.parse_ty_sum());
let mut path = if try!(self.eat_keyword(keywords::As)) {
try!(self.parse_path(LifetimeAndTypesWithoutColons))
} else {
ast::Path {
span: self.span,
global: false,
segments: vec![]
}
};
let qself = QSelf {
ty: self_type,
position: path.segments.len()
};
try!(self.expect(&token::Gt));
try!(self.expect(&token::ModSep));
let item_name = try!(self.parse_ident());
let parameters = match mode {
QPathParsingMode::NoParameters => ast::PathParameters::none(),
QPathParsingMode::MaybeParameters => {
if try!(self.eat(&token::ModSep)) {
try!(self.expect_lt());
// Consumed `item::<`, go look for types
let (lifetimes, types, bindings) =
try!(self.parse_generic_values_after_lt());
ast::AngleBracketedParameters(ast::AngleBracketedParameterData {
lifetimes: lifetimes,
types: OwnedSlice::from_vec(types),
bindings: OwnedSlice::from_vec(bindings),
})
} else {
ast::PathParameters::none()
}
}
};
path.segments.push(ast::PathSegment {
identifier: item_name,
parameters: parameters
});
if path.segments.len() == 1 {
path.span.lo = self.last_span.lo;
}
path.span.hi = self.last_span.hi;
Ok((qself, path))
}
/// Parses a path and optional type parameter bounds, depending on the
/// mode. The `mode` parameter determines whether lifetimes, types, and/or
/// bounds are permitted and whether `::` must precede type parameter
@ -2043,49 +2095,10 @@ impl<'a> Parser<'a> {
}
_ => {
if try!(self.eat_lt()){
// QUALIFIED PATH `<TYPE as TRAIT_REF>::item::<'a, T>`
let self_type = try!(self.parse_ty_sum());
let mut path = if try!(self.eat_keyword(keywords::As) ){
try!(self.parse_path(LifetimeAndTypesWithoutColons))
} else {
ast::Path {
span: self.span,
global: false,
segments: vec![]
}
};
let qself = QSelf {
ty: self_type,
position: path.segments.len()
};
try!(self.expect(&token::Gt));
try!(self.expect(&token::ModSep));
let item_name = try!(self.parse_ident());
let parameters = if try!(self.eat(&token::ModSep) ){
try!(self.expect_lt());
// Consumed `item::<`, go look for types
let (lifetimes, types, bindings) =
try!(self.parse_generic_values_after_lt());
ast::AngleBracketedParameters(ast::AngleBracketedParameterData {
lifetimes: lifetimes,
types: OwnedSlice::from_vec(types),
bindings: OwnedSlice::from_vec(bindings),
})
} else {
ast::PathParameters::none()
};
path.segments.push(ast::PathSegment {
identifier: item_name,
parameters: parameters
});
let (qself, path) =
try!(self.parse_qualified_path(QPathParsingMode::MaybeParameters));
if path.segments.len() == 1 {
path.span.lo = self.last_span.lo;
}
path.span.hi = self.last_span.hi;
let hi = self.span.hi;
return Ok(self.mk_expr(lo, hi, ExprPath(Some(qself), path)));
}
if try!(self.eat_keyword(keywords::Move) ){
@ -3158,16 +3171,25 @@ impl<'a> Parser<'a> {
fn parse_pat_range_end(&mut self) -> PResult<P<Expr>> {
if self.is_path_start() {
let lo = self.span.lo;
let path = try!(self.parse_path(LifetimeAndTypesWithColons));
let (qself, path) = if try!(self.eat_lt()) {
// Parse a qualified path
let (qself, path) =
try!(self.parse_qualified_path(QPathParsingMode::NoParameters));
(Some(qself), path)
} else {
// Parse an unqualified path
(None, try!(self.parse_path(LifetimeAndTypesWithColons)))
};
let hi = self.last_span.hi;
Ok(self.mk_expr(lo, hi, ExprPath(None, path)))
Ok(self.mk_expr(lo, hi, ExprPath(qself, path)))
} else {
self.parse_literal_maybe_minus()
}
}
fn is_path_start(&self) -> bool {
(self.token == token::ModSep || self.token.is_ident() || self.token.is_path())
(self.token == token::Lt || self.token == token::ModSep
|| self.token.is_ident() || self.token.is_path())
&& !self.token.is_keyword(keywords::True) && !self.token.is_keyword(keywords::False)
}
@ -3243,25 +3265,44 @@ impl<'a> Parser<'a> {
pat = try!(self.parse_pat_ident(BindByValue(MutImmutable)));
}
} else {
// Parse as a general path
let path = try!(self.parse_path(LifetimeAndTypesWithColons));
let (qself, path) = if try!(self.eat_lt()) {
// Parse a qualified path
let (qself, path) =
try!(self.parse_qualified_path(QPathParsingMode::NoParameters));
(Some(qself), path)
} else {
// Parse an unqualified path
(None, try!(self.parse_path(LifetimeAndTypesWithColons)))
};
match self.token {
token::DotDotDot => {
// Parse range
let hi = self.last_span.hi;
let begin = self.mk_expr(lo, hi, ExprPath(None, path));
let begin = self.mk_expr(lo, hi, ExprPath(qself, path));
try!(self.bump());
let end = try!(self.parse_pat_range_end());
pat = PatRange(begin, end);
}
token::OpenDelim(token::Brace) => {
// Parse struct pattern
if qself.is_some() {
let span = self.span;
self.span_err(span,
"unexpected `{` after qualified path");
self.abort_if_errors();
}
// Parse struct pattern
try!(self.bump());
let (fields, etc) = try!(self.parse_pat_fields());
try!(self.bump());
pat = PatStruct(path, fields, etc);
}
token::OpenDelim(token::Paren) => {
if qself.is_some() {
let span = self.span;
self.span_err(span,
"unexpected `(` after qualified path");
self.abort_if_errors();
}
// Parse tuple struct or enum pattern
if self.look_ahead(1, |t| *t == token::DotDot) {
// This is a "top constructor only" pat
@ -3278,6 +3319,10 @@ impl<'a> Parser<'a> {
pat = PatEnum(path, Some(args));
}
}
_ if qself.is_some() => {
// Parse qualified path
pat = PatQPath(qself.unwrap(), path);
}
_ => {
// Parse nullary enum
pat = PatEnum(path, Some(vec![]));
@ -4349,6 +4394,14 @@ impl<'a> Parser<'a> {
let typ = try!(self.parse_ty_sum());
try!(self.expect(&token::Semi));
(name, TypeImplItem(typ))
} else if try!(self.eat_keyword(keywords::Const)) {
let name = try!(self.parse_ident());
try!(self.expect(&token::Colon));
let typ = try!(self.parse_ty_sum());
try!(self.expect(&token::Eq));
let expr = try!(self.parse_expr_nopanic());
try!(self.commit_expr_expecting(&expr, token::Semi));
(name, ConstImplItem(typ, expr))
} else {
let (name, inner_attrs, node) = try!(self.parse_impl_method(vis));
attrs.extend(inner_attrs.into_iter());