mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #9146 from ethereum/containsNestedMappingType
[BREAKING] Implemented containsNestedMapping()
This commit is contained in:
commit
a79d7c1588
@ -8,6 +8,8 @@ Breaking changes:
|
||||
* Parser: NatSpec comments on variables are only allowed for public state variables.
|
||||
* Type Checker: Disallow shifts by signed types.
|
||||
* Type Checker: Exponentiation and shifts of literals by non-literals will always use ``uint256`` or ``int256`` as a type.
|
||||
* Type Checker: Disallow structs and arrays in memory or calldata if they contain nested mappings.
|
||||
* Type Checker: Disallow assignments to state variables that contain nested mappings.
|
||||
|
||||
Language Features:
|
||||
* Yul: Disallow EVM instruction `pc()`.
|
||||
|
@ -26,6 +26,7 @@ are allowed for state variables, as storage reference types
|
||||
in functions, or as parameters for library functions.
|
||||
They cannot be used as parameters or return parameters
|
||||
of contract functions that are publicly visible.
|
||||
These restrictions are also true for arrays and structs that contain mappings.
|
||||
|
||||
You can mark state variables of mapping type as ``public`` and Solidity creates a
|
||||
:ref:`getter <visibility-and-getters>` for you. The ``_KeyType`` becomes a parameter for the getter.
|
||||
|
@ -488,12 +488,11 @@ shown in the following example:
|
||||
|
||||
function newCampaign(address payable beneficiary, uint goal) public returns (uint campaignID) {
|
||||
campaignID = numCampaigns++; // campaignID is return variable
|
||||
// Creates new struct in memory and copies it to storage.
|
||||
// We leave out the mapping type, because it is not valid in memory.
|
||||
// If structs are copied (even from storage to storage),
|
||||
// types that are not valid outside of storage (ex. mappings and array of mappings)
|
||||
// are always omitted, because they cannot be enumerated.
|
||||
campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0);
|
||||
// We cannot use "campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0)"
|
||||
// because the RHS creates a memory-struct "Campaign" that contains a mapping.
|
||||
Campaign storage c = campaigns[campaignID];
|
||||
c.beneficiary = beneficiary;
|
||||
c.fundingGoal = goal;
|
||||
}
|
||||
|
||||
function contribute(uint campaignID) public payable {
|
||||
|
@ -112,18 +112,16 @@ bool DeclarationTypeChecker::visit(StructDefinition const& _struct)
|
||||
for (ASTPointer<VariableDeclaration> const& member: _struct.members())
|
||||
{
|
||||
Type const* memberType = member->annotation().type;
|
||||
while (auto arrayType = dynamic_cast<ArrayType const*>(memberType))
|
||||
{
|
||||
if (arrayType->isDynamicallySized())
|
||||
break;
|
||||
memberType = arrayType->baseType();
|
||||
}
|
||||
|
||||
if (auto arrayType = dynamic_cast<ArrayType const*>(memberType))
|
||||
memberType = arrayType->finalBaseType(true);
|
||||
|
||||
if (auto structType = dynamic_cast<StructType const*>(memberType))
|
||||
if (_cycleDetector.run(structType->structDefinition()))
|
||||
return;
|
||||
}
|
||||
};
|
||||
if (util::CycleDetector<StructDefinition>(visitor).run(_struct) != nullptr)
|
||||
if (util::CycleDetector<StructDefinition>(visitor).run(_struct))
|
||||
m_errorReporter.fatalTypeError(2046_error, _struct.location(), "Recursive struct definition.");
|
||||
|
||||
return false;
|
||||
|
@ -339,31 +339,21 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
|
||||
m_errorReporter.typeError(5587_error, _function.location(), "Internal functions cannot be payable.");
|
||||
}
|
||||
auto checkArgumentAndReturnParameter = [&](VariableDeclaration const& var) {
|
||||
if (type(var)->category() == Type::Category::Mapping)
|
||||
if (type(var)->containsNestedMapping())
|
||||
if (var.referenceLocation() == VariableDeclaration::Location::Storage)
|
||||
solAssert(
|
||||
_function.libraryFunction() || !_function.isPublic(),
|
||||
"Mapping types for parameters or return variables "
|
||||
"can only be used in internal or library functions."
|
||||
);
|
||||
if (_function.isPublic())
|
||||
{
|
||||
if (var.referenceLocation() != VariableDeclaration::Location::Storage)
|
||||
{
|
||||
if (!_function.libraryFunction() && _function.isPublic())
|
||||
m_errorReporter.typeError(3442_error, var.location(), "Mapping types can only have a data location of \"storage\" and thus only be parameters or return variables for internal or library functions.");
|
||||
else
|
||||
m_errorReporter.typeError(5380_error, var.location(), "Mapping types can only have a data location of \"storage\"." );
|
||||
}
|
||||
else
|
||||
solAssert(_function.libraryFunction() || !_function.isPublic(), "Mapping types for parameters or return variables can only be used in internal or library functions.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!type(var)->canLiveOutsideStorage() && _function.isPublic())
|
||||
m_errorReporter.typeError(3312_error, var.location(), "Type is required to live outside storage.");
|
||||
if (_function.isPublic())
|
||||
{
|
||||
auto iType = type(var)->interfaceType(_function.libraryFunction());
|
||||
auto iType = type(var)->interfaceType(_function.libraryFunction());
|
||||
|
||||
if (!iType)
|
||||
{
|
||||
solAssert(!iType.message().empty(), "Expected detailed error message!");
|
||||
m_errorReporter.fatalTypeError(4103_error, var.location(), iType.message());
|
||||
}
|
||||
if (!iType)
|
||||
{
|
||||
solAssert(!iType.message().empty(), "Expected detailed error message!");
|
||||
m_errorReporter.typeError(4103_error, var.location(), iType.message());
|
||||
}
|
||||
}
|
||||
if (
|
||||
@ -457,9 +447,13 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
|
||||
m_errorReporter.typeError(1273_error, _variable.location(), "The type of a variable cannot be a library.");
|
||||
if (_variable.value())
|
||||
{
|
||||
if (_variable.isStateVariable() && dynamic_cast<MappingType const*>(varType))
|
||||
if (_variable.isStateVariable() && varType->containsNestedMapping())
|
||||
{
|
||||
m_errorReporter.typeError(6280_error, _variable.location(), "Mappings cannot be assigned to.");
|
||||
m_errorReporter.typeError(
|
||||
6280_error,
|
||||
_variable.location(),
|
||||
"Types in storage containing (nested) mappings cannot be assigned to."
|
||||
);
|
||||
_variable.value()->accept(*this);
|
||||
}
|
||||
else
|
||||
@ -499,9 +493,16 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
|
||||
|
||||
if (!_variable.isStateVariable())
|
||||
{
|
||||
if (varType->dataStoredIn(DataLocation::Memory) || varType->dataStoredIn(DataLocation::CallData))
|
||||
if (!varType->canLiveOutsideStorage())
|
||||
m_errorReporter.typeError(4061_error, _variable.location(), "Type " + varType->toString() + " is only valid in storage.");
|
||||
if (
|
||||
_variable.referenceLocation() == VariableDeclaration::Location::CallData ||
|
||||
_variable.referenceLocation() == VariableDeclaration::Location::Memory
|
||||
)
|
||||
if (varType->containsNestedMapping())
|
||||
m_errorReporter.fatalTypeError(
|
||||
4061_error,
|
||||
_variable.location(),
|
||||
"Type " + varType->toString(true) + " is only valid in storage because it contains a (nested) mapping."
|
||||
);
|
||||
}
|
||||
else if (_variable.visibility() >= Visibility::Public)
|
||||
{
|
||||
@ -612,8 +613,12 @@ bool TypeChecker::visit(EventDefinition const& _eventDef)
|
||||
{
|
||||
if (var->isIndexed())
|
||||
numIndexed++;
|
||||
if (!type(*var)->canLiveOutsideStorage())
|
||||
m_errorReporter.typeError(3448_error, var->location(), "Type is required to live outside storage.");
|
||||
if (type(*var)->containsNestedMapping())
|
||||
m_errorReporter.typeError(
|
||||
3448_error,
|
||||
var->location(),
|
||||
"Type containing a (nested) mapping is not allowed as event parameter type."
|
||||
);
|
||||
if (!type(*var)->interfaceType(false))
|
||||
m_errorReporter.typeError(3417_error, var->location(), "Internal or recursive type is not allowed as event parameter type.");
|
||||
if (
|
||||
@ -1386,7 +1391,7 @@ void TypeChecker::checkExpressionAssignment(Type const& _type, Expression const&
|
||||
checkExpressionAssignment(*types[i], *tupleExpression->components()[i]);
|
||||
}
|
||||
}
|
||||
else if (_type.category() == Type::Category::Mapping)
|
||||
else if (_type.nameable() && _type.containsNestedMapping())
|
||||
{
|
||||
bool isLocalOrReturn = false;
|
||||
if (auto const* identifier = dynamic_cast<Identifier const*>(&_expression))
|
||||
@ -1394,7 +1399,7 @@ void TypeChecker::checkExpressionAssignment(Type const& _type, Expression const&
|
||||
if (variableDeclaration->isLocalOrReturn())
|
||||
isLocalOrReturn = true;
|
||||
if (!isLocalOrReturn)
|
||||
m_errorReporter.typeError(9214_error, _expression.location(), "Mappings cannot be assigned to.");
|
||||
m_errorReporter.typeError(9214_error, _expression.location(), "Types in storage containing (nested) mappings cannot be assigned to.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1530,8 +1535,12 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
|
||||
_tuple.location(),
|
||||
"Unable to deduce nameable type for array elements. Try adding explicit type conversion for the first element."
|
||||
);
|
||||
else if (!inlineArrayType->canLiveOutsideStorage())
|
||||
m_errorReporter.fatalTypeError(1545_error, _tuple.location(), "Type " + inlineArrayType->toString() + " is only valid in storage.");
|
||||
else if (inlineArrayType->containsNestedMapping())
|
||||
m_errorReporter.fatalTypeError(
|
||||
1545_error,
|
||||
_tuple.location(),
|
||||
"Type " + inlineArrayType->toString(true) + " is only valid in storage."
|
||||
);
|
||||
|
||||
_tuple.annotation().type = TypeProvider::array(DataLocation::Memory, inlineArrayType, types.size());
|
||||
}
|
||||
@ -2032,24 +2041,8 @@ void TypeChecker::typeCheckFunctionGeneralChecks(
|
||||
toString(parameterTypes.size()) +
|
||||
".";
|
||||
|
||||
// Extend error message in case we try to construct a struct with mapping member.
|
||||
if (isStructConstructorCall)
|
||||
{
|
||||
/// For error message: Struct members that were removed during conversion to memory.
|
||||
TypePointer const expressionType = type(_functionCall.expression());
|
||||
auto const& t = dynamic_cast<TypeType const&>(*expressionType);
|
||||
auto const& structType = dynamic_cast<StructType const&>(*t.actualType());
|
||||
set<string> membersRemovedForStructConstructor = structType.membersMissingInMemory();
|
||||
|
||||
if (!membersRemovedForStructConstructor.empty())
|
||||
{
|
||||
msg += " Members that have to be skipped in memory:";
|
||||
for (auto const& member: membersRemovedForStructConstructor)
|
||||
msg += " " + member;
|
||||
}
|
||||
|
||||
return { isVariadic ? 1123_error : 9755_error, msg };
|
||||
}
|
||||
else if (
|
||||
_functionType->kind() == FunctionType::Kind::BareCall ||
|
||||
_functionType->kind() == FunctionType::Kind::BareCallCode ||
|
||||
@ -2269,6 +2262,12 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
|
||||
|
||||
if (actualType->category() == Type::Category::Struct)
|
||||
{
|
||||
if (actualType->containsNestedMapping())
|
||||
m_errorReporter.fatalTypeError(
|
||||
9515_error,
|
||||
_functionCall.location(),
|
||||
"Struct containing a (nested) mapping cannot be constructed."
|
||||
);
|
||||
functionType = dynamic_cast<StructType const&>(*actualType).constructorType();
|
||||
funcCallAnno.kind = FunctionCallKind::StructConstructorCall;
|
||||
funcCallAnno.isPure = argumentsArePure;
|
||||
@ -2518,11 +2517,11 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
|
||||
}
|
||||
else if (type->category() == Type::Category::Array)
|
||||
{
|
||||
if (!type->canLiveOutsideStorage())
|
||||
if (type->containsNestedMapping())
|
||||
m_errorReporter.fatalTypeError(
|
||||
1164_error,
|
||||
_newExpression.typeName().location(),
|
||||
"Type cannot live outside storage."
|
||||
"Array containing a (nested) mapping cannot be constructed in memory."
|
||||
);
|
||||
if (!type->isDynamicallySized())
|
||||
m_errorReporter.typeError(
|
||||
|
@ -136,6 +136,9 @@ struct StructDeclarationAnnotation: TypeDeclarationAnnotation
|
||||
/// recursion immediately raises an error.
|
||||
/// Will be filled in by the DeclarationTypeChecker.
|
||||
std::optional<bool> recursive;
|
||||
/// Whether the struct contains a mapping type, either directly or, indirectly inside another
|
||||
/// struct or an array.
|
||||
std::optional<bool> containsNestedMapping;
|
||||
};
|
||||
|
||||
struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, StructurallyDocumentedAnnotation
|
||||
|
@ -1985,6 +1985,20 @@ TypeResult ArrayType::interfaceType(bool _inLibrary) const
|
||||
return result;
|
||||
}
|
||||
|
||||
Type const* ArrayType::finalBaseType(bool _breakIfDynamicArrayType) const
|
||||
{
|
||||
Type const* finalBaseType = this;
|
||||
|
||||
while (auto arrayType = dynamic_cast<ArrayType const*>(finalBaseType))
|
||||
{
|
||||
if (_breakIfDynamicArrayType && arrayType->isDynamicallySized())
|
||||
break;
|
||||
finalBaseType = arrayType->baseType();
|
||||
}
|
||||
|
||||
return finalBaseType;
|
||||
}
|
||||
|
||||
u256 ArrayType::memoryDataSize() const
|
||||
{
|
||||
solAssert(!isDynamicallySized(), "");
|
||||
@ -2213,7 +2227,7 @@ unsigned StructType::calldataEncodedSize(bool) const
|
||||
unsigned size = 0;
|
||||
for (auto const& member: members(nullptr))
|
||||
{
|
||||
solAssert(member.type->canLiveOutsideStorage(), "");
|
||||
solAssert(!member.type->containsNestedMapping(), "");
|
||||
// Struct members are always padded.
|
||||
size += member.type->calldataEncodedSize();
|
||||
}
|
||||
@ -2228,7 +2242,7 @@ unsigned StructType::calldataEncodedTailSize() const
|
||||
unsigned size = 0;
|
||||
for (auto const& member: members(nullptr))
|
||||
{
|
||||
solAssert(member.type->canLiveOutsideStorage(), "");
|
||||
solAssert(!member.type->containsNestedMapping(), "");
|
||||
// Struct members are always padded.
|
||||
size += member.type->calldataHeadSize();
|
||||
}
|
||||
@ -2240,7 +2254,7 @@ unsigned StructType::calldataOffsetOfMember(std::string const& _member) const
|
||||
unsigned offset = 0;
|
||||
for (auto const& member: members(nullptr))
|
||||
{
|
||||
solAssert(member.type->canLiveOutsideStorage(), "");
|
||||
solAssert(!member.type->containsNestedMapping(), "");
|
||||
if (member.name == _member)
|
||||
return offset;
|
||||
// Struct members are always padded.
|
||||
@ -2277,6 +2291,42 @@ u256 StructType::storageSize() const
|
||||
return max<u256>(1, members(nullptr).storageSize());
|
||||
}
|
||||
|
||||
bool StructType::containsNestedMapping() const
|
||||
{
|
||||
if (!m_struct.annotation().containsNestedMapping.has_value())
|
||||
{
|
||||
bool hasNestedMapping = false;
|
||||
|
||||
util::BreadthFirstSearch<StructDefinition const*> breadthFirstSearch{{&m_struct}};
|
||||
|
||||
breadthFirstSearch.run(
|
||||
[&](StructDefinition const* _struct, auto&& _addChild)
|
||||
{
|
||||
for (auto const& member: _struct->members())
|
||||
{
|
||||
TypePointer memberType = member->annotation().type;
|
||||
solAssert(memberType, "");
|
||||
|
||||
if (auto arrayType = dynamic_cast<ArrayType const*>(memberType))
|
||||
memberType = arrayType->finalBaseType(false);
|
||||
|
||||
if (dynamic_cast<MappingType const*>(memberType))
|
||||
{
|
||||
hasNestedMapping = true;
|
||||
breadthFirstSearch.abort();
|
||||
}
|
||||
else if (auto structType = dynamic_cast<StructType const*>(memberType))
|
||||
_addChild(&structType->structDefinition());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
m_struct.annotation().containsNestedMapping = hasNestedMapping;
|
||||
}
|
||||
|
||||
return m_struct.annotation().containsNestedMapping.value();
|
||||
}
|
||||
|
||||
string StructType::toString(bool _short) const
|
||||
{
|
||||
string ret = "struct " + m_struct.annotation().canonicalName;
|
||||
@ -2292,10 +2342,7 @@ MemberList::MemberMap StructType::nativeMembers(ASTNode const*) const
|
||||
{
|
||||
TypePointer type = variable->annotation().type;
|
||||
solAssert(type, "");
|
||||
// If we are not in storage, skip all members that cannot live outside of storage,
|
||||
// ex. mappings and array of mappings
|
||||
if (location() != DataLocation::Storage && !type->canLiveOutsideStorage())
|
||||
continue;
|
||||
solAssert(!(location() != DataLocation::Storage && type->containsNestedMapping()), "");
|
||||
members.emplace_back(
|
||||
variable->name(),
|
||||
copyForLocationIfReference(type),
|
||||
@ -2457,10 +2504,9 @@ FunctionTypePointer StructType::constructorType() const
|
||||
{
|
||||
TypePointers paramTypes;
|
||||
strings paramNames;
|
||||
solAssert(!containsNestedMapping(), "");
|
||||
for (auto const& member: members(nullptr))
|
||||
{
|
||||
if (!member.type->canLiveOutsideStorage())
|
||||
continue;
|
||||
paramNames.push_back(member.name);
|
||||
paramTypes.push_back(TypeProvider::withLocationIfReference(DataLocation::Memory, member.type));
|
||||
}
|
||||
@ -2494,20 +2540,12 @@ u256 StructType::memoryOffsetOfMember(string const& _name) const
|
||||
|
||||
TypePointers StructType::memoryMemberTypes() const
|
||||
{
|
||||
solAssert(!containsNestedMapping(), "");
|
||||
TypePointers types;
|
||||
for (ASTPointer<VariableDeclaration> const& variable: m_struct.members())
|
||||
if (variable->annotation().type->canLiveOutsideStorage())
|
||||
types.push_back(TypeProvider::withLocationIfReference(DataLocation::Memory, variable->annotation().type));
|
||||
return types;
|
||||
}
|
||||
types.push_back(TypeProvider::withLocationIfReference(DataLocation::Memory, variable->annotation().type));
|
||||
|
||||
set<string> StructType::membersMissingInMemory() const
|
||||
{
|
||||
set<string> missing;
|
||||
for (ASTPointer<VariableDeclaration> const& variable: m_struct.members())
|
||||
if (!variable->annotation().type->canLiveOutsideStorage())
|
||||
missing.insert(variable->name());
|
||||
return missing;
|
||||
return types;
|
||||
}
|
||||
|
||||
vector<tuple<string, TypePointer>> StructType::makeStackItems() const
|
||||
@ -3654,7 +3692,10 @@ TypeResult MappingType::interfaceType(bool _inLibrary) const
|
||||
}
|
||||
}
|
||||
else
|
||||
return TypeResult::err("Only libraries are allowed to use the mapping type in public or external functions.");
|
||||
return TypeResult::err(
|
||||
"Types containing (nested) mappings can only be parameters or "
|
||||
"return variables of internal or library functions."
|
||||
);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
@ -259,7 +259,11 @@ public:
|
||||
/// Returns true if the type can be stored in storage.
|
||||
virtual bool canBeStored() const { return true; }
|
||||
/// Returns false if the type cannot live outside the storage, i.e. if it includes some mapping.
|
||||
virtual bool canLiveOutsideStorage() const { return true; }
|
||||
virtual bool containsNestedMapping() const
|
||||
{
|
||||
solAssert(nameable(), "Called for a non nameable type.");
|
||||
return false;
|
||||
}
|
||||
/// Returns true if the type can be stored as a value (as opposed to a reference) on the stack,
|
||||
/// i.e. it behaves differently in lvalue context and in value context.
|
||||
virtual bool isValueType() const { return false; }
|
||||
@ -549,7 +553,6 @@ public:
|
||||
bool operator==(Type const& _other) const override;
|
||||
|
||||
bool canBeStored() const override { return false; }
|
||||
bool canLiveOutsideStorage() const override { return false; }
|
||||
|
||||
std::string toString(bool _short) const override;
|
||||
u256 literalValue(Literal const* _literal) const override;
|
||||
@ -610,7 +613,6 @@ public:
|
||||
bool operator==(Type const& _other) const override;
|
||||
|
||||
bool canBeStored() const override { return false; }
|
||||
bool canLiveOutsideStorage() const override { return false; }
|
||||
|
||||
std::string toString(bool) const override;
|
||||
TypePointer mobileType() const override;
|
||||
@ -781,8 +783,9 @@ public:
|
||||
bool isDynamicallySized() const override { return m_hasDynamicLength; }
|
||||
bool isDynamicallyEncoded() const override;
|
||||
u256 storageSize() const override;
|
||||
bool canLiveOutsideStorage() const override { return m_baseType->canLiveOutsideStorage(); }
|
||||
bool containsNestedMapping() const override { return m_baseType->containsNestedMapping(); }
|
||||
bool nameable() const override { return true; }
|
||||
|
||||
std::string toString(bool _short) const override;
|
||||
std::string canonicalName() const override;
|
||||
std::string signatureInExternalFunction(bool _structsByName) const override;
|
||||
@ -798,6 +801,7 @@ public:
|
||||
/// @returns true if this is a string
|
||||
bool isString() const { return m_arrayKind == ArrayKind::String; }
|
||||
Type const* baseType() const { solAssert(!!m_baseType, ""); return m_baseType; }
|
||||
Type const* finalBaseType(bool breakIfDynamicArrayType) const;
|
||||
u256 const& length() const { return m_length; }
|
||||
u256 memoryDataSize() const override;
|
||||
|
||||
@ -842,7 +846,6 @@ public:
|
||||
unsigned calldataEncodedTailSize() const override { return 32; }
|
||||
bool isDynamicallySized() const override { return true; }
|
||||
bool isDynamicallyEncoded() const override { return true; }
|
||||
bool canLiveOutsideStorage() const override { return m_arrayType.canLiveOutsideStorage(); }
|
||||
std::string toString(bool _short) const override;
|
||||
TypePointer mobileType() const override;
|
||||
|
||||
@ -883,7 +886,6 @@ public:
|
||||
}
|
||||
unsigned storageBytes() const override { solAssert(!isSuper(), ""); return 20; }
|
||||
bool leftAligned() const override { solAssert(!isSuper(), ""); return false; }
|
||||
bool canLiveOutsideStorage() const override { return !isSuper(); }
|
||||
bool isValueType() const override { return !isSuper(); }
|
||||
bool nameable() const override { return !isSuper(); }
|
||||
std::string toString(bool _short) const override;
|
||||
@ -945,7 +947,7 @@ public:
|
||||
bool isDynamicallyEncoded() const override;
|
||||
u256 memoryDataSize() const override;
|
||||
u256 storageSize() const override;
|
||||
bool canLiveOutsideStorage() const override { return true; }
|
||||
bool containsNestedMapping() const override;
|
||||
bool nameable() const override { return true; }
|
||||
std::string toString(bool _short) const override;
|
||||
|
||||
@ -975,8 +977,6 @@ public:
|
||||
|
||||
/// @returns the vector of types of members available in memory.
|
||||
TypePointers memoryMemberTypes() const;
|
||||
/// @returns the set of all members that are removed in the memory version (typically mappings).
|
||||
std::set<std::string> membersMissingInMemory() const;
|
||||
|
||||
void clearCache() const override;
|
||||
|
||||
@ -1007,7 +1007,6 @@ public:
|
||||
}
|
||||
unsigned storageBytes() const override;
|
||||
bool leftAligned() const override { return false; }
|
||||
bool canLiveOutsideStorage() const override { return true; }
|
||||
std::string toString(bool _short) const override;
|
||||
std::string canonicalName() const override;
|
||||
bool isValueType() const override { return true; }
|
||||
@ -1047,7 +1046,6 @@ public:
|
||||
std::string toString(bool) const override;
|
||||
bool canBeStored() const override { return false; }
|
||||
u256 storageSize() const override;
|
||||
bool canLiveOutsideStorage() const override { return false; }
|
||||
bool hasSimpleZeroValueInMemory() const override { return false; }
|
||||
TypePointer mobileType() const override;
|
||||
/// Converts components to their temporary types and performs some wildcard matching.
|
||||
@ -1218,7 +1216,6 @@ public:
|
||||
unsigned storageBytes() const override;
|
||||
bool isValueType() const override { return true; }
|
||||
bool nameable() const override;
|
||||
bool canLiveOutsideStorage() const override { return m_kind == Kind::Internal || m_kind == Kind::External; }
|
||||
bool hasSimpleZeroValueInMemory() const override { return false; }
|
||||
MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override;
|
||||
TypePointer encodingType() const override;
|
||||
@ -1351,7 +1348,7 @@ public:
|
||||
bool operator==(Type const& _other) const override;
|
||||
std::string toString(bool _short) const override;
|
||||
std::string canonicalName() const override;
|
||||
bool canLiveOutsideStorage() const override { return false; }
|
||||
bool containsNestedMapping() const override { return true; }
|
||||
TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; }
|
||||
Type const* encodingType() const override;
|
||||
TypeResult interfaceType(bool _inLibrary) const override;
|
||||
@ -1386,7 +1383,6 @@ public:
|
||||
bool operator==(Type const& _other) const override;
|
||||
bool canBeStored() const override { return false; }
|
||||
u256 storageSize() const override;
|
||||
bool canLiveOutsideStorage() const override { return false; }
|
||||
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
|
||||
std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; }
|
||||
MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override;
|
||||
@ -1412,7 +1408,6 @@ public:
|
||||
TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; }
|
||||
bool canBeStored() const override { return false; }
|
||||
u256 storageSize() const override;
|
||||
bool canLiveOutsideStorage() const override { return false; }
|
||||
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
|
||||
std::string richIdentifier() const override;
|
||||
bool operator==(Type const& _other) const override;
|
||||
@ -1439,7 +1434,6 @@ public:
|
||||
std::string richIdentifier() const override;
|
||||
bool operator==(Type const& _other) const override;
|
||||
bool canBeStored() const override { return false; }
|
||||
bool canLiveOutsideStorage() const override { return true; }
|
||||
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
|
||||
MemberList::MemberMap nativeMembers(ASTNode const*) const override;
|
||||
|
||||
@ -1479,7 +1473,6 @@ public:
|
||||
std::string richIdentifier() const override;
|
||||
bool operator==(Type const& _other) const override;
|
||||
bool canBeStored() const override { return false; }
|
||||
bool canLiveOutsideStorage() const override { return true; }
|
||||
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
|
||||
MemberList::MemberMap nativeMembers(ASTNode const*) const override;
|
||||
|
||||
@ -1512,7 +1505,6 @@ public:
|
||||
TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; }
|
||||
unsigned calldataEncodedSize(bool) const override { return 32; }
|
||||
bool canBeStored() const override { return false; }
|
||||
bool canLiveOutsideStorage() const override { return false; }
|
||||
bool isValueType() const override { return true; }
|
||||
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
|
||||
std::string toString(bool) const override { return "inaccessible dynamic type"; }
|
||||
|
@ -860,8 +860,7 @@ string ABIFunctions::abiEncodingFunctionStruct(
|
||||
for (auto const& member: _to.members(nullptr))
|
||||
{
|
||||
solAssert(member.type, "");
|
||||
if (!member.type->canLiveOutsideStorage())
|
||||
continue;
|
||||
solAssert(!member.type->containsNestedMapping(), "");
|
||||
TypePointer memberTypeTo = member.type->fullEncodingType(_options.encodeAsLibraryTypes, true, false);
|
||||
solUnimplementedAssert(memberTypeTo, "Encoding type \"" + member.type->toString() + "\" not yet implemented.");
|
||||
auto memberTypeFrom = _from.memberType(member.name);
|
||||
@ -1340,7 +1339,7 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr
|
||||
for (auto const& member: _type.members(nullptr))
|
||||
{
|
||||
solAssert(member.type, "");
|
||||
solAssert(member.type->canLiveOutsideStorage(), "");
|
||||
solAssert(!member.type->containsNestedMapping(), "");
|
||||
auto decodingType = member.type->decodingType();
|
||||
solAssert(decodingType, "");
|
||||
bool dynamic = decodingType->isDynamicallyEncoded();
|
||||
|
@ -1069,8 +1069,7 @@ void CompilerUtils::convertType(
|
||||
// stack: <memory ptr> <source ref> <memory ptr>
|
||||
for (auto const& member: typeOnStack->members(nullptr))
|
||||
{
|
||||
if (!member.type->canLiveOutsideStorage())
|
||||
continue;
|
||||
solAssert(!member.type->containsNestedMapping(), "");
|
||||
pair<u256, unsigned> const& offsets = typeOnStack->storageOffsetsOfMember(member.name);
|
||||
_context << offsets.first << Instruction::DUP3 << Instruction::ADD;
|
||||
_context << u256(offsets.second);
|
||||
|
@ -355,13 +355,13 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
|
||||
structType.structDefinition() == sourceType.structDefinition(),
|
||||
"Struct assignment with conversion."
|
||||
);
|
||||
solAssert(!structType.containsNestedMapping(), "");
|
||||
solAssert(sourceType.location() != DataLocation::CallData, "Structs in calldata not supported.");
|
||||
for (auto const& member: structType.members(nullptr))
|
||||
{
|
||||
// assign each member that can live outside of storage
|
||||
TypePointer const& memberType = member.type;
|
||||
if (!memberType->canLiveOutsideStorage())
|
||||
continue;
|
||||
solAssert(memberType->nameable(), "");
|
||||
TypePointer sourceMemberType = sourceType.memberType(member.name);
|
||||
if (sourceType.location() == DataLocation::Storage)
|
||||
{
|
||||
|
@ -3465,12 +3465,12 @@ BOOST_AUTO_TEST_CASE(array_copy_calldata_storage)
|
||||
ABI_CHECK(callContractFunction("retrieve()"), encodeArgs(9, 28, 9, 28, 4, 3, 32));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(array_copy_including_mapping)
|
||||
BOOST_AUTO_TEST_CASE(array_copy_including_array)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract c {
|
||||
mapping(uint=>uint)[90][] large;
|
||||
mapping(uint=>uint)[3][] small;
|
||||
uint[3][90][] large;
|
||||
uint[3][3][] small;
|
||||
function test() public returns (uint r) {
|
||||
for (uint i = 0; i < 7; i++) {
|
||||
large.push();
|
||||
@ -3505,9 +3505,8 @@ BOOST_AUTO_TEST_CASE(array_copy_including_mapping)
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode);
|
||||
ABI_CHECK(callContractFunction("test()"), encodeArgs(0x02000200));
|
||||
// storage is not empty because we cannot delete the mappings
|
||||
BOOST_CHECK(!storageEmpty(m_contractAddress));
|
||||
ABI_CHECK(callContractFunction("test()"), encodeArgs(0x02000202));
|
||||
BOOST_CHECK(storageEmpty(m_contractAddress));
|
||||
ABI_CHECK(callContractFunction("clear()"), encodeArgs(0, 0));
|
||||
BOOST_CHECK(storageEmpty(m_contractAddress));
|
||||
}
|
||||
|
@ -1,50 +0,0 @@
|
||||
contract Test {
|
||||
struct A {
|
||||
mapping(uint=>uint) m;
|
||||
}
|
||||
struct B {
|
||||
mapping(uint=>uint) m;
|
||||
uint x;
|
||||
}
|
||||
struct C {
|
||||
mapping(uint=>uint)[] ma;
|
||||
}
|
||||
struct D {
|
||||
A[] a;
|
||||
}
|
||||
A storageA;
|
||||
B storageB;
|
||||
C storageC;
|
||||
D storageD;
|
||||
constructor() public {
|
||||
storageA.m[1] = 2;
|
||||
storageB.m[3] = 4;
|
||||
storageB.x = 5;
|
||||
for (uint i = 0; i < 6; i++)
|
||||
storageC.ma.push();
|
||||
for (uint i = 0; i < 7; i++)
|
||||
storageD.a.push();
|
||||
}
|
||||
function run() public returns (uint, uint, uint, uint, uint, uint) {
|
||||
A memory memoryA = A();
|
||||
B memory memoryB = B(42);
|
||||
C memory memoryC = C();
|
||||
D memory memoryD1 = D(new A[](999));
|
||||
D memory memoryD2 = storageD;
|
||||
storageA = memoryA;
|
||||
storageB = memoryB;
|
||||
storageC = memoryC;
|
||||
// the following line does not compile because unimplemented
|
||||
// storageD = memoryD1;
|
||||
return (
|
||||
storageA.m[1],
|
||||
storageB.x,
|
||||
memoryB.x,
|
||||
storageC.ma.length,
|
||||
memoryD1.a.length,
|
||||
memoryD2.a.length
|
||||
);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// run() -> 2, 42, 42, 6, 999, 7
|
@ -1,24 +0,0 @@
|
||||
contract Test {
|
||||
struct S {
|
||||
uint8 a;
|
||||
mapping(uint256 => uint256) b;
|
||||
uint8 c;
|
||||
}
|
||||
S s;
|
||||
|
||||
function f() public returns (uint256) {
|
||||
S memory x;
|
||||
if (x.a != 0 || x.c != 0) return 1;
|
||||
x.a = 4;
|
||||
x.c = 5;
|
||||
s = x;
|
||||
if (s.a != 4 || s.c != 5) return 2;
|
||||
x = S(2, 3);
|
||||
if (x.a != 2 || x.c != 3) return 3;
|
||||
x = s;
|
||||
if (s.a != 4 || s.c != 5) return 4;
|
||||
}
|
||||
}
|
||||
|
||||
// ----
|
||||
// f() -> 0
|
@ -5,7 +5,6 @@ contract c {
|
||||
}
|
||||
struct Struct {
|
||||
uint256 a;
|
||||
mapping(uint256 => Struct) b;
|
||||
Nested nested;
|
||||
uint256 c;
|
||||
}
|
||||
|
8
test/libsolidity/syntaxTests/array/function_mapping.sol
Normal file
8
test/libsolidity/syntaxTests/array/function_mapping.sol
Normal file
@ -0,0 +1,8 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract Test {
|
||||
function f(mapping(uint => uint)[] memory x) public pure {}
|
||||
}
|
||||
// ----
|
||||
// TypeError 4103: (66-98): Types containing (nested) mappings can only be parameters or return variables of internal or library functions.
|
||||
// TypeError 4061: (66-98): Type mapping(uint256 => uint256)[] is only valid in storage because it contains a (nested) mapping.
|
@ -0,0 +1,6 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
library L {
|
||||
function f(mapping(uint => uint)[2] memory a) external pure returns (mapping(uint => uint)[2] memory) {}
|
||||
}
|
||||
// ----
|
||||
// TypeError 4061: (61-94): Type mapping(uint256 => uint256)[2] is only valid in storage because it contains a (nested) mapping.
|
@ -5,7 +5,6 @@ contract C {
|
||||
struct S { uint256 [ ] [ 0.425781 ether ] s1 ;
|
||||
uint [ 2 ** 0xFF ] [ 2 ** 0x42 ] s2 ;
|
||||
X s3 ;
|
||||
mapping ( uint => address payable ) c ;
|
||||
uint [ 9 hours ** 16 ] d ;
|
||||
string s ;
|
||||
}
|
||||
@ -14,4 +13,4 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 1534: (530-540): Type too large for memory.
|
||||
// TypeError 1534: (474-484): Type too large for memory.
|
||||
|
@ -11,3 +11,4 @@ contract Test {
|
||||
}
|
||||
// ----
|
||||
// DeclarationError 2333: (157-198): Identifier already declared.
|
||||
// TypeError 4061: (268-300): Type struct Test.RecursiveStruct[1] is only valid in storage because it contains a (nested) mapping.
|
||||
|
@ -4,4 +4,4 @@ library L {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 9214: (108-109): Mappings cannot be assigned to.
|
||||
// TypeError 9214: (108-109): Types in storage containing (nested) mappings cannot be assigned to.
|
||||
|
@ -9,4 +9,4 @@ contract Test {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 4994: (208-218): Member "b1" is not available in struct Test.S1 memory outside of storage.
|
||||
// TypeError 4061: (161-172): Type struct Test.S2 is only valid in storage because it contains a (nested) mapping.
|
||||
|
@ -3,8 +3,7 @@ contract Test {
|
||||
S s;
|
||||
function f() public {
|
||||
S memory x;
|
||||
x.b[1];
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 4994: (118-121): Member "b" is not available in struct Test.S memory outside of storage.
|
||||
// TypeError 4061: (104-114): Type struct Test.S is only valid in storage because it contains a (nested) mapping.
|
||||
|
@ -9,3 +9,4 @@ contract test {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 9214: (152-156): Types in storage containing (nested) mappings cannot be assigned to.
|
||||
|
@ -0,0 +1,9 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract C {
|
||||
struct S { mapping(uint => uint) a; }
|
||||
function f(S memory) public {}
|
||||
}
|
||||
// ----
|
||||
// TypeError 4103: (105-113): Types containing (nested) mappings can only be parameters or return variables of internal or library functions.
|
||||
// TypeError 4061: (105-113): Type struct C.S is only valid in storage because it contains a (nested) mapping.
|
@ -5,4 +5,5 @@ contract C {
|
||||
function f(S memory) public {}
|
||||
}
|
||||
// ----
|
||||
// TypeError 4103: (105-113): Only libraries are allowed to use the mapping type in public or external functions.
|
||||
// TypeError 4103: (105-113): Types containing (nested) mappings can only be parameters or return variables of internal or library functions.
|
||||
// TypeError 4061: (105-113): Type struct C.S is only valid in storage because it contains a (nested) mapping.
|
||||
|
@ -0,0 +1,10 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract C {
|
||||
struct T { mapping(uint => uint) a; }
|
||||
struct S { T[][2] b; }
|
||||
function f(S memory) public {}
|
||||
}
|
||||
// ----
|
||||
// TypeError 4103: (132-140): Types containing (nested) mappings can only be parameters or return variables of internal or library functions.
|
||||
// TypeError 4061: (132-140): Type struct C.S is only valid in storage because it contains a (nested) mapping.
|
@ -6,4 +6,5 @@ contract C {
|
||||
function f(S memory) public {}
|
||||
}
|
||||
// ----
|
||||
// TypeError 4103: (132-140): Only libraries are allowed to use the mapping type in public or external functions.
|
||||
// TypeError 4103: (132-140): Types containing (nested) mappings can only be parameters or return variables of internal or library functions.
|
||||
// TypeError 4061: (132-140): Type struct C.S is only valid in storage because it contains a (nested) mapping.
|
||||
|
@ -4,4 +4,4 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 1164: (94-117): Type cannot live outside storage.
|
||||
// TypeError 1164: (94-117): Array containing a (nested) mapping cannot be constructed in memory.
|
||||
|
@ -2,6 +2,6 @@ contract c {
|
||||
event e(uint indexed a, mapping(uint => uint) indexed b, bool indexed c, uint indexed d, uint indexed e) anonymous;
|
||||
}
|
||||
// ----
|
||||
// TypeError 3448: (41-72): Type is required to live outside storage.
|
||||
// TypeError 3448: (41-72): Type containing a (nested) mapping is not allowed as event parameter type.
|
||||
// TypeError 3417: (41-72): Internal or recursive type is not allowed as event parameter type.
|
||||
// TypeError 8598: (17-132): More than 4 indexed arguments for anonymous event.
|
||||
|
@ -6,4 +6,4 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 9755: (117-126): Wrong argument count for struct constructor: 1 arguments given but expected 2. Members that have to be skipped in memory: c
|
||||
// TypeError 9515: (117-126): Struct containing a (nested) mapping cannot be constructed.
|
||||
|
@ -0,0 +1,15 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract test {
|
||||
struct S {
|
||||
T t;
|
||||
}
|
||||
struct T {
|
||||
mapping (uint => uint) k;
|
||||
}
|
||||
function f(S calldata b) external {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 4103: (155-167): Types containing (nested) mappings can only be parameters or return variables of internal or library functions.
|
||||
// TypeError 4061: (155-167): Type struct test.S is only valid in storage because it contains a (nested) mapping.
|
@ -1,3 +1,5 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract Test {
|
||||
struct MyStructName {
|
||||
address addr;
|
||||
@ -7,4 +9,4 @@ contract Test {
|
||||
function f(MyStructName memory s) public {}
|
||||
}
|
||||
// ----
|
||||
// TypeError 4103: (112-133): Recursive type not allowed for public or external contract functions.
|
||||
// TypeError 4103: (147-168): Recursive type not allowed for public or external contract functions.
|
||||
|
@ -1,7 +1,9 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract C {
|
||||
struct S { uint a; S[] sub; }
|
||||
function f() public pure returns (uint, S memory) {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 4103: (91-99): Recursive type not allowed for public or external contract functions.
|
||||
// TypeError 4103: (126-134): Recursive type not allowed for public or external contract functions.
|
||||
|
@ -1,7 +1,9 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract C {
|
||||
struct S { uint a; S[2][] sub; }
|
||||
function f() public pure returns (uint, S memory) {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 4103: (94-102): Recursive type not allowed for public or external contract functions.
|
||||
// TypeError 4103: (129-137): Recursive type not allowed for public or external contract functions.
|
||||
|
@ -1,3 +1,5 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract C {
|
||||
struct S { uint a; S[][][] sub; }
|
||||
struct T { S s; }
|
||||
@ -5,4 +7,4 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 4103: (119-129): Recursive type not allowed for public or external contract functions.
|
||||
// TypeError 4103: (154-164): Recursive type not allowed for public or external contract functions.
|
||||
|
@ -7,4 +7,4 @@ contract D {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 9214: (160-161): Mappings cannot be assigned to.
|
||||
// TypeError 9214: (160-161): Types in storage containing (nested) mappings cannot be assigned to.
|
||||
|
@ -20,8 +20,18 @@ contract G {
|
||||
uint x = 1;
|
||||
mapping (uint => uint) b = x;
|
||||
}
|
||||
|
||||
contract H {
|
||||
struct S {mapping (uint => uint) a;}
|
||||
|
||||
S x;
|
||||
S y = x;
|
||||
S z = z;
|
||||
}
|
||||
// ----
|
||||
// TypeError 6280: (17-67): Mappings cannot be assigned to.
|
||||
// TypeError 6280: (120-148): Mappings cannot be assigned to.
|
||||
// TypeError 9214: (263-264): Mappings cannot be assigned to.
|
||||
// TypeError 6280: (312-340): Mappings cannot be assigned to.
|
||||
// TypeError 6280: (17-67): Types in storage containing (nested) mappings cannot be assigned to.
|
||||
// TypeError 6280: (120-148): Types in storage containing (nested) mappings cannot be assigned to.
|
||||
// TypeError 9214: (263-264): Types in storage containing (nested) mappings cannot be assigned to.
|
||||
// TypeError 6280: (312-340): Types in storage containing (nested) mappings cannot be assigned to.
|
||||
// TypeError 6280: (407-414): Types in storage containing (nested) mappings cannot be assigned to.
|
||||
// TypeError 6280: (417-424): Types in storage containing (nested) mappings cannot be assigned to.
|
||||
|
@ -8,7 +8,7 @@ contract test {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 9214: (126-129): Mappings cannot be assigned to.
|
||||
// TypeError 9214: (144-147): Mappings cannot be assigned to.
|
||||
// TypeError 9214: (163-166): Mappings cannot be assigned to.
|
||||
// TypeError 9214: (168-171): Mappings cannot be assigned to.
|
||||
// TypeError 9214: (126-129): Types in storage containing (nested) mappings cannot be assigned to.
|
||||
// TypeError 9214: (144-147): Types in storage containing (nested) mappings cannot be assigned to.
|
||||
// TypeError 9214: (163-166): Types in storage containing (nested) mappings cannot be assigned to.
|
||||
// TypeError 9214: (168-171): Types in storage containing (nested) mappings cannot be assigned to.
|
||||
|
@ -11,7 +11,7 @@ contract test {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 9214: (172-180): Mappings cannot be assigned to.
|
||||
// TypeError 9214: (195-203): Mappings cannot be assigned to.
|
||||
// TypeError 9214: (219-227): Mappings cannot be assigned to.
|
||||
// TypeError 9214: (229-237): Mappings cannot be assigned to.
|
||||
// TypeError 9214: (172-180): Types in storage containing (nested) mappings cannot be assigned to.
|
||||
// TypeError 9214: (195-203): Types in storage containing (nested) mappings cannot be assigned to.
|
||||
// TypeError 9214: (219-227): Types in storage containing (nested) mappings cannot be assigned to.
|
||||
// TypeError 9214: (229-237): Types in storage containing (nested) mappings cannot be assigned to.
|
||||
|
@ -0,0 +1,6 @@
|
||||
contract test {
|
||||
function f(mapping(uint => uint)[2] memory b) internal {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 4061: (31-64): Type mapping(uint256 => uint256)[2] is only valid in storage because it contains a (nested) mapping.
|
@ -0,0 +1,13 @@
|
||||
library Set {
|
||||
struct Data { mapping(uint => bool) flags; }
|
||||
|
||||
function insert(Data storage self, uint value)
|
||||
public
|
||||
returns (bool)
|
||||
{
|
||||
if (self.flags[value])
|
||||
return false; // already there
|
||||
self.flags[value] = true;
|
||||
return true;
|
||||
}
|
||||
}
|
@ -3,4 +3,3 @@ library L {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 3312: (27-58): Type is required to live outside storage.
|
||||
|
@ -3,4 +3,3 @@ library L {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 3312: (27-58): Type is required to live outside storage.
|
||||
|
@ -0,0 +1,7 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
library L {
|
||||
struct S { mapping(uint => uint) m; }
|
||||
function f(S memory a) external pure returns (S memory) {}
|
||||
}
|
||||
// ----
|
||||
// TypeError 4061: (103-113): Type struct L.S is only valid in storage because it contains a (nested) mapping.
|
@ -0,0 +1,6 @@
|
||||
library Test {
|
||||
struct Nested { mapping(uint => uint)[2][] a; uint y; }
|
||||
struct X { Nested n; }
|
||||
function f(X storage x) external {}
|
||||
}
|
||||
// ----
|
@ -2,5 +2,5 @@ contract c {
|
||||
function f1(mapping(uint => uint)[] calldata) pure external {}
|
||||
}
|
||||
// ----
|
||||
// TypeError 3312: (29-61): Type is required to live outside storage.
|
||||
// TypeError 4103: (29-61): Only libraries are allowed to use the mapping type in public or external functions.
|
||||
// TypeError 4103: (29-61): Types containing (nested) mappings can only be parameters or return variables of internal or library functions.
|
||||
// TypeError 4061: (29-61): Type mapping(uint256 => uint256)[] is only valid in storage because it contains a (nested) mapping.
|
||||
|
@ -2,5 +2,5 @@ contract c {
|
||||
function f1(mapping(uint => uint) calldata) pure external returns (mapping(uint => uint) memory) {}
|
||||
}
|
||||
// ----
|
||||
// TypeError 3442: (29-59): Mapping types can only have a data location of "storage" and thus only be parameters or return variables for internal or library functions.
|
||||
// TypeError 3442: (84-112): Mapping types can only have a data location of "storage" and thus only be parameters or return variables for internal or library functions.
|
||||
// TypeError 4103: (29-59): Types containing (nested) mappings can only be parameters or return variables of internal or library functions.
|
||||
// TypeError 4061: (29-59): Type mapping(uint256 => uint256) is only valid in storage because it contains a (nested) mapping.
|
||||
|
@ -3,4 +3,4 @@ contract c {
|
||||
function f5(mapping(uint => uint) memory) pure internal {}
|
||||
}
|
||||
// ----
|
||||
// TypeError 5380: (93-121): Mapping types can only have a data location of "storage".
|
||||
// TypeError 4061: (93-121): Type mapping(uint256 => uint256) is only valid in storage because it contains a (nested) mapping.
|
||||
|
@ -2,4 +2,5 @@ contract c {
|
||||
function f3(mapping(uint => uint) memory) view public {}
|
||||
}
|
||||
// ----
|
||||
// TypeError 3442: (29-57): Mapping types can only have a data location of "storage" and thus only be parameters or return variables for internal or library functions.
|
||||
// TypeError 4103: (29-57): Types containing (nested) mappings can only be parameters or return variables of internal or library functions.
|
||||
// TypeError 4061: (29-57): Type mapping(uint256 => uint256) is only valid in storage because it contains a (nested) mapping.
|
||||
|
@ -0,0 +1,12 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract test {
|
||||
struct S {
|
||||
mapping (uint => uint) s;
|
||||
}
|
||||
function f(S calldata b) external {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 4103: (121-133): Types containing (nested) mappings can only be parameters or return variables of internal or library functions.
|
||||
// TypeError 4061: (121-133): Type struct test.S is only valid in storage because it contains a (nested) mapping.
|
@ -3,4 +3,5 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 3442: (51-79): Mapping types can only have a data location of "storage" and thus only be parameters or return variables for internal or library functions.
|
||||
// TypeError 4103: (51-79): Types containing (nested) mappings can only be parameters or return variables of internal or library functions.
|
||||
// TypeError 4061: (51-79): Type mapping(uint256 => uint256) is only valid in storage because it contains a (nested) mapping.
|
||||
|
@ -0,0 +1,8 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
contract C {
|
||||
struct S { mapping(uint => uint) a; }
|
||||
function f(S memory) public {}
|
||||
}
|
||||
// ----
|
||||
// TypeError 4103: (104-112): Types containing (nested) mappings can only be parameters or return variables of internal or library functions.
|
||||
// TypeError 4061: (104-112): Type struct C.S is only valid in storage because it contains a (nested) mapping.
|
@ -0,0 +1,10 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
contract C {
|
||||
struct S { mapping(uint => uint) a; }
|
||||
struct T { S s; }
|
||||
struct U { T t; }
|
||||
function f(U memory) public {}
|
||||
}
|
||||
// ----
|
||||
// TypeError 4103: (148-156): Types containing (nested) mappings can only be parameters or return variables of internal or library functions.
|
||||
// TypeError 4061: (148-156): Type struct C.U is only valid in storage because it contains a (nested) mapping.
|
@ -0,0 +1,20 @@
|
||||
contract Test {
|
||||
struct S {
|
||||
T[] t;
|
||||
}
|
||||
|
||||
struct T {
|
||||
U[] u;
|
||||
}
|
||||
|
||||
struct U {
|
||||
S[] s;
|
||||
mapping (uint => S) map;
|
||||
}
|
||||
|
||||
function f() public {
|
||||
S memory s;
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 4061: (143-153): Type struct Test.S is only valid in storage because it contains a (nested) mapping.
|
@ -0,0 +1,15 @@
|
||||
contract Test {
|
||||
struct S {
|
||||
uint8 a;
|
||||
mapping(uint256 => uint256) b;
|
||||
uint8 c;
|
||||
}
|
||||
S s;
|
||||
|
||||
function f() public returns (uint256) {
|
||||
S memory x;
|
||||
}
|
||||
}
|
||||
|
||||
// ----
|
||||
// TypeError 4061: (172-182): Type struct Test.S is only valid in storage because it contains a (nested) mapping.
|
Loading…
Reference in New Issue
Block a user