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)
|
||||
if (auto varType = dynamic_cast<CompositeType const*>(_variable.annotation().type))
|
||||
for (Type const* type: varType->fullDecomposition())
|
||||
if (type->storageSizeUpperBound() >= (bigint(1) << 64))
|
||||
{
|
||||
TypePointer varType = _variable.annotation().type;
|
||||
for (Type const* subtype: frontend::oversizedSubtypes(*varType))
|
||||
{
|
||||
string message = "Type " + subtype->toString(true) +
|
||||
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