mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Tuple expressions.
This commit is contained in:
parent
7ba42f4707
commit
7ebd536e79
@ -1067,6 +1067,30 @@ private:
|
|||||||
ASTPointer<Expression> m_rightHandSide;
|
ASTPointer<Expression> m_rightHandSide;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tuple or just parenthesized expression.
|
||||||
|
* Examples: (1, 2), (x,), (x), ()
|
||||||
|
* Individual components might be empty shared pointers (as in the second example).
|
||||||
|
* The respective types in lvalue context are: 2-tuple, 2-tuple (with wildcard), type of x, 0-tuple
|
||||||
|
* Not in lvalue context: 2-tuple, _1_-tuple, type of x, 0-tuple.
|
||||||
|
*/
|
||||||
|
class TupleExpression: public Expression
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TupleExpression(
|
||||||
|
SourceLocation const& _location,
|
||||||
|
std::vector<ASTPointer<Expression>> const& _components
|
||||||
|
):
|
||||||
|
Expression(_location), m_components(_components) {}
|
||||||
|
virtual void accept(ASTVisitor& _visitor) override;
|
||||||
|
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||||
|
|
||||||
|
std::vector<ASTPointer<Expression>> const& components() const { return m_components; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<ASTPointer<Expression>> m_components;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Operation involving a unary operator, pre- or postfix.
|
* Operation involving a unary operator, pre- or postfix.
|
||||||
* Examples: ++i, delete x or !true
|
* Examples: ++i, delete x or !true
|
||||||
|
@ -69,6 +69,7 @@ class VariableDeclarationStatement;
|
|||||||
class ExpressionStatement;
|
class ExpressionStatement;
|
||||||
class Expression;
|
class Expression;
|
||||||
class Assignment;
|
class Assignment;
|
||||||
|
class TupleExpression;
|
||||||
class UnaryOperation;
|
class UnaryOperation;
|
||||||
class BinaryOperation;
|
class BinaryOperation;
|
||||||
class FunctionCall;
|
class FunctionCall;
|
||||||
|
@ -226,6 +226,12 @@ bool ASTJsonConverter::visit(Assignment const& _node)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ASTJsonConverter::visit(TupleExpression const&)
|
||||||
|
{
|
||||||
|
addJsonNode("TupleExpression",{}, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(UnaryOperation const& _node)
|
bool ASTJsonConverter::visit(UnaryOperation const& _node)
|
||||||
{
|
{
|
||||||
addJsonNode("UnaryOperation",
|
addJsonNode("UnaryOperation",
|
||||||
@ -396,6 +402,11 @@ void ASTJsonConverter::endVisit(Assignment const&)
|
|||||||
goUp();
|
goUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ASTJsonConverter::endVisit(TupleExpression const&)
|
||||||
|
{
|
||||||
|
goUp();
|
||||||
|
}
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(UnaryOperation const&)
|
void ASTJsonConverter::endVisit(UnaryOperation const&)
|
||||||
{
|
{
|
||||||
goUp();
|
goUp();
|
||||||
|
@ -68,6 +68,7 @@ public:
|
|||||||
bool visit(VariableDeclarationStatement const& _node) override;
|
bool visit(VariableDeclarationStatement const& _node) override;
|
||||||
bool visit(ExpressionStatement const& _node) override;
|
bool visit(ExpressionStatement const& _node) override;
|
||||||
bool visit(Assignment const& _node) override;
|
bool visit(Assignment const& _node) override;
|
||||||
|
bool visit(TupleExpression const& _node) override;
|
||||||
bool visit(UnaryOperation const& _node) override;
|
bool visit(UnaryOperation const& _node) override;
|
||||||
bool visit(BinaryOperation const& _node) override;
|
bool visit(BinaryOperation const& _node) override;
|
||||||
bool visit(FunctionCall const& _node) override;
|
bool visit(FunctionCall const& _node) override;
|
||||||
@ -99,6 +100,7 @@ public:
|
|||||||
void endVisit(VariableDeclarationStatement const&) override;
|
void endVisit(VariableDeclarationStatement const&) override;
|
||||||
void endVisit(ExpressionStatement const&) override;
|
void endVisit(ExpressionStatement const&) override;
|
||||||
void endVisit(Assignment const&) override;
|
void endVisit(Assignment const&) override;
|
||||||
|
void endVisit(TupleExpression const&) override;
|
||||||
void endVisit(UnaryOperation const&) override;
|
void endVisit(UnaryOperation const&) override;
|
||||||
void endVisit(BinaryOperation const&) override;
|
void endVisit(BinaryOperation const&) override;
|
||||||
void endVisit(FunctionCall const&) override;
|
void endVisit(FunctionCall const&) override;
|
||||||
|
@ -256,6 +256,14 @@ bool ASTPrinter::visit(Assignment const& _node)
|
|||||||
return goDeeper();
|
return goDeeper();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ASTPrinter::visit(TupleExpression const& _node)
|
||||||
|
{
|
||||||
|
writeLine(string("TupleExpression"));
|
||||||
|
printType(_node);
|
||||||
|
printSourcePart(_node);
|
||||||
|
return goDeeper();
|
||||||
|
}
|
||||||
|
|
||||||
bool ASTPrinter::visit(UnaryOperation const& _node)
|
bool ASTPrinter::visit(UnaryOperation const& _node)
|
||||||
{
|
{
|
||||||
writeLine(string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") +
|
writeLine(string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") +
|
||||||
@ -477,6 +485,11 @@ void ASTPrinter::endVisit(Assignment const&)
|
|||||||
m_indentation--;
|
m_indentation--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ASTPrinter::endVisit(TupleExpression const&)
|
||||||
|
{
|
||||||
|
m_indentation--;
|
||||||
|
}
|
||||||
|
|
||||||
void ASTPrinter::endVisit(UnaryOperation const&)
|
void ASTPrinter::endVisit(UnaryOperation const&)
|
||||||
{
|
{
|
||||||
m_indentation--;
|
m_indentation--;
|
||||||
|
@ -76,6 +76,7 @@ public:
|
|||||||
bool visit(VariableDeclarationStatement const& _node) override;
|
bool visit(VariableDeclarationStatement const& _node) override;
|
||||||
bool visit(ExpressionStatement const& _node) override;
|
bool visit(ExpressionStatement const& _node) override;
|
||||||
bool visit(Assignment const& _node) override;
|
bool visit(Assignment const& _node) override;
|
||||||
|
bool visit(TupleExpression const& _node) override;
|
||||||
bool visit(UnaryOperation const& _node) override;
|
bool visit(UnaryOperation const& _node) override;
|
||||||
bool visit(BinaryOperation const& _node) override;
|
bool visit(BinaryOperation const& _node) override;
|
||||||
bool visit(FunctionCall const& _node) override;
|
bool visit(FunctionCall const& _node) override;
|
||||||
@ -115,6 +116,7 @@ public:
|
|||||||
void endVisit(VariableDeclarationStatement const&) override;
|
void endVisit(VariableDeclarationStatement const&) override;
|
||||||
void endVisit(ExpressionStatement const&) override;
|
void endVisit(ExpressionStatement const&) override;
|
||||||
void endVisit(Assignment const&) override;
|
void endVisit(Assignment const&) override;
|
||||||
|
void endVisit(TupleExpression const&) override;
|
||||||
void endVisit(UnaryOperation const&) override;
|
void endVisit(UnaryOperation const&) override;
|
||||||
void endVisit(BinaryOperation const&) override;
|
void endVisit(BinaryOperation const&) override;
|
||||||
void endVisit(FunctionCall const&) override;
|
void endVisit(FunctionCall const&) override;
|
||||||
|
@ -73,6 +73,7 @@ public:
|
|||||||
virtual bool visit(VariableDeclarationStatement& _node) { return visitNode(_node); }
|
virtual bool visit(VariableDeclarationStatement& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(ExpressionStatement& _node) { return visitNode(_node); }
|
virtual bool visit(ExpressionStatement& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(Assignment& _node) { return visitNode(_node); }
|
virtual bool visit(Assignment& _node) { return visitNode(_node); }
|
||||||
|
virtual bool visit(TupleExpression& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(UnaryOperation& _node) { return visitNode(_node); }
|
virtual bool visit(UnaryOperation& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(BinaryOperation& _node) { return visitNode(_node); }
|
virtual bool visit(BinaryOperation& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(FunctionCall& _node) { return visitNode(_node); }
|
virtual bool visit(FunctionCall& _node) { return visitNode(_node); }
|
||||||
@ -113,6 +114,7 @@ public:
|
|||||||
virtual void endVisit(VariableDeclarationStatement& _node) { endVisitNode(_node); }
|
virtual void endVisit(VariableDeclarationStatement& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(ExpressionStatement& _node) { endVisitNode(_node); }
|
virtual void endVisit(ExpressionStatement& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(Assignment& _node) { endVisitNode(_node); }
|
virtual void endVisit(Assignment& _node) { endVisitNode(_node); }
|
||||||
|
virtual void endVisit(TupleExpression& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(UnaryOperation& _node) { endVisitNode(_node); }
|
virtual void endVisit(UnaryOperation& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(BinaryOperation& _node) { endVisitNode(_node); }
|
virtual void endVisit(BinaryOperation& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(FunctionCall& _node) { endVisitNode(_node); }
|
virtual void endVisit(FunctionCall& _node) { endVisitNode(_node); }
|
||||||
@ -165,6 +167,7 @@ public:
|
|||||||
virtual bool visit(VariableDeclarationStatement const& _node) { return visitNode(_node); }
|
virtual bool visit(VariableDeclarationStatement const& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(ExpressionStatement const& _node) { return visitNode(_node); }
|
virtual bool visit(ExpressionStatement const& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(Assignment const& _node) { return visitNode(_node); }
|
virtual bool visit(Assignment const& _node) { return visitNode(_node); }
|
||||||
|
virtual bool visit(TupleExpression const& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(UnaryOperation const& _node) { return visitNode(_node); }
|
virtual bool visit(UnaryOperation const& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(BinaryOperation const& _node) { return visitNode(_node); }
|
virtual bool visit(BinaryOperation const& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(FunctionCall const& _node) { return visitNode(_node); }
|
virtual bool visit(FunctionCall const& _node) { return visitNode(_node); }
|
||||||
@ -205,6 +208,7 @@ public:
|
|||||||
virtual void endVisit(VariableDeclarationStatement const& _node) { endVisitNode(_node); }
|
virtual void endVisit(VariableDeclarationStatement const& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(ExpressionStatement const& _node) { endVisitNode(_node); }
|
virtual void endVisit(ExpressionStatement const& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(Assignment const& _node) { endVisitNode(_node); }
|
virtual void endVisit(Assignment const& _node) { endVisitNode(_node); }
|
||||||
|
virtual void endVisit(TupleExpression const& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(UnaryOperation const& _node) { endVisitNode(_node); }
|
virtual void endVisit(UnaryOperation const& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(BinaryOperation const& _node) { endVisitNode(_node); }
|
virtual void endVisit(BinaryOperation const& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(FunctionCall const& _node) { endVisitNode(_node); }
|
virtual void endVisit(FunctionCall const& _node) { endVisitNode(_node); }
|
||||||
|
@ -559,6 +559,24 @@ void Assignment::accept(ASTConstVisitor& _visitor) const
|
|||||||
_visitor.endVisit(*this);
|
_visitor.endVisit(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TupleExpression::accept(ASTVisitor& _visitor)
|
||||||
|
{
|
||||||
|
if (_visitor.visit(*this))
|
||||||
|
for (auto const& component: m_components)
|
||||||
|
if (component)
|
||||||
|
component->accept(_visitor);
|
||||||
|
_visitor.endVisit(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TupleExpression::accept(ASTConstVisitor& _visitor) const
|
||||||
|
{
|
||||||
|
if (_visitor.visit(*this))
|
||||||
|
for (auto const& component: m_components)
|
||||||
|
if (component)
|
||||||
|
component->accept(_visitor);
|
||||||
|
_visitor.endVisit(*this);
|
||||||
|
}
|
||||||
|
|
||||||
void UnaryOperation::accept(ASTVisitor& _visitor)
|
void UnaryOperation::accept(ASTVisitor& _visitor)
|
||||||
{
|
{
|
||||||
if (_visitor.visit(*this))
|
if (_visitor.visit(*this))
|
||||||
|
@ -637,6 +637,7 @@ bool Compiler::visit(VariableDeclarationStatement const& _variableDeclarationSta
|
|||||||
for (size_t i = 0; i < assignments.size(); ++i)
|
for (size_t i = 0; i < assignments.size(); ++i)
|
||||||
{
|
{
|
||||||
size_t j = assignments.size() - i - 1;
|
size_t j = assignments.size() - i - 1;
|
||||||
|
solAssert(!!valueTypes[j], "");
|
||||||
VariableDeclaration const* varDecl = assignments[j];
|
VariableDeclaration const* varDecl = assignments[j];
|
||||||
if (!varDecl)
|
if (!varDecl)
|
||||||
utils.popStackElement(*valueTypes[j]);
|
utils.popStackElement(*valueTypes[j]);
|
||||||
|
@ -806,6 +806,7 @@ ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStateme
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
ASTNodeFactory varDeclNodeFactory(*this);
|
ASTNodeFactory varDeclNodeFactory(*this);
|
||||||
|
varDeclNodeFactory.markEndPosition();
|
||||||
ASTPointer<ASTString> name = expectIdentifierToken();
|
ASTPointer<ASTString> name = expectIdentifierToken();
|
||||||
var = varDeclNodeFactory.createNode<VariableDeclaration>(
|
var = varDeclNodeFactory.createNode<VariableDeclaration>(
|
||||||
ASTPointer<TypeName>(),
|
ASTPointer<TypeName>(),
|
||||||
@ -1009,10 +1010,25 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
|
|||||||
break;
|
break;
|
||||||
case Token::LParen:
|
case Token::LParen:
|
||||||
{
|
{
|
||||||
|
// Tuple or parenthesized expression.
|
||||||
|
// Special cases: () is empty tuple type, (x) is not a real tuple, (x,) is one-dimensional tuple
|
||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
ASTPointer<Expression> expression = parseExpression();
|
vector<ASTPointer<Expression>> components;
|
||||||
|
if (m_scanner->currentToken() != Token::RParen)
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (m_scanner->currentToken() != Token::Comma && m_scanner->currentToken() != Token::RParen)
|
||||||
|
components.push_back(parseExpression());
|
||||||
|
else
|
||||||
|
components.push_back(ASTPointer<Expression>());
|
||||||
|
if (m_scanner->currentToken() == Token::RParen)
|
||||||
|
break;
|
||||||
|
else if (m_scanner->currentToken() == Token::Comma)
|
||||||
|
m_scanner->next();
|
||||||
|
}
|
||||||
|
nodeFactory.markEndPosition();
|
||||||
expectToken(Token::RParen);
|
expectToken(Token::RParen);
|
||||||
return expression;
|
return nodeFactory.createNode<TupleExpression>(components);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
if (Token::isElementaryTypeName(token))
|
if (Token::isElementaryTypeName(token))
|
||||||
|
@ -560,13 +560,31 @@ void TypeChecker::endVisit(Return const& _return)
|
|||||||
return;
|
return;
|
||||||
ParameterList const* params = _return.annotation().functionReturnParameters;
|
ParameterList const* params = _return.annotation().functionReturnParameters;
|
||||||
if (!params)
|
if (!params)
|
||||||
|
{
|
||||||
typeError(_return, "Return arguments not allowed.");
|
typeError(_return, "Return arguments not allowed.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TypePointers returnTypes;
|
||||||
|
for (auto const& var: params->parameters())
|
||||||
|
returnTypes.push_back(type(*var));
|
||||||
|
if (auto tupleType = dynamic_cast<TupleType const*>(type(*_return.expression()).get()))
|
||||||
|
{
|
||||||
|
if (tupleType->components().size() != params->parameters().size())
|
||||||
|
typeError(_return, "Different number of arguments in return statement than in returns declaration.");
|
||||||
|
else if (!tupleType->isImplicitlyConvertibleTo(TupleType(returnTypes)))
|
||||||
|
typeError(
|
||||||
|
*_return.expression(),
|
||||||
|
"Return argument type " +
|
||||||
|
type(*_return.expression())->toString() +
|
||||||
|
" is not implicitly convertible to expected type " +
|
||||||
|
TupleType(returnTypes).toString(false) +
|
||||||
|
"."
|
||||||
|
);
|
||||||
|
}
|
||||||
else if (params->parameters().size() != 1)
|
else if (params->parameters().size() != 1)
|
||||||
typeError(_return, "Different number of arguments in return statement than in returns declaration.");
|
typeError(_return, "Different number of arguments in return statement than in returns declaration.");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// this could later be changed such that the paramaters type is an anonymous struct type,
|
|
||||||
// but for now, we only allow one return parameter
|
|
||||||
TypePointer const& expected = type(*params->parameters().front());
|
TypePointer const& expected = type(*params->parameters().front());
|
||||||
if (!type(*_return.expression())->isImplicitlyConvertibleTo(*expected))
|
if (!type(*_return.expression())->isImplicitlyConvertibleTo(*expected))
|
||||||
typeError(
|
typeError(
|
||||||
@ -590,7 +608,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
|
|||||||
VariableDeclaration const& varDecl = *_statement.declarations().front();
|
VariableDeclaration const& varDecl = *_statement.declarations().front();
|
||||||
if (!varDecl.annotation().type)
|
if (!varDecl.annotation().type)
|
||||||
fatalTypeError(_statement, "Assignment necessary for type detection.");
|
fatalTypeError(_statement, "Assignment necessary for type detection.");
|
||||||
if (auto ref = dynamic_cast<ReferenceType const*>(varDecl.annotation().type.get()))
|
if (auto ref = dynamic_cast<ReferenceType const*>(type(varDecl).get()))
|
||||||
{
|
{
|
||||||
if (ref->dataStoredIn(DataLocation::Storage))
|
if (ref->dataStoredIn(DataLocation::Storage))
|
||||||
{
|
{
|
||||||
@ -610,10 +628,10 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
|
|||||||
|
|
||||||
_statement.initialValue()->accept(*this);
|
_statement.initialValue()->accept(*this);
|
||||||
TypePointers valueTypes;
|
TypePointers valueTypes;
|
||||||
if (auto tupleType = dynamic_cast<TupleType const*>(_statement.initialValue()->annotation().type.get()))
|
if (auto tupleType = dynamic_cast<TupleType const*>(type(*_statement.initialValue()).get()))
|
||||||
valueTypes = tupleType->components();
|
valueTypes = tupleType->components();
|
||||||
else
|
else
|
||||||
valueTypes = TypePointers{_statement.initialValue()->annotation().type};
|
valueTypes = TypePointers{type(*_statement.initialValue())};
|
||||||
|
|
||||||
// Determine which component is assigned to which variable.
|
// Determine which component is assigned to which variable.
|
||||||
// If numbers do not match, fill up if variables begin or end empty (not both).
|
// If numbers do not match, fill up if variables begin or end empty (not both).
|
||||||
@ -741,6 +759,51 @@ bool TypeChecker::visit(Assignment const& _assignment)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TypeChecker::visit(TupleExpression const& _tuple)
|
||||||
|
{
|
||||||
|
vector<ASTPointer<Expression>> const& components = _tuple.components();
|
||||||
|
TypePointers types;
|
||||||
|
if (_tuple.annotation().lValueRequested)
|
||||||
|
{
|
||||||
|
for (auto const& component: components)
|
||||||
|
if (component)
|
||||||
|
{
|
||||||
|
requireLValue(*component);
|
||||||
|
types.push_back(type(*component));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
types.push_back(TypePointer());
|
||||||
|
_tuple.annotation().type = make_shared<TupleType>(types);
|
||||||
|
// If some of the components are not LValues, the error is reported above.
|
||||||
|
_tuple.annotation().isLValue = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < components.size(); ++i)
|
||||||
|
{
|
||||||
|
// Outside of an lvalue-context, the only situation where a component can be empty is (x,).
|
||||||
|
if (!components[i] && !(i == 1 && components.size() == 2))
|
||||||
|
fatalTypeError(_tuple, "Tuple component cannot be empty.");
|
||||||
|
else if (components[i])
|
||||||
|
{
|
||||||
|
components[i]->accept(*this);
|
||||||
|
types.push_back(type(*components[i]));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
types.push_back(TypePointer());
|
||||||
|
}
|
||||||
|
if (components.size() == 1)
|
||||||
|
_tuple.annotation().type = type(*components[0]);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (components.size() == 2 && !components[1])
|
||||||
|
types.pop_back();
|
||||||
|
_tuple.annotation().type = make_shared<TupleType>(types);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool TypeChecker::visit(UnaryOperation const& _operation)
|
bool TypeChecker::visit(UnaryOperation const& _operation)
|
||||||
{
|
{
|
||||||
// Inc, Dec, Add, Sub, Not, BitNot, Delete
|
// Inc, Dec, Add, Sub, Not, BitNot, Delete
|
||||||
@ -1236,10 +1299,10 @@ void TypeChecker::expectType(Expression const& _expression, Type const& _expecte
|
|||||||
|
|
||||||
void TypeChecker::requireLValue(Expression const& _expression)
|
void TypeChecker::requireLValue(Expression const& _expression)
|
||||||
{
|
{
|
||||||
|
_expression.annotation().lValueRequested = true;
|
||||||
_expression.accept(*this);
|
_expression.accept(*this);
|
||||||
if (!_expression.annotation().isLValue)
|
if (!_expression.annotation().isLValue)
|
||||||
typeError(_expression, "Expression has to be an lvalue.");
|
typeError(_expression, "Expression has to be an lvalue.");
|
||||||
_expression.annotation().lValueRequested = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TypeChecker::typeError(ASTNode const& _node, string const& _description)
|
void TypeChecker::typeError(ASTNode const& _node, string const& _description)
|
||||||
|
@ -90,6 +90,7 @@ private:
|
|||||||
virtual bool visit(VariableDeclarationStatement const& _variable) 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 bool visit(TupleExpression const& _tuple) override;
|
||||||
virtual void endVisit(BinaryOperation const& _operation) override;
|
virtual void endVisit(BinaryOperation const& _operation) override;
|
||||||
virtual bool visit(UnaryOperation const& _operation) override;
|
virtual bool visit(UnaryOperation const& _operation) override;
|
||||||
virtual bool visit(FunctionCall const& _functionCall) override;
|
virtual bool visit(FunctionCall const& _functionCall) override;
|
||||||
|
@ -1256,8 +1256,8 @@ string TupleType::toString(bool _short) const
|
|||||||
return "tuple()";
|
return "tuple()";
|
||||||
string str = "tuple(";
|
string str = "tuple(";
|
||||||
for (auto const& t: m_components)
|
for (auto const& t: m_components)
|
||||||
str += t->toString(_short) + ", ";
|
str += (t ? t->toString(_short) : "") + ",";
|
||||||
str.resize(str.size() - 2);
|
str.pop_back();
|
||||||
return str + ")";
|
return str + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1273,10 +1273,30 @@ unsigned TupleType::sizeOnStack() const
|
|||||||
{
|
{
|
||||||
unsigned size = 0;
|
unsigned size = 0;
|
||||||
for (auto const& t: m_components)
|
for (auto const& t: m_components)
|
||||||
size += t->sizeOnStack();
|
size += t ? t->sizeOnStack() : 0;
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TupleType::isImplicitlyConvertibleTo(Type const& _other) const
|
||||||
|
{
|
||||||
|
if (auto tupleType = dynamic_cast<TupleType const*>(&_other))
|
||||||
|
{
|
||||||
|
if (components().size() != tupleType->components().size())
|
||||||
|
return false;
|
||||||
|
for (size_t i = 0; i < components().size(); ++i)
|
||||||
|
if ((!components()[i]) != (!tupleType->components()[i]))
|
||||||
|
return false;
|
||||||
|
else if (
|
||||||
|
components()[i] &&
|
||||||
|
!components()[i]->isImplicitlyConvertibleTo(*tupleType->components()[i])
|
||||||
|
)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal):
|
FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal):
|
||||||
m_location(_isInternal ? Location::Internal : Location::External),
|
m_location(_isInternal ? Location::Internal : Location::External),
|
||||||
m_isConstant(_function.isDeclaredConst()),
|
m_isConstant(_function.isDeclaredConst()),
|
||||||
@ -1638,14 +1658,15 @@ FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary) const
|
|||||||
parameterTypes.push_back(t);
|
parameterTypes.push_back(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
//@todo make this more intelligent once we support destructuring assignments
|
// Removes dynamic types.
|
||||||
TypePointers returnParameterTypes;
|
TypePointers returnParameterTypes;
|
||||||
vector<string> returnParameterNames;
|
vector<string> returnParameterNames;
|
||||||
if (!m_returnParameterTypes.empty() && m_returnParameterTypes.front()->calldataEncodedSize() > 0)
|
for (size_t i = 0; i < m_returnParameterTypes.size(); ++i)
|
||||||
{
|
if (m_returnParameterTypes[i]->calldataEncodedSize() > 0)
|
||||||
returnParameterTypes.push_back(m_returnParameterTypes.front());
|
{
|
||||||
returnParameterNames.push_back(m_returnParameterNames.front());
|
returnParameterTypes.push_back(m_returnParameterTypes[i]);
|
||||||
}
|
returnParameterNames.push_back(m_returnParameterNames[i]);
|
||||||
|
}
|
||||||
return make_shared<FunctionType>(
|
return make_shared<FunctionType>(
|
||||||
parameterTypes,
|
parameterTypes,
|
||||||
returnParameterTypes,
|
returnParameterTypes,
|
||||||
|
@ -682,6 +682,7 @@ private:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Type that can hold a finite sequence of values of different types.
|
* Type that can hold a finite sequence of values of different types.
|
||||||
|
* In some cases, the components are empty pointers (when used as placeholders).
|
||||||
*/
|
*/
|
||||||
class TupleType: public Type
|
class TupleType: public Type
|
||||||
{
|
{
|
||||||
@ -695,6 +696,7 @@ public:
|
|||||||
virtual u256 storageSize() const override;
|
virtual u256 storageSize() const override;
|
||||||
virtual bool canLiveOutsideStorage() const override { return false; }
|
virtual bool canLiveOutsideStorage() const override { return false; }
|
||||||
virtual unsigned sizeOnStack() const override;
|
virtual unsigned sizeOnStack() const override;
|
||||||
|
virtual bool isImplicitlyConvertibleTo(Type const& _other) const override;
|
||||||
|
|
||||||
std::vector<TypePointer> const& components() const { return m_components; }
|
std::vector<TypePointer> const& components() const { return m_components; }
|
||||||
|
|
||||||
|
@ -5677,6 +5677,61 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration)
|
|||||||
BOOST_CHECK(callContractFunction("f()", encodeArgs()) == encodeArgs(true));
|
BOOST_CHECK(callContractFunction("f()", encodeArgs()) == encodeArgs(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(tuples)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
uint[] data;
|
||||||
|
function g() internal returns (uint a, uint b, uint[] storage c) {
|
||||||
|
return (1, 2, data);
|
||||||
|
}
|
||||||
|
function h() external returns (uint a, uint b) {
|
||||||
|
return (5, 6);
|
||||||
|
}
|
||||||
|
function f() returns (uint) {
|
||||||
|
data.length = 1;
|
||||||
|
data[0] = 3;
|
||||||
|
uint a; uint b;
|
||||||
|
(a, b) = this.h();
|
||||||
|
if (a != 1 || b != 2) return 1;
|
||||||
|
uint[] storage c;
|
||||||
|
(a, b, c) = g();
|
||||||
|
if (a != 5 || b != 6 || c[0] != 3) return 2;
|
||||||
|
(a, b) = (b, a);
|
||||||
|
if (a != 6 || b != 5) return 3;
|
||||||
|
(a, , b, ) = (8, 9, 10, 11, 12);
|
||||||
|
if (a != 8 || b != 10) return 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
compileAndRun(sourceCode);
|
||||||
|
BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(destructuring_assignment_wildcard)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
function f() returns (uint) {
|
||||||
|
uint a;
|
||||||
|
uint b;
|
||||||
|
uint c;
|
||||||
|
(a,) = (1,);
|
||||||
|
if (a != 1) return 1;
|
||||||
|
(,b) = (2,3,4);
|
||||||
|
if (b != 4) return 2;
|
||||||
|
(, c,) = (5,6,7);
|
||||||
|
if (c != 6) return 3;
|
||||||
|
(a, b,) = (11, 12, 13);
|
||||||
|
if (a != 11 || b != 12) return 4;
|
||||||
|
(, a, b) = (11, 12, 13);
|
||||||
|
if (a != 12 || b != 13) return 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
compileAndRun(sourceCode);
|
||||||
|
BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0)));
|
||||||
|
}
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2460,6 +2460,33 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_4)
|
|||||||
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
|
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(tuples)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
function f() {
|
||||||
|
uint a = (1);
|
||||||
|
var (b,) = (1,);
|
||||||
|
var (c,d) = (1, 2 + a);
|
||||||
|
var (e,) = (1, 2, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK_NO_THROW(parseAndAnalyse(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(tuples_empty_components)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
function f() {
|
||||||
|
(1,,2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_5)
|
BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_5)
|
||||||
{
|
{
|
||||||
char const* text = R"(
|
char const* text = R"(
|
||||||
|
@ -983,7 +983,7 @@ BOOST_AUTO_TEST_CASE(local_const_variable)
|
|||||||
BOOST_AUTO_TEST_CASE(multi_variable_declaration)
|
BOOST_AUTO_TEST_CASE(multi_variable_declaration)
|
||||||
{
|
{
|
||||||
char const* text = R"(
|
char const* text = R"(
|
||||||
library Lib {
|
contract C {
|
||||||
function f() {
|
function f() {
|
||||||
var (a,b,c) = g();
|
var (a,b,c) = g();
|
||||||
var (d) = 2;
|
var (d) = 2;
|
||||||
@ -1000,6 +1000,21 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration)
|
|||||||
BOOST_CHECK(successParse(text));
|
BOOST_CHECK(successParse(text));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(tuples)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
function f() {
|
||||||
|
uint a = (1);
|
||||||
|
var (b,) = (1,);
|
||||||
|
var (c,d) = (1, 2 + a);
|
||||||
|
var (e,) = (1, 2, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK_NO_THROW(parseText(text));
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user