aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity')
-rw-r--r--libsolidity/CMakeLists.txt17
-rw-r--r--libsolidity/analysis/ControlFlowAnalyzer.cpp5
-rw-r--r--libsolidity/analysis/GlobalContext.cpp4
-rw-r--r--libsolidity/analysis/ReferencesResolver.cpp2
-rw-r--r--libsolidity/analysis/TypeChecker.cpp46
-rw-r--r--libsolidity/analysis/TypeChecker.h3
-rw-r--r--libsolidity/ast/Types.cpp163
-rw-r--r--libsolidity/ast/Types.h29
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp2
-rw-r--r--libsolidity/formal/CVC4Interface.h11
-rw-r--r--libsolidity/interface/CompilerStack.cpp37
-rw-r--r--libsolidity/interface/CompilerStack.h21
-rw-r--r--libsolidity/interface/StandardCompiler.cpp9
13 files changed, 218 insertions, 131 deletions
diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt
index 49095fff..91a4678a 100644
--- a/libsolidity/CMakeLists.txt
+++ b/libsolidity/CMakeLists.txt
@@ -11,17 +11,11 @@ else()
list(REMOVE_ITEM sources "${CMAKE_CURRENT_SOURCE_DIR}/formal/Z3Interface.cpp")
endif()
-find_package(GMP QUIET)
find_package(CVC4 QUIET)
if (${CVC4_FOUND})
- if (${GMP_FOUND})
- include_directories(${CVC4_INCLUDE_DIR})
- add_definitions(-DHAVE_CVC4)
- message("CVC4 SMT solver and GMP found. This enables optional SMT checking with CVC4.")
- else()
- message("CVC4 SMT solver found but its dependency GMP was NOT found. Optional SMT checking with CVC4 will not be available. Please install GMP if it is desired.")
- list(REMOVE_ITEM sources "${CMAKE_CURRENT_SOURCE_DIR}/formal/CVC4Interface.cpp")
- endif()
+ include_directories(${CVC4_INCLUDE_DIR})
+ add_definitions(-DHAVE_CVC4)
+ message("CVC4 SMT solver found. This enables optional SMT checking with CVC4.")
else()
list(REMOVE_ITEM sources "${CMAKE_CURRENT_SOURCE_DIR}/formal/CVC4Interface.cpp")
endif()
@@ -38,7 +32,6 @@ if (${Z3_FOUND})
target_link_libraries(solidity PUBLIC ${Z3_LIBRARY})
endif()
-if (${CVC4_FOUND} AND ${GMP_FOUND})
- target_link_libraries(solidity PUBLIC ${CVC4_LIBRARY})
- target_link_libraries(solidity PUBLIC ${GMP_LIBRARY})
+if (${CVC4_FOUND})
+ target_link_libraries(solidity PUBLIC ${CVC4_LIBRARIES})
endif()
diff --git a/libsolidity/analysis/ControlFlowAnalyzer.cpp b/libsolidity/analysis/ControlFlowAnalyzer.cpp
index 483d08c8..ab6569be 100644
--- a/libsolidity/analysis/ControlFlowAnalyzer.cpp
+++ b/libsolidity/analysis/ControlFlowAnalyzer.cpp
@@ -75,7 +75,10 @@ void ControlFlowAnalyzer::checkUnassignedStorageReturnValues(
{
auto& unassignedAtFunctionEntry = unassigned[_functionEntry];
for (auto const& returnParameter: _function.returnParameterList()->parameters())
- if (returnParameter->type()->dataStoredIn(DataLocation::Storage))
+ if (
+ returnParameter->type()->dataStoredIn(DataLocation::Storage) ||
+ returnParameter->type()->category() == Type::Category::Mapping
+ )
unassignedAtFunctionEntry.insert(returnParameter.get());
}
diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp
index 7b4bc3aa..3ac8fd47 100644
--- a/libsolidity/analysis/GlobalContext.cpp
+++ b/libsolidity/analysis/GlobalContext.cpp
@@ -42,7 +42,7 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{
make_shared<MagicVariableDeclaration>("blockhash", make_shared<FunctionType>(strings{"uint256"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)),
make_shared<MagicVariableDeclaration>("ecrecover", make_shared<FunctionType>(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Kind::ECRecover, false, StateMutability::Pure)),
make_shared<MagicVariableDeclaration>("gasleft", make_shared<FunctionType>(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft, false, StateMutability::View)),
- make_shared<MagicVariableDeclaration>("keccak256", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::SHA3, false, StateMutability::Pure)),
+ make_shared<MagicVariableDeclaration>("keccak256", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)),
make_shared<MagicVariableDeclaration>("log0", make_shared<FunctionType>(strings{"bytes32"}, strings{}, FunctionType::Kind::Log0)),
make_shared<MagicVariableDeclaration>("log1", make_shared<FunctionType>(strings{"bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log1)),
make_shared<MagicVariableDeclaration>("log2", make_shared<FunctionType>(strings{"bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log2)),
@@ -58,7 +58,7 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{
make_shared<MagicVariableDeclaration>("ripemd160", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes20"}, FunctionType::Kind::RIPEMD160, false, StateMutability::Pure)),
make_shared<MagicVariableDeclaration>("selfdestruct", make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Kind::Selfdestruct)),
make_shared<MagicVariableDeclaration>("sha256", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::SHA256, false, StateMutability::Pure)),
- make_shared<MagicVariableDeclaration>("sha3", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::SHA3, false, StateMutability::Pure)),
+ make_shared<MagicVariableDeclaration>("sha3", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)),
make_shared<MagicVariableDeclaration>("suicide", make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Kind::Selfdestruct)),
make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::Transaction))
})
diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp
index 2cf09eff..fa0888dd 100644
--- a/libsolidity/analysis/ReferencesResolver.cpp
+++ b/libsolidity/analysis/ReferencesResolver.cpp
@@ -344,7 +344,7 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
"(remove the \"memory\" or \"storage\" keyword)."
);
}
- if (varLoc == Location::Default)
+ if (varLoc == Location::Default || varLoc == Location::CallData)
typeLoc = DataLocation::CallData;
else
typeLoc = varLoc == Location::Memory ? DataLocation::Memory : DataLocation::Storage;
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 6882a14f..601a5e7c 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -580,9 +580,6 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
bool TypeChecker::visit(StructDefinition const& _struct)
{
- if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface)
- m_errorReporter.typeError(_struct.location(), "Structs cannot be defined in interfaces.");
-
for (ASTPointer<VariableDeclaration> const& member: _struct.members())
if (!type(*member)->canBeStored())
m_errorReporter.typeError(member->location(), "Type cannot be used in struct.");
@@ -610,7 +607,10 @@ bool TypeChecker::visit(StructDefinition const& _struct)
if (CycleDetector<StructDefinition>(visitor).run(_struct) != nullptr)
m_errorReporter.fatalTypeError(_struct.location(), "Recursive struct definition.");
+ bool insideStruct = true;
+ swap(insideStruct, m_insideStruct);
ASTNode::listAccept(_struct.members(), *this);
+ m_insideStruct = insideStruct;
return false;
}
@@ -629,7 +629,10 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
}
for (ASTPointer<VariableDeclaration> const& var: _function.parameters() + _function.returnParameters())
{
- if (!type(*var)->canLiveOutsideStorage())
+ if (
+ !type(*var)->canLiveOutsideStorage() &&
+ !(_function.visibility() <= FunctionDefinition::Visibility::Internal)
+ )
m_errorReporter.typeError(var->location(), "Type is required to live outside storage.");
if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->interfaceType(isLibraryFunction)))
m_errorReporter.fatalTypeError(var->location(), "Internal or recursive type is not allowed for public or external functions.");
@@ -690,10 +693,12 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
bool TypeChecker::visit(VariableDeclaration const& _variable)
{
// Forbid any variable declarations inside interfaces unless they are part of
- // a function's input/output parameters.
+ // * a function's input/output parameters,
+ // * or inside of a struct definition.
if (
m_scope->contractKind() == ContractDefinition::ContractKind::Interface
&& !_variable.isCallableParameter()
+ && !m_insideStruct
)
m_errorReporter.typeError(_variable.location(), "Variables cannot be declared in interfaces.");
@@ -1626,10 +1631,13 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
else
{
TypePointer const& argType = type(*arguments.front());
+ // Resulting data location is memory unless we are converting from a reference
+ // type with a different data location.
+ // (data location cannot yet be specified for type conversions)
+ DataLocation dataLoc = DataLocation::Memory;
if (auto argRefType = dynamic_cast<ReferenceType const*>(argType.get()))
- // do not change the data location when converting
- // (data location cannot yet be specified for type conversions)
- resultType = ReferenceType::copyForLocationIfReference(argRefType->location(), resultType);
+ dataLoc = argRefType->location();
+ resultType = ReferenceType::copyForLocationIfReference(dataLoc, resultType);
if (!argType->isExplicitlyConvertibleTo(*resultType))
m_errorReporter.typeError(
_functionCall.location(),
@@ -1685,7 +1693,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
if (auto functionName = dynamic_cast<Identifier const*>(&_functionCall.expression()))
{
- if (functionName->name() == "sha3" && functionType->kind() == FunctionType::Kind::SHA3)
+ if (functionName->name() == "sha3" && functionType->kind() == FunctionType::Kind::KECCAK256)
m_errorReporter.typeError(_functionCall.location(), "\"sha3\" has been deprecated in favour of \"keccak256\"");
else if (functionName->name() == "suicide" && functionType->kind() == FunctionType::Kind::Selfdestruct)
m_errorReporter.typeError(_functionCall.location(), "\"suicide\" has been deprecated in favour of \"selfdestruct\"");
@@ -1759,7 +1767,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
msg += " This function requires a single bytes argument. If all your arguments are value types, you can use abi.encode(...) to properly generate it.";
}
else if (
- functionType->kind() == FunctionType::Kind::SHA3 ||
+ functionType->kind() == FunctionType::Kind::KECCAK256 ||
functionType->kind() == FunctionType::Kind::SHA256 ||
functionType->kind() == FunctionType::Kind::RIPEMD160
)
@@ -1804,7 +1812,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
)
msg += " This function requires a single bytes argument. If all your arguments are value types, you can use abi.encode(...) to properly generate it.";
else if (
- functionType->kind() == FunctionType::Kind::SHA3 ||
+ functionType->kind() == FunctionType::Kind::KECCAK256 ||
functionType->kind() == FunctionType::Kind::SHA256 ||
functionType->kind() == FunctionType::Kind::RIPEMD160
)
@@ -2348,22 +2356,6 @@ void TypeChecker::expectType(Expression const& _expression, Type const& _expecte
"."
);
}
-
- if (
- type(_expression)->category() == Type::Category::RationalNumber &&
- _expectedType.category() == Type::Category::FixedBytes
- )
- {
- auto literal = dynamic_cast<Literal const*>(&_expression);
-
- if (literal && !literal->isHexNumber())
- m_errorReporter.warning(
- _expression.location(),
- "Decimal literal assigned to bytesXX variable will be left-aligned. "
- "Use an explicit conversion to silence this warning."
- );
- }
-
}
void TypeChecker::requireLValue(Expression const& _expression)
diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h
index b696de85..ed316f9c 100644
--- a/libsolidity/analysis/TypeChecker.h
+++ b/libsolidity/analysis/TypeChecker.h
@@ -149,6 +149,9 @@ private:
/// Flag indicating whether we are currently inside an EmitStatement.
bool m_insideEmitStatement = false;
+ /// Flag indicating whether we are currently inside a StructDefinition.
+ bool m_insideStruct = false;
+
ErrorReporter& m_errorReporter;
};
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index 3dbbca91..68c201a8 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -355,13 +355,7 @@ TypePointer Type::forLiteral(Literal const& _literal)
case Token::FalseLiteral:
return make_shared<BoolType>();
case Token::Number:
- {
- tuple<bool, rational> validLiteral = RationalNumberType::isValidLiteral(_literal);
- if (get<0>(validLiteral) == true)
- return make_shared<RationalNumberType>(get<1>(validLiteral));
- else
- return TypePointer();
- }
+ return RationalNumberType::forLiteral(_literal);
case Token::StringLiteral:
return make_shared<StringLiteralType>(_literal);
default:
@@ -400,17 +394,17 @@ TypePointer Type::fullEncodingType(bool _inLibraryCall, bool _encoderV2, bool _p
encodingType = encodingType->interfaceType(_inLibraryCall);
if (encodingType)
encodingType = encodingType->encodingType();
- if (auto structType = dynamic_cast<StructType const*>(encodingType.get()))
- {
- // Structs are fine in the following circumstances:
- // - ABIv2 without packed encoding or,
- // - storage struct for a library
- if (!(
- (_encoderV2 && !_packed) ||
- (structType->location() == DataLocation::Storage && _inLibraryCall)
- ))
+ // Structs are fine in the following circumstances:
+ // - ABIv2 without packed encoding or,
+ // - storage struct for a library
+ if (_inLibraryCall && encodingType->dataStoredIn(DataLocation::Storage))
+ return encodingType;
+ TypePointer baseType = encodingType;
+ while (auto const* arrayType = dynamic_cast<ArrayType const*>(baseType.get()))
+ baseType = arrayType->baseType();
+ if (dynamic_cast<StructType const*>(baseType.get()))
+ if (!_encoderV2 || _packed)
return TypePointer();
- }
return encodingType;
}
@@ -779,6 +773,30 @@ tuple<bool, rational> RationalNumberType::parseRational(string const& _value)
}
}
+TypePointer RationalNumberType::forLiteral(Literal const& _literal)
+{
+ solAssert(_literal.token() == Token::Number, "");
+ tuple<bool, rational> validLiteral = isValidLiteral(_literal);
+ if (get<0>(validLiteral))
+ {
+ TypePointer compatibleBytesType;
+ if (_literal.isHexNumber())
+ {
+ size_t digitCount = count_if(
+ _literal.value().begin() + 2, // skip "0x"
+ _literal.value().end(),
+ [](unsigned char _c) -> bool { return isxdigit(_c); }
+ );
+ // require even number of digits
+ if (!(digitCount & 1))
+ compatibleBytesType = make_shared<FixedBytesType>(digitCount / 2);
+ }
+
+ return make_shared<RationalNumberType>(get<1>(validLiteral), compatibleBytesType);
+ }
+ return TypePointer();
+}
+
tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal)
{
rational value;
@@ -918,14 +936,7 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
return false;
}
case Category::FixedBytes:
- {
- FixedBytesType const& fixedBytes = dynamic_cast<FixedBytesType const&>(_convertTo);
- if (isFractional())
- return false;
- if (integerType())
- return fixedBytes.numBytes() * 8 >= integerType()->numBits();
- return false;
- }
+ return (m_value == rational(0)) || (m_compatibleBytesType && *m_compatibleBytesType == _convertTo);
default:
return false;
}
@@ -933,11 +944,15 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
bool RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
- TypePointer mobType = mobileType();
- return
- (mobType && mobType->isExplicitlyConvertibleTo(_convertTo)) ||
- (!isFractional() && _convertTo.category() == Category::FixedBytes)
- ;
+ if (isImplicitlyConvertibleTo(_convertTo))
+ return true;
+ else if (_convertTo.category() != Category::FixedBytes)
+ {
+ TypePointer mobType = mobileType();
+ return (mobType && mobType->isExplicitlyConvertibleTo(_convertTo));
+ }
+ else
+ return false;
}
TypePointer RationalNumberType::unaryOperatorResult(Token::Value _operator) const
@@ -1263,7 +1278,8 @@ shared_ptr<FixedPointType const> RationalNumberType::fixedPointType() const
return shared_ptr<FixedPointType const>();
// This means we round towards zero for positive and negative values.
bigint v = value.numerator() / value.denominator();
- if (negative)
+
+ if (negative && v != 0)
// modify value to satisfy bit requirements for negative numbers:
// add one bit for sign and decrement because negative numbers can be larger
v = (v - 1) << 1;
@@ -2509,7 +2525,7 @@ string FunctionType::richIdentifier() const
case Kind::Creation: id += "creation"; break;
case Kind::Send: id += "send"; break;
case Kind::Transfer: id += "transfer"; break;
- case Kind::SHA3: id += "sha3"; break;
+ case Kind::KECCAK256: id += "keccak256"; break;
case Kind::Selfdestruct: id += "selfdestruct"; break;
case Kind::Revert: id += "revert"; break;
case Kind::ECRecover: id += "ecrecover"; break;
@@ -2554,28 +2570,10 @@ bool FunctionType::operator==(Type const& _other) const
{
if (_other.category() != category())
return false;
-
FunctionType const& other = dynamic_cast<FunctionType const&>(_other);
- if (
- m_kind != other.m_kind ||
- m_stateMutability != other.stateMutability() ||
- m_parameterTypes.size() != other.m_parameterTypes.size() ||
- m_returnParameterTypes.size() != other.m_returnParameterTypes.size()
- )
- return false;
-
- auto typeCompare = [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; };
- if (
- !equal(m_parameterTypes.cbegin(), m_parameterTypes.cend(), other.m_parameterTypes.cbegin(), typeCompare) ||
- !equal(m_returnParameterTypes.cbegin(), m_returnParameterTypes.cend(), other.m_returnParameterTypes.cbegin(), typeCompare)
- )
- return false;
- //@todo this is ugly, but cannot be prevented right now
- if (m_gasSet != other.m_gasSet || m_valueSet != other.m_valueSet)
+ if (!equalExcludingStateMutability(other))
return false;
- if (bound() != other.bound())
- return false;
- if (bound() && *selfType() != *other.selfType())
+ if (m_stateMutability != other.stateMutability())
return false;
return true;
}
@@ -2591,6 +2589,31 @@ bool FunctionType::isExplicitlyConvertibleTo(Type const& _convertTo) const
return _convertTo.category() == category();
}
+bool FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const
+{
+ if (_convertTo.category() != category())
+ return false;
+
+ FunctionType const& convertTo = dynamic_cast<FunctionType const&>(_convertTo);
+
+ if (!equalExcludingStateMutability(convertTo))
+ return false;
+
+ // non-payable should not be convertible to payable
+ if (m_stateMutability != StateMutability::Payable && convertTo.stateMutability() == StateMutability::Payable)
+ return false;
+
+ // payable should be convertible to non-payable, because you are free to pay 0 ether
+ if (m_stateMutability == StateMutability::Payable && convertTo.stateMutability() == StateMutability::NonPayable)
+ return true;
+
+ // e.g. pure should be convertible to view, but not the other way around.
+ if (m_stateMutability > convertTo.stateMutability())
+ return false;
+
+ return true;
+}
+
TypePointer FunctionType::unaryOperatorResult(Token::Value _operator) const
{
if (_operator == Token::Value::Delete)
@@ -2848,6 +2871,38 @@ bool FunctionType::hasEqualParameterTypes(FunctionType const& _other) const
);
}
+bool FunctionType::hasEqualReturnTypes(FunctionType const& _other) const
+{
+ if (m_returnParameterTypes.size() != _other.m_returnParameterTypes.size())
+ return false;
+ return equal(
+ m_returnParameterTypes.cbegin(),
+ m_returnParameterTypes.cend(),
+ _other.m_returnParameterTypes.cbegin(),
+ [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; }
+ );
+}
+
+bool FunctionType::equalExcludingStateMutability(FunctionType const& _other) const
+{
+ if (m_kind != _other.m_kind)
+ return false;
+
+ if (!hasEqualParameterTypes(_other) || !hasEqualReturnTypes(_other))
+ return false;
+
+ //@todo this is ugly, but cannot be prevented right now
+ if (m_gasSet != _other.m_gasSet || m_valueSet != _other.m_valueSet)
+ return false;
+
+ if (bound() != _other.bound())
+ return false;
+
+ solAssert(!bound() || *selfType() == *_other.selfType(), "");
+
+ return true;
+}
+
bool FunctionType::isBareCall() const
{
switch (m_kind)
@@ -2894,7 +2949,7 @@ bool FunctionType::isPure() const
// FIXME: replace this with m_stateMutability == StateMutability::Pure once
// the callgraph analyzer is in place
return
- m_kind == Kind::SHA3 ||
+ m_kind == Kind::KECCAK256 ||
m_kind == Kind::ECRecover ||
m_kind == Kind::SHA256 ||
m_kind == Kind::RIPEMD160 ||
@@ -2999,7 +3054,7 @@ bool FunctionType::padArguments() const
case Kind::BareDelegateCall:
case Kind::SHA256:
case Kind::RIPEMD160:
- case Kind::SHA3:
+ case Kind::KECCAK256:
case Kind::ABIEncodePacked:
return false;
default:
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index 1fa2b2f5..34f862c3 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -423,12 +423,12 @@ public:
virtual Category category() const override { return Category::RationalNumber; }
- /// @returns true if the literal is a valid integer.
- static std::tuple<bool, rational> isValidLiteral(Literal const& _literal);
+ static TypePointer forLiteral(Literal const& _literal);
- explicit RationalNumberType(rational const& _value):
- m_value(_value)
+ explicit RationalNumberType(rational const& _value, TypePointer const& _compatibleBytesType = TypePointer()):
+ m_value(_value), m_compatibleBytesType(_compatibleBytesType)
{}
+
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
@@ -446,7 +446,8 @@ public:
/// @returns the smallest integer type that can hold the value or an empty pointer if not possible.
std::shared_ptr<IntegerType const> integerType() const;
- /// @returns the smallest fixed type that can hold the value or incurs the least precision loss.
+ /// @returns the smallest fixed type that can hold the value or incurs the least precision loss,
+ /// unless the value was truncated, then a suitable type will be chosen to indicate such event.
/// If the integer part does not fit, returns an empty pointer.
std::shared_ptr<FixedPointType const> fixedPointType() const;
@@ -462,6 +463,13 @@ public:
private:
rational m_value;
+ /// Bytes type to which the rational can be explicitly converted.
+ /// Empty for all rationals that are not directly parsed from hex literals.
+ TypePointer m_compatibleBytesType;
+
+ /// @returns true if the literal is a valid integer.
+ static std::tuple<bool, rational> isValidLiteral(Literal const& _literal);
+
/// @returns true if the literal is a valid rational number.
static std::tuple<bool, rational> parseRational(std::string const& _value);
@@ -899,7 +907,7 @@ public:
Creation, ///< external call using CREATE
Send, ///< CALL, but without data and gas
Transfer, ///< CALL, but without data and throws on error
- SHA3, ///< SHA3
+ KECCAK256, ///< KECCAK256
Selfdestruct, ///< SELFDESTRUCT
Revert, ///< REVERT
ECRecover, ///< CALL to special contract for ecrecover
@@ -1005,6 +1013,7 @@ public:
virtual std::string richIdentifier() const override;
virtual bool operator==(Type const& _other) const override;
+ virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override;
@@ -1034,8 +1043,12 @@ public:
/// @param _selfType if the function is bound, this has to be supplied and is the type of the
/// expression the function is called on.
bool canTakeArguments(TypePointers const& _arguments, TypePointer const& _selfType = TypePointer()) const;
- /// @returns true if the types of parameters are equal (doesn't check return parameter types)
+ /// @returns true if the types of parameters are equal (does not check return parameter types)
bool hasEqualParameterTypes(FunctionType const& _other) const;
+ /// @returns true iff the return types are equal (does not check parameter types)
+ bool hasEqualReturnTypes(FunctionType const& _other) const;
+ /// @returns true iff the function type is equal to the given type, ignoring state mutability differences.
+ bool equalExcludingStateMutability(FunctionType const& _other) const;
/// @returns true if the ABI is used for this call (only meaningful for external calls)
bool isBareCall() const;
@@ -1070,7 +1083,7 @@ public:
{
switch (m_kind)
{
- case FunctionType::Kind::SHA3:
+ case FunctionType::Kind::KECCAK256:
case FunctionType::Kind::SHA256:
case FunctionType::Kind::RIPEMD160:
case FunctionType::Kind::BareCall:
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 54518906..412a7255 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -701,7 +701,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context.appendRevert();
break;
}
- case FunctionType::Kind::SHA3:
+ case FunctionType::Kind::KECCAK256:
{
solAssert(arguments.size() == 1, "");
solAssert(!function.padArguments(), "");
diff --git a/libsolidity/formal/CVC4Interface.h b/libsolidity/formal/CVC4Interface.h
index dcd87525..cd6d761d 100644
--- a/libsolidity/formal/CVC4Interface.h
+++ b/libsolidity/formal/CVC4Interface.h
@@ -21,8 +21,19 @@
#include <boost/noncopyable.hpp>
+#if defined(__GLIBC__)
+// The CVC4 headers includes the deprecated system headers <ext/hash_map>
+// and <ext/hash_set>. These headers cause a warning that will break the
+// build, unless _GLIBCXX_PERMIT_BACKWARD_HASH is set.
+#define _GLIBCXX_PERMIT_BACKWARD_HASH
+#endif
+
#include <cvc4/cvc4.h>
+#if defined(__GLIBC__)
+#undef _GLIBCXX_PERMIT_BACKWARD_HASH
+#endif
+
namespace dev
{
namespace solidity
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index 836e30d2..e800b278 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -58,22 +58,31 @@ using namespace std;
using namespace dev;
using namespace dev::solidity;
-void CompilerStack::setRemappings(vector<string> const& _remappings)
+boost::optional<CompilerStack::Remapping> CompilerStack::parseRemapping(string const& _remapping)
+{
+ auto eq = find(_remapping.begin(), _remapping.end(), '=');
+ if (eq == _remapping.end())
+ return {};
+
+ auto colon = find(_remapping.begin(), eq, ':');
+
+ Remapping r;
+
+ r.context = colon == eq ? string() : string(_remapping.begin(), colon);
+ r.prefix = colon == eq ? string(_remapping.begin(), eq) : string(colon + 1, eq);
+ r.target = string(eq + 1, _remapping.end());
+
+ if (r.prefix.empty())
+ return {};
+
+ return r;
+}
+
+void CompilerStack::setRemappings(vector<Remapping> const& _remappings)
{
- vector<Remapping> remappings;
for (auto const& remapping: _remappings)
- {
- auto eq = find(remapping.begin(), remapping.end(), '=');
- if (eq == remapping.end())
- continue; // ignore
- auto colon = find(remapping.begin(), eq, ':');
- Remapping r;
- r.context = colon == eq ? string() : string(remapping.begin(), colon);
- r.prefix = colon == eq ? string(remapping.begin(), eq) : string(colon + 1, eq);
- r.target = string(eq + 1, remapping.end());
- remappings.push_back(r);
- }
- swap(m_remappings, remappings);
+ solAssert(!remapping.prefix.empty(), "");
+ m_remappings = _remappings;
}
void CompilerStack::setEVMVersion(EVMVersion _version)
diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h
index 2234a8c9..9a15fbf0 100644
--- a/libsolidity/interface/CompilerStack.h
+++ b/libsolidity/interface/CompilerStack.h
@@ -84,6 +84,13 @@ public:
CompilationSuccessful
};
+ struct Remapping
+ {
+ std::string context;
+ std::string prefix;
+ std::string target;
+ };
+
/// Creates a new compiler stack.
/// @param _readFile callback to used to read files for import statements. Must return
/// and must not emit exceptions.
@@ -103,8 +110,11 @@ public:
/// All settings, with the exception of remappings, are reset.
void reset(bool _keepSources = false);
- /// Sets path remappings in the format "context:prefix=target"
- void setRemappings(std::vector<std::string> const& _remappings);
+ // Parses a remapping of the format "context:prefix=target".
+ static boost::optional<Remapping> parseRemapping(std::string const& _remapping);
+
+ /// Sets path remappings.
+ void setRemappings(std::vector<Remapping> const& _remappings);
/// Sets library addresses. Addresses are cleared iff @a _libraries is missing.
/// Will not take effect before running compile.
@@ -319,13 +329,6 @@ private:
FunctionDefinition const& _function
) const;
- struct Remapping
- {
- std::string context;
- std::string prefix;
- std::string target;
- };
-
ReadCallback::Callback m_readFile;
ReadCallback::Callback m_smtQuery;
bool m_optimize = false;
diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp
index 58b84163..c1996777 100644
--- a/libsolidity/interface/StandardCompiler.cpp
+++ b/libsolidity/interface/StandardCompiler.cpp
@@ -326,9 +326,14 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
m_compilerStack.setEVMVersion(*version);
}
- vector<string> remappings;
+ vector<CompilerStack::Remapping> remappings;
for (auto const& remapping: settings.get("remappings", Json::Value()))
- remappings.push_back(remapping.asString());
+ {
+ if (auto r = CompilerStack::parseRemapping(remapping.asString()))
+ remappings.emplace_back(std::move(*r));
+ else
+ return formatFatalError("JSONError", "Invalid remapping: \"" + remapping.asString() + "\"");
+ }
m_compilerStack.setRemappings(remappings);
Json::Value optimizerSettings = settings.get("optimizer", Json::Value());