Added containsNestedMapping()

This commit is contained in:
Harikrishnan Mulackal 2020-06-03 16:08:35 +05:30
parent af0cd4ab98
commit d41eaeba56
8 changed files with 136 additions and 105 deletions

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