Initial introduction of array slices with partial implementation for dynamic calldata arrays.

This commit is contained in:
Daniel Kirchner
2019-09-13 10:57:53 +02:00
parent 50ce3b0ac8
commit 4782c800ec
50 changed files with 659 additions and 105 deletions
+60 -10
View File
@@ -136,19 +136,17 @@ TypePointers TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall c
);
if (arguments.size() >= 1)
{
BoolResult result = type(*arguments.front())->isImplicitlyConvertibleTo(*TypeProvider::bytesMemory());
if (!result)
m_errorReporter.typeErrorConcatenateDescriptions(
if (
!type(*arguments.front())->isImplicitlyConvertibleTo(*TypeProvider::bytesMemory()) &&
!type(*arguments.front())->isImplicitlyConvertibleTo(*TypeProvider::bytesCalldata())
)
m_errorReporter.typeError(
arguments.front()->location(),
"Invalid type for argument in function call. "
"Invalid implicit conversion from " +
"The first argument to \"abi.decode\" must be implicitly convertible to "
"bytes memory or bytes calldata, but is of type " +
type(*arguments.front())->toString() +
" to bytes memory requested.",
result.message()
"."
);
}
if (arguments.size() < 2)
return {};
@@ -2245,6 +2243,14 @@ bool TypeChecker::visit(IndexAccess const& _access)
Expression const* index = _access.indexExpression();
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:
{
ArrayType const& actualType = dynamic_cast<ArrayType const&>(*baseType);
@@ -2341,6 +2347,50 @@ bool TypeChecker::visit(IndexAccess const& _access)
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)
{
IdentifierAnnotation& annotation = _identifier.annotation();
+1
View File
@@ -136,6 +136,7 @@ private:
void endVisit(NewExpression const& _newExpression) override;
bool visit(MemberAccess const& _memberAccess) override;
bool visit(IndexAccess const& _indexAccess) override;
bool visit(IndexRangeAccess const& _indexRangeAccess) override;
bool visit(Identifier const& _identifier) override;
void endVisit(ElementaryTypeNameExpression const& _expr) override;
void endVisit(Literal const& _literal) override;
+7
View File
@@ -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)
{
solAssert(_modifier.name(), "");
+1
View File
@@ -58,6 +58,7 @@ private:
bool visit(MemberAccess const& _memberAccess) override;
void endVisit(MemberAccess const& _memberAccess) override;
void endVisit(IndexAccess const& _indexAccess) override;
void endVisit(IndexRangeAccess const& _indexAccess) override;
void endVisit(ModifierInvocation const& _modifier) override;
void endVisit(FunctionCall const& _functionCall) override;
void endVisit(InlineAssembly const& _inlineAssembly) override;
+26
View File
@@ -1642,6 +1642,32 @@ private:
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
* or variable references.
+12
View File
@@ -694,6 +694,18 @@ bool ASTJsonConverter::visit(IndexAccess const& _node)
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)
{
Json::Value overloads(Json::arrayValue);
+1
View File
@@ -111,6 +111,7 @@ public:
bool visit(NewExpression const& _node) override;
bool visit(MemberAccess const& _node) override;
bool visit(IndexAccess const& _node) override;
bool visit(IndexRangeAccess const& _node) override;
bool visit(Identifier const& _node) override;
bool visit(ElementaryTypeNameExpression const& _node) override;
bool visit(Literal const& _node) override;
+4
View File
@@ -87,6 +87,7 @@ public:
virtual bool visit(NewExpression& _node) { return visitNode(_node); }
virtual bool visit(MemberAccess& _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(ElementaryTypeNameExpression& _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(MemberAccess& _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(ElementaryTypeNameExpression& _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(MemberAccess 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(ElementaryTypeNameExpression 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(MemberAccess 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(ElementaryTypeNameExpression const& _node) { endVisitNode(_node); }
virtual void endVisit(Literal const& _node) { endVisitNode(_node); }
+26
View File
@@ -783,6 +783,32 @@ void IndexAccess::accept(ASTConstVisitor& _visitor) const
_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)
{
_visitor.visit(*this);
+14
View File
@@ -31,6 +31,7 @@ InaccessibleDynamicType const TypeProvider::m_inaccessibleDynamic{};
/// 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_bytesMemory;
unique_ptr<ArrayType> TypeProvider::m_bytesCalldata;
unique_ptr<ArrayType> TypeProvider::m_stringStorage;
unique_ptr<ArrayType> TypeProvider::m_stringMemory;
@@ -177,6 +178,7 @@ void TypeProvider::reset()
clearCache(m_inaccessibleDynamic);
clearCache(m_bytesStorage);
clearCache(m_bytesMemory);
clearCache(m_bytesCalldata);
clearCache(m_stringStorage);
clearCache(m_stringMemory);
clearCache(m_emptyTuple);
@@ -314,6 +316,13 @@ ArrayType const* TypeProvider::bytesMemory()
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()
{
if (!m_stringStorage)
@@ -500,6 +509,11 @@ ArrayType const* TypeProvider::array(DataLocation _location, Type const* _baseTy
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)
{
return createAndGet<ContractType>(_contractDef, _isSuper);
+4
View File
@@ -68,6 +68,7 @@ public:
static ArrayType const* bytesStorage();
static ArrayType const* bytesMemory();
static ArrayType const* bytesCalldata();
static ArrayType const* stringStorage();
static ArrayType const* stringMemory();
@@ -80,6 +81,8 @@ public:
/// Constructor for a fixed-size array type ("type[20]")
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* address() noexcept { return &m_address; }
@@ -203,6 +206,7 @@ private:
/// These are lazy-initialized because they depend on `byte` being available.
static std::unique_ptr<ArrayType> m_bytesStorage;
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_stringMemory;
+24
View File
@@ -1869,6 +1869,30 @@ std::unique_ptr<ReferenceType> ArrayType::copyForLocation(DataLocation _location
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
{
return (m_super ? "t_super" : "t_contract") + parenthesizeUserIdentifier(m_contract.name()) + to_string(m_contract.id());
+30 -1
View File
@@ -161,7 +161,7 @@ public:
enum class Category
{
Address, Integer, RationalNumber, StringLiteral, Bool, FixedPoint, Array,
Address, Integer, RationalNumber, StringLiteral, Bool, FixedPoint, Array, ArraySlice,
FixedBytes, Contract, Struct, Function, Enum, Tuple,
Mapping, TypeType, Modifier, Magic, Module,
InaccessibleDynamic
@@ -773,6 +773,35 @@ private:
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.
*/
+11
View File
@@ -981,6 +981,17 @@ void CompilerUtils::convertType(
}
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:
{
solAssert(targetTypeCategory == stackTypeCategory, "");
+138 -75
View File
@@ -1079,10 +1079,14 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
else
targetTypes = TypePointers{_functionCall.annotation().type};
if (
*firstArgType == ArrayType(DataLocation::CallData) ||
*firstArgType == ArrayType(DataLocation::CallData, true)
auto referenceType = dynamic_cast<ReferenceType const*>(firstArgType);
referenceType && referenceType->dataStoredIn(DataLocation::CallData)
)
{
solAssert(referenceType->isImplicitlyConvertibleTo(*TypeProvider::bytesCalldata()), "");
utils().convertType(*referenceType, *TypeProvider::bytesCalldata());
utils().abiDecode(targetTypes, false);
}
else
{
utils().convertType(*firstArgType, *TypeProvider::bytesMemory());
@@ -1503,90 +1507,149 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
Type const& baseType = *_indexAccess.baseExpression().annotation().type;
if (baseType.category() == Type::Category::Mapping)
switch (baseType.category())
{
// stack: storage_base_ref
TypePointer keyType = dynamic_cast<MappingType const&>(baseType).keyType();
solAssert(_indexAccess.indexExpression(), "Index expression expected.");
if (keyType->isDynamicallySized())
case Type::Category::Mapping:
{
_indexAccess.indexExpression()->accept(*this);
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
{
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())
// stack: storage_base_ref
TypePointer keyType = dynamic_cast<MappingType const&>(baseType).keyType();
solAssert(_indexAccess.indexExpression(), "Index expression expected.");
if (keyType->isDynamicallySized())
{
solAssert(!arrayType.isString(), "Index access to string is not allowed.");
setLValue<StorageByteArrayElement>(_indexAccess);
_indexAccess.indexExpression()->accept(*this);
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
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);
{
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);
break;
}
}
else if (baseType.category() == Type::Category::FixedBytes)
{
FixedBytesType const& fixedBytesType = dynamic_cast<FixedBytesType const&>(baseType);
solAssert(_indexAccess.indexExpression(), "Index expression expected.");
case Type::Category::ArraySlice:
{
auto const& arrayType = dynamic_cast<ArraySliceType const&>(baseType).arrayType();
solAssert(arrayType.location() == DataLocation::CallData && arrayType.isDynamicallySized(), "");
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();
acceptAndConvert(*_indexAccess.indexExpression(), *TypeProvider::uint256(), true);
ArrayUtils(m_context).accessCallDataArrayElement(arrayType);
break;
m_context << Instruction::BYTE;
utils().leftShiftNumberOnStack(256 - 8);
}
else if (baseType.category() == Type::Category::TypeType)
{
solAssert(baseType.sizeOnStack() == 0, "");
solAssert(_indexAccess.annotation().type->sizeOnStack() == 0, "");
// no-op - this seems to be a lone array type (`structType[];`)
}
case 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.");
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
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;
}
+1
View File
@@ -80,6 +80,7 @@ private:
bool visit(NewExpression const& _newExpression) override;
bool visit(MemberAccess const& _memberAccess) override;
bool visit(IndexAccess const& _indexAccess) override;
bool visit(IndexRangeAccess const& _indexAccess) override;
void endVisit(Identifier const& _identifier) override;
void endVisit(Literal const& _literal) override;
-5
View File
@@ -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)
{
solAssert(!_type.isByteArray(), "");
@@ -941,6 +941,11 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
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)
{
Declaration const* declaration = _identifier.annotation().referencedDeclaration;
@@ -63,6 +63,7 @@ public:
void endVisit(MemberAccess const& _memberAccess) override;
bool visit(InlineAssembly const& _inlineAsm) override;
void endVisit(IndexAccess const& _indexAccess) override;
void endVisit(IndexRangeAccess const& _indexRangeAccess) override;
void endVisit(Identifier const& _identifier) override;
bool visit(Literal const& _literal) override;
+9
View File
@@ -767,6 +767,15 @@ void SMTEncoder::endVisit(IndexAccess const& _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()
{
m_arrayAssignmentHappened = true;
+1
View File
@@ -86,6 +86,7 @@ protected:
void endVisit(Return const& _node) override;
bool visit(MemberAccess const& _node) override;
void endVisit(IndexAccess const& _node) override;
void endVisit(IndexRangeAccess const& _node) override;
bool visit(InlineAssembly const& _node) override;
/// Do not visit subtree if node is a RationalNumber.
+43 -12
View File
@@ -1607,11 +1607,24 @@ ASTPointer<Expression> Parser::parseLeftHandSideExpression(
{
m_scanner->next();
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();
nodeFactory.markEndPosition();
expectToken(Token::RBrack);
expression = nodeFactory.createNode<IndexAccess>(expression, index);
if (m_scanner->currentToken() == Token::Colon)
{
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;
}
case Token::Period:
@@ -1861,12 +1874,25 @@ Parser::IndexAccessedPath Parser::parseIndexAccessedPath()
{
expectToken(Token::LBrack);
ASTPointer<Expression> index;
if (m_scanner->currentToken() != Token::RBrack)
if (m_scanner->currentToken() != Token::RBrack && m_scanner->currentToken() != Token::Colon)
index = parseExpression();
SourceLocation indexLocation = iap.path.front()->location();
indexLocation.end = endPosition();
iap.indices.emplace_back(index, indexLocation);
expectToken(Token::RBrack);
if (m_scanner->currentToken() == Token::Colon)
{
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;
@@ -1898,8 +1924,10 @@ ASTPointer<TypeName> Parser::typeNameFromIndexAccessStructure(Parser::IndexAcces
}
for (auto const& lengthExpression: _iap.indices)
{
nodeFactory.setLocation(lengthExpression.second);
type = nodeFactory.createNode<ArrayTypeName>(type, lengthExpression.first);
if (lengthExpression.end)
parserError(lengthExpression.location, "Expected array length expression.");
nodeFactory.setLocation(lengthExpression.location);
type = nodeFactory.createNode<ArrayTypeName>(type, lengthExpression.start);
}
return type;
}
@@ -1927,8 +1955,11 @@ ASTPointer<Expression> Parser::expressionFromIndexAccessStructure(
}
for (auto const& index: _iap.indices)
{
nodeFactory.setLocation(index.second);
expression = nodeFactory.createNode<IndexAccess>(expression, index.first);
nodeFactory.setLocation(index.location);
if (index.end)
expression = nodeFactory.createNode<IndexRangeAccess>(expression, index.start, *index.end);
else
expression = nodeFactory.createNode<IndexAccess>(expression, index.start);
}
return expression;
}
+7 -1
View File
@@ -162,8 +162,14 @@ private:
/// or to a type name. For this to be valid, path cannot be empty, but indices can be empty.
struct IndexAccessedPath
{
struct Index
{
ASTPointer<Expression> start;
std::optional<ASTPointer<Expression>> end;
langutil::SourceLocation location;
};
std::vector<ASTPointer<PrimaryExpression>> path;
std::vector<std::pair<ASTPointer<Expression>, langutil::SourceLocation>> indices;
std::vector<Index> indices;
bool empty() const;
};