diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 26529c229..fe4207a3a 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -546,7 +546,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function) if (!type(*var)->canLiveOutsideStorage()) m_errorReporter.typeError(var->location(), "Type is required to live outside storage."); if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->interfaceType(isLibraryFunction))) - m_errorReporter.fatalTypeError(var->location(), "Internal type is not allowed for public or external functions."); + m_errorReporter.fatalTypeError(var->location(), "Internal or recursive type is not allowed for public or external functions."); var->accept(*this); } @@ -641,7 +641,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) _variable.visibility() >= VariableDeclaration::Visibility::Public && !FunctionType(_variable).interfaceFunctionType() ) - m_errorReporter.typeError(_variable.location(), "Internal type is not allowed for public state variables."); + m_errorReporter.typeError(_variable.location(), "Internal or recursive type is not allowed for public state variables."); if (varType->category() == Type::Category::Array) if (auto arrayType = dynamic_cast(varType.get())) @@ -728,7 +728,7 @@ bool TypeChecker::visit(EventDefinition const& _eventDef) if (!type(*var)->canLiveOutsideStorage()) m_errorReporter.typeError(var->location(), "Type is required to live outside storage."); if (!type(*var)->interfaceType(false)) - m_errorReporter.typeError(var->location(), "Internal type is not allowed as event parameter type."); + m_errorReporter.typeError(var->location(), "Internal or recursive type is not allowed as event parameter type."); } if (_eventDef.isAnonymous() && numIndexed > 4) m_errorReporter.typeError(_eventDef.location(), "More than 4 indexed arguments for anonymous event."); diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 3a93b74e2..443164030 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -1769,8 +1769,10 @@ TypePointer StructType::interfaceType(bool _inLibrary) const { if (_inLibrary && location() == DataLocation::Storage) return shared_from_this(); - else + else if (!recursive()) return copyForLocation(DataLocation::Memory, true); + else + return TypePointer(); } TypePointer StructType::copyForLocation(DataLocation _location, bool _isPointer) const @@ -1836,6 +1838,29 @@ set StructType::membersMissingInMemory() const return missing; } +bool StructType::recursive() const +{ + set structsSeen; + function check = [&](StructType const* t) -> bool + { + StructDefinition const* str = &t->structDefinition(); + if (structsSeen.count(str)) + return true; + structsSeen.insert(str); + for (ASTPointer const& variable: str->members()) + { + Type const* memberType = variable->annotation().type.get(); + while (dynamic_cast(memberType)) + memberType = dynamic_cast(memberType)->baseType().get(); + if (StructType const* innerStruct = dynamic_cast(memberType)) + if (check(innerStruct)) + return true; + } + return false; + }; + return check(this); +} + TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const { return _operator == Token::Delete ? make_shared() : TypePointer(); diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index d4d6da690..e6d3a7b1c 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -758,6 +758,10 @@ public: /// @returns the set of all members that are removed in the memory version (typically mappings). std::set membersMissingInMemory() const; + /// @returns true if the same struct is used recursively in one of its members. Only + /// analyses the "memory" representation, i.e. mappings are ignored in all structs. + bool recursive() const; + private: StructDefinition const& m_struct; };