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