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
|
||||
* 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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -770,14 +770,36 @@ ASTPointer<Statement> Parser::parseSimpleStatement()
|
||||
ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStatement(
|
||||
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;
|
||||
options.allowVar = true;
|
||||
options.allowInitialValue = true;
|
||||
options.allowLocationSpecifier = true;
|
||||
ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(options, _lookAheadArrayType);
|
||||
ASTNodeFactory nodeFactory(*this, variable);
|
||||
return nodeFactory.createNode<VariableDeclarationStatement>(variable);
|
||||
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(
|
||||
|
@ -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,8 +447,6 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
|
||||
);
|
||||
}
|
||||
}
|
||||
if (varType)
|
||||
{
|
||||
if (_variable.value())
|
||||
expectType(*_variable.value(), *varType);
|
||||
else
|
||||
@ -462,27 +461,6 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
|
||||
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 (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)
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user