mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Merge pull request #9062 from a3d4/fix-oversized-object-compiler-error
Fix internal compiler error related to oversized objects
This commit is contained in:
		
						commit
						cf5e9a3551
					
				| @ -5,6 +5,7 @@ Compiler Features: | ||||
| 
 | ||||
| Bugfixes: | ||||
|  * Type Checker: Fix overload resolution in combination with ``{value: ...}``. | ||||
|  * Type Checker: Fix internal compiler error related to oversized types. | ||||
| 
 | ||||
| Compiler Features: | ||||
|  * Optimizer: Add rule to remove shifts inside the byte opcode. | ||||
|  | ||||
| @ -155,29 +155,6 @@ bool StaticAnalyzer::visit(VariableDeclaration const& _variable) | ||||
| 			// This is not a no-op, the entry might pre-exist.
 | ||||
| 			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; | ||||
| } | ||||
| 
 | ||||
| @ -349,47 +326,3 @@ bool StaticAnalyzer::visit(FunctionCall const& _functionCall) | ||||
| 	} | ||||
| 	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(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; | ||||
| 
 | ||||
| 	/// 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/predicate.hpp> | ||||
| #include <boost/range/adaptor/reversed.hpp> | ||||
| 
 | ||||
| #include <memory> | ||||
| #include <vector> | ||||
| @ -91,6 +92,20 @@ bool TypeChecker::visit(ContractDefinition const& _contract) | ||||
| 	for (auto const& n: _contract.subNodes()) | ||||
| 		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; | ||||
| } | ||||
| 
 | ||||
| @ -523,6 +538,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."); | ||||
| 	} | ||||
| 
 | ||||
| 	bool isStructMemberDeclaration = dynamic_cast<StructDefinition const*>(_variable.scope()) != nullptr; | ||||
| 	if (isStructMemberDeclaration) | ||||
| 		return false; | ||||
| 
 | ||||
| 	if (auto referenceType = dynamic_cast<ReferenceType const*>(varType)) | ||||
| 	{ | ||||
| 		auto result = referenceType->validForLocation(referenceType->location()); | ||||
| @ -532,9 +551,33 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) | ||||
| 		{ | ||||
| 			solAssert(!result.message().empty(), "Expected detailed error 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; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -53,6 +53,88 @@ using namespace solidity::frontend; | ||||
| 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.
 | ||||
| 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 | ||||
| { | ||||
| 	m_members.clear(); | ||||
| @ -1749,6 +1847,8 @@ BoolResult ArrayType::validForLocation(DataLocation _loc) const | ||||
| 			break; | ||||
| 		} | ||||
| 		case DataLocation::Storage: | ||||
| 			if (storageSizeUpperBound(*this) >= bigint(1) << 256) | ||||
| 				return BoolResult::err("Type too large for storage."); | ||||
| 			break; | ||||
| 	} | ||||
| 	return true; | ||||
| @ -2388,6 +2488,13 @@ BoolResult StructType::validForLocation(DataLocation _loc) const | ||||
| 			if (!result) | ||||
| 				return result; | ||||
| 		} | ||||
| 
 | ||||
| 	if ( | ||||
| 		_loc == DataLocation::Storage && | ||||
| 		storageSizeUpperBound(*this) >= bigint(1) << 256 | ||||
| 	) | ||||
| 		return BoolResult::err("Type too large for storage."); | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -54,6 +54,14 @@ using rational = boost::rational<bigint>; | ||||
| using TypeResult = util::Result<TypePointer>; | ||||
| 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) | ||||
| { | ||||
| 	solAssert(_denominator != 0, "division by zero"); | ||||
|  | ||||
| @ -2,4 +2,4 @@ contract C { | ||||
|     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 | ||||
|     { | ||||
|         uint[1][][10**20 + 4] x; | ||||
|         uint[10**20 + 4][][1] y; | ||||
|         uint[][10**20 + 4] z; | ||||
|         uint[1][][10**20 + 1] x; | ||||
|         uint[10**20 + 2][][1] y; | ||||
|         uint[][10**20 + 3] z; | ||||
|         uint[10**20 + 4][] t; | ||||
|     } | ||||
|     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: (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-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[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-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 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 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 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. | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user