Merge pull request #9062 from a3d4/fix-oversized-object-compiler-error

Fix internal compiler error related to oversized objects
This commit is contained in:
a3d4 2020-07-10 16:43:43 +02:00 committed by GitHub
commit cf5e9a3551
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 205 additions and 93 deletions

View File

@ -5,6 +5,7 @@ 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:
* Optimizer: Add rule to remove shifts inside the byte opcode. * Optimizer: Add rule to remove shifts inside the byte opcode.

View File

@ -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);
}

View File

@ -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.

View File

@ -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;
} }
@ -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."); 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());
@ -532,9 +551,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;
} }

View File

@ -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();
@ -1749,6 +1847,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;
@ -2388,6 +2488,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;
} }

View File

@ -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");

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View 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.

View File

@ -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.