From 4fa0326813b4fc9d3c85cb9235f42cf22beead72 Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Tue, 22 Dec 2015 16:44:51 +0000 Subject: [PATCH 01/24] [cond-expr] add an AST node --- libsolidity/ast/AST.h | 27 +++++++++++++++++++++++++++ libsolidity/ast/ASTForward.h | 1 + libsolidity/ast/AST_accept.h | 22 ++++++++++++++++++++++ 3 files changed, 50 insertions(+) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 4baf95d30..e10634676 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -1119,6 +1119,33 @@ public: ExpressionAnnotation& annotation() const override; }; +class Conditional: public Expression +{ +public: + Conditional( + SourceLocation const& _location, + ASTPointer const& _condition, + ASTPointer const& _trueExpression, + ASTPointer 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 m_condition; + ASTPointer m_trueExpression; + ASTPointer m_falseExpression; +}; + /// Assignment, can also be a compound assignment. /// Examples: (a = 7 + 8) or (a *= 2) class Assignment: public Expression diff --git a/libsolidity/ast/ASTForward.h b/libsolidity/ast/ASTForward.h index 6aaa77ce0..dad2b2e2b 100644 --- a/libsolidity/ast/ASTForward.h +++ b/libsolidity/ast/ASTForward.h @@ -69,6 +69,7 @@ class Throw; class VariableDeclarationStatement; class ExpressionStatement; class Expression; +class Conditional; class Assignment; class TupleExpression; class UnaryOperation; diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h index 61370c557..dee9d5b14 100644 --- a/libsolidity/ast/AST_accept.h +++ b/libsolidity/ast/AST_accept.h @@ -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)) From 4563d4bd3c3525fd2bf26823567f3c99018482e1 Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Tue, 22 Dec 2015 16:47:00 +0000 Subject: [PATCH 02/24] [cond-expr] add visitor related functions --- libsolidity/ast/ASTJsonConverter.cpp | 11 +++++++++++ libsolidity/ast/ASTJsonConverter.h | 2 ++ libsolidity/ast/ASTPrinter.cpp | 13 +++++++++++++ libsolidity/ast/ASTPrinter.h | 2 ++ libsolidity/ast/ASTVisitor.h | 4 ++++ 5 files changed, 32 insertions(+) diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 377fa7e65..df836afe2 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -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(); diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h index de891cc6a..b7fc84e98 100644 --- a/libsolidity/ast/ASTJsonConverter.h +++ b/libsolidity/ast/ASTJsonConverter.h @@ -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; diff --git a/libsolidity/ast/ASTPrinter.cpp b/libsolidity/ast/ASTPrinter.cpp index d4f13e478..bc981f7d0 100644 --- a/libsolidity/ast/ASTPrinter.cpp +++ b/libsolidity/ast/ASTPrinter.cpp @@ -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--; diff --git a/libsolidity/ast/ASTPrinter.h b/libsolidity/ast/ASTPrinter.h index d9b5e252a..334fefc77 100644 --- a/libsolidity/ast/ASTPrinter.h +++ b/libsolidity/ast/ASTPrinter.h @@ -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; diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h index f04d96825..625f395db 100644 --- a/libsolidity/ast/ASTVisitor.h +++ b/libsolidity/ast/ASTVisitor.h @@ -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); } From 7eefa838a3384d3a8a81d73d7e544afa7f3193fc Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Tue, 22 Dec 2015 16:47:55 +0000 Subject: [PATCH 03/24] [cond-expr] parse _ ? _ : _ into conditional AST node --- libsolidity/parsing/Parser.cpp | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 4ac3381ce..cc06eb876 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -932,13 +932,26 @@ ASTPointer Parser::parseExpression( ) { ASTPointer expression = parseBinaryExpression(4, _lookAheadIndexAccessStructure); - if (!Token::isAssignmentOp(m_scanner->currentToken())) + if (Token::isAssignmentOp(m_scanner->currentToken())) + { + Token::Value assignmentOperator = expectAssignmentOperator(); + ASTPointer rightHandSide = parseExpression(); + ASTNodeFactory nodeFactory(*this, expression); + nodeFactory.setEndPositionFromNode(rightHandSide); + return nodeFactory.createNode(expression, assignmentOperator, rightHandSide); + } + else if (m_scanner->currentToken() == Token::Value::Conditional) + { + m_scanner->next(); + ASTPointer trueExpression = parseExpression(); + expectToken(Token::Colon); + ASTPointer falseExpression = parseExpression(); + ASTNodeFactory nodeFactory(*this, expression); + nodeFactory.setEndPositionFromNode(falseExpression); // TODO: + return nodeFactory.createNode(expression, trueExpression, falseExpression); + } + else return expression; - Token::Value assignmentOperator = expectAssignmentOperator(); - ASTPointer rightHandSide = parseExpression(); - ASTNodeFactory nodeFactory(*this, expression); - nodeFactory.setEndPositionFromNode(rightHandSide); - return nodeFactory.createNode(expression, assignmentOperator, rightHandSide); } ASTPointer Parser::parseBinaryExpression( From f1d21552fcf9b3f29c9d70b767810da05adeb011 Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Tue, 22 Dec 2015 16:49:09 +0000 Subject: [PATCH 04/24] [cond-expr] add parser test cases --- test/libsolidity/SolidityParser.cpp | 80 +++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 8e0bf77e8..aeaf2baf9 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -1111,6 +1111,86 @@ 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; + } + } + )"; + BOOST_CHECK(successParse(text)); +} + +BOOST_AUTO_TEST_CASE(conditional_as_left_value) +{ + char const* text = R"( + contract A { + function f() { + uint x; + uint y; + (true ? x : y) = 3; + } + } + )"; + BOOST_CHECK(successParse(text)); +} + BOOST_AUTO_TEST_SUITE_END() } From 047172eb9a49df200b3a046751e90f52fc64999f Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Tue, 22 Dec 2015 16:50:06 +0000 Subject: [PATCH 05/24] [cond-expr] add type checker --- libsolidity/analysis/TypeChecker.cpp | 27 +++++++++++++++++++++++++++ libsolidity/analysis/TypeChecker.h | 1 + 2 files changed, 28 insertions(+) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 693570432..058e879e1 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -744,6 +744,33 @@ void TypeChecker::endVisit(ExpressionStatement const& _statement) typeError(_statement.expression().location(), "Invalid integer constant."); } +void TypeChecker::endVisit(Conditional const& _conditional) +{ + TypePointer const& conditionType = type(_conditional.condition()); + if (!conditionType->isImplicitlyConvertibleTo(BoolType())) + typeError( + _conditional.location(), + "Conditional expression's type " + + conditionType->toString() + + " doesn't match bool type." + ); + + TypePointer const& trueType = type(_conditional.trueExpression()); + TypePointer const& falseType = type(_conditional.falseExpression()); + // we fake it as an equal operator, but any other comparison operator can work. + TypePointer commonType = trueType->binaryOperatorResult(Token::Equal, falseType); + if (!commonType) + typeError( + _conditional.location(), + "True expression's type " + + trueType->toString() + + " doesn't match false expression's type " + + falseType->toString() + + "." + ); + _conditional.annotation().type = commonType; +} + bool TypeChecker::visit(Assignment const& _assignment) { requireLValue(_assignment.leftHandSide()); diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index 7829a23d5..ae96229b3 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -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 void endVisit(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; From ff4f1666ab913ae3b90bc0af1477c2042f5ad535 Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Tue, 22 Dec 2015 16:50:24 +0000 Subject: [PATCH 06/24] [cond-expr] generate assembly for _ ? _ : _ --- libsolidity/codegen/ExpressionCompiler.cpp | 15 +++++++++++++++ libsolidity/codegen/ExpressionCompiler.h | 1 + 2 files changed, 16 insertions(+) diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 040217dac..6dd9d0056 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -176,6 +176,21 @@ 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); + m_context << eth::Instruction::ISZERO; + eth::AssemblyItem falseTag = m_context.appendConditionalJump(); + _condition.trueExpression().accept(*this); + eth::AssemblyItem endTag = m_context.appendJumpToNew(); + m_context << falseTag; + m_context.adjustStackOffset(-1); + _condition.falseExpression().accept(*this); + m_context << endTag; + return false; +} + bool ExpressionCompiler::visit(Assignment const& _assignment) { CompilerContext::LocationSetter locationSetter(m_context, _assignment); diff --git a/libsolidity/codegen/ExpressionCompiler.h b/libsolidity/codegen/ExpressionCompiler.h index 379aa65a4..f00b24e8e 100644 --- a/libsolidity/codegen/ExpressionCompiler.h +++ b/libsolidity/codegen/ExpressionCompiler.h @@ -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; From 0a45fe04f309e5524f06132e7fd0fe48b0183cbc Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Tue, 22 Dec 2015 17:05:06 +0000 Subject: [PATCH 07/24] [cond-expr] add end to end test --- test/libsolidity/SolidityEndToEndTest.cpp | 58 +++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 3ef5ebbee..d07ef483f 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -86,6 +86,64 @@ 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_false_literal) +{ + 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_as_left_value) +{ + char const* sourceCode = R"( + contract test { + function f(uint x) returns(uint d) { + uint y = 1; + uint z = 1; + (x > 10 ? y : z) = 3; + return y ** z; + } + })"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("f(uint256)", u256(20)) == toBigEndian(u256(3))); + BOOST_CHECK(callContractFunction("f(uint256)", u256(5)) == toBigEndian(u256(1))); +} + BOOST_AUTO_TEST_CASE(recursive_calls) { char const* sourceCode = "contract test {\n" From 4471a2ce3bda9ff802bb9701d042b5c68c312495 Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Tue, 22 Dec 2015 17:14:09 +0000 Subject: [PATCH 08/24] [cond-expr] add test cases for type resolution --- .../SolidityNameAndTypeResolution.cpp | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index c8e901e43..73f6a6c89 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -2960,6 +2960,30 @@ 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(invalid_left_value_in_conditional_expression) +{ + char const* text = R"( + contract C { + function f() { + (true ? 3 : 5) = 1; + } + } + )"; + BOOST_CHECK(expectError(text) == Error::Type::TypeError); +} + BOOST_AUTO_TEST_SUITE_END() } From 9cd96222dae55173d3afc17b3cf7a4701edae156 Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Wed, 23 Dec 2015 16:12:41 +0000 Subject: [PATCH 09/24] [cond-expr] support conditional expression as lvalue --- libsolidity/analysis/TypeChecker.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 058e879e1..416ac1efc 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -769,6 +769,14 @@ void TypeChecker::endVisit(Conditional const& _conditional) "." ); _conditional.annotation().type = commonType; + + if (_conditional.annotation().lValueRequested) + { + requireLValue(_conditional.trueExpression()); + requireLValue(_conditional.falseExpression()); + + _conditional.annotation().isLValue = true; + } } bool TypeChecker::visit(Assignment const& _assignment) From 985744545a9cae9c92ba535eef1d7ecd86ba846f Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Wed, 23 Dec 2015 12:16:50 +0000 Subject: [PATCH 10/24] [cond-expr] add another parser expression --- test/libsolidity/SolidityParser.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index aeaf2baf9..7ddfdf5ac 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -1171,6 +1171,7 @@ BOOST_AUTO_TEST_CASE(conditional_with_assignment) function f() { uint y = 1; uint x = 3 < 0 ? x = 3 : 6; + true ? x = 3 : 4; } } )"; From 08493589c1638b57721613d46e9e8d28f244f1d3 Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Wed, 23 Dec 2015 12:19:11 +0000 Subject: [PATCH 11/24] fixup end to end test --- test/libsolidity/SolidityEndToEndTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index d07ef483f..4e515b657 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -110,7 +110,7 @@ BOOST_AUTO_TEST_CASE(conditional_expression_false_literal) BOOST_CHECK(callContractFunction("f()", bytes()) == toBigEndian(u256(10))); } -BOOST_AUTO_TEST_CASE(conditional_expression_false_literal) +BOOST_AUTO_TEST_CASE(conditional_expression_multiple) { char const* sourceCode = R"( contract test { From 36a758e224a600d277d683aec8578520e71b8290 Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Wed, 23 Dec 2015 15:48:10 +0000 Subject: [PATCH 12/24] [cond-expr] fix the crash in ExpressionStatement --- libsolidity/analysis/TypeChecker.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 416ac1efc..b6b4ac90d 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -760,6 +760,7 @@ void TypeChecker::endVisit(Conditional const& _conditional) // we fake it as an equal operator, but any other comparison operator can work. TypePointer commonType = trueType->binaryOperatorResult(Token::Equal, falseType); if (!commonType) + { typeError( _conditional.location(), "True expression's type " + @@ -768,6 +769,10 @@ void TypeChecker::endVisit(Conditional const& _conditional) 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) From db6ce6d8a158e9c28853608794f324dc5e860822 Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Wed, 23 Dec 2015 16:04:11 +0000 Subject: [PATCH 13/24] comment out one un-supported test case --- test/libsolidity/SolidityEndToEndTest.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 4e515b657..46199578f 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -128,6 +128,7 @@ BOOST_AUTO_TEST_CASE(conditional_expression_multiple) BOOST_CHECK(callContractFunction("f(uint256)", u256(40)) == toBigEndian(u256(10))); } +/* BOOST_AUTO_TEST_CASE(conditional_expression_as_left_value) { char const* sourceCode = R"( @@ -143,6 +144,7 @@ BOOST_AUTO_TEST_CASE(conditional_expression_as_left_value) BOOST_CHECK(callContractFunction("f(uint256)", u256(20)) == toBigEndian(u256(3))); BOOST_CHECK(callContractFunction("f(uint256)", u256(5)) == toBigEndian(u256(1))); } +*/ BOOST_AUTO_TEST_CASE(recursive_calls) { From c781b130ef95c1ad841ab1b80fbc95d7c068db80 Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Wed, 23 Dec 2015 16:09:29 +0000 Subject: [PATCH 14/24] [cond-expr] make the codegen one instruction less --- libsolidity/codegen/ExpressionCompiler.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 6dd9d0056..5e9c69201 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -180,13 +180,12 @@ bool ExpressionCompiler::visit(Conditional const& _condition) { CompilerContext::LocationSetter locationSetter(m_context, _condition); _condition.condition().accept(*this); - m_context << eth::Instruction::ISZERO; - eth::AssemblyItem falseTag = m_context.appendConditionalJump(); - _condition.trueExpression().accept(*this); - eth::AssemblyItem endTag = m_context.appendJumpToNew(); - m_context << falseTag; - m_context.adjustStackOffset(-1); + eth::AssemblyItem trueTag = m_context.appendConditionalJump(); _condition.falseExpression().accept(*this); + eth::AssemblyItem endTag = m_context.appendJumpToNew(); + m_context << trueTag; + m_context.adjustStackOffset(-1); + _condition.trueExpression().accept(*this); m_context << endTag; return false; } From 82ee9503e9b3af35b55318f03caf3dc311a57539 Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Thu, 7 Jan 2016 09:01:46 +0000 Subject: [PATCH 15/24] [cond-expr] change endVisit() to visit() --- libsolidity/analysis/TypeChecker.cpp | 69 ++++++++++++++++------------ libsolidity/analysis/TypeChecker.h | 2 +- 2 files changed, 41 insertions(+), 30 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index b6b4ac90d..f2056474c 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -744,44 +744,55 @@ void TypeChecker::endVisit(ExpressionStatement const& _statement) typeError(_statement.expression().location(), "Invalid integer constant."); } -void TypeChecker::endVisit(Conditional const& _conditional) +bool TypeChecker::visit(Conditional const& _conditional) { - TypePointer const& conditionType = type(_conditional.condition()); - if (!conditionType->isImplicitlyConvertibleTo(BoolType())) - typeError( - _conditional.location(), - "Conditional expression's type " + - conditionType->toString() + - " doesn't match bool type." - ); - - TypePointer const& trueType = type(_conditional.trueExpression()); - TypePointer const& falseType = type(_conditional.falseExpression()); - // we fake it as an equal operator, but any other comparison operator can work. - TypePointer commonType = trueType->binaryOperatorResult(Token::Equal, 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; + expectType(_conditional.condition(), BoolType()); if (_conditional.annotation().lValueRequested) { requireLValue(_conditional.trueExpression()); requireLValue(_conditional.falseExpression()); + TypePointer const& trueType = type(_conditional.trueExpression()); + TypePointer const& falseType = type(_conditional.falseExpression()); + + // as a left value, we require exact match to prevent subtle conversion issues. + if (*trueType != *falseType) + typeError( + _conditional.location(), + "True expression's type " + + trueType->toString() + + " doesn't match false expression's type " + + falseType->toString() + + "." + ); + + _conditional.annotation().type = trueType; _conditional.annotation().isLValue = true; } + else + { + _conditional.trueExpression().accept(*this); + _conditional.falseExpression().accept(*this); + + TypePointer const& trueType = type(_conditional.trueExpression()); + TypePointer const& falseType = type(_conditional.falseExpression()); + + // we fake it as an equal operator, but any other comparison operator can work. + TypePointer commonType = trueType->binaryOperatorResult(Token::Equal, falseType); + if (!commonType) + typeError( + _conditional.location(), + "True expression's type " + + trueType->toString() + + " doesn't match false expression's type " + + falseType->toString() + + "." + ); + + _conditional.annotation().type = commonType; + } + return false; } bool TypeChecker::visit(Assignment const& _assignment) diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index ae96229b3..b884db49d 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -90,7 +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 void endVisit(Conditional const& _conditional) 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; From b003290638e16e595c25e433f2f65648ea6644da Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Thu, 7 Jan 2016 09:14:29 +0000 Subject: [PATCH 16/24] [cond-expr] fixup according to code review --- libsolidity/analysis/TypeChecker.cpp | 9 +++++++-- libsolidity/codegen/ExpressionCompiler.cpp | 4 +++- libsolidity/parsing/Parser.cpp | 2 +- test/libsolidity/SolidityEndToEndTest.cpp | 15 +++++++++++++-- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index f2056474c..f38406750 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -765,7 +765,7 @@ bool TypeChecker::visit(Conditional const& _conditional) " doesn't match false expression's type " + falseType->toString() + "." - ); + ); _conditional.annotation().type = trueType; _conditional.annotation().isLValue = true; @@ -781,6 +781,7 @@ bool TypeChecker::visit(Conditional const& _conditional) // we fake it as an equal operator, but any other comparison operator can work. TypePointer commonType = trueType->binaryOperatorResult(Token::Equal, falseType); if (!commonType) + { typeError( _conditional.location(), "True expression's type " + @@ -788,7 +789,11 @@ bool TypeChecker::visit(Conditional const& _conditional) " 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; } diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 5e9c69201..9536c7279 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -182,10 +182,12 @@ bool ExpressionCompiler::visit(Conditional const& _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(-1); + 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; } diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index cc06eb876..d9ec1a49f 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -947,7 +947,7 @@ ASTPointer Parser::parseExpression( expectToken(Token::Colon); ASTPointer falseExpression = parseExpression(); ASTNodeFactory nodeFactory(*this, expression); - nodeFactory.setEndPositionFromNode(falseExpression); // TODO: + nodeFactory.setEndPositionFromNode(falseExpression); return nodeFactory.createNode(expression, trueExpression, falseExpression); } else diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 46199578f..3b9b84490 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -128,7 +128,6 @@ BOOST_AUTO_TEST_CASE(conditional_expression_multiple) BOOST_CHECK(callContractFunction("f(uint256)", u256(40)) == toBigEndian(u256(10))); } -/* BOOST_AUTO_TEST_CASE(conditional_expression_as_left_value) { char const* sourceCode = R"( @@ -144,7 +143,19 @@ BOOST_AUTO_TEST_CASE(conditional_expression_as_left_value) BOOST_CHECK(callContractFunction("f(uint256)", u256(20)) == toBigEndian(u256(3))); BOOST_CHECK(callContractFunction("f(uint256)", u256(5)) == toBigEndian(u256(1))); } -*/ + +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(recursive_calls) { From bf7daf0814ac17fece679a9d1aa187a8bdcf901b Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Mon, 11 Jan 2016 15:08:28 +0800 Subject: [PATCH 17/24] [cond-expr] don't allow conditional as left value for the first stage --- libsolidity/analysis/TypeChecker.cpp | 76 +++++++------------ test/libsolidity/SolidityEndToEndTest.cpp | 16 ---- .../SolidityNameAndTypeResolution.cpp | 6 +- 3 files changed, 33 insertions(+), 65 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index f38406750..8f1b2b9dd 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -748,55 +748,37 @@ bool TypeChecker::visit(Conditional const& _conditional) { expectType(_conditional.condition(), BoolType()); + _conditional.trueExpression().accept(*this); + _conditional.falseExpression().accept(*this); + + TypePointer const& trueType = type(_conditional.trueExpression()); + TypePointer const& falseType = type(_conditional.falseExpression()); + + // we fake it as an equal operator, but any other comparison operator can work. + TypePointer commonType = trueType->binaryOperatorResult(Token::Equal, 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) - { - requireLValue(_conditional.trueExpression()); - requireLValue(_conditional.falseExpression()); + typeError( + _conditional.location(), + "Conditional expression as left value is not supported yet." + ); - TypePointer const& trueType = type(_conditional.trueExpression()); - TypePointer const& falseType = type(_conditional.falseExpression()); - - // as a left value, we require exact match to prevent subtle conversion issues. - if (*trueType != *falseType) - typeError( - _conditional.location(), - "True expression's type " + - trueType->toString() + - " doesn't match false expression's type " + - falseType->toString() + - "." - ); - - _conditional.annotation().type = trueType; - _conditional.annotation().isLValue = true; - } - else - { - _conditional.trueExpression().accept(*this); - _conditional.falseExpression().accept(*this); - - TypePointer const& trueType = type(_conditional.trueExpression()); - TypePointer const& falseType = type(_conditional.falseExpression()); - - // we fake it as an equal operator, but any other comparison operator can work. - TypePointer commonType = trueType->binaryOperatorResult(Token::Equal, 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; - } return false; } diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 3b9b84490..be291735f 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -128,22 +128,6 @@ BOOST_AUTO_TEST_CASE(conditional_expression_multiple) BOOST_CHECK(callContractFunction("f(uint256)", u256(40)) == toBigEndian(u256(10))); } -BOOST_AUTO_TEST_CASE(conditional_expression_as_left_value) -{ - char const* sourceCode = R"( - contract test { - function f(uint x) returns(uint d) { - uint y = 1; - uint z = 1; - (x > 10 ? y : z) = 3; - return y ** z; - } - })"; - compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction("f(uint256)", u256(20)) == toBigEndian(u256(3))); - BOOST_CHECK(callContractFunction("f(uint256)", u256(5)) == toBigEndian(u256(1))); -} - BOOST_AUTO_TEST_CASE(conditional_expression_with_return_values) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 73f6a6c89..7cd92c9cc 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -2972,12 +2972,14 @@ BOOST_AUTO_TEST_CASE(invalid_different_types_for_conditional_expression) BOOST_CHECK(expectError(text) == Error::Type::TypeError); } -BOOST_AUTO_TEST_CASE(invalid_left_value_in_conditional_expression) +BOOST_AUTO_TEST_CASE(left_value_in_conditional_expression_not_supported_yet) { char const* text = R"( contract C { function f() { - (true ? 3 : 5) = 1; + uint x; + uint y; + (true ? x : y) = 1; } } )"; From 41039705ac1cb97e4174c735c160c3df5bc01722 Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Mon, 11 Jan 2016 15:39:09 +0800 Subject: [PATCH 18/24] [cond-expr] add one doc about conditional expression --- docs/control-structures.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/control-structures.rst b/docs/control-structures.rst index c833fbfc0..4becfcf14 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -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 From 1cd32883117d7ae398b74797b7e33f077f6cd677 Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Mon, 11 Jan 2016 16:29:24 +0800 Subject: [PATCH 19/24] [cond-expr] add a test to assign memory to storage --- libsolidity/analysis/TypeChecker.cpp | 8 ++++-- test/libsolidity/SolidityEndToEndTest.cpp | 33 +++++++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 8f1b2b9dd..b4896b49e 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -754,8 +754,12 @@ bool TypeChecker::visit(Conditional const& _conditional) TypePointer const& trueType = type(_conditional.trueExpression()); TypePointer const& falseType = type(_conditional.falseExpression()); - // we fake it as an equal operator, but any other comparison operator can work. - TypePointer commonType = trueType->binaryOperatorResult(Token::Equal, falseType); + TypePointer commonType; + if (*trueType == *falseType) + commonType = trueType; + else + // we fake it as an equal operator, but any other comparison operator can work. + TypePointer commonType = trueType->binaryOperatorResult(Token::Equal, falseType); if (!commonType) { typeError( diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index be291735f..8bf309dfd 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -141,6 +141,39 @@ BOOST_AUTO_TEST_CASE(conditional_expression_with_return_values) BOOST_CHECK(callContractFunction("f(bool,uint256)", false, u256(20)) == encodeArgs(u256(0), u256(20))); } +BOOST_AUTO_TEST_CASE(conditional_expression_storage_memory) +{ + 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(recursive_calls) { char const* sourceCode = "contract test {\n" From ac3019298a42e87a2a9182b2a80a019e31d64f88 Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Mon, 11 Jan 2016 16:50:47 +0800 Subject: [PATCH 20/24] [cond-expr] add a test for different types --- libsolidity/analysis/TypeChecker.cpp | 2 +- test/libsolidity/SolidityEndToEndTest.cpp | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index b4896b49e..a81255732 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -759,7 +759,7 @@ bool TypeChecker::visit(Conditional const& _conditional) commonType = trueType; else // we fake it as an equal operator, but any other comparison operator can work. - TypePointer commonType = trueType->binaryOperatorResult(Token::Equal, falseType); + commonType = trueType->binaryOperatorResult(Token::Equal, falseType); if (!commonType) { typeError( diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 8bf309dfd..d7827946f 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -174,6 +174,21 @@ BOOST_AUTO_TEST_CASE(conditional_expression_storage_memory) 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))); +} BOOST_AUTO_TEST_CASE(recursive_calls) { char const* sourceCode = "contract test {\n" From c8b05339335d8e9166ef6863f7697bcc59f09260 Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Mon, 11 Jan 2016 15:30:25 +0000 Subject: [PATCH 21/24] [cond-expr] change the end to end test --- libsolidity/analysis/TypeChecker.cpp | 8 ++++++-- test/libsolidity/SolidityEndToEndTest.cpp | 9 +++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index a81255732..016fe66a3 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -758,8 +758,12 @@ bool TypeChecker::visit(Conditional const& _conditional) if (*trueType == *falseType) commonType = trueType; else - // we fake it as an equal operator, but any other comparison operator can work. - commonType = trueType->binaryOperatorResult(Token::Equal, falseType); + { + commonType = Type::commonType(trueType, falseType); + if (!commonType) + // we fake it as an equal operator, but any other comparison operator can work. + commonType = trueType->binaryOperatorResult(Token::Equal, falseType); + } if (!commonType) { typeError( diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index d7827946f..0f0eb417c 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -147,20 +147,21 @@ BOOST_AUTO_TEST_CASE(conditional_expression_storage_memory) contract test { bytes2[2] data1; function f(bool cond) returns (uint) { + data1[0] = "cc"; + bytes2[2] memory x; - x[0] = "aa"; bytes2[2] memory y; y[0] = "bb"; - data1 = cond ? x : y; + x = cond ? y : data1; uint ret = 0; - if (data1[0] == "aa") + if (x[0] == "bb") { ret = 1; } - if (data1[0] == "bb") + if (x[0] == "cc") { ret = 2; } From d36537e73720f181fae3e3b82a3d751d27782ca1 Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Mon, 11 Jan 2016 16:00:14 +0000 Subject: [PATCH 22/24] cond-expr: use the mobile type instead of the original type --- libsolidity/analysis/TypeChecker.cpp | 15 ++-------- test/libsolidity/SolidityEndToEndTest.cpp | 35 ++++++++++++++++++++++- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 016fe66a3..0d74ddbad 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -751,19 +751,10 @@ bool TypeChecker::visit(Conditional const& _conditional) _conditional.trueExpression().accept(*this); _conditional.falseExpression().accept(*this); - TypePointer const& trueType = type(_conditional.trueExpression()); - TypePointer const& falseType = type(_conditional.falseExpression()); + TypePointer trueType = type(_conditional.trueExpression())->mobileType(); + TypePointer falseType = type(_conditional.falseExpression())->mobileType(); - TypePointer commonType; - if (*trueType == *falseType) - commonType = trueType; - else - { - commonType = Type::commonType(trueType, falseType); - if (!commonType) - // we fake it as an equal operator, but any other comparison operator can work. - commonType = trueType->binaryOperatorResult(Token::Equal, falseType); - } + TypePointer commonType = Type::commonType(trueType, falseType); if (!commonType) { typeError( diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 0f0eb417c..0907525b2 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -141,7 +141,40 @@ BOOST_AUTO_TEST_CASE(conditional_expression_with_return_values) BOOST_CHECK(callContractFunction("f(bool,uint256)", false, u256(20)) == encodeArgs(u256(0), u256(20))); } -BOOST_AUTO_TEST_CASE(conditional_expression_storage_memory) +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 { From 5840a3513f6af498a1dda3aa6670d1f3c6efefb1 Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Fri, 15 Jan 2016 02:19:11 +0000 Subject: [PATCH 23/24] cond-expr: add more unit tests --- .../SolidityNameAndTypeResolution.cpp | 150 ++++++++++++++++++ test/libsolidity/SolidityParser.cpp | 14 -- 2 files changed, 150 insertions(+), 14 deletions(-) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 7cd92c9cc..820fd7d04 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -2986,6 +2986,156 @@ BOOST_AUTO_TEST_CASE(left_value_in_conditional_expression_not_supported_yet) 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() } diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 7ddfdf5ac..055fae49e 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -1178,20 +1178,6 @@ BOOST_AUTO_TEST_CASE(conditional_with_assignment) BOOST_CHECK(successParse(text)); } -BOOST_AUTO_TEST_CASE(conditional_as_left_value) -{ - char const* text = R"( - contract A { - function f() { - uint x; - uint y; - (true ? x : y) = 3; - } - } - )"; - BOOST_CHECK(successParse(text)); -} - BOOST_AUTO_TEST_SUITE_END() } From 51caa04238ce78420e6efc2ce15842effddf3856 Mon Sep 17 00:00:00 2001 From: Lu Guanqun Date: Tue, 19 Jan 2016 14:59:39 +0000 Subject: [PATCH 24/24] add more test cases for cond-expr --- test/libsolidity/SolidityEndToEndTest.cpp | 49 +++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 0907525b2..73e2d662b 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -223,6 +223,55 @@ BOOST_AUTO_TEST_CASE(conditional_expression_different_types) 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"