mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Initial introduction of array slices with partial implementation for dynamic calldata arrays.
This commit is contained in:
parent
50ce3b0ac8
commit
4782c800ec
@ -14,6 +14,7 @@ Language Features:
|
|||||||
* Allow global enums and structs.
|
* Allow global enums and structs.
|
||||||
* Allow underscores as delimiters in hex strings.
|
* Allow underscores as delimiters in hex strings.
|
||||||
* Allow explicit conversions from ``address`` to ``address payable`` via ``payable(...)``.
|
* Allow explicit conversions from ``address`` to ``address payable`` via ``payable(...)``.
|
||||||
|
* Introduce syntax for array slices and implement them for dynamic calldata arrays.
|
||||||
|
|
||||||
|
|
||||||
Compiler Features:
|
Compiler Features:
|
||||||
|
@ -86,6 +86,7 @@ Expression
|
|||||||
= Expression ('++' | '--')
|
= Expression ('++' | '--')
|
||||||
| NewExpression
|
| NewExpression
|
||||||
| IndexAccess
|
| IndexAccess
|
||||||
|
| IndexRangeAccess
|
||||||
| MemberAccess
|
| MemberAccess
|
||||||
| FunctionCall
|
| FunctionCall
|
||||||
| '(' Expression ')'
|
| '(' Expression ')'
|
||||||
@ -123,6 +124,7 @@ FunctionCallArguments = '{' NameValueList? '}'
|
|||||||
NewExpression = 'new' TypeName
|
NewExpression = 'new' TypeName
|
||||||
MemberAccess = Expression '.' Identifier
|
MemberAccess = Expression '.' Identifier
|
||||||
IndexAccess = Expression '[' Expression? ']'
|
IndexAccess = Expression '[' Expression? ']'
|
||||||
|
IndexRangeAccess = Expression '[' Expression? ':' Expression? ']'
|
||||||
|
|
||||||
BooleanLiteral = 'true' | 'false'
|
BooleanLiteral = 'true' | 'false'
|
||||||
NumberLiteral = ( HexNumber | DecimalNumber ) (' ' NumberUnit)?
|
NumberLiteral = ( HexNumber | DecimalNumber ) (' ' NumberUnit)?
|
||||||
|
@ -136,19 +136,17 @@ TypePointers TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall c
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (arguments.size() >= 1)
|
if (arguments.size() >= 1)
|
||||||
{
|
if (
|
||||||
BoolResult result = type(*arguments.front())->isImplicitlyConvertibleTo(*TypeProvider::bytesMemory());
|
!type(*arguments.front())->isImplicitlyConvertibleTo(*TypeProvider::bytesMemory()) &&
|
||||||
|
!type(*arguments.front())->isImplicitlyConvertibleTo(*TypeProvider::bytesCalldata())
|
||||||
if (!result)
|
)
|
||||||
m_errorReporter.typeErrorConcatenateDescriptions(
|
m_errorReporter.typeError(
|
||||||
arguments.front()->location(),
|
arguments.front()->location(),
|
||||||
"Invalid type for argument in function call. "
|
"The first argument to \"abi.decode\" must be implicitly convertible to "
|
||||||
"Invalid implicit conversion from " +
|
"bytes memory or bytes calldata, but is of type " +
|
||||||
type(*arguments.front())->toString() +
|
type(*arguments.front())->toString() +
|
||||||
" to bytes memory requested.",
|
"."
|
||||||
result.message()
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
if (arguments.size() < 2)
|
if (arguments.size() < 2)
|
||||||
return {};
|
return {};
|
||||||
@ -2245,6 +2243,14 @@ bool TypeChecker::visit(IndexAccess const& _access)
|
|||||||
Expression const* index = _access.indexExpression();
|
Expression const* index = _access.indexExpression();
|
||||||
switch (baseType->category())
|
switch (baseType->category())
|
||||||
{
|
{
|
||||||
|
case Type::Category::ArraySlice:
|
||||||
|
{
|
||||||
|
auto const& arrayType = dynamic_cast<ArraySliceType const&>(*baseType).arrayType();
|
||||||
|
if (arrayType.location() != DataLocation::CallData || !arrayType.isDynamicallySized())
|
||||||
|
m_errorReporter.typeError(_access.location(), "Index access is only implemented for slices of dynamic calldata arrays.");
|
||||||
|
baseType = &arrayType;
|
||||||
|
[[fallthrough]];
|
||||||
|
}
|
||||||
case Type::Category::Array:
|
case Type::Category::Array:
|
||||||
{
|
{
|
||||||
ArrayType const& actualType = dynamic_cast<ArrayType const&>(*baseType);
|
ArrayType const& actualType = dynamic_cast<ArrayType const&>(*baseType);
|
||||||
@ -2341,6 +2347,50 @@ bool TypeChecker::visit(IndexAccess const& _access)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TypeChecker::visit(IndexRangeAccess const& _access)
|
||||||
|
{
|
||||||
|
_access.baseExpression().accept(*this);
|
||||||
|
|
||||||
|
bool isLValue = false; // TODO: set this correctly when implementing slices for memory and storage arrays
|
||||||
|
bool isPure = _access.baseExpression().annotation().isPure;
|
||||||
|
|
||||||
|
if (Expression const* start = _access.startExpression())
|
||||||
|
{
|
||||||
|
expectType(*start, *TypeProvider::uint256());
|
||||||
|
if (!start->annotation().isPure)
|
||||||
|
isPure = false;
|
||||||
|
}
|
||||||
|
if (Expression const* end = _access.endExpression())
|
||||||
|
{
|
||||||
|
expectType(*end, *TypeProvider::uint256());
|
||||||
|
if (!end->annotation().isPure)
|
||||||
|
isPure = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TypePointer exprType = type(_access.baseExpression());
|
||||||
|
if (exprType->category() == Type::Category::TypeType)
|
||||||
|
{
|
||||||
|
m_errorReporter.typeError(_access.location(), "Types cannot be sliced.");
|
||||||
|
_access.annotation().type = exprType;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayType const* arrayType = nullptr;
|
||||||
|
if (auto const* arraySlice = dynamic_cast<ArraySliceType const*>(exprType))
|
||||||
|
arrayType = &arraySlice->arrayType();
|
||||||
|
else if (!(arrayType = dynamic_cast<ArrayType const*>(exprType)))
|
||||||
|
m_errorReporter.fatalTypeError(_access.location(), "Index range access is only possible for arrays and array slices.");
|
||||||
|
|
||||||
|
|
||||||
|
if (arrayType->location() != DataLocation::CallData || !arrayType->isDynamicallySized())
|
||||||
|
m_errorReporter.typeError(_access.location(), "Index range access is only supported for dynamic calldata arrays.");
|
||||||
|
_access.annotation().type = TypeProvider::arraySlice(*arrayType);
|
||||||
|
_access.annotation().isLValue = isLValue;
|
||||||
|
_access.annotation().isPure = isPure;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool TypeChecker::visit(Identifier const& _identifier)
|
bool TypeChecker::visit(Identifier const& _identifier)
|
||||||
{
|
{
|
||||||
IdentifierAnnotation& annotation = _identifier.annotation();
|
IdentifierAnnotation& annotation = _identifier.annotation();
|
||||||
|
@ -136,6 +136,7 @@ private:
|
|||||||
void endVisit(NewExpression const& _newExpression) override;
|
void endVisit(NewExpression const& _newExpression) override;
|
||||||
bool visit(MemberAccess const& _memberAccess) override;
|
bool visit(MemberAccess const& _memberAccess) override;
|
||||||
bool visit(IndexAccess const& _indexAccess) override;
|
bool visit(IndexAccess const& _indexAccess) override;
|
||||||
|
bool visit(IndexRangeAccess const& _indexRangeAccess) override;
|
||||||
bool visit(Identifier const& _identifier) override;
|
bool visit(Identifier const& _identifier) override;
|
||||||
void endVisit(ElementaryTypeNameExpression const& _expr) override;
|
void endVisit(ElementaryTypeNameExpression const& _expr) override;
|
||||||
void endVisit(Literal const& _literal) override;
|
void endVisit(Literal const& _literal) override;
|
||||||
|
@ -413,6 +413,13 @@ void ViewPureChecker::endVisit(IndexAccess const& _indexAccess)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ViewPureChecker::endVisit(IndexRangeAccess const& _indexRangeAccess)
|
||||||
|
{
|
||||||
|
bool writes = _indexRangeAccess.annotation().lValueRequested;
|
||||||
|
if (_indexRangeAccess.baseExpression().annotation().type->dataStoredIn(DataLocation::Storage))
|
||||||
|
reportMutability(writes ? StateMutability::NonPayable : StateMutability::View, _indexRangeAccess.location());
|
||||||
|
}
|
||||||
|
|
||||||
void ViewPureChecker::endVisit(ModifierInvocation const& _modifier)
|
void ViewPureChecker::endVisit(ModifierInvocation const& _modifier)
|
||||||
{
|
{
|
||||||
solAssert(_modifier.name(), "");
|
solAssert(_modifier.name(), "");
|
||||||
|
@ -58,6 +58,7 @@ private:
|
|||||||
bool visit(MemberAccess const& _memberAccess) override;
|
bool visit(MemberAccess const& _memberAccess) override;
|
||||||
void endVisit(MemberAccess const& _memberAccess) override;
|
void endVisit(MemberAccess const& _memberAccess) override;
|
||||||
void endVisit(IndexAccess const& _indexAccess) override;
|
void endVisit(IndexAccess const& _indexAccess) override;
|
||||||
|
void endVisit(IndexRangeAccess const& _indexAccess) override;
|
||||||
void endVisit(ModifierInvocation const& _modifier) override;
|
void endVisit(ModifierInvocation const& _modifier) override;
|
||||||
void endVisit(FunctionCall const& _functionCall) override;
|
void endVisit(FunctionCall const& _functionCall) override;
|
||||||
void endVisit(InlineAssembly const& _inlineAssembly) override;
|
void endVisit(InlineAssembly const& _inlineAssembly) override;
|
||||||
|
@ -1642,6 +1642,32 @@ private:
|
|||||||
ASTPointer<Expression> m_index;
|
ASTPointer<Expression> m_index;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Index range access to an array. Example: a[2:3]
|
||||||
|
*/
|
||||||
|
class IndexRangeAccess: public Expression
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
IndexRangeAccess(
|
||||||
|
SourceLocation const& _location,
|
||||||
|
ASTPointer<Expression> const& _base,
|
||||||
|
ASTPointer<Expression> const& _start,
|
||||||
|
ASTPointer<Expression> const& _end
|
||||||
|
):
|
||||||
|
Expression(_location), m_base(_base), m_start(_start), m_end(_end) {}
|
||||||
|
void accept(ASTVisitor& _visitor) override;
|
||||||
|
void accept(ASTConstVisitor& _visitor) const override;
|
||||||
|
|
||||||
|
Expression const& baseExpression() const { return *m_base; }
|
||||||
|
Expression const* startExpression() const { return m_start.get(); }
|
||||||
|
Expression const* endExpression() const { return m_end.get(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
ASTPointer<Expression> m_base;
|
||||||
|
ASTPointer<Expression> m_start;
|
||||||
|
ASTPointer<Expression> m_end;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Primary expression, i.e. an expression that cannot be divided any further. Examples are literals
|
* Primary expression, i.e. an expression that cannot be divided any further. Examples are literals
|
||||||
* or variable references.
|
* or variable references.
|
||||||
|
@ -694,6 +694,18 @@ bool ASTJsonConverter::visit(IndexAccess const& _node)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ASTJsonConverter::visit(IndexRangeAccess const& _node)
|
||||||
|
{
|
||||||
|
std::vector<pair<string, Json::Value>> attributes = {
|
||||||
|
make_pair("baseExpression", toJson(_node.baseExpression())),
|
||||||
|
make_pair("startExpression", toJsonOrNull(_node.startExpression())),
|
||||||
|
make_pair("endExpression", toJsonOrNull(_node.endExpression())),
|
||||||
|
};
|
||||||
|
appendExpressionAttributes(attributes, _node.annotation());
|
||||||
|
setJsonNode(_node, "IndexRangeAccess", std::move(attributes));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(Identifier const& _node)
|
bool ASTJsonConverter::visit(Identifier const& _node)
|
||||||
{
|
{
|
||||||
Json::Value overloads(Json::arrayValue);
|
Json::Value overloads(Json::arrayValue);
|
||||||
|
@ -111,6 +111,7 @@ public:
|
|||||||
bool visit(NewExpression const& _node) override;
|
bool visit(NewExpression const& _node) override;
|
||||||
bool visit(MemberAccess const& _node) override;
|
bool visit(MemberAccess const& _node) override;
|
||||||
bool visit(IndexAccess const& _node) override;
|
bool visit(IndexAccess const& _node) override;
|
||||||
|
bool visit(IndexRangeAccess const& _node) override;
|
||||||
bool visit(Identifier const& _node) override;
|
bool visit(Identifier const& _node) override;
|
||||||
bool visit(ElementaryTypeNameExpression const& _node) override;
|
bool visit(ElementaryTypeNameExpression const& _node) override;
|
||||||
bool visit(Literal const& _node) override;
|
bool visit(Literal const& _node) override;
|
||||||
|
@ -87,6 +87,7 @@ public:
|
|||||||
virtual bool visit(NewExpression& _node) { return visitNode(_node); }
|
virtual bool visit(NewExpression& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(MemberAccess& _node) { return visitNode(_node); }
|
virtual bool visit(MemberAccess& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(IndexAccess& _node) { return visitNode(_node); }
|
virtual bool visit(IndexAccess& _node) { return visitNode(_node); }
|
||||||
|
virtual bool visit(IndexRangeAccess& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(Identifier& _node) { return visitNode(_node); }
|
virtual bool visit(Identifier& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(ElementaryTypeNameExpression& _node) { return visitNode(_node); }
|
virtual bool visit(ElementaryTypeNameExpression& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(Literal& _node) { return visitNode(_node); }
|
virtual bool visit(Literal& _node) { return visitNode(_node); }
|
||||||
@ -134,6 +135,7 @@ public:
|
|||||||
virtual void endVisit(NewExpression& _node) { endVisitNode(_node); }
|
virtual void endVisit(NewExpression& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(MemberAccess& _node) { endVisitNode(_node); }
|
virtual void endVisit(MemberAccess& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(IndexAccess& _node) { endVisitNode(_node); }
|
virtual void endVisit(IndexAccess& _node) { endVisitNode(_node); }
|
||||||
|
virtual void endVisit(IndexRangeAccess& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(Identifier& _node) { endVisitNode(_node); }
|
virtual void endVisit(Identifier& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(ElementaryTypeNameExpression& _node) { endVisitNode(_node); }
|
virtual void endVisit(ElementaryTypeNameExpression& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(Literal& _node) { endVisitNode(_node); }
|
virtual void endVisit(Literal& _node) { endVisitNode(_node); }
|
||||||
@ -194,6 +196,7 @@ public:
|
|||||||
virtual bool visit(NewExpression const& _node) { return visitNode(_node); }
|
virtual bool visit(NewExpression const& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(MemberAccess const& _node) { return visitNode(_node); }
|
virtual bool visit(MemberAccess const& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(IndexAccess const& _node) { return visitNode(_node); }
|
virtual bool visit(IndexAccess const& _node) { return visitNode(_node); }
|
||||||
|
virtual bool visit(IndexRangeAccess const& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(Identifier const& _node) { return visitNode(_node); }
|
virtual bool visit(Identifier const& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(ElementaryTypeNameExpression const& _node) { return visitNode(_node); }
|
virtual bool visit(ElementaryTypeNameExpression const& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(Literal const& _node) { return visitNode(_node); }
|
virtual bool visit(Literal const& _node) { return visitNode(_node); }
|
||||||
@ -241,6 +244,7 @@ public:
|
|||||||
virtual void endVisit(NewExpression const& _node) { endVisitNode(_node); }
|
virtual void endVisit(NewExpression const& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(MemberAccess const& _node) { endVisitNode(_node); }
|
virtual void endVisit(MemberAccess const& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(IndexAccess const& _node) { endVisitNode(_node); }
|
virtual void endVisit(IndexAccess const& _node) { endVisitNode(_node); }
|
||||||
|
virtual void endVisit(IndexRangeAccess const& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(Identifier const& _node) { endVisitNode(_node); }
|
virtual void endVisit(Identifier const& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(ElementaryTypeNameExpression const& _node) { endVisitNode(_node); }
|
virtual void endVisit(ElementaryTypeNameExpression const& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(Literal const& _node) { endVisitNode(_node); }
|
virtual void endVisit(Literal const& _node) { endVisitNode(_node); }
|
||||||
|
@ -783,6 +783,32 @@ void IndexAccess::accept(ASTConstVisitor& _visitor) const
|
|||||||
_visitor.endVisit(*this);
|
_visitor.endVisit(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IndexRangeAccess::accept(ASTVisitor& _visitor)
|
||||||
|
{
|
||||||
|
if (_visitor.visit(*this))
|
||||||
|
{
|
||||||
|
m_base->accept(_visitor);
|
||||||
|
if (m_start)
|
||||||
|
m_start->accept(_visitor);
|
||||||
|
if (m_end)
|
||||||
|
m_end->accept(_visitor);
|
||||||
|
}
|
||||||
|
_visitor.endVisit(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IndexRangeAccess::accept(ASTConstVisitor& _visitor) const
|
||||||
|
{
|
||||||
|
if (_visitor.visit(*this))
|
||||||
|
{
|
||||||
|
m_base->accept(_visitor);
|
||||||
|
if (m_start)
|
||||||
|
m_start->accept(_visitor);
|
||||||
|
if (m_end)
|
||||||
|
m_end->accept(_visitor);
|
||||||
|
}
|
||||||
|
_visitor.endVisit(*this);
|
||||||
|
}
|
||||||
|
|
||||||
void Identifier::accept(ASTVisitor& _visitor)
|
void Identifier::accept(ASTVisitor& _visitor)
|
||||||
{
|
{
|
||||||
_visitor.visit(*this);
|
_visitor.visit(*this);
|
||||||
|
@ -31,6 +31,7 @@ InaccessibleDynamicType const TypeProvider::m_inaccessibleDynamic{};
|
|||||||
/// they rely on `byte` being available which we cannot guarantee in the static init context.
|
/// they rely on `byte` being available which we cannot guarantee in the static init context.
|
||||||
unique_ptr<ArrayType> TypeProvider::m_bytesStorage;
|
unique_ptr<ArrayType> TypeProvider::m_bytesStorage;
|
||||||
unique_ptr<ArrayType> TypeProvider::m_bytesMemory;
|
unique_ptr<ArrayType> TypeProvider::m_bytesMemory;
|
||||||
|
unique_ptr<ArrayType> TypeProvider::m_bytesCalldata;
|
||||||
unique_ptr<ArrayType> TypeProvider::m_stringStorage;
|
unique_ptr<ArrayType> TypeProvider::m_stringStorage;
|
||||||
unique_ptr<ArrayType> TypeProvider::m_stringMemory;
|
unique_ptr<ArrayType> TypeProvider::m_stringMemory;
|
||||||
|
|
||||||
@ -177,6 +178,7 @@ void TypeProvider::reset()
|
|||||||
clearCache(m_inaccessibleDynamic);
|
clearCache(m_inaccessibleDynamic);
|
||||||
clearCache(m_bytesStorage);
|
clearCache(m_bytesStorage);
|
||||||
clearCache(m_bytesMemory);
|
clearCache(m_bytesMemory);
|
||||||
|
clearCache(m_bytesCalldata);
|
||||||
clearCache(m_stringStorage);
|
clearCache(m_stringStorage);
|
||||||
clearCache(m_stringMemory);
|
clearCache(m_stringMemory);
|
||||||
clearCache(m_emptyTuple);
|
clearCache(m_emptyTuple);
|
||||||
@ -314,6 +316,13 @@ ArrayType const* TypeProvider::bytesMemory()
|
|||||||
return m_bytesMemory.get();
|
return m_bytesMemory.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ArrayType const* TypeProvider::bytesCalldata()
|
||||||
|
{
|
||||||
|
if (!m_bytesCalldata)
|
||||||
|
m_bytesCalldata = make_unique<ArrayType>(DataLocation::CallData, false);
|
||||||
|
return m_bytesCalldata.get();
|
||||||
|
}
|
||||||
|
|
||||||
ArrayType const* TypeProvider::stringStorage()
|
ArrayType const* TypeProvider::stringStorage()
|
||||||
{
|
{
|
||||||
if (!m_stringStorage)
|
if (!m_stringStorage)
|
||||||
@ -500,6 +509,11 @@ ArrayType const* TypeProvider::array(DataLocation _location, Type const* _baseTy
|
|||||||
return createAndGet<ArrayType>(_location, _baseType, _length);
|
return createAndGet<ArrayType>(_location, _baseType, _length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ArraySliceType const* TypeProvider::arraySlice(ArrayType const& _arrayType)
|
||||||
|
{
|
||||||
|
return createAndGet<ArraySliceType>(_arrayType);
|
||||||
|
}
|
||||||
|
|
||||||
ContractType const* TypeProvider::contract(ContractDefinition const& _contractDef, bool _isSuper)
|
ContractType const* TypeProvider::contract(ContractDefinition const& _contractDef, bool _isSuper)
|
||||||
{
|
{
|
||||||
return createAndGet<ContractType>(_contractDef, _isSuper);
|
return createAndGet<ContractType>(_contractDef, _isSuper);
|
||||||
|
@ -68,6 +68,7 @@ public:
|
|||||||
|
|
||||||
static ArrayType const* bytesStorage();
|
static ArrayType const* bytesStorage();
|
||||||
static ArrayType const* bytesMemory();
|
static ArrayType const* bytesMemory();
|
||||||
|
static ArrayType const* bytesCalldata();
|
||||||
static ArrayType const* stringStorage();
|
static ArrayType const* stringStorage();
|
||||||
static ArrayType const* stringMemory();
|
static ArrayType const* stringMemory();
|
||||||
|
|
||||||
@ -80,6 +81,8 @@ public:
|
|||||||
/// Constructor for a fixed-size array type ("type[20]")
|
/// Constructor for a fixed-size array type ("type[20]")
|
||||||
static ArrayType const* array(DataLocation _location, Type const* _baseType, u256 const& _length);
|
static ArrayType const* array(DataLocation _location, Type const* _baseType, u256 const& _length);
|
||||||
|
|
||||||
|
static ArraySliceType const* arraySlice(ArrayType const& _arrayType);
|
||||||
|
|
||||||
static AddressType const* payableAddress() noexcept { return &m_payableAddress; }
|
static AddressType const* payableAddress() noexcept { return &m_payableAddress; }
|
||||||
static AddressType const* address() noexcept { return &m_address; }
|
static AddressType const* address() noexcept { return &m_address; }
|
||||||
|
|
||||||
@ -203,6 +206,7 @@ private:
|
|||||||
/// These are lazy-initialized because they depend on `byte` being available.
|
/// These are lazy-initialized because they depend on `byte` being available.
|
||||||
static std::unique_ptr<ArrayType> m_bytesStorage;
|
static std::unique_ptr<ArrayType> m_bytesStorage;
|
||||||
static std::unique_ptr<ArrayType> m_bytesMemory;
|
static std::unique_ptr<ArrayType> m_bytesMemory;
|
||||||
|
static std::unique_ptr<ArrayType> m_bytesCalldata;
|
||||||
static std::unique_ptr<ArrayType> m_stringStorage;
|
static std::unique_ptr<ArrayType> m_stringStorage;
|
||||||
static std::unique_ptr<ArrayType> m_stringMemory;
|
static std::unique_ptr<ArrayType> m_stringMemory;
|
||||||
|
|
||||||
|
@ -1869,6 +1869,30 @@ std::unique_ptr<ReferenceType> ArrayType::copyForLocation(DataLocation _location
|
|||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BoolResult ArraySliceType::isImplicitlyConvertibleTo(Type const& _other) const
|
||||||
|
{
|
||||||
|
if (m_arrayType.location() == DataLocation::CallData && m_arrayType.isDynamicallySized() && m_arrayType == _other)
|
||||||
|
return true;
|
||||||
|
return (*this) == _other;
|
||||||
|
}
|
||||||
|
|
||||||
|
string ArraySliceType::richIdentifier() const
|
||||||
|
{
|
||||||
|
return m_arrayType.richIdentifier() + "_slice";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ArraySliceType::operator==(Type const& _other) const
|
||||||
|
{
|
||||||
|
if (auto const* other = dynamic_cast<ArraySliceType const*>(&_other))
|
||||||
|
return m_arrayType == other->m_arrayType;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string ArraySliceType::toString(bool _short) const
|
||||||
|
{
|
||||||
|
return m_arrayType.toString(_short) + " slice";
|
||||||
|
}
|
||||||
|
|
||||||
string ContractType::richIdentifier() const
|
string ContractType::richIdentifier() const
|
||||||
{
|
{
|
||||||
return (m_super ? "t_super" : "t_contract") + parenthesizeUserIdentifier(m_contract.name()) + to_string(m_contract.id());
|
return (m_super ? "t_super" : "t_contract") + parenthesizeUserIdentifier(m_contract.name()) + to_string(m_contract.id());
|
||||||
|
@ -161,7 +161,7 @@ public:
|
|||||||
|
|
||||||
enum class Category
|
enum class Category
|
||||||
{
|
{
|
||||||
Address, Integer, RationalNumber, StringLiteral, Bool, FixedPoint, Array,
|
Address, Integer, RationalNumber, StringLiteral, Bool, FixedPoint, Array, ArraySlice,
|
||||||
FixedBytes, Contract, Struct, Function, Enum, Tuple,
|
FixedBytes, Contract, Struct, Function, Enum, Tuple,
|
||||||
Mapping, TypeType, Modifier, Magic, Module,
|
Mapping, TypeType, Modifier, Magic, Module,
|
||||||
InaccessibleDynamic
|
InaccessibleDynamic
|
||||||
@ -773,6 +773,35 @@ private:
|
|||||||
mutable boost::optional<TypeResult> m_interfaceType_library;
|
mutable boost::optional<TypeResult> m_interfaceType_library;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ArraySliceType: public ReferenceType
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ArraySliceType(ArrayType const& _arrayType): ReferenceType(_arrayType.location()), m_arrayType(_arrayType) {}
|
||||||
|
Category category() const override { return Category::ArraySlice; }
|
||||||
|
|
||||||
|
BoolResult isImplicitlyConvertibleTo(Type const& _other) const override;
|
||||||
|
std::string richIdentifier() const override;
|
||||||
|
bool operator==(Type const& _other) const override;
|
||||||
|
unsigned calldataEncodedSize(bool) const override { solAssert(false, ""); }
|
||||||
|
unsigned calldataEncodedTailSize() const override { return 32; }
|
||||||
|
bool isDynamicallySized() const override { return true; }
|
||||||
|
bool isDynamicallyEncoded() const override { return true; }
|
||||||
|
bool canLiveOutsideStorage() const override { return m_arrayType.canLiveOutsideStorage(); }
|
||||||
|
unsigned sizeOnStack() const override { return 2; }
|
||||||
|
std::string toString(bool _short) const override;
|
||||||
|
|
||||||
|
/// @returns true if this is valid to be stored in calldata
|
||||||
|
bool validForCalldata() const { return m_arrayType.validForCalldata(); }
|
||||||
|
|
||||||
|
ArrayType const& arrayType() const { return m_arrayType; }
|
||||||
|
u256 memoryDataSize() const override { solAssert(false, ""); }
|
||||||
|
|
||||||
|
std::unique_ptr<ReferenceType> copyForLocation(DataLocation, bool) const override { solAssert(false, ""); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
ArrayType const& m_arrayType;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of a contract instance or library, there is one distinct type for each contract definition.
|
* The type of a contract instance or library, there is one distinct type for each contract definition.
|
||||||
*/
|
*/
|
||||||
|
@ -981,6 +981,17 @@ void CompilerUtils::convertType(
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case Type::Category::ArraySlice:
|
||||||
|
{
|
||||||
|
auto& typeOnStack = dynamic_cast<ArraySliceType const&>(_typeOnStack);
|
||||||
|
solAssert(_targetType == typeOnStack.arrayType(), "");
|
||||||
|
solUnimplementedAssert(
|
||||||
|
typeOnStack.arrayType().location() == DataLocation::CallData &&
|
||||||
|
typeOnStack.arrayType().isDynamicallySized(),
|
||||||
|
""
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case Type::Category::Struct:
|
case Type::Category::Struct:
|
||||||
{
|
{
|
||||||
solAssert(targetTypeCategory == stackTypeCategory, "");
|
solAssert(targetTypeCategory == stackTypeCategory, "");
|
||||||
|
@ -1079,10 +1079,14 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
else
|
else
|
||||||
targetTypes = TypePointers{_functionCall.annotation().type};
|
targetTypes = TypePointers{_functionCall.annotation().type};
|
||||||
if (
|
if (
|
||||||
*firstArgType == ArrayType(DataLocation::CallData) ||
|
auto referenceType = dynamic_cast<ReferenceType const*>(firstArgType);
|
||||||
*firstArgType == ArrayType(DataLocation::CallData, true)
|
referenceType && referenceType->dataStoredIn(DataLocation::CallData)
|
||||||
)
|
)
|
||||||
|
{
|
||||||
|
solAssert(referenceType->isImplicitlyConvertibleTo(*TypeProvider::bytesCalldata()), "");
|
||||||
|
utils().convertType(*referenceType, *TypeProvider::bytesCalldata());
|
||||||
utils().abiDecode(targetTypes, false);
|
utils().abiDecode(targetTypes, false);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
utils().convertType(*firstArgType, *TypeProvider::bytesMemory());
|
utils().convertType(*firstArgType, *TypeProvider::bytesMemory());
|
||||||
@ -1503,90 +1507,149 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
|
|||||||
|
|
||||||
Type const& baseType = *_indexAccess.baseExpression().annotation().type;
|
Type const& baseType = *_indexAccess.baseExpression().annotation().type;
|
||||||
|
|
||||||
if (baseType.category() == Type::Category::Mapping)
|
switch (baseType.category())
|
||||||
{
|
{
|
||||||
// stack: storage_base_ref
|
case Type::Category::Mapping:
|
||||||
TypePointer keyType = dynamic_cast<MappingType const&>(baseType).keyType();
|
|
||||||
solAssert(_indexAccess.indexExpression(), "Index expression expected.");
|
|
||||||
if (keyType->isDynamicallySized())
|
|
||||||
{
|
{
|
||||||
_indexAccess.indexExpression()->accept(*this);
|
// stack: storage_base_ref
|
||||||
utils().fetchFreeMemoryPointer();
|
TypePointer keyType = dynamic_cast<MappingType const&>(baseType).keyType();
|
||||||
// stack: base index mem
|
solAssert(_indexAccess.indexExpression(), "Index expression expected.");
|
||||||
// note: the following operations must not allocate memory!
|
if (keyType->isDynamicallySized())
|
||||||
utils().packedEncode(
|
|
||||||
TypePointers{_indexAccess.indexExpression()->annotation().type},
|
|
||||||
TypePointers{keyType}
|
|
||||||
);
|
|
||||||
m_context << Instruction::SWAP1;
|
|
||||||
utils().storeInMemoryDynamic(*TypeProvider::uint256());
|
|
||||||
utils().toSizeAfterFreeMemoryPointer();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_context << u256(0); // memory position
|
|
||||||
appendExpressionCopyToMemory(*keyType, *_indexAccess.indexExpression());
|
|
||||||
m_context << Instruction::SWAP1;
|
|
||||||
solAssert(CompilerUtils::freeMemoryPointer >= 0x40, "");
|
|
||||||
utils().storeInMemoryDynamic(*TypeProvider::uint256());
|
|
||||||
m_context << u256(0);
|
|
||||||
}
|
|
||||||
m_context << Instruction::KECCAK256;
|
|
||||||
m_context << u256(0);
|
|
||||||
setLValueToStorageItem(_indexAccess);
|
|
||||||
}
|
|
||||||
else if (baseType.category() == Type::Category::Array)
|
|
||||||
{
|
|
||||||
ArrayType const& arrayType = dynamic_cast<ArrayType const&>(baseType);
|
|
||||||
solAssert(_indexAccess.indexExpression(), "Index expression expected.");
|
|
||||||
|
|
||||||
acceptAndConvert(*_indexAccess.indexExpression(), *TypeProvider::uint256(), true);
|
|
||||||
// stack layout: <base_ref> [<length>] <index>
|
|
||||||
switch (arrayType.location())
|
|
||||||
{
|
|
||||||
case DataLocation::Storage:
|
|
||||||
ArrayUtils(m_context).accessIndex(arrayType);
|
|
||||||
if (arrayType.isByteArray())
|
|
||||||
{
|
{
|
||||||
solAssert(!arrayType.isString(), "Index access to string is not allowed.");
|
_indexAccess.indexExpression()->accept(*this);
|
||||||
setLValue<StorageByteArrayElement>(_indexAccess);
|
utils().fetchFreeMemoryPointer();
|
||||||
|
// stack: base index mem
|
||||||
|
// note: the following operations must not allocate memory!
|
||||||
|
utils().packedEncode(
|
||||||
|
TypePointers{_indexAccess.indexExpression()->annotation().type},
|
||||||
|
TypePointers{keyType}
|
||||||
|
);
|
||||||
|
m_context << Instruction::SWAP1;
|
||||||
|
utils().storeInMemoryDynamic(*TypeProvider::uint256());
|
||||||
|
utils().toSizeAfterFreeMemoryPointer();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
setLValueToStorageItem(_indexAccess);
|
{
|
||||||
break;
|
m_context << u256(0); // memory position
|
||||||
case DataLocation::Memory:
|
appendExpressionCopyToMemory(*keyType, *_indexAccess.indexExpression());
|
||||||
ArrayUtils(m_context).accessIndex(arrayType);
|
m_context << Instruction::SWAP1;
|
||||||
setLValue<MemoryItem>(_indexAccess, *_indexAccess.annotation().type, !arrayType.isByteArray());
|
solAssert(CompilerUtils::freeMemoryPointer >= 0x40, "");
|
||||||
break;
|
utils().storeInMemoryDynamic(*TypeProvider::uint256());
|
||||||
case DataLocation::CallData:
|
m_context << u256(0);
|
||||||
ArrayUtils(m_context).accessCallDataArrayElement(arrayType);
|
}
|
||||||
|
m_context << Instruction::KECCAK256;
|
||||||
|
m_context << u256(0);
|
||||||
|
setLValueToStorageItem(_indexAccess);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
case Type::Category::ArraySlice:
|
||||||
else if (baseType.category() == Type::Category::FixedBytes)
|
{
|
||||||
{
|
auto const& arrayType = dynamic_cast<ArraySliceType const&>(baseType).arrayType();
|
||||||
FixedBytesType const& fixedBytesType = dynamic_cast<FixedBytesType const&>(baseType);
|
solAssert(arrayType.location() == DataLocation::CallData && arrayType.isDynamicallySized(), "");
|
||||||
solAssert(_indexAccess.indexExpression(), "Index expression expected.");
|
solAssert(_indexAccess.indexExpression(), "Index expression expected.");
|
||||||
|
|
||||||
acceptAndConvert(*_indexAccess.indexExpression(), *TypeProvider::uint256(), true);
|
acceptAndConvert(*_indexAccess.indexExpression(), *TypeProvider::uint256(), true);
|
||||||
// stack layout: <value> <index>
|
ArrayUtils(m_context).accessCallDataArrayElement(arrayType);
|
||||||
// check out-of-bounds access
|
break;
|
||||||
m_context << u256(fixedBytesType.numBytes());
|
|
||||||
m_context << Instruction::DUP2 << Instruction::LT << Instruction::ISZERO;
|
|
||||||
// out-of-bounds access throws exception
|
|
||||||
m_context.appendConditionalInvalid();
|
|
||||||
|
|
||||||
m_context << Instruction::BYTE;
|
}
|
||||||
utils().leftShiftNumberOnStack(256 - 8);
|
case Type::Category::Array:
|
||||||
}
|
{
|
||||||
else if (baseType.category() == Type::Category::TypeType)
|
ArrayType const& arrayType = dynamic_cast<ArrayType const&>(baseType);
|
||||||
{
|
solAssert(_indexAccess.indexExpression(), "Index expression expected.");
|
||||||
solAssert(baseType.sizeOnStack() == 0, "");
|
|
||||||
solAssert(_indexAccess.annotation().type->sizeOnStack() == 0, "");
|
acceptAndConvert(*_indexAccess.indexExpression(), *TypeProvider::uint256(), true);
|
||||||
// no-op - this seems to be a lone array type (`structType[];`)
|
// stack layout: <base_ref> [<length>] <index>
|
||||||
|
switch (arrayType.location())
|
||||||
|
{
|
||||||
|
case DataLocation::Storage:
|
||||||
|
ArrayUtils(m_context).accessIndex(arrayType);
|
||||||
|
if (arrayType.isByteArray())
|
||||||
|
{
|
||||||
|
solAssert(!arrayType.isString(), "Index access to string is not allowed.");
|
||||||
|
setLValue<StorageByteArrayElement>(_indexAccess);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
setLValueToStorageItem(_indexAccess);
|
||||||
|
break;
|
||||||
|
case DataLocation::Memory:
|
||||||
|
ArrayUtils(m_context).accessIndex(arrayType);
|
||||||
|
setLValue<MemoryItem>(_indexAccess, *_indexAccess.annotation().type, !arrayType.isByteArray());
|
||||||
|
break;
|
||||||
|
case DataLocation::CallData:
|
||||||
|
ArrayUtils(m_context).accessCallDataArrayElement(arrayType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Type::Category::FixedBytes:
|
||||||
|
{
|
||||||
|
FixedBytesType const& fixedBytesType = dynamic_cast<FixedBytesType const&>(baseType);
|
||||||
|
solAssert(_indexAccess.indexExpression(), "Index expression expected.");
|
||||||
|
|
||||||
|
acceptAndConvert(*_indexAccess.indexExpression(), *TypeProvider::uint256(), true);
|
||||||
|
// stack layout: <value> <index>
|
||||||
|
// check out-of-bounds access
|
||||||
|
m_context << u256(fixedBytesType.numBytes());
|
||||||
|
m_context << Instruction::DUP2 << Instruction::LT << Instruction::ISZERO;
|
||||||
|
// out-of-bounds access throws exception
|
||||||
|
m_context.appendConditionalInvalid();
|
||||||
|
|
||||||
|
m_context << Instruction::BYTE;
|
||||||
|
utils().leftShiftNumberOnStack(256 - 8);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Type::Category::TypeType:
|
||||||
|
{
|
||||||
|
solAssert(baseType.sizeOnStack() == 0, "");
|
||||||
|
solAssert(_indexAccess.annotation().type->sizeOnStack() == 0, "");
|
||||||
|
// no-op - this seems to be a lone array type (`structType[];`)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
solAssert(false, "Index access only allowed for mappings or arrays.");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExpressionCompiler::visit(IndexRangeAccess const& _indexAccess)
|
||||||
|
{
|
||||||
|
CompilerContext::LocationSetter locationSetter(m_context, _indexAccess);
|
||||||
|
_indexAccess.baseExpression().accept(*this);
|
||||||
|
|
||||||
|
Type const& baseType = *_indexAccess.baseExpression().annotation().type;
|
||||||
|
|
||||||
|
ArrayType const *arrayType = dynamic_cast<ArrayType const*>(&baseType);
|
||||||
|
if (!arrayType)
|
||||||
|
if (ArraySliceType const* sliceType = dynamic_cast<ArraySliceType const*>(&baseType))
|
||||||
|
arrayType = &sliceType->arrayType();
|
||||||
|
|
||||||
|
solAssert(arrayType, "");
|
||||||
|
solUnimplementedAssert(arrayType->location() == DataLocation::CallData && arrayType->isDynamicallySized(), "");
|
||||||
|
|
||||||
|
if (_indexAccess.startExpression())
|
||||||
|
acceptAndConvert(*_indexAccess.startExpression(), *TypeProvider::uint256());
|
||||||
else
|
else
|
||||||
solAssert(false, "Index access only allowed for mappings or arrays.");
|
m_context << u256(0);
|
||||||
|
if (_indexAccess.endExpression())
|
||||||
|
acceptAndConvert(*_indexAccess.endExpression(), *TypeProvider::uint256());
|
||||||
|
else
|
||||||
|
m_context << Instruction::DUP2;
|
||||||
|
|
||||||
|
m_context.appendInlineAssembly(
|
||||||
|
Whiskers(R"({
|
||||||
|
if gt(sliceStart, sliceEnd) { revert(0, 0) }
|
||||||
|
if gt(sliceEnd, length) { revert(0, 0) }
|
||||||
|
|
||||||
|
offset := add(offset, mul(sliceStart, <stride>))
|
||||||
|
length := sub(sliceEnd, sliceStart)
|
||||||
|
})")("stride", toString(arrayType->calldataStride())).render(),
|
||||||
|
{"offset", "length", "sliceStart", "sliceEnd"}
|
||||||
|
);
|
||||||
|
|
||||||
|
m_context << Instruction::POP << Instruction::POP;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -80,6 +80,7 @@ private:
|
|||||||
bool visit(NewExpression const& _newExpression) override;
|
bool visit(NewExpression const& _newExpression) override;
|
||||||
bool visit(MemberAccess const& _memberAccess) override;
|
bool visit(MemberAccess const& _memberAccess) override;
|
||||||
bool visit(IndexAccess const& _indexAccess) override;
|
bool visit(IndexAccess const& _indexAccess) override;
|
||||||
|
bool visit(IndexRangeAccess const& _indexAccess) override;
|
||||||
void endVisit(Identifier const& _identifier) override;
|
void endVisit(Identifier const& _identifier) override;
|
||||||
void endVisit(Literal const& _literal) override;
|
void endVisit(Literal const& _literal) override;
|
||||||
|
|
||||||
|
@ -823,11 +823,6 @@ string YulUtilFunctions::memoryArrayIndexAccessFunction(ArrayType const& _type)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::calldataArrayIndexAccessFunction(ArrayType const& /*_type*/)
|
|
||||||
{
|
|
||||||
solUnimplemented("Calldata arrays not yet implemented!");
|
|
||||||
}
|
|
||||||
|
|
||||||
string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type)
|
string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type)
|
||||||
{
|
{
|
||||||
solAssert(!_type.isByteArray(), "");
|
solAssert(!_type.isByteArray(), "");
|
||||||
|
@ -941,6 +941,11 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
|
|||||||
solAssert(false, "Index access only allowed for mappings or arrays.");
|
solAssert(false, "Index access only allowed for mappings or arrays.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IRGeneratorForStatements::endVisit(IndexRangeAccess const&)
|
||||||
|
{
|
||||||
|
solUnimplementedAssert(false, "Index range accesses not yet implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
void IRGeneratorForStatements::endVisit(Identifier const& _identifier)
|
void IRGeneratorForStatements::endVisit(Identifier const& _identifier)
|
||||||
{
|
{
|
||||||
Declaration const* declaration = _identifier.annotation().referencedDeclaration;
|
Declaration const* declaration = _identifier.annotation().referencedDeclaration;
|
||||||
|
@ -63,6 +63,7 @@ public:
|
|||||||
void endVisit(MemberAccess const& _memberAccess) override;
|
void endVisit(MemberAccess const& _memberAccess) override;
|
||||||
bool visit(InlineAssembly const& _inlineAsm) override;
|
bool visit(InlineAssembly const& _inlineAsm) override;
|
||||||
void endVisit(IndexAccess const& _indexAccess) override;
|
void endVisit(IndexAccess const& _indexAccess) override;
|
||||||
|
void endVisit(IndexRangeAccess const& _indexRangeAccess) override;
|
||||||
void endVisit(Identifier const& _identifier) override;
|
void endVisit(Identifier const& _identifier) override;
|
||||||
bool visit(Literal const& _literal) override;
|
bool visit(Literal const& _literal) override;
|
||||||
|
|
||||||
|
@ -767,6 +767,15 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess)
|
|||||||
m_uninterpretedTerms.insert(&_indexAccess);
|
m_uninterpretedTerms.insert(&_indexAccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SMTEncoder::endVisit(IndexRangeAccess const& _indexRangeAccess)
|
||||||
|
{
|
||||||
|
createExpr(_indexRangeAccess);
|
||||||
|
m_errorReporter.warning(
|
||||||
|
_indexRangeAccess.location(),
|
||||||
|
"Assertion checker does not yet implement this expression."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void SMTEncoder::arrayAssignment()
|
void SMTEncoder::arrayAssignment()
|
||||||
{
|
{
|
||||||
m_arrayAssignmentHappened = true;
|
m_arrayAssignmentHappened = true;
|
||||||
|
@ -86,6 +86,7 @@ protected:
|
|||||||
void endVisit(Return const& _node) override;
|
void endVisit(Return const& _node) override;
|
||||||
bool visit(MemberAccess const& _node) override;
|
bool visit(MemberAccess const& _node) override;
|
||||||
void endVisit(IndexAccess const& _node) override;
|
void endVisit(IndexAccess const& _node) override;
|
||||||
|
void endVisit(IndexRangeAccess const& _node) override;
|
||||||
bool visit(InlineAssembly const& _node) override;
|
bool visit(InlineAssembly const& _node) override;
|
||||||
|
|
||||||
/// Do not visit subtree if node is a RationalNumber.
|
/// Do not visit subtree if node is a RationalNumber.
|
||||||
|
@ -1607,11 +1607,24 @@ ASTPointer<Expression> Parser::parseLeftHandSideExpression(
|
|||||||
{
|
{
|
||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
ASTPointer<Expression> index;
|
ASTPointer<Expression> index;
|
||||||
if (m_scanner->currentToken() != Token::RBrack)
|
ASTPointer<Expression> endIndex;
|
||||||
|
if (m_scanner->currentToken() != Token::RBrack && m_scanner->currentToken() != Token::Colon)
|
||||||
index = parseExpression();
|
index = parseExpression();
|
||||||
nodeFactory.markEndPosition();
|
if (m_scanner->currentToken() == Token::Colon)
|
||||||
expectToken(Token::RBrack);
|
{
|
||||||
expression = nodeFactory.createNode<IndexAccess>(expression, index);
|
expectToken(Token::Colon);
|
||||||
|
if (m_scanner->currentToken() != Token::RBrack)
|
||||||
|
endIndex = parseExpression();
|
||||||
|
nodeFactory.markEndPosition();
|
||||||
|
expectToken(Token::RBrack);
|
||||||
|
expression = nodeFactory.createNode<IndexRangeAccess>(expression, index, endIndex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nodeFactory.markEndPosition();
|
||||||
|
expectToken(Token::RBrack);
|
||||||
|
expression = nodeFactory.createNode<IndexAccess>(expression, index);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Token::Period:
|
case Token::Period:
|
||||||
@ -1861,12 +1874,25 @@ Parser::IndexAccessedPath Parser::parseIndexAccessedPath()
|
|||||||
{
|
{
|
||||||
expectToken(Token::LBrack);
|
expectToken(Token::LBrack);
|
||||||
ASTPointer<Expression> index;
|
ASTPointer<Expression> index;
|
||||||
if (m_scanner->currentToken() != Token::RBrack)
|
if (m_scanner->currentToken() != Token::RBrack && m_scanner->currentToken() != Token::Colon)
|
||||||
index = parseExpression();
|
index = parseExpression();
|
||||||
SourceLocation indexLocation = iap.path.front()->location();
|
SourceLocation indexLocation = iap.path.front()->location();
|
||||||
indexLocation.end = endPosition();
|
if (m_scanner->currentToken() == Token::Colon)
|
||||||
iap.indices.emplace_back(index, indexLocation);
|
{
|
||||||
expectToken(Token::RBrack);
|
expectToken(Token::Colon);
|
||||||
|
ASTPointer<Expression> endIndex;
|
||||||
|
if (m_scanner->currentToken() != Token::RBrack)
|
||||||
|
endIndex = parseExpression();
|
||||||
|
indexLocation.end = endPosition();
|
||||||
|
iap.indices.emplace_back(IndexAccessedPath::Index{index, {endIndex}, indexLocation});
|
||||||
|
expectToken(Token::RBrack);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
indexLocation.end = endPosition();
|
||||||
|
iap.indices.emplace_back(IndexAccessedPath::Index{index, {}, indexLocation});
|
||||||
|
expectToken(Token::RBrack);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return iap;
|
return iap;
|
||||||
@ -1898,8 +1924,10 @@ ASTPointer<TypeName> Parser::typeNameFromIndexAccessStructure(Parser::IndexAcces
|
|||||||
}
|
}
|
||||||
for (auto const& lengthExpression: _iap.indices)
|
for (auto const& lengthExpression: _iap.indices)
|
||||||
{
|
{
|
||||||
nodeFactory.setLocation(lengthExpression.second);
|
if (lengthExpression.end)
|
||||||
type = nodeFactory.createNode<ArrayTypeName>(type, lengthExpression.first);
|
parserError(lengthExpression.location, "Expected array length expression.");
|
||||||
|
nodeFactory.setLocation(lengthExpression.location);
|
||||||
|
type = nodeFactory.createNode<ArrayTypeName>(type, lengthExpression.start);
|
||||||
}
|
}
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
@ -1927,8 +1955,11 @@ ASTPointer<Expression> Parser::expressionFromIndexAccessStructure(
|
|||||||
}
|
}
|
||||||
for (auto const& index: _iap.indices)
|
for (auto const& index: _iap.indices)
|
||||||
{
|
{
|
||||||
nodeFactory.setLocation(index.second);
|
nodeFactory.setLocation(index.location);
|
||||||
expression = nodeFactory.createNode<IndexAccess>(expression, index.first);
|
if (index.end)
|
||||||
|
expression = nodeFactory.createNode<IndexRangeAccess>(expression, index.start, *index.end);
|
||||||
|
else
|
||||||
|
expression = nodeFactory.createNode<IndexAccess>(expression, index.start);
|
||||||
}
|
}
|
||||||
return expression;
|
return expression;
|
||||||
}
|
}
|
||||||
|
@ -162,8 +162,14 @@ private:
|
|||||||
/// or to a type name. For this to be valid, path cannot be empty, but indices can be empty.
|
/// or to a type name. For this to be valid, path cannot be empty, but indices can be empty.
|
||||||
struct IndexAccessedPath
|
struct IndexAccessedPath
|
||||||
{
|
{
|
||||||
|
struct Index
|
||||||
|
{
|
||||||
|
ASTPointer<Expression> start;
|
||||||
|
std::optional<ASTPointer<Expression>> end;
|
||||||
|
langutil::SourceLocation location;
|
||||||
|
};
|
||||||
std::vector<ASTPointer<PrimaryExpression>> path;
|
std::vector<ASTPointer<PrimaryExpression>> path;
|
||||||
std::vector<std::pair<ASTPointer<Expression>, langutil::SourceLocation>> indices;
|
std::vector<Index> indices;
|
||||||
bool empty() const;
|
bool empty() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
contract C {
|
||||||
|
function f(uint256 a, uint256 b) external returns (uint256 c, uint256 d, uint256 e, uint256 f) {
|
||||||
|
(c, d) = abi.decode(msg.data[4:], (uint256, uint256));
|
||||||
|
e = abi.decode(msg.data[4 : 4 + 32], (uint256));
|
||||||
|
f = abi.decode(msg.data[4 + 32 : 4 + 32 + 32], (uint256));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// f(uint256,uint256): 42, 23 -> 42, 23, 42, 23
|
@ -0,0 +1,44 @@
|
|||||||
|
contract C {
|
||||||
|
function f(uint256[] calldata x, uint256 start, uint256 end) external pure {
|
||||||
|
x[start:end];
|
||||||
|
}
|
||||||
|
function g(uint256[] calldata x, uint256 start, uint256 end, uint256 index) external pure returns (uint256, uint256, uint256) {
|
||||||
|
return (x[start:end][index], x[start:][0:end-start][index], x[:end][start:][index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// f(uint256[],uint256,uint256): 0x80, 0, 0, 0, 1, 42 ->
|
||||||
|
// f(uint256[],uint256,uint256): 0x80, 0, 1, 0, 1, 42 ->
|
||||||
|
// f(uint256[],uint256,uint256): 0x80, 0, 2, 0, 1, 42 -> FAILURE
|
||||||
|
// f(uint256[],uint256,uint256): 0x80, 1, 0, 0, 1, 42 -> FAILURE
|
||||||
|
// f(uint256[],uint256,uint256): 0x80, 1, 1, 0, 1, 42 ->
|
||||||
|
// f(uint256[],uint256,uint256): 0x80, 1, 2, 0, 1, 42 -> FAILURE
|
||||||
|
// f(uint256[],uint256,uint256): 0x80, 2, 0, 0, 1, 42 -> FAILURE
|
||||||
|
// f(uint256[],uint256,uint256): 0x80, 2, 1, 0, 1, 42 -> FAILURE
|
||||||
|
// f(uint256[],uint256,uint256): 0x80, 2, 2, 0, 1, 42 -> FAILURE
|
||||||
|
// f(uint256[],uint256,uint256): 0x80, 0, 2, 1, 0, 42 -> FAILURE
|
||||||
|
// f(uint256[],uint256,uint256): 0x80, 1, 2, 0, 2, 42, 23 ->
|
||||||
|
// f(uint256[],uint256,uint256): 0x80, 1, 3, 0, 2, 42, 23 -> FAILURE
|
||||||
|
// f(uint256[],uint256,uint256): 0x80, -1, 0, 0, 1, 42 -> FAILURE
|
||||||
|
// f(uint256[],uint256,uint256): 0x80, -1, -1, 0, 1, 42 -> FAILURE
|
||||||
|
// g(uint256[],uint256,uint256,uint256): 0x80, 0, 1, 0, 1, 42 -> 42, 42, 42
|
||||||
|
// g(uint256[],uint256,uint256,uint256): 0x80, 0, 1, 1, 1, 42 -> FAILURE
|
||||||
|
// g(uint256[],uint256,uint256,uint256): 0x80, 0, 0, 0, 1, 42 -> FAILURE
|
||||||
|
// g(uint256[],uint256,uint256,uint256): 0x80, 1, 1, 0, 1, 42 -> FAILURE
|
||||||
|
// g(uint256[],uint256,uint256,uint256): 0x80, 0, 5, 0, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> 0x4201, 0x4201, 0x4201
|
||||||
|
// g(uint256[],uint256,uint256,uint256): 0x80, 0, 5, 4, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> 0x4205, 0x4205, 0x4205
|
||||||
|
// g(uint256[],uint256,uint256,uint256): 0x80, 0, 5, 5, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> FAILURE
|
||||||
|
// g(uint256[],uint256,uint256,uint256): 0x80, 1, 5, 0, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> 0x4202, 0x4202, 0x4202
|
||||||
|
// g(uint256[],uint256,uint256,uint256): 0x80, 1, 5, 3, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> 0x4205, 0x4205, 0x4205
|
||||||
|
// g(uint256[],uint256,uint256,uint256): 0x80, 1, 5, 4, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> FAILURE
|
||||||
|
// g(uint256[],uint256,uint256,uint256): 0x80, 4, 5, 0, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> 0x4205, 0x4205, 0x4205
|
||||||
|
// g(uint256[],uint256,uint256,uint256): 0x80, 4, 5, 1, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> FAILURE
|
||||||
|
// g(uint256[],uint256,uint256,uint256): 0x80, 5, 5, 0, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> FAILURE
|
||||||
|
// g(uint256[],uint256,uint256,uint256): 0x80, 0, 1, 0, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> 0x4201, 0x4201, 0x4201
|
||||||
|
// g(uint256[],uint256,uint256,uint256): 0x80, 0, 1, 1, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> FAILURE
|
||||||
|
// g(uint256[],uint256,uint256,uint256): 0x80, 0, 1, 0, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> 0x4201, 0x4201, 0x4201
|
||||||
|
// g(uint256[],uint256,uint256,uint256): 0x80, 0, 1, 1, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> FAILURE
|
||||||
|
// g(uint256[],uint256,uint256,uint256): 0x80, 1, 2, 0, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> 0x4202, 0x4202, 0x4202
|
||||||
|
// g(uint256[],uint256,uint256,uint256): 0x80, 1, 2, 1, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> FAILURE
|
||||||
|
// g(uint256[],uint256,uint256,uint256): 0x80, 4, 5, 0, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> 0x4205, 0x4205, 0x4205
|
||||||
|
// g(uint256[],uint256,uint256,uint256): 0x80, 4, 5, 1, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> FAILURE
|
@ -0,0 +1,5 @@
|
|||||||
|
contract C {
|
||||||
|
function f(bytes calldata x) external pure {
|
||||||
|
x[1:2];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
contract C {
|
||||||
|
function f(bytes memory x) public pure {
|
||||||
|
x[1:2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (66-72): Index range access is only supported for dynamic calldata arrays.
|
@ -0,0 +1,8 @@
|
|||||||
|
contract C {
|
||||||
|
bytes x;
|
||||||
|
function f() public view {
|
||||||
|
x[1:2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (65-71): Index range access is only supported for dynamic calldata arrays.
|
@ -0,0 +1,5 @@
|
|||||||
|
contract C {
|
||||||
|
function f(uint256[] calldata x) external pure {
|
||||||
|
x[1:2];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
contract C {
|
||||||
|
function f(uint256[] calldata x) external pure {
|
||||||
|
x[1:2][0];
|
||||||
|
x[1:][0];
|
||||||
|
x[1:][1:2][0];
|
||||||
|
x[1:2][1:][0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
@ -0,0 +1,7 @@
|
|||||||
|
contract C {
|
||||||
|
function f(bytes calldata x) external {
|
||||||
|
bytes memory y = x[1:2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (65-88): Type bytes calldata slice is not implicitly convertible to expected type bytes memory.
|
@ -0,0 +1,7 @@
|
|||||||
|
contract C {
|
||||||
|
function f(uint256[] calldata x) external pure {
|
||||||
|
abi.encode(x[1:2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (85-91): This type cannot be encoded.
|
@ -0,0 +1,7 @@
|
|||||||
|
contract C {
|
||||||
|
function f(bytes calldata x) external {
|
||||||
|
return this.f(x[1:2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (79-85): Invalid type for argument in function call. Invalid implicit conversion from bytes calldata slice to bytes memory requested.
|
@ -0,0 +1,7 @@
|
|||||||
|
contract C {
|
||||||
|
function f(uint256[42] calldata x) external pure {
|
||||||
|
x[1:2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (76-82): Index range access is only supported for dynamic calldata arrays.
|
@ -0,0 +1,7 @@
|
|||||||
|
contract C {
|
||||||
|
function f(uint256[] memory x) public pure {
|
||||||
|
x[1:2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (70-76): Index range access is only supported for dynamic calldata arrays.
|
@ -0,0 +1,7 @@
|
|||||||
|
contract C {
|
||||||
|
function f(uint256[42] memory x) public pure {
|
||||||
|
x[1:2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (72-78): Index range access is only supported for dynamic calldata arrays.
|
@ -0,0 +1,7 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public pure {
|
||||||
|
1[1:];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (52-57): Index range access is only possible for arrays and array slices.
|
@ -0,0 +1,8 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public pure {
|
||||||
|
bytes memory y;
|
||||||
|
y[1:2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (76-82): Index range access is only supported for dynamic calldata arrays.
|
@ -0,0 +1,8 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public pure {
|
||||||
|
string memory y;
|
||||||
|
y[1:2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (77-83): Index range access is only supported for dynamic calldata arrays.
|
@ -0,0 +1,7 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public pure {
|
||||||
|
""[1:];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (52-58): Index range access is only possible for arrays and array slices.
|
@ -0,0 +1,8 @@
|
|||||||
|
contract C {
|
||||||
|
uint256[] x;
|
||||||
|
function f() public view {
|
||||||
|
x[1:2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (69-75): Index range access is only supported for dynamic calldata arrays.
|
@ -0,0 +1,8 @@
|
|||||||
|
contract C {
|
||||||
|
uint256[42] x;
|
||||||
|
function f() public view {
|
||||||
|
x[1:2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (71-77): Index range access is only supported for dynamic calldata arrays.
|
@ -0,0 +1,8 @@
|
|||||||
|
contract C {
|
||||||
|
function f(bool cond, bytes calldata x) external pure {
|
||||||
|
bytes1 a = x[cond ? 1 : 2]; a;
|
||||||
|
abi.decode(x[cond ? 1 : 2 : ], (uint256));
|
||||||
|
abi.decode(x[cond ? 1 : 2 : cond ? 3 : 4], (uint256));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
@ -0,0 +1,14 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public pure {
|
||||||
|
uint[] memory x;
|
||||||
|
uint[1:](x);
|
||||||
|
uint[1:2](x);
|
||||||
|
uint[][1:](x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError: (77-85): Types cannot be sliced.
|
||||||
|
// TypeError: (77-88): Explicit type conversion not allowed from "uint256[] memory" to "uint256".
|
||||||
|
// TypeError: (98-107): Types cannot be sliced.
|
||||||
|
// TypeError: (98-110): Explicit type conversion not allowed from "uint256[] memory" to "uint256".
|
||||||
|
// TypeError: (120-130): Types cannot be sliced.
|
12
test/libsolidity/syntaxTests/parsing/array_range_nested.sol
Normal file
12
test/libsolidity/syntaxTests/parsing/array_range_nested.sol
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
contract C {
|
||||||
|
function f(uint256[][] calldata x) external pure {
|
||||||
|
x[0][1:2];
|
||||||
|
x[1:2][1:2];
|
||||||
|
uint256 a = x[1:2][1:2][1:][3:][0][2];
|
||||||
|
uint256 b = x[1:][3:4][1][1:][2:3][0];
|
||||||
|
a; b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments.
|
@ -0,0 +1,6 @@
|
|||||||
|
contract C {
|
||||||
|
function f(uint256[] calldata x) external pure {
|
||||||
|
x[:][:10];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
14
test/libsolidity/syntaxTests/parsing/array_type_range.sol
Normal file
14
test/libsolidity/syntaxTests/parsing/array_type_range.sol
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public pure {
|
||||||
|
uint[][1:] memory x;
|
||||||
|
uint[][1:2] memory x;
|
||||||
|
uint[1:] memory x;
|
||||||
|
uint[1:2] memory x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----
|
||||||
|
// ParserError: (52-62): Expected array length expression.
|
||||||
|
// ParserError: (81-92): Expected array length expression.
|
||||||
|
// ParserError: (111-119): Expected array length expression.
|
||||||
|
// ParserError: (138-147): Expected array length expression.
|
@ -4,5 +4,5 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// TypeError: (57-61): Invalid type for argument in function call. Invalid implicit conversion from type(uint256) to bytes memory requested.
|
// TypeError: (57-61): The first argument to "abi.decode" must be implicitly convertible to bytes memory or bytes calldata, but is of type type(uint256).
|
||||||
// TypeError: (63-67): The second argument to "abi.decode" has to be a tuple of types.
|
// TypeError: (63-67): The second argument to "abi.decode" has to be a tuple of types.
|
||||||
|
Loading…
Reference in New Issue
Block a user