Merge pull request #9146 from ethereum/containsNestedMappingType

[BREAKING] Implemented containsNestedMapping()
This commit is contained in:
chriseth 2020-06-30 18:39:21 +02:00 committed by GitHub
commit a79d7c1588
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
56 changed files with 351 additions and 229 deletions

View File

@ -8,6 +8,8 @@ Breaking changes:
* Parser: NatSpec comments on variables are only allowed for public state variables. * Parser: NatSpec comments on variables are only allowed for public state variables.
* Type Checker: Disallow shifts by signed types. * 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: 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: Language Features:
* Yul: Disallow EVM instruction `pc()`. * Yul: Disallow EVM instruction `pc()`.

View File

@ -26,6 +26,7 @@ are allowed for state variables, as storage reference types
in functions, or as parameters for library functions. in functions, or as parameters for library functions.
They cannot be used as parameters or return parameters They cannot be used as parameters or return parameters
of contract functions that are publicly visible. 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 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. :ref:`getter <visibility-and-getters>` for you. The ``_KeyType`` becomes a parameter for the getter.

View File

@ -488,12 +488,11 @@ shown in the following example:
function newCampaign(address payable beneficiary, uint goal) public returns (uint campaignID) { function newCampaign(address payable beneficiary, uint goal) public returns (uint campaignID) {
campaignID = numCampaigns++; // campaignID is return variable campaignID = numCampaigns++; // campaignID is return variable
// Creates new struct in memory and copies it to storage. // We cannot use "campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0)"
// We leave out the mapping type, because it is not valid in memory. // because the RHS creates a memory-struct "Campaign" that contains a mapping.
// If structs are copied (even from storage to storage), Campaign storage c = campaigns[campaignID];
// types that are not valid outside of storage (ex. mappings and array of mappings) c.beneficiary = beneficiary;
// are always omitted, because they cannot be enumerated. c.fundingGoal = goal;
campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0);
} }
function contribute(uint campaignID) public payable { function contribute(uint campaignID) public payable {

View File

@ -112,18 +112,16 @@ bool DeclarationTypeChecker::visit(StructDefinition const& _struct)
for (ASTPointer<VariableDeclaration> const& member: _struct.members()) for (ASTPointer<VariableDeclaration> const& member: _struct.members())
{ {
Type const* memberType = member->annotation().type; Type const* memberType = member->annotation().type;
while (auto arrayType = dynamic_cast<ArrayType const*>(memberType))
{ if (auto arrayType = dynamic_cast<ArrayType const*>(memberType))
if (arrayType->isDynamicallySized()) memberType = arrayType->finalBaseType(true);
break;
memberType = arrayType->baseType();
}
if (auto structType = dynamic_cast<StructType const*>(memberType)) if (auto structType = dynamic_cast<StructType const*>(memberType))
if (_cycleDetector.run(structType->structDefinition())) if (_cycleDetector.run(structType->structDefinition()))
return; 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."); m_errorReporter.fatalTypeError(2046_error, _struct.location(), "Recursive struct definition.");
return false; return false;

View File

@ -339,31 +339,21 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
m_errorReporter.typeError(5587_error, _function.location(), "Internal functions cannot be payable."); m_errorReporter.typeError(5587_error, _function.location(), "Internal functions cannot be payable.");
} }
auto checkArgumentAndReturnParameter = [&](VariableDeclaration const& var) { 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) auto iType = type(var)->interfaceType(_function.libraryFunction());
{
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());
if (!iType) if (!iType)
{ {
solAssert(!iType.message().empty(), "Expected detailed error message!"); solAssert(!iType.message().empty(), "Expected detailed error message!");
m_errorReporter.fatalTypeError(4103_error, var.location(), iType.message()); m_errorReporter.typeError(4103_error, var.location(), iType.message());
}
} }
} }
if ( 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."); m_errorReporter.typeError(1273_error, _variable.location(), "The type of a variable cannot be a library.");
if (_variable.value()) 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); _variable.value()->accept(*this);
} }
else else
@ -499,9 +493,16 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
if (!_variable.isStateVariable()) if (!_variable.isStateVariable())
{ {
if (varType->dataStoredIn(DataLocation::Memory) || varType->dataStoredIn(DataLocation::CallData)) if (
if (!varType->canLiveOutsideStorage()) _variable.referenceLocation() == VariableDeclaration::Location::CallData ||
m_errorReporter.typeError(4061_error, _variable.location(), "Type " + varType->toString() + " is only valid in storage."); _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) else if (_variable.visibility() >= Visibility::Public)
{ {
@ -612,8 +613,12 @@ bool TypeChecker::visit(EventDefinition const& _eventDef)
{ {
if (var->isIndexed()) if (var->isIndexed())
numIndexed++; numIndexed++;
if (!type(*var)->canLiveOutsideStorage()) if (type(*var)->containsNestedMapping())
m_errorReporter.typeError(3448_error, var->location(), "Type is required to live outside storage."); m_errorReporter.typeError(
3448_error,
var->location(),
"Type containing a (nested) mapping is not allowed as event parameter type."
);
if (!type(*var)->interfaceType(false)) if (!type(*var)->interfaceType(false))
m_errorReporter.typeError(3417_error, var->location(), "Internal or recursive type is not allowed as event parameter type."); m_errorReporter.typeError(3417_error, var->location(), "Internal or recursive type is not allowed as event parameter type.");
if ( if (
@ -1386,7 +1391,7 @@ void TypeChecker::checkExpressionAssignment(Type const& _type, Expression const&
checkExpressionAssignment(*types[i], *tupleExpression->components()[i]); checkExpressionAssignment(*types[i], *tupleExpression->components()[i]);
} }
} }
else if (_type.category() == Type::Category::Mapping) else if (_type.nameable() && _type.containsNestedMapping())
{ {
bool isLocalOrReturn = false; bool isLocalOrReturn = false;
if (auto const* identifier = dynamic_cast<Identifier const*>(&_expression)) if (auto const* identifier = dynamic_cast<Identifier const*>(&_expression))
@ -1394,7 +1399,7 @@ void TypeChecker::checkExpressionAssignment(Type const& _type, Expression const&
if (variableDeclaration->isLocalOrReturn()) if (variableDeclaration->isLocalOrReturn())
isLocalOrReturn = true; isLocalOrReturn = true;
if (!isLocalOrReturn) 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(), _tuple.location(),
"Unable to deduce nameable type for array elements. Try adding explicit type conversion for the first element." "Unable to deduce nameable type for array elements. Try adding explicit type conversion for the first element."
); );
else if (!inlineArrayType->canLiveOutsideStorage()) else if (inlineArrayType->containsNestedMapping())
m_errorReporter.fatalTypeError(1545_error, _tuple.location(), "Type " + inlineArrayType->toString() + " is only valid in storage."); 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()); _tuple.annotation().type = TypeProvider::array(DataLocation::Memory, inlineArrayType, types.size());
} }
@ -2032,24 +2041,8 @@ void TypeChecker::typeCheckFunctionGeneralChecks(
toString(parameterTypes.size()) + toString(parameterTypes.size()) +
"."; ".";
// Extend error message in case we try to construct a struct with mapping member.
if (isStructConstructorCall) 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 }; return { isVariadic ? 1123_error : 9755_error, msg };
}
else if ( else if (
_functionType->kind() == FunctionType::Kind::BareCall || _functionType->kind() == FunctionType::Kind::BareCall ||
_functionType->kind() == FunctionType::Kind::BareCallCode || _functionType->kind() == FunctionType::Kind::BareCallCode ||
@ -2269,6 +2262,12 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
if (actualType->category() == Type::Category::Struct) 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(); functionType = dynamic_cast<StructType const&>(*actualType).constructorType();
funcCallAnno.kind = FunctionCallKind::StructConstructorCall; funcCallAnno.kind = FunctionCallKind::StructConstructorCall;
funcCallAnno.isPure = argumentsArePure; funcCallAnno.isPure = argumentsArePure;
@ -2518,11 +2517,11 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
} }
else if (type->category() == Type::Category::Array) else if (type->category() == Type::Category::Array)
{ {
if (!type->canLiveOutsideStorage()) if (type->containsNestedMapping())
m_errorReporter.fatalTypeError( m_errorReporter.fatalTypeError(
1164_error, 1164_error,
_newExpression.typeName().location(), _newExpression.typeName().location(),
"Type cannot live outside storage." "Array containing a (nested) mapping cannot be constructed in memory."
); );
if (!type->isDynamicallySized()) if (!type->isDynamicallySized())
m_errorReporter.typeError( m_errorReporter.typeError(

View File

@ -136,6 +136,9 @@ struct StructDeclarationAnnotation: TypeDeclarationAnnotation
/// recursion immediately raises an error. /// recursion immediately raises an error.
/// Will be filled in by the DeclarationTypeChecker. /// Will be filled in by the DeclarationTypeChecker.
std::optional<bool> recursive; 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 struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, StructurallyDocumentedAnnotation

View File

@ -1985,6 +1985,20 @@ TypeResult ArrayType::interfaceType(bool _inLibrary) const
return result; 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 u256 ArrayType::memoryDataSize() const
{ {
solAssert(!isDynamicallySized(), ""); solAssert(!isDynamicallySized(), "");
@ -2213,7 +2227,7 @@ unsigned StructType::calldataEncodedSize(bool) const
unsigned size = 0; unsigned size = 0;
for (auto const& member: members(nullptr)) for (auto const& member: members(nullptr))
{ {
solAssert(member.type->canLiveOutsideStorage(), ""); solAssert(!member.type->containsNestedMapping(), "");
// Struct members are always padded. // Struct members are always padded.
size += member.type->calldataEncodedSize(); size += member.type->calldataEncodedSize();
} }
@ -2228,7 +2242,7 @@ unsigned StructType::calldataEncodedTailSize() const
unsigned size = 0; unsigned size = 0;
for (auto const& member: members(nullptr)) for (auto const& member: members(nullptr))
{ {
solAssert(member.type->canLiveOutsideStorage(), ""); solAssert(!member.type->containsNestedMapping(), "");
// Struct members are always padded. // Struct members are always padded.
size += member.type->calldataHeadSize(); size += member.type->calldataHeadSize();
} }
@ -2240,7 +2254,7 @@ unsigned StructType::calldataOffsetOfMember(std::string const& _member) const
unsigned offset = 0; unsigned offset = 0;
for (auto const& member: members(nullptr)) for (auto const& member: members(nullptr))
{ {
solAssert(member.type->canLiveOutsideStorage(), ""); solAssert(!member.type->containsNestedMapping(), "");
if (member.name == _member) if (member.name == _member)
return offset; return offset;
// Struct members are always padded. // Struct members are always padded.
@ -2277,6 +2291,42 @@ u256 StructType::storageSize() const
return max<u256>(1, members(nullptr).storageSize()); 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 StructType::toString(bool _short) const
{ {
string ret = "struct " + m_struct.annotation().canonicalName; string ret = "struct " + m_struct.annotation().canonicalName;
@ -2292,10 +2342,7 @@ MemberList::MemberMap StructType::nativeMembers(ASTNode const*) const
{ {
TypePointer type = variable->annotation().type; TypePointer type = variable->annotation().type;
solAssert(type, ""); solAssert(type, "");
// If we are not in storage, skip all members that cannot live outside of storage, solAssert(!(location() != DataLocation::Storage && type->containsNestedMapping()), "");
// ex. mappings and array of mappings
if (location() != DataLocation::Storage && !type->canLiveOutsideStorage())
continue;
members.emplace_back( members.emplace_back(
variable->name(), variable->name(),
copyForLocationIfReference(type), copyForLocationIfReference(type),
@ -2457,10 +2504,9 @@ FunctionTypePointer StructType::constructorType() const
{ {
TypePointers paramTypes; TypePointers paramTypes;
strings paramNames; strings paramNames;
solAssert(!containsNestedMapping(), "");
for (auto const& member: members(nullptr)) for (auto const& member: members(nullptr))
{ {
if (!member.type->canLiveOutsideStorage())
continue;
paramNames.push_back(member.name); paramNames.push_back(member.name);
paramTypes.push_back(TypeProvider::withLocationIfReference(DataLocation::Memory, member.type)); paramTypes.push_back(TypeProvider::withLocationIfReference(DataLocation::Memory, member.type));
} }
@ -2494,20 +2540,12 @@ u256 StructType::memoryOffsetOfMember(string const& _name) const
TypePointers StructType::memoryMemberTypes() const TypePointers StructType::memoryMemberTypes() const
{ {
solAssert(!containsNestedMapping(), "");
TypePointers types; TypePointers types;
for (ASTPointer<VariableDeclaration> const& variable: m_struct.members()) for (ASTPointer<VariableDeclaration> const& variable: m_struct.members())
if (variable->annotation().type->canLiveOutsideStorage()) types.push_back(TypeProvider::withLocationIfReference(DataLocation::Memory, variable->annotation().type));
types.push_back(TypeProvider::withLocationIfReference(DataLocation::Memory, variable->annotation().type));
return types;
}
set<string> StructType::membersMissingInMemory() const return types;
{
set<string> missing;
for (ASTPointer<VariableDeclaration> const& variable: m_struct.members())
if (!variable->annotation().type->canLiveOutsideStorage())
missing.insert(variable->name());
return missing;
} }
vector<tuple<string, TypePointer>> StructType::makeStackItems() const vector<tuple<string, TypePointer>> StructType::makeStackItems() const
@ -3654,7 +3692,10 @@ TypeResult MappingType::interfaceType(bool _inLibrary) const
} }
} }
else 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; return this;
} }

View File

@ -259,7 +259,11 @@ public:
/// Returns true if the type can be stored in storage. /// Returns true if the type can be stored in storage.
virtual bool canBeStored() const { return true; } virtual bool canBeStored() const { return true; }
/// Returns false if the type cannot live outside the storage, i.e. if it includes some mapping. /// 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, /// 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. /// i.e. it behaves differently in lvalue context and in value context.
virtual bool isValueType() const { return false; } virtual bool isValueType() const { return false; }
@ -549,7 +553,6 @@ public:
bool operator==(Type const& _other) const override; bool operator==(Type const& _other) const override;
bool canBeStored() const override { return false; } bool canBeStored() const override { return false; }
bool canLiveOutsideStorage() const override { return false; }
std::string toString(bool _short) const override; std::string toString(bool _short) const override;
u256 literalValue(Literal const* _literal) const override; u256 literalValue(Literal const* _literal) const override;
@ -610,7 +613,6 @@ public:
bool operator==(Type const& _other) const override; bool operator==(Type const& _other) const override;
bool canBeStored() const override { return false; } bool canBeStored() const override { return false; }
bool canLiveOutsideStorage() const override { return false; }
std::string toString(bool) const override; std::string toString(bool) const override;
TypePointer mobileType() const override; TypePointer mobileType() const override;
@ -781,8 +783,9 @@ public:
bool isDynamicallySized() const override { return m_hasDynamicLength; } bool isDynamicallySized() const override { return m_hasDynamicLength; }
bool isDynamicallyEncoded() const override; bool isDynamicallyEncoded() const override;
u256 storageSize() 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; } bool nameable() const override { return true; }
std::string toString(bool _short) const override; std::string toString(bool _short) const override;
std::string canonicalName() const override; std::string canonicalName() const override;
std::string signatureInExternalFunction(bool _structsByName) const override; std::string signatureInExternalFunction(bool _structsByName) const override;
@ -798,6 +801,7 @@ public:
/// @returns true if this is a string /// @returns true if this is a string
bool isString() const { return m_arrayKind == ArrayKind::String; } bool isString() const { return m_arrayKind == ArrayKind::String; }
Type const* baseType() const { solAssert(!!m_baseType, ""); return m_baseType; } Type const* baseType() const { solAssert(!!m_baseType, ""); return m_baseType; }
Type const* finalBaseType(bool breakIfDynamicArrayType) const;
u256 const& length() const { return m_length; } u256 const& length() const { return m_length; }
u256 memoryDataSize() const override; u256 memoryDataSize() const override;
@ -842,7 +846,6 @@ public:
unsigned calldataEncodedTailSize() const override { return 32; } unsigned calldataEncodedTailSize() const override { return 32; }
bool isDynamicallySized() const override { return true; } bool isDynamicallySized() const override { return true; }
bool isDynamicallyEncoded() 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; std::string toString(bool _short) const override;
TypePointer mobileType() const override; TypePointer mobileType() const override;
@ -883,7 +886,6 @@ public:
} }
unsigned storageBytes() const override { solAssert(!isSuper(), ""); return 20; } unsigned storageBytes() const override { solAssert(!isSuper(), ""); return 20; }
bool leftAligned() const override { solAssert(!isSuper(), ""); return false; } bool leftAligned() const override { solAssert(!isSuper(), ""); return false; }
bool canLiveOutsideStorage() const override { return !isSuper(); }
bool isValueType() const override { return !isSuper(); } bool isValueType() const override { return !isSuper(); }
bool nameable() const override { return !isSuper(); } bool nameable() const override { return !isSuper(); }
std::string toString(bool _short) const override; std::string toString(bool _short) const override;
@ -945,7 +947,7 @@ public:
bool isDynamicallyEncoded() const override; bool isDynamicallyEncoded() const override;
u256 memoryDataSize() const override; u256 memoryDataSize() const override;
u256 storageSize() const override; u256 storageSize() const override;
bool canLiveOutsideStorage() const override { return true; } bool containsNestedMapping() const override;
bool nameable() const override { return true; } bool nameable() const override { return true; }
std::string toString(bool _short) const override; std::string toString(bool _short) const override;
@ -975,8 +977,6 @@ public:
/// @returns the vector of types of members available in memory. /// @returns the vector of types of members available in memory.
TypePointers memoryMemberTypes() const; 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; void clearCache() const override;
@ -1007,7 +1007,6 @@ public:
} }
unsigned storageBytes() const override; unsigned storageBytes() const override;
bool leftAligned() const override { return false; } bool leftAligned() const override { return false; }
bool canLiveOutsideStorage() const override { return true; }
std::string toString(bool _short) const override; std::string toString(bool _short) const override;
std::string canonicalName() const override; std::string canonicalName() const override;
bool isValueType() const override { return true; } bool isValueType() const override { return true; }
@ -1047,7 +1046,6 @@ public:
std::string toString(bool) const override; std::string toString(bool) const override;
bool canBeStored() const override { return false; } bool canBeStored() const override { return false; }
u256 storageSize() const override; u256 storageSize() const override;
bool canLiveOutsideStorage() const override { return false; }
bool hasSimpleZeroValueInMemory() const override { return false; } bool hasSimpleZeroValueInMemory() const override { return false; }
TypePointer mobileType() const override; TypePointer mobileType() const override;
/// Converts components to their temporary types and performs some wildcard matching. /// Converts components to their temporary types and performs some wildcard matching.
@ -1218,7 +1216,6 @@ public:
unsigned storageBytes() const override; unsigned storageBytes() const override;
bool isValueType() const override { return true; } bool isValueType() const override { return true; }
bool nameable() const override; bool nameable() const override;
bool canLiveOutsideStorage() const override { return m_kind == Kind::Internal || m_kind == Kind::External; }
bool hasSimpleZeroValueInMemory() const override { return false; } bool hasSimpleZeroValueInMemory() const override { return false; }
MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override; MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override;
TypePointer encodingType() const override; TypePointer encodingType() const override;
@ -1351,7 +1348,7 @@ public:
bool operator==(Type const& _other) const override; bool operator==(Type const& _other) const override;
std::string toString(bool _short) const override; std::string toString(bool _short) const override;
std::string canonicalName() 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; } TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; }
Type const* encodingType() const override; Type const* encodingType() const override;
TypeResult interfaceType(bool _inLibrary) const override; TypeResult interfaceType(bool _inLibrary) const override;
@ -1386,7 +1383,6 @@ public:
bool operator==(Type const& _other) const override; bool operator==(Type const& _other) const override;
bool canBeStored() const override { return false; } bool canBeStored() const override { return false; }
u256 storageSize() const override; u256 storageSize() const override;
bool canLiveOutsideStorage() const override { return false; }
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; } std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; }
MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override; MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override;
@ -1412,7 +1408,6 @@ public:
TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; } TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; }
bool canBeStored() const override { return false; } bool canBeStored() const override { return false; }
u256 storageSize() const override; u256 storageSize() const override;
bool canLiveOutsideStorage() const override { return false; }
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
std::string richIdentifier() const override; std::string richIdentifier() const override;
bool operator==(Type const& _other) const override; bool operator==(Type const& _other) const override;
@ -1439,7 +1434,6 @@ public:
std::string richIdentifier() const override; std::string richIdentifier() const override;
bool operator==(Type const& _other) const override; bool operator==(Type const& _other) const override;
bool canBeStored() const override { return false; } bool canBeStored() const override { return false; }
bool canLiveOutsideStorage() const override { return true; }
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
MemberList::MemberMap nativeMembers(ASTNode const*) const override; MemberList::MemberMap nativeMembers(ASTNode const*) const override;
@ -1479,7 +1473,6 @@ public:
std::string richIdentifier() const override; std::string richIdentifier() const override;
bool operator==(Type const& _other) const override; bool operator==(Type const& _other) const override;
bool canBeStored() const override { return false; } bool canBeStored() const override { return false; }
bool canLiveOutsideStorage() const override { return true; }
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
MemberList::MemberMap nativeMembers(ASTNode const*) const override; MemberList::MemberMap nativeMembers(ASTNode const*) const override;
@ -1512,7 +1505,6 @@ public:
TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; } TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; }
unsigned calldataEncodedSize(bool) const override { return 32; } unsigned calldataEncodedSize(bool) const override { return 32; }
bool canBeStored() const override { return false; } bool canBeStored() const override { return false; }
bool canLiveOutsideStorage() const override { return false; }
bool isValueType() const override { return true; } bool isValueType() const override { return true; }
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
std::string toString(bool) const override { return "inaccessible dynamic type"; } std::string toString(bool) const override { return "inaccessible dynamic type"; }

