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
|
Most of the control structures from C/JavaScript are available in Solidity
|
||||||
except for `switch` and `goto`. So
|
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.
|
the usual semantics known from C / JavaScript.
|
||||||
|
|
||||||
Parentheses can *not* be omitted for conditionals, but curly brances can be omitted
|
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.");
|
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)
|
bool TypeChecker::visit(Assignment const& _assignment)
|
||||||
{
|
{
|
||||||
requireLValue(_assignment.leftHandSide());
|
requireLValue(_assignment.leftHandSide());
|
||||||
|
@ -90,6 +90,7 @@ private:
|
|||||||
virtual void endVisit(Return const& _return) override;
|
virtual void endVisit(Return const& _return) override;
|
||||||
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(Conditional const& _conditional) override;
|
||||||
virtual bool visit(Assignment const& _assignment) override;
|
virtual bool visit(Assignment const& _assignment) override;
|
||||||
virtual bool visit(TupleExpression const& _tuple) override;
|
virtual bool visit(TupleExpression const& _tuple) override;
|
||||||
virtual void endVisit(BinaryOperation const& _operation) override;
|
virtual void endVisit(BinaryOperation const& _operation) override;
|
||||||
|
@ -1119,6 +1119,33 @@ public:
|
|||||||
ExpressionAnnotation& annotation() const override;
|
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.
|
/// Assignment, can also be a compound assignment.
|
||||||
/// Examples: (a = 7 + 8) or (a *= 2)
|
/// Examples: (a = 7 + 8) or (a *= 2)
|
||||||
class Assignment: public Expression
|
class Assignment: public Expression
|
||||||
|
@ -69,6 +69,7 @@ class Throw;
|
|||||||
class VariableDeclarationStatement;
|
class VariableDeclarationStatement;
|
||||||
class ExpressionStatement;
|
class ExpressionStatement;
|
||||||
class Expression;
|
class Expression;
|
||||||
|
class Conditional;
|
||||||
class Assignment;
|
class Assignment;
|
||||||
class TupleExpression;
|
class TupleExpression;
|
||||||
class UnaryOperation;
|
class UnaryOperation;
|
||||||
|
@ -217,6 +217,12 @@ bool ASTJsonConverter::visit(ExpressionStatement const&)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ASTJsonConverter::visit(Conditional const&)
|
||||||
|
{
|
||||||
|
addJsonNode("Conditional", {}, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(Assignment const& _node)
|
bool ASTJsonConverter::visit(Assignment const& _node)
|
||||||
{
|
{
|
||||||
addJsonNode("Assignment",
|
addJsonNode("Assignment",
|
||||||
@ -397,6 +403,11 @@ void ASTJsonConverter::endVisit(ExpressionStatement const&)
|
|||||||
goUp();
|
goUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ASTJsonConverter::endVisit(Conditional const&)
|
||||||
|
{
|
||||||
|
goUp();
|
||||||
|
}
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(Assignment const&)
|
void ASTJsonConverter::endVisit(Assignment const&)
|
||||||
{
|
{
|
||||||
goUp();
|
goUp();
|
||||||
|
@ -67,6 +67,7 @@ public:
|
|||||||
bool visit(Throw const& _node) override;
|
bool visit(Throw const& _node) override;
|
||||||
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(Conditional const& _node) override;
|
||||||
bool visit(Assignment const& _node) override;
|
bool visit(Assignment const& _node) override;
|
||||||
bool visit(TupleExpression const& _node) override;
|
bool visit(TupleExpression const& _node) override;
|
||||||
bool visit(UnaryOperation const& _node) override;
|
bool visit(UnaryOperation const& _node) override;
|
||||||
@ -99,6 +100,7 @@ public:
|
|||||||
void endVisit(Throw const&) override;
|
void endVisit(Throw const&) override;
|
||||||
void endVisit(VariableDeclarationStatement const&) override;
|
void endVisit(VariableDeclarationStatement const&) override;
|
||||||
void endVisit(ExpressionStatement const&) override;
|
void endVisit(ExpressionStatement const&) override;
|
||||||
|
void endVisit(Conditional const&) override;
|
||||||
void endVisit(Assignment const&) override;
|
void endVisit(Assignment const&) override;
|
||||||
void endVisit(TupleExpression const&) override;
|
void endVisit(TupleExpression const&) override;
|
||||||
void endVisit(UnaryOperation const&) override;
|
void endVisit(UnaryOperation const&) override;
|
||||||
|
@ -248,6 +248,14 @@ bool ASTPrinter::visit(ExpressionStatement const& _node)
|
|||||||
return goDeeper();
|
return goDeeper();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ASTPrinter::visit(Conditional const& _node)
|
||||||
|
{
|
||||||
|
writeLine("Conditional");
|
||||||
|
printType(_node);
|
||||||
|
printSourcePart(_node);
|
||||||
|
return goDeeper();
|
||||||
|
}
|
||||||
|
|
||||||
bool ASTPrinter::visit(Assignment const& _node)
|
bool ASTPrinter::visit(Assignment const& _node)
|
||||||
{
|
{
|
||||||
writeLine(string("Assignment using operator ") + Token::toString(_node.assignmentOperator()));
|
writeLine(string("Assignment using operator ") + Token::toString(_node.assignmentOperator()));
|
||||||
@ -480,6 +488,11 @@ void ASTPrinter::endVisit(ExpressionStatement const&)
|
|||||||
m_indentation--;
|
m_indentation--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ASTPrinter::endVisit(Conditional const&)
|
||||||
|
{
|
||||||
|
m_indentation--;
|
||||||
|
}
|
||||||
|
|
||||||
void ASTPrinter::endVisit(Assignment const&)
|
void ASTPrinter::endVisit(Assignment const&)
|
||||||
{
|
{
|
||||||
m_indentation--;
|
m_indentation--;
|
||||||
|
@ -75,6 +75,7 @@ public:
|
|||||||
bool visit(Throw const& _node) override;
|
bool visit(Throw const& _node) override;
|
||||||
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(Conditional const& _node) override;
|
||||||
bool visit(Assignment const& _node) override;
|
bool visit(Assignment const& _node) override;
|
||||||
bool visit(TupleExpression const& _node) override;
|
bool visit(TupleExpression const& _node) override;
|
||||||
bool visit(UnaryOperation const& _node) override;
|
bool visit(UnaryOperation const& _node) override;
|
||||||
@ -115,6 +116,7 @@ public:
|
|||||||
void endVisit(Throw const&) override;
|
void endVisit(Throw const&) override;
|
||||||
void endVisit(VariableDeclarationStatement const&) override;
|
void endVisit(VariableDeclarationStatement const&) override;
|
||||||
void endVisit(ExpressionStatement const&) override;
|
void endVisit(ExpressionStatement const&) override;
|
||||||
|
void endVisit(Conditional const&) override;
|
||||||
void endVisit(Assignment const&) override;
|
void endVisit(Assignment const&) override;
|
||||||
void endVisit(TupleExpression const&) override;
|
void endVisit(TupleExpression const&) override;
|
||||||
void endVisit(UnaryOperation const&) override;
|
void endVisit(UnaryOperation const&) override;
|
||||||
|
@ -73,6 +73,7 @@ public:
|
|||||||
virtual bool visit(Throw& _node) { return visitNode(_node); }
|
virtual bool visit(Throw& _node) { return visitNode(_node); }
|
||||||
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(Conditional& _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(TupleExpression& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(UnaryOperation& _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(Throw& _node) { endVisitNode(_node); }
|
||||||
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(Conditional& _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(TupleExpression& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(UnaryOperation& _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(Throw const& _node) { return visitNode(_node); }
|
||||||
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(Conditional 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(TupleExpression const& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(UnaryOperation 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(Throw const& _node) { endVisitNode(_node); }
|
||||||
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(Conditional 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(TupleExpression const& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(UnaryOperation 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);
|
_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)
|
void Assignment::accept(ASTVisitor& _visitor)
|
||||||
{
|
{
|
||||||
if (_visitor.visit(*this))
|
if (_visitor.visit(*this))
|
||||||
|
@ -176,6 +176,22 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
|
|||||||
m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction);
|
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)
|
bool ExpressionCompiler::visit(Assignment const& _assignment)
|
||||||
{
|
{
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, _assignment);
|
CompilerContext::LocationSetter locationSetter(m_context, _assignment);
|
||||||
|
@ -71,6 +71,7 @@ public:
|
|||||||
void appendConstStateVariableAccessor(const VariableDeclaration& _varDecl);
|
void appendConstStateVariableAccessor(const VariableDeclaration& _varDecl);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
virtual bool visit(Conditional const& _condition) override;
|
||||||
virtual bool visit(Assignment const& _assignment) override;
|
virtual bool visit(Assignment const& _assignment) override;
|
||||||
virtual bool visit(TupleExpression const& _tuple) override;
|
virtual bool visit(TupleExpression const& _tuple) override;
|
||||||
virtual bool visit(UnaryOperation const& _unaryOperation) override;
|
virtual bool visit(UnaryOperation const& _unaryOperation) override;
|
||||||
|
@ -932,13 +932,26 @@ ASTPointer<Expression> Parser::parseExpression(
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
ASTPointer<Expression> expression = parseBinaryExpression(4, _lookAheadIndexAccessStructure);
|
ASTPointer<Expression> expression = parseBinaryExpression(4, _lookAheadIndexAccessStructure);
|
||||||
if (!Token::isAssignmentOp(m_scanner->currentToken()))
|
if (Token::isAssignmentOp(m_scanner->currentToken()))
|
||||||
return expression;
|
{
|
||||||
Token::Value assignmentOperator = expectAssignmentOperator();
|
Token::Value assignmentOperator = expectAssignmentOperator();
|
||||||
ASTPointer<Expression> rightHandSide = parseExpression();
|
ASTPointer<Expression> rightHandSide = parseExpression();
|
||||||
ASTNodeFactory nodeFactory(*this, expression);
|
ASTNodeFactory nodeFactory(*this, expression);
|
||||||
nodeFactory.setEndPositionFromNode(rightHandSide);
|
nodeFactory.setEndPositionFromNode(rightHandSide);
|
||||||
return nodeFactory.createNode<Assignment>(expression, assignmentOperator, 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTPointer<Expression> Parser::parseBinaryExpression(
|
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_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)
|
BOOST_AUTO_TEST_CASE(recursive_calls)
|
||||||
{
|
{
|
||||||
char const* sourceCode = "contract test {\n"
|
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_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()
|
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_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()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user