mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #9388 from ethereum/develop
Merge develop into breaking.
This commit is contained in:
commit
8eee3ed3a2
@ -28,10 +28,10 @@ set -e
|
|||||||
|
|
||||||
REPODIR="$(realpath $(dirname $0)/..)"
|
REPODIR="$(realpath $(dirname $0)/..)"
|
||||||
|
|
||||||
|
EVM=istanbul OPTIMIZE=1 ABI_ENCODER_V2=1 ${REPODIR}/.circleci/soltest.sh
|
||||||
|
|
||||||
for OPTIMIZE in 0 1; do
|
for OPTIMIZE in 0 1; do
|
||||||
for EVM in homestead byzantium constantinople petersburg istanbul; do
|
for EVM in homestead byzantium constantinople petersburg istanbul; do
|
||||||
EVM=$EVM OPTIMIZE=$OPTIMIZE BOOST_TEST_ARGS="-t !@nooptions" ${REPODIR}/.circleci/soltest.sh
|
EVM=$EVM OPTIMIZE=$OPTIMIZE BOOST_TEST_ARGS="-t !@nooptions" ${REPODIR}/.circleci/soltest.sh
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
|
|
||||||
EVM=istanbul OPTIMIZE=1 ABI_ENCODER_V2=1 ${REPODIR}/.circleci/soltest.sh
|
|
||||||
|
@ -32,8 +32,10 @@ Compiler Features:
|
|||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
* Type Checker: Fix overload resolution in combination with ``{value: ...}``.
|
* Type Checker: Fix overload resolution in combination with ``{value: ...}``.
|
||||||
|
* Type Checker: Fix internal compiler error related to oversized types.
|
||||||
|
|
||||||
Compiler Features:
|
Compiler Features:
|
||||||
|
* Build System: Update internal dependency of jsoncpp to 1.9.3.
|
||||||
* Optimizer: Add rule to remove shifts inside the byte opcode.
|
* Optimizer: Add rule to remove shifts inside the byte opcode.
|
||||||
|
|
||||||
|
|
||||||
|
@ -50,6 +50,7 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA
|
|||||||
add_compile_options(-pedantic)
|
add_compile_options(-pedantic)
|
||||||
add_compile_options(-Wno-unknown-pragmas)
|
add_compile_options(-Wno-unknown-pragmas)
|
||||||
add_compile_options(-Wimplicit-fallthrough)
|
add_compile_options(-Wimplicit-fallthrough)
|
||||||
|
add_compile_options(-Wsign-conversion)
|
||||||
|
|
||||||
# Configuration-specific compiler settings.
|
# Configuration-specific compiler settings.
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3 -DETH_DEBUG")
|
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3 -DETH_DEBUG")
|
||||||
|
@ -37,15 +37,16 @@ endif()
|
|||||||
ExternalProject_Add(jsoncpp-project
|
ExternalProject_Add(jsoncpp-project
|
||||||
PREFIX "${prefix}"
|
PREFIX "${prefix}"
|
||||||
DOWNLOAD_DIR "${CMAKE_SOURCE_DIR}/deps/downloads"
|
DOWNLOAD_DIR "${CMAKE_SOURCE_DIR}/deps/downloads"
|
||||||
DOWNLOAD_NAME jsoncpp-1.9.2.tar.gz
|
DOWNLOAD_NAME jsoncpp-1.9.3.tar.gz
|
||||||
URL https://github.com/open-source-parsers/jsoncpp/archive/1.9.2.tar.gz
|
URL https://github.com/open-source-parsers/jsoncpp/archive/1.9.3.tar.gz
|
||||||
URL_HASH SHA256=77a402fb577b2e0e5d0bdc1cf9c65278915cdb25171e3452c68b6da8a561f8f0
|
URL_HASH SHA256=8593c1d69e703563d94d8c12244e2e18893eeb9a8a9f8aa3d09a327aa45c8f7d
|
||||||
CMAKE_COMMAND ${JSONCPP_CMAKE_COMMAND}
|
CMAKE_COMMAND ${JSONCPP_CMAKE_COMMAND}
|
||||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
|
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
|
||||||
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
|
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
|
||||||
-DCMAKE_INSTALL_LIBDIR=lib
|
-DCMAKE_INSTALL_LIBDIR=lib
|
||||||
# Build static lib but suitable to be included in a shared lib.
|
# Build static lib but suitable to be included in a shared lib.
|
||||||
-DCMAKE_POSITION_INDEPENDENT_CODE=${BUILD_SHARED_LIBS}
|
-DCMAKE_POSITION_INDEPENDENT_CODE=${BUILD_SHARED_LIBS}
|
||||||
|
-DJSONCPP_WITH_EXAMPLE=OFF
|
||||||
-DJSONCPP_WITH_TESTS=OFF
|
-DJSONCPP_WITH_TESTS=OFF
|
||||||
-DJSONCPP_WITH_PKGCONFIG_SUPPORT=OFF
|
-DJSONCPP_WITH_PKGCONFIG_SUPPORT=OFF
|
||||||
-DCMAKE_CXX_FLAGS=${JSONCPP_CXX_FLAGS}
|
-DCMAKE_CXX_FLAGS=${JSONCPP_CXX_FLAGS}
|
||||||
|
@ -245,14 +245,14 @@ unsigned SemVerMatchExpressionParser::parseVersionPart()
|
|||||||
return 0;
|
return 0;
|
||||||
else if ('1' <= c && c <= '9')
|
else if ('1' <= c && c <= '9')
|
||||||
{
|
{
|
||||||
unsigned v(c - '0');
|
auto v = static_cast<unsigned>(c - '0');
|
||||||
// If we skip to the next token, the current number is terminated.
|
// If we skip to the next token, the current number is terminated.
|
||||||
while (m_pos == startPos && '0' <= currentChar() && currentChar() <= '9')
|
while (m_pos == startPos && '0' <= currentChar() && currentChar() <= '9')
|
||||||
{
|
{
|
||||||
c = currentChar();
|
c = currentChar();
|
||||||
if (v * 10 < v || v * 10 + unsigned(c - '0') < v * 10)
|
if (v * 10 < v || v * 10 + static_cast<unsigned>(c - '0') < v * 10)
|
||||||
throw SemVerError();
|
throw SemVerError();
|
||||||
v = v * 10 + unsigned(c - '0');
|
v = v * 10 + static_cast<unsigned>(c - '0');
|
||||||
nextChar();
|
nextChar();
|
||||||
}
|
}
|
||||||
return v;
|
return v;
|
||||||
|
@ -191,7 +191,7 @@ CVC4::Expr CVC4Interface::toCVC4Expr(Expression const& _expr)
|
|||||||
return m_context.mkExpr(CVC4::kind::BITVECTOR_AND, arguments[0], arguments[1]);
|
return m_context.mkExpr(CVC4::kind::BITVECTOR_AND, arguments[0], arguments[1]);
|
||||||
else if (n == "int2bv")
|
else if (n == "int2bv")
|
||||||
{
|
{
|
||||||
size_t size = std::stoi(_expr.arguments[1].name);
|
size_t size = std::stoul(_expr.arguments[1].name);
|
||||||
auto i2bvOp = m_context.mkConst(CVC4::IntToBitVector(size));
|
auto i2bvOp = m_context.mkConst(CVC4::IntToBitVector(size));
|
||||||
// CVC4 treats all BVs as unsigned, so we need to manually apply 2's complement if needed.
|
// CVC4 treats all BVs as unsigned, so we need to manually apply 2's complement if needed.
|
||||||
return m_context.mkExpr(
|
return m_context.mkExpr(
|
||||||
|
@ -139,7 +139,7 @@ string SMTLib2Interface::toSExpr(Expression const& _expr)
|
|||||||
std::string sexpr = "(";
|
std::string sexpr = "(";
|
||||||
if (_expr.name == "int2bv")
|
if (_expr.name == "int2bv")
|
||||||
{
|
{
|
||||||
size_t size = std::stoi(_expr.arguments[1].name);
|
size_t size = std::stoul(_expr.arguments[1].name);
|
||||||
auto arg = toSExpr(_expr.arguments.front());
|
auto arg = toSExpr(_expr.arguments.front());
|
||||||
auto int2bv = "(_ int2bv " + to_string(size) + ")";
|
auto int2bv = "(_ int2bv " + to_string(size) + ")";
|
||||||
// Some solvers treat all BVs as unsigned, so we need to manually apply 2's complement if needed.
|
// Some solvers treat all BVs as unsigned, so we need to manually apply 2's complement if needed.
|
||||||
|
@ -184,7 +184,7 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr)
|
|||||||
return arguments[0] & arguments[1];
|
return arguments[0] & arguments[1];
|
||||||
else if (n == "int2bv")
|
else if (n == "int2bv")
|
||||||
{
|
{
|
||||||
size_t size = std::stoi(_expr.arguments[1].name);
|
size_t size = std::stoul(_expr.arguments[1].name);
|
||||||
return z3::int2bv(size, arguments[0]);
|
return z3::int2bv(size, arguments[0]);
|
||||||
}
|
}
|
||||||
else if (n == "bv2int")
|
else if (n == "bv2int")
|
||||||
|
@ -155,29 +155,6 @@ bool StaticAnalyzer::visit(VariableDeclaration const& _variable)
|
|||||||
// This is not a no-op, the entry might pre-exist.
|
// This is not a no-op, the entry might pre-exist.
|
||||||
m_localVarUseCount[make_pair(_variable.id(), &_variable)] += 0;
|
m_localVarUseCount[make_pair(_variable.id(), &_variable)] += 0;
|
||||||
}
|
}
|
||||||
else if (_variable.isStateVariable())
|
|
||||||
{
|
|
||||||
set<StructDefinition const*> structsSeen;
|
|
||||||
TypeSet oversizedSubTypes;
|
|
||||||
if (structureSizeEstimate(*_variable.type(), structsSeen, oversizedSubTypes) >= bigint(1) << 64)
|
|
||||||
m_errorReporter.warning(
|
|
||||||
3408_error,
|
|
||||||
_variable.location(),
|
|
||||||
"Variable " + util::escapeAndQuoteString(_variable.name()) +
|
|
||||||
" covers a large part of storage and thus makes collisions likely. "
|
|
||||||
"Either use mappings or dynamic arrays and allow their size to be increased only "
|
|
||||||
"in small quantities per transaction."
|
|
||||||
);
|
|
||||||
for (Type const* type: oversizedSubTypes)
|
|
||||||
m_errorReporter.warning(
|
|
||||||
7325_error,
|
|
||||||
_variable.location(),
|
|
||||||
"Type " + util::escapeAndQuoteString(type->canonicalName()) +
|
|
||||||
" has large size and thus makes collisions likely. "
|
|
||||||
"Either use mappings or dynamic arrays and allow their size to be increased only "
|
|
||||||
"in small quantities per transaction."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,47 +326,3 @@ bool StaticAnalyzer::visit(FunctionCall const& _functionCall)
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bigint StaticAnalyzer::structureSizeEstimate(
|
|
||||||
Type const& _type,
|
|
||||||
set<StructDefinition const*>& _structsSeen,
|
|
||||||
TypeSet& _oversizedSubTypes
|
|
||||||
)
|
|
||||||
{
|
|
||||||
switch (_type.category())
|
|
||||||
{
|
|
||||||
case Type::Category::Array:
|
|
||||||
{
|
|
||||||
auto const& t = dynamic_cast<ArrayType const&>(_type);
|
|
||||||
bigint baseTypeSize = structureSizeEstimate(*t.baseType(), _structsSeen, _oversizedSubTypes);
|
|
||||||
if (baseTypeSize >= bigint(1) << 64)
|
|
||||||
_oversizedSubTypes.insert(t.baseType());
|
|
||||||
if (!t.isDynamicallySized())
|
|
||||||
return structureSizeEstimate(*t.baseType(), _structsSeen, _oversizedSubTypes) * t.length();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Type::Category::Struct:
|
|
||||||
{
|
|
||||||
auto const& t = dynamic_cast<StructType const&>(_type);
|
|
||||||
bigint size = 1;
|
|
||||||
if (_structsSeen.count(&t.structDefinition()))
|
|
||||||
return size;
|
|
||||||
_structsSeen.insert(&t.structDefinition());
|
|
||||||
for (auto const& m: t.members(nullptr))
|
|
||||||
size += structureSizeEstimate(*m.type, _structsSeen, _oversizedSubTypes);
|
|
||||||
_structsSeen.erase(&t.structDefinition());
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
case Type::Category::Mapping:
|
|
||||||
{
|
|
||||||
auto const* valueType = dynamic_cast<MappingType const&>(_type).valueType();
|
|
||||||
bigint valueTypeSize = structureSizeEstimate(*valueType, _structsSeen, _oversizedSubTypes);
|
|
||||||
if (valueTypeSize >= bigint(1) << 64)
|
|
||||||
_oversizedSubTypes.insert(valueType);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return bigint(1);
|
|
||||||
}
|
|
||||||
|
@ -73,23 +73,6 @@ private:
|
|||||||
bool visit(BinaryOperation const& _operation) override;
|
bool visit(BinaryOperation const& _operation) override;
|
||||||
bool visit(FunctionCall const& _functionCall) override;
|
bool visit(FunctionCall const& _functionCall) override;
|
||||||
|
|
||||||
struct TypeComp
|
|
||||||
{
|
|
||||||
bool operator()(Type const* lhs, Type const* rhs) const
|
|
||||||
{
|
|
||||||
solAssert(lhs && rhs, "");
|
|
||||||
return lhs->richIdentifier() < rhs->richIdentifier();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
using TypeSet = std::set<Type const*, TypeComp>;
|
|
||||||
|
|
||||||
/// @returns the size of this type in storage, including all sub-types.
|
|
||||||
static bigint structureSizeEstimate(
|
|
||||||
Type const& _type,
|
|
||||||
std::set<StructDefinition const*>& _structsSeen,
|
|
||||||
TypeSet& _oversizedSubTypes
|
|
||||||
);
|
|
||||||
|
|
||||||
langutil::ErrorReporter& m_errorReporter;
|
langutil::ErrorReporter& m_errorReporter;
|
||||||
|
|
||||||
/// Flag that indicates whether the current contract definition is a library.
|
/// Flag that indicates whether the current contract definition is a library.
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
|
|
||||||
#include <boost/algorithm/string/join.hpp>
|
#include <boost/algorithm/string/join.hpp>
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -91,6 +92,20 @@ bool TypeChecker::visit(ContractDefinition const& _contract)
|
|||||||
for (auto const& n: _contract.subNodes())
|
for (auto const& n: _contract.subNodes())
|
||||||
n->accept(*this);
|
n->accept(*this);
|
||||||
|
|
||||||
|
bigint size = 0;
|
||||||
|
vector<VariableDeclaration const*> variables;
|
||||||
|
for (ContractDefinition const* contract: boost::adaptors::reverse(m_currentContract->annotation().linearizedBaseContracts))
|
||||||
|
for (VariableDeclaration const* variable: contract->stateVariables())
|
||||||
|
if (!(variable->isConstant() || variable->immutable()))
|
||||||
|
{
|
||||||
|
size += storageSizeUpperBound(*(variable->annotation().type));
|
||||||
|
if (size >= bigint(1) << 256)
|
||||||
|
{
|
||||||
|
m_errorReporter.typeError(7676_error, m_currentContract->location(), "Contract too large for storage.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -555,6 +570,10 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
|
|||||||
m_errorReporter.typeError(6744_error, _variable.location(), "Internal or recursive type is not allowed for public state variables.");
|
m_errorReporter.typeError(6744_error, _variable.location(), "Internal or recursive type is not allowed for public state variables.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isStructMemberDeclaration = dynamic_cast<StructDefinition const*>(_variable.scope()) != nullptr;
|
||||||
|
if (isStructMemberDeclaration)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (auto referenceType = dynamic_cast<ReferenceType const*>(varType))
|
if (auto referenceType = dynamic_cast<ReferenceType const*>(varType))
|
||||||
{
|
{
|
||||||
auto result = referenceType->validForLocation(referenceType->location());
|
auto result = referenceType->validForLocation(referenceType->location());
|
||||||
@ -564,9 +583,33 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
|
|||||||
{
|
{
|
||||||
solAssert(!result.message().empty(), "Expected detailed error message");
|
solAssert(!result.message().empty(), "Expected detailed error message");
|
||||||
m_errorReporter.typeError(1534_error, _variable.location(), result.message());
|
m_errorReporter.typeError(1534_error, _variable.location(), result.message());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (varType->dataStoredIn(DataLocation::Storage))
|
||||||
|
{
|
||||||
|
auto collisionMessage = [&](string const& variableOrType, bool isVariable) -> string {
|
||||||
|
return
|
||||||
|
(isVariable ? "Variable " : "Type ") +
|
||||||
|
util::escapeAndQuoteString(variableOrType) +
|
||||||
|
" covers a large part of storage and thus makes collisions likely."
|
||||||
|
" Either use mappings or dynamic arrays and allow their size to be increased only"
|
||||||
|
" in small quantities per transaction.";
|
||||||
|
};
|
||||||
|
|
||||||
|
if (storageSizeUpperBound(*varType) >= bigint(1) << 64)
|
||||||
|
{
|
||||||
|
if (_variable.isStateVariable())
|
||||||
|
m_errorReporter.warning(3408_error, _variable.location(), collisionMessage(_variable.name(), true));
|
||||||
|
else
|
||||||
|
m_errorReporter.warning(2332_error, _variable.typeName()->location(), collisionMessage(varType->canonicalName(), false));
|
||||||
|
}
|
||||||
|
vector<Type const*> oversizedSubtypes = frontend::oversizedSubtypes(*varType);
|
||||||
|
for (Type const* subtype: oversizedSubtypes)
|
||||||
|
m_errorReporter.warning(7325_error, _variable.typeName()->location(), collisionMessage(subtype->canonicalName(), false));
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,6 +53,88 @@ using namespace solidity::frontend;
|
|||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
|
struct TypeComp
|
||||||
|
{
|
||||||
|
bool operator()(Type const* lhs, Type const* rhs) const
|
||||||
|
{
|
||||||
|
solAssert(lhs && rhs, "");
|
||||||
|
return lhs->richIdentifier() < rhs->richIdentifier();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
using TypeSet = std::set<Type const*, TypeComp>;
|
||||||
|
|
||||||
|
bigint storageSizeUpperBoundInner(
|
||||||
|
Type const& _type,
|
||||||
|
set<StructDefinition const*>& _structsSeen
|
||||||
|
)
|
||||||
|
{
|
||||||
|
switch (_type.category())
|
||||||
|
{
|
||||||
|
case Type::Category::Array:
|
||||||
|
{
|
||||||
|
auto const& t = dynamic_cast<ArrayType const&>(_type);
|
||||||
|
if (!t.isDynamicallySized())
|
||||||
|
return storageSizeUpperBoundInner(*t.baseType(), _structsSeen) * t.length();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Type::Category::Struct:
|
||||||
|
{
|
||||||
|
auto const& t = dynamic_cast<StructType const&>(_type);
|
||||||
|
solAssert(!_structsSeen.count(&t.structDefinition()), "Recursive struct.");
|
||||||
|
bigint size = 1;
|
||||||
|
_structsSeen.insert(&t.structDefinition());
|
||||||
|
for (auto const& m: t.members(nullptr))
|
||||||
|
size += storageSizeUpperBoundInner(*m.type, _structsSeen);
|
||||||
|
_structsSeen.erase(&t.structDefinition());
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return bigint(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void oversizedSubtypesInner(
|
||||||
|
Type const& _type,
|
||||||
|
bool _includeType,
|
||||||
|
set<StructDefinition const*>& _structsSeen,
|
||||||
|
TypeSet& _oversizedSubtypes
|
||||||
|
)
|
||||||
|
{
|
||||||
|
switch (_type.category())
|
||||||
|
{
|
||||||
|
case Type::Category::Array:
|
||||||
|
{
|
||||||
|
auto const& t = dynamic_cast<ArrayType const&>(_type);
|
||||||
|
if (_includeType && storageSizeUpperBound(t) >= bigint(1) << 64)
|
||||||
|
_oversizedSubtypes.insert(&t);
|
||||||
|
oversizedSubtypesInner(*t.baseType(), t.isDynamicallySized(), _structsSeen, _oversizedSubtypes);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Type::Category::Struct:
|
||||||
|
{
|
||||||
|
auto const& t = dynamic_cast<StructType const&>(_type);
|
||||||
|
if (_structsSeen.count(&t.structDefinition()))
|
||||||
|
return;
|
||||||
|
if (_includeType && storageSizeUpperBound(t) >= bigint(1) << 64)
|
||||||
|
_oversizedSubtypes.insert(&t);
|
||||||
|
_structsSeen.insert(&t.structDefinition());
|
||||||
|
for (auto const& m: t.members(nullptr))
|
||||||
|
oversizedSubtypesInner(*m.type, false, _structsSeen, _oversizedSubtypes);
|
||||||
|
_structsSeen.erase(&t.structDefinition());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Type::Category::Mapping:
|
||||||
|
{
|
||||||
|
auto const* valueType = dynamic_cast<MappingType const&>(_type).valueType();
|
||||||
|
oversizedSubtypesInner(*valueType, true, _structsSeen, _oversizedSubtypes);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Check whether (_base ** _exp) fits into 4096 bits.
|
/// Check whether (_base ** _exp) fits into 4096 bits.
|
||||||
bool fitsPrecisionExp(bigint const& _base, bigint const& _exp)
|
bool fitsPrecisionExp(bigint const& _base, bigint const& _exp)
|
||||||
{
|
{
|
||||||
@ -149,6 +231,22 @@ util::Result<TypePointers> transformParametersToExternal(TypePointers const& _pa
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bigint solidity::frontend::storageSizeUpperBound(frontend::Type const& _type)
|
||||||
|
{
|
||||||
|
set<StructDefinition const*> structsSeen;
|
||||||
|
return storageSizeUpperBoundInner(_type, structsSeen);
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<frontend::Type const*> solidity::frontend::oversizedSubtypes(frontend::Type const& _type)
|
||||||
|
{
|
||||||
|
set<StructDefinition const*> structsSeen;
|
||||||
|
TypeSet oversized;
|
||||||
|
oversizedSubtypesInner(_type, false, structsSeen, oversized);
|
||||||
|
vector<frontend::Type const*> res;
|
||||||
|
copy(oversized.cbegin(), oversized.cend(), back_inserter(res));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
void Type::clearCache() const
|
void Type::clearCache() const
|
||||||
{
|
{
|
||||||
m_members.clear();
|
m_members.clear();
|
||||||
@ -1775,6 +1873,8 @@ BoolResult ArrayType::validForLocation(DataLocation _loc) const
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DataLocation::Storage:
|
case DataLocation::Storage:
|
||||||
|
if (storageSizeUpperBound(*this) >= bigint(1) << 256)
|
||||||
|
return BoolResult::err("Type too large for storage.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -2461,6 +2561,13 @@ BoolResult StructType::validForLocation(DataLocation _loc) const
|
|||||||
if (!result)
|
if (!result)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
_loc == DataLocation::Storage &&
|
||||||
|
storageSizeUpperBound(*this) >= bigint(1) << 256
|
||||||
|
)
|
||||||
|
return BoolResult::err("Type too large for storage.");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,6 +54,14 @@ using rational = boost::rational<bigint>;
|
|||||||
using TypeResult = util::Result<TypePointer>;
|
using TypeResult = util::Result<TypePointer>;
|
||||||
using BoolResult = util::Result<bool>;
|
using BoolResult = util::Result<bool>;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace solidity::frontend
|
||||||
|
{
|
||||||
|
|
||||||
|
bigint storageSizeUpperBound(frontend::Type const& _type);
|
||||||
|
std::vector<frontend::Type const*> oversizedSubtypes(frontend::Type const& _type);
|
||||||
|
|
||||||
inline rational makeRational(bigint const& _numerator, bigint const& _denominator)
|
inline rational makeRational(bigint const& _numerator, bigint const& _denominator)
|
||||||
{
|
{
|
||||||
solAssert(_denominator != 0, "division by zero");
|
solAssert(_denominator != 0, "division by zero");
|
||||||
|
@ -646,7 +646,7 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_context << swapInstruction(stackLayout.size() - static_cast<size_t>(stackLayout.back()) - 1);
|
m_context << swapInstruction(stackLayout.size() - static_cast<unsigned>(stackLayout.back()) - 1u);
|
||||||
swap(stackLayout[static_cast<size_t>(stackLayout.back())], stackLayout.back());
|
swap(stackLayout[static_cast<size_t>(stackLayout.back())], stackLayout.back());
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < stackLayout.size(); ++i)
|
for (size_t i = 0; i < stackLayout.size(); ++i)
|
||||||
|
@ -52,11 +52,11 @@ BMC::BMC(
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void BMC::analyze(SourceUnit const& _source, set<Expression const*> _safeAssertions)
|
void BMC::analyze(SourceUnit const& _source, map<ASTNode const*, set<VerificationTarget::Type>> _solvedTargets)
|
||||||
{
|
{
|
||||||
solAssert(_source.annotation().experimentalFeatures.count(ExperimentalFeature::SMTChecker), "");
|
solAssert(_source.annotation().experimentalFeatures.count(ExperimentalFeature::SMTChecker), "");
|
||||||
|
|
||||||
m_safeAssertions += move(_safeAssertions);
|
m_solvedTargets = move(_solvedTargets);
|
||||||
m_context.setSolver(m_interface.get());
|
m_context.setSolver(m_interface.get());
|
||||||
m_context.clear();
|
m_context.clear();
|
||||||
m_context.setAssertionAccumulation(true);
|
m_context.setAssertionAccumulation(true);
|
||||||
@ -684,16 +684,22 @@ void BMC::checkBalance(BMCVerificationTarget& _target)
|
|||||||
void BMC::checkAssert(BMCVerificationTarget& _target)
|
void BMC::checkAssert(BMCVerificationTarget& _target)
|
||||||
{
|
{
|
||||||
solAssert(_target.type == VerificationTarget::Type::Assert, "");
|
solAssert(_target.type == VerificationTarget::Type::Assert, "");
|
||||||
if (!m_safeAssertions.count(_target.expression))
|
|
||||||
checkCondition(
|
if (
|
||||||
_target.constraints && !_target.value,
|
m_solvedTargets.count(_target.expression) &&
|
||||||
_target.callStack,
|
m_solvedTargets.at(_target.expression).count(_target.type)
|
||||||
_target.modelExpressions,
|
)
|
||||||
_target.expression->location(),
|
return;
|
||||||
4661_error,
|
|
||||||
7812_error,
|
checkCondition(
|
||||||
"Assertion violation"
|
_target.constraints && !_target.value,
|
||||||
);
|
_target.callStack,
|
||||||
|
_target.modelExpressions,
|
||||||
|
_target.expression->location(),
|
||||||
|
4661_error,
|
||||||
|
7812_error,
|
||||||
|
"Assertion violation"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BMC::addVerificationTarget(
|
void BMC::addVerificationTarget(
|
||||||
|
@ -63,7 +63,7 @@ public:
|
|||||||
smtutil::SMTSolverChoice _enabledSolvers
|
smtutil::SMTSolverChoice _enabledSolvers
|
||||||
);
|
);
|
||||||
|
|
||||||
void analyze(SourceUnit const& _sources, std::set<Expression const*> _safeAssertions);
|
void analyze(SourceUnit const& _sources, std::map<ASTNode const*, std::set<VerificationTarget::Type>> _solvedTargets);
|
||||||
|
|
||||||
/// This is used if the SMT solver is not directly linked into this binary.
|
/// This is used if the SMT solver is not directly linked into this binary.
|
||||||
/// @returns a list of inputs to the SMT solver that were not part of the argument to
|
/// @returns a list of inputs to the SMT solver that were not part of the argument to
|
||||||
@ -180,8 +180,8 @@ private:
|
|||||||
|
|
||||||
std::vector<BMCVerificationTarget> m_verificationTargets;
|
std::vector<BMCVerificationTarget> m_verificationTargets;
|
||||||
|
|
||||||
/// Assertions that are known to be safe.
|
/// Targets that were already proven.
|
||||||
std::set<Expression const*> m_safeAssertions;
|
std::map<ASTNode const*, std::set<VerificationTarget::Type>> m_solvedTargets;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -96,48 +96,7 @@ void CHC::analyze(SourceUnit const& _source)
|
|||||||
for (auto const* source: sources)
|
for (auto const* source: sources)
|
||||||
source->accept(*this);
|
source->accept(*this);
|
||||||
|
|
||||||
for (auto const& [scope, target]: m_verificationTargets)
|
checkVerificationTargets();
|
||||||
{
|
|
||||||
if (target.type == VerificationTarget::Type::Assert)
|
|
||||||
{
|
|
||||||
auto assertions = transactionAssertions(scope);
|
|
||||||
for (auto const* assertion: assertions)
|
|
||||||
{
|
|
||||||
createErrorBlock();
|
|
||||||
connectBlocks(target.value, error(), target.constraints && (target.errorId == static_cast<size_t>(assertion->id())));
|
|
||||||
auto [result, model] = query(error(), assertion->location());
|
|
||||||
// This should be fine but it's a bug in the old compiler
|
|
||||||
(void)model;
|
|
||||||
if (result == smtutil::CheckResult::UNSATISFIABLE)
|
|
||||||
m_safeAssertions.insert(assertion);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (target.type == VerificationTarget::Type::PopEmptyArray)
|
|
||||||
{
|
|
||||||
solAssert(dynamic_cast<FunctionCall const*>(scope), "");
|
|
||||||
createErrorBlock();
|
|
||||||
connectBlocks(target.value, error(), target.constraints && (target.errorId == static_cast<size_t>(scope->id())));
|
|
||||||
auto [result, model] = query(error(), scope->location());
|
|
||||||
// This should be fine but it's a bug in the old compiler
|
|
||||||
(void)model;
|
|
||||||
if (result != smtutil::CheckResult::UNSATISFIABLE)
|
|
||||||
{
|
|
||||||
string msg = "Empty array \"pop\" ";
|
|
||||||
if (result == smtutil::CheckResult::SATISFIABLE)
|
|
||||||
msg += "detected here.";
|
|
||||||
else
|
|
||||||
msg += "might happen here.";
|
|
||||||
m_unsafeTargets.insert(scope);
|
|
||||||
m_outerErrorReporter.warning(
|
|
||||||
2529_error,
|
|
||||||
scope->location(),
|
|
||||||
msg
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
solAssert(false, "");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<string> CHC::unhandledQueries() const
|
vector<string> CHC::unhandledQueries() const
|
||||||
@ -540,7 +499,7 @@ void CHC::visitAssert(FunctionCall const& _funCall)
|
|||||||
m_currentBlock,
|
m_currentBlock,
|
||||||
m_currentFunction->isConstructor() ? summary(*m_currentContract) : summary(*m_currentFunction),
|
m_currentFunction->isConstructor() ? summary(*m_currentContract) : summary(*m_currentFunction),
|
||||||
currentPathConditions() && !m_context.expression(*args.front())->currentValue() && (
|
currentPathConditions() && !m_context.expression(*args.front())->currentValue() && (
|
||||||
m_error.currentValue() == static_cast<size_t>(_funCall.id())
|
m_error.currentValue() == newErrorId(_funCall)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -638,7 +597,7 @@ void CHC::makeArrayPopVerificationTarget(FunctionCall const& _arrayPop)
|
|||||||
connectBlocks(
|
connectBlocks(
|
||||||
m_currentBlock,
|
m_currentBlock,
|
||||||
m_currentFunction->isConstructor() ? summary(*m_currentContract) : summary(*m_currentFunction),
|
m_currentFunction->isConstructor() ? summary(*m_currentContract) : summary(*m_currentFunction),
|
||||||
currentPathConditions() && symbArray->length() <= 0 && m_error.currentValue() == static_cast<size_t>(_arrayPop.id())
|
currentPathConditions() && symbArray->length() <= 0 && m_error.currentValue() == newErrorId(_arrayPop)
|
||||||
);
|
);
|
||||||
|
|
||||||
m_context.addAssertion(m_error.currentValue() == previousError);
|
m_context.addAssertion(m_error.currentValue() == previousError);
|
||||||
@ -647,9 +606,10 @@ void CHC::makeArrayPopVerificationTarget(FunctionCall const& _arrayPop)
|
|||||||
void CHC::resetSourceAnalysis()
|
void CHC::resetSourceAnalysis()
|
||||||
{
|
{
|
||||||
m_verificationTargets.clear();
|
m_verificationTargets.clear();
|
||||||
m_safeAssertions.clear();
|
m_safeTargets.clear();
|
||||||
m_unsafeTargets.clear();
|
m_unsafeTargets.clear();
|
||||||
m_functionAssertions.clear();
|
m_functionAssertions.clear();
|
||||||
|
m_errorIds.clear();
|
||||||
m_callGraph.clear();
|
m_callGraph.clear();
|
||||||
m_summaries.clear();
|
m_summaries.clear();
|
||||||
}
|
}
|
||||||
@ -990,13 +950,13 @@ vector<smtutil::Expression> CHC::initialStateVariables(ContractDefinition const&
|
|||||||
return stateVariablesAtIndex(0, _contract);
|
return stateVariablesAtIndex(0, _contract);
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<smtutil::Expression> CHC::stateVariablesAtIndex(int _index)
|
vector<smtutil::Expression> CHC::stateVariablesAtIndex(unsigned _index)
|
||||||
{
|
{
|
||||||
solAssert(m_currentContract, "");
|
solAssert(m_currentContract, "");
|
||||||
return stateVariablesAtIndex(_index, *m_currentContract);
|
return stateVariablesAtIndex(_index, *m_currentContract);
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<smtutil::Expression> CHC::stateVariablesAtIndex(int _index, ContractDefinition const& _contract)
|
vector<smtutil::Expression> CHC::stateVariablesAtIndex(unsigned _index, ContractDefinition const& _contract)
|
||||||
{
|
{
|
||||||
return applyMap(
|
return applyMap(
|
||||||
stateVariablesIncludingInheritedAndPrivate(_contract),
|
stateVariablesIncludingInheritedAndPrivate(_contract),
|
||||||
@ -1167,7 +1127,98 @@ void CHC::addArrayPopVerificationTarget(ASTNode const* _scope, smtutil::Expressi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CHC::checkVerificationTargets()
|
||||||
|
{
|
||||||
|
for (auto const& [scope, target]: m_verificationTargets)
|
||||||
|
{
|
||||||
|
if (target.type == VerificationTarget::Type::Assert)
|
||||||
|
checkAssertTarget(scope, target);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string satMsg;
|
||||||
|
string unknownMsg;
|
||||||
|
|
||||||
|
if (target.type == VerificationTarget::Type::PopEmptyArray)
|
||||||
|
{
|
||||||
|
solAssert(dynamic_cast<FunctionCall const*>(scope), "");
|
||||||
|
satMsg = "Empty array \"pop\" detected here.";
|
||||||
|
unknownMsg = "Empty array \"pop\" might happen here.";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
solAssert(false, "");
|
||||||
|
|
||||||
|
auto it = m_errorIds.find(scope->id());
|
||||||
|
solAssert(it != m_errorIds.end(), "");
|
||||||
|
checkAndReportTarget(scope, target, it->second, satMsg, unknownMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHC::checkAssertTarget(ASTNode const* _scope, CHCVerificationTarget const& _target)
|
||||||
|
{
|
||||||
|
solAssert(_target.type == VerificationTarget::Type::Assert, "");
|
||||||
|
auto assertions = transactionAssertions(_scope);
|
||||||
|
for (auto const* assertion: assertions)
|
||||||
|
{
|
||||||
|
auto it = m_errorIds.find(assertion->id());
|
||||||
|
solAssert(it != m_errorIds.end(), "");
|
||||||
|
unsigned errorId = it->second;
|
||||||
|
|
||||||
|
createErrorBlock();
|
||||||
|
connectBlocks(_target.value, error(), _target.constraints && (_target.errorId == errorId));
|
||||||
|
auto [result, model] = query(error(), assertion->location());
|
||||||
|
// This should be fine but it's a bug in the old compiler
|
||||||
|
(void)model;
|
||||||
|
if (result == smtutil::CheckResult::UNSATISFIABLE)
|
||||||
|
m_safeTargets[assertion].insert(_target.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CHC::checkAndReportTarget(
|
||||||
|
ASTNode const* _scope,
|
||||||
|
CHCVerificationTarget const& _target,
|
||||||
|
unsigned _errorId,
|
||||||
|
string _satMsg,
|
||||||
|
string _unknownMsg
|
||||||
|
)
|
||||||
|
{
|
||||||
|
createErrorBlock();
|
||||||
|
connectBlocks(_target.value, error(), _target.constraints && (_target.errorId == _errorId));
|
||||||
|
auto [result, model] = query(error(), _scope->location());
|
||||||
|
// This should be fine but it's a bug in the old compiler
|
||||||
|
(void)model;
|
||||||
|
if (result == smtutil::CheckResult::UNSATISFIABLE)
|
||||||
|
m_safeTargets[_scope].insert(_target.type);
|
||||||
|
else if (result == smtutil::CheckResult::SATISFIABLE)
|
||||||
|
{
|
||||||
|
solAssert(!_satMsg.empty(), "");
|
||||||
|
m_unsafeTargets[_scope].insert(_target.type);
|
||||||
|
m_outerErrorReporter.warning(
|
||||||
|
2529_error,
|
||||||
|
_scope->location(),
|
||||||
|
_satMsg
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if (!_unknownMsg.empty())
|
||||||
|
m_outerErrorReporter.warning(
|
||||||
|
1147_error,
|
||||||
|
_scope->location(),
|
||||||
|
_unknownMsg
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
string CHC::uniquePrefix()
|
string CHC::uniquePrefix()
|
||||||
{
|
{
|
||||||
return to_string(m_blockCounter++);
|
return to_string(m_blockCounter++);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned CHC::newErrorId(frontend::Expression const& _expr)
|
||||||
|
{
|
||||||
|
unsigned errorId = m_context.newUniqueId();
|
||||||
|
// We need to make sure the error id is not zero,
|
||||||
|
// because error id zero actually means no error in the CHC encoding.
|
||||||
|
if (errorId == 0)
|
||||||
|
errorId = m_context.newUniqueId();
|
||||||
|
m_errorIds.emplace(_expr.id(), errorId);
|
||||||
|
return errorId;
|
||||||
|
}
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
|
|
||||||
#include <libsmtutil/CHCSolverInterface.h>
|
#include <libsmtutil/CHCSolverInterface.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
namespace solidity::frontend
|
namespace solidity::frontend
|
||||||
@ -54,7 +55,8 @@ public:
|
|||||||
|
|
||||||
void analyze(SourceUnit const& _sources);
|
void analyze(SourceUnit const& _sources);
|
||||||
|
|
||||||
std::set<Expression const*> const& safeAssertions() const { return m_safeAssertions; }
|
std::map<ASTNode const*, std::set<VerificationTarget::Type>> const& safeTargets() const { return m_safeTargets; }
|
||||||
|
std::map<ASTNode const*, std::set<VerificationTarget::Type>> const& unsafeTargets() const { return m_unsafeTargets; }
|
||||||
|
|
||||||
/// This is used if the Horn solver is not directly linked into this binary.
|
/// This is used if the Horn solver is not directly linked into this binary.
|
||||||
/// @returns a list of inputs to the Horn solver that were not part of the argument to
|
/// @returns a list of inputs to the Horn solver that were not part of the argument to
|
||||||
@ -152,8 +154,8 @@ private:
|
|||||||
/// of the current transaction.
|
/// of the current transaction.
|
||||||
std::vector<smtutil::Expression> initialStateVariables();
|
std::vector<smtutil::Expression> initialStateVariables();
|
||||||
std::vector<smtutil::Expression> initialStateVariables(ContractDefinition const& _contract);
|
std::vector<smtutil::Expression> initialStateVariables(ContractDefinition const& _contract);
|
||||||
std::vector<smtutil::Expression> stateVariablesAtIndex(int _index);
|
std::vector<smtutil::Expression> stateVariablesAtIndex(unsigned _index);
|
||||||
std::vector<smtutil::Expression> stateVariablesAtIndex(int _index, ContractDefinition const& _contract);
|
std::vector<smtutil::Expression> stateVariablesAtIndex(unsigned _index, ContractDefinition const& _contract);
|
||||||
/// @returns the current symbolic values of the current state variables.
|
/// @returns the current symbolic values of the current state variables.
|
||||||
std::vector<smtutil::Expression> currentStateVariables();
|
std::vector<smtutil::Expression> currentStateVariables();
|
||||||
std::vector<smtutil::Expression> currentStateVariables(ContractDefinition const& _contract);
|
std::vector<smtutil::Expression> currentStateVariables(ContractDefinition const& _contract);
|
||||||
@ -191,6 +193,18 @@ private:
|
|||||||
void addVerificationTarget(ASTNode const* _scope, VerificationTarget::Type _type, smtutil::Expression _from, smtutil::Expression _constraints, smtutil::Expression _errorId);
|
void addVerificationTarget(ASTNode const* _scope, VerificationTarget::Type _type, smtutil::Expression _from, smtutil::Expression _constraints, smtutil::Expression _errorId);
|
||||||
void addAssertVerificationTarget(ASTNode const* _scope, smtutil::Expression _from, smtutil::Expression _constraints, smtutil::Expression _errorId);
|
void addAssertVerificationTarget(ASTNode const* _scope, smtutil::Expression _from, smtutil::Expression _constraints, smtutil::Expression _errorId);
|
||||||
void addArrayPopVerificationTarget(ASTNode const* _scope, smtutil::Expression _errorId);
|
void addArrayPopVerificationTarget(ASTNode const* _scope, smtutil::Expression _errorId);
|
||||||
|
|
||||||
|
void checkVerificationTargets();
|
||||||
|
// Forward declaration. Definition is below.
|
||||||
|
struct CHCVerificationTarget;
|
||||||
|
void checkAssertTarget(ASTNode const* _scope, CHCVerificationTarget const& _target);
|
||||||
|
void checkAndReportTarget(
|
||||||
|
ASTNode const* _scope,
|
||||||
|
CHCVerificationTarget const& _target,
|
||||||
|
unsigned _errorId,
|
||||||
|
std::string _satMsg,
|
||||||
|
std::string _unknownMsg
|
||||||
|
);
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
/// Misc.
|
/// Misc.
|
||||||
@ -198,6 +212,10 @@ private:
|
|||||||
/// Returns a prefix to be used in a new unique block name
|
/// Returns a prefix to be used in a new unique block name
|
||||||
/// and increases the block counter.
|
/// and increases the block counter.
|
||||||
std::string uniquePrefix();
|
std::string uniquePrefix();
|
||||||
|
|
||||||
|
/// @returns a new unique error id associated with _expr and stores
|
||||||
|
/// it into m_errorIds.
|
||||||
|
unsigned newErrorId(Expression const& _expr);
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
/// Predicates.
|
/// Predicates.
|
||||||
@ -257,10 +275,10 @@ private:
|
|||||||
|
|
||||||
std::map<ASTNode const*, CHCVerificationTarget, IdCompare> m_verificationTargets;
|
std::map<ASTNode const*, CHCVerificationTarget, IdCompare> m_verificationTargets;
|
||||||
|
|
||||||
/// Assertions proven safe.
|
/// Targets proven safe.
|
||||||
std::set<Expression const*> m_safeAssertions;
|
std::map<ASTNode const*, std::set<VerificationTarget::Type>> m_safeTargets;
|
||||||
/// Targets proven unsafe.
|
/// Targets proven unsafe.
|
||||||
std::set<ASTNode const*> m_unsafeTargets;
|
std::map<ASTNode const*, std::set<VerificationTarget::Type>> m_unsafeTargets;
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
/// Control-flow.
|
/// Control-flow.
|
||||||
@ -271,6 +289,11 @@ private:
|
|||||||
|
|
||||||
std::map<ASTNode const*, std::set<Expression const*>, IdCompare> m_functionAssertions;
|
std::map<ASTNode const*, std::set<Expression const*>, IdCompare> m_functionAssertions;
|
||||||
|
|
||||||
|
/// Maps ASTNode ids to error ids.
|
||||||
|
/// A multimap is used instead of map anticipating the UnderOverflow
|
||||||
|
/// target which has 2 error ids.
|
||||||
|
std::multimap<unsigned, unsigned> m_errorIds;
|
||||||
|
|
||||||
/// The current block.
|
/// The current block.
|
||||||
smtutil::Expression m_currentBlock = smtutil::Expression(true);
|
smtutil::Expression m_currentBlock = smtutil::Expression(true);
|
||||||
|
|
||||||
|
@ -32,21 +32,21 @@ EncodingContext::EncodingContext():
|
|||||||
void EncodingContext::reset()
|
void EncodingContext::reset()
|
||||||
{
|
{
|
||||||
resetAllVariables();
|
resetAllVariables();
|
||||||
resetSlackId();
|
resetUniqueId();
|
||||||
m_expressions.clear();
|
m_expressions.clear();
|
||||||
m_globalContext.clear();
|
m_globalContext.clear();
|
||||||
m_state.reset();
|
m_state.reset();
|
||||||
m_assertions.clear();
|
m_assertions.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EncodingContext::resetSlackId()
|
void EncodingContext::resetUniqueId()
|
||||||
{
|
{
|
||||||
m_nextSlackId = 0;
|
m_nextUniqueId = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned EncodingContext::newSlackId()
|
unsigned EncodingContext::newUniqueId()
|
||||||
{
|
{
|
||||||
return m_nextSlackId++;
|
return m_nextUniqueId++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EncodingContext::clear()
|
void EncodingContext::clear()
|
||||||
|
@ -41,9 +41,9 @@ public:
|
|||||||
/// To be used in the beginning of a root function visit.
|
/// To be used in the beginning of a root function visit.
|
||||||
void reset();
|
void reset();
|
||||||
/// Resets the fresh id for slack variables.
|
/// Resets the fresh id for slack variables.
|
||||||
void resetSlackId();
|
void resetUniqueId();
|
||||||
/// Returns the current fresh slack id and increments it.
|
/// Returns the current fresh slack id and increments it.
|
||||||
unsigned newSlackId();
|
unsigned newUniqueId();
|
||||||
/// Clears the entire context, erasing everything.
|
/// Clears the entire context, erasing everything.
|
||||||
/// To be used before a model checking engine starts.
|
/// To be used before a model checking engine starts.
|
||||||
void clear();
|
void clear();
|
||||||
@ -173,8 +173,8 @@ private:
|
|||||||
bool m_accumulateAssertions = true;
|
bool m_accumulateAssertions = true;
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
/// Fresh ids for slack variables to be created deterministically.
|
/// Central source of unique ids.
|
||||||
unsigned m_nextSlackId = 0;
|
unsigned m_nextUniqueId = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,12 @@ void ModelChecker::analyze(SourceUnit const& _source)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
m_chc.analyze(_source);
|
m_chc.analyze(_source);
|
||||||
m_bmc.analyze(_source, m_chc.safeAssertions());
|
|
||||||
|
auto solvedTargets = m_chc.safeTargets();
|
||||||
|
for (auto const& target: m_chc.unsafeTargets())
|
||||||
|
solvedTargets[target.first] += target.second;
|
||||||
|
|
||||||
|
m_bmc.analyze(_source, solvedTargets);
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<string> ModelChecker::unhandledQueries()
|
vector<string> ModelChecker::unhandledQueries()
|
||||||
|
@ -1253,7 +1253,7 @@ pair<smtutil::Expression, smtutil::Expression> SMTEncoder::arithmeticOperation(
|
|||||||
// - RHS is -1
|
// - RHS is -1
|
||||||
// the result is then -(type.min), which wraps back to type.min
|
// the result is then -(type.min), which wraps back to type.min
|
||||||
smtutil::Expression maxLeft = _left == smt::minValue(*intType);
|
smtutil::Expression maxLeft = _left == smt::minValue(*intType);
|
||||||
smtutil::Expression minusOneRight = _right == -1;
|
smtutil::Expression minusOneRight = _right == numeric_limits<size_t >::max();
|
||||||
smtutil::Expression wrap = smtutil::Expression::ite(maxLeft && minusOneRight, smt::minValue(*intType), valueUnbounded);
|
smtutil::Expression wrap = smtutil::Expression::ite(maxLeft && minusOneRight, smt::minValue(*intType), valueUnbounded);
|
||||||
return {wrap, valueUnbounded};
|
return {wrap, valueUnbounded};
|
||||||
}
|
}
|
||||||
@ -1262,7 +1262,7 @@ pair<smtutil::Expression, smtutil::Expression> SMTEncoder::arithmeticOperation(
|
|||||||
auto symbMax = smt::maxValue(*intType);
|
auto symbMax = smt::maxValue(*intType);
|
||||||
|
|
||||||
smtutil::Expression intValueRange = (0 - symbMin) + symbMax + 1;
|
smtutil::Expression intValueRange = (0 - symbMin) + symbMax + 1;
|
||||||
string suffix = to_string(_operation.id()) + "_" + to_string(m_context.newSlackId());
|
string suffix = to_string(_operation.id()) + "_" + to_string(m_context.newUniqueId());
|
||||||
smt::SymbolicIntVariable k(intType, intType, "k_" + suffix, m_context);
|
smt::SymbolicIntVariable k(intType, intType, "k_" + suffix, m_context);
|
||||||
smt::SymbolicIntVariable m(intType, intType, "m_" + suffix, m_context);
|
smt::SymbolicIntVariable m(intType, intType, "m_" + suffix, m_context);
|
||||||
|
|
||||||
|
@ -32,8 +32,8 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
static_assert(
|
static_assert(
|
||||||
(JSONCPP_VERSION_MAJOR == 1) && (JSONCPP_VERSION_MINOR == 9) && (JSONCPP_VERSION_PATCH == 2),
|
(JSONCPP_VERSION_MAJOR == 1) && (JSONCPP_VERSION_MINOR == 9) && (JSONCPP_VERSION_PATCH == 3),
|
||||||
"Unexpected jsoncpp version: " JSONCPP_VERSION_STRING ". Expecting 1.9.2."
|
"Unexpected jsoncpp version: " JSONCPP_VERSION_STRING ". Expecting 1.9.3."
|
||||||
);
|
);
|
||||||
|
|
||||||
namespace solidity::util
|
namespace solidity::util
|
||||||
|
@ -237,7 +237,7 @@ void AsmAnalyzer::operator()(VariableDeclaration const& _varDecl)
|
|||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
3947_error,
|
3947_error,
|
||||||
variable.location,
|
variable.location,
|
||||||
"Assigning value of type \"" + givenType.str() + "\" to variable of type \"" + variable.type.str() + "."
|
"Assigning value of type \"" + givenType.str() + "\" to variable of type \"" + variable.type.str() + "\"."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,7 +165,7 @@ void CodeTransform::deleteVariable(Scope::Variable const& _var)
|
|||||||
{
|
{
|
||||||
yulAssert(m_allowStackOpt, "");
|
yulAssert(m_allowStackOpt, "");
|
||||||
yulAssert(m_context->variableStackHeights.count(&_var) > 0, "");
|
yulAssert(m_context->variableStackHeights.count(&_var) > 0, "");
|
||||||
m_unusedStackSlots.insert(m_context->variableStackHeights[&_var]);
|
m_unusedStackSlots.insert(static_cast<int>(m_context->variableStackHeights[&_var]));
|
||||||
m_context->variableStackHeights.erase(&_var);
|
m_context->variableStackHeights.erase(&_var);
|
||||||
m_context->variableReferences.erase(&_var);
|
m_context->variableReferences.erase(&_var);
|
||||||
m_variablesScheduledForDeletion.erase(&_var);
|
m_variablesScheduledForDeletion.erase(&_var);
|
||||||
@ -402,7 +402,7 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
|
|||||||
yulAssert(m_scope->identifiers.count(_function.name), "");
|
yulAssert(m_scope->identifiers.count(_function.name), "");
|
||||||
Scope::Function& function = std::get<Scope::Function>(m_scope->identifiers.at(_function.name));
|
Scope::Function& function = std::get<Scope::Function>(m_scope->identifiers.at(_function.name));
|
||||||
|
|
||||||
int height = m_evm15 ? 0 : 1;
|
size_t height = m_evm15 ? 0 : 1;
|
||||||
yulAssert(m_info.scopes.at(&_function.body), "");
|
yulAssert(m_info.scopes.at(&_function.body), "");
|
||||||
Scope* varScope = m_info.scopes.at(m_info.virtualBlocks.at(&_function).get()).get();
|
Scope* varScope = m_info.scopes.at(m_info.virtualBlocks.at(&_function).get()).get();
|
||||||
yulAssert(varScope, "");
|
yulAssert(varScope, "");
|
||||||
@ -420,7 +420,7 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
|
|||||||
else
|
else
|
||||||
m_assembly.appendLabel(functionEntryID(_function.name, function));
|
m_assembly.appendLabel(functionEntryID(_function.name, function));
|
||||||
|
|
||||||
m_assembly.setStackHeight(height);
|
m_assembly.setStackHeight(static_cast<int>(height));
|
||||||
|
|
||||||
for (auto const& v: _function.returnVariables)
|
for (auto const& v: _function.returnVariables)
|
||||||
{
|
{
|
||||||
@ -457,7 +457,7 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
|
|||||||
StackTooDeepError error(_error);
|
StackTooDeepError error(_error);
|
||||||
if (error.functionName.empty())
|
if (error.functionName.empty())
|
||||||
error.functionName = _function.name;
|
error.functionName = _function.name;
|
||||||
stackError(std::move(error), height);
|
stackError(std::move(error), static_cast<int>(height));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_assembly.appendLabel(m_context->functionExitPoints.top().label);
|
m_assembly.appendLabel(m_context->functionExitPoints.top().label);
|
||||||
@ -502,7 +502,7 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_assembly.appendInstruction(evmasm::swapInstruction(stackLayout.size() - static_cast<size_t>(stackLayout.back()) - 1));
|
m_assembly.appendInstruction(evmasm::swapInstruction(static_cast<unsigned>(stackLayout.size()) - static_cast<unsigned>(stackLayout.back()) - 1u));
|
||||||
swap(stackLayout[static_cast<size_t>(stackLayout.back())], stackLayout.back());
|
swap(stackLayout[static_cast<size_t>(stackLayout.back())], stackLayout.back());
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < stackLayout.size(); ++i)
|
for (size_t i = 0; i < stackLayout.size(); ++i)
|
||||||
|
@ -31,7 +31,7 @@ REPO_ROOT="$(dirname "$0")"/..
|
|||||||
fi
|
fi
|
||||||
# Add dependencies
|
# Add dependencies
|
||||||
mkdir -p "$SOLDIR/deps/downloads/" 2>/dev/null || true
|
mkdir -p "$SOLDIR/deps/downloads/" 2>/dev/null || true
|
||||||
wget -O "$SOLDIR/deps/downloads/jsoncpp-1.9.2.tar.gz" https://github.com/open-source-parsers/jsoncpp/archive/1.9.2.tar.gz
|
wget -O "$SOLDIR/deps/downloads/jsoncpp-1.9.3.tar.gz" https://github.com/open-source-parsers/jsoncpp/archive/1.9.3.tar.gz
|
||||||
mkdir -p "$REPO_ROOT/upload"
|
mkdir -p "$REPO_ROOT/upload"
|
||||||
tar --owner 0 --group 0 -czf "$REPO_ROOT/upload/solidity_$versionstring.tar.gz" -C "$TEMPDIR" "solidity_$versionstring"
|
tar --owner 0 --group 0 -czf "$REPO_ROOT/upload/solidity_$versionstring.tar.gz" -C "$TEMPDIR" "solidity_$versionstring"
|
||||||
rm -r "$TEMPDIR"
|
rm -r "$TEMPDIR"
|
||||||
|
@ -127,6 +127,12 @@ def find_ids_in_test_files(file_names):
|
|||||||
return used_ids
|
return used_ids
|
||||||
|
|
||||||
|
|
||||||
|
def find_ids_in_cmdline_test_err(file_name):
|
||||||
|
source = read_file(file_name)
|
||||||
|
pattern = r' \(\d\d\d\d\):'
|
||||||
|
return {m.group(0)[-6:-2] for m in re.finditer(pattern, source, flags=re.MULTILINE)}
|
||||||
|
|
||||||
|
|
||||||
def print_ids(ids):
|
def print_ids(ids):
|
||||||
for k, id in enumerate(sorted(ids)):
|
for k, id in enumerate(sorted(ids)):
|
||||||
if k % 10 > 0:
|
if k % 10 > 0:
|
||||||
@ -149,6 +155,11 @@ def examine_id_coverage(top_dir, used_ids):
|
|||||||
)
|
)
|
||||||
covered_ids = find_ids_in_test_files(test_file_names)
|
covered_ids = find_ids_in_test_files(test_file_names)
|
||||||
|
|
||||||
|
# special case, we are interested in warnings which are ignored by regular tests:
|
||||||
|
# Warning (1878): SPDX license identifier not provided in source file. ....
|
||||||
|
# Warning (3420): Source file does not specify required compiler version!
|
||||||
|
covered_ids |= find_ids_in_cmdline_test_err(path.join(top_dir, "test", "cmdlineTests", "error_codes", "err"))
|
||||||
|
|
||||||
print(f"IDs in source files: {len(used_ids)}")
|
print(f"IDs in source files: {len(used_ids)}")
|
||||||
print(f"IDs in test files : {len(covered_ids)} ({len(covered_ids) - len(used_ids)})")
|
print(f"IDs in test files : {len(covered_ids)} ({len(covered_ids) - len(used_ids)})")
|
||||||
print()
|
print()
|
||||||
|
@ -107,7 +107,7 @@ mv solidity solc
|
|||||||
|
|
||||||
# Fetch jsoncpp dependency
|
# Fetch jsoncpp dependency
|
||||||
mkdir -p ./solc/deps/downloads/ 2>/dev/null || true
|
mkdir -p ./solc/deps/downloads/ 2>/dev/null || true
|
||||||
wget -O ./solc/deps/downloads/jsoncpp-1.9.2.tar.gz https://github.com/open-source-parsers/jsoncpp/archive/1.9.2.tar.gz
|
wget -O ./solc/deps/downloads/jsoncpp-1.9.3.tar.gz https://github.com/open-source-parsers/jsoncpp/archive/1.9.3.tar.gz
|
||||||
|
|
||||||
# Determine version
|
# Determine version
|
||||||
cd solc
|
cd solc
|
||||||
|
@ -46,6 +46,7 @@ struct Testsuite
|
|||||||
bool smt;
|
bool smt;
|
||||||
bool needsVM;
|
bool needsVM;
|
||||||
TestCase::TestCaseCreator testCaseCreator;
|
TestCase::TestCaseCreator testCaseCreator;
|
||||||
|
std::vector<std::string> labels{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -64,8 +65,8 @@ Testsuite const g_interactiveTestsuites[] = {
|
|||||||
{"Semantic", "libsolidity", "semanticTests", false, true, &SemanticTest::create},
|
{"Semantic", "libsolidity", "semanticTests", false, true, &SemanticTest::create},
|
||||||
{"JSON AST", "libsolidity", "ASTJSON", false, false, &ASTJSONTest::create},
|
{"JSON AST", "libsolidity", "ASTJSON", false, false, &ASTJSONTest::create},
|
||||||
{"JSON ABI", "libsolidity", "ABIJson", false, false, &ABIJsonTest::create},
|
{"JSON ABI", "libsolidity", "ABIJson", false, false, &ABIJsonTest::create},
|
||||||
{"SMT Checker", "libsolidity", "smtCheckerTests", true, false, &SMTCheckerTest::create},
|
{"SMT Checker", "libsolidity", "smtCheckerTests", true, false, &SMTCheckerTest::create, {"nooptions"}},
|
||||||
{"SMT Checker JSON", "libsolidity", "smtCheckerTestsJSON", true, false, &SMTCheckerJSONTest::create},
|
{"SMT Checker JSON", "libsolidity", "smtCheckerTestsJSON", true, false, &SMTCheckerJSONTest::create, {"nooptions"}},
|
||||||
{"Gas Estimates", "libsolidity", "gasTests", false, false, &GasTest::create}
|
{"Gas Estimates", "libsolidity", "gasTests", false, false, &GasTest::create}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -65,6 +65,7 @@ int registerTests(
|
|||||||
boost::filesystem::path const& _basepath,
|
boost::filesystem::path const& _basepath,
|
||||||
boost::filesystem::path const& _path,
|
boost::filesystem::path const& _path,
|
||||||
bool _enforceViaYul,
|
bool _enforceViaYul,
|
||||||
|
vector<string> const& _labels,
|
||||||
TestCase::TestCaseCreator _testCaseCreator
|
TestCase::TestCaseCreator _testCaseCreator
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@ -83,6 +84,7 @@ int registerTests(
|
|||||||
*sub_suite,
|
*sub_suite,
|
||||||
_basepath, _path / entry.path().filename(),
|
_basepath, _path / entry.path().filename(),
|
||||||
_enforceViaYul,
|
_enforceViaYul,
|
||||||
|
_labels,
|
||||||
_testCaseCreator
|
_testCaseCreator
|
||||||
);
|
);
|
||||||
_suite.add(sub_suite);
|
_suite.add(sub_suite);
|
||||||
@ -95,7 +97,7 @@ int registerTests(
|
|||||||
static vector<unique_ptr<string const>> filenames;
|
static vector<unique_ptr<string const>> filenames;
|
||||||
|
|
||||||
filenames.emplace_back(make_unique<string>(_path.string()));
|
filenames.emplace_back(make_unique<string>(_path.string()));
|
||||||
_suite.add(make_test_case(
|
auto test_case = make_test_case(
|
||||||
[config, _testCaseCreator]
|
[config, _testCaseCreator]
|
||||||
{
|
{
|
||||||
BOOST_REQUIRE_NO_THROW({
|
BOOST_REQUIRE_NO_THROW({
|
||||||
@ -125,7 +127,10 @@ int registerTests(
|
|||||||
_path.stem().string(),
|
_path.stem().string(),
|
||||||
*filenames.back(),
|
*filenames.back(),
|
||||||
0
|
0
|
||||||
));
|
);
|
||||||
|
for (auto const& _label: _labels)
|
||||||
|
test_case->add_label(_label);
|
||||||
|
_suite.add(test_case);
|
||||||
numTestsAdded = 1;
|
numTestsAdded = 1;
|
||||||
}
|
}
|
||||||
return numTestsAdded;
|
return numTestsAdded;
|
||||||
@ -174,6 +179,7 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] )
|
|||||||
options.testPath / ts.path,
|
options.testPath / ts.path,
|
||||||
ts.subpath,
|
ts.subpath,
|
||||||
options.enforceViaYul,
|
options.enforceViaYul,
|
||||||
|
ts.labels,
|
||||||
ts.testCaseCreator
|
ts.testCaseCreator
|
||||||
) > 0, std::string("no ") + ts.title + " tests found");
|
) > 0, std::string("no ") + ts.title + " tests found");
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,29 @@
|
|||||||
|
Warning (1878): SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: <SPDX-License>" to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information.
|
||||||
|
--> error_codes/input.sol
|
||||||
|
|
||||||
Error (4937): No visibility specified. Did you intend to add "public"?
|
Error (4937): No visibility specified. Did you intend to add "public"?
|
||||||
--> error_codes/input.sol:4:5:
|
--> error_codes/input.sol:2:5:
|
||||||
|
|
|
|
||||||
4 | function f() {
|
2 | function f() {
|
||||||
| ^ (Relevant source part starts here and spans across multiple lines).
|
| ^ (Relevant source part starts here and spans across multiple lines).
|
||||||
|
|
||||||
Warning (3420): Source file does not specify required compiler version!
|
Warning (3420): Source file does not specify required compiler version!
|
||||||
--> error_codes/input.sol
|
--> error_codes/input.sol
|
||||||
|
|
||||||
Error (4247): Expression has to be an lvalue.
|
Error (4247): Expression has to be an lvalue.
|
||||||
--> error_codes/input.sol:5:9:
|
--> error_codes/input.sol:3:9:
|
||||||
|
|
|
|
||||||
5 | 2=0;
|
3 | 2=0;
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
Error (7407): Type int_const 0 is not implicitly convertible to expected type int_const 2.
|
Error (7407): Type int_const 0 is not implicitly convertible to expected type int_const 2.
|
||||||
--> error_codes/input.sol:5:11:
|
--> error_codes/input.sol:3:11:
|
||||||
|
|
|
|
||||||
5 | 2=0;
|
3 | 2=0;
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
Error (2614): Indexed expression has to be a type, mapping or array (is literal_string "")
|
Error (2614): Indexed expression has to be a type, mapping or array (is literal_string "")
|
||||||
--> error_codes/input.sol:6:9:
|
--> error_codes/input.sol:4:9:
|
||||||
|
|
|
|
||||||
6 | ""[2];
|
4 | ""[2];
|
||||||
| ^^
|
| ^^
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0
|
|
||||||
|
|
||||||
contract C {
|
contract C {
|
||||||
function f() {
|
function f() {
|
||||||
2=0;
|
2=0;
|
||||||
|
@ -55,7 +55,7 @@ protected:
|
|||||||
|
|
||||||
BOOST_FIXTURE_TEST_SUITE(SMTChecker, SMTCheckerFramework)
|
BOOST_FIXTURE_TEST_SUITE(SMTChecker, SMTCheckerFramework)
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(import_base)
|
BOOST_AUTO_TEST_CASE(import_base, *boost::unit_test::label("no_options"))
|
||||||
{
|
{
|
||||||
CompilerStack c;
|
CompilerStack c;
|
||||||
c.setSources({
|
c.setSources({
|
||||||
@ -97,7 +97,7 @@ BOOST_AUTO_TEST_CASE(import_base)
|
|||||||
BOOST_CHECK_EQUAL(asserts, 1);
|
BOOST_CHECK_EQUAL(asserts, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(import_library)
|
BOOST_AUTO_TEST_CASE(import_library, *boost::unit_test::label("no_options"))
|
||||||
{
|
{
|
||||||
CompilerStack c;
|
CompilerStack c;
|
||||||
c.setSources({
|
c.setSources({
|
||||||
|
@ -26,7 +26,7 @@ using namespace solidity::langutil;
|
|||||||
using namespace solidity::frontend;
|
using namespace solidity::frontend;
|
||||||
using namespace solidity::frontend::test;
|
using namespace solidity::frontend::test;
|
||||||
|
|
||||||
SMTCheckerTest::SMTCheckerTest(string const& _filename, langutil::EVMVersion _evmVersion): SyntaxTest(_filename, _evmVersion)
|
SMTCheckerTest::SMTCheckerTest(string const& _filename): SyntaxTest(_filename, EVMVersion{})
|
||||||
{
|
{
|
||||||
auto const& choice = m_reader.stringSetting("SMTSolvers", "any");
|
auto const& choice = m_reader.stringSetting("SMTSolvers", "any");
|
||||||
if (choice == "any")
|
if (choice == "any")
|
||||||
|
@ -31,9 +31,9 @@ class SMTCheckerTest: public SyntaxTest
|
|||||||
public:
|
public:
|
||||||
static std::unique_ptr<TestCase> create(Config const& _config)
|
static std::unique_ptr<TestCase> create(Config const& _config)
|
||||||
{
|
{
|
||||||
return std::make_unique<SMTCheckerTest>(_config.filename, _config.evmVersion);
|
return std::make_unique<SMTCheckerTest>(_config.filename);
|
||||||
}
|
}
|
||||||
SMTCheckerTest(std::string const& _filename, langutil::EVMVersion _evmVersion);
|
SMTCheckerTest(std::string const& _filename);
|
||||||
|
|
||||||
TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false) override;
|
TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false) override;
|
||||||
|
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
==== Source: a ====
|
||||||
|
contract A { }
|
||||||
|
==== Source: b ====
|
||||||
|
import {C} from "a";
|
||||||
|
contract B { }
|
||||||
|
// ----
|
||||||
|
// DeclarationError 2904: (b:0-20): Declaration "C" not found in "a" (referenced as "a").
|
@ -2,4 +2,4 @@ contract C {
|
|||||||
mapping(uint => uint[2**100]) x;
|
mapping(uint => uint[2**100]) x;
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// Warning 7325: (17-48): Type "uint256[1267650600228229401496703205376]" has large size and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
|
// Warning 7325: (17-46): Type "uint256[1267650600228229401496703205376]" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
|
||||||
|
@ -23,9 +23,9 @@ contract C {
|
|||||||
|
|
||||||
struct Q0
|
struct Q0
|
||||||
{
|
{
|
||||||
uint[1][][10**20 + 4] x;
|
uint[1][][10**20 + 1] x;
|
||||||
uint[10**20 + 4][][1] y;
|
uint[10**20 + 2][][1] y;
|
||||||
uint[][10**20 + 4] z;
|
uint[][10**20 + 3] z;
|
||||||
uint[10**20 + 4][] t;
|
uint[10**20 + 4][] t;
|
||||||
}
|
}
|
||||||
Q0 q0;
|
Q0 q0;
|
||||||
@ -57,11 +57,12 @@ contract C {
|
|||||||
// ----
|
// ----
|
||||||
// Warning 3408: (106-111): Variable "s0" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
|
// Warning 3408: (106-111): Variable "s0" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
|
||||||
// Warning 3408: (171-176): Variable "s1" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
|
// Warning 3408: (171-176): Variable "s1" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
|
||||||
// Warning 7325: (341-346): Type "C.P[103]" has large size and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
|
// Warning 7325: (341-343): Type "C.P[103]" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
|
||||||
// Warning 7325: (341-346): Type "C.P[104]" has large size and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
|
// Warning 7325: (341-343): Type "C.P[104]" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
|
||||||
// Warning 3408: (505-510): Variable "q0" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
|
// Warning 3408: (505-510): Variable "q0" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
|
||||||
// Warning 7325: (505-510): Type "uint256[100000000000000000004]" has large size and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
|
// Warning 7325: (505-507): Type "uint256[100000000000000000002]" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
|
||||||
|
// Warning 7325: (505-507): Type "uint256[100000000000000000004]" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
|
||||||
// Warning 3408: (576-581): Variable "q1" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
|
// Warning 3408: (576-581): Variable "q1" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
|
||||||
// Warning 7325: (647-652): Type "uint256[100000000000000000006]" has large size and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
|
// Warning 7325: (647-649): Type "uint256[100000000000000000006]" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
|
||||||
// Warning 3408: (715-720): Variable "q3" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
|
// Warning 3408: (715-720): Variable "q3" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
|
||||||
// Warning 7325: (783-788): Type "uint256[100000000000000000008]" has large size and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
|
// Warning 7325: (783-785): Type "uint256[100000000000000000008]" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
pragma solidity >= 0.0;
|
||||||
|
contract C {
|
||||||
|
uint[2**255][2] a;
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError 1534: (77-94): Type too large for storage.
|
||||||
|
// TypeError 7676: (60-97): Contract too large for storage.
|
@ -0,0 +1,10 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
pragma solidity >= 0.0;
|
||||||
|
contract C {
|
||||||
|
uint[2**255] a;
|
||||||
|
uint[2**255] b;
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning 3408: (77-91): Variable "a" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
|
||||||
|
// Warning 3408: (97-111): Variable "b" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
|
||||||
|
// TypeError 7676: (60-114): Contract too large for storage.
|
12
test/libsolidity/syntaxTests/largeTypes/oversized_struct.sol
Normal file
12
test/libsolidity/syntaxTests/largeTypes/oversized_struct.sol
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
pragma solidity >= 0.0;
|
||||||
|
contract C {
|
||||||
|
struct S {
|
||||||
|
uint[2**255] a;
|
||||||
|
uint[2**255] b;
|
||||||
|
}
|
||||||
|
S s;
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError 1534: (146-149): Type too large for storage.
|
||||||
|
// TypeError 7676: (60-152): Contract too large for storage.
|
@ -0,0 +1,6 @@
|
|||||||
|
contract C {
|
||||||
|
struct S { uint256[2**255] x; }
|
||||||
|
function f(S storage) internal {}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning 2332: (64-65): Type "C.S" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
|
3
test/libsolidity/syntaxTests/unexpected.sol
Normal file
3
test/libsolidity/syntaxTests/unexpected.sol
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
unexpected
|
||||||
|
// ----
|
||||||
|
// ParserError 7858: (0-10): Expected pragma, import directive or contract/interface/library/struct/enum definition.
|
@ -5,4 +5,4 @@
|
|||||||
// dialect: evmTyped
|
// dialect: evmTyped
|
||||||
// ----
|
// ----
|
||||||
// TypeError 5473: (15-28): "invalidType" is not a valid type (user defined types are not yet supported).
|
// TypeError 5473: (15-28): "invalidType" is not a valid type (user defined types are not yet supported).
|
||||||
// TypeError 3947: (10-11): Assigning value of type "invalidType" to variable of type "u256.
|
// TypeError 3947: (10-11): Assigning value of type "invalidType" to variable of type "u256".
|
||||||
|
@ -8,5 +8,5 @@
|
|||||||
// ====
|
// ====
|
||||||
// dialect: evmTyped
|
// dialect: evmTyped
|
||||||
// ----
|
// ----
|
||||||
// TypeError 3947: (126-127): Assigning value of type "bool" to variable of type "u256.
|
// TypeError 3947: (126-127): Assigning value of type "bool" to variable of type "u256".
|
||||||
// TypeError 3947: (129-136): Assigning value of type "u256" to variable of type "bool.
|
// TypeError 3947: (129-136): Assigning value of type "u256" to variable of type "bool".
|
||||||
|
@ -8,6 +8,7 @@ add_dependencies(ossfuzz
|
|||||||
strictasm_assembly_ossfuzz
|
strictasm_assembly_ossfuzz
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
if (OSSFUZZ)
|
if (OSSFUZZ)
|
||||||
add_custom_target(ossfuzz_proto)
|
add_custom_target(ossfuzz_proto)
|
||||||
add_dependencies(ossfuzz_proto
|
add_dependencies(ossfuzz_proto
|
||||||
@ -54,6 +55,7 @@ if (OSSFUZZ)
|
|||||||
protobuf.a
|
protobuf.a
|
||||||
)
|
)
|
||||||
set_target_properties(yul_proto_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
|
set_target_properties(yul_proto_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
|
||||||
|
target_compile_options(yul_proto_ossfuzz PUBLIC ${COMPILE_OPTIONS} -Wno-sign-conversion)
|
||||||
|
|
||||||
add_executable(yul_proto_diff_ossfuzz yulProto_diff_ossfuzz.cpp yulFuzzerCommon.cpp protoToYul.cpp yulProto.pb.cc)
|
add_executable(yul_proto_diff_ossfuzz yulProto_diff_ossfuzz.cpp yulFuzzerCommon.cpp protoToYul.cpp yulProto.pb.cc)
|
||||||
target_include_directories(yul_proto_diff_ossfuzz PRIVATE /usr/include/libprotobuf-mutator)
|
target_include_directories(yul_proto_diff_ossfuzz PRIVATE /usr/include/libprotobuf-mutator)
|
||||||
@ -64,6 +66,7 @@ if (OSSFUZZ)
|
|||||||
protobuf.a
|
protobuf.a
|
||||||
)
|
)
|
||||||
set_target_properties(yul_proto_diff_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
|
set_target_properties(yul_proto_diff_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
|
||||||
|
target_compile_options(yul_proto_diff_ossfuzz PUBLIC ${COMPILE_OPTIONS} -Wno-sign-conversion)
|
||||||
|
|
||||||
add_executable(yul_proto_diff_custom_mutate_ossfuzz
|
add_executable(yul_proto_diff_custom_mutate_ossfuzz
|
||||||
yulProto_diff_ossfuzz.cpp
|
yulProto_diff_ossfuzz.cpp
|
||||||
@ -80,6 +83,7 @@ if (OSSFUZZ)
|
|||||||
protobuf.a
|
protobuf.a
|
||||||
)
|
)
|
||||||
set_target_properties(yul_proto_diff_custom_mutate_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
|
set_target_properties(yul_proto_diff_custom_mutate_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
|
||||||
|
target_compile_options(yul_proto_diff_custom_mutate_ossfuzz PUBLIC ${COMPILE_OPTIONS} -Wno-sign-conversion)
|
||||||
|
|
||||||
add_executable(abiv2_proto_ossfuzz
|
add_executable(abiv2_proto_ossfuzz
|
||||||
../../EVMHost.cpp
|
../../EVMHost.cpp
|
||||||
@ -99,6 +103,7 @@ if (OSSFUZZ)
|
|||||||
protobuf.a
|
protobuf.a
|
||||||
)
|
)
|
||||||
set_target_properties(abiv2_proto_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
|
set_target_properties(abiv2_proto_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
|
||||||
|
target_compile_options(abiv2_proto_ossfuzz PUBLIC ${COMPILE_OPTIONS} -Wno-sign-conversion)
|
||||||
|
|
||||||
add_executable(sol_proto_ossfuzz
|
add_executable(sol_proto_ossfuzz
|
||||||
solProtoFuzzer.cpp
|
solProtoFuzzer.cpp
|
||||||
@ -118,6 +123,7 @@ if (OSSFUZZ)
|
|||||||
protobuf.a
|
protobuf.a
|
||||||
)
|
)
|
||||||
set_target_properties(sol_proto_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
|
set_target_properties(sol_proto_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
|
||||||
|
target_compile_options(sol_proto_ossfuzz PUBLIC ${COMPILE_OPTIONS} -Wno-sign-conversion)
|
||||||
else()
|
else()
|
||||||
add_library(solc_opt_ossfuzz
|
add_library(solc_opt_ossfuzz
|
||||||
solc_opt_ossfuzz.cpp
|
solc_opt_ossfuzz.cpp
|
||||||
|
@ -192,13 +192,13 @@ void ProtoConverter::visit(VarRef const& _x)
|
|||||||
{
|
{
|
||||||
// Ensure that there is at least one variable declaration to reference in function scope.
|
// Ensure that there is at least one variable declaration to reference in function scope.
|
||||||
yulAssert(m_currentFuncVars.size() > 0, "Proto fuzzer: No variables to reference.");
|
yulAssert(m_currentFuncVars.size() > 0, "Proto fuzzer: No variables to reference.");
|
||||||
m_output << *m_currentFuncVars[_x.varnum() % m_currentFuncVars.size()];
|
m_output << *m_currentFuncVars[static_cast<size_t>(_x.varnum()) % m_currentFuncVars.size()];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Ensure that there is at least one variable declaration to reference in nested scopes.
|
// Ensure that there is at least one variable declaration to reference in nested scopes.
|
||||||
yulAssert(m_currentGlobalVars.size() > 0, "Proto fuzzer: No global variables to reference.");
|
yulAssert(m_currentGlobalVars.size() > 0, "Proto fuzzer: No global variables to reference.");
|
||||||
m_output << *m_currentGlobalVars[_x.varnum() % m_currentGlobalVars.size()];
|
m_output << *m_currentGlobalVars[static_cast<size_t>(_x.varnum()) % m_currentGlobalVars.size()];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,7 +202,7 @@ DEFINE_PROTO_FUZZER(Program const& _input)
|
|||||||
// With libFuzzer binary run this to generate a YUL source file x.yul:
|
// With libFuzzer binary run this to generate a YUL source file x.yul:
|
||||||
// PROTO_FUZZER_DUMP_PATH=x.yul ./a.out proto-input
|
// PROTO_FUZZER_DUMP_PATH=x.yul ./a.out proto-input
|
||||||
ofstream of(dump_path);
|
ofstream of(dump_path);
|
||||||
of.write(sol_source.data(), sol_source.size());
|
of.write(sol_source.data(), static_cast<streamsize>(sol_source.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (char const* dump_path = getenv("SOL_DEBUG_FILE"))
|
if (char const* dump_path = getenv("SOL_DEBUG_FILE"))
|
||||||
|
@ -43,7 +43,7 @@ DEFINE_PROTO_FUZZER(Program const& _input)
|
|||||||
// With libFuzzer binary run this to generate a YUL source file x.yul:
|
// With libFuzzer binary run this to generate a YUL source file x.yul:
|
||||||
// PROTO_FUZZER_DUMP_PATH=x.yul ./a.out proto-input
|
// PROTO_FUZZER_DUMP_PATH=x.yul ./a.out proto-input
|
||||||
ofstream of(dump_path);
|
ofstream of(dump_path);
|
||||||
of.write(yul_source.data(), yul_source.size());
|
of.write(yul_source.data(), static_cast<streamsize>(yul_source.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (yul_source.size() > 1200)
|
if (yul_source.size() > 1200)
|
||||||
|
@ -64,7 +64,7 @@ DEFINE_PROTO_FUZZER(Program const& _input)
|
|||||||
// With libFuzzer binary run this to generate a YUL source file x.yul:
|
// With libFuzzer binary run this to generate a YUL source file x.yul:
|
||||||
// PROTO_FUZZER_DUMP_PATH=x.yul ./a.out proto-input
|
// PROTO_FUZZER_DUMP_PATH=x.yul ./a.out proto-input
|
||||||
ofstream of(dump_path);
|
ofstream of(dump_path);
|
||||||
of.write(yul_source.data(), yul_source.size());
|
of.write(yul_source.data(), static_cast<streamsize>(yul_source.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
YulStringRepository::reset();
|
YulStringRepository::reset();
|
||||||
|
@ -51,8 +51,9 @@ void copyZeroExtended(
|
|||||||
_target[_targetOffset + i] = _sourceOffset + i < _source.size() ? _source[_sourceOffset + i] : 0;
|
_target[_targetOffset + i] = _sourceOffset + i < _source.size() ? _source[_sourceOffset + i] : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Count leading zeros for uint64
|
/// Count leading zeros for uint64. Following WebAssembly rules, it returns 64 for @a _v being zero.
|
||||||
uint64_t clz(uint64_t _v)
|
/// NOTE: the clz builtin of the compiler may or may not do this
|
||||||
|
uint64_t clz64(uint64_t _v)
|
||||||
{
|
{
|
||||||
if (_v == 0)
|
if (_v == 0)
|
||||||
return 64;
|
return 64;
|
||||||
@ -133,7 +134,11 @@ u256 EwasmBuiltinInterpreter::evalBuiltin(YulString _fun, vector<u256> const& _a
|
|||||||
accessMemory(arg[0], 4);
|
accessMemory(arg[0], 4);
|
||||||
return readMemoryHalfWord(arg[0]);
|
return readMemoryHalfWord(arg[0]);
|
||||||
}
|
}
|
||||||
|
else if (_fun == "i32.clz"_yulstring)
|
||||||
|
// NOTE: the clz implementation assumes 64-bit inputs, hence the adjustment
|
||||||
|
return clz64(arg[0] & uint32_t(-1)) - 32;
|
||||||
|
else if (_fun == "i64.clz"_yulstring)
|
||||||
|
return clz64(arg[0]);
|
||||||
|
|
||||||
string prefix = _fun.str();
|
string prefix = _fun.str();
|
||||||
string suffix;
|
string suffix;
|
||||||
@ -202,8 +207,6 @@ u256 EwasmBuiltinInterpreter::evalWasmBuiltin(string const& _fun, vector<Word> c
|
|||||||
return arg[0] != arg[1] ? 1 : 0;
|
return arg[0] != arg[1] ? 1 : 0;
|
||||||
else if (_fun == "eqz")
|
else if (_fun == "eqz")
|
||||||
return arg[0] == 0 ? 1 : 0;
|
return arg[0] == 0 ? 1 : 0;
|
||||||
else if (_fun == "clz")
|
|
||||||
return clz(arg[0]);
|
|
||||||
else if (_fun == "lt_u")
|
else if (_fun == "lt_u")
|
||||||
return arg[0] < arg[1] ? 1 : 0;
|
return arg[0] < arg[1] ? 1 : 0;
|
||||||
else if (_fun == "gt_u")
|
else if (_fun == "gt_u")
|
||||||
|
@ -179,8 +179,8 @@ BOOST_AUTO_TEST_CASE(alternativeMutations_should_choose_between_mutations_with_g
|
|||||||
for (size_t i = 0; i < 10; ++i)
|
for (size_t i = 0; i < 10; ++i)
|
||||||
{
|
{
|
||||||
Chromosome mutatedChromosome = mutation(chromosome);
|
Chromosome mutatedChromosome = mutation(chromosome);
|
||||||
cCount += (mutatedChromosome == Chromosome("c") ? 1 : 0);
|
cCount += (mutatedChromosome == Chromosome("c") ? 1u : 0u);
|
||||||
fCount += (mutatedChromosome == Chromosome("f") ? 1 : 0);
|
fCount += (mutatedChromosome == Chromosome("f") ? 1u : 0u);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This particular seed results in 7 "c"s out of 10 which looks plausible given the 80% chance.
|
// This particular seed results in 7 "c"s out of 10 which looks plausible given the 80% chance.
|
||||||
|
Loading…
Reference in New Issue
Block a user