Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(518)

Unified Diff: runtime/vm/parser.cc

Issue 10828053: Support const modifier in fields, variables (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « runtime/vm/parser.h ('k') | runtime/vm/raw_object.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: runtime/vm/parser.cc
===================================================================
--- runtime/vm/parser.cc (revision 9982)
+++ runtime/vm/parser.cc (working copy)
@@ -757,7 +757,9 @@
// leave the evaluation to the getter function.
const intptr_t expr_pos = TokenPos();
AstNode* expr = ParseExpr(kAllowConst, kConsumeCascades);
- if (field.is_const()) {
+ // TODO(hausner): Remove is_final check below once we support
+ // non-const finals.
+ if (field.is_const() || field.is_final()) {
// This getter will only be called once at compile time.
if (expr->EvalConstExpr() == NULL) {
ErrorMsg(expr_pos, "initializer must be a compile time constant");
@@ -2480,10 +2482,8 @@
ASSERT(field->type != NULL);
ASSERT(field->name_pos > 0);
ASSERT(current_member_ == field);
+ ASSERT(!field->has_const || field->has_final);
- if (field->has_const) {
- ErrorMsg("keyword 'const' not allowed in field declaration");
- }
if (field->has_abstract) {
ErrorMsg("keyword 'abstract' not allowed in field declaration");
}
@@ -2508,17 +2508,22 @@
// is compiled.
SkipExpr();
} else {
- if (field->has_static && field->has_final) {
+ if (field->has_const || (field->has_static && field->has_final)) {
ErrorMsg(field->name_pos,
- "static final field '%s' must have an initializer expression",
+ "%s%s field '%s' must have an initializer expression",
+ field->has_static ? "static " : "",
+ field->has_const ? "const" : "final",
field->name->ToCString());
}
}
// Create the field object.
+ // TODO(hausner): For now, all static final fields are constant. Remove
+ // this when lazy init of static variables is implemented.
class_field = Field::New(*field->name,
field->has_static,
field->has_final,
+ field->has_const || field->has_final,
field->name_pos);
class_field.set_type(*field->type);
class_field.set_has_initializer(has_initializer);
@@ -2798,11 +2803,16 @@
CurrentToken() == Token::kCOMMA ||
CurrentToken() == Token::kASSIGN) {
// Field definition.
+ if (member.has_const) {
+ // const fields are implicitly final.
+ member.has_final = true;
+ }
if (member.type == NULL) {
if (member.has_final) {
member.type = &Type::ZoneHandle(Type::DynamicType());
} else {
- ErrorMsg("missing 'var', 'final' or type in field declaration");
+ ErrorMsg("missing 'var', 'final', 'const' or type"
+ " in field declaration");
}
}
if (members->is_interface() && member.has_static && !member.has_final) {
@@ -3432,10 +3442,12 @@
void Parser::ParseTopLevelVariable(TopLevel* top_level) {
TRACE_PARSER("ParseTopLevelVariable");
const bool is_final = (CurrentToken() == Token::kFINAL);
+ const bool is_const = (CurrentToken() == Token::kCONST);
const bool is_static = true;
- const AbstractType& type = AbstractType::ZoneHandle(ParseFinalVarOrType(
- FLAG_enable_type_checks ? ClassFinalizer::kTryResolve :
- ClassFinalizer::kIgnore));
+ const AbstractType& type =
+ AbstractType::ZoneHandle(ParseConstFinalVarOrType(
+ FLAG_enable_type_checks ? ClassFinalizer::kTryResolve :
+ ClassFinalizer::kIgnore));
Field& field = Field::Handle();
Function& getter = Function::Handle();
while (true) {
@@ -3456,7 +3468,7 @@
var_name.ToCString());
}
- field = Field::New(var_name, is_static, is_final, name_pos);
+ field = Field::New(var_name, is_static, is_final, is_const, name_pos);
field.set_type(type);
field.set_value(Instance::Handle(Instance::null()));
top_level->fields.Add(field);
@@ -3471,8 +3483,8 @@
is_static, is_final, name_pos);
getter.set_result_type(type);
top_level->functions.Add(getter);
- } else if (is_final) {
- ErrorMsg(name_pos, "missing initializer for final variable");
+ } else if (is_final || is_const) {
+ ErrorMsg(name_pos, "missing initializer for final or const variable");
}
if (CurrentToken() == Token::kCOMMA) {
@@ -4065,8 +4077,9 @@
// Returns ast nodes of the variable initialization.
-AstNode* Parser::ParseVariableDeclaration(
- const AbstractType& type, bool is_final) {
+AstNode* Parser::ParseVariableDeclaration(const AbstractType& type,
+ bool is_final,
+ bool is_const) {
TRACE_PARSER("ParseVariableDeclaration");
ASSERT(IsIdentifier());
const intptr_t ident_pos = TokenPos();
@@ -4080,41 +4093,42 @@
// Variable initialization.
const intptr_t assign_pos = TokenPos();
ConsumeToken();
- AstNode* expr = ParseExpr(kAllowConst, kConsumeCascades);
+ AstNode* expr = ParseExpr(is_const, kConsumeCascades);
initialization = new StoreLocalNode(assign_pos, *variable, expr);
- } else if (is_final) {
- ErrorMsg(ident_pos, "missing initialization of 'final' variable");
+ } else if (is_final || is_const) {
+ ErrorMsg(ident_pos,
+ "missing initialization of 'final' or 'const' variable");
} else {
// Initialize variable with null.
AstNode* null_expr = new LiteralNode(ident_pos, Instance::ZoneHandle());
initialization = new StoreLocalNode(ident_pos, *variable, null_expr);
}
- // Add variable to cope after parsing the initalizer expression.
+ // Add variable to scope after parsing the initalizer expression.
// The expression must not be able to refer to the variable.
if (!current_block_->scope->AddVariable(variable)) {
ErrorMsg(ident_pos, "identifier '%s' already defined",
variable->name().ToCString());
}
- if (is_final) {
+ if (is_final || is_const) {
variable->set_is_final();
}
return initialization;
}
-// Parses ('var' | 'final' [type] | type).
-// The presence of 'final' must be detected and remembered before the call.
-// If a type is parsed, it may be resolved and finalized according to the given
-// type finalization mode.
-RawAbstractType* Parser::ParseFinalVarOrType(
+// Parses ('var' | 'final' [type] | 'const' [type] | type).
+// The presence of 'final' or 'const' must be detected and remembered
+// before the call. If a type is parsed, it may be resolved and finalized
+// according to the given type finalization mode.
+RawAbstractType* Parser::ParseConstFinalVarOrType(
ClassFinalizer::FinalizationKind finalization) {
- TRACE_PARSER("ParseFinalVarOrType");
+ TRACE_PARSER("ParseConstFinalVarOrType");
if (CurrentToken() == Token::kVAR) {
ConsumeToken();
return Type::DynamicType();
}
bool type_is_optional = false;
- if (CurrentToken() == Token::kFINAL) {
+ if ((CurrentToken() == Token::kFINAL) || (CurrentToken() == Token::kCONST)) {
ConsumeToken();
type_is_optional = true;
}
@@ -4146,14 +4160,15 @@
AstNode* Parser::ParseVariableDeclarationList() {
TRACE_PARSER("ParseVariableDeclarationList");
bool is_final = (CurrentToken() == Token::kFINAL);
- const AbstractType& type = AbstractType::ZoneHandle(ParseFinalVarOrType(
+ bool is_const = (CurrentToken() == Token::kCONST);
+ const AbstractType& type = AbstractType::ZoneHandle(ParseConstFinalVarOrType(
FLAG_enable_type_checks ? ClassFinalizer::kCanonicalize :
ClassFinalizer::kIgnore));
if (!IsIdentifier()) {
ErrorMsg("identifier expected");
}
- AstNode* initializers = ParseVariableDeclaration(type, is_final);
+ AstNode* initializers = ParseVariableDeclaration(type, is_final, is_const);
ASSERT(initializers != NULL);
while (CurrentToken() == Token::kCOMMA) {
ConsumeToken();
@@ -4165,7 +4180,7 @@
SequenceNode* sequence = NodeAsSequenceNode(initializers->token_pos(),
initializers,
NULL);
- sequence->Add(ParseVariableDeclaration(type, is_final));
+ sequence->Add(ParseVariableDeclaration(type, is_final, is_const));
initializers = sequence;
}
return initializers;
@@ -4438,21 +4453,41 @@
// Look ahead to detect whether the next tokens should be parsed as
// a variable declaration. Returns true if we detect the token pattern:
-// ('var' | 'final' | type ident (';' | '=' | ','))
+// 'var'
+// | 'final'
+// | const [type] ident (';' | '=' | ',')
+// | type ident (';' | '=' | ',')
// Token position remains unchanged.
bool Parser::IsVariableDeclaration() {
if ((CurrentToken() == Token::kVAR) ||
(CurrentToken() == Token::kFINAL)) {
return true;
}
- if (CurrentToken() != Token::kIDENT) {
- // Not a legal type identifier.
+ if ((CurrentToken() != Token::kIDENT) && (CurrentToken() != Token::kCONST)) {
+ // Not a legal type identifier or const keyword
return false;
}
const intptr_t saved_pos = TokenPos();
bool is_var_decl = false;
- if (TryParseOptionalType()) {
- if (IsIdentifier()) {
+ bool have_type = false;
+ if (CurrentToken() == Token::kCONST) {
+ ConsumeToken();
+ have_type = true; // Type is Dynamic.
+ }
+ if (IsIdentifier()) { // Type or variable name.
+ Token::Kind follower = LookaheadToken(1);
+ if ((follower == Token::kLT) || // Parameterized type.
+ (follower == Token::kPERIOD) || // Qualified class name of type.
+ Token::IsIdentifier(follower)) { // Variable name following a type.
+ // We see the beginning of something that could be a type.
+ const intptr_t type_pos = TokenPos();
+ if (TryParseOptionalType()) {
+ have_type = true;
+ } else {
+ SetPosition(type_pos);
+ }
+ }
+ if (have_type && IsIdentifier()) {
ConsumeToken();
if ((CurrentToken() == Token::kSEMICOLON) ||
(CurrentToken() == Token::kCOMMA) ||
@@ -4876,6 +4911,9 @@
SourceLabel* label) {
TRACE_PARSER("ParseForInStatement");
bool is_final = (CurrentToken() == Token::kFINAL);
+ if (CurrentToken() == Token::kCONST) {
+ ErrorMsg("Loop variable cannot be 'const'");
+ }
const String* loop_var_name = NULL;
LocalVariable* loop_var = NULL;
intptr_t loop_var_pos = 0;
@@ -4884,9 +4922,10 @@
loop_var_name = ExpectIdentifier("variable name expected");
} else {
// The case without a type is handled above, so require a type here.
- const AbstractType& type = AbstractType::ZoneHandle(ParseFinalVarOrType(
- FLAG_enable_type_checks ? ClassFinalizer::kCanonicalize :
- ClassFinalizer::kIgnore));
+ const AbstractType& type =
+ AbstractType::ZoneHandle(ParseConstFinalVarOrType(
+ FLAG_enable_type_checks ? ClassFinalizer::kCanonicalize :
+ ClassFinalizer::kIgnore));
loop_var_pos = TokenPos();
loop_var_name = ExpectIdentifier("variable name expected");
loop_var = new LocalVariable(loop_var_pos, *loop_var_name, type);
@@ -5137,8 +5176,11 @@
catch_param->is_final = (CurrentToken() == Token::kFINAL);
// The type of the catch parameter must always be resolved, even in unchecked
// mode.
+ if (CurrentToken() == Token::kCONST) {
+ ErrorMsg("Catch parameter cannot be 'const'");
+ }
catch_param->type = &AbstractType::ZoneHandle(
- ParseFinalVarOrType(ClassFinalizer::kCanonicalizeWellFormed));
+ ParseConstFinalVarOrType(ClassFinalizer::kCanonicalizeWellFormed));
catch_param->token_pos = TokenPos();
catch_param->var = ExpectIdentifier("identifier expected");
}
@@ -6534,7 +6576,9 @@
return initializing_getter;
}
// The field is initialized.
- if (field.is_const()) {
+ // TODO(hausner): Remove the is_final check when we support non-const
+ // final static variables.
+ if (field.is_const() || field.is_final()) {
ASSERT(field.value() != Object::sentinel());
ASSERT(field.value() != Object::transition_sentinel());
return new LiteralNode(ident_pos, Instance::ZoneHandle(field.value()));
@@ -6992,7 +7036,9 @@
ASSERT(field.is_static());
const Instance& value = Instance::Handle(field.value());
if (value.raw() == Object::transition_sentinel()) {
- if (field.is_const()) {
+ // TODO(hausner): Remove the check for is_final() once we support
+ // non-const final fields.
+ if (field.is_const() || field.is_final()) {
ErrorMsg("circular dependency while initializing static field '%s'",
String::Handle(field.name()).ToCString());
} else {
@@ -7006,7 +7052,9 @@
// This field has not been referenced yet and thus the value has
// not been evaluated. If the field is const, call the static getter method
// to evaluate the expression and canonicalize the value.
- if (field.is_const()) {
+ // TODO(hausner): Remove the check for is_final() once we support
+ // non-const final fields.
+ if (field.is_const() || field.is_final()) {
field.set_value(Instance::Handle(Object::transition_sentinel()));
const String& field_name = String::Handle(field.name());
const String& getter_name =
@@ -8186,10 +8234,15 @@
}
// Check if this interpolated string is still considered a compile time
// constant. If it is we need to evaluate if the current string part is
- // a constant or not.
+ // a constant or not. Only stings, numbers, booleans and null values
+ // are allowed in compile time const interpolations.
if (is_compiletime_const) {
const Object* const_expr = expr->EvalConstExpr();
- if (const_expr != NULL) {
+ if ((const_expr != NULL) &&
+ (const_expr->IsNumber() ||
+ const_expr->IsString() ||
+ const_expr->IsBool() ||
+ const_expr->IsNull())) {
// Change expr into a literal.
expr = new LiteralNode(expr_pos, EvaluateConstExpr(expr));
} else {
« no previous file with comments | « runtime/vm/parser.h ('k') | runtime/vm/raw_object.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698