mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Annotate struct definitions with a recursive flag.
This commit is contained in:
@@ -257,12 +257,13 @@ TypeNameAnnotation& TypeName::annotation() const
|
||||
|
||||
TypePointer StructDefinition::type() const
|
||||
{
|
||||
solAssert(annotation().recursive.has_value(), "Requested struct type before DeclarationTypeChecker.");
|
||||
return TypeProvider::typeType(TypeProvider::structType(*this, DataLocation::Storage));
|
||||
}
|
||||
|
||||
TypeDeclarationAnnotation& StructDefinition::annotation() const
|
||||
StructDeclarationAnnotation& StructDefinition::annotation() const
|
||||
{
|
||||
return initAnnotation<TypeDeclarationAnnotation>();
|
||||
return initAnnotation<StructDeclarationAnnotation>();
|
||||
}
|
||||
|
||||
TypePointer EnumValue::type() const
|
||||
|
||||
@@ -607,7 +607,7 @@ public:
|
||||
bool isVisibleInDerivedContracts() const override { return true; }
|
||||
bool isVisibleViaContractTypeAccess() const override { return true; }
|
||||
|
||||
TypeDeclarationAnnotation& annotation() const override;
|
||||
StructDeclarationAnnotation& annotation() const override;
|
||||
|
||||
private:
|
||||
std::vector<ASTPointer<VariableDeclaration>> m_members;
|
||||
|
||||
@@ -128,6 +128,12 @@ struct TypeDeclarationAnnotation: DeclarationAnnotation
|
||||
std::string canonicalName;
|
||||
};
|
||||
|
||||
struct StructDeclarationAnnotation: TypeDeclarationAnnotation
|
||||
{
|
||||
/// Whether the struct is recursive. Will be filled in by the DeclarationTypeChecker.
|
||||
std::optional<bool> recursive;
|
||||
};
|
||||
|
||||
struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, StructurallyDocumentedAnnotation
|
||||
{
|
||||
/// List of functions without a body. Can also contain functions from base classes.
|
||||
|
||||
+86
-82
@@ -2175,93 +2175,107 @@ MemberList::MemberMap StructType::nativeMembers(ContractDefinition const*) const
|
||||
|
||||
TypeResult StructType::interfaceType(bool _inLibrary) const
|
||||
{
|
||||
if (_inLibrary && m_interfaceType_library.has_value())
|
||||
return *m_interfaceType_library;
|
||||
|
||||
if (!_inLibrary && m_interfaceType.has_value())
|
||||
if (!_inLibrary)
|
||||
{
|
||||
if (!m_interfaceType.has_value())
|
||||
{
|
||||
if (recursive())
|
||||
m_interfaceType = TypeResult::err("Recursive type not allowed for public or external contract functions.");
|
||||
else
|
||||
{
|
||||
TypeResult result{TypePointer{}};
|
||||
for (ASTPointer<VariableDeclaration> const& member: m_struct.members())
|
||||
{
|
||||
if (!member->annotation().type)
|
||||
{
|
||||
result = TypeResult::err("Invalid type!");
|
||||
break;
|
||||
}
|
||||
auto interfaceType = member->annotation().type->interfaceType(false);
|
||||
if (!interfaceType.get())
|
||||
{
|
||||
solAssert(!interfaceType.message().empty(), "Expected detailed error message!");
|
||||
result = interfaceType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result.message().empty())
|
||||
m_interfaceType = TypeProvider::withLocation(this, DataLocation::Memory, true);
|
||||
else
|
||||
m_interfaceType = result;
|
||||
}
|
||||
}
|
||||
return *m_interfaceType;
|
||||
}
|
||||
else if (m_interfaceType_library.has_value())
|
||||
return *m_interfaceType_library;
|
||||
|
||||
TypeResult result{TypePointer{}};
|
||||
|
||||
m_recursive = false;
|
||||
|
||||
auto visitor = [&](
|
||||
StructDefinition const& _struct,
|
||||
util::CycleDetector<StructDefinition>& _cycleDetector,
|
||||
size_t /*_depth*/
|
||||
)
|
||||
{
|
||||
// Check that all members have interface types.
|
||||
// Return an error if at least one struct member does not have a type.
|
||||
// This might happen, for example, if the type of the member does not exist.
|
||||
for (ASTPointer<VariableDeclaration> const& variable: _struct.members())
|
||||
{
|
||||
// If the struct member does not have a type return false.
|
||||
// A TypeError is expected in this case.
|
||||
if (!variable->annotation().type)
|
||||
{
|
||||
result = TypeResult::err("Invalid type!");
|
||||
return;
|
||||
}
|
||||
|
||||
Type const* memberType = variable->annotation().type;
|
||||
|
||||
while (dynamic_cast<ArrayType const*>(memberType))
|
||||
memberType = dynamic_cast<ArrayType const*>(memberType)->baseType();
|
||||
|
||||
if (StructType const* innerStruct = dynamic_cast<StructType const*>(memberType))
|
||||
if (
|
||||
innerStruct->m_recursive == true ||
|
||||
_cycleDetector.run(innerStruct->structDefinition())
|
||||
)
|
||||
util::BreadthFirstSearch<StructDefinition const*> breadthFirstSearch{{&m_struct}};
|
||||
breadthFirstSearch.run(
|
||||
[&](StructDefinition const* _struct, auto&& _addChild) {
|
||||
// Check that all members have interface types.
|
||||
// Return an error if at least one struct member does not have a type.
|
||||
// This might happen, for example, if the type of the member does not exist.
|
||||
for (ASTPointer<VariableDeclaration> const& variable: _struct->members())
|
||||
{
|
||||
m_recursive = true;
|
||||
if (_inLibrary && location() == DataLocation::Storage)
|
||||
continue;
|
||||
else
|
||||
// If the struct member does not have a type return false.
|
||||
// A TypeError is expected in this case.
|
||||
if (!variable->annotation().type)
|
||||
{
|
||||
result = TypeResult::err("Recursive structs can only be passed as storage pointers to libraries, not as memory objects to contract functions.");
|
||||
result = TypeResult::err("Invalid type!");
|
||||
breadthFirstSearch.abort();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto iType = memberType->interfaceType(_inLibrary);
|
||||
if (!iType.get())
|
||||
{
|
||||
solAssert(!iType.message().empty(), "Expected detailed error message!");
|
||||
result = iType;
|
||||
return;
|
||||
Type const* memberType = variable->annotation().type;
|
||||
|
||||
while (dynamic_cast<ArrayType const*>(memberType))
|
||||
memberType = dynamic_cast<ArrayType const*>(memberType)->baseType();
|
||||
|
||||
if (StructType const* innerStruct = dynamic_cast<StructType const*>(memberType))
|
||||
{
|
||||
if (innerStruct->recursive() && !(_inLibrary && location() == DataLocation::Storage))
|
||||
{
|
||||
result = TypeResult::err(
|
||||
"Recursive structs can only be passed as storage pointers to libraries, not as memory objects to contract functions."
|
||||
);
|
||||
breadthFirstSearch.abort();
|
||||
return;
|
||||
}
|
||||
else
|
||||
_addChild(&innerStruct->structDefinition());
|
||||
}
|
||||
else
|
||||
{
|
||||
auto iType = memberType->interfaceType(_inLibrary);
|
||||
if (!iType.get())
|
||||
{
|
||||
solAssert(!iType.message().empty(), "Expected detailed error message!");
|
||||
result = iType;
|
||||
breadthFirstSearch.abort();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
m_recursive = m_recursive.value() || (util::CycleDetector<StructDefinition>(visitor).run(structDefinition()) != nullptr);
|
||||
if (!result.message().empty())
|
||||
return result;
|
||||
|
||||
std::string const recursiveErrMsg = "Recursive type not allowed for public or external contract functions.";
|
||||
|
||||
if (_inLibrary)
|
||||
{
|
||||
if (!result.message().empty())
|
||||
m_interfaceType_library = result;
|
||||
else if (location() == DataLocation::Storage)
|
||||
m_interfaceType_library = this;
|
||||
else
|
||||
m_interfaceType_library = TypeProvider::withLocation(this, DataLocation::Memory, true);
|
||||
|
||||
if (m_recursive.value())
|
||||
m_interfaceType = TypeResult::err(recursiveErrMsg);
|
||||
|
||||
return *m_interfaceType_library;
|
||||
}
|
||||
|
||||
if (m_recursive.value())
|
||||
m_interfaceType = TypeResult::err(recursiveErrMsg);
|
||||
else if (!result.message().empty())
|
||||
m_interfaceType = result;
|
||||
if (location() == DataLocation::Storage)
|
||||
m_interfaceType_library = this;
|
||||
else
|
||||
m_interfaceType = TypeProvider::withLocation(this, DataLocation::Memory, true);
|
||||
m_interfaceType_library = TypeProvider::withLocation(this, DataLocation::Memory, true);
|
||||
return *m_interfaceType_library;
|
||||
}
|
||||
|
||||
return *m_interfaceType;
|
||||
bool StructType::recursive() const
|
||||
{
|
||||
solAssert(m_struct.annotation().recursive.has_value(), "Called StructType::recursive() before DeclarationTypeChecker.");
|
||||
return *m_struct.annotation().recursive;
|
||||
}
|
||||
|
||||
std::unique_ptr<ReferenceType> StructType::copyForLocation(DataLocation _location, bool _isPointer) const
|
||||
@@ -2644,21 +2658,11 @@ FunctionType::FunctionType(FunctionTypeName const& _typeName):
|
||||
for (auto const& t: _typeName.parameterTypes())
|
||||
{
|
||||
solAssert(t->annotation().type, "Type not set for parameter.");
|
||||
if (m_kind == Kind::External)
|
||||
solAssert(
|
||||
t->annotation().type->interfaceType(false).get(),
|
||||
"Internal type used as parameter for external function."
|
||||
);
|
||||
m_parameterTypes.push_back(t->annotation().type);
|
||||
}
|
||||
for (auto const& t: _typeName.returnParameterTypes())
|
||||
{
|
||||
solAssert(t->annotation().type, "Type not set for return parameter.");
|
||||
if (m_kind == Kind::External)
|
||||
solAssert(
|
||||
t->annotation().type->interfaceType(false).get(),
|
||||
"Internal type used as return parameter for external function."
|
||||
);
|
||||
m_returnParameterTypes.push_back(t->annotation().type);
|
||||
}
|
||||
|
||||
|
||||
+1
-10
@@ -934,15 +934,7 @@ public:
|
||||
Type const* encodingType() const override;
|
||||
TypeResult interfaceType(bool _inLibrary) const override;
|
||||
|
||||
bool recursive() const
|
||||
{
|
||||
if (m_recursive.has_value())
|
||||
return m_recursive.value();
|
||||
|
||||
interfaceType(false);
|
||||
|
||||
return m_recursive.value();
|
||||
}
|
||||
bool recursive() const;
|
||||
|
||||
std::unique_ptr<ReferenceType> copyForLocation(DataLocation _location, bool _isPointer) const override;
|
||||
|
||||
@@ -971,7 +963,6 @@ private:
|
||||
// Caches for interfaceType(bool)
|
||||
mutable std::optional<TypeResult> m_interfaceType;
|
||||
mutable std::optional<TypeResult> m_interfaceType_library;
|
||||
mutable std::optional<bool> m_recursive;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user