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