aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorchriseth <c@ethdev.com>2015-10-13 05:02:35 +0800
committerchriseth <c@ethdev.com>2015-10-15 23:38:42 +0800
commit7ebd536e79215f06f5ce7e14591aa494d06032b6 (patch)
treecca1f7597e4c6ddd674db92337424b0fd412b501
parent7ba42f470753f9af25531017f319cf94eb26d3f2 (diff)
downloaddexon-solidity-7ebd536e79215f06f5ce7e14591aa494d06032b6.tar.gz
dexon-solidity-7ebd536e79215f06f5ce7e14591aa494d06032b6.tar.zst
dexon-solidity-7ebd536e79215f06f5ce7e14591aa494d06032b6.zip
Tuple expressions.
-rw-r--r--libsolidity/AST.h24
-rw-r--r--libsolidity/ASTForward.h1
-rw-r--r--libsolidity/ASTJsonConverter.cpp11
-rw-r--r--libsolidity/ASTJsonConverter.h2
-rw-r--r--libsolidity/ASTPrinter.cpp13
-rw-r--r--libsolidity/ASTPrinter.h2
-rw-r--r--libsolidity/ASTVisitor.h4
-rw-r--r--libsolidity/AST_accept.h18
-rw-r--r--libsolidity/Compiler.cpp1
-rw-r--r--libsolidity/Parser.cpp20
-rw-r--r--libsolidity/TypeChecker.cpp75
-rw-r--r--libsolidity/TypeChecker.h1
-rw-r--r--libsolidity/Types.cpp39
-rw-r--r--libsolidity/Types.h2
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp55
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp27
-rw-r--r--test/libsolidity/SolidityParser.cpp17
17 files changed, 294 insertions, 18 deletions
diff --git a/libsolidity/AST.h b/libsolidity/AST.h
index 9dd67cc2..fc1db3f3 100644
--- a/libsolidity/AST.h
+++ b/libsolidity/AST.h
@@ -1068,6 +1068,30 @@ private:
};
/**
+ * 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.
* Examples: ++i, delete x or !true
*/
diff --git a/libsolidity/ASTForward.h b/libsolidity/ASTForward.h
index 396cf50a..02dd054a 100644
--- a/libsolidity/ASTForward.h
+++ b/libsolidity/ASTForward.h
@@ -69,6 +69,7 @@ class VariableDeclarationStatement;
class ExpressionStatement;
class Expression;
class Assignment;
+class TupleExpression;
class UnaryOperation;
class BinaryOperation;
class FunctionCall;
diff --git a/libsolidity/ASTJsonConverter.cpp b/libsolidity/ASTJsonConverter.cpp
index 4c14f2b2..34012c73 100644
--- a/libsolidity/ASTJsonConverter.cpp
+++ b/libsolidity/ASTJsonConverter.cpp
@@ -226,6 +226,12 @@ bool ASTJsonConverter::visit(Assignment const& _node)
return true;
}
+bool ASTJsonConverter::visit(TupleExpression const&)
+{
+ addJsonNode("TupleExpression",{}, true);
+ return true;
+}
+
bool ASTJsonConverter::visit(UnaryOperation const& _node)
{
addJsonNode("UnaryOperation",
@@ -396,6 +402,11 @@ void ASTJsonConverter::endVisit(Assignment const&)
goUp();
}
+void ASTJsonConverter::endVisit(TupleExpression const&)
+{
+ goUp();
+}
+
void ASTJsonConverter::endVisit(UnaryOperation const&)
{
goUp();
diff --git a/libsolidity/ASTJsonConverter.h b/libsolidity/ASTJsonConverter.h
index 61f87860..a62259e2 100644
--- a/libsolidity/ASTJsonConverter.h
+++ b/libsolidity/ASTJsonConverter.h
@@ -68,6 +68,7 @@ public:
bool visit(VariableDeclarationStatement const& _node) override;
bool visit(ExpressionStatement const& _node) override;
bool visit(Assignment const& _node) override;
+ bool visit(TupleExpression const& _node) override;
bool visit(UnaryOperation const& _node) override;
bool visit(BinaryOperation const& _node) override;
bool visit(FunctionCall const& _node) override;
@@ -99,6 +100,7 @@ public:
void endVisit(VariableDeclarationStatement const&) override;
void endVisit(ExpressionStatement const&) override;
void endVisit(Assignment const&) override;
+ void endVisit(TupleExpression const&) override;
void endVisit(UnaryOperation const&) override;
void endVisit(BinaryOperation const&) override;
void endVisit(FunctionCall const&) override;
diff --git a/libsolidity/ASTPrinter.cpp b/libsolidity/ASTPrinter.cpp
index 534f7c78..cb231842 100644
--- a/libsolidity/ASTPrinter.cpp
+++ b/libsolidity/ASTPrinter.cpp
@@ -256,6 +256,14 @@ bool ASTPrinter::visit(Assignment const& _node)
return goDeeper();
}
+bool ASTPrinter::visit(TupleExpression const& _node)
+{
+ writeLine(string("TupleExpression"));
+ printType(_node);
+ printSourcePart(_node);
+ return goDeeper();
+}
+
bool ASTPrinter::visit(UnaryOperation const& _node)
{
writeLine(string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") +
@@ -477,6 +485,11 @@ void ASTPrinter::endVisit(Assignment const&)
m_indentation--;
}
+void ASTPrinter::endVisit(TupleExpression const&)
+{
+ m_indentation--;
+}
+
void ASTPrinter::endVisit(UnaryOperation const&)
{
m_indentation--;
diff --git a/libsolidity/ASTPrinter.h b/libsolidity/ASTPrinter.h
index a12ec0aa..95656436 100644
--- a/libsolidity/ASTPrinter.h
+++ b/libsolidity/ASTPrinter.h
@@ -76,6 +76,7 @@ public:
bool visit(VariableDeclarationStatement const& _node) override;
bool visit(ExpressionStatement const& _node) override;
bool visit(Assignment const& _node) override;
+ bool visit(TupleExpression const& _node) override;
bool visit(UnaryOperation const& _node) override;
bool visit(BinaryOperation const& _node) override;
bool visit(FunctionCall const& _node) override;
@@ -115,6 +116,7 @@ public:
void endVisit(VariableDeclarationStatement const&) override;
void endVisit(ExpressionStatement const&) override;
void endVisit(Assignment const&) override;
+ void endVisit(TupleExpression const&) override;
void endVisit(UnaryOperation const&) override;
void endVisit(BinaryOperation const&) override;
void endVisit(FunctionCall const&) override;
diff --git a/libsolidity/ASTVisitor.h b/libsolidity/ASTVisitor.h
index e665396c..3e50fb28 100644
--- a/libsolidity/ASTVisitor.h
+++ b/libsolidity/ASTVisitor.h
@@ -73,6 +73,7 @@ public:
virtual bool visit(VariableDeclarationStatement& _node) { return visitNode(_node); }
virtual bool visit(ExpressionStatement& _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(BinaryOperation& _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(ExpressionStatement& _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(BinaryOperation& _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(ExpressionStatement 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(BinaryOperation 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(ExpressionStatement 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(BinaryOperation const& _node) { endVisitNode(_node); }
virtual void endVisit(FunctionCall const& _node) { endVisitNode(_node); }
diff --git a/libsolidity/AST_accept.h b/libsolidity/AST_accept.h
index 994bfc8d..eb1f6098 100644
--- a/libsolidity/AST_accept.h
+++ b/libsolidity/AST_accept.h
@@ -559,6 +559,24 @@ void Assignment::accept(ASTConstVisitor& _visitor) const
_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)
{
if (_visitor.visit(*this))
diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp
index 7c6c7831..cc1228a1 100644
--- a/libsolidity/Compiler.cpp
+++ b/libsolidity/Compiler.cpp
@@ -637,6 +637,7 @@ bool Compiler::visit(VariableDeclarationStatement const& _variableDeclarationSta
for (size_t i = 0; i < assignments.size(); ++i)
{
size_t j = assignments.size() - i - 1;
+ solAssert(!!valueTypes[j], "");
VariableDeclaration const* varDecl = assignments[j];
if (!varDecl)
utils.popStackElement(*valueTypes[j]);
diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp
index ab2d0094..1bb16b84 100644
--- a/libsolidity/Parser.cpp
+++ b/libsolidity/Parser.cpp
@@ -806,6 +806,7 @@ ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStateme
)
{
ASTNodeFactory varDeclNodeFactory(*this);
+ varDeclNodeFactory.markEndPosition();
ASTPointer<ASTString> name = expectIdentifierToken();
var = varDeclNodeFactory.createNode<VariableDeclaration>(
ASTPointer<TypeName>(),
@@ -1009,10 +1010,25 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
break;
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();
- 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);
- return expression;
+ return nodeFactory.createNode<TupleExpression>(components);
}
default:
if (Token::isElementaryTypeName(token))
diff --git a/libsolidity/TypeChecker.cpp b/libsolidity/TypeChecker.cpp
index ca5b1eb7..c7b693bb 100644
--- a/libsolidity/TypeChecker.cpp
+++ b/libsolidity/TypeChecker.cpp
@@ -560,13 +560,31 @@ void TypeChecker::endVisit(Return const& _return)
return;
ParameterList const* params = _return.annotation().functionReturnParameters;
if (!params)
+ {
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)
typeError(_return, "Different number of arguments in return statement than in returns declaration.");
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());
if (!type(*_return.expression())->isImplicitlyConvertibleTo(*expected))
typeError(
@@ -590,7 +608,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
VariableDeclaration const& varDecl = *_statement.declarations().front();
if (!varDecl.annotation().type)
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))
{
@@ -610,10 +628,10 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
_statement.initialValue()->accept(*this);
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();
else
- valueTypes = TypePointers{_statement.initialValue()->annotation().type};
+ valueTypes = TypePointers{type(*_statement.initialValue())};
// Determine which component is assigned to which variable.
// 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;
}
+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)
{
// 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)
{
+ _expression.annotation().lValueRequested = true;
_expression.accept(*this);
if (!_expression.annotation().isLValue)
typeError(_expression, "Expression has to be an lvalue.");
- _expression.annotation().lValueRequested = true;
}
void TypeChecker::typeError(ASTNode const& _node, string const& _description)
diff --git a/libsolidity/TypeChecker.h b/libsolidity/TypeChecker.h
index d9cb39ae..7af5473b 100644
--- a/libsolidity/TypeChecker.h
+++ b/libsolidity/TypeChecker.h
@@ -90,6 +90,7 @@ private:
virtual bool visit(VariableDeclarationStatement const& _variable) override;
virtual void endVisit(ExpressionStatement const& _statement) override;
virtual bool visit(Assignment const& _assignment) override;
+ virtual bool visit(TupleExpression const& _tuple) override;
virtual void endVisit(BinaryOperation const& _operation) override;
virtual bool visit(UnaryOperation const& _operation) override;
virtual bool visit(FunctionCall const& _functionCall) override;
diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp
index 51df5fbf..d7beab26 100644
--- a/libsolidity/Types.cpp
+++ b/libsolidity/Types.cpp
@@ -1256,8 +1256,8 @@ string TupleType::toString(bool _short) const
return "tuple()";
string str = "tuple(";
for (auto const& t: m_components)
- str += t->toString(_short) + ", ";
- str.resize(str.size() - 2);
+ str += (t ? t->toString(_short) : "") + ",";
+ str.pop_back();
return str + ")";
}
@@ -1273,10 +1273,30 @@ unsigned TupleType::sizeOnStack() const
{
unsigned size = 0;
for (auto const& t: m_components)
- size += t->sizeOnStack();
+ size += t ? t->sizeOnStack() : 0;
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):
m_location(_isInternal ? Location::Internal : Location::External),
m_isConstant(_function.isDeclaredConst()),
@@ -1638,14 +1658,15 @@ FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary) const
parameterTypes.push_back(t);
}
- //@todo make this more intelligent once we support destructuring assignments
+ // Removes dynamic types.
TypePointers returnParameterTypes;
vector<string> returnParameterNames;
- if (!m_returnParameterTypes.empty() && m_returnParameterTypes.front()->calldataEncodedSize() > 0)
- {
- returnParameterTypes.push_back(m_returnParameterTypes.front());
- returnParameterNames.push_back(m_returnParameterNames.front());
- }
+ for (size_t i = 0; i < m_returnParameterTypes.size(); ++i)
+ if (m_returnParameterTypes[i]->calldataEncodedSize() > 0)
+ {
+ returnParameterTypes.push_back(m_returnParameterTypes[i]);
+ returnParameterNames.push_back(m_returnParameterNames[i]);
+ }
return make_shared<FunctionType>(
parameterTypes,
returnParameterTypes,
diff --git a/libsolidity/Types.h b/libsolidity/Types.h
index 5c4aacdc..e8d65c41 100644
--- a/libsolidity/Types.h
+++ b/libsolidity/Types.h
@@ -682,6 +682,7 @@ private:
/**
* 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
{
@@ -695,6 +696,7 @@ public:
virtual u256 storageSize() const override;
virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned sizeOnStack() const override;
+ virtual bool isImplicitlyConvertibleTo(Type const& _other) const override;
std::vector<TypePointer> const& components() const { return m_components; }
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index bee19010..1d9a403a 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -5677,6 +5677,61 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration)
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()
}
diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp
index 2558ba97..0b5a86d7 100644
--- a/test/libsolidity/SolidityNameAndTypeResolution.cpp
+++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp
@@ -2460,6 +2460,33 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_4)
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)
{
char const* text = R"(
diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp
index 35393811..9aa4a8ef 100644
--- a/test/libsolidity/SolidityParser.cpp
+++ b/test/libsolidity/SolidityParser.cpp
@@ -983,7 +983,7 @@ BOOST_AUTO_TEST_CASE(local_const_variable)
BOOST_AUTO_TEST_CASE(multi_variable_declaration)
{
char const* text = R"(
- library Lib {
+ contract C {
function f() {
var (a,b,c) = g();
var (d) = 2;
@@ -1000,6 +1000,21 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration)
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()
}