View File

@ -860,8 +860,7 @@ string ABIFunctions::abiEncodingFunctionStruct(
for (auto const& member: _to.members(nullptr)) for (auto const& member: _to.members(nullptr))
{ {
solAssert(member.type, ""); solAssert(member.type, "");
if (!member.type->canLiveOutsideStorage()) solAssert(!member.type->containsNestedMapping(), "");
continue;
TypePointer memberTypeTo = member.type->fullEncodingType(_options.encodeAsLibraryTypes, true, false); TypePointer memberTypeTo = member.type->fullEncodingType(_options.encodeAsLibraryTypes, true, false);
solUnimplementedAssert(memberTypeTo, "Encoding type \"" + member.type->toString() + "\" not yet implemented."); solUnimplementedAssert(memberTypeTo, "Encoding type \"" + member.type->toString() + "\" not yet implemented.");
auto memberTypeFrom = _from.memberType(member.name); 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)) for (auto const& member: _type.members(nullptr))
{ {
solAssert(member.type, ""); solAssert(member.type, "");
solAssert(member.type->canLiveOutsideStorage(), ""); solAssert(!member.type->containsNestedMapping(), "");
auto decodingType = member.type->decodingType(); auto decodingType = member.type->decodingType();
solAssert(decodingType, ""); solAssert(decodingType, "");
bool dynamic = decodingType->isDynamicallyEncoded(); bool dynamic = decodingType->isDynamicallyEncoded();

View File

@ -1069,8 +1069,7 @@ void CompilerUtils::convertType(
// stack: <memory ptr> <source ref> <memory ptr> // stack: <memory ptr> <source ref> <memory ptr>
for (auto const& member: typeOnStack->members(nullptr)) for (auto const& member: typeOnStack->members(nullptr))
{ {
if (!member.type->canLiveOutsideStorage()) solAssert(!member.type->containsNestedMapping(), "");
continue;
pair<u256, unsigned> const& offsets = typeOnStack->storageOffsetsOfMember(member.name); pair<u256, unsigned> const& offsets = typeOnStack->storageOffsetsOfMember(member.name);
_context << offsets.first << Instruction::DUP3 << Instruction::ADD; _context << offsets.first << Instruction::DUP3 << Instruction::ADD;
_context << u256(offsets.second); _context << u256(offsets.second);

View File

@ -355,13 +355,13 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
structType.structDefinition() == sourceType.structDefinition(), structType.structDefinition() == sourceType.structDefinition(),
"Struct assignment with conversion." "Struct assignment with conversion."
); );
solAssert(!structType.containsNestedMapping(), "");
solAssert(sourceType.location() != DataLocation::CallData, "Structs in calldata not supported."); solAssert(sourceType.location() != DataLocation::CallData, "Structs in calldata not supported.");
for (auto const& member: structType.members(nullptr)) for (auto const& member: structType.members(nullptr))
{ {
// assign each member that can live outside of storage // assign each member that can live outside of storage
TypePointer const& memberType = member.type; TypePointer const& memberType = member.type;
if (!memberType->canLiveOutsideStorage()) solAssert(memberType->nameable(), "");
continue;
TypePointer sourceMemberType = sourceType.memberType(member.name); TypePointer sourceMemberType = sourceType.memberType(member.name);
if (sourceType.location() == DataLocation::Storage) if (sourceType.location() == DataLocation::Storage)
{ {

View File

@ -3465,12 +3465,12 @@ BOOST_AUTO_TEST_CASE(array_copy_calldata_storage)
ABI_CHECK(callContractFunction("retrieve()"), encodeArgs(9, 28, 9, 28, 4, 3, 32)); 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"( char const* sourceCode = R"(
contract c { contract c {
mapping(uint=>uint)[90][] large; uint[3][90][] large;
mapping(uint=>uint)[3][] small; uint[3][3][] small;
function test() public returns (uint r) { function test() public returns (uint r) {
for (uint i = 0; i < 7; i++) { for (uint i = 0; i < 7; i++) {
large.push(); large.push();
@ -3505,9 +3505,8 @@ BOOST_AUTO_TEST_CASE(array_copy_including_mapping)
} }
)"; )";
compileAndRun(sourceCode); compileAndRun(sourceCode);
ABI_CHECK(callContractFunction("test()"), encodeArgs(0x02000200)); ABI_CHECK(callContractFunction("test()"), encodeArgs(0x02000202));
// storage is not empty because we cannot delete the mappings BOOST_CHECK(storageEmpty(m_contractAddress));
BOOST_CHECK(!storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("clear()"), encodeArgs(0, 0)); ABI_CHECK(callContractFunction("clear()"), encodeArgs(0, 0));
BOOST_CHECK(storageEmpty(m_contractAddress)); BOOST_CHECK(storageEmpty(m_contractAddress));
} }

View File

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

View File

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

View File

@ -5,7 +5,6 @@ contract c {
} }
struct Struct { struct Struct {
uint256 a; uint256 a;
mapping(uint256 => Struct) b;
Nested nested; Nested nested;
uint256 c; uint256 c;
} }

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

View File

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

View File

@ -5,7 +5,6 @@ contract C {
struct S { uint256 [ ] [ 0.425781 ether ] s1 ; struct S { uint256 [ ] [ 0.425781 ether ] s1 ;
uint [ 2 ** 0xFF ] [ 2 ** 0x42 ] s2 ; uint [ 2 ** 0xFF ] [ 2 ** 0x42 ] s2 ;
X s3 ; X s3 ;
mapping ( uint => address payable ) c ;
uint [ 9 hours ** 16 ] d ; uint [ 9 hours ** 16 ] d ;
string s ; 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.

View File

@ -11,3 +11,4 @@ contract Test {
} }
// ---- // ----
// DeclarationError 2333: (157-198): Identifier already declared. // 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.

View File

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

View File

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

View File

@ -3,8 +3,7 @@ contract Test {
S s; S s;
function f() public { function f() public {
S memory x; 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.

View File

@ -9,3 +9,4 @@ contract test {
} }
} }
// ---- // ----
// TypeError 9214: (152-156): Types in storage containing (nested) mappings cannot be assigned to.

View File

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

View File

@ -5,4 +5,5 @@ contract C {
function f(S memory) public {} 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.

View File

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

View File

@ -6,4 +6,5 @@ contract C {
function f(S memory) public {} 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.

View File

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

View File

@ -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; 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 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. // TypeError 8598: (17-132): More than 4 indexed arguments for anonymous event.

View File

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

View File

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

View File

@ -1,3 +1,5 @@
pragma experimental ABIEncoderV2;
contract Test { contract Test {
struct MyStructName { struct MyStructName {
address addr; address addr;
@ -7,4 +9,4 @@ contract Test {
function f(MyStructName memory s) public {} 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.

View File

@ -1,7 +1,9 @@
pragma experimental ABIEncoderV2;
contract C { contract C {
struct S { uint a; S[] sub; } struct S { uint a; S[] sub; }
function f() public pure returns (uint, S memory) { 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.

View File

@ -1,7 +1,9 @@
pragma experimental ABIEncoderV2;
contract C { contract C {
struct S { uint a; S[2][] sub; } struct S { uint a; S[2][] sub; }
function f() public pure returns (uint, S memory) { 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.

View File

@ -1,3 +1,5 @@
pragma experimental ABIEncoderV2;
contract C { contract C {
struct S { uint a; S[][][] sub; } struct S { uint a; S[][][] sub; }
struct T { S s; } 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.

View File

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

View File

@ -20,8 +20,18 @@ contract G {
uint x = 1; uint x = 1;
mapping (uint => uint) b = x; 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: (17-67): Types in storage containing (nested) mappings cannot be assigned to.
// TypeError 6280: (120-148): Mappings cannot be assigned to. // TypeError 6280: (120-148): Types in storage containing (nested) mappings cannot be assigned to.
// TypeError 9214: (263-264): Mappings cannot be assigned to. // TypeError 9214: (263-264): Types in storage containing (nested) mappings cannot be assigned to.
// TypeError 6280: (312-340): 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.

View File

@ -8,7 +8,7 @@ contract test {
} }
} }
// ---- // ----
// TypeError 9214: (126-129): Mappings cannot be assigned to. // TypeError 9214: (126-129): Types in storage containing (nested) mappings cannot be assigned to.
// TypeError 9214: (144-147): Mappings cannot be assigned to. // TypeError 9214: (144-147): Types in storage containing (nested) mappings cannot be assigned to.
// TypeError 9214: (163-166): Mappings cannot be assigned to. // TypeError 9214: (163-166): Types in storage containing (nested) mappings cannot be assigned to.
// TypeError 9214: (168-171): Mappings cannot be assigned to. // TypeError 9214: (168-171): Types in storage containing (nested) mappings cannot be assigned to.

View File

@ -11,7 +11,7 @@ contract test {
} }
} }
// ---- // ----
// TypeError 9214: (172-180): Mappings cannot be assigned to. // TypeError 9214: (172-180): Types in storage containing (nested) mappings cannot be assigned to.
// TypeError 9214: (195-203): Mappings cannot be assigned to. // TypeError 9214: (195-203): Types in storage containing (nested) mappings cannot be assigned to.
// TypeError 9214: (219-227): Mappings cannot be assigned to. // TypeError 9214: (219-227): Types in storage containing (nested) mappings cannot be assigned to.
// TypeError 9214: (229-237): Mappings cannot be assigned to. // TypeError 9214: (229-237): Types in storage containing (nested) mappings cannot be assigned to.

View File

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

View File

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

View File

@ -3,4 +3,3 @@ library L {
} }
} }
// ---- // ----
// TypeError 3312: (27-58): Type is required to live outside storage.

View File

@ -3,4 +3,3 @@ library L {
} }
} }
// ---- // ----
// TypeError 3312: (27-58): Type is required to live outside storage.

View File

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

View File

@ -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 {}
}
// ----

View File

@ -2,5 +2,5 @@ contract c {
function f1(mapping(uint => uint)[] calldata) pure external {} function f1(mapping(uint => uint)[] calldata) pure external {}
} }
// ---- // ----
// TypeError 3312: (29-61): Type is required to live outside storage. // TypeError 4103: (29-61): Types containing (nested) mappings can only be parameters or return variables of internal or library functions.
// TypeError 4103: (29-61): Only libraries are allowed to use the mapping type in public or external functions. // TypeError 4061: (29-61): Type mapping(uint256 => uint256)[] is only valid in storage because it contains a (nested) mapping.

View File

@ -2,5 +2,5 @@ contract c {
function f1(mapping(uint => uint) calldata) pure external returns (mapping(uint => uint) memory) {} 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 4103: (29-59): Types containing (nested) mappings can only be parameters or return variables of 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 4061: (29-59): Type mapping(uint256 => uint256) is only valid in storage because it contains a (nested) mapping.

View File

@ -3,4 +3,4 @@ contract c {
function f5(mapping(uint => uint) memory) pure internal {} 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.

View File

@ -2,4 +2,5 @@ contract c {
function f3(mapping(uint => uint) memory) view public {} 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.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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