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
* also be "var") but the actual assignment can be missing.
* 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
{
public:
VariableDeclarationStatement(SourceLocation const& _location, ASTPointer<VariableDeclaration> _variable):
Statement(_location), m_variable(_variable) {}
VariableDeclarationStatement(
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(ASTConstVisitor& _visitor) const override;
VariableDeclaration const& declaration() const { return *m_variable; }
Expression const* expression() const { return m_variable->value().get(); }
std::vector<ASTPointer<VariableDeclaration>> const& declarations() const { return m_variables; }
Expression const* initialValue() const { return m_initialValue.get(); }
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)
{
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);
}
void VariableDeclarationStatement::accept(ASTConstVisitor& _visitor) const
{
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);
}

View File

@ -623,10 +623,13 @@ bool Compiler::visit(VariableDeclarationStatement const& _variableDeclarationSta
{
StackHeightChecker checker(m_context);
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);
CompilerUtils(m_context).moveToStackVariable(_variableDeclarationStatement.declaration());
compileExpression(*expression, varDecl.annotation().type);
CompilerUtils(m_context).moveToStackVariable(varDecl);
}
checker.check();
return false;

View File

@ -350,7 +350,9 @@ void DeclarationRegistrationHelper::endVisit(VariableDeclarationStatement& _vari
// Register the local variables with the function
// This does not fit here perfectly, but it saves us another AST visit.
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)

View File

@ -771,13 +771,35 @@ ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStateme
ASTPointer<TypeName> const& _lookAheadArrayType
)
{
VarDeclParserOptions options;
options.allowVar = true;
options.allowInitialValue = true;
options.allowLocationSpecifier = true;
ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(options, _lookAheadArrayType);
ASTNodeFactory nodeFactory(*this, variable);
return nodeFactory.createNode<VariableDeclarationStatement>(variable);
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;
options.allowVar = true;
options.allowLocationSpecifier = true;
options.allowInitialValue = false;
variables.push_back(parseVariableDeclaration(options, _lookAheadArrayType));
}
if (m_scanner->currentToken() == Token::Assign)
{
m_scanner->next();
value = parseExpression();
nodeFactory.setEndPositionFromNode(value);
}
return nodeFactory.createNode<VariableDeclarationStatement>(variables, value);
}
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
// rules inherited from JavaScript.
// This only infers the type from its type name.
// If an explicit type is required, it throws, otherwise it returns TypePointer();
// type is filled either by ReferencesResolver directly from the type name or by
// TypeChecker at the VariableDeclarationStatement level.
TypePointer varType = _variable.annotation().type;
solAssert(!!varType, "Failed to infer variable type.");
if (_variable.isConstant())
{
if (!dynamic_cast<ContractDefinition const*>(_variable.scope()))
typeError(_variable, "Illegal use of \"constant\" specifier.");
if (!_variable.value())
typeError(_variable, "Uninitialized \"constant\" variable.");
if (varType && !varType->isValueType())
if (!varType->isValueType())
{
bool constImplemented = false;
if (auto arrayType = dynamic_cast<ArrayType const*>(varType.get()))
@ -446,43 +447,20 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
);
}
}
if (varType)
{
if (_variable.value())
expectType(*_variable.value(), *varType);
else
{
if (auto ref = dynamic_cast<ReferenceType const *>(varType.get()))
if (ref->dataStoredIn(DataLocation::Storage) && _variable.isLocalVariable() && !_variable.isCallableParameter())
{
auto err = make_shared<Warning>();
*err <<
errinfo_sourceLocation(_variable.location()) <<
errinfo_comment("Uninitialized storage pointer. Did you mean '<type> memory " + _variable.name() + "'?");
m_errors.push_back(err);
}
}
}
if (_variable.value())
expectType(*_variable.value(), *varType);
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();
if (auto ref = dynamic_cast<ReferenceType const *>(varType.get()))
if (ref->dataStoredIn(DataLocation::Storage) && _variable.isLocalVariable() && !_variable.isCallableParameter())
{
auto err = make_shared<Warning>();
*err <<
errinfo_sourceLocation(_variable.location()) <<
errinfo_comment("Uninitialized storage pointer. Did you mean '<type> memory " + _variable.name() + "'?");
m_errors.push_back(err);
}
}
solAssert(!!varType, "");
_variable.annotation().type = varType;
if (!_variable.isStateVariable())
{
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)
{
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(ForStatement const& _forStatement) override;
virtual void endVisit(Return const& _return) override;
virtual bool visit(VariableDeclarationStatement const& _variable) override;
virtual void endVisit(ExpressionStatement const& _statement) override;
virtual bool visit(Assignment const& _assignment) override;
virtual void endVisit(BinaryOperation const& _operation) override;