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.
* 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()`.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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 {
uint256 a;
mapping(uint256 => Struct) b;
Nested nested;
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 ;
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.

View File

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

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

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 {}
}
// ----
// 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 {}
}
// ----
// 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;
}
// ----
// 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.

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

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.