mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Refactoring to allow multi-variable declarations.
This commit is contained in:
parent
23865e3929
commit
b9a166061b
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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)
|
||||||
|
@ -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(
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user