mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Merge pull request #9818 from a3d4/introduce-compositetype
Introduce CompositeType
This commit is contained in:
		
						commit
						83aa8c1e23
					
				| @ -465,7 +465,6 @@ void ContractLevelChecker::checkPayableFallbackWithoutReceive(ContractDefinition | ||||
| void ContractLevelChecker::checkStorageSize(ContractDefinition const& _contract) | ||||
| { | ||||
| 	bigint size = 0; | ||||
| 	vector<VariableDeclaration const*> variables; | ||||
| 	for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.annotation().linearizedBaseContracts)) | ||||
| 		for (VariableDeclaration const* variable: contract->stateVariables()) | ||||
| 			if (!(variable->isConstant() || variable->immutable())) | ||||
| @ -473,7 +472,7 @@ void ContractLevelChecker::checkStorageSize(ContractDefinition const& _contract) | ||||
| 				size += variable->annotation().type->storageSizeUpperBound(); | ||||
| 				if (size >= bigint(1) << 256) | ||||
| 				{ | ||||
| 					m_errorReporter.typeError(7676_error, _contract.location(), "Contract too large for storage."); | ||||
| 					m_errorReporter.typeError(7676_error, _contract.location(), "Contract requires too much storage."); | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| @ -158,17 +158,16 @@ bool StaticAnalyzer::visit(VariableDeclaration const& _variable) | ||||
| 	} | ||||
| 
 | ||||
| 	if (_variable.isStateVariable() || _variable.referenceLocation() == VariableDeclaration::Location::Storage) | ||||
| 	{ | ||||
| 		TypePointer varType = _variable.annotation().type; | ||||
| 		for (Type const* subtype: frontend::oversizedSubtypes(*varType)) | ||||
| 		{ | ||||
| 			string message = "Type " + subtype->toString(true) + | ||||
| 				" 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."; | ||||
| 			m_errorReporter.warning(7325_error, _variable.typeName().location(), message); | ||||
| 		} | ||||
| 	} | ||||
| 		if (auto varType = dynamic_cast<CompositeType const*>(_variable.annotation().type)) | ||||
| 			for (Type const* type: varType->fullDecomposition()) | ||||
| 				if (type->storageSizeUpperBound() >= (bigint(1) << 64)) | ||||
| 				{ | ||||
| 					string message = "Type " + type->toString(true) + | ||||
| 						" 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."; | ||||
| 					m_errorReporter.warning(7325_error, _variable.typeName().location(), message); | ||||
| 				} | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| @ -44,6 +44,7 @@ | ||||
| #include <boost/range/algorithm/copy.hpp> | ||||
| 
 | ||||
| #include <limits> | ||||
| #include <unordered_set> | ||||
| #include <utility> | ||||
| 
 | ||||
| using namespace std; | ||||
| @ -54,57 +55,6 @@ 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>; | ||||
| 
 | ||||
