Add InlineArrayType to support literals conversion to statically and dynamically allocated arrays.

This commit is contained in:
wechman
2022-08-29 07:18:35 +02:00
parent 7bfec3ba70
commit 1ef2f60049
63 changed files with 1581 additions and 384 deletions
+5
View File
@@ -407,6 +407,11 @@ TupleType const* TypeProvider::tuple(vector<Type const*> members)
return createAndGet<TupleType>(move(members));
}
InlineArrayType const* TypeProvider::inlineArray(vector<Type const*> _members)
{
return createAndGet<InlineArrayType>(move(_members));
}
ReferenceType const* TypeProvider::withLocation(ReferenceType const* _type, DataLocation _location, bool _isPointer)
{
if (_type->location() == _location && _type->isPointer() == _isPointer)
+2
View File
@@ -109,6 +109,8 @@ public:
static TupleType const* emptyTuple() noexcept { return &m_emptyTuple; }
static InlineArrayType const* inlineArray(std::vector<Type const*> members);
static ReferenceType const* withLocation(ReferenceType const* _type, DataLocation _location, bool _isPointer);
/// @returns a copy of @a _type having the same location as this (and is not a pointer type)
+96
View File
@@ -323,6 +323,7 @@ Type const* Type::fullEncodingType(bool _inLibraryCall, bool _encoderV2, bool) c
if (_inLibraryCall && encodingType && encodingType->dataStoredIn(DataLocation::Storage))
return encodingType;
Type const* baseType = encodingType;
while (auto const* arrayType = dynamic_cast<ArrayType const*>(baseType))
{
baseType = arrayType->baseType();
@@ -2740,6 +2741,101 @@ Type const* TupleType::mobileType() const
return TypeProvider::tuple(move(mobiles));
}
BoolResult InlineArrayType::isImplicitlyConvertibleTo(Type const& _other) const
{
auto arrayType = dynamic_cast<ArrayType const*>(&_other);
if (!arrayType || arrayType->isByteArrayOrString())
return BoolResult::err("Array literal can not be converted to byte array or string.");
if (!arrayType->isDynamicallySized() && arrayType->length() != components().size())
return BoolResult::err(
"Number of components in array literal (" + to_string(components().size()) + ") " +
"does not match array size (" + to_string(arrayType->length().convert_to<unsigned>()) + ")."
);
for (Type const* c: components())
{
BoolResult result = c->isImplicitlyConvertibleTo(*arrayType->baseType());
if (!result)
return BoolResult::err(
"Invalid conversion from " + c->toString(false) +
" to " + arrayType->baseType()->toString(false) + "." +
(result.message().empty() ? "" : " ") + result.message()
);
}
return true;
}
string InlineArrayType::richIdentifier() const
{
return "t_inline_array" + identifierList(components());
}
bool InlineArrayType::operator==(Type const& _other) const
{
if (auto inlineArrayType = dynamic_cast<InlineArrayType const*>(&_other))
{
std::vector<Type const*> const& otherComponents = inlineArrayType->components();
if (m_components.size() != otherComponents.size())
return false;
for (unsigned i = 0; i < m_components.size(); ++i)
if (!(*m_components[i] == *otherComponents[i]))
return false;
return true;
}
else
return false;
}
string InlineArrayType::toString(bool _short) const
{
vector<string> result;
for (auto const& t: components())
result.push_back(t->toString(_short));
return "inline_array(" + util::joinHumanReadable(result) + ")";
}
u256 InlineArrayType::storageSize() const
{
solAssert(false, "Storage size of non-storable InlineArrayType type requested.");
}
Type const* InlineArrayType::mobileType() const
{
Type const* baseType = componentsCommonMobileType();
return
baseType ?
TypeProvider::array(DataLocation::Memory, baseType, components().size()) :
nullptr;
}
Type const* InlineArrayType::componentsCommonMobileType() const
{
solAssert(!m_components.empty(), "Empty array literal");
Type const* commonType = nullptr;
for (Type const* type: m_components)
commonType =
commonType ?
Type::commonType(commonType, type->mobileType()) :
type->mobileType();
return TypeProvider::withLocationIfReference(DataLocation::Memory, commonType);
}
vector<tuple<string, Type const*>> InlineArrayType::makeStackItems() const
{
vector<tuple<string, Type const*>> slots;
for (auto && [index, type]: components() | ranges::views::enumerate)
slots.emplace_back("component_" + std::to_string(index + 1), type);
return slots;
}
FunctionType::FunctionType(FunctionDefinition const& _function, Kind _kind):
m_kind(_kind),
m_stateMutability(_function.stateMutability()),
+36 -1
View File
@@ -175,7 +175,7 @@ public:
enum class Category
{
Address, Integer, RationalNumber, StringLiteral, Bool, FixedPoint, Array, ArraySlice,
FixedBytes, Contract, Struct, Function, Enum, UserDefinedValueType, Tuple,
FixedBytes, Contract, Struct, Function, Enum, UserDefinedValueType, Tuple, InlineArray,
Mapping, TypeType, Modifier, Magic, Module,
InaccessibleDynamic
};
@@ -1204,6 +1204,41 @@ private:
std::vector<Type const*> const m_components;
};
class InlineArrayType: public CompositeType
{
public:
explicit InlineArrayType(std::vector<Type const*> _types): m_components(std::move(_types)) {}
Category category() const override { return Category::InlineArray; }
BoolResult isImplicitlyConvertibleTo(Type const& _other) const override;
std::string richIdentifier() const override;
bool operator==(Type const& _other) const override;
TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; }
std::string toString(bool) const override;
bool canBeStored() const override { return false; }
u256 storageSize() const override;
bool hasSimpleZeroValueInMemory() const override { return false; }
Type const* mobileType() const override;
Type const* componentsCommonMobileType() const;
std::vector<Type const*> const& components() const { return m_components; }
protected:
std::vector<std::tuple<std::string, Type const*>> makeStackItems() const override;
std::vector<Type const*> decomposition() const override
{
// Currently calling InlineArrayType::decomposition() is not expected, because we cannot declare a variable of a InlineArrayType type.
// If that changes, before removing the solAssert, make sure the function does the right thing and is used properly.
// Note that different InlineArrayType members can have different data locations, so using decomposition() to check
// the tuple validity for a data location might require special care.
solUnimplemented("InlineArray decomposition is not expected.");
return m_components;
}
private:
std::vector<Type const*> const m_components;
};
/**
* The type of a function, identified by its (return) parameter types.
* @todo the return parameters should also have names, i.e. return parameters should be a struct