diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 6d6a13cff..7333c024a 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -21,6 +21,7 @@ */ #include +#include #include #include #include @@ -434,23 +435,29 @@ void StructDefinition::checkMemberTypes() const void StructDefinition::checkRecursion() const { - set definitionsSeen; - vector queue = {this}; - while (!queue.empty()) + using StructPointer = StructDefinition const*; + using StructPointersSet = set; + function check = [&](StructPointer _struct, StructPointersSet const& _parents) { - StructDefinition const* def = queue.back(); - queue.pop_back(); - if (definitionsSeen.count(def)) - BOOST_THROW_EXCEPTION(ParserError() << errinfo_sourceLocation(def->getLocation()) - << errinfo_comment("Recursive struct definition.")); - definitionsSeen.insert(def); - for (ASTPointer const& member: def->getMembers()) + if (_parents.count(_struct)) + BOOST_THROW_EXCEPTION( + ParserError() << + errinfo_sourceLocation(_struct->getLocation()) << + errinfo_comment("Recursive struct definition.") + ); + set parents = _parents; + parents.insert(_struct); + for (ASTPointer const& member: _struct->getMembers()) if (member->getType()->getCategory() == Type::Category::Struct) { - UserDefinedTypeName const& typeName = dynamic_cast(*member->getTypeName()); - queue.push_back(&dynamic_cast(*typeName.getReferencedDeclaration())); + auto const& typeName = dynamic_cast(*member->getTypeName()); + check( + &dynamic_cast(*typeName.getReferencedDeclaration()), + parents + ); } - } + }; + check(this, {}); } TypePointer EnumDefinition::getType(ContractDefinition const*) const diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index fced12848..afa8b727f 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -190,6 +190,17 @@ BOOST_AUTO_TEST_CASE(struct_definition_indirectly_recursive) BOOST_CHECK_THROW(parseTextAndResolveNames(text), ParserError); } +BOOST_AUTO_TEST_CASE(struct_definition_not_really_recursive) +{ + char const* text = R"( + contract test { + struct s1 { uint a; } + struct s2 { s1 x; s1 y; } + } + )"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); +} + BOOST_AUTO_TEST_CASE(struct_definition_recursion_via_mapping) { char const* text = "contract test {\n"