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

This commit is contained in:
wechman
2022-06-07 11:54:22 +02:00
parent 3948391ca8
commit 998488db3c
74 changed files with 1504 additions and 400 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)
+100
View File
@@ -315,6 +315,16 @@ Type const* Type::fullEncodingType(bool _inLibraryCall, bool _encoderV2, bool) c
if (_inLibraryCall && encodingType && encodingType->dataStoredIn(DataLocation::Storage))
return encodingType;
Type const* baseType = encodingType;
if (auto const inlineArray = dynamic_cast<InlineArrayType const*>(baseType))
{
baseType = TypeProvider::array(
DataLocation::Memory,
inlineArray->componentsCommonMobileType(),
inlineArray->components().size()
);
}
while (auto const* arrayType = dynamic_cast<ArrayType const*>(baseType))
{
baseType = arrayType->baseType();
@@ -2698,6 +2708,96 @@ 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.");
else
{
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)
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))
// TODO: raise issue - do not compare by pointer for tuple type
return components() == inlineArrayType->components();
else
return false;
}
string InlineArrayType::toString(bool _short) const
{
vector<string> result;
for (auto const& t: components())
result.push_back(t->toString(_short));
// TODO joinHumanReadable - is it fine to have a space here?
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
{
return TypeProvider::array(
DataLocation::Memory,
componentsCommonMobileType(),
components().size()
);
}
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
};
@@ -1200,6 +1200,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