diff options
24 files changed, 218 insertions, 57 deletions
diff --git a/Changelog.md b/Changelog.md index 1ae2f57e..851f39a0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,13 @@ Features: * Do-while loops: support for a C-style do{<block>}while(<expr>); control structure + * Type checker: now more eagerly searches for a common type of an inline array with mixed types + * Code generator: generates a runtime error when an out-of-range value is converted into an enum type. + +Bugfixes: + + * Parser: disallow empty enum definitions. + * Type checker: disallow conversion between different enum types. ### 0.4.4 (2016-10-31) diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index af6ae928..0c307288 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -71,7 +71,7 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA add_compile_options(-fPIC) # Configuration-specific compiler settings. - set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -DETH_DEBUG") + set(CMAKE_CXX_FLAGS_DEBUG "-Og -g -DETH_DEBUG") set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG") set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g") diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 51f43015..bbb90e6a 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -329,9 +329,10 @@ Currently, there are situations, where exceptions happen automatically in Solidi 3. If you call a function via a message call but it does not finish properly (i.e. it runs out of gas, has no matching function, or throws an exception itself), except when a low level operation ``call``, ``send``, ``delegatecall`` or ``callcode`` is used. The low level operations never throw exceptions but indicate failures by returning ``false``. 4. If you create a contract using the ``new`` keyword but the contract creation does not finish properly (see above for the definition of "not finish properly"). 5. If you divide or modulo by zero (e.g. ``5 / 0`` or ``23 % 0``). -6. If you perform an external function call targeting a contract that contains no code. -7. If your contract receives Ether via a public function without ``payable`` modifier (including the constructor and the fallback function). -8. If your contract receives Ether via a public accessor function. +6. If you convert a value too big or negative into an enum type. +7. If you perform an external function call targeting a contract that contains no code. +8. If your contract receives Ether via a public function without ``payable`` modifier (including the constructor and the fallback function). +9. If your contract receives Ether via a public accessor function. Internally, Solidity performs an "invalid jump" when an exception is thrown and thus causes the EVM to revert all changes made to the state. The reason for this is that there is no safe way to continue execution, because an expected effect did not occur. Because we want to retain the atomicity of transactions, the safest thing to do is to revert all changes and make the whole transaction (or at least call) without effect. diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index eeea85a7..4a3de441 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -25,7 +25,7 @@ Storage storedData = x; } - function get() constant returns (uint retVal) { + function get() constant returns (uint) { return storedData; } } @@ -136,7 +136,7 @@ like this one. The accessor function created by the ``public`` keyword is a bit more complex in this case. It roughly looks like the following:: - function balances(address _account) returns (uint balance) { + function balances(address _account) returns (uint) { return balances[_account]; } diff --git a/docs/types.rst b/docs/types.rst index 9e7d9b4a..9ec9e526 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -237,7 +237,8 @@ Enums ===== Enums are one way to create a user-defined type in Solidity. They are explicitly convertible -to and from all integer types but implicit conversion is not allowed. +to and from all integer types but implicit conversion is not allowed. The explicit conversions +check the value ranges at runtime and a failure causes an exception. Enums needs at least one member. :: diff --git a/libdevcore/CommonData.cpp b/libdevcore/CommonData.cpp index b27271cf..062d1b29 100644 --- a/libdevcore/CommonData.cpp +++ b/libdevcore/CommonData.cpp @@ -20,9 +20,6 @@ */ #include "CommonData.h" -#if defined(_MSC_VER) -#pragma warning(pop) -#endif #include "Exceptions.h" using namespace std; using namespace dev; diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 46f4f7f6..f934b2c8 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -996,9 +996,9 @@ bool TypeChecker::visit(TupleExpression const& _tuple) fatalTypeError(components[i]->location(), "Invalid mobile type."); if (i == 0) - inlineArrayType = types[i]->mobileType(); + inlineArrayType = types[i]; else if (inlineArrayType) - inlineArrayType = Type::commonType(inlineArrayType, types[i]->mobileType()); + inlineArrayType = Type::commonType(inlineArrayType, types[i]); } } else diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 7fe97fa7..6ad74d28 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -200,10 +200,10 @@ TypePointer Type::commonType(TypePointer const& _a, TypePointer const& _b) { if (!_a || !_b) return TypePointer(); - else if (_b->isImplicitlyConvertibleTo(*_a)) - return _a; - else if (_a->isImplicitlyConvertibleTo(*_b)) - return _b; + else if (_b->isImplicitlyConvertibleTo(*_a->mobileType())) + return _a->mobileType(); + else if (_a->isImplicitlyConvertibleTo(*_b->mobileType())) + return _b->mobileType(); else return TypePointer(); } @@ -1451,7 +1451,7 @@ u256 StructType::storageSize() const string StructType::toString(bool _short) const { - string ret = "struct " + m_struct.name(); + string ret = "struct " + m_struct.annotation().canonicalName; if (!_short) ret += " " + stringForReferencePart(); return ret; @@ -1561,7 +1561,7 @@ bool EnumType::operator==(Type const& _other) const unsigned EnumType::storageBytes() const { - size_t elements = m_enum.members().size(); + size_t elements = numberOfMembers(); if (elements <= 1) return 1; else @@ -1570,7 +1570,7 @@ unsigned EnumType::storageBytes() const string EnumType::toString(bool) const { - return string("enum ") + m_enum.name(); + return string("enum ") + m_enum.annotation().canonicalName; } string EnumType::canonicalName(bool) const @@ -1578,9 +1578,14 @@ string EnumType::canonicalName(bool) const return m_enum.annotation().canonicalName; } +size_t EnumType::numberOfMembers() const +{ + return m_enum.members().size(); +}; + bool EnumType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - return _convertTo.category() == category() || _convertTo.category() == Category::Integer; + return _convertTo == *this || _convertTo.category() == Category::Integer; } unsigned EnumType::memberValue(ASTString const& _member) const diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 3f94d11a..082e16a6 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -738,6 +738,7 @@ public: EnumDefinition const& enumDefinition() const { return m_enum; } /// @returns the value that the string has in the Enum unsigned int memberValue(ASTString const& _member) const; + size_t numberOfMembers() const; private: EnumDefinition const& m_enum; diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index e7e8a98e..b5f5cd8e 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -270,7 +270,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWordBoundaries) const { - solAssert( + solUnimplementedAssert( !_sourceType.baseType()->isDynamicallySized(), "Nested dynamic arrays not implemented here." ); diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index e064c1a6..58d1caa9 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -138,7 +138,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries); if (numBytes > 0) { - solAssert( + solUnimplementedAssert( _type.sizeOnStack() == 1, "Memory store of types with stack size != 1 not implemented." ); @@ -161,7 +161,7 @@ void CompilerUtils::encodeToMemory( solAssert(targetTypes.size() == _givenTypes.size(), ""); for (TypePointer& t: targetTypes) { - solAssert( + solUnimplementedAssert( t->mobileType() && t->mobileType()->interfaceType(_encodeAsLibraryTypes) && t->mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType(), @@ -315,6 +315,8 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp Type::Category stackTypeCategory = _typeOnStack.category(); Type::Category targetTypeCategory = _targetType.category(); + bool enumOverflowCheckPending = (targetTypeCategory == Type::Category::Enum || stackTypeCategory == Type::Category::Enum); + switch (stackTypeCategory) { case Type::Category::FixedBytes: @@ -348,10 +350,18 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp } break; case Type::Category::Enum: - solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Enum, ""); + solAssert(_targetType == _typeOnStack || targetTypeCategory == Type::Category::Integer, ""); + if (enumOverflowCheckPending) + { + EnumType const& enumType = dynamic_cast<decltype(enumType)>(_typeOnStack); + solAssert(enumType.numberOfMembers() > 0, "empty enum should have caused a parser error."); + m_context << u256(enumType.numberOfMembers() - 1) << Instruction::DUP2 << Instruction::GT; + m_context.appendConditionalJumpTo(m_context.errorTag()); + enumOverflowCheckPending = false; + } break; case Type::Category::FixedPoint: - solAssert(false, "Not yet implemented - FixedPointType."); + solUnimplemented("Not yet implemented - FixedPointType."); case Type::Category::Integer: case Type::Category::Contract: case Type::Category::RationalNumber: @@ -372,6 +382,11 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp solAssert(_typeOnStack.mobileType(), ""); // just clean convertType(_typeOnStack, *_typeOnStack.mobileType(), true); + EnumType const& enumType = dynamic_cast<decltype(enumType)>(_targetType); + solAssert(enumType.numberOfMembers() > 0, "empty enum should have caused a parser error."); + m_context << u256(enumType.numberOfMembers() - 1) << Instruction::DUP2 << Instruction::GT; + m_context.appendConditionalJumpTo(m_context.errorTag()); + enumOverflowCheckPending = false; } else if (targetTypeCategory == Type::Category::FixedPoint) { @@ -386,7 +401,7 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp if (auto typeOnStack = dynamic_cast<IntegerType const*>(&_typeOnStack)) if (targetFixedPointType.integerBits() > typeOnStack->numBits()) cleanHigherOrderBits(*typeOnStack); - solAssert(false, "Not yet implemented - FixedPointType."); + solUnimplemented("Not yet implemented - FixedPointType."); } else { @@ -399,7 +414,7 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp RationalNumberType const& constType = dynamic_cast<RationalNumberType const&>(_typeOnStack); // We know that the stack is clean, we only have to clean for a narrowing conversion // where cleanup is forced. - solAssert(!constType.isFractional(), "Not yet implemented - FixedPointType."); + solUnimplementedAssert(!constType.isFractional(), "Not yet implemented - FixedPointType."); if (targetType.numBits() < constType.integerType()->numBits() && _cleanupNeeded) cleanHigherOrderBits(targetType); } @@ -651,11 +666,14 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp solAssert(_targetType == _typeOnStack, "Invalid conversion for bool."); if (_cleanupNeeded) m_context << Instruction::ISZERO << Instruction::ISZERO; + break; default: // All other types should not be convertible to non-equal types. solAssert(_typeOnStack == _targetType, "Invalid type conversion requested."); break; } + + solAssert(!enumOverflowCheckPending, "enum overflow checking missing."); } void CompilerUtils::pushZeroValue(Type const& _type) diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 1404963f..2aec3055 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -296,10 +296,10 @@ void ContractCompiler::appendCalldataUnpacker(TypePointers const& _typeParameter if (type->category() == Type::Category::Array) { auto const& arrayType = dynamic_cast<ArrayType const&>(*type); - solAssert(!arrayType.baseType()->isDynamicallySized(), "Nested arrays not yet implemented."); + solUnimplementedAssert(!arrayType.baseType()->isDynamicallySized(), "Nested arrays not yet implemented."); if (_fromMemory) { - solAssert( + solUnimplementedAssert( arrayType.baseType()->isValueType(), "Nested memory arrays not yet implemented here." ); diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 9a096e2d..e3f05c21 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -99,7 +99,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& if (auto mappingType = dynamic_cast<MappingType const*>(returnType.get())) { solAssert(CompilerUtils::freeMemoryPointer >= 0x40, ""); - solAssert( + solUnimplementedAssert( !paramTypes[i]->isDynamicallySized(), "Accessors for mapping with dynamically-sized keys not yet implemented." ); @@ -211,7 +211,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment) Token::Value op = _assignment.assignmentOperator(); if (op != Token::Assign) // compound assignment { - solAssert(_assignment.annotation().type->isValueType(), "Compound operators not implemented for non-value types."); + solUnimplementedAssert(_assignment.annotation().type->isValueType(), "Compound operators not implemented for non-value types."); unsigned lvalueSize = m_currentLValue->sizeOnStack(); unsigned itemSize = _assignment.annotation().type->sizeOnStack(); if (lvalueSize > 0) @@ -312,7 +312,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation) if (!_unaryOperation.isPrefixOperation()) { // store value for later - solAssert(_unaryOperation.annotation().type->sizeOnStack() == 1, "Stack size != 1 not implemented."); + solUnimplementedAssert(_unaryOperation.annotation().type->sizeOnStack() == 1, "Stack size != 1 not implemented."); m_context << Instruction::DUP1; if (m_currentLValue->sizeOnStack() > 0) for (unsigned i = 1 + m_currentLValue->sizeOnStack(); i > 0; --i) @@ -1141,7 +1141,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) break; case DataLocation::CallData: //@todo if we implement this, the value in calldata has to be added to the base offset - solAssert(!arrayType.baseType()->isDynamicallySized(), "Nested arrays not yet implemented."); + solUnimplementedAssert(!arrayType.baseType()->isDynamicallySized(), "Nested arrays not yet implemented."); if (arrayType.baseType()->isValueType()) CompilerUtils(m_context).loadFromMemoryDynamic( *arrayType.baseType(), @@ -1318,7 +1318,7 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Ty bool const c_isSigned = type.isSigned(); if (_type.category() == Type::Category::FixedPoint) - solAssert(false, "Not yet implemented - FixedPointType."); + solUnimplemented("Not yet implemented - FixedPointType."); switch (_operator) { @@ -1372,7 +1372,7 @@ void ExpressionCompiler::appendBitOperatorCode(Token::Value _operator) void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator) { - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Shift operators not yet implemented.")); + BOOST_THROW_EXCEPTION(UnimplementedFeatureError() << errinfo_comment("Shift operators not yet implemented.")); switch (_operator) { case Token::SHL: @@ -1634,7 +1634,7 @@ void ExpressionCompiler::appendExternalFunctionCall( void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression) { - solAssert(_expectedType.isValueType(), "Not implemented for non-value types."); + solUnimplementedAssert(_expectedType.isValueType(), "Not implemented for non-value types."); _expression.accept(*this); utils().convertType(*_expression.annotation().type, _expectedType, true); utils().storeInMemoryDynamic(_expectedType); diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp index c1e05792..69a80b6a 100644 --- a/libsolidity/codegen/LValue.cpp +++ b/libsolidity/codegen/LValue.cpp @@ -120,7 +120,7 @@ void MemoryItem::storeValue(Type const& _sourceType, SourceLocation const&, bool } else { - solAssert(_sourceType == *m_dataType, "Conversion not implemented for assignment to memory."); + solUnimplementedAssert(_sourceType == *m_dataType, "Conversion not implemented for assignment to memory."); solAssert(m_dataType->sizeOnStack() == 1, ""); if (!_move) @@ -181,7 +181,7 @@ void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const << u256(0x100) << Instruction::EXP << Instruction::SWAP1 << Instruction::DIV; if (m_dataType->category() == Type::Category::FixedPoint) // implementation should be very similar to the integer case. - solAssert(false, "Not yet implemented - FixedPointType."); + solUnimplemented("Not yet implemented - FixedPointType."); if (m_dataType->category() == Type::Category::FixedBytes) m_context << (u256(0x1) << (256 - 8 * m_dataType->storageBytes())) << Instruction::MUL; else if ( diff --git a/libsolidity/grammar.txt b/libsolidity/grammar.txt index 586c41e9..6ad8994a 100644 --- a/libsolidity/grammar.txt +++ b/libsolidity/grammar.txt @@ -41,8 +41,8 @@ ArrayTypeName = TypeName StorageLocation? '[' Expression? ']' StorageLocation = 'memory' | 'storage' Block = '{' Statement* '}' -Statement = IfStatement | WhileStatement | DoWhileStatement | ForStatement | Block | - ( PlaceholderStatement | Continue | Break | Return | +Statement = IfStatement | WhileStatement | ForStatement | Block | + ( DoWhileStatement | PlaceholderStatement | Continue | Break | Return | Throw | SimpleStatement ) ';' ExpressionStatement = Expression @@ -51,7 +51,7 @@ WhileStatement = 'while' '(' Expression ')' Statement PlaceholderStatement = '_' SimpleStatement = VariableDefinition | ExpressionStatement ForStatement = 'for' '(' (SimpleStatement)? ';' (Expression)? ';' (ExpressionStatement)? ')' Statement -DoWhileStatement = 'do' Statement 'while' '(' Expression ')' ';' +DoWhileStatement = 'do' Statement 'while' '(' Expression ')' Continue = 'continue' Break = 'break' Return = 'return' Expression? @@ -65,7 +65,7 @@ Expression = | Expression '**' Expression | Expression ('*' | '/' | '%') Expression | Expression ('+' | '-') Expression - | Expression ('<<' | '>>' | '>>>') + | Expression ('<<' | '>>') | Expression '&' Expression | Expression '^' Expression | Expression '|' Expression diff --git a/libsolidity/interface/Exceptions.h b/libsolidity/interface/Exceptions.h index 07835320..c651548a 100644 --- a/libsolidity/interface/Exceptions.h +++ b/libsolidity/interface/Exceptions.h @@ -37,6 +37,7 @@ using ErrorList = std::vector<std::shared_ptr<Error const>>; struct CompilerError: virtual Exception {}; struct InternalCompilerError: virtual Exception {}; struct FatalError: virtual Exception {}; +struct UnimplementedFeatureError: virtual Exception{}; class Error: virtual public Exception { diff --git a/libsolidity/interface/Utils.h b/libsolidity/interface/Utils.h index 738669ac..eef8c917 100644 --- a/libsolidity/interface/Utils.h +++ b/libsolidity/interface/Utils.h @@ -30,6 +30,7 @@ namespace dev namespace solidity { struct InternalCompilerError; +struct UnimplementedFeatureError; } } @@ -37,3 +38,8 @@ struct InternalCompilerError; #define solAssert(CONDITION, DESCRIPTION) \ assertThrow(CONDITION, ::dev::solidity::InternalCompilerError, DESCRIPTION) +#define solUnimplementedAssert(CONDITION, DESCRIPTION) \ + assertThrow(CONDITION, ::dev::solidity::UnimplementedFeatureError, DESCRIPTION) + +#define solUnimplemented(DESCRIPTION) \ + solUnimplementedAssert(false, DESCRIPTION) diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 52b53619..df3ed7b2 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -406,6 +406,8 @@ ASTPointer<EnumDefinition> Parser::parseEnumDefinition() if (m_scanner->currentToken() != Token::Identifier) fatalParserError(string("Expected Identifier after ','")); } + if (members.size() == 0) + parserError({"enum with no members is not allowed."}); nodeFactory.markEndPosition(); expectToken(Token::RBrace); diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 84cc2534..83168f86 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -604,6 +604,12 @@ bool CommandLineInterface::processInput() << boost::diagnostic_information(_exception); return false; } + catch (UnimplementedFeatureError const& _exception) + { + cerr << "Unimplemented feature:" << endl + << boost::diagnostic_information(_exception); + return false; + } catch (Error const& _error) { if (_error.type() == Error::Type::DocstringParsingError) diff --git a/solc/jsonCompiler.cpp b/solc/jsonCompiler.cpp index ef69105e..e5be8404 100644 --- a/solc/jsonCompiler.cpp +++ b/solc/jsonCompiler.cpp @@ -189,6 +189,10 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback { errors.append(formatError(exception, "Internal compiler error", scannerFromSourceName)); } + catch (UnimplementedFeatureError const& exception) + { + errors.append(formatError(exception, "Unimplemented feature", scannerFromSourceName)); + } catch (Exception const& exception) { errors.append("Exception during compilation: " + boost::diagnostic_information(exception)); diff --git a/test/libsolidity/ASTJSON.cpp b/test/libsolidity/ASTJSON.cpp index a0fc5dd7..6c062ee8 100644 --- a/test/libsolidity/ASTJSON.cpp +++ b/test/libsolidity/ASTJSON.cpp @@ -94,20 +94,6 @@ BOOST_AUTO_TEST_CASE(using_for_directive) BOOST_CHECK_EQUAL(usingFor["children"][1]["attributes"]["name"], "uint"); } -BOOST_AUTO_TEST_CASE(enum_definition) -{ - CompilerStack c; - c.addSource("a", "contract C { enum E {} }"); - c.parse(); - map<string, unsigned> sourceIndices; - sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); - Json::Value enumDefinition = astJson["children"][0]["children"][0]; - BOOST_CHECK_EQUAL(enumDefinition["name"], "EnumDefinition"); - BOOST_CHECK_EQUAL(enumDefinition["attributes"]["name"], "E"); - BOOST_CHECK_EQUAL(enumDefinition["src"], "13:9:1"); -} - BOOST_AUTO_TEST_CASE(enum_value) { CompilerStack c; diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index a1430b02..739a2efd 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -3342,6 +3342,42 @@ BOOST_AUTO_TEST_CASE(using_enums) BOOST_CHECK(callContractFunction("getChoice()") == encodeArgs(2)); } +BOOST_AUTO_TEST_CASE(enum_explicit_overflow) +{ + char const* sourceCode = R"( + contract test { + enum ActionChoices { GoLeft, GoRight, GoStraight } + function test() + { + } + function getChoiceExp(uint x) returns (uint d) + { + choice = ActionChoices(x); + d = uint256(choice); + } + function getChoiceFromSigned(int x) returns (uint d) + { + choice = ActionChoices(x); + d = uint256(choice); + } + function getChoiceFromNegativeLiteral() returns (uint d) + { + choice = ActionChoices(-1); + d = uint256(choice); + } + ActionChoices choice; + } + )"; + compileAndRun(sourceCode); + // These should throw + BOOST_CHECK(callContractFunction("getChoiceExp(uint256)", 3) == encodeArgs()); + BOOST_CHECK(callContractFunction("getChoiceFromSigned(int256)", -1) == encodeArgs()); + BOOST_CHECK(callContractFunction("getChoiceFromNegativeLiteral()") == encodeArgs()); + // These should work + BOOST_CHECK(callContractFunction("getChoiceExp(uint256)", 2) == encodeArgs(2)); + BOOST_CHECK(callContractFunction("getChoiceExp(uint256)", 0) == encodeArgs(0)); +} + BOOST_AUTO_TEST_CASE(using_contract_enums_with_explicit_contract_name) { char const* sourceCode = R"( @@ -4463,6 +4499,67 @@ BOOST_AUTO_TEST_CASE(external_types_in_calls) BOOST_CHECK(callContractFunction("t2()") == encodeArgs(u256(9))); } +BOOST_AUTO_TEST_CASE(invalid_enum_as_external_ret) +{ + char const* sourceCode = R"( + contract C { + enum X { A, B } + + function test_return() returns (X) { + X garbled; + assembly { + garbled := 5 + } + return garbled; + } + function test_inline_assignment() returns (X _ret) { + assembly { + _ret := 5 + } + } + function test_assignment() returns (X _ret) { + X tmp; + assembly { + tmp := 5 + } + _ret = tmp; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + // both should throw + BOOST_CHECK(callContractFunction("test_return()") == encodeArgs()); + BOOST_CHECK(callContractFunction("test_inline_assignment()") == encodeArgs()); + BOOST_CHECK(callContractFunction("test_assignment()") == encodeArgs()); +} + +BOOST_AUTO_TEST_CASE(invalid_enum_as_external_arg) +{ + char const* sourceCode = R"( + contract C { + enum X { A, B } + + function tested (X x) returns (uint) { + return 1; + } + + function test() returns (uint) { + X garbled; + + assembly { + garbled := 5 + } + + return this.tested(garbled); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + // should throw + BOOST_CHECK(callContractFunction("test()") == encodeArgs()); +} + + BOOST_AUTO_TEST_CASE(proper_order_of_overwriting_of_attributes) { // bug #1798 @@ -6353,6 +6450,20 @@ BOOST_AUTO_TEST_CASE(decayed_tuple) BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(2))); } +BOOST_AUTO_TEST_CASE(inline_tuple_with_rational_numbers) +{ + char const* sourceCode = R"( + contract c { + function f() returns (int8) { + int8[5] memory foo3 = [int8(1), -1, 0, 0, 0]; + return foo3[0]; + } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(1))); +} + BOOST_AUTO_TEST_CASE(destructuring_assignment) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 099c3c00..f498a0b9 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -1535,6 +1535,21 @@ BOOST_AUTO_TEST_CASE(enum_implicit_conversion_is_not_okay) BOOST_CHECK(expectError(text) == Error::Type::TypeError); } +BOOST_AUTO_TEST_CASE(enum_to_enum_conversion_is_not_okay) +{ + char const* text = R"( + contract test { + enum Paper { Up, Down, Left, Right } + enum Ground { North, South, West, East } + function test() + { + Ground(Paper.Up); + } + } + )"; + BOOST_CHECK(expectError(text) == Error::Type::TypeError); +} + BOOST_AUTO_TEST_CASE(enum_duplicate_values) { char const* text = R"( diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index a81a9828..ec23d5fd 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -824,7 +824,7 @@ BOOST_AUTO_TEST_CASE(empty_enum_declaration) contract c { enum foo { } })"; - BOOST_CHECK(successParse(text)); + BOOST_CHECK(!successParse(text)); } BOOST_AUTO_TEST_CASE(malformed_enum_declaration) |