mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #326 from guanqun/cond-expr
support conditional expression _ ? _ : _
This commit is contained in:
commit
194679f77a
@ -9,7 +9,7 @@ Control Structures
|
||||
|
||||
Most of the control structures from C/JavaScript are available in Solidity
|
||||
except for `switch` and `goto`. So
|
||||
there is: `if`, `else`, `while`, `for`, `break`, `continue`, `return`, with
|
||||
there is: `if`, `else`, `while`, `for`, `break`, `continue`, `return`, `? :`, with
|
||||
the usual semantics known from C / JavaScript.
|
||||
|
||||
Parentheses can *not* be omitted for conditionals, but curly brances can be omitted
|
||||
|
@ -744,6 +744,43 @@ void TypeChecker::endVisit(ExpressionStatement const& _statement)
|
||||
typeError(_statement.expression().location(), "Invalid integer constant.");
|
||||
}
|
||||
|
||||
bool TypeChecker::visit(Conditional const& _conditional)
|
||||
{
|
||||
expectType(_conditional.condition(), BoolType());
|
||||
|
||||
_conditional.trueExpression().accept(*this);
|
||||
_conditional.falseExpression().accept(*this);
|
||||
|
||||
TypePointer trueType = type(_conditional.trueExpression())->mobileType();
|
||||
TypePointer falseType = type(_conditional.falseExpression())->mobileType();
|
||||
|
||||
TypePointer commonType = Type::commonType(trueType, falseType);
|
||||
if (!commonType)
|
||||
{
|
||||
typeError(
|
||||
_conditional.location(),
|
||||
"True expression's type " +
|
||||
trueType->toString() +
|
||||
" doesn't match false expression's type " +
|
||||
falseType->toString() +
|
||||
"."
|
||||
);
|
||||
// even we can't find a common type, we have to set a type here,
|
||||
// otherwise the upper statement will not be able to check the type.
|
||||
commonType = trueType;
|
||||
}
|
||||
|
||||
_conditional.annotation().type = commonType;
|
||||
|
||||
if (_conditional.annotation().lValueRequested)
|
||||
typeError(
|
||||
_conditional.location(),
|
||||
"Conditional expression as left value is not supported yet."
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TypeChecker::visit(Assignment const& _assignment)
|
||||
{
|
||||
requireLValue(_assignment.leftHandSide());
|
||||
|
@ -90,6 +90,7 @@ private:
|
||||
virtual void endVisit(Return const& _return) override;
|
||||
virtual bool visit(VariableDeclarationStatement const& _variable) override;
|
||||
virtual void endVisit(ExpressionStatement const& _statement) override;
|
||||
virtual bool visit(Conditional const& _conditional) override;
|
||||
virtual bool visit(Assignment const& _assignment) override;
|
||||
virtual bool visit(TupleExpression const& _tuple) override;
|
||||
virtual void endVisit(BinaryOperation const& _operation) override;
|
||||
|
@ -1119,6 +1119,33 @@ public:
|
||||
ExpressionAnnotation& annotation() const override;
|
||||
};
|
||||
|
||||
class Conditional: public Expression
|
||||
{
|
||||
public:
|
||||
Conditional(
|
||||
SourceLocation const& _location,
|
||||
ASTPointer<Expression> const& _condition,
|
||||
ASTPointer<Expression> const& _trueExpression,
|
||||
ASTPointer<Expression> const& _falseExpression
|
||||
):
|
||||
Expression(_location),
|
||||
m_condition(_condition),
|
||||
m_trueExpression(_trueExpression),
|
||||
m_falseExpression(_falseExpression)
|
||||
{}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
|
||||
Expression const& condition() const { return *m_condition; }
|
||||
Expression const& trueExpression() const { return *m_trueExpression; }
|
||||
Expression const& falseExpression() const { return *m_falseExpression; }
|
||||
|
||||
private:
|
||||
ASTPointer<Expression> m_condition;
|
||||
ASTPointer<Expression> m_trueExpression;
|
||||
ASTPointer<Expression> m_falseExpression;
|
||||
};
|
||||
|
||||
/// Assignment, can also be a compound assignment.
|
||||
/// Examples: (a = 7 + 8) or (a *= 2)
|
||||
class Assignment: public Expression
|
||||
|
@ -69,6 +69,7 @@ class Throw;
|
||||
class VariableDeclarationStatement;
|
||||
class ExpressionStatement;
|
||||
class Expression;
|
||||
class Conditional;
|
||||
class Assignment;
|
||||
class TupleExpression;
|
||||
class UnaryOperation;
|
||||
|
@ -217,6 +217,12 @@ bool ASTJsonConverter::visit(ExpressionStatement const&)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ASTJsonConverter::visit(Conditional const&)
|
||||
{
|
||||
addJsonNode("Conditional", {}, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ASTJsonConverter::visit(Assignment const& _node)
|
||||
{
|
||||
addJsonNode("Assignment",
|
||||
@ -397,6 +403,11 @@ void ASTJsonConverter::endVisit(ExpressionStatement const&)
|
||||
goUp();
|
||||
}
|
||||
|
||||
void ASTJsonConverter::endVisit(Conditional const&)
|
||||
{
|
||||
goUp();
|
||||
}
|
||||
|
||||
void ASTJsonConverter::endVisit(Assignment const&)
|
||||
{
|
||||
goUp();
|
||||
|
@ -67,6 +67,7 @@ public:
|
||||
bool visit(Throw const& _node) override;
|
||||
bool visit(VariableDeclarationStatement const& _node) override;
|
||||
bool visit(ExpressionStatement const& _node) override;
|
||||
bool visit(Conditional const& _node) override;
|
||||
bool visit(Assignment const& _node) override;
|
||||
bool visit(TupleExpression const& _node) override;
|
||||
bool visit(UnaryOperation const& _node) override;
|
||||
@ -99,6 +100,7 @@ public:
|
||||
void endVisit(Throw const&) override;
|
||||
void endVisit(VariableDeclarationStatement const&) override;
|
||||
void endVisit(ExpressionStatement const&) override;
|
||||
void endVisit(Conditional const&) override;
|
||||
void endVisit(Assignment const&) override;
|
||||
void endVisit(TupleExpression const&) override;
|
||||
void endVisit(UnaryOperation const&) override;
|
||||
|
@ -248,6 +248,14 @@ bool ASTPrinter::visit(ExpressionStatement const& _node)
|
||||
return goDeeper();
|
||||
}
|
||||
|
||||
bool ASTPrinter::visit(Conditional const& _node)
|
||||
{
|
||||
writeLine("Conditional");
|
||||
printType(_node);
|
||||
printSourcePart(_node);
|
||||
return goDeeper();
|
||||
}
|
||||
|
||||
bool ASTPrinter::visit(Assignment const& _node)
|
||||
{
|
||||
writeLine(string("Assignment using operator ") + Token::toString(_node.assignmentOperator()));
|
||||
@ -480,6 +488,11 @@ void ASTPrinter::endVisit(ExpressionStatement const&)
|
||||
m_indentation--;
|
||||
}
|
||||
|
||||
void ASTPrinter::endVisit(Conditional const&)
|
||||
{
|
||||
m_indentation--;
|
||||
}
|
||||
|
||||
void ASTPrinter::endVisit(Assignment const&)
|
||||
{
|
||||
m_indentation--;
|
||||
|
@ -75,6 +75,7 @@ public:
|
||||
bool visit(Throw const& _node) override;
|
||||
bool visit(VariableDeclarationStatement const& _node) override;
|
||||
bool visit(ExpressionStatement const& _node) override;
|
||||
bool visit(Conditional const& _node) override;
|
||||
bool visit(Assignment const& _node) override;
|
||||
bool visit(TupleExpression const& _node) override;
|
||||
bool visit(UnaryOperation const& _node) override;
|
||||
@ -115,6 +116,7 @@ public:
|
||||
void endVisit(Throw const&) override;
|
||||
void endVisit(VariableDeclarationStatement const&) override;
|
||||
void endVisit(ExpressionStatement const&) override;
|
||||
void endVisit(Conditional const&) override;
|
||||
void endVisit(Assignment const&) override;
|
||||
void endVisit(TupleExpression const&) override;
|
||||
void endVisit(UnaryOperation const&) override;
|
||||
|
@ -73,6 +73,7 @@ public:
|
||||
virtual bool visit(Throw& _node) { return visitNode(_node); }
|
||||
virtual bool visit(VariableDeclarationStatement& _node) { return visitNode(_node); }
|
||||
virtual bool visit(ExpressionStatement& _node) { return visitNode(_node); }
|
||||
virtual bool visit(Conditional& _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); }
|
||||
@ -115,6 +116,7 @@ public:
|
||||
virtual void endVisit(Throw& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(VariableDeclarationStatement& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(ExpressionStatement& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(Conditional& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(Assignment& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(TupleExpression& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(UnaryOperation& _node) { endVisitNode(_node); }
|
||||
@ -169,6 +171,7 @@ public:
|
||||
virtual bool visit(Throw 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(Conditional 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); }
|
||||
@ -211,6 +214,7 @@ public:
|
||||
virtual void endVisit(Throw const& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(VariableDeclarationStatement const& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(ExpressionStatement const& _node) { endVisitNode(_node); }
|
||||
virtual void endVisit(Conditional 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); }
|
||||
|
@ -551,6 +551,28 @@ void VariableDeclarationStatement::accept(ASTConstVisitor& _visitor) const
|
||||
_visitor.endVisit(*this);
|
||||
}
|
||||
|
||||
void Conditional::accept(ASTVisitor& _visitor)
|
||||
{
|
||||
if (_visitor.visit(*this))
|
||||
{
|
||||
m_condition->accept(_visitor);
|
||||
m_trueExpression->accept(_visitor);
|
||||
m_falseExpression->accept(_visitor);
|
||||
}
|
||||
_visitor.endVisit(*this);
|
||||
}
|
||||
|
||||
void Conditional::accept(ASTConstVisitor& _visitor) const
|
||||
{
|
||||
if (_visitor.visit(*this))
|
||||
{
|
||||
m_condition->accept(_visitor);
|
||||
m_trueExpression->accept(_visitor);
|
||||
m_falseExpression->accept(_visitor);
|
||||
}
|
||||
_visitor.endVisit(*this);
|
||||
}
|
||||
|
||||
void Assignment::accept(ASTVisitor& _visitor)
|
||||
{
|
||||
if (_visitor.visit(*this))
|
||||
|
@ -176,6 +176,22 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
|
||||
m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction);
|
||||
}
|
||||
|
||||
bool ExpressionCompiler::visit(Conditional const& _condition)
|
||||
{
|
||||
CompilerContext::LocationSetter locationSetter(m_context, _condition);
|
||||
_condition.condition().accept(*this);
|
||||
eth::AssemblyItem trueTag = m_context.appendConditionalJump();
|
||||
_condition.falseExpression().accept(*this);
|
||||
utils().convertType(*_condition.falseExpression().annotation().type, *_condition.annotation().type);
|
||||
eth::AssemblyItem endTag = m_context.appendJumpToNew();
|
||||
m_context << trueTag;
|
||||
m_context.adjustStackOffset(-_condition.annotation().type->sizeOnStack());
|
||||
_condition.trueExpression().accept(*this);
|
||||
utils().convertType(*_condition.trueExpression().annotation().type, *_condition.annotation().type);
|
||||
m_context << endTag;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ExpressionCompiler::visit(Assignment const& _assignment)
|
||||
{
|
||||
CompilerContext::LocationSetter locationSetter(m_context, _assignment);
|
||||
|
@ -71,6 +71,7 @@ public:
|
||||
void appendConstStateVariableAccessor(const VariableDeclaration& _varDecl);
|
||||
|
||||
private:
|
||||
virtual bool visit(Conditional const& _condition) override;
|
||||
virtual bool visit(Assignment const& _assignment) override;
|
||||
virtual bool visit(TupleExpression const& _tuple) override;
|
||||
virtual bool visit(UnaryOperation const& _unaryOperation) override;
|
||||
|
@ -932,13 +932,26 @@ ASTPointer<Expression> Parser::parseExpression(
|
||||
)
|
||||
{
|
||||
ASTPointer<Expression> expression = parseBinaryExpression(4, _lookAheadIndexAccessStructure);
|
||||
if (!Token::isAssignmentOp(m_scanner->currentToken()))
|
||||
if (Token::isAssignmentOp(m_scanner->currentToken()))
|
||||
{
|
||||
Token::Value assignmentOperator = expectAssignmentOperator();
|
||||
ASTPointer<Expression> rightHandSide = parseExpression();
|
||||
ASTNodeFactory nodeFactory(*this, expression);
|
||||
nodeFactory.setEndPositionFromNode(rightHandSide);
|
||||
return nodeFactory.createNode<Assignment>(expression, assignmentOperator, rightHandSide);
|
||||
}
|
||||
else if (m_scanner->currentToken() == Token::Value::Conditional)
|
||||
{
|
||||
m_scanner->next();
|
||||
ASTPointer<Expression> trueExpression = parseExpression();
|
||||
expectToken(Token::Colon);
|
||||
ASTPointer<Expression> falseExpression = parseExpression();
|
||||
ASTNodeFactory nodeFactory(*this, expression);
|
||||
nodeFactory.setEndPositionFromNode(falseExpression);
|
||||
return nodeFactory.createNode<Conditional>(expression, trueExpression, falseExpression);
|
||||
}
|
||||
else
|
||||
return expression;
|
||||
Token::Value assignmentOperator = expectAssignmentOperator();
|
||||
ASTPointer<Expression> rightHandSide = parseExpression();
|
||||
ASTNodeFactory nodeFactory(*this, expression);
|
||||
nodeFactory.setEndPositionFromNode(rightHandSide);
|
||||
return nodeFactory.createNode<Assignment>(expression, assignmentOperator, rightHandSide);
|
||||
}
|
||||
|
||||
ASTPointer<Expression> Parser::parseBinaryExpression(
|
||||
|
@ -86,6 +86,192 @@ BOOST_AUTO_TEST_CASE(exp_operator_const_signed)
|
||||
BOOST_CHECK(callContractFunction("f()", bytes()) == toBigEndian(u256(-8)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(conditional_expression_true_literal)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
function f() returns(uint d) {
|
||||
return true ? 5 : 10;
|
||||
}
|
||||
})";
|
||||
compileAndRun(sourceCode);
|
||||
BOOST_CHECK(callContractFunction("f()", bytes()) == toBigEndian(u256(5)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(conditional_expression_false_literal)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
function f() returns(uint d) {
|
||||
return false ? 5 : 10;
|
||||
}
|
||||
})";
|
||||
compileAndRun(sourceCode);
|
||||
BOOST_CHECK(callContractFunction("f()", bytes()) == toBigEndian(u256(10)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(conditional_expression_multiple)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
function f(uint x) returns(uint d) {
|
||||
return x > 100 ?
|
||||
x > 1000 ? 1000 : 100
|
||||
:
|
||||
x > 50 ? 50 : 10;
|
||||
}
|
||||
})";
|
||||
compileAndRun(sourceCode);
|
||||
BOOST_CHECK(callContractFunction("f(uint256)", u256(1001)) == toBigEndian(u256(1000)));
|
||||
BOOST_CHECK(callContractFunction("f(uint256)", u256(500)) == toBigEndian(u256(100)));
|
||||
BOOST_CHECK(callContractFunction("f(uint256)", u256(80)) == toBigEndian(u256(50)));
|
||||
BOOST_CHECK(callContractFunction("f(uint256)", u256(40)) == toBigEndian(u256(10)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(conditional_expression_with_return_values)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
function f(bool cond, uint v) returns (uint a, uint b) {
|
||||
cond ? a = v : b = v;
|
||||
}
|
||||
})";
|
||||
compileAndRun(sourceCode);
|
||||
BOOST_CHECK(callContractFunction("f(bool,uint256)", true, u256(20)) == encodeArgs(u256(20), u256(0)));
|
||||
BOOST_CHECK(callContractFunction("f(bool,uint256)", false, u256(20)) == encodeArgs(u256(0), u256(20)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(conditional_expression_storage_memory_1)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
bytes2[2] data1;
|
||||
function f(bool cond) returns (uint) {
|
||||
bytes2[2] memory x;
|
||||
x[0] = "aa";
|
||||
bytes2[2] memory y;
|
||||
y[0] = "bb";
|
||||
|
||||
data1 = cond ? x : y;
|
||||
|
||||
uint ret = 0;
|
||||
if (data1[0] == "aa")
|
||||
{
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
if (data1[0] == "bb")
|
||||
{
|
||||
ret = 2;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode);
|
||||
BOOST_CHECK(callContractFunction("f(bool)", true) == encodeArgs(u256(1)));
|
||||
BOOST_CHECK(callContractFunction("f(bool)", false) == encodeArgs(u256(2)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(conditional_expression_storage_memory_2)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
bytes2[2] data1;
|
||||
function f(bool cond) returns (uint) {
|
||||
data1[0] = "cc";
|
||||
|
||||
bytes2[2] memory x;
|
||||
bytes2[2] memory y;
|
||||
y[0] = "bb";
|
||||
|
||||
x = cond ? y : data1;
|
||||
|
||||
uint ret = 0;
|
||||
if (x[0] == "bb")
|
||||
{
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
if (x[0] == "cc")
|
||||
{
|
||||
ret = 2;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode);
|
||||
BOOST_CHECK(callContractFunction("f(bool)", true) == encodeArgs(u256(1)));
|
||||
BOOST_CHECK(callContractFunction("f(bool)", false) == encodeArgs(u256(2)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(conditional_expression_different_types)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
function f(bool cond) returns (uint) {
|
||||
uint8 x = 0xcd;
|
||||
uint16 y = 0xabab;
|
||||
return cond ? x : y;
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode);
|
||||
BOOST_CHECK(callContractFunction("f(bool)", true) == encodeArgs(u256(0xcd)));
|
||||
BOOST_CHECK(callContractFunction("f(bool)", false) == encodeArgs(u256(0xabab)));
|
||||
}
|
||||
|
||||
/* let's add this back when I figure out the correct type conversion.
|
||||
BOOST_AUTO_TEST_CASE(conditional_expression_string_literal)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
function f(bool cond) returns (bytes32) {
|
||||
return cond ? "true" : "false";
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode);
|
||||
BOOST_CHECK(callContractFunction("f(bool)", true) == encodeArgs(string("true", 4)));
|
||||
BOOST_CHECK(callContractFunction("f(bool)", false) == encodeArgs(string("false", 5)));
|
||||
}
|
||||
*/
|
||||
|
||||
BOOST_AUTO_TEST_CASE(conditional_expression_tuples)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
function f(bool cond) returns (uint, uint) {
|
||||
return cond ? (1, 2) : (3, 4);
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode);
|
||||
BOOST_CHECK(callContractFunction("f(bool)", true) == encodeArgs(u256(1), u256(2)));
|
||||
BOOST_CHECK(callContractFunction("f(bool)", false) == encodeArgs(u256(3), u256(4)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(conditional_expression_functions)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
function x() returns (uint) { return 1; }
|
||||
function y() returns (uint) { return 2; }
|
||||
|
||||
function f(bool cond) returns (uint) {
|
||||
var z = cond ? x : y;
|
||||
return z();
|
||||
}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode);
|
||||
BOOST_CHECK(callContractFunction("f(bool)", true) == encodeArgs(u256(1)));
|
||||
BOOST_CHECK(callContractFunction("f(bool)", false) == encodeArgs(u256(2)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(recursive_calls)
|
||||
{
|
||||
char const* sourceCode = "contract test {\n"
|
||||
|
@ -2960,6 +2960,182 @@ BOOST_AUTO_TEST_CASE(continue_not_in_loop_2)
|
||||
BOOST_CHECK(expectError(text) == Error::Type::SyntaxError);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(invalid_different_types_for_conditional_expression)
|
||||
{
|
||||
char const* text = R"(
|
||||
contract C {
|
||||
function f() {
|
||||
true ? true : 2;
|
||||
}
|
||||
}
|
||||
)";
|
||||
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(left_value_in_conditional_expression_not_supported_yet)
|
||||
{
|
||||
char const* text = R"(
|
||||
contract C {
|
||||
function f() {
|
||||
uint x;
|
||||
uint y;
|
||||
(true ? x : y) = 1;
|
||||
}
|
||||
}
|
||||
)";
|
||||
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(conditional_expression_with_different_struct)
|
||||
{
|
||||
char const* text = R"(
|
||||
contract C {
|
||||
struct s1 {
|
||||
uint x;
|
||||
}
|
||||
struct s2 {
|
||||
uint x;
|
||||
}
|
||||
function f() {
|
||||
s1 x;
|
||||
s2 y;
|
||||
true ? x : y;
|
||||
}
|
||||
}
|
||||
)";
|
||||
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(conditional_expression_with_different_function_type)
|
||||
{
|
||||
char const* text = R"(
|
||||
contract C {
|
||||
function x(bool) {}
|
||||
function y() {}
|
||||
|
||||
function f() {
|
||||
true ? x : y;
|
||||
}
|
||||
}
|
||||
)";
|
||||
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(conditional_expression_with_different_enum)
|
||||
{
|
||||
char const* text = R"(
|
||||
contract C {
|
||||
enum small { A, B, C, D }
|
||||
enum big { A, B, C, D }
|
||||
|
||||
function f() {
|
||||
small x;
|
||||
big y;
|
||||
|
||||
true ? x : y;
|
||||
}
|
||||
}
|
||||
)";
|
||||
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(conditional_expression_with_different_mapping)
|
||||
{
|
||||
char const* text = R"(
|
||||
contract C {
|
||||
mapping(uint8 => uint8) table1;
|
||||
mapping(uint32 => uint8) table2;
|
||||
|
||||
function f() {
|
||||
true ? table1 : table2;
|
||||
}
|
||||
}
|
||||
)";
|
||||
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(conditional_with_all_types)
|
||||
{
|
||||
char const* text = R"(
|
||||
contract C {
|
||||
struct s1 {
|
||||
uint x;
|
||||
}
|
||||
s1 struct_x;
|
||||
s1 struct_y;
|
||||
|
||||
function fun_x() {}
|
||||
function fun_y() {}
|
||||
|
||||
enum small { A, B, C, D }
|
||||
|
||||
mapping(uint8 => uint8) table1;
|
||||
mapping(uint8 => uint8) table2;
|
||||
|
||||
function f() {
|
||||
// integers
|
||||
uint x;
|
||||
uint y;
|
||||
true ? x : y;
|
||||
|
||||
// integer constants
|
||||
true ? 1 : 3;
|
||||
|
||||
// string literal
|
||||
true ? "hello" : "world";
|
||||
|
||||
// bool
|
||||
true ? true : false;
|
||||
|
||||
// real is not there yet.
|
||||
|
||||
// array
|
||||
byte[2] memory a;
|
||||
byte[2] memory b;
|
||||
true ? a : b;
|
||||
|
||||
bytes memory e;
|
||||
bytes memory f;
|
||||
true ? e : f;
|
||||
|
||||
// fixed bytes
|
||||
bytes2 c;
|
||||
bytes2 d;
|
||||
true ? c : d;
|
||||
|
||||
// contract doesn't fit in here
|
||||
|
||||
// struct
|
||||
true ? struct_x : struct_y;
|
||||
|
||||
// function
|
||||
true ? fun_x : fun_y;
|
||||
|
||||
// enum
|
||||
small enum_x;
|
||||
small enum_y;
|
||||
true ? enum_x : enum_y;
|
||||
|
||||
// tuple
|
||||
true ? (1, 2) : (3, 4);
|
||||
|
||||
// mapping
|
||||
true ? table1 : table2;
|
||||
|
||||
// typetype
|
||||
true ? uint32(1) : uint32(2);
|
||||
|
||||
// modifier doesn't fit in here
|
||||
|
||||
// magic doesn't fit in here
|
||||
|
||||
// module doesn't fit in here
|
||||
}
|
||||
}
|
||||
)";
|
||||
BOOST_CHECK(success(text));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
}
|
||||
|
@ -1111,6 +1111,73 @@ BOOST_AUTO_TEST_CASE(inline_array_empty_cells_check_without_lvalue)
|
||||
BOOST_CHECK(!successParse(text));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(conditional_true_false_literal)
|
||||
{
|
||||
char const* text = R"(
|
||||
contract A {
|
||||
function f() {
|
||||
uint x = true ? 1 : 0;
|
||||
uint y = false ? 0 : 1;
|
||||
}
|
||||
}
|
||||
)";
|
||||
BOOST_CHECK(successParse(text));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(conditional_with_constants)
|
||||
{
|
||||
char const* text = R"(
|
||||
contract A {
|
||||
function f() {
|
||||
uint x = 3 > 0 ? 3 : 0;
|
||||
uint y = (3 > 0) ? 3 : 0;
|
||||
}
|
||||
}
|
||||
)";
|
||||
BOOST_CHECK(successParse(text));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(conditional_with_variables)
|
||||
{
|
||||
char const* text = R"(
|
||||
contract A {
|
||||
function f() {
|
||||
uint x = 3;
|
||||
uint y = 1;
|
||||
uint z = (x > y) ? x : y;
|
||||
uint w = x > y ? x : y;
|
||||
}
|
||||
}
|
||||
)";
|
||||
BOOST_CHECK(successParse(text));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(conditional_multiple)
|
||||
{
|
||||
char const* text = R"(
|
||||
contract A {
|
||||
function f() {
|
||||
uint x = 3 < 0 ? 2 > 1 ? 2 : 1 : 7 > 2 ? 7 : 6;
|
||||
}
|
||||
}
|
||||
)";
|
||||
BOOST_CHECK(successParse(text));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(conditional_with_assignment)
|
||||
{
|
||||
char const* text = R"(
|
||||
contract A {
|
||||
function f() {
|
||||
uint y = 1;
|
||||
uint x = 3 < 0 ? x = 3 : 6;
|
||||
true ? x = 3 : 4;
|
||||
}
|
||||
}
|
||||
)";
|
||||
BOOST_CHECK(successParse(text));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user