Refactoring to allow multi-variable declarations.

This commit is contained in:
chriseth 2015-10-08 18:01:12 +02:00
parent 23865e3929
commit b9a166061b
7 changed files with 120 additions and 55 deletions

View File

@ -963,20 +963,31 @@ public:
* Definition of a variable as a statement inside a function. It requires a type name (which can * Definition of a variable as a statement inside a function. It requires a type name (which can
* also be "var") but the actual assignment can be missing. * also be "var") but the actual assignment can be missing.
* Examples: var a = 2; uint256 a; * Examples: var a = 2; uint256 a;
* As a second form, multiple variables can be declared, cannot have a type and must be assigned
* right away. If the first or last component is unnamed, it can "consume" an arbitrary number
* of components.
* Examples: var (a, b) = f(); var (a,,,c) = g(); var (a,) = d();
*/ */
class VariableDeclarationStatement: public Statement class VariableDeclarationStatement: public Statement
{ {
public: public:
VariableDeclarationStatement(SourceLocation const& _location, ASTPointer<VariableDeclaration> _variable): VariableDeclarationStatement(
Statement(_location), m_variable(_variable) {} SourceLocation const& _location,
std::vector<ASTPointer<VariableDeclaration>> const& _variables,
ASTPointer<Expression> const& _initialValue
):
Statement(_location), m_variables(_variables), m_initialValue(_initialValue) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
VariableDeclaration const& declaration() const { return *m_variable; } std::vector<ASTPointer<VariableDeclaration>> const& declarations() const { return m_variables; }
Expression const* expression() const { return m_variable->value().get(); } Expression const* initialValue() const { return m_initialValue.get(); }
private: private:
ASTPointer<VariableDeclaration> m_variable; /// List of variables, some of which can be empty pointers (unnamed components).
std::vector<ASTPointer<VariableDeclaration>> m_variables;
/// The assigned expression / initial value.
ASTPointer<Expression> m_initialValue;
}; };
/** /**

View File

@ -516,14 +516,26 @@ void ExpressionStatement::accept(ASTConstVisitor& _visitor) const
void VariableDeclarationStatement::accept(ASTVisitor& _visitor) void VariableDeclarationStatement::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
m_variable->accept(_visitor); {
for (ASTPointer<VariableDeclaration> const& var: m_variables)
if (var)
var->accept(_visitor);
if (m_initialValue)
m_initialValue->accept(_visitor);
}
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }
void VariableDeclarationStatement::accept(ASTConstVisitor& _visitor) const void VariableDeclarationStatement::accept(ASTConstVisitor& _visitor) const
{ {
if (_visitor.visit(*this)) if (_visitor.visit(*this))
m_variable->accept(_visitor); {
for (ASTPointer<VariableDeclaration> const& var: m_variables)
if (var)
var->accept(_visitor);
if (m_initialValue)
m_initialValue->accept(_visitor);
}
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }

View File

@ -623,10 +623,13 @@ bool Compiler::visit(VariableDeclarationStatement const& _variableDeclarationSta
{ {
StackHeightChecker checker(m_context); StackHeightChecker checker(m_context);
CompilerContext::LocationSetter locationSetter(m_context, _variableDeclarationStatement); CompilerContext::LocationSetter locationSetter(m_context, _variableDeclarationStatement);
if (Expression const* expression = _variableDeclarationStatement.expression()) solAssert(_variableDeclarationStatement.declarations().size() == 1, "To be implemented.");
solAssert(!!_variableDeclarationStatement.declarations().front(), "");
VariableDeclaration const& varDecl = *_variableDeclarationStatement.declarations().front();
if (Expression const* expression = _variableDeclarationStatement.initialValue())
{ {
compileExpression(*expression, _variableDeclarationStatement.declaration().annotation().type); compileExpression(*expression, varDecl.annotation().type);
CompilerUtils(m_context).moveToStackVariable(_variableDeclarationStatement.declaration()); CompilerUtils(m_context).moveToStackVariable(varDecl);
} }
checker.check(); checker.check();
return false; return false;

View File

@ -350,7 +350,9 @@ void DeclarationRegistrationHelper::endVisit(VariableDeclarationStatement& _vari
// Register the local variables with the function // Register the local variables with the function
// This does not fit here perfectly, but it saves us another AST visit. // This does not fit here perfectly, but it saves us another AST visit.
solAssert(m_currentFunction, "Variable declaration without function."); solAssert(m_currentFunction, "Variable declaration without function.");
m_currentFunction->addLocalVariable(_variableDeclarationStatement.declaration()); for (ASTPointer<VariableDeclaration> const& var: _variableDeclarationStatement.declarations())
if (var)
m_currentFunction->addLocalVariable(*var);
} }
bool DeclarationRegistrationHelper::visit(VariableDeclaration& _declaration) bool DeclarationRegistrationHelper::visit(VariableDeclaration& _declaration)

View File

@ -771,13 +771,35 @@ ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStateme
ASTPointer<TypeName> const& _lookAheadArrayType ASTPointer<TypeName> const& _lookAheadArrayType
) )
{ {
ASTNodeFactory nodeFactory(*this);
if (_lookAheadArrayType)
nodeFactory.setLocation(_lookAheadArrayType->location());
vector<ASTPointer<VariableDeclaration>> variables;
ASTPointer<Expression> value;
if (
!_lookAheadArrayType &&
m_scanner->currentToken() == Token::Var &&
m_scanner->peekNextToken() == Token::LParen
)
{
// Parse `var (a, b, ,, c) = ...` into a single VariableDeclarationStatement with multiple variables.
solAssert(false, "To be implemented.");
}
else
{
VarDeclParserOptions options; VarDeclParserOptions options;
options.allowVar = true; options.allowVar = true;
options.allowInitialValue = true;
options.allowLocationSpecifier = true; options.allowLocationSpecifier = true;
ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(options, _lookAheadArrayType); options.allowInitialValue = false;
ASTNodeFactory nodeFactory(*this, variable); variables.push_back(parseVariableDeclaration(options, _lookAheadArrayType));
return nodeFactory.createNode<VariableDeclarationStatement>(variable); }
if (m_scanner->currentToken() == Token::Assign)
{
m_scanner->next();
value = parseExpression();
nodeFactory.setEndPositionFromNode(value);
}
return nodeFactory.createNode<VariableDeclarationStatement>(variables, value);
} }
ASTPointer<ExpressionStatement> Parser::parseExpressionStatement( ASTPointer<ExpressionStatement> Parser::parseExpressionStatement(

View File

@ -424,16 +424,17 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
// Note that assignments before the first declaration are legal because of the special scoping // Note that assignments before the first declaration are legal because of the special scoping
// rules inherited from JavaScript. // rules inherited from JavaScript.
// This only infers the type from its type name. // type is filled either by ReferencesResolver directly from the type name or by
// If an explicit type is required, it throws, otherwise it returns TypePointer(); // TypeChecker at the VariableDeclarationStatement level.
TypePointer varType = _variable.annotation().type; TypePointer varType = _variable.annotation().type;
solAssert(!!varType, "Failed to infer variable type.");
if (_variable.isConstant()) if (_variable.isConstant())
{ {
if (!dynamic_cast<ContractDefinition const*>(_variable.scope())) if (!dynamic_cast<ContractDefinition const*>(_variable.scope()))
typeError(_variable, "Illegal use of \"constant\" specifier."); typeError(_variable, "Illegal use of \"constant\" specifier.");
if (!_variable.value()) if (!_variable.value())
typeError(_variable, "Uninitialized \"constant\" variable."); typeError(_variable, "Uninitialized \"constant\" variable.");
if (varType && !varType->isValueType()) if (!varType->isValueType())
{ {
bool constImplemented = false; bool constImplemented = false;
if (auto arrayType = dynamic_cast<ArrayType const*>(varType.get())) if (auto arrayType = dynamic_cast<ArrayType const*>(varType.get()))
@ -446,8 +447,6 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
); );
} }
} }
if (varType)
{
if (_variable.value()) if (_variable.value())
expectType(*_variable.value(), *varType); expectType(*_variable.value(), *varType);
else else
@ -462,27 +461,6 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
m_errors.push_back(err); m_errors.push_back(err);
} }
} }
}
else
{
// Infer type from value.
if (!_variable.value())
fatalTypeError(_variable, "Assignment necessary for type detection.");
_variable.value()->accept(*this);
TypePointer const& valueType = type(*_variable.value());
solAssert(!!valueType, "");
if (
valueType->category() == Type::Category::IntegerConstant &&
!dynamic_pointer_cast<IntegerConstantType const>(valueType)->integerType()
)
fatalTypeError(*_variable.value(), "Invalid integer constant " + valueType->toString() + ".");
else if (valueType->category() == Type::Category::Void)
fatalTypeError(_variable, "Variable cannot have void type.");
varType = valueType->mobileType();
}
solAssert(!!varType, "");
_variable.annotation().type = varType;
if (!_variable.isStateVariable()) if (!_variable.isStateVariable())
{ {
if (varType->dataStoredIn(DataLocation::Memory) || varType->dataStoredIn(DataLocation::CallData)) if (varType->dataStoredIn(DataLocation::Memory) || varType->dataStoredIn(DataLocation::CallData))
@ -621,6 +599,42 @@ void TypeChecker::endVisit(Return const& _return)
} }
} }
bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
{
solAssert(_statement.declarations().size() == 1, "To be implemented.");
solAssert(!!_statement.declarations().front(), "");
VariableDeclaration const& var = *_statement.declarations().front();
solAssert(!var.value(), "Value has to be tied to statement.");
if (!var.annotation().type)
{
solAssert(!var.typeName(), "");
// Infer type from value.
if (!_statement.initialValue())
fatalTypeError(_statement, "Assignment necessary for type detection.");
_statement.initialValue()->accept(*this);
TypePointer const& valueType = type(*_statement.initialValue());
solAssert(!!valueType, "");
if (
valueType->category() == Type::Category::IntegerConstant &&
!dynamic_pointer_cast<IntegerConstantType const>(valueType)->integerType()
)
fatalTypeError(*_statement.initialValue(), "Invalid integer constant " + valueType->toString() + ".");
else if (valueType->category() == Type::Category::Void)
fatalTypeError(_statement, "Variable cannot have void type.");
var.annotation().type = valueType->mobileType();
var.accept(*this);
return false;
}
else
{
var.accept(*this);
if (_statement.initialValue())
expectType(*_statement.initialValue(), *var.annotation().type);
}
return false;
}
void TypeChecker::endVisit(ExpressionStatement const& _statement) void TypeChecker::endVisit(ExpressionStatement const& _statement)
{ {
if (type(_statement.expression())->category() == Type::Category::IntegerConstant) if (type(_statement.expression())->category() == Type::Category::IntegerConstant)

View File

@ -87,6 +87,7 @@ private:
virtual bool visit(WhileStatement const& _whileStatement) override; virtual bool visit(WhileStatement const& _whileStatement) override;
virtual bool visit(ForStatement const& _forStatement) override; virtual bool visit(ForStatement const& _forStatement) override;
virtual void endVisit(Return const& _return) override; virtual void endVisit(Return const& _return) override;
virtual bool visit(VariableDeclarationStatement const& _variable) override;
virtual void endVisit(ExpressionStatement const& _statement) override; virtual void endVisit(ExpressionStatement const& _statement) override;
virtual bool visit(Assignment const& _assignment) override; virtual bool visit(Assignment const& _assignment) override;
virtual void endVisit(BinaryOperation const& _operation) override; virtual void endVisit(BinaryOperation const& _operation) override;