Skip to content

Commit

Permalink
Merge pull request ethereum#9818 from a3d4/introduce-compositetype
Browse files Browse the repository at this point in the history
Introduce CompositeType
  • Loading branch information
chriseth authored Sep 21, 2020
2 parents 7fa9a9d + 6b77a20 commit 83aa8c1
Show file tree
Hide file tree
Showing 11 changed files with 97 additions and 85 deletions.
3 changes: 1 addition & 2 deletions libsolidity/analysis/ContractLevelChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -465,15 +465,14 @@ 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()))
{
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;
}
}
Expand Down
21 changes: 10 additions & 11 deletions libsolidity/analysis/StaticAnalyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
84 changes: 23 additions & 61 deletions libsolidity/ast/Types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#include <boost/range/algorithm/copy.hpp>

#include <limits>
#include <unordered_set>
#include <utility>

using namespace std;
Expand All @@ -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)
{
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
{
Expand Down
53 changes: 48 additions & 5 deletions libsolidity/ast/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -694,11 +692,37 @@ class BoolType: public Type
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) {}
Expand Down Expand Up @@ -829,6 +853,8 @@ class ArrayType: public ReferenceType

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 };
Expand Down Expand Up @@ -869,6 +895,8 @@ class ArraySliceType: public ReferenceType

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;
};
Expand Down Expand Up @@ -994,6 +1022,8 @@ class StructType: public ReferenceType

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)
Expand Down Expand Up @@ -1044,7 +1074,7 @@ class EnumType: public Type
* 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)) {}
Expand All @@ -1067,6 +1097,16 @@ class TupleType: public Type

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;
};
Expand Down Expand Up @@ -1349,7 +1389,7 @@ class FunctionType: public Type
* 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):
Expand All @@ -1373,6 +1413,9 @@ class MappingType: public Type
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -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.

0 comments on commit 83aa8c1

Please sign in to comment.