| 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 && t.storageSizeUpperBound() >= 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 && t.storageSizeUpperBound() >= 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) | ||||
| { | ||||
| @ -201,16 +151,6 @@ util::Result<TypePointers> transformParametersToExternal(TypePointers const& _pa | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| vector<frontend::Type const*> solidity::frontend::oversizedSubtypes(frontend::Type const& _type) | ||||
| { | ||||
| 	set<StructDefinition const*> structsSeen; | ||||
| 	TypeSet oversized; | ||||
| 	oversizedSubtypesInner(_type, true, structsSeen, oversized); | ||||
| 	vector<frontend::Type const*> res; | ||||
| 	copy(oversized.cbegin(), oversized.cend(), back_inserter(res)); | ||||
| 	return res; | ||||
| } | ||||
| 
 | ||||
| void Type::clearCache() const | ||||
| { | ||||
| 	m_members.clear(); | ||||
| @ -1612,6 +1552,21 @@ TypeResult ContractType::unaryOperatorResult(Token _operator) const | ||||
| 		return nullptr; | ||||
| } | ||||
| 
 | ||||
| vector<Type const*> CompositeType::fullDecomposition() const | ||||
| { | ||||
| 	vector<Type const*> res = {this}; | ||||
| 	unordered_set<string> seen = {richIdentifier()}; | ||||
| 	for (size_t k = 0; k < res.size(); ++k) | ||||
| 		if (auto composite = dynamic_cast<CompositeType const*>(res[k])) | ||||
| 			for (Type const* next: composite->decomposition()) | ||||
| 				if (seen.count(next->richIdentifier()) == 0) | ||||
| 				{ | ||||
| 					seen.insert(next->richIdentifier()); | ||||
| 					res.push_back(next); | ||||
| 				} | ||||
| 	return res; | ||||
| } | ||||
| 
 | ||||
| Type const* ReferenceType::withLocation(DataLocation _location, bool _isPointer) const | ||||
| { | ||||
| 	return TypeProvider::withLocation(this, _location, _isPointer); | ||||
| @ -2649,6 +2604,13 @@ vector<tuple<string, TypePointer>> StructType::makeStackItems() const | ||||
| 	solAssert(false, ""); | ||||
| } | ||||
| 
 | ||||
| vector<Type const*> StructType::decomposition() const | ||||
| { | ||||
| 	vector<Type const*> res; | ||||
| 	for (MemberList::Member const& member: members(nullptr)) | ||||
| 		res.push_back(member.type); | ||||
| 	return res; | ||||
| } | ||||
| 
 | ||||
| TypePointer EnumType::encodingType() const | ||||
| { | ||||
|  | ||||
| @ -60,8 +60,6 @@ using BoolResult = util::Result<bool>; | ||||
| namespace solidity::frontend | ||||
| { | ||||
| 
 | ||||
| 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"); | ||||
| @ -694,11 +692,37 @@ public: | ||||
| 	TypeResult interfaceType(bool) const override { return this; } | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Base class for types which can be thought of as several elements of other types put together. | ||||
|  * For example a struct is composed of its members, an array is composed of multiple copies of its | ||||
|  * base element and a mapping is composed of its value type elements (note that keys are not | ||||
|  * stored anywhere). | ||||
|  */ | ||||
| class CompositeType: public Type | ||||
| { | ||||
| protected: | ||||
| 	CompositeType() = default; | ||||
| 
 | ||||
| public: | ||||
| 	/// @returns a list containing the type itself, elements of its decomposition,
 | ||||
| 	/// elements of decomposition of these elements and so on, up to non-composite types.
 | ||||
| 	/// Each type is included only once.
 | ||||
| 	std::vector<Type const*> fullDecomposition() const; | ||||
| 
 | ||||
| protected: | ||||
| 	/// @returns a list of types that together make up the data part of this type.
 | ||||
| 	/// Contains all types that will have to be implicitly stored, whenever an object of this type is stored.
 | ||||
| 	/// In particular, it returns the base type for arrays and array slices, the member types for structs,
 | ||||
| 	/// the component types for tuples and the value type for mappings
 | ||||
| 	/// (note that the key type of a mapping is *not* part of the list).
 | ||||
| 	virtual std::vector<Type const*> decomposition() const = 0; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Base class used by types which are not value types and can be stored either in storage, memory | ||||
|  * or calldata. This is currently used by arrays and structs. | ||||
|  */ | ||||
| class ReferenceType: public Type | ||||
| class ReferenceType: public CompositeType | ||||
| { | ||||
| protected: | ||||
| 	explicit ReferenceType(DataLocation _location): m_location(_location) {} | ||||
| @ -829,6 +853,8 @@ public: | ||||
| 
 | ||||
| protected: | ||||
| 	std::vector<std::tuple<std::string, TypePointer>> makeStackItems() const override; | ||||
| 	std::vector<Type const*> decomposition() const override { return {m_baseType}; } | ||||
| 
 | ||||
| private: | ||||
| 	/// String is interpreted as a subtype of Bytes.
 | ||||
| 	enum class ArrayKind { Ordinary, Bytes, String }; | ||||
| @ -869,6 +895,8 @@ public: | ||||
| 
 | ||||
| protected: | ||||
| 	std::vector<std::tuple<std::string, TypePointer>> makeStackItems() const override; | ||||
| 	std::vector<Type const*> decomposition() const override { return {m_arrayType.baseType()}; } | ||||
| 
 | ||||
| private: | ||||
| 	ArrayType const& m_arrayType; | ||||
| }; | ||||
| @ -994,6 +1022,8 @@ public: | ||||
| 
 | ||||
| protected: | ||||
| 	std::vector<std::tuple<std::string, TypePointer>> makeStackItems() const override; | ||||
| 	std::vector<Type const*> decomposition() const override; | ||||
| 
 | ||||
| private: | ||||
| 	StructDefinition const& m_struct; | ||||
| 	// Caches for interfaceType(bool)
 | ||||
| @ -1044,7 +1074,7 @@ private: | ||||
|  * Type that can hold a finite sequence of values of different types. | ||||
|  * In some cases, the components are empty pointers (when used as placeholders). | ||||
|  */ | ||||
| class TupleType: public Type | ||||
| class TupleType: public CompositeType | ||||
| { | ||||
| public: | ||||
| 	explicit TupleType(std::vector<TypePointer> _types = {}): m_components(std::move(_types)) {} | ||||
| @ -1067,6 +1097,16 @@ public: | ||||
| 
 | ||||
| protected: | ||||
| 	std::vector<std::tuple<std::string, TypePointer>> makeStackItems() const override; | ||||
| 	std::vector<Type const*> decomposition() const override | ||||
| 	{ | ||||
| 		// Currently calling TupleType::decomposition() is not expected, because we cannot declare a variable of a tuple type.
 | ||||
| 		// If that changes, before removing the solAssert, make sure the function does the right thing and is used properly.
 | ||||
| 		// Note that different tuple members can have different data locations, so using decomposition() to check
 | ||||
| 		// the tuple validity for a data location might require special care.
 | ||||
| 		solUnimplemented("Tuple decomposition is not expected."); | ||||
| 		return m_components; | ||||
| 	} | ||||
| 
 | ||||
| private: | ||||
| 	std::vector<TypePointer> const m_components; | ||||
| }; | ||||
| @ -1349,7 +1389,7 @@ private: | ||||
|  * The type of a mapping, there is one distinct type per key/value type pair. | ||||
|  * Mappings always occupy their own storage slot, but do not actually use it. | ||||
|  */ | ||||
| class MappingType: public Type | ||||
| class MappingType: public CompositeType | ||||
| { | ||||
| public: | ||||
| 	MappingType(Type const* _keyType, Type const* _valueType): | ||||
| @ -1373,6 +1413,9 @@ public: | ||||
| 	Type const* keyType() const { return m_keyType; } | ||||
| 	Type const* valueType() const { return m_valueType; } | ||||
| 
 | ||||
| protected: | ||||
| 	std::vector<Type const*> decomposition() const override { return {m_valueType}; } | ||||
| 
 | ||||
| private: | ||||
| 	TypePointer m_keyType; | ||||
| 	TypePointer m_valueType; | ||||
|  | ||||
| @ -13,6 +13,8 @@ contract b { | ||||
| } | ||||
| // ---- | ||||
| // Warning 7325: (66-67): Type struct b.c 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: (66-67): Type uint256[14474011154664524427946373126085988481658748083205070504932198000989141204992] 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: (111-112): Type struct b.c 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: (111-112): Type uint256[14474011154664524427946373126085988481658748083205070504932198000989141204992] 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: (152-169): Type function ()[984770902183611232881] 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 2072: (152-180): Unused local variable. | ||||
|  | ||||
| @ -56,13 +56,19 @@ contract C { | ||||
| } | ||||
| // ---- | ||||
| // Warning 7325: (106-108): Type struct C.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 7325: (106-108): Type struct C.P[101] 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: (171-173): Type struct C.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: (171-173): Type struct C.P[102] 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 struct 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 struct 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 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 7325: (505-507): Type struct C.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-507): Type uint256[1][][100000000000000000001] 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[][100000000000000000003] 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 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: (576-578): Type struct C.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: (576-578): Type uint256[1][][100000000000000000005] 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-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 7325: (715-717): Type struct C.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: (715-717): Type uint256[][100000000000000000007] 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-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. | ||||
|  | ||||
| @ -4,5 +4,5 @@ contract C { | ||||
|     uint[2**255][2] a; | ||||
| } | ||||
| // ---- | ||||
| // TypeError 7676: (60-97): Contract too large for storage. | ||||
| // TypeError 7676: (60-97): Contract requires too much storage. | ||||
| // TypeError 1534: (77-94): Type too large for storage. | ||||
|  | ||||
| @ -5,4 +5,4 @@ contract C { | ||||
|     uint[2**255] b; | ||||
| } | ||||
| // ---- | ||||
| // TypeError 7676: (60-114): Contract too large for storage. | ||||
| // TypeError 7676: (60-114): Contract requires too much storage. | ||||
|  | ||||
| @ -7,4 +7,4 @@ contract D is C { | ||||
|     uint[2**255] b; | ||||
| } | ||||
| // ---- | ||||
| // TypeError 7676: (95-134): Contract too large for storage. | ||||
| // TypeError 7676: (95-134): Contract requires too much storage. | ||||
|  | ||||
| @ -8,5 +8,5 @@ contract C { | ||||
|     S s; | ||||
| } | ||||
| // ---- | ||||
| // TypeError 7676: (60-152): Contract too large for storage. | ||||
| // TypeError 7676: (60-152): Contract requires too much storage. | ||||
| // TypeError 1534: (146-149): Type too large for storage. | ||||
|  | ||||
| @ -4,3 +4,4 @@ contract C { | ||||
| } | ||||
| // ---- | ||||
| // Warning 7325: (64-65): Type struct 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. | ||||
| // Warning 7325: (64-65): Type uint256[57896044618658097711785492504343953926634992332820282019728792003956564819968] 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