aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Changelog.md7
-rw-r--r--cmake/EthCompilerSettings.cmake2
-rw-r--r--docs/control-structures.rst7
-rw-r--r--docs/introduction-to-smart-contracts.rst4
-rw-r--r--docs/types.rst3
-rw-r--r--libdevcore/CommonData.cpp3
-rw-r--r--libsolidity/analysis/TypeChecker.cpp4
-rw-r--r--libsolidity/ast/Types.cpp21
-rw-r--r--libsolidity/ast/Types.h1
-rw-r--r--libsolidity/codegen/ArrayUtils.cpp2
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp30
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp4
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp14
-rw-r--r--libsolidity/codegen/LValue.cpp4
-rw-r--r--libsolidity/grammar.txt8
-rw-r--r--libsolidity/interface/Exceptions.h1
-rw-r--r--libsolidity/interface/Utils.h6
-rw-r--r--libsolidity/parsing/Parser.cpp2
-rw-r--r--solc/CommandLineInterface.cpp6
-rw-r--r--solc/jsonCompiler.cpp4
-rw-r--r--test/libsolidity/ASTJSON.cpp14
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp111
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp15
-rw-r--r--test/libsolidity/SolidityParser.cpp2
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)